218 lines
8.8 KiB
YAML
218 lines
8.8 KiB
YAML
name: Community Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
REGISTRY: git.itexp.me
|
|
IMAGE_TAG: ${{ gitea.ref_name }}
|
|
ADMIN_API_IMAGE: git.itexp.me/bsodfather/crank-community-admin-api
|
|
MCP_SERVER_IMAGE: git.itexp.me/bsodfather/crank-community-mcp-server
|
|
UI_IMAGE: git.itexp.me/bsodfather/crank-community-ui
|
|
OPENBAO_ENV_FILE: .openbao-env
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Use preinstalled Rust toolchain
|
|
run: |
|
|
set -eu
|
|
toolchain_dir="${RUSTUP_HOME:-$HOME/.rustup}/toolchains/1.96.1-x86_64-unknown-linux-gnu"
|
|
toolchain_bin="$toolchain_dir/bin"
|
|
if [ ! -x "$toolchain_bin/rustc" ] || [ ! -x "$toolchain_bin/cargo" ]; then
|
|
echo "Rust 1.96.1 is not preinstalled at $toolchain_dir." >&2
|
|
echo "Install it in the Gitea runner image/host before running CI:" >&2
|
|
echo "rustup toolchain install 1.96.1 --profile minimal --component clippy --component rustfmt" >&2
|
|
exit 1
|
|
fi
|
|
printf '%s\n' "$toolchain_bin" >> "$GITHUB_PATH"
|
|
"$toolchain_bin/rustc" --version
|
|
"$toolchain_bin/cargo" --version
|
|
"$toolchain_bin/rustfmt" --version
|
|
"$toolchain_bin/cargo-clippy" --version
|
|
|
|
- name: Verify runner toolchain
|
|
run: |
|
|
python3 --version
|
|
rustc --version
|
|
cargo --version
|
|
node --version
|
|
npm --version
|
|
docker --version
|
|
command -v bao
|
|
bao version
|
|
|
|
- name: Install dependency policy tool
|
|
run: cargo install cargo-deny --version 0.20.2 --locked
|
|
|
|
- name: Run tooling unit tests
|
|
run: python3 -m unittest discover -s tests/unit
|
|
|
|
- name: Check typed runtime configuration contract
|
|
run: |
|
|
cargo run -p crank-config --bin crank-config-contract -- --check
|
|
python3 scripts/check-runtime-config.py --root .
|
|
python3 scripts/check-config-boundaries.py --root .
|
|
scripts/check-rust-boundaries.sh
|
|
|
|
- name: Check canonical migration contract
|
|
run: cargo run -p admin-api --bin crank-migrate -- plan --check
|
|
|
|
- name: Check typed metrics contract
|
|
run: |
|
|
cargo run -p crank-metrics --bin crank-metrics-contract -- --check
|
|
python3 scripts/check-metrics-boundaries.py --root .
|
|
|
|
- name: Check Capability Inventory
|
|
run: |
|
|
required_args=""
|
|
for number in $(seq 1 54); do
|
|
required_args="$required_args --required-fr FR-$number"
|
|
done
|
|
python3 scripts/validate-capability-inventory.py \
|
|
--root . \
|
|
--inventory docs/capability-inventory.json \
|
|
--schema docs/schemas/capability-inventory.schema.json \
|
|
$required_args
|
|
|
|
- name: Check Capability Baseline
|
|
run: |
|
|
python3 scripts/validate-capability-baseline.py \
|
|
--root . \
|
|
--manifest docs/capability-baseline/manifest.json \
|
|
--schema docs/schemas/capability-baseline.schema.json
|
|
|
|
- name: Check Community scope
|
|
run: scripts/check-community-scope.sh
|
|
|
|
- name: Run release quality gates
|
|
run: |
|
|
cargo fmt --all --check
|
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
|
cargo test --workspace --all-targets -- --test-threads=1
|
|
cargo deny --locked check advisories bans licenses sources
|
|
|
|
- name: Build release binaries
|
|
run: cargo build --release -p admin-api -p mcp-server
|
|
|
|
- name: Install UI dependencies
|
|
working-directory: apps/ui
|
|
run: npm ci
|
|
|
|
- name: Audit UI dependencies
|
|
working-directory: apps/ui
|
|
run: npm audit --audit-level=high
|
|
|
|
- name: Build UI dist
|
|
working-directory: apps/ui
|
|
run: npm run build
|
|
|
|
- name: Install Playwright browser
|
|
working-directory: apps/ui
|
|
run: npx playwright install --with-deps chromium
|
|
|
|
- name: Run release end-to-end tests
|
|
working-directory: apps/ui
|
|
run: npm run e2e
|
|
|
|
- name: Validate deployment manifests
|
|
run: |
|
|
docker compose -f docker-compose.yml --env-file .env.example config -q
|
|
docker compose -f deploy/community/docker-compose.yml --env-file deploy/community/.env.example config -q
|
|
docker compose -f deploy/community/docker-compose.images.yml --env-file deploy/community/.env.images.example --profile local-db config -q
|
|
|
|
- name: Package release artifacts
|
|
run: |
|
|
mkdir -p dist/release
|
|
cp target/release/admin-api dist/release/admin-api
|
|
cp target/release/crank-migrate dist/release/crank-migrate
|
|
cp target/release/mcp-server dist/release/mcp-server
|
|
tar -C dist/release -czf dist/crank-community-admin-api-${IMAGE_TAG}.tar.gz admin-api crank-migrate
|
|
tar -C dist/release -czf dist/crank-community-mcp-server-${IMAGE_TAG}.tar.gz mcp-server
|
|
tar -C apps/ui/dist -czf dist/crank-community-ui-${IMAGE_TAG}.tar.gz .
|
|
sha256sum \
|
|
dist/crank-community-admin-api-${IMAGE_TAG}.tar.gz \
|
|
dist/crank-community-mcp-server-${IMAGE_TAG}.tar.gz \
|
|
dist/crank-community-ui-${IMAGE_TAG}.tar.gz \
|
|
> dist/crank-community-${IMAGE_TAG}-checksums.txt
|
|
|
|
- name: Generate SBOM
|
|
run: |
|
|
docker run --rm -v "$PWD:/workspace" anchore/syft:latest \
|
|
dir:/workspace -o spdx-json \
|
|
> dist/crank-community-${IMAGE_TAG}-sbom.spdx.json
|
|
|
|
- name: Load deployment secrets from OpenBao
|
|
env:
|
|
BAO_ADDR: ${{ secrets.BAO_ADDR }}
|
|
BAO_ROLE_ID: ${{ secrets.BAO_ROLE_ID }}
|
|
BAO_SECRET_ID: ${{ secrets.BAO_SECRET_ID }}
|
|
OPENBAO_APP: crank
|
|
run: scripts/load-openbao-env.sh
|
|
|
|
- name: Login to registry
|
|
run: |
|
|
. "$OPENBAO_ENV_FILE"
|
|
printf '%s' "$DEPLOY_REGISTRY_TOKEN" | \
|
|
docker login '${{ env.REGISTRY }}' -u "$DEPLOY_REGISTRY_USER" --password-stdin
|
|
|
|
- name: Build and push release images
|
|
run: |
|
|
docker build -f apps/admin-api/Dockerfile \
|
|
-t '${{ env.ADMIN_API_IMAGE }}:${{ env.IMAGE_TAG }}' \
|
|
-t '${{ env.ADMIN_API_IMAGE }}:latest' .
|
|
docker build -f apps/mcp-server/Dockerfile \
|
|
-t '${{ env.MCP_SERVER_IMAGE }}:${{ env.IMAGE_TAG }}' \
|
|
-t '${{ env.MCP_SERVER_IMAGE }}:latest' .
|
|
docker build -f apps/ui/Dockerfile \
|
|
-t '${{ env.UI_IMAGE }}:${{ env.IMAGE_TAG }}' \
|
|
-t '${{ env.UI_IMAGE }}:latest' .
|
|
mkdir -p .tmp
|
|
cat > .tmp/release-migration-smoke.env <<EOF
|
|
COMPOSE_PROJECT_NAME=crank-release-migration-${{ github.run_id }}-${{ github.run_attempt }}
|
|
POSTGRES_HOST=postgres
|
|
POSTGRES_DB=crank
|
|
POSTGRES_USER=crank
|
|
POSTGRES_PASSWORD=release-smoke-password
|
|
CRANK_ADMIN_API_IMAGE=${{ env.ADMIN_API_IMAGE }}:${{ env.IMAGE_TAG }}
|
|
CRANK_MCP_SERVER_IMAGE=${{ env.MCP_SERVER_IMAGE }}:${{ env.IMAGE_TAG }}
|
|
CRANK_UI_IMAGE=${{ env.UI_IMAGE }}:${{ env.IMAGE_TAG }}
|
|
CRANK_MASTER_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
|
CRANK_SESSION_SECRET=release-smoke-session
|
|
CRANK_PASSWORD_PEPPER=release-smoke-pepper
|
|
CRANK_BOOTSTRAP_ADMIN_EMAIL=owner@crank.test
|
|
CRANK_BOOTSTRAP_ADMIN_PASSWORD=release-smoke-password
|
|
CRANK_BASE_URL=http://127.0.0.1
|
|
CRANK_PUBLISH_BIND=127.0.0.1
|
|
CRANK_ADMIN_PUBLISH_PORT=0
|
|
CRANK_MCP_PUBLISH_PORT=0
|
|
CRANK_UI_PUBLISH_PORT=0
|
|
EOF
|
|
trap 'docker compose -f deploy/community/docker-compose.images.yml --env-file .tmp/release-migration-smoke.env --profile local-db down -v --remove-orphans || true' EXIT
|
|
docker compose -f deploy/community/docker-compose.images.yml \
|
|
--env-file .tmp/release-migration-smoke.env --profile local-db up -d --wait
|
|
docker compose -f deploy/community/docker-compose.images.yml \
|
|
--env-file .tmp/release-migration-smoke.env --profile local-db logs migrate | grep '"status":"applied"'
|
|
scripts/scan-images.sh \
|
|
'${{ env.ADMIN_API_IMAGE }}:${{ env.IMAGE_TAG }}' \
|
|
'${{ env.MCP_SERVER_IMAGE }}:${{ env.IMAGE_TAG }}' \
|
|
'${{ env.UI_IMAGE }}:${{ env.IMAGE_TAG }}'
|
|
docker push '${{ env.ADMIN_API_IMAGE }}:${{ env.IMAGE_TAG }}'
|
|
docker push '${{ env.ADMIN_API_IMAGE }}:latest'
|
|
docker push '${{ env.MCP_SERVER_IMAGE }}:${{ env.IMAGE_TAG }}'
|
|
docker push '${{ env.MCP_SERVER_IMAGE }}:latest'
|
|
docker push '${{ env.UI_IMAGE }}:${{ env.IMAGE_TAG }}'
|
|
docker push '${{ env.UI_IMAGE }}:latest'
|
|
|
|
- name: Show release bundle summary
|
|
run: |
|
|
ls -lh dist
|