diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bc8dbe..9f5c0e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node: [16.x, 15.x, 14.x, 13.x, 12.x, 10.x] + node: [22.x, 20.x, 18.x, 16.x, 15.x, 14.x, 13.x, 12.x, 10.x] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index e572109..d2696dd 100644 --- a/README.md +++ b/README.md @@ -66,12 +66,51 @@ When you run the release tool for a regular release, the following steps take pl 1. Calculates the next release version based on the [commit message format](http://eslint.org/docs/developer-guide/contributing/pull-requests#step-2-make-your-changes) of the changes since the last release 1. Updates `CHANGELOG.md` and commits the changes 1. Runs `npm version` to update the version -1. Pushes the repository to origin/master with tags (only outside of CI release) +1. Pushes the current branch to origin, with tags +1. Creates GitHub release marked as Latest 1. Converts all line endings to Unix style 1. Publishes the package to npm 1. Reverts any file changes -When you do a prerelease, the same steps are taken except that package is published to npm under the `next` tag instead of `latest`. +When you do a prerelease, the same steps are taken except that package is published to npm under the `next` tag instead of `latest`, and the GitHub release is marked as Pre-release. + +## API Usage + +This package exports two functions: + +* `generateRelease(prereleaseId, packageTag)` - This corresponds to the CLI command `eslint-generate-release` when `prereleaseId` is `undefined`, and the CLI command `eslint-generate-prerelease prereleaseId` when `prereleaseId` is a string value. +* `publishRelease()` - This corresponds to the CLI command `eslint-publish-release`. + +`packageTag` is used as the `--tag` value in the `npm publish` command. It's also used to determine whether a regular release will be marked as Latest on GitHub: it will be marked as Latest only if `packageTag` is `"latest"`. This parameter is optional and defaults to `"latest"` when `prereleaseId` is `undefined`, `"next"` otherwise. + +### Examples + +Publish a regular latest release: + +```js +const ReleaseOps = require("eslint-release"); + +ReleaseOps.generateRelease(); +ReleaseOps.publishRelease(); +``` + +Publish a regular release with `maintenance` tag: + +```js +const ReleaseOps = require("eslint-release"); + +ReleaseOps.generateRelease(undefined, "maintenance"); +ReleaseOps.publishRelease(); +``` + +Publish an `alpha` prerelease: + +```js +const ReleaseOps = require("eslint-release"); + +ReleaseOps.generateRelease("alpha"); +ReleaseOps.publishRelease(); +``` ## Contributing diff --git a/lib/release-ops.js b/lib/release-ops.js index 3c1acfa..247cd62 100644 --- a/lib/release-ops.js +++ b/lib/release-ops.js @@ -139,7 +139,9 @@ function getPrereleaseVersion(currentVersion, prereleaseId, releaseType) { * @private */ function getVersionTags() { - const tags = ShellOps.execSilent("git tag").trim().split("\n"); + + // Using `--merged` to only list tags whose commits are reachable from HEAD + const tags = ShellOps.execSilent("git tag --merged").trim().split("\n"); return tags.reduce((list, tag) => { if (semver.valid(tag)) { @@ -347,10 +349,12 @@ function writeChangelog(releaseInfo) { const now = new Date(), today = dateformat(now, "mmmm d, yyyy"); + releaseInfo.markdownChangelog = `v${releaseInfo.version} - ${today}\n\n${releaseInfo.rawChangelog}\n\n`; + // output header and changelog fs.writeFileSync( "CHANGELOG.tmp", - `v${releaseInfo.version} - ${today}\n\n${releaseInfo.rawChangelog}\n\n` + releaseInfo.markdownChangelog ); // ensure there's a CHANGELOG.md file @@ -370,9 +374,10 @@ function writeChangelog(releaseInfo) { * Creates a release version tag and pushes to origin and npm. * @param {string} [prereleaseId] The prerelease ID (alpha, beta, rc, etc.). * Only include when doing a prerelease. + * @param {string} [packageTag] Tag added to the package submitted to the npm registry. * @returns {Object} The information about the release. */ -function generateRelease(prereleaseId) { +function generateRelease(prereleaseId, packageTag = prereleaseId ? "next" : "latest") { validateSetup(); @@ -382,7 +387,10 @@ function generateRelease(prereleaseId) { console.log("Calculating changes for release"); const releaseInfo = calculateReleaseInfo(prereleaseId); + releaseInfo.packageTag = packageTag; + console.log("Release is %s", releaseInfo.version); + console.log("Package tag is %s", releaseInfo.packageTag); console.log("Generating changelog"); writeChangelog(releaseInfo); @@ -444,7 +452,8 @@ function publishReleaseToGitHub(releaseInfo) { return repo.createRelease({ tag_name: tag, // eslint-disable-line camelcase body: generateReleaseBody(releaseInfo.changelog), - prerelease: !!semver.prerelease(releaseInfo.version) + prerelease: !!semver.prerelease(releaseInfo.version), + make_latest: String(releaseInfo.packageTag === "latest") // eslint-disable-line camelcase }).then(() => { console.log("Posted release notes to GitHub"); }).catch(ex => { @@ -480,11 +489,7 @@ function publishRelease() { // if there's a prerelease ID, publish under "next" tag console.log("Publishing to npm"); - let command = "npm publish"; - - if (semver.prerelease(releaseInfo.version)) { - command += " --tag next"; - } + let command = `npm publish --tag ${releaseInfo.packageTag}`; if (process.env.NPM_OTP && /^\d+$/.test(process.env.NPM_OTP)) { command += ` --otp=${process.env.NPM_OTP}`;