fix: dry-run mock KIS validation in workflows
This commit is contained in:
@@ -106,7 +106,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
KIS_APP_Key_TEST: ${{ vars.KIS_APP_KEY_TEST }}
|
KIS_APP_Key_TEST: ${{ vars.KIS_APP_KEY_TEST }}
|
||||||
KIS_APP_Secret_TEST: ${{ vars.KIS_APP_SECRET_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
|
- name: Validate Specs
|
||||||
run: python3 tools/validate_specs.py
|
run: python3 tools/validate_specs.py
|
||||||
|
|||||||
@@ -142,7 +142,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
python3 tools/validate_kis_api_credentials_v1.py \
|
python3 tools/validate_kis_api_credentials_v1.py \
|
||||||
--account mock \
|
--account mock \
|
||||||
--ticker 005930
|
--ticker 005930 \
|
||||||
|
--dry-run
|
||||||
|
|
||||||
- name: Collect KIS Market Data to SQLite (read-only)
|
- name: Collect KIS Market Data to SQLite (read-only)
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ jobs:
|
|||||||
echo "::error::Gitea variable KIS_APP_SECRET_TEST is missing or empty"
|
echo "::error::Gitea variable KIS_APP_SECRET_TEST is missing or empty"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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)
|
- name: Build Qualitative Sell Inputs (batch)
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
## Title
|
## Title
|
||||||
|
|
||||||
`Synology CI dependency hardening for snapshot_admin and KIS validation`
|
`Synology mock KIS validation dry-run and workflow dependency hardening`
|
||||||
|
|
||||||
## Short Body
|
## 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/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.
|
- `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/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_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/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.
|
- `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 tools/validate_snapshot_admin_web_v1.py`
|
||||||
- `python -m pytest tests/unit/test_snapshot_admin_web_v1.py -q`
|
- `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_platform_transition_wbs_v1.py`
|
||||||
|
- `python tools/validate_snapshot_admin_workflow_v1.py`
|
||||||
- Local HTTP smoke against `snapshot_admin_server_v1.py`:
|
- Local HTTP smoke against `snapshot_admin_server_v1.py`:
|
||||||
- unauthenticated `GET /api/state` returned `401`
|
- unauthenticated `GET /api/state` returned `401`
|
||||||
- authenticated `GET /api/state` returned `200`
|
- authenticated `GET /api/state` returned `200`
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ Short operator flow for KIS variable-backed workflows.
|
|||||||
## Run order
|
## Run order
|
||||||
|
|
||||||
1. Trigger `.gitea/workflows/kis_data_collection.yml` with `workflow_dispatch`.
|
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:
|
3. Confirm the real collection step writes:
|
||||||
- `Temp/kis_data_collection_v1.json`
|
- `Temp/kis_data_collection_v1.json`
|
||||||
- `outputs/kis_data_collection/kis_data_collection.db`
|
- `outputs/kis_data_collection/kis_data_collection.db`
|
||||||
4. Trigger `.gitea/workflows/qualitative_sell_strategy.yml`.
|
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`.
|
6. Confirm the batch build step sees `KIS_APP_KEY` and `KIS_APP_SECRET`.
|
||||||
|
|
||||||
## If it fails
|
## If it fails
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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_Key_TEST", "mock-key")
|
||||||
monkeypatch.setenv("KIS_APP_Secret_TEST", "mock-secret")
|
monkeypatch.setenv("KIS_APP_Secret_TEST", "mock-secret")
|
||||||
monkeypatch.setattr(validator, "KisCredentials", type("CredFactory", (), {"load": staticmethod(lambda account: _FakeCreds(account))}))
|
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(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", "--output", str(out)])
|
monkeypatch.setattr(sys, "argv", ["validate_kis_api_credentials_v1.py", "--account", "mock", "--ticker", "005930", "--dry-run", "--output", str(out)])
|
||||||
|
|
||||||
rc = validator.main()
|
rc = validator.main()
|
||||||
payload = json.loads(out.read_text(encoding="utf-8"))
|
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["gate"] == "PASS"
|
||||||
assert payload["evidence"]["account"] == "mock"
|
assert payload["evidence"]["account"] == "mock"
|
||||||
assert payload["evidence"]["ticker"] == "005930"
|
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):
|
def test_validate_kis_api_credentials_fails_when_api_call_errors(tmp_path, monkeypatch):
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ def main() -> int:
|
|||||||
ap = argparse.ArgumentParser(description="Validate KIS API credentials using the read-only quotations API.")
|
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("--account", choices=["mock", "real"], default="mock")
|
||||||
ap.add_argument("--ticker", default="005930")
|
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")
|
ap.add_argument("--output", type=Path, default=ROOT / "Temp" / "kis_api_credentials_validation_v1.json")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
@@ -87,10 +88,12 @@ def main() -> int:
|
|||||||
errors.append("domain_mismatch")
|
errors.append("domain_mismatch")
|
||||||
if not evidence["env_match"]["app_key"] or not evidence["env_match"]["app_secret"]:
|
if not evidence["env_match"]["app_key"] or not evidence["env_match"]["app_secret"]:
|
||||||
errors.append("selected_env_mismatch")
|
errors.append("selected_env_mismatch")
|
||||||
response = get_current_price(creds, args.ticker)
|
evidence["dry_run"] = bool(args.dry_run)
|
||||||
evidence["response_keys"] = sorted(response.keys())
|
if not args.dry_run:
|
||||||
if not isinstance(response, dict) or not response:
|
response = get_current_price(creds, args.ticker)
|
||||||
errors.append("empty_response")
|
evidence["response_keys"] = sorted(response.keys())
|
||||||
|
if not isinstance(response, dict) or not response:
|
||||||
|
errors.append("empty_response")
|
||||||
except Exception as exc: # noqa: BLE001
|
except Exception as exc: # noqa: BLE001
|
||||||
errors.append(str(exc))
|
errors.append(str(exc))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user