name: "make" on: push: branches: - "main" pull_request: branches: - "main" permissions: contents: "read" jobs: # ── Snapshot: build frontend apps ────────────────────────────────── build-apps: name: "build-apps" if: github.event_name == 'push' runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: "read" steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "./.github/actions/setup" with: go: "false" - run: "npm --workspace @probo/emails run build" - run: "make relay" - name: "Build console" run: | npm --workspace @probo/console run check NODE_ENV=production npm --workspace @probo/console run build - name: "Build trust" run: | npm --workspace @probo/trust run check NODE_ENV=production npm --workspace @probo/trust run build - uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7 with: name: "frontend-apps" path: | apps/console/dist/ apps/trust/dist/ packages/emails/dist/ retention-days: 1 # ── Snapshot: build Go binaries (matrix by GOOS/GOARCH) ──────────── build-snapshot-binary: name: "binary (${{ matrix.goos }}/${{ matrix.goarch }})" if: github.event_name == 'push' needs: [build-apps] runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: "read" strategy: fail-fast: false matrix: include: - { goos: linux, goarch: amd64 } - { goos: linux, goarch: arm64 } - { goos: darwin, goarch: amd64 } - { goos: darwin, goarch: arm64 } - { goos: windows, goarch: amd64 } - { goos: freebsd, goarch: amd64 } - { goos: freebsd, goarch: arm64 } - { goos: openbsd, goarch: amd64 } - { goos: openbsd, goarch: arm64 } steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "./.github/actions/setup" with: node: "false" - uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8 with: name: "frontend-apps" - name: "Generate Go code" run: | go generate ./pkg/server/api/connect/v1 go generate ./pkg/server/api/console/v1 go generate ./pkg/server/api/trust/v1 go generate ./pkg/server/api/mcp/v1 - name: "Build binaries" env: CGO_ENABLED: "0" GOOS: "${{ matrix.goos }}" GOARCH: "${{ matrix.goarch }}" run: | EXT="" if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi go build -ldflags "-s -w -X 'main.version=snapshot' -X 'main.env=prod'" \ -gcflags="-e" -o "dist/probod${EXT}" ./cmd/probod/main.go go build -ldflags "-s -w" \ -gcflags="-e" -o "dist/probod-bootstrap${EXT}" ./cmd/probod-bootstrap/main.go go build -ldflags "-s -w -X 'main.version=snapshot'" \ -gcflags="-e" -o "dist/prb${EXT}" ./cmd/prb/main.go - uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7 with: name: "binary-${{ matrix.goos }}-${{ matrix.goarch }}" path: "dist/" retention-days: 1 # ── Snapshot: build Docker images (matrix by architecture) ───────── build-snapshot-docker: name: "docker (${{ matrix.arch }})" if: github.event_name == 'push' needs: [build-snapshot-binary] runs-on: "runs-on=${{ github.run_id }}/runner=${{ matrix.runner }}/extras=s3-cache" permissions: contents: "read" security-events: "write" strategy: fail-fast: false matrix: include: - arch: amd64 platform: "linux/amd64" runner: "4cpu-linux-x64" - arch: arm64 platform: "linux/arm64" runner: "4cpu-linux-arm64" steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd" # v4.0.0 - uses: "docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121" # v4.1.0 with: username: "gearnode" password: ${{ secrets.DOCKER_HUB_TOKEN }} - uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8 with: name: "binary-linux-${{ matrix.arch }}" path: "linux/${{ matrix.arch }}" - name: "Prepare binaries" run: "chmod +x linux/${{ matrix.arch }}/*" - name: "Build Docker image" run: | docker buildx build \ --platform "${{ matrix.platform }}" \ --tag "ghcr.io/getprobo/probo:snapshot-${{ matrix.arch }}" \ --load \ . - name: "Cache Trivy database" uses: "runs-on/cache@a5f51d6f3fece787d03b7b4e981c82538a0654ed" # v4 with: path: ~/.cache/trivy key: "trivy-db-${{ matrix.arch }}-${{ github.run_id }}" restore-keys: "trivy-db-${{ matrix.arch }}-" - name: "Scan Docker image with Trivy" uses: "aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25" # 0.36.0 with: image-ref: "ghcr.io/getprobo/probo:snapshot-${{ matrix.arch }}" format: "table" exit-code: 1 ignore-unfixed: true vuln-type: "os,library" severity: "CRITICAL,HIGH" cache-dir: ~/.cache/trivy # ── Snapshot: SBOM & vulnerability scan ──────────────────────────── snapshot-scan: name: "snapshot-scan" if: github.event_name == 'push' needs: [build-snapshot-binary] runs-on: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache" permissions: contents: "read" steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610" # v0.24.0 with: path: ./ format: cyclonedx-json output-file: sbom.json - uses: "anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2" # v7.4.0 with: sbom: "sbom.json" fail-build: true severity-cutoff: critical output-format: table # ── Build (PR + push validation) ─────────────────────────────────── build: name: "build" runs-on: "runs-on=${{ github.run_id }}/runner=8cpu-linux-x64/extras=s3-cache" permissions: contents: "read" steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "./.github/actions/setup" with: node: "false" - name: "Create placeholder dist files" run: | mkdir -p apps/console/dist apps/trust/dist packages/emails/dist echo dev-server > apps/console/dist/index.html echo dev-server > apps/trust/dist/index.html echo dev-server > packages/emails/dist/placeholder - name: "Generate Go code" run: | go generate ./pkg/server/api/connect/v1 go generate ./pkg/server/api/console/v1 go generate ./pkg/server/api/trust/v1 go generate ./pkg/server/api/mcp/v1 - name: "Build binaries" env: CGO_ENABLED: "0" run: | pids=() go build -ldflags "-s -w -X 'main.version=snapshot' -X 'main.env=prod'" \ -gcflags="-e" -o bin/probod ./cmd/probod/main.go & pids+=($!) go build -ldflags "-s -w" \ -gcflags="-e" -o bin/probod-bootstrap ./cmd/probod-bootstrap/main.go & pids+=($!) go build -ldflags "-s -w -X 'main.version=snapshot'" \ -gcflags="-e" -o bin/prb ./cmd/prb/main.go & pids+=($!) for pid in "${pids[@]}"; do wait "$pid"; done lint-go: name: "lint-go" runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: "read" pull-requests: "write" steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "./.github/actions/setup" with: node: "false" - uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: install-only: true - uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 - name: "Create placeholder dist files" run: | mkdir -p apps/console/dist apps/trust/dist packages/emails/dist echo dev-server > apps/console/dist/index.html echo dev-server > apps/trust/dist/index.html echo dev-server > packages/emails/dist/placeholder - name: "Generate Go code" run: | go generate ./pkg/server/api/connect/v1 go generate ./pkg/server/api/console/v1 go generate ./pkg/server/api/trust/v1 go generate ./pkg/server/api/mcp/v1 - name: "Run gofmt" run: | output="$(gofmt -l apps cmd packages pkg e2e)" if [ -n "$output" ]; then echo "error: 'gofmt' found unformatted files:" echo "$output" exit 1 fi - name: "Run go fix" run: | output="$(CGO_ENABLED=0 go fix -diff -omitzero=false ./apps/... ./cmd/... ./packages/... ./pkg/... ./e2e/...)" if [ -n "$output" ]; then echo "error: 'go fix' suggests changes; please apply them" echo "$output" exit 1 fi - name: "Run golangci-lint" run: golangci-lint run ./... - name: "Annotate PR with golangci-lint findings" if: failure() && github.event_name == 'pull_request' env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | golangci-lint run --out-format=line-number ./... 2>&1 | \ reviewdog -f=golangci-lint -reporter=github-pr-review -filter-mode=nofilter -name="golangci-lint" || true lint-js: name: "lint-js" runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: "read" pull-requests: "write" steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "./.github/actions/setup" with: go: "false" - uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 - name: "Generate Relay artifacts" run: make relay - name: "Run eslint" run: npm run lint - name: "Annotate PR with eslint findings" if: failure() && github.event_name == 'pull_request' env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | for dir in apps/console apps/trust packages/ui packages/eslint-config; do (cd "$dir" && npx eslint . --concurrency 4 --format stylish 2>/dev/null) | \ reviewdog -f=eslint -reporter=github-pr-review -filter-mode=nofilter -name="eslint ($dir)" || true done test: name: "test" runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: "read" steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "./.github/actions/setup" - name: "Create placeholder dist files" run: | mkdir -p apps/console/dist apps/trust/dist echo dev-server > apps/console/dist/index.html echo dev-server > apps/trust/dist/index.html - run: "npm --workspace @probo/emails run build" - run: "make test" env: GOTESTSUM_JUNITFILE: "junit.xml" - name: "Upload test results" uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7 if: "always()" continue-on-error: true with: name: "junit-results" path: "junit.xml" retention-days: 30 - run: "go tool cover -html=coverage.out -o coverage.html" - uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7 continue-on-error: true with: name: "coverage-reports" path: | coverage.out coverage.html retention-days: 30 test-e2e: name: "test-e2e" runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: "read" env: HAS_DOCKER_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN != '' }} steps: - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "./.github/actions/setup" - uses: "docker/setup-compose-action@8cccb8c14b6500aaffebff1aa49c502c34d2e5e6" # v2.1.0 - uses: "docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121" # v4.1.0 if: env.HAS_DOCKER_TOKEN == 'true' with: username: "gearnode" password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: "Install mkcert" run: | go install filippo.io/mkcert@latest sudo mkcert -install 2>&1 | grep -v 'no Firefox and/or Chrome/Chromium security databases found' || true - name: "Cache Docker images" id: docker-cache uses: "runs-on/cache@a5f51d6f3fece787d03b7b4e981c82538a0654ed" # v4 with: path: /tmp/docker-images key: "docker-images-${{ hashFiles('compose.yaml') }}" - name: "Load cached Docker images" if: steps.docker-cache.outputs.cache-hit == 'true' run: "docker load -i /tmp/docker-images/images.tar" - name: "Pull and save Docker images" if: steps.docker-cache.outputs.cache-hit != 'true' run: | docker compose pull mkdir -p /tmp/docker-images docker save $(docker compose config --images) -o /tmp/docker-images/images.tar - name: "Build and start stack in parallel" run: | make stack-up & STACK_PID=$! make bin/probod wait $STACK_PID - run: "make stack-ps" - name: "Run e2e tests" env: PROBO_E2E_BINARY: "${{ github.workspace }}/bin/probod" GOTESTSUM_FORMAT: "testname" GOTESTSUM_JUNITFILE: "junit-e2e.xml" run: | ACME_ROOT_CA="$(cat compose/pebble/certs/rootCA.pem)" \ CGO_ENABLED=1 go tool gotestsum -- -race -cover -coverprofile=coverage.out -count=1 ./e2e/console/... - name: "Upload test results" uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7 if: "always()" continue-on-error: true with: name: "junit-e2e-results" path: "junit-e2e.xml" retention-days: 30