Skip to content

Release pipelines from main #5209

Release pipelines from main

Release pipelines from main #5209

Workflow file for this run

name: Release
run-name: ${{ github.event_name == 'issue_comment' && format('Snapshot release by {0}', github.actor) || format('Release pipelines from {0}', github.ref_name) }}
on:
push:
branches:
- main
issue_comment:
types: [created]
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'issue_comment' && format('issue-{0}-{1}', github.event.issue.number, github.actor) || github.ref }}
cancel-in-progress: true
jobs:
release:
name: Release
if: ${{ github.event_name == 'push' && github.repository == 'clerk/javascript' }}
runs-on: ${{ vars.RUNNER_NORMAL || 'ubuntu-latest' }}
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }}
permissions:
contents: write
id-token: write
packages: write
pull-requests: write
issues: read
statuses: write
checks: write
steps:
- name: Echo github context
run: echo "$GITHUB_CONTEXT"
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0
show-progress: false
- name: Setup
id: config
uses: ./.github/actions/init
with:
playwright-enabled: true # Must be present to enable caching on branched workflows
turbo-enabled: false # Release uses --force, so turbo cache is not needed
# turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# turbo-team: ${{ vars.TURBO_TEAM }}
# turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-team: ""
turbo-token: ""
- name: Upgrade npm for trusted publishing
run: npm install -g npm@latest
- name: Build release
run: pnpm turbo build $TURBO_ARGS --force
- name: Create Release PR
id: changesets
uses: changesets/action@v1
with:
commit: "ci(repo): Version packages"
title: "ci(repo): Version packages"
publish: pnpm release
# Workaround for https://github.com/changesets/changesets/issues/421
version: pnpm version-packages
env:
GITHUB_TOKEN: ${{ secrets.CLERK_COOKIE_PAT }}
HUSKY: "0"
NPM_CONFIG_PROVENANCE: true
- name: Trigger workflows on related repos
if: steps.changesets.outputs.published == 'true'
uses: actions/github-script@v7
with:
result-encoding: string
retries: 3
retry-exempt-status-codes: 400,401
github-token: ${{ secrets.CLERK_COOKIE_PAT }}
script: |
const preMode = require("fs").existsSync("./.changeset/pre.json");
if (!preMode) {
const clerkjsVersion = require('./packages/clerk-js/package.json').version;
const clerkUiVersion = require('./packages/ui/package.json').version;
const nextjsVersion = require('./packages/nextjs/package.json').version;
const dispatches = [
github.rest.actions.createWorkflowDispatch({
owner: 'clerk',
repo: 'sdk-infra-workers',
workflow_id: 'update-pkg-versions.yml',
ref: 'main',
inputs: { clerkjsVersion, clerkUiVersion }
}),
github.rest.actions.createWorkflowDispatch({
owner: 'clerk',
repo: 'dashboard',
workflow_id: 'prepare-nextjs-sdk-update.yml',
ref: 'main',
inputs: { version: nextjsVersion }
}),
github.rest.actions.createWorkflowDispatch({
owner: 'clerk',
repo: 'clerk-docs',
workflow_id: 'typedoc.yml',
ref: 'main',
}),
];
await Promise.all(dispatches);
} else{
core.warning("Changeset in pre-mode should not prepare a ClerkJS production release")
}
- name: Generate notification payload
id: notification
if: steps.changesets.outputs.published == 'true'
run: payload=$(node scripts/notify.mjs '${{ steps.changesets.outputs.publishedPackages }}' '${{ github.actor }}') && echo ::set-output name=payload::${payload//$'\n'/'%0A'}
- name: Send commit log to Slack
id: slack
if: steps.changesets.outputs.published == 'true'
uses: slackapi/slack-github-action@v1.24.0
with:
payload: ${{ steps.notification.outputs.payload }}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CHANGELOG_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
- name: Notify Slack on failure
if: ${{ always() && steps.changesets.outcome == 'failure' }}
uses: slackapi/slack-github-action@v1.24.0
with:
payload: |
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*:red_circle: Stable release failed*\n*Repo:* `${{ github.repository }}`\n*Workflow:* `${{ github.workflow }}`\n*Commit:* `${{ github.sha }}`\n*Triggered by:* `${{ github.actor }}`\n*Run:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View logs>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SDK_SLACKER_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
canary-release:
name: Canary release
if: ${{ github.event_name == 'push' && github.repository == 'clerk/javascript' }}
runs-on: ${{ vars.RUNNER_NORMAL || 'ubuntu-latest' }}
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }}
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: remote:rw
permissions:
contents: read
id-token: write
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 1
fetch-tags: false
filter: 'blob:none'
- name: Setup
id: config
uses: ./.github/actions/init
with:
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-team: ${{ vars.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
playwright-enabled: true # Must be present to enable caching on branched workflows
- name: Upgrade npm for trusted publishing
run: npm install -g npm@latest
- name: Version packages for canary
id: version-packages
run: pnpm version-packages:canary | tail -1 >> "$GITHUB_OUTPUT"
- name: Build release
if: steps.version-packages.outputs.success == '1'
run: pnpm turbo build $TURBO_ARGS
- name: Canary release
id: publish
if: steps.version-packages.outputs.success == '1'
run: pnpm release:canary
env:
NPM_CONFIG_PROVENANCE: true
- name: Trigger workflows on related repos
if: steps.publish.outcome == 'success'
uses: actions/github-script@v7
with:
result-encoding: string
retries: 3
retry-exempt-status-codes: 400,401
github-token: ${{ secrets.CLERK_COOKIE_PAT }}
script: |
const clerkjsVersion = require('./packages/clerk-js/package.json').version;
const clerkUiVersion = require('./packages/ui/package.json').version;
const nextjsVersion = require('./packages/nextjs/package.json').version;
const dispatches = [
github.rest.actions.createWorkflowDispatch({
owner: 'clerk',
repo: 'sdk-infra-workers',
workflow_id: 'update-pkg-versions.yml',
ref: 'main',
inputs: { clerkjsVersion, clerkUiVersion, sourceCommit: context.sha }
}),
];
if (nextjsVersion.includes('canary')) {
console.log('clerk/nextjs changed, will notify clerk/accounts');
dispatches.push(
github.rest.actions.createWorkflowDispatch({
owner: 'clerk',
repo: 'accounts',
workflow_id: 'release-staging.yml',
ref: 'main',
inputs: { version: nextjsVersion }
}),
);
}
await Promise.all(dispatches);
- name: Notify Slack on failure
if: ${{ always() && steps.publish.outcome == 'failure' }}
uses: slackapi/slack-github-action@v1.24.0
with:
payload: |
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*:red_circle: Canary release failed*\n*Repo:* `${{ github.repository }}`\n*Workflow:* `${{ github.workflow }}`\n*Commit:* `${{ github.sha }}`\n*Triggered by:* `${{ github.actor }}`\n*Run:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View logs>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SDK_SLACKER_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
snapshot-release:
name: Snapshot release
if: ${{ github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '!snapshot') && github.repository == 'clerk/javascript' && github.event.issue.pull_request }}
runs-on: ${{ vars.RUNNER_LARGE || 'ubuntu-latest-l' }}
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }}
permissions:
contents: read
id-token: write
pull-requests: write
steps:
- name: Limit action to Clerk members
uses: actions/github-script@v7
with:
result-encoding: string
retries: 3
retry-exempt-status-codes: 400,401
github-token: ${{ secrets.CLERK_COOKIE_PAT }}
script: |
try {
const { data } = await github.rest.orgs.getMembershipForUser({
org: 'clerk',
username: context.actor
});
if (data.state !== 'active') {
core.setFailed(`@${context.actor} is not an active member of the Clerk organization`);
}
} catch (e) {
core.setFailed(`@${context.actor} is not a member of the Clerk organization`);
}
- name: Checkout repo
uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.issue.number }}/head
fetch-depth: 1
fetch-tags: false
filter: 'blob:none'
- name: Ensure the PR hasn't changed since initiating the !snapshot command.
uses: actions/github-script@v7
with:
result-encoding: string
retries: 3
retry-exempt-status-codes: 400,401
script: |
const commentCreated = new Date(context.payload.comment.created_at);
const { data: pr } = await github.rest.pulls.get({
owner: 'clerk',
repo: 'javascript',
pull_number: context.issue.number,
});
const prLastUpdated = new Date(pr.updated_at);
if (prLastUpdated > commentCreated) {
core.setFailed("The PR has been updated since !snapshot was initiated. Please review the changes and re-run the !snapshot command.");
}
- name: Setup
id: config
uses: ./.github/actions/init
with:
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-team: ${{ vars.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
- name: Upgrade npm for trusted publishing
run: npm install -g npm@latest
- name: Extract snapshot name
id: extract-snapshot-name
uses: actions/github-script@v7
with:
script: |
const match = context.payload.comment.body.match(/!snapshot (.*)/)
const name = match && match[1] || '';
const isKebabCase = name.match(/^[a-z]+(-[a-z]+)*$/)
if(name && !isKebabCase) {
core.setFailed(`Invalid snapshot name: ${name}`);
}
core.setOutput('name', name);
- name: Version packages for snapshot
id: version-packages
run: pnpm version-packages:snapshot ${{ steps.extract-snapshot-name.outputs.name }} | tail -1 >> "$GITHUB_OUTPUT"
- name: Build release
if: steps.version-packages.outputs.success == '1'
run: pnpm turbo build $TURBO_ARGS
- name: Snapshot release
if: steps.version-packages.outputs.success == '1'
run: pnpm release:snapshot
env:
NPM_CONFIG_PROVENANCE: true
- name: Package info
if: steps.version-packages.outputs.success == '1'
id: package-info
uses: actions/github-script@v7
with:
script: |
const fs = require("fs");
const files = await (await glob.create("./packages/*/package.json")).glob();
const descriptors = files
.map((file) => {
const { name, version } = JSON.parse(fs.readFileSync(file, "utf8"));
return { name, version };
})
.filter(({ version }) => version.includes("-${{ steps.extract-snapshot-name.outputs.name }}"));
let table = `| Package | Version |\n| --- | --- |\n`;
descriptors.forEach(({ name, version }) => {
table += `| ${name} | ${version} |\n`;
});
const snippets = descriptors
.map(
({ name, version }) =>
`\`${name}\`\n\`\`\`sh\nnpm i ${name}@${version} --save-exact\n\`\`\``
)
.join("\n");
core.setOutput("table", table);
core.setOutput("snippets", snippets);
- name: Update Comment
if: steps.version-packages.outputs.success == '1'
uses: peter-evans/create-or-update-comment@v3.0.0
with:
comment-id: ${{ github.event.comment.id }}
reactions: heart
- name: Minimize previous snapshot comments
if: steps.version-packages.outputs.success == '1'
uses: actions/github-script@v7
with:
script: |
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
});
const snapshotComments = comments.filter(
(comment) =>
comment.body?.includes('the snapshot version command generated the following package versions')
);
for (const comment of snapshotComments) {
await github.graphql(`
mutation MinimizeComment($id: ID!) {
minimizeComment(input: { subjectId: $id, classifier: OUTDATED }) {
minimizedComment {
isMinimized
}
}
}
`, { id: comment.node_id });
}
- name: Create snapshot release comment
if: steps.version-packages.outputs.success == '1'
uses: peter-evans/create-or-update-comment@v3.0.0
with:
issue-number: ${{ github.event.issue.number }}
body: |
Hey @${{ github.event.comment.user.login }} - the snapshot version command generated the following package versions:
${{ steps.package-info.outputs.table }}
Tip: Use the snippet copy button below to quickly install the required packages.
${{ steps.package-info.outputs.snippets }}
# We're running the CI workflow (where node v20 modules are cached) in
# merge_group and not on main, we need to explicitly cache node_modules here so
# that follow-on branches can use the cached version of node_modules rather
# than recreating them every time.
cache-for-alternate-node-versions:
name: Cache for Alternate Node Versions
if: ${{ github.event_name == 'push' }}
runs-on: ${{ vars.RUNNER_NORMAL || 'ubuntu-latest' }}
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }}
continue-on-error: true
strategy:
matrix:
version: [22] # NOTE: 18 is cached in the main release workflow
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 1
fetch-tags: false
filter: "blob:none"
show-progress: false
- name: Cache node_modules (Node v${{ matrix.version }})
uses: ./.github/actions/init
with:
node-version: ${{ matrix.version }}
turbo-enabled: false
turbo-team: ""
turbo-token: ""