1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

Replace auto-changelog script (#10993)

The `auto-changelog` script has been replaced with the package
`@metamask/auto-changelog`. This package includes a script that has
an `update` command that is roughly equivalent to the old
`auto-changelog.js` script, except better. The script also has a
`validate` command.

The `repository` field was added to `package.json` because it's
utilized by the `auto-changelog` script, and this was easier than
specifying the repository URL with a CLI argument.
This commit is contained in:
Mark Stacey 2021-05-06 11:50:19 -02:30 committed by GitHub
parent 0e17ad3450
commit 20b0346d8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 34 additions and 775 deletions

View File

@ -1,67 +0,0 @@
#!/usr/bin/env node
const fs = require('fs').promises;
const path = require('path');
const { version } = require('../app/manifest/_base.json');
const { updateChangelog } = require('./lib/changelog/updateChangelog');
const { unreleased } = require('./lib/changelog/constants');
const REPO_URL = 'https://github.com/MetaMask/metamask-extension';
const command = 'yarn update-changelog';
const helpText = `Usage: ${command} [--rc] [-h|--help]
Update CHANGELOG.md with any changes made since the most recent release.
Options:
--rc Add new changes to the current release header, rather than to the
'${unreleased}' section.
-h, --help Display this help and exit.
New commits will be added to the "${unreleased}" section (or to the section for the
current release if the '--rc' flag is used) in reverse chronological order. Any
commits for PRs that are represented already in the changelog will be ignored.
If the '--rc' flag is used and the section for the current release does not yet
exist, it will be created.
`;
async function main() {
const args = process.argv.slice(2);
let isReleaseCandidate = false;
for (const arg of args) {
if (arg === '--rc') {
isReleaseCandidate = true;
} else if (['--help', '-h'].includes(arg)) {
console.log(helpText);
process.exit(0);
} else {
console.error(
`Unrecognized argument: ${arg}\nTry '${command} --help' for more information.\n`,
);
process.exit(1);
}
}
const changelogFilename = path.resolve(__dirname, '..', 'CHANGELOG.md');
const changelogContent = await fs.readFile(changelogFilename, {
encoding: 'utf8',
});
const newChangelogContent = await updateChangelog({
changelogContent,
currentVersion: version,
repoUrl: REPO_URL,
isReleaseCandidate,
});
await fs.writeFile(changelogFilename, newChangelogContent);
console.log('CHANGELOG updated');
}
main().catch((error) => {
console.error(error);
process.exit(1);
});

View File

@ -1,305 +0,0 @@
const semver = require('semver');
const { orderedChangeCategories, unreleased } = require('./constants');
const changelogTitle = '# Changelog';
const changelogDescription = `All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).`;
// Stringification helpers
function stringifyCategory(category, changes) {
const categoryHeader = `### ${category}`;
if (changes.length === 0) {
return categoryHeader;
}
const changeDescriptions = changes
.map((description) => `- ${description}`)
.join('\n');
return `${categoryHeader}\n${changeDescriptions}`;
}
function stringifyRelease(version, categories, { date, status } = {}) {
const releaseHeader = `## [${version}]${date ? ` - ${date}` : ''}${
status ? ` [${status}]` : ''
}`;
const categorizedChanges = orderedChangeCategories
.filter((category) => categories[category])
.map((category) => {
const changes = categories[category];
return stringifyCategory(category, changes);
})
.join('\n\n');
if (categorizedChanges === '') {
return releaseHeader;
}
return `${releaseHeader}\n${categorizedChanges}`;
}
function stringifyReleases(releases, changes) {
const stringifiedUnreleased = stringifyRelease(
unreleased,
changes[unreleased],
);
const stringifiedReleases = releases.map(({ version, date, status }) => {
const categories = changes[version];
return stringifyRelease(version, categories, { date, status });
});
return [stringifiedUnreleased, ...stringifiedReleases].join('\n\n');
}
function withTrailingSlash(url) {
return url.endsWith('/') ? url : `${url}/`;
}
function getCompareUrl(repoUrl, firstRef, secondRef) {
return `${withTrailingSlash(repoUrl)}compare/${firstRef}...${secondRef}`;
}
function getTagUrl(repoUrl, tag) {
return `${withTrailingSlash(repoUrl)}releases/tag/${tag}`;
}
function stringifyLinkReferenceDefinitions(repoUrl, releases) {
const orderedReleases = releases
.map(({ version }) => version)
.sort((a, b) => semver.gt(a, b));
// The "Unreleased" section represents all changes made since the *highest*
// release, not the most recent release. This is to accomodate patch releases
// of older versions that don't represent the latest set of changes.
//
// For example, if a library has a v2.0.0 but the v1.0.0 release needed a
// security update, the v1.0.1 release would then be the most recent, but the
// range of unreleased changes would remain `v2.0.0...HEAD`.
const unreleasedLinkReferenceDefinition = `[${unreleased}]: ${getCompareUrl(
repoUrl,
`v${orderedReleases[0]}`,
'HEAD',
)}`;
// The "previous" release that should be used for comparison is not always
// the most recent release chronologically. The _highest_ version that is
// lower than the current release is used as the previous release, so that
// patch releases on older releases can be accomodated.
const releaseLinkReferenceDefinitions = releases
.map(({ version }) => {
if (version === orderedReleases[orderedReleases.length - 1]) {
return `[${version}]: ${getTagUrl(repoUrl, `v${version}`)}`;
}
const versionIndex = orderedReleases.indexOf(version);
const previousVersion = orderedReleases
.slice(versionIndex)
.find((releaseVersion) => {
return semver.gt(version, releaseVersion);
});
return `[${version}]: ${getCompareUrl(
repoUrl,
`v${previousVersion}`,
`v${version}`,
)}`;
})
.join('\n');
return `${unreleasedLinkReferenceDefinition}\n${releaseLinkReferenceDefinitions}${
releases.length > 0 ? '\n' : ''
}`;
}
/**
* @typedef {import('./constants.js').Unreleased} Unreleased
* @typedef {import('./constants.js').ChangeCategories ChangeCategories}
*/
/**
* @typedef {import('./constants.js').Version} Version
*/
/**
* Release metadata.
* @typedef {Object} ReleaseMetadata
* @property {string} date - An ISO-8601 formatted date, representing the
* release date.
* @property {string} status -The status of the release (e.g. 'WITHDRAWN', 'DEPRECATED')
* @property {Version} version - The version of the current release.
*/
/**
* Category changes. A list of changes in a single category.
* @typedef {Array<string>} CategoryChanges
*/
/**
* Release changes, organized by category
* @typedef {Record<keyof ChangeCategories, CategoryChanges>} ReleaseChanges
*/
/**
* Changelog changes, organized by release and by category.
* @typedef {Record<Version|Unreleased, ReleaseChanges>} ChangelogChanges
*/
/**
* A changelog that complies with the ["keep a changelog" v1.1.0 guidelines]{@link https://keepachangelog.com/en/1.0.0/}.
*
* This changelog starts out completely empty, and allows new releases and
* changes to be added such that the changelog remains compliant at all times.
* This can be used to help validate the contents of a changelog, normalize
* formatting, update a changelog, or build one from scratch.
*/
class Changelog {
/**
* Construct an empty changelog
*
* @param {Object} options
* @param {string} options.repoUrl - The GitHub repository URL for the current project
*/
constructor({ repoUrl }) {
this._releases = [];
this._changes = { [unreleased]: {} };
this._repoUrl = repoUrl;
}
/**
* Add a release to the changelog
*
* @param {Object} options
* @param {boolean} [options.addToStart] - Determines whether the release is
* added to the top or bottom of the changelog. This defaults to 'true'
* because new releases should be added to the top of the changelog. This
* should be set to 'false' when parsing a changelog top-to-bottom.
* @param {string} [options.date] - An ISO-8601 formatted date, representing the
* release date.
* @param {string} [options.status] - The status of the release (e.g.
* 'WITHDRAWN', 'DEPRECATED')
* @param {Version} options.version - The version of the current release,
* which should be a [semver]{@link https://semver.org/spec/v2.0.0.html}-
* compatible version.
*/
addRelease({ addToStart = true, date, status, version }) {
if (!version) {
throw new Error('Version required');
} else if (semver.valid(version) === null) {
throw new Error(`Not a valid semver version: '${version}'`);
} else if (this._changes[version]) {
throw new Error(`Release already exists: '${version}'`);
}
this._changes[version] = {};
const newRelease = { version, date, status };
if (addToStart) {
this._releases.unshift(newRelease);
} else {
this._releases.push(newRelease);
}
}
/**
* Add a change to the changelog
*
* @param {Object} options
* @param {boolean} [options.addToStart] - Determines whether the change is
* added to the top or bottom of the list of changes in this category. This
* defaults to 'true' because changes should be in reverse-chronological
* order. This should be set to 'false' when parsing a changelog top-to-
* bottom.
* @param {string} options.category - The category of the change.
* @param {string} options.description - The description of the change.
* @param {Version} [options.version] - The version this change was released
* in. If this is not given, the change is assumed to be unreleased.
*/
addChange({ addToStart = true, category, description, version }) {
if (!category) {
throw new Error('Category required');
} else if (!orderedChangeCategories.includes(category)) {
throw new Error(`Unrecognized category: '${category}'`);
} else if (!description) {
throw new Error('Description required');
} else if (version !== undefined && !this._changes[version]) {
throw new Error(`Specified release version does not exist: '${version}'`);
}
const release = version
? this._changes[version]
: this._changes[unreleased];
if (!release[category]) {
release[category] = [];
}
if (addToStart) {
release[category].unshift(description);
} else {
release[category].push(description);
}
}
/**
* Migrate all unreleased changes to a release section.
*
* Changes are migrated in their existing categories, and placed above any
* pre-existing changes in that category.
*
* @param {Version} version - The release version to migrate unreleased
* changes to.
*/
migrateUnreleasedChangesToRelease(version) {
const releaseChanges = this._changes[version];
if (!releaseChanges) {
throw new Error(`Specified release version does not exist: '${version}'`);
}
const unreleasedChanges = this._changes[unreleased];
for (const category of Object.keys(unreleasedChanges)) {
if (releaseChanges[category]) {
releaseChanges[category] = [
...unreleasedChanges[category],
...releaseChanges[category],
];
} else {
releaseChanges[category] = unreleasedChanges[category];
}
}
this._changes[unreleased] = {};
}
/**
* Gets the metadata for all releases.
* @returns {Array<ReleaseMetadata>} The metadata for each release.
*/
getReleases() {
return this._releases;
}
/**
* Gets the changes in the given release, organized by category.
* @param {Version} version - The version of the release being retrieved.
* @returns {ReleaseChanges} The changes included in the given released.
*/
getReleaseChanges(version) {
return this._changes[version];
}
/**
* Gets all changes that have not yet been released
* @returns {ReleaseChanges} The changes that have not yet been released.
*/
getUnreleasedChanges() {
return this._changes[unreleased];
}
/**
* The stringified changelog, formatted according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* @returns {string} The stringified changelog.
*/
toString() {
return `${changelogTitle}
${changelogDescription}
${stringifyReleases(this._releases, this._changes)}
${stringifyLinkReferenceDefinitions(this._repoUrl, this._releases)}`;
}
}
module.exports = Changelog;

View File

@ -1,68 +0,0 @@
/**
* Version string
* @typedef {string} Version - A [SemVer]{@link https://semver.org/spec/v2.0.0.html}-
* compatible version string.
*/
/**
* Change categories.
*
* Most of these categories are from [Keep a Changelog]{@link https://keepachangelog.com/en/1.0.0/}.
* The "Uncategorized" category was added because we have many changes from
* older releases that would be difficult to categorize.
*
* @typedef {Record<string, string>} ChangeCategories
* @property {'Added'} Added - for new features.
* @property {'Changed'} Changed - for changes in existing functionality.
* @property {'Deprecated'} Deprecated - for soon-to-be removed features.
* @property {'Fixed'} Fixed - for any bug fixes.
* @property {'Removed'} Removed - for now removed features.
* @property {'Security'} Security - in case of vulnerabilities.
* @property {'Uncategorized'} Uncategorized - for any changes that have not
* yet been categorized.
*/
/**
* @type {ChangeCategories}
*/
const changeCategories = {
Added: 'Added',
Changed: 'Changed',
Deprecated: 'Deprecated',
Fixed: 'Fixed',
Removed: 'Removed',
Security: 'Security',
Uncategorized: 'Uncategorized',
};
/**
* Change categories in the order in which they should be listed in the
* changelog.
*
* @type {Array<keyof ChangeCategories>}
*/
const orderedChangeCategories = [
'Uncategorized',
'Added',
'Changed',
'Deprecated',
'Removed',
'Fixed',
'Security',
];
/**
* The header for the section of the changelog listing unreleased changes.
* @typedef {'Unreleased'} Unreleased
*/
/**
* @type {Unreleased}
*/
const unreleased = 'Unreleased';
module.exports = {
changeCategories,
orderedChangeCategories,
unreleased,
};

View File

@ -1,84 +0,0 @@
const Changelog = require('./changelog');
const { unreleased } = require('./constants');
function truncated(line) {
return line.length > 80 ? `${line.slice(0, 80)}...` : line;
}
/**
* Constructs a Changelog instance that represents the given changelog, which
* is parsed for release and change informatino.
* @param {Object} options
* @param {string} options.changelogContent - The changelog to parse
* @param {string} options.repoUrl - The GitHub repository URL for the current
* project.
* @returns {Changelog} A changelog instance that reflects the changelog text
* provided.
*/
function parseChangelog({ changelogContent, repoUrl }) {
const changelogLines = changelogContent.split('\n');
const changelog = new Changelog({ repoUrl });
const unreleasedHeaderIndex = changelogLines.indexOf(`## [${unreleased}]`);
if (unreleasedHeaderIndex === -1) {
throw new Error(`Failed to find ${unreleased} header`);
}
const unreleasedLinkReferenceDefinition = changelogLines.findIndex((line) => {
return line.startsWith(`[${unreleased}]: `);
});
if (unreleasedLinkReferenceDefinition === -1) {
throw new Error(`Failed to find ${unreleased} link reference definition`);
}
const contentfulChangelogLines = changelogLines
.slice(unreleasedHeaderIndex + 1, unreleasedLinkReferenceDefinition)
.filter((line) => line !== '');
let mostRecentRelease;
let mostRecentCategory;
for (const line of contentfulChangelogLines) {
if (line.startsWith('## [')) {
const results = line.match(
/^## \[(\d+\.\d+\.\d+)\](?: - (\d\d\d\d-\d\d-\d\d))?(?: \[(\w+)\])?/u,
);
if (results === null) {
throw new Error(`Malformed release header: '${truncated(line)}'`);
}
mostRecentRelease = results[1];
mostRecentCategory = undefined;
const date = results[2];
const status = results[3];
changelog.addRelease({
addToStart: false,
date,
status,
version: mostRecentRelease,
});
} else if (line.startsWith('### ')) {
const results = line.match(/^### (\w+)$\b/u);
if (results === null) {
throw new Error(`Malformed category header: '${truncated(line)}'`);
}
mostRecentCategory = results[1];
} else if (line.startsWith('- ')) {
if (mostRecentCategory === undefined) {
throw new Error(`Category missing for change: '${truncated(line)}'`);
}
const description = line.slice(2);
changelog.addChange({
addToStart: false,
category: mostRecentCategory,
description,
version: mostRecentRelease,
});
} else if (mostRecentRelease === null) {
continue;
} else {
throw new Error(`Unrecognized line: '${truncated(line)}'`);
}
}
return changelog;
}
module.exports = { parseChangelog };

View File

@ -1,171 +0,0 @@
const assert = require('assert').strict;
const runCommand = require('../runCommand');
const { parseChangelog } = require('./parseChangelog');
const { changeCategories } = require('./constants');
async function getMostRecentTag() {
const [mostRecentTagCommitHash] = await runCommand('git', [
'rev-list',
'--tags',
'--max-count=1',
]);
const [mostRecentTag] = await runCommand('git', [
'describe',
'--tags',
mostRecentTagCommitHash,
]);
assert.equal(mostRecentTag[0], 'v', 'Most recent tag should start with v');
return mostRecentTag;
}
async function getCommits(commitHashes) {
const commits = [];
for (const commitHash of commitHashes) {
const [subject] = await runCommand('git', [
'show',
'-s',
'--format=%s',
commitHash,
]);
let prNumber;
let description = subject;
// Squash & Merge: the commit subject is parsed as `<description> (#<PR ID>)`
if (subject.match(/\(#\d+\)/u)) {
const matchResults = subject.match(/\(#(\d+)\)/u);
prNumber = matchResults[1];
description = subject.match(/^(.+)\s\(#\d+\)/u)[1];
// Merge: the PR ID is parsed from the git subject (which is of the form `Merge pull request
// #<PR ID> from <branch>`, and the description is assumed to be the first line of the body.
// If no body is found, the description is set to the commit subject
} else if (subject.match(/#\d+\sfrom/u)) {
const matchResults = subject.match(/#(\d+)\sfrom/u);
prNumber = matchResults[1];
const [firstLineOfBody] = await runCommand('git', [
'show',
'-s',
'--format=%b',
commitHash,
]);
description = firstLineOfBody || subject;
}
// Otherwise:
// Normal commits: The commit subject is the description, and the PR ID is omitted.
commits.push({ prNumber, description });
}
return commits;
}
function getAllChangeDescriptions(changelog) {
const releases = changelog.getReleases();
const changeDescriptions = Object.values(
changelog.getUnreleasedChanges(),
).flat();
for (const release of releases) {
changeDescriptions.push(
...Object.values(changelog.getReleaseChanges(release.version)).flat(),
);
}
return changeDescriptions;
}
function getAllLoggedPrNumbers(changelog) {
const changeDescriptions = getAllChangeDescriptions(changelog);
const prNumbersWithChangelogEntries = [];
for (const description of changeDescriptions) {
const matchResults = description.match(/^\[#(\d+)\]/u);
if (matchResults === null) {
continue;
}
const prNumber = matchResults[1];
prNumbersWithChangelogEntries.push(prNumber);
}
return prNumbersWithChangelogEntries;
}
/**
* @typedef {import('./constants.js').Version} Version
*/
/**
* Update a changelog with any commits made since the last release. Commits for
* PRs that are already included in the changelog are omitted.
* @param {Object} options
* @param {string} options.changelogContent - The current changelog
* @param {Version} options.currentVersion - The current version
* @param {string} options.repoUrl - The GitHub repository URL for the current
* project.
* @param {boolean} options.isReleaseCandidate - Denotes whether the current
* project is in the midst of release preparation or not. If this is set, any
* new changes are listed under the current release header. Otherwise, they
* are listed under the 'Unreleased' section.
* @returns
*/
async function updateChangelog({
changelogContent,
currentVersion,
repoUrl,
isReleaseCandidate,
}) {
const changelog = parseChangelog({ changelogContent, repoUrl });
// Ensure we have all tags on remote
await runCommand('git', ['fetch', '--tags']);
const mostRecentTag = await getMostRecentTag();
const commitsHashesSinceLastRelease = await runCommand('git', [
'rev-list',
`${mostRecentTag}..HEAD`,
]);
const commits = await getCommits(commitsHashesSinceLastRelease);
const loggedPrNumbers = getAllLoggedPrNumbers(changelog);
const newCommits = commits.filter(
({ prNumber }) => !loggedPrNumbers.includes(prNumber),
);
const hasUnreleasedChanges = changelog.getUnreleasedChanges().length !== 0;
if (
newCommits.length === 0 &&
(!isReleaseCandidate || hasUnreleasedChanges)
) {
return undefined;
}
// Ensure release header exists, if necessary
if (
isReleaseCandidate &&
!changelog
.getReleases()
.find((release) => release.version === currentVersion)
) {
changelog.addRelease({ version: currentVersion });
}
if (isReleaseCandidate && hasUnreleasedChanges) {
changelog.migrateUnreleasedChangesToRelease(currentVersion);
}
const newChangeEntries = newCommits.map(({ prNumber, description }) => {
if (prNumber) {
const prefix = `[#${prNumber}](${repoUrl}/pull/${prNumber})`;
return `${prefix}: ${description}`;
}
return description;
});
for (const description of newChangeEntries.reverse()) {
changelog.addChange({
version: isReleaseCandidate ? currentVersion : undefined,
category: changeCategories.Uncategorized,
description,
});
}
return changelog.toString();
}
module.exports = { updateChangelog };

View File

@ -1,79 +0,0 @@
const spawn = require('cross-spawn');
/**
* Run a command to completion using the system shell.
*
* This will run a command with the specified arguments, and resolve when the
* process has exited. The STDOUT stream is monitored for output, which is
* returned after being split into lines. All output is expected to be UTF-8
* encoded, and empty lines are removed from the output.
*
* Anything received on STDERR is assumed to indicate a problem, and is tracked
* as an error.
*
* @param {string} command - The command to run
* @param {Array<string>} [args] - The arguments to pass to the command
* @returns {Array<string>} Lines of output received via STDOUT
*/
async function runCommand(command, args) {
const output = [];
let mostRecentError;
let errorSignal;
let errorCode;
const internalError = new Error('Internal');
try {
await new Promise((resolve, reject) => {
const childProcess = spawn(command, args, { encoding: 'utf8' });
childProcess.stdout.setEncoding('utf8');
childProcess.stderr.setEncoding('utf8');
childProcess.on('error', (error) => {
mostRecentError = error;
});
childProcess.stdout.on('data', (message) => {
const nonEmptyLines = message.split('\n').filter((line) => line !== '');
output.push(...nonEmptyLines);
});
childProcess.stderr.on('data', (message) => {
mostRecentError = new Error(message.trim());
});
childProcess.once('exit', (code, signal) => {
if (code === 0) {
return resolve();
}
errorCode = code;
errorSignal = signal;
return reject(internalError);
});
});
} catch (error) {
/**
* The error is re-thrown here in an `async` context to preserve the stack trace. If this was
* was thrown inside the Promise constructor, the stack trace would show a few frames of
* Node.js internals then end, without indicating where `runCommand` was called.
*/
if (error === internalError) {
let errorMessage;
if (errorCode !== null && errorSignal !== null) {
errorMessage = `Terminated by signal '${errorSignal}'; exited with code '${errorCode}'`;
} else if (errorSignal !== null) {
errorMessage = `Terminaled by signal '${errorSignal}'`;
} else if (errorCode === null) {
errorMessage = 'Exited with no code or signal';
} else {
errorMessage = `Exited with code '${errorCode}'`;
}
const improvedError = new Error(errorMessage);
if (mostRecentError) {
improvedError.cause = mostRecentError;
}
throw improvedError;
}
}
return output;
}
module.exports = runCommand;

View File

@ -2,6 +2,10 @@
"name": "metamask-crx",
"version": "0.0.0",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/metamask-extension"
},
"scripts": {
"setup": "yarn install && yarn setup:postinstall",
"setup:postinstall": "yarn patch-package && yarn allow-scripts",
@ -57,7 +61,7 @@
"storybook": "start-storybook -p 6006 -c .storybook --static-dir ./app ./storybook/images",
"storybook:build": "build-storybook -c .storybook -o storybook-build --static-dir ./app ./storybook/images",
"storybook:deploy": "storybook-to-ghpages --existing-output-dir storybook-build --remote storybook --branch master",
"update-changelog": "node ./development/auto-changelog.js",
"update-changelog": "auto-changelog update",
"generate:migration": "./development/generate-migration.sh",
"lavamoat:auto": "lavamoat ./development/build/index.js --writeAutoPolicy",
"lavamoat:debug": "lavamoat ./development/build/index.js --writeAutoPolicyDebug"
@ -205,6 +209,7 @@
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.5.5",
"@lavamoat/allow-scripts": "^1.0.4",
"@metamask/auto-changelog": "^1.0.0",
"@metamask/eslint-config": "^6.0.0",
"@metamask/eslint-config-jest": "^6.0.0",
"@metamask/eslint-config-mocha": "^6.0.0",

View File

@ -2619,6 +2619,16 @@
prop-types "^15.7.2"
react-is "^16.8.0"
"@metamask/auto-changelog@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@metamask/auto-changelog/-/auto-changelog-1.0.0.tgz#ca6a71d1b983cf08b715bdcd8e240d746974d0c7"
integrity sha512-3Bcm+JsEmNllPi7kRtzS6EAjYTzz+Isa4QFq2DQ4DFwIsv2HUxdR+KNU2GJ1BdX4lbPcQTrpTdaPgBZ9G4NhLA==
dependencies:
cross-spawn "^7.0.3"
diff "^5.0.0"
semver "^7.3.5"
yargs "^17.0.1"
"@metamask/contract-metadata@^1.19.0", "@metamask/contract-metadata@^1.22.0", "@metamask/contract-metadata@^1.23.0":
version "1.25.0"
resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.25.0.tgz#442ace91fb40165310764b68d8096d0017bb0492"
@ -9185,6 +9195,11 @@ diff@^4.0.2:
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
diff@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
diffie-hellman@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"
@ -27561,6 +27576,19 @@ yargs@^16.0.0, yargs@^16.2.0:
y18n "^5.0.5"
yargs-parser "^20.2.2"
yargs@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb"
integrity sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.0"
y18n "^5.0.5"
yargs-parser "^20.2.2"
yargs@^7.1.0:
version "7.1.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.1.tgz#67f0ef52e228d4ee0d6311acede8850f53464df6"