{"id":51200315,"url":"https://github.com/softwarity/release-flow","last_synced_at":"2026-06-28T00:01:31.761Z","repository":{"id":363566193,"uuid":"1263907376","full_name":"softwarity/release-flow","owner":"softwarity","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-09T13:15:49.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T13:22:43.190Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/softwarity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-09T11:30:44.000Z","updated_at":"2026-06-09T13:21:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/softwarity/release-flow","commit_stats":null,"previous_names":["softwarity/release-notes-action"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/softwarity/release-flow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwarity%2Frelease-flow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwarity%2Frelease-flow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwarity%2Frelease-flow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwarity%2Frelease-flow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/softwarity","download_url":"https://codeload.github.com/softwarity/release-flow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwarity%2Frelease-flow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34872279,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-28T00:01:31.024Z","updated_at":"2026-06-28T00:01:31.743Z","avatar_url":"https://github.com/softwarity.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Release Flow — GitHub Action\n\nA small, dependency-free composite action that turns a single manual choice\n(`patch` / `minor` / `major`) into a complete release:\n\n1. **Bumps the version** in the right place for your language\n   (`package.json` for Node, or **the git tags** otherwise — no file to manage).\n2. **Resolves the release notes**: renames the unreleased\n   `## NEXT RELEASE` section to the new version number and extracts its body.\n3. **Commits and tags** that state — so the tag captures the finished notes.\n4. **Publishes a GitHub Release** from the extracted body.\n5. **Re-opens a fresh `## NEXT RELEASE` section** for the next cycle — so\n   contributors only ever *fill in* a section, never add one.\n\n\u003e The placeholder is the whole point: at authoring time you don't yet know\n\u003e whether the next release is a patch, minor or major, so you write under a\n\u003e stable `## NEXT RELEASE` heading. The action stamps the real number at release\n\u003e time, once the bump is known.\n\n## Quick start\n\n```yaml\nname: Release\n\non:\n  workflow_dispatch:            # adds a \"Run workflow\" button in the Actions tab\n    inputs:\n      bump:\n        description: 'Version bump type'\n        required: true\n        default: patch\n        type: choice            # \u003c-- this is what renders the patch/minor/major dropdown\n        options:\n          - patch\n          - minor\n          - major\n\npermissions:\n  contents: write\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.PAT_TOKEN }}   # PAT so the pushed tag can trigger a publish workflow\n      - uses: actions/setup-node@v4\n        with: { node-version: '20', cache: npm }\n      - run: npm ci \u0026\u0026 npm run build \u0026\u0026 npm test\n      - uses: softwarity/release-flow@v1\n        with:\n          bump: ${{ inputs.bump }}           # the value picked in the dropdown\n```\n\n## Choosing patch / minor / major\n\nThe bump is **not** decided by the action — *you* pick it when you launch the\nworkflow. The `workflow_dispatch` + `type: choice` block above is what GitHub\nturns into a dropdown:\n\n\u003e **Actions** tab → pick the *Release* workflow → **Run workflow** → choose\n\u003e `patch` / `minor` / `major` → **Run workflow**.\n\nThat choice arrives as `${{ inputs.bump }}` (equivalently\n`${{ github.event.inputs.bump }}`) and is forwarded to the action's `bump`\ninput. The action just trusts that string — so you can trigger it any other way\ntoo (a PR label, a `release` event, the API…) as long as you pass\n`patch | minor | major` into `bump:`. The dropdown is simply the common case.\n\nA full, copy-pasteable workflow is in [`examples/release.yml`](examples/release.yml).\n\n## Your `RELEASE_NOTES.md`\n\n```markdown\n# Release Notes\n\n## NEXT RELEASE\n\n### Features\n\n- The thing you just built\n\n---\n\n## 2.0.1\n\n- The previous release\n```\n\nDuring the cycle, contributors edit the `## NEXT RELEASE` section. On release\nwith `bump: patch`, given the last version `2.0.1`, the action produces:\n\n```markdown\n# Release Notes\n\n## NEXT RELEASE          \u003c- fresh \u0026 empty: where the NEXT release is drafted\n\n---\n\n## 2.0.2                 \u003c- was NEXT RELEASE; this is the v2.0.2 release\n\n### Features\n\n- The thing you just built\n\n---\n\n## 2.0.1\n...\n```\n\n…and a GitHub Release **v2.0.2** whose body is the `### Features …` block — the\nempty `## NEXT RELEASE` is **not** part of it: the body is sliced from the\n`## 2.0.2` section *before* the placeholder is re-added.\n\nBy default (`single-commit: true`) this is **one commit**, so the `v2.0.2` tag\nincludes the empty `## NEXT RELEASE` at the top. That's intentional — it's just\nthe (still-empty) drafting section for the next version, and it never reaches the\nGitHub Release body or the published package. Want a tag with no placeholder at\nall? Set `single-commit: false` (see [One commit per release](#one-commit-per-release-and-the-tag)).\n\nIf the notes file doesn't exist, the action creates it.\n\n## Versioning convention\n\nThe **last published** version is the starting point; the action bumps from it.\nFor Node that's `npm version \u003cbump\u003e --no-git-tag-version` (which also keeps\n`package-lock.json` in sync). With no manifest, the last published version is read\nstraight from the **git tags** — the highest `vX.Y.Z`, with moving pointers like\n`v1` ignored, and `0.0.0` if there are none — so there is no version file to keep\nin sync. A committed `.version` file is still available via `language: generic`.\n\n## Language support\n\n| `language` | Version source | Bump |\n|------------|----------------|------|\n| `node`     | `package.json` `\"version\"` | `npm version \u003cbump\u003e` |\n| `tag`      | the highest `vX.Y.Z` **git tag** (no file) | semver math |\n| `generic`  | `.version` (or `version-file`) | semver math |\n| `auto` (default) | `node` if `package.json` exists, else `tag` | — |\n\n`tag` mode needs the tags fetched — check out with `fetch-depth: 0`. `Python` /\n`Rust` / `PHP` manifests are on the roadmap behind the same interface.\n\n## Inputs\n\n| Input | Default | Description |\n|-------|---------|-------------|\n| `bump` | `patch` | `patch` \\| `minor` \\| `major` |\n| `notes-file` | `RELEASE_NOTES.md` | Path to the notes markdown file |\n| `placeholder` | `NEXT RELEASE` | Heading text of the unreleased section (no `## `) |\n| `language` | `auto` | `auto` \\| `node` \\| `tag` \\| `generic` |\n| `version-file` | `.version` | Version file for `language: generic` |\n| `tag-prefix` | `v` | Prefix prepended to the version to form the tag |\n| `create-release` | `true` | Create a GitHub Release from the notes |\n| `release-draft` | `false` | Create the Release as a draft |\n| `release-prerelease` | `false` | Mark the Release as a prerelease |\n| `push` | `true` | Push the commits and the tag |\n| `major-tag` | `false` | Also force-move the major tag (`v1`) to this release — for publishing reusable actions |\n| `single-commit` | `true` | One commit per release (fold the placeholder in; the tag then includes the empty `## NEXT RELEASE`) |\n| `commit-user-name` | `github-actions[bot]` | git `user.name` for the commits |\n| `commit-user-email` | `github-actions[bot]@users.noreply.github.com` | git `user.email` |\n| `token` | `${{ github.token }}` | Token used to create the Release (needs `contents: write`) |\n| `dry-run` | `false` | Edit files but skip commit/tag/push/release |\n\n## Outputs\n\n| Output | Description |\n|--------|-------------|\n| `version` | New version, no prefix (e.g. `2.0.2`) |\n| `previous-version` | Version before the bump |\n| `tag` | Tag created, with prefix (e.g. `v2.0.2`) |\n| `notes` | Extracted release-notes body |\n| `release-url` | URL of the created GitHub Release |\n| `notes-file-created` | `true` if the notes file was created this run |\n\n## Permissions \u0026amp; tokens\n\n- The job needs `permissions: contents: write` (push commits/tags, create the Release).\n- The **GitHub Release** is created with `token` (default `GITHUB_TOKEN`).\n- The **git push** uses whatever credentials `actions/checkout` set up. If a\n  pushed tag must trigger another workflow (e.g. an NPM publish on `push: tags`),\n  check out with a **PAT** — pushes made with `GITHUB_TOKEN` do not trigger\n  workflows.\n\n## One commit per release (and the tag)\n\nBy default (`single-commit: true`) a release is **one commit**: the resolved\nnotes (`## X.Y.Z`) and a fresh empty `## NEXT RELEASE` are folded together, and\nthat commit is tagged — so contributors pull a single commit per release. The\ntrade-off: the `RELEASE_NOTES.md` *inside the tag* shows an empty `## NEXT\nRELEASE` at the top. It's cosmetic — the GitHub Release body (extracted before)\nand the published package are unaffected.\n\nSet `single-commit: false` for a **pure tag** instead: the action tags the\n`version + resolved notes` commit (no placeholder), then re-opens `## NEXT\nRELEASE` in a *separate* follow-up commit carrying `[skip ci]`. The tag never\ncontains the placeholder, at the cost of two commits per release.\n\n(Folding with `git commit --amend` after tagging is deliberately **not** used: it\nwould leave the tag pointing at a commit *off the branch*, breaking `git\ndescribe` and GitHub's \"N commits since this release\".)\n\n## Migrating an existing `RELEASE_NOTES.md`\n\nIf your top section is currently a *guessed* next number (e.g. `## 0.2.10`),\nrename that heading once to `## NEXT RELEASE`. From then on the action fills it in.\n\n## Publishing a reusable action\n\nIf the repo you're releasing **is itself a GitHub Action** (consumed as\n`you/action@v1`), set `major-tag: true`. After tagging `vX.Y.Z`, the action also\nforce-moves the major tag (`vX`) to the same commit, so your `@v1` consumers get the\nnew release with no extra step — no `npm`, no PAT needed. release-flow uses this on\nitself (see its own [`.github/workflows/release.yml`](.github/workflows/release.yml)).\n\n## Local development\n\n```bash\nnode scripts/test.mjs     # pure-logic smoke tests (notes parsing + semver)\n```\n\nRun the whole flow without side effects on any repo with:\n\n```yaml\n- uses: softwarity/release-flow@v1\n  with: { bump: patch, dry-run: true }\n```\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftwarity%2Frelease-flow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoftwarity%2Frelease-flow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftwarity%2Frelease-flow/lists"}