[*] Feature: Automated nightly releases (#6204)

This commit is contained in:
Bob Ippolito
2024-06-08 21:23:47 -07:00
committed by GitHub
parent 28b3f90144
commit 8bc70509f3
10 changed files with 300 additions and 72 deletions

View File

@ -0,0 +1,67 @@
name: Increment Version
on:
workflow_call:
inputs:
increment:
required: true
type: string
dry-run:
required: true
type: boolean
channel:
required: true
type: string
git-repo:
required: true
type: string
secrets:
SSH_KEY:
required: true
outputs:
version:
description: 'The new package.json version, e.g. "0.16.0"'
value: ${{ jobs.release.outputs.version }}
tag-ref:
description: 'The fully qualified ref for the tag, e.g. "refs/tags/v0.16.0"'
value: ${{ jobs.release.outputs.tag-ref }}
latest-release:
description: 'The latest release (per GitHub releases) prior to this increment, e.g. "v0.15.0"'
value: ${{ jobs.release.outputs.latest-release }}
jobs:
release:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.increment-version.outputs.version }}
tag-ref: ${{ steps.increment-version.outputs.tag-ref }}
latest-release: ${{ steps.latest.outputs.release }}
steps:
- uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.SSH_KEY }}
fetch-depth: 0
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: |
git config user.name "Lexical GitHub Actions Bot"
git config user.email "<>"
- id: latest
uses: pozetroninc/github-action-get-latest-release@master
with:
owner: facebook
repo: lexical
excludes: draft
- id: increment-version
run: npm run increment-version
env:
# These are passed in the environment as they are used by
# the postversion script
INCREMENT: ${{ inputs.increment }}
CHANNEL: ${{ inputs.channel }}
LATEST_RELEASE: ${{ steps.latest.outputs.release }}
DRY_RUN: ${{ inputs.dry-run && '1' || '' }}
GIT_REPO: ${{ inputs.git-repo }}

36
.github/workflows/call-npm-publish.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: (call) Publish to NPM
on:
workflow_call:
inputs:
ref:
required: true
type: string
dry-run:
required: true
type: boolean
channel:
required: true
type: string
secrets:
NPM_TOKEN:
required: true
jobs:
release:
runs-on: ubuntu-latest
env:
DRY_RUN_ARG: ${{ inputs.dry-run && '--dry-run' || '' }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run prepare-release
- run: node ./scripts/npm/release.js --non-interactive $DRY_RUN_ARG --channel='${{ inputs.channel }}'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -1,28 +1,26 @@
name: Nightly Release Branch
on:
# remove the workflow_dispatch when this is turned on
workflow_dispatch
# Run daily at 2:30am UTC
# schedule:
# - cron: '30 2 * * 1-5'
schedule:
- cron: '30 2 * * 1-5'
jobs:
release:
increment-version:
# prevents this action from running on forks
if: github.repository_owner == 'facebook'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.SSH_KEY }}
fetch-depth: 0
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: 'https://registry.npmjs.org'
- run: |
git config user.name "Lexical GitHub Actions Bot"
git config user.email "<>"
- run: npm ci
- run: npm run increment-version -- --i prerelease
- run: git push -u git@github.com:facebook/lexical.git --follow-tags
uses: ./.github/workflows/call-increment-version.yml
with:
channel: nightly
increment: prerelease
dry-run: false
git-repo: 'git@github.com:facebook/lexical.git'
secrets:
SSH_KEY: ${{ secrets.SSH_KEY }}
npm-release:
uses: ./.github/workflows/call-npm-publish.yml
needs: [increment-version]
with:
ref: ${{ needs.increment-version.outputs.tag-ref }}
dry-run: false
channel: nightly
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -3,16 +3,10 @@ on: workflow_dispatch
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run prepare-release
- run: node ./scripts/npm/release.js --non-interactive --dry-run=${{ secrets.RELEASE_DRY_RUN }} --channel='latest'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
uses: ./.github/workflows/call-npm-publish.yml
with:
ref: main
dry-run: false
channel: latest
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -13,29 +13,11 @@ on:
- minor
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.SSH_KEY }}
fetch-depth: 0
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: 'https://registry.npmjs.org'
- run: |
git config user.name "Lexical GitHub Actions Bot"
git config user.email "<>"
- run: npm install
- id: latest
uses: pozetroninc/github-action-get-latest-release@master
with:
owner: facebook
repo: lexical
excludes: draft
- run: LATEST_RELEASE=${{ steps.latest.outputs.release }} npm run increment-version -- --i $INCREMENT
env:
INCREMENT: ${{ inputs.increment }}
- run: npm install
- run: git push -u git@github.com:facebook/lexical.git --follow-tags
uses: ./.github/workflows/call-increment-version.yml
with:
increment: ${{ inputs.increment }}
dry-run: false
channel: ${{ inputs.increment == 'prerelease' && 'next' || 'latest' }}
git-repo: 'git@github.com:facebook/lexical.git'
secrets:
SSH_KEY: ${{ secrets.SSH_KEY }}

