Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 58 additions & 8 deletions .github/workflows/longbridge-api-probe.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,29 @@ jobs:
id-token: write
environment: longbridge-hk
env:
CLOUD_RUN_REGION: ${{ vars.CLOUD_RUN_REGION }}
CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }}
LONGPORT_APP_KEY_SECRET_NAME: ${{ vars.LONGPORT_APP_KEY_SECRET_NAME }}
LONGPORT_APP_SECRET_SECRET_NAME: ${{ vars.LONGPORT_APP_SECRET_SECRET_NAME }}
LONGPORT_SECRET_NAME: ${{ vars.LONGPORT_SECRET_NAME }}
LONGPORT_APP_KEY_VALUE: ${{ secrets.LONGPORT_APP_KEY }}
LONGPORT_APP_SECRET_VALUE: ${{ secrets.LONGPORT_APP_SECRET }}
LONGPORT_ACCESS_TOKEN_VALUE: ${{ secrets.LONGPORT_ACCESS_TOKEN }}
steps:
- name: Validate inputs
run: |
set -euo pipefail

missing_vars=()
for var_name in LONGPORT_APP_KEY_SECRET_NAME LONGPORT_APP_SECRET_SECRET_NAME LONGPORT_SECRET_NAME; do
if [ -z "${!var_name:-}" ]; then
missing_vars+=("${var_name}")
fi
done
if [ -z "${LONGPORT_APP_KEY_VALUE:-}" ] && [ -z "${LONGPORT_APP_KEY_SECRET_NAME:-}" ]; then
missing_vars+=("LONGPORT_APP_KEY secret or LONGPORT_APP_KEY_SECRET_NAME variable")
fi
if [ -z "${LONGPORT_APP_SECRET_VALUE:-}" ] && [ -z "${LONGPORT_APP_SECRET_SECRET_NAME:-}" ]; then
missing_vars+=("LONGPORT_APP_SECRET secret or LONGPORT_APP_SECRET_SECRET_NAME variable")
fi
if [ -z "${LONGPORT_ACCESS_TOKEN_VALUE:-}" ] && [ -z "${LONGPORT_SECRET_NAME:-}" ]; then
missing_vars+=("LONGPORT_ACCESS_TOKEN secret or LONGPORT_SECRET_NAME variable")
fi

if [ "${#missing_vars[@]}" -gt 0 ]; then
printf 'Missing longbridge-hk variables:\n' >&2
Expand Down Expand Up @@ -82,9 +91,50 @@ jobs:
run: |
set -euo pipefail

longport_app_key="$(gcloud secrets versions access latest --secret="${LONGPORT_APP_KEY_SECRET_NAME}")"
longport_app_secret="$(gcloud secrets versions access latest --secret="${LONGPORT_APP_SECRET_SECRET_NAME}")"
longport_access_token="$(gcloud secrets versions access latest --secret="${LONGPORT_SECRET_NAME}")"
read_secret_manager_value() {
local secret_name="$1"
local error_file
error_file="$(mktemp)"
if value="$(gcloud secrets versions access latest --secret="${secret_name}" 2>"${error_file}")"; then
rm -f "${error_file}"
printf '%s' "${value}"
return 0
fi

if [ -n "${CLOUD_RUN_REGION:-}" ] && [ -n "${CLOUD_RUN_SERVICE:-}" ]; then
runtime_service_account="$(
gcloud run services describe "${CLOUD_RUN_SERVICE}" \
--region "${CLOUD_RUN_REGION}" \
--format='value(spec.template.spec.serviceAccountName)' 2>/dev/null || true
)"
if [ -n "${runtime_service_account}" ] \
&& value="$(gcloud secrets versions access latest \
--secret="${secret_name}" \
--impersonate-service-account="${runtime_service_account}" 2>"${error_file}")"; then
rm -f "${error_file}"
printf '%s' "${value}"
return 0
fi
fi

cat "${error_file}" >&2
rm -f "${error_file}"
return 1
}

