From 85b80f512eaecef278c438bd9dd567096ada15fb Mon Sep 17 00:00:00 2001 From: Trevor Pierce <1Copenut@users.noreply.github.com> Date: Sun, 3 Dec 2023 05:26:59 +0000 Subject: [PATCH] Better backport and prerelease DX (#7390) Co-authored-by: Cee Chen --- catalog-info.yaml | 6 +- package.json | 5 +- scripts/release.js | 190 ++++++++++-------- scripts/tests/update-versions-log.test.js | 47 ++++- scripts/update-changelog.js | 35 ++-- scripts/update-token-changelog.js | 17 +- scripts/update-versions-log.js | 24 ++- wiki/eui-team-processes/releasing-versions.md | 52 ++--- 8 files changed, 204 insertions(+), 172 deletions(-) diff --git a/catalog-info.yaml b/catalog-info.yaml index bd25e1197db..287c858db51 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -223,11 +223,13 @@ spec: spec: repository: elastic/eui pipeline_file: ".buildkite/pipelines/pipeline_release_deploy_docs.yml" - default_branch: main provider_settings: build_branches: false - build_tags: true # Will trigger job when GitHub tags are created + build_tags: true # Will trigger job when GitHub release tags are created build_pull_requests: false + filter_enabled: true + filter_condition: | # Eg 100.100.100 but skip 100.100.100.1 or 100.100.100-rc + build.tag =~ /^[0-9]+\.[0-9]+\.[0-9]+$/ teams: eui-team: access_level: MANAGE_BUILD_AND_READ diff --git a/package.json b/package.json index 17527f770d4..89ac8805778 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,11 @@ "test-cypress-a11y": "yarn test-cypress --a11y", "combine-test-coverage": "sh ./scripts/combine-coverage.sh", "yo-component": "yo ./generator-eui/app/component.js", - "update-token-changelog": "node ./scripts/update-token-changelog.js", - "update-changelog-manual": "node -e \"require('./scripts/update-changelog').manualChangelog('${npm_config_release}')\"", "yo-doc": "yo ./generator-eui/app/documentation.js", "yo-changelog": "yo ./generator-eui/changelog/index.js", "release": "node ./scripts/release.js", + "release-backport": "node ./scripts/release.js --type=backport", + "release-rc": "node ./scripts/release.js --type=prerelease", "storybook": "yarn compile-scss && storybook dev -p 6006", "build-storybook": "yarn compile-scss && storybook build" }, @@ -152,7 +152,6 @@ "@typescript-eslint/eslint-plugin": "^5.59.7", "@typescript-eslint/parser": "^5.59.7", "@wojtekmaj/enzyme-adapter-react-17": "^0.6.6", - "argparse": "^2.0.1", "assert": "^2.0.0", "autoprefixer": "^9.8.6", "axe-core": "^4.4.1", diff --git a/scripts/release.js b/scripts/release.js index b69c71fb1a7..ca83e443f3b 100644 --- a/scripts/release.js +++ b/scripts/release.js @@ -1,4 +1,5 @@ -const argparse = require('argparse'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); const chalk = require('chalk'); const path = require('path'); let { execSync } = require('child_process'); @@ -7,6 +8,7 @@ const cwd = path.resolve(__dirname, '..'); const stdio = 'inherit'; const execOptions = { cwd, stdio }; +const updateTokenChangelog = require('./update-token-changelog'); const { collateChangelogFiles, updateChangelog, @@ -16,26 +18,70 @@ const { updateDocsVersionSwitcher, } = require('./update-versions-log'); -const TYPE_MAJOR = 0; -const TYPE_MINOR = 1; -const TYPE_PATCH = 2; -const humanReadableTypes = { - [TYPE_MAJOR]: 'major', - [TYPE_MINOR]: 'minor', - [TYPE_PATCH]: 'patch', -}; +const TYPE_MAJOR = 'major'; +const TYPE_MINOR = 'minor'; +const TYPE_PATCH = 'patch'; +const TYPE_BACKPORT = 'backport'; +const TYPE_PRERELEASE = 'prerelease'; + +// NOTE: Because this script has to be run with `npm`, args must be passed after an extra `--` +// e.g. `npm run release -- --dry-run`, `npm run release -- --steps=build,version` +const args = yargs(hideBin(process.argv)) + .parserConfiguration({ + 'camel-case-expansion': false, + 'halt-at-non-option': true, + }) + .describe('Tag and publish a new version of EUI') + .options({ + 'dry-run': { + type: 'boolean', + default: false, + describe: 'Dry run mode; no changes are made', + }, + type: { + type: 'string', + choices: [ + TYPE_MAJOR, + TYPE_MINOR, + TYPE_PATCH, + TYPE_BACKPORT, + TYPE_PRERELEASE, + ], + describe: + 'Version type; For normal releases, can be "major", "minor" or "patch". Special releases: "backport" and "prerelease". If not passed, will be automatically prompted for based on the upcoming changelogs.', + }, + steps: { + type: 'string', + describe: + 'Which release steps to run; a comma-separated list of values that can include "test", "build", "version", "tag", and "publish". If no value is given, all steps are run. Example: --steps=test,build,version,tag', + coerce: (value) => { + if (value) { + const allSteps = ['test', 'build', 'version', 'tag', 'publish']; + const steps = value.split(',').map((step) => step.trim()); + const invalidSteps = steps.filter((step) => !allSteps.includes(step)); + if (invalidSteps.length > 0) { + console.error(`Invalid --step(s): ${invalidSteps.join(', ')}`); + process.exit(1); + } + } + return value; + }, + }, + }).argv; -const args = parseArguments(); +const isSpecialRelease = + args.type === TYPE_BACKPORT || args.type === TYPE_PRERELEASE; -if (args.dry_run) { - console.warn( - chalk.yellow('Dry run mode: no changes will be pushed to npm or Github') - ); - execSync = function () { - console.log.apply(null, arguments); - }; -} +const isDryRun = args['dry-run'] === true; + +const hasStep = (step) => { + if (!args.steps) return true; // If no steps were passed, run them all + return args.steps.includes(step); +}; +/** + * Main script + */ (async function () { // make sure the release script is being run by npm (required for `npm publish` step) // https://github.com/yarnpkg/yarn/issues/5063 @@ -45,51 +91,60 @@ if (args.dry_run) { process.exit(1); } - // ensure git and local setup is at latest - await ensureCorrectSetup(); + if (isDryRun) { + console.warn( + chalk.yellow('Dry run mode: no changes will be pushed to npm or Github') + ); + } else { + // ensure git and local setup is at latest + await ensureCorrectSetup(); + } // run lint, unit, and e2e tests - if (args.steps.indexOf('test') > -1) { + if (hasStep('test')) { execSync('npm run test-ci', execOptions); } // (trans|com)pile `src` into `lib` and `dist` - if (args.steps.indexOf('build') > -1) { + if (hasStep('build')) { execSync('npm run build', execOptions); } let versionTarget; - if (args.steps.indexOf('version') > -1) { + if (hasStep('version')) { // Fetch latest tags and clear any local ones execSync('git fetch upstream --tags --prune --prune-tags --force'); + // Prompt user for what type of version bump to make (major|minor|patch) based on the upcoming changelogs const { changelogMap, changelog } = collateChangelogFiles(); + const versionType = await getVersionTypeFromChangelog(changelogMap); - // prompt user for what type of version bump to make (major|minor|patch) - versionTarget = await getVersionTypeFromChangelog(changelogMap); + // Get the upcoming version target + versionTarget = getUpcomingVersion(versionType); // build may have generated a new i18ntokens.json file, dirtying the git workspace // it's important to track those changes with this release, so determine the changes and write them // to i18ntokens_changelog.json, committing both to the workspace before running `npm version` - execSync(`npm run update-token-changelog -- ${versionTarget}`, execOptions); + await updateTokenChangelog(versionTarget); - // Update version switcher data and CHANGELOG.md - const upcomingVersion = getUpcomingVersion(versionTarget); - updateDocsVersionSwitcher(upcomingVersion); - updateChangelog(changelog, upcomingVersion); + // Update version switcher data and changelog + if (!isSpecialRelease) updateDocsVersionSwitcher(versionTarget); + updateChangelog(changelog, versionTarget); execSync('git commit -m "Updated changelog" -n'); // update package.json & package-lock.json version, git commit, git tag execSync(`npm version ${versionTarget}`, execOptions); } - if (args.steps.indexOf('tag') > -1) { + if (hasStep('tag') && !isDryRun) { // push the version commit & tag to upstream - execSync('git push upstream --follow-tags', execOptions); + // conditionally skip prepush test hook if we already ran the test step earlier + const withTests = hasStep('test') ? '--no-verify' : ''; + execSync(`git push upstream --follow-tags ${withTests}`, execOptions); } - if (args.steps.indexOf('publish') > -1) { + if (hasStep('publish') && !isDryRun) { // prompt user for npm 2FA const otp = await getOneTimePassword(versionTarget); @@ -98,45 +153,6 @@ if (args.dry_run) { } })().catch((e) => console.error(e)); -function parseArguments() { - const parser = new argparse.ArgumentParser({ - add_help: true, - description: 'Tag and publish a new version of EUI', - }); - - parser.add_argument('--type', { - help: 'Version type; can be "major", "minor" or "patch"', - choices: Object.values(humanReadableTypes), - }); - - parser.add_argument('--dry-run', { - action: 'store_true', - default: false, - help: 'Dry run mode; no changes are made', - }); - - const allSteps = ['test', 'build', 'version', 'tag', 'publish']; - parser.add_argument('--steps', { - help: 'Which release steps to run; a comma-separated list of values that can include "test", "build", "version", "tag", and "publish". If no value is given, all steps are run. Example: --steps=test,build,version,tag', - default: allSteps.join(','), - }); - - const args = parser.parse_args(); - - // validate --steps argument - const steps = args.steps.split(',').map((step) => step.trim()); - const diff = steps.filter((x) => allSteps.indexOf(x) === -1); - if (diff.length > 0) { - console.error(`Invalid --step value(s): ${diff.join(', ')}`); - process.exit(1); - } - - return { - ...args, - steps, - }; -} - async function ensureCorrectSetup() { if (process.env.CI === 'true') { return; @@ -174,10 +190,13 @@ async function ensureCorrectSetup() { if ( !branchStatus.includes("Your branch is up to date with 'upstream/main'.") ) { - console.error( - 'Your branch is not pointed at "upstream/main". Please ensure your `main` branch is pointed at the correct remote first before proceeding.' - ); - process.exit(1); + // Backports and prereleases do not need to be made from main branch + if (!isSpecialRelease) { + console.error( + 'Your branch is not pointed at "upstream/main". Please ensure your `main` branch is pointed at the correct remote first before proceeding.' + ); + process.exit(1); + } } if (!branchStatus.endsWith('nothing to commit, working tree clean')) { console.error( @@ -194,6 +213,16 @@ async function ensureCorrectSetup() { } async function getVersionTypeFromChangelog(changelogMap) { + // Special releases don't need to check recommended semver + if (isSpecialRelease) { + console.log( + `${chalk.magenta('--type set to')} ${chalk.blue( + args.type + )}. Creating a special release` + ); + return args.type; + } + // @see update-changelog.js const hasFeatures = changelogMap['Features'].length > 0; const hasBugFixes = changelogMap['Bug fixes'].length > 0; @@ -212,7 +241,6 @@ async function getVersionTypeFromChangelog(changelogMap) { recommendedType = TYPE_MAJOR; } - const humanReadableRecommendation = humanReadableTypes[recommendedType]; console.log(chalk.magenta('Detected the following upcoming changelogs:')); console.log(''); Object.entries(changelogMap).forEach(([section, items]) => { @@ -222,7 +250,7 @@ async function getVersionTypeFromChangelog(changelogMap) { console.log( `${chalk.magenta( 'The recommended version update for these changes is' - )} ${chalk.blue(humanReadableRecommendation)}` + )} ${chalk.blue(recommendedType)}` ); // checking for --type argument value; used by CI to automate releases @@ -235,7 +263,7 @@ async function getVersionTypeFromChangelog(changelogMap) { )}` ); - if (versionType !== humanReadableRecommendation) { + if (versionType !== recommendedType) { console.warn( `${chalk.yellow( 'WARNING: --type argument does not match recommended version update' @@ -251,7 +279,7 @@ async function getVersionTypeFromChangelog(changelogMap) { )} ${chalk.gray('(major, minor, patch)')}` ); - return await promptUserForVersionType(humanReadableRecommendation); + return await promptUserForVersionType(recommendedType); } } @@ -262,7 +290,7 @@ async function promptUserForVersionType(recommendedType) { type: 'list', name: 'versionType', message: 'Your choice must be major, minor, or patch', - choices: ['major', 'minor', 'patch'], + choices: [TYPE_MAJOR, TYPE_MINOR, TYPE_PATCH], default: recommendedType || '', }, ]); diff --git a/scripts/tests/update-versions-log.test.js b/scripts/tests/update-versions-log.test.js index ea9739884bd..b4a12fa7fdb 100644 --- a/scripts/tests/update-versions-log.test.js +++ b/scripts/tests/update-versions-log.test.js @@ -66,18 +66,45 @@ describe('updateDocsVersionSwitcher', () => { }); }); +import pkg from '../../package.json'; +jest.mock('../../package.json', () => ({})); describe('getUpcomingVersion', () => { - jest.mock('../../package.json', () => ({ - version: '1.2.3', - })); - - test('patch', () => { - expect(getUpcomingVersion('patch')).toEqual('1.2.4'); + beforeEach(() => { + pkg.version = '1.2.3'; }); - test('minor', () => { - expect(getUpcomingVersion('minor')).toEqual('1.3.0'); + + describe('main releases', () => { + test('patch', () => { + expect(getUpcomingVersion('patch')).toEqual('1.2.4'); + }); + test('minor', () => { + expect(getUpcomingVersion('minor')).toEqual('1.3.0'); + }); + test('major', () => { + expect(getUpcomingVersion('major')).toEqual('2.0.0'); + }); }); - test('major', () => { - expect(getUpcomingVersion('major')).toEqual('2.0.0'); + + describe('special releases', () => { + test('new backport', () => { + expect(getUpcomingVersion('backport')).toEqual('1.2.3-backport.0'); + }); + test('exising backport', () => { + pkg.version = '1.2.3-backport.0'; + expect(getUpcomingVersion('backport')).toEqual('1.2.3-backport.1'); + }); + + test('new prerelease', () => { + expect(getUpcomingVersion('prerelease')).toEqual('1.2.3-rc.0'); + }); + test('exising prerelease', () => { + pkg.version = '1.2.3-rc.1'; + expect(getUpcomingVersion('prerelease')).toEqual('1.2.3-rc.2'); + }); + + it('increments odd formats', () => { + pkg.version = '1.2.3-backport-alpha'; + expect(getUpcomingVersion('backport')).toEqual('1.2.3-backport.0'); + }); }); }); diff --git a/scripts/update-changelog.js b/scripts/update-changelog.js index 87d08687697..c0126594acc 100644 --- a/scripts/update-changelog.js +++ b/scripts/update-changelog.js @@ -116,9 +116,20 @@ const updateChangelog = (upcomingChangelog, version) => { } const pathToChangelog = path.resolve(rootDir, 'CHANGELOG.md'); - const changelogArchive = fs.readFileSync(pathToChangelog).toString(); - const latestVersionHeading = `## [\`${version}\`](https://github.com/elastic/eui/tree/v${version})`; + let changelogArchive = ''; + try { + changelogArchive = fs.readFileSync(pathToChangelog).toString(); + } catch {} + + let latestVersionHeading = `## [\`v${version}\`](https://github.com/elastic/eui/releases/v${version})`; + if (version.includes('-backport')) { + latestVersionHeading += + '\n\n**This is a backport release only intended for use by Kibana.**'; + } else if (version.includes('-rc')) { + latestVersionHeading += + '\n\n**This is a prerelease candidate not intended for public use.**'; + } if (changelogArchive.startsWith(latestVersionHeading)) { throwError('Cannot update CHANGELOG.md - already on latest version'); @@ -133,27 +144,7 @@ const updateChangelog = (upcomingChangelog, version) => { execSync('git add CHANGELOG.md upcoming_changelogs/'); }; -/** - * Command to manually update the changelog (standalone from release.js). - * Primarily used for backports. Usage from project root: - * - * npm run update-changelog-manual --release=patch|minor|major (must be `npm` and not `yarn` to specify the release arg) - * OR - * node -e "require('./scripts/update-changelog').manualChangelog('patch|minor|major')" - */ -const manualChangelog = (release) => { - versionTarget = release || 'patch'; // Unfortunately can't be a = fallback, because the package.json script passes an empty string - console.log( - chalk.magenta( - `Manually updating CHANGELOG.md to next ${versionTarget} version.` - ) - ); - const { changelog } = collateChangelogFiles(); - updateChangelog(changelog, versionTarget); -}; - module.exports = { collateChangelogFiles, updateChangelog, - manualChangelog, }; diff --git a/scripts/update-token-changelog.js b/scripts/update-token-changelog.js index 331c33d0196..5493bc1f963 100644 --- a/scripts/update-token-changelog.js +++ b/scripts/update-token-changelog.js @@ -1,6 +1,5 @@ const fs = require('fs'); const path = require('path'); -const semver = require('semver'); const { execSync } = require('child_process'); const repoDir = path.resolve(__dirname, '..'); @@ -8,16 +7,7 @@ const packagePath = path.resolve(repoDir, 'package.json'); const tokensPath = path.resolve(repoDir, 'i18ntokens.json'); const tokensChangelogPath = path.resolve(repoDir, 'i18ntokens_changelog.json'); -const validVersionTypes = new Set(['patch', 'minor', 'major']); -const [, , versionIncrementType] = process.argv; - -if (validVersionTypes.has(versionIncrementType) === false) { - console.error(`Invalid version increment "${versionIncrementType}" passed`); - process.exit(1); -} - const { version: oldPackageVersion } = require(packagePath); -const newPackageVersion = semver.inc(oldPackageVersion, versionIncrementType); function getTokenMap(tokenInstances) { // tokenInstances is the total set of tokens across all files @@ -107,7 +97,7 @@ async function getPreviousI18nTokens() { return JSON.parse(fileContents); } -async function main() { +async function main(newPackageVersion) { // check for i18n token differences between the current file & the most recent EUI version const originalTokens = await getPreviousI18nTokens(); const newTokens = require(tokensPath); @@ -128,7 +118,4 @@ async function main() { await commitTokenChanges(); } -main().catch((e) => { - console.error(e); - process.exit(1); -}); +module.exports = main; diff --git a/scripts/update-versions-log.js b/scripts/update-versions-log.js index 91a3fb72649..ed3d4f53541 100644 --- a/scripts/update-versions-log.js +++ b/scripts/update-versions-log.js @@ -38,12 +38,29 @@ const updateDocsVersionSwitcher = (versionToAdd, file = versionsLogFile) => { /** * Get the current EUI version and increment it based on the - * user-input versionTarget (major/minor/patch) + * user-input versionTarget (major/minor/patch, backport/prerelease) */ const getUpcomingVersion = (versionTarget) => { const pathToPackage = rootDir + '/package.json'; const { version } = require(pathToPackage); + + // Normal releases let [major, minor, patch] = version.split('.').map(Number); + + // Special releases, e.g. `v1.1.1-backport.0` or `v2.2.2-rc.1` + const incrementPreId = (preId) => { + const [versionWithoutPreId, affix] = version.split(`-${preId}`); + // Releasing from a main release, e.g. `v1.1.1` - add the preId and number automatically + if (!affix) return `${version}-${preId}.0`; + + const releaseNumber = Number(affix.split('.')[1]); + // Edge case for odd formats - coerce to the format we want + if (isNaN(releaseNumber)) return `${versionWithoutPreId}-${preId}.0`; + + // Otherwise, increment the existing release number + return `${versionWithoutPreId}-${preId}.${releaseNumber + 1}`; + }; + switch (versionTarget) { case 'major': major += 1; @@ -57,7 +74,12 @@ const getUpcomingVersion = (versionTarget) => { case 'patch': patch += 1; break; + case 'backport': + return incrementPreId('backport'); + case 'prerelease': + return incrementPreId('rc'); } + return [major, minor, patch].join('.'); }; diff --git a/wiki/eui-team-processes/releasing-versions.md b/wiki/eui-team-processes/releasing-versions.md index 3b557316be3..70ff31a6764 100644 --- a/wiki/eui-team-processes/releasing-versions.md +++ b/wiki/eui-team-processes/releasing-versions.md @@ -66,25 +66,19 @@ For information on releasing the eslint plugin checkout the readme in [packages/ # Backport process -In general, we strongly encourage updating to the latest version of EUI to obtain bug fixes, and we do not actively consider backporting fixes to previous major or minor release versions. The exception to this is when supporting Kibana's release process, as we want to avoid pushing larger changes near the feature freeze. +In general, we strongly encourage updating to the latest version of EUI to obtain bug fixes, and we do not actively consider backporting fixes to previous major or minor release versions. -### Tracking issue +As such, backports are typically only released to support Kibana's [upgrade process](./upgrading-kibana.md), typically when we want to avoid pushing larger changes near the feature freeze. -When preparing for a backport a GitHub issue should be created in EUI referencing the relevant issues and/or PRs to be included - see https://github.com/elastic/eui/issues/3386 as an example. This issue is used to keep track of the patch's completion progress and to ensure the desired changes are included in the release. -### Patch process +If it's possible to avoid a backport by performing a full release or patch release instead, we generally encourage doing so. -This provides a walkthrough of the patching & backport release process; examples are taken from the release of v22.3.1 based on https://github.com/elastic/eui/issues/3386 - -* Unless it is unreasonable, begin by performing a full release from the `main` branch. This ensures the changelog is prepared for referencing later by the backport, and pulls in all commits that will be used by the backport. - * Switch to `main` - `git checkout main` - * Run the release script and follow the prompts - `npm run release` -* Identify the target version of EUI to patch; GitHub issue says the new version should be `22.3.1` and I confirmed the patch's base is `22.3.0` - * in the EUI git repo, checkout the release tag the patch is intended for - `git checkout v22.3.0` - * create a new branch from the versioned tag, the name is unimportant but I use the target version without a leading `v` - `git checkout -b 22.3.1` +* Identify the target version of EUI to patch (based on [Kibana's package.json](https://github.com/elastic/kibana/blob/main/package.json)) + * in the EUI git repo, checkout the release tag the patch is intended for - e.g., `git checkout v22.3.0` + * create a new branch from the versioned tag, e.g. `git checkout -b 22.3.0-backport` (the name is not terribly important as the branch should not be pushed up to remote) * Run `yarn` to ensure you have the correct dependencies for that point in time installed * Apply the commit(s) with the desired changes - * GitHub issue references #3369, #3378, #3330, and #3398 + * If the fixes we backporting are #3369, #3378, #3330, and #3398: * We always use squash merges, so each PR has a single commit hash to include * For each PR, find the merge commit * For example, #3369's merge message is @@ -96,41 +90,23 @@ This provides a walkthrough of the patching & backport release process; examples * You may need to re-run yarn in order to commit changes, if the commit modified dependencies * Remember to continue cherry picking with `git cherry-pick --continue` until all commits have been applied * Start the dev server and check that the intended changes have been properly applied, you don't want to repeat this process to patch the patch - `yarn start` -* Once everything looks correct, it's time to release; the `yarn release` script only works when releasing from `main`, so we'll run [a subset of those steps](https://github.com/elastic/eui/blob/main/scripts/release.js) manually - * Run the unit tests again - `npm run test-ci` - * Create the release builds - `npm run build` - * Update the I18n tokens - `npm run update-token-changelog -- patch` - * Update the changelog - `npm run update-changelog-manual --release=patch` - * Use npm to update package.json & package-lock.json version, git commit, and git tag - `npm version patch` - * Push the version commit & tag to upstream - `git push upstream --tags` - * Publish the new version to npm - * Get your npm One Time Password (OTP) from Google Authenticator, Authy, etc - * Publish with your OPT and the new version as the tag - `npm publish --tag=backport --otp=your-one-time-password` -* Let people know the backport is released -* Celebrate profusely +* Once everything looks correct, it's time to [release](https://github.com/elastic/eui/blob/main/scripts/release.js): + * `npm run release-backport` +* Update Kibana's `package.json` to point at the newly-released backport, e.g. `"@elastic/eui": "90.0.0-backport.0"` # Pre-release process Some changes may be particularly difficult to test in local EUI environments alone, or effects may be so wide-ranging that they should be tested against Kibana's expansive set of CI tests beforehand to catch as many regressions as possible. In those scenarios, we should utilize a release candidate (RC) that we can point to other staging or CI environments for easier testing. -The prerelease process is very similar to the backport process above, with different arguments for the `npm version` and `npm publish` steps. +The prerelease process is very similar to the backport process above. - Check out the latest release: - If testing against Kibana specifically, [use the latest EUI version specified in Kibana main's package.json](https://github.com/elastic/kibana/blob/main/package.json#L101) and check out that release, e.g. `git checkout v80.0.0` - Otherwise, simply check out the [latest EUI release](https://github.com/elastic/eui/releases), e.g. `git checkout v83.0.0` - The purpose of this step (instead of releasing from latest `main`) is to reduce as much noise as possible and ensure you're *only* testing the changes you want to test. This is particularly necessary for Kibana CI testing. - Apply the commit(s) with the desired changes, e.g. `git cherry-pick [commit-id]` -- We cannot run the full [release script](https://github.com/elastic/eui/blob/main/scripts/release.js), so run a subset of the script's steps instead: - ```sh - yarn && yarn test-ci && yarn build - ``` - The i18n token and changelog steps are skippable as prereleases are not officially documented releases. -- Run npm version/publish steps specific to a prerelease build: - ```sh - npm version prerelease --preid rc1 # the `1` can be incremented as necessary for additional RC builds - npm publish --tag=prerelease # will require an OTP - ``` - The step for pushing git tags can/should be skipped, as prereleases are not officially documented releases and are primarily for testing purposes. -- Go to https://www.npmjs.com/package/@elastic/eui?activeTab=versions and confirm that your pre-release has been pushed up with the correct version and tag, e.g. `83.1.1-rc1.0` +- Once everything looks correct, it's time to [release](https://github.com/elastic/eui/blob/main/scripts/release.js): + * `npm run release-rc` +- Go to https://www.npmjs.com/package/@elastic/eui?activeTab=versions and confirm that your pre-release has been pushed up with the correct version and tag, e.g. `83.1.1-rc.0` - Update Kibana or CodeSandbox (or whatever other environment you are using to test) to point at that version - QA away!