View File

@ -6,6 +6,7 @@
!scripts/npm/**
**/.output/**
**/.browser-profiles/**
!scripts/npm/**
**/__tests__/integration/fixtures/**
packages/**/.wxt/**
packages/playwright

View File

@ -104,7 +104,7 @@
"update-flowconfig": "node ./scripts/update-flowconfig",
"create-www-stubs": "node ./scripts/create-www-stubs",
"update-packages": "npm run update-version && npm run update-tsconfig && npm run update-flowconfig && npm run create-docs && npm run create-www-stubs",
"postversion": "git checkout -b ${npm_package_version}__release && npm run update-version && npm install && npm run update-packages && npm run extract-codes && npm run update-changelog && git add -A && git commit -m v${npm_package_version} && git tag -a v${npm_package_version} -m v${npm_package_version}",
"postversion": "node ./scripts/npm/postversion",
"publish-extension": "npm run zip -w @lexical/devtools && npm run publish -w @lexical/devtools",
"release": "npm run prepare-release && node ./scripts/npm/release.js",
"size": "npm run build-prod && size-limit"

View File

@ -258,7 +258,7 @@ The postversion script will:
- Create a version commit and tag from the branch
This is typically executed through the `version.yml` GitHub Workflow which
will also push the tag.
will also push the tag and branch.
### npm run changelog
@ -270,3 +270,20 @@ Update the changelog from git history.
plus creating a tag in git, and likely other steps.
Runs prepare-release to do a full build and then uploads to npm.
## Release Procedure
This is the current release procedure for public releases, at least as of
May 2024 (~0.15.0).
The main branch should be "frozen" during this procedure (no other PRs should
be merged during this time). This avoids a mismatch between the contents of
the GitHub release (created from main in step 1) and the NPM release (created
from main in step 4).
1. Create a new version with the Github Actions "Create New Release Branch" workflow (`version.yml`)
2. Raise a PR against version branch created by that action
3. After PR is approved with passing tests, merge PR
4. After PR is merged to main, publish to NPM with the Github Actions "Publish to NPM" workflow (`pre-release.yml`)
5. Create a GitHub release from the tag created in step 1, manually editing the release notes
6. Announce the release in #announcements on Discord

View File

@ -10,21 +10,57 @@
'use strict';
const {exec} = require('child-process-promise');
const {spawn} = require('child-process-promise');
const argv = require('minimist')(process.argv.slice(2));
const increment = argv.i;
const validIncrements = new Set(['minor', 'patch', 'prerelease']);
if (!validIncrements.has(increment)) {
console.error(`Invalid value for increment: ${increment}`);
const increment = argv.i || process.env.INCREMENT;
const channel = argv.channel || process.env.CHANNEL;
const validChannels = new Set(['next', 'latest', 'nightly', 'dev']);
if (!validChannels.has(channel)) {
console.error(`Invalid value for channel: ${channel}`);
process.exit(1);
}
const validIncrements = new Set(['minor', 'patch', 'prerelease']);
if (
!validIncrements.has(increment) ||
(channel === 'nightly' && increment !== 'prerelease')
) {
console.error(
`Invalid value for increment in ${channel} channel: ${increment}`,
);
process.exit(1);
}
function incrementArgs() {
return [
...(increment === 'prerelease'
? [
'--preid',
channel === 'nightly'
? `${channel}.${new Date()
.toISOString()
.split('T')[0]
.replaceAll('-', '')}`
: channel,
]
: []),
increment,
];
}
async function incrementVersion() {
const preId = increment === 'prerelease' ? '--preid next' : '';
const workspaces = '';
const command = `npm --no-git-tag-version version ${increment} --include-workspace-root true ${preId} ${workspaces}`;
await exec(command);
const commandArr = [
'npm',
'version',
'--no-git-tag-version',
'--include-workspace-root',
'true',
...incrementArgs(),
];
console.log(commandArr.join(' '));
await spawn(commandArr[0], commandArr.slice(1), {stdio: 'inherit'});
}
incrementVersion();

View File

@ -0,0 +1,97 @@
#!/usr/bin/env bash
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
'use strict';
const {spawn} = require('child-process-promise');
const {npm_package_version, CHANNEL, GIT_REPO, GITHUB_OUTPUT} = process.env;
// Previously this script was defined directly in package.json as the
// following (in one line):
//
// git checkout -b ${npm_package_version}__release && \
// npm run update-version && \
// npm install && \
// npm run update-packages && \
// npm run extract-codes && \
// npm run update-changelog && \
// git add -A && \
// git commit -m v${npm_package_version} && \
// git tag -a v${npm_package_version} -m v${npm_package_version}
//
async function main() {
// CHANNEL should already be validated by increment-version which calls this (indirectly)
for (const [k, v] of Object.entries({
CHANNEL,
GIT_REPO,
npm_package_version,
})) {
if (!v) {
console.error(`Expecting ${k} to be set in the environment`);
process.exit(1);
}
}
const commands = [
// Create or force update the channel branch to build the docs site from
['git', 'checkout', '-B', `${CHANNEL}__release`],
// Update all package.json versions in the monorepo
`npm run update-version`,
// Update package-lock.json
`npm install`,
// Fix up all package.json files
`npm run update-packages`,
// Extract error codes and update changelog, but only in production
...(CHANNEL === 'latest'
? [`npm run extract-codes`, `npm run update-changelog`]
: []),
`git add -A`,
['git', 'commit', '-m', `v${npm_package_version}`],
[
'git',
'tag',
'-a',
`v${npm_package_version}`,
'-m',
`v${npm_package_version}`,
],
];
const refs = [
`refs/tags/v${npm_package_version}`,
`refs/heads/${CHANNEL}__release`,
];
if (CHANNEL !== 'nightly') {
// Create or force update the remote version branch for creating a PR
refs.push(
`refs/heads/${CHANNEL}__release:refs/heads/${npm_package_version}__release`,
);
}
commands.push([
'git',
'push',
...(process.env.DRY_RUN === '1' ? ['--dry-run'] : []),
GIT_REPO,
...refs.map((ref) => `+${ref}`),
]);
if (GITHUB_OUTPUT) {
commands.push(
`echo "version=${npm_package_version}" >> '${GITHUB_OUTPUT}'`,
);
commands.push(`echo "tag-ref=${refs[0]}" >> '${GITHUB_OUTPUT}'`);
}
for (const command of commands) {
const commandArr = Array.isArray(command)
? command
: ['bash', '-c', command];
console.log(commandArr.join(' '));
await spawn(commandArr[0], commandArr.slice(1), {stdio: 'inherit'});
}
}
main();