Skip to content

Commit c136a79

Browse files
committed
publish release packages to GitHub Packages
1 parent 61b20dc commit c136a79

3 files changed

Lines changed: 114 additions & 18 deletions

File tree

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,39 @@ const VERSION = process.env.VERSION || "";
1212
const dryRun = process.argv.includes("--dry-run");
1313
const checkOnly = process.argv.includes("--check");
1414
const resumeExisting = process.argv.includes("--resume-existing");
15+
const registryArg = process.argv.find((arg) => arg.startsWith("--registry="));
1516
const unknownArgs = process.argv
1617
.slice(2)
17-
.filter((arg) => !["--dry-run", "--check", "--resume-existing"].includes(arg));
18+
.filter(
19+
(arg) =>
20+
!["--dry-run", "--check", "--resume-existing"].includes(arg) &&
21+
!arg.startsWith("--registry=")
22+
);
23+
24+
const registries = {
25+
npm: {
26+
label: "npm",
27+
url: "https://registry.npmjs.org/",
28+
publishArgs: [],
29+
},
30+
"github-packages": {
31+
label: "GitHub Packages",
32+
url: "https://npm.pkg.github.com/",
33+
// Provenance is generated for npmjs.org publishes; GitHub Packages uses GITHUB_TOKEN auth.
34+
publishArgs: ["--provenance=false"],
35+
},
36+
};
37+
const registryName = registryArg ? registryArg.slice("--registry=".length) : "npm";
38+
const registry = registries[registryName];
1839

