From 3002149fcebfdb23e22e44abe82c4f1bc880237e Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Sun, 21 Jun 2026 23:22:22 +0900 Subject: [PATCH] fix: dry-run mock KIS validation in workflows --- .gitea/workflows/ci.yml | 2 +- .gitea/workflows/kis_data_collection.yml | 3 +- .../workflows/qualitative_sell_strategy.yml | 2 +- .../CHANGESET_COMMIT_PR_SUMMARY_2026-06-21.md | 7 +- docs/GITEA_VARIABLES_RUNBOOK.md | 4 +- docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md | 66 +++++++++++++++++++ .../test_validate_kis_api_credentials_v1.py | 5 +- tools/validate_kis_api_credentials_v1.py | 11 ++-- 8 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index c658ce9..2d1bc1c 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: env: KIS_APP_Key_TEST: ${{ vars.KIS_APP_KEY_TEST }} KIS_APP_Secret_TEST: ${{ vars.KIS_APP_SECRET_TEST }} - run: python3 tools/validate_kis_api_credentials_v1.py --account mock --ticker 005930 + run: python3 tools/validate_kis_api_credentials_v1.py --account mock --ticker 005930 --dry-run - name: Validate Specs run: python3 tools/validate_specs.py diff --git a/.gitea/workflows/kis_data_collection.yml b/.gitea/workflows/kis_data_collection.yml index cb16eca..36f86ed 100644 --- a/.gitea/workflows/kis_data_collection.yml +++ b/.gitea/workflows/kis_data_collection.yml @@ -142,7 +142,8 @@ jobs: fi python3 tools/validate_kis_api_credentials_v1.py \ --account mock \ - --ticker 005930 + --ticker 005930 \ + --dry-run - name: Collect KIS Market Data to SQLite (read-only) env: diff --git a/.gitea/workflows/qualitative_sell_strategy.yml b/.gitea/workflows/qualitative_sell_strategy.yml index ff58813..8625287 100644 --- a/.gitea/workflows/qualitative_sell_strategy.yml +++ b/.gitea/workflows/qualitative_sell_strategy.yml @@ -110,7 +110,7 @@ jobs: echo "::error::Gitea variable KIS_APP_SECRET_TEST is missing or empty" exit 1 fi - python3 tools/validate_kis_api_credentials_v1.py --account mock --ticker 005930 + python3 tools/validate_kis_api_credentials_v1.py --account mock --ticker 005930 --dry-run - name: Build Qualitative Sell Inputs (batch) env: diff --git a/docs/CHANGESET_COMMIT_PR_SUMMARY_2026-06-21.md b/docs/CHANGESET_COMMIT_PR_SUMMARY_2026-06-21.md index 54adf53..0bc92c3 100644 --- a/docs/CHANGESET_COMMIT_PR_SUMMARY_2026-06-21.md +++ b/docs/CHANGESET_COMMIT_PR_SUMMARY_2026-06-21.md @@ -2,14 +2,15 @@ ## Title -`Synology CI dependency hardening for snapshot_admin and KIS validation` +`Synology mock KIS validation dry-run and workflow dependency hardening` ## Short Body - `src/quant_engine/snapshot_admin_store_v1.py` no longer imports `zoneinfo`, so the Synology Python 3.8.12 runner can import the snapshot admin store without crashing. - `src/quant_engine/kis_api_client_v1.py` now lazy-loads `requests`, which keeps the module importable in tests and turns missing dependency failures into explicit runtime validation errors. - `.gitea/workflows/ci.yml`, `.gitea/workflows/kis_data_collection.yml`, and `.gitea/workflows/qualitative_sell_strategy.yml` now reinstall their Python dependencies on every run so cached venvs cannot hide missing packages. -- `.gitea/workflows/snapshot_admin.yml` now has a Python setup step that installs `pyyaml` before the snapshot admin workflow validators run. +- `tools/validate_kis_api_credentials_v1.py` now supports `--dry-run`, and the mock validation steps in the workflows use it so repo-variable checks no longer depend on live KIS API access. +- `docs/GITEA_VARIABLES_RUNBOOK.md` and `docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md` now say the mock credential check is dry-run only. - `docs/SYNOLOGY_SNAPSHOT_ADMIN_FINAL_EXECUTION_ONE_PAGER.md` remains the compact NAS field run sheet for the `WBS-7.9` live verification sequence. - `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md` points at the one-page run sheet and keeps the evidence rule explicit so `WBS-7.9` stays open until NAS-side verification is archived. - `docs/ROADMAP_WBS.md` still states the `WBS-4.1 -> WBS-4.2 -> WBS-4.3` wait order explicitly and separates loopback smoke success from actual NAS live verification. @@ -18,8 +19,8 @@ - `python tools/validate_snapshot_admin_web_v1.py` - `python -m pytest tests/unit/test_snapshot_admin_web_v1.py -q` -- `python tools/validate_snapshot_admin_workflow_v1.py` - `python tools/validate_platform_transition_wbs_v1.py` +- `python tools/validate_snapshot_admin_workflow_v1.py` - Local HTTP smoke against `snapshot_admin_server_v1.py`: - unauthenticated `GET /api/state` returned `401` - authenticated `GET /api/state` returned `200` diff --git a/docs/GITEA_VARIABLES_RUNBOOK.md b/docs/GITEA_VARIABLES_RUNBOOK.md index 326c0b1..e7c8a47 100644 --- a/docs/GITEA_VARIABLES_RUNBOOK.md +++ b/docs/GITEA_VARIABLES_RUNBOOK.md @@ -18,12 +18,12 @@ Short operator flow for KIS variable-backed workflows. ## Run order 1. Trigger `.gitea/workflows/kis_data_collection.yml` with `workflow_dispatch`. -2. Confirm the mock credential step passes. +2. Confirm the mock credential step passes in `--dry-run` mode. 3. Confirm the real collection step writes: - `Temp/kis_data_collection_v1.json` - `outputs/kis_data_collection/kis_data_collection.db` 4. Trigger `.gitea/workflows/qualitative_sell_strategy.yml`. -5. Confirm the mock credential step passes. +5. Confirm the mock credential step passes in `--dry-run` mode. 6. Confirm the batch build step sees `KIS_APP_KEY` and `KIS_APP_SECRET`. ## If it fails diff --git a/docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md b/docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md new file mode 100644 index 0000000..7d6c88a --- /dev/null +++ b/docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md @@ -0,0 +1,66 @@ +# Gitea Variables Smoke Checklist + +Use this after registering values in `Settings > Actions > Variables`. + +## Operator Quick Run + +1. Confirm the four KIS variables exist. +2. Confirm the seed snapshot exists as either `GatherTradingData.json` or `GatherTradingData.xlsx`. +3. Trigger `kis_data_collection.yml` manually. +4. Confirm the credential step passes in `--dry-run` mode. +5. Confirm the SQLite artifact is written. +6. Trigger `qualitative_sell_strategy.yml` manually. +7. Confirm the mock credential step passes in `--dry-run` mode and the batch build step resolves the same variables. +8. If any step fails, check the troubleshooting section below. + +See also: + +- [Runbook](/C:/Temp/data_feed/docs/GITEA_VARIABLES_RUNBOOK.md) +- [Failure Analysis](/C:/Temp/data_feed/docs/GITEA_VARIABLES_FAILURE_ANALYSIS.md) + +## Variable names to verify + +- `KIS_APP_KEY_TEST` +- `KIS_APP_SECRET_TEST` +- `KIS_APP_KEY` +- `KIS_APP_SECRET` + +## Expected workflow consumers + +- `.gitea/workflows/kis_data_collection.yml` +- `.gitea/workflows/qualitative_sell_strategy.yml` +- `.gitea/workflows/ci.yml` + +## Smoke test steps + +1. Open `Settings > Actions > Variables` and confirm the four KIS variables exist. +2. Trigger `.gitea/workflows/kis_data_collection.yml` with `workflow_dispatch`. +3. Check the job log for the credential validation step. +4. Check the collection step. +5. Confirm the job writes: + - `Temp/kis_data_collection_v1.json` + - `outputs/kis_data_collection/kis_data_collection.db` +6. Trigger `.gitea/workflows/qualitative_sell_strategy.yml`. +7. Confirm the mock credential validation step reads the same variable names. +8. Confirm the batch build step sees `KIS_APP_KEY` and `KIS_APP_SECRET`. +9. If the job fails, inspect whether the variables are missing, renamed, or empty. + +## Pass criteria + +- The workflow no longer references `secrets.KIS_APP_*` for KIS values. +- The job starts without variable resolution errors. +- The collector outputs are written successfully. +- The log shows the KIS credential validation step running with the configured variables. + +## Troubleshooting + +- Missing variable name: Gitea variable key does not match the exact `KIS_APP_*` spelling. +- Empty variable: the workflow resolves the name, but the Python loader treats the value as missing. +- Wrong scope: the variable exists in another repository or organization scope, not this repo. +- Runner mismatch: the job runs on a stale self-hosted runner cache or old workflow revision. + +## Notes + +- Gitea variables are repository-scoped when configured in the repo settings. +- Variable names are case-sensitive. +- Empty values behave as missing values for the Python loader. diff --git a/tests/unit/test_validate_kis_api_credentials_v1.py b/tests/unit/test_validate_kis_api_credentials_v1.py index 7511fa4..f100c29 100644 --- a/tests/unit/test_validate_kis_api_credentials_v1.py +++ b/tests/unit/test_validate_kis_api_credentials_v1.py @@ -25,8 +25,8 @@ def test_validate_kis_api_credentials_writes_pass_json(tmp_path, monkeypatch): monkeypatch.setenv("KIS_APP_Key_TEST", "mock-key") monkeypatch.setenv("KIS_APP_Secret_TEST", "mock-secret") monkeypatch.setattr(validator, "KisCredentials", type("CredFactory", (), {"load": staticmethod(lambda account: _FakeCreds(account))})) - monkeypatch.setattr(validator, "get_current_price", lambda creds, ticker: {"ticker": ticker, "price": 1000}) - monkeypatch.setattr(sys, "argv", ["validate_kis_api_credentials_v1.py", "--account", "mock", "--ticker", "005930", "--output", str(out)]) + monkeypatch.setattr(validator, "get_current_price", lambda creds, ticker: (_ for _ in ()).throw(RuntimeError("network should not be called in dry-run"))) + monkeypatch.setattr(sys, "argv", ["validate_kis_api_credentials_v1.py", "--account", "mock", "--ticker", "005930", "--dry-run", "--output", str(out)]) rc = validator.main() payload = json.loads(out.read_text(encoding="utf-8")) @@ -35,6 +35,7 @@ def test_validate_kis_api_credentials_writes_pass_json(tmp_path, monkeypatch): assert payload["gate"] == "PASS" assert payload["evidence"]["account"] == "mock" assert payload["evidence"]["ticker"] == "005930" + assert payload["evidence"]["dry_run"] is True def test_validate_kis_api_credentials_fails_when_api_call_errors(tmp_path, monkeypatch): diff --git a/tools/validate_kis_api_credentials_v1.py b/tools/validate_kis_api_credentials_v1.py index 5940fac..1ec25ad 100644 --- a/tools/validate_kis_api_credentials_v1.py +++ b/tools/validate_kis_api_credentials_v1.py @@ -50,6 +50,7 @@ def main() -> int: ap = argparse.ArgumentParser(description="Validate KIS API credentials using the read-only quotations API.") ap.add_argument("--account", choices=["mock", "real"], default="mock") ap.add_argument("--ticker", default="005930") + ap.add_argument("--dry-run", action="store_true", help="Validate env wiring without calling the live KIS API.") ap.add_argument("--output", type=Path, default=ROOT / "Temp" / "kis_api_credentials_validation_v1.json") args = ap.parse_args() @@ -87,10 +88,12 @@ def main() -> int: errors.append("domain_mismatch") if not evidence["env_match"]["app_key"] or not evidence["env_match"]["app_secret"]: errors.append("selected_env_mismatch") - response = get_current_price(creds, args.ticker) - evidence["response_keys"] = sorted(response.keys()) - if not isinstance(response, dict) or not response: - errors.append("empty_response") + evidence["dry_run"] = bool(args.dry_run) + if not args.dry_run: + response = get_current_price(creds, args.ticker) + evidence["response_keys"] = sorted(response.keys()) + if not isinstance(response, dict) or not response: + errors.append("empty_response") except Exception as exc: # noqa: BLE001 errors.append(str(exc))