Automate release process via Changesets (#3915)
Co-authored-by: Francisco <fg@frang.io>
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -euo pipefail
|
||||
|
||||
OUTDIR="$(node -p 'require("./docs/config.js").outputDir')"
|
||||
|
||||
|
||||
33
scripts/release/format-changelog.js
Executable file
33
scripts/release/format-changelog.js
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Adjusts the format of the changelog that changesets generates.
|
||||
// This is run automatically when npm version is run.
|
||||
|
||||
const fs = require('fs');
|
||||
const changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
|
||||
|
||||
// Groups:
|
||||
// - 1: Pull Request Number and URL
|
||||
// - 2: Changeset entry
|
||||
const RELEASE_LINE_REGEX = /^- (\[#.*?\]\(.*?\))?.*?! - (.*)$/gm;
|
||||
|
||||
// Captures vX.Y.Z or vX.Y.Z-rc.W
|
||||
const VERSION_TITLE_REGEX = /^## (\d+\.\d+\.\d+(-rc\.\d+)?)$/gm;
|
||||
|
||||
const isPrerelease = process.env.PRERELEASE === 'true';
|
||||
|
||||
const formatted = changelog
|
||||
// Remove titles
|
||||
.replace(/^### Major Changes\n\n/gm, '')
|
||||
.replace(/^### Minor Changes\n\n/gm, '')
|
||||
.replace(/^### Patch Changes\n\n/gm, '')
|
||||
// Remove extra whitespace between items
|
||||
.replace(/^(- \[.*\n)\n(?=-)/gm, '$1')
|
||||
// Format each release line
|
||||
.replace(RELEASE_LINE_REGEX, (_, pr, entry) => (pr ? `- ${entry} (${pr})` : `- ${entry}`))
|
||||
// Add date to new version
|
||||
.replace(VERSION_TITLE_REGEX, `\n## $1 (${new Date().toISOString().split('T')[0]})`)
|
||||
// Conditionally allow vX.Y.Z.rc-.W sections only in prerelease
|
||||
.replace(/^## \d\.\d\.\d-rc\S+[^]+?(?=^#)/gm, section => (isPrerelease ? section : ''));
|
||||
|
||||
fs.writeFileSync('CHANGELOG.md', formatted);
|
||||
@ -1,145 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Exit script as soon as a command fails.
|
||||
set -o errexit
|
||||
|
||||
# Default the prerelease version suffix to rc
|
||||
: ${PRERELEASE_SUFFIX:=rc}
|
||||
|
||||
log() {
|
||||
# Print to stderr to prevent this from being 'returned'
|
||||
echo "$@" > /dev/stderr
|
||||
}
|
||||
|
||||
current_version() {
|
||||
echo "v$(node --print --eval "require('./package.json').version")"
|
||||
}
|
||||
|
||||
current_release_branch() {
|
||||
v="$(current_version)" # 3.3.0-rc.0
|
||||
vf="${v%-"$PRERELEASE_SUFFIX".*}" # 3.3.0
|
||||
r="${vf%.*}" # 3.3
|
||||
echo "release-$r"
|
||||
}
|
||||
|
||||
assert_current_branch() {
|
||||
current_branch="$(git symbolic-ref --short HEAD)"
|
||||
expected_branch="$1"
|
||||
if [[ "$current_branch" != "$expected_branch" ]]; then
|
||||
log "Current branch '$current_branch' is not '$expected_branch'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
push_release_branch_and_tag() {
|
||||
git push upstream "$(current_release_branch)" "$(current_version)"
|
||||
}
|
||||
|
||||
publish() {
|
||||
dist_tag="$1"
|
||||
|
||||
log "Publishing @openzeppelin/contracts on npm"
|
||||
cd contracts
|
||||
npm publish --tag "$dist_tag" --otp "$(prompt_otp)"
|
||||
cd ..
|
||||
|
||||
if [[ "$dist_tag" == "latest" ]]; then
|
||||
npm dist-tag rm --otp "$(prompt_otp)" @openzeppelin/contracts next
|
||||
fi
|
||||
}
|
||||
|
||||
push_and_publish() {
|
||||
dist_tag="$1"
|
||||
|
||||
log "Pushing release branch and tags to upstream"
|
||||
push_release_branch_and_tag
|
||||
|
||||
publish "$dist_tag"
|
||||
}
|
||||
|
||||
prompt_otp() {
|
||||
log -n "Enter npm 2FA token: "
|
||||
read -r otp
|
||||
echo "$otp"
|
||||
}
|
||||
|
||||
environment_check() {
|
||||
if ! git remote get-url upstream &> /dev/null; then
|
||||
log "No 'upstream' remote found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if npm whoami &> /dev/null; then
|
||||
log "Will publish as '$(npm whoami)'"
|
||||
else
|
||||
log "Not logged in into npm, run 'npm login' first"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
environment_check
|
||||
|
||||
if [[ "$*" == "push" ]]; then
|
||||
push_and_publish next
|
||||
|
||||
elif [[ "$*" == "start minor" ]]; then
|
||||
log "Creating new minor pre-release"
|
||||
|
||||
assert_current_branch master
|
||||
|
||||
# Create temporary release branch
|
||||
git checkout -b release-temp
|
||||
|
||||
# This bumps minor and adds prerelease suffix, commits the changes, and tags the commit
|
||||
npm version preminor --preid="$PRERELEASE_SUFFIX"
|
||||
|
||||
# Rename the release branch
|
||||
git branch --move "$(current_release_branch)"
|
||||
|
||||
push_and_publish next
|
||||
|
||||
elif [[ "$*" == "start major" ]]; then
|
||||
log "Creating new major pre-release"
|
||||
|
||||
assert_current_branch master
|
||||
|
||||
# Create temporary release branch
|
||||
git checkout -b release-temp
|
||||
|
||||
# This bumps major and adds prerelease suffix, commits the changes, and tags the commit
|
||||
npm version premajor --preid="$PRERELEASE_SUFFIX"
|
||||
|
||||
# Rename the release branch
|
||||
git branch --move "$(current_release_branch)"
|
||||
|
||||
push_and_publish next
|
||||
|
||||
elif [[ "$*" == "rc" ]]; then
|
||||
log "Bumping pre-release"
|
||||
|
||||
assert_current_branch "$(current_release_branch)"
|
||||
|
||||
# Bumps prerelease number, commits and tags
|
||||
npm version prerelease
|
||||
|
||||
push_and_publish next
|
||||
|
||||
elif [[ "$*" == "final" ]]; then
|
||||
# Update changelog release date, remove prerelease suffix, tag, push to git, publish in npm, remove next dist-tag
|
||||
log "Creating final release"
|
||||
|
||||
assert_current_branch "$(current_release_branch)"
|
||||
|
||||
# This will remove the prerelease suffix from the version
|
||||
npm version patch
|
||||
|
||||
push_release_branch_and_tag
|
||||
|
||||
push_and_publish latest
|
||||
|
||||
log "Remember to merge the release branch into master and push upstream"
|
||||
|
||||
else
|
||||
log "Unknown command: '$*'"
|
||||
exit 1
|
||||
fi
|
||||
@ -4,13 +4,12 @@
|
||||
// This is run automatically when npm version is run.
|
||||
|
||||
const fs = require('fs');
|
||||
const cp = require('child_process');
|
||||
|
||||
setVersion('contracts/package.json');
|
||||
setVersion('package.json', 'contracts/package.json');
|
||||
|
||||
function setVersion(file) {
|
||||
const json = JSON.parse(fs.readFileSync(file));
|
||||
json.version = process.env.npm_package_version;
|
||||
fs.writeFileSync(file, JSON.stringify(json, null, 2) + '\n');
|
||||
cp.execFileSync('git', ['add', file]);
|
||||
function setVersion(from, to) {
|
||||
const fromJson = JSON.parse(fs.readFileSync(from));
|
||||
const toJson = JSON.parse(fs.readFileSync(to));
|
||||
toJson.version = fromJson.version;
|
||||
fs.writeFileSync(to, JSON.stringify(toJson, null, 2) + '\n');
|
||||
}
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Sets the release date of the current release in the changelog.
|
||||
// This is run automatically when npm version is run.
|
||||
|
||||
const fs = require('fs');
|
||||
const cp = require('child_process');
|
||||
|
||||
const suffix = process.env.PRERELEASE_SUFFIX || 'rc';
|
||||
|
||||
const changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
|
||||
|
||||
// The changelog entry to be updated looks like this:
|
||||
// ## Unreleased
|
||||
// We need to add the version and release date in a YYYY-MM-DD format, so that it looks like this:
|
||||
// ## 2.5.3 (2019-04-25)
|
||||
|
||||
const pkg = require('../../package.json');
|
||||
const version = pkg.version.replace(new RegExp('-' + suffix + '\\..*'), '');
|
||||
|
||||
const header = new RegExp(`^## (Unreleased|${version})$`, 'm');
|
||||
|
||||
if (!header.test(changelog)) {
|
||||
console.error('Missing changelog entry');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const newHeader =
|
||||
pkg.version.indexOf(suffix) === -1 ? `## ${version} (${new Date().toISOString().split('T')[0]})` : `## ${version}`;
|
||||
|
||||
fs.writeFileSync('CHANGELOG.md', changelog.replace(header, newHeader));
|
||||
|
||||
cp.execSync('git add CHANGELOG.md', { stdio: 'inherit' });
|
||||
@ -32,5 +32,3 @@ for (const file of files) {
|
||||
);
|
||||
fs.writeFileSync(file, updated);
|
||||
}
|
||||
|
||||
run('git', 'add', '--update', 'contracts');
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -euo pipefail
|
||||
|
||||
scripts/release/update-changelog-release-date.js
|
||||
changeset version
|
||||
|
||||
scripts/release/format-changelog.js
|
||||
scripts/release/synchronize-versions.js
|
||||
scripts/release/update-comment.js
|
||||
|
||||
|
||||
8
scripts/release/workflow/exit-prerelease.sh
Normal file
8
scripts/release/workflow/exit-prerelease.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
npx changeset pre exit rc
|
||||
git add .
|
||||
git commit -m "Exit release candidate"
|
||||
git push origin
|
||||
47
scripts/release/workflow/github-release.js
Normal file
47
scripts/release/workflow/github-release.js
Normal file
@ -0,0 +1,47 @@
|
||||
const { readFileSync } = require('fs');
|
||||
const { join } = require('path');
|
||||
const { version } = require(join(__dirname, '../../../package.json'));
|
||||
|
||||
module.exports = async ({ github, context }) => {
|
||||
const changelog = readFileSync('CHANGELOG.md', 'utf8');
|
||||
|
||||
await github.rest.repos.createRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag_name: `v${version}`,
|
||||
body: extractSection(changelog, version),
|
||||
prerelease: process.env.PRERELEASE === 'true',
|
||||
});
|
||||
};
|
||||
|
||||
// From https://github.com/frangio/extract-changelog/blob/master/src/utils/word-regexp.ts
|
||||
function makeWordRegExp(word) {
|
||||
const start = word.length > 0 && /\b/.test(word[0]) ? '\\b' : '';
|
||||
const end = word.length > 0 && /\b/.test(word[word.length - 1]) ? '\\b' : '';
|
||||
return new RegExp(start + [...word].map(c => (/[a-z0-9]/i.test(c) ? c : '\\' + c)).join('') + end);
|
||||
}
|
||||
|
||||
// From https://github.com/frangio/extract-changelog/blob/master/src/core.ts
|
||||
function extractSection(document, wantedHeading) {
|
||||
// ATX Headings as defined in GitHub Flavored Markdown (https://github.github.com/gfm/#atx-headings)
|
||||
const heading = /^ {0,3}(?<lead>#{1,6})(?: [ \t\v\f]*(?<text>.*?)[ \t\v\f]*)?(?:[\n\r]+|$)/gm;
|
||||
|
||||
const wantedHeadingRe = makeWordRegExp(wantedHeading);
|
||||
|
||||
let start, end;
|
||||
|
||||
for (const m of document.matchAll(heading)) {
|
||||
if (!start) {
|
||||
if (m.groups.text.search(wantedHeadingRe) === 0) {
|
||||
start = m;
|
||||
}
|
||||
} else if (m.groups.lead.length <= start.groups.lead.length) {
|
||||
end = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (start) {
|
||||
return document.slice(start.index + start[0].length, end?.index);
|
||||
}
|
||||
}
|
||||
25
scripts/release/workflow/pack.sh
Normal file
25
scripts/release/workflow/pack.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
dist_tag() {
|
||||
PACKAGE_JSON_NAME="$(jq -r .name ./package.json)"
|
||||
LATEST_NPM_VERSION="$(npm info "$PACKAGE_JSON_NAME" version)"
|
||||
PACKAGE_JSON_VERSION="$(jq -r .version ./package.json)"
|
||||
|
||||
if [ "$PRERELEASE" = "true" ]; then
|
||||
echo "next"
|
||||
elif npx semver -r ">$LATEST_NPM_VERSION" "$PACKAGE_JSON_VERSION" > /dev/null; then
|
||||
echo "latest"
|
||||
else
|
||||
# This is a patch for an older version
|
||||
# npm can't publish without a tag
|
||||
echo "tmp"
|
||||
fi
|
||||
}
|
||||
|
||||
cd contracts
|
||||
TARBALL="$(npm pack | tee /dev/stderr | tail -1)"
|
||||
echo "tarball=$(pwd)/$TARBALL" >> $GITHUB_OUTPUT
|
||||
echo "tag=$(dist_tag)" >> $GITHUB_OUTPUT
|
||||
cd ..
|
||||
24
scripts/release/workflow/prepare-release-merge.sh
Normal file
24
scripts/release/workflow/prepare-release-merge.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Define merge branch name
|
||||
MERGE_BRANCH=merge/$GITHUB_REF_NAME
|
||||
|
||||
# Create the branch and force to start from ref
|
||||
git checkout -B "$MERGE_BRANCH" "$GITHUB_REF_NAME"
|
||||
|
||||
# Get deleted changesets in this branch that might conflict with master
|
||||
readarray -t DELETED_CHANGESETS < <(git diff origin/master --name-only -- '.changeset/*.md')
|
||||
|
||||
# Merge master, which will take those files cherry-picked. Auto-resolve conflicts favoring master.
|
||||
git merge origin/master -m "Merge master to $GITHUB_REF_NAME" -X theirs
|
||||
|
||||
# Remove the originally deleted changesets to correctly sync with master
|
||||
rm -f "${DELETED_CHANGESETS[@]}"
|
||||
|
||||
git add .changeset/
|
||||
|
||||
# Allow empty here since there may be no changes if `rm -f` failed for all changesets
|
||||
git commit --allow-empty -m "Sync changesets with master"
|
||||
git push -f origin "$MERGE_BRANCH"
|
||||
15
scripts/release/workflow/publish.sh
Normal file
15
scripts/release/workflow/publish.sh
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Intentionally escape $ to avoid interpolation and writing the token to disk
|
||||
echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc
|
||||
|
||||
# Actual publish
|
||||
npm publish "$TARBALL" --tag "$TAG"
|
||||
|
||||
if [ "$TAG" = "tmp" ]; then
|
||||
# Remove tmp tag
|
||||
PACKAGE_JSON_NAME="$(tar xfO "$TARBALL" package/package.json | jq -r .name)"
|
||||
npm dist-tag rm "$PACKAGE_JSON_NAME" "$TAG"
|
||||
fi
|
||||
7
scripts/release/workflow/rerun.js
Normal file
7
scripts/release/workflow/rerun.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = ({ github, context }) =>
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'release-cycle.yml',
|
||||
ref: process.env.REF || process.env.GITHUB_REF_NAME,
|
||||
});
|
||||
17
scripts/release/workflow/set-changesets-pr-title.js
Normal file
17
scripts/release/workflow/set-changesets-pr-title.js
Normal file
@ -0,0 +1,17 @@
|
||||
const { coerce, inc, rsort } = require('semver');
|
||||
const { join } = require('path');
|
||||
const { version } = require(join(__dirname, '../../../package.json'));
|
||||
|
||||
module.exports = async ({ core }) => {
|
||||
// Variables not in the context
|
||||
const refName = process.env.GITHUB_REF_NAME;
|
||||
|
||||
// Compare package.json version's next patch vs. first version patch
|
||||
// A recently opened branch will give the next patch for the previous minor
|
||||
// So, we get the max against the patch 0 of the release branch's version
|
||||
const branchPatch0 = coerce(refName.replace('release-v', '')).version;
|
||||
const packageJsonNextPatch = inc(version, 'patch');
|
||||
const [nextVersion] = rsort([branchPatch0, packageJsonNextPatch], false);
|
||||
|
||||
core.exportVariable('TITLE', `Release v${nextVersion}`);
|
||||
};
|
||||
32
scripts/release/workflow/start.sh
Normal file
32
scripts/release/workflow/start.sh
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Set changeset status location
|
||||
# This is needed because `changeset status --output` only works with relative routes
|
||||
CHANGESETS_STATUS_JSON="$(realpath --relative-to=. "$RUNNER_TEMP/status.json")"
|
||||
|
||||
# Save changeset status to temp file
|
||||
npx changeset status --output="$CHANGESETS_STATUS_JSON"
|
||||
|
||||
# Defensive assertion. SHOULD NOT BE REACHED
|
||||
if [ "$(jq '.releases | length' "$CHANGESETS_STATUS_JSON")" != 1 ]; then
|
||||
echo "::error file=$CHANGESETS_STATUS_JSON::The status doesn't contain only 1 release"
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
# Create branch
|
||||
BRANCH_SUFFIX="$(jq -r '.releases[0].newVersion | gsub("\\.\\d+$"; "")' $CHANGESETS_STATUS_JSON)"
|
||||
RELEASE_BRANCH="release-v$BRANCH_SUFFIX"
|
||||
git checkout -b "$RELEASE_BRANCH"
|
||||
|
||||
# Output branch
|
||||
echo "branch=$RELEASE_BRANCH" >> $GITHUB_OUTPUT
|
||||
|
||||
# Enter in prerelease state
|
||||
npx changeset pre enter rc
|
||||
git add .
|
||||
git commit -m "Start release candidate"
|
||||
|
||||
# Push branch
|
||||
git push origin "$RELEASE_BRANCH"
|
||||
104
scripts/release/workflow/state.js
Normal file
104
scripts/release/workflow/state.js
Normal file
@ -0,0 +1,104 @@
|
||||
const { readPreState } = require('@changesets/pre');
|
||||
const { default: readChangesets } = require('@changesets/read');
|
||||
const { join } = require('path');
|
||||
const { version } = require(join(__dirname, '../../../package.json'));
|
||||
|
||||
module.exports = async ({ github, context, core }) => {
|
||||
const state = await getState({ github, context, core });
|
||||
|
||||
function setOutput(key, value) {
|
||||
core.info(`State ${key} = ${value}`);
|
||||
core.setOutput(key, value);
|
||||
}
|
||||
|
||||
// Jobs to trigger
|
||||
setOutput('start', shouldRunStart(state));
|
||||
setOutput('promote', shouldRunPromote(state));
|
||||
setOutput('changesets', shouldRunChangesets(state));
|
||||
setOutput('publish', shouldRunPublish(state));
|
||||
setOutput('merge', shouldRunMerge(state));
|
||||
|
||||
// Global Variables
|
||||
setOutput('is_prerelease', state.prerelease);
|
||||
};
|
||||
|
||||
function shouldRunStart({ isMaster, isWorkflowDispatch, botRun }) {
|
||||
return isMaster && isWorkflowDispatch && !botRun;
|
||||
}
|
||||
|
||||
function shouldRunPromote({ isReleaseBranch, isWorkflowDispatch, botRun }) {
|
||||
return isReleaseBranch && isWorkflowDispatch && !botRun;
|
||||
}
|
||||
|
||||
function shouldRunChangesets({ isReleaseBranch, isPush, isWorkflowDispatch, botRun }) {
|
||||
return (isReleaseBranch && isPush) || (isReleaseBranch && isWorkflowDispatch && botRun);
|
||||
}
|
||||
|
||||
function shouldRunPublish({ isReleaseBranch, isPush, hasPendingChangesets }) {
|
||||
return isReleaseBranch && isPush && !hasPendingChangesets;
|
||||
}
|
||||
|
||||
function shouldRunMerge({
|
||||
isReleaseBranch,
|
||||
isPush,
|
||||
prerelease,
|
||||
isCurrentFinalVersion,
|
||||
hasPendingChangesets,
|
||||
prBackExists,
|
||||
}) {
|
||||
return isReleaseBranch && isPush && !prerelease && isCurrentFinalVersion && !hasPendingChangesets && prBackExists;
|
||||
}
|
||||
|
||||
async function getState({ github, context, core }) {
|
||||
// Variables not in the context
|
||||
const refName = process.env.GITHUB_REF_NAME;
|
||||
const botRun = process.env.TRIGGERING_ACTOR === 'github-actions[bot]';
|
||||
|
||||
const { changesets, preState } = await readChangesetState();
|
||||
|
||||
// Static vars
|
||||
const state = {
|
||||
refName,
|
||||
hasPendingChangesets: changesets.length > 0,
|
||||
prerelease: preState?.mode === 'pre',
|
||||
isMaster: refName === 'master',
|
||||
isReleaseBranch: refName.startsWith('release-v'),
|
||||
isWorkflowDispatch: context.eventName === 'workflow_dispatch',
|
||||
isPush: context.eventName === 'push',
|
||||
isCurrentFinalVersion: !version.includes('-rc.'),
|
||||
botRun,
|
||||
};
|
||||
|
||||
// Async vars
|
||||
const { data: prs } = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
head: `${context.repo.owner}:merge/${state.refName}`,
|
||||
base: 'master',
|
||||
state: 'open',
|
||||
});
|
||||
|
||||
state.prBackExists = prs.length === 0;
|
||||
|
||||
// Log every state value in debug mode
|
||||
if (core.isDebug()) for (const [key, value] of Object.entries(state)) core.debug(`${key}: ${value}`);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// From https://github.com/changesets/action/blob/v1.4.1/src/readChangesetState.ts
|
||||
async function readChangesetState(cwd = process.cwd()) {
|
||||
const preState = await readPreState(cwd);
|
||||
const isInPreMode = preState !== undefined && preState.mode === 'pre';
|
||||
|
||||
let changesets = await readChangesets(cwd);
|
||||
|
||||
if (isInPreMode) {
|
||||
changesets = changesets.filter(x => !preState.changesets.includes(x.id));
|
||||
}
|
||||
|
||||
return {
|
||||
preState: isInPreMode ? preState : undefined,
|
||||
changesets,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user