Compare commits

..
20 Commits
Author SHA1 Message Date
github-actions[bot] 8f7aee55a0 v2.0.0-next.2 2026-06-24 10:38:52 +00:00
changesets-org-version-bot[bot]andgithub-actions[bot] b12c36a7bf Version Packages (next) (#671)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-24 10:28:35 +00:00
Mateusz Burzyński 5a8b9b721b Avoid putting the github token in the global .netrc file (#670)
* Avoid putting the github token in the global `.netrc` file

* fix wording
2026-06-24 10:07:11 +00:00
changesets-org-version-bot[bot]andgithub-actions[bot] 0490518932 Version Packages (next) (#664)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-23 11:29:25 +00:00
Mateusz Burzyński dc29b738b5 Fix the /version subaction to not crash on missing pr-base-branch input (#666)
* use `getOptionalInput` consistently

* introduce `getRequiredInput`

* add changeset
2026-06-23 11:26:43 +00:00
Mateusz Burzyński ccb38113ea Fix the computed publish plan path passed internally to changeset pack by the /pack subaction (#663)
* Fix the computed publish plan path passed internally to `changeset pack` by the `/pack` subaction

* fix it
2026-06-23 10:00:39 +00:00
Mateusz Burzyński cf37561cd9 Fix description of the /version's script input (#665) 2026-06-23 09:55:13 +00:00
Mateusz Burzyński 5c88881fff Fixed usage of --from-publish-plan flag used by the /pack subaction (#662) 2026-06-23 09:25:48 +00:00
Mateusz Burzyński dad5baf18b Pass the publishing app token to the release step (#661) 2026-06-23 07:31:13 +00:00
5210f6da68 Version Packages (next) (#660)
* Version Packages (next)

* fmt

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
2026-06-23 06:56:02 +00:00
Bjorn LuandMateusz Burzyński a12d90de83 Add select-mode, version, pack and publish sub-actions (#656)
* Add select-mode, version, and publish sub-actions

* Rename

* No github-token needed for select-mode for now

* extend select-mode with publish-plan check and upload

* Rename base-branch to pr-base-branch

* add `pack` subaction

* `--from-pack` support in the publish subaction

* rename CLI arg

* update rolldown config

* rename pack dir input/output

* tweak retendion days

---------

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
2026-06-23 06:42:37 +00:00
Bjorn Lu 5649ff41b8 Remove cwd option (#659) 2026-06-23 06:35:46 +00:00
Mateusz Burzyński 4f718b5921 Removed compatibility support for old Changesets v1 (#657) 2026-06-15 08:03:20 +00:00
Mateusz Burzyński 1bf7c2782b Enter pre mode (#652) 2026-06-10 08:23:13 +00:00
Mateusz Burzyński 310b913a84 Don't update main release lines for prerelease versions (#653) 2026-06-10 01:40:16 +00:00
dependabot[bot] 7e238815d9 Bump the github-actions group across 1 directory with 3 updates (#648)
Bumps the github-actions group with 3 updates in the / directory: [actions/checkout](https://github.com/actions/checkout), [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action) and [actions/create-github-app-token](https://github.com/actions/create-github-app-token).


Updates `actions/checkout` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)

Updates `zizmorcore/zizmor-action` from 0.5.3 to 0.5.6
- [Release notes](https://github.com/zizmorcore/zizmor-action/releases)
- [Commits](https://github.com/zizmorcore/zizmor-action/compare/b1d7e1fb5de872772f31590499237e7cce841e8e...5f14fd08f7cf1cb1609c1e344975f152c7ee938d)

Updates `actions/create-github-app-token` from 3.1.1 to 3.2.0
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Changelog](https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/create-github-app-token/compare/1b10c78c7865c340bc4f6099eb2f838309f1e8c3...bcd2ba49218906704ab6c1aa796996da409d3eb1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/create-github-app-token
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: zizmorcore/zizmor-action
  dependency-version: 0.5.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-09 16:46:46 +00:00
dependabot[bot] c211f5efab Bump the production-dependencies group with 2 updates (#649)
Bumps the production-dependencies group with 2 updates: [semver](https://github.com/npm/node-semver) and [tinyexec](https://github.com/tinylibs/tinyexec).


Updates `semver` from 7.8.0 to 7.8.1
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.8.0...v7.8.1)

Updates `tinyexec` from 1.1.2 to 1.2.4
- [Release notes](https://github.com/tinylibs/tinyexec/releases)
- [Commits](https://github.com/tinylibs/tinyexec/compare/1.1.2...1.2.4)

---
updated-dependencies:
- dependency-name: semver
  dependency-version: 7.8.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: tinyexec
  dependency-version: 1.2.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-09 16:45:27 +00:00
Bjorn LuandMateusz Burzyński 70a83f1bd1 Update CI for prerelease and backports (#646)
* Update CI for prerelease and backports

* Fix

* Update permissions

* Apply suggestions from code review

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>

* comment

---------

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
2026-06-09 09:45:37 +00:00
Bjorn Lu 5273feb188 Update dependabot config (#647) 2026-06-09 08:06:36 +00:00
Adam Haglundandbluwy 8b7799e7d8 update note regarding pull_request_target (#644)
* Update README with pull_request_target usage guidelines

Add caution and warning notes regarding the use of pull_request_target event.

* Update README with pull_request_target caution

Added caution about using pull_request_target and alternative options.

* update wording for executing untrusted code

* Format

---------

Co-authored-by: bluwy <bjornlu.dev@gmail.com>
2026-06-08 15:05:55 +00:00
54 changed files with 2232 additions and 495 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@changesets/action": patch
---
Fix the computed publish plan path passed internally to `changeset pack` by the `/pack` subaction.
+5
View File
@@ -0,0 +1,5 @@
---
"@changesets/action": minor
---
Add new `/select-mode`, `/version`, and `/publish` sub-actions to better control version and publish steps
+5
View File
@@ -0,0 +1,5 @@
---
"@changesets/action": major
---
Removed compatibility support for old Changesets v1.
+5
View File
@@ -0,0 +1,5 @@
---
"@changesets/action": patch
---
Authenticate git CLI pushes with the configured GitHub token using Git extra headers instead of writing to a global `.netrc` file.
+5
View File
@@ -0,0 +1,5 @@
---
"@changesets/action": patch
---
Derive the Git server URL from the GitHub Actions context when configuring git CLI authentication to support GitHub Enterprise Server setups.
+5
View File
@@ -0,0 +1,5 @@
---
"@changesets/action": major
---
Remove `cwd` option for `changesets/action`. Use the step `working-directory` option instead to change the directory.
+17
View File
@@ -0,0 +1,17 @@
{
"mode": "pre",
"tag": "next",
"initialVersions": {
"@changesets/action": "1.9.0"
},
"changesets": [
"all-cars-grin",
"eight-clocks-pull",
"eighty-doors-swim",
"git-cli-auth-header",
"git-cli-server-url",
"lemon-turtles-itch",
"ready-mangos-boil",
"silent-tips-dance"
]
}
+5
View File
@@ -0,0 +1,5 @@
---
"@changesets/action": patch
---
Fixed usage of `--from-publish-plan` flag used by the `/pack` subaction
+5
View File
@@ -0,0 +1,5 @@
---
"@changesets/action": patch
---
Fix the `/version` subaction to not crash on missing `pr-base-branch` input. This input is meant to be optional.
+27 -8
View File
@@ -1,13 +1,32 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
interval: "monthly"
cooldown:
default-days: 7
open-pull-requests-limit: 10
versioning-strategy: increase
groups:
production-dependencies:
dependency-type: "production"
update-types:
- minor
- patch
development-dependencies:
dependency-type: "development"
update-types:
- minor
- patch
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
cooldown:
default-days: 7
open-pull-requests-limit: 5
groups:
github-actions:
patterns:
- "*"
+11 -11
View File
@@ -7,6 +7,9 @@ on:
merge_group:
branches:
- main
- next
# merge group rulesets don't allow wildcards so in settings each maintenance branch needs to be added separately
- "maintenance/v*" # branch rulesets don't support v[0-9]+
permissions:
contents: read
@@ -24,12 +27,12 @@ jobs:
security-events: write # allow writing security events
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
with:
persona: pedantic
annotations: true
@@ -37,12 +40,11 @@ jobs:
test:
name: Test
timeout-minutes: 20
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Check out repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -53,12 +55,11 @@ jobs:
typecheck:
name: Typecheck
timeout-minutes: 20
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Check out repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -69,12 +70,11 @@ jobs:
lint:
name: Lint
timeout-minutes: 20
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Check out repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
+12 -10
View File
@@ -4,6 +4,8 @@ on:
push:
branches:
- main
- next
- "maintenance/v*" # branch rulesets don't support v[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -21,12 +23,10 @@ jobs:
outputs:
hasChangesets: ${{ steps.changesets.outputs.hasChangesets }}
permissions:
contents: write # to create release (changesets/action)
issues: write # to post issue comments (changesets/action)
pull-requests: write # to create pull request (changesets/action)
contents: read # to check out repo (actions/checkout)
steps:
- name: Check out repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -37,11 +37,13 @@ jobs:
- name: Build
run: pnpm build
- uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
client-id: ${{ vars.APP_CLIENT_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
permission-contents: write # to create version commits (changesets/action)
permission-pull-requests: write # to create pull request (changesets/action)
- name: Create or update release pull request
id: changesets
@@ -60,18 +62,17 @@ jobs:
permissions:
contents: write # to create release and have it authored by github-actions (changesets/action)
steps:
- uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
client-id: ${{ vars.APP_CLIENT_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
permission-contents: write # to bypass branch and tag protection rules (release.ts)
- name: Check out repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: true
# we need this token to bypass the branch and tag protection rules
token: ${{ steps.app-token.outputs.token }}
persist-credentials: false
- uses: ./.github/actions/ci-setup
with:
@@ -83,4 +84,5 @@ jobs:
- name: Publish to marketplace
uses: ./
with:
github-token: ${{ steps.app-token.outputs.token }}
publish: pnpm release
+10 -1
View File
@@ -122,8 +122,15 @@ jobs:
pull-requests: write # to comment on the pull request with the release result (changesets/action)
steps:
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
client-id: ${{ vars.APP_CLIENT_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
permission-contents: write # to push the release snapshot branch (release-pr.ts)
- name: Check out repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -173,6 +180,8 @@ jobs:
- name: Release snapshot version
run: pnpm release:pr
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
- name: Add success reaction
run: gh api "/repos/$GH_REPO/issues/comments/$COMMENT_ID/reactions" -f content='rocket'
+30
View File
@@ -1,5 +1,35 @@
# @changesets/action
## 2.0.0-next.2
### Patch Changes
- [#670](https://github.com/changesets/action/pull/670) [`5a8b9b7`](https://github.com/changesets/action/commit/5a8b9b721bf683e3bf9bd72ea92a685dc024147b) Thanks [@Andarist](https://github.com/Andarist)! - Authenticate git CLI pushes with the configured GitHub token using Git extra headers instead of writing to a global `.netrc` file.
- [#670](https://github.com/changesets/action/pull/670) [`5a8b9b7`](https://github.com/changesets/action/commit/5a8b9b721bf683e3bf9bd72ea92a685dc024147b) Thanks [@Andarist](https://github.com/Andarist)! - Derive the Git server URL from the GitHub Actions context when configuring git CLI authentication to support GitHub Enterprise Server setups.
## 2.0.0-next.1
### Patch Changes
- [#663](https://github.com/changesets/action/pull/663) [`ccb3811`](https://github.com/changesets/action/commit/ccb38113eaaa4a13eb6aabe09520e49cc8519b84) Thanks [@Andarist](https://github.com/Andarist)! - Fix the computed publish plan path passed internally to `changeset pack` by the `/pack` subaction.
- [#662](https://github.com/changesets/action/pull/662) [`5c88881`](https://github.com/changesets/action/commit/5c88881fff1f0d8b055d4a2d3a74a8ce843db6f3) Thanks [@Andarist](https://github.com/Andarist)! - Fixed usage of `--from-publish-plan` flag used by the `/pack` subaction
- [#666](https://github.com/changesets/action/pull/666) [`dc29b73`](https://github.com/changesets/action/commit/dc29b738b532d9e95f46b9bf493e2e3fdf48a7ed) Thanks [@Andarist](https://github.com/Andarist)! - Fix the `/version` subaction to not crash on missing `pr-base-branch` input. This input is meant to be optional.
## 2.0.0-next.0
### Major Changes
- [#657](https://github.com/changesets/action/pull/657) [`4f718b5`](https://github.com/changesets/action/commit/4f718b5921490b1efeb015a81abb6a4633892851) Thanks [@Andarist](https://github.com/Andarist)! - Removed compatibility support for old Changesets v1.
- [#659](https://github.com/changesets/action/pull/659) [`5649ff4`](https://github.com/changesets/action/commit/5649ff41b8eb1b7a281011e45df781c6d4628fd0) Thanks [@bluwy](https://github.com/bluwy)! - Remove `cwd` option for `changesets/action`. Use the step `working-directory` option instead to change the directory.
### Minor Changes
- [#656](https://github.com/changesets/action/pull/656) [`a12d90d`](https://github.com/changesets/action/commit/a12d90de8394e63c952622b2fd3f98c8729c48b0) Thanks [@bluwy](https://github.com/bluwy)! - Add new `/select-mode`, `/version`, and `/publish` sub-actions to better control version and publish steps
## 1.9.0
### Minor Changes
+8 -5
View File
@@ -1,5 +1,8 @@
# Changesets Release Action
> [!IMPORTANT]
> This is the development branch for `changesets/action` v2 compatible with Changesets v3. For the v1 code compatible with Changesets v2, check out the [`maintenance/v1`](https://github.com/changesets/action/tree/maintenance/v1) branch.
This action for [Changesets](https://github.com/changesets/changesets) creates a pull request with all of the package versions updated and changelogs updated and when there are new changesets on [your configured `baseBranch`](https://github.com/changesets/changesets/blob/main/docs/config-file-options.md#basebranch-git-branch-name), the PR will be updated. When you're ready, you can merge the pull request and you can either publish the packages to npm manually or setup the action to do it for you.
There are also sub-actions hosted in this repository. Check out their respective READMEs for more details:
@@ -62,7 +65,7 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Create Release Pull Request
uses: changesets/action@v1
uses: changesets/action@v2
```
#### With Publishing
@@ -100,7 +103,7 @@ jobs:
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
uses: changesets/action@v2
with:
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: pnpm release
@@ -167,7 +170,7 @@ jobs:
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
uses: changesets/action@v2
- name: Publish
if: steps.changesets.outputs.hasChangesets == 'false'
@@ -211,7 +214,7 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Create Release Pull Request
uses: changesets/action@v1
uses: changesets/action@v2
with:
# this expects you to have a npm script called version that runs some logic and then calls `changeset version`.
version: pnpm version
@@ -222,7 +225,7 @@ jobs:
If you are using [Yarn Plug'n'Play](https://yarnpkg.com/features/pnp), you should use a custom `version` command so that the action can resolve the `changeset` CLI:
```yaml
- uses: changesets/action@v1
- uses: changesets/action@v2
with:
version: yarn changeset version
# ...
-3
View File
@@ -14,9 +14,6 @@ inputs:
version:
description: "The command to update version, edit CHANGELOG, read and delete changesets. Default to `changeset version` if not provided"
required: false
cwd:
description: Sets the cwd for the node process. Default to `process.cwd()`
required: false
commit:
description: |
The commit message. Default to `Version Packages`
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-18
View File
File diff suppressed because one or more lines are too long
+2 -150
View File
File diff suppressed because one or more lines are too long
-96
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
import{_ as e,v as t}from"./get-packages.cjs-Dvdg-NAr.js";import{f as n,n as r,s as i,t as a}from"./utils-DQTbg3yF.js";import o from"node:fs/promises";import s from"node:path";import c from"node:os";try{await l()}catch(t){e(t.message)}async function l(){let e=i(`publish-plan-artifact-id`),r=process.cwd(),a=process.env.RUNNER_TEMP??await o.realpath(c.tmpdir()),l=s.join(a,`changeset-pack-${Date.now()}`);await u(r,{outDir:l,publishPlanPath:e?await d(a,Number(e)):void 0});let p=await n.uploadArtifact(`changeset-pack-${Date.now()}`,await f(l),l,{retentionDays:30});if(p.id===void 0)throw Error(`Packed artifact upload did not return an artifact id`);t(`pack-dir-artifact-id`,String(p.id))}async function u(e,t){let n=[`pack`,`--out-dir`,t.outDir];t.publishPlanPath&&n.push(`--from-publish-plan`,t.publishPlanPath),await r(n,{cwd:e,env:process.env})}async function d(e,t){let n=await a(e,t,`changeset-publish-plan`);return s.join(n,`publish-plan.json`)}async function f(e){let t=await o.readdir(e,{withFileTypes:!0});return(await Promise.all(t.map(async t=>{let n=s.join(e,t.name);return t.isDirectory()?f(n):[n]}))).flat()}export{};
+1 -1
View File
@@ -1 +1 @@
import{a as e,c as t,n,o as r,s as i,t as a}from"./github-DTObde6f.js";try{await o()}catch(e){i(e.message)}async function o(){let i=a.payload.pull_request;if(!i)throw Error("This action should only be run on `pull_request_target` or `pull_request` events");let o=e(`github-token`,{required:!0}),c=e(`body`,{required:!0}),l=e(`update-id`),u=l?s(l):null,d=u?`${u}\n\n${c}`:c,f={repo:i.base.repo.name,owner:i.base.repo.owner.login,issue_number:i.number,body:d},p=n(o),m;u&&(r(`Checking for existing comment...`),m=await p.rest.issues.listComments({repo:i.base.repo.name,owner:i.base.repo.owner.login,issue_number:i.number}).then(e=>e.data.find(e=>e.body?.includes(u))?.id)),m?(r(`Updating existing comment (id: ${m})...`),await p.rest.issues.updateComment({...f,comment_id:m}),t(`comment-id`,m)):(r(`Creating new comment...`),t(`comment-id`,(await p.rest.issues.createComment(f)).data.id)),r(`Done!`)}function s(e){let t=`changesets-action-pr-comment`;return t===e?`<!-- ${t} -->`:`<!-- ${t}:${e} -->`}export{};
import{_ as e,c as t,g as n,l as r,v as i}from"./get-packages.cjs-Dvdg-NAr.js";import{c as a,s as o}from"./utils-DQTbg3yF.js";try{await s()}catch(t){e(t.message)}async function s(){let e=t.payload.pull_request;if(!e)throw Error("This action should only be run on `pull_request_target` or `pull_request` events");let s=a(`github-token`),l=a(`body`),u=o(`update-id`),d=u?c(u):null,f=d?`${d}\n\n${l}`:l,p={repo:e.base.repo.name,owner:e.base.repo.owner.login,issue_number:e.number,body:f},m=r(s),h;d&&(n(`Checking for existing comment...`),h=await m.rest.issues.listComments({repo:e.base.repo.name,owner:e.base.repo.owner.login,issue_number:e.number}).then(e=>e.data.find(e=>e.body?.includes(d))?.id)),h?(n(`Updating existing comment (id: ${h})...`),await m.rest.issues.updateComment({...p,comment_id:h}),i(`comment-id`,h)):(n(`Creating new comment...`),i(`comment-id`,(await m.rest.issues.createComment(p)).data.id)),n(`Done!`)}function c(e){let t=`changesets-action-pr-comment`;return t===e?`<!-- ${t} -->`:`<!-- ${t}:${e} -->`}export{};
+10 -10
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
import{_ as e,m as t,v as n}from"./get-packages.cjs-Dvdg-NAr.js";import{r,t as i}from"./run-Cekyj2rz.js";import{c as a,s as o,t as s}from"./utils-DQTbg3yF.js";import c from"node:fs/promises";import l from"node:os";try{await u()}catch(t){e(t.message)}async function u(){let e=a(`github-token`),u=o(`script`),d=o(`pack-dir-artifact-id`),f=t(`create-github-releases`),p=process.cwd(),m=await i({script:u,github:new r({cwd:p,githubToken:e,commitMode:`github-api`}),createGithubReleases:f,cwd:p,fromPackDir:d?await s(process.env.RUNNER_TEMP??await c.realpath(l.tmpdir()),Number(d),`changeset-pack`):void 0});if(m.published?(n(`published`,`true`),n(`published-packages`,JSON.stringify(m.publishedPackages))):n(`published`,`false`),m.exitCode!==0)throw Error(`Publish command exited with code ${m.exitCode}${m.published?`, but some packages were published: ${m.publishedPackages.map(e=>`${e.name}@${e.version}`).join(`, `)}`:``}`)}export{};
+1
View File
@@ -0,0 +1 @@
import{o as e,t}from"./changesets-read.esm-D6fW_L6x.js";async function n(n=process.cwd()){let r=await e(n),i=await t(n);if(r!==void 0&&r.mode===`pre`){let e=new Set(r.changesets);return{preState:r,changesets:i.filter(t=>!e.has(t.id))}}return{preState:void 0,changesets:i}}export{n as t};
+149
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
import{_ as e,v as t}from"./get-packages.cjs-Dvdg-NAr.js";import{f as n,n as r}from"./utils-DQTbg3yF.js";import{t as i}from"./readChangesetState-D8jsqLyj.js";import a from"node:fs/promises";import o from"node:path";import s from"node:os";try{await c()}catch(t){e(t.message)}async function c(){let e=await l();if(t(`mode`,e.mode),e.mode===`publish`){let r=await n.uploadArtifact(o.basename(e.publishPlanPath,`.json`),[e.publishPlanPath],o.dirname(e.publishPlanPath),{skipArchive:!0,retentionDays:30});if(r.id===void 0)throw Error(`Publish plan artifact upload did not return an artifact id`);t(`publish-plan-artifact-id`,String(r.id))}}async function l(){let{changesets:e}=await i();if(e.length>0)return e.some(e=>e.releases.length>0)?{mode:`version`}:{mode:`none`};let t=process.cwd(),n=o.join(process.env.RUNNER_TEMP??await a.realpath(s.tmpdir()),`changeset-publish-plan-${Date.now()}`,`publish-plan.json`);return await r([`publish-plan`,`--output`,n],{cwd:t,env:process.env}),(await u(n)).length===0?{mode:`none`}:{mode:`publish`,publishPlanPath:n}}async function u(e){let t;try{t=await a.readFile(e,`utf8`)}catch(t){throw Error(`Failed to read publish plan at ${e}`,{cause:t})}let n;try{n=JSON.parse(t)}catch(t){throw Error(`Failed to parse publish plan at ${e}`,{cause:t})}if(typeof n!=`object`||!n||!(`version`in n)||typeof n.version!=`number`||!(`plan`in n)||!Array.isArray(n.plan))throw Error(`Invalid publish plan at ${e}: expected { version: number; plan: unknown[] }`);return n.plan}export{};
+80
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
import{_ as e,g as t,m as n,v as r}from"./get-packages.cjs-Dvdg-NAr.js";import{n as i,r as a}from"./run-Cekyj2rz.js";import{c as o,s}from"./utils-DQTbg3yF.js";try{await c()}catch(t){e(t.message)}async function c(){let e=o(`github-token`),c=s(`script`),l=o(`commit-message`),u=o(`pr-title`),d=s(`pr-draft`),f=s(`pr-base-branch`),p=s(`commit-mode`)??`git-cli`,m=n(`setup-git-user`);if(d!==void 0&&d!==`always`&&d!==`create`)throw Error(`Invalid pr-draft input: ${d}`);if(p!==`git-cli`&&p!==`github-api`)throw Error(`Invalid commit-mode input: ${p}`);let h=process.cwd(),g=new a({cwd:h,githubToken:e,commitMode:p});m&&(t(`setting git user`),await g.setupUser());let{pullRequestNumber:_}=await i({script:c,github:g,cwd:h,prTitle:u,commitMessage:l,hasPublishScript:!0,prDraft:d,branch:f});r(`pr-number`,String(_))}export{};
+3
View File
@@ -0,0 +1,3 @@
# changesets/action/pack
TODO
+15
View File
@@ -0,0 +1,15 @@
name: Changesets - Pack
description: Pack publishable packages into tarballs
runs:
using: node24
main: ../dist/pack.js
inputs:
publish-plan-artifact-id:
description: "Artifact id for a publish plan generated by the select-mode subaction"
required: false
outputs:
pack-dir-artifact-id:
description: "Artifact id for the packed output directory"
branding:
icon: package
color: blue
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@changesets/action",
"version": "1.9.0",
"version": "2.0.0-next.2",
"license": "MIT",
"type": "module",
"main": "dist/index.js",
@@ -17,6 +17,7 @@
"release:pr": "node --experimental-strip-types ./scripts/release-pr.ts"
},
"dependencies": {
"@actions/artifact": "^6.2.1",
"@actions/core": "^3.0.1",
"@actions/exec": "^3.0.0",
"@actions/github": "^9.1.1",
@@ -30,8 +31,8 @@
"@octokit/plugin-throttling": "^11.0.3",
"human-id": "^4.1.3",
"markdown-table": "^3.0.4",
"semver": "^7.8.0",
"tinyexec": "^1.1.2"
"semver": "^7.8.1",
"tinyexec": "^1.2.4"
},
"devDependencies": {
"@changesets/changelog-github": "^0.7.0",
+968 -18
View File
File diff suppressed because it is too large Load Diff
+15 -5
View File
@@ -2,7 +2,18 @@
This action generates the changesets status in PRs, e.g. whether it has changeset files and which packages will be released if the PR is merged.
It requires the repo to be checked out, and automatically fetches the PR head ref into a temporary detached worktree in order to infer the changed files and packages. It also requires the [`pull_request_target`](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target) event to be triggered in order to have permissions to comment on the PR and to work in PRs from forks.
It requires the repo to be checked out, and automatically fetches the PR head ref into a temporary detached worktree in order to infer the changed files and packages.
See the [action metadata](action.yml) for details on the inputs and outputs.
> [!CAUTION]
> **This action uses `pull_request_target` by default to support PRs from forks.**
>
> **Do not run untrusted code** when using the `pull_request_target` event.
>
> The example below only **_checks out_** and **_reads_** code, and does not **_execute_** any code from the fork.
>
> Read more about the `pull_request_target` event in the [GitHub documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target).
You can also use the [`pull_request`](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request) event if you prefer to lock permissions down and not run for PRs from forks. Make sure to add an if check to prevent the action from failing in fork PRs:
@@ -13,10 +24,7 @@ jobs:
# ...
```
See the [action metadata](action.yml) for details on the inputs and outputs.
> [!WARNING]
> **Do not run untrusted code** when using the `pull_request_target` event. The example below only checks out code and does not run any code from the PR. Read more about the `pull_request_target` event in the [GitHub documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target).
You can also use the [Changesets Bot](https://github.com/apps/changeset-bot) if you don't want an extra action in your repo, or are worried about the `pull_request_target` event.
## Example setup
@@ -27,6 +35,8 @@ name: Comment Changesets status in PRs
on:
pull_request_target:
permissions: {} # require explicitly stating all permissions in each job
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
+3
View File
@@ -0,0 +1,3 @@
# changesets/action/publish
TODO
+29
View File
@@ -0,0 +1,29 @@
name: Changesets - Publish
description: Publish packages to npm
runs:
using: node24
main: ../dist/publish.js
inputs:
github-token:
description: "The GitHub token to use for authentication. Defaults to the GitHub-provided token."
required: false
default: ${{ github.token }}
script:
description: "The command to use to publish packages"
required: false
pack-dir-artifact-id:
description: "Artifact id for packed publish output generated by the pack subaction"
required: false
create-github-releases:
description: "Whether to create Github releases after publish"
required: false
default: true
outputs:
published:
description: "A boolean value to indicate whether a publishing has happened or not"
published-packages:
description: >
A JSON array to present the published packages. The format is `[{"name": "@xx/xx", "version": "1.2.0"}, {"name": "@xx/xy", "version": "0.8.9"}]`
branding:
icon: package
color: blue
+6 -2
View File
@@ -3,8 +3,12 @@ import { defineConfig } from "rolldown";
export default defineConfig({
input: {
index: "src/index.ts",
["pr-status"]: "src/pr-status/index.ts",
["pr-comment"]: "src/pr-comment/index.ts",
pack: "src/pack/index.ts",
"pr-status": "src/pr-status/index.ts",
"pr-comment": "src/pr-comment/index.ts",
"select-mode": "src/select-mode/index.ts",
version: "src/version/index.ts",
publish: "src/publish/index.ts",
},
output: {
dir: "dist",
+19 -6
View File
@@ -1,9 +1,21 @@
import { Buffer } from "node:buffer";
import path from "node:path";
import { exec } from "@actions/exec";
import pkgJson from "../package.json" with { type: "json" };
const tag = `v${pkgJson.version}`;
const releaseLine = "pr-release";
const githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
throw new Error("GITHUB_TOKEN is required");
}
const basic = Buffer.from(`x-access-token:${githubToken}`).toString("base64");
const gitEnv = {
...process.env,
GIT_CONFIG_COUNT: "1",
GIT_CONFIG_KEY_0: "http.https://github.com/.extraheader",
GIT_CONFIG_VALUE_0: `AUTHORIZATION: basic ${basic}`,
};
process.chdir(path.join(import.meta.dirname, ".."));
@@ -11,9 +23,10 @@ await exec("git", ["checkout", "--detach"]);
await exec("git", ["add", "--force", "dist"]);
await exec("git", ["commit", "-m", tag]);
await exec("git", [
"push",
"--force",
"origin",
`HEAD:refs/heads/${releaseLine}`,
]);
await exec(
"git",
["push", "--force", "origin", `HEAD:refs/heads/${releaseLine}`],
{
env: gitEnv,
},
);
+33 -7
View File
@@ -1,9 +1,22 @@
import { Buffer } from "node:buffer";
import path from "node:path";
import { exec, getExecOutput } from "@actions/exec";
import pkgJson from "../package.json" with { type: "json" };
const tag = `v${pkgJson.version}`;
const releaseLine = `v${pkgJson.version.split(".")[0]}`;
const isPrerelease = pkgJson.version.includes("-");
const githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
throw new Error("GITHUB_TOKEN is required");
}
const basic = Buffer.from(`x-access-token:${githubToken}`).toString("base64");
const gitEnv = {
...process.env,
GIT_CONFIG_COUNT: "1",
GIT_CONFIG_KEY_0: "http.https://github.com/.extraheader",
GIT_CONFIG_VALUE_0: `AUTHORIZATION: basic ${basic}`,
};
process.chdir(path.join(import.meta.dirname, ".."));
@@ -31,11 +44,24 @@ process.chdir(path.join(import.meta.dirname, ".."));
await exec("changeset", ["tag"]);
await exec("git", [
"push",
"--force",
"--follow-tags",
"origin",
`HEAD:refs/heads/${releaseLine}`,
]);
if (isPrerelease) {
await exec("git", ["push", "origin", `refs/tags/${tag}`], {
env: gitEnv,
});
return;
}
await exec(
"git",
[
"push",
"--force",
"--follow-tags",
"origin",
`HEAD:refs/heads/${releaseLine}`,
],
{
env: gitEnv,
},
);
})();
+3
View File
@@ -0,0 +1,3 @@
# changesets/action/select-mode
TODO
+14
View File
@@ -0,0 +1,14 @@
name: Changesets - Select Mode
description: Whether to version or publish in the current repo state
runs:
using: node24
main: ../dist/select-mode.js
inputs: {}
outputs:
mode:
description: "The mode to use for the current repo state: 'version', 'publish', or 'none'."
publish-plan-artifact-id:
description: "Artifact id for the generated publish plan when mode is `publish`"
branding:
icon: package
color: blue
+68 -18
View File
@@ -1,11 +1,15 @@
import { Buffer } from "node:buffer";
import * as core from "@actions/core";
import { exec, getExecOutput } from "@actions/exec";
import * as github from "@actions/github";
import { context } from "@actions/github";
import { commitChangesFromRepo } from "@changesets/ghcommit/git";
import type { Octokit } from "./octokit.ts";
import { setupOctokit, type Octokit } from "./octokit.ts";
export type CommitMode = "git-cli" | "github-api";
type GitOptions = {
cwd: string;
env?: Record<string, string>;
};
const push = async (branch: string, options: GitOptions) => {
@@ -46,17 +50,51 @@ const checkIfClean = async (options: GitOptions): Promise<boolean> => {
return !stdout.length;
};
export class Git {
readonly octokit: Octokit | null;
export class GitHub {
readonly #githubToken: string;
readonly octokit: Octokit;
readonly cwd: string;
readonly commitMode: CommitMode;
constructor(args: { octokit?: Octokit; cwd: string }) {
this.octokit = args.octokit ?? null;
this.cwd = args.cwd;
constructor(options: {
githubToken: string;
cwd: string;
commitMode?: CommitMode;
}) {
this.#githubToken = options.githubToken;
this.cwd = options.cwd;
this.commitMode = options.commitMode ?? "git-cli";
this.octokit = setupOctokit(options.githubToken);
}
getToken() {
return this.#githubToken;
}
#getCliAuthEnv(): Record<string, string> {
const basic = Buffer.from(`x-access-token:${this.#githubToken}`).toString(
"base64",
);
const serverUrl = (
context.serverUrl ??
process.env.GITHUB_SERVER_URL ??
"https://github.com"
).replace(/\/+$/, "");
const gitConfigCount = Number(process.env.GIT_CONFIG_COUNT ?? 0);
if (!Number.isInteger(gitConfigCount) || gitConfigCount < 0) {
throw new Error(
`Invalid GIT_CONFIG_COUNT value: ${process.env.GIT_CONFIG_COUNT}`,
);
}
return {
GIT_CONFIG_COUNT: String(gitConfigCount + 1),
[`GIT_CONFIG_KEY_${gitConfigCount}`]: `http.${serverUrl}/.extraheader`,
[`GIT_CONFIG_VALUE_${gitConfigCount}`]: `AUTHORIZATION: basic ${basic}`,
};
}
async setupUser() {
if (this.octokit) {
if (this.commitMode === "github-api") {
return;
}
await exec("git", ["config", "user.name", `"github-actions[bot]"`], {
@@ -76,32 +114,38 @@ export class Git {
}
async pushTag(tag: string) {
if (this.octokit) {
if (this.commitMode === "github-api") {
return this.octokit.rest.git
.createRef({
...github.context.repo,
...context.repo,
ref: `refs/tags/${tag}`,
sha: github.context.sha,
sha: context.sha,
})
.catch((err) => {
// Assuming tag was manually pushed in custom publish script
core.warning(`Failed to create tag ${tag}: ${err.message}`);
});
}
await exec("git", ["push", "origin", tag], { cwd: this.cwd });
await exec("git", ["push", "origin", tag], {
cwd: this.cwd,
env: {
...process.env,
...this.#getCliAuthEnv(),
} as Record<string, string>,
});
}
async prepareBranch(branch: string) {
if (this.octokit) {
if (this.commitMode === "github-api") {
// Preparing a new local branch is not necessary when using the API
return;
}
await switchToMaybeExistingBranch(branch, { cwd: this.cwd });
await reset(github.context.sha, { cwd: this.cwd });
await reset(context.sha, { cwd: this.cwd });
}
async pushChanges({ branch, message }: { branch: string; message: string }) {
if (this.octokit) {
if (this.commitMode === "github-api") {
/**
* Only add files form the current working directory
*
@@ -111,11 +155,11 @@ export class Git {
const addFromDirectory = this.cwd;
return commitChangesFromRepo({
octokit: this.octokit,
...github.context.repo,
...context.repo,
branch,
message,
base: {
commit: github.context.sha,
commit: context.sha,
},
cwd: this.cwd,
force: true,
@@ -124,6 +168,12 @@ export class Git {
if (!(await checkIfClean({ cwd: this.cwd }))) {
await commitAll(message, { cwd: this.cwd });
}
await push(branch, { cwd: this.cwd });
await push(branch, {
cwd: this.cwd,
env: {
...process.env,
...this.#getCliAuthEnv(),
} as Record<string, string>,
});
}
}
+10 -25
View File
@@ -1,13 +1,9 @@
import fs from "node:fs/promises";
import path from "node:path";
import * as core from "@actions/core";
import { Git } from "./git.ts";
import { setupOctokit } from "./octokit.ts";
import { GitHub } from "./github.ts";
import readChangesetState from "./readChangesetState.ts";
import { runPublish, runVersion } from "./run.ts";
import { fileExists } from "./utils.ts";
const getOptionalInput = (name: string) => core.getInput(name) || undefined;
import { fileExists, getOptionalInput } from "./utils.ts";
(async () => {
// to maintain compatibility with workflows created before github-token input was introduced
@@ -19,10 +15,9 @@ const getOptionalInput = (name: string) => core.getInput(name) || undefined;
return;
}
const cwd = path.resolve(getOptionalInput("cwd") ?? "");
core.info(`using resolved cwd: ${cwd}`);
// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();
const octokit = setupOctokit(githubToken);
const commitMode = getOptionalInput("commitMode") ?? "git-cli";
const prDraft = getOptionalInput("prDraft");
if (commitMode !== "git-cli" && commitMode !== "github-api") {
@@ -33,24 +28,19 @@ const getOptionalInput = (name: string) => core.getInput(name) || undefined;
core.setFailed(`Invalid prDraft: ${prDraft}`);
return;
}
const git = new Git({
octokit: commitMode === "github-api" ? octokit : undefined,
const github = new GitHub({
cwd,
githubToken,
commitMode,
});
let setupGitUser = core.getBooleanInput("setupGitUser");
if (setupGitUser) {
core.info("setting git user");
await git.setupUser();
await github.setupUser();
}
core.info("setting GitHub credentials");
await fs.writeFile(
`${process.env.HOME}/.netrc`,
`machine github.com\nlogin github-actions[bot]\npassword ${githubToken}`,
);
let { changesets } = await readChangesetState(cwd);
let publishScript = core.getInput("publish");
@@ -122,9 +112,7 @@ const getOptionalInput = (name: string) => core.getInput(name) || undefined;
const result = await runPublish({
script: publishScript,
githubToken,
git,
octokit,
github,
createGithubReleases: core.getBooleanInput("createGithubReleases"),
cwd,
});
@@ -155,12 +143,9 @@ const getOptionalInput = (name: string) => core.getInput(name) || undefined;
core.info("All changesets are empty; not creating PR");
return;
case hasChangesets: {
const octokit = setupOctokit(githubToken);
const { pullRequestNumber } = await runVersion({
script: getOptionalInput("version"),
githubToken,
git,
octokit,
github,
cwd,
prTitle: getOptionalInput("title"),
commitMessage: getOptionalInput("commit"),
+86
View File
@@ -0,0 +1,86 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import artifact from "@actions/artifact";
import * as core from "@actions/core";
import {
downloadArtifact,
execChangesetsCli,
getOptionalInput,
} from "../utils.ts";
try {
await main();
} catch (err) {
core.setFailed((err as Error).message);
}
async function main() {
const publishPlanArtifactId = getOptionalInput("publish-plan-artifact-id");
// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();
const tmpDir = process.env.RUNNER_TEMP ?? (await fs.realpath(os.tmpdir()));
const outDir = path.join(tmpDir, `changeset-pack-${Date.now()}`);
await pack(cwd, {
outDir,
publishPlanPath: publishPlanArtifactId
? await downloadPublishPlanArtifact(tmpDir, Number(publishPlanArtifactId))
: undefined,
});
const packDirArtifact = await artifact.uploadArtifact(
`changeset-pack-${Date.now()}`,
await getFiles(outDir),
outDir,
{
retentionDays: 30,
},
);
if (packDirArtifact.id === undefined) {
throw new Error("Packed artifact upload did not return an artifact id");
}
core.setOutput("pack-dir-artifact-id", String(packDirArtifact.id));
}
async function pack(
cwd: string,
args: {
outDir: string;
publishPlanPath?: string;
},
) {
const cliArgs = ["pack", "--out-dir", args.outDir];
if (args.publishPlanPath) {
cliArgs.push("--from-publish-plan", args.publishPlanPath);
}
await execChangesetsCli(cliArgs, {
cwd,
env: process.env,
});
}
async function downloadPublishPlanArtifact(tmpDir: string, artifactId: number) {
const downloadPath = await downloadArtifact(
tmpDir,
artifactId,
"changeset-publish-plan",
);
return path.join(downloadPath, "publish-plan.json");
}
async function getFiles(dir: string): Promise<string[]> {
const entries = await fs.readdir(dir, { withFileTypes: true });
const files = await Promise.all(
entries.map(async (entry) => {
const entryPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
return getFiles(entryPath);
}
return [entryPath];
}),
);
return files.flat();
}
+4 -3
View File
@@ -1,5 +1,6 @@
import * as core from "@actions/core";
import * as github from "@actions/github";
import { getOptionalInput, getRequiredInput } from "../utils.ts";
type Octokit = ReturnType<typeof github.getOctokit>;
type CreateCommentParams = NonNullable<
@@ -23,9 +24,9 @@ async function main() {
);
}
const githubToken = core.getInput("github-token", { required: true });
const body = core.getInput("body", { required: true });
const updateId = core.getInput("update-id");
const githubToken = getRequiredInput("github-token");
const body = getRequiredInput("body");
const updateId = getOptionalInput("update-id");
const commentMarker = updateId ? getCommentMarker(updateId) : null;
const commentBody = commentMarker ? `${commentMarker}\n\n${body}` : body;
+67
View File
@@ -0,0 +1,67 @@
import fs from "node:fs/promises";
import os from "node:os";
import * as core from "@actions/core";
import { GitHub } from "../github.ts";
import { runPublish } from "../run.ts";
import {
downloadArtifact,
getOptionalInput,
getRequiredInput,
} from "../utils.ts";
try {
await main();
} catch (err) {
core.setFailed((err as Error).message);
}
async function main() {
const githubToken = getRequiredInput("github-token");
const script = getOptionalInput("script");
const packDirArtifactId = getOptionalInput("pack-dir-artifact-id");
const createGithubReleases = core.getBooleanInput("create-github-releases");
// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();
// NOTE: Always use API mode here as publish does not need a commit-mode.
const github = new GitHub({ cwd, githubToken, commitMode: "github-api" });
const fromPackDir = packDirArtifactId
? await downloadArtifact(
process.env.RUNNER_TEMP ?? (await fs.realpath(os.tmpdir())),
Number(packDirArtifactId),
"changeset-pack",
)
: undefined;
const result = await runPublish({
script,
github,
createGithubReleases,
cwd,
fromPackDir,
});
if (result.published) {
core.setOutput("published", "true");
core.setOutput(
"published-packages",
JSON.stringify(result.publishedPackages),
);
} else {
core.setOutput("published", "false");
}
if (result.exitCode !== 0) {
throw new Error(
`Publish command exited with code ${result.exitCode}${
result.published
? `, but some packages were published: ${result.publishedPackages
.map((p) => `${p.name}@${p.version}`)
.join(", ")}`
: ""
}`,
);
}
}
+16 -27
View File
@@ -3,8 +3,7 @@ import type { Changeset } from "@changesets/types";
import writeChangeset from "@changesets/write";
import { createFixture } from "fs-fixture";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { Git } from "./git.ts";
import { setupOctokit } from "./octokit.ts";
import { GitHub } from "./github.ts";
import { runVersion } from "./run.ts";
vi.mock("@actions/github", () => ({
@@ -21,7 +20,6 @@ vi.mock("@actions/github", () => ({
graphql: mockedGraphql,
}),
}));
vi.mock("./git.ts");
vi.mock("@changesets/ghcommit/git");
let mockedGithubMethods = {
@@ -91,6 +89,13 @@ const writeChangesets = (changesets: Changeset[], cwd: string) => {
return Promise.all(changesets.map((commit) => writeChangeset(commit, cwd)));
};
const createGithub = (cwd: string) =>
new GitHub({
cwd,
githubToken: "@@GITHUB_TOKEN",
commitMode: "github-api",
});
beforeEach(() => {
vi.clearAllMocks();
});
@@ -126,9 +131,7 @@ describe("version", () => {
);
await runVersion({
octokit: setupOctokit("@@GITHUB_TOKEN"),
githubToken: "@@GITHUB_TOKEN",
git: new Git({ cwd }),
github: createGithub(cwd),
cwd,
});
@@ -161,9 +164,7 @@ describe("version", () => {
);
await runVersion({
octokit: setupOctokit("@@GITHUB_TOKEN"),
githubToken: "@@GITHUB_TOKEN",
git: new Git({ cwd }),
github: createGithub(cwd),
cwd,
prDraft: "create",
});
@@ -197,9 +198,7 @@ describe("version", () => {
);
await runVersion({
octokit: setupOctokit("@@GITHUB_TOKEN"),
githubToken: "@@GITHUB_TOKEN",
git: new Git({ cwd }),
github: createGithub(cwd),
cwd,
});
@@ -232,9 +231,7 @@ describe("version", () => {
);
await runVersion({
octokit: setupOctokit("@@GITHUB_TOKEN"),
githubToken: "@@GITHUB_TOKEN",
git: new Git({ cwd }),
github: createGithub(cwd),
cwd,
});
@@ -287,9 +284,7 @@ fluminis divesque vulnere aquis parce lapsis rabie si visa fulmineis.
);
await runVersion({
octokit: setupOctokit("@@GITHUB_TOKEN"),
githubToken: "@@GITHUB_TOKEN",
git: new Git({ cwd }),
github: createGithub(cwd),
cwd,
prBodyMaxCharacters: 1000,
});
@@ -346,9 +341,7 @@ fluminis divesque vulnere aquis parce lapsis rabie si visa fulmineis.
);
await runVersion({
octokit: setupOctokit("@@GITHUB_TOKEN"),
githubToken: "@@GITHUB_TOKEN",
git: new Git({ cwd }),
github: createGithub(cwd),
cwd,
prBodyMaxCharacters: 500,
});
@@ -383,9 +376,7 @@ fluminis divesque vulnere aquis parce lapsis rabie si visa fulmineis.
);
await runVersion({
octokit: setupOctokit("@@GITHUB_TOKEN"),
githubToken: "@@GITHUB_TOKEN",
git: new Git({ cwd }),
github: createGithub(cwd),
cwd,
prDraft: "create",
});
@@ -417,9 +408,7 @@ fluminis divesque vulnere aquis parce lapsis rabie si visa fulmineis.
);
await runVersion({
octokit: setupOctokit("@@GITHUB_TOKEN"),
githubToken: "@@GITHUB_TOKEN",
git: new Git({ cwd }),
github: createGithub(cwd),
cwd,
prDraft: "always",
});
+54 -68
View File
@@ -1,25 +1,28 @@
import fs from "node:fs/promises";
import { createRequire } from "node:module";
import path from "node:path";
import * as core from "@actions/core";
import { exec, getExecOutput } from "@actions/exec";
import * as github from "@actions/github";
import {
exec,
getExecOutput,
type ExecOptions,
type ExecOutput,
} from "@actions/exec";
import { context } from "@actions/github";
import type { PreState } from "@changesets/types";
import { type Package, getPackages } from "@manypkg/get-packages";
import semverLt from "semver/functions/lt.js";
import { Git } from "./git.ts";
import type { GitHub } from "./github.ts";
import type { Octokit } from "./octokit.ts";
import readChangesetState from "./readChangesetState.ts";
import {
execChangesetsCli,
getChangedPackages,
getChangelogEntry,
getExecOutputChangesetsCli,
getVersionsByDirectory,
isErrorWithCode,
sortTheThings,
} from "./utils.ts";
const require = createRequire(import.meta.url);
// GitHub Issues/PRs messages have a max size limit on the
// message body payload.
// `body is too long (maximum is 65536 characters)`.
@@ -54,16 +57,15 @@ const createRelease = async (
tag_name: tagName,
body: changelogEntry.content,
prerelease: pkg.packageJson.version.includes("-"),
...github.context.repo,
...context.repo,
});
};
type PublishOptions = {
script: string;
githubToken: string;
octokit: Octokit;
script?: string;
fromPackDir?: string;
createGithubReleases: boolean;
git: Git;
github: GitHub;
cwd: string;
};
@@ -82,17 +84,35 @@ type PublishResult =
export async function runPublish({
script,
githubToken,
git,
octokit,
fromPackDir,
github,
createGithubReleases,
cwd,
}: PublishOptions): Promise<PublishResult> {
let changesetPublishOutput = await getExecOutput(script, undefined, {
const { octokit } = github;
let changesetPublishOutput: ExecOutput;
const execOptions: ExecOptions = {
cwd,
ignoreReturnCode: true,
env: { ...process.env, GITHUB_TOKEN: githubToken },
});
env: { ...process.env, GITHUB_TOKEN: github.getToken() },
};
if (script) {
changesetPublishOutput = await getExecOutput(
script,
undefined,
execOptions,
);
} else {
const args = ["publish"];
if (fromPackDir) {
args.push("--from-pack-dir", fromPackDir);
}
changesetPublishOutput = await getExecOutputChangesetsCli(
args,
execOptions,
);
}
let { packages, tool } = await getPackages(cwd);
let releasedPackages: Package[] = [];
@@ -121,7 +141,7 @@ export async function runPublish({
await Promise.all(
releasedPackages.map(async (pkg) => {
const tagName = `${pkg.packageJson.name}@${pkg.packageJson.version}`;
await git.pushTag(tagName);
await github.pushTag(tagName);
await createRelease(octokit, { pkg, tagName });
}),
);
@@ -143,7 +163,7 @@ export async function runPublish({
releasedPackages.push(pkg);
if (createGithubReleases) {
const tagName = `v${pkg.packageJson.version}`;
await git.pushTag(tagName);
await github.pushTag(tagName);
await createRelease(octokit, { pkg, tagName });
}
break;
@@ -165,24 +185,6 @@ export async function runPublish({
return { published: false, exitCode: changesetPublishOutput.exitCode };
}
const requireChangesetsCliPkgJson = (cwd: string) => {
try {
return require(
require.resolve("@changesets/cli/package.json", {
paths: [cwd],
}),
);
} catch (err) {
if (isErrorWithCode(err, "MODULE_NOT_FOUND")) {
throw new Error(
`Have you forgotten to install \`@changesets/cli\` in "${cwd}"?`,
{ cause: err },
);
}
throw err;
}
};
type GetMessageOptions = {
hasPublishScript: boolean;
branch: string;
@@ -254,9 +256,7 @@ export async function getVersionPrBody({
type VersionOptions = {
script?: string;
githubToken: string;
git: Git;
octokit: Octokit;
github: GitHub;
cwd?: string;
prTitle?: string;
commitMessage?: string;
@@ -272,47 +272,30 @@ type RunVersionResult = {
export async function runVersion({
script,
githubToken,
git,
octokit,
github,
cwd = process.cwd(),
prTitle = "Version Packages",
commitMessage = "Version Packages",
hasPublishScript = false,
prBodyMaxCharacters = MAX_CHARACTERS_PER_MESSAGE,
branch = github.context.ref.replace("refs/heads/", ""),
branch = context.ref.replace("refs/heads/", ""),
prDraft,
}: VersionOptions): Promise<RunVersionResult> {
const { octokit } = github;
let versionBranch = `changeset-release/${branch}`;
let { preState } = await readChangesetState(cwd);
await git.prepareBranch(versionBranch);
await github.prepareBranch(versionBranch);
let versionsByDirectory = await getVersionsByDirectory(cwd);
const env = { ...process.env, GITHUB_TOKEN: githubToken };
const env = { ...process.env, GITHUB_TOKEN: github.getToken() };
if (script) {
await exec(script, undefined, { cwd, env });
} else {
let changesetsCliPkgJson = requireChangesetsCliPkgJson(cwd);
let cmd = semverLt(changesetsCliPkgJson.version, "2.0.0")
? "bump"
: "version";
await exec(
"node",
[
require.resolve("@changesets/cli/bin.js", {
paths: [cwd],
}),
cmd,
],
{
cwd,
env,
},
);
await execChangesetsCli(["version"], { cwd, env });
}
let changedPackages = await getChangedPackages(cwd, versionsByDirectory);
@@ -346,9 +329,9 @@ export async function runVersion({
* which GitHub will then react to by closing the PRs)
*/
const existingPullRequests = await octokit.rest.pulls.list({
...github.context.repo,
...context.repo,
state: "open",
head: `${github.context.repo.owner}:${versionBranch}`,
head: `${context.repo.owner}:${versionBranch}`,
base: branch,
});
core.info(
@@ -359,7 +342,10 @@ export async function runVersion({
)}`,
);
await git.pushChanges({ branch: versionBranch, message: finalCommitMessage });
await github.pushChanges({
branch: versionBranch,
message: finalCommitMessage,
});
const changedPackagesInfo = (await changedPackagesInfoPromises)
.filter((x) => x)
@@ -381,7 +367,7 @@ export async function runVersion({
title: finalPrTitle,
body: prBody,
draft: prDraft !== undefined,
...github.context.repo,
...context.repo,
});
return {
+119
View File
@@ -0,0 +1,119 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import artifact from "@actions/artifact";
import * as core from "@actions/core";
import readChangesetState from "../readChangesetState.ts";
import { execChangesetsCli } from "../utils.ts";
type ModeResult =
| {
mode: "none";
}
| {
mode: "version";
}
| {
mode: "publish";
publishPlanPath: string;
};
type PublishPlan = unknown[];
try {
await main();
} catch (err) {
core.setFailed((err as Error).message);
}
async function main() {
const result = await getMode();
core.setOutput("mode", result.mode);
if (result.mode === "publish") {
const publishPlanArtifact = await artifact.uploadArtifact(
path.basename(result.publishPlanPath, ".json"),
[result.publishPlanPath],
path.dirname(result.publishPlanPath),
{
skipArchive: true,
retentionDays: 30,
},
);
if (publishPlanArtifact.id === undefined) {
throw new Error(
"Publish plan artifact upload did not return an artifact id",
);
}
core.setOutput("publish-plan-artifact-id", String(publishPlanArtifact.id));
}
}
async function getMode(): Promise<ModeResult> {
const { changesets } = await readChangesetState();
if (changesets.length > 0) {
const hasNonEmptyChangesets = changesets.some(
(changeset) => changeset.releases.length > 0,
);
if (hasNonEmptyChangesets) {
return { mode: "version" };
}
return { mode: "none" };
}
const cwd = process.cwd();
const publishPlanPath = path.join(
process.env.RUNNER_TEMP ?? (await fs.realpath(os.tmpdir())),
`changeset-publish-plan-${Date.now()}`,
// we need a stable filename here (in a unique dirname) so the artifact download can find this cleanly
"publish-plan.json",
);
await execChangesetsCli(["publish-plan", "--output", publishPlanPath], {
cwd,
env: process.env,
});
const publishPlan = await readPublishPlan(publishPlanPath);
if (publishPlan.length === 0) {
return { mode: "none" };
}
return {
mode: "publish",
publishPlanPath,
};
}
async function readPublishPlan(publishPlanPath: string): Promise<PublishPlan> {
let rawPlan: string;
try {
rawPlan = await fs.readFile(publishPlanPath, "utf8");
} catch (err) {
throw new Error(`Failed to read publish plan at ${publishPlanPath}`, {
cause: err,
});
}
let plan: unknown;
try {
plan = JSON.parse(rawPlan);
} catch (err) {
throw new Error(`Failed to parse publish plan at ${publishPlanPath}`, {
cause: err,
});
}
if (
typeof plan !== "object" ||
plan === null ||
!("version" in plan) ||
typeof plan.version !== "number" ||
!("plan" in plan) ||
!Array.isArray(plan.plan)
) {
throw new Error(
`Invalid publish plan at ${publishPlanPath}: expected { version: number; plan: unknown[] }`,
);
}
return plan.plan;
}
+77
View File
@@ -1,6 +1,18 @@
import fs from "node:fs/promises";
import { createRequire } from "node:module";
import path from "node:path";
import artifact from "@actions/artifact";
import * as core from "@actions/core";
import {
exec,
getExecOutput,
type ExecOptions as ActionsExecOptions,
type ExecOutput,
} from "@actions/exec";
import { getPackages, type Package } from "@manypkg/get-packages";
const require = createRequire(import.meta.url);
export const BumpLevels = {
dep: 0,
patch: 1,
@@ -111,3 +123,68 @@ export function fileExists(filePath: string) {
() => false,
);
}
export function getOptionalInput(name: string) {
// normalize empty string default return value of `core.getInput` to undefined
return core.getInput(name) || undefined;
}
export function getRequiredInput(name: string) {
// it's just a small utility wrapper, mainly introduced for usage parity with our custom `getOptionalInput`
// note: `core.getBooleanInput` gets used directly as it already normalizes the return value nicely
return core.getInput(name, { required: true });
}
function resolveChangesetsCli(cwd: string) {
return require.resolve("@changesets/cli/bin.js", {
paths: [cwd],
});
}
interface ExecOptions extends Omit<ActionsExecOptions, "env"> {
env?: Record<string, string | undefined>;
}
export function execChangesetsCli(args: string[], options?: ExecOptions) {
return exec(
"node",
[resolveChangesetsCli(options?.cwd ?? process.cwd()), ...args],
options as ActionsExecOptions,
);
}
export function getExecOutputChangesetsCli(
args: string[],
options?: ExecOptions,
): Promise<ExecOutput> {
return getExecOutput(
"node",
[resolveChangesetsCli(options?.cwd ?? process.cwd()), ...args],
options as ActionsExecOptions,
);
}
export async function downloadArtifact(
tmpDir: string,
artifactId: number,
name: string,
) {
if (!Number.isInteger(artifactId) || artifactId <= 0) {
throw new Error(
`Invalid ${JSON.stringify(name)} artifact id: ${artifactId}`,
);
}
const downloadPath = path.join(tmpDir, `${name}-${artifactId}-${Date.now()}`);
const result = await artifact.downloadArtifact(artifactId, {
path: downloadPath,
});
if (!result.downloadPath) {
throw new Error(
`${JSON.stringify(name)} artifact download did not return a path for artifact ${artifactId}`,
);
}
return result.downloadPath;
}
+57
View File
@@ -0,0 +1,57 @@
import * as core from "@actions/core";
import { GitHub } from "../github.ts";
import { runVersion } from "../run.ts";
import { getOptionalInput, getRequiredInput } from "../utils.ts";
try {
await main();
} catch (err) {
core.setFailed((err as Error).message);
}
async function main() {
const githubToken = getRequiredInput("github-token");
const script = getOptionalInput("script");
const commitMessage = getRequiredInput("commit-message");
const prTitle = getRequiredInput("pr-title");
const prDraft = getOptionalInput("pr-draft");
const prBaseBranch = getOptionalInput("pr-base-branch");
const commitMode = getOptionalInput("commit-mode") ?? "git-cli";
const setupGitUser = core.getBooleanInput("setup-git-user");
// Validations
if (prDraft !== undefined && prDraft !== "always" && prDraft !== "create") {
throw new Error(`Invalid pr-draft input: ${prDraft}`);
}
if (commitMode !== "git-cli" && commitMode !== "github-api") {
throw new Error(`Invalid commit-mode input: ${commitMode}`);
}
// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();
const github = new GitHub({
cwd,
githubToken,
commitMode,
});
if (setupGitUser) {
core.info("setting git user");
await github.setupUser();
}
const { pullRequestNumber } = await runVersion({
script,
github,
cwd,
prTitle,
commitMessage,
// TODO: Use neutral message for PR description
hasPublishScript: true,
prDraft,
branch: prBaseBranch,
});
core.setOutput("pr-number", String(pullRequestNumber));
}
+3
View File
@@ -0,0 +1,3 @@
# changesets/action/version
TODO
+43
View File
@@ -0,0 +1,43 @@
name: Changesets - Version
description: Version packages
runs:
using: node24
main: ../dist/version.js
inputs:
github-token:
description: "The GitHub token to use for authentication. Defaults to the GitHub-provided token."
required: false
default: ${{ github.token }}
script:
description: "The command to use to version packages"
required: false
commit-message:
description: "The commit message. Default to `Version Packages`"
required: false
default: "Version Packages"
pr-title:
description: "The pull request title. Default to `Version Packages`"
required: false
default: "Version Packages"
pr-draft:
description: "Controls draft PR behavior. Use 'create' to create new version PRs as draft, or 'always' to also convert existing version PRs back to draft when updating them."
required: false
pr-base-branch:
description: "Sets the base branch of the PR. Defaults to `github.ref_name`."
required: false
commit-mode:
description: >
An enum to specify the commit mode. Use "git-cli" to push changes using the Git CLI,
or "github-api" to push changes via the GitHub API. When using "github-api",
all commits and tags are signed using GitHub's GPG key and attributed to the user
or app who owns the GITHUB_TOKEN.
required: false
default: "git-cli"
setup-git-user:
description: Sets up the git user for commits as `"github-actions[bot]"`. Default to `true`
required: false
default: true
outputs: {}
branding:
icon: package
color: blue