diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ec4878..4898c30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,6 +61,10 @@ jobs: uses: golangci/golangci-lint-action@v7 with: version: v2.1.6 + # Build golangci-lint from source with the repo's Go toolchain: the + # prebuilt binaries are compiled with an older Go than the project's + # go directive (1.26.4), which golangci-lint refuses to run against. + install-mode: goinstall working-directory: backend args: --timeout=5m @@ -132,10 +136,15 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + # Run the gitleaks binary directly rather than gitleaks/gitleaks-action, + # which now requires a paid license for organization-owned repos. + - name: Install gitleaks + run: | + VERSION=8.21.2 + curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_x64.tar.gz" \ + | tar -xz -C /usr/local/bin gitleaks - name: gitleaks - uses: gitleaks/gitleaks-action@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gitleaks detect --source . --config .gitleaks.toml --redact --no-banner --exit-code 1 validate-dashboards: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 18c8be5..22efa1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,12 @@ name: Release +# Image publishing runs on version tags (and can be triggered manually), not on +# every push to main — building/pushing/scanning container images per commit is +# unnecessary and made ordinary commits show a red pipeline. on: push: - branches: [main] tags: ['v*'] + workflow_dispatch: permissions: contents: read @@ -30,6 +33,9 @@ jobs: - name: Extract metadata id: meta run: | + # ghcr requires the repository path to be lowercase; the owner may + # be mixed-case (e.g. "Corevice"). + echo "image_base=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" if [[ "${{ github.ref_type }}" == "tag" ]]; then echo "image_tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" echo "is_tag=true" >> "$GITHUB_OUTPUT" @@ -45,15 +51,15 @@ jobs: file: ./Dockerfile push: true tags: | - ghcr.io/${{ github.repository }}/web:sha-${{ github.sha }} - ${{ steps.meta.outputs.is_tag == 'true' && format('ghcr.io/{0}/web:{1}', github.repository, github.ref_name) || '' }} + ${{ steps.meta.outputs.image_base }}/web:sha-${{ github.sha }} + ${{ steps.meta.outputs.is_tag == 'true' && format('{0}/web:{1}', steps.meta.outputs.image_base, github.ref_name) || '' }} cache-from: type=gha cache-to: type=gha,mode=max - name: Scan frontend image for vulnerabilities uses: aquasecurity/trivy-action@master with: - image-ref: ghcr.io/open-git/web:sha-${{ github.sha }} + image-ref: ${{ steps.meta.outputs.image_base }}/web:sha-${{ github.sha }} format: sarif output: trivy-frontend.sarif severity: HIGH,CRITICAL @@ -78,15 +84,15 @@ jobs: file: ./backend/Dockerfile push: true tags: | - ghcr.io/${{ github.repository }}/api:sha-${{ github.sha }} - ${{ steps.meta.outputs.is_tag == 'true' && format('ghcr.io/{0}/api:{1}', github.repository, github.ref_name) || '' }} + ${{ steps.meta.outputs.image_base }}/api:sha-${{ github.sha }} + ${{ steps.meta.outputs.is_tag == 'true' && format('{0}/api:{1}', steps.meta.outputs.image_base, github.ref_name) || '' }} cache-from: type=gha cache-to: type=gha,mode=max - name: Scan backend image for vulnerabilities uses: aquasecurity/trivy-action@master with: - image-ref: ghcr.io/open-git/api:sha-${{ github.sha }} + image-ref: ${{ steps.meta.outputs.image_base }}/api:sha-${{ github.sha }} format: sarif output: trivy-backend.sarif severity: HIGH,CRITICAL diff --git a/.gitleaks.toml b/.gitleaks.toml index f4db285..8540fcf 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -6,11 +6,14 @@ useDefault = true [allowlist] description = "Allow fake/sample secrets used only in test fixtures" # base64("test-publicKey") — a placeholder MCP verification key in unit tests, -# not a real credential. +# and the fixed fake JWT signing key used by the router integration test. +# Neither is a real credential. regexes = [ '''dGVzdC1wdWJsaWNLZXk=''', + '''integration-test-secret-key-1234567890''', ] -# Scope: the MCP secret handler test that defines the placeholder above. +# Scope: test files that define the placeholders above. paths = [ '''backend/internal/handler/secret_handler_test\.go''', + '''backend/cmd/server/integration_test\.go''', ] diff --git a/docs/components/DocHeader.tsx b/docs/components/DocHeader.tsx index b41acb4..507fbd0 100644 --- a/docs/components/DocHeader.tsx +++ b/docs/components/DocHeader.tsx @@ -15,14 +15,12 @@ export function DocHeader() { open-git diff --git a/docs/postcss.config.mjs b/docs/postcss.config.mjs new file mode 100644 index 0000000..4e83494 --- /dev/null +++ b/docs/postcss.config.mjs @@ -0,0 +1,10 @@ +// The docs site (Nextra) does not use the root project's Tailwind pipeline. +// Without this local config, Next.js walks up the directory tree and loads the +// repository-root postcss.config.mjs, which requires @tailwindcss/postcss — a +// dependency that is not installed in docs/. An empty plugin set keeps the docs +// build self-contained. +const config = { + plugins: {}, +}; + +export default config;