Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion .github/workflows/_pr-release-reusable.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
name: PR release to npm (Reusable)
on:
workflow_call:
inputs:
custom_version:
description: 'Optional: exact semver prerelease version to publish, overriding the auto-computed snapshot version.'
required: false
type: string
default: ''
dist_tag:
description: 'Optional: npm dist-tag override. Required when custom_version is set. Cannot be "latest".'
required: false
type: string
default: ''

permissions:
id-token: write
Expand All @@ -11,6 +22,20 @@ jobs:
runs-on: ubuntu-latest
name: Release to npm
steps:
- name: Validate custom-version inputs
if: inputs.custom_version != ''
env:
CUSTOM_VERSION: ${{ inputs.custom_version }}
DIST_TAG: ${{ inputs.dist_tag }}
run: |
if [ -z "$DIST_TAG" ]; then
echo "::error::dist_tag is required when custom_version is set"
exit 1
fi
if [ "$DIST_TAG" = "latest" ]; then
echo "::error::dist_tag cannot be 'latest'"
exit 1
fi
- uses: actions/checkout@v4
with:
fetch-depth: 0
Expand All @@ -25,7 +50,15 @@ jobs:
- name: Set up project
run: pnpm run bootstrap
- name: Release to NPM
run: pnpm run release --prRelease
env:
CUSTOM_VERSION: ${{ inputs.custom_version }}
DIST_TAG: ${{ inputs.dist_tag }}
run: |
if [ -n "$CUSTOM_VERSION" ]; then
pnpm run release --prRelease --customVersion="$CUSTOM_VERSION" --distTag="$DIST_TAG"
else
pnpm run release --prRelease
fi
- name: Get commit message
run: | # puts the first line of the last commit message to the commmit_message env var
echo "commmit_message=$(git log --format=%B -n 1 ${{ github.event.after }} | head -n 1)" >> $GITHUB_ENV
Expand Down
17 changes: 16 additions & 1 deletion .github/workflows/release_to_npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ on:
- manual
- pr-snapshot
default: 'manual'
custom_version:
description: 'Optional: exact semver prerelease version to publish (pr-snapshot only). Overrides the auto-computed snapshot version. Example: 11.7.3-SECURITY.0'
required: false
type: string
default: ''
dist_tag:
description: 'Optional: npm dist-tag override (pr-snapshot only). Required when custom_version is set. Cannot be "latest".'
required: false
type: string
default: ''

permissions:
id-token: write # Required for OIDC token generation
Expand All @@ -38,10 +48,15 @@ jobs:
contents: write
secrets: inherit

# PR snapshot release
# PR snapshot release. Optionally accepts custom_version + dist_tag to mirror
# a previously-published private security release onto the public registry
# under a non-latest dist-tag.
pr-release:
if: github.event_name == 'workflow_dispatch' && inputs.release_type == 'pr-snapshot'
uses: ./.github/workflows/_pr-release-reusable.yml
with:
custom_version: ${{ inputs.custom_version }}
dist_tag: ${{ inputs.dist_tag }}
permissions:
id-token: write
contents: write
Expand Down
76 changes: 66 additions & 10 deletions packages/ui-scripts/lib/commands/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,32 @@ export default {
describe: 'If true pnpm publish will use vXYZ-pr-snapshot as version',
default: false
})

yargs.option('customVersion', {
type: 'string',
describe:
'Publish all packages at this exact semver prerelease version (e.g. 11.7.3-SECURITY.0) instead of the version in package.json. Must be combined with --distTag.',
default: ''
})

yargs.option('distTag', {
type: 'string',
describe:
'Override the npm dist-tag used for this release. Required with --customVersion. Cannot be "latest".',
default: ''
})
},
handler: async (argv) => {
const { isMaintenance, prRelease } = argv
const { isMaintenance, prRelease, customVersion, distTag } = argv
try {
const pkgJSON = pkgUtils.getPackageJSON()
await publish({
packageName: pkgJSON.name,
version: pkgJSON.version,
isMaintenance,
prRelease
prRelease,
customVersion,
distTag
})
} catch (err) {
error(err)
Expand All @@ -66,9 +82,18 @@ export default {
}
}

async function publish({ packageName, version, isMaintenance, prRelease }) {
async function publish({
packageName,
version,
isMaintenance,
prRelease,
customVersion,
distTag
}) {
const isRegularRelease = isReleaseCommit(version)

validateCustomVersionInputs({ customVersion, distTag })

checkNpmAuth()

try {
Expand All @@ -87,21 +112,51 @@ async function publish({ packageName, version, isMaintenance, prRelease }) {
packages
})
} else {
const tag = prRelease ? 'pr-snapshot' : 'snapshot'
const tag = distTag || (prRelease ? 'pr-snapshot' : 'snapshot')
info(`📦 Version: ${version}, Tag: ${tag}`)
await publishSnapshotVersion({
version,
packageName,
packages,
tag,
prRelease
prRelease,
customVersion
})
}
} finally {
cleanupNPMRCFile()
}
}

/**
* Validates the optional --customVersion / --distTag pair used to mirror a
* private security release onto the public registry under a non-latest tag.
* Throws if the inputs are inconsistent or unsafe.
*/
function validateCustomVersionInputs({ customVersion, distTag }) {
if (!customVersion && !distTag) return

if (customVersion) {
if (!semver.valid(customVersion)) {
throw new Error(
`--customVersion must be a valid semver, got: "${customVersion}"`
)
}
if (!semver.prerelease(customVersion)) {
throw new Error(
`--customVersion must be a prerelease (e.g. 11.7.3-SECURITY.0); got "${customVersion}". Refusing to take over a stable version slot.`
)
}
if (!distTag) {
throw new Error('--distTag is required when --customVersion is set.')
}
}

if (distTag === 'latest') {
throw new Error('--distTag cannot be "latest".')
}
}

/**
* Publishes each package to pnpm.
*/
Expand All @@ -113,13 +168,14 @@ async function publishRegularVersion(arg) {
}

/**
* Calculates the new snapshot version based on the latest tag
* and the current commit, then publishes each package to npm
* with the new snapshot version.
* Bumps packages to a snapshot/prerelease version (either operator-supplied
* via --customVersion, or auto-computed from the commit history) and
* publishes them under the given dist-tag.
*/
async function publishSnapshotVersion(arg) {
const { version, packageName, packages, tag, prRelease } = arg
const snapshotVersion = calculateNextSnapshotVersion(version, prRelease)
const { version, packageName, packages, tag, prRelease, customVersion } = arg
const snapshotVersion =
customVersion || calculateNextSnapshotVersion(version, prRelease)

info(`applying new snapshot version (${snapshotVersion}) to each package`)

Expand Down
Loading