diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml new file mode 100644 index 000000000..ef85b22a9 --- /dev/null +++ b/.github/actions/setup/action.yaml @@ -0,0 +1,30 @@ +name: "Setup" +description: "Set up Go and/or Node.js toolchains" +inputs: + go: + description: "Set up Go toolchain and download modules" + required: false + default: "true" + node: + description: "Set up Node.js and install dependencies" + required: false + default: "true" +runs: + using: "composite" + steps: + - if: inputs.go == 'true' + uses: "actions/setup-go@v6" + with: + go-version: "1.26.1" + cache: true + - if: inputs.go == 'true' + shell: bash + run: go mod download + - if: inputs.node == 'true' + uses: "actions/setup-node@v6" + with: + node-version-file: ".nvmrc" + cache: "npm" + - if: inputs.node == 'true' + shell: bash + run: npm ci diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d468fdaa7..99fece8e6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -8,6 +8,9 @@ on: schedule: - cron: "35 23 * * 4" +permissions: + contents: "read" + jobs: analyze: name: Analyze (${{ matrix.language }}) @@ -16,17 +19,10 @@ jobs: # - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/using-larger-runners (GitHub.com only) # Consider using larger runners or machines with greater resources for possible analysis time improvements. - runs-on: "ubuntu-latest" + runs-on: "runs-on=${{ github.run_id }}/runner=8cpu-linux-x64/extras=s3-cache" permissions: - # required for all workflows - security-events: write - - # required to fetch internal or private CodeQL packs - packages: read - - # only required for workflows in private repositories - actions: read - contents: read + security-events: "write" + contents: "read" strategy: fail-fast: false @@ -35,7 +31,7 @@ jobs: - language: actions build-mode: none - language: go - build-mode: autobuild + build-mode: manual - language: javascript-typescript build-mode: none # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' @@ -48,46 +44,42 @@ jobs: # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: submodules: recursive + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - # Add any setup steps before running the `github/codeql-action/init` action. - # This includes steps like installing compilers or runtimes (`actions/setup-node` - # or others). This is typically only required for manual builds. - # - name: Setup runtime (example) - # uses: actions/setup-example@v1 + - if: matrix.language == 'go' + uses: "./.github/actions/setup" + with: + node: "false" + - if: matrix.language == 'go' + name: "Prepare Go sources" + shell: bash + 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 + pids=() + go generate ./pkg/server/api/connect/v1 & pids+=($!) + go generate ./pkg/server/api/console/v1 & pids+=($!) + go generate ./pkg/server/api/trust/v1 & pids+=($!) + go generate ./pkg/server/api/mcp/v1 & pids+=($!) + for pid in "${pids[@]}"; do wait "$pid"; done - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # If the analyze step fails for one of the languages you are analyzing with - # "We were unable to automatically build your code", modify the matrix above - # to set the build mode to "manual" for that language. Then modify this step - # to build your code. - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - if: matrix.build-mode == 'manual' + - if: matrix.language == 'go' + name: "Build Go" shell: bash - run: | - echo 'If you are using a "manual" build mode for one or more of the' \ - 'languages you are analyzing, replace this with the commands to build' \ - 'your code, for example:' - echo ' make bootstrap' - echo ' make release' - exit 1 + run: go build ./... - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/make.yaml b/.github/workflows/make.yaml index fe411ec2f..6ed5e2f48 100644 --- a/.github/workflows/make.yaml +++ b/.github/workflows/make.yaml @@ -7,54 +7,152 @@ on: branches: - "main" +permissions: + contents: "read" + jobs: - release-snapshot: - name: "release-snapshot" - runs-on: "ubuntu-24.04-64cores" + # ── 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" - packages: "write" - id-token: "write" - security-events: "write" steps: - - uses: "actions/checkout@v6" + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: - fetch-depth: 0 submodules: recursive - - uses: "actions/setup-go@v6" + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "./.github/actions/setup" with: - go-version: "1.26.1" - cache: true - - run: "go mod download" - - uses: "actions/setup-node@v6" + go: "false" + - run: "npm --workspace @probo/emails run build" + - name: "Build console" + run: | + npm --workspace @probo/console run relay + npm --workspace @probo/console run check + NODE_ENV=production npm --workspace @probo/console run build + - name: "Build trust" + run: | + npm --workspace @probo/trust run relay + npm --workspace @probo/trust run check + NODE_ENV=production npm --workspace @probo/trust run build + - uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 with: - node-version-file: ".nvmrc" - cache: "npm" - - run: "npm i -g npm@11.8.0" - - run: "npm ci" - - uses: "docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a" # v4.0.0 + 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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # 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: "sigstore/cosign-installer@053f9b74638557590800a301da1ba82351507e2c" # v3.8.1 - - name: Cache Trivy database - uses: "actions/cache@v5" + - 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-${{ runner.os }}-${{ github.run_id }} - restore-keys: | - trivy-db-${{ runner.os }}- - - uses: "anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610" # v0.24.0 - - uses: "goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29" # v7.0.0 - with: - distribution: "goreleaser" - version: "~> v2" - args: "release --clean --snapshot" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Scan Docker image with Trivy - if: github.ref == 'refs/heads/main' + key: "trivy-db-${{ matrix.arch }}-${{ github.run_id }}" + restore-keys: "trivy-db-${{ matrix.arch }}-" + - name: "Scan Docker image with Trivy" uses: "aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1" # 0.35.0 with: - image-ref: "ghcr.io/getprobo/probo:latest-amd64" + image-ref: "ghcr.io/getprobo/probo:snapshot-${{ matrix.arch }}" format: "sarif" output: "trivy-results.sarif" exit-code: 0 @@ -62,100 +160,124 @@ jobs: vuln-type: "os,library" severity: "CRITICAL,HIGH" cache-dir: ~/.cache/trivy - - name: Scan Docker image with Trivy - if: github.ref != 'refs/heads/main' - uses: "aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1" # 0.35.0 - with: - image-ref: "ghcr.io/getprobo/probo:latest-amd64" - format: "table" - exit-code: 1 - ignore-unfixed: true - vuln-type: "os,library" - severity: "CRITICAL,HIGH" - cache-dir: ~/.cache/trivy - - name: Upload Trivy scan results to GitHub Security tab - if: github.ref == 'refs/heads/main' - uses: github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4.32.1 + - name: "Upload Trivy scan results" + uses: "github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314" # v4.32.1 with: sarif_file: "trivy-results.sarif" - - uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 #v0.24.0 + category: "trivy-${{ matrix.arch }}" + + # ── 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 + - 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: "ubuntu-22.04" + runs-on: "runs-on=${{ github.run_id }}/runner=8cpu-linux-x64/extras=s3-cache" permissions: contents: "read" steps: - - uses: "actions/checkout@v6" + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - - uses: "actions/setup-go@v6" + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "./.github/actions/setup" with: - go-version: "1.26.1" - cache: true - - run: "go mod download" - - uses: "actions/setup-node@v6" - with: - node-version-file: ".nvmrc" - cache: "npm" - - run: "npm i -g npm@11.8.0" - - run: "npm ci" - - run: "make build" - - uses: "actions/upload-artifact@v7" - with: - name: "build-artifacts" - path: | - bin/probod - apps/console/dist/ - apps/trust/dist/ - packages/emails/dist/ - retention-days: 1 + 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: | + pids=() + go generate ./pkg/server/api/connect/v1 & pids+=($!) + go generate ./pkg/server/api/console/v1 & pids+=($!) + go generate ./pkg/server/api/trust/v1 & pids+=($!) + go generate ./pkg/server/api/mcp/v1 & pids+=($!) + for pid in "${pids[@]}"; do wait "$pid"; done + - 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: - name: "lint" - needs: [build] - runs-on: "ubuntu-22.04" + 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" - checks: "write" steps: - - uses: "actions/checkout@v6" + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - - uses: "actions/setup-go@v6" + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "./.github/actions/setup" with: - go-version: "1.26.1" - cache: true - - run: "go mod download" - - uses: "actions/setup-node@v6" - with: - node-version-file: ".nvmrc" - cache: "npm" + node: "false" - uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: install-only: true - uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 - - run: "npm i -g npm@11.8.0" - - run: "npm ci" - - uses: "actions/download-artifact@v8" - with: - name: "build-artifacts" - - run: "chmod +x bin/probod" - - run: "make generate" - - run: "make go-fmt go-fix" - - name: "Run go vet" - run: "go vet ./..." + - 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" env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -166,6 +288,26 @@ jobs: else golangci-lint run ./... fi + + 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: | + npm --workspace @probo/console run relay + npm --workspace @probo/trust run relay - name: "Run eslint" env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -182,82 +324,82 @@ jobs: test: name: "test" - needs: [build] - runs-on: "ubuntu-22.04" + runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: "read" steps: - - uses: "actions/checkout@v6" + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - - uses: "actions/setup-go@v6" - with: - go-version: "1.26.1" - cache: true - - run: "go mod download" - - uses: "actions/download-artifact@v8" - with: - name: "build-artifacts" - - run: "chmod +x bin/probod" - - run: "make generate" + - 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@v7" + uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 if: "always()" + continue-on-error: true with: name: "junit-results" path: "junit.xml" retention-days: 30 - - run: "make coverage-report" - - uses: "actions/upload-artifact@v7" + - run: "go tool cover -html=coverage.out -o coverage.html" + - uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 + continue-on-error: true with: name: "coverage-reports" path: | coverage.out coverage.html retention-days: 30 - # Trivy ignore does not work for license scanning in Github action - # - uses: "aquasecurity/trivy-action@0.33.1" - # with: - # scan-type: "fs" - # scanners: "license" - # severity: "UNKNOWN,HIGH,CRITICAL" - # exit-code: 1 - # trivyignores: ".trivyignore.yaml" - # trivy-config: "trivy.yaml" test-e2e: name: "test-e2e" - runs-on: "ubuntu-22.04" + runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: "read" steps: - - uses: "actions/checkout@v6" + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: submodules: recursive - - uses: "actions/setup-go@v6" - with: - go-version: "1.26.1" - cache: true - - run: "go mod download" - - uses: "actions/setup-node@v6" - with: - node-version-file: ".nvmrc" - cache: "npm" - - run: "npm i -g npm@11.8.0" - - run: "sudo apt-get install -y mkcert" - - run: "sudo mkcert -install 2>&1 | grep -v 'no Firefox and/or Chrome/Chromium security databases found' || true" - - uses: "docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a" # v4.0.0 - - uses: "docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd" # v4.0.0 + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "./.github/actions/setup" - uses: "docker/setup-compose-action@8cccb8c14b6500aaffebff1aa49c502c34d2e5e6" # v2.1.0 - - run: "npm ci" - - run: "make stack-up" + - 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=$! + SKIP_APPS=1 make bin/probod + wait $STACK_PID - run: "make stack-ps" - name: "Inject root CA into e2e config" run: | - # Use Python to properly inject the root CA PEM content into YAML python3 << 'EOF' import yaml @@ -272,12 +414,17 @@ jobs: with open('e2e/console/testdata/config.yaml', 'w') as f: yaml.dump(config, f, default_flow_style=False, allow_unicode=True) EOF - - run: "SKIP_APPS=1 make test-e2e" + - name: "Run e2e tests" env: + PROBO_E2E_BINARY: "${{ github.workspace }}/bin/probod" + PROBO_E2E_CONFIG: "${{ github.workspace }}/e2e/console/testdata/config.yaml" + GOTESTSUM_FORMAT: "testname" GOTESTSUM_JUNITFILE: "junit-e2e.xml" + run: "CGO_ENABLED=1 go tool gotestsum -- -race -cover -coverprofile=coverage.out -count=1 ./e2e/console/..." - name: "Upload test results" - uses: "actions/upload-artifact@v7" + uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 if: "always()" + continue-on-error: true with: name: "junit-e2e-results" path: "junit-e2e.xml" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 49be8a860..7a40d0994 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,4 +1,4 @@ -name: Release +name: "Release" on: push: @@ -6,80 +6,313 @@ on: - "v*" permissions: - contents: write - packages: write - id-token: write - attestations: write - security-events: write + contents: "read" jobs: - github-release: - runs-on: "ubuntu-24.04-64cores" - outputs: - hashes: ${{ steps.hash.outputs.hashes }} - image-digest: ${{ steps.image.outputs.digest }} + # ── Build frontend apps ────────────────────────────────────────────── + build-apps: + name: "build-apps" + runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" + permissions: + contents: "read" steps: - - name: Checkout - uses: actions/checkout@v6 + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: - fetch-depth: 0 submodules: recursive - - - name: Set up Go - uses: actions/setup-go@v6 + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "./.github/actions/setup" with: - go-version: "1.26.1" - cache: true - - - run: "go mod download" - - - name: Set up Node.js - uses: actions/setup-node@v6 + go: "false" + - run: "npm --workspace @probo/emails run build" + - name: "Build console" + run: | + npm --workspace @probo/console run relay + npm --workspace @probo/console run check + NODE_ENV=production npm --workspace @probo/console run build + - name: "Build trust" + run: | + npm --workspace @probo/trust run relay + npm --workspace @probo/trust run check + NODE_ENV=production npm --workspace @probo/trust run build + - uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 with: - node-version-file: ".nvmrc" - cache: "npm" + name: "frontend-apps" + path: | + apps/console/dist/ + apps/trust/dist/ + packages/emails/dist/ + retention-days: 1 - - name: Update npm - run: npm i -g npm@11.8.0 - - - name: Install dependencies - run: npm ci - - - name: Install Cosign - uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 + # ── Build Go binaries (matrix by GOOS/GOARCH) ─────────────────────── + build-binary: + name: "binary (${{ matrix.goos }}/${{ matrix.goarch }})" + 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: - cosign-release: "v3.0.4" - - - name: Initialize Cosign TUF root - run: cosign initialize - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - - name: Set up QEMU - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + submodules: recursive + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "./.github/actions/setup" with: - platforms: all + 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: | + VERSION="${GITHUB_REF_NAME#v}" + EXT="" + if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi - - name: Log in to GitHub Container Registry - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 + go build -ldflags "-s -w -X 'main.version=${VERSION}' -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=${VERSION}'" \ + -gcflags="-e" -o "dist/prb${EXT}" ./cmd/prb/main.go + - name: "Create archives" + env: + GOOS: "${{ matrix.goos }}" + GOARCH: "${{ matrix.goarch }}" + run: | + case "$GOOS" in + linux) OS="Linux" ;; + darwin) OS="Darwin" ;; + windows) OS="Windows" ;; + freebsd) OS="Freebsd" ;; + openbsd) OS="Openbsd" ;; + esac + case "$GOARCH" in + amd64) ARCH="x86_64" ;; + *) ARCH="$GOARCH" ;; + esac + EXT="" + if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi + + mkdir -p archives + + PROBOD_DIR="probod_${OS}_${ARCH}" + mkdir -p "staging/${PROBOD_DIR}" + cp "dist/probod${EXT}" "dist/probod-bootstrap${EXT}" "dist/prb${EXT}" \ + README.md LICENSE CHANGELOG.md "staging/${PROBOD_DIR}/" + if [ "$GOOS" = "windows" ]; then + (cd staging && zip -r "../archives/${PROBOD_DIR}.zip" "${PROBOD_DIR}") + else + tar -czf "archives/${PROBOD_DIR}.tar.gz" -C staging "${PROBOD_DIR}" + fi + + PRB_DIR="prb_${OS}_${ARCH}" + mkdir -p "staging/${PRB_DIR}" + cp "dist/prb${EXT}" README.md LICENSE CHANGELOG.md "staging/${PRB_DIR}/" + if [ "$GOOS" = "windows" ]; then + (cd staging && zip -r "../archives/${PRB_DIR}.zip" "${PRB_DIR}") + else + tar -czf "archives/${PRB_DIR}.tar.gz" -C staging "${PRB_DIR}" + fi + - uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 + with: + name: "binary-${{ matrix.goos }}-${{ matrix.goarch }}" + path: "dist/" + retention-days: 1 + - uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 + with: + name: "archive-${{ matrix.goos }}-${{ matrix.goarch }}" + path: "archives/" + retention-days: 1 + + # ── Build Docker images (matrix by architecture) ───────────────────── + build-docker: + name: "docker (${{ matrix.arch }})" + needs: [build-binary] + runs-on: "runs-on=${{ github.run_id }}/runner=${{ matrix.runner }}/extras=s3-cache" + permissions: + contents: "read" + packages: "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@b45d80f862d83dbcd57f89517bcf500b2ab88fb2" # v4.0.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 + - uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8 with: - distribution: goreleaser - version: "~> v2" - args: release --clean - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} + name: "binary-linux-${{ matrix.arch }}" + path: "linux/${{ matrix.arch }}" + - name: "Prepare binaries" + run: "chmod +x linux/${{ matrix.arch }}/*" + - name: "Build and push Docker image" + run: | + docker buildx build \ + --platform "${{ matrix.platform }}" \ + --label "org.opencontainers.image.title=probod" \ + --label "org.opencontainers.image.description=Probo compliance management platform" \ + --label "org.opencontainers.image.url=https://github.com/getprobo/probo" \ + --label "org.opencontainers.image.source=https://github.com/getprobo/probo" \ + --label "org.opencontainers.image.version=${{ github.ref_name }}" \ + --label "org.opencontainers.image.revision=${{ github.sha }}" \ + --label "org.opencontainers.image.licenses=MIT" \ + --tag "ghcr.io/getprobo/probo:${{ github.ref_name }}-${{ matrix.arch }}" \ + --push \ + . - - name: Scan Docker image with Trivy - uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0 + # ── Create multi-arch Docker manifest and sign ────────────────────── + docker-manifest: + name: "docker-manifest" + needs: [build-docker] + runs-on: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache" + permissions: + packages: "write" + id-token: "write" + outputs: + digest: ${{ steps.digest.outputs.digest }} + steps: + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd" # v4.0.0 + - uses: "docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2" # v4.0.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: "sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad" # v4.0.0 + - name: "Create and push manifest" + run: | + docker buildx imagetools create \ + --tag "ghcr.io/getprobo/probo:${{ github.ref_name }}" \ + --tag "ghcr.io/getprobo/probo:latest" \ + "ghcr.io/getprobo/probo:${{ github.ref_name }}-amd64" \ + "ghcr.io/getprobo/probo:${{ github.ref_name }}-arm64" + - name: "Get manifest digest" + id: digest + run: | + DIGEST=$(docker buildx imagetools inspect "ghcr.io/getprobo/probo:${{ github.ref_name }}" --format '{{json .Manifest.Digest}}' | jq -r) + echo "digest=$DIGEST" >> "$GITHUB_OUTPUT" + - name: "Sign Docker manifest" + run: cosign sign "ghcr.io/getprobo/probo@${{ steps.digest.outputs.digest }}" --yes + + # ── Create GitHub release ─────────────────────────────────────────── + github-release: + name: "github-release" + needs: [build-binary] + runs-on: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache" + permissions: + contents: "write" + id-token: "write" + steps: + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 + with: + fetch-depth: 0 + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad" # v4.0.0 + - uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8 + with: + pattern: "archive-*" + path: "archives" + merge-multiple: true + - name: "Generate checksums and sign" + run: | + cd archives + sha256sum *.tar.gz *.zip > checksums.txt + cosign sign-blob --bundle="checksums.txt.bundle" checksums.txt --yes + - name: "Generate release notes" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api "repos/${{ github.repository }}/releases/generate-notes" \ + -f tag_name="${{ github.ref_name }}" \ + --jq '.body' > release-notes.md + + cat >> release-notes.md << EOF + + ## Docker Images + - \`ghcr.io/getprobo/probo:${{ github.ref_name }}\` (multi-arch: linux/amd64, linux/arm64) + - \`ghcr.io/getprobo/probo:latest\` (multi-arch: linux/amd64, linux/arm64) + EOF + - name: "Create GitHub release" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PRERELEASE_FLAG="" + if echo "${{ github.ref_name }}" | grep -qE '(alpha|beta|rc)'; then + PRERELEASE_FLAG="--prerelease" + fi + + gh release delete "${{ github.ref_name }}" --yes 2>/dev/null || true + + gh release create "${{ github.ref_name }}" \ + --title "${{ github.ref_name }}" \ + --notes-file release-notes.md \ + $PRERELEASE_FLAG \ + archives/* + + # ── Security scan, SBOM, and attestations ─────────────────────────── + security-scan: + name: "security-scan" + needs: [docker-manifest, github-release] + runs-on: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache" + permissions: + contents: "read" + packages: "write" + id-token: "write" + attestations: "write" + security-events: "write" + steps: + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 + with: + submodules: recursive + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2" # v4.0.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8 + with: + pattern: "archive-*" + path: "archives" + merge-multiple: true + - name: "Scan Docker image with Trivy" + uses: "aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1" # 0.35.0 with: image-ref: "ghcr.io/getprobo/probo:${{ github.ref_name }}" format: "sarif" @@ -88,114 +321,168 @@ jobs: ignore-unfixed: true vuln-type: "os,library" severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4.32.1 + - name: "Upload Trivy scan results" + uses: "github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314" # v4.32.1 if: always() with: sarif_file: "trivy-results.sarif" - - - name: Generate SBOM - uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 #v0.24.0 + - name: "Generate SBOM" + uses: "anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610" # v0.24.0 with: path: ./ format: cyclonedx-json output-file: sbom.json - - - name: Run vulnerability scan - uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 #v7.4.0 + - name: "Run vulnerability scan" + uses: "anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2" # v7.4.0 with: sbom: "sbom.json" fail-build: true severity-cutoff: critical - - - name: Generate subject for attestation - id: hash - run: | - set -euo pipefail - ( - cd dist - find . -type f -name '*.tar.gz' -o -name '*.zip' | while read file; do - echo "$(sha256sum "$file" | head -c 64) $file" - done - ) > checksums.txt - echo "hashes=$(cat checksums.txt | base64 -w0)" >> "$GITHUB_OUTPUT" - - - name: Attest SBOM - uses: actions/attest-sbom@v4 + - name: "Attest SBOM for archives" + uses: "actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e" # v4 with: - subject-path: "dist/*.tar.gz, dist/*.zip" + subject-path: "archives/*.tar.gz, archives/*.zip" sbom-path: "sbom.json" - - - name: Attest build provenance - uses: actions/attest-build-provenance@v4 + - name: "Attest build provenance for archives" + uses: "actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32" # v4 with: - subject-path: "dist/*.tar.gz, dist/*.zip" - - - name: Get image digest - id: image - run: | - echo "Getting image digest..." - DIGEST=$(docker buildx imagetools inspect ghcr.io/getprobo/probo:${{ github.ref_name }} --format '{{json .Manifest.Digest}}' | jq -r) - echo "Successfully retrieved digest: $DIGEST" - echo "digest=$DIGEST" >> "$GITHUB_OUTPUT" - - - name: Attest Docker image SBOM - uses: actions/attest-sbom@v4 + subject-path: "archives/*.tar.gz, archives/*.zip" + - name: "Attest Docker image SBOM" + uses: "actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e" # v4 with: subject-name: "ghcr.io/getprobo/probo" - subject-digest: ${{ steps.image.outputs.digest }} + subject-digest: ${{ needs.docker-manifest.outputs.digest }} sbom-path: "sbom.json" - - - name: Upload SBOM as artifact - uses: actions/upload-artifact@v7 + - uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 with: - name: sbom - path: | - sbom.json - checksums.txt + name: "sbom" + path: "sbom.json" retention-days: 30 + # ── Publish Homebrew formula ──────────────────────────────────────── + homebrew: + name: "homebrew" + needs: [github-release] + runs-on: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache" + permissions: + contents: "read" + steps: + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - name: "Download checksums from release" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release download "${{ github.ref_name }}" \ + --repo "${{ github.repository }}" \ + --pattern "checksums.txt" + - name: "Generate and publish Homebrew formula" + env: + GH_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} + run: | + VERSION="${GITHUB_REF_NAME#v}" + TAG="${GITHUB_REF_NAME}" + BASE_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}" + + DARWIN_ARM64_SHA=$(grep "prb_Darwin_arm64.tar.gz" checksums.txt | awk '{print $1}') + DARWIN_X86_64_SHA=$(grep "prb_Darwin_x86_64.tar.gz" checksums.txt | awk '{print $1}') + LINUX_ARM64_SHA=$(grep "prb_Linux_arm64.tar.gz" checksums.txt | awk '{print $1}') + LINUX_X86_64_SHA=$(grep "prb_Linux_x86_64.tar.gz" checksums.txt | awk '{print $1}') + + cat > prb.rb << RUBY + # typed: false + # frozen_string_literal: true + + class Prb < Formula + desc "Probo CLI" + homepage "https://github.com/getprobo/probo" + version "${VERSION}" + license "MIT" + + on_macos do + on_arm do + url "${BASE_URL}/prb_Darwin_arm64.tar.gz" + sha256 "${DARWIN_ARM64_SHA}" + end + on_intel do + url "${BASE_URL}/prb_Darwin_x86_64.tar.gz" + sha256 "${DARWIN_X86_64_SHA}" + end + end + + on_linux do + on_arm do + url "${BASE_URL}/prb_Linux_arm64.tar.gz" + sha256 "${LINUX_ARM64_SHA}" + end + on_intel do + url "${BASE_URL}/prb_Linux_x86_64.tar.gz" + sha256 "${LINUX_X86_64_SHA}" + end + end + + def install + bin.install "prb" + end + + test do + assert_match version.to_s, shell_output("#{bin}/prb --version") + end + end + RUBY + + EXISTING_SHA=$(gh api "repos/getprobo/homebrew-tap/contents/Formula/prb.rb" --jq '.sha' 2>/dev/null || echo "") + + if [ -n "$EXISTING_SHA" ]; then + gh api "repos/getprobo/homebrew-tap/contents/Formula/prb.rb" \ + --method PUT \ + -f message="prb ${VERSION}" \ + -f content="$(base64 -w0 prb.rb)" \ + -f sha="${EXISTING_SHA}" + else + gh api "repos/getprobo/homebrew-tap/contents/Formula/prb.rb" \ + --method PUT \ + -f message="prb ${VERSION}" \ + -f content="$(base64 -w0 prb.rb)" + fi + + # ── npm release (independent) ─────────────────────────────────────── npm-release: - runs-on: "ubuntu-24.04-64cores" + name: "npm-release" + runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache" permissions: contents: read id-token: write attestations: write steps: - - uses: actions/checkout@v6 + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: fetch-depth: 0 submodules: recursive - - - uses: actions/setup-node@v6 + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 + - uses: "actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f" # v6 with: node-version-file: ".nvmrc" cache: "npm" registry-url: "https://registry.npmjs.org" scope: "@probo" - - - run: npm install -g npm@latest - - run: npm ci - - run: npm --workspace @probo/n8n-nodes-probo run build - - name: Set package version from git tag + - run: "npm install -g npm@latest" + - run: "npm ci" + - run: "npm --workspace @probo/n8n-nodes-probo run build" + - name: "Set package version from git tag" run: | VERSION="${GITHUB_REF_NAME#v}" npm --workspace @probo/n8n-nodes-probo version "$VERSION" --no-git-tag-version - - - uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 #v0.24.0 + - uses: "anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610" # v0.24.0 with: path: ./packages/n8n-node format: cyclonedx-json output-file: packages/n8n-node/sbom.json - - - uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 #v7.4.0 + - uses: "anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2" # v7.4.0 with: path: ./packages/n8n-node fail-build: true severity-cutoff: critical - - - name: Generate checksums for dist files + - name: "Generate checksums for dist files" id: checksum run: | cd packages/n8n-node/dist @@ -203,22 +490,18 @@ jobs: echo "$(sha256sum "$file" | head -c 64) $file" done > ../checksums.txt echo "hashes=$(cat ../checksums.txt | base64 -w0)" >> "$GITHUB_OUTPUT" - - - run: npm --workspace @probo/n8n-nodes-probo publish --access public --dry-run - - run: npm --workspace @probo/n8n-nodes-probo publish --access public - - - uses: actions/attest-sbom@v4 + - run: "npm --workspace @probo/n8n-nodes-probo publish --access public --dry-run" + - run: "npm --workspace @probo/n8n-nodes-probo publish --access public" + - uses: "actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e" # v4 with: subject-path: "packages/n8n-node/dist/**" sbom-path: "packages/n8n-node/sbom.json" - - - uses: actions/attest-build-provenance@v4 + - uses: "actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32" # v4 with: subject-path: "packages/n8n-node/dist/**" - - - uses: actions/upload-artifact@v7 + - uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7 with: - name: npm-sbom + name: "npm-sbom" path: | packages/n8n-node/sbom.json packages/n8n-node/checksums.txt diff --git a/.github/workflows/secrets.yaml b/.github/workflows/secrets.yaml index e1635587e..c2fee23f2 100644 --- a/.github/workflows/secrets.yaml +++ b/.github/workflows/secrets.yaml @@ -11,12 +11,15 @@ permissions: jobs: scan: - runs-on: "ubuntu-latest" + runs-on: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache" + permissions: + contents: "read" steps: - - uses: "actions/checkout@v6" + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 with: fetch-depth: 0 submodules: recursive + - uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2 - uses: "trufflesecurity/trufflehog@e48f9039c82786db50685e8ef7d980b209f06186" # main with: extra_args: "--results=verified,unknown --exclude-paths=.trufflehog-exclude" diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index cbaabb2bb..000000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,231 +0,0 @@ -# yaml-language-server: $schema=https://goreleaser.com/static/schema.json -version: 2 - -project_name: probod - -before: - hooks: - - make generate @probo/emails @probo/console @probo/trust - -builds: - - id: probod - main: ./cmd/probod/main.go - binary: probod - ldflags: - - -s -w - - -X 'main.version={{.Version}}' - - -X 'main.env=prod' - gcflags: - - -e - env: - - CGO_ENABLED=0 - goos: - - windows - - darwin - - freebsd - - openbsd - goarch: - - amd64 - - arm64 - ignore: - - goos: windows - goarch: arm64 - - id: probod-bootstrap - main: ./cmd/probod-bootstrap/main.go - binary: probod-bootstrap - ldflags: - - -s -w - gcflags: - - -e - env: - - CGO_ENABLED=0 - goos: - - windows - - darwin - - freebsd - - openbsd - goarch: - - amd64 - - arm64 - ignore: - - goos: windows - goarch: arm64 - # Docker-specific builds for Linux only - - id: probod-docker - main: ./cmd/probod/main.go - binary: probod - ldflags: - - -s -w - - -X 'main.version={{.Version}}' - - -X 'main.env=prod' - gcflags: - - -e - env: - - CGO_ENABLED=0 - goos: - - linux - goarch: - - amd64 - - arm64 - - id: probod-bootstrap-docker - main: ./cmd/probod-bootstrap/main.go - binary: probod-bootstrap - ldflags: - - -s -w - gcflags: - - -e - env: - - CGO_ENABLED=0 - goos: - - linux - goarch: - - amd64 - - arm64 - - id: prb-docker - main: ./cmd/prb/main.go - binary: prb - ldflags: - - -s -w - - -X 'main.version={{.Version}}' - gcflags: - - -e - env: - - CGO_ENABLED=0 - goos: - - linux - goarch: - - amd64 - - arm64 - -archives: - - id: probod - builds: - - probod - - probod-bootstrap - - prb - name_template: >- - {{ .ProjectName }}_ - {{- title .Os }}_ - {{- if eq .Arch "amd64" }}x86_64 - {{- else if eq .Arch "386" }}i386 - {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }} - files: - - README.md - - LICENSE - - CHANGELOG.md - - id: prb - builds: - - prb - name_template: >- - prb_ - {{- title .Os }}_ - {{- if eq .Arch "amd64" }}x86_64 - {{- else if eq .Arch "386" }}i386 - {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }} - files: - - README.md - - LICENSE - - CHANGELOG.md - -checksum: - name_template: "checksums.txt" - -signs: - - cmd: sh - env: - - COSIGN_EXPERIMENTAL=1 - signature: "${artifact}.bundle" - args: - - -c - - | - if [ "{{ .IsSnapshot }}" = "true" ]; then - echo "Skipping signing (snapshot: {{ .IsSnapshot }})" - touch "${signature}" - else - cosign sign-blob --bundle="${signature}" "${artifact}" --yes - fi - artifacts: checksum - output: true - -dockers_v2: - - images: - - "ghcr.io/getprobo/probo" - tags: - - "{{ .Tag }}" - - latest - dockerfile: Dockerfile - ids: - - probod-docker - - probod-bootstrap-docker - extra_files: - - entrypoint.sh - labels: - "org.opencontainers.image.title": "{{.ProjectName}}" - "org.opencontainers.image.description": "Probo compliance management platform" - "org.opencontainers.image.url": "https://github.com/getprobo/probo" - "org.opencontainers.image.source": "https://github.com/getprobo/probo" - "org.opencontainers.image.version": "{{.Version}}" - "org.opencontainers.image.created": '{{time "2006-01-02T15:04:05Z07:00"}}' - "org.opencontainers.image.revision": "{{.FullCommit}}" - "org.opencontainers.image.licenses": "MIT" - platforms: - - linux/amd64 - - linux/arm64 - -docker_signs: - - id: images - cmd: sh - env: - - COSIGN_EXPERIMENTAL=1 - artifacts: images - output: true - args: - - -c - - | - if [ "{{ .IsSnapshot }}" = "true" ]; then - echo "Skipping Docker image signing (snapshot: {{ .IsSnapshot }})" - else - cosign sign "${artifact}@${digest}" --yes - fi - - id: manifests - cmd: sh - env: - - COSIGN_EXPERIMENTAL=1 - artifacts: manifests - output: true - args: - - -c - - | - if [ "{{ .IsSnapshot }}" = "true" ]; then - echo "Skipping Docker manifest signing (snapshot: {{ .IsSnapshot }})" - else - cosign sign "${artifact}@${digest}" --yes - fi - -changelog: - sort: asc - filters: - exclude: - - "^docs:" - - "^test:" - - "^chore:" - - "^style:" - - "^refactor:" - - "^ci:" - - "^build:" - - Merge pull request - - Merge branch - - go mod tidy - -release: - draft: false - prerelease: auto - mode: replace - header: | - ## Changes in {{ .Tag }} - footer: | - ## Docker Images - - `ghcr.io/getprobo/probo:{{ .Tag }}` (multi-arch: linux/amd64, linux/arm64) - - `ghcr.io/getprobo/probo:latest` (multi-arch: linux/amd64, linux/arm64) diff --git a/GNUmakefile b/GNUmakefile index 5f9f1fa08..947f87dbe 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,3 +1,5 @@ +MAKEFLAGS := --jobs=$(shell nproc) + CAT ?= cat CP ?= cp DOCKER ?= docker @@ -62,7 +64,13 @@ endif all: build .PHONY: lint -lint: vet go-fmt go-fix go-lint npm-lint +lint: lint-go lint-js + +.PHONY: lint-go +lint-go: vet go-fmt go-fix go-lint + +.PHONY: lint-js +lint-js: npm-lint .PHONY: vet vet: generate apps/console/dist/index.html apps/trust/dist/index.html @probo/emails @@ -301,14 +309,6 @@ stack-ps: ## List the docker stack containers psql: ## Open a psql shell to the postgres container $(DOCKER_COMPOSE) exec postgres psql -U probod -d probod -.PHONY: goreleaser-snapshot -goreleaser-snapshot: ## Build a snapshot release with goreleaser - goreleaser release --snapshot --clean --config .goreleaser.yaml - -.PHONY: goreleaser-check -goreleaser-check: ## Check goreleaser configuration - goreleaser check - compose/pebble/certs/rootCA.pem: @$(MKDIR) compose/pebble/certs $(MKCERT) -cert-file compose/pebble/certs/pebble.crt \