From 8e9a0b230ae2b2da2fa3183f1227389c18aef154 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Thu, 30 Jul 2026 10:12:08 -0700 Subject: [PATCH 01/13] ci: adopt release-please for monorepo releases Replaces the Changesets machinery with release-please in manifest mode for the four published packages (issue #870): - release-please-config.json / .release-please-manifest.json seeded with current versions; existing name@version tag format preserved - release-please.yml maintains per-package Release PRs and fans out to the publish workflows via outputs (GITHUB_TOKEN-created releases do not trigger on:release workflows) - cd-deploy-lib-*.yml lose the release-creation job (release-please owns tag + release + notes) and become workflow_call with a workflow_dispatch fallback for re-running a publish - ci-bump-version.yml, .changeset/, the release-notes script and configs, and the vestigial lib/python-sdk/package.json are removed; the Python version now lives only in pyproject.toml (mirrored into __init__.py, which was stuck at 0.1.0) - ci-pr-title.yml enforces conventional-commit PR titles, which drive version bumps once squash-merged - tomlq/tomlkit dev-deps removed (only used by the deleted bump script) --- .changeset/README.md | 8 - .changeset/config.json | 19 - .github/release-config/lib-cli.yml | 5 - .github/release-config/lib-core.yml | 5 - .github/release-config/lib-pysdk.yml | 5 - .github/release-config/lib-ts-sdk.yml | 6 - .../validate-and-generate-release-notes.sh | 124 ---- .github/workflows/cd-deploy-lib-cli.yml | 59 +- .github/workflows/cd-deploy-lib-core.yml | 59 +- .github/workflows/cd-deploy-lib-pysdk.yml | 59 +- .github/workflows/cd-deploy-lib-ts-sdk.yml | 59 +- .github/workflows/ci-bump-version.yml | 133 ---- .github/workflows/ci-pr-title.yml | 19 + .github/workflows/deps-catalog-check.yml | 2 +- .github/workflows/release-please.yml | 66 ++ .release-please-manifest.json | 6 + lib/python-sdk/common_grants_sdk/__init__.py | 2 +- lib/python-sdk/package.json | 5 - lib/python-sdk/poetry.lock | 65 +- lib/python-sdk/pyproject.toml | 2 - package.json | 6 +- pnpm-lock.yaml | 571 +----------------- pnpm-workspace.yaml | 1 - release-please-config.json | 43 ++ 24 files changed, 179 insertions(+), 1150 deletions(-) delete mode 100644 .changeset/README.md delete mode 100644 .changeset/config.json delete mode 100644 .github/release-config/lib-cli.yml delete mode 100644 .github/release-config/lib-core.yml delete mode 100644 .github/release-config/lib-pysdk.yml delete mode 100644 .github/release-config/lib-ts-sdk.yml delete mode 100755 .github/scripts/validate-and-generate-release-notes.sh delete mode 100644 .github/workflows/ci-bump-version.yml create mode 100644 .github/workflows/ci-pr-title.yml create mode 100644 .github/workflows/release-please.yml create mode 100644 .release-please-manifest.json delete mode 100644 lib/python-sdk/package.json create mode 100644 release-please-config.json diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index e5b6d8d6a..000000000 --- a/.changeset/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Changesets - -Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works -with multi-package repos, or single-package repos to help you version and publish your code. You can -find the full documentation for it [in our repository](https://github.com/changesets/changesets) - -We have a quick list of common questions to get you started engaging with this project in -[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json deleted file mode 100644 index 7bc8f57be..000000000 --- a/.changeset/config.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", - "changelog": "@changesets/cli/changelog", - "commit": false, - "fixed": [], - "linked": [], - "access": "restricted", - "baseBranch": "main", - "updateInternalDependencies": "patch", - "ignore": [ - "typespec-versioning-changelog" - ], - "packages": [ - "lib/python-sdk", - "lib/core", - "lib/cli", - "lib/ts-sdk" - ] -} diff --git a/.github/release-config/lib-cli.yml b/.github/release-config/lib-cli.yml deleted file mode 100644 index a391538f3..000000000 --- a/.github/release-config/lib-cli.yml +++ /dev/null @@ -1,5 +0,0 @@ -changelog: - categories: - - title: Changes - labels: - - cli diff --git a/.github/release-config/lib-core.yml b/.github/release-config/lib-core.yml deleted file mode 100644 index d7515d334..000000000 --- a/.github/release-config/lib-core.yml +++ /dev/null @@ -1,5 +0,0 @@ -changelog: - categories: - - title: Changes - labels: - - core diff --git a/.github/release-config/lib-pysdk.yml b/.github/release-config/lib-pysdk.yml deleted file mode 100644 index 0e1acb6a4..000000000 --- a/.github/release-config/lib-pysdk.yml +++ /dev/null @@ -1,5 +0,0 @@ -changelog: - categories: - - title: Changes - labels: - - py-sdk diff --git a/.github/release-config/lib-ts-sdk.yml b/.github/release-config/lib-ts-sdk.yml deleted file mode 100644 index 1856fa655..000000000 --- a/.github/release-config/lib-ts-sdk.yml +++ /dev/null @@ -1,6 +0,0 @@ -changelog: - categories: - - title: Changes - labels: - - ts-sdk - diff --git a/.github/scripts/validate-and-generate-release-notes.sh b/.github/scripts/validate-and-generate-release-notes.sh deleted file mode 100755 index aa1283acf..000000000 --- a/.github/scripts/validate-and-generate-release-notes.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/bin/bash -# Script to validate tags and generate release notes -# Usage: validate-and-generate-release-notes.sh \ -# --prefix PREFIX \ -# --config-file CONFIG \ -# --release-tag TAG \ -# [--previous-tag PREV_TAG] \ -# [--dry-run] \ -# [--repository REPO] \ -# [--github-output OUTPUT_FILE] - -set -e - -# Parse arguments -PREFIX="" -CONFIG_FILE="" -RELEASE_TAG="" -PREVIOUS_TAG="" -DRY_RUN="false" -REPOSITORY="${GITHUB_REPOSITORY}" -GITHUB_OUTPUT_FILE="${GITHUB_OUTPUT:-$GITHUB_OUTPUT}" - -while [[ $# -gt 0 ]]; do - case $1 in - --prefix) - PREFIX="$2" - shift 2 - ;; - --config-file) - CONFIG_FILE="$2" - shift 2 - ;; - --release-tag) - RELEASE_TAG="$2" - shift 2 - ;; - --previous-tag) - # Optional argument: accepts a value if the next argument exists and isn't another flag - # Handles these scenarios: - # --previous-tag "v1.0.0" → PREVIOUS_TAG="v1.0.0", shift 2 - # --previous-tag "" → PREVIOUS_TAG="", shift 2 (explicit empty string) - # --previous-tag --other-flag → PREVIOUS_TAG unchanged, shift 1 (next arg is a flag) - # --previous-tag (at end) → PREVIOUS_TAG unchanged, shift 1 (no more args) - if [[ $# -gt 1 ]] && [[ ! "$2" =~ ^-- ]]; then - PREVIOUS_TAG="$2" - shift 2 - else - shift - fi - ;; - --dry-run) - DRY_RUN="true" - shift - ;; - --repository) - REPOSITORY="$2" - shift 2 - ;; - --github-output) - GITHUB_OUTPUT_FILE="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done - -# Validate required arguments -if [[ -z "$PREFIX" ]] || [[ -z "$CONFIG_FILE" ]] || [[ -z "$RELEASE_TAG" ]] || [[ -z "$REPOSITORY" ]]; then - echo "Error: Missing required arguments" - echo "Usage: $0 --prefix PREFIX --config-file CONFIG --release-tag TAG [--previous-tag PREV_TAG] [--dry-run] [--repository REPO] [--github-output OUTPUT_FILE]" - exit 1 -fi - -# Validate tag prefix -if [[ ! "$RELEASE_TAG" == "$PREFIX"* ]]; then - echo "✗ Latest tag '$RELEASE_TAG' must start with '$PREFIX'" - exit 1 -fi - -# Validate tag exists -if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then - echo "✗ Tag '$RELEASE_TAG' does not exist" - exit 1 -fi - -echo "✓ Latest tag '$RELEASE_TAG' is valid" - -# Generate release notes -if [[ -n "$PREVIOUS_TAG" ]]; then - echo "Generating release notes between $PREVIOUS_TAG and $RELEASE_TAG" - NOTES=$(gh api repos/"$REPOSITORY"/releases/generate-notes \ - -f tag_name="$RELEASE_TAG" \ - -f previous_tag_name="$PREVIOUS_TAG" \ - -f configuration_file_path="$CONFIG_FILE" \ - -q '.body') -else - echo "Generating release notes for $RELEASE_TAG" - NOTES=$(gh api repos/"$REPOSITORY"/releases/generate-notes \ - -f tag_name="$RELEASE_TAG" \ - -f configuration_file_path="$CONFIG_FILE" \ - -q '.body') -fi - -# Output notes using multiline format -if [[ -n "$GITHUB_OUTPUT_FILE" ]]; then - echo "notes<> "$GITHUB_OUTPUT_FILE" - echo "$NOTES" >> "$GITHUB_OUTPUT_FILE" - echo "EOF" >> "$GITHUB_OUTPUT_FILE" -fi - -# Display notes in dry-run mode -if [[ "$DRY_RUN" == "true" ]]; then - echo "" - echo "=== DRY RUN MODE - Release notes preview ===" - echo "Latest tag: $RELEASE_TAG" - echo "Previous tag: $PREVIOUS_TAG" - echo "" - echo "$NOTES" - echo "=== End of preview ===" -fi - diff --git a/.github/workflows/cd-deploy-lib-cli.yml b/.github/workflows/cd-deploy-lib-cli.yml index a420f7620..b2fe6a348 100644 --- a/.github/workflows/cd-deploy-lib-cli.yml +++ b/.github/workflows/cd-deploy-lib-cli.yml @@ -1,62 +1,23 @@ name: "CD - Deploy CLI Package to npm" on: - workflow_dispatch: + # Called by release-please.yml when a CLI release is created + workflow_call: inputs: release_tag: description: "Release tag (e.g., @common-grants/cli@0.2.0)" required: true type: string - previous_tag: - description: "Previous tag (e.g., @common-grants/cli@0.1.0) - optional" - required: false + # Manual fallback for re-running a publish against an existing tag + workflow_dispatch: + inputs: + release_tag: + description: "Release tag (e.g., @common-grants/cli@0.2.0)" + required: true type: string - dry_run: - description: "Dry run mode (default: false) - set to true to preview the release notes" - required: false - type: boolean - default: false jobs: - create-release-from-tag: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Validate tags and generate release notes - id: release-notes - run: | - DRY_RUN_FLAG="" - if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then - DRY_RUN_FLAG="--dry-run" - fi - .github/scripts/validate-and-generate-release-notes.sh \ - --prefix "@common-grants/cli@" \ - --config-file ".github/release-config/lib-cli.yml" \ - --release-tag "${{ github.event.inputs.release_tag }}" \ - --previous-tag "${{ github.event.inputs.previous_tag }}" \ - --repository "${{ github.repository }}" \ - $DRY_RUN_FLAG - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create release - if: github.event.inputs.dry_run != 'true' - uses: softprops/action-gh-release@v3 - with: - tag_name: ${{ github.event.inputs.release_tag }} - body: ${{ steps.release-notes.outputs.notes }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - deploy: - needs: create-release-from-tag - if: github.event.inputs.dry_run != 'true' runs-on: ubuntu-latest environment: name: npm-cli @@ -72,7 +33,7 @@ jobs: - name: Checkout code uses: actions/checkout@v6 with: - ref: ${{ github.event.inputs.release_tag }} + ref: ${{ inputs.release_tag }} # Setup .npmrc file to publish to npm - name: Install Node.js @@ -98,4 +59,4 @@ jobs: - name: Notify completion run: | echo "CLI package deployed to npm" - echo "Tag: ${{ github.event.inputs.release_tag }}" + echo "Tag: ${{ inputs.release_tag }}" diff --git a/.github/workflows/cd-deploy-lib-core.yml b/.github/workflows/cd-deploy-lib-core.yml index 18b03c73e..3a1fd014e 100644 --- a/.github/workflows/cd-deploy-lib-core.yml +++ b/.github/workflows/cd-deploy-lib-core.yml @@ -1,62 +1,23 @@ name: "CD - Deploy Core Package to npm" on: - workflow_dispatch: + # Called by release-please.yml when a core release is created + workflow_call: inputs: release_tag: description: "Release tag (e.g., @common-grants/core@0.2.0)" required: true type: string - previous_tag: - description: "Previous tag (e.g., @common-grants/core@0.1.0) - optional" - required: false + # Manual fallback for re-running a publish against an existing tag + workflow_dispatch: + inputs: + release_tag: + description: "Release tag (e.g., @common-grants/core@0.2.0)" + required: true type: string - dry_run: - description: "Dry run mode (default: false) - set to true to preview the release notes" - required: false - type: boolean - default: false jobs: - create-release-from-tag: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Validate tags and generate release notes - id: release-notes - run: | - DRY_RUN_FLAG="" - if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then - DRY_RUN_FLAG="--dry-run" - fi - .github/scripts/validate-and-generate-release-notes.sh \ - --prefix "@common-grants/core@" \ - --config-file ".github/release-config/lib-core.yml" \ - --release-tag "${{ github.event.inputs.release_tag }}" \ - --previous-tag "${{ github.event.inputs.previous_tag }}" \ - --repository "${{ github.repository }}" \ - $DRY_RUN_FLAG - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create release - if: github.event.inputs.dry_run != 'true' - uses: softprops/action-gh-release@v3 - with: - tag_name: ${{ github.event.inputs.release_tag }} - body: ${{ steps.release-notes.outputs.notes }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - deploy: - needs: create-release-from-tag - if: github.event.inputs.dry_run != 'true' runs-on: ubuntu-latest environment: name: npm-core @@ -72,7 +33,7 @@ jobs: - name: Checkout code uses: actions/checkout@v6 with: - ref: ${{ github.event.inputs.release_tag }} + ref: ${{ inputs.release_tag }} # Setup .npmrc file to publish to npm - name: Install Node.js @@ -98,4 +59,4 @@ jobs: - name: Notify completion run: | echo "Core package deployed to npm" - echo "Tag: ${{ github.event.inputs.release_tag }}" + echo "Tag: ${{ inputs.release_tag }}" diff --git a/.github/workflows/cd-deploy-lib-pysdk.yml b/.github/workflows/cd-deploy-lib-pysdk.yml index e9854d7e7..514d1be6b 100644 --- a/.github/workflows/cd-deploy-lib-pysdk.yml +++ b/.github/workflows/cd-deploy-lib-pysdk.yml @@ -1,62 +1,23 @@ name: "CD - Deploy Python Package to PyPI" on: - workflow_dispatch: + # Called by release-please.yml when a Python SDK release is created + workflow_call: inputs: release_tag: description: "Release tag (e.g., common-grants-sdk@0.3.0)" required: true type: string - previous_tag: - description: "Previous tag (e.g., common-grants-sdk@0.2.0) - optional" - required: false + # Manual fallback for re-running a publish against an existing tag + workflow_dispatch: + inputs: + release_tag: + description: "Release tag (e.g., common-grants-sdk@0.3.0)" + required: true type: string - dry_run: - description: "Dry run mode (default: false) - set to true to preview the release notes" - required: false - type: boolean - default: false jobs: - create-release-from-tag: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Validate tags and generate release notes - id: release-notes - run: | - DRY_RUN_FLAG="" - if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then - DRY_RUN_FLAG="--dry-run" - fi - .github/scripts/validate-and-generate-release-notes.sh \ - --prefix "common-grants-sdk@" \ - --config-file ".github/release-config/lib-pysdk.yml" \ - --release-tag "${{ github.event.inputs.release_tag }}" \ - --previous-tag "${{ github.event.inputs.previous_tag }}" \ - --repository "${{ github.repository }}" \ - $DRY_RUN_FLAG - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create release - if: github.event.inputs.dry_run != 'true' - uses: softprops/action-gh-release@v3 - with: - tag_name: ${{ github.event.inputs.release_tag }} - body: ${{ steps.release-notes.outputs.notes }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - deploy: - needs: create-release-from-tag - if: github.event.inputs.dry_run != 'true' runs-on: ubuntu-latest environment: name: pypi @@ -72,7 +33,7 @@ jobs: - name: Checkout code uses: actions/checkout@v6 with: - ref: ${{ github.event.inputs.release_tag }} + ref: ${{ inputs.release_tag }} - name: Install Python uses: actions/setup-python@v6 @@ -95,4 +56,4 @@ jobs: - name: Notify completion run: | echo "Python SDK deployed to PyPI" - echo "Tag: ${{ github.event.inputs.release_tag }}" + echo "Tag: ${{ inputs.release_tag }}" diff --git a/.github/workflows/cd-deploy-lib-ts-sdk.yml b/.github/workflows/cd-deploy-lib-ts-sdk.yml index 40012e04b..dbeba3ab2 100644 --- a/.github/workflows/cd-deploy-lib-ts-sdk.yml +++ b/.github/workflows/cd-deploy-lib-ts-sdk.yml @@ -1,62 +1,23 @@ name: "CD - Deploy TypeScript SDK to npm" on: - workflow_dispatch: + # Called by release-please.yml when a TypeScript SDK release is created + workflow_call: inputs: release_tag: description: "Release tag (e.g., @common-grants/sdk@0.2.0)" required: true type: string - previous_tag: - description: "Previous tag (e.g., @common-grants/sdk@0.1.0) - optional" - required: false + # Manual fallback for re-running a publish against an existing tag + workflow_dispatch: + inputs: + release_tag: + description: "Release tag (e.g., @common-grants/sdk@0.2.0)" + required: true type: string - dry_run: - description: "Dry run mode (default: false) - set to true to preview the release notes" - required: false - type: boolean - default: false jobs: - create-release-from-tag: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Validate tags and generate release notes - id: release-notes - run: | - DRY_RUN_FLAG="" - if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then - DRY_RUN_FLAG="--dry-run" - fi - .github/scripts/validate-and-generate-release-notes.sh \ - --prefix "@common-grants/sdk@" \ - --config-file ".github/release-config/lib-ts-sdk.yml" \ - --release-tag "${{ github.event.inputs.release_tag }}" \ - --previous-tag "${{ github.event.inputs.previous_tag }}" \ - --repository "${{ github.repository }}" \ - $DRY_RUN_FLAG - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create release - if: github.event.inputs.dry_run != 'true' - uses: softprops/action-gh-release@v3 - with: - tag_name: ${{ github.event.inputs.release_tag }} - body: ${{ steps.release-notes.outputs.notes }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - deploy: - needs: create-release-from-tag - if: github.event.inputs.dry_run != 'true' runs-on: ubuntu-latest environment: name: npm-sdk @@ -72,7 +33,7 @@ jobs: - name: Checkout code uses: actions/checkout@v6 with: - ref: ${{ github.event.inputs.release_tag }} + ref: ${{ inputs.release_tag }} # Setup .npmrc file to publish to npm - name: Install Node.js @@ -98,4 +59,4 @@ jobs: - name: Notify completion run: | echo "TypeScript SDK package deployed to npm" - echo "Tag: ${{ github.event.inputs.release_tag }}" + echo "Tag: ${{ inputs.release_tag }}" diff --git a/.github/workflows/ci-bump-version.yml b/.github/workflows/ci-bump-version.yml deleted file mode 100644 index 899994f0e..000000000 --- a/.github/workflows/ci-bump-version.yml +++ /dev/null @@ -1,133 +0,0 @@ -name: "CI - Bump Version & Create Tag" - -on: - push: - branches: - - main - pull_request: - -permissions: - contents: write - -jobs: - version: - runs-on: ubuntu-latest - timeout-minutes: 10 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - name: Checkout repo - uses: actions/checkout@v6 - with: - # Full history: Changesets deepens a shallow clone (git fetch --deepen) - # to find each changeset's adding commit, which can hang in CI. - fetch-depth: 0 - - - name: Install Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - - - name: Install pnpm - uses: pnpm/action-setup@v6 - - - name: Install Python - uses: actions/setup-python@v6 - with: - python-version: "3.11" - - - name: Install Poetry - uses: Gr1N/setup-poetry@v9 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build core library - run: pnpm --filter @common-grants/core run build - - - name: Build cli library - run: pnpm --filter @common-grants/cli run build - - - name: Install python-sdk dependencies - working-directory: ./lib/python-sdk - run: poetry install - - - name: Detect changesets - id: check_changesets - run: | - # Find changeset files - CHANGESET_FILES=$(find .changeset -type f -name "*.md" ! -name "README.md") - if [ -z "$CHANGESET_FILES" ]; then - echo "No changeset files found" - echo "found=false" >> $GITHUB_OUTPUT - exit 0 - fi - # Inspect changeset files - echo "Found changeset files: $CHANGESET_FILES" - echo "found=true" >> $GITHUB_OUTPUT - PY_CHANGELOG=$(grep '"common-grants-sdk":' $CHANGESET_FILES || true) - if [ -z "$PY_CHANGELOG" ]; then - echo "found_py=false" >> $GITHUB_OUTPUT - exit 0 - else - echo "found_py=true" >> $GITHUB_OUTPUT - # Multiple changesets can bump common-grants-sdk (e.g. a feature + - # a fix landing in the same release), so PY_CHANGELOG may be - # multiline — write it via the heredoc form, not key=value. - { - echo "py_changelog<> $GITHUB_OUTPUT - fi - - - name: Bump version for Python package - working-directory: ./lib/python-sdk - if: steps.check_changesets.outputs.found_py == 'true' - run: | - CHANGELOG="${{ steps.check_changesets.outputs.py_changelog }}" - if [ -z "$CHANGELOG" ]; then - echo "No bump needed for Python package" - exit 0 - elif echo "$CHANGELOG" | grep -q 'major'; then - echo "Major bump needed for Python package" - poetry version major - elif echo "$CHANGELOG" | grep -q 'minor'; then - echo "Minor bump needed for Python package" - poetry version minor - elif echo "$CHANGELOG" | grep -q 'patch'; then - echo "Patch bump needed for Python package" - poetry version patch - fi - - - name: Bump version for Node package - if: steps.check_changesets.outputs.found == 'true' - run: | - pnpm changeset version - git status - - - name: Commit bumped version - if: steps.check_changesets.outputs.found == 'true' && github.event_name != 'pull_request' - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add . - git commit --no-verify -m "chore: bump version [skip ci]" || echo "No changes to commit" - git push - - - name: Create tag for Python package - if: steps.check_changesets.outputs.found_py == 'true' - working-directory: ./lib/python-sdk - run: | - PKG_NAME=$(poetry run tomlq -r .tool.poetry.name pyproject.toml) - PY_VERSION=$(poetry run tomlq -r .tool.poetry.version pyproject.toml) - git tag ${PKG_NAME}@${PY_VERSION} - echo "Added tag: ${PKG_NAME}@${PY_VERSION}" - - - name: Create tag for Node package - if: steps.check_changesets.outputs.found == 'true' - run: pnpm changeset tag - - - name: Push tags - if: steps.check_changesets.outputs.found == 'true' && github.event_name != 'pull_request' - run: git push --tags diff --git a/.github/workflows/ci-pr-title.yml b/.github/workflows/ci-pr-title.yml new file mode 100644 index 000000000..30a2cea8c --- /dev/null +++ b/.github/workflows/ci-pr-title.yml @@ -0,0 +1,19 @@ +name: "CI - PR Title" + +on: + pull_request: + types: [opened, edited, synchronize] + +permissions: + pull-requests: read + +jobs: + lint-pr-title: + runs-on: ubuntu-latest + steps: + # PR titles become the squashed commit subject on main, which + # release-please parses to decide version bumps and changelog entries. + - name: Validate conventional-commit PR title + uses: amannn/action-semantic-pull-request@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/deps-catalog-check.yml b/.github/workflows/deps-catalog-check.yml index 7f9517a2f..f2e69196c 100644 --- a/.github/workflows/deps-catalog-check.yml +++ b/.github/workflows/deps-catalog-check.yml @@ -85,7 +85,7 @@ jobs: - [ ] If TypeSpec packages updated: verify `pnpm run typespec` output is sensible - [ ] If website deps updated: spot-check key pages render correctly - [ ] If SDK-affecting deps updated: verify SDK examples still work - - [ ] Add changeset if peerDep ranges changed for `@common-grants/core` + - [ ] If peerDep ranges changed for `@common-grants/core`, retitle this PR `fix(core): ...` so the change ships in a release (`chore(deps)` does not trigger a version bump) labels: | dependencies commit-message: "chore(deps): update catalog dependencies" diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 000000000..7a4282050 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,66 @@ +name: "Release Please" + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - name: Run release-please + id: release + uses: googleapis/release-please-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + manifest-file: .release-please-manifest.json + config-file: release-please-config.json + outputs: + core_released: ${{ steps.release.outputs['lib/core--release_created'] }} + core_tag: ${{ steps.release.outputs['lib/core--tag_name'] }} + cli_released: ${{ steps.release.outputs['lib/cli--release_created'] }} + cli_tag: ${{ steps.release.outputs['lib/cli--tag_name'] }} + ts_sdk_released: ${{ steps.release.outputs['lib/ts-sdk--release_created'] }} + ts_sdk_tag: ${{ steps.release.outputs['lib/ts-sdk--tag_name'] }} + py_sdk_released: ${{ steps.release.outputs['lib/python-sdk--release_created'] }} + py_sdk_tag: ${{ steps.release.outputs['lib/python-sdk--tag_name'] }} + + # Publishes fan out from the outputs above. Tags and releases created with + # GITHUB_TOKEN do not trigger other workflows, so the deploys must be called + # from this run rather than via `on: release`. + publish-core: + needs: release-please + if: needs.release-please.outputs.core_released == 'true' + uses: ./.github/workflows/cd-deploy-lib-core.yml + with: + release_tag: ${{ needs.release-please.outputs.core_tag }} + secrets: inherit + + publish-cli: + needs: release-please + if: needs.release-please.outputs.cli_released == 'true' + uses: ./.github/workflows/cd-deploy-lib-cli.yml + with: + release_tag: ${{ needs.release-please.outputs.cli_tag }} + secrets: inherit + + publish-ts-sdk: + needs: release-please + if: needs.release-please.outputs.ts_sdk_released == 'true' + uses: ./.github/workflows/cd-deploy-lib-ts-sdk.yml + with: + release_tag: ${{ needs.release-please.outputs.ts_sdk_tag }} + secrets: inherit + + publish-py-sdk: + needs: release-please + if: needs.release-please.outputs.py_sdk_released == 'true' + uses: ./.github/workflows/cd-deploy-lib-pysdk.yml + with: + release_tag: ${{ needs.release-please.outputs.py_sdk_tag }} + secrets: inherit diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 000000000..3674f076f --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,6 @@ +{ + "lib/core": "0.3.5", + "lib/cli": "0.3.7", + "lib/ts-sdk": "0.6.1", + "lib/python-sdk": "0.8.0" +} diff --git a/lib/python-sdk/common_grants_sdk/__init__.py b/lib/python-sdk/common_grants_sdk/__init__.py index 9d2b9bad3..0e304e2ec 100644 --- a/lib/python-sdk/common_grants_sdk/__init__.py +++ b/lib/python-sdk/common_grants_sdk/__init__.py @@ -4,7 +4,7 @@ A Python implementation of the CommonGrants protocol. """ -__version__ = "0.1.0" +__version__ = "0.8.0" # x-release-please-version from . import schemas from .client import Auth, Client, Config diff --git a/lib/python-sdk/package.json b/lib/python-sdk/package.json deleted file mode 100644 index 89ffa4d61..000000000 --- a/lib/python-sdk/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "common-grants-sdk", - "version": "0.8.0", - "private": true -} diff --git a/lib/python-sdk/poetry.lock b/lib/python-sdk/poetry.lock index 05a80d337..24fde28d9 100644 --- a/lib/python-sdk/poetry.lock +++ b/lib/python-sdk/poetry.lock @@ -1059,33 +1059,6 @@ files = [ {file = "ruff-0.15.22.tar.gz", hash = "sha256:3f15175b1fb580126f58285a5dae6b2ea89000136d980c64499211f116b54809"}, ] -[[package]] -name = "tomlkit" -version = "0.15.1" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304"}, - {file = "tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97"}, -] - -[[package]] -name = "tomlq" -version = "0.1.0" -description = "A Python wrapper for yq exposing the toml functionality" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "tomlq-0.1.0-py2.py3-none-any.whl", hash = "sha256:4b966fd999ed2bf69081b7c7f5caadbc4c9542d0ed5fcf2e9b7b4d8d7ada3c82"}, - {file = "tomlq-0.1.0.tar.gz", hash = "sha256:e775720e90da3e405142b9fe476145e71c0389f787b1ff9933f92a1704d8c6e7"}, -] - -[package.dependencies] -yq = {version = ">=2.7.2", extras = ["toml"]} - [[package]] name = "typeguard" version = "4.5.2" @@ -1140,43 +1113,7 @@ files = [ [package.dependencies] typing-extensions = ">=4.12.0" -[[package]] -name = "xmltodict" -version = "1.0.4" -description = "Makes working with XML feel like you are working with JSON" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "xmltodict-1.0.4-py3-none-any.whl", hash = "sha256:a4a00d300b0e1c59fc2bfccb53d7b2e88c32f200df138a0dd2229f842497026a"}, - {file = "xmltodict-1.0.4.tar.gz", hash = "sha256:6d94c9f834dd9e44514162799d344d815a3a4faec913717a9ecbfa5be1bb8e61"}, -] - -[package.extras] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "yq" -version = "3.4.3" -description = "Command-line YAML/XML processor - jq wrapper for YAML/XML documents" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "yq-3.4.3-py3-none-any.whl", hash = "sha256:547e34bc3caacce83665fd3429bf7c85f8e8b6b9aaee3f953db1ad716ff3434d"}, - {file = "yq-3.4.3.tar.gz", hash = "sha256:ba586a1a6f30cf705b2f92206712df2281cd320280210e7b7b80adcb8f256e3b"}, -] - -[package.dependencies] -argcomplete = ">=1.8.1" -PyYAML = ">=5.3.1" -tomlkit = ">=0.11.6" -xmltodict = ">=0.11.0" - -[package.extras] -tests = ["build", "coverage", "mypy", "ruff", "wheel"] - [metadata] lock-version = "2.1" python-versions = "^3.11" -content-hash = "8f27f5c4b35d8eb9b365c8a5939ca24b6edc45c6060308e9fdbed1e0afd59dce" +content-hash = "969a305d8e20998d63252e2bd93978e16cdd163717db1ad0bad68d157840499a" diff --git a/lib/python-sdk/pyproject.toml b/lib/python-sdk/pyproject.toml index 18615f73b..ff5ef57c3 100644 --- a/lib/python-sdk/pyproject.toml +++ b/lib/python-sdk/pyproject.toml @@ -21,8 +21,6 @@ pytest = "^8.0.0" pytest-cov = "^5.0.0" cg-grants-gov = ">=0.2,<0.4" ruff = ">=0.11,<0.16" -tomlkit = ">=0.13.3,<0.16.0" -tomlq = "^0.1.0" types-pyyaml = "^6.0.12.20250915" datamodel-code-generator = ">=0.43.1,<0.70.0" diff --git a/package.json b/package.json index 0c9fd3373..7c3ff7f40 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,5 @@ "keywords": [], "author": "", "license": "ISC", - "packageManager": "pnpm@10.33.0", - "devDependencies": { - "@changesets/changelog-github": "^0.7.0", - "@changesets/cli": "^2.31.1" - } + "packageManager": "pnpm@10.33.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f92943e6b..c1d633e42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,14 +92,7 @@ overrides: importers: - .: - devDependencies: - '@changesets/changelog-github': - specifier: ^0.7.0 - version: 0.7.0 - '@changesets/cli': - specifier: ^2.31.1 - version: 2.31.1(@types/node@25.2.3) + .: {} lib/changelog-emitter: dependencies: @@ -299,8 +292,6 @@ importers: specifier: 'catalog:' version: 8.61.1(eslint@10.5.0)(typescript@5.9.3) - lib/python-sdk: {} - lib/ts-sdk: dependencies: zod: @@ -840,67 +831,6 @@ packages: resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} engines: {node: '>=18'} - '@changesets/apply-release-plan@7.1.1': - resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} - - '@changesets/assemble-release-plan@6.0.10': - resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} - - '@changesets/changelog-git@0.2.1': - resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - - '@changesets/changelog-github@0.7.0': - resolution: {integrity: sha512-rBsbRvc4TVn+FvFnOVM3LxlFJfTXXCp8gfVJ+0BubxWNSVnLuAzowi5j+IEraLLP52w8AAs9QfKbPS3MMiXQJA==} - - '@changesets/cli@2.31.1': - resolution: {integrity: sha512-uO05WTcRBwuVOJVSW8Cmpqw6q0WDL53ajGCMyszutvOe5toOnunbpM4jZzf+qxBOz7i0AzopZ8diBuewjmF40w==} - hasBin: true - - '@changesets/config@3.1.4': - resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} - - '@changesets/errors@0.2.0': - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - - '@changesets/get-dependents-graph@2.1.4': - resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} - - '@changesets/get-github-info@0.8.0': - resolution: {integrity: sha512-cRnC+xdF0JIik7coko3iUP9qbnfi1iJQ3sAa6dE+Tx3+ET8bjFEm63PA4WEohgjYcmsOikPHWzPsMWWiZmntOQ==} - - '@changesets/get-release-plan@4.0.16': - resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} - - '@changesets/get-version-range-type@0.4.0': - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - - '@changesets/git@3.0.4': - resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} - - '@changesets/logger@0.1.1': - resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - - '@changesets/parse@0.4.3': - resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} - - '@changesets/pre@2.0.2': - resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - - '@changesets/read@0.6.7': - resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} - - '@changesets/should-skip-package@0.1.2': - resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} - - '@changesets/types@4.1.0': - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - - '@changesets/types@6.1.0': - resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} - - '@changesets/write@0.4.0': - resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@clack/core@1.4.1': resolution: {integrity: sha512-FILJa1gGKEFTGZAJE9RpVhrjKz3c3h4ar60dSv6cGuDqufQ84YEIS3GAGvZiN+H6yaLbbvTFNejjCC4tXpZEuw==} engines: {node: '>= 20.12.0'} @@ -1957,12 +1887,6 @@ packages: '@jsonforms/react': 3.8.0 react: ^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@manypkg/find-root@1.1.0': - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} - - '@manypkg/get-packages@1.1.3': - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} @@ -2590,9 +2514,6 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@22.20.0': resolution: {integrity: sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==} @@ -2975,10 +2896,6 @@ packages: resolution: {integrity: sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==} hasBin: true - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -3035,10 +2952,6 @@ packages: array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} @@ -3119,10 +3032,6 @@ packages: bcp-47@2.1.0: resolution: {integrity: sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==} - better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -3445,9 +3354,6 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - dataloader@1.4.0: - resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} - debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -3508,10 +3414,6 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -3533,10 +3435,6 @@ packages: resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - direction@2.0.1: resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} hasBin: true @@ -3557,10 +3455,6 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dotenv@8.6.0: - resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} - engines: {node: '>=10'} - drange@1.1.1: resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} engines: {node: '>=4'} @@ -3598,10 +3492,6 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -3810,9 +3700,6 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3889,10 +3776,6 @@ packages: resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3959,14 +3842,6 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -4031,17 +3906,10 @@ packages: resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} engines: {node: '>=18'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - h3@1.15.11: resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} @@ -4161,10 +4029,6 @@ packages: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - human-id@4.2.0: - resolution: {integrity: sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==} - hasBin: true - i18next@26.3.1: resolution: {integrity: sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==} peerDependencies: @@ -4283,10 +4147,6 @@ packages: resolution: {integrity: sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==} engines: {node: '>=20'} - is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - is-typed-array@1.1.15: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} @@ -4299,10 +4159,6 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -4333,10 +4189,6 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.15.0: - resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} - hasBin: true - js-yaml@4.3.0: resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} hasBin: true @@ -4383,9 +4235,6 @@ packages: jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} @@ -4483,10 +4332,6 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -4494,9 +4339,6 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -4782,10 +4624,6 @@ packages: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -4844,15 +4682,6 @@ packages: node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true @@ -4928,17 +4757,6 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -4947,18 +4765,10 @@ packages: resolution: {integrity: sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==} engines: {node: '>=20'} - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - p-queue@9.3.0: resolution: {integrity: sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==} engines: {node: '>=20'} @@ -4967,16 +4777,9 @@ packages: resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} engines: {node: '>=20'} - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-manager-detector@0.2.11: - resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} - package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} @@ -5030,10 +4833,6 @@ packages: path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -5055,10 +4854,6 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -5093,11 +4888,6 @@ packages: resolution: {integrity: sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw==} engines: {node: ^14.15.0 || >=16.0.0} - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - prettier@3.8.4: resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} engines: {node: '>=14'} @@ -5133,9 +4923,6 @@ packages: resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} engines: {node: '>=0.6'} - quansync@0.2.11: - resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -5230,10 +5017,6 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} - read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -5534,10 +5317,6 @@ packages: engines: {node: '>=20.19.5', npm: '>=10.8.2'} hasBin: true - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - smol-toml@1.6.1: resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} @@ -5560,9 +5339,6 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - spawndamnit@3.0.1: - resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -5612,10 +5388,6 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - strnum@2.3.0: resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} @@ -5686,10 +5458,6 @@ packages: temporal-spec@0.3.1: resolution: {integrity: sha512-B4TUhezh9knfSIMwt7RVggApDRJZo73uZdj8AacL2mZ8RP5KtLianh2MXxL06GN9ESYiIsiuoLQhgVfwe55Yhw==} - term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - terminal-link@5.0.0: resolution: {integrity: sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==} engines: {node: '>=20'} @@ -5735,9 +5503,6 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tree-sitter-json@0.24.8: resolution: {integrity: sha512-Tc9ZZYwHyWZ3Tt1VEw7Pa2scu1YO7/d2BCBbKTx5hXwig3UfdQjsOPkPyLpDJOn/m1UBEWYAtSdGAwCSyagBqQ==} peerDependencies: @@ -5894,10 +5659,6 @@ packages: unist-util-visit@5.1.0: resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -6219,12 +5980,6 @@ packages: web-tree-sitter@0.24.5: resolution: {integrity: sha512-+J/2VSHN8J47gQUAvF8KDadrfz6uFYVjxoxbKWDoXVsH2u7yLdarCnIURnrMA6uSRkgX3SdmqM5BOoQjPdSh5w==} - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-typed-array@1.1.22: resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} @@ -6802,164 +6557,6 @@ snapshots: dependencies: fontkitten: 1.0.3 - '@changesets/apply-release-plan@7.1.1': - dependencies: - '@changesets/config': 3.1.4 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.8.5 - - '@changesets/assemble-release-plan@6.0.10': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.8.5 - - '@changesets/changelog-git@0.2.1': - dependencies: - '@changesets/types': 6.1.0 - - '@changesets/changelog-github@0.7.0': - dependencies: - '@changesets/get-github-info': 0.8.0 - '@changesets/types': 6.1.0 - dotenv: 8.6.0 - transitivePeerDependencies: - - encoding - - '@changesets/cli@2.31.1(@types/node@25.2.3)': - dependencies: - '@changesets/apply-release-plan': 7.1.1 - '@changesets/assemble-release-plan': 6.0.10 - '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.4 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/get-release-plan': 4.0.16 - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.7 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@25.2.3) - '@manypkg/get-packages': 1.1.3 - ansi-colors: 4.1.3 - enquirer: 2.4.1 - fs-extra: 7.0.1 - mri: 1.2.0 - package-manager-detector: 0.2.11 - picocolors: 1.1.1 - resolve-from: 5.0.0 - semver: 7.8.5 - spawndamnit: 3.0.1 - term-size: 2.2.1 - transitivePeerDependencies: - - '@types/node' - - '@changesets/config@3.1.4': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/logger': 0.1.1 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 - - '@changesets/errors@0.2.0': - dependencies: - extendable-error: 0.1.7 - - '@changesets/get-dependents-graph@2.1.4': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.1 - semver: 7.8.5 - - '@changesets/get-github-info@0.8.0': - dependencies: - dataloader: 1.4.0 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - '@changesets/get-release-plan@4.0.16': - dependencies: - '@changesets/assemble-release-plan': 6.0.10 - '@changesets/config': 3.1.4 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.7 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/get-version-range-type@0.4.0': {} - - '@changesets/git@3.0.4': - dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 3.0.1 - - '@changesets/logger@0.1.1': - dependencies: - picocolors: 1.1.1 - - '@changesets/parse@0.4.3': - dependencies: - '@changesets/types': 6.1.0 - js-yaml: 4.3.0 - - '@changesets/pre@2.0.2': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - - '@changesets/read@0.6.7': - dependencies: - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.3 - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.1 - - '@changesets/should-skip-package@0.1.2': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/types@4.1.0': {} - - '@changesets/types@6.1.0': {} - - '@changesets/write@0.4.0': - dependencies: - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - human-id: 4.2.0 - prettier: 2.8.8 - '@clack/core@1.4.1': dependencies: fast-wrap-ansi: 0.2.2 @@ -7674,13 +7271,6 @@ snapshots: optionalDependencies: '@types/node': 22.20.0 - '@inquirer/external-editor@1.0.3(@types/node@25.2.3)': - dependencies: - chardet: 2.2.0 - iconv-lite: 0.7.3 - optionalDependencies: - '@types/node': 25.2.3 - '@inquirer/external-editor@3.0.3(@types/node@22.20.0)': dependencies: chardet: 2.2.0 @@ -7816,22 +7406,6 @@ snapshots: lodash: 4.18.1 react: 18.3.1 - '@manypkg/find-root@1.1.0': - dependencies: - '@babel/runtime': 7.29.7 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - - '@manypkg/get-packages@1.1.3': - dependencies: - '@babel/runtime': 7.29.7 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - '@mdx-js/mdx@3.1.1': dependencies: '@types/estree': 1.0.9 @@ -8707,8 +8281,6 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/node@12.20.55': {} - '@types/node@22.20.0': dependencies: undici-types: 6.21.0 @@ -9169,8 +8741,6 @@ snapshots: dependencies: process-ancestry: 0.1.0 - ansi-colors@4.1.3: {} - ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -9214,8 +8784,6 @@ snapshots: array-timsort@1.0.3: {} - array-union@2.1.0: {} - asap@2.0.6: {} assertion-error@2.0.1: {} @@ -9389,10 +8957,6 @@ snapshots: is-alphanumerical: 2.0.1 is-decimal: 2.0.1 - better-path-resolve@1.0.0: - dependencies: - is-windows: 1.0.2 - bl@4.1.0: dependencies: buffer: 5.7.1 @@ -9748,8 +9312,6 @@ snapshots: csstype@3.2.3: {} - dataloader@1.4.0: {} - debug@2.6.9: dependencies: ms: 2.0.0 @@ -9790,8 +9352,6 @@ snapshots: destroy@1.2.0: {} - detect-indent@6.1.0: {} - detect-libc@2.1.2: {} devalue@5.8.1: {} @@ -9809,10 +9369,6 @@ snapshots: diff@8.0.4: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - direction@2.0.1: {} dom-serializer@2.0.0: @@ -9837,8 +9393,6 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 - dotenv@8.6.0: {} - drange@1.1.1: {} dset@3.1.4: {} @@ -9868,11 +9422,6 @@ snapshots: encodeurl@2.0.0: {} - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - entities@4.5.0: {} entities@6.0.1: {} @@ -10154,8 +9703,6 @@ snapshots: extend@3.0.2: {} - extendable-error@0.1.7: {} - fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -10239,11 +9786,6 @@ snapshots: transitivePeerDependencies: - supports-color - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -10304,18 +9846,6 @@ snapshots: fresh@0.5.2: {} - fs-extra@7.0.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - fsevents@2.3.3: optional: true @@ -10378,19 +9908,8 @@ snapshots: globals@17.7.0: {} - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - gopd@1.2.0: {} - graceful-fs@4.2.11: {} - h3@1.15.11: dependencies: cookie-es: 1.2.3 @@ -10643,8 +10162,6 @@ snapshots: transitivePeerDependencies: - supports-color - human-id@4.2.0: {} - i18next@26.3.1(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -10738,10 +10255,6 @@ snapshots: is-safe-filename@0.1.1: {} - is-subdir@1.2.0: - dependencies: - better-path-resolve: 1.0.0 - is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.22 @@ -10750,8 +10263,6 @@ snapshots: is-unicode-supported@2.1.0: {} - is-windows@1.0.2: {} - isarray@2.0.5: {} isexe@2.0.0: {} @@ -10781,11 +10292,6 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@3.15.0: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - js-yaml@4.3.0: dependencies: argparse: 2.0.1 @@ -10822,10 +10328,6 @@ snapshots: jsonc-parser@3.3.1: {} - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - jsonpointer@5.0.1: {} keyv@4.5.4: @@ -10892,18 +10394,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash.debounce@4.0.8: {} - lodash.startcase@4.4.0: {} - lodash@4.18.1: {} log-symbols@4.1.0: @@ -11468,8 +10964,6 @@ snapshots: dependencies: minipass: 7.1.3 - mri@1.2.0: {} - mrmime@2.0.1: {} ms@2.0.0: {} @@ -11505,10 +10999,6 @@ snapshots: node-fetch-native@1.6.7: {} - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - node-gyp-build@4.8.4: optional: true @@ -11593,16 +11083,6 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - outdent@0.5.0: {} - - p-filter@2.1.0: - dependencies: - p-map: 2.1.0 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -11611,16 +11091,10 @@ snapshots: dependencies: yocto-queue: 1.2.2 - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 - p-map@2.1.0: {} - p-queue@9.3.0: dependencies: eventemitter3: 5.0.4 @@ -11628,14 +11102,8 @@ snapshots: p-timeout@7.0.1: {} - p-try@2.2.0: {} - package-json-from-dist@1.0.1: {} - package-manager-detector@0.2.11: - dependencies: - quansync: 0.2.11 - package-manager-detector@1.6.0: {} pagefind@1.5.2: @@ -11698,8 +11166,6 @@ snapshots: path-to-regexp@6.3.0: {} - path-type@4.0.0: {} - pathe@2.0.3: {} piccolore@0.1.3: {} @@ -11712,8 +11178,6 @@ snapshots: picomatch@4.0.5: {} - pify@4.0.1: {} - possible-typed-array-names@1.1.0: {} postcss-nested@6.2.0(postcss@8.5.22): @@ -11749,8 +11213,6 @@ snapshots: prettier: 3.8.4 sass-formatter: 0.7.9 - prettier@2.8.8: {} - prettier@3.8.4: {} prismjs@1.30.0: {} @@ -11778,8 +11240,6 @@ snapshots: dependencies: side-channel: 1.1.0 - quansync@0.2.11: {} - querystringify@2.2.0: {} queue-microtask@1.2.3: {} @@ -11870,13 +11330,6 @@ snapshots: dependencies: loose-envify: 1.4.0 - read-yaml-file@1.1.0: - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.15.0 - pify: 4.0.1 - strip-bom: 3.0.0 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -12378,8 +11831,6 @@ snapshots: arg: 5.0.2 sax: 1.6.0 - slash@3.0.0: {} - smol-toml@1.6.1: {} source-map-js@1.2.1: {} @@ -12395,11 +11846,6 @@ snapshots: space-separated-tokens@2.0.2: {} - spawndamnit@3.0.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - sprintf-js@1.0.3: {} stackback@0.0.2: {} @@ -12462,8 +11908,6 @@ snapshots: dependencies: ansi-regex: 6.2.2 - strip-bom@3.0.0: {} - strnum@2.3.0: {} style-to-js@1.1.21: @@ -12637,8 +12081,6 @@ snapshots: temporal-spec@0.3.1: {} - term-size@2.2.1: {} - terminal-link@5.0.0: dependencies: ansi-escapes: 7.3.0 @@ -12678,8 +12120,6 @@ snapshots: toidentifier@1.0.1: {} - tr46@0.0.3: {} - tree-sitter-json@0.24.8(tree-sitter@0.21.1): dependencies: node-addon-api: 8.9.0 @@ -12860,8 +12300,6 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - universalify@0.1.2: {} - unpipe@1.0.0: {} unraw@3.0.0: {} @@ -13088,13 +12526,6 @@ snapshots: web-tree-sitter@0.24.5: optional: true - webidl-conversions@3.0.1: {} - - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - which-typed-array@1.1.22: dependencies: available-typed-arrays: 1.0.7 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b2f58dd39..0d965d14f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,6 @@ packages: - lib/cli - lib/core - - lib/python-sdk - lib/changelog-emitter - lib/ts-sdk - website diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 000000000..20f8f2f3a --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "node", + "bump-minor-pre-major": true, + "include-component-in-tag": true, + "tag-separator": "@", + "include-v-in-tag": false, + "separate-pull-requests": true, + "changelog-sections": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "docs", "section": "Documentation" }, + { "type": "refactor", "section": "Refactoring" }, + { "type": "build", "section": "Build System" }, + { "type": "chore", "section": "Miscellaneous", "hidden": true }, + { "type": "ci", "section": "Miscellaneous", "hidden": true }, + { "type": "test", "section": "Miscellaneous", "hidden": true } + ], + "packages": { + "lib/core": { + "package-name": "@common-grants/core", + "component": "@common-grants/core", + "changelog-path": "CHANGELOG.md" + }, + "lib/cli": { + "package-name": "@common-grants/cli", + "component": "@common-grants/cli", + "changelog-path": "CHANGELOG.md" + }, + "lib/ts-sdk": { + "package-name": "@common-grants/sdk", + "component": "@common-grants/sdk", + "changelog-path": "CHANGELOG.md" + }, + "lib/python-sdk": { + "release-type": "python", + "package-name": "common-grants-sdk", + "component": "common-grants-sdk", + "changelog-path": "CHANGELOG.md", + "extra-files": ["common_grants_sdk/__init__.py"] + } + } +} From ac57329cd088d403c3644c19eb71302dad08cea9 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Thu, 30 Jul 2026 10:12:08 -0700 Subject: [PATCH 02/13] docs: update release process docs and supersede ADR 0010 - ADR 0027 records the release-please decision; ADR 0010 gets a superseded banner - lib/README.md, per-package DEVELOPMENT.md runbooks, DEPENDENCY_MANAGEMENT.md, and CONTRIBUTING.md describe the conventional-commit + Release PR flow --- CONTRIBUTING.md | 14 ++ DEPENDENCY_MANAGEMENT.md | 12 +- lib/README.md | 141 ++++++----------- lib/cli/DEVELOPMENT.md | 9 +- lib/core/DEVELOPMENT.md | 17 +- lib/python-sdk/DEVELOPMENT.md | 9 +- .../governance/adr/0010-release-workflow.md | 4 + .../governance/adr/0027-release-please.md | 149 ++++++++++++++++++ 8 files changed, 237 insertions(+), 118 deletions(-) create mode 100644 website/src/content/docs/governance/adr/0027-release-please.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fef7d02fd..d9388493b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -123,6 +123,20 @@ If you've implemented a new feature, fixed a bug, or made some documentation cle 5. **Submit a pull request:** When you're ready to make your contribution, [submit a pull request against the upstream repo](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) and fill out the PR template sections so that project maintainers can begin to review your contributions. 6. **Fix requested changes:** As maintainers review your proposed contribution, they may request specific changes. If so, simply add and commit those changes to the feature branch directly and they'll automatically show up on the PR. +#### PR title format + +PR titles must follow the [Conventional Commits](https://www.conventionalcommits.org/) format — `type(scope): description` — and a CI check enforces this. PRs are squash-merged with the PR title as the commit subject, and that history drives releases: [release-please](https://github.com/googleapis/release-please) reads the commit type to decide version bumps and changelog entries for the published packages. + +| Type | Effect on the touched package(s) | Example | +|------|----------------------------------|---------| +| `fix` | Patch release | `fix(ts-sdk): preserve plain dates in JSON` | +| `feat` | Minor release | `feat(core): add award schemas` | +| `feat!` or `fix!` (or a `BREAKING CHANGE:` footer) | Breaking release (minor while pre-1.0) | `feat(cli)!: drop Node 20 support` | +| `docs`, `refactor`, `build` | No release by itself; appears in the next release's changelog | `docs(website): clarify filter usage` | +| `chore`, `ci`, `test` | No release; hidden from changelogs | `chore(deps): bump the runtime group` | + +The scope is free-form; use the package or area you touched (`core`, `cli`, `ts-sdk`, `py-sdk`, `website`, `deps`). See [lib/README.md](lib/README.md) for how releases are cut from this history. + ## Getting started **CommonGrants resources** diff --git a/DEPENDENCY_MANAGEMENT.md b/DEPENDENCY_MANAGEMENT.md index d02212cd5..3c1467584 100644 --- a/DEPENDENCY_MANAGEMENT.md +++ b/DEPENDENCY_MANAGEMENT.md @@ -84,20 +84,20 @@ The `website` catalog has its own `vitest` version (`^4.x`). The update script i ./.github/scripts/update-catalog-deps.sh ``` -## Changeset expectations +## Release expectations -Most dependency PRs don't need a changeset. You only need one when a **production dependency changes in a published package**. +Version bumps are driven by [release-please](https://github.com/googleapis/release-please) from the merged PR's conventional-commit title — there is no separate file to add. What matters is choosing the right type: `chore(deps)` does **not** trigger a release, while `fix(deps)` (or `fix()`) does. -| Package | Published to | Needs changeset when... | +Most dependency PRs should stay `chore(deps)`. Use a releasing type only when a **production dependency changes in a published package**: + +| Package | Published to | Needs a releasing title when... | |---------|-------------|------------------------| | `lib/core` (`@common-grants/core`) | npm | TypeSpec peer dep ranges change | | `lib/cli` (`@common-grants/cli`) | npm | Runtime dep versions change | | `lib/ts-sdk` (`@common-grants/sdk`) | npm | Runtime dep versions change | | `lib/python-sdk` (`common-grants-sdk`) | PyPI | Runtime dep versions change | -Dev-only dep bumps (vitest, eslint, type definitions, etc.) don't need changesets. When in doubt: if the dep appears in `dependencies` or `peerDependencies` in the package's `package.json`, add a changeset. If it's in `devDependencies`, skip it. - -Run `pnpm changeset` from the repo root to create one. +Dev-only dep bumps (vitest, eslint, type definitions, etc.) don't warrant a release. When in doubt: if the dep appears in `dependencies` or `peerDependencies` in the package's manifest, retitle the PR `fix(): ...` before merging. If it's in `devDependencies`, keep `chore(deps)`. ## Catalog validation diff --git a/lib/README.md b/lib/README.md index 68ee23b7b..e4e23b842 100644 --- a/lib/README.md +++ b/lib/README.md @@ -2,7 +2,7 @@ This directory contains independently versioned packages for the Simpler Grants Protocol, supporting both Python and Node.js. -Versioning is managed via [Changesets](https://github.com/changesets/changesets), using native support for Node.js and a Poetry-based workflow for Python packages. +Versioning is managed via [release-please](https://github.com/googleapis/release-please), driven by the conventional-commit history on `main`. There is no separate versioning file to author: the squashed commit title of each merged PR determines whether and how each package's version bumps. --- @@ -10,11 +10,11 @@ Versioning is managed via [Changesets](https://github.com/changesets/changesets) This setup enables: -- Independent versioning of Python and Node.js packages -- Semantic version tracking via Changesets -- Automated changelog generation -- Git tagging per package -- Optional GitHub release creation from tags +- Independent versioning of Python and Node.js packages +- Version bumps derived from conventional commits (no manual bookkeeping) +- Automated changelog generation +- Git tagging and GitHub release creation per package +- Automated publishing to npm / PyPI when a Release PR is merged --- @@ -22,125 +22,82 @@ This setup enables: ``` lib/ -├── core/ # Node package stub +├── core/ # Node package │ ├── package.json -│ └── index.js -├── cli/ # Node package stub +│ └── CHANGELOG.md # Auto-maintained by release-please +├── cli/ # Node package │ ├── package.json -│ └── index.js +│ └── CHANGELOG.md +├── ts-sdk/ # Node package +│ ├── package.json +│ └── CHANGELOG.md └── python-sdk/ # Python package - ├── package.json # Required for Changesets detection - ├── pyproject.toml # Python project metadata - ├── CHANGELOG.md # Auto-maintained by Changesets + ├── pyproject.toml # Python project metadata (owns the version) + ├── CHANGELOG.md └── common_grants_sdk/ # SDK source code ``` --- -## Versioning Overview - -### Node Packages - -- Versioning is handled automatically by Changesets when `pnpm changeset version` is run -- Applies the following changes: - - Bumps version in `package.json` - - Updates or creates `CHANGELOG.md` - - Creates a Git tag (e.g. `core@1.0.0`) +## How releases work -### Python Packages - -- Versioning is handled by CI using Poetry, based on `.changeset/*.md` content -- Applies the following changes: - - Bumps version in `pyproject.toml` - - Bumps version in `package.json` - - Updates or creates `CHANGELOG.md` - - Creates a Git tag (e.g. `common_grants_sdk@0.3.1`) - ---- - -## Developer Instructions - -### Step 1: Make Your Code Changes +### Step 1: Make your code changes Modify the appropriate files under any `lib/` package. -### Step 2: Generate a Changeset - -Run this from the root directory `simpler-grants-protocol/`: +### Step 2: Title your PR with a conventional commit -```bash -pnpm changeset -``` +The PR title becomes the squashed commit subject on `main`, which release-please parses. The type determines the version bump for every package whose files the PR touches: -Follow the CLI prompts: +| PR title | Bump | +|----------|------| +| `fix(core): handle empty filter values` | patch | +| `feat(ts-sdk): add transform helpers` | minor | +| `feat(cli)!: drop Node 20 support` | major (minor while packages are pre-1.0) | +| `chore: ...`, `ci: ...`, `test: ...` | no release | +| `docs: ...`, `refactor: ...`, `build: ...` | no bump by itself; listed in the changelog of the next release | -- Select the package(s) affected (e.g. `common_grants_sdk`) -- Choose version bump type (patch, minor, major) -- Provide a short summary +A `BREAKING CHANGE:` footer in the squashed commit body also triggers a breaking bump. -This will create a Markdown file in `.changeset/` like: +### Step 3: Merge the PR -```markdown ---- -"common_grants_sdk": patch ---- - -Fix logic bug in base class method -``` +Once the PR is merged into `main`, the `release-please.yml` workflow updates (or opens) a **Release PR** for each affected package. The Release PR accumulates every releasable commit since the package's last release, and contains the version bump (`package.json` for Node packages, `pyproject.toml` and `common_grants_sdk/__init__.py` for Python) plus the generated `CHANGELOG.md` entry. -### Step 3: Commit the Changes +Nothing is published at this point — merges can stack in the Release PR until the team is ready to ship. -Include code changes and the `.changeset/*.md` file in a PR. +### Step 4: Merge the Release PR to publish -### Step 4: Merge the PR +Merging a package's Release PR: -Once the PR is merged into `main`, the `ci-package-bump-version.yml` GitHub Action will: +1. Tags `main` with the new version (e.g. `@common-grants/core@0.4.0`, `common-grants-sdk@0.9.0`) +2. Creates the GitHub release with the changelog entry as its notes +3. Triggers the package's deploy workflow, publishing to npm (Node packages) or PyPI (Python SDK) -- **Python Packages** - - Determine bump type from `.changeset/*.md` - - Use Poetry to bump the version and update files - - Create a Git tag (e.g. `common_grants_sdk@0.3.1`) +### Re-running a failed publish -- **Node Packages** - - Run `pnpm changeset version` - - Apply version and changelog updates - - Create Git tags (e.g. `core@1.0.0`) - -### Step 5: (Optional) Trigger GitHub Release from Tag - -To manually generate a GitHub Release from a tag: - -1. Go to the **Actions** tab on GitHub -2. Select **Create GitHub Release from Tag** -3. Click **Run workflow** -4. Enter or select a tag (e.g. `common_grants_sdk@0.3.1`) -5. Click **Run workflow** to generate the release +The per-package deploy workflows (`cd-deploy-lib-*.yml`) keep a `workflow_dispatch` trigger. To retry a publish for an existing tag, run the matching workflow from the **Actions** tab and enter the release tag. --- ## Manual Validation Checklist -After a PR is merged and the `version.yml` workflow runs: - -1. **Confirm version bump** - - For Python packages: check `pyproject.toml` and `package.json` under `lib/python-sdk/` - - For Node packages: check `package.json` under each affected package (e.g. `lib/core/`, `lib/cli/`) +After merging a Release PR: -2. **Confirm changelog** - - Open the `CHANGELOG.md` for each affected package - - Example path: `lib/python-sdk/CHANGELOG.md` - - Ensure the top entry reflects the correct version and summary +1. **Confirm the tag and GitHub release** + - Go to GitHub > Code > Tags and verify the new per-package tag exists + - Verify a GitHub release with changelog notes was created for that tag -3. **Confirm Git tag** - - Go to GitHub > Code > Tags - - Verify that a tag was created for each updated package (e.g. `common_grants_sdk@0.3.1`, `core@1.0.0`) +2. **Confirm the published artifact** + - Node packages: check the new version on npm (`@common-grants/core`, `@common-grants/cli`, `@common-grants/sdk`) + - Python SDK: check the new version on PyPI (`common-grants-sdk`) -4. **Confirm version commit** - - Check the commit history for a message like `chore: bump version [skip ci]` from `github-actions[bot]` +3. **Confirm changelog** + - Open the `CHANGELOG.md` for the released package and ensure the top entry reflects the new version --- ## Notes -- Python packages must include a `package.json` file to satisfy Changesets requirements -- All version bumps are driven by the existence and content of `.changeset/*.md` files +- Per-package versions are tracked in `.release-please-manifest.json`; release-please config lives in `release-please-config.json` (both at the repo root). +- The Python SDK's version lives only in `pyproject.toml` (mirrored into `common_grants_sdk/__init__.py` by release-please); it has no `package.json`. +- `lib/changelog-emitter` is private and excluded from releases. diff --git a/lib/cli/DEVELOPMENT.md b/lib/cli/DEVELOPMENT.md index 868f5058b..05abfbfcc 100644 --- a/lib/cli/DEVELOPMENT.md +++ b/lib/cli/DEVELOPMENT.md @@ -37,15 +37,14 @@ Steps to follow when preparing a new release of the CommonGrants core library. - [ ] The [man page](./man/cg.1) has been updated to include them - [ ] `npm pack` has been run, the package has been installed in a new directory outside this repo, and all the commands listed in the [README](README.md#usage) have been tested - [ ] (Optional) A pre-release of the new version with an `alpha`, `beta`, or `rc` tag has been manually published to test the installation from npm - - [ ] A [changeset](../README.md#step-2-generate-a-changeset) has been created with the correct revision type (MAJOR, MINOR, PATCH) and a meaningful summary of the changes made in this version + - [ ] The PR title uses the [conventional commit type](../README.md#step-2-title-your-pr-with-a-conventional-commit) matching the intended release impact (`fix` = patch, `feat` = minor, `!` = breaking) and meaningfully summarizes the change - [ ] After merging: - - [ ] The [CI - Bump Version & Create Tag](https://github.com/HHS/simpler-grants-protocol/actions/workflows/ci-bump-version.yml) GitHub action ran successfully - - [ ] A new `@common-grants/cli` [tag](https://github.com/HHS/simpler-grants-protocol/tags) was created + - [ ] The [Release Please](https://github.com/HHS/simpler-grants-protocol/actions/workflows/release-please.yml) workflow ran and updated (or opened) the `@common-grants/cli` Release PR ### Release checklist -- [ ] [CD - Deploy CLI Package to npm](https://github.com/HHS/simpler-grants-protocol/actions/workflows/cd-deploy-lib-cli.yml) has been manually triggered with the new `@common-grants/cli` tag -- [ ] A new [GitHub release](https://github.com/HHS/simpler-grants-protocol/releases) has been created, and the auto-generated release notes are accurate +- [ ] The `@common-grants/cli` Release PR has been merged — this tags the release, creates the [GitHub release](https://github.com/HHS/simpler-grants-protocol/releases), and publishes to npm automatically +- [ ] The auto-generated release notes are accurate - [ ] The new version appears on [npm](https://www.npmjs.com/package/@common-grants/cli) ### Post-release checklist diff --git a/lib/core/DEVELOPMENT.md b/lib/core/DEVELOPMENT.md index 560b1322f..f5c385800 100644 --- a/lib/core/DEVELOPMENT.md +++ b/lib/core/DEVELOPMENT.md @@ -40,22 +40,19 @@ Steps to follow when preparing a new release of the CommonGrants core library. - [ ] Before merging: - [ ] (Optional) A pre-release of the new version with an `alpha`, `beta`, or `rc` tag has been manually published to test the installation from npm - - [ ] A [changeset](../README.md#step-2-generate-a-changeset) has been created with the correct revision type (MAJOR, MINOR, PATCH) and a meaningful summary of the changes made in this version + - [ ] The PR title uses the [conventional commit type](../README.md#step-2-title-your-pr-with-a-conventional-commit) matching the intended release impact (`fix` = patch, `feat` = minor, `!` = breaking) and meaningfully summarizes the change + - [ ] If the change affects the CLI's vendored spec output, the regenerated [`lib/cli/lib/openapi/`](../cli/lib/openapi/) files are included in the PR — touching those paths is what carries the change into the next CLI release - [ ] After merging: - - [ ] The [CI - Bump Version & Create Tag](https://github.com/HHS/simpler-grants-protocol/actions/workflows/ci-bump-version.yml) GitHub action ran successfully - - [ ] A new `@common-grants/core` [tag](https://github.com/HHS/simpler-grants-protocol/tags) was created for this new version - - [ ] A new `@common-grants/cli` [tag](https://github.com/HHS/simpler-grants-protocol/tags) was created with a patch update + - [ ] The [Release Please](https://github.com/HHS/simpler-grants-protocol/actions/workflows/release-please.yml) workflow ran and updated (or opened) the `@common-grants/core` Release PR — and the `@common-grants/cli` Release PR if CLI files were touched ### Release checklist - [ ] For the core library release: - - [ ] [CD - Deploy Core Package to npm](https://github.com/HHS/simpler-grants-protocol/actions/workflows/cd-deploy-lib-core.yml) has been manually triggered with the new `@common-grants/core` tag - - [ ] A new [GitHub release](https://github.com/HHS/simpler-grants-protocol/releases) has been created, and the auto-generated release notes are accurate. + - [ ] The `@common-grants/core` Release PR has been merged — this tags the release, creates the [GitHub release](https://github.com/HHS/simpler-grants-protocol/releases), and publishes to npm automatically + - [ ] The auto-generated release notes are accurate - [ ] The new version appears on [npm](https://www.npmjs.com/package/@common-grants/core) -- [ ] For CLI patch release (after the core library is released): - - [ ] [CD - Deploy CLI Package to npm](https://github.com/HHS/simpler-grants-protocol/actions/workflows/cd-deploy-lib-cli.yml) has been manually triggered with the new `@common-grants/cli` tag - - [ ] A new [GitHub release](https://github.com/HHS/simpler-grants-protocol/releases) has been created, and the auto-generated release notes are accurate - - [ ] The new version appears on [npm](https://www.npmjs.com/package/@common-grants/cli) +- [ ] For a CLI release (after the core library is released): + - [ ] The `@common-grants/cli` Release PR has been merged and the new version appears on [npm](https://www.npmjs.com/package/@common-grants/cli) ### Post-release checklist diff --git a/lib/python-sdk/DEVELOPMENT.md b/lib/python-sdk/DEVELOPMENT.md index f06009d64..1c433ec3f 100644 --- a/lib/python-sdk/DEVELOPMENT.md +++ b/lib/python-sdk/DEVELOPMENT.md @@ -66,15 +66,14 @@ Steps to follow when preparing a new release of the CommonGrants Python SDK libr - [ ] The package has been installed in a new directory, and all examples listed in the [README](README.md) have been tested - [ ] (Optional) A pre-release of the new version with an `alpha`, `beta`, or `rc` tag has been manually published to test the installation from PyPI - [ ] An API using this SDK passes the CommonGrants CLI `check spec` command - - [ ] A [changeset](../README.md#step-2-generate-a-changeset) has been created with the correct revision type (MAJOR, MINOR, PATCH) and a meaningful summary of the changes made in this version + - [ ] The PR title uses the [conventional commit type](../README.md#step-2-title-your-pr-with-a-conventional-commit) matching the intended release impact (`fix` = patch, `feat` = minor, `!` = breaking) and meaningfully summarizes the change - [ ] After merging: - - [ ] The [CI - Bump Version & Create Tag](https://github.com/HHS/simpler-grants-protocol/actions/workflows/ci-bump-version.yml) GitHub action ran successfully - - [ ] A new `common-grants-sdk` [tag](https://github.com/HHS/simpler-grants-protocol/tags) was created for this new version + - [ ] The [Release Please](https://github.com/HHS/simpler-grants-protocol/actions/workflows/release-please.yml) workflow ran and updated (or opened) the `common-grants-sdk` Release PR ### Release checklist -- [ ] [CD - Deploy Python Package to npm](https://github.com/HHS/simpler-grants-protocol/actions/workflows/cd-deploy-lib-pysdk.yml) has been manually triggered with the new `common-grants-sdk` tag -- [ ] A new [GitHub release](https://github.com/HHS/simpler-grants-protocol/releases) has been created, and the auto-generated release notes are accurate. +- [ ] The `common-grants-sdk` Release PR has been merged — this tags the release, creates the [GitHub release](https://github.com/HHS/simpler-grants-protocol/releases), and publishes to PyPI automatically +- [ ] The auto-generated release notes are accurate - [ ] The new version appears on [PyPI](https://pypi.org/project/common-grants-sdk/) ### Post-release checklist diff --git a/website/src/content/docs/governance/adr/0010-release-workflow.md b/website/src/content/docs/governance/adr/0010-release-workflow.md index 324ea78a1..47a893ad2 100644 --- a/website/src/content/docs/governance/adr/0010-release-workflow.md +++ b/website/src/content/docs/governance/adr/0010-release-workflow.md @@ -3,6 +3,10 @@ title: Release workflow description: Records decision to adopt a CI/CD workflow that prioritizes pre-releases initially, and then stable releases using Changesets. --- +:::caution[Superseded] +This ADR has been superseded by [ADR 0027: Release automation with release-please](/governance/adr/0027-release-please/), which replaces Changesets with release-please for versioning and releasing the monorepo's published packages. +::: + The CommonGrants codebase is a monorepo containing multiple interrelated libraries, including the core TypeSpec library, CLI tools, and other packages. We need a strategy for publishing these libraries to `npm` (and other package managers) that supports both releases and pre-releases effectively, while ensuring transparency and minimizing complexity. diff --git a/website/src/content/docs/governance/adr/0027-release-please.md b/website/src/content/docs/governance/adr/0027-release-please.md new file mode 100644 index 000000000..b611f47b5 --- /dev/null +++ b/website/src/content/docs/governance/adr/0027-release-please.md @@ -0,0 +1,149 @@ +--- +title: Release automation with release-please +description: Records the decision to replace Changesets with release-please for versioning and releasing the monorepo's published packages. +--- + +The CommonGrants monorepo publishes four packages — `@common-grants/core`, `@common-grants/cli`, and `@common-grants/sdk` to npm, and `common-grants-sdk` to PyPI. [ADR 0010](/governance/adr/0010-release-workflow/) chose Changesets to version and release them independently. + +In practice, the Changesets-based workflow developed several recurring problems: + +- Contributors must remember to author a `.changeset/*.md` file for every release-worthy change; a forgotten file ships the change silently unversioned. +- The version-bump workflow runs the moment any changeset lands on `main`, so the team cannot stack several merged changes into one release. +- The bump commit is pushed directly to `main` by CI, which requires keeping branch protections disabled. +- Changesets is a Node-only tool: the Python SDK needs a vestigial `package.json` and a shell script that greps changeset files to decide the Poetry version bump. +- Publishing is a separate, manually triggered step that someone must remember to run after each version bump. + +This ADR supersedes ADR 0010's choice of Changesets. + +## Decision + +We will adopt [release-please](https://github.com/googleapis/release-please) in manifest mode for the four published packages. Version bumps and changelog entries are derived from the conventional-commit history on `main` (the squashed PR titles). Each package gets its own Release PR that accumulates changes until the team merges it; merging tags the release, creates the GitHub release, and triggers publishing automatically. + +### Positive consequences + +- The commit history is the release metadata — there is no separate file for contributors to forget. +- Multiple merged changes stack in an open Release PR and ship only when it is merged, so the team controls release timing. +- Version bumps land through a normal PR, so branch protections on `main` can be re-enabled. +- The Python SDK uses `release-type: python` against the real `pyproject.toml`; the vestigial `package.json` and shell-parsing step are removed. +- Merging a Release PR publishes automatically — no separate manual deploy step to remember. +- Release notes are generated from PR titles, reading like a changelog rather than a list of changeset summaries. + +### Negative consequences + +- Conventional-commit PR titles become mandatory and release-facing; a mistyped title puts a change in the wrong category (or omits it entirely), so a CI title check is required. +- Contributors must know which commit types map to which version bumps. +- Commits that do not follow the conventional format are invisible to release-please, so changelog entries for the period before adoption may under-report. +- A commit's type applies to every package whose files it touches; PRs that span packages need deliberate scoping. + +## Criteria + +- No release step that depends on contributors remembering a separate artifact. +- The team controls when releases ship, independent of when changes merge. +- Branch protections on `main` stay enabled. +- First-class support for the Python package. +- Automated changelogs, tags, and GitHub releases. +- Publishing triggered by the release step itself. + +## Options considered + +- **Option 1:** Keep the current Changesets workflow. +- **Option 2:** Keep Changesets, but adopt the official `changesets/action` Version-PR mode. +- **Option 3:** Adopt release-please in manifest mode. + +## Evaluation + +### Side-by-side + +- ✅ Criterion met +- ❌ Criterion not met +- 🟡 Partially met or unsure + +| Criteria | Option 1 | Option 2 | Option 3 | +| --------------------------------- | :------: | :------: | :------: | +| No forgettable release artifact | ❌ | ❌ | ✅ | +| Team-controlled release timing | ❌ | ✅ | ✅ | +| Branch protections stay enabled | ❌ | ✅ | ✅ | +| First-class Python support | ❌ | ❌ | ✅ | +| Automated changelogs and releases | 🟡 | 🟡 | ✅ | +| Publish triggered by release step | ❌ | ✅ | ✅ | +| No new contributor conventions | ✅ | ✅ | ❌ | + +### Option 1: Keep the current Changesets workflow + +:::note[Bottom line] +Option 1 is best if: + +- We want zero migration effort and no new contributor conventions. +- We can live with forgotten changesets, immediate bumps, and disabled branch protections. + ::: + +#### How it works + +- **Summary:** The status quo. Contributors author `.changeset/*.md` files; a CI workflow detects them on `main`, bumps versions (via `pnpm changeset version` for Node, a shell script + Poetry for Python), commits directly to `main`, and tags. Publishing is a separate manually dispatched workflow per package. + +#### Tradeoffs + +- **Pros** + - No migration work; the team already knows the flow. + - Bump type is declared explicitly per change, not inferred from commit type. +- **Cons** + - A forgotten changeset ships a change unversioned, with no structural backstop. + - Versions bump as soon as one changeset lands — no batching. + - CI pushes to `main` force branch protections off. + - Python support is bolted on (vestigial `package.json`, shell parsing). + - Publishing requires a separate manual step. + +### Option 2: Changesets with the official Version-PR mode + +:::note[Bottom line] +Option 2 is best if: + +- We want batching and PR-based bumps with the smallest change to contributor habits. +- We accept that forgotten changesets and second-class Python support are inherent to Changesets. + ::: + +#### How it works + +- **Summary:** Replace the custom bump workflow with the official [`changesets/action`](https://github.com/changesets/action), which maintains a "Version Packages" PR that accumulates pending changesets. Merging that PR applies the bumps and can trigger publishing. + +#### Tradeoffs + +- **Pros** + - Batching and team-controlled timing, like release-please. + - Bumps land via a PR, so branch protections can be re-enabled. + - Contributor-facing workflow (authoring changesets) is unchanged. +- **Cons** + - The forgotten-changeset failure mode is unchanged — it is inherent to the design. + - Python stays second-class: the vestigial `package.json` and custom bump script remain. + - Still a single combined Version PR for all Node packages rather than one per package. + +### Option 3: Adopt release-please in manifest mode + +:::note[Bottom line] +Option 3 is best if: + +- We want the commit history itself to drive releases, with no separate artifact to forget. +- We are willing to adopt conventional-commit PR titles and enforce them in CI. + ::: + +#### How it works + +- **Summary:** A GitHub workflow runs [release-please](https://github.com/googleapis/release-please) on every push to `main`. It parses conventional commits, attributes them to packages by the paths they touch, and maintains one Release PR per package (`separate-pull-requests`). Merging a Release PR bumps the version (`package.json` or `pyproject.toml`), updates the changelog, tags with the existing `name@version` format, creates the GitHub release, and fans out to the per-package publish workflows. +- **Common workflows:** + 1. **Regular change:** Merge a PR titled `fix(ts-sdk): ...` or `feat(core): ...`; the affected package's Release PR is opened or updated automatically. + 2. **Stacking changes:** Merge several PRs; they accumulate in the open Release PR until the team merges it. + 3. **Release:** Merge the Release PR; tagging, GitHub release, and npm/PyPI publish happen automatically. + 4. **Merge without release-worthy changes:** `chore:`/`ci:`/`test:` commits do not open Release PRs. + +#### Tradeoffs + +- **Pros** + - No forgettable artifact; every squashed commit is parsed. + - Per-package Release PRs give batching and independent release timing. + - Native Python release type; version lives only in `pyproject.toml`. + - Bumps via PR, so branch protections can stay enabled. + - Existing `name@version` tag format is preserved exactly. +- **Cons** + - Requires conventional-commit PR titles, enforced by a CI check. + - Bump types are inferred from commit types, which contributors must learn. + - Non-conventional commits are invisible to changelogs. From ac87b6e1391dafe4bcb6bb928ee25424551a9c90 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Mon, 3 Aug 2026 15:23:03 -0700 Subject: [PATCH 03/13] ci: validate PR titles inline and align accepted types with release sections The title lint accepted commitizen's full type list while changelog-sections listed only eight, so a `perf`/`revert`/`style` title passed CI and then shipped unversioned: release-please drops a commit whose type has no section, and skips the release entirely when the resulting notes are empty. Add `perf` and `revert` as releasable sections and validate titles against exactly that set, so an unreleasable title fails at PR time instead of silently after merge. The check is a regex over the event payload rather than a third-party action, so the job needs no token scopes at all. Also correct the release tables in CONTRIBUTING.md and lib/README.md: `docs`, `refactor`, and `build` are un-hidden in changelog-sections, so they cut a patch release rather than being release-neutral as documented. --- .github/workflows/ci-pr-title.yml | 25 +++++++++++++++++++------ CONTRIBUTING.md | 2 +- lib/README.md | 2 +- release-please-config.json | 2 ++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-pr-title.yml b/.github/workflows/ci-pr-title.yml index 30a2cea8c..1d4f92472 100644 --- a/.github/workflows/ci-pr-title.yml +++ b/.github/workflows/ci-pr-title.yml @@ -4,16 +4,29 @@ on: pull_request: types: [opened, edited, synchronize] -permissions: - pull-requests: read +# Reads the title from the event payload, so no token scopes are needed. +permissions: {} jobs: lint-pr-title: runs-on: ubuntu-latest steps: - # PR titles become the squashed commit subject on main, which - # release-please parses to decide version bumps and changelog entries. + # PR titles become the squashed commit subject on main, which release-please + # parses to decide version bumps and changelog entries. The accepted types are + # exactly the ones listed in release-please-config.json's changelog-sections: + # a type release-please doesn't recognize produces no changelog entry, so it + # skips the release entirely and the change ships unversioned with no error. + # Keep this list and changelog-sections in sync. - name: Validate conventional-commit PR title - uses: amannn/action-semantic-pull-request@v6 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Via env, never inlined into the script — the title is untrusted input. + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + if [[ ! "$PR_TITLE" =~ ^(feat|fix|perf|revert|docs|refactor|build|chore|ci|test)(\([a-zA-Z0-9._/-]+\))?!?:\ .+ ]]; then + echo "::error::PR title must follow Conventional Commits: 'type(scope): description'" + echo "Allowed types: feat, fix, perf, revert, docs, refactor, build, chore, ci, test" + echo "Append '!' for a breaking change, e.g. 'feat(cli)!: drop Node 20 support'." + echo "Got: $PR_TITLE" + exit 1 + fi + echo "OK: $PR_TITLE" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9388493b..b8e4e3bee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -132,7 +132,7 @@ PR titles must follow the [Conventional Commits](https://www.conventionalcommits | `fix` | Patch release | `fix(ts-sdk): preserve plain dates in JSON` | | `feat` | Minor release | `feat(core): add award schemas` | | `feat!` or `fix!` (or a `BREAKING CHANGE:` footer) | Breaking release (minor while pre-1.0) | `feat(cli)!: drop Node 20 support` | -| `docs`, `refactor`, `build` | No release by itself; appears in the next release's changelog | `docs(website): clarify filter usage` | +| `perf`, `revert`, `docs`, `refactor`, `build` | Patch release | `docs(website): clarify filter usage` | | `chore`, `ci`, `test` | No release; hidden from changelogs | `chore(deps): bump the runtime group` | The scope is free-form; use the package or area you touched (`core`, `cli`, `ts-sdk`, `py-sdk`, `website`, `deps`). See [lib/README.md](lib/README.md) for how releases are cut from this history. diff --git a/lib/README.md b/lib/README.md index e4e23b842..d4df36548 100644 --- a/lib/README.md +++ b/lib/README.md @@ -55,7 +55,7 @@ The PR title becomes the squashed commit subject on `main`, which release-please | `feat(ts-sdk): add transform helpers` | minor | | `feat(cli)!: drop Node 20 support` | major (minor while packages are pre-1.0) | | `chore: ...`, `ci: ...`, `test: ...` | no release | -| `docs: ...`, `refactor: ...`, `build: ...` | no bump by itself; listed in the changelog of the next release | +| `perf: ...`, `revert: ...`, `docs: ...`, `refactor: ...`, `build: ...` | patch | A `BREAKING CHANGE:` footer in the squashed commit body also triggers a breaking bump. diff --git a/release-please-config.json b/release-please-config.json index 20f8f2f3a..fcca4d0f9 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -9,6 +9,8 @@ "changelog-sections": [ { "type": "feat", "section": "Features" }, { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "revert", "section": "Reverts" }, { "type": "docs", "section": "Documentation" }, { "type": "refactor", "section": "Refactoring" }, { "type": "build", "section": "Build System" }, From d16894c9774972c6643b1f4af60a04322aa14b84 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Mon, 3 Aug 2026 16:16:30 -0700 Subject: [PATCH 04/13] review: address correctness, security, type-design, and test-coverage findings - Drop the redundant extra-files entry and x-release-please-version marker: release-please's python strategy natively updates common_grants_sdk/__init__.py (hyphen->underscore candidate paths in strategies/python.ts) - Remove the unused id-token: write permission from the four deploy jobs; no step mints an OIDC token, and workflow_call would cap it to none anyway - Restore a release_tag prefix guard on the deploy workflows, replacing the validation lost with the deleted release-notes script - Document that GitHub's Revert button title fails the PR-title check and how to retitle, and that a breaking ! marker on chore/ci/test still releases --- .github/workflows/cd-deploy-lib-cli.yml | 12 +++++++++++- .github/workflows/cd-deploy-lib-core.yml | 12 +++++++++++- .github/workflows/cd-deploy-lib-pysdk.yml | 12 +++++++++++- .github/workflows/cd-deploy-lib-ts-sdk.yml | 12 +++++++++++- CONTRIBUTING.md | 4 ++-- lib/README.md | 2 +- lib/python-sdk/common_grants_sdk/__init__.py | 2 +- release-please-config.json | 3 +-- .../docs/governance/adr/0027-release-please.md | 2 +- 9 files changed, 50 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cd-deploy-lib-cli.yml b/.github/workflows/cd-deploy-lib-cli.yml index b2fe6a348..a4aa9936a 100644 --- a/.github/workflows/cd-deploy-lib-cli.yml +++ b/.github/workflows/cd-deploy-lib-cli.yml @@ -24,7 +24,6 @@ jobs: url: https://www.npmjs.com/package/@common-grants/cli permissions: contents: read - id-token: write defaults: run: working-directory: ./lib/cli @@ -35,6 +34,17 @@ jobs: with: ref: ${{ inputs.release_tag }} + # Guards the manual workflow_dispatch fallback against a mistyped ref + # (a branch, or another package's tag). + - name: Validate release tag + env: + RELEASE_TAG: ${{ inputs.release_tag }} + run: | + if [[ "$RELEASE_TAG" != "@common-grants/cli@"* ]]; then + echo "::error::release_tag must start with '@common-grants/cli@'. Got: $RELEASE_TAG" + exit 1 + fi + # Setup .npmrc file to publish to npm - name: Install Node.js uses: actions/setup-node@v6 diff --git a/.github/workflows/cd-deploy-lib-core.yml b/.github/workflows/cd-deploy-lib-core.yml index 3a1fd014e..69e7cd493 100644 --- a/.github/workflows/cd-deploy-lib-core.yml +++ b/.github/workflows/cd-deploy-lib-core.yml @@ -24,7 +24,6 @@ jobs: url: https://www.npmjs.com/package/@common-grants/core permissions: contents: read - id-token: write defaults: run: working-directory: ./lib/core @@ -35,6 +34,17 @@ jobs: with: ref: ${{ inputs.release_tag }} + # Guards the manual workflow_dispatch fallback against a mistyped ref + # (a branch, or another package's tag). + - name: Validate release tag + env: + RELEASE_TAG: ${{ inputs.release_tag }} + run: | + if [[ "$RELEASE_TAG" != "@common-grants/core@"* ]]; then + echo "::error::release_tag must start with '@common-grants/core@'. Got: $RELEASE_TAG" + exit 1 + fi + # Setup .npmrc file to publish to npm - name: Install Node.js uses: actions/setup-node@v6 diff --git a/.github/workflows/cd-deploy-lib-pysdk.yml b/.github/workflows/cd-deploy-lib-pysdk.yml index 514d1be6b..6c9149059 100644 --- a/.github/workflows/cd-deploy-lib-pysdk.yml +++ b/.github/workflows/cd-deploy-lib-pysdk.yml @@ -24,7 +24,6 @@ jobs: url: https://pypi.org/p/common-grants-sdk permissions: contents: read - id-token: write defaults: run: working-directory: ./lib/python-sdk @@ -35,6 +34,17 @@ jobs: with: ref: ${{ inputs.release_tag }} + # Guards the manual workflow_dispatch fallback against a mistyped ref + # (a branch, or another package's tag). + - name: Validate release tag + env: + RELEASE_TAG: ${{ inputs.release_tag }} + run: | + if [[ "$RELEASE_TAG" != "common-grants-sdk@"* ]]; then + echo "::error::release_tag must start with 'common-grants-sdk@'. Got: $RELEASE_TAG" + exit 1 + fi + - name: Install Python uses: actions/setup-python@v6 with: diff --git a/.github/workflows/cd-deploy-lib-ts-sdk.yml b/.github/workflows/cd-deploy-lib-ts-sdk.yml index dbeba3ab2..b93cd5db6 100644 --- a/.github/workflows/cd-deploy-lib-ts-sdk.yml +++ b/.github/workflows/cd-deploy-lib-ts-sdk.yml @@ -24,7 +24,6 @@ jobs: url: https://www.npmjs.com/package/@common-grants/sdk permissions: contents: read - id-token: write defaults: run: working-directory: ./lib/ts-sdk @@ -35,6 +34,17 @@ jobs: with: ref: ${{ inputs.release_tag }} + # Guards the manual workflow_dispatch fallback against a mistyped ref + # (a branch, or another package's tag). + - name: Validate release tag + env: + RELEASE_TAG: ${{ inputs.release_tag }} + run: | + if [[ "$RELEASE_TAG" != "@common-grants/sdk@"* ]]; then + echo "::error::release_tag must start with '@common-grants/sdk@'. Got: $RELEASE_TAG" + exit 1 + fi + # Setup .npmrc file to publish to npm - name: Install Node.js uses: actions/setup-node@v6 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8e4e3bee..3767160d3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -133,9 +133,9 @@ PR titles must follow the [Conventional Commits](https://www.conventionalcommits | `feat` | Minor release | `feat(core): add award schemas` | | `feat!` or `fix!` (or a `BREAKING CHANGE:` footer) | Breaking release (minor while pre-1.0) | `feat(cli)!: drop Node 20 support` | | `perf`, `revert`, `docs`, `refactor`, `build` | Patch release | `docs(website): clarify filter usage` | -| `chore`, `ci`, `test` | No release; hidden from changelogs | `chore(deps): bump the runtime group` | +| `chore`, `ci`, `test` | No release; hidden from changelogs (a `!` marker still cuts a breaking release) | `chore(deps): bump the runtime group` | -The scope is free-form; use the package or area you touched (`core`, `cli`, `ts-sdk`, `py-sdk`, `website`, `deps`). See [lib/README.md](lib/README.md) for how releases are cut from this history. +The scope is free-form; use the package or area you touched (`core`, `cli`, `ts-sdk`, `py-sdk`, `website`, `deps`). GitHub's **Revert** button titles the new PR `Revert "..."`, which fails this check — retitle it `revert: ` before merging. See [lib/README.md](lib/README.md) for how releases are cut from this history. ## Getting started diff --git a/lib/README.md b/lib/README.md index d4df36548..430e2637a 100644 --- a/lib/README.md +++ b/lib/README.md @@ -54,7 +54,7 @@ The PR title becomes the squashed commit subject on `main`, which release-please | `fix(core): handle empty filter values` | patch | | `feat(ts-sdk): add transform helpers` | minor | | `feat(cli)!: drop Node 20 support` | major (minor while packages are pre-1.0) | -| `chore: ...`, `ci: ...`, `test: ...` | no release | +| `chore: ...`, `ci: ...`, `test: ...` | no release (a breaking `!` marker still releases) | | `perf: ...`, `revert: ...`, `docs: ...`, `refactor: ...`, `build: ...` | patch | A `BREAKING CHANGE:` footer in the squashed commit body also triggers a breaking bump. diff --git a/lib/python-sdk/common_grants_sdk/__init__.py b/lib/python-sdk/common_grants_sdk/__init__.py index 0e304e2ec..60e169737 100644 --- a/lib/python-sdk/common_grants_sdk/__init__.py +++ b/lib/python-sdk/common_grants_sdk/__init__.py @@ -4,7 +4,7 @@ A Python implementation of the CommonGrants protocol. """ -__version__ = "0.8.0" # x-release-please-version +__version__ = "0.8.0" from . import schemas from .client import Auth, Client, Config diff --git a/release-please-config.json b/release-please-config.json index fcca4d0f9..7ec87c40c 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -38,8 +38,7 @@ "release-type": "python", "package-name": "common-grants-sdk", "component": "common-grants-sdk", - "changelog-path": "CHANGELOG.md", - "extra-files": ["common_grants_sdk/__init__.py"] + "changelog-path": "CHANGELOG.md" } } } diff --git a/website/src/content/docs/governance/adr/0027-release-please.md b/website/src/content/docs/governance/adr/0027-release-please.md index b611f47b5..634e5f06a 100644 --- a/website/src/content/docs/governance/adr/0027-release-please.md +++ b/website/src/content/docs/governance/adr/0027-release-please.md @@ -133,7 +133,7 @@ Option 3 is best if: 1. **Regular change:** Merge a PR titled `fix(ts-sdk): ...` or `feat(core): ...`; the affected package's Release PR is opened or updated automatically. 2. **Stacking changes:** Merge several PRs; they accumulate in the open Release PR until the team merges it. 3. **Release:** Merge the Release PR; tagging, GitHub release, and npm/PyPI publish happen automatically. - 4. **Merge without release-worthy changes:** `chore:`/`ci:`/`test:` commits do not open Release PRs. + 4. **Merge without release-worthy changes:** `chore:`/`ci:`/`test:` commits do not open Release PRs (unless marked breaking with `!`). #### Tradeoffs From 17ddcdaef5fa52638755521a04a4ddccad7c8d5c Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Mon, 3 Aug 2026 16:32:06 -0700 Subject: [PATCH 05/13] review: address security and correctness findings - Tighten the release-tag guard to an anchored X.Y.Z format check plus a refs/tags existence check, so a branch named like a tag can't be published via the manual workflow_dispatch fallback - Route the Notify step's tag echo through env instead of inlining the workflow input into the script - Note the breaking-! exception in DEPENDENCY_MANAGEMENT.md to match the other release docs, and correct ADR 0027 on where the Python version lives --- .github/workflows/cd-deploy-lib-cli.yml | 17 ++++++++++++----- .github/workflows/cd-deploy-lib-core.yml | 17 ++++++++++++----- .github/workflows/cd-deploy-lib-pysdk.yml | 17 ++++++++++++----- .github/workflows/cd-deploy-lib-ts-sdk.yml | 17 ++++++++++++----- DEPENDENCY_MANAGEMENT.md | 2 +- .../docs/governance/adr/0027-release-please.md | 2 +- 6 files changed, 50 insertions(+), 22 deletions(-) diff --git a/.github/workflows/cd-deploy-lib-cli.yml b/.github/workflows/cd-deploy-lib-cli.yml index a4aa9936a..395a79c6b 100644 --- a/.github/workflows/cd-deploy-lib-cli.yml +++ b/.github/workflows/cd-deploy-lib-cli.yml @@ -34,14 +34,19 @@ jobs: with: ref: ${{ inputs.release_tag }} - # Guards the manual workflow_dispatch fallback against a mistyped ref - # (a branch, or another package's tag). + # Rejects a mistyped or spoofed ref (a branch, or another package's tag). + # release-please's own tag always passes, so this only ever fires on the + # manual workflow_dispatch fallback. - name: Validate release tag env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ "$RELEASE_TAG" != "@common-grants/cli@"* ]]; then - echo "::error::release_tag must start with '@common-grants/cli@'. Got: $RELEASE_TAG" + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/cli@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::release_tag must look like '@common-grants/cli@X.Y.Z'. Got: $RELEASE_TAG" + exit 1 + fi + if ! git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then + echo "::error::release_tag is not an existing tag. Got: $RELEASE_TAG" exit 1 fi @@ -67,6 +72,8 @@ jobs: run: pnpm publish --access public --no-git-checks - name: Notify completion + env: + RELEASE_TAG: ${{ inputs.release_tag }} run: | echo "CLI package deployed to npm" - echo "Tag: ${{ inputs.release_tag }}" + echo "Tag: $RELEASE_TAG" diff --git a/.github/workflows/cd-deploy-lib-core.yml b/.github/workflows/cd-deploy-lib-core.yml index 69e7cd493..9285beac1 100644 --- a/.github/workflows/cd-deploy-lib-core.yml +++ b/.github/workflows/cd-deploy-lib-core.yml @@ -34,14 +34,19 @@ jobs: with: ref: ${{ inputs.release_tag }} - # Guards the manual workflow_dispatch fallback against a mistyped ref - # (a branch, or another package's tag). + # Rejects a mistyped or spoofed ref (a branch, or another package's tag). + # release-please's own tag always passes, so this only ever fires on the + # manual workflow_dispatch fallback. - name: Validate release tag env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ "$RELEASE_TAG" != "@common-grants/core@"* ]]; then - echo "::error::release_tag must start with '@common-grants/core@'. Got: $RELEASE_TAG" + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/core@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::release_tag must look like '@common-grants/core@X.Y.Z'. Got: $RELEASE_TAG" + exit 1 + fi + if ! git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then + echo "::error::release_tag is not an existing tag. Got: $RELEASE_TAG" exit 1 fi @@ -67,6 +72,8 @@ jobs: run: pnpm publish --access public --no-git-checks - name: Notify completion + env: + RELEASE_TAG: ${{ inputs.release_tag }} run: | echo "Core package deployed to npm" - echo "Tag: ${{ inputs.release_tag }}" + echo "Tag: $RELEASE_TAG" diff --git a/.github/workflows/cd-deploy-lib-pysdk.yml b/.github/workflows/cd-deploy-lib-pysdk.yml index 6c9149059..d61fdc274 100644 --- a/.github/workflows/cd-deploy-lib-pysdk.yml +++ b/.github/workflows/cd-deploy-lib-pysdk.yml @@ -34,14 +34,19 @@ jobs: with: ref: ${{ inputs.release_tag }} - # Guards the manual workflow_dispatch fallback against a mistyped ref - # (a branch, or another package's tag). + # Rejects a mistyped or spoofed ref (a branch, or another package's tag). + # release-please's own tag always passes, so this only ever fires on the + # manual workflow_dispatch fallback. - name: Validate release tag env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ "$RELEASE_TAG" != "common-grants-sdk@"* ]]; then - echo "::error::release_tag must start with 'common-grants-sdk@'. Got: $RELEASE_TAG" + if [[ ! "$RELEASE_TAG" =~ ^common-grants-sdk@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::release_tag must look like 'common-grants-sdk@X.Y.Z'. Got: $RELEASE_TAG" + exit 1 + fi + if ! git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then + echo "::error::release_tag is not an existing tag. Got: $RELEASE_TAG" exit 1 fi @@ -64,6 +69,8 @@ jobs: poetry publish - name: Notify completion + env: + RELEASE_TAG: ${{ inputs.release_tag }} run: | echo "Python SDK deployed to PyPI" - echo "Tag: ${{ inputs.release_tag }}" + echo "Tag: $RELEASE_TAG" diff --git a/.github/workflows/cd-deploy-lib-ts-sdk.yml b/.github/workflows/cd-deploy-lib-ts-sdk.yml index b93cd5db6..99bfda0ff 100644 --- a/.github/workflows/cd-deploy-lib-ts-sdk.yml +++ b/.github/workflows/cd-deploy-lib-ts-sdk.yml @@ -34,14 +34,19 @@ jobs: with: ref: ${{ inputs.release_tag }} - # Guards the manual workflow_dispatch fallback against a mistyped ref - # (a branch, or another package's tag). + # Rejects a mistyped or spoofed ref (a branch, or another package's tag). + # release-please's own tag always passes, so this only ever fires on the + # manual workflow_dispatch fallback. - name: Validate release tag env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ "$RELEASE_TAG" != "@common-grants/sdk@"* ]]; then - echo "::error::release_tag must start with '@common-grants/sdk@'. Got: $RELEASE_TAG" + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/sdk@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::release_tag must look like '@common-grants/sdk@X.Y.Z'. Got: $RELEASE_TAG" + exit 1 + fi + if ! git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then + echo "::error::release_tag is not an existing tag. Got: $RELEASE_TAG" exit 1 fi @@ -67,6 +72,8 @@ jobs: run: pnpm publish --access public --no-git-checks - name: Notify completion + env: + RELEASE_TAG: ${{ inputs.release_tag }} run: | echo "TypeScript SDK package deployed to npm" - echo "Tag: ${{ inputs.release_tag }}" + echo "Tag: $RELEASE_TAG" diff --git a/DEPENDENCY_MANAGEMENT.md b/DEPENDENCY_MANAGEMENT.md index 3c1467584..7024732d2 100644 --- a/DEPENDENCY_MANAGEMENT.md +++ b/DEPENDENCY_MANAGEMENT.md @@ -86,7 +86,7 @@ The `website` catalog has its own `vitest` version (`^4.x`). The update script i ## Release expectations -Version bumps are driven by [release-please](https://github.com/googleapis/release-please) from the merged PR's conventional-commit title — there is no separate file to add. What matters is choosing the right type: `chore(deps)` does **not** trigger a release, while `fix(deps)` (or `fix()`) does. +Version bumps are driven by [release-please](https://github.com/googleapis/release-please) from the merged PR's conventional-commit title — there is no separate file to add. What matters is choosing the right type: `chore(deps)` does **not** trigger a release (unless marked breaking with `!`), while `fix(deps)` (or `fix()`) does. Most dependency PRs should stay `chore(deps)`. Use a releasing type only when a **production dependency changes in a published package**: diff --git a/website/src/content/docs/governance/adr/0027-release-please.md b/website/src/content/docs/governance/adr/0027-release-please.md index 634e5f06a..bf14d6352 100644 --- a/website/src/content/docs/governance/adr/0027-release-please.md +++ b/website/src/content/docs/governance/adr/0027-release-please.md @@ -140,7 +140,7 @@ Option 3 is best if: - **Pros** - No forgettable artifact; every squashed commit is parsed. - Per-package Release PRs give batching and independent release timing. - - Native Python release type; version lives only in `pyproject.toml`. + - Native Python release type; the version is edited only in `pyproject.toml` (release-please mirrors it into `common_grants_sdk/__init__.py` on each release). - Bumps via PR, so branch protections can stay enabled. - Existing `name@version` tag format is preserved exactly. - **Cons** From 06cf608cd233b40a9b31366abcf7a7702db31617 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Mon, 3 Aug 2026 16:41:35 -0700 Subject: [PATCH 06/13] review: close branch/tag ambiguity in the deploy checkout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit actions/checkout resolves an unqualified ref to a branch when a branch and tag share a name, so the show-ref guard could pass while a same-named branch was actually checked out. Qualify the checkout ref as refs/tags/ instead — the tag namespace is pinned at fetch time and a nonexistent tag fails the checkout — and keep the guard as a format check only, now tolerant of prerelease/build suffixes so a Release-As prerelease wouldn't fail the automated publish path. --- .github/workflows/cd-deploy-lib-cli.yml | 16 +++++++--------- .github/workflows/cd-deploy-lib-core.yml | 16 +++++++--------- .github/workflows/cd-deploy-lib-pysdk.yml | 16 +++++++--------- .github/workflows/cd-deploy-lib-ts-sdk.yml | 16 +++++++--------- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/.github/workflows/cd-deploy-lib-cli.yml b/.github/workflows/cd-deploy-lib-cli.yml index 395a79c6b..94328a87b 100644 --- a/.github/workflows/cd-deploy-lib-cli.yml +++ b/.github/workflows/cd-deploy-lib-cli.yml @@ -29,26 +29,24 @@ jobs: working-directory: ./lib/cli steps: + # The refs/tags/ qualifier pins the checkout to the tag namespace — a + # branch with the same name can't be substituted, and a nonexistent tag + # fails the checkout outright. - name: Checkout code uses: actions/checkout@v6 with: - ref: ${{ inputs.release_tag }} + ref: refs/tags/${{ inputs.release_tag }} - # Rejects a mistyped or spoofed ref (a branch, or another package's tag). - # release-please's own tag always passes, so this only ever fires on the - # manual workflow_dispatch fallback. + # Rejects another package's tag or a malformed version on the manual + # workflow_dispatch fallback; release-please's own tag always passes. - name: Validate release tag env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ ! "$RELEASE_TAG" =~ ^@common-grants/cli@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/cli@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then echo "::error::release_tag must look like '@common-grants/cli@X.Y.Z'. Got: $RELEASE_TAG" exit 1 fi - if ! git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then - echo "::error::release_tag is not an existing tag. Got: $RELEASE_TAG" - exit 1 - fi # Setup .npmrc file to publish to npm - name: Install Node.js diff --git a/.github/workflows/cd-deploy-lib-core.yml b/.github/workflows/cd-deploy-lib-core.yml index 9285beac1..6ee7128c0 100644 --- a/.github/workflows/cd-deploy-lib-core.yml +++ b/.github/workflows/cd-deploy-lib-core.yml @@ -29,26 +29,24 @@ jobs: working-directory: ./lib/core steps: + # The refs/tags/ qualifier pins the checkout to the tag namespace — a + # branch with the same name can't be substituted, and a nonexistent tag + # fails the checkout outright. - name: Checkout code uses: actions/checkout@v6 with: - ref: ${{ inputs.release_tag }} + ref: refs/tags/${{ inputs.release_tag }} - # Rejects a mistyped or spoofed ref (a branch, or another package's tag). - # release-please's own tag always passes, so this only ever fires on the - # manual workflow_dispatch fallback. + # Rejects another package's tag or a malformed version on the manual + # workflow_dispatch fallback; release-please's own tag always passes. - name: Validate release tag env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ ! "$RELEASE_TAG" =~ ^@common-grants/core@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/core@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then echo "::error::release_tag must look like '@common-grants/core@X.Y.Z'. Got: $RELEASE_TAG" exit 1 fi - if ! git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then - echo "::error::release_tag is not an existing tag. Got: $RELEASE_TAG" - exit 1 - fi # Setup .npmrc file to publish to npm - name: Install Node.js diff --git a/.github/workflows/cd-deploy-lib-pysdk.yml b/.github/workflows/cd-deploy-lib-pysdk.yml index d61fdc274..38733a8a1 100644 --- a/.github/workflows/cd-deploy-lib-pysdk.yml +++ b/.github/workflows/cd-deploy-lib-pysdk.yml @@ -29,26 +29,24 @@ jobs: working-directory: ./lib/python-sdk steps: + # The refs/tags/ qualifier pins the checkout to the tag namespace — a + # branch with the same name can't be substituted, and a nonexistent tag + # fails the checkout outright. - name: Checkout code uses: actions/checkout@v6 with: - ref: ${{ inputs.release_tag }} + ref: refs/tags/${{ inputs.release_tag }} - # Rejects a mistyped or spoofed ref (a branch, or another package's tag). - # release-please's own tag always passes, so this only ever fires on the - # manual workflow_dispatch fallback. + # Rejects another package's tag or a malformed version on the manual + # workflow_dispatch fallback; release-please's own tag always passes. - name: Validate release tag env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ ! "$RELEASE_TAG" =~ ^common-grants-sdk@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ ! "$RELEASE_TAG" =~ ^common-grants-sdk@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then echo "::error::release_tag must look like 'common-grants-sdk@X.Y.Z'. Got: $RELEASE_TAG" exit 1 fi - if ! git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then - echo "::error::release_tag is not an existing tag. Got: $RELEASE_TAG" - exit 1 - fi - name: Install Python uses: actions/setup-python@v6 diff --git a/.github/workflows/cd-deploy-lib-ts-sdk.yml b/.github/workflows/cd-deploy-lib-ts-sdk.yml index 99bfda0ff..0d1f6cd2a 100644 --- a/.github/workflows/cd-deploy-lib-ts-sdk.yml +++ b/.github/workflows/cd-deploy-lib-ts-sdk.yml @@ -29,26 +29,24 @@ jobs: working-directory: ./lib/ts-sdk steps: + # The refs/tags/ qualifier pins the checkout to the tag namespace — a + # branch with the same name can't be substituted, and a nonexistent tag + # fails the checkout outright. - name: Checkout code uses: actions/checkout@v6 with: - ref: ${{ inputs.release_tag }} + ref: refs/tags/${{ inputs.release_tag }} - # Rejects a mistyped or spoofed ref (a branch, or another package's tag). - # release-please's own tag always passes, so this only ever fires on the - # manual workflow_dispatch fallback. + # Rejects another package's tag or a malformed version on the manual + # workflow_dispatch fallback; release-please's own tag always passes. - name: Validate release tag env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ ! "$RELEASE_TAG" =~ ^@common-grants/sdk@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/sdk@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then echo "::error::release_tag must look like '@common-grants/sdk@X.Y.Z'. Got: $RELEASE_TAG" exit 1 fi - if ! git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then - echo "::error::release_tag is not an existing tag. Got: $RELEASE_TAG" - exit 1 - fi # Setup .npmrc file to publish to npm - name: Install Node.js From 27d7e3886761c6598d4be210b288741b3b5eced7 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Mon, 3 Aug 2026 16:49:47 -0700 Subject: [PATCH 07/13] review: validate the release tag before checkout Move the format guard ahead of checkout (bare-workspace working-directory) so no step ever touches content from an unvalidated ref, and allow the combined semver prerelease+build suffix the widened pattern missed. --- .github/workflows/cd-deploy-lib-cli.yml | 21 ++++++++++++--------- .github/workflows/cd-deploy-lib-core.yml | 21 ++++++++++++--------- .github/workflows/cd-deploy-lib-pysdk.yml | 21 ++++++++++++--------- .github/workflows/cd-deploy-lib-ts-sdk.yml | 21 ++++++++++++--------- 4 files changed, 48 insertions(+), 36 deletions(-) diff --git a/.github/workflows/cd-deploy-lib-cli.yml b/.github/workflows/cd-deploy-lib-cli.yml index 94328a87b..a77fae80a 100644 --- a/.github/workflows/cd-deploy-lib-cli.yml +++ b/.github/workflows/cd-deploy-lib-cli.yml @@ -29,25 +29,28 @@ jobs: working-directory: ./lib/cli steps: - # The refs/tags/ qualifier pins the checkout to the tag namespace — a - # branch with the same name can't be substituted, and a nonexistent tag - # fails the checkout outright. - - name: Checkout code - uses: actions/checkout@v6 - with: - ref: refs/tags/${{ inputs.release_tag }} - # Rejects another package's tag or a malformed version on the manual # workflow_dispatch fallback; release-please's own tag always passes. + # Runs before checkout (in the bare workspace) so nothing ever touches + # content from an unvalidated ref. - name: Validate release tag + working-directory: . env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ ! "$RELEASE_TAG" =~ ^@common-grants/cli@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/cli@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.+-]+)?$ ]]; then echo "::error::release_tag must look like '@common-grants/cli@X.Y.Z'. Got: $RELEASE_TAG" exit 1 fi + # The refs/tags/ qualifier pins the checkout to the tag namespace — a + # branch with the same name can't be substituted, and a nonexistent tag + # fails the checkout outright. + - name: Checkout code + uses: actions/checkout@v6 + with: + ref: refs/tags/${{ inputs.release_tag }} + # Setup .npmrc file to publish to npm - name: Install Node.js uses: actions/setup-node@v6 diff --git a/.github/workflows/cd-deploy-lib-core.yml b/.github/workflows/cd-deploy-lib-core.yml index 6ee7128c0..37a577059 100644 --- a/.github/workflows/cd-deploy-lib-core.yml +++ b/.github/workflows/cd-deploy-lib-core.yml @@ -29,25 +29,28 @@ jobs: working-directory: ./lib/core steps: - # The refs/tags/ qualifier pins the checkout to the tag namespace — a - # branch with the same name can't be substituted, and a nonexistent tag - # fails the checkout outright. - - name: Checkout code - uses: actions/checkout@v6 - with: - ref: refs/tags/${{ inputs.release_tag }} - # Rejects another package's tag or a malformed version on the manual # workflow_dispatch fallback; release-please's own tag always passes. + # Runs before checkout (in the bare workspace) so nothing ever touches + # content from an unvalidated ref. - name: Validate release tag + working-directory: . env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ ! "$RELEASE_TAG" =~ ^@common-grants/core@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/core@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.+-]+)?$ ]]; then echo "::error::release_tag must look like '@common-grants/core@X.Y.Z'. Got: $RELEASE_TAG" exit 1 fi + # The refs/tags/ qualifier pins the checkout to the tag namespace — a + # branch with the same name can't be substituted, and a nonexistent tag + # fails the checkout outright. + - name: Checkout code + uses: actions/checkout@v6 + with: + ref: refs/tags/${{ inputs.release_tag }} + # Setup .npmrc file to publish to npm - name: Install Node.js uses: actions/setup-node@v6 diff --git a/.github/workflows/cd-deploy-lib-pysdk.yml b/.github/workflows/cd-deploy-lib-pysdk.yml index 38733a8a1..a96f31d30 100644 --- a/.github/workflows/cd-deploy-lib-pysdk.yml +++ b/.github/workflows/cd-deploy-lib-pysdk.yml @@ -29,25 +29,28 @@ jobs: working-directory: ./lib/python-sdk steps: - # The refs/tags/ qualifier pins the checkout to the tag namespace — a - # branch with the same name can't be substituted, and a nonexistent tag - # fails the checkout outright. - - name: Checkout code - uses: actions/checkout@v6 - with: - ref: refs/tags/${{ inputs.release_tag }} - # Rejects another package's tag or a malformed version on the manual # workflow_dispatch fallback; release-please's own tag always passes. + # Runs before checkout (in the bare workspace) so nothing ever touches + # content from an unvalidated ref. - name: Validate release tag + working-directory: . env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ ! "$RELEASE_TAG" =~ ^common-grants-sdk@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then + if [[ ! "$RELEASE_TAG" =~ ^common-grants-sdk@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.+-]+)?$ ]]; then echo "::error::release_tag must look like 'common-grants-sdk@X.Y.Z'. Got: $RELEASE_TAG" exit 1 fi + # The refs/tags/ qualifier pins the checkout to the tag namespace — a + # branch with the same name can't be substituted, and a nonexistent tag + # fails the checkout outright. + - name: Checkout code + uses: actions/checkout@v6 + with: + ref: refs/tags/${{ inputs.release_tag }} + - name: Install Python uses: actions/setup-python@v6 with: diff --git a/.github/workflows/cd-deploy-lib-ts-sdk.yml b/.github/workflows/cd-deploy-lib-ts-sdk.yml index 0d1f6cd2a..992034acc 100644 --- a/.github/workflows/cd-deploy-lib-ts-sdk.yml +++ b/.github/workflows/cd-deploy-lib-ts-sdk.yml @@ -29,25 +29,28 @@ jobs: working-directory: ./lib/ts-sdk steps: - # The refs/tags/ qualifier pins the checkout to the tag namespace — a - # branch with the same name can't be substituted, and a nonexistent tag - # fails the checkout outright. - - name: Checkout code - uses: actions/checkout@v6 - with: - ref: refs/tags/${{ inputs.release_tag }} - # Rejects another package's tag or a malformed version on the manual # workflow_dispatch fallback; release-please's own tag always passes. + # Runs before checkout (in the bare workspace) so nothing ever touches + # content from an unvalidated ref. - name: Validate release tag + working-directory: . env: RELEASE_TAG: ${{ inputs.release_tag }} run: | - if [[ ! "$RELEASE_TAG" =~ ^@common-grants/sdk@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then + if [[ ! "$RELEASE_TAG" =~ ^@common-grants/sdk@[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.+-]+)?$ ]]; then echo "::error::release_tag must look like '@common-grants/sdk@X.Y.Z'. Got: $RELEASE_TAG" exit 1 fi + # The refs/tags/ qualifier pins the checkout to the tag namespace — a + # branch with the same name can't be substituted, and a nonexistent tag + # fails the checkout outright. + - name: Checkout code + uses: actions/checkout@v6 + with: + ref: refs/tags/${{ inputs.release_tag }} + # Setup .npmrc file to publish to npm - name: Install Node.js uses: actions/setup-node@v6 From 1217e822662282c6d812b151ebdf12e628e1bbbe Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Tue, 4 Aug 2026 09:49:34 -0700 Subject: [PATCH 08/13] review: pin python __version__, scope publish secrets, fix emitter note - Add lib/python-sdk/tests/test_version.py pinning __version__ to the pyproject.toml version. release-please's python strategy keeps the two in sync on release, but nothing caught the drift that left __version__ at 0.1.0 while the package shipped through 0.8.0. - Replace `secrets: inherit` on the four publish calls with the single secret each one consumes, declared in the callee's workflow_call. inherit passed every repo secret to workflows that reference exactly one. - lib/changelog-emitter has no `"private": true` field; it is excluded from releases by omission from release-please-config.json, so say that instead. --- .github/workflows/cd-deploy-lib-cli.yml | 3 +++ .github/workflows/cd-deploy-lib-core.yml | 3 +++ .github/workflows/cd-deploy-lib-pysdk.yml | 3 +++ .github/workflows/cd-deploy-lib-ts-sdk.yml | 3 +++ .github/workflows/release-please.yml | 12 ++++++++---- lib/README.md | 2 +- lib/python-sdk/tests/test_version.py | 19 +++++++++++++++++++ 7 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 lib/python-sdk/tests/test_version.py diff --git a/.github/workflows/cd-deploy-lib-cli.yml b/.github/workflows/cd-deploy-lib-cli.yml index a77fae80a..8af864f53 100644 --- a/.github/workflows/cd-deploy-lib-cli.yml +++ b/.github/workflows/cd-deploy-lib-cli.yml @@ -8,6 +8,9 @@ on: description: "Release tag (e.g., @common-grants/cli@0.2.0)" required: true type: string + secrets: + NPM_TOKEN: + required: true # Manual fallback for re-running a publish against an existing tag workflow_dispatch: inputs: diff --git a/.github/workflows/cd-deploy-lib-core.yml b/.github/workflows/cd-deploy-lib-core.yml index 37a577059..5a376a444 100644 --- a/.github/workflows/cd-deploy-lib-core.yml +++ b/.github/workflows/cd-deploy-lib-core.yml @@ -8,6 +8,9 @@ on: description: "Release tag (e.g., @common-grants/core@0.2.0)" required: true type: string + secrets: + NPM_TOKEN: + required: true # Manual fallback for re-running a publish against an existing tag workflow_dispatch: inputs: diff --git a/.github/workflows/cd-deploy-lib-pysdk.yml b/.github/workflows/cd-deploy-lib-pysdk.yml index a96f31d30..a023e9332 100644 --- a/.github/workflows/cd-deploy-lib-pysdk.yml +++ b/.github/workflows/cd-deploy-lib-pysdk.yml @@ -8,6 +8,9 @@ on: description: "Release tag (e.g., common-grants-sdk@0.3.0)" required: true type: string + secrets: + PYPI_TOKEN: + required: true # Manual fallback for re-running a publish against an existing tag workflow_dispatch: inputs: diff --git a/.github/workflows/cd-deploy-lib-ts-sdk.yml b/.github/workflows/cd-deploy-lib-ts-sdk.yml index 992034acc..332f66728 100644 --- a/.github/workflows/cd-deploy-lib-ts-sdk.yml +++ b/.github/workflows/cd-deploy-lib-ts-sdk.yml @@ -8,6 +8,9 @@ on: description: "Release tag (e.g., @common-grants/sdk@0.2.0)" required: true type: string + secrets: + NPM_TOKEN: + required: true # Manual fallback for re-running a publish against an existing tag workflow_dispatch: inputs: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 7a4282050..ab222a1f6 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -39,7 +39,8 @@ jobs: uses: ./.github/workflows/cd-deploy-lib-core.yml with: release_tag: ${{ needs.release-please.outputs.core_tag }} - secrets: inherit + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} publish-cli: needs: release-please @@ -47,7 +48,8 @@ jobs: uses: ./.github/workflows/cd-deploy-lib-cli.yml with: release_tag: ${{ needs.release-please.outputs.cli_tag }} - secrets: inherit + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} publish-ts-sdk: needs: release-please @@ -55,7 +57,8 @@ jobs: uses: ./.github/workflows/cd-deploy-lib-ts-sdk.yml with: release_tag: ${{ needs.release-please.outputs.ts_sdk_tag }} - secrets: inherit + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} publish-py-sdk: needs: release-please @@ -63,4 +66,5 @@ jobs: uses: ./.github/workflows/cd-deploy-lib-pysdk.yml with: release_tag: ${{ needs.release-please.outputs.py_sdk_tag }} - secrets: inherit + secrets: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} diff --git a/lib/README.md b/lib/README.md index 430e2637a..c9c4182c1 100644 --- a/lib/README.md +++ b/lib/README.md @@ -100,4 +100,4 @@ After merging a Release PR: - Per-package versions are tracked in `.release-please-manifest.json`; release-please config lives in `release-please-config.json` (both at the repo root). - The Python SDK's version lives only in `pyproject.toml` (mirrored into `common_grants_sdk/__init__.py` by release-please); it has no `package.json`. -- `lib/changelog-emitter` is private and excluded from releases. +- `lib/changelog-emitter` is unpublished and excluded from releases by omission from `release-please-config.json`. diff --git a/lib/python-sdk/tests/test_version.py b/lib/python-sdk/tests/test_version.py new file mode 100644 index 000000000..57da0e790 --- /dev/null +++ b/lib/python-sdk/tests/test_version.py @@ -0,0 +1,19 @@ +"""The package version has two on-disk homes; release-please bumps both. + +`release-type: python` rewrites `[tool.poetry] version` and, via its native +`/__init__.py` discovery, `__version__` too. This pins them +together so a hand-edit of one that misses the other fails here instead of +shipping a wheel whose `__version__` disagrees with its metadata. +""" + +import tomllib +from pathlib import Path + +import common_grants_sdk + +PYPROJECT = Path(__file__).resolve().parent.parent / "pyproject.toml" + + +def test_dunder_version_matches_pyproject() -> None: + pyproject = tomllib.loads(PYPROJECT.read_text()) + assert common_grants_sdk.__version__ == pyproject["tool"]["poetry"]["version"] From 408d2e9a7cad634d523b5b5507326ba3d0dde1a2 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Tue, 4 Aug 2026 10:28:07 -0700 Subject: [PATCH 09/13] ci: supersede stale PR-title lint runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow fires on every title or body edit and had no concurrency group, so superseded runs kept queuing and holding a runner slot instead of being cancelled. Ten runs accumulated on this branch, each checking a title that a later run rechecked anyway. Groups per PR number with cancel-in-progress, so only the newest title is checked. This does not shorten GitHub's runner queue, which is what actually delayed the runs (one waited 5m09s for a runner and then executed in under a second) — it just stops spending a slot per edit. --- .github/workflows/ci-pr-title.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci-pr-title.yml b/.github/workflows/ci-pr-title.yml index 1d4f92472..7dc10877e 100644 --- a/.github/workflows/ci-pr-title.yml +++ b/.github/workflows/ci-pr-title.yml @@ -4,6 +4,12 @@ on: pull_request: types: [opened, edited, synchronize] +# Every title or body edit fires a run, so supersede rather than queue them; only +# the newest title is worth checking. +concurrency: + group: pr-title-${{ github.event.pull_request.number }} + cancel-in-progress: true + # Reads the title from the event payload, so no token scopes are needed. permissions: {} From 04d97c61da245f9e1878a5f6ad3b3d8b984b8db3 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Wed, 5 Aug 2026 10:32:20 -0700 Subject: [PATCH 10/13] ci: sync release-please manifest to the released versions lib/cli 0.3.7 -> 0.3.8 and lib/ts-sdk 0.6.1 -> 0.7.0, matching what the typescript v6 and zod v4 bumps released. Stale baselines here would make release-please compute the next version from the wrong tag. --- .release-please-manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b4c59bd49..c338a6896 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,6 +1,6 @@ { "lib/core": "0.4.0", - "lib/cli": "0.3.7", - "lib/ts-sdk": "0.6.1", + "lib/cli": "0.3.8", + "lib/ts-sdk": "0.7.0", "lib/python-sdk": "0.8.0" } From 7ac89f4c633311060c030466adb0a1287c4fdea6 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Wed, 5 Aug 2026 10:49:49 -0700 Subject: [PATCH 11/13] ci: remove the unreleased-tag check made obsolete by release-please The nightly job flagged tags that had no GitHub release, a gap that existed because ci-bump-version.yml pushed tags on merge while release creation was a separate manual workflow_dispatch. release-please creates the tag and the release in one step, so the state it watched for can no longer arise. --- .github/scripts/check-for-unreleased-tags.sh | 81 -------------------- .github/workflows/missing-release-check.yml | 34 -------- 2 files changed, 115 deletions(-) delete mode 100755 .github/scripts/check-for-unreleased-tags.sh delete mode 100644 .github/workflows/missing-release-check.yml diff --git a/.github/scripts/check-for-unreleased-tags.sh b/.github/scripts/check-for-unreleased-tags.sh deleted file mode 100755 index d17e7c4fe..000000000 --- a/.github/scripts/check-for-unreleased-tags.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash -#Script to check tags against releases -# check-for-unreleased-tags.sh -# example check-for-unreleased-tags.sh -set -e - -echo "=== Checking Git tags against GitHub releases ===" -echo "" - -DAYS="$1" - -#Check that Days is a positive integer -if [[ $DAYS =~ ^[0-9]+$ ]]; then - echo "$DAYS is an integer, continuing" -else - echo "Bad input for first argument. It should be a positive integer eg: check-for-unreleased-tags.sh 30" - exit 1 -fi - - -# Calculate date X days ago (compatible with both Linux and macOS) -if date -v-1d > /dev/null 2>&1; then - # macOS date syntax - NUMBER_OF_DAYS_AGO=$(date -u -v-${DAYS}d '+%Y-%m-%d %H:%M:%S') - NUMBER_OF_DAYS_AGO_UNIX=$(date -u -v-${DAYS}d '+%s') -else - # GNU date syntax (Linux) - NUMBER_OF_DAYS_AGO=$(date -u -d "$DAYS days ago" '+%Y-%m-%d %H:%M:%S') - NUMBER_OF_DAYS_AGO_UNIX=$(date -u -d "$DAYS days ago" '+%s') -fi -echo "Checking tags and releases created since: $NUMBER_OF_DAYS_AGO UTC" -echo "" - -echo "Fetching Git tags" -git fetch --tags -ALL_TAGS=$(git tag --sort=-creatordate --format='%(creatordate:unix) %(refname:short)' | \ -awk -v cutoff="$NUMBER_OF_DAYS_AGO_UNIX" '$1 >= cutoff {print $2}') -TAG_COUNT=$(echo "$ALL_TAGS" | grep -c . || true) -echo "Found $TAG_COUNT Git tags" -echo "" - - -echo "Fetching GitHub releases" -RELEASES=$(gh release list --limit 100 --json tagName,createdAt | \ -jq -r --arg cutoff "$NUMBER_OF_DAYS_AGO" '.[] | select(.createdAt >= $cutoff) | .tagName') -RELEASE_COUNT=$(echo "$RELEASES" | grep -c . || true) -echo "Found $RELEASE_COUNT GitHub releases" -echo "" - -# Find tags without releases -echo "=== Tags without corresponding releases ===" -MISSING_RELEASES=() -if [ $TAG_COUNT -gt 0 ]; then - while IFS= read -r tag; do - if [ -n "$tag" ] && ! echo "$RELEASES" | grep -qx "$tag"; then - echo " ❌ Tag '$tag' has no GitHub release" - MISSING_RELEASES+=("$tag") - fi - done <<< "$ALL_TAGS" -fi - -if [ ${#MISSING_RELEASES[@]} -eq 0 ]; then - echo " ✅ All tags have corresponding releases" -fi -echo "" - -# Summary and exit status -echo "=== Summary (Last $DAYS Days) ===" -echo "Git tags created: $TAG_COUNT" -echo "GitHub releases created: $RELEASE_COUNT" -echo "Tags without releases: ${#MISSING_RELEASES[@]}" -echo "" - -# Fail if any mismatches found -if [ ${#MISSING_RELEASES[@]} -gt 0 ]; then - echo "❌ FAILURE: Tags without releases detected!" - exit 1 -else - echo "✅ SUCCESS: All tags have corresponding releases!" - exit 0 -fi \ No newline at end of file diff --git a/.github/workflows/missing-release-check.yml b/.github/workflows/missing-release-check.yml deleted file mode 100644 index a7982dd60..000000000 --- a/.github/workflows/missing-release-check.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Check for Unreleased Tags - -on: - schedule: - # Runs at 6 PM UTC every day - - cron: "0 18 * * *" - pull_request: - paths: - - .github/workflows/missing-release-check.yml - - .github/scripts/check-for-unreleased-tags.sh - workflow_dispatch: # Allows manual trigger - inputs: - days: - description: "Number of days to check" - required: false - type: number - default: 30 - -jobs: - check-tags-releases: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 # Fetch all tags and history - - - name: Check tags against releases - env: - GH_TOKEN: ${{ github.token }} - run: | - DAYS=${{ github.event.inputs.days || 30 }} - .github/scripts/check-for-unreleased-tags.sh "$DAYS" From 7da241387f00361e20b3cdde2a04478df72316a2 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Wed, 5 Aug 2026 10:49:57 -0700 Subject: [PATCH 12/13] docs: clarify release path scoping, cross-package PRs, and the revert retitle Releases are scoped by file path, not by the title's scope, so docs(website) releases nothing while docs inside a package does. Records what happens when a PR spans two packages and when that warrants splitting it, and why the Revert retitle matters: release-please cannot parse 'Revert "..."', so a revert merged under that title ships with no version bump and no error. --- CONTRIBUTING.md | 8 ++++++-- .../content/docs/governance/adr/0027-release-please.md | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3767160d3..bde7acdc4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -132,10 +132,14 @@ PR titles must follow the [Conventional Commits](https://www.conventionalcommits | `fix` | Patch release | `fix(ts-sdk): preserve plain dates in JSON` | | `feat` | Minor release | `feat(core): add award schemas` | | `feat!` or `fix!` (or a `BREAKING CHANGE:` footer) | Breaking release (minor while pre-1.0) | `feat(cli)!: drop Node 20 support` | -| `perf`, `revert`, `docs`, `refactor`, `build` | Patch release | `docs(website): clarify filter usage` | +| `perf`, `revert`, `docs`, `refactor`, `build` | Patch release | `docs(core): clarify filter usage` | | `chore`, `ci`, `test` | No release; hidden from changelogs (a `!` marker still cuts a breaking release) | `chore(deps): bump the runtime group` | -The scope is free-form; use the package or area you touched (`core`, `cli`, `ts-sdk`, `py-sdk`, `website`, `deps`). GitHub's **Revert** button titles the new PR `Revert "..."`, which fails this check — retitle it `revert: ` before merging. See [lib/README.md](lib/README.md) for how releases are cut from this history. +The scope is free-form; use the package or area you touched (`core`, `cli`, `ts-sdk`, `py-sdk`, `website`, `deps`). Releases are scoped by path rather than by that scope: only files under `lib/core/`, `lib/cli/`, `lib/ts-sdk/`, or `lib/python-sdk/` can trigger one, so `docs(website): ...` releases nothing. Inside those paths a releasing type does release the package — `docs` included, since `README.md` ships in the published artifact. Use `chore` for a package's internal notes that shouldn't ship. + +A PR touching two packages bumps both at the title's type. That is usually right, since both packages changed; split the PR when they warrant different bumps, such as a `feat` in `lib/core/` alongside a mechanical regeneration in `lib/ts-sdk/`. + +GitHub's **Revert** button titles the new PR `Revert "..."`, which fails this check — retitle it `revert: ` before merging. release-please cannot parse `Revert "..."` either, so a revert merged under that title would ship with no version bump and no error. See [lib/README.md](lib/README.md) for how releases are cut from this history. ## Getting started diff --git a/website/src/content/docs/governance/adr/0027-release-please.md b/website/src/content/docs/governance/adr/0027-release-please.md index bf14d6352..041c9bff2 100644 --- a/website/src/content/docs/governance/adr/0027-release-please.md +++ b/website/src/content/docs/governance/adr/0027-release-please.md @@ -33,7 +33,7 @@ We will adopt [release-please](https://github.com/googleapis/release-please) in - Conventional-commit PR titles become mandatory and release-facing; a mistyped title puts a change in the wrong category (or omits it entirely), so a CI title check is required. - Contributors must know which commit types map to which version bumps. - Commits that do not follow the conventional format are invisible to release-please, so changelog entries for the period before adoption may under-report. -- A commit's type applies to every package whose files it touches; PRs that span packages need deliberate scoping. +- A commit's type applies to every package whose files it touches, so a PR spanning `lib/core/` and `lib/ts-sdk/` opens a Release PR for both at that type's level. That is usually correct — both packages changed — but a PR needs splitting when the packages warrant different bumps, such as a `feat` in one alongside a mechanical regeneration in the other. See [PR title format](https://github.com/HHS/simpler-grants-protocol/blob/main/CONTRIBUTING.md#pr-title-format). ## Criteria From 60f176ca8aaf1493e18bcb9564e2af45a3a9f15c Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Wed, 5 Aug 2026 10:58:02 -0700 Subject: [PATCH 13/13] ci: sync release baselines to the 0.7.1 / 0.8.1 release Manifest moves ts-sdk to 0.7.1 and python-sdk to 0.8.1. The Python dunder was left at 0.8.0 by the changesets bump, which pyproject.toml already moved to 0.8.1; tests/test_version.py pins the two together. --- .release-please-manifest.json | 4 ++-- lib/python-sdk/common_grants_sdk/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c338a6896..890520cbf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,6 +1,6 @@ { "lib/core": "0.4.0", "lib/cli": "0.3.8", - "lib/ts-sdk": "0.7.0", - "lib/python-sdk": "0.8.0" + "lib/ts-sdk": "0.7.1", + "lib/python-sdk": "0.8.1" } diff --git a/lib/python-sdk/common_grants_sdk/__init__.py b/lib/python-sdk/common_grants_sdk/__init__.py index 60e169737..8d4313286 100644 --- a/lib/python-sdk/common_grants_sdk/__init__.py +++ b/lib/python-sdk/common_grants_sdk/__init__.py @@ -4,7 +4,7 @@ A Python implementation of the CommonGrants protocol. """ -__version__ = "0.8.0" +__version__ = "0.8.1" from . import schemas from .client import Auth, Client, Config