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;