diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6a6bb14..0b57a3b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,9 @@ updates: schedule: interval: weekly open-pull-requests-limit: 10 + commit-message: + prefix: build + include: scope labels: - Dependencies @@ -13,6 +16,9 @@ updates: schedule: interval: weekly open-pull-requests-limit: 10 + commit-message: + prefix: build + include: scope versioning-strategy: increase labels: - Dependencies @@ -22,6 +28,9 @@ updates: schedule: interval: weekly open-pull-requests-limit: 10 + commit-message: + prefix: build + include: scope versioning-strategy: increase labels: - Dependencies diff --git a/.github/release-starter/config.json b/.github/release-starter/config.json new file mode 100644 index 0000000..ac72ee9 --- /dev/null +++ b/.github/release-starter/config.json @@ -0,0 +1,69 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "node", + "changelog-path": "CHANGELOG.md", + "include-v-in-tag": true, + "include-component-in-tag": false, + "changelog-sections": [ + { + "type": "feat", + "section": "Added" + }, + { + "type": "fix", + "section": "Fixed" + }, + { + "type": "perf", + "section": "Changed" + }, + { + "type": "revert", + "section": "Changed" + }, + { + "type": "build", + "section": "Changed" + }, + { + "type": "docs", + "section": "Documentation", + "hidden": true + }, + { + "type": "style", + "section": "Styles", + "hidden": true + }, + { + "type": "chore", + "section": "Miscellaneous", + "hidden": true + }, + { + "type": "refactor", + "section": "Code Refactoring", + "hidden": true + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "extra-files": [ + { + "type": "generic", + "path": "project-name-features.php" + } + ] + } + } +} diff --git a/.github/release-starter/manifest.json b/.github/release-starter/manifest.json new file mode 100644 index 0000000..37fcefa --- /dev/null +++ b/.github/release-starter/manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.0.0" +} diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index a6668fc..8349dfc 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -17,11 +17,8 @@ jobs: runs-on: ubuntu-latest permissions: pull-requests: read - statuses: write steps: - name: Validate pull request title uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - wip: true + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/release-starter.yml b/.github/workflows/release-starter.yml new file mode 100644 index 0000000..fe65506 --- /dev/null +++ b/.github/workflows/release-starter.yml @@ -0,0 +1,42 @@ +name: Release (skeleton) + +# release-please reads the Conventional Commit subjects squash-merged to main and keeps one +# release pull request open that bumps the version and writes CHANGELOG.md. Merging that pull +# request tags vX.Y.Z on main and publishes the GitHub release with the changelog entry as notes. +# +# The release pull request is opened with GITHUB_TOKEN, so GitHub starts no workflow runs on it +# (no tests, no PR title check). That is why neither check is a required status check. Switching +# the token below to a GitHub App token would give the release pull request its CI runs. +# +# This file releases the skeleton itself, from the config and manifest in .github/release-starter/. +# Init cleanup removes all of them, so projects made from the skeleton don't inherit them. + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +# Serialise runs so two merges in quick succession can't race on the release pull request. +concurrency: + group: release + cancel-in-progress: false + +jobs: + release-please: + name: Release Please + runs-on: ubuntu-latest + permissions: + contents: write + issues: write + pull-requests: write + steps: + - name: Open or update the release pull request, or publish the release + uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 + with: + token: ${{ github.token }} + config-file: .github/release-starter/config.json + manifest-file: .github/release-starter/manifest.json diff --git a/CHANGELOG.md b/CHANGELOG.md index bc4956c..f785df6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + ## Unreleased ## 1.0.0 - 2026-10-01 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 542a459..e755a95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,16 +32,16 @@ npm run lint:css Then: - If you changed an example, a module, `Main::CLASSES` or `bin/scaffold.config.js`, validate init in a disposable clone (see [release validation](docs/internal/maintenance.md#release-validation)). -- If you changed behaviour a developer sees, update the guide that describes it, and `CHANGELOG.md` under **Unreleased**. +- If you changed behaviour a developer sees, update the guide that describes it. Don't edit `CHANGELOG.md`: release-please writes it from the PR title. - If you changed the `init` or `scaffold` workflow, update both the Claude skill and the Copilot prompt. ## Pull request checklist - [ ] Tests cover new or changed behaviour (written first). - [ ] The checks above pass. -- [ ] Documentation and `CHANGELOG.md` are updated. +- [ ] Documentation is updated. - [ ] Placeholders (`Project Name` / `project-name` / `Project_Name`) are intact. -- [ ] Commits follow [Conventional Commits](https://www.conventionalcommits.org/) (enforced by the commit-message hook). +- [ ] Commits follow [Conventional Commits](https://www.conventionalcommits.org/) (enforced by the commit-message hook), and so does the PR title (checked by `pr-title.yml`): pull requests are squash-merged, and the title becomes the commit on `main` and the changelog entry. - [ ] `graphify-out/graph.json` is not part of the change unless the pull request is a deliberate baseline refresh. Use the pull request template and describe how you verified the change. diff --git a/bin/scaffold.config.js b/bin/scaffold.config.js index 846bb3f..c82a648 100644 --- a/bin/scaffold.config.js +++ b/bin/scaffold.config.js @@ -102,7 +102,7 @@ module.exports = { { path: 'package.json', kind: 'json', key: 'version' }, ], - steps: { composer: true, cleanup: false, git: true, hooks: true }, + steps: { composer: true, cleanup: true, git: true, hooks: true }, // Dep/build-config-carrying capabilities. Toggling adds/removes their deps. featuresDir: 'bin/features', @@ -228,6 +228,16 @@ module.exports = { ], }, + // The skeleton's own release workflow, config and manifest. Cleanup also runs + // on re-init and --clean, so both paths are named for the skeleton: a + // project's own release-please files never live there. + cleanup: { + targets: [ + '.github/release-starter', + '.github/workflows/release-starter.yml', + ], + }, + docsUrl: 'https://github.com/rtCamp/plugin-elementary/blob/main/docs/index.md', repoUrl: 'https://github.com/rtCamp/plugin-elementary', diff --git a/docs/initialization.md b/docs/initialization.md index c9a29b0..79accf5 100644 --- a/docs/initialization.md +++ b/docs/initialization.md @@ -74,7 +74,7 @@ What each set contains is listed in [Included features](features.md). | Kept example sets | Stay as working references. Their `// wp:example:` markers are removed. | | Optional features | `hmr` edits `.env.local`; `tailwind` adds `src/css/tailwind.css`, `postcss.config.js`, dependencies and flips the Tailwind constant. | | State | `.wp-scaffold.json` records identity and feature choices. Do not edit it by hand. | -| Cleanup | Nothing is removed. Copilot files, workflows and templates under `.github/` stay. | +| Cleanup | Only the skeleton's own release automation is removed: `.github/workflows/release-starter.yml` and `.github/release-starter/`. Copilot files, the other workflows and templates under `.github/` stay. | | AI instructions | `sync-ai` refreshes `.github/instructions/framework-php.instructions.md` from the installed framework. | Init does not generate a translation template. Run `npm run pot` when preparing translations. diff --git a/docs/internal/maintenance.md b/docs/internal/maintenance.md index afa5ae1..bcddb19 100644 --- a/docs/internal/maintenance.md +++ b/docs/internal/maintenance.md @@ -81,6 +81,18 @@ Use the explicit commands in [Local development](../local-development.md#check-a Put journey and link-check results in the pull request description or release hand-off: the date, skeleton commit (and any uncommitted changes), dependency revisions, Node/PHP versions, environment and local overrides. Record one row per route, init mode, removal or feature selection, scaffold or manual example, build and check: the command, the expected result, the actual result, and **pass / fail / blocked** with evidence. +## Releasing the skeleton + +Releases of the skeleton itself are cut by [release-please](https://github.com/googleapis/release-please) (`.github/workflows/release-starter.yml`), from the config and manifest in `.github/release-starter/`. Init cleanup removes the workflow and that folder, so projects made from the skeleton don't inherit them; projects release by hand with `npm run release:bump`, `npm run release:changelog` and `npm run release:zip`. + +1. Pull requests are squash-merged, so each PR title becomes one Conventional Commit on `main`. release-please reads those subjects and keeps a release pull request open that bumps the version in `package.json`, `package-lock.json` and the plugin header in `project-name-features.php` (between the `x-release-please-start-version` and `x-release-please-end` lines), and writes the next `CHANGELOG.md` section. +2. The title's type decides the version: `fix:` is a patch, `feat:` a minor, and `feat!:` (or a `BREAKING CHANGE:` footer) a major. Anything that changes what init generates, renames a script or a file a project relies on, or raises a minimum version is a `feat!:`. `docs:`, `test:`, `ci:`, `chore:` and `refactor:` cut no release. Dependabot pull requests are prefixed `build` (see `.github/dependabot.yml`), which lists them under Changed and makes them releasable: `composer create-project` installs the latest tag, so a dependency fix only reaches new projects once it is released. Each merge only updates the open release pull request, so merge that when you want to release, for example after a batch of security updates. +3. When you want to release, run the [release validation](#release-validation) against the release pull request's branch, reword its entries if a title reads badly, then squash-merge it. release-please tags `vX.Y.Z` and publishes the GitHub release with that section as the notes. + +The release pull request is opened with `GITHUB_TOKEN`, so GitHub starts no workflow runs on it, and the PR title check is not a required status check for that reason. A GitHub App token in the workflow would give the release pull request its own runs. Never push a `v*` tag or edit `.github/release-starter/manifest.json` by hand. + +`PROJECT_NAME_FEATURES_VERSION` stays at `1.0.0` on purpose. Init rewrites the header and `package.json` to a new project's version but not the constant, so a starter release that moved it would give every new project a constant that disagrees with its header. + ## Dependency updates | Dependency | Where it is declared | When bumping | diff --git a/project-name-features.php b/project-name-features.php index 5a605f1..cfb2b6b 100644 --- a/project-name-features.php +++ b/project-name-features.php @@ -10,7 +10,9 @@ * Plugin Name: Project Name Features * Plugin URI: https://rtcamp.com * Description: All backend functionality: post types, taxonomies, blocks, REST endpoints, settings, shortcodes, and custom roles. + * x-release-please-start-version * Version: 1.0.0 + * x-release-please-end * Author: rtCamp * Author URI: https://rtcamp.com * License: GPL-2.0-or-later