longport_app_key="${LONGPORT_APP_KEY_VALUE:-}"
longport_app_secret="${LONGPORT_APP_SECRET_VALUE:-}"
longport_access_token="${LONGPORT_ACCESS_TOKEN_VALUE:-}"

if [ -z "${longport_app_key}" ]; then
longport_app_key="$(read_secret_manager_value "${LONGPORT_APP_KEY_SECRET_NAME}")"
fi
if [ -z "${longport_app_secret}" ]; then
longport_app_secret="$(read_secret_manager_value "${LONGPORT_APP_SECRET_SECRET_NAME}")"
fi
if [ -z "${longport_access_token}" ]; then
longport_access_token="$(read_secret_manager_value "${LONGPORT_SECRET_NAME}")"
fi

echo "::add-mask::${longport_app_key}"
echo "::add-mask::${longport_app_secret}"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ Important:
### Manual API probes

- `.github/workflows/longbridge-api-probe.yml` can be triggered manually against the `longbridge-hk` GitHub Environment. It checks out a selected `QuantPlatformKit` ref and runs the skipped LongBridge fractional-order API probe with HK simulated-account credentials from Secret Manager.
- The workflow can also use `LONGPORT_APP_KEY`, `LONGPORT_APP_SECRET`, and `LONGPORT_ACCESS_TOKEN` as GitHub Environment secrets when the deployment service account cannot read Secret Manager versions directly.
- The probe is for broker API validation only. It does not run on normal CI or `main` pushes.

