k-skill/.github/workflows/deploy-k-skill-proxy.yml
Jeffrey (Dongkyu) Kim f485591ac2
feat(k-skill-proxy): fold Korean law lookups into k-skill-proxy, drop Beopmang (#315)
Add hosted korean-law proxy routes and make the korean-law-search skill
proxy-first, removing the unstable Beopmang fallback from the support list.

- proxy: new src/korean-law.js wrapping official 법제처 DRF lawSearch.do /
  lawService.do, injecting LAW_OC + browser User-Agent/Referer (the real
  cause of "사용자 정보 검증 실패") and retrying empty/HTML responses.
- proxy: /v1/korean-law/search and /v1/korean-law/detail routes + lawOc
  config + koreanLawConfigured health flag; 17 module + 6 route tests.
- skill/docs: korean-law-search becomes proxy-first (no per-user LAW_OC,
  no local CLI). Drop Beopmang everywhere; credit chrisryugj/korean-law-mcp
  as design reference and 법제처 open.law.go.kr as official source.
- ops: LAW_OC added to deploy doc KEYS, secret accessor loop, and the
  Cloud Run deploy workflow set-secrets.
- changeset: k-skill-proxy minor.
2026-06-12 18:07:03 +09:00

147 lines
5 KiB
YAML

name: Deploy k-skill-proxy to Cloud Run
# Live: https://k-skill-proxy.nomadamas.org
# GCP project: k-skill-proxy, region: asia-northeast1
# Auth: Workload Identity Federation. Setup: docs/deploy-k-skill-proxy.md
on:
push:
branches: [main]
workflow_dispatch: {}
permissions:
contents: read
id-token: write
concurrency:
group: deploy-k-skill-proxy
cancel-in-progress: false
env:
GCP_PROJECT_ID: k-skill-proxy
GCP_REGION: asia-northeast1
AR_REPO: k-skill
SERVICE_NAME: k-skill-proxy
IMAGE_NAME: k-skill-proxy
jobs:
deploy:
name: Build and deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Authenticate to Google Cloud (Workload Identity Federation)
id: auth
uses: google-github-actions/auth@v3
with:
project_id: ${{ env.GCP_PROJECT_ID }}
workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }}
service_account: ${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}
token_format: access_token
- name: Set up gcloud CLI
uses: google-github-actions/setup-gcloud@v3
with:
project_id: ${{ env.GCP_PROJECT_ID }}
- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker ${{ env.GCP_REGION }}-docker.pkg.dev --quiet
- name: Resolve image URI
id: image
run: |
IMAGE_URI="${GCP_REGION}-docker.pkg.dev/${GCP_PROJECT_ID}/${AR_REPO}/${IMAGE_NAME}:${GITHUB_SHA}"
echo "uri=${IMAGE_URI}" >> "$GITHUB_OUTPUT"
echo "Image: ${IMAGE_URI}"
- name: Build container image
run: |
docker build \
--tag "${{ steps.image.outputs.uri }}" \
--file packages/k-skill-proxy/Dockerfile \
.
- name: Push image to Artifact Registry
run: docker push "${{ steps.image.outputs.uri }}"
- name: Deploy to Cloud Run
id: deploy
uses: google-github-actions/deploy-cloudrun@v3
with:
service: ${{ env.SERVICE_NAME }}
region: ${{ env.GCP_REGION }}
image: ${{ steps.image.outputs.uri }}
secrets: |-
AIR_KOREA_OPEN_API_KEY=AIR_KOREA_OPEN_API_KEY:latest
KMA_OPEN_API_KEY=KMA_OPEN_API_KEY:latest
SEOUL_OPEN_API_KEY=SEOUL_OPEN_API_KEY:latest
HRFCO_OPEN_API_KEY=HRFCO_OPEN_API_KEY:latest
OPINET_API_KEY=OPINET_API_KEY:latest
DATA_GO_KR_API_KEY=DATA_GO_KR_API_KEY:latest
KEDU_INFO_KEY=KEDU_INFO_KEY:latest
DATA4LIBRARY_AUTH_KEY=DATA4LIBRARY_AUTH_KEY:latest
FOODSAFETYKOREA_API_KEY=FOODSAFETYKOREA_API_KEY:latest
KAKAO_REST_API_KEY=KAKAO_REST_API_KEY:latest
KRX_API_KEY=KRX_API_KEY:latest
KOSIS_API_KEY=KOSIS_API_KEY:latest
NAVER_SEARCH_CLIENT_ID=NAVER_SEARCH_CLIENT_ID:latest
NAVER_SEARCH_CLIENT_SECRET=NAVER_SEARCH_CLIENT_SECRET:latest
LAW_OC=LAW_OC:latest
env_vars: |-
KSKILL_PROXY_HOST=0.0.0.0
KSKILL_PROXY_NAME=k-skill-proxy
KSKILL_PROXY_CACHE_TTL_MS=300000
KSKILL_PROXY_RATE_LIMIT_WINDOW_MS=60000
KSKILL_PROXY_RATE_LIMIT_MAX=60
flags: >-
--platform=managed
--allow-unauthenticated
--cpu=1
--memory=512Mi
--min-instances=0
--max-instances=3
--concurrency=80
--timeout=60
--execution-environment=gen2
--cpu-boost
- name: Smoke test /health on the new revision
env:
SERVICE_URL: ${{ steps.deploy.outputs.url }}
run: |
set -euo pipefail
echo "Service URL: ${SERVICE_URL}"
for attempt in 1 2 3 4 5; do
if curl -fsS --max-time 15 "${SERVICE_URL}/health" >/tmp/health.json; then
break
fi
echo "Health probe attempt ${attempt} failed, retrying in 5s..."
sleep 5
done
python3 -c "
import json, sys
data = json.load(open('/tmp/health.json'))
if not data.get('ok'):
print('Health response is not ok:', data)
sys.exit(1)
missing = [k for k, v in data.get('upstreams', {}).items() if k.endswith('Configured') and v is not True]
if missing:
print('Upstreams not configured:', missing)
sys.exit(1)
print('Health OK. All upstreams configured.')
"
- name: Smoke test custom domain (k-skill-proxy.nomadamas.org)
run: |
set -euo pipefail
if curl -fsS --max-time 15 https://k-skill-proxy.nomadamas.org/health >/tmp/prod-health.json; then
python3 -c "
import json
data = json.load(open('/tmp/prod-health.json'))
print('Prod /health ok:', data.get('ok'))
"
else
echo "::warning::Custom domain /health probe failed; revision may need traffic split or DNS warm-up."
fi