diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 998e2b0..75be55d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,6 +24,24 @@ updates: labels: - "dependencies" + - package-ecosystem: "npm" + directory: "/mcp/apache-projects-mcp" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + cooldown: + default-days: 7 + semver-major-days: 21 + semver-minor-days: 7 + semver-patch-days: 3 + groups: + npm-minor-patch: + update-types: + - "minor" + - "patch" + labels: + - "dependencies" + - package-ecosystem: "github-actions" directory: "/" schedule: diff --git a/.github/license-header.txt b/.github/license-header.txt new file mode 100644 index 0000000..60b675e --- /dev/null +++ b/.github/license-header.txt @@ -0,0 +1,16 @@ +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. diff --git a/.github/workflows/ponymail-mcp-tests.yml b/.github/workflows/mcp-tests.yml similarity index 59% rename from .github/workflows/ponymail-mcp-tests.yml rename to .github/workflows/mcp-tests.yml index dbac030..f106ccc 100644 --- a/.github/workflows/ponymail-mcp-tests.yml +++ b/.github/workflows/mcp-tests.yml @@ -1,21 +1,23 @@ -name: ponymail-mcp tests +name: MCP server tests on: push: - branches: ["main", "rbowen-ponymail-mcp"] + branches: ["main"] paths: - - "mcp/ponymail-mcp/**" - - ".github/workflows/ponymail-mcp-tests.yml" + - "mcp/**" + - "scripts/check-licenses.mjs" + - ".github/workflows/mcp-tests.yml" pull_request: paths: - - "mcp/ponymail-mcp/**" - - ".github/workflows/ponymail-mcp-tests.yml" + - "mcp/**" + - "scripts/check-licenses.mjs" + - ".github/workflows/mcp-tests.yml" permissions: {} jobs: test: - name: Test on Node ${{ matrix.node-version }} + name: ${{ matrix.project }} on Node ${{ matrix.node-version }} runs-on: ubuntu-latest timeout-minutes: 10 permissions: @@ -24,11 +26,12 @@ jobs: strategy: fail-fast: false matrix: + project: ["ponymail-mcp", "apache-projects-mcp"] node-version: ["20", "22"] defaults: run: - working-directory: mcp/ponymail-mcp + working-directory: mcp/${{ matrix.project }} steps: - name: Checkout @@ -42,7 +45,10 @@ jobs: node-version: ${{ matrix.node-version }} - name: Install dependencies - run: npm install --no-audit --no-fund + run: npm ci - name: Run tests run: npm test + + - name: Check dependency licenses + run: npm run licenses diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml new file mode 100644 index 0000000..3edfa60 --- /dev/null +++ b/.github/workflows/static-checks.yml @@ -0,0 +1,29 @@ +name: Static checks + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +permissions: {} + +jobs: + prek: + name: prek (pre-commit hooks) + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + + - name: Run prek + run: uvx prek run --all-files --show-diff-on-failure diff --git a/.gitignore b/.gitignore index 2af3644..316db71 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ asf-highlights/birthdays/ project-activity/DATA/ project-activity/REPORTS/ node_modules/ -package-lock.json -mcp/apache-projects-mcp/node_modules/ +__pycache__/ +*.pyc diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..85495ae --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,94 @@ +# prek / pre-commit configuration. +# Docs: https://github.com/j178/prek and https://pre-commit.com/ +# +# One-time setup (see AGENTS.md): +# uv tool install prek +# prek install -t pre-commit -t commit-msg -t pre-push +# +# Run manually: +# prek run --all-files # fast checks (headers, yaml, ...) +# prek run --all-files --hook-stage pre-push # full suite (tests, licenses, zizmor) + +default_install_hook_types: [pre-commit, commit-msg, pre-push] + +exclude: | + (?x)^( + (.*/)?node_modules/.*| + (.*/)?package-lock\.json| + (.*/)?LICENSE| + \.github/license-header\.txt + )$ + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-merge-conflict + - id: check-case-conflict + - id: check-added-large-files + - id: check-executables-have-shebangs + - id: mixed-line-ending + args: [--fix=lf] + + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.37.2 + hooks: + - id: check-github-workflows + - id: check-dependabot + + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.6 + hooks: + - id: insert-license + name: insert license header (python/shell) + files: \.(py|sh)$ + args: + - --license-filepath=.github/license-header.txt + - --comment-style=# + - id: insert-license + name: insert license header (javascript) + files: \.m?js$ + args: + - --license-filepath=.github/license-header.txt + - --comment-style=// + - id: insert-license + name: insert license header (html) + files: \.html$ + args: + - --license-filepath=.github/license-header.txt + - --comment-style= + + - repo: local + hooks: + - id: no-coauthored-by + name: reject Co-authored-by trailers + entry: scripts/check-no-coauthor.sh + language: script + stages: [commit-msg] + + - id: mcp-tests + name: MCP server tests (pre-push) + entry: bash -c 'for d in mcp/*/; do (cd "$d" && npm test) || exit 1; done' + language: system + pass_filenames: false + files: ^mcp/ + stages: [pre-push] + + - id: license-allowlist + name: dependency license allowlist (pre-push) + entry: bash -c 'for d in mcp/*/; do node scripts/check-licenses.mjs "$d" || exit 1; done' + language: system + pass_filenames: false + stages: [pre-push] + + - id: zizmor + name: zizmor GitHub Actions lint (pre-push) + entry: uvx zizmor .github/workflows + language: system + pass_filenames: false + files: ^\.github/ + stages: [pre-push] diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3203683 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,103 @@ +# AGENTS.md + +Guidance for AI coding agents (and humans) working in the `apache/comdev` +repository. Read this before making changes. + +## Repository layout + +| Path | What it is | Toolchain | +|------|------------|-----------| +| `mcp/ponymail-mcp/` | MCP server for Apache PonyMail archives | Node.js (≥20), npm | +| `mcp/apache-projects-mcp/` | MCP server for `projects.apache.org` data | Node.js (≥20), npm | +| `asf-highlights/` | ASF activity / birthday report scripts | Python via `uv` (PEP-723 inline scripts) | +| `project-activity/` | Project activity report script | Python via `uv` (PEP-723 inline scripts) | +| `scripts/` | Repo tooling (license allowlist check, git hooks) | Node / Bash | +| `.github/` | CI workflows, dependabot, security config | GitHub Actions | + +## Attribution policy (ASF requirement — read this) + +Commits produced with AI/agent assistance **MUST** be attributed with a +`Generated-by:` trailer that names **the agent and its version** (the agent that +made the change), optionally followed by the model: + +``` +Generated-by: [(model)] +``` + +Concrete example: + +``` +Generated-by: Claude Code 2.1.158 (Claude Opus 4.8) +``` + +**Do NOT add `Co-authored-by:` trailers.** The ASF does not attribute commits to +AI tools as co-authors. This is enforced by a `commit-msg` hook +(`scripts/check-no-coauthor.sh`) that rejects any commit message containing a +`Co-authored-by:` line — install the hooks (below) so the check runs locally. + +## One-time setup + +Install [`prek`](https://github.com/j178/prek) (a fast, drop-in pre-commit +runner) and wire up the git hooks: + +```bash +uv tool install prek # or: pipx install prek +prek install -t pre-commit -t commit-msg -t pre-push +``` + +This installs three hook types: +- **pre-commit** — fast checks: license headers, trailing whitespace, YAML/JSON + validity, workflow + dependabot schema validation. +- **commit-msg** — rejects `Co-authored-by:` trailers (see policy above). +- **pre-push** — the full suite: MCP test suites, dependency license allowlist, + and `zizmor` GitHub Actions lint. + +## Running the checks + +Run the fast checks at any time: + +```bash +prek run --all-files +``` + +**Run all pre-push checks before you push** (this is the gate CI enforces): + +```bash +prek run --all-files --hook-stage pre-push +``` + +The pre-push hooks shell out to `npm` and `uvx`, so for them to pass you need +each MCP project installed. Equivalently, run per project: + +```bash +cd mcp/ponymail-mcp # and mcp/apache-projects-mcp +npm ci # reproducible install from the committed lock file +npm test # unit tests +npm run licenses # dependency license allowlist check +``` + +## Conventions + +- **License headers.** Every source file (`*.js`, `*.mjs`, `*.py`, `*.sh`, + `*.html`) must carry the Apache-2.0 header from `.github/license-header.txt`. + The `insert-license` pre-commit hook adds it automatically; for files with a + `#!` shebang or a PEP-723 (`# /// script`) block, the header goes *after* + those lines. +- **Dependency licenses.** New npm dependencies must use an ASF Category-A + license (see the allowlist in `scripts/check-licenses.mjs`). Vetted exceptions + go in a per-project `.license-allowlist-exceptions.json`. +- **Lock files are committed.** `package-lock.json` is tracked for reproducible + installs and license checks — commit it alongside `package.json` changes and + install with `npm ci`. +- **GitHub Actions** are SHA-pinned (with a `# vX.Y.Z` comment), use top-level + `permissions: {}` with minimal per-job grants, and set + `persist-credentials: false` on checkout. `zizmor` enforces this. +- **Python** scripts are self-contained PEP-723 `uv` scripts — run them with + `uv run