mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-06-24 02:04:11 +00:00
Keep dev CI/CD workflows plannable
Fix the Python release workflow and ticket helper import behavior so remote CI can run to completion after recent dev merges. Constraint: Python release automation remains scaffold-only until python-packages/* contains a real pyproject.toml Rejected: Installing ad-hoc Python dependencies in CI | the repository does not yet have a Python package dependency contract Confidence: high Scope-risk: narrow Directive: Keep workflow-time package detection in a checked-out job, not job-level hashFiles guards Tested: PR #240 GitHub Actions validate; local npm run ci Not-tested: Actual PyPI publication because no Python package release exists yet
This commit is contained in:
commit
ca9a7df933
3 changed files with 83 additions and 14 deletions
21
.github/workflows/release-python.yml
vendored
21
.github/workflows/release-python.yml
vendored
|
|
@ -16,14 +16,31 @@ permissions:
|
|||
id-token: write
|
||||
|
||||
jobs:
|
||||
detect_python_packages:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
has_python_packages: ${{ steps.detect.outputs.has_python_packages }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: detect
|
||||
shell: bash
|
||||
run: |
|
||||
if find python-packages -mindepth 2 -maxdepth 2 -name pyproject.toml -print -quit | grep -q .; then
|
||||
echo "has_python_packages=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_python_packages=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
scaffold-only:
|
||||
if: ${{ hashFiles('python-packages/**/pyproject.toml') == '' }}
|
||||
needs: detect_python_packages
|
||||
if: ${{ needs.detect_python_packages.outputs.has_python_packages != 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "No Python package exists yet. release-please remains scaffold-only."
|
||||
|
||||
release:
|
||||
if: ${{ hashFiles('python-packages/**/pyproject.toml') != '' }}
|
||||
needs: detect_python_packages
|
||||
if: ${{ needs.detect_python_packages.outputs.has_python_packages == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
|||
|
|
@ -26,7 +26,27 @@ import time
|
|||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
try:
|
||||
import httpx
|
||||
except ModuleNotFoundError: # pragma: no cover - depends on user environment
|
||||
httpx = None
|
||||
|
||||
|
||||
class MissingHttpxError(RuntimeError):
|
||||
"""Raised when the optional httpx runtime dependency is unavailable."""
|
||||
|
||||
|
||||
def _require_httpx():
|
||||
if httpx is None:
|
||||
raise MissingHttpxError(
|
||||
"Python package 'httpx' is required. Install it with: python3 -m pip install httpx"
|
||||
)
|
||||
return httpx
|
||||
|
||||
|
||||
HTTPX_HTTP_ERROR = (
|
||||
getattr(httpx, "HTTPError", MissingHttpxError) if httpx else MissingHttpxError
|
||||
)
|
||||
|
||||
# ── URL Parsing ───────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -98,7 +118,8 @@ INTERPARK_BASE = "https://api-ticketfront.interpark.com"
|
|||
|
||||
class Yes24Client:
|
||||
def __init__(self) -> None:
|
||||
self.http = httpx.Client(
|
||||
http = _require_httpx()
|
||||
self.http = http.Client(
|
||||
headers=HEADERS_YES24, timeout=20, follow_redirects=True
|
||||
)
|
||||
|
||||
|
|
@ -227,7 +248,8 @@ class Yes24Client:
|
|||
|
||||
class InterparkClient:
|
||||
def __init__(self) -> None:
|
||||
self.http = httpx.Client(
|
||||
http = _require_httpx()
|
||||
self.http = http.Client(
|
||||
headers=HEADERS_INTERPARK, timeout=20, follow_redirects=True
|
||||
)
|
||||
|
||||
|
|
@ -332,6 +354,7 @@ def cmd_seats(args: argparse.Namespace) -> int:
|
|||
|
||||
|
||||
def cmd_health(args: argparse.Namespace) -> int:
|
||||
http = _require_httpx()
|
||||
results: dict = {}
|
||||
for name, url in [
|
||||
("yes24",
|
||||
|
|
@ -341,12 +364,12 @@ def cmd_health(args: argparse.Namespace) -> int:
|
|||
]:
|
||||
try:
|
||||
if name == "yes24":
|
||||
r = httpx.post(url, headers=HEADERS_YES24,
|
||||
r = http.post(url, headers=HEADERS_YES24,
|
||||
data={"pGetMode": "days", "pIdPerf": "0",
|
||||
"pPerfMonth": "2000-01", "pIdCode": "",
|
||||
"pIsMania": "0"}, timeout=10)
|
||||
else:
|
||||
r = httpx.get(url, headers=HEADERS_INTERPARK,
|
||||
r = http.get(url, headers=HEADERS_INTERPARK,
|
||||
params={"goodsCode": "00000000",
|
||||
"isBookableDate": "true",
|
||||
"page": "1", "pageSize": "1",
|
||||
|
|
@ -395,7 +418,10 @@ def main(argv: list[str] | None = None) -> int:
|
|||
except ValueError as e:
|
||||
print(f"error: {e}", file=sys.stderr)
|
||||
return 2
|
||||
except httpx.HTTPError as e:
|
||||
except MissingHttpxError as e:
|
||||
print(f"dependency error: {e}", file=sys.stderr)
|
||||
return 4
|
||||
except HTTPX_HTTP_ERROR as e:
|
||||
print(f"http error: {e}", file=sys.stderr)
|
||||
return 3
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,27 @@ import time
|
|||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
try:
|
||||
import httpx
|
||||
except ModuleNotFoundError: # pragma: no cover - depends on user environment
|
||||
httpx = None
|
||||
|
||||
|
||||
class MissingHttpxError(RuntimeError):
|
||||
"""Raised when the optional httpx runtime dependency is unavailable."""
|
||||
|
||||
|
||||
def _require_httpx():
|
||||
if httpx is None:
|
||||
raise MissingHttpxError(
|
||||
"Python package 'httpx' is required. Install it with: python3 -m pip install httpx"
|
||||
)
|
||||
return httpx
|
||||
|
||||
|
||||
HTTPX_HTTP_ERROR = (
|
||||
getattr(httpx, "HTTPError", MissingHttpxError) if httpx else MissingHttpxError
|
||||
)
|
||||
|
||||
# ── URL Parsing ───────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -98,7 +118,8 @@ INTERPARK_BASE = "https://api-ticketfront.interpark.com"
|
|||
|
||||
class Yes24Client:
|
||||
def __init__(self) -> None:
|
||||
self.http = httpx.Client(
|
||||
http = _require_httpx()
|
||||
self.http = http.Client(
|
||||
headers=HEADERS_YES24, timeout=20, follow_redirects=True
|
||||
)
|
||||
|
||||
|
|
@ -227,7 +248,8 @@ class Yes24Client:
|
|||
|
||||
class InterparkClient:
|
||||
def __init__(self) -> None:
|
||||
self.http = httpx.Client(
|
||||
http = _require_httpx()
|
||||
self.http = http.Client(
|
||||
headers=HEADERS_INTERPARK, timeout=20, follow_redirects=True
|
||||
)
|
||||
|
||||
|
|
@ -332,6 +354,7 @@ def cmd_seats(args: argparse.Namespace) -> int:
|
|||
|
||||
|
||||
def cmd_health(args: argparse.Namespace) -> int:
|
||||
http = _require_httpx()
|
||||
results: dict = {}
|
||||
for name, url in [
|
||||
("yes24",
|
||||
|
|
@ -341,12 +364,12 @@ def cmd_health(args: argparse.Namespace) -> int:
|
|||
]:
|
||||
try:
|
||||
if name == "yes24":
|
||||
r = httpx.post(url, headers=HEADERS_YES24,
|
||||
r = http.post(url, headers=HEADERS_YES24,
|
||||
data={"pGetMode": "days", "pIdPerf": "0",
|
||||
"pPerfMonth": "2000-01", "pIdCode": "",
|
||||
"pIsMania": "0"}, timeout=10)
|
||||
else:
|
||||
r = httpx.get(url, headers=HEADERS_INTERPARK,
|
||||
r = http.get(url, headers=HEADERS_INTERPARK,
|
||||
params={"goodsCode": "00000000",
|
||||
"isBookableDate": "true",
|
||||
"page": "1", "pageSize": "1",
|
||||
|
|
@ -395,7 +418,10 @@ def main(argv: list[str] | None = None) -> int:
|
|||
except ValueError as e:
|
||||
print(f"error: {e}", file=sys.stderr)
|
||||
return 2
|
||||
except httpx.HTTPError as e:
|
||||
except MissingHttpxError as e:
|
||||
print(f"dependency error: {e}", file=sys.stderr)
|
||||
return 4
|
||||
except HTTPX_HTTP_ERROR as e:
|
||||
print(f"http error: {e}", file=sys.stderr)
|
||||
return 3
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue