Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ gitlab-mcp-server/
│ ├── audit_image_vulns/ # The image's Alpine packages against Trivy's vulnerability database before the push (make check-image-vulns)
│ ├── reproduce_release/ # Release binaries rebuilt from the tag and compared byte for byte (make check-reproducible, make check-repeatable)
│ ├── audit_site_headers/ # The documentation site's security headers, judged after each deploy (make check-site-headers)
│ ├── audit_dco/ # Every commit of a pull request signed off by its author, the Developer Certificate of Origin (make check-dco)
│ ├── audit_surface_quality/ # MCP surface metadata + output quality (-view; was audit_tools + audit_output)
│ ├── audit_supply_chain/ # Release-configuration invariants (SHA-pinned uses:, Dependabot cooldowns, SECURITY.md, installers)
│ ├── audit_tokens/ # Token overhead; -footprint, --compare-schemas (was audit_meta_schema); cl100k_base tokenizer (tokens.go)
Expand Down
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ What a review checks and what a merge requires are in [Code Review](https://gith
- [ ] Contributor-facing change (a gate, a generator, a convention): `docs/development/` or `CLAUDE.md` updated
- [ ] If introducing/removing a tool: tool reference regenerated (`make gen-tool-reference`), the site's counts refreshed (`make gen-site-stats`) and the fine-grained table regenerated (`make gen-action-grants`); generated pages are never edited by hand (`make update-all` runs every generator that needs no measurement, GitLab instance or paid run)
- [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/)
- [ ] Every commit is signed off by its author (`git commit -s`), which certifies the [Developer Certificate of Origin](https://github.com/jmrplens/gitlab-mcp-server/blob/main/CONTRIBUTING.md#developer-certificate-of-origin) for it; `make check-dco` lists any that is not

### Security

Expand Down
5 changes: 3 additions & 2 deletions .github/skills/git-commit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ Analyze the diff to determine:

```bash
# Single line
git commit -m "<type>[scope]: <description>"
git commit -s -m "<type>[scope]: <description>"

# Multi-line with body/footer
git commit -m "$(cat <<'EOF'
git commit -s -m "$(cat <<'EOF'
<type>[scope]: <description>

<optional body>
Expand All @@ -114,6 +114,7 @@ EOF
- Imperative mood: "fix bug" not "fixes bug"
- Reference issues: `Closes #123`, `Refs #456`
- Keep description under 72 characters
- Sign off every commit (`-s`), which certifies the [Developer Certificate of Origin](../../../CONTRIBUTING.md#developer-certificate-of-origin) for it; CI's `dco` job fails a pull request with a commit that lacks the sign-off, and `make check-dco` lists any before a push. A sign-off is the author's, not AI attribution

## Git Safety Protocol

Expand Down
57 changes: 48 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
# docker (×2) ─── docker-build
# cross_platform (×3) ─── cross_platform_summary
# generated · golangci-lint · govulncheck · markdown · site-lint · site-build
# hadolint · supply-chain · docs-audit · em-dash ─── checks
# └──► ci
# hadolint · supply-chain · docs-audit · em-dash · dco ─── checks
# └──► ci
#
# Every edge joins neighbouring columns, and that is deliberate. GitHub draws
# a job with no parent and no child at the end of the first column, in one box
Expand Down Expand Up @@ -1171,10 +1171,10 @@ jobs:
fetch-depth: 0
- name: Check the lines this change adds
env:
# On a pull request, the commit the branch grew from, which for a
# stacked pull request is the stack's base rather than the layer
# below, since GitHub tests a stacked layer as the stack merged into
# that base. On a push to main, the commit that was there before.
# On a pull request, the base commit its event names, which for a
# stacked pull request is the head of the layer below, so each
# layer is checked for the lines it adds itself. On a push to main,
# the commit that was there before.
EM_DASH_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
run: make check-em-dash
# plan/ is ignored by git, so a file there is tracked only when it was
Expand All @@ -1183,6 +1183,44 @@ jobs:
- name: Check nothing under plan/ is tracked
run: make check-plan-untracked

# The Developer Certificate of Origin: every commit a pull request brings
# carries a Signed-off-by trailer naming its author, which is how the author
# certifies the certificate for it (CONTRIBUTING.md, Developer Certificate of
# Origin). The commits judged are the ones between the base and the head the
# event names, the range the em dash job reads. A stacked pull request's
# event names the head of the layer below as the base, so each layer's
# pull request judges that layer's commits and no others. A merge commit
# that brings no change of its own is not judged, and Dependabot's commits
# are exempt in a pull request Dependabot opened, which is why the job is
# told who opened it.
#
# A push to main has nothing for it to judge, so the job skips itself there
# and the verdict below counts the skip as a pass: main receives the squash
# commit, into which GitHub copies the sign-offs of the pull request's
# commits, and the release workflow's version stamp, which never sits in a
# pull request.
dco:
name: "✍️ DCO sign-off"
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Resolved
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
# The whole history, like the em dash job: the range runs from the
# pull request's base, which a shallow clone does not hold.
fetch-depth: 0
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: go.mod
- name: Check every commit is signed off by its author
env:
DCO_BASE: ${{ github.event.pull_request.base.sha }}
DCO_HEAD: ${{ github.event.pull_request.head.sha }}
DCO_OPENED_BY: ${{ github.event.pull_request.user.login }}
run: make check-dco DCO_FLAGS=-annotate

# One node for the jobs that depend on nothing. They are fanned in here
# rather than straight into "ci" for the graph's sake, explained at the top
# of the file: this node lands below the matrix summaries, because its
Expand Down Expand Up @@ -1218,6 +1256,7 @@ jobs:
- supply-chain
- docs-audit
- em-dash
- dco
- coverage
- compile
- typecheck
Expand All @@ -1236,9 +1275,9 @@ jobs:
env:
RESULTS: ${{ toJSON(needs) }}
INFORMATIONAL: docs-audit
# The image build only runs for a pull request; on a push it is
# skipped by design rather than failed.
MAY_SKIP: docker-build
# The image build and the sign-off check only run for a pull
# request; on a push they are skipped by design rather than failed.
MAY_SKIP: docker-build dco
run: bash .github/scripts/needs-verdict.sh
# The other half of the em dash gate, and the half no later commit can
# fix: this repository squash merges, so the pull request title and body
Expand Down
10 changes: 9 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
> documents, or a skip-checks trailer), not even quoted in backticks: the squash commit of #1094 carried one, and
> main ran no CI and the v3.1.0 tag on it no release. `make check-pr-description`
> refuses it.
>
> **Every commit is signed off** (`git commit -s`): a `Signed-off-by:` trailer naming
> its author certifies the Developer Certificate of Origin, and the `dco` job of CI
> fails a pull request with a commit that lacks one. A sign-off is a person's, so it is
> not AI attribution and the rule above still holds. `make check-dco` judges the
> branch before a push, and [CONTRIBUTING](CONTRIBUTING.md#developer-certificate-of-origin)
> says what it certifies and how to sign off commits already made.

**This file is loaded into every session, so it holds the rules and an index, and the long-form references live in `docs/development/`**, read when a task needs them: [Repository Map](docs/development/repository-map.md) (the annotated tree of commands and packages), [Environment Variables and Flags](docs/development/configuration.md), [Test Infrastructure](docs/development/test-infrastructure.md), [Request Paths and Fine-Grained Grants](docs/development/request-paths.md) (R-PATH, R-PAGE and R-GRANT), [Adding or Changing a Limit](docs/development/adding-a-limit.md), [Release Process](docs/development/release-process.md), [Tier Gating](docs/development/tier-gating.md), [HTTP Mode Invariants](docs/development/http-invariants.md) and [Resource Subscriptions](docs/development/resource-subscriptions.md). Keep it that way: what a new section adds here is the rule and a link, and its explanation goes to the page that owns the topic. Claude Code warns at every start once this file passes a size limit that scales with the model's context window and never falls below 40,000 characters (150,000 for current models in a one-million-token window).

Expand Down Expand Up @@ -54,7 +61,7 @@
| MCP Icons | 51 icons (50 domain + brand mark), each a 3-entry `[]mcp.Icon`: one SVG (base64 data URI, `Sizes: ["any"]`, `currentColor`) plus light/dark 16×16 lossless WebP fallbacks (`Theme`-tagged, `cmd/gen_icon_webp`) for clients that reject SVG. The brand mark is the generated "fan-out" (`cmd/gen_brand` → `brandmark_gen.go`), original artwork replacing the former tanuki |
| Source files (tools) | 788 non-test Go files under `internal/tools/` |
| Test files (tools) | 488 test files under `internal/tools/` |
| Go packages | 308 in the module (`go list ./...`); 335 directories hold tracked `.go` files, the twenty-seven more being what `go list` leaves out: twenty-six `testdata` fixture packages (the two stand-in programs, the coverage audit's planted trees, the closed-server audit's planted module, the stand-in module the SDK route reader's tests read and the platform module the condition measurement's discovery lists) and `test/e2e/modeleval/internal/provider`, whose every file sits behind a build tag. 182 under `internal/tools/...` (the root package plus 181 sub-packages) |
| Go packages | 309 in the module (`go list ./...`); 336 directories hold tracked `.go` files, the twenty-seven more being what `go list` leaves out: twenty-six `testdata` fixture packages (the two stand-in programs, the coverage audit's planted trees, the closed-server audit's planted module, the stand-in module the SDK route reader's tests read and the platform module the condition measurement's discovery lists) and `test/e2e/modeleval/internal/provider`, whose every file sits behind a build tag. 182 under `internal/tools/...` (the root package plus 181 sub-packages) |

### Orbit live tests

Expand Down Expand Up @@ -85,6 +92,7 @@ gitlab-mcp-server/
│ ├── audit_image_vulns/ # The image's Alpine packages against Trivy's database, before the push (make check-image-vulns)
│ ├── reproduce_release/ # Release binaries rebuilt from the tag and compared byte for byte (make check-reproducible, check-repeatable)
│ ├── audit_site_headers/ # The documentation site's security headers, after each deploy (make check-site-headers)
│ ├── audit_dco/ # Every commit of a pull request signed off by its author, the DCO (make check-dco)
│ ├── godoc_tool/ # Go doc auditor and fixer (make check-godocs)
│ ├── audit_md_escaping/ # GitLab-authored values reaching Markdown unescaped (make check-md-escaping)
│ ├── audit_metrics/ # Tool, resource and prompt metrics
Expand Down
26 changes: 24 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ How the project makes decisions, who holds which role and what it depends on to
- [Development Setup](#development-setup)
- [Branch Naming](#branch-naming)
- [Commit Messages](#commit-messages)
- [Developer Certificate of Origin](#developer-certificate-of-origin)
- [Pull Requests](#pull-requests)
- [Code Review](#code-review)
- [Code Standards](#code-standards)
Expand Down Expand Up @@ -132,6 +133,26 @@ test(branches): increase coverage to 90%
docs(site): describe the wiki tools in English and Spanish
```

## Developer Certificate of Origin

Every commit in a pull request is signed off: its message ends with a `Signed-off-by:` trailer naming the commit's author, with the name and email the commit is authored under. The sign-off is how the author certifies the [Developer Certificate of Origin](https://developercertificate.org/) 1.1 for that commit, which in short says that the contribution is theirs to submit under the project's [MIT license](LICENSE): they wrote it, or it is based on earlier work under a compatible open source license that they may submit with their changes, or it was given to them by somebody who certified one of those and they have not changed it; and that the contribution and its sign-off are public and kept, and may be redistributed under the license. The certificate's own text is the one that applies.

```bash
git commit -s -m "fix(config): handle empty GITLAB_URL gracefully"
```

`-s` (`--signoff`) writes the trailer from your git identity, so it names you when you are the author:

```text
Signed-off-by: Jane Doe <jane@example.org>
```

To sign off commits already on your branch where they stand, rebase it on its own base and push it again: `git rebase --keep-base --signoff origin/main`, then `git push --force-with-lease`. A commit somebody else authored is signed off by that person. A sign-off is not a signature: signing commits with a GPG or SSH key is welcome, and it does not replace the trailer.

`make check-dco` judges the commits of your branch against `origin/main` before you push, and the `✍️ DCO sign-off` job of CI judges every pull request the same way, from the base commit its event names to its head; for a stacked pull request that base is the head of the layer below, so each layer's pull request judges that layer's commits. It reads the trailer the way git does, and passes a commit when one of its `Signed-off-by` trailers names its author, the name and the email compared without regard to case. A merge commit that brings no change of its own, such as the one GitHub's Update branch button writes, is not judged; a merge that resolves a conflict or changes anything while merging is judged like any other commit, so conclude it with `git commit -s`. Dependabot's commits are exempt in the pull requests Dependabot opens, because an app cannot certify anything and what it commits is a dependency bump; the same commit in a pull request somebody else opened is judged like any other. The release workflow's version stamp reaches `main` without a pull request, so nothing judges it. The [command's reference](docs/development/cmd-utilities.md#audit_dco) says what else it refuses.

GitHub signs off a commit made in its web interface, such as an applied review suggestion, when the repository requires sign-off for web commits; that setting is not on yet ([Repository Settings](docs/development/repository-settings.md#web-commit-sign-off)), so until it is, such a commit needs the trailer typed into its message. The squash commit that lands on `main` needs nothing more: GitHub copies the sign-offs of the pull request's commits into it, below the description, so the description carries no trailer of its own.

## Pull Requests

### Before Submitting
Expand All @@ -142,6 +163,7 @@ docs(site): describe the wiki tools in English and Spanish
- [ ] New tools have tests, and total coverage stays at or above the 90% CI enforces
- [ ] Documentation is updated where the change shows: the site page for user-facing behavior (English and Spanish), `docs/development/` for contributor-facing changes (see [Documentation](#documentation))
- [ ] Commit messages follow conventional commits
- [ ] Every commit is signed off by its author (`git commit -s`), which certifies the [Developer Certificate of Origin](#developer-certificate-of-origin); `make check-dco` says which are not

### PR Process

Expand Down Expand Up @@ -176,11 +198,11 @@ Every change reaches `main` through a pull request, except the version stamp the
- **The published surface.** Tool and action names, input and output schemas, annotations and descriptions are deliberate, and what a model reads names only actions and tools the server serves.
- **Code standards.** The change follows [Code Standards](#code-standards), and any exception the linters allow is marked where it occurs with the reason.
- **Documentation.** A change a user can see updates its site page in English and Spanish together, a change to how the repository works updates `docs/development/`, and every generated artifact is regenerated rather than edited (see [Documentation](#documentation)).
- **History.** Commit messages follow [Commit Messages](#commit-messages), and the description, which becomes the squash commit's message, carries no skip command and no block a review bot injected.
- **History.** Commit messages follow [Commit Messages](#commit-messages), every commit is signed off by its author as [Developer Certificate of Origin](#developer-certificate-of-origin) requires, and the description, which becomes the squash commit's message, carries no skip command and no block a review bot injected.

### What a change needs to be accepted

- The required `✅ CI` check passes. It aggregates the build, the tests on Linux, macOS and Windows, the 90% coverage floor, the linters and the repository's own gates, `make check-pr-description` among them.
- The required `✅ CI` check passes. It aggregates the build, the tests on Linux, macOS and Windows, the 90% coverage floor, the linters and the repository's own gates, `make check-pr-description` and the sign-off check among them.
- Every review thread is resolved. The branch rules refuse the merge otherwise, except to the repository admin role, which is on their bypass list; the maintainer, who holds that role, merges a pull request only once this item and the one above hold.
- A contributor's pull request completes the template's checklist, with any item that does not apply marked so. The maintainer's own pull requests carry the same information in the description's account of what was run.
- The maintainer agrees the change belongs in the project. A correct change can still be declined when it falls outside the [roadmap](https://docs.jmrp.io/gitlab-mcp-server/roadmap/) or a recorded decision; the reason is given in the pull request.
Expand Down
Loading
Loading