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:
Jeffrey (Dongkyu) Kim 2026-05-14 12:31:04 +09:00 committed by GitHub
commit ca9a7df933
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 83 additions and 14 deletions

View file

@ -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

View file

@ -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

View file

@ -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