### Quick deploy
Expand Down Expand Up @@ -307,6 +308,7 @@ Secret Manager 中需存在 `LONGPORT_SECRET_NAME` 指定的密钥(默认: `lo
### 手动 API probe

- `.github/workflows/longbridge-api-probe.yml` 可手动触发,固定使用 `longbridge-hk` GitHub Environment。它会 checkout 指定的 `QuantPlatformKit` ref,并用 Secret Manager 中的 HK 模拟盘 LongPort 凭证运行默认跳过的碎股下单 API probe。
- 如果部署服务账号不能直接读取 Secret Manager version,这个 workflow 也可以改用 GitHub Environment secrets: `LONGPORT_APP_KEY`、`LONGPORT_APP_SECRET`、`LONGPORT_ACCESS_TOKEN`。
- 这个 probe 只用于券商 API 验证,不会进入普通 CI 或 `main` push 流程。

### 快速部署
Expand Down
17 changes: 11 additions & 6 deletions tests/test_longbridge_api_probe_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@ def test_workflow_uses_hk_environment_and_gcp_secrets(self) -> None:
self.assertIn("ref: ${{ inputs.qpk_ref }}", workflow)
self.assertIn("google-github-actions/auth@v3", workflow)
self.assertIn("google-github-actions/setup-gcloud@v3", workflow)
self.assertIn("CLOUD_RUN_REGION: ${{ vars.CLOUD_RUN_REGION }}", workflow)
self.assertIn("CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }}", workflow)
self.assertIn("LONGPORT_APP_KEY_SECRET_NAME: ${{ vars.LONGPORT_APP_KEY_SECRET_NAME }}", workflow)
self.assertIn("LONGPORT_APP_SECRET_SECRET_NAME: ${{ vars.LONGPORT_APP_SECRET_SECRET_NAME }}", workflow)
self.assertIn("LONGPORT_SECRET_NAME: ${{ vars.LONGPORT_SECRET_NAME }}", workflow)
self.assertIn('gcloud secrets versions access latest --secret="${LONGPORT_APP_KEY_SECRET_NAME}"', workflow)
self.assertIn(
'gcloud secrets versions access latest --secret="${LONGPORT_APP_SECRET_SECRET_NAME}"',
workflow,
)
self.assertIn('gcloud secrets versions access latest --secret="${LONGPORT_SECRET_NAME}"', workflow)
self.assertIn("LONGPORT_APP_KEY_VALUE: ${{ secrets.LONGPORT_APP_KEY }}", workflow)
self.assertIn("LONGPORT_APP_SECRET_VALUE: ${{ secrets.LONGPORT_APP_SECRET }}", workflow)
self.assertIn("LONGPORT_ACCESS_TOKEN_VALUE: ${{ secrets.LONGPORT_ACCESS_TOKEN }}", workflow)
self.assertIn("read_secret_manager_value()", workflow)
self.assertIn('read_secret_manager_value "${LONGPORT_APP_KEY_SECRET_NAME}"', workflow)
self.assertIn('read_secret_manager_value "${LONGPORT_APP_SECRET_SECRET_NAME}"', workflow)
self.assertIn('read_secret_manager_value "${LONGPORT_SECRET_NAME}"', workflow)
self.assertIn('gcloud secrets versions access latest --secret="${secret_name}"', workflow)
self.assertIn("--impersonate-service-account", workflow)
self.assertIn("python -m pip install -e quant-platform-kit pytest longport", workflow)
self.assertIn('LONGBRIDGE_API_PROBE: "1"', workflow)
self.assertIn("test_longbridge_fractional_order_api_probe.py", workflow)
Expand Down
14 changes: 11 additions & 3 deletions tests/test_longbridge_api_probe_workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ grep -Fq "repository: QuantStrategyLab/QuantPlatformKit" "$workflow_file"
grep -Fq "ref: \${{ inputs.qpk_ref }}" "$workflow_file"
grep -Fq "google-github-actions/auth@v3" "$workflow_file"
grep -Fq "google-github-actions/setup-gcloud@v3" "$workflow_file"
grep -Fq "CLOUD_RUN_REGION: \${{ vars.CLOUD_RUN_REGION }}" "$workflow_file"
grep -Fq "CLOUD_RUN_SERVICE: \${{ vars.CLOUD_RUN_SERVICE }}" "$workflow_file"
grep -Fq "LONGPORT_APP_KEY_SECRET_NAME: \${{ vars.LONGPORT_APP_KEY_SECRET_NAME }}" "$workflow_file"
grep -Fq "LONGPORT_APP_SECRET_SECRET_NAME: \${{ vars.LONGPORT_APP_SECRET_SECRET_NAME }}" "$workflow_file"
grep -Fq "LONGPORT_SECRET_NAME: \${{ vars.LONGPORT_SECRET_NAME }}" "$workflow_file"
grep -Fq 'gcloud secrets versions access latest --secret="${LONGPORT_APP_KEY_SECRET_NAME}"' "$workflow_file"
grep -Fq 'gcloud secrets versions access latest --secret="${LONGPORT_APP_SECRET_SECRET_NAME}"' "$workflow_file"
grep -Fq 'gcloud secrets versions access latest --secret="${LONGPORT_SECRET_NAME}"' "$workflow_file"
grep -Fq "LONGPORT_APP_KEY_VALUE: \${{ secrets.LONGPORT_APP_KEY }}" "$workflow_file"
grep -Fq "LONGPORT_APP_SECRET_VALUE: \${{ secrets.LONGPORT_APP_SECRET }}" "$workflow_file"
grep -Fq "LONGPORT_ACCESS_TOKEN_VALUE: \${{ secrets.LONGPORT_ACCESS_TOKEN }}" "$workflow_file"
grep -Fq "read_secret_manager_value()" "$workflow_file"
grep -Fq 'read_secret_manager_value "${LONGPORT_APP_KEY_SECRET_NAME}"' "$workflow_file"
grep -Fq 'read_secret_manager_value "${LONGPORT_APP_SECRET_SECRET_NAME}"' "$workflow_file"
grep -Fq 'read_secret_manager_value "${LONGPORT_SECRET_NAME}"' "$workflow_file"
grep -Fq 'gcloud secrets versions access latest --secret="${secret_name}"' "$workflow_file"
grep -Fq -- "--impersonate-service-account" "$workflow_file"
grep -Fq "python -m pip install -e quant-platform-kit pytest longport" "$workflow_file"
grep -Fq 'LONGBRIDGE_API_PROBE: "1"' "$workflow_file"
grep -Fq "test_longbridge_fractional_order_api_probe.py" "$workflow_file"