fix(security): bump gh 2.90.0, yq 4.53.2 to address CVEs #213
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: "v2: CI" | |
| # v2 Bash/Docker CI Pipeline | |
| # Triggers on changes to v2 directory and related configurations | |
| on: | |
| push: | |
| branches: [main, develop] | |
| paths: | |
| - 'v2/**' | |
| - '.github/workflows/ci-v2.yml' | |
| - '.github/actions/shared/**' | |
| - 'package.json' | |
| pull_request: | |
| branches: [main, develop] | |
| paths: | |
| - 'v2/**' | |
| - '.github/workflows/ci-v2.yml' | |
| - '.github/actions/shared/**' | |
| - 'package.json' | |
| workflow_dispatch: | |
| inputs: | |
| providers: | |
| description: | | |
| Providers to test (comma-separated or 'all'). | |
| Options: docker, fly, devpod-aws, devpod-gcp, devpod-azure, devpod-do, devpod-k8s | |
| required: false | |
| default: "docker,fly,devpod-k8s" | |
| type: string | |
| test-level: | |
| description: Test level to run | |
| required: false | |
| default: "profile" | |
| type: choice | |
| options: | |
| - quick | |
| - extension | |
| - profile | |
| - all | |
| extension-profile: | |
| description: Extension profile to test (affects resource requirements) | |
| required: false | |
| default: "minimal" | |
| type: choice | |
| options: | |
| - minimal | |
| - mobile | |
| - fullstack | |
| - ai-dev | |
| - systems | |
| - devops | |
| - anthropic-dev | |
| - enterprise | |
| - visionflow-core | |
| - visionflow-data-scientist | |
| - visionflow-creative | |
| - visionflow-full | |
| extension: | |
| description: Extension to test (when test-level is 'extension') | |
| required: false | |
| default: "nodejs" | |
| type: string | |
| skip-cleanup: | |
| description: Skip resource cleanup for debugging | |
| required: false | |
| default: false | |
| type: boolean | |
| concurrency: | |
| group: ci-v2-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| DOCKER_BUILDKIT: 1 | |
| jobs: | |
| # NOTE: Validation is handled by dedicated workflows: | |
| # - validate-shell.yml (shellcheck) | |
| # - validate-markdown.yml (markdownlint) | |
| # - validate-yaml.yml (YAML/schema validation) | |
| # ============================================ | |
| # Build Job | |
| # ============================================ | |
| build: | |
| name: Build v2 Docker Image | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write # NEW: for GHCR push | |
| outputs: | |
| image-tag: ${{ steps.build.outputs.tag }} | |
| image-digest: ${{ steps.build.outputs.digest }} | |
| image-ref: ${{ steps.meta.outputs.tags }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| # NEW: Login to GHCR | |
| - name: Login to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # NEW: Generate metadata with CI tags | |
| # NOTE: Use full SHA (${{ github.sha }}) not {{sha}} template (which gives short SHA) | |
| # to ensure consistency between build tags and artifact save step | |
| - name: Generate metadata | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| images: ghcr.io/${{ github.repository }} | |
| tags: | | |
| type=raw,value=v2-ci-${{ github.sha }} | |
| labels: | | |
| org.opencontainers.image.title=Sindri v2 | |
| org.opencontainers.image.description=Multi-cloud development environment orchestrator | |
| sindri.version=v2 | |
| sindri.ci.run=${{ github.run_id }} | |
| sindri.ci.branch=${{ github.ref_name }} | |
| # MODIFIED: Build with fresh layers + push to GHCR | |
| - name: Build and push to GHCR | |
| id: build | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: v2/Dockerfile | |
| push: true # NEW: Push to registry | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| platforms: linux/amd64 # Required for single-platform image with attestations | |
| no-cache: true # NEW: Fresh builds | |
| pull: true # NEW: Latest base images | |
| secrets: | | |
| github_token=${{ secrets.GITHUB_TOKEN }} | |
| provenance: mode=max | |
| sbom: true | |
| # NOTE: Image is pushed to GHCR and pulled directly by test jobs | |
| # This avoids attestation/provenance issues with artifact-based distribution | |
| - name: Export image metadata | |
| run: | | |
| echo "tag=ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }}" >> $GITHUB_OUTPUT | |
| echo "✅ Image built and pushed" | |
| echo " Image: ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }}" | |
| echo " Note: Test jobs will pull directly from GHCR" | |
| # ============================================ | |
| # Test Matrix Generation | |
| # ============================================ | |
| generate-matrix: | |
| name: Generate Test Matrix | |
| runs-on: ubuntu-latest | |
| outputs: | |
| providers: ${{ steps.matrix.outputs.providers }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Generate matrices | |
| id: matrix | |
| run: | | |
| # Determine which providers to test based on trigger type | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| INPUT_PROVIDERS="${{ github.event.inputs.providers }}" | |
| if [[ "$INPUT_PROVIDERS" == "all" ]]; then | |
| PROVIDERS='["docker", "fly", "devpod-aws", "devpod-gcp", "devpod-azure", "devpod-do", "devpod-k8s"]' | |
| else | |
| PROVIDERS=$(echo "$INPUT_PROVIDERS" | jq -Rc 'split(",") | map(ltrimstr(" ") | rtrimstr(" "))') | |
| fi | |
| else | |
| # Push and PR events all test the same provider set | |
| PROVIDERS='["docker", "fly", "devpod-k8s"]' | |
| fi | |
| # Skip fly provider for Dependabot PRs (no access to FLY_API_TOKEN secret) | |
| if [[ "${{ github.actor }}" == "dependabot[bot]" ]]; then | |
| PROVIDERS=$(echo "$PROVIDERS" | jq -c 'map(select(. != "fly"))') | |
| echo "::notice title=Dependabot Detected::Skipping fly provider (no secret access)" | |
| fi | |
| echo "providers=$PROVIDERS" >> $GITHUB_OUTPUT | |
| echo "::notice title=Test Providers::$PROVIDERS" | |
| # ============================================ | |
| # Security Scanning | |
| # ============================================ | |
| security-scan: | |
| name: Security Scan | |
| needs: build | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: read | |
| security-events: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Login to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Scan with Trivy | |
| uses: aquasecurity/trivy-action@0.35.0 | |
| with: | |
| image-ref: ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }} | |
| format: sarif | |
| output: trivy-results.sarif | |
| severity: CRITICAL,HIGH,MEDIUM | |
| ignore-unfixed: true | |
| - name: Upload to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: trivy-results.sarif | |
| - name: Scan summary | |
| if: always() | |
| run: | | |
| echo "## Security Scan Results" >> $GITHUB_STEP_SUMMARY | |
| echo "Image: \`ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -f trivy-results.sarif ]; then | |
| ISSUES=$(jq '.runs[0].results | length' trivy-results.sarif) | |
| echo "- **Issues Found**: $ISSUES" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # ============================================ | |
| # Unified Provider Testing | |
| # ============================================ | |
| test-docker: | |
| name: Test v2 on Docker | |
| needs: [build, generate-matrix] | |
| if: contains(fromJson(needs.generate-matrix.outputs.providers), 'docker') | |
| uses: ./.github/workflows/v2-test-provider.yml | |
| with: | |
| provider: docker | |
| test-level: ${{ github.event.inputs.test-level || 'profile' }} | |
| extension-profile: ${{ github.event.inputs.extension-profile || 'minimal' }} | |
| extension: ${{ github.event.inputs.extension || 'nodejs' }} | |
| skip-cleanup: ${{ github.event.inputs.skip-cleanup == 'true' }} | |
| image: ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }} | |
| use-registry: true | |
| secrets: inherit | |
| test-fly: | |
| name: Test v2 on Fly.io | |
| needs: [build, generate-matrix] | |
| if: contains(fromJson(needs.generate-matrix.outputs.providers), 'fly') | |
| uses: ./.github/workflows/v2-test-provider.yml | |
| with: | |
| provider: fly | |
| test-level: ${{ github.event.inputs.test-level || 'profile' }} | |
| extension-profile: ${{ github.event.inputs.extension-profile || 'minimal' }} | |
| extension: ${{ github.event.inputs.extension || 'nodejs' }} | |
| skip-cleanup: ${{ github.event.inputs.skip-cleanup == 'true' }} | |
| image: ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }} | |
| use-registry: true | |
| secrets: inherit | |
| test-devpod-k8s: | |
| name: Test v2 on DevPod K8s | |
| needs: [build, generate-matrix] | |
| if: contains(fromJson(needs.generate-matrix.outputs.providers), 'devpod-k8s') | |
| uses: ./.github/workflows/v2-test-provider.yml | |
| with: | |
| provider: devpod-k8s | |
| test-level: ${{ github.event.inputs.test-level || 'profile' }} | |
| extension-profile: ${{ github.event.inputs.extension-profile || 'minimal' }} | |
| extension: ${{ github.event.inputs.extension || 'nodejs' }} | |
| skip-cleanup: ${{ github.event.inputs.skip-cleanup == 'true' }} | |
| image: ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }} | |
| use-registry: true | |
| secrets: inherit | |
| # ============================================ | |
| # Mark Promotion Candidate | |
| # ============================================ | |
| mark-passed: | |
| name: Mark as Promotion Candidate | |
| needs: [build, security-scan, test-docker, test-fly, test-devpod-k8s] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Login to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Tag as promotion candidate | |
| run: | | |
| echo "Pulling CI image..." | |
| docker pull ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }} | |
| echo "Tagging as v2-ci-passed..." | |
| docker tag ghcr.io/${{ github.repository }}:v2-ci-${{ github.sha }} \ | |
| ghcr.io/${{ github.repository }}:v2-ci-passed-${{ github.sha }} | |
| echo "Pushing promotion candidate tag..." | |
| docker push ghcr.io/${{ github.repository }}:v2-ci-passed-${{ github.sha }} | |
| echo "✅ Image marked as promotion candidate" | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Promotion Candidate" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Image**: \`ghcr.io/${{ github.repository }}:v2-ci-passed-${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Status**: Ready for release promotion" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Tests**: All provider tests passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Security**: Vulnerability scan completed" >> $GITHUB_STEP_SUMMARY | |
| # ============================================ | |
| # CI Status Checks | |
| # ============================================ | |
| ci-required: | |
| name: CI v2 Required Checks | |
| runs-on: ubuntu-latest | |
| needs: | |
| - build | |
| - security-scan | |
| if: ${{ always() }} | |
| steps: | |
| - name: Check required jobs | |
| run: | | |
| # Check if any required job failed | |
| # NOTE: Validation is handled by dedicated workflows: | |
| # - validate-shell.yml, validate-markdown.yml, validate-yaml.yml | |
| BUILD="${{ needs.build.result }}" | |
| SECURITY="${{ needs.security-scan.result }}" | |
| if [[ "$BUILD" != "success" ]]; then | |
| echo "❌ Required CI v2 checks failed" | |
| echo "Results:" | |
| echo " build: $BUILD" | |
| echo " security-scan: $SECURITY" | |
| exit 1 | |
| fi | |
| if [[ "$SECURITY" != "success" ]]; then | |
| echo "⚠️ Security scan failed but not blocking" | |
| echo " security-scan: $SECURITY" | |
| fi | |
| echo "✅ Required CI v2 checks passed" | |
| ci-status: | |
| name: CI v2 Status | |
| runs-on: ubuntu-latest | |
| needs: | |
| - ci-required | |
| - test-docker | |
| - test-fly | |
| - test-devpod-k8s | |
| if: ${{ always() }} | |
| steps: | |
| - name: Check overall status | |
| run: | | |
| REQUIRED="${{ needs.ci-required.result }}" | |
| TEST_DOCKER="${{ needs.test-docker.result }}" | |
| TEST_FLY="${{ needs.test-fly.result }}" | |
| TEST_DEVPOD_K8S="${{ needs.test-devpod-k8s.result }}" | |
| # Required checks must pass | |
| if [[ "$REQUIRED" != "success" ]]; then | |
| echo "❌ CI v2 failed - required checks did not pass" | |
| exit 1 | |
| fi | |
| # Provider tests should pass if they ran | |
| if [[ "$TEST_DOCKER" != "skipped" ]] && [[ "$TEST_DOCKER" != "success" ]]; then | |
| echo "❌ CI v2 failed - docker provider tests failed" | |
| echo " test-docker: $TEST_DOCKER" | |
| exit 1 | |
| fi | |
| if [[ "$TEST_FLY" != "skipped" ]] && [[ "$TEST_FLY" != "success" ]]; then | |
| echo "❌ CI v2 failed - fly provider tests failed" | |
| echo " test-fly: $TEST_FLY" | |
| exit 1 | |
| fi | |
| if [[ "$TEST_DEVPOD_K8S" != "skipped" ]] && [[ "$TEST_DEVPOD_K8S" != "success" ]]; then | |
| echo "❌ CI v2 failed - devpod-k8s provider tests failed" | |
| echo " test-devpod-k8s: $TEST_DEVPOD_K8S" | |
| exit 1 | |
| fi | |
| echo "✅ CI v2 passed" | |
| echo "Results:" | |
| echo " ci-required: $REQUIRED" | |
| echo " test-docker: $TEST_DOCKER" | |
| echo " test-fly: $TEST_FLY" | |
| echo " test-devpod-k8s: $TEST_DEVPOD_K8S" | |
| - name: Generate summary | |
| if: always() | |
| run: | | |
| echo "# CI v2 Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Unified Provider Testing (v2)" >> $GITHUB_STEP_SUMMARY | |
| echo "Each selected provider receives **complete test coverage**:" >> $GITHUB_STEP_SUMMARY | |
| echo "- CLI Tests (sindri, extension-manager commands)" >> $GITHUB_STEP_SUMMARY | |
| echo "- Extension Tests (validation, installation)" >> $GITHUB_STEP_SUMMARY | |
| echo "- Integration Tests (end-to-end workflows)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Job Results" >> $GITHUB_STEP_SUMMARY | |
| echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Required Checks | ${{ needs.ci-required.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Test Docker | ${{ needs.test-docker.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Test Fly.io | ${{ needs.test-fly.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Test DevPod K8s | ${{ needs.test-devpod-k8s.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Metadata" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Version**: v2 (Bash/Docker)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Triggered by**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Branch**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Commit**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Run**: ${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY |