mirror of
https://github.com/changesets/action
synced 2026-10-26 16:39:56 +08:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b83f36ae5 | ||
|
|
dad5baf18b | ||
|
|
5210f6da68 | ||
|
|
a12d90de83 | ||
|
|
5649ff41b8 | ||
|
|
4f718b5921 | ||
|
|
1bf7c2782b |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@changesets/action": minor
|
||||
---
|
||||
|
||||
Add new `/select-mode`, `/version`, and `/publish` sub-actions to better control version and publish steps
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@changesets/action": major
|
||||
---
|
||||
|
||||
Removed compatibility support for old Changesets v1.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@changesets/action": major
|
||||
---
|
||||
|
||||
Remove `cwd` option for `changesets/action`. Use the step `working-directory` option instead to change the directory.
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"tag": "next",
|
||||
"initialVersions": {
|
||||
"@changesets/action": "1.9.0"
|
||||
},
|
||||
"changesets": ["eight-clocks-pull", "eighty-doors-swim", "lemon-turtles-itch"]
|
||||
}
|
||||
@@ -73,7 +73,6 @@ jobs:
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- uses: ./.github/actions/ci-setup
|
||||
with:
|
||||
@@ -85,4 +84,5 @@ jobs:
|
||||
- name: Publish to marketplace
|
||||
uses: ./
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
publish: pnpm release
|
||||
|
||||
@@ -122,6 +122,13 @@ 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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
@@ -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'
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @changesets/action
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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`
|
||||
|
||||
Vendored
+49
File diff suppressed because one or more lines are too long
Vendored
+48
File diff suppressed because one or more lines are too long
Vendored
+18
File diff suppressed because one or more lines are too long
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import{c as e,d as t,l as n,o as r,s as i,u as a}from"./github-B-FmP-qI.js";import{i as o,n as s,r as c,t as l}from"./run-CVDXZtwd.js";import{r as u}from"./utils-CZUacPOo.js";import{t as d}from"./readChangesetState-Otq6gfkp.js";import f from"node:fs/promises";import"node:path";const p=t=>e(t)||void 0;(async()=>{let m=process.env.GITHUB_TOKEN||e(`github-token`);if(!m){a(`Please add the GITHUB_TOKEN to the changesets action`);return}let h=process.cwd(),g=c(m),_=p(`commitMode`)??`git-cli`,v=p(`prDraft`);if(_!==`git-cli`&&_!==`github-api`){a(`Invalid commit mode: ${_}`);return}if(v!==void 0&&v!==`always`&&v!==`create`){a(`Invalid prDraft: ${v}`);return}let y=new o({octokit:_===`github-api`?g:void 0,cwd:h});i(`setupGitUser`)&&(n(`setting git user`),await y.setupUser()),n(`setting GitHub credentials`),await f.writeFile(`${process.env.HOME}/.netrc`,`machine github.com\nlogin github-actions[bot]\npassword ${m}`);let{changesets:b}=await d(h),x=e(`publish`),S=b.length!==0,C=b.some(e=>e.releases.length>0),w=!!x;switch(t(`published`,`false`),t(`publishedPackages`,`[]`),t(`hasChangesets`,String(S)),!0){case!S&&!w:n(`No changesets present or were removed by merging release PR. Not publishing because no publish script found.`);return;case!S&&w:{if(n(`No changesets found. Attempting to publish any unpublished packages to npm`),process.env.NPM_TOKEN){let e=`${process.env.HOME}/.npmrc`;await u(e)?(n(`Found existing user .npmrc file`),(await f.readFile(e,`utf8`)).split(`
|
||||
`).find(e=>/^\s*\/\/registry\.npmjs\.org\/:[_-]authToken=/i.test(e))?n(`Found existing auth token for the npm registry in the user .npmrc file`):(n(`Didn't find existing auth token for the npm registry in the user .npmrc file, creating one`),await f.appendFile(e,`\n//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}\n`))):(n(`No user .npmrc file found, creating one with NPM_TOKEN used as auth token`),await f.writeFile(e,`//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}\n`))}else process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN&&process.env.ACTIONS_ID_TOKEN_REQUEST_URL?n(`No NPM_TOKEN found, but OIDC is available - using npm trusted publishing`):n(`No NPM_TOKEN or OIDC available - assuming npm is already authenticated`);let e=await l({script:x,githubToken:m,git:y,octokit:g,createGithubReleases:i(`createGithubReleases`),cwd:h});e.published&&(t(`published`,`true`),t(`publishedPackages`,JSON.stringify(e.publishedPackages))),e.exitCode!==0&&(r(`Publish command exited with code ${e.exitCode}${e.published?`, but some packages were published: ${e.publishedPackages.map(e=>`${e.name}@${e.version}`).join(`, `)}`:``}`),process.exit(e.exitCode));return}case S&&!C:n(`All changesets are empty; not creating PR`);return;case S:{let e=c(m),{pullRequestNumber:n}=await s({script:p(`version`),githubToken:m,git:y,octokit:e,cwd:h,prTitle:p(`title`),commitMessage:p(`commit`),hasPublishScript:w,prDraft:v,branch:p(`branch`)});t(`pullRequestNumber`,String(n));return}}})().catch(e=>{r(e),a(e.message)});export{};
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{c as e,d as t,u as n}from"./github-B-FmP-qI.js";import{n as r,t as i,u as a}from"./utils-CZUacPOo.js";import o from"node:fs/promises";import s from"node:path";import c from"node:os";try{await l()}catch(e){n(e.message)}async function l(){let n=e(`publish-plan-artifact-id`),r=process.cwd(),i=process.env.RUNNER_TEMP??await o.realpath(c.tmpdir()),l=s.join(i,`changeset-pack-${Date.now()}`);await u(r,{outDir:l,publishPlanPath:n?await d(i,Number(n)):void 0});let p=await a.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-plan`,t.publishPlanPath),await r(n,{cwd:e,env:process.env})}async function d(e,t){let n=await i(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{};
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{c as e,d as t,l as n,n as r,t as i,u as a}from"./github-B-FmP-qI.js";try{await o()}catch(e){a(e.message)}async function o(){let a=i.payload.pull_request;if(!a)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:a.base.repo.name,owner:a.base.repo.owner.login,issue_number:a.number,body:d},p=r(o),m;u&&(n(`Checking for existing comment...`),m=await p.rest.issues.listComments({repo:a.base.repo.name,owner:a.base.repo.owner.login,issue_number:a.number}).then(e=>e.data.find(e=>e.body?.includes(u))?.id)),m?(n(`Updating existing comment (id: ${m})...`),await p.rest.issues.updateComment({...f,comment_id:m}),t(`comment-id`,m)):(n(`Creating new comment...`),t(`comment-id`,(await p.rest.issues.createComment(f)).data.id)),n(`Done!`)}function s(e){let t=`changesets-action-pr-comment`;return t===e?`<!-- ${t} -->`:`<!-- ${t}:${e} -->`}export{};
|
||||
Vendored
+39
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{c as e,d as t,s as n,u as r}from"./github-B-FmP-qI.js";import{i,r as a,t as o}from"./run-CVDXZtwd.js";import{t as s}from"./utils-CZUacPOo.js";import c from"node:fs/promises";import l from"node:os";try{await u()}catch(e){r(e.message)}async function u(){let r=e(`github-token`,{required:!0}),u=e(`script`),d=e(`pack-dir-artifact-id`),f=n(`create-github-releases`),p=process.cwd(),m=a(r),h=await o({script:u,githubToken:r,git:new i({octokit:m,cwd:p}),octokit:m,createGithubReleases:f,cwd:p,fromPackDir:d?await s(process.env.RUNNER_TEMP??await c.realpath(l.tmpdir()),Number(d),`changeset-pack`):void 0});if(h.published?(t(`published`,`true`),t(`published-packages`,JSON.stringify(h.publishedPackages))):t(`published`,`false`),h.exitCode!==0)throw Error(`Publish command exited with code ${h.exitCode}${h.published?`, but some packages were published: ${h.publishedPackages.map(e=>`${e.name}@${e.version}`).join(`, `)}`:``}`)}export{};
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{o as e,t}from"./changesets-read.esm-Cr0YoPBA.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};
|
||||
Vendored
+149
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{d as e,u as t}from"./github-B-FmP-qI.js";import{n,u as r}from"./utils-CZUacPOo.js";import{t as i}from"./readChangesetState-Otq6gfkp.js";import a from"node:fs/promises";import o from"node:path";import s from"node:os";try{await c()}catch(e){t(e.message)}async function c(){let t=await l();if(e(`mode`,t.mode),t.mode===`publish`){let n=await r.uploadArtifact(o.basename(t.publishPlanPath,`.json`),[t.publishPlanPath],o.dirname(t.publishPlanPath),{skipArchive:!0,retentionDays:30});if(n.id===void 0)throw Error(`Publish plan artifact upload did not return an artifact id`);e(`publish-plan-artifact-id`,String(n.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(),r=o.join(process.env.RUNNER_TEMP??await a.realpath(s.tmpdir()),`changeset-publish-plan-${Date.now()}.json`);return await n([`publish-plan`,`--output`,r],{cwd:t,env:process.env}),(await u(r)).length===0?{mode:`none`}:{mode:`publish`,publishPlanPath:r}}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{};
|
||||
Vendored
+80
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
import{c as e,d as t,l as n,s as r,u as i}from"./github-B-FmP-qI.js";import{i as a,n as o,r as s}from"./run-CVDXZtwd.js";try{await c()}catch(e){i(e.message)}async function c(){let i=e(`github-token`,{required:!0}),c=e(`script`),l=e(`commit-message`,{required:!0}),u=e(`pr-title`,{required:!0}),d=e(`pr-draft`)||void 0,f=e(`pr-base-branch`),p=e(`commit-mode`)||`git-cli`,m=r(`setup-git-user`);if(d!==void 0&&d!==`always`&&d!==`create`)throw Error(`Invalid pr-draft input: ${d}`);let h=process.cwd(),g=s(i),_=new a({octokit:p===`github-api`?g:void 0,cwd:h});m&&(n(`setting git user`),await _.setupUser());let{pullRequestNumber:v}=await o({script:c,githubToken:i,git:_,octokit:g,cwd:h,prTitle:u,commitMessage:l,hasPublishScript:!0,prDraft:d,branch:f});t(`pr-number`,String(v))}export{};
|
||||
@@ -0,0 +1,3 @@
|
||||
# changesets/action/pack
|
||||
|
||||
TODO
|
||||
@@ -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
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@changesets/action",
|
||||
"version": "1.9.0",
|
||||
"version": "2.0.0-next.0",
|
||||
"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",
|
||||
|
||||
Generated
+943
-2
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
# changesets/action/publish
|
||||
|
||||
TODO
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
},
|
||||
);
|
||||
|
||||
+28
-8
@@ -1,3 +1,4 @@
|
||||
import { Buffer } from "node:buffer";
|
||||
import path from "node:path";
|
||||
import { exec, getExecOutput } from "@actions/exec";
|
||||
import pkgJson from "../package.json" with { type: "json" };
|
||||
@@ -5,6 +6,17 @@ 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, ".."));
|
||||
|
||||
@@ -33,15 +45,23 @@ process.chdir(path.join(import.meta.dirname, ".."));
|
||||
await exec("changeset", ["tag"]);
|
||||
|
||||
if (isPrerelease) {
|
||||
await exec("git", ["push", "origin", `refs/tags/${tag}`]);
|
||||
await exec("git", ["push", "origin", `refs/tags/${tag}`], {
|
||||
env: gitEnv,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await exec("git", [
|
||||
"push",
|
||||
"--force",
|
||||
"--follow-tags",
|
||||
"origin",
|
||||
`HEAD:refs/heads/${releaseLine}`,
|
||||
]);
|
||||
await exec(
|
||||
"git",
|
||||
[
|
||||
"push",
|
||||
"--force",
|
||||
"--follow-tags",
|
||||
"origin",
|
||||
`HEAD:refs/heads/${releaseLine}`,
|
||||
],
|
||||
{
|
||||
env: gitEnv,
|
||||
},
|
||||
);
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# changesets/action/select-mode
|
||||
|
||||
TODO
|
||||
@@ -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
|
||||
+2
-2
@@ -19,8 +19,8 @@ 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";
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
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 } from "../utils.ts";
|
||||
|
||||
try {
|
||||
await main();
|
||||
} catch (err) {
|
||||
core.setFailed((err as Error).message);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const publishPlanArtifactId = core.getInput("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-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();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import * as core from "@actions/core";
|
||||
import { Git } from "../git.ts";
|
||||
import { setupOctokit } from "../octokit.ts";
|
||||
import { runPublish } from "../run.ts";
|
||||
import { downloadArtifact } from "../utils.ts";
|
||||
|
||||
try {
|
||||
await main();
|
||||
} catch (err) {
|
||||
core.setFailed((err as Error).message);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const githubToken = core.getInput("github-token", { required: true });
|
||||
const script = core.getInput("script");
|
||||
const packDirArtifactId = core.getInput("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();
|
||||
|
||||
const octokit = setupOctokit(githubToken);
|
||||
// NOTE: Always pass octokit here as publish does not need a commit-mode
|
||||
const git = new Git({ octokit, cwd });
|
||||
|
||||
const fromPackDir = packDirArtifactId
|
||||
? await downloadArtifact(
|
||||
process.env.RUNNER_TEMP ?? (await fs.realpath(os.tmpdir())),
|
||||
Number(packDirArtifactId),
|
||||
"changeset-pack",
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const result = await runPublish({
|
||||
script,
|
||||
githubToken,
|
||||
git,
|
||||
octokit,
|
||||
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(", ")}`
|
||||
: ""
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+32
-43
@@ -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 {
|
||||
exec,
|
||||
getExecOutput,
|
||||
type ExecOptions,
|
||||
type ExecOutput,
|
||||
} from "@actions/exec";
|
||||
import * as github 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 { 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)`.
|
||||
@@ -59,7 +62,8 @@ const createRelease = async (
|
||||
};
|
||||
|
||||
type PublishOptions = {
|
||||
script: string;
|
||||
script?: string;
|
||||
fromPackDir?: string;
|
||||
githubToken: string;
|
||||
octokit: Octokit;
|
||||
createGithubReleases: boolean;
|
||||
@@ -82,17 +86,36 @@ type PublishResult =
|
||||
|
||||
export async function runPublish({
|
||||
script,
|
||||
fromPackDir,
|
||||
githubToken,
|
||||
git,
|
||||
octokit,
|
||||
createGithubReleases,
|
||||
cwd,
|
||||
}: PublishOptions): Promise<PublishResult> {
|
||||
let changesetPublishOutput = await getExecOutput(script, undefined, {
|
||||
let changesetPublishOutput: ExecOutput;
|
||||
const execOptions: ExecOptions = {
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
env: { ...process.env, GITHUB_TOKEN: githubToken },
|
||||
});
|
||||
};
|
||||
|
||||
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[] = [];
|
||||
@@ -165,24 +188,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;
|
||||
@@ -296,23 +301,7 @@ export async function runVersion({
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
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()}.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;
|
||||
}
|
||||
@@ -1,6 +1,17 @@
|
||||
import fs from "node:fs/promises";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import artifact from "@actions/artifact";
|
||||
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 +122,57 @@ export function fileExists(filePath: string) {
|
||||
() => false,
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import * as core from "@actions/core";
|
||||
import { Git } from "../git.ts";
|
||||
import { setupOctokit } from "../octokit.ts";
|
||||
import { runVersion } from "../run.ts";
|
||||
|
||||
try {
|
||||
await main();
|
||||
} catch (err) {
|
||||
core.setFailed((err as Error).message);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const githubToken = core.getInput("github-token", { required: true });
|
||||
const script = core.getInput("script");
|
||||
const commitMessage = core.getInput("commit-message", { required: true });
|
||||
const prTitle = core.getInput("pr-title", { required: true });
|
||||
const prDraft = core.getInput("pr-draft") || undefined;
|
||||
const prBaseBranch = core.getInput("pr-base-branch");
|
||||
const commitMode = core.getInput("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 the user needs to change the cwd, set `working-directory` in the step instead
|
||||
const cwd = process.cwd();
|
||||
|
||||
const octokit = setupOctokit(githubToken);
|
||||
const git = new Git({
|
||||
octokit: commitMode === "github-api" ? octokit : undefined,
|
||||
cwd,
|
||||
});
|
||||
|
||||
if (setupGitUser) {
|
||||
core.info("setting git user");
|
||||
await git.setupUser();
|
||||
}
|
||||
|
||||
const { pullRequestNumber } = await runVersion({
|
||||
script,
|
||||
githubToken,
|
||||
git,
|
||||
octokit,
|
||||
cwd,
|
||||
prTitle,
|
||||
commitMessage,
|
||||
// TODO: Use neutral message for PR description
|
||||
hasPublishScript: true,
|
||||
prDraft,
|
||||
branch: prBaseBranch,
|
||||
});
|
||||
|
||||
core.setOutput("pr-number", String(pullRequestNumber));
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# changesets/action/version
|
||||
|
||||
TODO
|
||||
@@ -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 publish 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
|
||||
Reference in New Issue
Block a user