mirror of
https://github.com/kochetkov-ma/claude-brewcode.git
synced 2026-09-14 20:16:41 +08:00
205 lines
7.3 KiB
YAML
205 lines
7.3 KiB
YAML
name: Deploy Docs
|
|
|
|
on:
|
|
workflow_run:
|
|
workflows: ["Docs"]
|
|
types: [completed]
|
|
workflow_dispatch:
|
|
inputs:
|
|
image_tag:
|
|
description: "Docker image tag to deploy"
|
|
required: true
|
|
default: "latest"
|
|
|
|
concurrency:
|
|
group: deploy-docs
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
HEALTH_URL: https://doc-claude.brewcode.app/getting-started/
|
|
|
|
permissions:
|
|
contents: read
|
|
deployments: write
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
if: >
|
|
github.event_name == 'workflow_dispatch' ||
|
|
github.event.workflow_run.conclusion == 'success'
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
|
|
fetch-depth: 0
|
|
|
|
- name: Compute image tag
|
|
id: tag
|
|
run: |
|
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
|
echo "value=${{ inputs.image_tag }}" >> "$GITHUB_OUTPUT"
|
|
else
|
|
REF="${{ github.event.workflow_run.head_branch || github.ref_name }}"
|
|
if [[ "$REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
|
echo "value=${REF#v}" >> "$GITHUB_OUTPUT"
|
|
else
|
|
BRANCH_SAFE=$(echo "$REF" | sed 's/[^a-zA-Z0-9._-]/-/g')
|
|
DESC=$(git describe --tags --long --match "v*.*.*" 2>/dev/null || echo "0.0.0-0-g$(git rev-parse --short HEAD)")
|
|
BASE_VERSION=$(echo "$DESC" | sed 's/^v//' | sed 's/-.*//')
|
|
COMMITS_AFTER=$(echo "$DESC" | sed 's/.*-\([0-9]*\)-g.*/\1/')
|
|
echo "value=${BASE_VERSION}-${BRANCH_SAFE}-${COMMITS_AFTER}" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
fi
|
|
|
|
- name: Create deployment
|
|
id: deployment
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
const deployment = await github.rest.repos.createDeployment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
ref: context.sha,
|
|
environment: 'docs',
|
|
auto_merge: false,
|
|
required_contexts: [],
|
|
description: `Deploy docs v${{ steps.tag.outputs.value }}`,
|
|
});
|
|
await github.rest.repos.createDeploymentStatus({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
deployment_id: deployment.data.id,
|
|
state: 'in_progress',
|
|
log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
|
|
});
|
|
return deployment.data.id;
|
|
|
|
- name: Copy deploy files to VPS
|
|
uses: appleboy/scp-action@v1
|
|
with:
|
|
host: ${{ secrets.VPS_HOST }}
|
|
username: ${{ secrets.VPS_USER }}
|
|
key: ${{ secrets.VPS_SSH_KEY }}
|
|
source: "web/docs/deploy/docker-compose.yml,web/docs/deploy/docs.Caddyfile,web/docs/deploy/brewcode-docs.caddy"
|
|
target: /tmp/brewcode-docs-sync
|
|
strip_components: 3
|
|
|
|
- name: Deploy docs service
|
|
uses: appleboy/ssh-action@v1
|
|
env:
|
|
TAG: ${{ steps.tag.outputs.value }}
|
|
with:
|
|
host: ${{ secrets.VPS_HOST }}
|
|
username: ${{ secrets.VPS_USER }}
|
|
key: ${{ secrets.VPS_SSH_KEY }}
|
|
envs: TAG
|
|
script: |
|
|
set -euo pipefail
|
|
|
|
DOCS_PATH=/opt/brewcode-docs
|
|
CADDY_SITES=/opt/caddy/sites
|
|
|
|
cp /tmp/brewcode-docs-sync/docker-compose.yml "$DOCS_PATH/"
|
|
cp /tmp/brewcode-docs-sync/docs.Caddyfile "$DOCS_PATH/"
|
|
cp /tmp/brewcode-docs-sync/brewcode-docs.caddy "$CADDY_SITES/"
|
|
rm -rf /tmp/brewcode-docs-sync
|
|
|
|
cd "$DOCS_PATH"
|
|
|
|
[ -f .env ] && cp .env .env.bak || touch .env.bak
|
|
|
|
if grep -q "^DOCS_TAG=" .env 2>/dev/null; then
|
|
sed -i "s/^DOCS_TAG=.*/DOCS_TAG=${TAG}/" .env
|
|
else
|
|
echo "DOCS_TAG=${TAG}" >> .env
|
|
fi
|
|
|
|
for attempt in $(seq 1 10); do
|
|
if docker compose pull docs 2>&1; then
|
|
break
|
|
fi
|
|
if [ "$attempt" -eq 10 ]; then
|
|
echo "Failed to pull image after 10 attempts"
|
|
exit 1
|
|
fi
|
|
echo "Image not available yet, retrying in 15s... (attempt $attempt/10)"
|
|
sleep 15
|
|
done
|
|
|
|
docker compose up -d --no-deps --force-recreate docs
|
|
|
|
# Reload main caddy to pick up the docs site snippet.
|
|
# admin is bound 0.0.0.0:2019 with origins {caddy:2019, localhost:2019};
|
|
# a plain `caddy reload` derives Origin http://0.0.0.0:2019 (the config's admin
|
|
# address), which is NOT in that allowlist -> HTTP 403. Connect via localhost:2019,
|
|
# which IS allowed, so the graceful reload succeeds.
|
|
docker exec caddy caddy reload --config /etc/caddy/Caddyfile --address localhost:2019
|
|
|
|
for i in $(seq 1 10); do
|
|
if curl -sf -o /dev/null "https://doc-claude.brewcode.app/getting-started/"; then
|
|
echo "Health check passed (attempt $i)"
|
|
exit 0
|
|
fi
|
|
echo "Waiting for docs... (attempt $i/10)"
|
|
sleep 5
|
|
done
|
|
|
|
echo "Health check failed, rolling back..."
|
|
cp .env.bak .env
|
|
docker compose pull docs
|
|
docker compose up -d --no-deps --force-recreate docs
|
|
exit 1
|
|
|
|
- name: Verify from runner
|
|
run: |
|
|
for i in $(seq 1 5); do
|
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" || true)
|
|
if [[ "$STATUS" == "200" ]]; then
|
|
echo "External health check passed (HTTP $STATUS)"
|
|
exit 0
|
|
fi
|
|
echo "Waiting... (attempt $i/5, HTTP $STATUS)"
|
|
sleep 5
|
|
done
|
|
echo "::warning::External health check did not return 200"
|
|
|
|
- name: Update deployment (success)
|
|
if: success()
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
await github.rest.repos.createDeploymentStatus({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
deployment_id: ${{ steps.deployment.outputs.result }},
|
|
state: 'success',
|
|
environment_url: 'https://doc-claude.brewcode.app',
|
|
log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
|
|
});
|
|
|
|
- name: Update deployment (failure)
|
|
if: failure()
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
const id = ${{ steps.deployment.outputs.result || 0 }};
|
|
if (!id) return;
|
|
await github.rest.repos.createDeploymentStatus({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
deployment_id: id,
|
|
state: 'failure',
|
|
log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
|
|
});
|
|
|
|
- name: Summary
|
|
if: always()
|
|
run: |
|
|
echo "### Deploy Docs" >> "$GITHUB_STEP_SUMMARY"
|
|
echo "" >> "$GITHUB_STEP_SUMMARY"
|
|
echo "**Tag:** \`${{ steps.tag.outputs.value }}\`" >> "$GITHUB_STEP_SUMMARY"
|
|
echo "**Health:** $HEALTH_URL" >> "$GITHUB_STEP_SUMMARY"
|