Merge pull request #141 from Agent-Hellboy/ci/trivy_operator_image_bu… #299
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| branches: [ "*" ] | |
| push: | |
| branches: [ "main" ] | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: read | |
| jobs: | |
| changed-paths: | |
| name: Detect Docs/Website Changes | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| code: ${{ steps.filter.outputs.code }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| website: ${{ steps.filter.outputs.website }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Check changed paths | |
| id: filter | |
| uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3 | |
| with: | |
| filters: | | |
| code: | |
| - '**' | |
| - '!docs/**' | |
| - '!website/**' | |
| - '!README.md' | |
| docs: | |
| - 'docs/**' | |
| website: | |
| - 'website/**' | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-24.04 | |
| needs: [changed-paths] | |
| if: github.event_name == 'workflow_dispatch' || needs.changed-paths.outputs.code == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Install linters | |
| run: | | |
| go install honnef.co/go/tools/cmd/staticcheck@v0.7.0 | |
| - name: Run go fmt check | |
| run: | | |
| if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then | |
| echo "Code is not formatted. Run 'go fmt ./...'" | |
| gofmt -s -d . | |
| exit 1 | |
| fi | |
| - name: Run go vet | |
| run: go vet ./... | |
| - name: Run staticcheck | |
| run: staticcheck ./... | |
| test: | |
| name: Unit + Integration Tests | |
| runs-on: ubuntu-24.04 | |
| needs: [changed-paths] | |
| if: github.event_name == 'workflow_dispatch' || needs.changed-paths.outputs.code == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Install envtest | |
| run: | | |
| export KUBEBUILDER_ASSETS=$(go run sigs.k8s.io/controller-runtime/tools/setup-envtest@v0.24.0 use -p path) | |
| echo "KUBEBUILDER_ASSETS=$KUBEBUILDER_ASSETS" >> $GITHUB_ENV | |
| - name: Run unit tests with coverage | |
| run: | | |
| module_path="$(go list -m)" | |
| go test -v -race -coverprofile=unit.out \ | |
| $(go list ./... | grep -v -x "${module_path}/test/integration") | |
| - name: Test Sentinel service modules (api, ui) | |
| run: | | |
| (cd services/api && go test -v -race -count=1 ./...) | |
| (cd services/ui && go test -v -race -count=1 ./...) | |
| - name: Run integration tests with coverage | |
| run: | | |
| go test -v -race -timeout 30m -coverprofile=integration.out \ | |
| -coverpkg=./internal/...,./pkg/...,./api/... \ | |
| ./test/integration/... | |
| env: | |
| KUBEBUILDER_ASSETS: ${{ env.KUBEBUILDER_ASSETS }} | |
| - name: Merge coverage files | |
| run: | | |
| # Remove mode line from integration.out and append to unit.out | |
| tail -n +2 integration.out >> unit.out | |
| mv unit.out coverage.out | |
| - name: Upload coverage | |
| uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 | |
| with: | |
| files: ./coverage.out | |
| flags: pre-merge | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: false | |
| e2e-kind: | |
| name: Kind E2E | |
| runs-on: ubuntu-24.04 | |
| needs: [changed-paths, lint, test] | |
| if: github.event_name == 'workflow_dispatch' || needs.changed-paths.outputs.code == 'true' | |
| timeout-minutes: 90 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Go | |
| uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Free runner disk space | |
| run: | | |
| df -h | |
| sudo rm -rf \ | |
| /opt/ghc \ | |
| /opt/hostedtoolcache/CodeQL \ | |
| /usr/local/lib/android \ | |
| /usr/local/share/boost \ | |
| /usr/share/dotnet | |
| docker system prune -af --volumes || true | |
| df -h | |
| - name: Validate E2E scenario selectors | |
| run: bash test/e2e/scenarios_test.sh | |
| - name: Install kubectl | |
| run: | | |
| curl -fsSL -o kubectl "https://dl.k8s.io/release/v1.34.1/bin/linux/amd64/kubectl" | |
| chmod +x kubectl | |
| sudo mv kubectl /usr/local/bin/kubectl | |
| - name: Install kind | |
| run: | | |
| curl -fsSL -o kind "https://kind.sigs.k8s.io/dl/v0.30.0/kind-linux-amd64" | |
| chmod +x kind | |
| sudo mv kind /usr/local/bin/kind | |
| - name: Install E2E dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y python3 ripgrep | |
| - name: Show tool versions | |
| run: | | |
| docker version | |
| kubectl version --client | |
| kind version | |
| python3 --version | |
| rg --version | |
| - name: Validate OpenAI API key | |
| id: openai-key | |
| if: github.actor != 'dependabot[bot]' | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| run: | | |
| if [ -z "${OPENAI_API_KEY:-}" ]; then | |
| echo "valid=false" >> "$GITHUB_OUTPUT" | |
| echo "::warning::OPENAI_API_KEY is not configured; skipping Kind e2e for this non-Dependabot run." | |
| exit 0 | |
| fi | |
| status="$(curl -sS -o /tmp/openai-key-check.json -w "%{http_code}" \ | |
| "https://api.openai.com/v1/models" \ | |
| -H "Authorization: Bearer ${OPENAI_API_KEY}" || true)" | |
| if [ "${status}" = "200" ]; then | |
| echo "valid=true" >> "$GITHUB_OUTPUT" | |
| echo "OpenAI API key was accepted by /v1/models." | |
| else | |
| echo "valid=false" >> "$GITHUB_OUTPUT" | |
| echo "::warning::OpenAI API key validation failed with HTTP ${status}; skipping Kind e2e." | |
| fi | |
| - name: Run kind e2e | |
| if: github.actor == 'dependabot[bot]' || steps.openai-key.outputs.valid == 'true' | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| E2E_ARTIFACT_DIR: ${{ github.workspace }}/.e2e-artifacts/kind | |
| E2E_SCENARIOS: ${{ github.actor == 'dependabot[bot]' && 'smoke-auth,governance' || 'all' }} | |
| run: bash test/e2e/kind.sh | |
| - name: Report skipped Kind e2e | |
| if: github.actor != 'dependabot[bot]' && steps.openai-key.outputs.valid != 'true' | |
| run: echo "::notice::Kind e2e was skipped because OPENAI_API_KEY was missing or rejected by OpenAI." | |
| - name: Upload e2e artifacts | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: kind-e2e-artifacts | |
| path: .e2e-artifacts/kind | |
| if-no-files-found: ignore | |
| benchmark: | |
| name: Benchmarks | |
| runs-on: ubuntu-24.04 | |
| needs: [changed-paths] | |
| if: github.event_name == 'workflow_dispatch' || needs.changed-paths.outputs.code == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Run benchmarks | |
| run: | | |
| go test -run=^$ -bench=. -benchmem -count=1 ./test/benchmark/... | |
| generated-drift: | |
| name: Generated File Drift | |
| runs-on: ubuntu-24.04 | |
| needs: [changed-paths] | |
| if: github.event_name == 'workflow_dispatch' || needs.changed-paths.outputs.code == 'true' || needs.changed-paths.outputs.docs == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Regenerate CRDs/manifests | |
| run: make -f Makefile.operator generate manifests | |
| - name: Regenerate Go package reference | |
| run: python3 docs/scripts/generate_go_package_reference.py | |
| - name: Check for drift | |
| run: | | |
| if ! git diff --quiet; then | |
| echo "Generated files are out of date. Run 'make -f Makefile.operator generate manifests' and 'python3 docs/scripts/generate_go_package_reference.py'." | |
| git diff | |
| exit 1 | |
| fi | |
| sbom: | |
| name: SBOM | |
| runs-on: ubuntu-24.04 | |
| needs: [changed-paths] | |
| if: github.event_name == 'workflow_dispatch' || needs.changed-paths.outputs.code == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Generate repository SBOM | |
| uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 | |
| with: | |
| path: . | |
| format: spdx-json | |
| output-file: mcp-runtime-repository.spdx.json | |
| - name: Upload repository SBOM | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: mcp-runtime-repository-sbom | |
| path: mcp-runtime-repository.spdx.json | |
| if-no-files-found: error | |
| deploy-docs: | |
| name: Deploy Docs (docs.mcpruntime.org) | |
| runs-on: ubuntu-24.04 | |
| concurrency: | |
| group: deploy-docs-${{ github.ref }} | |
| cancel-in-progress: true | |
| needs: [changed-paths] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.changed-paths.outputs.docs == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Validate docs deploy secrets | |
| env: | |
| DOCS_DEPLOY_HOST: ${{ secrets.DOCS_DEPLOY_HOST }} | |
| DOCS_DEPLOY_USER: ${{ secrets.DOCS_DEPLOY_USER }} | |
| DOCS_DEPLOY_PATH: ${{ secrets.DOCS_DEPLOY_PATH }} | |
| DOCS_DEPLOY_SSH_KEY: ${{ secrets.DOCS_DEPLOY_SSH_KEY }} | |
| DOCS_DEPLOY_HOST_KEY: ${{ secrets.DOCS_DEPLOY_HOST_KEY }} | |
| run: | | |
| test -n "$DOCS_DEPLOY_HOST" | |
| test -n "$DOCS_DEPLOY_USER" | |
| test -n "$DOCS_DEPLOY_PATH" | |
| test -n "$DOCS_DEPLOY_SSH_KEY" | |
| - name: Set up SSH key | |
| env: | |
| DOCS_DEPLOY_HOST: ${{ secrets.DOCS_DEPLOY_HOST }} | |
| DOCS_DEPLOY_SSH_KEY: ${{ secrets.DOCS_DEPLOY_SSH_KEY }} | |
| DOCS_DEPLOY_HOST_KEY: ${{ secrets.DOCS_DEPLOY_HOST_KEY }} | |
| run: | | |
| install -m 700 -d ~/.ssh | |
| printf "%s\n" "$DOCS_DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519 | |
| chmod 600 ~/.ssh/id_ed25519 | |
| docs_host="${DOCS_DEPLOY_HOST//$'\r'/}" | |
| docs_host="${docs_host#"${docs_host%%[![:space:]]*}"}" | |
| docs_host="${docs_host%"${docs_host##*[![:space:]]}"}" | |
| printf "DOCS_DEPLOY_HOST_CLEAN=%s\n" "$docs_host" >> "$GITHUB_ENV" | |
| docs_known_hosts="$(mktemp)" | |
| docs_known_host="${DOCS_DEPLOY_HOST_KEY//$'\r'/}" | |
| if [ -n "$docs_known_host" ]; then | |
| case "$docs_known_host" in | |
| ssh-*|ecdsa-*) printf "%s %s\n" "$docs_host" "$docs_known_host" > "$docs_known_hosts" ;; | |
| *" ssh-"*|*" ecdsa-"*) printf "%s\n" "$docs_known_host" > "$docs_known_hosts" ;; | |
| *) echo "::warning::Ignoring DOCS_DEPLOY_HOST_KEY because it is not a known_hosts entry or bare SSH host key." ;; | |
| esac | |
| fi | |
| if ! ssh-keygen -F "$docs_host" -f "$docs_known_hosts" >/dev/null 2>&1; then | |
| ssh-keyscan -T 10 -t ed25519,rsa "$docs_host" > "$docs_known_hosts" | |
| fi | |
| mv "$docs_known_hosts" ~/.ssh/known_hosts | |
| chmod 600 ~/.ssh/known_hosts | |
| if ! ssh-keygen -F "$docs_host" -f ~/.ssh/known_hosts >/dev/null; then | |
| echo "::error::Could not create a known_hosts entry for DOCS_DEPLOY_HOST." | |
| exit 1 | |
| fi | |
| - name: Verify docs SSH access | |
| run: | | |
| ssh \ | |
| -i "$HOME/.ssh/id_ed25519" \ | |
| -o BatchMode=yes \ | |
| -o IdentitiesOnly=yes \ | |
| -o StrictHostKeyChecking=yes \ | |
| -o UserKnownHostsFile="$HOME/.ssh/known_hosts" \ | |
| "${{ secrets.DOCS_DEPLOY_USER }}@$DOCS_DEPLOY_HOST_CLEAN" \ | |
| "true" | |
| - name: Sync docs/ | |
| run: | | |
| rsync -az --delete \ | |
| -e "ssh -i $HOME/.ssh/id_ed25519 -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/known_hosts" \ | |
| docs/ \ | |
| "${{ secrets.DOCS_DEPLOY_USER }}@$DOCS_DEPLOY_HOST_CLEAN:${{ secrets.DOCS_DEPLOY_PATH }}/" | |
| - name: Deploy docs container on remote host | |
| env: | |
| DOCS_DEPLOY_COMMAND: ${{ secrets.DOCS_DEPLOY_COMMAND }} | |
| DOCS_DEPLOY_PATH: ${{ secrets.DOCS_DEPLOY_PATH }} | |
| DOCS_CONTAINER_NAME: ${{ secrets.DOCS_CONTAINER_NAME }} | |
| DOCS_IMAGE_NAME: ${{ secrets.DOCS_IMAGE_NAME }} | |
| DOCS_HOST_PORT: ${{ secrets.DOCS_HOST_PORT }} | |
| DOCS_CONTAINER_PORT: ${{ secrets.DOCS_CONTAINER_PORT }} | |
| run: | | |
| if [ -n "$DOCS_DEPLOY_COMMAND" ]; then | |
| ssh "${{ secrets.DOCS_DEPLOY_USER }}@$DOCS_DEPLOY_HOST_CLEAN" "$DOCS_DEPLOY_COMMAND" | |
| exit 0 | |
| fi | |
| container_name="${DOCS_CONTAINER_NAME:-mcp-runtime-docs}" | |
| image_name="${DOCS_IMAGE_NAME:-mcp-runtime-docs:latest}" | |
| host_port="${DOCS_HOST_PORT:-8081}" | |
| container_port="${DOCS_CONTAINER_PORT:-80}" | |
| b64() { printf '%s' "$1" | base64 -w 0; } | |
| # shellcheck disable=SC2029 | |
| ssh "${{ secrets.DOCS_DEPLOY_USER }}@$DOCS_DEPLOY_HOST_CLEAN" \ | |
| "DOCS_DEPLOY_PATH_B64=$(b64 "$DOCS_DEPLOY_PATH") \ | |
| DOCS_CONTAINER_NAME_B64=$(b64 "$container_name") \ | |
| DOCS_IMAGE_NAME_B64=$(b64 "$image_name") \ | |
| DOCS_HOST_PORT_B64=$(b64 "$host_port") \ | |
| DOCS_CONTAINER_PORT_B64=$(b64 "$container_port") \ | |
| bash -s" <<'REMOTE' | |
| set -euo pipefail | |
| decode() { printf '%s' "$1" | base64 -d; } | |
| deploy_path="$(decode "$DOCS_DEPLOY_PATH_B64")" | |
| container_name="$(decode "$DOCS_CONTAINER_NAME_B64")" | |
| image_name="$(decode "$DOCS_IMAGE_NAME_B64")" | |
| host_port="$(decode "$DOCS_HOST_PORT_B64")" | |
| container_port="$(decode "$DOCS_CONTAINER_PORT_B64")" | |
| cd "$deploy_path" | |
| docker rm -f "$container_name" >/dev/null 2>&1 || true | |
| docker build --pull -t "$image_name" . | |
| docker run -d --name "$container_name" \ | |
| --restart unless-stopped \ | |
| -p "$host_port:$container_port" \ | |
| "$image_name" | |
| docker image prune -f >/dev/null | |
| REMOTE | |
| deploy-website: | |
| name: Deploy Website (mcpruntime.org) | |
| runs-on: ubuntu-24.04 | |
| concurrency: | |
| group: deploy-website-${{ github.ref }} | |
| cancel-in-progress: true | |
| needs: [changed-paths] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.changed-paths.outputs.website == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Validate website deploy secrets | |
| env: | |
| WEBSITE_DEPLOY_HOST: ${{ secrets.WEBSITE_DEPLOY_HOST }} | |
| WEBSITE_DEPLOY_USER: ${{ secrets.WEBSITE_DEPLOY_USER }} | |
| WEBSITE_DEPLOY_PATH: ${{ secrets.WEBSITE_DEPLOY_PATH }} | |
| WEBSITE_DEPLOY_SSH_KEY: ${{ secrets.WEBSITE_DEPLOY_SSH_KEY }} | |
| WEBSITE_DEPLOY_HOST_KEY: ${{ secrets.WEBSITE_DEPLOY_HOST_KEY }} | |
| WEBSITE_BASE_URL: ${{ secrets.WEBSITE_BASE_URL }} | |
| run: | | |
| test -n "$WEBSITE_DEPLOY_HOST" | |
| test -n "$WEBSITE_DEPLOY_USER" | |
| test -n "$WEBSITE_DEPLOY_PATH" | |
| test -n "$WEBSITE_DEPLOY_SSH_KEY" | |
| - name: Set up SSH key | |
| env: | |
| WEBSITE_DEPLOY_HOST: ${{ secrets.WEBSITE_DEPLOY_HOST }} | |
| WEBSITE_DEPLOY_SSH_KEY: ${{ secrets.WEBSITE_DEPLOY_SSH_KEY }} | |
| WEBSITE_DEPLOY_HOST_KEY: ${{ secrets.WEBSITE_DEPLOY_HOST_KEY }} | |
| run: | | |
| install -m 700 -d ~/.ssh | |
| printf "%s\n" "$WEBSITE_DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519 | |
| chmod 600 ~/.ssh/id_ed25519 | |
| website_host="${WEBSITE_DEPLOY_HOST//$'\r'/}" | |
| website_host="${website_host#"${website_host%%[![:space:]]*}"}" | |
| website_host="${website_host%"${website_host##*[![:space:]]}"}" | |
| printf "WEBSITE_DEPLOY_HOST_CLEAN=%s\n" "$website_host" >> "$GITHUB_ENV" | |
| website_known_hosts="$(mktemp)" | |
| website_known_host="${WEBSITE_DEPLOY_HOST_KEY//$'\r'/}" | |
| if [ -n "$website_known_host" ]; then | |
| case "$website_known_host" in | |
| ssh-*|ecdsa-*) printf "%s %s\n" "$website_host" "$website_known_host" > "$website_known_hosts" ;; | |
| *" ssh-"*|*" ecdsa-"*) printf "%s\n" "$website_known_host" > "$website_known_hosts" ;; | |
| *) echo "::warning::Ignoring WEBSITE_DEPLOY_HOST_KEY because it is not a known_hosts entry or bare SSH host key." ;; | |
| esac | |
| fi | |
| if ! ssh-keygen -F "$website_host" -f "$website_known_hosts" >/dev/null 2>&1; then | |
| ssh-keyscan -T 10 -t ed25519,rsa "$website_host" > "$website_known_hosts" | |
| fi | |
| mv "$website_known_hosts" ~/.ssh/known_hosts | |
| chmod 600 ~/.ssh/known_hosts | |
| if ! ssh-keygen -F "$website_host" -f ~/.ssh/known_hosts >/dev/null; then | |
| echo "::error::Could not create a known_hosts entry for WEBSITE_DEPLOY_HOST." | |
| exit 1 | |
| fi | |
| - name: Verify website SSH access | |
| run: | | |
| ssh \ | |
| -i "$HOME/.ssh/id_ed25519" \ | |
| -o BatchMode=yes \ | |
| -o IdentitiesOnly=yes \ | |
| -o StrictHostKeyChecking=yes \ | |
| -o UserKnownHostsFile="$HOME/.ssh/known_hosts" \ | |
| "${{ secrets.WEBSITE_DEPLOY_USER }}@$WEBSITE_DEPLOY_HOST_CLEAN" \ | |
| "true" | |
| - name: Sync website/ | |
| run: | | |
| rsync -az --delete \ | |
| -e "ssh -i $HOME/.ssh/id_ed25519 -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/known_hosts" \ | |
| website/ \ | |
| "${{ secrets.WEBSITE_DEPLOY_USER }}@$WEBSITE_DEPLOY_HOST_CLEAN:${{ secrets.WEBSITE_DEPLOY_PATH }}/" | |
| - name: Deploy website container on remote host | |
| env: | |
| WEBSITE_DEPLOY_COMMAND: ${{ secrets.WEBSITE_DEPLOY_COMMAND }} | |
| WEBSITE_DEPLOY_PATH: ${{ secrets.WEBSITE_DEPLOY_PATH }} | |
| WEBSITE_DOCS_URL: ${{ secrets.WEBSITE_DOCS_URL }} | |
| WEBSITE_BASE_URL: ${{ secrets.WEBSITE_BASE_URL }} | |
| WEBSITE_CONTAINER_NAME: ${{ secrets.WEBSITE_CONTAINER_NAME }} | |
| WEBSITE_IMAGE_NAME: ${{ secrets.WEBSITE_IMAGE_NAME }} | |
| WEBSITE_HOST_PORT: ${{ secrets.WEBSITE_HOST_PORT }} | |
| WEBSITE_CONTAINER_PORT: ${{ secrets.WEBSITE_CONTAINER_PORT }} | |
| run: | | |
| if [ -n "$WEBSITE_DEPLOY_COMMAND" ]; then | |
| ssh "${{ secrets.WEBSITE_DEPLOY_USER }}@$WEBSITE_DEPLOY_HOST_CLEAN" "$WEBSITE_DEPLOY_COMMAND" | |
| exit 0 | |
| fi | |
| container_name="${WEBSITE_CONTAINER_NAME:-mcp-runtime-website}" | |
| image_name="${WEBSITE_IMAGE_NAME:-mcp-runtime-website:latest}" | |
| host_port="${WEBSITE_HOST_PORT:-8080}" | |
| container_port="${WEBSITE_CONTAINER_PORT:-8080}" | |
| docs_url="${WEBSITE_DOCS_URL:-https://docs.mcpruntime.org/}" | |
| base_url="${WEBSITE_BASE_URL:-https://mcpruntime.org}" | |
| git_sha="${GITHUB_SHA:-unknown}" | |
| b64() { printf '%s' "$1" | base64 -w 0; } | |
| # shellcheck disable=SC2029 | |
| ssh "${{ secrets.WEBSITE_DEPLOY_USER }}@$WEBSITE_DEPLOY_HOST_CLEAN" \ | |
| "WEBSITE_DEPLOY_PATH_B64=$(b64 "$WEBSITE_DEPLOY_PATH") \ | |
| WEBSITE_CONTAINER_NAME_B64=$(b64 "$container_name") \ | |
| WEBSITE_IMAGE_NAME_B64=$(b64 "$image_name") \ | |
| WEBSITE_HOST_PORT_B64=$(b64 "$host_port") \ | |
| WEBSITE_CONTAINER_PORT_B64=$(b64 "$container_port") \ | |
| WEBSITE_DOCS_URL_B64=$(b64 "$docs_url") \ | |
| WEBSITE_BASE_URL_B64=$(b64 "$base_url") \ | |
| WEBSITE_GIT_SHA_B64=$(b64 "$git_sha") \ | |
| bash -s" <<'REMOTE' | |
| set -euo pipefail | |
| decode() { printf '%s' "$1" | base64 -d; } | |
| deploy_path="$(decode "$WEBSITE_DEPLOY_PATH_B64")" | |
| container_name="$(decode "$WEBSITE_CONTAINER_NAME_B64")" | |
| image_name="$(decode "$WEBSITE_IMAGE_NAME_B64")" | |
| host_port="$(decode "$WEBSITE_HOST_PORT_B64")" | |
| container_port="$(decode "$WEBSITE_CONTAINER_PORT_B64")" | |
| docs_url="$(decode "$WEBSITE_DOCS_URL_B64")" | |
| base_url="$(decode "$WEBSITE_BASE_URL_B64")" | |
| git_sha="$(decode "$WEBSITE_GIT_SHA_B64")" | |
| cd "$deploy_path" | |
| docker rm -f "$container_name" >/dev/null 2>&1 || true | |
| docker build --pull --build-arg GIT_SHA="$git_sha" -t "$image_name" . | |
| docker run -d --name "$container_name" \ | |
| --restart unless-stopped \ | |
| -p "$host_port:$container_port" \ | |
| -e MCP_DOCS_URL="$docs_url" \ | |
| -e MCP_WEBSITE_BASE_URL="$base_url" \ | |
| "$image_name" | |
| docker image prune -f >/dev/null | |
| REMOTE |