1940
if (unknownArgs.length > 0) {
2041
console.error(`Unknown argument(s): ${unknownArgs.join(", ")}`);
2142
process.exit(1);
2243
}
44+
if (!registry) {
45+
console.error(`Unknown registry: ${registryName}`);
46+
process.exit(1);
47+
}
2348
if (dryRun && checkOnly) {
2449
console.error("--dry-run and --check cannot be used together");
2550
process.exit(1);
@@ -58,10 +83,14 @@ function fail(message) {
5883

5984
function npmView(packageName) {
6085
try {
61-
const output = execFileSync("npm", ["view", `${packageName}@${version}`, "--json"], {
62-
encoding: "utf8",
63-
stdio: ["ignore", "pipe", "pipe"],
64-
}).trim();
86+
const output = execFileSync(
87+
"npm",
88+
["view", `${packageName}@${version}`, "--json", "--registry", registry.url],
89+
{
90+
encoding: "utf8",
91+
stdio: ["ignore", "pipe", "pipe"],
92+
}
93+
).trim();
6594
if (!output) {
6695
return null;
6796
}
@@ -152,8 +181,9 @@ function publish(packageDir) {
152181
return;
153182
}
154183
const args = dryRun
155-
? ["publish", "--dry-run", "--access", "public"]
156-
: ["publish", "--access", "public"];
184+
? ["publish", "--dry-run", "--access", "public", "--registry", registry.url]
185+
: ["publish", "--access", "public", "--registry", registry.url];
186+
args.push(...registry.publishArgs);
157187
execFileSync("npm", args, { cwd: packageDir, stdio: "inherit" });
158188
}
159189

@@ -189,7 +219,7 @@ if (!dryRun && !checkOnly) {
189219
const remotePackage = npmView(pkg.name);
190220
if (remotePackage) {
191221
if (!resumeExisting) {
192-
fail(`${pkg.name}@${version} already exists; use --resume-existing only after verifying recovery is intended`);
222+
fail(`${pkg.name}@${version} already exists in ${registry.label}; use --resume-existing only after verifying recovery is intended`);
193223
}
194224
assertRemotePackageMatches(pkg.dir, pkg.name, remotePackage);
195225
existingPackages.set(pkg.name, true);
@@ -199,7 +229,7 @@ if (!dryRun && !checkOnly) {
199229

200230
for (const pkg of packages) {
201231
if (existingPackages.has(pkg.name)) {
202-
console.log(`Skipping existing matching package ${pkg.name}@${version}`);
232+
console.log(`Skipping existing matching ${registry.label} package ${pkg.name}@${version}`);
203233
continue;
204234
}
205235
publish(pkg.dir);

.github/scripts/release-workflow.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function validateDispatch(env = process.env) {
3333

3434
if (ctx.dryRun) {
3535
if (ctx.resumeExistingNpm) {
36-
fail("resume_existing_npm is only valid for real npm publish recovery");
36+
fail("resume_existing_npm is only valid for real package publishing recovery");
3737
}
3838
if (ctx.githubRef !== "refs/heads/main") {
3939
fail("release dry-run must be dispatched from refs/heads/main");
@@ -43,7 +43,7 @@ function validateDispatch(env = process.env) {
4343

4444
if (ctx.resumeExistingNpm) {
4545
if (ctx.githubRef !== ctx.tagRef) {
46-
fail(`npm publish recovery must be dispatched from ${ctx.tagRef}`);
46+
fail(`package publishing recovery must be dispatched from ${ctx.tagRef}`);
4747
}
4848
return ctx;
4949
}
@@ -200,7 +200,7 @@ function assertExistingRelease(env = process.env) {
200200
])
201201
);
202202
} catch (err) {
203-
fail(`resume_existing_npm requires an existing non-draft GitHub Release for ${ctx.tag}`);
203+
fail(`package publishing recovery requires an existing non-draft GitHub Release for ${ctx.tag}`);
204204
}
205205

206206
if (release.tagName !== ctx.tag) {
@@ -210,7 +210,7 @@ function assertExistingRelease(env = process.env) {
210210
fail(`GitHub Release ${ctx.tag} is still a draft`);
211211
}
212212

213-
console.log(`Resuming npm publish after existing GitHub Release: ${release.url}`);
213+
console.log(`Resuming package publishing after existing GitHub Release: ${release.url}`);
214214
return ctx;
215215
}
216216

.github/workflows/release.yml

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ on:
1111
required: true
1212
type: string
1313
dry_run:
14-
description: "Build and validate artifacts without publishing a GitHub Release or npm packages"
14+
description: "Build and validate artifacts without publishing a GitHub Release or packages"
1515
required: true
1616
default: true
1717
type: boolean
1818
resume_existing_npm:
19-
description: "Resume npm publish by skipping already-published matching package versions"
19+
description: "Resume package publishing by skipping already-published matching package versions"
2020
required: true
2121
default: false
2222
type: boolean
@@ -124,12 +124,24 @@ jobs:
124124
- name: Validate package metadata
125125
env:
126126
VERSION: ${{ inputs.version }}
127-
run: node .github/scripts/publish-npm.js --check
127+
run: node .github/scripts/publish-packages.js --check
128128

129129
- name: Validate npm publish command
130130
env:
131131
VERSION: ${{ inputs.version }}
132-
run: node .github/scripts/publish-npm.js --dry-run
132+
run: node .github/scripts/publish-packages.js --dry-run
133+
134+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
135+
with:
136+
node-version: 24
137+
registry-url: https://npm.pkg.github.com
138+
scope: "@customerio"
139+
140+
- name: Validate GitHub Packages publish command
141+
env:
142+
VERSION: ${{ inputs.version }}
143+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
144+
run: node .github/scripts/publish-packages.js --registry=github-packages --dry-run
133145

134146
github_release:
135147
needs: validate_dispatch
@@ -280,4 +292,58 @@ jobs:
280292
if [[ "$RESUME_EXISTING_NPM" == "true" ]]; then
281293
args+=(--resume-existing)
282294
fi
283-
node .github/scripts/publish-npm.js "${args[@]}"
295+
node .github/scripts/publish-packages.js "${args[@]}"
296+
297+
github_packages_publish:
298+
needs: npm_publish
299+
if: >-
300+
${{
301+
always() &&
302+
!inputs.dry_run &&
303+
needs.npm_publish.result == 'success'
304+
}}
305+
runs-on: ubuntu-latest
306+
environment: release
307+
permissions:
308+
contents: read
309+
packages: write
310+
steps:
311+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
312+
with:
313+
fetch-depth: 0
314+
ref: ${{ github.sha }}
315+
316+
- name: Assert checkout and tag ref
317+
env:
318+
VERSION_INPUT: ${{ inputs.version }}
319+
DRY_RUN: ${{ inputs.dry_run }}
320+
RESUME_EXISTING_NPM: ${{ inputs.resume_existing_npm }}
321+
run: node .github/scripts/release-workflow.js assert-tag-run
322+
323+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
324+
with:
325+
node-version: 24
326+
registry-url: https://npm.pkg.github.com
327+
scope: "@customerio"
328+
329+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
330+
with:
331+
name: goreleaser-dist
332+
path: dist/
333+
334+
- name: Prepare npm packages
335+
env:
336+
VERSION: ${{ inputs.version }}
337+
run: node .github/scripts/prepare-npm-packages.js
338+
339+
- name: Publish GitHub Packages
340+
env:
341+
VERSION: ${{ inputs.version }}
342+
RESUME_EXISTING_NPM: ${{ inputs.resume_existing_npm }}
343+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
344+
run: |
345+
args=(--registry=github-packages)
346+
if [[ "$RESUME_EXISTING_NPM" == "true" ]]; then
347+
args+=(--resume-existing)
348+
fi
349+
node .github/scripts/publish-packages.js "${args[@]}"

0 commit comments

Comments
 (0)