chore(deps): update extractions/setup-just action to v4 #4721
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: [main] | |
| merge_group: | |
| workflow_dispatch: | |
| jobs: | |
| frontend-test: | |
| if: (github.event_name == 'pull_request' && !startsWith(github.head_ref, 'release-please--')) || github.event_name == 'merge_group' | |
| runs-on: depot-ubuntu-24.04-8 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "24" | |
| cache: "pnpm" | |
| cache-dependency-path: dashboard/pnpm-lock.yaml | |
| - name: Install dependencies | |
| working-directory: ./dashboard | |
| run: pnpm install --frozen-lockfile | |
| - name: Install just | |
| uses: extractions/setup-just@v4 | |
| - name: Run frontend CI pipeline | |
| id: ci-step | |
| run: | | |
| START_TIME=$(date +%s) | |
| just ci ts | |
| END_TIME=$(date +%s) | |
| CI_DURATION=$((END_TIME - START_TIME)) | |
| echo "ci_duration=$CI_DURATION" >> $GITHUB_OUTPUT | |
| echo "Frontend CI pipeline completed in ${CI_DURATION}s" | |
| # Report metrics to Somnial | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/frontend-ci-duration?value=${CI_DURATION}" || true | |
| - name: Extract frontend coverage metrics | |
| id: coverage-step | |
| working-directory: ./dashboard | |
| run: | | |
| # Extract coverage from already generated coverage files (from 'just ci ts') | |
| echo "Checking for coverage files..." | |
| ls -la coverage/ || echo "Coverage directory not found" | |
| if [ -f coverage/lcov.info ]; then | |
| echo "Found lcov.info, extracting coverage..." | |
| # Parse coverage/lcov.info using awk (clean and always available) | |
| COVERAGE=$(awk ' | |
| /^LF:/ { lf += $2 } | |
| /^LH:/ { lh += $2 } | |
| END { | |
| if (lf > 0) | |
| printf "%.2f", (lh * 100) / lf | |
| else | |
| print "0" | |
| } | |
| ' FS=: coverage/lcov.info) | |
| echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT | |
| echo "Frontend code coverage: ${COVERAGE}%" | |
| # Report coverage to Somnial | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/frontend-code-coverage?value=${COVERAGE}" || true | |
| else | |
| echo "No coverage report found at coverage/lcov.info - coverage may not have been generated" | |
| echo "coverage=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Calculate bundle metrics | |
| id: build-step | |
| run: | | |
| cd dashboard | |
| # Calculate bundle metrics from the build output | |
| TOTAL_SIZE=$(find dist -name "*.js" -o -name "*.css" | xargs wc -c | tail -1 | awk '{print $1}') | |
| GZIPPED_SIZE=$(find dist -name "*.js" -o -name "*.css" | xargs gzip -c | wc -c) | |
| echo "total_size=$TOTAL_SIZE" >> $GITHUB_OUTPUT | |
| echo "gzipped_size=$GZIPPED_SIZE" >> $GITHUB_OUTPUT | |
| echo "Bundle: ${TOTAL_SIZE} bytes (${GZIPPED_SIZE} gzipped)" | |
| # Report bundle metrics to Somnial | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/total-bundle-size?value=${TOTAL_SIZE}" || true | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/gzipped-bundle-size?value=${GZIPPED_SIZE}" || true | |
| backend-test: | |
| if: (github.event_name == 'pull_request' && !startsWith(github.head_ref, 'release-please--')) || github.event_name == 'merge_group' | |
| runs-on: depot-ubuntu-24.04-16 | |
| # Postgres is started manually below (not via `services:`) so we can pass | |
| # `-c max_connections=300` — the 16-core runner runs `#[sqlx::test]` | |
| # tests with high parallelism, and each one opens its own pools + underway | |
| # PgListener connections, so the default 100 is insufficient. | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Start Postgres | |
| run: | | |
| docker run -d --name postgres \ | |
| -e POSTGRES_DB=test \ | |
| -e POSTGRES_USER=postgres \ | |
| -e POSTGRES_PASSWORD=postgres \ | |
| -e POSTGRES_HOST_AUTH_METHOD=trust \ | |
| -p 5432:5432 \ | |
| postgres:latest \ | |
| -c max_connections=300 | |
| for _ in $(seq 1 30); do | |
| docker exec postgres pg_isready -U postgres && break | |
| sleep 1 | |
| done | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: llvm-tools-preview | |
| # used only for caching binary installs | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install just | |
| uses: extractions/setup-just@v4 | |
| - name: Install sqlx-cli and cargo-llvm-cov | |
| run: | | |
| cargo install sqlx-cli --no-default-features --features native-tls,postgres --locked | |
| cargo install cargo-llvm-cov --locked | |
| - name: Run backend CI pipeline | |
| id: ci-step | |
| env: | |
| DB_PASS: postgres | |
| run: | | |
| START_TIME=$(date +%s) | |
| just ci rust -- -D warnings | |
| END_TIME=$(date +%s) | |
| CI_DURATION=$((END_TIME - START_TIME)) | |
| echo "ci_duration=$CI_DURATION" >> $GITHUB_OUTPUT | |
| echo "Rust CI pipeline completed in ${CI_DURATION}s" | |
| # Report metrics to Somnial | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/backend-ci-duration?value=${CI_DURATION}" || true | |
| - name: Extract backend coverage metrics | |
| id: coverage-step | |
| working-directory: ./dwctl | |
| run: | | |
| # Extract coverage from already generated lcov.info file (from 'just ci rust') | |
| echo "Checking for coverage files..." | |
| ls -la lcov.info || echo "lcov.info not found" | |
| if [ -f lcov.info ]; then | |
| echo "Found lcov.info, parsing coverage data..." | |
| # Parse lcov.info using awk (clean and always available) | |
| COVERAGE=$(awk ' | |
| /^LF:/ { lf += $2 } | |
| /^LH:/ { lh += $2 } | |
| END { | |
| if (lf > 0) | |
| printf "%.2f", (lh * 100) / lf | |
| else | |
| print "0" | |
| } | |
| ' FS=: lcov.info) | |
| echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT | |
| echo "Backend code coverage: ${COVERAGE}%" | |
| # Report coverage to Somnial | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/backend-code-coverage?value=${COVERAGE}" || true | |
| else | |
| echo "No coverage report found at lcov.info - coverage may not have been generated" | |
| echo "coverage=0" >> $GITHUB_OUTPUT | |
| fi | |
| build: | |
| if: (github.event_name == 'pull_request' && !startsWith(github.head_ref, 'release-please--')) || github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' | |
| runs-on: depot-ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| env: | |
| DOCKER_METADATA_PR_HEAD_SHA: true | |
| with: | |
| images: ghcr.io/doublewordai/control-layer | |
| sep-tags: "\n" | |
| tags: | | |
| type=sha,prefix=sha- | |
| type=raw,value=manual,enable=${{ github.event_name == 'workflow_dispatch' }} | |
| - name: Build and push all images | |
| id: build-step | |
| run: | | |
| START_TIME=$(date +%s) | |
| echo "start_time=$START_TIME" >> $GITHUB_OUTPUT | |
| echo "Starting Docker build at $(date)" | |
| - name: Build and push with Depot | |
| uses: depot/build-push-action@v1 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| sbom: true | |
| provenance: true | |
| - name: Record build time | |
| id: build-time-step | |
| run: | | |
| END_TIME=$(date +%s) | |
| BUILD_DURATION=$((END_TIME - ${{ steps.build-step.outputs.start_time }})) | |
| echo "build_duration=$BUILD_DURATION" >> $GITHUB_OUTPUT | |
| echo "Docker build completed in ${BUILD_DURATION}s" | |
| # Report build time to Somnial | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/docker-build-duration?value=${BUILD_DURATION}" || true | |
| outputs: | |
| image-tag: ${{ steps.meta.outputs.tags }} | |
| security-scan: | |
| if: (github.event_name == 'pull_request' && !startsWith(github.head_ref, 'release-please--')) || github.event_name == 'merge_group' | |
| runs-on: depot-ubuntu-24.04 | |
| needs: build | |
| permissions: | |
| contents: read | |
| packages: read | |
| security-events: write # For uploading SARIF results | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install Grype | |
| run: | | |
| curl -sSfL https://get.anchore.io/grype | sudo sh -s -- -b /usr/local/bin | |
| grype version | |
| - name: Security scan with Grype | |
| id: security-scan | |
| run: | | |
| # Define image tags using the built images | |
| TAG_LIST="${{ needs.build.outputs.image-tag }}" | |
| # Extract just the SHA tag for our scan | |
| TAG=$(echo "$TAG_LIST" | head -n1 | cut -d':' -f2) | |
| CONTROL_LAYER_TAG="ghcr.io/doublewordai/control-layer:$TAG" | |
| echo "Scanning images for security vulnerabilities..." | |
| echo "Tag: $TAG" | |
| # Parse results and calculate metrics | |
| calculate_vulns() { | |
| local file=$1 | |
| local severity=$2 | |
| jq -r --arg sev "$severity" '[.matches[] | select(.vulnerability.severity == $sev)] | length' "$file" | |
| } | |
| # Scan control-layer image | |
| echo "Scanning control-layer image: $CONTROL_LAYER_TAG" | |
| grype "$CONTROL_LAYER_TAG" --output json --file control-layer-vulnerabilities.json | |
| CONTROL_LAYER_CRITICAL=$(calculate_vulns control-layer-vulnerabilities.json "Critical") | |
| CONTROL_LAYER_HIGH=$(calculate_vulns control-layer-vulnerabilities.json "High") | |
| # Console output | |
| echo "Security scan completed:" | |
| echo " Critical vulnerabilities: $CONTROL_LAYER_CRITICAL" | |
| echo " High vulnerabilities: $CONTROL_LAYER_HIGH" | |
| # Set outputs for metrics reporting | |
| echo "total_critical=$CONTROL_LAYER_CRITICAL" >> $GITHUB_OUTPUT | |
| echo "total_high=$CONTROL_LAYER_HIGH" >> $GITHUB_OUTPUT | |
| - name: Report vulnerability counts to Somnial | |
| if: always() | |
| run: | | |
| # Report high and critical vulnerability counts to Somnial | |
| CRITICAL="${{ steps.security-scan.outputs.total_critical || '0' }}" | |
| HIGH="${{ steps.security-scan.outputs.total_high || '0' }}" | |
| echo "Reporting vulnerability counts to Somnial..." | |
| echo " Critical vulnerabilities: ${CRITICAL}" | |
| echo " High vulnerabilities: ${HIGH}" | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/critical-vulnerabilities?value=${CRITICAL}" || echo "Failed to report critical vulnerabilities" | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/high-vulnerabilities?value=${HIGH}" || echo "Failed to report high vulnerabilities" | |
| e2e-test-docker: | |
| if: (github.event_name == 'pull_request' && !startsWith(github.head_ref, 'release-please--')) || github.event_name == 'merge_group' | |
| runs-on: depot-ubuntu-24.04 | |
| needs: build | |
| permissions: | |
| contents: read | |
| packages: read | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Install just | |
| uses: extractions/setup-just@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "24" | |
| cache: "pnpm" | |
| cache-dependency-path: dashboard/pnpm-lock.yaml | |
| - name: Install dashboard dependencies | |
| working-directory: ./dashboard | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Playwright browsers | |
| working-directory: ./dashboard | |
| run: npx playwright install chromium --only-shell | |
| - name: Install hurl | |
| # Pinned: hurl 8.0.0 (2026-04-27) ships an RFC 9535 JSONPath rewrite | |
| # that breaks our existing `count` / `nth` patterns on filter results | |
| # that yield 0 or 1 items (see tests/perms/{models,users,requests, | |
| # transactions}.hurl and tests/authenticated/admin-crud-group.hurl). | |
| # Hold at 7.x until the assertions are migrated to RFC 9535 semantics. | |
| uses: gacts/install-hurl@v1 | |
| with: | |
| version: 7.1.0 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up Docker Compose | |
| uses: docker/setup-compose-action@v2 | |
| # Run end to end docker-compose tests | |
| - name: Run Docker end-to-end tests | |
| id: e2e-step | |
| run: | | |
| # Extract first image from newline-separated list | |
| IMAGE_LIST="${{ needs.build.outputs.image-tag }}" | |
| export IMAGE=$(echo "$IMAGE_LIST" | head -n1) | |
| echo "Using image: $IMAGE" | |
| START_TIME=$(date +%s) | |
| just test docker | |
| END_TIME=$(date +%s) | |
| E2E_DURATION=$((END_TIME - START_TIME)) | |
| echo "e2e_duration=$E2E_DURATION" >> $GITHUB_OUTPUT | |
| echo "Docker E2E tests completed in ${E2E_DURATION}s" | |
| # Report metrics to Somnial | |
| curl -X POST "https://charts.somnial.co/doubleword-control-layer/e2e-docker-duration?value=${E2E_DURATION}" || true |