# ----------------------------------------------------------------------------- # stellaops-gate-example.yml # Sprint: SPRINT_20251226_001_BE_cicd_gate_integration # Task: CICD-GATE-07 - GitHub Actions example workflow using stella gate evaluate # Description: Example workflow demonstrating StellaOps release gate integration # ----------------------------------------------------------------------------- # # This workflow demonstrates how to integrate StellaOps release gates into your # GitHub Actions CI/CD pipeline. The gate evaluates security drift between your # current build and the approved baseline, blocking releases that introduce new # reachable vulnerabilities. # # Prerequisites: # 1. StellaOps CLI installed (see setup step below) # 2. STELLAOPS_API_TOKEN secret configured # 3. Container image built and pushed to registry # # Exit codes: # 0 = Pass - Release may proceed # 1 = Warn - Release may proceed with warnings (configurable) # 2 = Fail - Release blocked due to security policy violation # name: StellaOps Release Gate Example on: push: branches: [main, release/*] pull_request: branches: [main] env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} STELLAOPS_BACKEND_URL: ${{ vars.STELLAOPS_BACKEND_URL || 'https://stellaops.internal' }} jobs: build: name: Build Container Image runs-on: ubuntu-latest outputs: image_digest: ${{ steps.build.outputs.digest }} image_ref: ${{ steps.build.outputs.image_ref }} permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=sha,prefix= type=ref,event=branch type=ref,event=pr - name: Build and push id: build uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max - name: Output image reference id: output run: | echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT echo "image_ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT gate: name: StellaOps Release Gate needs: build runs-on: ubuntu-latest # Continue on gate failure to allow override workflow continue-on-error: ${{ github.event_name == 'pull_request' }} permissions: contents: read id-token: write # Required for OIDC token acquisition outputs: gate_status: ${{ steps.gate.outputs.status }} gate_decision_id: ${{ steps.gate.outputs.decision_id }} steps: - name: Install StellaOps CLI run: | # Download and install the StellaOps CLI curl -sSL https://get.stella-ops.org/cli | bash echo "$HOME/.stellaops/bin" >> $GITHUB_PATH - name: Acquire OIDC Token (Keyless) id: oidc if: ${{ vars.STELLAOPS_USE_KEYLESS == 'true' }} uses: actions/github-script@v7 with: script: | const token = await core.getIDToken('stellaops'); core.setSecret(token); core.setOutput('token', token); - name: Evaluate Release Gate id: gate env: STELLAOPS_API_TOKEN: ${{ secrets.STELLAOPS_API_TOKEN }} STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }} run: | # Determine baseline strategy based on branch if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then BASELINE="production" elif [[ "${{ github.ref }}" == refs/heads/release/* ]]; then BASELINE="last-approved" else BASELINE="previous-build" fi echo "Evaluating gate for image: ${{ needs.build.outputs.image_digest }}" echo "Baseline strategy: ${BASELINE}" # Run gate evaluation # --output json provides machine-readable output # --ci-context identifies the CI system for audit logging RESULT=$(stella gate evaluate \ --image "${{ needs.build.outputs.image_digest }}" \ --baseline "${BASELINE}" \ --output json \ --ci-context "github-actions" \ --repository "${{ github.repository }}" \ --tag "${{ github.sha }}" \ 2>&1) || EXIT_CODE=$? EXIT_CODE=${EXIT_CODE:-0} # Parse JSON output for decision details DECISION_ID=$(echo "$RESULT" | jq -r '.decisionId // "unknown"') STATUS=$(echo "$RESULT" | jq -r '.status // "unknown"') SUMMARY=$(echo "$RESULT" | jq -r '.summary // "No summary available"') echo "decision_id=${DECISION_ID}" >> $GITHUB_OUTPUT echo "status=${STATUS}" >> $GITHUB_OUTPUT echo "exit_code=${EXIT_CODE}" >> $GITHUB_OUTPUT # Create summary echo "## StellaOps Gate Evaluation" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY echo "| Decision ID | \`${DECISION_ID}\` |" >> $GITHUB_STEP_SUMMARY echo "| Status | **${STATUS}** |" >> $GITHUB_STEP_SUMMARY echo "| Image | \`${{ needs.build.outputs.image_digest }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Baseline | ${BASELINE} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Summary" >> $GITHUB_STEP_SUMMARY echo "${SUMMARY}" >> $GITHUB_STEP_SUMMARY # Exit with the gate's exit code exit ${EXIT_CODE} - name: Gate Status Badge if: always() run: | case "${{ steps.gate.outputs.status }}" in Pass) echo "::notice::Gate PASSED - Release may proceed" ;; Warn) echo "::warning::Gate PASSED WITH WARNINGS - Review recommended" ;; Fail) echo "::error::Gate BLOCKED - Security policy violation detected" ;; esac deploy: name: Deploy to Staging needs: [build, gate] if: ${{ needs.gate.outputs.gate_status == 'Pass' || needs.gate.outputs.gate_status == 'Warn' }} runs-on: ubuntu-latest environment: staging steps: - name: Deploy to staging run: | echo "Deploying ${{ needs.build.outputs.image_ref }} to staging..." # Add your deployment commands here # Optional: Manual override for blocked releases (requires elevated permissions) override: name: Request Gate Override needs: [build, gate] if: ${{ failure() && needs.gate.outputs.gate_status == 'Fail' }} runs-on: ubuntu-latest environment: security-override # Requires manual approval steps: - name: Install StellaOps CLI run: | curl -sSL https://get.stella-ops.org/cli | bash echo "$HOME/.stellaops/bin" >> $GITHUB_PATH - name: Request Override with Justification env: STELLAOPS_API_TOKEN: ${{ secrets.STELLAOPS_OVERRIDE_TOKEN }} run: | # This requires the security-override environment approval # and a separate token with override permissions stella gate evaluate \ --image "${{ needs.build.outputs.image_digest }}" \ --baseline "last-approved" \ --allow-override \ --justification "Emergency release approved by ${{ github.actor }} - see PR #${{ github.event.pull_request.number }}" \ --ci-context "github-actions-override"