diff --git a/.github/workflows/sync-cloud-run-env.yml b/.github/workflows/sync-cloud-run-env.yml index 7fe0c47..c975399 100644 --- a/.github/workflows/sync-cloud-run-env.yml +++ b/.github/workflows/sync-cloud-run-env.yml @@ -4,6 +4,43 @@ on: push: branches: [ main ] workflow_dispatch: + inputs: + target: + description: "Deployment target to run. Use hk-verify for an isolated HK dry-run Cloud Run service." + required: true + type: choice + default: configured + options: + - configured + - hk-verify + cloud_run_region: + description: "Cloud Run region for hk-verify. Leave blank to use repository/environment variables." + required: false + type: string + cloud_run_service: + description: "Cloud Run service for hk-verify." + required: false + type: string + default: interactive-brokers-hk-verify-service + account_group: + description: "IBKR account-group selector for hk-verify." + required: false + type: string + default: hk-verify + account_group_config_secret_name: + description: "Optional Secret Manager secret name for the IBKR account-group config." + required: false + type: string + deploy_image: + description: "Build and deploy the container image for hk-verify." + required: true + type: boolean + default: true + sync_env: + description: "Sync hk-verify Cloud Run environment variables." + required: true + type: boolean + default: true env: GCP_PROJECT_ID: interactivebrokersquant @@ -23,6 +60,13 @@ jobs: ENABLE_GITHUB_CLOUD_RUN_DEPLOY: ${{ vars.ENABLE_GITHUB_CLOUD_RUN_DEPLOY }} ENABLE_GITHUB_ENV_SYNC: ${{ vars.ENABLE_GITHUB_ENV_SYNC }} ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION: ${{ vars.ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION }} + WORKFLOW_TARGET: ${{ inputs.target || 'configured' }} + INPUT_CLOUD_RUN_REGION: ${{ inputs.cloud_run_region }} + INPUT_CLOUD_RUN_SERVICE: ${{ inputs.cloud_run_service }} + INPUT_ACCOUNT_GROUP: ${{ inputs.account_group }} + INPUT_ACCOUNT_GROUP_CONFIG_SECRET_NAME: ${{ inputs.account_group_config_secret_name }} + INPUT_DEPLOY_IMAGE: ${{ inputs.deploy_image }} + INPUT_SYNC_ENV: ${{ inputs.sync_env }} GCP_ARTIFACT_REGISTRY_HOSTNAME: ${{ vars.GCP_ARTIFACT_REGISTRY_HOSTNAME }} CLOUD_RUN_REGION: ${{ vars.CLOUD_RUN_REGION }} CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }} @@ -108,12 +152,21 @@ jobs: exit 0 fi - if [ "${ENABLE_GITHUB_CLOUD_RUN_DEPLOY:-}" = "true" ]; then - deploy_enabled=true - fi + if [ "${GITHUB_EVENT_NAME:-}" = "workflow_dispatch" ] && [ "${WORKFLOW_TARGET:-configured}" = "hk-verify" ]; then + if [ "${INPUT_DEPLOY_IMAGE:-true}" = "true" ]; then + deploy_enabled=true + fi + if [ "${INPUT_SYNC_ENV:-true}" = "true" ]; then + env_sync_enabled=true + fi + else + if [ "${ENABLE_GITHUB_CLOUD_RUN_DEPLOY:-}" = "true" ]; then + deploy_enabled=true + fi - if [ "${ENABLE_GITHUB_ENV_SYNC:-}" = "true" ]; then - env_sync_enabled=true + if [ "${ENABLE_GITHUB_ENV_SYNC:-}" = "true" ]; then + env_sync_enabled=true + fi fi echo "deploy_enabled=${deploy_enabled}" >> "$GITHUB_OUTPUT" @@ -131,6 +184,60 @@ jobs: if: steps.config.outputs.enabled == 'true' uses: actions/checkout@v6 + - name: Apply HK verify-only dispatch defaults + if: steps.config.outputs.enabled == 'true' && github.event_name == 'workflow_dispatch' && inputs.target == 'hk-verify' + run: | + set -euo pipefail + + service="${INPUT_CLOUD_RUN_SERVICE:-interactive-brokers-hk-verify-service}" + account_group="${INPUT_ACCOUNT_GROUP:-hk-verify}" + region="${INPUT_CLOUD_RUN_REGION:-${CLOUD_RUN_REGION:-}}" + runtime_target="$(SERVICE="${service}" ACCOUNT_GROUP_VALUE="${account_group}" python3 - <<'PY' + import json + import os + + print( + json.dumps( + { + "platform_id": "ibkr", + "strategy_profile": "hk_listed_global_etf_rotation", + "deployment_selector": os.environ["ACCOUNT_GROUP_VALUE"], + "account_scope": os.environ["ACCOUNT_GROUP_VALUE"], + "execution_mode": "paper", + "dry_run_only": True, + "service_name": os.environ["SERVICE"], + }, + separators=(",", ":"), + ) + ) + PY + )" + + { + echo "CLOUD_RUN_SERVICE=${service}" + echo "CLOUD_RUN_SERVICES=" + echo "CLOUD_RUN_SERVICE_TARGETS_JSON=" + echo "ACCOUNT_GROUP=${account_group}" + echo "RUNTIME_TARGET_JSON=${runtime_target}" + echo "IBKR_DRY_RUN_ONLY=true" + echo "IBKR_MARKET=HK" + echo "IBKR_MARKET_CALENDAR=XHKG" + echo "IBKR_MARKET_CURRENCY=HKD" + echo "IBKR_MARKET_DATA_SYMBOL_SUFFIX=.HK" + echo "IBKR_MARKET_EXCHANGE=SEHK" + echo "IBKR_MARKET_TIMEZONE=Asia/Hong_Kong" + } >> "$GITHUB_ENV" + + if [ -n "${region}" ]; then + echo "CLOUD_RUN_REGION=${region}" >> "$GITHUB_ENV" + fi + if [ -n "${INPUT_ACCOUNT_GROUP_CONFIG_SECRET_NAME:-}" ]; then + echo "IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=${INPUT_ACCOUNT_GROUP_CONFIG_SECRET_NAME}" >> "$GITHUB_ENV" + fi + if [ "${INPUT_DEPLOY_IMAGE:-true}" != "true" ]; then + echo "CLOUD_RUN_ENV_SYNC_WAIT_FOR_COMMIT=false" >> "$GITHUB_ENV" + fi + - name: Set up Python for strategy requirement resolution if: steps.config.outputs.env_sync_enabled == 'true' uses: actions/setup-python@v6 diff --git a/README.md b/README.md index cb3d9e7..e073341 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,11 @@ Snapshot-backed profiles use upstream artifacts from `UsEquitySnapshotPipelines` For the HK-equity runtime scope, platform matrix, and env defaults, see [`docs/hk_equity_runtime.md`](docs/hk_equity_runtime.md). -For HK verify-only rollout planning, print the switch plan first instead of changing Cloud Run directly: +For HK verify-only rollout planning, print the switch plan first. To deploy an isolated dry-run service, manually trigger the `Deploy Cloud Run` workflow with `target=hk-verify`: ```bash python scripts/print_strategy_switch_env_plan.py --profile hk_listed_global_etf_rotation --dry-run-only --deployment-selector hk-verify --account-scope hk-verify --account-group hk-verify --service-name interactive-brokers-hk-verify-service --json +gh workflow run sync-cloud-run-env.yml --repo QuantStrategyLab/InteractiveBrokersPlatform -f target=hk-verify -f cloud_run_region= -f cloud_run_service=interactive-brokers-hk-verify-service -f account_group=hk-verify -f account_group_config_secret_name=ibkr-account-groups -f deploy_image=true -f sync_env=true ``` Example runtime pointer: @@ -433,10 +434,11 @@ feature-snapshot 类策略使用 `UsEquitySnapshotPipelines` 或 `HkEquitySnapsh 港股运行时范围、平台矩阵和环境变量默认值见 [`docs/hk_equity_runtime.md`](docs/hk_equity_runtime.md)。 -港股 verify-only 接入先打印切换计划,不直接改 Cloud Run: +港股 verify-only 接入先打印切换计划;如需部署独立 dry-run 服务,再手动触发 `Deploy Cloud Run` workflow 的 `target=hk-verify`: ```bash python scripts/print_strategy_switch_env_plan.py --profile hk_listed_global_etf_rotation --dry-run-only --deployment-selector hk-verify --account-scope hk-verify --account-group hk-verify --service-name interactive-brokers-hk-verify-service --json +gh workflow run sync-cloud-run-env.yml --repo QuantStrategyLab/InteractiveBrokersPlatform -f target=hk-verify -f cloud_run_region= -f cloud_run_service=interactive-brokers-hk-verify-service -f account_group=hk-verify -f account_group_config_secret_name=ibkr-account-groups -f deploy_image=true -f sync_env=true ``` ### 架构 diff --git a/docs/hk_equity_runtime.md b/docs/hk_equity_runtime.md index e5487f5..097e74a 100644 --- a/docs/hk_equity_runtime.md +++ b/docs/hk_equity_runtime.md @@ -92,6 +92,41 @@ python scripts/print_strategy_switch_env_plan.py \ 合并代码或打印计划不会触发生产部署;只有单独执行 Cloud Run env 更新/部署命令才会改变服务配置。 +## 显式部署 HK verify-only Cloud Run + +仓库的 `Deploy Cloud Run` workflow 支持手动 `workflow_dispatch` 目标 `hk-verify`。这个目标会覆盖为独立服务和港股 dry-run 环境: + +- `CLOUD_RUN_SERVICE=interactive-brokers-hk-verify-service`(可通过输入改名) +- `STRATEGY_PROFILE=hk_listed_global_etf_rotation` +- `ACCOUNT_GROUP=hk-verify` +- `RUNTIME_TARGET_JSON.execution_mode=paper`、`dry_run_only=true` +- `IBKR_DRY_RUN_ONLY=true` +- `IBKR_MARKET=HK`、`IBKR_MARKET_EXCHANGE=SEHK`、`IBKR_MARKET_CURRENCY=HKD`、`IBKR_MARKET_DATA_SYMBOL_SUFFIX=.HK` + +手动部署示例: + +```bash +gh workflow run sync-cloud-run-env.yml \ + --repo QuantStrategyLab/InteractiveBrokersPlatform \ + -f target=hk-verify \ + -f cloud_run_region= \ + -f cloud_run_service=interactive-brokers-hk-verify-service \ + -f account_group=hk-verify \ + -f account_group_config_secret_name=ibkr-account-groups \ + -f deploy_image=true \ + -f sync_env=true +``` + +如果只想同步环境变量、不重新部署镜像,可以设置 `-f deploy_image=false -f sync_env=true`;workflow 会跳过 commit wait,避免等待一个并未部署的新 revision。 + +执行前确认: + +- 目标 Cloud Run service 是独立 HK verify service,不是当前生产服务。 +- GitHub 变量或输入里有 `CLOUD_RUN_REGION`、`GLOBAL_TELEGRAM_CHAT_ID`、`NOTIFY_LANG`、`IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME`。 +- `TELEGRAM_TOKEN_SECRET_NAME` 或 `TELEGRAM_TOKEN` 可用。 +- `IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME` 指向的账号组里存在 `hk-verify`,且只绑定预期的 HK paper/verify 账号。 +- GCP deploy service account 仍只负责部署;IBKR 登录、账号、Gateway 地址等私密配置继续放在 Secret Manager 的 account-group payload。 + ## 订单、组合和行情口径 - 股票订单通过 `Stock(symbol, IBKR_MARKET_EXCHANGE, IBKR_MARKET_CURRENCY)` 构造;港股默认是 `SEHK/HKD`。 diff --git a/scripts/print_strategy_switch_env_plan.py b/scripts/print_strategy_switch_env_plan.py index c50a03f..6a350ea 100644 --- a/scripts/print_strategy_switch_env_plan.py +++ b/scripts/print_strategy_switch_env_plan.py @@ -176,6 +176,14 @@ def build_switch_plan( "checks": HK_DRY_RUN_CHECKS, "blocked_actions": HK_BLOCKED_DRY_RUN_ACTIONS if dry_run_only else [], } + if dry_run_only: + dry_run_plan["workflow_dispatch"] = { + "workflow": "sync-cloud-run-env.yml", + "target": "hk-verify", + "cloud_run_service": service_name or "interactive-brokers-hk-verify-service", + "deploy_image": True, + "sync_env": True, + } notes.append( "HK-equity switch plans are environment plans only; merge alone must not change production Cloud Run." ) diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index e109245..1c57994 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -898,6 +898,13 @@ def test_print_strategy_switch_env_plan_for_hk_global_etf_dry_run(): assert "IBKR_FEATURE_SNAPSHOT_PATH" in plan["remove_if_present"] assert plan["dry_run_plan"]["dry_run_only"] is True assert plan["dry_run_plan"]["verify_only"] is True + assert plan["dry_run_plan"]["workflow_dispatch"] == { + "workflow": "sync-cloud-run-env.yml", + "target": "hk-verify", + "cloud_run_service": "interactive-brokers-hk-verify-service", + "deploy_image": True, + "sync_env": True, + } assert any("lot-size" in check for check in plan["dry_run_plan"]["checks"]) assert any("production Cloud Run" in action for action in plan["dry_run_plan"]["blocked_actions"]) diff --git a/tests/test_sync_cloud_run_env_workflow.sh b/tests/test_sync_cloud_run_env_workflow.sh index 65d56ed..aaa8924 100644 --- a/tests/test_sync_cloud_run_env_workflow.sh +++ b/tests/test_sync_cloud_run_env_workflow.sh @@ -15,6 +15,15 @@ grep -Fq 'uses: actions/setup-python@v6' "$workflow_file" grep -Fq 'python -m pip install -r requirements.txt' "$workflow_file" grep -Fq 'ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION: ${{ vars.ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION }}' "$workflow_file" +grep -Fq 'target:' "$workflow_file" +grep -Fq 'default: hk-verify' "$workflow_file" +grep -Fq 'INPUT_DEPLOY_IMAGE: ${{ inputs.deploy_image }}' "$workflow_file" +grep -Fq 'Apply HK verify-only dispatch defaults' "$workflow_file" +grep -Fq '"strategy_profile": "hk_listed_global_etf_rotation"' "$workflow_file" +grep -Fq 'echo "IBKR_DRY_RUN_ONLY=true"' "$workflow_file" +grep -Fq 'echo "IBKR_MARKET=HK"' "$workflow_file" +grep -Fq 'echo "IBKR_MARKET_EXCHANGE=SEHK"' "$workflow_file" +grep -Fq 'echo "CLOUD_RUN_ENV_SYNC_WAIT_FOR_COMMIT=false" >> "$GITHUB_ENV"' "$workflow_file" grep -Fq 'CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }}' "$workflow_file" grep -Fq 'CLOUD_RUN_SERVICES: ${{ vars.CLOUD_RUN_SERVICES }}' "$workflow_file" grep -Fq 'CLOUD_RUN_SERVICE_TARGETS_JSON: ${{ vars.CLOUD_RUN_SERVICE_TARGETS_JSON }}' "$workflow_file"