diff --git a/package.json b/package.json index 53ac85296..27518536f 100644 --- a/package.json +++ b/package.json @@ -72,9 +72,6 @@ "ts-jest": "^25.0.0", "typescript": "~3.7.2" }, - "prettier": { - "singleQuote": true - }, "husky": { "hooks": { "pre-commit": "lint-staged" diff --git a/packages/bot-list/src/index.ts b/packages/bot-list/src/index.ts index 89d0feec6..8c18f82a5 100644 --- a/packages/bot-list/src/index.ts +++ b/packages/bot-list/src/index.ts @@ -1,10 +1,10 @@ export default [ - 'dependabot-preview[bot]', - 'greenkeeper[bot]', - 'dependabot[bot]', - 'fossabot', - 'renovate', - 'renovate-bot', - 'renovate[bot]', - 'renovate-approve' + "dependabot-preview[bot]", + "greenkeeper[bot]", + "dependabot[bot]", + "fossabot", + "renovate", + "renovate-bot", + "renovate[bot]", + "renovate-approve", ]; diff --git a/packages/cli/__tests__/args.test.ts b/packages/cli/__tests__/args.test.ts index e1d7899e1..13b080be1 100644 --- a/packages/cli/__tests__/args.test.ts +++ b/packages/cli/__tests__/args.test.ts @@ -1,87 +1,87 @@ -import parseArgs from '../src/parse-args'; +import parseArgs from "../src/parse-args"; const log = jest.fn(); // @ts-ignore global.console = { log }; -describe('root parser', () => { +describe("root parser", () => { beforeEach(() => { log.mockClear(); }); - test('should print version', () => { - parseArgs('--version'.split(' ')); + test("should print version", () => { + parseArgs("--version".split(" ")); expect(log).toHaveBeenCalled(); }); - test('should print help', () => { - parseArgs('--help'.split(' ')); + test("should print help", () => { + parseArgs("--help".split(" ")); expect(log).toHaveBeenCalled(); }); - test('should print help for simple command', () => { - parseArgs('init --help'.split(' ')); + test("should print help for simple command", () => { + parseArgs("init --help".split(" ")); expect(log).toHaveBeenCalled(); }); - test('should print help for complex command', () => { - parseArgs('pr-status --help'.split(' ')); + test("should print help for complex command", () => { + parseArgs("pr-status --help".split(" ")); expect(log).toHaveBeenCalled(); }); - test('should exit when required arg is not included', () => { + test("should exit when required arg is not included", () => { process.exit = jest.fn() as any; - parseArgs(['pr-status']); + parseArgs(["pr-status"]); expect(process.exit).toHaveBeenCalled(); }); - test('should exit when required string is provided as flag', () => { + test("should exit when required string is provided as flag", () => { process.exit = jest.fn() as any; - parseArgs(['pr-check', '--pr', '24', '--url']); + parseArgs(["pr-check", "--pr", "24", "--url"]); expect(process.exit).toHaveBeenCalled(); }); - test('should parse just provided args', () => { - expect(parseArgs('label --pr 2 --owner adam'.split(' '))).toStrictEqual([ - 'label', + test("should parse just provided args", () => { + expect(parseArgs("label --pr 2 --owner adam".split(" "))).toStrictEqual([ + "label", expect.objectContaining({ pr: 2, - owner: 'adam' - }) + owner: "adam", + }), ]); }); - test('should parse args as camelCase', () => { - expect(parseArgs('changelog -d'.split(' '))).toStrictEqual([ - 'changelog', + test("should parse args as camelCase", () => { + expect(parseArgs("changelog -d".split(" "))).toStrictEqual([ + "changelog", expect.objectContaining({ - dryRun: true - }) + dryRun: true, + }), ]); }); - test('error when on in array of options to is not present', () => { + test("error when on in array of options to is not present", () => { process.exit = jest.fn() as any; - parseArgs(['comment']); + parseArgs(["comment"]); expect(process.exit).toHaveBeenCalled(); }); - test('allow array of options to or', () => { - expect(parseArgs(['comment', '--message', 'foo'])).toStrictEqual([ - 'comment', + test("allow array of options to or", () => { + expect(parseArgs(["comment", "--message", "foo"])).toStrictEqual([ + "comment", expect.objectContaining({ - message: 'foo' - }) + message: "foo", + }), ]); }); - test('allow edit in comment', () => { - expect(parseArgs(['comment', '--edit', '--message', 'foo'])).toStrictEqual([ - 'comment', + test("allow edit in comment", () => { + expect(parseArgs(["comment", "--edit", "--message", "foo"])).toStrictEqual([ + "comment", expect.objectContaining({ edit: true, - message: 'foo' - }) + message: "foo", + }), ]); }); }); diff --git a/packages/cli/__tests__/main.test.ts b/packages/cli/__tests__/main.test.ts index eee19997e..71fb43210 100644 --- a/packages/cli/__tests__/main.test.ts +++ b/packages/cli/__tests__/main.test.ts @@ -1,21 +1,21 @@ -import main, { run } from '../src/run'; +import main, { run } from "../src/run"; -process.env.GH_TOKEN = 'XXXX'; +process.env.GH_TOKEN = "XXXX"; -test('throws error for unknown args', async () => { +test("throws error for unknown args", async () => { process.exit = jest.fn() as any; console.log = jest.fn() as any; - await run('foo', {}); + await run("foo", {}); expect(process.exit).toHaveBeenCalledWith(1); }); -test('throws exits for caught error', async () => { +test("throws exits for caught error", async () => { console.log = jest.fn() as any; process.exit = jest.fn() as any; - await main('foo', {}); + await main("foo", {}); expect(process.exit).toHaveBeenCalledWith(1); }); diff --git a/packages/cli/src/bin/auto.ts b/packages/cli/src/bin/auto.ts index a1e972150..168aa3629 100644 --- a/packages/cli/src/bin/auto.ts +++ b/packages/cli/src/bin/auto.ts @@ -1,36 +1,36 @@ #!/usr/bin/env node -import moduleAlias from 'module-alias'; -import path from 'path'; +import moduleAlias from "module-alias"; +import path from "path"; try { // eslint-disable-next-line - const json = require(path.join(__dirname, '../../package.json')); + const json = require(path.join(__dirname, "../../package.json")); - if (json.name.startsWith('@auto-canary')) { + if (json.name.startsWith("@auto-canary")) { moduleAlias.addAliases({ - '@auto-it': (fromPath: string, request: string) => + "@auto-it": (fromPath: string, request: string) => // We want to rewrite all the imports for canary (ex: npm plugin requiring core) - request.startsWith('@auto-it') && + request.startsWith("@auto-it") && // but we also want to be able to require official plugins from a canary - !fromPath.endsWith('noop.js') && + !fromPath.endsWith("noop.js") && // and don't want to override those plugins's imports - !fromPath.includes('@auto-it') - ? '@auto-canary' - : '@auto-it' + !fromPath.includes("@auto-it") + ? "@auto-canary" + : "@auto-it", }); } } catch (error) {} -import chalk from 'chalk'; -import parseArgs from '../parse-args'; -import run from '../run'; +import chalk from "chalk"; +import parseArgs from "../parse-args"; +import run from "../run"; const [command, args] = parseArgs(); if (command && args) { run(command, args).catch((e: Error) => { - console.error(chalk.redBright('Error: '), e.message); + console.error(chalk.redBright("Error: "), e.message); process.exit(1); }); } diff --git a/packages/cli/src/parse-args.ts b/packages/cli/src/parse-args.ts index a9871bc59..f8de86bd3 100644 --- a/packages/cli/src/parse-args.ts +++ b/packages/cli/src/parse-args.ts @@ -1,7 +1,7 @@ -import { getAutoVersion } from '@auto-it/core'; -import chalk from 'chalk'; -import { app, Command, Option } from 'command-line-application'; -import endent from 'endent'; +import { getAutoVersion } from "@auto-it/core"; +import chalk from "chalk"; +import { app, Command, Option } from "command-line-application"; +import endent from "endent"; import { ApiOptions, @@ -16,8 +16,8 @@ import { IPRStatusOptions, IReleaseOptions, IShipItOptions, - IVersionOptions -} from '@auto-it/core'; + IVersionOptions, +} from "@auto-it/core"; export type Flags = | keyof GlobalOptions @@ -33,10 +33,10 @@ export type Flags = | keyof IChangelogOptions | keyof ICanaryOptions; -const p = chalk.hex('#870048'); -const y = chalk.hex('#F1A60E'); -const r = chalk.hex('#C5000B'); -const g = chalk.hex('#888888'); +const p = chalk.hex("#870048"); +const y = chalk.hex("#F1A60E"); +const r = chalk.hex("#C5000B"); +const g = chalk.hex("#888888"); // prettier-ignore const logo = ` @@ -58,117 +58,117 @@ interface AutoOption extends Option { } const version: AutoOption = { - name: 'version', - alias: 'V', + name: "version", + alias: "V", type: Boolean, description: "Display auto's version", - group: 'global' + group: "global", }; const defaultOptions: AutoOption[] = [ { - name: 'verbose', - alias: 'v', + name: "verbose", + alias: "v", type: Boolean, - description: 'Show some more logs. Pass -vv for very verbose logs.', - group: 'global', - multiple: true + description: "Show some more logs. Pass -vv for very verbose logs.", + group: "global", + multiple: true, }, { - name: 'repo', + name: "repo", type: String, description: - 'The repo to set the status on. Defaults to looking in the package definition for the platform', - group: 'global' + "The repo to set the status on. Defaults to looking in the package definition for the platform", + group: "global", }, { - name: 'owner', + name: "owner", type: String, description: - 'The owner of the GitHub repo. Defaults to reading from the package definition for the platform', - group: 'global' + "The owner of the GitHub repo. Defaults to reading from the package definition for the platform", + group: "global", }, { - name: 'github-api', + name: "github-api", type: String, - description: 'GitHub API to use', - group: 'global' + description: "GitHub API to use", + group: "global", }, { - name: 'plugins', + name: "plugins", type: String, multiple: true, - description: 'Plugins to load auto with. (defaults to just npm)', - group: 'global' - } + description: "Plugins to load auto with. (defaults to just npm)", + group: "global", + }, ]; const baseBranch: AutoOption = { - name: 'base-branch', + name: "base-branch", type: String, description: 'Branch to treat as the "master" branch', - group: 'global' + group: "global", }; const pr: AutoOption = { - name: 'pr', + name: "pr", type: Number, description: - 'The pull request the command should use. Detects PR number in CI', - group: 'main' + "The pull request the command should use. Detects PR number in CI", + group: "main", }; const dryRun: AutoOption = { - name: 'dry-run', - alias: 'd', + name: "dry-run", + alias: "d", type: Boolean, - description: 'Report what command will do but do not actually do anything', - group: 'main' + description: "Report what command will do but do not actually do anything", + group: "main", }; const url: AutoOption = { - name: 'url', + name: "url", type: String, - description: 'URL to associate with this status', - group: 'main' + description: "URL to associate with this status", + group: "main", }; const noVersionPrefix: AutoOption = { - name: 'no-version-prefix', + name: "no-version-prefix", type: Boolean, description: "Use the version as the tag without the 'v' prefix. WARNING: some plugins might need extra config to use this option (ex: npm)", - group: 'main' + group: "main", }; const name: AutoOption = { - name: 'name', + name: "name", type: String, description: - 'Git name to commit with. Defaults to package definition for the platform', - group: 'main' + "Git name to commit with. Defaults to package definition for the platform", + group: "main", }; const email: AutoOption = { - name: 'email', + name: "email", type: String, description: - 'Git email to commit with. Defaults to package definition for the platform', - group: 'main' + "Git email to commit with. Defaults to package definition for the platform", + group: "main", }; const context: AutoOption = { - name: 'context', + name: "context", type: String, - description: 'A string label to differentiate this status from others', - group: 'main' + description: "A string label to differentiate this status from others", + group: "main", }; const message: AutoOption = { - name: 'message', - group: 'main', + name: "message", + group: "main", type: String, - alias: 'm' + alias: "m", }; interface AutoCommand extends Command { @@ -178,175 +178,175 @@ interface AutoCommand extends Command { export const commands: AutoCommand[] = [ { - name: 'init', - group: 'Setup Command', - description: 'Interactive setup for minimum working configuration.', - examples: ['{green $} auto init'] + name: "init", + group: "Setup Command", + description: "Interactive setup for minimum working configuration.", + examples: ["{green $} auto init"], }, { - name: 'info', - group: 'Setup Command', + name: "info", + group: "Setup Command", description: - 'Determine the environment and check if auto is set up correctly', - examples: ['{green $} auto info'] + "Determine the environment and check if auto is set up correctly", + examples: ["{green $} auto info"], }, { - name: 'create-labels', - group: 'Setup Command', + name: "create-labels", + group: "Setup Command", description: "Create your project's labels on github. If labels exist it will update them.", - examples: ['{green $} auto create-labels'], - options: [dryRun] + examples: ["{green $} auto create-labels"], + options: [dryRun], }, { - name: 'label', - group: 'Pull Request Interaction Commands', + name: "label", + group: "Pull Request Interaction Commands", description: "Get the labels for a pull request. Doesn't do much, but the return value lets you write you own scripts based off of the PR labels!", options: [ - { ...pr, description: `${pr.description} (defaults to last merged PR)` } + { ...pr, description: `${pr.description} (defaults to last merged PR)` }, ], - examples: ['{green $} auto label --pr 123'] + examples: ["{green $} auto label --pr 123"], }, { - name: 'comment', - group: 'Pull Request Interaction Commands', + name: "comment", + group: "Pull Request Interaction Commands", description: - 'Comment on a pull request with a markdown message. Each comment has a context, and each context only has one comment.', - require: [['message', 'delete']], + "Comment on a pull request with a markdown message. Each comment has a context, and each context only has one comment.", + require: [["message", "delete"]], options: [ pr, context, { - name: 'edit', + name: "edit", type: Boolean, - alias: 'e', - group: 'main', - description: 'Edit old comment', - config: true + alias: "e", + group: "main", + description: "Edit old comment", + config: true, }, { - name: 'delete', + name: "delete", type: Boolean, - group: 'main', - description: 'Delete old comment', - config: true + group: "main", + description: "Delete old comment", + config: true, }, - { ...message, description: 'Message to post to comment' }, - dryRun + { ...message, description: "Message to post to comment" }, + dryRun, ], examples: [ - '{green $} auto comment --delete', + "{green $} auto comment --delete", '{green $} auto comment --pr 123 --message "# Why you\'re wrong..."', - '{green $} auto comment --pr 123 --edit --message "This smells..." --context code-smell' - ] + '{green $} auto comment --pr 123 --edit --message "This smells..." --context code-smell', + ], }, { - name: 'pr-check', - group: 'Pull Request Interaction Commands', - description: 'Check that a pull request has a SemVer label', - require: ['url'], + name: "pr-check", + group: "Pull Request Interaction Commands", + description: "Check that a pull request has a SemVer label", + require: ["url"], options: [ pr, url, dryRun, { ...context, - defaultValue: 'ci/pr-check' - } + defaultValue: "ci/pr-check", + }, ], - examples: ['{green $} auto pr-check --url http://your-ci.com/build/123'] + examples: ["{green $} auto pr-check --url http://your-ci.com/build/123"], }, { - name: 'pr-status', - group: 'Pull Request Interaction Commands', - description: 'Set the status on a PR commit', - require: ['state', 'url', 'description', 'context'], + name: "pr-status", + group: "Pull Request Interaction Commands", + description: "Set the status on a PR commit", + require: ["state", "url", "description", "context"], options: [ { - name: 'sha', + name: "sha", type: String, - group: 'main', + group: "main", description: - 'Specify a custom git sha. Defaults to the HEAD for a git repo in the current repository' + "Specify a custom git sha. Defaults to the HEAD for a git repo in the current repository", }, { ...pr, - description: 'PR to set the status on. Detects PR number in CI' + description: "PR to set the status on. Detects PR number in CI", }, url, { - name: 'state', + name: "state", type: String, - group: 'main', + group: "main", description: - "State of the PR. ['pending', 'success', 'error', 'failure']" + "State of the PR. ['pending', 'success', 'error', 'failure']", }, { - name: 'description', + name: "description", type: String, - group: 'main', - description: 'A description of the status' + group: "main", + description: "A description of the status", }, { - name: 'context', + name: "context", type: String, - group: 'main', - description: 'A string label to differentiate this status from others' + group: "main", + description: "A string label to differentiate this status from others", }, - dryRun + dryRun, ], examples: [ - `{green $} auto pr \\\\ \n --state pending \\\\ \n --description "Build still running..." \\\\ \n --context build-check` - ] + `{green $} auto pr \\\\ \n --state pending \\\\ \n --description "Build still running..." \\\\ \n --context build-check`, + ], }, { - name: 'pr-body', - group: 'Pull Request Interaction Commands', + name: "pr-body", + group: "Pull Request Interaction Commands", description: - 'Update the body of a PR with a message. Appends to PR and will not overwrite user content. Each comment has a context, and each context only has one comment.', - require: ['message'], + "Update the body of a PR with a message. Appends to PR and will not overwrite user content. Each comment has a context, and each context only has one comment.", + require: ["message"], options: [ pr, context, - { ...message, description: 'Message to post to PR body' }, - dryRun + { ...message, description: "Message to post to PR body" }, + dryRun, ], examples: [ - '{green $} auto pr-body --delete', - '{green $} auto pr-body --pr 123 --comment "The new version is: 1.2.3"' - ] + "{green $} auto pr-body --delete", + '{green $} auto pr-body --pr 123 --comment "The new version is: 1.2.3"', + ], }, { - name: 'version', - group: 'Release Commands', + name: "version", + group: "Release Commands", description: - 'Get the semantic version bump for the given changes. Requires all PRs to have labels for the change type. If a PR does not have a label associated with it, it will default to `patch`.', + "Get the semantic version bump for the given changes. Requires all PRs to have labels for the change type. If a PR does not have a label associated with it, it will default to `patch`.", options: [ { - name: 'only-publish-with-release-label', + name: "only-publish-with-release-label", type: Boolean, description: "Only bump version if 'release' label is on pull request", - group: 'main' + group: "main", }, { - name: 'from', + name: "from", type: String, - group: 'main', + group: "main", description: - 'Git revision (tag, commit sha, ...) to calculate version bump from. Defaults to latest github release' - } + "Git revision (tag, commit sha, ...) to calculate version bump from. Defaults to latest github release", + }, ], examples: [ { - desc: 'Get the new version using the last release to head', - example: '{green $} auto version' - } - ] + desc: "Get the new version using the last release to head", + example: "{green $} auto version", + }, + ], }, { - name: 'changelog', - group: 'Release Commands', + name: "changelog", + group: "Release Commands", description: "Prepend release notes to `CHANGELOG.md`, create one if it doesn't exist, and commit the changes.", options: [ @@ -355,77 +355,77 @@ export const commands: AutoCommand[] = [ name, email, { - name: 'from', + name: "from", type: String, - group: 'main', + group: "main", description: - 'Tag to start changelog generation on. Defaults to latest tag.' + "Tag to start changelog generation on. Defaults to latest tag.", }, { - name: 'to', + name: "to", type: String, - group: 'main', - description: 'Tag to end changelog generation on. Defaults to HEAD.' + group: "main", + description: "Tag to end changelog generation on. Defaults to HEAD.", }, { ...message, description: "Message to commit the changelog with. Defaults to 'Update CHANGELOG.md [skip ci]'", - config: true + config: true, }, - baseBranch + baseBranch, ], examples: [ { - desc: 'Generate a changelog from the last release to head', - example: '{green $} auto changelog' + desc: "Generate a changelog from the last release to head", + example: "{green $} auto changelog", }, { - desc: 'Generate a changelog across specific versions', - example: '{green $} auto changelog --from v0.20.1 --to v0.21.0' - } - ] + desc: "Generate a changelog across specific versions", + example: "{green $} auto changelog --from v0.20.1 --to v0.21.0", + }, + ], }, { - name: 'release', - group: 'Release Commands', - description: 'Auto-generate a github release', + name: "release", + group: "Release Commands", + description: "Auto-generate a github release", options: [ dryRun, noVersionPrefix, name, email, { - name: 'from', + name: "from", type: String, - group: 'main', + group: "main", description: - 'Git revision (tag, commit sha, ...) to start release notes from. Defaults to latest tag.' + "Git revision (tag, commit sha, ...) to start release notes from. Defaults to latest tag.", }, { - name: 'use-version', + name: "use-version", type: String, - group: 'main', + group: "main", description: - 'Version number to publish as. Defaults to reading from the package definition for the platform.' + "Version number to publish as. Defaults to reading from the package definition for the platform.", }, baseBranch, { - name: 'prerelease', + name: "prerelease", type: Boolean, - group: 'main', - description: 'Publish a prerelease.', - config: true - } + group: "main", + description: "Publish a prerelease.", + config: true, + }, ], examples: [ - '{green $} auto release', - '{green $} auto release --from v0.20.1 --use-version v0.21.0' - ] + "{green $} auto release", + "{green $} auto release --from v0.20.1 --use-version v0.21.0", + ], }, { - name: 'shipit', - group: 'Release Commands', + name: "shipit", + group: "Release Commands", description: endent` Context aware publishing. @@ -434,33 +434,33 @@ export const commands: AutoCommand[] = [ 3. call from PR in CI -> canary version released (CANARY) 4. call locally when not on base/prerelease branch -> canary version released (CANARY) `, - examples: ['{green $} auto shipit'], + examples: ["{green $} auto shipit"], options: [ baseBranch, dryRun, { - name: 'only-graduate-with-release-label', + name: "only-graduate-with-release-label", type: Boolean, defaultValue: false, - group: 'main', + group: "main", description: 'Make auto publish prerelease versions when merging to master. Only PRs merged with "release" label will generate a "latest" release. Only use this flag if you do not want to maintain a prerelease branch, and instead only want to use master.', - config: true - } - ] + config: true, + }, + ], }, { - name: 'latest', - group: 'Release Commands', + name: "latest", + group: "Release Commands", description: endent` Run the full \`auto\` release pipeline. Force a release to latest and bypass \`shipit\` safeguards. `, - examples: ['{green $} auto latest'], - options: [baseBranch, dryRun] + examples: ["{green $} auto latest"], + options: [baseBranch, dryRun], }, { - name: 'canary', - group: 'Release Commands', + name: "canary", + group: "Release Commands", description: endent` Make a canary release of the project. Useful on PRs. If ran locally, \`canary\` will release a canary version for your current git HEAD. This is ran automatically from "shipit". @@ -468,45 +468,45 @@ export const commands: AutoCommand[] = [ 2. Locally: 1.2.3-canary.1810cfd `, examples: [ - '{green $} auto canary', - '{green $} auto canary --force', - '{green $} auto canary --pr 123 --build 5', + "{green $} auto canary", + "{green $} auto canary --force", + "{green $} auto canary --pr 123 --build 5", '{green $} auto canary --message "Install PR version: `yarn add -D my-project@%v`"', - '{green $} auto canary --message false' + "{green $} auto canary --message false", ], options: [ dryRun, { ...pr, description: - 'PR number to use to create the canary version. Detected in CI env' + "PR number to use to create the canary version. Detected in CI env", }, { - name: 'build', + name: "build", type: String, - group: 'main', + group: "main", description: - 'Build number to use to create the canary version. Detected in CI env' + "Build number to use to create the canary version. Detected in CI env", }, { ...message, description: "Message to comment on PR with. Defaults to 'Published PR with canary version: %v'. Pass false to disable the comment", - config: true + config: true, }, { - name: 'force', + name: "force", type: Boolean, - group: 'main', + group: "main", description: - 'Force a canary release, even if the PR is marked to skip the release', - config: true - } - ] + "Force a canary release, even if the PR is marked to skip the release", + config: true, + }, + ], }, { - name: 'next', - group: 'Release Commands', + name: "next", + group: "Release Commands", description: endent` Make a release for your "prerelease" release line. This is ran automatically from "shipit". @@ -515,32 +515,32 @@ export const commands: AutoCommand[] = [ Calling the \`next\` command from a prerelease branch will publish a prerelease, otherwise it will publish to the default prerelease branch. `, - examples: ['{green $} auto next'], + examples: ["{green $} auto next"], options: [ dryRun, { ...message, description: - 'The message used when attaching the prerelease version to a PR', - config: true - } - ] - } + "The message used when attaching the prerelease version to a PR", + config: true, + }, + ], + }, ]; /** Parse the CLI args and return command + options provided. */ export default function parseArgs(testArgs?: string[]) { const mainOptions = app( { - name: 'auto', + name: "auto", logo, description: - 'Generate releases based on semantic version labels on pull requests, and other pull request automation tools.', + "Generate releases based on semantic version labels on pull requests, and other pull request automation tools.", commands, - options: [version, ...defaultOptions] + options: [version, ...defaultOptions], }, { - argv: testArgs + argv: testArgs, } ); diff --git a/packages/cli/src/run.ts b/packages/cli/src/run.ts index 6676bf1fc..fb1692220 100644 --- a/packages/cli/src/run.ts +++ b/packages/cli/src/run.ts @@ -13,31 +13,31 @@ import Auto, { IReleaseOptions, IShipItOptions, IVersionOptions, - INextOptions -} from '@auto-it/core'; -import endent from 'endent'; -import on from 'await-to-js'; -import link from 'terminal-link'; + INextOptions, +} from "@auto-it/core"; +import endent from "endent"; +import on from "await-to-js"; +import link from "terminal-link"; /** Spin up the "auto" node API and provide it the parsed CLI args. */ export async function run(command: string, args: ApiOptions) { const auto = new Auto(args); try { - if (command === 'init') { + if (command === "init") { await auto.init(); return; } await auto.loadConfig(); - if (args.verbose || command === 'info') { + if (args.verbose || command === "info") { try { - // We don't want auto.info throwing an error during another + // We don't want auto.info throwing an error during another // command const { hasError } = await auto.info(); - if (command === 'info') { + if (command === "info") { // eslint-disable-next-line max-depth if (hasError) { process.exit(1); @@ -46,50 +46,50 @@ export async function run(command: string, args: ApiOptions) { } } } catch (error) { - if (command === 'info') { + if (command === "info") { process.exit(1); } } } switch (command) { - case 'create-labels': + case "create-labels": await auto.createLabels(args as ICreateLabelsOptions); break; - case 'label': + case "label": await auto.label(args as ILabelOptions); break; - case 'pr-check': + case "pr-check": await auto.prCheck(args as IPRCheckOptions); break; - case 'pr-status': + case "pr-status": await auto.prStatus(args as IPRStatusOptions); break; - case 'comment': + case "comment": await auto.comment(args as ICommentOptions); break; - case 'pr-body': + case "pr-body": await auto.prBody(args as IPRBodyOptions); break; - case 'version': + case "version": await auto.version(args as IVersionOptions); break; - case 'changelog': + case "changelog": await auto.changelog(args as IChangelogOptions); break; - case 'release': + case "release": await auto.runRelease(args as IReleaseOptions); break; - case 'shipit': + case "shipit": await auto.shipit(args as IShipItOptions); break; - case 'latest': + case "latest": await auto.latest(args as IShipItOptions); break; - case 'canary': + case "canary": await auto.canary(args as ICanaryOptions); break; - case 'next': + case "next": await auto.next(args as INextOptions); break; default: @@ -100,7 +100,7 @@ export async function run(command: string, args: ApiOptions) { const [, project] = await on(auto.git!.getProject()); const repoLink = link( `${auto.git?.options.owner}/${auto.git?.options.repo}`, - project?.html_url || '' + project?.html_url || "" ); auto.logger.log.error(endent` @@ -112,7 +112,7 @@ export async function run(command: string, args: ApiOptions) { You can check the permission for you token by running "auto info". `); - console.log(''); + console.log(""); auto.logger.verbose.error(error.request); } else { console.log(error); diff --git a/packages/core/src/__tests__/__snapshots__/release.test.ts.snap b/packages/core/src/__tests__/__snapshots__/release.test.ts.snap index 8c5325320..6c7f0e4b4 100644 --- a/packages/core/src/__tests__/__snapshots__/release.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/release.test.ts.snap @@ -424,3 +424,34 @@ Array [ }, ] `; + +exports[`Release getCommits should use latest title of PR 1`] = ` +Array [ + Object { + "authorEmail": "adam@dierkens.com", + "authorName": "Adam Dierkens", + "authors": Array [ + Object { + "email": "adam@dierkens.com", + "name": "Adam Dierkens", + }, + Object { + "login": "adam", + "name": "Adam Dierkens", + "username": "adam", + }, + ], + "files": Array [], + "hash": "foo", + "labels": Array [ + "skip-release", + "minor", + ], + "pullRequest": Object { + "body": undefined, + "number": 124, + }, + "subject": "Updated Title", + }, +] +`; diff --git a/packages/core/src/__tests__/auto-canary-local.test.ts b/packages/core/src/__tests__/auto-canary-local.test.ts index c208f93c8..0323c88d2 100644 --- a/packages/core/src/__tests__/auto-canary-local.test.ts +++ b/packages/core/src/__tests__/auto-canary-local.test.ts @@ -1,51 +1,51 @@ -import Auto from '../auto'; -import SEMVER from '../semver'; -import { dummyLog } from '../utils/logger'; +import Auto from "../auto"; +import SEMVER from "../semver"; +import { dummyLog } from "../utils/logger"; -jest.mock('env-ci', () => () => ({ - branch: 'local-test' +jest.mock("env-ci", () => () => ({ + branch: "local-test", })); const defaults = { - owner: 'foo', - repo: 'bar' + owner: "foo", + repo: "bar", }; -process.env.GH_TOKEN = 'XXXX'; +process.env.GH_TOKEN = "XXXX"; -jest.mock('@octokit/rest', () => { +jest.mock("@octokit/rest", () => { const Octokit = class MockOctokit { static plugin = () => Octokit; authenticate = () => undefined; repos = { - get: jest.fn().mockReturnValue({}) + get: jest.fn().mockReturnValue({}), }; hook = { - error: () => undefined + error: () => undefined, }; }; return { Octokit }; }); -test('shipit should publish canary in locally when not on master', async () => { +test("shipit should publish canary in locally when not on master", async () => { const auto = new Auto({ ...defaults, plugins: [] }); auto.logger = dummyLog(); // @ts-ignore auto.checkClean = () => Promise.resolve(true); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - auto.git!.getSha = () => Promise.resolve('abc'); - jest.spyOn(auto.git!, 'createComment').mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + auto.git!.getSha = () => Promise.resolve("abc"); + jest.spyOn(auto.git!, "createComment").mockImplementation(); auto.release!.getCommitsInRelease = () => Promise.resolve([]); auto.release!.getCommits = () => Promise.resolve([]); const canary = jest.fn(); - auto.hooks.canary.tap('test', canary); + auto.hooks.canary.tap("test", canary); await auto.shipit(); - expect(canary).toHaveBeenCalledWith(SEMVER.patch, '.abc'); + expect(canary).toHaveBeenCalledWith(SEMVER.patch, ".abc"); }); diff --git a/packages/core/src/__tests__/auto-comment.test.ts b/packages/core/src/__tests__/auto-comment.test.ts index 90f0f0340..ba4312f89 100644 --- a/packages/core/src/__tests__/auto-comment.test.ts +++ b/packages/core/src/__tests__/auto-comment.test.ts @@ -1,35 +1,35 @@ -import Auto from '../auto'; -import { dummyLog } from '../utils/logger'; +import Auto from "../auto"; +import { dummyLog } from "../utils/logger"; -jest.mock('env-ci', () => () => ({ pr: 123 })); +jest.mock("env-ci", () => () => ({ pr: 123 })); const defaults = { - owner: 'foo', - repo: 'bar' + owner: "foo", + repo: "bar", }; -process.env.GH_TOKEN = 'XXXX'; +process.env.GH_TOKEN = "XXXX"; -jest.mock('@octokit/rest', () => { +jest.mock("@octokit/rest", () => { const Octokit = class MockOctokit { static plugin = () => Octokit; authenticate = () => undefined; repos = { - get: jest.fn().mockReturnValue({}) + get: jest.fn().mockReturnValue({}), }; hook = { - error: () => undefined + error: () => undefined, }; }; return { Octokit }; }); -describe('comment', () => { - test('should find PR number from CI', async () => { +describe("comment", () => { + test("should find PR number from CI", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -37,7 +37,7 @@ describe('comment', () => { const createComment = jest.fn(); auto.git!.createComment = createComment; - await auto.comment({ message: 'foo' }); + await auto.comment({ message: "foo" }); expect(createComment).toHaveBeenCalled(); }); }); diff --git a/packages/core/src/__tests__/auto-env.test.ts b/packages/core/src/__tests__/auto-env.test.ts index 53c434b9f..6229e6e03 100644 --- a/packages/core/src/__tests__/auto-env.test.ts +++ b/packages/core/src/__tests__/auto-env.test.ts @@ -1,23 +1,23 @@ -import Auto from '../auto'; +import Auto from "../auto"; -jest.mock('fs', () => ({ +jest.mock("fs", () => ({ readFileSync: () => 'FOO="test value"', closeSync: () => undefined, existsSync: () => true, readFile: () => undefined, - ReadStream: function() {}, - WriteStream: function() {}, - writeFile: () => undefined + ReadStream: function () {}, + WriteStream: function () {}, + writeFile: () => undefined, })); -test('should load .env file and override and env vars that are already set', async () => { - process.env.FOO = 'old value'; +test("should load .env file and override and env vars that are already set", async () => { + process.env.FOO = "old value"; const auto = new Auto({ - owner: 'foo', - repo: 'bar' + owner: "foo", + repo: "bar", }); expect(auto).toBeDefined(); - expect(process.env.FOO).toBe('test value'); + expect(process.env.FOO).toBe("test value"); }); diff --git a/packages/core/src/__tests__/auto-in-pr-ci.test.ts b/packages/core/src/__tests__/auto-in-pr-ci.test.ts index 691bddfd0..fcf025edd 100644 --- a/packages/core/src/__tests__/auto-in-pr-ci.test.ts +++ b/packages/core/src/__tests__/auto-in-pr-ci.test.ts @@ -1,44 +1,44 @@ -import Auto from '../auto'; -import SEMVER from '../semver'; -import { dummyLog } from '../utils/logger'; -import makeCommitFromMsg from './make-commit-from-msg'; -import endent from 'endent'; +import Auto from "../auto"; +import SEMVER from "../semver"; +import { dummyLog } from "../utils/logger"; +import makeCommitFromMsg from "./make-commit-from-msg"; +import endent from "endent"; -jest.mock('env-ci', () => () => ({ +jest.mock("env-ci", () => () => ({ pr: 123, build: 1, isCi: true, isPr: true, - branch: 'ci-test' + branch: "ci-test", })); const defaults = { - owner: 'foo', - repo: 'bar' + owner: "foo", + repo: "bar", }; -process.env.GH_TOKEN = 'XXX'; +process.env.GH_TOKEN = "XXX"; -jest.mock('@octokit/rest', () => { +jest.mock("@octokit/rest", () => { const Octokit = class MockOctokit { static plugin = () => Octokit; authenticate = () => undefined; repos = { - get: jest.fn().mockReturnValue({}) + get: jest.fn().mockReturnValue({}), }; hook = { - error: () => undefined + error: () => undefined, }; }; return { Octokit }; }); -describe('canary in ci', () => { - test('calls the canary hook with the canary version', async () => { +describe("canary in ci", () => { + test("calls the canary hook with the canary version", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); @@ -46,54 +46,54 @@ describe('canary in ci', () => { auto.logger = dummyLog(); await auto.loadConfig(); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const canary = jest.fn(); - auto.hooks.canary.tap('test', canary); + auto.hooks.canary.tap("test", canary); const addToPrBody = jest.fn(); auto.git!.addToPrBody = addToPrBody; - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.canary(); - expect(canary).toHaveBeenCalledWith(SEMVER.patch, '.123.1'); + expect(canary).toHaveBeenCalledWith(SEMVER.patch, ".123.1"); }); - test('comments on PR in CI', async () => { + test("comments on PR in CI", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const addToPrBody = jest.fn(); auto.git!.addToPrBody = addToPrBody; - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); - auto.hooks.canary.tap('test', () => '1.2.4-canary.123.1'); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); + auto.hooks.canary.tap("test", () => "1.2.4-canary.123.1"); const version = await auto.canary({ pr: 123, build: 1 }); expect(addToPrBody).toHaveBeenCalled(); - expect(version!.newVersion).toBe('1.2.4-canary.123.1'); + expect(version!.newVersion).toBe("1.2.4-canary.123.1"); }); - test('should fail when canaries not implemented', async () => { + test("should fail when canaries not implemented", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); // @ts-ignore - jest.spyOn(process, 'exit').mockImplementationOnce(() => {}); + jest.spyOn(process, "exit").mockImplementationOnce(() => {}); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const addToPrBody = jest.fn(); auto.git!.addToPrBody = addToPrBody; - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); - await auto.canary({ pr: 123, build: 1, message: 'false' }); + await auto.canary({ pr: 123, build: 1, message: "false" }); expect(process.exit).toHaveBeenCalled(); }); @@ -104,19 +104,19 @@ describe('canary in ci', () => { auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const addToPrBody = jest.fn(); auto.git!.addToPrBody = addToPrBody; - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); - auto.hooks.canary.tap('test', (bump, post) => `1.2.4-canary${post}`); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); + auto.hooks.canary.tap("test", (bump, post) => `1.2.4-canary${post}`); - await auto.canary({ pr: 123, build: 1, message: 'false' }); + await auto.canary({ pr: 123, build: 1, message: "false" }); expect(addToPrBody).not.toHaveBeenCalled(); }); - test('can override pr and build', async () => { + test("can override pr and build", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); @@ -124,19 +124,19 @@ describe('canary in ci', () => { auto.logger = dummyLog(); await auto.loadConfig(); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const addToPrBody = jest.fn(); auto.git!.addToPrBody = addToPrBody; - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); - auto.hooks.canary.tap('test', (bump, post) => `1.2.4-canary${post}`); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); + auto.hooks.canary.tap("test", (bump, post) => `1.2.4-canary${post}`); const version = await auto.canary({ pr: 456, build: 5 }); - expect(version!.newVersion).toBe('1.2.4-canary.456.5'); + expect(version!.newVersion).toBe("1.2.4-canary.456.5"); }); }); -describe('shipit in ci', () => { - test('should publish canary in PR', async () => { +describe("shipit in ci", () => { + test("should publish canary in PR", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); @@ -144,20 +144,20 @@ describe('shipit in ci', () => { auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - jest.spyOn(auto.git!, 'addToPrBody').mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + jest.spyOn(auto.git!, "addToPrBody").mockImplementation(); auto.release!.getCommitsInRelease = () => Promise.resolve([]); auto.release!.getCommits = () => Promise.resolve([]); const canary = jest.fn(); - auto.hooks.canary.tap('test', canary); + auto.hooks.canary.tap("test", canary); await auto.shipit(); - expect(canary).toHaveBeenCalledWith(SEMVER.patch, '.123.1'); + expect(canary).toHaveBeenCalledWith(SEMVER.patch, ".123.1"); }); }); -describe('next in ci', () => { - test('should post comment with new version', async () => { +describe("next in ci", () => { + test("should post comment with new version", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore @@ -168,20 +168,20 @@ describe('next in ci', () => { auto.logger = dummyLog(); await auto.loadConfig(); auto.git!.publish = () => Promise.resolve({} as any); - auto.git!.getLatestTagInBranch = () => Promise.resolve('1.2.3'); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - auto.release!.generateReleaseNotes = () => Promise.resolve('notes'); + auto.git!.getLatestTagInBranch = () => Promise.resolve("1.2.3"); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + auto.release!.generateReleaseNotes = () => Promise.resolve("notes"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const afterRelease = jest.fn(); - auto.hooks.afterRelease.tap('test', afterRelease); - auto.hooks.next.tap('test', () => ['1.2.4-next.0']); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.afterRelease.tap("test", afterRelease); + auto.hooks.next.tap("test", () => ["1.2.4-next.0"]); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.next({}); expect(prBody).toHaveBeenCalledWith({ - context: 'prerelease-version', + context: "prerelease-version", pr: 123, message: endent` # Version @@ -192,7 +192,7 @@ describe('next in ci', () => { Changelog notes - ` + `, }); }); }); diff --git a/packages/core/src/__tests__/auto-make-changelog.test.ts b/packages/core/src/__tests__/auto-make-changelog.test.ts index 5e175fd39..e3ea31bd0 100644 --- a/packages/core/src/__tests__/auto-make-changelog.test.ts +++ b/packages/core/src/__tests__/auto-make-changelog.test.ts @@ -1,48 +1,48 @@ -import Auto from '../auto'; -import { dummyLog } from '../utils/logger'; -import child from 'child_process'; +import Auto from "../auto"; +import { dummyLog } from "../utils/logger"; +import child from "child_process"; jest - .spyOn(child, 'execSync') + .spyOn(child, "execSync") .mockImplementation() // @ts-ignore - .mockReturnValue(''); + .mockReturnValue(""); const importMock = jest.fn(); -jest.mock('import-cwd', () => (path: string) => importMock(path)); -jest.mock('env-ci', () => () => ({ isCi: false, branch: 'master' })); -jest.mock('../utils/exec-promise', () => () => Promise.resolve('')); +jest.mock("import-cwd", () => (path: string) => importMock(path)); +jest.mock("env-ci", () => () => ({ isCi: false, branch: "master" })); +jest.mock("../utils/exec-promise", () => () => Promise.resolve("")); const defaults = { - owner: 'foo', - repo: 'bar' + owner: "foo", + repo: "bar", }; -process.env.GH_TOKEN = 'XXXX'; +process.env.GH_TOKEN = "XXXX"; const search = jest.fn(); -jest.mock('cosmiconfig', () => ({ +jest.mock("cosmiconfig", () => ({ cosmiconfig: () => ({ - search - }) + search, + }), })); -jest.mock('@octokit/rest', () => { +jest.mock("@octokit/rest", () => { const Octokit = class MockOctokit { static plugin = () => Octokit; authenticate = () => undefined; search = { - issuesAndPullRequests: () => ({ data: { items: [] } }) + issuesAndPullRequests: () => ({ data: { items: [] } }), }; repos = { - get: jest.fn().mockReturnValue(Promise.resolve({})) + get: jest.fn().mockReturnValue(Promise.resolve({})), }; hook = { - error: () => undefined + error: () => undefined, }; }; @@ -50,22 +50,22 @@ jest.mock('@octokit/rest', () => { }); // @ts-ignore -jest.mock('gitlogplus', () => (a, cb) => { +jest.mock("gitlogplus", () => (a, cb) => { cb(undefined, [ { - rawBody: 'foo' + rawBody: "foo", }, { - rawBody: 'foo' - } + rawBody: "foo", + }, ]); }); -describe('Auto', () => { - test('should add to changelog', async () => { +describe("Auto", () => { + test("should add to changelog", async () => { const auto = new Auto({ plugins: [], - ...defaults + ...defaults, }); auto.logger = dummyLog(); @@ -73,10 +73,10 @@ describe('Auto', () => { const addToChangelog = jest.fn(); auto.release!.addToChangelog = addToChangelog; - jest.spyOn(auto.release!, 'generateReleaseNotes').mockImplementation(); - jest.spyOn(auto.release!, 'getCommitsInRelease').mockImplementation(); + jest.spyOn(auto.release!, "generateReleaseNotes").mockImplementation(); + jest.spyOn(auto.release!, "getCommitsInRelease").mockImplementation(); - await auto.changelog({ from: 'v1.0.0' }); + await auto.changelog({ from: "v1.0.0" }); expect(addToChangelog).toHaveBeenCalled(); }); }); diff --git a/packages/core/src/__tests__/auto.test.ts b/packages/core/src/__tests__/auto.test.ts index 836d62e23..24486c854 100644 --- a/packages/core/src/__tests__/auto.test.ts +++ b/packages/core/src/__tests__/auto.test.ts @@ -1,54 +1,54 @@ -import Auto from '../auto'; -import { IPRStatusOptions } from '../auto-args'; -import SEMVER from '../semver'; -import { dummyLog } from '../utils/logger'; -import makeCommitFromMsg from './make-commit-from-msg'; -import loadPlugin from '../utils/load-plugins'; -import child from 'child_process'; +import Auto from "../auto"; +import { IPRStatusOptions } from "../auto-args"; +import SEMVER from "../semver"; +import { dummyLog } from "../utils/logger"; +import makeCommitFromMsg from "./make-commit-from-msg"; +import loadPlugin from "../utils/load-plugins"; +import child from "child_process"; const importMock = jest.fn(); -jest.mock('../utils/load-plugins.ts'); -jest.mock('../utils/verify-auth.ts', () => () => true); -jest.mock('import-cwd', () => (path: string) => importMock(path)); -jest.mock('env-ci', () => () => ({ isCi: false, branch: 'master' })); +jest.mock("../utils/load-plugins.ts"); +jest.mock("../utils/verify-auth.ts", () => () => true); +jest.mock("import-cwd", () => (path: string) => importMock(path)); +jest.mock("env-ci", () => () => ({ isCi: false, branch: "master" })); const defaults = { - owner: 'foo', - repo: 'bar' + owner: "foo", + repo: "bar", }; -process.env.GH_TOKEN = 'XXXX'; +process.env.GH_TOKEN = "XXXX"; const labels = [ - { name: 'Version: Major', releaseType: SEMVER.major, overwrite: true }, - { name: 'Version: Patch', releaseType: SEMVER.patch, overwrite: true }, - { name: 'Version: Minor', releaseType: SEMVER.minor, overwrite: true } + { name: "Version: Major", releaseType: SEMVER.major, overwrite: true }, + { name: "Version: Patch", releaseType: SEMVER.patch, overwrite: true }, + { name: "Version: Minor", releaseType: SEMVER.minor, overwrite: true }, ]; const search = jest.fn(); -jest.mock('cosmiconfig', () => ({ +jest.mock("cosmiconfig", () => ({ cosmiconfig: () => ({ - search - }) + search, + }), })); -jest.mock('@octokit/rest', () => { +jest.mock("@octokit/rest", () => { const Octokit = class MockOctokit { static plugin = () => Octokit; authenticate = () => undefined; search = { - issuesAndPullRequests: () => ({ data: { items: [] } }) + issuesAndPullRequests: () => ({ data: { items: [] } }), }; repos = { - get: jest.fn().mockReturnValue({}) + get: jest.fn().mockReturnValue({}), }; hook = { - error: () => undefined + error: () => undefined, }; }; @@ -56,39 +56,39 @@ jest.mock('@octokit/rest', () => { }); // @ts-ignore -jest.mock('gitlogplus', () => (a, cb) => { +jest.mock("gitlogplus", () => (a, cb) => { cb(undefined, [ { - rawBody: 'foo', - hash: '123' + rawBody: "foo", + hash: "123", }, { - rawBody: 'foo', - hash: '456' - } + rawBody: "foo", + hash: "456", + }, ]); }); -describe('Auto', () => { +describe("Auto", () => { beforeEach(() => { // @ts-ignore loadPlugin.mockClear(); jest - .spyOn(child, 'execSync') + .spyOn(child, "execSync") .mockImplementation() // @ts-ignore - .mockReturnValue(''); + .mockReturnValue(""); }); - test('should use args', async () => { + test("should use args", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); expect(auto.release).toBeDefined(); }); - test('should load config', async () => { + test("should load config", async () => { search.mockReturnValueOnce({ config: defaults }); const auto = new Auto(); auto.logger = dummyLog(); @@ -96,7 +96,7 @@ describe('Auto', () => { expect(auto.release).toBeDefined(); }); - test('should default to npm in non-pkg', async () => { + test("should default to npm in non-pkg", async () => { search.mockReturnValueOnce({ config: defaults }); // @ts-ignore loadPlugin.mockReturnValueOnce(() => ({ apply: () => {} })); @@ -106,10 +106,10 @@ describe('Auto', () => { await auto.loadConfig(); // @ts-ignore - expect(loadPlugin.mock.calls[1][0][0]).toBe('npm'); + expect(loadPlugin.mock.calls[1][0][0]).toBe("npm"); }); - test('should default to git-tag in pkg', async () => { + test("should default to git-tag in pkg", async () => { // @ts-ignore process.pkg = true; search.mockReturnValueOnce({ config: defaults }); @@ -121,23 +121,23 @@ describe('Auto', () => { await auto.loadConfig(); // @ts-ignore - expect(loadPlugin.mock.calls[1][0][0]).toBe('git-tag'); + expect(loadPlugin.mock.calls[1][0][0]).toBe("git-tag"); // @ts-ignore process.pkg = undefined; }); - test('should throw if now GH_TOKEN set', async () => { + test("should throw if now GH_TOKEN set", async () => { const auto = new Auto(); auto.logger = dummyLog(); process.env.GH_TOKEN = undefined; await expect(auto.loadConfig()).rejects.toBeInstanceOf(Error); - process.env.GH_TOKEN = 'XXXX'; + process.env.GH_TOKEN = "XXXX"; }); - test('should extend config', async () => { - search.mockReturnValueOnce({ config: { ...defaults, extends: '@artsy' } }); - importMock.mockImplementation(path => - path === '@artsy/auto-config/package.json' + test("should extend config", async () => { + search.mockReturnValueOnce({ config: { ...defaults, extends: "@artsy" } }); + importMock.mockImplementation((path) => + path === "@artsy/auto-config/package.json" ? { auto: { onlyPublishWithReleaseLabel: true } } : undefined ); @@ -148,7 +148,7 @@ describe('Auto', () => { expect(auto.release!.config).toMatchSnapshot(); }); - test('should exit with errors in config', async () => { + test("should exit with errors in config", async () => { search.mockReturnValueOnce({ config: { name: 123 } }); process.exit = jest.fn() as any; const auto = new Auto(); @@ -157,14 +157,14 @@ describe('Auto', () => { expect(process.exit).toHaveBeenCalled(); }); - test('should extend local config', async () => { + test("should extend local config", async () => { const orig = process.cwd; - process.cwd = () => '/foo/'; + process.cwd = () => "/foo/"; search.mockReturnValueOnce({ - config: { ...defaults, extends: './fake.json' } + config: { ...defaults, extends: "./fake.json" }, }); - importMock.mockImplementation(path => - path === '/foo/fake.json' ? { noVersionPrefix: true } : undefined + importMock.mockImplementation((path) => + path === "/foo/fake.json" ? { noVersionPrefix: true } : undefined ); const auto = new Auto(); @@ -174,141 +174,143 @@ describe('Auto', () => { process.cwd = orig; }); - test('should use labels from config config', async () => { + test("should use labels from config config", async () => { search.mockReturnValueOnce({ - config: { ...defaults, labels } + config: { ...defaults, labels }, }); const auto = new Auto(); auto.logger = dummyLog(); await auto.loadConfig(); expect([...auto.semVerLabels!.values()]).toStrictEqual([ - ['Version: Major'], - ['Version: Patch'], - ['Version: Minor'], - ['skip-release'], - ['release'], - ['internal', 'documentation'] + ["Version: Major"], + ["Version: Patch"], + ["Version: Minor"], + ["skip-release"], + ["release"], + ["internal", "documentation"], ]); }); - test('should be able to add label as string', async () => { + test("should be able to add label as string", async () => { search.mockReturnValueOnce({ config: { ...defaults, labels: [ { - name: 'feature', - releaseType: SEMVER.minor - } - ] - } + name: "feature", + releaseType: SEMVER.minor, + }, + ], + }, }); const auto = new Auto(); auto.logger = dummyLog(); await auto.loadConfig(); - expect(auto.config!.labels.find(l => l.name === 'feature')).toStrictEqual({ - description: 'Increment the minor version when merged', - name: 'feature', - changelogTitle: '🚀 Enhancement', - releaseType: SEMVER.minor - }); + expect(auto.config!.labels.find((l) => l.name === "feature")).toStrictEqual( + { + description: "Increment the minor version when merged", + name: "feature", + changelogTitle: "🚀 Enhancement", + releaseType: SEMVER.minor, + } + ); }); - test('should be able to omit properties from label definition', async () => { + test("should be able to omit properties from label definition", async () => { search.mockReturnValueOnce({ config: { ...defaults, - labels: [{ name: 'minor', description: 'This is a test' }] - } + labels: [{ name: "minor", description: "This is a test" }], + }, }); const auto = new Auto(); auto.logger = dummyLog(); await auto.loadConfig(); expect( - auto.config!.labels.find(l => l.description === 'This is a test') + auto.config!.labels.find((l) => l.description === "This is a test") ).toStrictEqual({ - description: 'This is a test', - name: 'minor', - changelogTitle: '🚀 Enhancement', - releaseType: SEMVER.minor + description: "This is a test", + name: "minor", + changelogTitle: "🚀 Enhancement", + releaseType: SEMVER.minor, }); }); - test('arbitrary labels should be able to omit name', async () => { + test("arbitrary labels should be able to omit name", async () => { search.mockReturnValueOnce({ config: { ...defaults, labels: [ { - name: 'fooBar', - description: 'This is a test' - } - ] - } + name: "fooBar", + description: "This is a test", + }, + ], + }, }); const auto = new Auto(); auto.logger = dummyLog(); await auto.loadConfig(); - expect(auto.config!.labels.find(l => l.name === 'fooBar')).toStrictEqual({ - description: 'This is a test', - name: 'fooBar' + expect(auto.config!.labels.find((l) => l.name === "fooBar")).toStrictEqual({ + description: "This is a test", + name: "fooBar", }); }); - describe('createLabels', () => { - test('should throw when not initialized', async () => { + describe("createLabels", () => { + test("should throw when not initialized", async () => { search.mockReturnValueOnce({ - config: { ...defaults, labels } + config: { ...defaults, labels }, }); const auto = new Auto(); auto.logger = dummyLog(); await expect(auto.createLabels()).rejects.not.toBeUndefined(); }); - test('should create the labels', async () => { + test("should create the labels", async () => { search.mockReturnValueOnce({ - config: { ...defaults, labels } + config: { ...defaults, labels }, }); const auto = new Auto(); auto.logger = dummyLog(); await auto.loadConfig(); - jest.spyOn(auto.release!, 'addLabelsToProject').mockImplementation(); + jest.spyOn(auto.release!, "addLabelsToProject").mockImplementation(); await auto.createLabels(); expect(auto.release!.addLabelsToProject).toMatchSnapshot(); }); }); - describe('label', () => { - test('should throw when not initialized', async () => { + describe("label", () => { + test("should throw when not initialized", async () => { search.mockReturnValueOnce({ - config: { ...defaults, labels } + config: { ...defaults, labels }, }); const auto = new Auto(); auto.logger = dummyLog(); await expect(auto.label({ pr: 13 })).rejects.not.toBeUndefined(); }); - test('should get labels', async () => { + test("should get labels", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); const getLabels = jest.fn(); auto.git!.getLabels = getLabels; - getLabels.mockReturnValueOnce(['foo']); - jest.spyOn(console, 'log').mockImplementation(); + getLabels.mockReturnValueOnce(["foo"]); + jest.spyOn(console, "log").mockImplementation(); await auto.label({ pr: 13 }); - expect(console.log).toHaveBeenCalledWith('foo'); + expect(console.log).toHaveBeenCalledWith("foo"); }); - test('should get labels for last merged PR', async () => { + test("should get labels for last merged PR", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -318,21 +320,21 @@ describe('Auto', () => { auto.git!.getPullRequests = getPullRequests; getPullRequests.mockReturnValueOnce([ { - merged_at: '2019-01-08T03:45:33.000Z', - labels: [{ name: 'wubbalublub' }] + merged_at: "2019-01-08T03:45:33.000Z", + labels: [{ name: "wubbalublub" }], }, { - merged_at: '2019-01-10T03:45:33.000Z', - labels: [{ name: 'foo' }, { name: 'bar' }] - } + merged_at: "2019-01-10T03:45:33.000Z", + labels: [{ name: "foo" }, { name: "bar" }], + }, ]); - jest.spyOn(console, 'log').mockImplementation(); + jest.spyOn(console, "log").mockImplementation(); await auto.label(); - expect(console.log).toHaveBeenCalledWith('foo\nbar'); + expect(console.log).toHaveBeenCalledWith("foo\nbar"); }); - test('should do nothing when no last merge found', async () => { + test("should do nothing when no last merge found", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -341,17 +343,14 @@ describe('Auto', () => { const getPullRequests = jest.fn(); auto.git!.getPullRequests = getPullRequests; getPullRequests.mockReturnValueOnce([]); - jest - .spyOn(console, 'log') - .mockImplementation() - .mockReset(); + jest.spyOn(console, "log").mockImplementation().mockReset(); await auto.label(); expect(console.log).not.toHaveBeenCalled(); }); }); - describe('pr', () => { + describe("pr", () => { let createStatus: jest.Mock; beforeEach(() => { @@ -359,20 +358,20 @@ describe('Auto', () => { }); const required: IPRStatusOptions = { - url: 'https://google.com', - state: 'pending', - description: 'foo', - context: 'bar' + url: "https://google.com", + state: "pending", + description: "foo", + context: "bar", }; - test('should throw when not initialized', async () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await expect(auto.prStatus(required)).rejects.not.toBeUndefined(); }); - test('should catch exceptions when status fails to post', async () => { + test("should catch exceptions when status fails to post", async () => { const auto = new Auto(defaults); process.exit = jest.fn() as any; @@ -380,37 +379,37 @@ describe('Auto', () => { await auto.loadConfig(); auto.git!.createStatus = createStatus; - auto.prStatus({ ...required, sha: '1234' }); + auto.prStatus({ ...required, sha: "1234" }); expect(process.exit).toHaveBeenCalled(); expect(createStatus).toHaveBeenCalled(); }); - test('should do nothing ', async () => { + test("should do nothing ", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); - await auto.prStatus({ ...required, sha: '1234', dryRun: true }); + await auto.prStatus({ ...required, sha: "1234", dryRun: true }); expect(createStatus).not.toHaveBeenCalled(); }); - test('should use provided SHA', async () => { + test("should use provided SHA", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); auto.git!.createStatus = createStatus; - await auto.prStatus({ ...required, sha: '1234' }); + await auto.prStatus({ ...required, sha: "1234" }); expect(createStatus).toHaveBeenCalledWith( expect.objectContaining({ - sha: '1234' + sha: "1234", }) ); }); - test('should use HEAD SHA', async () => { + test("should use HEAD SHA", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -419,17 +418,17 @@ describe('Auto', () => { const getSha = jest.fn(); auto.git!.getSha = getSha; - getSha.mockReturnValueOnce('abc'); + getSha.mockReturnValueOnce("abc"); await auto.prStatus({ ...required }); expect(createStatus).toHaveBeenCalledWith( expect.objectContaining({ - sha: 'abc' + sha: "abc", }) ); }); - test('should use lookup SHA for PR', async () => { + test("should use lookup SHA for PR", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -438,18 +437,18 @@ describe('Auto', () => { const getPullRequest = jest.fn(); auto.git!.getPullRequest = getPullRequest; - getPullRequest.mockReturnValueOnce({ data: { head: { sha: 'deep' } } }); + getPullRequest.mockReturnValueOnce({ data: { head: { sha: "deep" } } }); await auto.prStatus({ ...required, pr: 14 }); expect(createStatus).toHaveBeenCalledWith( expect.objectContaining({ - sha: 'deep' + sha: "deep", }) ); }); }); - describe('pr-check', () => { + describe("pr-check", () => { jest.setTimeout(10 * 1000); let createStatus: jest.Mock; @@ -458,10 +457,10 @@ describe('Auto', () => { }); const required = { - url: 'https://google.com' + url: "https://google.com", }; - test('should throw when not initialized', async () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -470,7 +469,7 @@ describe('Auto', () => { ).rejects.not.toBeUndefined(); }); - test('should do nothing with dryRun', async () => { + test("should do nothing with dryRun", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -479,7 +478,7 @@ describe('Auto', () => { expect(createStatus).not.toHaveBeenCalled(); }); - test('should catch errors', async () => { + test("should catch errors", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -488,12 +487,12 @@ describe('Auto', () => { await auto.prCheck({ ...required, pr: 13 }); expect(createStatus).toHaveBeenCalledWith( expect.objectContaining({ - state: 'error' + state: "error", }) ); }); - test('should catch status errors', async () => { + test("should catch status errors", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -506,7 +505,7 @@ describe('Auto', () => { expect(createStatus).toHaveBeenCalled(); }); - test('should error with no label', async () => { + test("should error with no label", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -515,7 +514,7 @@ describe('Auto', () => { const getPullRequest = jest.fn(); auto.git!.getPullRequest = getPullRequest; - getPullRequest.mockReturnValueOnce({ data: { head: { sha: 'sha' } } }); + getPullRequest.mockReturnValueOnce({ data: { head: { sha: "sha" } } }); const getLabels = jest.fn(); auto.git!.getLabels = getLabels; @@ -524,12 +523,12 @@ describe('Auto', () => { await auto.prCheck({ ...required, pr: 13 }); expect(createStatus).toHaveBeenCalledWith( expect.objectContaining({ - description: 'No semver label!' + description: "No semver label!", }) ); }); - test('should pass with semver label', async () => { + test("should pass with semver label", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -538,21 +537,21 @@ describe('Auto', () => { const getPullRequest = jest.fn(); auto.git!.getPullRequest = getPullRequest; - getPullRequest.mockReturnValueOnce({ data: { head: { sha: 'sha' } } }); + getPullRequest.mockReturnValueOnce({ data: { head: { sha: "sha" } } }); const getLabels = jest.fn(); auto.git!.getLabels = getLabels; - getLabels.mockReturnValueOnce(['major']); + getLabels.mockReturnValueOnce(["major"]); await auto.prCheck({ ...required, pr: 13 }); expect(createStatus).toHaveBeenCalledWith( expect.objectContaining({ - description: 'CI - major' + description: "CI - major", }) ); }); - test('should pass with skip release label', async () => { + test("should pass with skip release label", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -561,21 +560,21 @@ describe('Auto', () => { const getPullRequest = jest.fn(); auto.git!.getPullRequest = getPullRequest; - getPullRequest.mockReturnValueOnce({ data: { head: { sha: 'sha' } } }); + getPullRequest.mockReturnValueOnce({ data: { head: { sha: "sha" } } }); const getLabels = jest.fn(); auto.git!.getLabels = getLabels; - getLabels.mockReturnValueOnce(['major', 'skip-release']); + getLabels.mockReturnValueOnce(["major", "skip-release"]); await auto.prCheck({ ...required, pr: 13 }); expect(createStatus).toHaveBeenCalledWith( expect.objectContaining({ - description: 'PR will not create a release' + description: "PR will not create a release", }) ); }); - test('should pass with release label', async () => { + test("should pass with release label", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -584,32 +583,32 @@ describe('Auto', () => { const getPullRequest = jest.fn(); auto.git!.getPullRequest = getPullRequest; - getPullRequest.mockReturnValueOnce({ data: { head: { sha: 'sha' } } }); + getPullRequest.mockReturnValueOnce({ data: { head: { sha: "sha" } } }); const getLabels = jest.fn(); auto.git!.getLabels = getLabels; - getLabels.mockReturnValueOnce(['major', 'release']); + getLabels.mockReturnValueOnce(["major", "release"]); await auto.prCheck({ ...required, pr: 13 }); expect(createStatus).toHaveBeenCalledWith( expect.objectContaining({ - description: 'PR will create release once merged - major' + description: "PR will create release once merged - major", }) ); }); }); - describe('comment', () => { - test('should throw when not initialized', async () => { + describe("comment", () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await expect( - auto.comment({ pr: 10, message: 'foo' }) + auto.comment({ pr: 10, message: "foo" }) ).rejects.not.toBeUndefined(); }); - test('should make a comment', async () => { + test("should make a comment", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -617,11 +616,11 @@ describe('Auto', () => { const createComment = jest.fn(); auto.git!.createComment = createComment; - await auto.comment({ pr: 10, message: 'foo' }); + await auto.comment({ pr: 10, message: "foo" }); expect(createComment).toHaveBeenCalled(); }); - test('should delete a comment', async () => { + test("should delete a comment", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -633,7 +632,7 @@ describe('Auto', () => { expect(deleteComment).toHaveBeenCalled(); }); - test('should edit a comment', async () => { + test("should edit a comment", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -641,11 +640,11 @@ describe('Auto', () => { const editComment = jest.fn(); auto.git!.editComment = editComment; - await auto.comment({ pr: 10, message: 'foo', edit: true }); + await auto.comment({ pr: 10, message: "foo", edit: true }); expect(editComment).toHaveBeenCalled(); }); - test('should not delete a comment in dry run mode', async () => { + test("should not delete a comment in dry run mode", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -655,7 +654,7 @@ describe('Auto', () => { const editComment = jest.fn(); auto.git!.editComment = editComment; - await auto.comment({ pr: 10, message: 'foo bar', dryRun: true }); + await auto.comment({ pr: 10, message: "foo bar", dryRun: true }); expect(deleteComment).not.toHaveBeenCalled(); await auto.comment({ pr: 10, delete: true, dryRun: true }); @@ -663,26 +662,26 @@ describe('Auto', () => { await auto.comment({ pr: 10, - message: 'foo bar', + message: "foo bar", edit: true, - dryRun: true + dryRun: true, }); expect(deleteComment).not.toHaveBeenCalled(); expect(editComment).not.toHaveBeenCalled(); }); }); - describe('prBody', () => { - test('should throw when not initialized', async () => { + describe("prBody", () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await expect( - auto.prBody({ pr: 10, message: 'foo' }) + auto.prBody({ pr: 10, message: "foo" }) ).rejects.not.toBeUndefined(); }); - test('should make a pr body update', async () => { + test("should make a pr body update", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -690,11 +689,11 @@ describe('Auto', () => { const addToPrBody = jest.fn(); auto.git!.addToPrBody = addToPrBody; - await auto.prBody({ pr: 10, message: 'foo' }); + await auto.prBody({ pr: 10, message: "foo" }); expect(addToPrBody).toHaveBeenCalled(); }); - test('should delete old pr body update', async () => { + test("should delete old pr body update", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -703,10 +702,10 @@ describe('Auto', () => { auto.git!.addToPrBody = addToPrBody; await auto.prBody({ pr: 10, delete: true }); - expect(addToPrBody).toHaveBeenCalledWith('', 10, 'default'); + expect(addToPrBody).toHaveBeenCalledWith("", 10, "default"); }); - test('should not update pr body a dry run mode', async () => { + test("should not update pr body a dry run mode", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); @@ -714,7 +713,7 @@ describe('Auto', () => { const addToPrBody = jest.fn(); auto.git!.addToPrBody = addToPrBody; - await auto.prBody({ pr: 10, message: 'foo bar', dryRun: true }); + await auto.prBody({ pr: 10, message: "foo bar", dryRun: true }); expect(addToPrBody).not.toHaveBeenCalled(); await auto.prBody({ pr: 10, delete: true, dryRun: true }); @@ -722,59 +721,59 @@ describe('Auto', () => { }); }); - describe('version', () => { - test('should throw when not initialized', async () => { + describe("version", () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await expect(auto.version()).rejects.not.toBeUndefined(); }); - test('should calculate version with default options', async () => { + test("should calculate version with default options", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); jest - .spyOn(auto.git!, 'getLatestRelease') - .mockImplementation(() => Promise.resolve('v1.2.3')); + .spyOn(auto.git!, "getLatestRelease") + .mockImplementation(() => Promise.resolve("v1.2.3")); jest - .spyOn(auto.release!, 'getSemverBump') + .spyOn(auto.release!, "getSemverBump") .mockImplementation(() => Promise.resolve(SEMVER.patch)); - jest.spyOn(console, 'log').mockImplementation(); + jest.spyOn(console, "log").mockImplementation(); await auto.version(); - expect(auto.release!.getSemverBump).toHaveBeenCalledWith('v1.2.3'); - expect(console.log).toHaveBeenCalledWith('patch'); + expect(auto.release!.getSemverBump).toHaveBeenCalledWith("v1.2.3"); + expect(console.log).toHaveBeenCalledWith("patch"); }); - test('should calculate version with from option', async () => { + test("should calculate version with from option", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await auto.loadConfig(); jest - .spyOn(auto.git!, 'getLatestRelease') - .mockImplementation(() => Promise.resolve('v1.2.3')); + .spyOn(auto.git!, "getLatestRelease") + .mockImplementation(() => Promise.resolve("v1.2.3")); jest - .spyOn(auto.release!, 'getSemverBump') + .spyOn(auto.release!, "getSemverBump") .mockImplementation(() => Promise.resolve(SEMVER.minor)); - jest.spyOn(console, 'log').mockImplementation(); + jest.spyOn(console, "log").mockImplementation(); - await auto.version({ from: 'v1.1.0' }); - expect(auto.release!.getSemverBump).toHaveBeenCalledWith('v1.1.0'); - expect(console.log).toHaveBeenCalledWith('minor'); + await auto.version({ from: "v1.1.0" }); + expect(auto.release!.getSemverBump).toHaveBeenCalledWith("v1.1.0"); + expect(console.log).toHaveBeenCalledWith("minor"); }); }); - describe('changelog', () => { - test('should throw when not initialized', async () => { + describe("changelog", () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); await expect(auto.changelog()).rejects.not.toBeUndefined(); }); - test('should do nothing on a dryRun', async () => { + test("should do nothing on a dryRun", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -782,37 +781,37 @@ describe('Auto', () => { const addToChangelog = jest.fn(); auto.release!.addToChangelog = addToChangelog; - jest.spyOn(auto.release!, 'generateReleaseNotes').mockImplementation(); + jest.spyOn(auto.release!, "generateReleaseNotes").mockImplementation(); - await auto.changelog({ from: 'v1.0.0', dryRun: true }); + await auto.changelog({ from: "v1.0.0", dryRun: true }); expect(addToChangelog).not.toHaveBeenCalled(); }); - test('should skip getRepository hook if passed in via cli', async () => { - process.env.GH_TOKEN = 'XXXX'; + test("should skip getRepository hook if passed in via cli", async () => { + process.env.GH_TOKEN = "XXXX"; const auto = new Auto({ - repo: 'test', - owner: 'adierkens' + repo: "test", + owner: "adierkens", }); auto.logger = dummyLog(); const hookFn = jest.fn(); - auto.hooks.getRepository.tap('test', hookFn); + auto.hooks.getRepository.tap("test", hookFn); await auto.loadConfig(); await auto.prStatus({ - url: 'foo.bar', - state: 'pending', - description: 'Waiting for stuffs', - context: 'tests', - dryRun: true + url: "foo.bar", + state: "pending", + description: "Waiting for stuffs", + context: "tests", + dryRun: true, }); expect(hookFn).not.toHaveBeenCalled(); }); }); - describe('release', () => { - test('should exit when no tags found', async () => { + describe("release", () => { + test("should exit when no tags found", async () => { const auto = new Auto({ ...defaults, plugins: [] }); const exit = jest.fn(); @@ -822,9 +821,9 @@ describe('Auto', () => { auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve(''); + auto.git!.getLatestRelease = () => Promise.resolve(""); auto.git!.getLatestTagInBranch = () => - Promise.reject(new Error('No names found, cannot describe anything.')); + Promise.reject(new Error("No names found, cannot describe anything.")); await auto.runRelease(); expect(exit).toHaveBeenCalled(); @@ -834,177 +833,177 @@ describe('Auto', () => { const auto = new Auto({ ...defaults, plugins: [] }); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - jest.spyOn(auto.release!, 'generateReleaseNotes').mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + jest.spyOn(auto.release!, "generateReleaseNotes").mockImplementation(); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); - auto.hooks.getPreviousVersion.tap('test', () => '1.2.3'); + auto.hooks.getPreviousVersion.tap("test", () => "1.2.3"); const afterRelease = jest.fn(); - auto.hooks.afterRelease.tap('test', afterRelease); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.afterRelease.tap("test", afterRelease); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.runRelease(); expect(afterRelease).not.toHaveBeenCalled(); }); - test('should publish with default options', async () => { + test("should publish with default options", async () => { const auto = new Auto({ ...defaults, plugins: [] }); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); - jest.spyOn(auto.git!, 'publish').mockReturnValueOnce({} as any); + jest.spyOn(auto.git!, "publish").mockReturnValueOnce({} as any); jest - .spyOn(auto.release!, 'generateReleaseNotes') - .mockImplementation(() => Promise.resolve('releaseNotes')); + .spyOn(auto.release!, "generateReleaseNotes") + .mockImplementation(() => Promise.resolve("releaseNotes")); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); - auto.hooks.getPreviousVersion.tap('test', () => '1.2.4'); + auto.hooks.getPreviousVersion.tap("test", () => "1.2.4"); const afterRelease = jest.fn(); - auto.hooks.afterRelease.tap('test', afterRelease); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.afterRelease.tap("test", afterRelease); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.runRelease(); expect(auto.release!.generateReleaseNotes).toHaveBeenCalledWith( - 'v1.2.3', + "v1.2.3", undefined, undefined ); expect(auto.git!.publish).toHaveBeenCalledWith( - 'releaseNotes', - 'v1.2.4', + "releaseNotes", + "v1.2.4", false ); expect(afterRelease).toHaveBeenCalledWith( expect.objectContaining({ - lastRelease: 'v1.2.3', - newVersion: 'v1.2.4' + lastRelease: "v1.2.3", + newVersion: "v1.2.4", }) ); }); - test('should a prerelease', async () => { + test("should a prerelease", async () => { const auto = new Auto({ ...defaults, plugins: [] }); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getPreviousTagInBranch = () => Promise.resolve('1.2.3'); - auto.git!.getLatestTagInBranch = () => Promise.resolve('1.2.4'); + auto.git!.getPreviousTagInBranch = () => Promise.resolve("1.2.3"); + auto.git!.getLatestTagInBranch = () => Promise.resolve("1.2.4"); - jest.spyOn(auto.git!, 'publish').mockReturnValueOnce({} as any); + jest.spyOn(auto.git!, "publish").mockReturnValueOnce({} as any); jest - .spyOn(auto.release!, 'generateReleaseNotes') - .mockImplementation(() => Promise.resolve('releaseNotes')); + .spyOn(auto.release!, "generateReleaseNotes") + .mockImplementation(() => Promise.resolve("releaseNotes")); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); - auto.hooks.getPreviousVersion.tap('test', () => '1.2.4'); + auto.hooks.getPreviousVersion.tap("test", () => "1.2.4"); const afterRelease = jest.fn(); - auto.hooks.afterRelease.tap('test', afterRelease); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.afterRelease.tap("test", afterRelease); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.runRelease({ prerelease: true }); expect(auto.release!.generateReleaseNotes).toHaveBeenCalledWith( - 'v1.2.3', + "v1.2.3", undefined, undefined ); expect(auto.git!.publish).toHaveBeenCalledWith( - 'releaseNotes', - 'v1.2.4', + "releaseNotes", + "v1.2.4", true ); expect(afterRelease).toHaveBeenCalledWith( expect.objectContaining({ - lastRelease: 'v1.2.3', - newVersion: 'v1.2.4' + lastRelease: "v1.2.3", + newVersion: "v1.2.4", }) ); }); - test('should publish with lastRelease using from option', async () => { + test("should publish with lastRelease using from option", async () => { const auto = new Auto({ ...defaults, plugins: [] }); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); - jest.spyOn(auto.git!, 'publish').mockReturnValueOnce({} as any); + jest.spyOn(auto.git!, "publish").mockReturnValueOnce({} as any); jest - .spyOn(auto.release!, 'generateReleaseNotes') - .mockImplementation(() => Promise.resolve('releaseNotes')); + .spyOn(auto.release!, "generateReleaseNotes") + .mockImplementation(() => Promise.resolve("releaseNotes")); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); - auto.hooks.getPreviousVersion.tap('test', () => '1.2.4'); + auto.hooks.getPreviousVersion.tap("test", () => "1.2.4"); const afterRelease = jest.fn(); - auto.hooks.afterRelease.tap('test', afterRelease); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.afterRelease.tap("test", afterRelease); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.runRelease({ - from: 'v1.2.0' + from: "v1.2.0", }); expect(auto.release!.generateReleaseNotes).toHaveBeenCalledWith( - 'v1.2.0', + "v1.2.0", undefined, undefined ); expect(auto.git!.publish).toHaveBeenCalledWith( - 'releaseNotes', - 'v1.2.4', + "releaseNotes", + "v1.2.4", false ); expect(afterRelease).toHaveBeenCalledWith( expect.objectContaining({ - lastRelease: 'v1.2.0', - newVersion: 'v1.2.4' + lastRelease: "v1.2.0", + newVersion: "v1.2.4", }) ); }); - test('should publish with newVersion using useVersion option', async () => { + test("should publish with newVersion using useVersion option", async () => { const auto = new Auto({ ...defaults, plugins: [] }); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); - jest.spyOn(auto.git!, 'publish').mockReturnValueOnce({} as any); + jest.spyOn(auto.git!, "publish").mockReturnValueOnce({} as any); jest - .spyOn(auto.release!, 'generateReleaseNotes') - .mockImplementation(() => Promise.resolve('releaseNotes')); + .spyOn(auto.release!, "generateReleaseNotes") + .mockImplementation(() => Promise.resolve("releaseNotes")); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); - auto.hooks.getPreviousVersion.tap('test', () => '1.2.4'); + auto.hooks.getPreviousVersion.tap("test", () => "1.2.4"); const afterRelease = jest.fn(); - auto.hooks.afterRelease.tap('test', afterRelease); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.afterRelease.tap("test", afterRelease); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.runRelease({ - useVersion: 'v1.3.0' + useVersion: "v1.3.0", }); expect(auto.release!.generateReleaseNotes).toHaveBeenCalledWith( - 'v1.2.3', + "v1.2.3", undefined, undefined ); expect(auto.git!.publish).toHaveBeenCalledWith( - 'releaseNotes', - 'v1.3.0', + "releaseNotes", + "v1.3.0", false ); expect(afterRelease).toHaveBeenCalledWith( expect.objectContaining({ - lastRelease: 'v1.2.3', - newVersion: 'v1.3.0' + lastRelease: "v1.2.3", + newVersion: "v1.3.0", }) ); }); }); - describe('canary', () => { - test('should throw when not initialized', async () => { + describe("canary", () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); // @ts-ignore auto.checkClean = () => Promise.resolve(true); @@ -1013,47 +1012,47 @@ describe('Auto', () => { await expect(auto.canary()).rejects.not.toBeUndefined(); }); - test('does not call canary hook in dry-run', async () => { + test("does not call canary hook in dry-run", async () => { const auto = new Auto(defaults); // @ts-ignore auto.checkClean = () => Promise.resolve(true); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const canary = jest.fn(); - auto.hooks.canary.tap('test', canary); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.canary.tap("test", canary); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.canary({ pr: 123, build: 1, dryRun: true }); expect(canary).not.toHaveBeenCalled(); }); - test('calls the canary hook with the pr info', async () => { + test("calls the canary hook with the pr info", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - auto.git!.getSha = () => Promise.resolve('abc'); - jest.spyOn(auto.git!, 'addToPrBody').mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + auto.git!.getSha = () => Promise.resolve("abc"); + jest.spyOn(auto.git!, "addToPrBody").mockImplementation(); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const canary = jest.fn(); - canary.mockReturnValueOnce('1.2.3'); - auto.hooks.canary.tap('test', canary); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + canary.mockReturnValueOnce("1.2.3"); + auto.hooks.canary.tap("test", canary); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.canary({ pr: 123, build: 1 }); - expect(canary).toHaveBeenCalledWith(SEMVER.patch, '.123.1'); + expect(canary).toHaveBeenCalledWith(SEMVER.patch, ".123.1"); expect(auto.git!.addToPrBody).toHaveBeenCalled(); }); - test('falls back to first commit', async () => { + test("falls back to first commit", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); @@ -1063,41 +1062,41 @@ describe('Auto', () => { throw new Error(); }; - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - auto.git!.getSha = () => Promise.resolve('abc'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + auto.git!.getSha = () => Promise.resolve("abc"); auto.release!.getCommits = () => Promise.resolve([]); - auto.git!.getFirstCommit = () => Promise.resolve('abcd'); - jest.spyOn(auto.git!, 'addToPrBody').mockImplementation(); + auto.git!.getFirstCommit = () => Promise.resolve("abcd"); + jest.spyOn(auto.git!, "addToPrBody").mockImplementation(); jest - .spyOn(auto.release!, 'getCommitsInRelease') + .spyOn(auto.release!, "getCommitsInRelease") .mockImplementation() - .mockReturnValue(Promise.resolve([makeCommitFromMsg('Test Commit')])); + .mockReturnValue(Promise.resolve([makeCommitFromMsg("Test Commit")])); const canary = jest.fn(); - canary.mockReturnValue('abcd'); - auto.hooks.canary.tap('test', canary); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + canary.mockReturnValue("abcd"); + auto.hooks.canary.tap("test", canary); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.canary({ pr: 123, build: 1 }); - expect(auto.release!.getCommits).toHaveBeenCalledWith('abcd'); + expect(auto.release!.getCommits).toHaveBeenCalledWith("abcd"); }); - test('adds sha if no pr or build number is found', async () => { + test("adds sha if no pr or build number is found", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - auto.git!.getSha = () => Promise.resolve('abc'); - jest.spyOn(auto.git!, 'addToPrBody').mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + auto.git!.getSha = () => Promise.resolve("abc"); + jest.spyOn(auto.git!, "addToPrBody").mockImplementation(); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const canary = jest.fn(); - auto.hooks.canary.tap('test', canary); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.canary.tap("test", canary); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.canary(); - expect(canary).toHaveBeenCalledWith(SEMVER.patch, '.abc'); + expect(canary).toHaveBeenCalledWith(SEMVER.patch, ".abc"); }); test("doesn't comment if there is an error", async () => { @@ -1106,37 +1105,37 @@ describe('Auto', () => { auto.checkClean = () => Promise.resolve(true); auto.logger = dummyLog(); await auto.loadConfig(); - jest.spyOn(auto, 'prBody').mockImplementation(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - auto.git!.getSha = () => Promise.resolve('abc'); + jest.spyOn(auto, "prBody").mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + auto.git!.getSha = () => Promise.resolve("abc"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const canary = jest.fn(); - canary.mockReturnValue({ error: 'ooops' }); - auto.hooks.canary.tap('test', canary); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + canary.mockReturnValue({ error: "ooops" }); + auto.hooks.canary.tap("test", canary); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.canary({ pr: 123, build: 1 }); expect(auto.prBody).not.toHaveBeenCalled(); }); - test('defaults to sha when run locally', async () => { + test("defaults to sha when run locally", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getSha = () => Promise.resolve('abcd'); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getSha = () => Promise.resolve("abcd"); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const canary = jest.fn(); - auto.hooks.canary.tap('test', canary); + auto.hooks.canary.tap("test", canary); await auto.canary(); - expect(canary).toHaveBeenCalledWith(SEMVER.patch, '.abcd'); + expect(canary).toHaveBeenCalledWith(SEMVER.patch, ".abcd"); }); test('should not publish when is present "skip-release" label', async () => { @@ -1146,24 +1145,24 @@ describe('Auto', () => { auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getSha = () => Promise.resolve('abcd'); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); + auto.git!.getSha = () => Promise.resolve("abcd"); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); auto.release!.getCommitsInRelease = () => Promise.resolve([ - makeCommitFromMsg('Test Commit', { - labels: ['skip-release'] - }) + makeCommitFromMsg("Test Commit", { + labels: ["skip-release"], + }), ]); const canary = jest.fn(); - auto.hooks.canary.tap('test', canary); + auto.hooks.canary.tap("test", canary); await auto.canary(); expect(canary).not.toHaveBeenCalled(); }); }); - describe('next', () => { - test('should throw when not initialized', async () => { + describe("next", () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); // @ts-ignore auto.checkClean = () => Promise.resolve(true); @@ -1172,7 +1171,7 @@ describe('Auto', () => { await expect(auto.next({})).rejects.not.toBeUndefined(); }); - test('does not call next hook in dry-run', async () => { + test("does not call next hook in dry-run", async () => { const auto = new Auto(defaults); // @ts-ignore @@ -1180,21 +1179,21 @@ describe('Auto', () => { auto.logger = dummyLog(); await auto.loadConfig(); auto.git!.getProject = () => Promise.resolve({ data: {} } as any); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - auto.release!.generateReleaseNotes = () => Promise.resolve('notes'); - auto.git!.getLatestTagInBranch = () => Promise.resolve('1.2.3'); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + auto.release!.generateReleaseNotes = () => Promise.resolve("notes"); + auto.git!.getLatestTagInBranch = () => Promise.resolve("1.2.3"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const next = jest.fn(); - auto.hooks.next.tap('test', next); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.next.tap("test", next); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.next({ dryRun: true }); expect(next).not.toHaveBeenCalled(); }); - test('calls the next hook with the release info', async () => { + test("calls the next hook with the release info", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore @@ -1202,24 +1201,24 @@ describe('Auto', () => { auto.logger = dummyLog(); await auto.loadConfig(); auto.git!.publish = () => Promise.resolve({} as any); - auto.git!.getLatestTagInBranch = () => Promise.resolve('1.2.3'); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - auto.release!.generateReleaseNotes = () => Promise.resolve('notes'); + auto.git!.getLatestTagInBranch = () => Promise.resolve("1.2.3"); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + auto.release!.generateReleaseNotes = () => Promise.resolve("notes"); auto.release!.getCommitsInRelease = () => - Promise.resolve([makeCommitFromMsg('Test Commit')]); + Promise.resolve([makeCommitFromMsg("Test Commit")]); const afterRelease = jest.fn(); - auto.hooks.afterRelease.tap('test', afterRelease); - auto.hooks.next.tap('test', () => ['1.2.4-next.0']); - jest.spyOn(auto.release!, 'getCommits').mockImplementation(); + auto.hooks.afterRelease.tap("test", afterRelease); + auto.hooks.next.tap("test", () => ["1.2.4-next.0"]); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); await auto.next({}); expect(afterRelease).toHaveBeenCalled(); }); }); - describe('shipit', () => { - test('should throw when not initialized', async () => { + describe("shipit", () => { + test("should throw when not initialized", async () => { const auto = new Auto(defaults); // @ts-ignore auto.checkClean = () => Promise.resolve(true); @@ -1228,7 +1227,7 @@ describe('Auto', () => { await expect(auto.shipit()).rejects.not.toBeUndefined(); }); - test('should not publish when no latest version found', async () => { + test("should not publish when no latest version found", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); @@ -1236,44 +1235,44 @@ describe('Auto', () => { await auto.loadConfig(); auto.release!.getCommitsInRelease = () => Promise.resolve([]); - auto.git!.getLatestRelease = () => Promise.resolve(''); + auto.git!.getLatestRelease = () => Promise.resolve(""); auto.release!.getSemverBump = () => Promise.resolve(SEMVER.noVersion); const afterShipIt = jest.fn(); - auto.hooks.afterShipIt.tap('test', afterShipIt); + auto.hooks.afterShipIt.tap("test", afterShipIt); await auto.shipit(); expect(afterShipIt).not.toHaveBeenCalled(); }); - test('should publish to latest on base branch', async () => { + test("should publish to latest on base branch", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); auto.logger = dummyLog(); await auto.loadConfig(); - auto.remote = 'https://github.com/intuit/auto'; + auto.remote = "https://github.com/intuit/auto"; // @ts-ignore auto.makeChangelog = () => Promise.resolve(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - jest.spyOn(auto.git!, 'publish').mockImplementation(); - jest.spyOn(auto.release!, 'getCommitsInRelease').mockImplementation(); - jest.spyOn(auto.release!, 'generateReleaseNotes').mockImplementation(); - jest.spyOn(auto.release!, 'addToChangelog').mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + jest.spyOn(auto.git!, "publish").mockImplementation(); + jest.spyOn(auto.release!, "getCommitsInRelease").mockImplementation(); + jest.spyOn(auto.release!, "generateReleaseNotes").mockImplementation(); + jest.spyOn(auto.release!, "addToChangelog").mockImplementation(); const afterShipIt = jest.fn(); - auto.hooks.afterShipIt.tap('test', afterShipIt); + auto.hooks.afterShipIt.tap("test", afterShipIt); await auto.shipit(); expect(afterShipIt).toHaveBeenCalled(); }); - test('should not publish when behind remote', async () => { - jest.spyOn(child, 'execSync').mockImplementation(command => { - if (command.startsWith('git')) { + test("should not publish when behind remote", async () => { + jest.spyOn(child, "execSync").mockImplementation((command) => { + if (command.startsWith("git")) { throw new Error(); } - return Buffer.from(''); + return Buffer.from(""); }); const auto = new Auto({ ...defaults, plugins: [] }); @@ -1283,39 +1282,39 @@ describe('Auto', () => { // eslint-disable-next-line jest/prefer-spy-on process.exit = jest.fn(); await auto.loadConfig(); - auto.remote = 'https://github.com/intuit/auto'; + auto.remote = "https://github.com/intuit/auto"; // @ts-ignore auto.logger = dummyLog(); // @ts-ignore auto.makeChangelog = () => Promise.resolve(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - jest.spyOn(auto.git!, 'publish').mockImplementation(); - jest.spyOn(auto.release!, 'getCommitsInRelease').mockImplementation(); - jest.spyOn(auto.release!, 'generateReleaseNotes').mockImplementation(); - jest.spyOn(auto.release!, 'addToChangelog').mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + jest.spyOn(auto.git!, "publish").mockImplementation(); + jest.spyOn(auto.release!, "getCommitsInRelease").mockImplementation(); + jest.spyOn(auto.release!, "generateReleaseNotes").mockImplementation(); + jest.spyOn(auto.release!, "addToChangelog").mockImplementation(); const afterShipIt = jest.fn(); - auto.hooks.afterShipIt.tap('test', afterShipIt); + auto.hooks.afterShipIt.tap("test", afterShipIt); await auto.shipit(); expect(process.exit).toHaveBeenCalled(); }); - test('should skip publish in dry run', async () => { + test("should skip publish in dry run", async () => { const auto = new Auto({ ...defaults, plugins: [] }); // @ts-ignore auto.checkClean = () => Promise.resolve(true); auto.logger = dummyLog(); await auto.loadConfig(); - auto.git!.getLatestRelease = () => Promise.resolve('1.2.3'); - jest.spyOn(auto.git!, 'publish').mockImplementation(); - jest.spyOn(auto.release!, 'getCommitsInRelease').mockImplementation(); - jest.spyOn(auto.release!, 'generateReleaseNotes').mockImplementation(); - jest.spyOn(auto.release!, 'addToChangelog').mockImplementation(); + auto.git!.getLatestRelease = () => Promise.resolve("1.2.3"); + jest.spyOn(auto.git!, "publish").mockImplementation(); + jest.spyOn(auto.release!, "getCommitsInRelease").mockImplementation(); + jest.spyOn(auto.release!, "generateReleaseNotes").mockImplementation(); + jest.spyOn(auto.release!, "addToChangelog").mockImplementation(); const spy = jest.fn(); - auto.hooks.version.tap('test', spy); - auto.hooks.afterRelease.tap('test', spy); + auto.hooks.version.tap("test", spy); + auto.hooks.afterRelease.tap("test", spy); await auto.shipit({ dryRun: true }); expect(spy).not.toHaveBeenCalled(); @@ -1323,84 +1322,84 @@ describe('Auto', () => { }); }); -describe('hooks', () => { +describe("hooks", () => { beforeEach(() => { jest - .spyOn(child, 'execSync') + .spyOn(child, "execSync") .mockImplementation() // @ts-ignore - .mockReturnValue(''); + .mockReturnValue(""); }); - test('should be able to modifyConfig', async () => { + test("should be able to modifyConfig", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); - auto.hooks.modifyConfig.tap('test', testConfig => { + auto.hooks.modifyConfig.tap("test", (testConfig) => { testConfig.labels.push({ - name: 'released', - description: 'This issue/pull request has been released', - releaseType: 'none' + name: "released", + description: "This issue/pull request has been released", + releaseType: "none", }); return testConfig; }); await auto.loadConfig(); - expect(auto.labels?.find(l => l.name === 'released')).toStrictEqual({ - description: 'This issue/pull request has been released', - name: 'released', - releaseType: 'none' + expect(auto.labels?.find((l) => l.name === "released")).toStrictEqual({ + description: "This issue/pull request has been released", + name: "released", + releaseType: "none", }); }); - describe('logParse', () => { - test('should be able to tap parseCommit', async () => { + describe("logParse", () => { + test("should be able to tap parseCommit", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); - auto.hooks.onCreateLogParse.tap('test', logParse => { - logParse.hooks.parseCommit.tap('test parse', commit => { + auto.hooks.onCreateLogParse.tap("test", (logParse) => { + logParse.hooks.parseCommit.tap("test parse", (commit) => { commit.labels = [...auto.semVerLabels!.get(SEMVER.major)!]; return commit; }); }); await auto.loadConfig(); - auto.git!.getLatestRelease = async () => Promise.resolve('1.0.0'); + auto.git!.getLatestRelease = async () => Promise.resolve("1.0.0"); - jest.spyOn(console, 'log').mockImplementation(); + jest.spyOn(console, "log").mockImplementation(); await auto.version(); - expect(console.log).toHaveBeenCalledWith('major'); + expect(console.log).toHaveBeenCalledWith("major"); }); - test('should be able to tap omitCommit', async () => { + test("should be able to tap omitCommit", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); - auto.hooks.onCreateLogParse.tap('test', logParse => { - logParse.hooks.parseCommit.tap('test parse', commit => { + auto.hooks.onCreateLogParse.tap("test", (logParse) => { + logParse.hooks.parseCommit.tap("test parse", (commit) => { commit.labels = [...auto.semVerLabels!.get(SEMVER.major)!]; return commit; }); }); - auto.hooks.onCreateLogParse.tap('test', logParse => { - logParse.hooks.omitCommit.tap('test omit', commit => { - if (commit.labels.includes('major')) { + auto.hooks.onCreateLogParse.tap("test", (logParse) => { + logParse.hooks.omitCommit.tap("test omit", (commit) => { + if (commit.labels.includes("major")) { return true; } }); }); await auto.loadConfig(); - auto.git!.getLatestRelease = async () => Promise.resolve('1.0.0'); + auto.git!.getLatestRelease = async () => Promise.resolve("1.0.0"); - jest.spyOn(console, 'log').mockImplementation(); + jest.spyOn(console, "log").mockImplementation(); await auto.version(); - expect(console.log).toHaveBeenCalledWith('patch'); + expect(console.log).toHaveBeenCalledWith("patch"); }); }); }); diff --git a/packages/core/src/__tests__/changelog.test.ts b/packages/core/src/__tests__/changelog.test.ts index 1dcc44df0..b2f77c671 100644 --- a/packages/core/src/__tests__/changelog.test.ts +++ b/packages/core/src/__tests__/changelog.test.ts @@ -1,116 +1,116 @@ -import endent from 'endent'; -import Changelog, { IGenerateReleaseNotesOptions } from '../changelog'; -import LogParse from '../log-parse'; -import { defaultLabels } from '../release'; -import { dummyLog } from '../utils/logger'; +import endent from "endent"; +import Changelog, { IGenerateReleaseNotesOptions } from "../changelog"; +import LogParse from "../log-parse"; +import { defaultLabels } from "../release"; +import { dummyLog } from "../utils/logger"; -import makeCommitFromMsg from './make-commit-from-msg'; -import SEMVER from '../semver'; -import { getCurrentBranch } from '../utils/get-current-branch'; +import makeCommitFromMsg from "./make-commit-from-msg"; +import SEMVER from "../semver"; +import { getCurrentBranch } from "../utils/get-current-branch"; const currentBranchSpy = getCurrentBranch as jest.Mock; -jest.mock('../utils/get-current-branch'); +jest.mock("../utils/get-current-branch"); const testOptions = (): IGenerateReleaseNotesOptions => ({ - owner: 'foobar', - repo: 'auto', - baseUrl: 'https://github.custom.com/foobar/auto', + owner: "foobar", + repo: "auto", + baseUrl: "https://github.custom.com/foobar/auto", labels: [...defaultLabels], - baseBranch: 'master', - prereleaseBranches: ['next'] + baseBranch: "master", + prereleaseBranches: ["next"], }); const logParse = new LogParse(); -describe('createUserLink', () => { - test('should ', () => { +describe("createUserLink", () => { + test("should ", () => { const changelog = new Changelog(dummyLog(), { - owner: '', - repo: '', - baseUrl: 'https://github.custom.com/', + owner: "", + repo: "", + baseUrl: "https://github.custom.com/", labels: [...defaultLabels], - baseBranch: 'master', - prereleaseBranches: ['next'] + baseBranch: "master", + prereleaseBranches: ["next"], }); changelog.loadDefaultHooks(); expect( changelog.createUserLink( { - name: 'none', + name: "none", email: undefined, - username: 'invalid-email-address' + username: "invalid-email-address", }, { - hash: '1', + hash: "1", files: [], labels: [], pullRequest: { - number: 22 + number: 22, }, - authorName: 'none', - authorEmail: 'default@email.com', + authorName: "none", + authorEmail: "default@email.com", authors: [ { - name: 'none', - email: undefined - } + name: "none", + email: undefined, + }, ], - subject: '' + subject: "", } ) ).toBe(undefined); }); - test('should find email', () => { + test("should find email", () => { const changelog = new Changelog(dummyLog(), { - owner: '', - repo: '', - baseUrl: 'https://github.custom.com/', + owner: "", + repo: "", + baseUrl: "https://github.custom.com/", labels: [...defaultLabels], - baseBranch: 'master', - prereleaseBranches: ['next'] + baseBranch: "master", + prereleaseBranches: ["next"], }); changelog.loadDefaultHooks(); expect( changelog.createUserLink( { - name: 'none', - email: undefined + name: "none", + email: undefined, }, { - hash: '1', + hash: "1", files: [], labels: [], pullRequest: { - number: 22 + number: 22, }, - authorName: 'none', - authorEmail: 'default@email.com', + authorName: "none", + authorEmail: "default@email.com", authors: [ { - name: 'none', - email: undefined - } + name: "none", + email: undefined, + }, ], - subject: '' + subject: "", } ) - ).toBe('default@email.com'); + ).toBe("default@email.com"); }); }); -describe('Hooks', () => { - test('title', async () => { +describe("Hooks", () => { + test("title", async () => { const changelog = new Changelog(dummyLog(), testOptions()); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)') + makeCommitFromMsg("Some Feature (#1234)"), ]); changelog.hooks.renderChangelogTitle.tap( - 'test', + "test", (label, changelogTitles) => `:heart: ${changelogTitles[label]} :heart:` ); changelog.loadDefaultHooks(); @@ -118,19 +118,19 @@ describe('Hooks', () => { expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('author', async () => { + test("author", async () => { const changelog = new Changelog(dummyLog(), testOptions()); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)') + makeCommitFromMsg("Some Feature (#1234)"), ]); changelog.hooks.renderChangelogAuthor.tap( - 'test', + "test", (author, commit) => `:heart: ${author.name}/${commit.authorEmail} :heart:` ); changelog.hooks.renderChangelogAuthorLine.tap( - 'test', + "test", (author, user) => `:shipit: ${author.name} (${user})` ); changelog.loadDefaultHooks(); @@ -139,174 +139,176 @@ describe('Hooks', () => { }); }); -describe('generateReleaseNotes', () => { - test('should create note for PR commits', async () => { +describe("generateReleaseNotes", () => { + test("should create note for PR commits", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)', { labels: ['minor'] }) + makeCommitFromMsg("Some Feature (#1234)", { labels: ["minor"] }), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should omit authors with invalid email addresses', async () => { + test("should omit authors with invalid email addresses", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)', { labels: ['minor'] }) + makeCommitFromMsg("Some Feature (#1234)", { labels: ["minor"] }), ]); - normalized[0].authors[0].username = 'invalid-email-address'; + normalized[0].authors[0].username = "invalid-email-address"; expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should create note for PR commits without labels', async () => { + test("should create note for PR commits without labels", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)') + makeCommitFromMsg("Some Feature (#1234)"), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should create note for PR commits without labels with custom patch label', async () => { + test("should create note for PR commits without labels with custom patch label", async () => { const options = testOptions(); options.labels = [ { - name: 'Version: Patch', - changelogTitle: '🐛 Bug Fix', - description: 'N/A', - releaseType: SEMVER.patch - } + name: "Version: Patch", + changelogTitle: "🐛 Bug Fix", + description: "N/A", + releaseType: SEMVER.patch, + }, ]; const changelog = new Changelog(dummyLog(), options); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)') + makeCommitFromMsg("Some Feature (#1234)"), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should create note for PR commits with only non config labels', async () => { + test("should create note for PR commits with only non config labels", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)', { - labels: ['someOtherNonConfigLabel'] - }) + makeCommitFromMsg("Some Feature (#1234)", { + labels: ["someOtherNonConfigLabel"], + }), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should prefer section with highest releaseType for PR with multiple labels', async () => { + test("should prefer section with highest releaseType for PR with multiple labels", async () => { const options = testOptions(); options.labels = [ { - name: 'Internal', - changelogTitle: 'Internal Section', - releaseType: 'none' + name: "Internal", + changelogTitle: "Internal Section", + releaseType: "none", }, { - name: 'Version: Minor', - changelogTitle: 'Minor Section', - releaseType: SEMVER.minor - } + name: "Version: Minor", + changelogTitle: "Minor Section", + releaseType: SEMVER.minor, + }, ]; const changelog = new Changelog(dummyLog(), options); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)', { - labels: ['Internal', 'Version: Minor'] - }) + makeCommitFromMsg("Some Feature (#1234)", { + labels: ["Internal", "Version: Minor"], + }), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should prefer section defined first in config for PR with multiple labels of same releaseType', async () => { + test("should prefer section defined first in config for PR with multiple labels of same releaseType", async () => { const options = testOptions(); options.labels = [ { - name: 'Internal', - changelogTitle: 'Internal Section', - releaseType: 'none' + name: "Internal", + changelogTitle: "Internal Section", + releaseType: "none", }, { - name: 'Typescript', - changelogTitle: 'Typescript Section', - releaseType: 'none' - } + name: "Typescript", + changelogTitle: "Typescript Section", + releaseType: "none", + }, ]; const changelog = new Changelog(dummyLog(), options); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)', { - labels: ['Typescript', 'Internal'] - }) + makeCommitFromMsg("Some Feature (#1234)", { + labels: ["Typescript", "Internal"], + }), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should prefer section of default label for PR with multiple labels of same releaseType', async () => { + test("should prefer section of default label for PR with multiple labels of same releaseType", async () => { const options = testOptions(); options.labels = [ ...options.labels, { - name: 'Minor2', - changelogTitle: 'Minor 2 Section', - releaseType: SEMVER.minor - } + name: "Minor2", + changelogTitle: "Minor 2 Section", + releaseType: SEMVER.minor, + }, ]; const changelog = new Changelog(dummyLog(), options); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)', { labels: ['Minor2', 'minor'] }) + makeCommitFromMsg("Some Feature (#1234)", { + labels: ["Minor2", "minor"], + }), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should use username if present', async () => { + test("should use username if present", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)', { - labels: ['minor'], - username: 'adam' - }) + makeCommitFromMsg("Some Feature (#1234)", { + labels: ["minor"], + username: "adam", + }), ]); - normalized[0].authors[0].username = 'adam'; + normalized[0].authors[0].username = "adam"; expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should combine pr w/no label and labelled pr', async () => { + test("should combine pr w/no label and labelled pr", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)'), - makeCommitFromMsg('Third', { labels: ['patch'] }) + makeCommitFromMsg("Some Feature (#1234)"), + makeCommitFromMsg("Third", { labels: ["patch"] }), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); - test('should include prs with released label', async () => { + test("should include prs with released label", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('Some Feature (#1234)', { labels: ['released'] }), - makeCommitFromMsg('Third', { labels: ['patch'] }) + makeCommitFromMsg("Some Feature (#1234)", { labels: ["released"] }), + makeCommitFromMsg("Third", { labels: ["patch"] }), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); @@ -317,84 +319,84 @@ describe('generateReleaseNotes', () => { changelog.loadDefaultHooks(); const commits = await logParse.normalizeCommits([ { - hash: 'foo', + hash: "foo", files: [], labels: [], - authorEmail: 'adam@dierkens.com', - subject: 'Another Feature (#1234)' + authorEmail: "adam@dierkens.com", + subject: "Another Feature (#1234)", }, { - hash: 'foo', + hash: "foo", files: [], labels: [], - subject: 'One Feature (#1235)' - } + subject: "One Feature (#1235)", + }, ]); expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('should include PR-less commits as patches', async () => { + test("should include PR-less commits as patches", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const commits = await logParse.normalizeCommits([ { - hash: '1', + hash: "1", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'I was a push to master\n\nfoo bar', - labels: ['pushToBaseBranch'] + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "I was a push to master\n\nfoo bar", + labels: ["pushToBaseBranch"], }, { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], + }, ]); expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); test('should add "Push to Next"', async () => { - currentBranchSpy.mockReturnValueOnce('next'); + currentBranchSpy.mockReturnValueOnce("next"); const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const commits = await logParse.normalizeCommits([ { - hash: '1', + hash: "1", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'I was a push to master\n\n', - labels: ['pushToBaseBranch'] + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "I was a push to master\n\n", + labels: ["pushToBaseBranch"], }, { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], + }, ]); expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('should order the section major, minor, patch, then the rest', async () => { + test("should order the section major, minor, patch, then the rest", async () => { const options = testOptions(); options.labels = [ - options.labels.find(l => l.name === 'documentation')!, - options.labels.find(l => l.name === 'internal')!, - options.labels.find(l => l.name === 'patch')!, - options.labels.find(l => l.name === 'minor')!, - options.labels.find(l => l.name === 'major')! + options.labels.find((l) => l.name === "documentation")!, + options.labels.find((l) => l.name === "internal")!, + options.labels.find((l) => l.name === "patch")!, + options.labels.find((l) => l.name === "minor")!, + options.labels.find((l) => l.name === "major")!, ]; const changelog = new Changelog(dummyLog(), options); @@ -402,87 +404,87 @@ describe('generateReleaseNotes', () => { const commits = await logParse.normalizeCommits([ { - hash: '0a', + hash: "0a", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'something\n\n', - labels: ['internal'] + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "something\n\n", + labels: ["internal"], }, { - hash: '0', + hash: "0", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'docs\n\n', - labels: ['documentation'] + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "docs\n\n", + labels: ["documentation"], }, { - hash: '1', + hash: "1", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'I was a push to master\n\n', - labels: ['patch'] + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "I was a push to master\n\n", + labels: ["patch"], }, { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], }, { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['major'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["major"], + }, ]); expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('should match authors correctly', async () => { + test("should match authors correctly", async () => { const options = testOptions(); const changelog = new Changelog(dummyLog(), options); changelog.loadDefaultHooks(); const commits = await logParse.normalizeCommits([ { - hash: '0a', + hash: "0a", files: [], - subject: 'something\n\n', - labels: ['internal'] + subject: "something\n\n", + labels: ["internal"], }, { - hash: '0', + hash: "0", files: [], - subject: 'docs\n\n', - labels: ['documentation'] + subject: "docs\n\n", + labels: ["documentation"], }, { - hash: '0', + hash: "0", files: [], - subject: 'another\n\n', - labels: ['documentation'] - } + subject: "another\n\n", + labels: ["documentation"], + }, ]); commits.forEach((commit, index) => { switch (index) { case 0: - commit.authors = [{ email: 'andrew@email.com' }]; + commit.authors = [{ email: "andrew@email.com" }]; break; case 1: - commit.authors = [{ email: 'kendall@email.com' }]; + commit.authors = [{ email: "kendall@email.com" }]; break; case 2: - commit.authors = [{ username: 'rdubzz' }]; + commit.authors = [{ username: "rdubzz" }]; break; default: } @@ -491,16 +493,16 @@ describe('generateReleaseNotes', () => { expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('should be able to customize pushToBaseBranch title', async () => { + test("should be able to customize pushToBaseBranch title", async () => { const options = testOptions(); options.labels = [ ...options.labels, { - name: 'pushToBaseBranch', - changelogTitle: 'Custom Title', - description: 'N/A', - releaseType: 'none' - } + name: "pushToBaseBranch", + changelogTitle: "Custom Title", + description: "N/A", + releaseType: "none", + }, ]; const changelog = new Changelog(dummyLog(), options); @@ -508,73 +510,73 @@ describe('generateReleaseNotes', () => { const commits = await logParse.normalizeCommits([ { - hash: '1', + hash: "1", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'I was a push to master\n\n', - labels: ['pushToBaseBranch'] + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "I was a push to master\n\n", + labels: ["pushToBaseBranch"], }, { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], + }, ]); expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('should omit changelog item for next branches', async () => { + test("should omit changelog item for next branches", async () => { const options = testOptions(); const changelog = new Changelog(dummyLog(), options); changelog.loadDefaultHooks(); const commits = await logParse.normalizeCommits([ { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], + }, ]); expect( await changelog.generateReleaseNotes([ { ...commits[0], - hash: '1', + hash: "1", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'V8\n\n', - labels: [''], + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "V8\n\n", + labels: [""], pullRequest: { - base: 'intuit/next', + base: "intuit/next", number: 123, - body: '# Release Notes\n\nfoobar' - } + body: "# Release Notes\n\nfoobar", + }, }, - ...commits + ...commits, ]) ).toMatchSnapshot(); }); - test('should be able to customize titles', async () => { + test("should be able to customize titles", async () => { const options = testOptions(); options.labels = [ ...options.labels, { - name: 'Version: Minor', - changelogTitle: 'Woo Woo New Features', - description: 'N/A', - releaseType: SEMVER.minor - } + name: "Version: Minor", + changelogTitle: "Woo Woo New Features", + description: "N/A", + releaseType: SEMVER.minor, + }, ]; const changelog = new Changelog(dummyLog(), options); @@ -582,33 +584,33 @@ describe('generateReleaseNotes', () => { const commits = await logParse.normalizeCommits([ { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['Version: Minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["Version: Minor"], + }, ]); expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('should merge sections with same changelog title', async () => { + test("should merge sections with same changelog title", async () => { const options = testOptions(); options.labels = [ ...options.labels, { - name: 'new-component', - changelogTitle: 'Enhancement', - releaseType: SEMVER.minor + name: "new-component", + changelogTitle: "Enhancement", + releaseType: SEMVER.minor, }, { - name: 'Version: Minor', - changelogTitle: 'Enhancement', - description: 'N/A', - releaseType: SEMVER.minor - } + name: "Version: Minor", + changelogTitle: "Enhancement", + description: "N/A", + releaseType: SEMVER.minor, + }, ]; const changelog = new Changelog(dummyLog(), options); @@ -616,39 +618,39 @@ describe('generateReleaseNotes', () => { const commits = await logParse.normalizeCommits([ { - hash: '3', + hash: "3", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'Second Feature (#1236)', - labels: ['new-component'] + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "Second Feature (#1236)", + labels: ["new-component"], }, { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['Version: Minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["Version: Minor"], + }, ]); expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('should add additional release notes', async () => { + test("should add additional release notes", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const commits = await logParse.normalizeCommits([ { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], + }, ]); commits[0].pullRequest!.body = endent` # Why @@ -667,19 +669,19 @@ describe('generateReleaseNotes', () => { expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('additional release notes should be able to contain sub-headers', async () => { + test("additional release notes should be able to contain sub-headers", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const commits = await logParse.normalizeCommits([ { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], + }, ]); commits[0].pullRequest!.body = endent` # Why @@ -708,13 +710,13 @@ describe('generateReleaseNotes', () => { const commits = await logParse.normalizeCommits([ { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], + }, ]); commits[0].pullRequest!.body = endent` # Why @@ -726,21 +728,21 @@ describe('generateReleaseNotes', () => { expect(res).toMatchSnapshot(); }); - test('additional release notes should omit renovate prs', async () => { + test("additional release notes should omit renovate prs", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); const commits = await logParse.normalizeCommits([ { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor"], + }, ]); - commits[0].authors[0].username = 'renovate-bot'; + commits[0].authors[0].username = "renovate-bot"; commits[0].pullRequest!.body = endent` # Why @@ -762,25 +764,25 @@ describe('generateReleaseNotes', () => { expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); - test('additional release notes should have tappable omit', async () => { + test("additional release notes should have tappable omit", async () => { const changelog = new Changelog(dummyLog(), testOptions()); changelog.loadDefaultHooks(); - changelog.hooks.omitReleaseNotes.tap('test', commit => { - if (commit.labels.includes('no-notes')) { + changelog.hooks.omitReleaseNotes.tap("test", (commit) => { + if (commit.labels.includes("no-notes")) { return true; } }); const commits = await logParse.normalizeCommits([ { - hash: '2', + hash: "2", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'First Feature (#1235)', - labels: ['minor', 'no-notes'] - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "First Feature (#1235)", + labels: ["minor", "no-notes"], + }, ]); commits[0].pullRequest!.body = endent` # Why diff --git a/packages/core/src/__tests__/config.test.ts b/packages/core/src/__tests__/config.test.ts index 7f93b08f4..463dba67c 100644 --- a/packages/core/src/__tests__/config.test.ts +++ b/packages/core/src/__tests__/config.test.ts @@ -1,12 +1,12 @@ -import Config, { normalizeLabel, normalizeLabels } from '../config'; -import { dummyLog } from '../utils/logger'; -import SEMVER from '../semver'; -import { ILabelDefinition } from '../release'; +import Config, { normalizeLabel, normalizeLabels } from "../config"; +import { dummyLog } from "../utils/logger"; +import SEMVER from "../semver"; +import { ILabelDefinition } from "../release"; const fetchSpy = jest.fn(); // @ts-ignore -jest.mock('node-fetch', () => (...args) => fetchSpy(...args)); +jest.mock("node-fetch", () => (...args) => fetchSpy(...args)); beforeEach(() => { fetchSpy.mockClear(); @@ -15,136 +15,136 @@ beforeEach(() => { const log = dummyLog(); const importMock = jest.fn(); -jest.mock('import-cwd', () => (path: string) => importMock(path)); +jest.mock("import-cwd", () => (path: string) => importMock(path)); -describe('normalizeLabel', () => { - test('should extend base label', () => { +describe("normalizeLabel", () => { + test("should extend base label", () => { const label: ILabelDefinition = { - name: 'foo', - releaseType: SEMVER.major + name: "foo", + releaseType: SEMVER.major, }; expect(normalizeLabel(label)).toStrictEqual({ - description: 'Increment the major version when merged', - name: 'foo', - changelogTitle: '💥 Breaking Change', - releaseType: SEMVER.major + description: "Increment the major version when merged", + name: "foo", + changelogTitle: "💥 Breaking Change", + releaseType: SEMVER.major, }); }); }); -describe('normalizeLabels', () => { - test('user labels should override defaults', () => { - expect(normalizeLabels({}).find(l => l.name === 'minor')).toStrictEqual({ - description: 'Increment the minor version when merged', - name: 'minor', - changelogTitle: '🚀 Enhancement', - releaseType: SEMVER.minor +describe("normalizeLabels", () => { + test("user labels should override defaults", () => { + expect(normalizeLabels({}).find((l) => l.name === "minor")).toStrictEqual({ + description: "Increment the minor version when merged", + name: "minor", + changelogTitle: "🚀 Enhancement", + releaseType: SEMVER.minor, }); expect( - normalizeLabels({ labels: [{ name: 'foo', releaseType: 'minor' }] }).find( - l => l.name === 'foo' + normalizeLabels({ labels: [{ name: "foo", releaseType: "minor" }] }).find( + (l) => l.name === "foo" ) ).toStrictEqual({ - description: 'Increment the minor version when merged', - name: 'foo', - changelogTitle: '🚀 Enhancement', - releaseType: SEMVER.minor + description: "Increment the minor version when merged", + name: "foo", + changelogTitle: "🚀 Enhancement", + releaseType: SEMVER.minor, }); }); }); -describe('loadExtendConfig', () => { - test('should reject when no config found', async () => { +describe("loadExtendConfig", () => { + test("should reject when no config found", async () => { const config = new Config(log); - await expect(config.loadExtendConfig('nothing')).rejects.toBeInstanceOf( + await expect(config.loadExtendConfig("nothing")).rejects.toBeInstanceOf( Error ); }); - test('should load file path', async () => { + test("should load file path", async () => { const config = new Config(log); - importMock.mockImplementation(path => - path === '../fake/path.json' ? { someOption: 'url' } : undefined + importMock.mockImplementation((path) => + path === "../fake/path.json" ? { someOption: "url" } : undefined ); - expect(await config.loadExtendConfig('../fake/path.json')).toStrictEqual({ - someOption: 'url' + expect(await config.loadExtendConfig("../fake/path.json")).toStrictEqual({ + someOption: "url", }); }); - test('should load package.json file from path', async () => { + test("should load package.json file from path", async () => { const config = new Config(log); - importMock.mockImplementation(path => - path === './package.json' ? { auto: { someOption: 'url' } } : undefined + importMock.mockImplementation((path) => + path === "./package.json" ? { auto: { someOption: "url" } } : undefined ); - expect(await config.loadExtendConfig('./package.json')).toStrictEqual({ - someOption: 'url' + expect(await config.loadExtendConfig("./package.json")).toStrictEqual({ + someOption: "url", }); }); - test('should fail if file path points to js file', async () => { + test("should fail if file path points to js file", async () => { const config = new Config(log); - importMock.mockImplementation(path => - path === '../fake/path.js' ? { someOption: 'url' } : undefined + importMock.mockImplementation((path) => + path === "../fake/path.js" ? { someOption: "url" } : undefined ); await expect( - config.loadExtendConfig('../fake/path.js') + config.loadExtendConfig("../fake/path.js") ).rejects.toBeInstanceOf(Error); }); - test('should call fetch on URL with config', async () => { + test("should call fetch on URL with config", async () => { const config = new Config(log); const mockFetchJson = jest.fn(); mockFetchJson.mockReturnValue({}); fetchSpy.mockResolvedValueOnce({ - json: mockFetchJson + json: mockFetchJson, }); - await config.loadExtendConfig('http://www.test.com/config.json'); + await config.loadExtendConfig("http://www.test.com/config.json"); expect(fetchSpy).toHaveBeenCalled(); expect(mockFetchJson).toHaveBeenCalled(); }); - test('should reject if extends URL fails to fetch', async () => { + test("should reject if extends URL fails to fetch", async () => { const config = new Config(log); fetchSpy.mockRejectedValueOnce(new Error()); await expect( - config.loadExtendConfig('http://www.test.com/config.json') + config.loadExtendConfig("http://www.test.com/config.json") ).rejects.toBeInstanceOf(Error); }); - test('should load @NAME/auto-config', async () => { + test("should load @NAME/auto-config", async () => { const config = new Config(log); - importMock.mockImplementation(path => - path === '@artsy/auto-config/package.json' + importMock.mockImplementation((path) => + path === "@artsy/auto-config/package.json" ? { auto: { onlyPublishWithReleaseLabel: true } } : undefined ); - expect(await config.loadExtendConfig('@artsy')).toStrictEqual({ - extends: '@artsy/auto-config/package.json', - onlyPublishWithReleaseLabel: true + expect(await config.loadExtendConfig("@artsy")).toStrictEqual({ + extends: "@artsy/auto-config/package.json", + onlyPublishWithReleaseLabel: true, }); }); - test('should load auto-config-NAME', async () => { + test("should load auto-config-NAME", async () => { const config = new Config(log); - importMock.mockImplementation(path => - path === 'auto-config-fuego/package.json' + importMock.mockImplementation((path) => + path === "auto-config-fuego/package.json" ? { auto: { noVersionPrefix: true } } : undefined ); - expect(await config.loadExtendConfig('fuego')).toStrictEqual({ - extends: 'auto-config-fuego/package.json', - noVersionPrefix: true + expect(await config.loadExtendConfig("fuego")).toStrictEqual({ + extends: "auto-config-fuego/package.json", + noVersionPrefix: true, }); }); }); diff --git a/packages/core/src/__tests__/get-remote.test.ts b/packages/core/src/__tests__/get-remote.test.ts index d77f89f8e..b4c47f6b9 100644 --- a/packages/core/src/__tests__/get-remote.test.ts +++ b/packages/core/src/__tests__/get-remote.test.ts @@ -1,49 +1,49 @@ -import Auto from '../auto'; +import Auto from "../auto"; -jest.mock('child_process'); +jest.mock("child_process"); -describe('getRemote', () => { - test('should fall back to origin with no git client', async () => { +describe("getRemote", () => { + test("should fall back to origin with no git client", async () => { const auto = new Auto(); // @ts-ignore - expect(await auto.getRemote()).toBe('origin'); + expect(await auto.getRemote()).toBe("origin"); }); - test('should use html_url if we can push', async () => { + test("should use html_url if we can push", async () => { const auto = new Auto(); - const html_url = 'https://github.com/fake/remote'; + const html_url = "https://github.com/fake/remote"; auto.git = { verifyAuth: (url: string) => url === html_url, - getProject: () => Promise.resolve({ html_url }) + getProject: () => Promise.resolve({ html_url }), } as any; // @ts-ignore expect(await auto.getRemote()).toBe(html_url); }); - test('should put token in url', async () => { + test("should put token in url", async () => { const auto = new Auto(); - const html_url = 'https://github.com/fake/remote'; - process.env.GH_TOKEN = 'XXXX'; + const html_url = "https://github.com/fake/remote"; + process.env.GH_TOKEN = "XXXX"; auto.git = { - verifyAuth: (url: string) => url.includes('XXXX'), - getProject: () => Promise.resolve({ html_url }) + verifyAuth: (url: string) => url.includes("XXXX"), + getProject: () => Promise.resolve({ html_url }), } as any; // @ts-ignore - expect(await auto.getRemote()).toBe('https://XXXX@github.com/fake/remote'); + expect(await auto.getRemote()).toBe("https://XXXX@github.com/fake/remote"); }); - test('should GitHub action user in url', async () => { + test("should GitHub action user in url", async () => { const auto = new Auto(); - const html_url = 'https://github.com/fake/remote'; - process.env.GITHUB_TOKEN = 'XXXX'; - process.env.GITHUB_ACTION = 'true'; + const html_url = "https://github.com/fake/remote"; + process.env.GITHUB_TOKEN = "XXXX"; + process.env.GITHUB_ACTION = "true"; auto.git = { - verifyAuth: (url: string) => url.includes('x-access-token:'), - getProject: () => Promise.resolve({ html_url }) + verifyAuth: (url: string) => url.includes("x-access-token:"), + getProject: () => Promise.resolve({ html_url }), } as any; // @ts-ignore expect(await auto.getRemote()).toBe( - 'https://x-access-token:XXXX@github.com/fake/remote' + "https://x-access-token:XXXX@github.com/fake/remote" ); }); }); diff --git a/packages/core/src/__tests__/git.test.ts b/packages/core/src/__tests__/git.test.ts index 6919b7a65..24547941c 100644 --- a/packages/core/src/__tests__/git.test.ts +++ b/packages/core/src/__tests__/git.test.ts @@ -1,4 +1,4 @@ -import Git from '../git'; +import Git from "../git"; const authenticate = jest.fn(); const listLabelsOnIssue = jest.fn(); @@ -27,7 +27,7 @@ const get = jest.fn(); const update = jest.fn(); const paginate = jest.fn(); -jest.mock('@octokit/rest', () => { +jest.mock("@octokit/rest", () => { const Octokit = class MockOctokit { static plugin = () => Octokit; @@ -37,7 +37,7 @@ jest.mock('@octokit/rest', () => { pulls = { get: getPr, listCommits: { endpoint: listCommits }, - list + list, }; issues = { @@ -53,227 +53,227 @@ jest.mock('@octokit/rest', () => { removeLabel, lock, get, - update + update, }; repos = { createStatus, createRelease, getLatestRelease, - get: getProject + get: getProject, }; search = { users: getUser, - issuesAndPullRequests + issuesAndPullRequests, }; users = { - getByUsername + getByUsername, }; hook = { - error: errorHook + error: errorHook, }; }; return { Octokit }; }); -jest.mock('@octokit/graphql', () => ({ +jest.mock("@octokit/graphql", () => ({ graphql: () => ({ - data: [] - }) + data: [], + }), })); const options = { - owner: 'Adam Dierkens', - repo: 'test', - token: 'MyToken', - baseBranch: 'master' + owner: "Adam Dierkens", + repo: "test", + token: "MyToken", + baseBranch: "master", }; -describe('github', () => { +describe("github", () => { beforeEach(() => { jest.clearAllMocks(); }); - describe('getLabels', () => { - test('successful', async () => { + describe("getLabels", () => { + test("successful", async () => { const gh = new Git(options); listLabelsOnIssue.mockReturnValueOnce({ data: [ - { name: 'minor' }, - { name: 'documentation' }, - { name: 'major' }, - { name: 'internal' } - ] + { name: "minor" }, + { name: "documentation" }, + { name: "major" }, + { name: "internal" }, + ], }); expect(await gh.getLabels(123)).toStrictEqual([ - 'minor', - 'documentation', - 'major', - 'internal' + "minor", + "documentation", + "major", + "internal", ]); }); - test('handles errors', async () => { + test("handles errors", async () => { const gh = new Git(options); await expect(gh.getLabels(123)).rejects.toBeInstanceOf(Error); }); }); - test('publish', async () => { + test("publish", async () => { const gh = new Git(options); - await gh.publish('releaseNotes', 'tag'); + await gh.publish("releaseNotes", "tag"); expect(createRelease).toHaveBeenCalled(); }); - test('graphql', async () => { + test("graphql", async () => { const gh = new Git(options); - const result = await gh.graphql('{ someQuery }'); + const result = await gh.graphql("{ someQuery }"); expect(result!.data).not.toBeUndefined(); }); - test('getFirstCommit ', async () => { + test("getFirstCommit ", async () => { const gh = new Git(options); expect(await gh.getFirstCommit()).toBe( - '0b2af75d8b55c8869cda93d0e5589ad9f2677e18' + "0b2af75d8b55c8869cda93d0e5589ad9f2677e18" ); }); - test('addLabelToPr ', async () => { + test("addLabelToPr ", async () => { const gh = new Git(options); - await gh.addLabelToPr(123, 'foo bar'); + await gh.addLabelToPr(123, "foo bar"); expect(addLabels).toHaveBeenCalled(); }); - test('removeLabel ', async () => { + test("removeLabel ", async () => { const gh = new Git(options); - await gh.removeLabel(123, 'foo bar'); + await gh.removeLabel(123, "foo bar"); expect(removeLabel).toHaveBeenCalled(); }); - test('lockIssue ', async () => { + test("lockIssue ", async () => { const gh = new Git(options); await gh.lockIssue(123); expect(lock).toHaveBeenCalled(); }); - test('getCommitDate ', async () => { + test("getCommitDate ", async () => { const gh = new Git(options); expect( - await gh.getCommitDate('0b2af75d8b55c8869cda93d0e5589ad9f2677e18') - ).toBe('2018-12-03T15:19:38-0800'); + await gh.getCommitDate("0b2af75d8b55c8869cda93d0e5589ad9f2677e18") + ).toBe("2018-12-03T15:19:38-0800"); }); - test('getSha', async () => { + test("getSha", async () => { const gh = new Git(options); expect(await gh.getSha()).toBeDefined(); }); - test('getLatestTagInBranch', async () => { + test("getLatestTagInBranch", async () => { const gh = new Git(options); expect(await gh.getLatestTagInBranch()).toBeDefined(); }); - test('getTags', async () => { + test("getTags", async () => { const gh = new Git(options); - expect(Array.isArray(await gh.getTags('master'))).toBe(true); + expect(Array.isArray(await gh.getTags("master"))).toBe(true); }); - test('getLastTagNotInBaseBranch', async () => { + test("getLastTagNotInBaseBranch", async () => { const getTags = jest.fn(); const gh = new Git(options); gh.getTags = getTags; - getTags.mockReturnValueOnce(['0.1.0', '0.2.0', '0.3.0']); + getTags.mockReturnValueOnce(["0.1.0", "0.2.0", "0.3.0"]); getTags.mockReturnValueOnce([ - '0.1.0', - '0.2.0', - '0.4.0-alpha.0', - '0.4.0-alpha.1', - '0.3.0' + "0.1.0", + "0.2.0", + "0.4.0-alpha.0", + "0.4.0-alpha.1", + "0.3.0", ]); - expect(await gh.getLastTagNotInBaseBranch('alpha')).toBe('0.4.0-alpha.1'); + expect(await gh.getLastTagNotInBaseBranch("alpha")).toBe("0.4.0-alpha.1"); }); - test('getGitLog ', async () => { + test("getGitLog ", async () => { const gh = new Git(options); expect( await gh.getGitLog( - '0b2af75d8b55c8869cda93d0e5589ad9f2677e18', - 'a7f6634429731055a5a44bae24ac88c5f9822e58' + "0b2af75d8b55c8869cda93d0e5589ad9f2677e18", + "a7f6634429731055a5a44bae24ac88c5f9822e58" ) ).toMatchSnapshot(); }); - test('getGitLog - merge commits', async () => { + test("getGitLog - merge commits", async () => { const gh = new Git(options); expect( ( await gh.getGitLog( - 'fb857b8939fa2c95a3202613d36a12aa3341d109', - '024c66aa21cff5947957ed3bdfb4a616aa3f0046' + "fb857b8939fa2c95a3202613d36a12aa3341d109", + "024c66aa21cff5947957ed3bdfb4a616aa3f0046" ) )[0] ).toMatchSnapshot(); }); - test('getUser', async () => { + test("getUser", async () => { const gh = new Git(options); - getPr.mockReturnValueOnce('asdfasdf'); + getPr.mockReturnValueOnce("asdfasdf"); - expect(await gh.getPullRequest(22)).toBe('asdfasdf'); + expect(await gh.getPullRequest(22)).toBe("asdfasdf"); }); - test('createStatus', async () => { + test("createStatus", async () => { const gh = new Git(options); createStatus.mockReturnValueOnce(true); expect( await gh.createStatus({ - state: 'pending', - sha: '', - context: 'foo', - target_url: 'google.com', - description: 'testing' + state: "pending", + sha: "", + context: "foo", + target_url: "google.com", + description: "testing", }) ).not.toBeUndefined(); }); - test('search', async () => { + test("search", async () => { const gh = new Git(options); issuesAndPullRequests.mockReturnValueOnce({ data: true }); await gh.searchRepo({ - q: 'is:pr is:open', - order: 'desc' + q: "is:pr is:open", + order: "desc", }); expect(issuesAndPullRequests).toHaveBeenCalledWith({ - q: 'repo:Adam Dierkens/test is:pr is:open', - order: 'desc' + q: "repo:Adam Dierkens/test is:pr is:open", + order: "desc", }); }); - test('getProject', async () => { + test("getProject", async () => { const gh = new Git(options); getProject.mockReturnValueOnce({ data: true }); @@ -281,48 +281,48 @@ describe('github', () => { expect(await gh.getProject()).not.toBeUndefined(); }); - describe('createComment', () => { - test('should post comment if none exists', async () => { + describe("createComment", () => { + test("should post comment if none exists", async () => { const gh = new Git(options); listComments.mockReturnValueOnce({ data: [] }); - await gh.createComment('Some long thing', 22, 'default'); + await gh.createComment("Some long thing", 22, "default"); expect(createComment).toHaveBeenCalled(); }); - test('should delete old comment', async () => { + test("should delete old comment", async () => { const gh = new Git(options); listComments.mockReturnValueOnce({ data: [ { - body: '\nSome cool message', - id: 1337 - } - ] + body: "\nSome cool message", + id: 1337, + }, + ], }); - await gh.createComment('Some long thing', 22, 'default'); + await gh.createComment("Some long thing", 22, "default"); expect(deleteComment).toHaveBeenCalled(); expect(deleteComment.mock.calls[0][0].comment_id).toBe(1337); expect(createComment).toHaveBeenCalled(); }); - test('should be able to comment in different contexts', async () => { + test("should be able to comment in different contexts", async () => { const gh = new Git(options); listComments.mockReturnValueOnce({ data: [ { - body: '\nSome cool message', - id: 1337 - } - ] + body: "\nSome cool message", + id: 1337, + }, + ], }); - await gh.createComment('Some long thing', 22, 'PERF'); + await gh.createComment("Some long thing", 22, "PERF"); expect(deleteComment).not.toHaveBeenCalled(); expect(createComment).toHaveBeenCalled(); @@ -330,354 +330,354 @@ describe('github', () => { listComments.mockReturnValueOnce({ data: [ { - body: '\nSome cool message', - id: 1337 + body: "\nSome cool message", + id: 1337, }, { - body: '\nSome cool message', - id: 1000 - } - ] + body: "\nSome cool message", + id: 1000, + }, + ], }); - await gh.createComment('Some new thing', 22, 'PERF'); + await gh.createComment("Some new thing", 22, "PERF"); expect(deleteComment).toHaveBeenCalled(); expect(deleteComment.mock.calls[0][0].comment_id).toBe(1000); }); }); - describe('editComment', () => { - test('should post comment if none exists', async () => { + describe("editComment", () => { + test("should post comment if none exists", async () => { const gh = new Git(options); listComments.mockReturnValue({ data: [] }); - await gh.editComment('Some long thing', 22, 'default'); + await gh.editComment("Some long thing", 22, "default"); expect(createComment).toHaveBeenCalled(); expect(updateComment).not.toHaveBeenCalled(); }); - test('should edit old comment', async () => { + test("should edit old comment", async () => { const gh = new Git(options); listComments.mockReturnValue({ data: [ { - body: '\nSome cool message', - id: 1337 - } - ] + body: "\nSome cool message", + id: 1337, + }, + ], }); - await gh.editComment('Some long thing', 22, 'default'); + await gh.editComment("Some long thing", 22, "default"); expect(updateComment).toHaveBeenCalled(); expect(updateComment.mock.calls[0][0].comment_id).toBe(1337); expect(createComment).not.toHaveBeenCalled(); }); - test('should return a non-negative number if context exists', async () => { + test("should return a non-negative number if context exists", async () => { const gh = new Git(options); listComments.mockReturnValue({ data: [ { - body: '\nSome cool message', - id: 1337 - } - ] + body: "\nSome cool message", + id: 1337, + }, + ], }); - const commentId = await gh.getCommentId(22, 'default'); + const commentId = await gh.getCommentId(22, "default"); expect(commentId).toStrictEqual(1337); }); - test('should return -1 if context does not exist', async () => { + test("should return -1 if context does not exist", async () => { const gh = new Git(options); listComments.mockReturnValue({ data: [] }); - const commentId = await gh.getCommentId(22, 'default'); + const commentId = await gh.getCommentId(22, "default"); expect(commentId).toStrictEqual(-1); }); }); - describe('addToPrBody', () => { - test('should add to PR body if exists', async () => { + describe("addToPrBody", () => { + test("should add to PR body if exists", async () => { const gh = new Git(options); - get.mockReturnValueOnce({ data: { body: '# My Content' } }); - await gh.addToPrBody('Some long thing', 22); + get.mockReturnValueOnce({ data: { body: "# My Content" } }); + await gh.addToPrBody("Some long thing", 22); expect(update).toHaveBeenCalledWith( expect.objectContaining({ body: - '# My Content\n\nSome long thing\n\n' + "# My Content\n\nSome long thing\n\n", }) ); }); - test('should add to PR body if none exists', async () => { + test("should add to PR body if none exists", async () => { const gh = new Git(options); - get.mockReturnValueOnce({ data: { body: '# My Content' } }); - await gh.addToPrBody('', 22); + get.mockReturnValueOnce({ data: { body: "# My Content" } }); + await gh.addToPrBody("", 22); expect(update).toHaveBeenCalledWith( expect.objectContaining({ - body: '# My Content' + body: "# My Content", }) ); }); - test('should overwrite old context', async () => { + test("should overwrite old context", async () => { const gh = new Git(options); get.mockReturnValueOnce({ data: { body: - '# My Content\n\n\n\nSome long thing\nand more\n\n' - } + "# My Content\n\n\n\nSome long thing\nand more\n\n", + }, }); - await gh.addToPrBody('Something else', 22); + await gh.addToPrBody("Something else", 22); expect(update).toHaveBeenCalledWith( expect.objectContaining({ body: - '# My Content\n\nSomething else\n\n' + "# My Content\n\nSomething else\n\n", }) ); }); - test('should be able to add to body in different contexts', async () => { + test("should be able to add to body in different contexts", async () => { const gh = new Git(options); get.mockReturnValueOnce({ data: { body: - '# My Content\n\nSomething else\n\n' - } + "# My Content\n\nSomething else\n\n", + }, }); - await gh.addToPrBody('Some long thing', 22, 'PERF'); + await gh.addToPrBody("Some long thing", 22, "PERF"); expect(update).toHaveBeenCalledWith( expect.objectContaining({ body: - '# My Content\n\nSomething else\n\n\n\nSome long thing\n\n' + "# My Content\n\nSomething else\n\n\n\nSome long thing\n\n", }) ); }); - test('should clear pr body section if message blank', async () => { + test("should clear pr body section if message blank", async () => { const gh = new Git(options); - get.mockReturnValueOnce({ data: { body: '# My Content' } }); + get.mockReturnValueOnce({ data: { body: "# My Content" } }); get.mockReturnValueOnce({ data: { body: - '# My Content\n\nSomething else\n' - } + "# My Content\n\nSomething else\n", + }, }); - await gh.addToPrBody('Some long thing', 22); + await gh.addToPrBody("Some long thing", 22); update.mockClear(); - await gh.addToPrBody('', 22); + await gh.addToPrBody("", 22); expect(update).toHaveBeenCalledWith( expect.objectContaining({ - body: '# My Content\n' + body: "# My Content\n", }) ); }); }); - test('getCommitsForPR', async () => { + test("getCommitsForPR", async () => { const gh = new Git(options); listCommits.mockReturnValueOnce({ - data: undefined + data: undefined, }); await gh.getCommitsForPR(22); expect(listCommits).toHaveBeenCalled(); }); - test('getPullRequests', async () => { + test("getPullRequests", async () => { const gh = new Git(options); list.mockReturnValueOnce({ - data: undefined + data: undefined, }); await gh.getPullRequests(); expect(list).toHaveBeenCalled(); }); - describe('getUserByUsername', () => { - test('exists', async () => { + describe("getUserByUsername", () => { + test("exists", async () => { const gh = new Git(options); getByUsername.mockReturnValueOnce({ - data: { name: 'Andrew Lisowski' } + data: { name: "Andrew Lisowski" }, }); - expect(await gh.getUserByUsername('andrew')).toStrictEqual({ - name: 'Andrew Lisowski' + expect(await gh.getUserByUsername("andrew")).toStrictEqual({ + name: "Andrew Lisowski", }); }); - test('not found', async () => { + test("not found", async () => { const gh = new Git(options); getByUsername.mockRejectedValueOnce(Error); - expect(await gh.getUserByUsername('andrew')).toBeUndefined(); + expect(await gh.getUserByUsername("andrew")).toBeUndefined(); }); }); - describe('getUserByEmail', () => { - test('exists', async () => { + describe("getUserByEmail", () => { + test("exists", async () => { const gh = new Git(options); getUser.mockReturnValueOnce({ - data: { items: [{ login: 'hipstersmoothie' }] } + data: { items: [{ login: "hipstersmoothie" }] }, }); - expect(await gh.getUserByEmail('lisowski54@gmail.com')).toStrictEqual({ - login: 'hipstersmoothie' + expect(await gh.getUserByEmail("lisowski54@gmail.com")).toStrictEqual({ + login: "hipstersmoothie", }); }); - test('doesnt exist', async () => { + test("doesnt exist", async () => { const gh = new Git(options); getUser.mockReturnValueOnce({ - data: undefined + data: undefined, }); - expect(await gh.getUserByEmail('lisowski54@gmail.com')).toStrictEqual({}); + expect(await gh.getUserByEmail("lisowski54@gmail.com")).toStrictEqual({}); }); - test('errors', async () => { + test("errors", async () => { const gh = new Git(options); getByUsername.mockRejectedValueOnce(Error); - expect(await gh.getUserByEmail('lisowski54@gmail.com')).toBeUndefined(); + expect(await gh.getUserByEmail("lisowski54@gmail.com")).toBeUndefined(); }); }); - describe('getLatestRelease', () => { - test('has tag ', async () => { + describe("getLatestRelease", () => { + test("has tag ", async () => { const gh = new Git(options); - getLatestRelease.mockReturnValueOnce({ data: { tag_name: '1.0.0' } }); + getLatestRelease.mockReturnValueOnce({ data: { tag_name: "1.0.0" } }); - expect(await gh.getLatestRelease()).toBe('1.0.0'); + expect(await gh.getLatestRelease()).toBe("1.0.0"); }); - test('no tags', async () => { + test("no tags", async () => { const gh = new Git(options); getLatestRelease.mockRejectedValueOnce({ status: 404 }); expect(await gh.getLatestRelease()).toBe( - '0b2af75d8b55c8869cda93d0e5589ad9f2677e18' + "0b2af75d8b55c8869cda93d0e5589ad9f2677e18" ); }); - test('handles errors', async () => { + test("handles errors", async () => { const gh = new Git(options); await expect(gh.getLatestRelease()).rejects.not.toBeUndefined(); }); }); - describe('getProjectLabels ', () => { - test('return labels', async () => { + describe("getProjectLabels ", () => { + test("return labels", async () => { const gh = new Git(options); paginate.mockReturnValueOnce( - Promise.resolve([{ name: 'first label' }, { name: 'second label' }]) + Promise.resolve([{ name: "first label" }, { name: "second label" }]) ); expect(await gh.getProjectLabels()).toStrictEqual([ - 'first label', - 'second label' + "first label", + "second label", ]); }); - test('throw for errors', async () => { + test("throw for errors", async () => { const gh = new Git(options); await expect(gh.getProjectLabels()).rejects.not.toBeUndefined(); }); }); - describe('getPr ', () => { - test('return pr', async () => { + describe("getPr ", () => { + test("return pr", async () => { const gh = new Git(options); get.mockReturnValueOnce({ success: true }); expect(await gh.getPr(123)).toStrictEqual({ success: true }); }); - test('throw for errors', async () => { + test("throw for errors", async () => { const gh = new Git(options); get.mockRejectedValueOnce(Error); await expect(gh.getPr(120)).rejects.not.toBeUndefined(); }); }); - test('updateLabel', async () => { + test("updateLabel", async () => { const gh = new Git(options); await gh.updateLabel({ - name: 'Foo bar', - description: 'test', - releaseType: 'release' + name: "Foo bar", + description: "test", + releaseType: "release", }); expect(updateLabel).toHaveBeenCalledWith( expect.objectContaining({ - owner: 'Adam Dierkens', - repo: 'test', - current_name: 'Foo bar', - description: 'test' + owner: "Adam Dierkens", + repo: "test", + current_name: "Foo bar", + description: "test", }) ); }); - describe('createLabel', () => { - test('should create a label', async () => { + describe("createLabel", () => { + test("should create a label", async () => { const gh = new Git(options); await gh.createLabel({ - name: 'Foo bar', - description: 'test', - releaseType: 'release' + name: "Foo bar", + description: "test", + releaseType: "release", }); expect(createLabel).toHaveBeenCalledWith( expect.objectContaining({ - owner: 'Adam Dierkens', - repo: 'test', - name: 'Foo bar' + owner: "Adam Dierkens", + repo: "test", + name: "Foo bar", }) ); }); - test('throw for errors', async () => { + test("throw for errors", async () => { const gh = new Git(options); await expect(gh.getProjectLabels()).rejects.not.toBeUndefined(); }); }); - describe('error hook', () => { - test('strip authorization headers from error', () => { - type HookError = import('@octokit/rest').Octokit.HookError; + describe("error hook", () => { + test("strip authorization headers from error", () => { + type HookError = import("@octokit/rest").Octokit.HookError; const error = (headers = {}): HookError => ({ - name: 'Request failed', - message: 'The request has failed', + name: "Request failed", + message: "The request has failed", status: 404, - headers + headers, }); // eslint-disable-next-line no-new @@ -690,7 +690,7 @@ describe('github', () => { ) => void; expect( - errorHandler.bind(undefined, error({ authorization: 'token abc' })) + errorHandler.bind(undefined, error({ authorization: "token abc" })) ).toThrow(error()); }); }); diff --git a/packages/core/src/__tests__/log-parse.test.ts b/packages/core/src/__tests__/log-parse.test.ts index 5b9d96704..ea9bd3978 100644 --- a/packages/core/src/__tests__/log-parse.test.ts +++ b/packages/core/src/__tests__/log-parse.test.ts @@ -1,54 +1,54 @@ -import LogParse, { parsePR, parseSquashPR } from '../log-parse'; +import LogParse, { parsePR, parseSquashPR } from "../log-parse"; -import makeCommitFromMsg from './make-commit-from-msg'; +import makeCommitFromMsg from "./make-commit-from-msg"; -describe('parsePR', () => { - test('should do nothing without merge commit', () => { - const commit = makeCommitFromMsg('Not a merge'); +describe("parsePR", () => { + test("should do nothing without merge commit", () => { + const commit = makeCommitFromMsg("Not a merge"); expect(parsePR(commit)).toStrictEqual(commit); }); - test('should parse number base and comment', () => { + test("should parse number base and comment", () => { const commit = makeCommitFromMsg( - 'Merge pull request #1234 from Another PR\nComments about the PR' + "Merge pull request #1234 from Another PR\nComments about the PR" ); expect(parsePR(commit)).toStrictEqual({ ...commit, pullRequest: { number: 1234, - base: 'Another PR' + base: "Another PR", }, - subject: 'Comments about the PR' + subject: "Comments about the PR", }); }); }); -describe('parseSquashPR', () => { - test('should do nothing squash merge commit', () => { - const commit = makeCommitFromMsg('Not a squash'); +describe("parseSquashPR", () => { + test("should do nothing squash merge commit", () => { + const commit = makeCommitFromMsg("Not a squash"); expect(parseSquashPR(commit)).toStrictEqual(commit); }); - test('should parse PR number and comment', () => { - const commit = makeCommitFromMsg('Some Message (#1234)'); + test("should parse PR number and comment", () => { + const commit = makeCommitFromMsg("Some Message (#1234)"); expect(parseSquashPR(commit)).toStrictEqual({ ...commit, pullRequest: { - number: 1234 + number: 1234, }, - subject: 'Some Message' + subject: "Some Message", }); }); }); -describe('normalizeCommits', () => { - test('should do nothing with normal commits', async () => { +describe("normalizeCommits", () => { + test("should do nothing with normal commits", async () => { const logParse = new LogParse(); const commits = [ - makeCommitFromMsg('First'), - makeCommitFromMsg('Second'), - makeCommitFromMsg('Third') + makeCommitFromMsg("First"), + makeCommitFromMsg("Second"), + makeCommitFromMsg("Third"), ]; expect(await logParse.normalizeCommits(commits)).toMatchSnapshot(); diff --git a/packages/core/src/__tests__/make-commit-from-msg.ts b/packages/core/src/__tests__/make-commit-from-msg.ts index 861ab8702..be7442ca8 100644 --- a/packages/core/src/__tests__/make-commit-from-msg.ts +++ b/packages/core/src/__tests__/make-commit-from-msg.ts @@ -1,4 +1,4 @@ -import { IExtendedCommit } from '../log-parse'; +import { IExtendedCommit } from "../log-parse"; /** Construct a dummy commit for testing. */ const makeCommitFromMsg = ( @@ -17,7 +17,7 @@ const makeCommitFromMsg = ( /** Packages effected by the commit */ packages?: string[]; /** The type of user */ - type?: 'Bot' | 'User'; + type?: "Bot" | "User"; /** PR info for the commit */ pullRequest?: { /** PR number attached to commit */ @@ -27,13 +27,13 @@ const makeCommitFromMsg = ( files?: string[]; } = {} ): IExtendedCommit => ({ - hash: options.hash || 'foo', + hash: options.hash || "foo", labels: options.labels || [], authorName: options.name !== undefined && options.name !== null ? options.name - : 'Adam Dierkens', - authorEmail: options.email || 'adam@dierkens.com', + : "Adam Dierkens", + authorEmail: options.email || "adam@dierkens.com", files: options.files || [], authors: [ { @@ -41,13 +41,13 @@ const makeCommitFromMsg = ( name: options.name !== undefined && options.name !== null ? options.name - : 'Adam Dierkens', - email: options.email || 'adam@dierkens.com', - ...(options.username ? { username: options.username } : {}) - } + : "Adam Dierkens", + email: options.email || "adam@dierkens.com", + ...(options.username ? { username: options.username } : {}), + }, ], subject, - pullRequest: options.pullRequest + pullRequest: options.pullRequest, }); export default makeCommitFromMsg; diff --git a/packages/core/src/__tests__/match-sha-to-pr.test.ts b/packages/core/src/__tests__/match-sha-to-pr.test.ts index 3fa562d80..07cde9ad3 100644 --- a/packages/core/src/__tests__/match-sha-to-pr.test.ts +++ b/packages/core/src/__tests__/match-sha-to-pr.test.ts @@ -1,16 +1,16 @@ -import { parse } from 'graphql'; +import { parse } from "graphql"; -import { buildSearchQuery } from '../match-sha-to-pr'; +import { buildSearchQuery } from "../match-sha-to-pr"; -describe('buildSearchQuery', () => { - test('generates a valid query', () => { - const query = buildSearchQuery('Andrew', 'test', ['abc123', '3def78']); +describe("buildSearchQuery", () => { + test("generates a valid query", () => { + const query = buildSearchQuery("Andrew", "test", ["abc123", "3def78"]); expect(() => parse(query!)).not.toThrow(); expect(query).toMatchSnapshot(); }); test("doesn't generate a query without commits", () => { - const query = buildSearchQuery('Andrew', 'test', []); + const query = buildSearchQuery("Andrew", "test", []); expect(query).toBeUndefined(); }); }); diff --git a/packages/core/src/__tests__/release.test.ts b/packages/core/src/__tests__/release.test.ts index 2ab50dbd6..a7d826c4b 100644 --- a/packages/core/src/__tests__/release.test.ts +++ b/packages/core/src/__tests__/release.test.ts @@ -1,18 +1,18 @@ -import Git from '../git'; -import LogParse from '../log-parse'; +import Git from "../git"; +import LogParse from "../log-parse"; import Release, { defaultLabels, getVersionMap, - ILabelDefinition -} from '../release'; -import SEMVER from '../semver'; -import { dummyLog } from '../utils/logger'; -import makeCommitFromMsg from './make-commit-from-msg'; -import child from 'child_process'; + ILabelDefinition, +} from "../release"; +import SEMVER from "../semver"; +import { dummyLog } from "../utils/logger"; +import makeCommitFromMsg from "./make-commit-from-msg"; +import child from "child_process"; const { execSync } = child; const exec = jest.fn(); -child.execSync = exec.mockReturnValue(''); +child.execSync = exec.mockReturnValue(""); afterAll(() => { child.execSync = execSync; @@ -28,7 +28,7 @@ const getLatestRelease = jest.fn(); const getSha = jest.fn(); const getCommit = jest .fn() - .mockReturnValue(Promise.resolve({ data: { author: { login: '' } } })); + .mockReturnValue(Promise.resolve({ data: { author: { login: "" } } })); const createStatus = jest.fn(); const getProject = jest.fn(); const createComment = jest.fn(); @@ -45,23 +45,23 @@ const getCommitDate = jest.fn(); const getFirstCommit = jest.fn(); getProject.mockResolvedValue({ - html_url: 'https://github.com/web/site' + html_url: "https://github.com/web/site", }); const mockLabels = (labels: string[]) => ({ - data: { labels: labels.map(label => ({ name: label })), user: {} } + data: { labels: labels.map((label) => ({ name: label })), user: {} }, }); // @ts-ignore jest.mock( - '../git.ts', + "../git.ts", () => class MockGit { constructor(...args: any[]) { constructor(...args); } - options = { owner: 'test', repo: 'test', version: '1.0.0' }; + options = { owner: "test", repo: "test", version: "1.0.0" }; graphql = graphql; getGitLog = getGitLog; getPr = getPr; @@ -91,105 +91,105 @@ getGitLog.mockReturnValue([]); const execSpy = jest.fn(); // @ts-ignore -jest.mock('../utils/exec-promise.ts', () => (...args) => execSpy(...args)); +jest.mock("../utils/exec-promise.ts", () => (...args) => execSpy(...args)); const existsSync = jest.fn(); const writeSpy = jest.fn(); -let readResult = '{}'; +let readResult = "{}"; -jest.mock('fs', () => ({ +jest.mock("fs", () => ({ // @ts-ignore existsSync: (...args) => existsSync(...args), // @ts-ignore readFile: (a, b, cb) => { cb(undefined, readResult); }, - ReadStream: function() {}, - WriteStream: function() {}, + ReadStream: function () {}, + WriteStream: function () {}, // @ts-ignore closeSync: () => undefined, // @ts-ignore writeFile: (file, data, cb) => { cb(undefined, writeSpy(file, data)); - } + }, })); const logParse = new LogParse(); const git = new Git({ - owner: 'Andrew', - repo: 'test', - token: 'MY_TOKEN', - baseBranch: 'master' + owner: "Andrew", + repo: "test", + token: "MY_TOKEN", + baseBranch: "master", }); -describe('getVersionMap', () => { - test('should return the default map', () => { +describe("getVersionMap", () => { + test("should return the default map", () => { expect(getVersionMap()).toStrictEqual( new Map([ - ['major', ['major']], - ['minor', ['minor']], - ['patch', ['patch']], - ['skip', ['skip-release']], - ['release', ['release']], - ['none', ['internal', 'documentation']] + ["major", ["major"]], + ["minor", ["minor"]], + ["patch", ["patch"]], + ["skip", ["skip-release"]], + ["release", ["release"]], + ["none", ["internal", "documentation"]], ]) ); }); - test('should add custom labels', () => { + test("should add custom labels", () => { expect( getVersionMap([ - { name: 'major', releaseType: SEMVER.major }, - { name: 'BREAKING', releaseType: SEMVER.major } + { name: "major", releaseType: SEMVER.major }, + { name: "BREAKING", releaseType: SEMVER.major }, ]) - ).toStrictEqual(new Map([['major', ['major', 'BREAKING']]])); + ).toStrictEqual(new Map([["major", ["major", "BREAKING"]]])); }); }); -describe('Release', () => { +describe("Release", () => { beforeEach(() => { jest.clearAllMocks(); getUserByUsername.mockReset(); }); - describe('getCommits', () => { - test('should default to HEAD', async () => { + describe("getCommits", () => { + test("should default to HEAD", async () => { const gh = new Release(git); - await gh.getCommits('12345'); + await gh.getCommits("12345"); expect(getGitLog).toHaveBeenCalled(); }); - test('should use configured HEAD', async () => { + test("should use configured HEAD", async () => { const gh = new Release(git); - await gh.getCommits('12345', '1234'); + await gh.getCommits("12345", "1234"); expect(getGitLog).toHaveBeenCalled(); }); - test('should not resolve authors with no PR commits', async () => { + test("should not resolve authors with no PR commits", async () => { const commits = [ - makeCommitFromMsg('First'), - makeCommitFromMsg('Second'), - makeCommitFromMsg('Third') + makeCommitFromMsg("First"), + makeCommitFromMsg("Second"), + makeCommitFromMsg("Third"), ]; getGitLog.mockReturnValueOnce(commits); const gh = new Release(git); - expect(await gh.getCommits('12345', '1234')).toMatchSnapshot(); + expect(await gh.getCommits("12345", "1234")).toMatchSnapshot(); }); - test('should resolve authors with PR commits', async () => { + test("should resolve authors with PR commits", async () => { const commits = await logParse.normalizeCommits([ - makeCommitFromMsg('First'), - makeCommitFromMsg('Second (#123)', { - name: 'Andrew Lisowski', - email: 'andrew@users.noreply.github.com' + makeCommitFromMsg("First"), + makeCommitFromMsg("Second (#123)", { + name: "Andrew Lisowski", + email: "andrew@users.noreply.github.com", }), - makeCommitFromMsg('Second (#123)', { - name: 'Andrew Lisowski', - email: 'lisowski54@gmail.com' + makeCommitFromMsg("Second (#123)", { + name: "Andrew Lisowski", + email: "lisowski54@gmail.com", }), - makeCommitFromMsg('Third') + makeCommitFromMsg("Third"), ]); getGitLog.mockReturnValueOnce(commits); @@ -197,43 +197,43 @@ describe('Release', () => { Promise.resolve([ { author: { - login: 'andrew' - } - } + login: "andrew", + }, + }, ]) ); getCommit.mockReturnValueOnce( - Promise.resolve({ data: { author: { login: 'adam' } } }) + Promise.resolve({ data: { author: { login: "adam" } } }) ); - getUserByUsername.mockImplementation(username => { - if (username === 'andrew') { + getUserByUsername.mockImplementation((username) => { + if (username === "andrew") { return { - login: 'andrew', - name: 'Andrew Lisowski' + login: "andrew", + name: "Andrew Lisowski", }; } return { - login: 'adam', - name: 'Adam Dierkens' + login: "adam", + name: "Adam Dierkens", }; }); const gh = new Release(git); - const modifiedCommits = await gh.getCommits('12345', '1234'); + const modifiedCommits = await gh.getCommits("12345", "1234"); expect(getUserByUsername).toHaveBeenCalled(); expect(modifiedCommits).toMatchSnapshot(); }); - test('should be able to omit by username', async () => { + test("should be able to omit by username", async () => { const commits = await logParse.normalizeCommits([ - makeCommitFromMsg('First'), - makeCommitFromMsg('Second (#123)', { - name: 'Andrew Lisowski', - email: 'lisowski54@gmail.com' + makeCommitFromMsg("First"), + makeCommitFromMsg("Second (#123)", { + name: "Andrew Lisowski", + email: "lisowski54@gmail.com", }), - makeCommitFromMsg('Third') + makeCommitFromMsg("Third"), ]); getGitLog.mockReturnValueOnce(commits); @@ -241,193 +241,224 @@ describe('Release', () => { Promise.resolve([ { author: { - login: 'andrew' - } - } + login: "andrew", + }, + }, ]) ); getCommit.mockReturnValueOnce( - Promise.resolve({ data: { author: { login: 'adam' } } }) + Promise.resolve({ data: { author: { login: "adam" } } }) ); getCommit.mockReturnValueOnce( - Promise.resolve({ data: { author: { login: 'adam' } } }) + Promise.resolve({ data: { author: { login: "adam" } } }) ); - getUserByUsername.mockImplementation(username => { - if (username === 'andrew') { + getUserByUsername.mockImplementation((username) => { + if (username === "andrew") { return { - login: 'andrew', - name: 'Andrew Lisowski' + login: "andrew", + name: "Andrew Lisowski", }; } return { - login: 'adam', - name: 'Adam Dierkens' + login: "adam", + name: "Adam Dierkens", }; }); const gh = new Release(git); - gh.hooks.onCreateLogParse.tap('test', parser => { - parser.hooks.omitCommit.tap('test', commit => - Boolean(commit.authors.find(author => author.username === 'adam')) + gh.hooks.onCreateLogParse.tap("test", (parser) => { + parser.hooks.omitCommit.tap("test", (commit) => + Boolean(commit.authors.find((author) => author.username === "adam")) ); }); - const modifiedCommits = await gh.getCommits('12345', '1234'); + const modifiedCommits = await gh.getCommits("12345", "1234"); expect(modifiedCommits).toMatchSnapshot(); }); - test('should ignore rebased commits if no last release', async () => { + test("should ignore rebased commits if no last release", async () => { const gh = new Release(git); getLatestReleaseInfo.mockReturnValueOnce({}); const commits = await logParse.normalizeCommits([ - makeCommitFromMsg('Second (#123)') + makeCommitFromMsg("Second (#123)"), ]); getGitLog.mockReturnValueOnce(commits); - expect(await gh.getCommits('12345', '1234')).toMatchSnapshot(); + expect(await gh.getCommits("12345", "1234")).toMatchSnapshot(); }); - test('should match rebased commits to PRs', async () => { + test("should match rebased commits to PRs", async () => { const gh = new Release(git); getLatestReleaseInfo.mockReturnValueOnce({ - published_at: '2019-01-16' + published_at: "2019-01-16", }); searchRepo.mockReturnValueOnce({ items: [{ number: 123 }] }); getPullRequest.mockReturnValueOnce({ data: { number: 123, - merge_commit_sha: '1a2b', - labels: [{ name: 'skip-release' }, { name: 'minor' }] - } + merge_commit_sha: "1a2b", + labels: [{ name: "skip-release" }, { name: "minor" }], + }, }); getGitLog.mockReturnValueOnce( await logParse.normalizeCommits([ - makeCommitFromMsg('Feature (#124)'), - makeCommitFromMsg('I was rebased', { - hash: '1a2b' - }) + makeCommitFromMsg("Feature (#124)"), + makeCommitFromMsg("I was rebased", { + hash: "1a2b", + }), ]) ); getUserByUsername.mockImplementationOnce(() => { return { - login: 'adam', - name: 'Adam Dierkens' + login: "adam", + name: "Adam Dierkens", }; }); - expect(await gh.getCommits('12345', '1234')).toMatchSnapshot(); + expect(await gh.getCommits("12345", "1234")).toMatchSnapshot(); }); - test('should match rebased commits to PRs with first commit', async () => { + test("should match rebased commits to PRs with first commit", async () => { const gh = new Release(git); getLatestReleaseInfo.mockImplementationOnce(() => { - throw new Error('no releases yet'); + throw new Error("no releases yet"); }); - getCommitDate.mockReturnValueOnce('2019-01-16'); + getCommitDate.mockReturnValueOnce("2019-01-16"); searchRepo.mockReturnValueOnce({ items: [{ number: 123 }] }); getPullRequest.mockReturnValueOnce({ data: { number: 123, - merge_commit_sha: '1a2b', - labels: [{ name: 'skip-release' }, { name: 'minor' }] - } + merge_commit_sha: "1a2b", + labels: [{ name: "skip-release" }, { name: "minor" }], + }, }); getGitLog.mockReturnValueOnce( await logParse.normalizeCommits([ - makeCommitFromMsg('Feature (#124)'), - makeCommitFromMsg('I was rebased', { - hash: '1a2b' - }) + makeCommitFromMsg("Feature (#124)"), + makeCommitFromMsg("I was rebased", { + hash: "1a2b", + }), ]) ); getUserByUsername.mockImplementationOnce(() => { return { - login: 'adam', - name: 'Adam Dierkens' + login: "adam", + name: "Adam Dierkens", }; }); - expect(await gh.getCommits('12345', '1234')).toMatchSnapshot(); + expect(await gh.getCommits("12345", "1234")).toMatchSnapshot(); }); - test('should omit commits that have already been released', async () => { + test("should omit commits that have already been released", async () => { const gh = new Release(git); - jest.spyOn(console, 'log').mockImplementationOnce(() => {}); + jest.spyOn(console, "log").mockImplementationOnce(() => {}); getLatestReleaseInfo.mockReturnValueOnce({ - published_at: '2019-01-16' + published_at: "2019-01-16", }); searchRepo.mockReturnValueOnce({ items: [{ number: 123 }] }); getPullRequest.mockReturnValueOnce({ data: { number: 123, - merge_commit_sha: '1a2b', - labels: [{ name: 'skip-release' }, { name: 'minor' }] - } + merge_commit_sha: "1a2b", + labels: [{ name: "skip-release" }, { name: "minor" }], + }, }); getGitLog.mockReturnValueOnce( await logParse.normalizeCommits([ - makeCommitFromMsg('Feature (#124)'), - makeCommitFromMsg('I was released previously', { - hash: '1a2b' - }) + makeCommitFromMsg("Feature (#124)"), + makeCommitFromMsg("I was released previously", { + hash: "1a2b", + }), ]) ); - exec.mockReturnValueOnce('0'); + exec.mockReturnValueOnce("0"); exec.mockImplementationOnce(() => { throw new Error(); }); - - expect(await gh.getCommits('12345', '1234')).toMatchSnapshot(); + + expect(await gh.getCommits("12345", "1234")).toMatchSnapshot(); }); - test('should include PR opener in authors (in case of external rebase)', async () => { + test("should include PR opener in authors (in case of external rebase)", async () => { const gh = new Release(git); const info = { data: { number: 124, - merge_commit_sha: '1a2b', - labels: [{ name: 'skip-release' }, { name: 'minor' }], - user: {login: 'renovate'} - } - } + merge_commit_sha: "1a2b", + labels: [{ name: "skip-release" }, { name: "minor" }], + user: { login: "renovate" }, + }, + }; getLatestReleaseInfo.mockReturnValueOnce({ - published_at: '2019-01-16' + published_at: "2019-01-16", }); searchRepo.mockReturnValueOnce({ items: [{ number: 124 }] }); getPullRequest.mockReturnValueOnce(info); getGitLog.mockReturnValueOnce( - await logParse.normalizeCommits([ - makeCommitFromMsg('Feature (#124)') - ]) + await logParse.normalizeCommits([makeCommitFromMsg("Feature (#124)")]) ); - getPr.mockReturnValueOnce(Promise.resolve(info)) + getPr.mockReturnValueOnce(Promise.resolve(info)); getUserByUsername.mockReturnValueOnce({ - name: 'Renovate', - email: 'renovate@automation.com', - login: 'renovate' - }) - exec.mockReturnValueOnce('1'); + name: "Renovate", + email: "renovate@automation.com", + login: "renovate", + }); + exec.mockReturnValueOnce("1"); + + expect(await gh.getCommits("12345", "1234")).toMatchSnapshot(); + }); + + test("should use latest title of PR", async () => { + const gh = new Release(git); + + const info = { + data: { + number: 124, + merge_commit_sha: "1a2b", + labels: [{ name: "skip-release" }, { name: "minor" }], + user: { login: "renovate" }, + title: "Updated Title", + }, + }; + + getLatestReleaseInfo.mockReturnValueOnce({ + published_at: "2019-01-16", + }); + searchRepo.mockReturnValueOnce({ items: [{ number: 124 }] }); + getPullRequest.mockReturnValueOnce(info); + getGitLog.mockReturnValueOnce( + await logParse.normalizeCommits([makeCommitFromMsg("Feature (#124)")]) + ); + getPr.mockReturnValueOnce(Promise.resolve(info)); + getUserByUsername.mockImplementationOnce(() => { + return { + login: "adam", + name: "Adam Dierkens", + }; + }); + exec.mockReturnValueOnce("1"); - expect(await gh.getCommits('12345', '1234')).toMatchSnapshot(); + expect(await gh.getCommits("12345", "1234")).toMatchSnapshot(); }); }); - describe('addToChangelog', () => { + describe("addToChangelog", () => { test("creates new changelog if one didn't exist - from 0", async () => { const gh = new Release(git); await gh.addToChangelog( - '# My new Notes', - 'klajsdlfk4lj51l43k5hj234l', - 'v0.0.0' + "# My new Notes", + "klajsdlfk4lj51l43k5hj234l", + "v0.0.0" ); expect(writeSpy.mock.calls[0][1].includes(`# My new Notes`)).toBe(true); @@ -435,270 +466,270 @@ describe('Release', () => { test("creates new changelog if one didn't exist", async () => { const gh = new Release(git); - await gh.addToChangelog('# My new Notes', 'v1.0.0', 'v1.0.0'); + await gh.addToChangelog("# My new Notes", "v1.0.0", "v1.0.0"); expect(writeSpy.mock.calls[0][1].includes(`v1.0.1`)).toBe(true); }); - test('creates changelog with v in versions', async () => { + test("creates changelog with v in versions", async () => { const gh = new Release(git, { noVersionPrefix: true, - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: defaultLabels, - baseBranch: 'master' + baseBranch: "master", }); - await gh.addToChangelog('# My new Notes', '1.0.0', '1.0.0'); + await gh.addToChangelog("# My new Notes", "1.0.0", "1.0.0"); expect(writeSpy.mock.calls[0][1].includes(`1.0.1`)).toBe(true); }); - test('prepends to old changelog', async () => { + test("prepends to old changelog", async () => { const gh = new Release(git); existsSync.mockReturnValueOnce(true); - readResult = '# My old Notes'; + readResult = "# My old Notes"; await gh.addToChangelog( - '# My new Notes', - 'asdfasdlkfjlkj435l2j', - 'v0.0.0' + "# My new Notes", + "asdfasdlkfjlkj435l2j", + "v0.0.0" ); expect(writeSpy.mock.calls[0][1].includes(readResult)).toBe(true); }); }); - describe('generateReleaseNotes', () => { - test('should default to HEAD', async () => { + describe("generateReleaseNotes", () => { + test("should default to HEAD", async () => { const gh = new Release(git); - expect(await gh.generateReleaseNotes('1234')).toBe(''); + expect(await gh.generateReleaseNotes("1234")).toBe(""); }); - test('should use configured HEAD', async () => { + test("should use configured HEAD", async () => { const gh = new Release(git); - expect(await gh.generateReleaseNotes('1234', '123')).toBe(''); + expect(await gh.generateReleaseNotes("1234", "123")).toBe(""); }); - test('should include PR-less commits', async () => { + test("should include PR-less commits", async () => { const gh = new Release(git); const commits = [ { - hash: '1', - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', + hash: "1", + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", authors: [ { - name: 'Adam Dierkens', - email: 'adam@dierkens.com' - } + name: "Adam Dierkens", + email: "adam@dierkens.com", + }, ], - subject: 'I should be included\nBut this should not.' + subject: "I should be included\nBut this should not.", }, { - hash: '2', - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', + hash: "2", + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", authors: [ { - name: 'Adam Dierkens', - email: 'adam@dierkens.com' - } + name: "Adam Dierkens", + email: "adam@dierkens.com", + }, ], - subject: 'First Feature', + subject: "First Feature", pullRequest: { - number: '1235' - } + number: "1235", + }, }, { - hash: '3', - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', + hash: "3", + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", authors: [ { - name: 'Adam Dierkens', - email: 'adam@dierkens.com' - } + name: "Adam Dierkens", + email: "adam@dierkens.com", + }, ], - subject: 'Random Commit for pr 1235' - } + subject: "Random Commit for pr 1235", + }, ]; getGitLog.mockReturnValueOnce(commits); getCommitsForPR.mockReturnValueOnce(Promise.resolve(undefined)); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['minor']))); - getCommitsForPR.mockReturnValueOnce(Promise.resolve([{ sha: '3' }])); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["minor"]))); + getCommitsForPR.mockReturnValueOnce(Promise.resolve([{ sha: "3" }])); graphql.mockReturnValueOnce({ - hash_1: { edges: [] } + hash_1: { edges: [] }, }); - expect(await gh.generateReleaseNotes('1234', '123')).toMatchSnapshot(); + expect(await gh.generateReleaseNotes("1234", "123")).toMatchSnapshot(); }); - test('should get extra user data for login', async () => { + test("should get extra user data for login", async () => { const gh = new Release(git); const commits = [ { - hash: '1', + hash: "1", authors: [], - subject: 'I have a login attached', + subject: "I have a login attached", pullRequest: { - number: '1235' - } - } + number: "1235", + }, + }, ]; getGitLog.mockReturnValueOnce(commits); getPr.mockReturnValueOnce( Promise.resolve({ - data: { labels: [], user: { login: 'adierkens' } } + data: { labels: [], user: { login: "adierkens" } }, }) ); getUserByUsername.mockReturnValueOnce({ - login: 'adierkens', - name: 'Adam Dierkens' + login: "adierkens", + name: "Adam Dierkens", }); - expect(await gh.generateReleaseNotes('1234', '123')).toMatchSnapshot(); + expect(await gh.generateReleaseNotes("1234", "123")).toMatchSnapshot(); }); - test('should allow user to configure section headings', async () => { + test("should allow user to configure section headings", async () => { const gh = new Release(git); const commits = [ - makeCommitFromMsg('First (#1234)'), - makeCommitFromMsg('Second (#1235)'), - makeCommitFromMsg('Third (#1236)'), - makeCommitFromMsg('Fourth (#1237)') + makeCommitFromMsg("First (#1234)"), + makeCommitFromMsg("Second (#1235)"), + makeCommitFromMsg("Third (#1236)"), + makeCommitFromMsg("Fourth (#1237)"), ]; getGitLog.mockReturnValueOnce(commits); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['major']))); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['minor']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["major"]))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["minor"]))); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['documentation', 'internal'])) + Promise.resolve(mockLabels(["documentation", "internal"])) ); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['patch']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["patch"]))); - expect(await gh.generateReleaseNotes('1234', '123')).toMatchSnapshot(); + expect(await gh.generateReleaseNotes("1234", "123")).toMatchSnapshot(); }); - test('should match rebased commits to PRs', async () => { + test("should match rebased commits to PRs", async () => { const gh = new Release(git); getLatestReleaseInfo.mockReturnValueOnce({ - published_at: '2019-01-16' + published_at: "2019-01-16", }); getCommitsForPR.mockReturnValueOnce(Promise.resolve(undefined)); // Rebased PR will have different commit SHAs than the commits in base branch - getCommitsForPR.mockReturnValueOnce(Promise.resolve([{ sha: '1a1a' }])); + getCommitsForPR.mockReturnValueOnce(Promise.resolve([{ sha: "1a1a" }])); searchRepo.mockReturnValueOnce({ items: [{ number: 123 }] }); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['minor']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["minor"]))); getPullRequest.mockReturnValueOnce({ data: { number: 123, - merge_commit_sha: '1a2b', - labels: [{ name: 'skip-release' }, { name: 'minor' }] - } + merge_commit_sha: "1a2b", + labels: [{ name: "skip-release" }, { name: "minor" }], + }, }); getGitLog.mockReturnValueOnce([ - makeCommitFromMsg('Feature (#124)'), - makeCommitFromMsg('I was rebased\n\n', { - hash: '1a2b' + makeCommitFromMsg("Feature (#124)"), + makeCommitFromMsg("I was rebased\n\n", { + hash: "1a2b", }), { - hash: '1', - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'I am a commit to master' - } + hash: "1", + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "I am a commit to master", + }, ]); graphql.mockReturnValueOnce({ - hash_1: { edges: [] } + hash_1: { edges: [] }, }); - expect(await gh.generateReleaseNotes('12345', '1234')).toMatchSnapshot(); + expect(await gh.generateReleaseNotes("12345", "1234")).toMatchSnapshot(); }); - test('should match commits with related PRs', async () => { + test("should match commits with related PRs", async () => { const gh = new Release(git); getLatestReleaseInfo.mockReturnValueOnce({ - published_at: '2019-01-16' + published_at: "2019-01-16", }); getCommitsForPR.mockReturnValueOnce(Promise.resolve(undefined)); // Rebased PR will have different commit SHAs than the commits in base branch - getCommitsForPR.mockReturnValueOnce(Promise.resolve([{ sha: '1a1a' }])); + getCommitsForPR.mockReturnValueOnce(Promise.resolve([{ sha: "1a1a" }])); searchRepo.mockReturnValueOnce({ items: [{ number: 123 }] }); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['minor']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["minor"]))); getPullRequest.mockReturnValueOnce({ data: { number: 123, - merge_commit_sha: '1a2b', - labels: [{ name: 'skip-release' }, { name: 'minor' }] - } + merge_commit_sha: "1a2b", + labels: [{ name: "skip-release" }, { name: "minor" }], + }, }); getGitLog.mockReturnValueOnce( await logParse.normalizeCommits([ - makeCommitFromMsg('Feature (#124)'), + makeCommitFromMsg("Feature (#124)"), { - hash: '1', + hash: "1", files: [], - authorName: 'Adam Dierkens', - authorEmail: 'adam@dierkens.com', - subject: 'I am a commit with a related PR' - } + authorName: "Adam Dierkens", + authorEmail: "adam@dierkens.com", + subject: "I am a commit with a related PR", + }, ]) ); searchRepo.mockReturnValueOnce({ total_count: 1, - items: [{ labels: [{ name: 'patch' }] }] + items: [{ labels: [{ name: "patch" }] }], }); - expect(await gh.generateReleaseNotes('12345', '1234')).toMatchSnapshot(); + expect(await gh.generateReleaseNotes("12345", "1234")).toMatchSnapshot(); }); - test('should find matching PRs for shas through search', async () => { + test("should find matching PRs for shas through search", async () => { const gh = new Release(git); getGitLog.mockReturnValueOnce([ - makeCommitFromMsg('Doom Patrol enabled', { - hash: '1' + makeCommitFromMsg("Doom Patrol enabled", { + hash: "1", + }), + makeCommitFromMsg("Autobots roll out!", { + hash: "2", }), - makeCommitFromMsg('Autobots roll out!', { - hash: '2' - }) ]); graphql.mockReturnValueOnce({ hash_1: { edges: [ - { node: { labels: { edges: [{ node: { name: 'major' } }] } } } - ] - } + { node: { labels: { edges: [{ node: { name: "major" } }] } } }, + ], + }, }); // PR with no label, should become patch graphql.mockReturnValueOnce({ hash_2: { - edges: [{ node: { labels: undefined } }] - } + edges: [{ node: { labels: undefined } }], + }, }); - expect(await gh.generateReleaseNotes('1234', '123')).toMatchSnapshot(); + expect(await gh.generateReleaseNotes("1234", "123")).toMatchSnapshot(); }); - test('should find ignore closed prs', async () => { + test("should find ignore closed prs", async () => { const gh = new Release(git); getGitLog.mockReturnValueOnce([ - makeCommitFromMsg('Doom Patrol enabled', { - hash: '1' + makeCommitFromMsg("Doom Patrol enabled", { + hash: "1", + }), + makeCommitFromMsg("Autobots roll out!", { + hash: "2", }), - makeCommitFromMsg('Autobots roll out!', { - hash: '2' - }) ]); graphql.mockReturnValueOnce({ @@ -707,454 +738,454 @@ describe('Release', () => { { node: { labels: { - edges: [{ node: { name: 'major', state: 'CLOSED' } }] - } - } - } - ] - } + edges: [{ node: { name: "major", state: "CLOSED" } }], + }, + }, + }, + ], + }, }); // PR with no label, should become patch graphql.mockReturnValueOnce({ hash_2: { - edges: [{ node: { labels: undefined } }] - } + edges: [{ node: { labels: undefined } }], + }, }); - expect(await gh.generateReleaseNotes('1234', '123')).toMatchSnapshot(); + expect(await gh.generateReleaseNotes("1234", "123")).toMatchSnapshot(); }); - test('should include PRs merged to other PRs', async () => { + test("should include PRs merged to other PRs", async () => { const gh = new Release(git); getGitLog.mockReturnValueOnce([ - makeCommitFromMsg('Doom (#12343)', { - hash: '1' + makeCommitFromMsg("Doom (#12343)", { + hash: "1", }), - makeCommitFromMsg('Dino (#1235)', { - hash: '2' + makeCommitFromMsg("Dino (#1235)", { + hash: "2", + }), + makeCommitFromMsg("Foo Bar", { + hash: "3", }), - makeCommitFromMsg('Foo Bar', { - hash: '3' - }) ]); getCommitsForPR.mockReturnValue( Promise.resolve([ { - sha: '2', + sha: "2", commit: {}, author: { - name: 'Adam Dierkens', - email: 'adam@dierkens.com' - } - } + name: "Adam Dierkens", + email: "adam@dierkens.com", + }, + }, ]) ); getCommitsForPR.mockReturnValue( Promise.resolve([ { - sha: '2', + sha: "2", commit: {}, author: { - name: 'Adam Dierkens', - email: 'adam@dierkens.com' - } - } + name: "Adam Dierkens", + email: "adam@dierkens.com", + }, + }, ]) ); getCommitsForPR.mockReturnValue( Promise.resolve([ { - sha: '3', + sha: "3", commit: {}, author: { - name: 'Adam Dierkens', - email: 'adam@dierkens.com' - } - } + name: "Adam Dierkens", + email: "adam@dierkens.com", + }, + }, ]) ); getCommitsForPR.mockReturnValue( Promise.resolve([ { - sha: '3', + sha: "3", commit: {}, author: { - name: 'Adam Dierkens', - email: 'adam@dierkens.com' - } - } + name: "Adam Dierkens", + email: "adam@dierkens.com", + }, + }, ]) ); - expect(await gh.generateReleaseNotes('1234', '123')).toMatchSnapshot(); + expect(await gh.generateReleaseNotes("1234", "123")).toMatchSnapshot(); }); - test('should gracefully handle failed fetches to merged PRs', async () => { + test("should gracefully handle failed fetches to merged PRs", async () => { const gh = new Release(git); const commits = await logParse.normalizeCommits([ - makeCommitFromMsg('First'), - makeCommitFromMsg('Second (#123)') + makeCommitFromMsg("First"), + makeCommitFromMsg("Second (#123)"), ]); getGitLog.mockReturnValueOnce(commits); getCommitsForPR - .mockReturnValueOnce(Promise.reject(new Error('bah'))) - .mockReturnValueOnce(Promise.reject(new Error('bah'))); + .mockReturnValueOnce(Promise.reject(new Error("bah"))) + .mockReturnValueOnce(Promise.reject(new Error("bah"))); await expect( - gh.generateReleaseNotes('1234', '123') + gh.generateReleaseNotes("1234", "123") ).resolves.toBeDefined(); }); }); - describe('getSemverBump', () => { - test('default to patch', async () => { + describe("getSemverBump", () => { + test("default to patch", async () => { const gh = new Release(git); const commits = [ - makeCommitFromMsg('First'), - makeCommitFromMsg('Second'), - makeCommitFromMsg('Third') + makeCommitFromMsg("First"), + makeCommitFromMsg("Second"), + makeCommitFromMsg("Third"), ]; getGitLog.mockReturnValueOnce(commits); - expect(await gh.getSemverBump('1234')).toBe(SEMVER.patch); + expect(await gh.getSemverBump("1234")).toBe(SEMVER.patch); }); - test('should use higher version', async () => { + test("should use higher version", async () => { const gh = new Release(git); const commits = [ - makeCommitFromMsg('First (#1234)'), - makeCommitFromMsg('Second'), - makeCommitFromMsg('Third') + makeCommitFromMsg("First (#1234)"), + makeCommitFromMsg("Second"), + makeCommitFromMsg("Third"), ]; getGitLog.mockReturnValueOnce(commits); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['minor']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["minor"]))); - expect(await gh.getSemverBump('1234', '123')).toBe(SEMVER.minor); + expect(await gh.getSemverBump("1234", "123")).toBe(SEMVER.minor); }); - test('should not publish a release', async () => { + test("should not publish a release", async () => { const gh = new Release(git); const commits = [ - makeCommitFromMsg('First (#1234)'), - makeCommitFromMsg('Second (#1235)'), - makeCommitFromMsg('Third (#1236)') + makeCommitFromMsg("First (#1234)"), + makeCommitFromMsg("Second (#1235)"), + makeCommitFromMsg("Third (#1236)"), ]; getGitLog.mockReturnValueOnce(commits); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['skip-release', 'patch'])) + Promise.resolve(mockLabels(["skip-release", "patch"])) ); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['skip-release', 'patch'])) + Promise.resolve(mockLabels(["skip-release", "patch"])) ); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['skip-release', 'minor'])) + Promise.resolve(mockLabels(["skip-release", "minor"])) ); - expect(await gh.getSemverBump('1234', '123')).toBe(''); + expect(await gh.getSemverBump("1234", "123")).toBe(""); }); - test('should publish a release', async () => { + test("should publish a release", async () => { const gh = new Release(git); const commits = [ - makeCommitFromMsg('First (#1234)'), - makeCommitFromMsg('Second (#1235)'), - makeCommitFromMsg('Third (#1236)') + makeCommitFromMsg("First (#1234)"), + makeCommitFromMsg("Second (#1235)"), + makeCommitFromMsg("Third (#1236)"), ]; getGitLog.mockReturnValueOnce(commits); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['patch']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["patch"]))); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['skip-release', 'patch'])) + Promise.resolve(mockLabels(["skip-release", "patch"])) ); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['skip-release', 'minor'])) + Promise.resolve(mockLabels(["skip-release", "minor"])) ); - expect(await gh.getSemverBump('1234', '123')).toBe(SEMVER.minor); + expect(await gh.getSemverBump("1234", "123")).toBe(SEMVER.minor); }); - test('should default to publish a prepatch', async () => { + test("should default to publish a prepatch", async () => { const gh = new Release(git); const commits = [ - makeCommitFromMsg('First (#1234)'), - makeCommitFromMsg('Second (#1235)'), - makeCommitFromMsg('Third (#1236)') + makeCommitFromMsg("First (#1234)"), + makeCommitFromMsg("Second (#1235)"), + makeCommitFromMsg("Third (#1236)"), ]; getGitLog.mockReturnValueOnce(commits); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['skip-release']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["skip-release"]))); getPr.mockReturnValueOnce(Promise.resolve(mockLabels([]))); getPr.mockReturnValueOnce(Promise.resolve(mockLabels([]))); - expect(await gh.getSemverBump('1234', '123')).toBe(''); + expect(await gh.getSemverBump("1234", "123")).toBe(""); }); - test('should not publish a release in onlyPublishWithReleaseLabel without label', async () => { + test("should not publish a release in onlyPublishWithReleaseLabel without label", async () => { const gh = new Release(git, { onlyPublishWithReleaseLabel: true, - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: defaultLabels, - baseBranch: 'master' + baseBranch: "master", }); const commits = [ - makeCommitFromMsg('First (#1234)'), - makeCommitFromMsg('Second (#1235)'), - makeCommitFromMsg('Third (#1236)') + makeCommitFromMsg("First (#1234)"), + makeCommitFromMsg("Second (#1235)"), + makeCommitFromMsg("Third (#1236)"), ]; getGitLog.mockReturnValueOnce(commits); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['patch']))); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['major']))); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['patch']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["patch"]))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["major"]))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["patch"]))); - expect(await gh.getSemverBump('1234', '123')).toBe(''); + expect(await gh.getSemverBump("1234", "123")).toBe(""); }); - test('should publish a release in onlyPublishWithReleaseLabel with label', async () => { + test("should publish a release in onlyPublishWithReleaseLabel with label", async () => { const gh = new Release(git, { onlyPublishWithReleaseLabel: true, - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: defaultLabels, - baseBranch: 'master' + baseBranch: "master", }); const commits = [ - makeCommitFromMsg('First (#1234)'), - makeCommitFromMsg('Second (#1235)'), - makeCommitFromMsg('Third (#1236)') + makeCommitFromMsg("First (#1234)"), + makeCommitFromMsg("Second (#1235)"), + makeCommitFromMsg("Third (#1236)"), ]; getGitLog.mockReturnValueOnce(commits); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['release', 'patch'])) + Promise.resolve(mockLabels(["release", "patch"])) ); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['patch']))); - getPr.mockReturnValueOnce(Promise.resolve(mockLabels(['minor']))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["patch"]))); + getPr.mockReturnValueOnce(Promise.resolve(mockLabels(["minor"]))); - expect(await gh.getSemverBump('1234', '123')).toBe(SEMVER.minor); + expect(await gh.getSemverBump("1234", "123")).toBe(SEMVER.minor); }); - test('should be able to configure labels', async () => { + test("should be able to configure labels", async () => { const customLabels = [ ...defaultLabels, - { name: 'Version: Major', releaseType: SEMVER.major }, - { name: 'Version: Minor', releaseType: SEMVER.minor }, - { name: 'Version: Patch', releaseType: SEMVER.patch }, - { name: 'Deploy', releaseType: 'release' } + { name: "Version: Major", releaseType: SEMVER.major }, + { name: "Version: Minor", releaseType: SEMVER.minor }, + { name: "Version: Patch", releaseType: SEMVER.patch }, + { name: "Deploy", releaseType: "release" }, ] as ILabelDefinition[]; const gh = new Release(git, { onlyPublishWithReleaseLabel: true, - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: customLabels, - baseBranch: 'master' + baseBranch: "master", }); const commits = [ - makeCommitFromMsg('First (#1234)'), - makeCommitFromMsg('Second (#1235)'), - makeCommitFromMsg('Third (#1236)') + makeCommitFromMsg("First (#1234)"), + makeCommitFromMsg("Second (#1235)"), + makeCommitFromMsg("Third (#1236)"), ]; // Test default labels do nothing getGitLog.mockReturnValueOnce(commits); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['Version: Major'])) + Promise.resolve(mockLabels(["Version: Major"])) ); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['Version: Patch'])) + Promise.resolve(mockLabels(["Version: Patch"])) ); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['Version: Minor', 'release'])) + Promise.resolve(mockLabels(["Version: Minor", "release"])) ); - expect(await gh.getSemverBump('1234', '123')).toBe(''); + expect(await gh.getSemverBump("1234", "123")).toBe(""); getGitLog.mockReturnValueOnce(commits); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['Version: Minor', 'Deploy'])) + Promise.resolve(mockLabels(["Version: Minor", "Deploy"])) ); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['Version: Major'])) + Promise.resolve(mockLabels(["Version: Major"])) ); getPr.mockReturnValueOnce( - Promise.resolve(mockLabels(['Version: Patch'])) + Promise.resolve(mockLabels(["Version: Patch"])) ); - expect(await gh.getSemverBump('1234', '123')).toBe(SEMVER.major); + expect(await gh.getSemverBump("1234", "123")).toBe(SEMVER.major); }); }); - describe('addLabelsToProject', () => { - test('should add labels', async () => { + describe("addLabelsToProject", () => { + test("should add labels", async () => { const gh = new Release(git); const customLabels: ILabelDefinition[] = [ - { name: '1', description: 'major', releaseType: SEMVER.major }, - { name: '2', description: 'minor', releaseType: SEMVER.minor }, - { name: '3', description: 'patch', releaseType: SEMVER.patch } + { name: "1", description: "major", releaseType: SEMVER.major }, + { name: "2", description: "minor", releaseType: SEMVER.minor }, + { name: "3", description: "patch", releaseType: SEMVER.patch }, ]; await gh.addLabelsToProject(customLabels); expect(createLabel).toHaveBeenCalledWith({ - name: '1', - description: 'major', - releaseType: SEMVER.major + name: "1", + description: "major", + releaseType: SEMVER.major, }); expect(createLabel).toHaveBeenCalledWith({ - name: '2', - description: 'minor', - releaseType: SEMVER.minor + name: "2", + description: "minor", + releaseType: SEMVER.minor, }); expect(createLabel).toHaveBeenCalledWith({ - name: '3', - description: 'patch', - releaseType: SEMVER.patch + name: "3", + description: "patch", + releaseType: SEMVER.patch, }); }); - test('should log that it has created the labels', async () => { + test("should log that it has created the labels", async () => { const mockLogger = dummyLog(); - jest.spyOn(mockLogger.log, 'log').mockImplementation(); + jest.spyOn(mockLogger.log, "log").mockImplementation(); const gh = new Release( git, { - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: defaultLabels, - baseBranch: 'master' + baseBranch: "master", }, mockLogger ); const labels: ILabelDefinition[] = [ - { name: '3', description: 'three', releaseType: SEMVER.patch } + { name: "3", description: "three", releaseType: SEMVER.patch }, ]; await gh.addLabelsToProject(labels); - expect(mockLogger.log.log).toHaveBeenCalledWith('Created labels: 3'); + expect(mockLogger.log.log).toHaveBeenCalledWith("Created labels: 3"); expect(mockLogger.log.log).toHaveBeenCalledWith( - '\nYou can see these, and more at https://github.com/web/site/labels' + "\nYou can see these, and more at https://github.com/web/site/labels" ); }); - test('should not add old labels', async () => { + test("should not add old labels", async () => { const gh = new Release(git); const labels: ILabelDefinition[] = [ - { name: '1', description: 'major', releaseType: SEMVER.major }, - { name: '2', description: 'minor', releaseType: SEMVER.minor } + { name: "1", description: "major", releaseType: SEMVER.major }, + { name: "2", description: "minor", releaseType: SEMVER.minor }, ]; - getProjectLabels.mockReturnValueOnce(['1']); + getProjectLabels.mockReturnValueOnce(["1"]); await gh.addLabelsToProject(labels); expect(updateLabel).toHaveBeenCalledWith({ - name: '1', - description: 'major', - releaseType: SEMVER.major + name: "1", + description: "major", + releaseType: SEMVER.major, }); expect(createLabel).toHaveBeenCalledWith({ - description: 'minor', - name: '2', - releaseType: SEMVER.minor + description: "minor", + name: "2", + releaseType: SEMVER.minor, }); }); - test('should not add old labels - case sensitive', async () => { + test("should not add old labels - case sensitive", async () => { const gh = new Release(git); const labels: ILabelDefinition[] = [ - { name: 'major', description: '', releaseType: SEMVER.major }, - { name: 'Minor', description: '', releaseType: SEMVER.minor } + { name: "major", description: "", releaseType: SEMVER.major }, + { name: "Minor", description: "", releaseType: SEMVER.minor }, ]; - getProjectLabels.mockReturnValueOnce(['Major', 'minor']); + getProjectLabels.mockReturnValueOnce(["Major", "minor"]); await gh.addLabelsToProject(labels); expect(updateLabel).toHaveBeenCalledWith({ - name: 'major', - description: '', - releaseType: SEMVER.major + name: "major", + description: "", + releaseType: SEMVER.major, }); expect(updateLabel).toHaveBeenCalledWith({ - description: '', - name: 'Minor', - releaseType: SEMVER.minor + description: "", + name: "Minor", + releaseType: SEMVER.minor, }); }); - test('should add release label in onlyPublishWithReleaseLabel mode', async () => { + test("should add release label in onlyPublishWithReleaseLabel mode", async () => { let gh = new Release(git, { - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: defaultLabels, - baseBranch: 'master' + baseBranch: "master", }); const labels: ILabelDefinition[] = [ { - name: 'deploy', - description: 'release the code', - releaseType: 'release' - } + name: "deploy", + description: "release the code", + releaseType: "release", + }, ]; await gh.addLabelsToProject(labels); expect(createLabel).not.toHaveBeenCalledWith({ - name: 'deploy', - description: 'release the code', - releaseType: 'release' + name: "deploy", + description: "release the code", + releaseType: "release", }); gh = new Release(git, { onlyPublishWithReleaseLabel: true, - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: defaultLabels, - baseBranch: 'master' + baseBranch: "master", }); await gh.addLabelsToProject(labels); expect(createLabel).toHaveBeenCalledWith({ - name: 'deploy', - description: 'release the code', - releaseType: 'release' + name: "deploy", + description: "release the code", + releaseType: "release", }); }); - test('should add skip-release label not in onlyPublishWithReleaseLabel mode', async () => { + test("should add skip-release label not in onlyPublishWithReleaseLabel mode", async () => { let gh = new Release(git, { onlyPublishWithReleaseLabel: true, - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: defaultLabels, - baseBranch: 'master' + baseBranch: "master", }); const labels: ILabelDefinition[] = [ { - name: 'no!', - description: 'Do not create a release', - releaseType: 'skip' - } + name: "no!", + description: "Do not create a release", + releaseType: "skip", + }, ]; await gh.addLabelsToProject(labels); expect(createLabel).not.toHaveBeenCalledWith({ - name: 'no!', - description: 'Do not create a release', - releaseType: 'skip' + name: "no!", + description: "Do not create a release", + releaseType: "skip", }); gh = new Release(git, { - prereleaseBranches: ['next'], + prereleaseBranches: ["next"], labels: defaultLabels, - baseBranch: 'master' + baseBranch: "master", }); await gh.addLabelsToProject(labels); expect(createLabel).toHaveBeenCalledWith({ - description: 'Do not create a release', - name: 'no!', - releaseType: 'skip' + description: "Do not create a release", + name: "no!", + releaseType: "skip", }); }); }); diff --git a/packages/core/src/__tests__/remote.test.ts b/packages/core/src/__tests__/remote.test.ts index ceee9914c..984fe2d61 100644 --- a/packages/core/src/__tests__/remote.test.ts +++ b/packages/core/src/__tests__/remote.test.ts @@ -1,28 +1,28 @@ -import Auto from '../auto'; -import { dummyLog } from '../utils/logger'; +import Auto from "../auto"; +import { dummyLog } from "../utils/logger"; -const defaultRemote = 'git@github.foo.com'; +const defaultRemote = "git@github.foo.com"; const defaults = { - owner: 'foo', - repo: 'bar' + owner: "foo", + repo: "bar", }; -process.env.GH_TOKEN = 'XXXX'; +process.env.GH_TOKEN = "XXXX"; const reposGet = jest.fn(); -jest.mock('@octokit/rest', () => { +jest.mock("@octokit/rest", () => { const Octokit = class MockOctokit { static plugin = () => Octokit; authenticate = () => undefined; repos = { - get: reposGet + get: reposGet, }; hook = { - error: () => undefined + error: () => undefined, }; }; @@ -31,20 +31,20 @@ jest.mock('@octokit/rest', () => { const execSpy = jest.fn(); // @ts-ignore -jest.mock('../utils/exec-promise.ts', () => (...args) => execSpy(...args)); +jest.mock("../utils/exec-promise.ts", () => (...args) => execSpy(...args)); -describe('remote parsing', () => { - test('should fall back to origin when no git', async () => { +describe("remote parsing", () => { + test("should fall back to origin when no git", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); execSpy.mockReturnValue(Promise.resolve()); // @ts-ignore - expect(await auto.getRemote()).toBe('origin'); + expect(await auto.getRemote()).toBe("origin"); }); - test('should fall back to configured remote when no git', async () => { + test("should fall back to configured remote when no git", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); @@ -54,44 +54,44 @@ describe('remote parsing', () => { expect(await auto.getRemote()).toBe(defaultRemote); }); - test('should fall back to configured remote', async () => { + test("should fall back to configured remote", async () => { const auto = new Auto(defaults); auto.logger = dummyLog(); execSpy.mockReturnValue(Promise.resolve(defaultRemote)); auto.git = { getProject: () => {}, - verifyAuth: () => false + verifyAuth: () => false, } as any; // @ts-ignore expect(await auto.getRemote()).toBe(defaultRemote); }); - test('use html_url when authed', async () => { - const html_url = 'https://my.repo'; + test("use html_url when authed", async () => { + const html_url = "https://my.repo"; const auto = new Auto(defaults); auto.logger = dummyLog(); execSpy.mockReturnValue(Promise.resolve(defaultRemote)); auto.git = { getProject: () => ({ html_url }), - verifyAuth: (url: string) => url === html_url + verifyAuth: (url: string) => url === html_url, } as any; // @ts-ignore expect(await auto.getRemote()).toBe(html_url); }); - test('use fall back to default when not authed', async () => { - const html_url = 'https://my.repo'; + test("use fall back to default when not authed", async () => { + const html_url = "https://my.repo"; const auto = new Auto(defaults); auto.logger = dummyLog(); execSpy.mockReturnValue(Promise.resolve(defaultRemote)); auto.git = { getProject: () => ({ html_url }), - verifyAuth: () => false + verifyAuth: () => false, } as any; // @ts-ignore @@ -99,18 +99,18 @@ describe('remote parsing', () => { }); test("add token to url if html doesn't auth", async () => { - const html_url = 'https://my.repo'; + const html_url = "https://my.repo"; const auto = new Auto(defaults); - process.env.GH_TOKEN = 'XXXX'; + process.env.GH_TOKEN = "XXXX"; auto.logger = dummyLog(); execSpy.mockReturnValue(Promise.resolve(defaultRemote)); auto.git = { getProject: () => ({ html_url }), - verifyAuth: (url: string) => url !== html_url + verifyAuth: (url: string) => url !== html_url, } as any; // @ts-ignore - expect(await auto.getRemote()).toBe('https://XXXX@my.repo/'); + expect(await auto.getRemote()).toBe("https://XXXX@my.repo/"); }); }); diff --git a/packages/core/src/__tests__/semver.test.ts b/packages/core/src/__tests__/semver.test.ts index ef7408b5b..e2a0a2c63 100644 --- a/packages/core/src/__tests__/semver.test.ts +++ b/packages/core/src/__tests__/semver.test.ts @@ -1,59 +1,59 @@ -import { getVersionMap, defaultLabels } from '../release'; -import SEMVER, { calculateSemVerBump, getHigherSemverTag } from '../semver'; +import { getVersionMap, defaultLabels } from "../release"; +import SEMVER, { calculateSemVerBump, getHigherSemverTag } from "../semver"; const semverMap = getVersionMap([ ...defaultLabels, - { name: 'documentation', releaseType: 'skip' }, - { name: 'none', releaseType: 'none' } + { name: "documentation", releaseType: "skip" }, + { name: "none", releaseType: "none" }, ]); -test('ranks releases right', () => { - expect(getHigherSemverTag(SEMVER.major, 'minor')).toBe('major'); - expect(getHigherSemverTag(SEMVER.noVersion, 'bar')).toBe('patch'); - expect(getHigherSemverTag(SEMVER.minor, 'patch')).toBe('minor'); +test("ranks releases right", () => { + expect(getHigherSemverTag(SEMVER.major, "minor")).toBe("major"); + expect(getHigherSemverTag(SEMVER.noVersion, "bar")).toBe("patch"); + expect(getHigherSemverTag(SEMVER.minor, "patch")).toBe("minor"); }); -describe('calculateSemVerBump', () => { - test('should be able to use multiple labels for skip-release', () => { - expect(calculateSemVerBump([['skip-release', 'major']], semverMap)).toBe( +describe("calculateSemVerBump", () => { + test("should be able to use multiple labels for skip-release", () => { + expect(calculateSemVerBump([["skip-release", "major"]], semverMap)).toBe( SEMVER.noVersion ); - expect(calculateSemVerBump([['documentation', 'major']], semverMap)).toBe( + expect(calculateSemVerBump([["documentation", "major"]], semverMap)).toBe( SEMVER.noVersion ); - expect(calculateSemVerBump([['major']], semverMap)).toBe(SEMVER.major); + expect(calculateSemVerBump([["major"]], semverMap)).toBe(SEMVER.major); }); - test('should skip none sometimes', () => { - expect(calculateSemVerBump([['none']], semverMap)).toBe(SEMVER.noVersion); + test("should skip none sometimes", () => { + expect(calculateSemVerBump([["none"]], semverMap)).toBe(SEMVER.noVersion); - expect(calculateSemVerBump([['none', 'major']], semverMap)).toBe( + expect(calculateSemVerBump([["none", "major"]], semverMap)).toBe( SEMVER.major ); expect( - calculateSemVerBump([['none'], ['unknown'], ['documentation']], semverMap) + calculateSemVerBump([["none"], ["unknown"], ["documentation"]], semverMap) ).toBe(SEMVER.noVersion); }); - test('should release a patch for unlabeled pr merged along with none releases', () => { - expect(calculateSemVerBump([[], ['documentation']], semverMap)).toBe( + test("should release a patch for unlabeled pr merged along with none releases", () => { + expect(calculateSemVerBump([[], ["documentation"]], semverMap)).toBe( SEMVER.patch ); }); - test('should not skip things before none', () => { - expect(calculateSemVerBump([['none'], ['major']], semverMap)).toBe( + test("should not skip things before none", () => { + expect(calculateSemVerBump([["none"], ["major"]], semverMap)).toBe( SEMVER.major ); }); - test('should respect onlyPublishWithReleaseLabel when no labels present', () => { + test("should respect onlyPublishWithReleaseLabel when no labels present", () => { expect( calculateSemVerBump([[]], semverMap, { - onlyPublishWithReleaseLabel: true + onlyPublishWithReleaseLabel: true, }) ).toBe(SEMVER.noVersion); }); diff --git a/packages/core/src/__tests__/validate-config.test.ts b/packages/core/src/__tests__/validate-config.test.ts index 0b1935938..c14ac6aa4 100644 --- a/packages/core/src/__tests__/validate-config.test.ts +++ b/packages/core/src/__tests__/validate-config.test.ts @@ -3,271 +3,271 @@ import { validatePlugins, ValidatePluginHook, formatError, - validatePluginConfiguration -} from '../validate-config'; -import { AsyncSeriesBailHook } from 'tapable'; -import * as t from 'io-ts'; + validatePluginConfiguration, +} from "../validate-config"; +import { AsyncSeriesBailHook } from "tapable"; +import * as t from "io-ts"; -describe('formatError', () => { +describe("formatError", () => { test.each([ - { path: 'test', expectedType: 'number', value: '123' }, - { path: 'test', expectedType: 'string', value: 123 }, - { path: 'test', expectedType: 'string', value: ['123', 2, true] }, - { path: 'test', expectedType: 'number', value: { foo: 'bar' } } - ])('should format errors %#', configError => { + { path: "test", expectedType: "number", value: "123" }, + { path: "test", expectedType: "string", value: 123 }, + { path: "test", expectedType: "string", value: ["123", 2, true] }, + { path: "test", expectedType: "number", value: { foo: "bar" } }, + ])("should format errors %#", (configError) => { expect(formatError(configError)).toMatchSnapshot(); }); }); -describe('validateConfig', () => { - test('should not return errors when there are none', async () => { +describe("validateConfig", () => { + test("should not return errors when there are none", async () => { expect( await validateAutoRc({ - name: 'Andrew', - email: 'andrew@lisowski.com', - owner: 'bar', - repo: 'foo', + name: "Andrew", + email: "andrew@lisowski.com", + owner: "bar", + repo: "foo", comment: { delete: true, - edit: true + edit: true, }, changelog: { - message: 'foo' + message: "foo", }, canary: { - message: 'foo' + message: "foo", }, next: { - message: 'foo' + message: "foo", }, release: { - prerelease: true + prerelease: true, }, shipit: { - onlyGraduateWithReleaseLabel: true - } + onlyGraduateWithReleaseLabel: true, + }, }) ).toStrictEqual([]); }); - test('should not return errors when there are none - more complex', async () => { + test("should not return errors when there are none - more complex", async () => { expect(await validateAutoRc({ verbose: true })).toStrictEqual([]); expect(await validateAutoRc({ verbose: [true, true] })).toStrictEqual([]); }); - test('should not return errors when there are none - multiple type', async () => { + test("should not return errors when there are none - multiple type", async () => { expect(await validateAutoRc({ versionBranches: true })).toStrictEqual([]); - expect(await validateAutoRc({ versionBranches: 'foo-' })).toStrictEqual([]); + expect(await validateAutoRc({ versionBranches: "foo-" })).toStrictEqual([]); expect(await validateAutoRc({ versionBranches: 123 })).toStrictEqual([ { expectedType: '"boolean" or "string"', - path: 'versionBranches', - value: 123 - } + path: "versionBranches", + value: 123, + }, ]); }); - test('should catch misconfigured options', async () => { + test("should catch misconfigured options", async () => { expect( await validateAutoRc({ name: 123, - owner: 456 + owner: 456, }) ).toStrictEqual([ { expectedType: '"string"', - path: 'owner', - value: 456 + path: "owner", + value: 456, }, { expectedType: '"string"', - path: 'name', - value: 123 - } + path: "name", + value: 123, + }, ]); }); - test('should catch unknown options', async () => { + test("should catch unknown options", async () => { expect( await validateAutoRc({ - name: 'Andrew', - foo: 456 + name: "Andrew", + foo: 456, }) ).toMatchSnapshot(); }); - test('should validate labels', async () => { + test("should validate labels", async () => { expect( await validateAutoRc({ - name: 'Andrew', + name: "Andrew", labels: [ { - name: 'Version: Minor' + name: "Version: Minor", }, { - name: 'Version: Major', - changelogTitle: 'The API has changed:', - description: 'Add this label to a PR to create a major release', - color: 'blue', - releaseType: 'major' - } - ] + name: "Version: Major", + changelogTitle: "The API has changed:", + description: "Add this label to a PR to create a major release", + color: "blue", + releaseType: "major", + }, + ], }) ).toStrictEqual([]); }); - test('should catch errors in labels', async () => { + test("should catch errors in labels", async () => { expect( await validateAutoRc({ - name: 'Andrew', + name: "Andrew", labels: [ { - name: 'Version: Minor' + name: "Version: Minor", }, { - name: 'Version: Major', - changelogTitle: 123 - } - ] + name: "Version: Major", + changelogTitle: 123, + }, + ], }) ).toStrictEqual([ { expectedType: '"string"', - path: 'labels.1.changelogTitle', - value: 123 - } + path: "labels.1.changelogTitle", + value: 123, + }, ]); }); - test('should not fail w/plugin configuration', async () => { + test("should not fail w/plugin configuration", async () => { expect( await validateAutoRc({ plugins: [ [ - 'npm', + "npm", { - canaryScope: '@auto-canary' - } - ] + canaryScope: "@auto-canary", + }, + ], ], labels: [ { - name: 'dependencies', - changelogTitle: '🔩 Dependency Updates', - releasepe: 'none' + name: "dependencies", + changelogTitle: "🔩 Dependency Updates", + releasepe: "none", }, { - name: 'blog-post', - changelogTitle: '📚 Blog Post', - releaseType: 'none' - } - ] + name: "blog-post", + changelogTitle: "📚 Blog Post", + releaseType: "none", + }, + ], }) ).toMatchSnapshot(); }); - test('should not go too deep', async () => { + test("should not go too deep", async () => { expect( await validateAutoRc({ - name: 'Andrew', + name: "Andrew", labelz: [ { - name: 'Version: Minor' - } - ] + name: "Version: Minor", + }, + ], }) ).toMatchSnapshot(); }); - test('should error on invalid plugin config', async () => { + test("should error on invalid plugin config", async () => { expect( await validateAutoRc({ - name: 'Andrew', - plugins: [123, true] + name: "Andrew", + plugins: [123, true], }) ).toStrictEqual([ { expectedType: '"string" or "[string, any]"', - path: 'plugins.0', - value: 123 + path: "plugins.0", + value: 123, }, { expectedType: '"string" or "[string, any]"', - path: 'plugins.1', - value: true - } + path: "plugins.1", + value: true, + }, ]); }); - test('should handle basic plugin config', async () => { + test("should handle basic plugin config", async () => { expect( await validateAutoRc({ - name: 'Andrew', - plugins: ['npm', 'release'] + name: "Andrew", + plugins: ["npm", "release"], }) ).toStrictEqual([]); }); - test('should warn about required props', async () => { + test("should warn about required props", async () => { expect( await validateAutoRc({ - name: 'Andrew', - labels: [{}] + name: "Andrew", + labels: [{}], }) ).toStrictEqual([ { expectedType: '"string"', - path: 'labels.0.name', - value: undefined - } + path: "labels.0.name", + value: undefined, + }, ]); }); - test('should handle complex plugin config', async () => { + test("should handle complex plugin config", async () => { // NOTE: since these plugins aren't loaded they do not get their // options validated expect( await validateAutoRc({ - name: 'Andrew', + name: "Andrew", plugins: [ - ['npm', { forcePublish: true }], + ["npm", { forcePublish: true }], [ - 'released', + "released", { - label: ':shipit:' - } - ] - ] + label: ":shipit:", + }, + ], + ], }) ).toStrictEqual([]); }); - test('should validate plugin configuration', async () => { + test("should validate plugin configuration", async () => { const hook: ValidatePluginHook = new AsyncSeriesBailHook([ - 'name', - 'options' + "name", + "options", ]); - hook.tap('test', (name, options) => { - if (name === 'test-plugin') { + hook.tap("test", (name, options) => { + if (name === "test-plugin") { const errors: string[] = []; - if (options.label && typeof options.label !== 'string') { + if (options.label && typeof options.label !== "string") { errors.push( formatError({ - path: 'npm.label', - expectedType: 'string', - value: options.label + path: "npm.label", + expectedType: "string", + value: options.label, }) ); } - if (options.other && typeof options.other !== 'number') { + if (options.other && typeof options.other !== "number") { errors.push( formatError({ - path: 'npm.other', - expectedType: 'number', - value: options.other + path: "npm.other", + expectedType: "number", + value: options.other, }) ); } @@ -280,52 +280,52 @@ describe('validateConfig', () => { expect( await validatePlugins(hook, { - name: 'Andrew', + name: "Andrew", plugins: [ [ - 'test-plugin', + "test-plugin", { - label: 123 - } - ] - ] + label: 123, + }, + ], + ], }) ).toMatchSnapshot(); expect( await validatePlugins(hook, { - name: 'Andrew', + name: "Andrew", plugins: [ [ - 'test-plugin', + "test-plugin", { - label: 'foo', - other: 123 - } - ] - ] + label: "foo", + other: 123, + }, + ], + ], }) ).toStrictEqual([]); }); }); -describe('validatePlugin', () => { - test('should validate plugin configuration - nested objects', async () => { +describe("validatePlugin", () => { + test("should validate plugin configuration - nested objects", async () => { const hook: ValidatePluginHook = new AsyncSeriesBailHook([ - 'name', - 'options' + "name", + "options", ]); const pluginOptions = t.partial({ types: t.partial({ - docs: t.string - }) + docs: t.string, + }), }); - hook.tapPromise('test', async (name, options) => { - if (name === 'test-plugin') { + hook.tapPromise("test", async (name, options) => { + if (name === "test-plugin") { return validatePluginConfiguration( - 'test-plugin', + "test-plugin", pluginOptions, options ); @@ -336,32 +336,32 @@ describe('validatePlugin', () => { await validatePlugins(hook, { plugins: [ [ - 'test-plugin', + "test-plugin", { types: { - doc: 'foo' - } - } - ] - ] + doc: "foo", + }, + }, + ], + ], }) ).toMatchSnapshot(); }); - test('should not include redundant errors', async () => { + test("should not include redundant errors", async () => { const hook: ValidatePluginHook = new AsyncSeriesBailHook([ - 'name', - 'options' + "name", + "options", ]); const pluginOptions = t.partial({ - types: t.union([t.string, t.array(t.string)]) + types: t.union([t.string, t.array(t.string)]), }); - hook.tapPromise('test', async (name, options) => { - if (name === 'test-plugin') { + hook.tapPromise("test", async (name, options) => { + if (name === "test-plugin") { return validatePluginConfiguration( - 'test-plugin', + "test-plugin", pluginOptions, options ); @@ -372,40 +372,40 @@ describe('validatePlugin', () => { await validatePlugins(hook, { plugins: [ [ - 'test-plugin', + "test-plugin", { - types: ['foo', 'bar', true] - } - ] - ] + types: ["foo", "bar", true], + }, + ], + ], }) ).toStrictEqual([ { expectedType: '"string"', - path: 'test-plugin.types.2', - value: true - } + path: "test-plugin.types.2", + value: true, + }, ]); }); - test('should validate plugin configuration - array of objects', async () => { + test("should validate plugin configuration - array of objects", async () => { const hook: ValidatePluginHook = new AsyncSeriesBailHook([ - 'name', - 'options' + "name", + "options", ]); const pluginOptions = t.partial({ exclude: t.array( t.partial({ - name: t.string + name: t.string, }) - ) + ), }); - hook.tapPromise('test', async (name, options) => { - if (name === 'test-plugin') { + hook.tapPromise("test", async (name, options) => { + if (name === "test-plugin") { return validatePluginConfiguration( - 'test-plugin', + "test-plugin", pluginOptions, options ); @@ -416,12 +416,12 @@ describe('validatePlugin', () => { await validatePlugins(hook, { plugins: [ [ - 'test-plugin', + "test-plugin", { - exclude: [{ name: 'foo' }, { name: 'bar', extra: true }] - } - ] - ] + exclude: [{ name: "foo" }, { name: "bar", extra: true }], + }, + ], + ], }) ).toMatchSnapshot(); }); diff --git a/packages/core/src/auto-args.ts b/packages/core/src/auto-args.ts index 862619632..ab05a6b21 100644 --- a/packages/core/src/auto-args.ts +++ b/packages/core/src/auto-args.ts @@ -3,8 +3,8 @@ import { RepoInformation, GithubInformation, LogOptions, - ReleaseCalculationOptions -} from './types'; + ReleaseCalculationOptions, +} from "./types"; export interface ICreateLabelsOptions { /** Do not actually do anything */ @@ -35,7 +35,7 @@ export interface IPRStatusOptions { /** URL to attach to the checkmark */ url: string; /** The state to set the checkmark to */ - state: 'pending' | 'success' | 'error' | 'failure'; + state: "pending" | "success" | "error" | "failure"; /** The description to attach to the checkmark */ description: string; /** The context the check should be attached to */ @@ -90,7 +90,7 @@ export interface ICommentOptions { edit?: boolean; } -export type IPRBodyOptions = Omit; +export type IPRBodyOptions = Omit; export interface IShipItOptions { /** Do not actually do anything */ @@ -112,7 +112,7 @@ export interface ICanaryOptions { /** The build to attach the canary to */ build?: number; /** The message used when attaching the canary version to a PR */ - message?: string | 'false'; + message?: string | "false"; /** Always deploy a canary, even if the PR is marked as skip release */ force?: boolean; } diff --git a/packages/core/src/auto.ts b/packages/core/src/auto.ts index 0baba1cb8..f285a0c26 100644 --- a/packages/core/src/auto.ts +++ b/packages/core/src/auto.ts @@ -1,25 +1,25 @@ -import { Octokit } from '@octokit/rest'; -import dotenv from 'dotenv'; -import envCi from 'env-ci'; -import fs from 'fs'; -import path from 'path'; -import link from 'terminal-link'; -import icons from 'log-symbols'; -import chalk from 'chalk'; -import { eq, gt, lte, inc, parse, ReleaseType, major } from 'semver'; +import { Octokit } from "@octokit/rest"; +import dotenv from "dotenv"; +import envCi from "env-ci"; +import fs from "fs"; +import path from "path"; +import link from "terminal-link"; +import icons from "log-symbols"; +import chalk from "chalk"; +import { eq, gt, lte, inc, parse, ReleaseType, major } from "semver"; import { AsyncParallelHook, AsyncSeriesBailHook, SyncHook, SyncWaterfallHook, AsyncSeriesHook, - AsyncSeriesWaterfallHook -} from 'tapable'; -import endent from 'endent'; -import { parse as parseUrl, format } from 'url'; -import on from 'await-to-js'; + AsyncSeriesWaterfallHook, +} from "tapable"; +import endent from "endent"; +import { parse as parseUrl, format } from "url"; +import on from "await-to-js"; -import createHttpsProxyAgent from 'https-proxy-agent'; +import createHttpsProxyAgent from "https-proxy-agent"; import { ApiOptions, ICanaryOptions, @@ -32,37 +32,37 @@ import { IReleaseOptions, IShipItOptions, IVersionOptions, - INextOptions -} from './auto-args'; -import Changelog from './changelog'; -import { preVersionMap } from './semver'; -import Config from './config'; -import Git, { IGitOptions, IPRInfo } from './git'; -import InteractiveInit from './init'; -import LogParse, { IExtendedCommit } from './log-parse'; -import Release, { getVersionMap, ILabelDefinition } from './release'; -import SEMVER, { calculateSemVerBump, IVersionLabels } from './semver'; -import execPromise from './utils/exec-promise'; -import loadPlugin, { IPlugin } from './utils/load-plugins'; -import createLog, { ILogger, setLogLevel } from './utils/logger'; -import { makeHooks } from './utils/make-hooks'; -import { getCurrentBranch } from './utils/get-current-branch'; -import { buildSearchQuery, ISearchResult } from './match-sha-to-pr'; -import getRepository from './utils/get-repository'; + INextOptions, +} from "./auto-args"; +import Changelog from "./changelog"; +import { preVersionMap } from "./semver"; +import Config from "./config"; +import Git, { IGitOptions, IPRInfo } from "./git"; +import InteractiveInit from "./init"; +import LogParse, { IExtendedCommit } from "./log-parse"; +import Release, { getVersionMap, ILabelDefinition } from "./release"; +import SEMVER, { calculateSemVerBump, IVersionLabels } from "./semver"; +import execPromise from "./utils/exec-promise"; +import loadPlugin, { IPlugin } from "./utils/load-plugins"; +import createLog, { ILogger, setLogLevel } from "./utils/logger"; +import { makeHooks } from "./utils/make-hooks"; +import { getCurrentBranch } from "./utils/get-current-branch"; +import { buildSearchQuery, ISearchResult } from "./match-sha-to-pr"; +import getRepository from "./utils/get-repository"; import { RepoInformation, AuthorInformation, AutoRc, - LoadedAutoRc -} from './types'; + LoadedAutoRc, +} from "./types"; import { validateAutoRc, validatePlugins, ValidatePluginHook, - formatError -} from './validate-config'; -import { omit } from './utils/omit'; -import { execSync } from 'child_process'; + formatError, +} from "./validate-config"; +import { omit } from "./utils/omit"; +import { execSync } from "child_process"; const proxyUrl = process.env.https_proxy || process.env.http_proxy; const env = envCi(); @@ -85,7 +85,7 @@ interface TestingToken { token?: string; } -type ShipitContext = 'canary' | 'next' | 'latest' | 'old'; +type ShipitContext = "canary" | "next" | "latest" | "old"; interface ShipitInfo { /** The Version published when shipit ran */ @@ -106,7 +106,7 @@ const makeDetail = (summary: string, body: string) => endent` `; -type ShipitRelease = 'latest' | 'old' | 'next' | 'canary'; +type ShipitRelease = "latest" | "old" | "next" | "canary"; interface BeforeShipitContext { /** The type of release that will be made when shipit runs. */ @@ -233,7 +233,7 @@ export interface IAutoHooks { /** Load the .env file into process.env. Useful for local usage. */ const loadEnv = () => { - const envFile = path.resolve(process.cwd(), '.env'); + const envFile = path.resolve(process.cwd(), ".env"); if (!fs.existsSync(envFile)) { return; @@ -248,7 +248,7 @@ const loadEnv = () => { /** Get the pr number from user input or the CI env. */ function getPrNumberFromEnv(pr?: number) { - const envPr = 'pr' in env && Number(env.pr); + const envPr = "pr" in env && Number(env.pr); const prNumber = pr || envPr; return prNumber; @@ -269,23 +269,23 @@ export function determineNextVersion( tag: string ) { const next = - inc(lastVersion, `pre${bump}` as ReleaseType, tag) || 'prerelease'; + inc(lastVersion, `pre${bump}` as ReleaseType, tag) || "prerelease"; return lte(next, currentVersion) - ? inc(currentVersion, 'prerelease', tag) || 'prerelease' + ? inc(currentVersion, "prerelease", tag) || "prerelease" : next; } /** Print the current version of "auto" */ export function getAutoVersion() { - const packagePath = path.join(__dirname, '../package.json'); - const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8')); + const packagePath = path.join(__dirname, "../package.json"); + const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); return packageJson.version; } /** Escape a string for use in a Regex */ function escapeRegExp(str: string) { - return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string } /** @@ -321,47 +321,50 @@ export default class Auto { /** Initialize auto and it's environment */ constructor(options: ApiOptions = {}) { this.options = options; - this.baseBranch = options.baseBranch || 'master'; + this.baseBranch = options.baseBranch || "master"; setLogLevel( Array.isArray(options.verbose) && options.verbose.length > 1 - ? 'veryVerbose' + ? "veryVerbose" : options.verbose - ? 'verbose' + ? "verbose" : undefined ); this.logger = createLog(); this.hooks = makeHooks(); this.hooks.getRepository.tapPromise( - 'Get repo info from origin', + "Get repo info from origin", getRepository ); - this.hooks.onCreateRelease.tap('Link onCreateChangelog', release => { + this.hooks.onCreateRelease.tap("Link onCreateChangelog", (release) => { release.hooks.onCreateChangelog.tap( - 'Link onCreateChangelog', + "Link onCreateChangelog", (changelog, version) => { this.hooks.onCreateChangelog.call(changelog, version); } ); }); - this.hooks.onCreateRelease.tap('Link onCreateLogParse', release => { - release.hooks.onCreateLogParse.tap('Link onCreateLogParse', logParse => { - this.hooks.onCreateLogParse.call(logParse); - }); + this.hooks.onCreateRelease.tap("Link onCreateLogParse", (release) => { + release.hooks.onCreateLogParse.tap( + "Link onCreateLogParse", + (logParse) => { + this.hooks.onCreateLogParse.call(logParse); + } + ); }); this.hooks.beforeCommitChangelog.tapPromise( - 'Old Version Branches', + "Old Version Branches", async ({ bump }) => { if (bump === SEMVER.major && this.config?.versionBranches) { const branch = `${this.config.versionBranches}${major( await this.hooks.getPreviousVersion.promise() )}`; - await execPromise('git', [ - 'branch', - await this.git?.getLatestTagInBranch() + await execPromise("git", [ + "branch", + await this.git?.getLatestTagInBranch(), ]); - await execPromise('git', ['push', this.remote, branch]); + await execPromise("git", ["push", this.remote, branch]); } } ); @@ -370,32 +373,32 @@ export default class Auto { * Determine if repo is behind HEAD of current branch. We do this in * the "afterVersion" hook so the check happens as late as possible. */ - this.hooks.afterVersion.tapPromise('Check remote for commits', async () => { + this.hooks.afterVersion.tapPromise("Check remote for commits", async () => { // Credit from https://github.com/semantic-release/semantic-release/blob/b2b7b57fbd51af3fe25accdd6cd8499beb9005e5/lib/git.js#L179 // `true` is the HEAD of the current local branch is the same as the HEAD of the remote branch, falsy otherwise. try { - const heads = await execPromise('git', [ - 'ls-remote', - '--heads', + const heads = await execPromise("git", [ + "ls-remote", + "--heads", this.remote, - getCurrentBranch() + getCurrentBranch(), ]); const [, remoteHead] = heads.match(/^(\w+)?/) || []; if (remoteHead) { // This will throw if the branch is ahead of the current branch execSync(`git merge-base --is-ancestor ${remoteHead} HEAD`, { - stdio: 'ignore' + stdio: "ignore", }); } this.logger.verbose.info( - 'Current branch is up to date, proceeding with release' + "Current branch is up to date, proceeding with release" ); } catch (error) { // If we are behind or there is no match, exit and skip the release this.logger.log.warn( - 'Current commit is behind, skipping the release to avoid collisions.' + "Current commit is behind, skipping the release to avoid collisions." ); process.exit(0); } @@ -403,7 +406,7 @@ export default class Auto { loadEnv(); - this.logger.verbose.info('ENV:', env); + this.logger.verbose.info("ENV:", env); } /** @@ -411,7 +414,7 @@ export default class Auto { * plugins take precedence. */ private loadDefaultBehavior() { - this.hooks.makeRelease.tapPromise('Default', async options => { + this.hooks.makeRelease.tapPromise("Default", async (options) => { if (options.dryRun) { const bump = await this.getVersion({ from: options.from }); @@ -444,12 +447,12 @@ export default class Auto { const configLoader = new Config(this.logger); const userConfig = await configLoader.loadConfig(); - this.logger.verbose.success('Loaded `auto` with config:', userConfig); + this.logger.verbose.success("Loaded `auto` with config:", userConfig); // Allow plugins to be overriden for testing this.config = { ...userConfig, - plugins: this.options.plugins || userConfig.plugins + plugins: this.options.plugins || userConfig.plugins, }; this.loadPlugins(this.config!); this.loadDefaultBehavior(); @@ -460,7 +463,7 @@ export default class Auto { const errors = [ ...(await validateAutoRc(this.config)), - ...(await validatePlugins(this.hooks.validateConfig, this.config)) + ...(await validatePlugins(this.hooks.validateConfig, this.config)), ]; if (errors.length) { @@ -468,17 +471,17 @@ export default class Auto { endent` Found configuration errors: - ${errors.map(formatError).join('\n')} + ${errors.map(formatError).join("\n")} `, - '\n' + "\n" ); this.logger.log.warn( - 'These errors are for the fully loaded configuration (this is why some paths might seem off).' + "These errors are for the fully loaded configuration (this is why some paths might seem off)." ); if (this.config.extends) { this.logger.log.warn( - 'Some errors might originate from an extend config.' + "Some errors might originate from an extend config." ); } @@ -489,8 +492,8 @@ export default class Auto { ...this.config, // This Line overrides config with args // eslint-disable-next-line @typescript-eslint/no-explicit-any - ...omit(this.options, ['_command', '_all', 'main'] as any), - baseBranch: this.baseBranch + ...omit(this.options, ["_command", "_all", "main"] as any), + baseBranch: this.baseBranch, }; this.config = config; const repository = await this.getRepo(config); @@ -499,11 +502,11 @@ export default class Auto { process.env.GH_TOKEN || process.env.GITHUB_TOKEN; - if (!token || token === 'undefined') { + if (!token || token === "undefined") { this.logger.log.error( - 'No GitHub was found. Make sure it is available on process.env.GH_TOKEN.' + "No GitHub was found. Make sure it is available on process.env.GH_TOKEN." ); - throw new Error('GitHub token not found!'); + throw new Error("GitHub token not found!"); } const githubOptions = { @@ -512,9 +515,9 @@ export default class Auto { ...repository, token, agent: proxyUrl ? createHttpsProxyAgent(proxyUrl) : undefined, - baseUrl: config.githubApi || 'https://api.github.com', + baseUrl: config.githubApi || "https://api.github.com", graphqlBaseUrl: - config.githubGraphqlApi || config.githubApi || 'https://api.github.com' + config.githubGraphqlApi || config.githubApi || "https://api.github.com", }; this.git = this.startGit(githubOptions as IGitOptions); @@ -533,24 +536,24 @@ export default class Auto { /** Determine the remote we have auth to push to. */ private async getRemote(): Promise { - const [, configuredRemote = 'origin'] = await on( - execPromise('git', ['remote', 'get-url', 'origin']) + const [, configuredRemote = "origin"] = await on( + execPromise("git", ["remote", "get-url", "origin"]) ); if (!this.git) { return configuredRemote; } - const { html_url } = (await this.git.getProject()) || { html_url: '' }; + const { html_url } = (await this.git.getProject()) || { html_url: "" }; const GIT_TOKENS: Record = { // GitHub Actions require the "x-access-token:" prefix for git access // https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#http-based-git-access-by-an-installation GITHUB_TOKEN: process.env.GITHUB_ACTION ? `x-access-token:${process.env.GITHUB_TOKEN}` - : undefined + : undefined, }; - const envVar = Object.keys(GIT_TOKENS).find(v => process.env[v]) || ''; + const envVar = Object.keys(GIT_TOKENS).find((v) => process.env[v]) || ""; const gitCredentials = GIT_TOKENS[envVar] || process.env.GH_TOKEN; if (gitCredentials) { @@ -559,21 +562,21 @@ export default class Auto { const urlWithAuth = format({ ...parsed, auth: gitCredentials, - host: `${hostname}${port ? `:${port}` : ''}` + host: `${hostname}${port ? `:${port}` : ""}`, }); if (await this.git.verifyAuth(urlWithAuth)) { - this.logger.veryVerbose.note('Using token + html URL as remote'); + this.logger.veryVerbose.note("Using token + html URL as remote"); return urlWithAuth; } } if (html_url && (await this.git.verifyAuth(html_url))) { - this.logger.veryVerbose.note('Using bare html URL as remote'); + this.logger.veryVerbose.note("Using bare html URL as remote"); return html_url; } - this.logger.veryVerbose.note('Using remote set in environment'); + this.logger.veryVerbose.note("Using remote set in environment"); return configuredRemote; } @@ -589,7 +592,7 @@ export default class Auto { return { hasError: false }; } - const [, gitVersion = ''] = await on(execPromise('git', ['--version'])); + const [, gitVersion = ""] = await on(execPromise("git", ["--version"])); const [noProject, project] = await on(this.git.getProject()); const repo = (await this.getRepo(this.config!)) || {}; const repoLink = link(`${repo.owner}/${repo.repo}`, project?.html_url!); @@ -600,27 +603,27 @@ export default class Auto { const [err, latestRelease] = await on(this.git.getLatestReleaseInfo()); const latestReleaseLink = latestRelease ? link(latestRelease.tag_name, latestRelease.html_url) - : ''; - const { headers } = await this.git.github.request('HEAD /'); + : ""; + const { headers } = await this.git.github.request("HEAD /"); const access = headers as Record; const rateLimitRefresh = new Date( - Number(access['x-ratelimit-reset']) * 1000 + Number(access["x-ratelimit-reset"]) * 1000 ); - const token = this.git.options.token || ''; + const token = this.git.options.token || ""; const tokenRefresh = `${rateLimitRefresh.toLocaleTimeString()} ${rateLimitRefresh.toLocaleDateString( - 'en-us' + "en-us" )}`; const projectLabels = await this.git.getProjectLabels(); const hasLabels = this.config?.labels.reduce((acc, label) => { if ( - label.name === 'release' && + label.name === "release" && !this.config?.onlyPublishWithReleaseLabel ) { return acc; } if ( - label.name === 'skip-release' && + label.name === "skip-release" && this.config?.onlyPublishWithReleaseLabel ) { return acc; @@ -643,7 +646,7 @@ export default class Auto { return icons.success; }; - console.log(''); + console.log(""); // prettier-ignore console.log(endent` ${chalk.underline.white('Environment Information:')} @@ -673,7 +676,7 @@ export default class Auto { ${logSuccess(!access['x-oauth-scopes'].includes('repo'))} Enabled Scopes: ${access['x-oauth-scopes']} ${logSuccess(Number(access['x-ratelimit-remaining']) === 0)} Rate Limit: ${access['x-ratelimit-remaining'] || '∞'}/${access['x-ratelimit-limit'] || '∞'} ${access['ratelimit-reset'] ? `(Renews @ ${tokenRefresh})` : ''} `); - console.log(''); + console.log(""); return { hasError }; } @@ -732,22 +735,22 @@ export default class Auto { labels = await this.git.getLabels(number); } else { const pulls = await this.git.getPullRequests({ - state: 'closed' + state: "closed", }); const lastMerged = pulls .sort( (a, b) => new Date(b.merged_at).getTime() - new Date(a.merged_at).getTime() ) - .find(pull => pull.merged_at); + .find((pull) => pull.merged_at); if (lastMerged) { - labels = lastMerged.labels.map(label => label.name); + labels = lastMerged.labels.map((label) => label.name); } } if (labels.length) { - console.log(labels.join('\n')); + console.log(labels.join("\n")); } } @@ -765,7 +768,7 @@ export default class Auto { let prNumber: number | undefined; try { - prNumber = this.getPrNumber('pr', pr); + prNumber = this.getPrNumber("pr", pr); } catch (error) { // default to sha if no PR found } @@ -773,26 +776,26 @@ export default class Auto { this.logger.verbose.info("Using command: 'pr-status'"); if (!sha && prNumber) { - this.logger.verbose.info('Getting commit SHA from PR.'); + this.logger.verbose.info("Getting commit SHA from PR."); const res = await this.git.getPullRequest(prNumber); sha = res.data.head.sha; } else if (!sha) { - this.logger.verbose.info('No PR found, getting commit SHA from HEAD.'); + this.logger.verbose.info("No PR found, getting commit SHA from HEAD."); sha = await this.git.getSha(); } - this.logger.verbose.info('Found PR SHA:', sha); + this.logger.verbose.info("Found PR SHA:", sha); const target_url = url; if (dryRun) { - this.logger.verbose.info('`pr` dry run complete.'); + this.logger.verbose.info("`pr` dry run complete."); } else { try { await this.git.createStatus({ ...options, sha, - target_url + target_url, }); } catch (error) { throw new Error( @@ -800,10 +803,10 @@ export default class Auto { ); } - this.logger.log.success('Posted status to Pull Request.'); + this.logger.log.success("Posted status to Pull Request."); } - this.logger.verbose.success('Finished `pr` command'); + this.logger.verbose.success("Finished `pr` command"); } /** @@ -819,7 +822,7 @@ export default class Auto { this.logger.verbose.info(`Using command: 'pr-check' for '${url}'`); const target_url = url; - const prNumber = this.getPrNumber('prCheck', pr); + const prNumber = this.getPrNumber("prCheck", pr); let msg; let sha; @@ -829,22 +832,22 @@ export default class Auto { const labels = await this.git.getLabels(prNumber); const labelValues = [...this.semVerLabels.values()]; - const releaseTag = labels.find(l => l === 'release'); + const releaseTag = labels.find((l) => l === "release"); const skipReleaseLabels = ( - this.config?.labels.filter(l => l.releaseType === 'skip') || [] - ).map(l => l.name); - const skipReleaseTag = labels.find(l => skipReleaseLabels.includes(l)); + this.config?.labels.filter((l) => l.releaseType === "skip") || [] + ).map((l) => l.name); + const skipReleaseTag = labels.find((l) => skipReleaseLabels.includes(l)); const semverTag = labels.find( - l => - labelValues.some(labelValue => labelValue.includes(l)) && + (l) => + labelValues.some((labelValue) => labelValue.includes(l)) && !skipReleaseLabels.includes(l) && - l !== 'release' + l !== "release" ); if (semverTag === undefined && !skipReleaseTag) { - throw new Error('No semver label!'); + throw new Error("No semver label!"); } this.logger.log.success( @@ -854,7 +857,7 @@ export default class Auto { let description; if (skipReleaseTag) { - description = 'PR will not create a release'; + description = "PR will not create a release"; } else if (releaseTag) { description = `PR will create release once merged - ${semverTag}`; } else { @@ -863,29 +866,29 @@ export default class Auto { msg = { description, - state: 'success' + state: "success", }; } catch (error) { msg = { description: error.message, - state: 'error' + state: "error", }; } - this.logger.verbose.info('Posting status to GitHub\n', msg); + this.logger.verbose.info("Posting status to GitHub\n", msg); if (dryRun) { - this.logger.verbose.info('`pr-check` dry run complete.'); + this.logger.verbose.info("`pr-check` dry run complete."); } else { try { await this.git.createStatus({ ...options, ...msg, target_url, - sha + sha, } as IPRInfo); - this.logger.log.success('Posted status to Pull Request.'); + this.logger.log.success("Posted status to Pull Request."); } catch (error) { throw new Error( `Failed to post status to Pull Request with error code ${error.status}` @@ -893,7 +896,7 @@ export default class Auto { } } - this.logger.verbose.success('Finished `pr-check` command'); + this.logger.verbose.success("Finished `pr-check` command"); } /** @@ -903,14 +906,14 @@ export default class Auto { * @param args - Options for the comment functionality */ async comment(args: ICommentOptions) { - const options = { ...this.getCommandDefault('comment'), ...args }; + const options = { ...this.getCommandDefault("comment"), ...args }; const { message, pr, - context = 'default', + context = "default", dryRun, delete: deleteFlag, - edit: editFlag + edit: editFlag, } = options; if (!this.git) { @@ -918,7 +921,7 @@ export default class Auto { } this.logger.verbose.info("Using command: 'comment'"); - const prNumber = this.getPrNumber('comment', pr); + const prNumber = this.getPrNumber("comment", pr); if (dryRun) { if (deleteFlag) { @@ -965,9 +968,9 @@ export default class Auto { const { message, pr, - context = 'default', + context = "default", dryRun, - delete: deleteFlag + delete: deleteFlag, } = options; if (!this.git) { @@ -975,7 +978,7 @@ export default class Auto { } this.logger.verbose.info("Using command: 'pr-body'"); - const prNumber = this.getPrNumber('pr-body', pr); + const prNumber = this.getPrNumber("pr-body", pr); if (dryRun) { if (deleteFlag) { @@ -989,7 +992,7 @@ export default class Auto { } } else { if (deleteFlag) { - await this.git.addToPrBody('', prNumber, context); + await this.git.addToPrBody("", prNumber, context); } if (message) { @@ -1028,7 +1031,7 @@ export default class Auto { /** Create a canary (or test) version of the project */ // eslint-disable-next-line complexity async canary(args: ICanaryOptions = {}): Promise { - const options = { ...this.getCommandDefault('canary'), ...args }; + const options = { ...this.getCommandDefault("canary"), ...args }; if (!this.git || !this.release) { throw this.createErrorMessage(); @@ -1051,11 +1054,11 @@ export default class Auto { pr = options.pr ? String(options.pr) : pr; build = options.build ? String(options.build) : build; - this.logger.verbose.info('Canary info found:', { pr, build }); + this.logger.verbose.info("Canary info found:", { pr, build }); - const from = (await this.git.shaExists('HEAD^')) ? 'HEAD^' : 'HEAD'; + const from = (await this.git.shaExists("HEAD^")) ? "HEAD^" : "HEAD"; const head = await this.release.getCommitsInRelease(from); - const labels = head.map(commit => commit.labels); + const labels = head.map((commit) => commit.labels); const version = calculateSemVerBump( labels, this.semVerLabels!, @@ -1064,13 +1067,13 @@ export default class Auto { if (version === SEMVER.noVersion && !options.force) { this.logger.log.info( - 'Skipping canary release due to PR being specifying no release. Use `auto canary --force` to override this setting' + "Skipping canary release due to PR being specifying no release. Use `auto canary --force` to override this setting" ); return; } - let canaryVersion = ''; - let newVersion = ''; + let canaryVersion = ""; + let newVersion = ""; if (pr) { canaryVersion = `${canaryVersion}.${pr}`; @@ -1089,10 +1092,10 @@ export default class Auto { `Published canary identifier would be: "-canary${canaryVersion}"` ); } else { - this.logger.verbose.info('Calling canary hook'); + this.logger.verbose.info("Calling canary hook"); const result = await this.hooks.canary.promise(version, canaryVersion); - if (typeof result === 'object' && 'error' in result) { + if (typeof result === "object" && "error" in result) { this.logger.log.warn(result.error); return; } @@ -1101,31 +1104,31 @@ export default class Auto { return; } - newVersion = typeof result === 'string' ? result : result.newVersion; + newVersion = typeof result === "string" ? result : result.newVersion; const messageHeader = ( - options.message || '📦 Published PR as canary version: %v' + options.message || "📦 Published PR as canary version: %v" ).replace( - '%v', - !newVersion || newVersion.includes('\n') + "%v", + !newVersion || newVersion.includes("\n") ? newVersion : `${newVersion}` ); - if (options.message !== 'false' && pr) { + if (options.message !== "false" && pr) { const message = - typeof result === 'string' + typeof result === "string" ? messageHeader : makeDetail(messageHeader, result.details); await this.prBody({ pr: Number(pr), - context: 'canary-version', - message + context: "canary-version", + message, }); } this.logger.log.success( - `Published canary version${newVersion ? `: ${newVersion}` : ''}` + `Published canary version${newVersion ? `: ${newVersion}` : ""}` ); } @@ -1138,7 +1141,7 @@ export default class Auto { } const commitsInRelease = await this.release.getCommits(latestTag); - return { newVersion, commitsInRelease, context: 'canary' }; + return { newVersion, commitsInRelease, context: "canary" }; } /** @@ -1146,7 +1149,7 @@ export default class Auto { * release to the default "next" branch. */ async next(args: INextOptions): Promise { - const options = { ...this.getCommandDefault('next'), ...args }; + const options = { ...this.getCommandDefault("next"), ...args }; if (!this.git || !this.release) { throw this.createErrorMessage(); @@ -1170,7 +1173,7 @@ export default class Auto { const lastTag = await this.git.getLatestTagInBranch(); const commits = await this.release.getCommitsInRelease(lastTag); const releaseNotes = await this.release.generateReleaseNotes(lastTag); - const labels = commits.map(commit => commit.labels); + const labels = commits.map((commit) => commit.labels); const bump = calculateSemVerBump(labels, this.semVerLabels!, this.config) || SEMVER.patch; @@ -1180,15 +1183,15 @@ export default class Auto { `Would have created prerelease version with: ${bump}` ); - return { newVersion: '', commitsInRelease: commits, context: 'next' }; + return { newVersion: "", commitsInRelease: commits, context: "next" }; } this.logger.verbose.info(`Calling "next" hook with: ${bump}`); const result = await this.hooks.next.promise([], bump); - const newVersion = result.join(', '); + const newVersion = result.join(", "); await Promise.all( - result.map(async prerelease => { + result.map(async (prerelease) => { const release = await this.git?.publish(releaseNotes, prerelease, true); this.logger.verbose.info(release); @@ -1198,39 +1201,39 @@ export default class Auto { newVersion: prerelease, commits, releaseNotes, - response: release + response: release, }); }) ); this.logger.log.success( - `Published next version${result.length > 1 ? `s` : ''}: ${newVersion}` + `Published next version${result.length > 1 ? `s` : ""}: ${newVersion}` ); const { pr } = await this.getPrEnvInfo(); if (pr) { - const message = options.message || 'Published prerelease version: %v'; + const message = options.message || "Published prerelease version: %v"; if (pr) { await this.prBody({ pr: Number(pr), - context: 'prerelease-version', + context: "prerelease-version", message: endent` # Version - ${message.replace('%v', result.map(r => `\`${r}\``).join('\n'))} + ${message.replace("%v", result.map((r) => `\`${r}\``).join("\n"))}
Changelog ${await this.release.generateReleaseNotes(lastRelease)}
- ` + `, }); } } - return { newVersion, commitsInRelease: commits, context: 'next' }; + return { newVersion, commitsInRelease: commits, context: "next" }; } /** Force a release to latest and bypass `shipit` safeguards. */ @@ -1248,8 +1251,8 @@ export default class Auto { */ async shipit(args: IShipItOptions = {}) { const options: IShipItOptions = { - ...this.getCommandDefault('shipit'), - ...args + ...this.getCommandDefault("shipit"), + ...args, }; if (!this.git || !this.release) { @@ -1258,8 +1261,8 @@ export default class Auto { this.logger.verbose.info("Using command: 'shipit'"); - const isPR = 'isPr' in env && env.isPr; - const from = (await this.git.shaExists('HEAD^')) ? 'HEAD^' : 'HEAD'; + const isPR = "isPr" in env && env.isPr; + const from = (await this.git.shaExists("HEAD^")) ? "HEAD^" : "HEAD"; const head = await this.release.getCommitsInRelease(from); // env-ci sets branch to target branch (ex: master) in some CI services. // so we should make sure we aren't in a PR just to be safe @@ -1268,11 +1271,11 @@ export default class Auto { const shouldGraduate = !options.onlyGraduateWithReleaseLabel || (options.onlyGraduateWithReleaseLabel && - head[0].labels.some(l => - this.semVerLabels?.get('release')?.includes(l) + head[0].labels.some((l) => + this.semVerLabels?.get("release")?.includes(l) )); const isPrereleaseBranch = this.config?.prereleaseBranches?.some( - branch => currentBranch === branch + (branch) => currentBranch === branch ); const publishPrerelease = isPrereleaseBranch || @@ -1285,34 +1288,34 @@ export default class Auto { isPR, shouldGraduate, isPrereleaseBranch, - publishPrerelease + publishPrerelease, }); let publishInfo: ShipitInfo | undefined; - let releaseType: ShipitRelease = 'canary'; + let releaseType: ShipitRelease = "canary"; if (isBaseBrach && shouldGraduate) { - releaseType = 'latest'; + releaseType = "latest"; } else if (this.inOldVersionBranch()) { - releaseType = 'old'; + releaseType = "old"; } else if (publishPrerelease) { - releaseType = 'next'; + releaseType = "next"; } await this.hooks.beforeShipIt.promise({ releaseType }); - if (releaseType === 'latest') { + if (releaseType === "latest") { publishInfo = await this.publishFullRelease(options); - } else if (releaseType === 'old') { + } else if (releaseType === "old") { publishInfo = await this.oldRelease(options); - } else if (releaseType === 'next') { + } else if (releaseType === "next") { publishInfo = await this.next(options); } else { publishInfo = await this.canary(options); if (options.dryRun) { this.logger.log.success( - 'Below is what would happen upon merge of the current branch into master' + "Below is what would happen upon merge of the current branch into master" ); await this.publishFullRelease(options); } @@ -1324,17 +1327,17 @@ export default class Auto { const { newVersion, commitsInRelease, context } = publishInfo; await this.hooks.afterShipIt.promise(newVersion, commitsInRelease, { - context + context, }); } /** Get the latest version number of the project */ async getCurrentVersion(lastRelease: string) { - this.hooks.getPreviousVersion.tap('None', () => { + this.hooks.getPreviousVersion.tap("None", () => { this.logger.veryVerbose.info( - 'No previous release found, using 0.0.0 as previous version.' + "No previous release found, using 0.0.0 as previous version." ); - return this.prefixRelease('0.0.0'); + return this.prefixRelease("0.0.0"); }); const lastVersion = await this.hooks.getPreviousVersion.promise(); @@ -1344,7 +1347,7 @@ export default class Auto { parse(lastVersion) && gt(lastRelease, lastVersion) ) { - this.logger.veryVerbose.info('Using latest release as previous version'); + this.logger.veryVerbose.info("Using latest release as previous version"); return lastRelease; } @@ -1367,11 +1370,11 @@ export default class Auto { const latestTag = await this.git?.getLatestTagInBranch(); const result = await this.publishFullRelease({ ...options, - from: latestTag + from: latestTag, }); if (result) { - result.context = 'old'; + result.context = "old"; } return result; @@ -1392,8 +1395,8 @@ export default class Auto { this.logger.log.success(`Calculated version bump: ${version}`); - if (version === '') { - this.logger.log.info('No version published.'); + if (version === "") { + this.logger.log.info("No version published."); return; } @@ -1406,13 +1409,13 @@ export default class Auto { if (!options.dryRun) { await this.checkClean(); - this.logger.verbose.info('Calling version hook'); + this.logger.verbose.info("Calling version hook"); await this.hooks.version.promise(version); - this.logger.verbose.info('Calling after version hook'); + this.logger.verbose.info("Calling after version hook"); await this.hooks.afterVersion.promise(); - this.logger.verbose.info('Calling publish hook'); + this.logger.verbose.info("Calling publish hook"); await this.hooks.publish.promise(version); - this.logger.verbose.info('Calling after publish hook'); + this.logger.verbose.info("Calling after publish hook"); await this.hooks.afterPublish.promise(); } @@ -1428,7 +1431,7 @@ export default class Auto { } } - return { newVersion, commitsInRelease, context: 'latest' }; + return { newVersion, commitsInRelease, context: "latest" }; } /** Get a pr number from user input or the env */ @@ -1453,18 +1456,18 @@ export default class Auto { /** Create a client to interact with git */ private startGit(gitOptions: IGitOptions) { if (!gitOptions.owner || !gitOptions.repo || !gitOptions.token) { - throw new Error('Must set owner, repo, and GitHub token.'); + throw new Error("Must set owner, repo, and GitHub token."); } - this.logger.verbose.info('Options contain repo information.'); + this.logger.verbose.info("Options contain repo information."); // So that --verbose can be used on public CIs const tokenlessArgs = { ...gitOptions, - token: `[Token starting with ${gitOptions.token.substring(0, 4)}]` + token: `[Token starting with ${gitOptions.token.substring(0, 4)}]`, }; - this.logger.verbose.info('Initializing GitHub API with:\n', tokenlessArgs); + this.logger.verbose.info("Initializing GitHub API with:\n", tokenlessArgs); return new Git( { owner: gitOptions.owner, @@ -1473,7 +1476,7 @@ export default class Auto { baseUrl: gitOptions.baseUrl, baseBranch: this.baseBranch, graphqlBaseUrl: gitOptions.graphqlBaseUrl, - agent: gitOptions.agent + agent: gitOptions.agent, }, this.logger ); @@ -1501,12 +1504,12 @@ export default class Auto { /** Make a changelog over a range of commits */ private async makeChangelog(args: IChangelogOptions = {}) { - const options = { ...this.getCommandDefault('changelog'), ...args }; + const options = { ...this.getCommandDefault("changelog"), ...args }; const { dryRun, from, to, - message = 'Update CHANGELOG.md [skip ci]' + message = "Update CHANGELOG.md [skip ci]", } = options; if (!this.release || !this.git) { @@ -1524,12 +1527,12 @@ export default class Auto { ); if (dryRun) { - this.logger.log.info('Potential Changelog Addition:\n', releaseNotes); - this.logger.verbose.info('`changelog` dry run complete.'); + this.logger.log.info("Potential Changelog Addition:\n", releaseNotes); + this.logger.verbose.info("`changelog` dry run complete."); return; } - this.logger.log.info('New Release Notes\n', releaseNotes); + this.logger.log.info("New Release Notes\n", releaseNotes); const currentVersion = await this.getCurrentVersion(lastRelease); @@ -1544,19 +1547,19 @@ export default class Auto { commits: await this.release.getCommits(lastRelease, to || undefined), releaseNotes, lastRelease, - currentVersion + currentVersion, }; await this.hooks.beforeCommitChangelog.promise(context); - await execPromise('git', ['commit', '-m', `"${message}"`, '--no-verify']); - this.logger.verbose.info('Committed new changelog.'); + await execPromise("git", ["commit", "-m", `"${message}"`, "--no-verify"]); + this.logger.verbose.info("Committed new changelog."); await this.hooks.afterAddToChangelog.promise(context); } /** Make a release over a range of commits */ private async makeRelease(args: IReleaseOptions = {}) { - const options = { ...this.getCommandDefault('release'), ...args }; + const options = { ...this.getCommandDefault("release"), ...args }; const { dryRun, from, useVersion, prerelease = false } = options; if (!this.release || !this.git) { @@ -1566,7 +1569,7 @@ export default class Auto { // This will usually resolve to something on head const [err, latestTag] = await on(this.git.getLatestTagInBranch()); - if (err && err.message.includes('No names found')) { + if (err && err.message.includes("No names found")) { this.logger.log.error( endent` Could not find any tags in the local repository. Exiting early. @@ -1575,7 +1578,7 @@ export default class Auto { If there are no tags there is nothing to release. If you don't use "shipit" ensure you tag your releases with the new version number. `, - '\n' + "\n" ); this.logger.verbose.error(err); return process.exit(1); @@ -1614,7 +1617,7 @@ export default class Auto { latestTag; if (!rawVersion) { - this.logger.log.error('Could not calculate next version from last tag.'); + this.logger.log.error("Could not calculate next version from last tag."); return; } @@ -1640,7 +1643,7 @@ export default class Auto { isPrerelease, newVersion, fullReleaseNotes: releaseNotes, - commits: commitsInRelease + commits: commitsInRelease, }); if (release) { @@ -1649,7 +1652,7 @@ export default class Auto { newVersion, commits: commitsInRelease, releaseNotes, - response: release + response: release, }); } @@ -1658,16 +1661,16 @@ export default class Auto { /** Check if `git status` is clean. */ readonly checkClean = async () => { - const status = await execPromise('git', ['status', '--porcelain']); + const status = await execPromise("git", ["status", "--porcelain"]); if (!status) { return; } - this.logger.log.error('Changed Files:\n', status); + this.logger.log.error("Changed Files:\n", status); throw new Error( - 'Working direction is not clean, make sure all files are committed' + "Working direction is not clean, make sure all files are committed" ); }; @@ -1677,7 +1680,7 @@ export default class Auto { throw this.createErrorMessage(); } - return this.config?.noVersionPrefix || release.startsWith('v') + return this.config?.noVersionPrefix || release.startsWith("v") ? release : `v${release}`; }; @@ -1695,12 +1698,12 @@ export default class Auto { return { /** The git user is already set in the current env */ system: true, - email: await execPromise('git', ['config', 'user.email']), - name: await execPromise('git', ['config', 'user.name']) + email: await execPromise("git", ["config", "user.email"]), + name: await execPromise("git", ["config", "user.name"]), }; } catch (error) { this.logger.verbose.warn( - 'Could not find git user or email configured in git config' + "Could not find git user or email configured in git config" ); if (!this.release) { @@ -1742,12 +1745,12 @@ export default class Auto { } if (user.email) { - await execPromise('git', ['config', 'user.email', `"${user.email}"`]); + await execPromise("git", ["config", "user.email", `"${user.email}"`]); this.logger.verbose.warn(`Set git email to ${user.email}`); } if (user.name) { - await execPromise('git', ['config', 'user.name', `"${user.name}"`]); + await execPromise("git", ["config", "user.name", `"${user.name}"`]); this.logger.verbose.warn(`Set git name to ${user.name}`); } } @@ -1771,7 +1774,7 @@ export default class Auto { - configure the repo for your package manager (ex: set "repository" in package.json) - configure your git remote 'origin' to point to your project on GitHub. `, - '' + "" ); process.exit(1); } @@ -1784,10 +1787,10 @@ export default class Auto { */ private loadPlugins(config: AutoRc) { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const defaultPlugins = [(process as any).pkg ? 'git-tag' : 'npm']; + const defaultPlugins = [(process as any).pkg ? "git-tag" : "npm"]; const pluginsPaths = [ - require.resolve('./plugins/filter-non-pull-request'), - ...(Array.isArray(config.plugins) ? config.plugins : defaultPlugins) + require.resolve("./plugins/filter-non-pull-request"), + ...(Array.isArray(config.plugins) ? config.plugins : defaultPlugins), ]; let extendedLocation: string | undefined; @@ -1800,13 +1803,13 @@ export default class Auto { } pluginsPaths - .map(plugin => + .map((plugin) => // eslint-disable-next-line @typescript-eslint/no-explicit-any - typeof plugin === 'string' ? ([plugin, {}] as [string, any]) : plugin + typeof plugin === "string" ? ([plugin, {}] as [string, any]) : plugin ) - .map(plugin => loadPlugin(plugin, this.logger, extendedLocation)) + .map((plugin) => loadPlugin(plugin, this.logger, extendedLocation)) .filter((plugin): plugin is IPlugin => Boolean(plugin)) - .forEach(plugin => { + .forEach((plugin) => { this.logger.verbose.info(`Using ${plugin.name} Plugin...`); plugin.apply(this); }); @@ -1818,10 +1821,10 @@ export default class Auto { let pr: string | undefined; let build: string | undefined; - if ('pr' in env && 'build' in env) { + if ("pr" in env && "build" in env) { ({ pr } = env); ({ build } = env); - } else if ('pr' in env && 'commit' in env) { + } else if ("pr" in env && "commit" in env) { ({ pr } = env); build = env.commit; } @@ -1857,21 +1860,21 @@ export default class Auto { } const commandConfig = this.config[name as keyof AutoRc]; - return typeof commandConfig === 'object' ? commandConfig : {}; + return typeof commandConfig === "object" ? commandConfig : {}; } } // Plugin Utils -export * from './auto-args'; -export { default as InteractiveInit } from './init'; -export { getCurrentBranch } from './utils/get-current-branch'; -export { validatePluginConfiguration } from './validate-config'; -export { ILogger } from './utils/logger'; -export { IPlugin } from './utils/load-plugins'; -export { default as Auto } from './auto'; -export { default as SEMVER } from './semver'; -export { default as execPromise } from './utils/exec-promise'; -export { default as getLernaPackages } from './utils/get-lerna-packages'; -export { default as inFolder } from './utils/in-folder'; -export { VersionLabel } from './release'; +export * from "./auto-args"; +export { default as InteractiveInit } from "./init"; +export { getCurrentBranch } from "./utils/get-current-branch"; +export { validatePluginConfiguration } from "./validate-config"; +export { ILogger } from "./utils/logger"; +export { IPlugin } from "./utils/load-plugins"; +export { default as Auto } from "./auto"; +export { default as SEMVER } from "./semver"; +export { default as execPromise } from "./utils/exec-promise"; +export { default as getLernaPackages } from "./utils/get-lerna-packages"; +export { default as inFolder } from "./utils/in-folder"; +export { VersionLabel } from "./release"; diff --git a/packages/core/src/changelog.ts b/packages/core/src/changelog.ts index 760a01113..c0e2283b1 100644 --- a/packages/core/src/changelog.ts +++ b/packages/core/src/changelog.ts @@ -1,14 +1,14 @@ -import { AsyncSeriesBailHook, AsyncSeriesWaterfallHook } from 'tapable'; -import { URL } from 'url'; -import join from 'url-join'; -import botList from '@auto-it/bot-list'; - -import { ICommitAuthor, IExtendedCommit } from './log-parse'; -import { ILabelDefinition } from './release'; -import { ILogger } from './utils/logger'; -import { makeChangelogHooks } from './utils/make-hooks'; -import { getCurrentBranch } from './utils/get-current-branch'; -import SEMVER from './semver'; +import { AsyncSeriesBailHook, AsyncSeriesWaterfallHook } from "tapable"; +import { URL } from "url"; +import join from "url-join"; +import botList from "@auto-it/bot-list"; + +import { ICommitAuthor, IExtendedCommit } from "./log-parse"; +import { ILabelDefinition } from "./release"; +import { ILogger } from "./utils/logger"; +import { makeChangelogHooks } from "./utils/make-hooks"; +import { getCurrentBranch } from "./utils/get-current-branch"; +import SEMVER from "./semver"; export interface IGenerateReleaseNotesOptions { /** Github repo owner (user) */ @@ -59,11 +59,11 @@ export interface IChangelogHooks { /** Determine how deep the markdown headers are in a string */ const getHeaderDepth = (line: string) => - line.split('').reduce((count, char) => (char === '#' ? count + 1 : count), 0); + line.split("").reduce((count, char) => (char === "#" ? count + 1 : count), 0); /** Filter for only commits that have a specific label */ const filterLabel = (commits: IExtendedCommit[], label: string) => - commits.filter(commit => commit.labels.includes(label)); + commits.filter((commit) => commit.labels.includes(label)); /** * Manages creating the "Release Notes" that are included in @@ -87,7 +87,7 @@ export default class Changelog { this.hooks = makeChangelogHooks(); const currentBranch = getCurrentBranch(); - if (!this.options.labels.find(l => l.name === 'pushToBaseBranch')) { + if (!this.options.labels.find((l) => l.name === "pushToBaseBranch")) { // Either put the name of a prerelease branch or the base-branch in the changelog const branch = (currentBranch && @@ -96,36 +96,36 @@ export default class Changelog { options.baseBranch; this.options.labels.push({ - name: 'pushToBaseBranch', + name: "pushToBaseBranch", changelogTitle: `⚠️ Pushed to \`${branch}\``, - description: 'N/A', - releaseType: SEMVER.patch + description: "N/A", + releaseType: SEMVER.patch, }); } } /** Load the default configuration */ loadDefaultHooks() { - this.hooks.renderChangelogAuthor.tap('Default', (author, commit) => + this.hooks.renderChangelogAuthor.tap("Default", (author, commit) => this.createUserLink(author, commit) ); - this.hooks.renderChangelogAuthorLine.tap('Default', (author, user) => { + this.hooks.renderChangelogAuthorLine.tap("Default", (author, user) => { const authorString = author.name && user ? `${author.name} (${user})` : user; return authorString ? `- ${authorString}` : undefined; }); - this.hooks.renderChangelogLine.tap('Default', ([commit, line]) => [ + this.hooks.renderChangelogLine.tap("Default", ([commit, line]) => [ commit, - line + line, ]); this.hooks.renderChangelogTitle.tap( - 'Default', + "Default", (label, changelogTitles) => `#### ${changelogTitles[label]}\n` ); - this.hooks.omitReleaseNotes.tap('Bots', commit => { + this.hooks.omitReleaseNotes.tap("Bots", (commit) => { if ( commit.authors.some( - author => + (author) => (author.name && botList.includes(author.name)) || (author.username && botList.includes(author.username)) ) @@ -138,31 +138,31 @@ export default class Changelog { /** Generate the release notes for a group of commits */ async generateReleaseNotes(commits: IExtendedCommit[]): Promise { if (commits.length === 0) { - return ''; + return ""; } - this.logger.verbose.info('Generating release notes for:\n', commits); + this.logger.verbose.info("Generating release notes for:\n", commits); const split = this.splitCommits(commits); - this.logger.verbose.info('Split commits into groups'); - this.logger.veryVerbose.info('\n', split); + this.logger.verbose.info("Split commits into groups"); + this.logger.veryVerbose.info("\n", split); const sections: string[] = []; const extraNotes = (await this.hooks.addToBody.promise([], commits)) || []; - extraNotes.filter(Boolean).forEach(note => sections.push(note)); + extraNotes.filter(Boolean).forEach((note) => sections.push(note)); await this.createReleaseNotesSection(commits, sections); - this.logger.verbose.info('Added release notes to changelog'); + this.logger.verbose.info("Added release notes to changelog"); this.authors = this.getAllAuthors(split); await this.createLabelSection(split, sections); - this.logger.verbose.info('Added groups to changelog'); + this.logger.verbose.info("Added groups to changelog"); await this.createAuthorSection(sections); - this.logger.verbose.info('Added authors to changelog'); + this.logger.verbose.info("Added authors to changelog"); - const result = sections.join('\n\n'); - this.logger.verbose.info('Successfully generated release notes.'); + const result = sections.join("\n\n"); + this.logger.verbose.info("Successfully generated release notes."); return result; } @@ -171,7 +171,7 @@ export default class Changelog { createUserLink(author: ICommitAuthor, commit: IExtendedCommit) { const githubUrl = new URL(this.options.baseUrl).origin; - if (author.username === 'invalid-email-address') { + if (author.username === "invalid-email-address") { return; } @@ -183,39 +183,39 @@ export default class Changelog { /** Split commits into changelogTitle sections. */ private splitCommits(commits: IExtendedCommit[]): ICommitSplit { let currentCommits = [...commits]; - const order = ['major', 'minor', 'patch']; + const order = ["major", "minor", "patch"]; const sections = this.options.labels - .filter(label => label.changelogTitle) + .filter((label) => label.changelogTitle) .sort((a, b) => { const bIndex = - order.indexOf(b.releaseType || '') + 1 || order.length + 1; + order.indexOf(b.releaseType || "") + 1 || order.length + 1; const aIndex = - order.indexOf(a.releaseType || '') + 1 || order.length + 1; + order.indexOf(a.releaseType || "") + 1 || order.length + 1; return aIndex - bIndex; }) .reduce((acc, item) => [...acc, item], []); const defaultPatchLabelName = this.getFirstLabelNameFromLabelKey( this.options.labels, - 'patch' + "patch" ); commits .filter( ({ labels }) => // in case pr commit doesn't contain a label for section inclusion - !sections.some(section => labels.includes(section.name)) || + !sections.some((section) => labels.includes(section.name)) || // in this case we auto attached a patch when it was merged - (labels[0] === 'released' && labels.length === 1) + (labels[0] === "released" && labels.length === 1) ) .map(({ labels }) => labels.push(defaultPatchLabelName)); return Object.assign( {}, - ...sections.map(label => { + ...sections.map((label) => { const matchedCommits = filterLabel(currentCommits, label.name); currentCommits = currentCommits.filter( - commit => !matchedCommits.includes(commit) + (commit) => !matchedCommits.includes(commit) ); return matchedCommits.length === 0 @@ -230,7 +230,7 @@ export default class Changelog { labels: ILabelDefinition[], labelKey: string ) { - return labels.find(l => l.releaseType === labelKey)?.name || labelKey; + return labels.find((l) => l.releaseType === labelKey)?.name || labelKey; } /** Create a list of users */ @@ -238,7 +238,7 @@ export default class Changelog { const result = new Set(); await Promise.all( - commit.authors.map(async rawAuthor => { + commit.authors.map(async (rawAuthor) => { const data = (this.authors!.find( ([, commitAuthor]) => (commitAuthor.name && @@ -264,31 +264,31 @@ export default class Changelog { }) ); - return [...result].join(' '); + return [...result].join(" "); } /** Transform a commit into a line in the changelog */ private async generateCommitNote(commit: IExtendedCommit) { const subject = commit.subject ? commit.subject - .split('\n')[0] + .split("\n")[0] .trim() - .replace('[skip ci]', '\\[skip ci\\]') - : ''; + .replace("[skip ci]", "\\[skip ci\\]") + : ""; - let pr = ''; + let pr = ""; if (commit.pullRequest?.number) { const prLink = join( this.options.baseUrl, - 'pull', + "pull", commit.pullRequest.number.toString() ); pr = `[#${commit.pullRequest.number}](${prLink})`; } const user = await this.createUserLinkList(commit); - return `- ${subject}${pr ? ` ${pr}` : ''}${user ? ` (${user})` : ''}`; + return `- ${subject}${pr ? ` ${pr}` : ""}${user ? ` (${user})` : ""}`; } /** Get all the authors in the provided commits */ @@ -302,17 +302,17 @@ export default class Changelog { ); return commits - .map(commit => + .map((commit) => commit.authors .filter( - author => - author.username !== 'invalid-email-address' && + (author) => + author.username !== "invalid-email-address" && (author.name || author.email || author.username) ) - .map(author => [commit, author] as [IExtendedCommit, ICommitAuthor]) + .map((author) => [commit, author] as [IExtendedCommit, ICommitAuthor]) ) .reduce((all, more) => [...all, ...more], []) - .sort(a => ('id' in a[1] ? 0 : 1)); + .sort((a) => ("id" in a[1] ? 0 : 1)); } /** Create a section in the changelog to showcase contributing authors */ @@ -320,13 +320,13 @@ export default class Changelog { const authors = new Set(); const authorsWithFullData = this.authors!.map( ([, author]) => author - ).filter(author => 'id' in author); + ).filter((author) => "id" in author); await Promise.all( this.authors!.map(async ([commit, author]) => { const info = authorsWithFullData.find( - u => + (u) => (author.name && u.name === author.name) || (author.email && u.email === author.email) ) || author; @@ -351,7 +351,7 @@ export default class Changelog { } let authorSection = `#### Authors: ${authors.size}\n\n`; - authorSection += [...authors].sort((a, b) => a.localeCompare(b)).join('\n'); + authorSection += [...authors].sort((a, b) => a.localeCompare(b)).join("\n"); sections.push(authorSection); } @@ -375,9 +375,9 @@ export default class Changelog { const lines = new Set(); await Promise.all( - labelCommits.map(async commit => { - const base = commit.pullRequest?.base || ''; - const branch = base.includes('/') ? base.split('/')[1] : base; + labelCommits.map(async (commit) => { + const base = commit.pullRequest?.base || ""; + const branch = base.includes("/") ? base.split("/")[1] : base; // We want to keep the release notes for a prerelease branch but // omit the changelog item @@ -387,7 +387,7 @@ export default class Changelog { const [, line] = await this.hooks.renderChangelogLine.promise([ commit, - await this.generateCommitNote(commit) + await this.generateCommitNote(commit), ]); lines.add(line); @@ -396,7 +396,9 @@ export default class Changelog { return [ title as string, - [...lines].sort((a, b) => a.split('\n').length - b.split('\n').length) + [...lines].sort( + (a, b) => a.split("\n").length - b.split("\n").length + ), ] as [string, string[]]; }) ); @@ -404,14 +406,14 @@ export default class Changelog { const mergedSections = labelSections.reduce( (acc, [title, commits]) => ({ ...acc, - [title]: [...(acc[title] || []), ...commits] + [title]: [...(acc[title] || []), ...commits], }), {} as Record ); Object.entries(mergedSections) - .map(([title, lines]) => [title, ...lines].join('\n')) - .map(section => sections.push(section)); + .map(([title, lines]) => [title, ...lines].join("\n")) + .map((section) => sections.push(section)); } /** Gather extra release notes to display at the top of the changelog */ @@ -423,10 +425,10 @@ export default class Changelog { return; } - let section = ''; + let section = ""; const visited = new Set(); const included = await Promise.all( - commits.map(async commit => { + commits.map(async (commit) => { const omit = await this.hooks.omitReleaseNotes.promise(commit); if (!omit) { @@ -435,7 +437,7 @@ export default class Changelog { }) ); - included.forEach(commit => { + included.forEach((commit) => { if (!commit) { return; } @@ -447,8 +449,8 @@ export default class Changelog { } const title = /^[#]{0,5}[ ]*[R|r]elease [N|n]otes$/; - const lines = pr.body.split('\n').map(line => line.replace(/\r$/, '')); - const notesStart = lines.findIndex(line => Boolean(line.match(title))); + const lines = pr.body.split("\n").map((line) => line.replace(/\r$/, "")); + const notesStart = lines.findIndex((line) => Boolean(line.match(title))); if (notesStart === -1 || visited.has(pr.number)) { return; @@ -456,13 +458,13 @@ export default class Changelog { const depth = getHeaderDepth(lines[notesStart]); visited.add(pr.number); - let notes = ''; + let notes = ""; for (let index = notesStart; index < lines.length; index++) { const line = lines[index]; const isTitle = line.match(title); - if (line.startsWith('#') && getHeaderDepth(line) <= depth && !isTitle) { + if (line.startsWith("#") && getHeaderDepth(line) <= depth && !isTitle) { break; } diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 7eb0cb2bd..cdc3168fb 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -1,12 +1,12 @@ -import { cosmiconfig } from 'cosmiconfig'; -import merge from 'deepmerge'; -import fetch from 'node-fetch'; -import * as path from 'path'; +import { cosmiconfig } from "cosmiconfig"; +import merge from "deepmerge"; +import fetch from "node-fetch"; +import * as path from "path"; -import { defaultLabels, getVersionMap, ILabelDefinition } from './release'; -import { ILogger } from './utils/logger'; -import tryRequire from './utils/try-require'; -import endent from 'endent'; +import { defaultLabels, getVersionMap, ILabelDefinition } from "./release"; +import { ILogger } from "./utils/logger"; +import tryRequire from "./utils/try-require"; +import endent from "endent"; // eslint-disable-next-line @typescript-eslint/no-explicit-any type ConfigObject = any; @@ -16,10 +16,10 @@ export function normalizeLabel( label: Partial ): Partial { const baseLabel = - defaultLabels.find(l => { + defaultLabels.find((l) => { let isBase = false; - if (label.releaseType !== 'none') { + if (label.releaseType !== "none") { isBase = l.releaseType === label.releaseType; } @@ -36,9 +36,9 @@ export function normalizeLabels(config: ConfigObject) { if (config.labels) { const userLabels: ILabelDefinition[] = config.labels.map(normalizeLabel); const baseLabels = defaultLabels.filter( - d => + (d) => !userLabels.some( - u => u.releaseType && u.releaseType === d.releaseType && u.overwrite + (u) => u.releaseType && u.releaseType === d.releaseType && u.overwrite ) ); @@ -63,14 +63,14 @@ export default class Config { * load the extends property, load the plugins and start the git remote interface. */ async loadConfig() { - const explorer = cosmiconfig('auto', { + const explorer = cosmiconfig("auto", { searchPlaces: [ `.autorc`, `.autorc.json`, `.autorc.yaml`, `.autorc.yml`, - 'package.json' - ] + "package.json", + ], }); const result = await explorer.search(); @@ -91,16 +91,16 @@ export default class Config { const labels = normalizeLabels(rawConfig); const semVerLabels = getVersionMap(labels); - this.logger.verbose.success('Using SEMVER labels:', '\n', semVerLabels); + this.logger.verbose.success("Using SEMVER labels:", "\n", semVerLabels); return { ...rawConfig, labels, - prereleaseBranches: rawConfig.prereleaseBranches || ['next'], + prereleaseBranches: rawConfig.prereleaseBranches || ["next"], versionBranches: - typeof rawConfig.versionBranches === 'boolean' - ? 'version-' - : rawConfig.versionBranches + typeof rawConfig.versionBranches === "boolean" + ? "version-" + : rawConfig.versionBranches, }; } @@ -120,11 +120,11 @@ export default class Config { auto: ConfigObject; }; - if (extend.endsWith('.js') || extend.endsWith('.mjs')) { - throw new Error('Extended config cannot be a JavaScript file'); + if (extend.endsWith(".js") || extend.endsWith(".mjs")) { + throw new Error("Extended config cannot be a JavaScript file"); } - if (extend.startsWith('http')) { + if (extend.startsWith("http")) { try { config = (await fetch(extend)).json(); this.logger.verbose.note(`${extend} found: ${config}`); @@ -132,10 +132,10 @@ export default class Config { error.message = `Failed to get extended config from ${extend} -- ${error.message}`; throw error; } - } else if (extend.startsWith('.')) { + } else if (extend.startsWith(".")) { config = tryRequire(extend); - if (extend.endsWith('package.json')) { + if (extend.endsWith("package.json")) { config = config?.auto; } diff --git a/packages/core/src/git.ts b/packages/core/src/git.ts index bbc49622f..bedca92f7 100644 --- a/packages/core/src/git.ts +++ b/packages/core/src/git.ts @@ -1,32 +1,32 @@ -import { graphql } from '@octokit/graphql'; -import { enterpriseCompatibility } from '@octokit/plugin-enterprise-compatibility'; -import path from 'path'; -import { retry } from '@octokit/plugin-retry'; -import { throttling } from '@octokit/plugin-throttling'; -import { Octokit } from '@octokit/rest'; -import gitlogNode from 'gitlogplus'; -import { HttpsProxyAgent } from 'https-proxy-agent'; -import tinyColor from 'tinycolor2'; -import { promisify } from 'util'; -import endent from 'endent'; -import on from 'await-to-js'; - -import { Memoize as memoize } from 'typescript-memoize'; - -import { ILabelDefinition } from './release'; -import verifyAuth from './utils/verify-auth'; -import execPromise from './utils/exec-promise'; -import { dummyLog, ILogger } from './utils/logger'; -import { gt } from 'semver'; -import { ICommit } from './log-parse'; -import { buildSearchQuery, ISearchResult } from './match-sha-to-pr'; +import { graphql } from "@octokit/graphql"; +import { enterpriseCompatibility } from "@octokit/plugin-enterprise-compatibility"; +import path from "path"; +import { retry } from "@octokit/plugin-retry"; +import { throttling } from "@octokit/plugin-throttling"; +import { Octokit } from "@octokit/rest"; +import gitlogNode from "gitlogplus"; +import { HttpsProxyAgent } from "https-proxy-agent"; +import tinyColor from "tinycolor2"; +import { promisify } from "util"; +import endent from "endent"; +import on from "await-to-js"; + +import { Memoize as memoize } from "typescript-memoize"; + +import { ILabelDefinition } from "./release"; +import verifyAuth from "./utils/verify-auth"; +import execPromise from "./utils/exec-promise"; +import { dummyLog, ILogger } from "./utils/logger"; +import { gt } from "semver"; +import { ICommit } from "./log-parse"; +import { buildSearchQuery, ISearchResult } from "./match-sha-to-pr"; const gitlog = promisify(gitlogNode); type Omit = Pick> & Partial>; -export type IPRInfo = Omit; +export type IPRInfo = Omit; export interface IGitOptions { /** Github repo owner (user) */ @@ -63,11 +63,11 @@ const makeIdentifier = (type: string, context: string) => /** Make an identifier for `auto comment` */ const makeCommentIdentifier = (context: string) => - makeIdentifier('COMMENT', context); + makeIdentifier("COMMENT", context); /** Make an identifier for `auto pr-body` */ const makePrBodyIdentifier = (context: string) => - makeIdentifier('PR BODY', context); + makeIdentifier("PR BODY", context); interface ThrottleOpts { /** The request object */ @@ -99,7 +99,7 @@ export default class Git { constructor(options: IGitOptions, logger: ILogger = dummyLog()) { this.logger = logger; this.options = options; - this.baseUrl = this.options.baseUrl || 'https://api.github.com'; + this.baseUrl = this.options.baseUrl || "https://api.github.com"; this.graphqlBaseUrl = this.options.graphqlBaseUrl || this.baseUrl; this.logger.veryVerbose.info(`Initializing GitHub with: ${this.baseUrl}`); const GitHub = Octokit.plugin(enterpriseCompatibility) @@ -108,7 +108,7 @@ export default class Git { this.github = new GitHub({ baseUrl: this.baseUrl, auth: this.options.token, - previews: ['symmetra-preview'], + previews: ["symmetra-preview"], request: { agent: this.options.agent }, throttle: { onRateLimit: (retryAfter: number, opts: ThrottleOpts) => { @@ -126,10 +126,10 @@ export default class Git { this.logger.log.error( `Went over abuse rate limit ${opts.method} ${opts.url}` ); - } - } + }, + }, }); - this.github.hook.error('request', error => { + this.github.hook.error("request", (error) => { if (error?.headers?.authorization) { delete error.headers.authorization; } @@ -148,7 +148,7 @@ export default class Git { async getLatestReleaseInfo() { const latestRelease = await this.github.repos.getLatestRelease({ owner: this.options.owner, - repo: this.options.repo + repo: this.options.repo, }); return latestRelease.data; @@ -164,7 +164,7 @@ export default class Git { 'Got response for "getLatestRelease":\n', latestRelease ); - this.logger.verbose.info('Got latest release:\n', latestRelease); + this.logger.verbose.info("Got latest release:\n", latestRelease); return latestRelease.tag_name; } catch (e) { @@ -181,24 +181,24 @@ export default class Git { /** Get the date a commit sha was created */ async getCommitDate(sha: string): Promise { - const date = await execPromise('git', ['show', '-s', '--format=%ci', sha]); - const [day, time, timezone] = date.split(' '); + const date = await execPromise("git", ["show", "-s", "--format=%ci", sha]); + const [day, time, timezone] = date.split(" "); return `${day}T${time}${timezone}`; } /** Get the first commit for the repo */ async getFirstCommit(): Promise { - const list = await execPromise('git', ['rev-list', 'HEAD']); - return list.split('\n').pop() as string; + const list = await execPromise("git", ["rev-list", "HEAD"]); + return list.split("\n").pop() as string; } /** Get the SHA of the latest commit */ async getSha(short?: boolean): Promise { - const result = await execPromise('git', [ - 'rev-parse', - short && '--short', - 'HEAD' + const result = await execPromise("git", [ + "rev-parse", + short && "--short", + "HEAD", ]); this.logger.verbose.info(`Got commit SHA from HEAD: ${result}`); @@ -209,7 +209,7 @@ export default class Git { /** Get the SHA of the latest commit */ async shaExists(sha?: string): Promise { try { - await execPromise('git', ['rev-parse', '--verify', sha]); + await execPromise("git", ["rev-parse", "--verify", sha]); return true; } catch (error) { return false; @@ -224,10 +224,10 @@ export default class Git { const args: Octokit.IssuesListLabelsOnIssueParams = { owner: this.options.owner, repo: this.options.repo, - issue_number: prNumber + issue_number: prNumber, }; - this.logger.verbose.info('Getting issue labels using:', args); + this.logger.verbose.info("Getting issue labels using:", args); try { const labels = await this.github.issues.listLabelsOnIssue(args); @@ -235,11 +235,11 @@ export default class Git { 'Got response for "listLabelsOnIssue":\n', labels ); - this.logger.verbose.info('Found labels on PR:\n', labels.data); + this.logger.verbose.info("Found labels on PR:\n", labels.data); - return labels.data.map(l => l.name); + return labels.data.map((l) => l.name); } catch (e) { - throw new GitAPIError('listLabelsOnIssue', args, e); + throw new GitAPIError("listLabelsOnIssue", args, e); } } @@ -251,17 +251,17 @@ export default class Git { const args: Octokit.IssuesListLabelsOnIssueParams = { owner: this.options.owner, repo: this.options.repo, - issue_number: prNumber + issue_number: prNumber, }; - this.logger.verbose.info('Getting issue info using:', args); + this.logger.verbose.info("Getting issue info using:", args); try { const info = await this.github.issues.get(args); this.logger.veryVerbose.info('Got response for "issues.get":\n', info); return info; } catch (e) { - throw new GitAPIError('getPr', args, e); + throw new GitAPIError("getPr", args, e); } } @@ -274,7 +274,7 @@ export default class Git { const info = await this.github.repos.getCommit({ owner: this.options.owner, repo: this.options.repo, - ref: sha + ref: sha, }); this.logger.veryVerbose.info( 'Got response for "repos.getCommit":\n', @@ -282,7 +282,7 @@ export default class Git { ); return info; } catch (e) { - throw new GitAPIError('getCommit', [], e); + throw new GitAPIError("getCommit", [], e); } } @@ -294,7 +294,7 @@ export default class Git { const args = { owner: this.options.owner, - repo: this.options.repo + repo: this.options.repo, }; try { @@ -306,32 +306,32 @@ export default class Git { 'Got response for "getProjectLabels":\n', labels ); - this.logger.verbose.info('Found labels on project:\n', labels); + this.logger.verbose.info("Found labels on project:\n", labels); - return labels.map(l => l.name); + return labels.map((l) => l.name); } catch (e) { - throw new GitAPIError('getProjectLabels', args, e); + throw new GitAPIError("getProjectLabels", args, e); } } /** Get the git log for a range of commits */ @memoize() - async getGitLog(start: string, end = 'HEAD'): Promise { + async getGitLog(start: string, end = "HEAD"): Promise { try { const log = await gitlog({ repo: process.cwd(), number: Number.MAX_SAFE_INTEGER, - fields: ['hash', 'authorName', 'authorEmail', 'rawBody'], + fields: ["hash", "authorName", "authorEmail", "rawBody"], branch: `${start.trim()}..${end.trim()}`, - execOptions: { maxBuffer: 1000 * 1024 } + execOptions: { maxBuffer: 1000 * 1024 }, }); - return log.map(commit => ({ + return log.map((commit) => ({ hash: commit.hash, authorName: commit.authorName, authorEmail: commit.authorEmail, subject: commit.rawBody!, - files: (commit.files || []).map(file => path.resolve(file)) + files: (commit.files || []).map((file) => path.resolve(file)), })); } catch (error) { const tag = error.match(/ambiguous argument '(\S+)\.\.\S+'/); @@ -359,7 +359,7 @@ export default class Git { try { const search = ( await this.github.search.users({ - q: `in:email ${email}` + q: `in:email ${email}`, }) ).data; @@ -376,7 +376,7 @@ export default class Git { if (!user) { return { - permission: 'none' + permission: "none", }; } @@ -385,14 +385,14 @@ export default class Git { await this.github.repos.getCollaboratorPermissionLevel({ owner: this.options.owner, repo: this.options.repo, - username: user.data.login + username: user.data.login, }) ).data; return { permission, user: user.data }; } catch (error) { this.logger.verbose.error(`Could not get permissions for token`); - return { permission: 'read', user: user.data }; + return { permission: "read", user: user.data }; } } @@ -401,7 +401,7 @@ export default class Git { async getUserByUsername(username: string) { try { const user = await this.github.users.getByUsername({ - username + username, }); return user.data; @@ -418,15 +418,15 @@ export default class Git { const args: Octokit.PullsGetParams = { owner: this.options.owner, repo: this.options.repo, - pull_number: pr + pull_number: pr, }; - this.logger.verbose.info('Getting pull request info using:', args); + this.logger.verbose.info("Getting pull request info using:", args); const result = await this.github.pulls.get(args); - this.logger.veryVerbose.info('Got pull request data\n', result); - this.logger.verbose.info('Got pull request info'); + this.logger.veryVerbose.info("Got pull request data\n", result); + this.logger.verbose.info("Got pull request info"); return result; } @@ -436,29 +436,29 @@ export default class Git { const repo = `repo:${this.options.owner}/${this.options.repo}`; options.q = `${repo} ${options.q}`; - this.logger.verbose.info('Searching repo using:\n', options); + this.logger.verbose.info("Searching repo using:\n", options); const result = await this.github.search.issuesAndPullRequests(options); - this.logger.veryVerbose.info('Got response from search\n', result); - this.logger.verbose.info('Searched repo on GitHub.'); + this.logger.veryVerbose.info("Got response from search\n", result); + this.logger.verbose.info("Searched repo on GitHub."); return result.data; } /** Run a graphql query on the GitHub project */ async graphql(query: string) { - this.logger.verbose.info('Querying Github using GraphQL:\n', query); + this.logger.verbose.info("Querying Github using GraphQL:\n", query); const data = await graphql(query, { baseUrl: this.graphqlBaseUrl, request: { agent: this.options.agent }, headers: { - authorization: `token ${this.options.token}` - } + authorization: `token ${this.options.token}`, + }, }); - this.logger.veryVerbose.info('Got response from query\n', data); + this.logger.veryVerbose.info("Got response from query\n", data); return data; } @@ -467,15 +467,15 @@ export default class Git { const args = { ...prInfo, owner: this.options.owner, - repo: this.options.repo + repo: this.options.repo, }; - this.logger.verbose.info('Creating status using:\n', args); + this.logger.verbose.info("Creating status using:\n", args); const result = await this.github.repos.createStatus(args); - this.logger.veryVerbose.info('Got response from createStatues\n', result); - this.logger.verbose.info('Created status on GitHub.'); + this.logger.veryVerbose.info("Got response from createStatues\n", result); + this.logger.verbose.info("Created status on GitHub."); return result; } @@ -483,22 +483,22 @@ export default class Git { /** Add a label to the project */ async createLabel(label: ILabelDefinition) { this.logger.verbose.info( - `Creating "${label.releaseType || 'general'}" label :\n${label.name}` + `Creating "${label.releaseType || "general"}" label :\n${label.name}` ); const color = label.color - ? tinyColor(label.color).toString('hex6') - : tinyColor.random().toString('hex6'); + ? tinyColor(label.color).toString("hex6") + : tinyColor.random().toString("hex6"); const result = await this.github.issues.createLabel({ name: label.name, owner: this.options.owner, repo: this.options.repo, - color: color.replace('#', ''), - description: label.description + color: color.replace("#", ""), + description: label.description, }); - this.logger.veryVerbose.info('Got response from createLabel\n', result); - this.logger.verbose.info('Created label on GitHub.'); + this.logger.veryVerbose.info("Got response from createLabel\n", result); + this.logger.verbose.info("Created label on GitHub."); return result; } @@ -506,23 +506,23 @@ export default class Git { /** Update a label on the project */ async updateLabel(label: ILabelDefinition) { this.logger.verbose.info( - `Updating "${label.releaseType || 'generic'}" label :\n${label.name}` + `Updating "${label.releaseType || "generic"}" label :\n${label.name}` ); const color = label.color - ? tinyColor(label.color).toString('hex6') - : tinyColor.random().toString('hex6'); + ? tinyColor(label.color).toString("hex6") + : tinyColor.random().toString("hex6"); const result = await this.github.issues.updateLabel({ current_name: label.name, name: label.name, owner: this.options.owner, repo: this.options.repo, - color: color.replace('#', ''), - description: label.description + color: color.replace("#", ""), + description: label.description, }); - this.logger.veryVerbose.info('Got response from updateLabel\n', result); - this.logger.verbose.info('Updated label on GitHub.'); + this.logger.veryVerbose.info("Got response from updateLabel\n", result); + this.logger.verbose.info("Updated label on GitHub."); return result; } @@ -535,11 +535,11 @@ export default class Git { issue_number: pr, owner: this.options.owner, repo: this.options.repo, - labels: [label] + labels: [label], }); - this.logger.veryVerbose.info('Got response from addLabels\n', result); - this.logger.verbose.info('Added labels on Pull Request.'); + this.logger.veryVerbose.info("Got response from addLabels\n", result); + this.logger.verbose.info("Added labels on Pull Request."); return result; } @@ -552,11 +552,11 @@ export default class Git { issue_number: pr, owner: this.options.owner, repo: this.options.repo, - name: label + name: label, }); - this.logger.veryVerbose.info('Got response from removeLabel\n', result); - this.logger.verbose.info('Removed label on Pull Request.'); + this.logger.veryVerbose.info("Got response from removeLabel\n", result); + this.logger.verbose.info("Removed label on Pull Request."); return result; } @@ -568,11 +568,11 @@ export default class Git { const result = await this.github.issues.lock({ issue_number: issue, owner: this.options.owner, - repo: this.options.repo + repo: this.options.repo, }); - this.logger.veryVerbose.info('Got response from lock\n', result); - this.logger.verbose.info('Locked issue.'); + this.logger.veryVerbose.info("Got response from lock\n", result); + this.logger.verbose.info("Locked issue."); return result; } @@ -580,35 +580,35 @@ export default class Git { /** Get information about the GitHub project */ @memoize() async getProject() { - this.logger.verbose.info('Getting project from GitHub'); + this.logger.verbose.info("Getting project from GitHub"); const result = ( await this.github.repos.get({ owner: this.options.owner, - repo: this.options.repo + repo: this.options.repo, }) ).data; - this.logger.veryVerbose.info('Got response from repos\n', result); - this.logger.verbose.info('Got project information.'); + this.logger.veryVerbose.info("Got response from repos\n", result); + this.logger.verbose.info("Got project information."); return result; } /** Get all the pull requests for a project */ async getPullRequests(options?: Partial) { - this.logger.verbose.info('Getting pull requests...'); + this.logger.verbose.info("Getting pull requests..."); const result = ( await this.github.pulls.list({ owner: this.options.owner.toLowerCase(), repo: this.options.repo.toLowerCase(), - ...options + ...options, }) ).data; - this.logger.veryVerbose.info('Got response from pull requests', result); - this.logger.verbose.info('Got pull request'); + this.logger.veryVerbose.info("Got response from pull requests", result); + this.logger.verbose.info("Got pull request"); return result; } @@ -624,7 +624,7 @@ export default class Git { this.github.pulls.listCommits.endpoint({ owner: this.options.owner.toLowerCase(), repo: this.options.repo.toLowerCase(), - pull_number: pr + pull_number: pr, }) ); @@ -635,20 +635,20 @@ export default class Git { } /** Find a comment that is using the context in a PR */ - async getCommentId(pr: number, context = 'default') { + async getCommentId(pr: number, context = "default") { const commentIdentifier = makeCommentIdentifier(context); - this.logger.verbose.info('Getting previous comments on:', pr); + this.logger.verbose.info("Getting previous comments on:", pr); const comments = await this.github.issues.listComments({ owner: this.options.owner, repo: this.options.repo, - issue_number: pr + issue_number: pr, }); - this.logger.veryVerbose.info('Got PR comments\n', comments); + this.logger.veryVerbose.info("Got PR comments\n", comments); - const oldMessage = comments.data.find(comment => + const oldMessage = comments.data.find((comment) => comment.body.includes(commentIdentifier) ); @@ -656,12 +656,12 @@ export default class Git { return -1; } - this.logger.verbose.info('Found previous message from same scope.'); + this.logger.verbose.info("Found previous message from same scope."); return oldMessage.id; } /** Delete a comment on an issue or pull request */ - async deleteComment(pr: number, context = 'default') { + async deleteComment(pr: number, context = "default") { const commentId = await this.getCommentId(pr, context); if (commentId === -1) { @@ -672,95 +672,95 @@ export default class Git { await this.github.issues.deleteComment({ owner: this.options.owner, repo: this.options.repo, - comment_id: commentId + comment_id: commentId, }); this.logger.verbose.info(`Successfully deleted comment: ${commentId}`); } /** Create a comment on an issue or pull request */ - async createComment(message: string, pr: number, context = 'default') { + async createComment(message: string, pr: number, context = "default") { const commentIdentifier = makeCommentIdentifier(context); - this.logger.verbose.info('Using comment identifier:', commentIdentifier); + this.logger.verbose.info("Using comment identifier:", commentIdentifier); await this.deleteComment(pr, context); - this.logger.verbose.info('Creating new comment'); + this.logger.verbose.info("Creating new comment"); const result = await this.github.issues.createComment({ owner: this.options.owner, repo: this.options.repo, issue_number: pr, - body: `${commentIdentifier}\n${message}` + body: `${commentIdentifier}\n${message}`, }); this.logger.veryVerbose.info( - 'Got response from creating comment\n', + "Got response from creating comment\n", result ); - this.logger.verbose.info('Successfully posted comment to PR'); + this.logger.verbose.info("Successfully posted comment to PR"); return result; } /** Edit a comment on an issue or pull request */ - async editComment(message: string, pr: number, context = 'default') { + async editComment(message: string, pr: number, context = "default") { const commentIdentifier = makeCommentIdentifier(context); - this.logger.verbose.info('Using comment identifier:', commentIdentifier); + this.logger.verbose.info("Using comment identifier:", commentIdentifier); const commentId = await this.getCommentId(pr, context); if (commentId === -1) { return this.createComment(message, pr, context); } - this.logger.verbose.info('Editing comment'); + this.logger.verbose.info("Editing comment"); const result = await this.github.issues.updateComment({ owner: this.options.owner, repo: this.options.repo, comment_id: commentId, - body: `${commentIdentifier}\n${message}` + body: `${commentIdentifier}\n${message}`, }); - this.logger.veryVerbose.info('Got response from editing comment\n', result); - this.logger.verbose.info('Successfully edited comment on PR'); + this.logger.veryVerbose.info("Got response from editing comment\n", result); + this.logger.verbose.info("Successfully edited comment on PR"); return result; } /** Create a comment on a pull request body */ - async addToPrBody(message: string, pr: number, context = 'default') { + async addToPrBody(message: string, pr: number, context = "default") { const id = makePrBodyIdentifier(context); - this.logger.verbose.info('Using PR body identifier:', id); - this.logger.verbose.info('Getting previous pr body on:', pr); + this.logger.verbose.info("Using PR body identifier:", id); + this.logger.verbose.info("Getting previous pr body on:", pr); const issue = await this.github.issues.get({ owner: this.options.owner, repo: this.options.repo, - issue_number: pr + issue_number: pr, }); - this.logger.veryVerbose.info('Got PR description\n', issue.data.body); + this.logger.veryVerbose.info("Got PR description\n", issue.data.body); const regex = new RegExp(`(${id})\\s*([\\S\\s]*)\\s*(${id})`); let body = issue.data.body; if (!body) { - body = message ? `\n${id}\n${message}\n${id}\n` : ''; + body = message ? `\n${id}\n${message}\n${id}\n` : ""; } else if (body.match(regex)) { - this.logger.verbose.info('Found previous message from same scope.'); - this.logger.verbose.info('Replacing pr body comment'); - body = body.replace(regex, message ? `$1\n${message}\n$3` : ''); + this.logger.verbose.info("Found previous message from same scope."); + this.logger.verbose.info("Replacing pr body comment"); + body = body.replace(regex, message ? `$1\n${message}\n$3` : ""); } else { - body += message ? `\n${id}\n${message}\n${id}\n` : ''; + body += message ? `\n${id}\n${message}\n${id}\n` : ""; } - this.logger.verbose.info('Creating new pr body'); + this.logger.verbose.info("Creating new pr body"); const result = await this.github.issues.update({ owner: this.options.owner, repo: this.options.repo, issue_number: pr, - body + body, }); - this.logger.veryVerbose.info('Got response from updating body\n', result); + this.logger.veryVerbose.info("Got response from updating body\n", result); this.logger.verbose.info(`Successfully updated body of PR #${pr}`); return result; @@ -768,25 +768,25 @@ export default class Git { /** Create a release for the GitHub projecct */ async publish(releaseNotes: string, tag: string, prerelease = false) { - this.logger.verbose.info('Creating release on GitHub for tag:', tag); + this.logger.verbose.info("Creating release on GitHub for tag:", tag); const result = await this.github.repos.createRelease({ owner: this.options.owner, repo: this.options.repo, tag_name: tag, body: releaseNotes, - prerelease + prerelease, }); - this.logger.veryVerbose.info('Got response from createRelease\n', result); - this.logger.verbose.info('Created GitHub release.'); + this.logger.veryVerbose.info("Got response from createRelease\n", result); + this.logger.verbose.info("Created GitHub release."); return result; } /** Get the latest tag in the git tree */ async getLatestTagInBranch(since?: string) { - return execPromise('git', ['describe', '--tags', '--abbrev=0', since]); + return execPromise("git", ["describe", "--tags", "--abbrev=0", since]); } /** Get the tag before latest in the git tree */ @@ -797,16 +797,16 @@ export default class Git { /** Get all the tags for a given branch. */ async getTags(branch: string) { - const tags = await execPromise('git', [ - 'tag', + const tags = await execPromise("git", [ + "tag", "--sort='creatordate'", - '--merged', - branch + "--merged", + branch, ]); return tags - .split('\n') - .map(tag => tag.trim()) + .split("\n") + .map((tag) => tag.trim()) .filter(Boolean); } @@ -824,9 +824,9 @@ export default class Git { return result; }); - this.logger.verbose.info('Tags found in base branch:', baseTags); - this.logger.verbose.info('Tags found in branch:', branchTags); - this.logger.verbose.info('Latest tag in branch:', firstGreatestUnique); + this.logger.verbose.info("Tags found in base branch:", baseTags); + this.logger.verbose.info("Tags found in branch:", branchTags); + this.logger.verbose.info("Latest tag in branch:", firstGreatestUnique); return firstGreatestUnique; } @@ -834,7 +834,7 @@ export default class Git { /** Determine the pull request for a commit hash */ async matchCommitToPr(sha: string) { const query = buildSearchQuery(this.options.owner, this.options.repo, [ - sha + sha, ]); if (!query) { @@ -852,7 +852,7 @@ export default class Git { return { ...pr, - labels: pr.labels ? pr.labels.edges.map(edge => edge.node.name) : [] + labels: pr.labels ? pr.labels.edges.map((edge) => edge.node.name) : [], }; } } diff --git a/packages/core/src/init.ts b/packages/core/src/init.ts index 5315babb7..242f43fe5 100644 --- a/packages/core/src/init.ts +++ b/packages/core/src/init.ts @@ -1,21 +1,21 @@ /* eslint-disable no-await-in-loop, @typescript-eslint/ban-ts-ignore */ -import endent from 'endent'; -import { prompt } from 'enquirer'; -import { AsyncSeriesBailHook, AsyncSeriesWaterfallHook } from 'tapable'; - -import { makeInteractiveInitHooks } from './utils/make-hooks'; -import { defaultLabels, ILabelDefinition } from './release'; -import SEMVER from './semver'; -import loadPlugin from './utils/load-plugins'; -import { ILogger } from './utils/logger'; -import { readFileSync, writeFileSync } from 'fs'; +import endent from "endent"; +import { prompt } from "enquirer"; +import { AsyncSeriesBailHook, AsyncSeriesWaterfallHook } from "tapable"; + +import { makeInteractiveInitHooks } from "./utils/make-hooks"; +import { defaultLabels, ILabelDefinition } from "./release"; +import SEMVER from "./semver"; +import loadPlugin from "./utils/load-plugins"; +import { ILogger } from "./utils/logger"; +import { readFileSync, writeFileSync } from "fs"; import { AutoRc, RepoInformation, AuthorInformation, - PluginConfig -} from './types'; + PluginConfig, +} from "./types"; // const writeFile = promisify(fs.writeFile); @@ -24,7 +24,7 @@ interface Confirmation { confirmed: boolean; } -interface InputResponse { +interface InputResponse { /** he value of the input prompt */ value: T; } @@ -62,9 +62,9 @@ async function getLabel(label?: ILabelDefinition) { } const response = await prompt({ - type: 'snippet', - name: 'value', - message: label ? `Edit "${label.name}" label:` : 'Add a label:', + type: "snippet", + name: "value", + message: label ? `Edit "${label.name}" label:` : "Add a label:", // @ts-ignore template: label ? endent`{ @@ -72,7 +72,7 @@ async function getLabel(label?: ILabelDefinition) { ${ label.changelogTitle ? `changelogTitle: #{changelogTitle:${label.changelogTitle}},` - : '' + : "" } description: #{description:${label.description}}, releaseType: #{releaseType:${label.releaseType}} @@ -88,16 +88,16 @@ async function getLabel(label?: ILabelDefinition) { values: ILabelDefinition; }) => { if (!state.values.name) { - return 'name is required for new label'; + return "name is required for new label"; } const releaseTypes = [ SEMVER.major, SEMVER.minor, SEMVER.patch, - 'none', - 'skip', - 'release' + "none", + "skip", + "release", ]; if ( @@ -105,19 +105,19 @@ async function getLabel(label?: ILabelDefinition) { !releaseTypes.includes(state.values.releaseType) ) { return `Release type can only be one of the following: ${releaseTypes.join( - ', ' + ", " )}`; } return true; - } + }, }); const { name, changelogTitle, description, - releaseType + releaseType, } = response.value.values; return { name, changelogTitle, description, releaseType }; } @@ -127,20 +127,20 @@ async function getAdditionalLabels() { const labels: ILabelDefinition[] = []; let addLabels = await prompt({ - type: 'confirm', - name: 'confirmed', - message: 'Would you like to add more labels?', - initial: 'no' + type: "confirm", + name: "confirmed", + message: "Would you like to add more labels?", + initial: "no", }); while (addLabels.confirmed) { labels.push(await getLabel()); addLabels = await prompt({ - type: 'confirm', - name: 'confirmed', - message: 'Would you like to add another label?', - initial: 'no' + type: "confirm", + name: "confirmed", + message: "Would you like to add another label?", + initial: "no", }); } @@ -151,10 +151,10 @@ async function getAdditionalLabels() { async function getCustomizedDefaultLabels() { const labels: ILabelDefinition[] = []; const addLabels = await prompt({ - type: 'confirm', - name: 'confirmed', - message: 'Would you like to use customize the default labels?', - initial: 'no' + type: "confirm", + name: "confirmed", + message: "Would you like to use customize the default labels?", + initial: "no", }); if (addLabels.confirmed) { @@ -174,79 +174,79 @@ async function getCustomizedDefaultLabels() { /** Get the plugins the user wants to use */ async function getPlugins() { const releasePlugins = { - 'Chrome Web Store': 'chrome', - 'Rust Crate': 'crates', - 'Git Tag': 'git-tag', - 'npm Package': 'npm', - Maven: 'maven' + "Chrome Web Store": "chrome", + "Rust Crate": "crates", + "Git Tag": "git-tag", + "npm Package": "npm", + Maven: "maven", }; const releasePlugin = await prompt({ - type: 'select', - name: 'value', + type: "select", + name: "value", required: true, message: - 'What package manager plugin would you like to publish your project with?', - choices: Object.keys(releasePlugins) + "What package manager plugin would you like to publish your project with?", + choices: Object.keys(releasePlugins), }); const featurePlugin = await prompt>({ - type: 'multiselect', - name: 'value', + type: "multiselect", + name: "value", required: true, - message: 'What other plugins would you like to use?', + message: "What other plugins would you like to use?", choices: [ { - name: 'all-contributors', + name: "all-contributors", message: - 'All Contributors - Automatically add contributors as changelogs are produced' + "All Contributors - Automatically add contributors as changelogs are produced", }, { - name: 'conventional-commits', - message: 'Conventional Commits - Parse conventional commit messages' + name: "conventional-commits", + message: "Conventional Commits - Parse conventional commit messages", }, { - name: 'first-time-contributor', + name: "first-time-contributor", message: - 'First Time Contributor - Thank first time contributors for their work right in your release notes' + "First Time Contributor - Thank first time contributors for their work right in your release notes", }, { - name: 'jira', - message: 'Jira - Include Jira story information' + name: "jira", + message: "Jira - Include Jira story information", }, { - name: 'released', - message: 'Released - Mark PRs as released' + name: "released", + message: "Released - Mark PRs as released", }, { - name: 'slack', - message: 'Slack - Post your release notes to a slack channel' + name: "slack", + message: "Slack - Post your release notes to a slack channel", }, { - name: 'twitter', - message: 'Twitter - Post tweets after a release is made' - } - ] + name: "twitter", + message: "Twitter - Post tweets after a release is made", + }, + ], }); return [ releasePlugins[releasePlugin.value as keyof typeof releasePlugins], - ...featurePlugin.value + ...featurePlugin.value, ]; } /** Get env vars, create .env file, add to .gitignore */ -async function createEnv(hook: InteractiveInitHooks['createEnv']) { +async function createEnv(hook: InteractiveInitHooks["createEnv"]) { let currentEnv: string; try { - currentEnv = readFileSync('.env', { encoding: 'utf8' }); + currentEnv = readFileSync(".env", { encoding: "utf8" }); } catch (error) { - currentEnv = ''; + currentEnv = ""; } const env = (await hook.promise([])).filter( - envVar => !currentEnv.includes(envVar.variable) + (envVar) => !currentEnv.includes(envVar.variable) ); if (env.length === 0) { @@ -254,11 +254,11 @@ async function createEnv(hook: InteractiveInitHooks['createEnv']) { } const shouldCreateEnv = await prompt({ - type: 'confirm', - name: 'confirmed', + type: "confirm", + name: "confirmed", message: - 'Would you like to create an .env file? This makes it easy to test and use auto locally.', - initial: 'yes' + "Would you like to create an .env file? This makes it easy to test and use auto locally.", + initial: "yes", }); if (!shouldCreateEnv.confirmed) { @@ -270,28 +270,28 @@ async function createEnv(hook: InteractiveInitHooks['createEnv']) { await last; const token = await prompt({ - type: 'input', - name: 'value', + type: "input", + name: "value", message: envVar.message, - required: true + required: true, }); currentEnv += `${envVar.variable}=${token.value}\n`; }, Promise.resolve()); - writeFileSync('.env', currentEnv); + writeFileSync(".env", currentEnv); let gitIgnore: string; try { - gitIgnore = readFileSync('.env', { encoding: 'utf8' }); + gitIgnore = readFileSync(".env", { encoding: "utf8" }); } catch (error) { - gitIgnore = ''; + gitIgnore = ""; } // Add env to gitignore if not already there - if (!gitIgnore.includes('.env')) { - writeFileSync('.env', gitIgnore ? `${gitIgnore}\n.env` : '.env'); + if (!gitIgnore.includes(".env")) { + writeFileSync(".env", gitIgnore ? `${gitIgnore}\n.env` : ".env"); } } @@ -318,14 +318,14 @@ export default class InteractiveInit { /** Run a prompt to get the author information */ async getAuthorInformation() { const response = await prompt({ - type: 'snippet', - name: 'author', + type: "snippet", + name: "author", message: `What git user would you like to make commits with?`, required: true, // @ts-ignore template: endent` Name: #{name} - Email: #{email}` + Email: #{email}`, }); return response.author.values as AuthorInformation; @@ -334,12 +334,12 @@ export default class InteractiveInit { /** Run a prompt to get the repo information */ async getRepoInformation() { const response = await prompt({ - type: 'snippet', - name: 'repoInfo', + type: "snippet", + name: "repoInfo", message: `What GitHub project you would like to publish?`, required: true, // @ts-ignore - template: endent`#{owner}/#{repo}` + template: endent`#{owner}/#{repo}`, }); return response.repoInfo.values as RepoInformation; @@ -347,17 +347,17 @@ export default class InteractiveInit { /** Load the default behavior */ private tapDefaults() { - this.hooks.getRepo.tapPromise('Init Default', this.getRepoInformation); - this.hooks.getAuthor.tapPromise('Init Default', this.getAuthorInformation); - this.hooks.createEnv.tap('Init Default', vars => [ + this.hooks.getRepo.tapPromise("Init Default", this.getRepoInformation); + this.hooks.getAuthor.tapPromise("Init Default", this.getAuthorInformation); + this.hooks.createEnv.tap("Init Default", (vars) => [ ...vars, { - variable: 'GH_TOKEN', - message: `Enter a personal access token for the GitHub API https://github.com/settings/tokens/new` - } + variable: "GH_TOKEN", + message: `Enter a personal access token for the GitHub API https://github.com/settings/tokens/new`, + }, ]); - this.hooks.writeRcFile.tap('Init Default', rc => { - const filename = '.autorc'; + this.hooks.writeRcFile.tap("Init Default", (rc) => { + const filename = ".autorc"; writeFileSync(filename, JSON.stringify(rc, null, 2)); this.logger.log.success(`Wrote configuration to: ${filename}`); }); @@ -371,8 +371,8 @@ export default class InteractiveInit { if (plugins) { plugins - .map(name => loadPlugin([name, {}], this.logger)) - .forEach(plugin => { + .map((name) => loadPlugin([name, {}], this.logger)) + .forEach((plugin) => { if (plugin?.init) { plugin.init(this); } @@ -381,7 +381,7 @@ export default class InteractiveInit { autoRc.plugins = await plugins.reduce(async (last, plugin) => { return [ ...(await last), - (await this.hooks.configurePlugin.promise(plugin)) || plugin + (await this.hooks.configurePlugin.promise(plugin)) || plugin, ]; }, Promise.resolve([] as PluginConfig[])); } @@ -389,21 +389,21 @@ export default class InteractiveInit { this.tapDefaults(); const repoInfo = await this.hooks.getRepo.promise(); - if (typeof repoInfo === 'object') { + if (typeof repoInfo === "object") { autoRc = { ...autoRc, ...repoInfo }; } const author = await this.hooks.getAuthor.promise(); - if (typeof author === 'object') { + if (typeof author === "object") { autoRc = { ...autoRc, ...author }; } const onlyPublishWithReleaseLabel = await prompt({ - type: 'confirm', - name: 'confirmed', + type: "confirm", + name: "confirmed", message: 'Only make releases if "release" label is on pull request?', - initial: 'no' + initial: "no", }); if (onlyPublishWithReleaseLabel.confirmed) { @@ -411,22 +411,22 @@ export default class InteractiveInit { } const isEnterprise = await prompt({ - type: 'confirm', - name: 'confirmed', - message: 'Are you using an enterprise instance of GitHub?', - initial: 'no' + type: "confirm", + name: "confirmed", + message: "Are you using an enterprise instance of GitHub?", + initial: "no", }); if (isEnterprise.confirmed) { const response = await prompt({ - type: 'snippet', - name: 'repoInfo', + type: "snippet", + name: "repoInfo", message: `What are the api URLs for your GitHub enterprise instance?`, required: true, // @ts-ignore template: endent` GitHub API: #{githubApi} - Graphql API: #{githubGraphqlApi}` + Graphql API: #{githubGraphqlApi}`, }); autoRc = { ...autoRc, ...response.repoInfo.values }; @@ -436,7 +436,7 @@ export default class InteractiveInit { const newLabels = [ ...(await getCustomizedDefaultLabels()), - ...(await getAdditionalLabels()) + ...(await getAdditionalLabels()), ]; if (newLabels.length > 0) { diff --git a/packages/core/src/log-parse.ts b/packages/core/src/log-parse.ts index 44245fe06..40b14d441 100644 --- a/packages/core/src/log-parse.ts +++ b/packages/core/src/log-parse.ts @@ -1,5 +1,5 @@ -import { AsyncSeriesBailHook, AsyncSeriesWaterfallHook } from 'tapable'; -import { makeLogParseHooks } from './utils/make-hooks'; +import { AsyncSeriesBailHook, AsyncSeriesWaterfallHook } from "tapable"; +import { makeLogParseHooks } from "./utils/make-hooks"; export interface ICommitAuthor { /** Author's name */ @@ -11,7 +11,7 @@ export interface ICommitAuthor { /** The commit this author created */ hash?: string; /** The type of user */ - type?: 'Bot' | 'User' | string; + type?: "Bot" | "User" | string; } export interface IPullRequest { @@ -76,15 +76,15 @@ export function parsePR(commit: IExtendedCommit): IExtendedCommit { ...commit, pullRequest: { number: Number(prMatch[1]), - base: prMatch[2] + base: prMatch[2], }, - subject: prMatch[3].trim() + subject: prMatch[3].trim(), }; } /** Parse the PR information for the squashed commit message */ export function parseSquashPR(commit: IExtendedCommit): IExtendedCommit { - const firstLine = commit.subject.split('\n')[0]; + const firstLine = commit.subject.split("\n")[0]; const squashMerge = /\(#(\d+)\)$/; const squashMergeMatch = firstLine.match(squashMerge); @@ -96,11 +96,11 @@ export function parseSquashPR(commit: IExtendedCommit): IExtendedCommit { return { ...commit, pullRequest: { - number: Number(squashMergeMatch[1]) + number: Number(squashMergeMatch[1]), }, subject: firstLine .substr(0, firstLine.length - squashMergeMatch[0].length) - .trim() + .trim(), }; } @@ -124,14 +124,14 @@ export default class LogParse { constructor() { this.hooks = makeLogParseHooks(); - this.hooks.parseCommit.tap('Merge Commit', parsePR); - this.hooks.parseCommit.tap('Squash Merge Commit', parseSquashPR); + this.hooks.parseCommit.tap("Merge Commit", parsePR); + this.hooks.parseCommit.tap("Squash Merge Commit", parseSquashPR); } /** Run the log parser over a set of commits */ async normalizeCommits(commits: ICommit[]): Promise { const eCommits = await Promise.all( - commits.map(async commit => this.normalizeCommit(commit)) + commits.map(async (commit) => this.normalizeCommit(commit)) ); return eCommits.filter(Boolean) as IExtendedCommit[]; @@ -142,7 +142,7 @@ export default class LogParse { const extended = await this.hooks.parseCommit.promise({ labels: [], ...commit, - authors: [{ name: commit.authorName, email: commit.authorEmail }] + authors: [{ name: commit.authorName, email: commit.authorEmail }], }); const shouldOmit = await this.hooks.omitCommit.promise(extended); diff --git a/packages/core/src/match-sha-to-pr.ts b/packages/core/src/match-sha-to-pr.ts index 8e9aaaeb5..e706e72f0 100644 --- a/packages/core/src/match-sha-to-pr.ts +++ b/packages/core/src/match-sha-to-pr.ts @@ -1,6 +1,6 @@ -import endent from 'endent'; +import endent from "endent"; -import { IExtendedCommit } from './log-parse'; +import { IExtendedCommit } from "./log-parse"; interface ISearchEdge { /** Graphql search node */ @@ -8,7 +8,7 @@ interface ISearchEdge { /** PR number */ number: number; /** State of the PR */ - state: 'MERGED' | 'CLOSED' | 'OPEN'; + state: "MERGED" | "CLOSED" | "OPEN"; /** Body of the PR */ body: string; /** Labels attached to the PR */ @@ -67,7 +67,7 @@ export function buildSearchQuery( } } `; - }, ''); + }, ""); if (!query) { return; @@ -90,9 +90,9 @@ export function processQueryResult( result: ISearchResult, commitsWithoutPR: IExtendedCommit[] ) { - const hash = key.split('hash_')[1]; + const hash = key.split("hash_")[1]; const commit = commitsWithoutPR.find( - commitWithoutPR => commitWithoutPR.hash === hash + (commitWithoutPR) => commitWithoutPR.hash === hash ); if (!commit) { @@ -100,7 +100,7 @@ export function processQueryResult( } if (result.edges.length > 0) { - if (result.edges[0].node.state === 'CLOSED') { + if (result.edges[0].node.state === "CLOSED") { return; } @@ -108,18 +108,18 @@ export function processQueryResult( /** The label */ name: string; }[] = result.edges[0].node.labels - ? result.edges[0].node.labels.edges.map(edge => edge.node) + ? result.edges[0].node.labels.edges.map((edge) => edge.node) : []; commit.pullRequest = { number: result.edges[0].node.number, - body: result.edges[0].node.body + body: result.edges[0].node.body, }; - commit.labels = [...labels.map(label => label.name), ...commit.labels]; + commit.labels = [...labels.map((label) => label.name), ...commit.labels]; } else { - commit.labels = ['pushToBaseBranch', ...commit.labels]; + commit.labels = ["pushToBaseBranch", ...commit.labels]; } - commit.subject = commit.subject.split('\n')[0]; + commit.subject = commit.subject.split("\n")[0]; return commit; } diff --git a/packages/core/src/plugins/__tests__/filter-non-pull-request.test.ts b/packages/core/src/plugins/__tests__/filter-non-pull-request.test.ts index 540b2638b..831e1eb9f 100644 --- a/packages/core/src/plugins/__tests__/filter-non-pull-request.test.ts +++ b/packages/core/src/plugins/__tests__/filter-non-pull-request.test.ts @@ -1,10 +1,10 @@ -import makeCommitFromMsg from '../../__tests__/make-commit-from-msg'; -import Auto from '../../auto'; -import Git from '../../git'; -import LogParse from '../../log-parse'; -import { makeHooks, makeLogParseHooks } from '../../utils/make-hooks'; +import makeCommitFromMsg from "../../__tests__/make-commit-from-msg"; +import Auto from "../../auto"; +import Git from "../../git"; +import LogParse from "../../log-parse"; +import { makeHooks, makeLogParseHooks } from "../../utils/make-hooks"; -import FilterNonPullRequestPlugin from '../filter-non-pull-request'; +import FilterNonPullRequestPlugin from "../filter-non-pull-request"; const getPr = jest.fn(); @@ -19,34 +19,34 @@ const setup = () => { return logParseHooks; }; -describe('Filter Non Pull Request Plugin', () => { - test('should do nothing for non-prs', async () => { +describe("Filter Non Pull Request Plugin", () => { + test("should do nothing for non-prs", async () => { const hooks = setup(); - const commit = makeCommitFromMsg('foo'); + const commit = makeCommitFromMsg("foo"); expect(await hooks.omitCommit.promise(commit)).toBeUndefined(); }); - test('should not filter bad PR numbers', async () => { + test("should not filter bad PR numbers", async () => { const hooks = setup(); - const commit = makeCommitFromMsg('foo', { pullRequest: { number: 404 } }); + const commit = makeCommitFromMsg("foo", { pullRequest: { number: 404 } }); - getPr.mockRejectedValueOnce(new Error('Not Found')); + getPr.mockRejectedValueOnce(new Error("Not Found")); expect(await hooks.omitCommit.promise(commit)).toBe(true); }); - test('should throw unknown errors', async () => { + test("should throw unknown errors", async () => { const hooks = setup(); - const commit = makeCommitFromMsg('foo', { pullRequest: { number: 123 } }); + const commit = makeCommitFromMsg("foo", { pullRequest: { number: 123 } }); - getPr.mockRejectedValueOnce(new Error('Some error')); + getPr.mockRejectedValueOnce(new Error("Some error")); await expect(hooks.omitCommit.promise(commit)).rejects.toBeInstanceOf( Error ); }); - test('should with no PR return value', async () => { + test("should with no PR return value", async () => { const hooks = setup(); - const commit = makeCommitFromMsg('foo', { pullRequest: { number: 123 } }); + const commit = makeCommitFromMsg("foo", { pullRequest: { number: 123 } }); getPr.mockReturnValueOnce(Promise.resolve()); await expect(hooks.omitCommit.promise(commit)).rejects.toBeInstanceOf( @@ -54,17 +54,17 @@ describe('Filter Non Pull Request Plugin', () => { ); }); - test('should filter issues', async () => { + test("should filter issues", async () => { const hooks = setup(); - const commit = makeCommitFromMsg('foo', { pullRequest: { number: 123 } }); + const commit = makeCommitFromMsg("foo", { pullRequest: { number: 123 } }); getPr.mockReturnValueOnce(Promise.resolve({ data: {} })); expect(await hooks.omitCommit.promise(commit)).toBe(true); }); - test('should not filter PRs', async () => { + test("should not filter PRs", async () => { const hooks = setup(); - const commit = makeCommitFromMsg('foo', { pullRequest: { number: 123 } }); + const commit = makeCommitFromMsg("foo", { pullRequest: { number: 123 } }); getPr.mockReturnValueOnce( Promise.resolve({ data: { pull_request: true } }) diff --git a/packages/core/src/plugins/filter-non-pull-request.ts b/packages/core/src/plugins/filter-non-pull-request.ts index 6e6ca4ebd..a823a1a7b 100644 --- a/packages/core/src/plugins/filter-non-pull-request.ts +++ b/packages/core/src/plugins/filter-non-pull-request.ts @@ -1,22 +1,22 @@ -import on from 'await-to-js'; +import on from "await-to-js"; -import { Auto, IPlugin } from '../auto'; +import { Auto, IPlugin } from "../auto"; /** Filter out PR numbers that might generate errors. */ export default class FilterNonPullRequestPlugin implements IPlugin { /** The name of the plugin */ - name = 'Filter Non Pull Request'; + name = "Filter Non Pull Request"; /** Tap into auto plugin points. */ apply(auto: Auto) { - auto.hooks.onCreateLogParse.tap(this.name, logParse => { - logParse.hooks.omitCommit.tapPromise(this.name, async commit => { + auto.hooks.onCreateLogParse.tap(this.name, (logParse) => { + logParse.hooks.omitCommit.tapPromise(this.name, async (commit) => { if (commit.pullRequest?.number) { const { number: prNumber } = commit.pullRequest; const [err, info] = await on(auto.git!.getPr(prNumber)); // Omit PRs that don't exist on the repo - if (err?.message.includes('Not Found')) { + if (err?.message.includes("Not Found")) { return true; } diff --git a/packages/core/src/release.ts b/packages/core/src/release.ts index 193b56abe..2999e7d4f 100644 --- a/packages/core/src/release.ts +++ b/packages/core/src/release.ts @@ -1,43 +1,43 @@ -import { GraphQlQueryResponse } from '@octokit/graphql/dist-types/types'; -import { Octokit } from '@octokit/rest'; -import on from 'await-to-js'; -import * as fs from 'fs'; -import chunk from 'lodash.chunk'; -import { inc, ReleaseType } from 'semver'; -import { promisify } from 'util'; -import * as t from 'io-ts'; - -import { AsyncSeriesBailHook, SyncHook } from 'tapable'; -import { Memoize as memoize } from 'typescript-memoize'; -import { ICreateLabelsOptions } from './auto-args'; -import Changelog from './changelog'; -import Git from './git'; -import LogParse, { ICommitAuthor, IExtendedCommit } from './log-parse'; -import SEMVER, { calculateSemVerBump, IVersionLabels } from './semver'; -import execPromise from './utils/exec-promise'; -import { dummyLog, ILogger } from './utils/logger'; -import { makeReleaseHooks } from './utils/make-hooks'; -import { execSync } from 'child_process'; +import { GraphQlQueryResponse } from "@octokit/graphql/dist-types/types"; +import { Octokit } from "@octokit/rest"; +import on from "await-to-js"; +import * as fs from "fs"; +import chunk from "lodash.chunk"; +import { inc, ReleaseType } from "semver"; +import { promisify } from "util"; +import * as t from "io-ts"; + +import { AsyncSeriesBailHook, SyncHook } from "tapable"; +import { Memoize as memoize } from "typescript-memoize"; +import { ICreateLabelsOptions } from "./auto-args"; +import Changelog from "./changelog"; +import Git from "./git"; +import LogParse, { ICommitAuthor, IExtendedCommit } from "./log-parse"; +import SEMVER, { calculateSemVerBump, IVersionLabels } from "./semver"; +import execPromise from "./utils/exec-promise"; +import { dummyLog, ILogger } from "./utils/logger"; +import { makeReleaseHooks } from "./utils/make-hooks"; +import { execSync } from "child_process"; import { buildSearchQuery, ISearchResult, - processQueryResult -} from './match-sha-to-pr'; -import { LoadedAutoRc } from './types'; + processQueryResult, +} from "./match-sha-to-pr"; +import { LoadedAutoRc } from "./types"; export type VersionLabel = | SEMVER.major | SEMVER.minor | SEMVER.patch - | 'skip' - | 'release'; + | "skip" + | "release"; export const releaseLabels: VersionLabel[] = [ SEMVER.major, SEMVER.minor, SEMVER.patch, - 'skip', - 'release' + "skip", + "release", ]; /** Determine if a label is a label used for versioning */ @@ -46,7 +46,7 @@ export const isVersionLabel = (label: string): label is VersionLabel => const labelDefinitionRequired = t.type({ /** The label text */ - name: t.string + name: t.string, }); const labelDefinitionOptional = t.partial({ @@ -58,67 +58,67 @@ const labelDefinitionOptional = t.partial({ description: t.string, /** What type of release this label signifies */ releaseType: t.union([ - t.literal('none'), - t.literal('skip'), - ...releaseLabels.map(l => t.literal(l)) + t.literal("none"), + t.literal("skip"), + ...releaseLabels.map((l) => t.literal(l)), ]), /** Whether to overwrite the base label */ - overwrite: t.boolean + overwrite: t.boolean, }); export const labelDefinition = t.intersection([ labelDefinitionOptional, - labelDefinitionRequired + labelDefinitionRequired, ]); export type ILabelDefinition = t.TypeOf; export const defaultLabels: ILabelDefinition[] = [ { - name: 'major', - changelogTitle: '💥 Breaking Change', - description: 'Increment the major version when merged', - releaseType: SEMVER.major + name: "major", + changelogTitle: "💥 Breaking Change", + description: "Increment the major version when merged", + releaseType: SEMVER.major, }, { - name: 'minor', - changelogTitle: '🚀 Enhancement', - description: 'Increment the minor version when merged', - releaseType: SEMVER.minor + name: "minor", + changelogTitle: "🚀 Enhancement", + description: "Increment the minor version when merged", + releaseType: SEMVER.minor, }, { - name: 'patch', - changelogTitle: '🐛 Bug Fix', - description: 'Increment the patch version when merged', - releaseType: SEMVER.patch + name: "patch", + changelogTitle: "🐛 Bug Fix", + description: "Increment the patch version when merged", + releaseType: SEMVER.patch, }, { - name: 'skip-release', - description: 'Preserve the current version when merged', - releaseType: 'skip' + name: "skip-release", + description: "Preserve the current version when merged", + releaseType: "skip", }, { - name: 'release', - description: 'Create a release when this pr is merged', - releaseType: 'release' + name: "release", + description: "Create a release when this pr is merged", + releaseType: "release", }, { - name: 'internal', - changelogTitle: '🏠 Internal', - description: 'Changes only affect the internal API', - releaseType: 'none' + name: "internal", + changelogTitle: "🏠 Internal", + description: "Changes only affect the internal API", + releaseType: "none", }, { - name: 'documentation', - changelogTitle: '📝 Documentation', - description: 'Changes only affect the documentation', - releaseType: 'none' - } + name: "documentation", + changelogTitle: "📝 Documentation", + description: "Changes only affect the documentation", + releaseType: "none", + }, ]; /** Construct a map of label => semver label */ export const getVersionMap = (labels = defaultLabels) => labels.reduce((semVer, { releaseType: type, name }) => { - if (type && (isVersionLabel(type) || type === 'none')) { + if (type && (isVersionLabel(type) || type === "none")) { const list = semVer.get(type) || []; semVer.set(type, [...list, name]); } @@ -158,9 +158,9 @@ export default class Release { constructor( git: Git, config: LoadedAutoRc = { - baseBranch: 'master', - prereleaseBranches: ['next'], - labels: defaultLabels + baseBranch: "master", + prereleaseBranches: ["next"], + labels: defaultLabels, }, logger: ILogger = dummyLog() ) { @@ -181,7 +181,7 @@ export default class Release { baseUrl: project.html_url, labels: this.config.labels, baseBranch: this.config.baseBranch, - prereleaseBranches: this.config.prereleaseBranches + prereleaseBranches: this.config.prereleaseBranches, }); this.hooks.onCreateChangelog.call(changelog, version); @@ -198,7 +198,7 @@ export default class Release { */ async generateReleaseNotes( from: string, - to = 'HEAD', + to = "HEAD", version?: SEMVER ): Promise { const commits = await this.getCommitsInRelease(from, to); @@ -208,12 +208,12 @@ export default class Release { } /** Get all the commits that will be included in a release */ - async getCommitsInRelease(from: string, to = 'HEAD') { + async getCommitsInRelease(from: string, to = "HEAD") { const allCommits = await this.getCommits(from, to); const allPrCommits = await Promise.all( allCommits - .filter(commit => commit.pullRequest) - .map(async commit => { + .filter((commit) => commit.pullRequest) + .map(async (commit) => { const [err, commits = []] = await on( this.git.getCommitsForPR(Number(commit.pullRequest!.number)) ); @@ -223,32 +223,32 @@ export default class Release { const allPrCommitHashes = allPrCommits .filter(Boolean) .reduce( - (all, pr) => [...all, ...pr.map(subCommit => subCommit.sha)], + (all, pr) => [...all, ...pr.map((subCommit) => subCommit.sha)], [] as string[] ); const uniqueCommits = allCommits.filter( - commit => + (commit) => (commit.pullRequest || !allPrCommitHashes.includes(commit.hash)) && - !commit.subject.includes('[skip ci]') + !commit.subject.includes("[skip ci]") ); const commitsWithoutPR = uniqueCommits.filter( - commit => !commit.pullRequest + (commit) => !commit.pullRequest ); const batches = chunk(commitsWithoutPR, 10); const queries = await Promise.all( batches - .map(batch => + .map((batch) => buildSearchQuery( this.git.options.owner, this.git.options.repo, - batch.map(c => c.hash) + batch.map((c) => c.hash) ) ) .filter((q): q is string => Boolean(q)) - .map(q => this.git.graphql(q)) + .map((q) => this.git.graphql(q)) ); const data = queries.filter((q): q is GraphQlQueryResponse => Boolean(q)); @@ -257,12 +257,12 @@ export default class Release { } const commitsInRelease: (IExtendedCommit | undefined)[] = [ - ...uniqueCommits + ...uniqueCommits, ]; const logParse = await this.createLogParse(); Promise.all( - data.map(results => + data.map((results) => Object.entries(results) .filter((result): result is [string, ISearchResult] => Boolean(result[1]) @@ -271,9 +271,9 @@ export default class Release { processQueryResult(key, result, commitsWithoutPR) ) .filter((commit): commit is IExtendedCommit => Boolean(commit)) - .map(async commit => { + .map(async (commit) => { const index = commitsWithoutPR.findIndex( - commitWithoutPR => commitWithoutPR.hash === commit.hash + (commitWithoutPR) => commitWithoutPR.hash === commit.hash ); commitsInRelease[index] = await logParse.normalizeCommit(commit); @@ -293,7 +293,7 @@ export default class Release { changelogPath: string ) { const date = new Date().toDateString(); - let newChangelog = '#'; + let newChangelog = "#"; if (title) { newChangelog += ` ${title}`; @@ -302,14 +302,14 @@ export default class Release { newChangelog += ` (${date})\n\n${releaseNotes}`; if (fs.existsSync(changelogPath)) { - this.logger.verbose.info('Old changelog exists, prepending changes.'); - const oldChangelog = await readFile(changelogPath, 'utf8'); + this.logger.verbose.info("Old changelog exists, prepending changes."); + const oldChangelog = await readFile(changelogPath, "utf8"); newChangelog = `${newChangelog}\n\n---\n\n${oldChangelog}`; } await writeFile(changelogPath, newChangelog); - this.logger.verbose.info('Wrote new changelog to filesystem.'); - await execPromise('git', ['add', changelogPath]); + this.logger.verbose.info("Wrote new changelog to filesystem."); + await execPromise("git", ["add", changelogPath]); } /** @@ -324,7 +324,7 @@ export default class Release { lastRelease: string, currentVersion: string ) { - this.hooks.createChangelogTitle.tapPromise('Default', async () => { + this.hooks.createChangelogTitle.tapPromise("Default", async () => { let version; if (lastRelease.match(/\d+\.\d+\.\d+/)) { @@ -335,21 +335,21 @@ export default class Release { version = inc(currentVersion, bump as ReleaseType); } - this.logger.verbose.info('Calculated next version to be:', version); + this.logger.verbose.info("Calculated next version to be:", version); if (!version) { - return ''; + return ""; } - return this.config.noVersionPrefix || version.startsWith('v') + return this.config.noVersionPrefix || version.startsWith("v") ? version : `v${version}`; }); - this.logger.verbose.info('Adding new changes to changelog.'); + this.logger.verbose.info("Adding new changes to changelog."); const title = await this.hooks.createChangelogTitle.promise(); - await this.updateChangelogFile(title || '', releaseNotes, 'CHANGELOG.md'); + await this.updateChangelogFile(title || "", releaseNotes, "CHANGELOG.md"); } /** @@ -358,41 +358,43 @@ export default class Release { * @param from - Tag or SHA to start at * @param to - Tag or SHA to end at (defaults to HEAD) */ - async getCommits(from: string, to = 'HEAD'): Promise { + async getCommits(from: string, to = "HEAD"): Promise { this.logger.verbose.info(`Getting commits from ${from} to ${to}`); const gitlog = await this.git.getGitLog(from, to); - this.logger.veryVerbose.info('Got gitlog:\n', gitlog); + this.logger.veryVerbose.info("Got gitlog:\n", gitlog); const logParse = await this.createLogParse(); - const commits = (await logParse.normalizeCommits(gitlog)).filter(commit => { - let released: boolean; - - try { - // This determines: Is this commit an ancestor of this commit? - // ↓ ↓ - execSync(`git merge-base --is-ancestor ${from} ${commit.hash}`, { - encoding: 'utf8' - }); - released = false; - } catch (error) { - // --is-ancestor returned false so the commit is **before** "from" - // so do not release this commit again - released = true; - } + const commits = (await logParse.normalizeCommits(gitlog)).filter( + (commit) => { + let released: boolean; + + try { + // This determines: Is this commit an ancestor of this commit? + // ↓ ↓ + execSync(`git merge-base --is-ancestor ${from} ${commit.hash}`, { + encoding: "utf8", + }); + released = false; + } catch (error) { + // --is-ancestor returned false so the commit is **before** "from" + // so do not release this commit again + released = true; + } - if (released) { - const shortHash = commit.hash.slice(0, 8); - this.logger.verbose.warn( - `Commit already released, omitting: ${shortHash}: "${commit.subject}"` - ); - } + if (released) { + const shortHash = commit.hash.slice(0, 8); + this.logger.verbose.warn( + `Commit already released, omitting: ${shortHash}: "${commit.subject}"` + ); + } - return !released; - }); + return !released; + } + ); - this.logger.veryVerbose.info('Added labels to commits:\n', commits); + this.logger.veryVerbose.info("Added labels to commits:\n", commits); return commits; } @@ -402,19 +404,19 @@ export default class Release { labels: ILabelDefinition[], options: ICreateLabelsOptions = {} ) { - const oldLabels = ((await this.git.getProjectLabels()) || []).map(l => + const oldLabels = ((await this.git.getProjectLabels()) || []).map((l) => l.toLowerCase() ); - const labelsToCreate = labels.filter(label => { + const labelsToCreate = labels.filter((label) => { if ( - label.releaseType === 'release' && + label.releaseType === "release" && !this.config.onlyPublishWithReleaseLabel ) { return false; } if ( - label.releaseType === 'skip' && + label.releaseType === "skip" && this.config.onlyPublishWithReleaseLabel ) { return false; @@ -425,8 +427,8 @@ export default class Release { if (!options.dryRun) { await Promise.all( - labelsToCreate.map(async label => { - if (oldLabels.some(o => label.name.toLowerCase() === o)) { + labelsToCreate.map(async (label) => { + if (oldLabels.some((o) => label.name.toLowerCase() === o)) { return this.git.updateLabel(label); } @@ -442,10 +444,10 @@ export default class Release { ); if (justLabelNames.length > 0) { - const state = options.dryRun ? 'Would have created' : 'Created'; - this.logger.log.log(`${state} labels: ${justLabelNames.join(', ')}`); + const state = options.dryRun ? "Would have created" : "Created"; + this.logger.log.log(`${state} labels: ${justLabelNames.join(", ")}`); } else { - const state = options.dryRun ? 'would have been' : 'were'; + const state = options.dryRun ? "would have been" : "were"; this.logger.log.log( `No labels ${state} created, they must have already been present on your project.` ); @@ -466,21 +468,21 @@ export default class Release { * @param from - Tag or SHA to start at * @param to - Tag or SHA to end at (defaults to HEAD) */ - async getSemverBump(from: string, to = 'HEAD'): Promise { + async getSemverBump(from: string, to = "HEAD"): Promise { const commits = await this.getCommits(from, to); - const labels = commits.map(commit => commit.labels); + const labels = commits.map((commit) => commit.labels); const { onlyPublishWithReleaseLabel } = this.config; const options = { onlyPublishWithReleaseLabel }; - this.logger.verbose.info('Calculating SEMVER bump using:\n', { + this.logger.verbose.info("Calculating SEMVER bump using:\n", { labels, versionLabels: this.versionLabels, - options + options, }); const result = calculateSemVerBump(labels, this.versionLabels, options); - this.logger.verbose.success('Calculated SEMVER bump:', result); + this.logger.verbose.success("Calculated SEMVER bump:", result); return result; } @@ -496,13 +498,13 @@ export default class Release { private async createLogParse() { const logParse = new LogParse(); - logParse.hooks.parseCommit.tapPromise('Author Info', async commit => + logParse.hooks.parseCommit.tapPromise("Author Info", async (commit) => this.attachAuthor(commit) ); - logParse.hooks.parseCommit.tapPromise('PR Information', async commit => + logParse.hooks.parseCommit.tapPromise("PR Information", async (commit) => this.addPrInfoToCommit(commit) ); - logParse.hooks.parseCommit.tapPromise('PR Commits', async commit => { + logParse.hooks.parseCommit.tapPromise("PR Commits", async (commit) => { const prsSinceLastRelease = await this.getPRsSinceLastRelease(); return this.getPRForRebasedCommits(commit, prsSinceLastRelease); }); @@ -526,7 +528,7 @@ export default class Release { const firstCommit = await this.git.getFirstCommit(); lastRelease = { - published_at: await this.git.getCommitDate(firstCommit) + published_at: await this.git.getCommitDate(firstCommit), }; } @@ -535,7 +537,7 @@ export default class Release { } const prsSinceLastRelease = await this.git.searchRepo({ - q: `is:pr is:merged merged:>=${lastRelease.published_at}` + q: `is:pr is:merged merged:>=${lastRelease.published_at}`, }); if (!prsSinceLastRelease || !prsSinceLastRelease.items) { @@ -551,7 +553,7 @@ export default class Release { ) ); - return data.map(item => item.data); + return data.map((item) => item.data); } /** @@ -575,13 +577,14 @@ export default class Release { return modifiedCommit; } - const labels = info ? info.data.labels.map(l => l.name) : []; + const labels = info ? info.data.labels.map((l) => l.name) : []; modifiedCommit.labels = [ - ...new Set([...labels, ...modifiedCommit.labels]) + ...new Set([...labels, ...modifiedCommit.labels]), ]; modifiedCommit.pullRequest.body = info.data.body; + modifiedCommit.subject = info.data.title || modifiedCommit.subject; const hasPrOpener = modifiedCommit.authors.some( - author => author.username === info.data.user.login + (author) => author.username === info.data.user.login ); // If we can't find the use who opened the PR in authors attempt @@ -608,14 +611,14 @@ export default class Release { pullRequests: Octokit.PullsGetResponse[] ) { const matchPr = pullRequests.find( - pr => pr.merge_commit_sha === commit.hash + (pr) => pr.merge_commit_sha === commit.hash ); if (!commit.pullRequest && matchPr) { - const labels = matchPr.labels.map(label => label.name) || []; + const labels = matchPr.labels.map((label) => label.name) || []; commit.labels = [...new Set([...labels, ...commit.labels])]; commit.pullRequest = { - number: matchPr.number + number: matchPr.number, }; } @@ -645,7 +648,7 @@ export default class Release { } resolvedAuthors = await Promise.all( - prCommits.map(async prCommit => { + prCommits.map(async (prCommit) => { if (!prCommit.author) { return prCommit.commit.author; } @@ -653,7 +656,7 @@ export default class Release { return { ...prCommit.author, ...(await this.git.getUserByUsername(prCommit.author.login)), - hash: prCommit.sha + hash: prCommit.sha, }; }) ); @@ -668,7 +671,7 @@ export default class Release { name: commit.authorName, email: commit.authorEmail, ...author, - hash: commit.hash + hash: commit.hash, }); } else if (commit.authorEmail) { const author = await this.git.getUserByEmail(commit.authorEmail); @@ -677,17 +680,17 @@ export default class Release { email: commit.authorEmail, name: commit.authorName, ...author, - hash: commit.hash + hash: commit.hash, }); } } - modifiedCommit.authors = resolvedAuthors.map(author => ({ + modifiedCommit.authors = resolvedAuthors.map((author) => ({ ...author, - ...(author && 'login' in author ? { username: author.login } : {}) + ...(author && "login" in author ? { username: author.login } : {}), })); - modifiedCommit.authors.forEach(author => { + modifiedCommit.authors.forEach((author) => { this.logger.veryVerbose.info( `Found author: ${author.username} ${author.email} ${author.name}` ); diff --git a/packages/core/src/semver.ts b/packages/core/src/semver.ts index 168f85352..3a59d7f4d 100644 --- a/packages/core/src/semver.ts +++ b/packages/core/src/semver.ts @@ -1,22 +1,22 @@ -import { VersionLabel } from './release'; +import { VersionLabel } from "./release"; enum SEMVER { - major = 'major', - premajor = 'premajor', - minor = 'minor', - preminor = 'preminor', - patch = 'patch', - prepatch = 'prepatch', - noVersion = '' + major = "major", + premajor = "premajor", + minor = "minor", + preminor = "preminor", + patch = "patch", + prepatch = "prepatch", + noVersion = "", } export const preVersionMap = new Map([ [SEMVER.major, SEMVER.premajor], [SEMVER.minor, SEMVER.preminor], - [SEMVER.patch, SEMVER.prepatch] + [SEMVER.patch, SEMVER.prepatch], ]); -export type IVersionLabels = Map; +export type IVersionLabels = Map; export default SEMVER; @@ -49,7 +49,7 @@ export function calculateSemVerBump( { onlyPublishWithReleaseLabel }: ISemVerOptions = {} ) { const labelSet = new Set(); - const skipReleaseLabels = labelMap.get('skip') || []; + const skipReleaseLabels = labelMap.get("skip") || []; labels.forEach((pr, index) => { // If the head pr has no labels we default to a patch @@ -57,8 +57,8 @@ export function calculateSemVerBump( labelSet.add(SEMVER.patch); } - pr.forEach(label => { - const userLabel = [...labelMap.entries()].find(pair => + pr.forEach((label) => { + const userLabel = [...labelMap.entries()].find((pair) => pair[1].includes(label) ); @@ -71,17 +71,17 @@ export function calculateSemVerBump( let skipRelease = false; if (labels.length > 0) { - const releaseLabels = labelMap.get('release') || []; + const releaseLabels = labelMap.get("release") || []; skipRelease = onlyPublishWithReleaseLabel - ? !labels[0].some(label => releaseLabels.includes(label)) - : labels[0].some(label => skipReleaseLabels.includes(label)); + ? !labels[0].some((label) => releaseLabels.includes(label)) + : labels[0].some((label) => skipReleaseLabels.includes(label)); } // If PRs only have none or skip labels, skip the release const onlyNoReleaseLabels = [...labelSet].reduce( (condition, releaseType) => - condition && (releaseType === 'none' || releaseType === 'skip'), + condition && (releaseType === "none" || releaseType === "skip"), true ); diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 19870c545..cd1d61350 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,12 +1,12 @@ -import * as t from 'io-ts'; +import * as t from "io-ts"; -import { labelDefinition } from './release'; +import { labelDefinition } from "./release"; const author = t.partial({ /** The name of the author to make commits with */ name: t.string, /** The email of the author to make commits with */ - email: t.string + email: t.string, }); export type AuthorInformation = t.TypeOf; @@ -17,7 +17,7 @@ const githubInformation = t.partial({ /** The github graphql api to interact with */ githubGraphqlApi: t.string, /** The branch that is used as the base. defaults to master */ - baseBranch: t.string + baseBranch: t.string, }); export type GithubInformation = t.TypeOf; @@ -26,7 +26,7 @@ const repoInformation = t.partial({ /** The repo of to publish, might be set in package manager file. */ repo: t.string, /** The owner of the repo to publish, might be set in package manager file. */ - owner: t.string + owner: t.string, }); export type RepoInformation = t.TypeOf; @@ -35,7 +35,7 @@ export type PluginConfig = [string, any] | string; const releaseCalculationOptions = t.partial({ /** Instead of publishing every PR only publish when "release" label is present */ - onlyPublishWithReleaseLabel: t.boolean + onlyPublishWithReleaseLabel: t.boolean, }); export type ReleaseCalculationOptions = t.TypeOf< @@ -44,7 +44,7 @@ export type ReleaseCalculationOptions = t.TypeOf< const logOptions = t.partial({ /** Show more logs */ - verbose: t.union([t.boolean, t.tuple([t.boolean, t.boolean])]) + verbose: t.union([t.boolean, t.tuple([t.boolean, t.boolean])]), }); export type LogOptions = t.TypeOf; @@ -70,29 +70,29 @@ const globalOptions = t.partial({ /** Options to pass to "auto comment" */ comment: t.partial({ delete: t.boolean, - edit: t.boolean + edit: t.boolean, }), /** Options to pass to "auto changelog" */ changelog: t.partial({ - message: t.string + message: t.string, }), /** Options to pass to "auto release" */ release: t.partial({ - prerelease: t.boolean + prerelease: t.boolean, }), /** Options to pass to "auto shipit" */ shipit: t.partial({ - onlyGraduateWithReleaseLabel: t.boolean + onlyGraduateWithReleaseLabel: t.boolean, }), /** Options to pass to "auto canary" */ canary: t.partial({ force: t.boolean, - message: t.union([t.literal(false), t.string]) + message: t.union([t.literal(false), t.string]), }), /** Options to pass to "auto next" */ next: t.partial({ - message: t.string - }) + message: t.string, + }), }); export type GlobalOptions = t.TypeOf; @@ -104,8 +104,8 @@ export const autoRc = t.intersection([ githubInformation, author, releaseCalculationOptions, - logOptions - ]) + logOptions, + ]), ]); export type AutoRc = t.TypeOf; @@ -118,8 +118,8 @@ export const loadedAutoRc = t.intersection([ /** Branches to create pre-releases from */ prereleaseBranches: t.array(t.string), /** The branch that is used as the base. defaults to master */ - baseBranch: t.string - }) + baseBranch: t.string, + }), ]); export type LoadedAutoRc = t.TypeOf; diff --git a/packages/core/src/utils/__tests__/exec-promise.test.ts b/packages/core/src/utils/__tests__/exec-promise.test.ts index f91aae2c1..e693020ee 100644 --- a/packages/core/src/utils/__tests__/exec-promise.test.ts +++ b/packages/core/src/utils/__tests__/exec-promise.test.ts @@ -1,38 +1,38 @@ -import exec from '../exec-promise'; +import exec from "../exec-promise"; const warn = jest.fn(); const error = jest.fn(); -jest.mock('../logger.ts', () => () => ({ +jest.mock("../logger.ts", () => () => ({ // @ts-ignore log: { warn: (...args) => warn(...args) }, // @ts-ignore - verbose: { error: (...args) => error(...args) } + verbose: { error: (...args) => error(...args) }, })); beforeEach(() => { jest.clearAllMocks(); }); -test('resolves stdout', async () => { - expect(await exec('echo', ['foo'])).toBe('foo'); +test("resolves stdout", async () => { + expect(await exec("echo", ["foo"])).toBe("foo"); }); -test('filters out anything but strings', async () => { - expect(await exec('echo', ['foo', false, undefined, 'baz'])).toBe('foo baz'); +test("filters out anything but strings", async () => { + expect(await exec("echo", ["foo", false, undefined, "baz"])).toBe("foo baz"); }); -test('fails correctly', async () => { +test("fails correctly", async () => { expect.assertions(1); - return expect(exec('false')).rejects.toMatchInlineSnapshot( + return expect(exec("false")).rejects.toMatchInlineSnapshot( `[Error: Running command 'false' with args [] failed]` ); }); -test('appends stdout and stderr', async () => { +test("appends stdout and stderr", async () => { expect.assertions(1); return expect( - exec('echo', ['foo', '&&', '>&2', 'echo', '"this error"', '&&', 'false']) + exec("echo", ["foo", "&&", ">&2", "echo", '"this error"', "&&", "false"]) ).rejects.toMatchInlineSnapshot(` [Error: Running command 'echo' with args [foo, &&, >&2, echo, "this error", &&, false] failed @@ -44,9 +44,9 @@ test('appends stdout and stderr', async () => { `); }); -test('prints stderr when exec exits without a code', async () => { - jest.spyOn(console, 'log').mockImplementation(); +test("prints stderr when exec exits without a code", async () => { + jest.spyOn(console, "log").mockImplementation(); await exec('>&2 echo "this error"'); - return expect(warn).toHaveBeenCalledWith('this error\n'); + return expect(warn).toHaveBeenCalledWith("this error\n"); }); diff --git a/packages/core/src/utils/__tests__/get-repository.test.ts b/packages/core/src/utils/__tests__/get-repository.test.ts index db4a7cadd..091b03500 100644 --- a/packages/core/src/utils/__tests__/get-repository.test.ts +++ b/packages/core/src/utils/__tests__/get-repository.test.ts @@ -1,26 +1,26 @@ -import execPromise from '../exec-promise'; -import getRepository from '../get-repository'; +import execPromise from "../exec-promise"; +import getRepository from "../get-repository"; const execSpy = execPromise as jest.Mock; // @ts-ignore -jest.mock('../exec-promise.ts'); +jest.mock("../exec-promise.ts"); -describe('getRepository', () => { - test('should do nothing without a configured remote', async () => { - execSpy.mockReturnValueOnce(Promise.resolve('')); +describe("getRepository", () => { + test("should do nothing without a configured remote", async () => { + execSpy.mockReturnValueOnce(Promise.resolve("")); expect(await getRepository()).toBeUndefined(); }); - test('should do nothing if parsing origin fails', async () => { - execSpy.mockReturnValueOnce(Promise.resolve('foo')); + test("should do nothing if parsing origin fails", async () => { + execSpy.mockReturnValueOnce(Promise.resolve("foo")); expect(await getRepository()).toBeUndefined(); }); - test('should return owner/repo if possible', async () => { - execSpy.mockReturnValueOnce(Promise.resolve('foo/bar')); + test("should return owner/repo if possible", async () => { + execSpy.mockReturnValueOnce(Promise.resolve("foo/bar")); expect(await getRepository()).toStrictEqual({ - owner: 'foo', - repo: 'bar' + owner: "foo", + repo: "bar", }); }); }); diff --git a/packages/core/src/utils/__tests__/load-plugin-canary.test.ts b/packages/core/src/utils/__tests__/load-plugin-canary.test.ts index 5b4f95945..284987329 100644 --- a/packages/core/src/utils/__tests__/load-plugin-canary.test.ts +++ b/packages/core/src/utils/__tests__/load-plugin-canary.test.ts @@ -1,22 +1,22 @@ -import loadPlugin from '../load-plugins'; -import { dummyLog } from '../logger'; +import loadPlugin from "../load-plugins"; +import { dummyLog } from "../logger"; const logger = dummyLog(); jest.mock( - '@auto-canary/baz', + "@auto-canary/baz", () => ({ default: class { - name = 'baz'; - } + name = "baz"; + }, }), { - virtual: true + virtual: true, } ); -describe('loadPlugins', () => { - test('should load canary plugins', () => { - expect(loadPlugin(['baz', {}], logger)?.name).toBe('baz'); +describe("loadPlugins", () => { + test("should load canary plugins", () => { + expect(loadPlugin(["baz", {}], logger)?.name).toBe("baz"); }); }); diff --git a/packages/core/src/utils/__tests__/load-plugin-windows.test.ts b/packages/core/src/utils/__tests__/load-plugin-windows.test.ts index 00e3dc3f4..6bab6ed7c 100644 --- a/packages/core/src/utils/__tests__/load-plugin-windows.test.ts +++ b/packages/core/src/utils/__tests__/load-plugin-windows.test.ts @@ -1,24 +1,24 @@ -import loadPlugin from '../load-plugins'; -import { dummyLog } from '../logger'; +import loadPlugin from "../load-plugins"; +import { dummyLog } from "../logger"; const logger = dummyLog(); jest.mock( - 'C:\\plugins\\filter-non-pull-request.js', + "C:\\plugins\\filter-non-pull-request.js", () => ({ default: class { - name = 'foo'; - } + name = "foo"; + }, }), { - virtual: true + virtual: true, } ); -describe('loadPlugins', () => { - test('should load official plugins', () => { +describe("loadPlugins", () => { + test("should load official plugins", () => { expect( - loadPlugin(['C:\\plugins\\filter-non-pull-request.js', {}], logger)?.name - ).toBe('foo'); + loadPlugin(["C:\\plugins\\filter-non-pull-request.js", {}], logger)?.name + ).toBe("foo"); }); }); diff --git a/packages/core/src/utils/__tests__/load-plugin.test.ts b/packages/core/src/utils/__tests__/load-plugin.test.ts index af51a2ab1..d1448428b 100644 --- a/packages/core/src/utils/__tests__/load-plugin.test.ts +++ b/packages/core/src/utils/__tests__/load-plugin.test.ts @@ -1,88 +1,88 @@ -import path from 'path'; -import loadPlugin from '../load-plugins'; -import { dummyLog } from '../logger'; +import path from "path"; +import loadPlugin from "../load-plugins"; +import { dummyLog } from "../logger"; const logger = dummyLog(); jest.mock( - 'auto-plugin-foo', + "auto-plugin-foo", () => ({ default: class { - name = 'foo'; - } + name = "foo"; + }, }), { - virtual: true + virtual: true, } ); jest.mock( - '@my-scope/auto-plugin-bar', + "@my-scope/auto-plugin-bar", () => ({ default: class { - name = 'bar'; - } + name = "bar"; + }, }), { virtual: true } ); jest.mock( - '@auto-it/baz', + "@auto-it/baz", () => ({ default: class { - name = 'baz'; - } + name = "baz"; + }, }), { - virtual: true + virtual: true, } ); -describe('loadPlugins', () => { - test('should load official plugins', () => { - expect(loadPlugin(['baz', {}], logger)?.name).toBe('baz'); - expect(loadPlugin(['@auto-it/baz', {}], logger)?.name).toBe('baz'); +describe("loadPlugins", () => { + test("should load official plugins", () => { + expect(loadPlugin(["baz", {}], logger)?.name).toBe("baz"); + expect(loadPlugin(["@auto-it/baz", {}], logger)?.name).toBe("baz"); }); - test('should load community plugins', () => { - expect(loadPlugin(['foo', {}], logger)?.name).toBe('foo'); - expect(loadPlugin(['auto-plugin-foo', {}], logger)?.name).toBe('foo'); + test("should load community plugins", () => { + expect(loadPlugin(["foo", {}], logger)?.name).toBe("foo"); + expect(loadPlugin(["auto-plugin-foo", {}], logger)?.name).toBe("foo"); }); - test('should load scoped plugins', () => { - expect(loadPlugin(['@my-scope/auto-plugin-bar', {}], logger)?.name).toBe( - 'bar' + test("should load scoped plugins", () => { + expect(loadPlugin(["@my-scope/auto-plugin-bar", {}], logger)?.name).toBe( + "bar" ); }); - test('should require custom plugins -- fallback to cwd', () => { + test("should require custom plugins -- fallback to cwd", () => { expect( - loadPlugin([path.join(__dirname, './test-plugin.ts'), {}], logger) + loadPlugin([path.join(__dirname, "./test-plugin.ts"), {}], logger) ).toStrictEqual( expect.objectContaining({ - name: 'foo', - config: {} + name: "foo", + config: {}, }) ); }); - test('should require custom plugins -- surface errors', () => { + test("should require custom plugins -- surface errors", () => { expect(() => loadPlugin( - [path.join(__dirname, './test-plugin-malformed.js'), {}], + [path.join(__dirname, "./test-plugin-malformed.js"), {}], logger ) ).toThrow(); }); - test('should load config', () => { + test("should load config", () => { expect( loadPlugin( - [path.join(__dirname, './test-plugin.ts'), 'do the thing'], + [path.join(__dirname, "./test-plugin.ts"), "do the thing"], logger ) ).toStrictEqual( expect.objectContaining({ - name: 'foo', - config: 'do the thing' + name: "foo", + config: "do the thing", }) ); }); diff --git a/packages/core/src/utils/__tests__/test-plugin.ts b/packages/core/src/utils/__tests__/test-plugin.ts index a9df2c1c2..89b96d471 100644 --- a/packages/core/src/utils/__tests__/test-plugin.ts +++ b/packages/core/src/utils/__tests__/test-plugin.ts @@ -1,6 +1,6 @@ module.exports = class Test { /** The name of the plugin */ - name = 'foo'; + name = "foo"; /** The options of the plugin */ config: {}; diff --git a/packages/core/src/utils/__tests__/test.js b/packages/core/src/utils/__tests__/test.js index e684e3a35..f5cd6246a 100644 --- a/packages/core/src/utils/__tests__/test.js +++ b/packages/core/src/utils/__tests__/test.js @@ -1 +1 @@ -module.exports = 'success'; \ No newline at end of file +module.exports = "success"; diff --git a/packages/core/src/utils/__tests__/try-require.test.ts b/packages/core/src/utils/__tests__/try-require.test.ts index 9dd32d3d7..bae7354cd 100644 --- a/packages/core/src/utils/__tests__/try-require.test.ts +++ b/packages/core/src/utils/__tests__/try-require.test.ts @@ -1,30 +1,30 @@ -import path from 'path'; -import tryRequire from '../try-require'; +import path from "path"; +import tryRequire from "../try-require"; -jest.mock('test', () => 'success', { - virtual: true +jest.mock("test", () => "success", { + virtual: true, }); -jest.mock('npm', () => 'success', { - virtual: true +jest.mock("npm", () => "success", { + virtual: true, }); -describe('try require', () => { - test('should fall back to normal require', async () => { - expect(tryRequire('test')).toBe('success'); +describe("try require", () => { + test("should fall back to normal require", async () => { + expect(tryRequire("test")).toBe("success"); }); - test('should not fall back to normal require for npm', async () => { - expect(tryRequire('npm')).not.toBe('success'); + test("should not fall back to normal require for npm", async () => { + expect(tryRequire("npm")).not.toBe("success"); }); - test('should return nothing if not found', async () => { - expect(tryRequire('foobar')).toBeUndefined(); + test("should return nothing if not found", async () => { + expect(tryRequire("foobar")).toBeUndefined(); }); - test('should require from a directory', async () => { + test("should require from a directory", async () => { expect( - tryRequire('./__tests__/test', path.resolve(path.join(__dirname, '..'))) - ).toBe('success'); + tryRequire("./__tests__/test", path.resolve(path.join(__dirname, ".."))) + ).toBe("success"); }); }); diff --git a/packages/core/src/utils/__tests__/verify-auth.test.ts b/packages/core/src/utils/__tests__/verify-auth.test.ts index 1fb626f0d..e7d84391b 100644 --- a/packages/core/src/utils/__tests__/verify-auth.test.ts +++ b/packages/core/src/utils/__tests__/verify-auth.test.ts @@ -1,36 +1,36 @@ -import verifyAuth from '../verify-auth'; -import childProcess from 'child_process'; +import verifyAuth from "../verify-auth"; +import childProcess from "child_process"; const spawn = childProcess.spawn as jest.Mock; -jest.mock('child_process'); +jest.mock("child_process"); -describe('verify-auth', () => { - test('should handle error', async () => { +describe("verify-auth", () => { + test("should handle error", async () => { spawn.mockImplementationOnce(() => ({ stderr: { on: () => {} }, on: () => { throw new Error(); - } + }, })); - expect(await verifyAuth('origin', 'master')).toBe(false); + expect(await verifyAuth("origin", "master")).toBe(false); }); - test('should verify auth when we can push to remote', async () => { + test("should verify auth when we can push to remote", async () => { spawn.mockImplementationOnce(() => ({ stderr: { on: () => {} }, - on: (_: string, cb: () => void) => cb() + on: (_: string, cb: () => void) => cb(), })); - expect(await verifyAuth('origin', 'master')).toBe(true); + expect(await verifyAuth("origin", "master")).toBe(true); }); test("should not verify auth when we can't push to remote", async () => { spawn.mockImplementationOnce(() => ({ stderr: { on: (_: string, cb: (data: string) => void) => - cb('fatal: could not read Username') + cb("fatal: could not read Username"), }, - on: (_: string, cb: () => void) => cb() + on: (_: string, cb: () => void) => cb(), })); - expect(await verifyAuth('bad', 'master')).toBe(false); + expect(await verifyAuth("bad", "master")).toBe(false); }); }); diff --git a/packages/core/src/utils/exec-promise.ts b/packages/core/src/utils/exec-promise.ts index 764467bbb..8df7995f7 100644 --- a/packages/core/src/utils/exec-promise.ts +++ b/packages/core/src/utils/exec-promise.ts @@ -1,5 +1,5 @@ -import { spawn } from 'child_process'; -import createLog from './logger'; +import { spawn } from "child_process"; +import createLog from "./logger"; const log = createLog(); @@ -16,57 +16,57 @@ export default async function execPromise( ) { const callSite = new Error().stack; const filteredArgs = args.filter( - (arg): arg is string => typeof arg === 'string' + (arg): arg is string => typeof arg === "string" ); return new Promise((completed, reject) => { const child = spawn(cmd, filteredArgs, { cwd: process.cwd(), env: process.env, - shell: true + shell: true, }); - let allStdout = ''; - let allStderr = ''; + let allStdout = ""; + let allStderr = ""; if (child.stdout) { - child.stdout.on('data', async (data: Buffer) => { + child.stdout.on("data", async (data: Buffer) => { const stdout = data.toString(); allStdout += stdout; }); } if (child.stderr) { - child.stderr.on('data', (data: Buffer) => { + child.stderr.on("data", (data: Buffer) => { const stderr = data.toString(); allStderr += stderr; }); } // This usually occurs during dev-time, when you have the wrong command - child.on('error', err => { + child.on("error", (err) => { reject( new Error(`Failed to run '${cmd}' - ${err.message} \n\n\n${allStderr}`) ); }); - child.on('exit', code => { + child.on("exit", (code) => { // No code, no errors if (code) { // The command bailed for whatever reason, print a verbose error message // with the stdout underneath - let appendedStdErr = ''; - appendedStdErr += allStdout.length ? `\n\n${allStdout}` : ''; - appendedStdErr += allStderr.length ? `\n\n${allStderr}` : ''; + let appendedStdErr = ""; + appendedStdErr += allStdout.length ? `\n\n${allStdout}` : ""; + appendedStdErr += allStderr.length ? `\n\n${allStderr}` : ""; reject( new Error( `Running command '${cmd}' with args [${args.join( - ', ' + ", " )}] failed${appendedStdErr}` ) ); - log.verbose.error('Called from:', callSite); + log.verbose.error("Called from:", callSite); } else { // Tools can occasionally print to stderr but not fail, so print that just in case. if (allStderr.length) { diff --git a/packages/core/src/utils/get-current-branch.ts b/packages/core/src/utils/get-current-branch.ts index b376e7c44..a89034cca 100644 --- a/packages/core/src/utils/get-current-branch.ts +++ b/packages/core/src/utils/get-current-branch.ts @@ -1,16 +1,16 @@ -import envCi from 'env-ci'; -import { execSync } from 'child_process'; +import envCi from "env-ci"; +import { execSync } from "child_process"; const env = envCi(); /** Get the current branch the git repo is set to */ export function getCurrentBranch() { - const isPR = 'isPr' in env && env.isPr; + const isPR = "isPr" in env && env.isPr; let branch: string | undefined; // env-ci sets branch to target branch (ex: master) in some CI services. // so we should make sure we aren't in a PR just to be safe - if (isPR && 'prBranch' in env) { + if (isPR && "prBranch" in env) { branch = env.prBranch; } else { branch = env.branch; @@ -18,9 +18,9 @@ export function getCurrentBranch() { if (!branch) { try { - branch = execSync('git symbolic-ref --short HEAD', { - encoding: 'utf8', - stdio: 'ignore' + branch = execSync("git symbolic-ref --short HEAD", { + encoding: "utf8", + stdio: "ignore", }); } catch (error) {} } diff --git a/packages/core/src/utils/get-lerna-packages.ts b/packages/core/src/utils/get-lerna-packages.ts index cea10bcbe..8e29e025e 100644 --- a/packages/core/src/utils/get-lerna-packages.ts +++ b/packages/core/src/utils/get-lerna-packages.ts @@ -1,4 +1,4 @@ -import execPromise from './exec-promise'; +import execPromise from "./exec-promise"; interface LernaPackage { /** Path to package */ @@ -11,9 +11,9 @@ interface LernaPackage { /** Get all of the packages in the lerna monorepo */ export default async function getLernaPackages(): Promise { - return execPromise('npx', ['lerna', 'ls', '-pl']).then(res => - res.split('\n').map(packageInfo => { - const [packagePath, name, version] = packageInfo.split(':'); + return execPromise("npx", ["lerna", "ls", "-pl"]).then((res) => + res.split("\n").map((packageInfo) => { + const [packagePath, name, version] = packageInfo.split(":"); return { path: packagePath, name, version }; }) ); diff --git a/packages/core/src/utils/get-repository.ts b/packages/core/src/utils/get-repository.ts index d43b8c6cc..5dd177754 100644 --- a/packages/core/src/utils/get-repository.ts +++ b/packages/core/src/utils/get-repository.ts @@ -1,14 +1,14 @@ -import parseGitHubUrl from 'parse-github-url'; -import on from 'await-to-js'; +import parseGitHubUrl from "parse-github-url"; +import on from "await-to-js"; -import execPromise from './exec-promise'; +import execPromise from "./exec-promise"; /** * Get the owner and repo from the configure remote "origin" */ export default async function getRepository() { const [, origin] = await on( - execPromise('git', ['remote', 'get-url', 'origin']) + execPromise("git", ["remote", "get-url", "origin"]) ); if (origin) { diff --git a/packages/core/src/utils/in-folder.ts b/packages/core/src/utils/in-folder.ts index 84e3c2d54..ae83ae787 100644 --- a/packages/core/src/utils/in-folder.ts +++ b/packages/core/src/utils/in-folder.ts @@ -1,10 +1,10 @@ -import path from 'path'; +import path from "path"; /** Check if one path is within a parent path */ const inFolder = (parent: string, child: string) => { const relative = path.relative(parent, child); - return Boolean(!relative?.startsWith('..') && !path.isAbsolute(relative)); + return Boolean(!relative?.startsWith("..") && !path.isAbsolute(relative)); }; -export default inFolder; \ No newline at end of file +export default inFolder; diff --git a/packages/core/src/utils/load-plugins.ts b/packages/core/src/utils/load-plugins.ts index 8a73bcf88..f824643ea 100644 --- a/packages/core/src/utils/load-plugins.ts +++ b/packages/core/src/utils/load-plugins.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import * as path from 'path'; -import Auto from '../auto'; -import { ILogger } from './logger'; -import tryRequire from './try-require'; -import InteractiveInit from '../init'; +import * as path from "path"; +import Auto from "../auto"; +import { ILogger } from "./logger"; +import tryRequire from "./try-require"; +import InteractiveInit from "../init"; export type IPluginConstructor = new (options?: any) => IPlugin; @@ -40,8 +40,8 @@ export default function loadPlugin( extendedLocation?: string ): IPlugin | undefined { const isLocal = - pluginPath.startsWith('.') || - pluginPath.startsWith('/') || + pluginPath.startsWith(".") || + pluginPath.startsWith("/") || pluginPath.match(/^[A-Z]:\\/); // Support for windows paths /** Attempt to require a plugin */ @@ -75,9 +75,9 @@ export default function loadPlugin( if (!plugin) { const pkgPath = path.join( __dirname, - '../../../../../plugins/', + "../../../../../plugins/", pluginPath, - 'dist/index.js' + "dist/index.js" ); plugin = attempt(pkgPath); } @@ -89,20 +89,20 @@ export default function loadPlugin( // Try importing official plugin if (!plugin) { - plugin = attempt(path.join('@auto-it', pluginPath)); + plugin = attempt(path.join("@auto-it", pluginPath)); } // Try importing canary version of plugin if (!plugin) { - plugin = attempt(path.join('@auto-canary', pluginPath)); + plugin = attempt(path.join("@auto-canary", pluginPath)); } // Try requiring a package if ( !plugin && - (pluginPath.includes('/auto-plugin-') || - pluginPath.startsWith('auto-plugin-') || - pluginPath.startsWith('@auto-it')) + (pluginPath.includes("/auto-plugin-") || + pluginPath.startsWith("auto-plugin-") || + pluginPath.startsWith("@auto-it")) ) { plugin = attempt(pluginPath); } @@ -113,7 +113,7 @@ export default function loadPlugin( } try { - if ('default' in plugin && plugin.default) { + if ("default" in plugin && plugin.default) { // eslint-disable-next-line new-cap return new plugin.default(options); } diff --git a/packages/core/src/utils/logger.ts b/packages/core/src/utils/logger.ts index b44355882..8ae9baf83 100644 --- a/packages/core/src/utils/logger.ts +++ b/packages/core/src/utils/logger.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/ban-ts-ignore */ -import signale from 'signale'; +import signale from "signale"; -export type LogLevel = undefined | 'verbose' | 'veryVerbose'; +export type LogLevel = undefined | "verbose" | "veryVerbose"; export interface ILogger { /** The level at which to log messages */ @@ -20,19 +20,19 @@ export function dummyLog(): ILogger { return { log: new signale.Signale({ disabled: true }), verbose: new signale.Signale({ disabled: true }), - veryVerbose: new signale.Signale({ disabled: true }) + veryVerbose: new signale.Signale({ disabled: true }), }; } const logger: ILogger = { log: new signale.Signale(), verbose: new signale.Signale(), - veryVerbose: new signale.Signale() + veryVerbose: new signale.Signale(), }; /** Turn the logs on an off */ function toggleLogs( - options: Record, boolean> + options: Record, boolean> ) { Object.entries(options).forEach(([level, enabled]) => { if (enabled) { @@ -49,9 +49,9 @@ function toggleLogs( export function setLogLevel(newLogLevel: LogLevel) { logger.logLevel = newLogLevel; - if (logger.logLevel === 'verbose') { + if (logger.logLevel === "verbose") { toggleLogs({ log: true, verbose: true, veryVerbose: false }); - } else if (logger.logLevel === 'veryVerbose') { + } else if (logger.logLevel === "veryVerbose") { toggleLogs({ log: true, verbose: true, veryVerbose: true }); } else { toggleLogs({ log: true, verbose: false, veryVerbose: false }); diff --git a/packages/core/src/utils/make-hooks.ts b/packages/core/src/utils/make-hooks.ts index 7ca8bd1a4..9c65ff378 100644 --- a/packages/core/src/utils/make-hooks.ts +++ b/packages/core/src/utils/make-hooks.ts @@ -4,71 +4,71 @@ import { AsyncSeriesWaterfallHook, SyncHook, SyncWaterfallHook, - AsyncSeriesHook -} from 'tapable'; -import { IAutoHooks } from '../auto'; -import { IChangelogHooks } from '../changelog'; -import { ILogParseHooks } from '../log-parse'; -import { IReleaseHooks } from '../release'; -import { InteractiveInitHooks } from '../init'; + AsyncSeriesHook, +} from "tapable"; +import { IAutoHooks } from "../auto"; +import { IChangelogHooks } from "../changelog"; +import { ILogParseHooks } from "../log-parse"; +import { IReleaseHooks } from "../release"; +import { InteractiveInitHooks } from "../init"; /** Make the hooks for "auto" */ export const makeHooks = (): IAutoHooks => ({ - beforeRun: new SyncHook(['config']), - modifyConfig: new SyncWaterfallHook(['config']), - validateConfig: new AsyncSeriesBailHook(['name', 'options']), - beforeShipIt: new AsyncSeriesHook(['context']), - afterAddToChangelog: new AsyncSeriesHook(['context']), - beforeCommitChangelog: new AsyncSeriesHook(['context']), - afterShipIt: new AsyncParallelHook(['version', 'commits', 'context']), - makeRelease: new AsyncSeriesBailHook(['releaseInfo']), - afterRelease: new AsyncParallelHook(['releaseInfo']), - onCreateRelease: new SyncHook(['options']), - onCreateChangelog: new SyncHook(['changelog', 'version']), - onCreateLogParse: new SyncHook(['logParse']), + beforeRun: new SyncHook(["config"]), + modifyConfig: new SyncWaterfallHook(["config"]), + validateConfig: new AsyncSeriesBailHook(["name", "options"]), + beforeShipIt: new AsyncSeriesHook(["context"]), + afterAddToChangelog: new AsyncSeriesHook(["context"]), + beforeCommitChangelog: new AsyncSeriesHook(["context"]), + afterShipIt: new AsyncParallelHook(["version", "commits", "context"]), + makeRelease: new AsyncSeriesBailHook(["releaseInfo"]), + afterRelease: new AsyncParallelHook(["releaseInfo"]), + onCreateRelease: new SyncHook(["options"]), + onCreateChangelog: new SyncHook(["changelog", "version"]), + onCreateLogParse: new SyncHook(["logParse"]), getAuthor: new AsyncSeriesBailHook(), getPreviousVersion: new AsyncSeriesBailHook(), getRepository: new AsyncSeriesBailHook(), - version: new AsyncParallelHook(['version']), + version: new AsyncParallelHook(["version"]), afterVersion: new AsyncParallelHook(), - publish: new AsyncParallelHook(['version']), + publish: new AsyncParallelHook(["version"]), afterPublish: new AsyncParallelHook(), - canary: new AsyncSeriesBailHook(['canaryVersion', 'postFix']), - next: new AsyncSeriesWaterfallHook(['preReleaseVersions', 'bump']) + canary: new AsyncSeriesBailHook(["canaryVersion", "postFix"]), + next: new AsyncSeriesWaterfallHook(["preReleaseVersions", "bump"]), }); /** Make the hooks for "Release" */ export const makeReleaseHooks = (): IReleaseHooks => ({ - onCreateChangelog: new SyncHook(['changelog', 'version']), + onCreateChangelog: new SyncHook(["changelog", "version"]), createChangelogTitle: new AsyncSeriesBailHook([]), - onCreateLogParse: new SyncHook(['logParse']) + onCreateLogParse: new SyncHook(["logParse"]), }); /** Make the hooks for "LogParse" */ export const makeLogParseHooks = (): ILogParseHooks => ({ - parseCommit: new AsyncSeriesWaterfallHook(['commit']), - omitCommit: new AsyncSeriesBailHook(['commit']) + parseCommit: new AsyncSeriesWaterfallHook(["commit"]), + omitCommit: new AsyncSeriesBailHook(["commit"]), }); /** Make the hooks for "InteractiveInit" */ export const makeInteractiveInitHooks = (): InteractiveInitHooks => ({ - writeRcFile: new AsyncSeriesBailHook(['rcFile']), + writeRcFile: new AsyncSeriesBailHook(["rcFile"]), getRepo: new AsyncSeriesBailHook([]), getAuthor: new AsyncSeriesBailHook([]), - createEnv: new AsyncSeriesWaterfallHook(['vars']), - configurePlugin: new AsyncSeriesBailHook(['pluginName']) + createEnv: new AsyncSeriesWaterfallHook(["vars"]), + configurePlugin: new AsyncSeriesBailHook(["pluginName"]), }); /** Make the hooks for "Changelog" */ export const makeChangelogHooks = (): IChangelogHooks => ({ - addToBody: new AsyncSeriesWaterfallHook(['notes', 'commits']), - renderChangelogLine: new AsyncSeriesWaterfallHook(['lineData']), - renderChangelogTitle: new AsyncSeriesBailHook(['commits', 'lineRender']), + addToBody: new AsyncSeriesWaterfallHook(["notes", "commits"]), + renderChangelogLine: new AsyncSeriesWaterfallHook(["lineData"]), + renderChangelogTitle: new AsyncSeriesBailHook(["commits", "lineRender"]), renderChangelogAuthor: new AsyncSeriesBailHook([ - 'author', - 'commit', - 'options' + "author", + "commit", + "options", ]), - renderChangelogAuthorLine: new AsyncSeriesBailHook(['author', 'user']), - omitReleaseNotes: new AsyncSeriesBailHook(['commit']) + renderChangelogAuthorLine: new AsyncSeriesBailHook(["author", "user"]), + omitReleaseNotes: new AsyncSeriesBailHook(["commit"]), }); diff --git a/packages/core/src/utils/omit.ts b/packages/core/src/utils/omit.ts index 6eefb0097..49d178092 100644 --- a/packages/core/src/utils/omit.ts +++ b/packages/core/src/utils/omit.ts @@ -1,4 +1,4 @@ -import fromEntries from 'fromentries'; +import fromEntries from "fromentries"; /** Omit properties from an object */ export const omit = ( diff --git a/packages/core/src/utils/try-require.ts b/packages/core/src/utils/try-require.ts index 230662aad..3efd7be88 100644 --- a/packages/core/src/utils/try-require.ts +++ b/packages/core/src/utils/try-require.ts @@ -1,6 +1,6 @@ -import importCwd from 'import-cwd'; -import importFrom from 'import-from'; -import createLog from './logger'; +import importCwd from "import-cwd"; +import importFrom from "import-from"; +import createLog from "./logger"; const logger = createLog(); @@ -12,12 +12,12 @@ export default function tryRequire(tryPath: string, from?: string) { } catch (error) { // if we try to actually require npm we will import something that is the actual npm API // not the plugin that we want - if (tryPath === 'npm') { + if (tryPath === "npm") { return; } // If a plugin has any errors we want to inform the user - if (error.code !== 'MODULE_NOT_FOUND') { + if (error.code !== "MODULE_NOT_FOUND") { throw error; } diff --git a/packages/core/src/utils/verify-auth.ts b/packages/core/src/utils/verify-auth.ts index 9325f9a65..028b6bf7d 100644 --- a/packages/core/src/utils/verify-auth.ts +++ b/packages/core/src/utils/verify-auth.ts @@ -1,10 +1,10 @@ -import { spawn } from 'child_process'; +import { spawn } from "child_process"; /** * */ export default function verifyAuth(remote: string, branch: string) { - return new Promise(resolve => { + return new Promise((resolve) => { try { const child = spawn( `git push --dry-run --no-verify ${remote} HEAD:${branch} -q`, @@ -12,18 +12,18 @@ export default function verifyAuth(remote: string, branch: string) { cwd: process.cwd(), env: process.env, detached: true, - shell: true + shell: true, } ); - let err = ''; + let err = ""; - child.stderr.on('data', data => { + child.stderr.on("data", (data) => { err += data.toString(); }); - child.on('exit', () => { - resolve(!err.startsWith('fatal: could not read Username')); + child.on("exit", () => { + resolve(!err.startsWith("fatal: could not read Username")); }); } catch (error) { resolve(false); diff --git a/packages/core/src/validate-config.ts b/packages/core/src/validate-config.ts index 1de4a2471..d1de5abb1 100644 --- a/packages/core/src/validate-config.ts +++ b/packages/core/src/validate-config.ts @@ -1,14 +1,14 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import * as t from 'io-ts'; -import { isRight } from 'fp-ts/lib/Either'; -import { autoRc, AutoRc } from './types'; -import { AsyncSeriesBailHook } from 'tapable'; -import { omit } from './utils/omit'; -import chalk from 'chalk'; -import endent from 'endent'; - -const ignoreTypes = ['PartialType', 'IntersectionType', 'ExactType']; +import * as t from "io-ts"; +import { isRight } from "fp-ts/lib/Either"; +import { autoRc, AutoRc } from "./types"; +import { AsyncSeriesBailHook } from "tapable"; +import { omit } from "./utils/omit"; +import chalk from "chalk"; +import endent from "endent"; + +const ignoreTypes = ["PartialType", "IntersectionType", "ExactType"]; const unexpectedValue = chalk.redBright.bold; const errorPath = chalk.underline.bold; @@ -25,7 +25,7 @@ export type ConfigError = string | ConfigOptionError; /** Format and error as a string */ export function formatError(error: ConfigError) { - if (typeof error === 'string') { + if (typeof error === "string") { return error; } @@ -34,10 +34,10 @@ export function formatError(error: ConfigError) { (Array.isArray(value) && endent` [ - ${value.join(',\n')} + ${value.join(",\n")} ] `) || - (typeof value === 'object' && JSON.stringify(value, null, 2)) || + (typeof value === "object" && JSON.stringify(value, null, 2)) || value; return `${errorPath(`"${path}"`)}\n\nExpected ${chalk.greenBright.bold( @@ -47,41 +47,41 @@ export function formatError(error: ConfigError) { /** Report configuration errors */ function reporter(validation: t.Validation) { - if (validation._tag !== 'Left') { + if (validation._tag !== "Left") { return false; } - const errors: ConfigError[] = validation.left.map(error => { - let parentType = ''; + const errors: ConfigError[] = validation.left.map((error) => { + let parentType = ""; const path = error.context // The context entry with an empty key is the original type ("default // context"), not an type error. - .filter(c => c.key.length > 0) - .filter(c => { + .filter((c) => c.key.length > 0) + .filter((c) => { const tag = (c.type as any)._tag; const include = - parentType === 'ArrayType' || + parentType === "ArrayType" || (!ignoreTypes.includes(tag) && - parentType !== 'UnionType' && - parentType !== 'IntersectionType'); + parentType !== "UnionType" && + parentType !== "IntersectionType"); parentType = tag; return include; }) - .map(c => c.key) - .join('.'); + .map((c) => c.key) + .join("."); return { path, expectedType: error.context[error.context.length - 1].type.name, - value: error.value + value: error.value, }; }); const otherErrors: string[] = []; const grouped = errors.reduce((acc, item) => { - if (typeof item === 'string') { + if (typeof item === "string") { otherErrors.push(item); return acc; } @@ -99,30 +99,30 @@ function reporter(validation: t.Validation) { ...otherErrors, ...Object.entries(grouped) .filter(([path]) => { - return !paths.some(p => p.includes(path) && p !== path); + return !paths.some((p) => p.includes(path) && p !== path); }) .map(([path, group]) => { const expectedType = group - .map(g => g.expectedType) - .map(t => `"${t}"`) - .join(' or '); + .map((g) => g.expectedType) + .map((t) => `"${t}"`) + .join(" or "); const value = group[0].value; return { expectedType, path, - value + value, }; - }) + }), ]; } /** Convert nested object to array of flat key paths */ function flatKeys(obj: Record): string[] { return Object.keys(obj) - .map(key => { - if (typeof obj[key] === 'object') { - return flatKeys(obj[key]).map(sub => `${key}.${sub}`); + .map((key) => { + if (typeof obj[key] === "object") { + return flatKeys(obj[key]).map((sub) => `${key}.${sub}`); } return [key]; @@ -147,7 +147,7 @@ export async function validatePlugins( } await Promise.all( - rc.plugins.map(async plugin => { + rc.plugins.map(async (plugin) => { if (!Array.isArray(plugin)) { return; } @@ -164,10 +164,10 @@ export async function validatePlugins( } const shouldRecurse = [ - 'PartialType', - 'IntersectionType', - 'ArrayType', - 'InterfaceType' + "PartialType", + "IntersectionType", + "ArrayType", + "InterfaceType", ]; /** @@ -180,8 +180,8 @@ function makeExactType( let strictConfigDeclaration = configDeceleration; if ( - 'props' in configDeceleration && - configDeceleration._tag !== 'StrictType' + "props" in configDeceleration && + configDeceleration._tag !== "StrictType" ) { const props: Record = {}; @@ -194,20 +194,20 @@ function makeExactType( ); strictConfigDeclaration = t.exact( - configDeceleration._tag === 'InterfaceType' + configDeceleration._tag === "InterfaceType" ? t.interface({ ...props }) : t.partial({ ...props }) ); - } else if ('types' in configDeceleration) { - const exactInterfaces: t.Any[] = configDeceleration.types.map(propType => + } else if ("types" in configDeceleration) { + const exactInterfaces: t.Any[] = configDeceleration.types.map((propType) => shouldRecurse.includes(propType._tag) ? makeExactType(propType) : propType ); strictConfigDeclaration = - configDeceleration._tag === 'IntersectionType' + configDeceleration._tag === "IntersectionType" ? t.intersection(exactInterfaces as [t.Any, t.Any]) : t.union(exactInterfaces as [t.Any, t.Any]); - } else if ('type' in configDeceleration) { + } else if ("type" in configDeceleration) { strictConfigDeclaration = t.array(makeExactType(configDeceleration.type)); } @@ -236,11 +236,11 @@ export const validateIoConfiguration = ( const correctKeys = flatKeys(exactRc.right); const unknownTopKeys = Object.keys(looseRc.right).filter( - k => !((k as keyof typeof exactRc.right) in exactRc.right) + (k) => !((k as keyof typeof exactRc.right) in exactRc.right) ); const unknownDeepKeys = flatKeys( omit(looseRc.right, unknownTopKeys as any) - ).filter(k => !correctKeys.includes(k)); + ).filter((k) => !correctKeys.includes(k)); const unknownKeys = [...unknownTopKeys, ...unknownDeepKeys]; if (unknownKeys.length === 0) { @@ -251,12 +251,12 @@ export const validateIoConfiguration = ( `${errorPath( `"${name}"` )}\n\nFound unknown configuration keys: ${unexpectedValue( - unknownKeys.join(', ') - )}\n` + unknownKeys.join(", ") + )}\n`, ]; }; -export const validateAutoRc = validateIoConfiguration('.autorc', autoRc); +export const validateAutoRc = validateIoConfiguration(".autorc", autoRc); /** Validate a plugin's configuration. */ export async function validatePluginConfiguration( @@ -267,14 +267,14 @@ export async function validatePluginConfiguration( const validateConfig = validateIoConfiguration(name, pluginDefinition); const errors = await validateConfig(providedOptions); - return errors.map(error => { - if (typeof error === 'string') { + return errors.map((error) => { + if (typeof error === "string") { return error; } return { ...error, - path: error.path ? `${name}.${error.path}` : name + path: error.path ? `${name}.${error.path}` : name, }; }); } diff --git a/plugins/all-contributors/__tests__/all-contributors.test.ts b/plugins/all-contributors/__tests__/all-contributors.test.ts index a77c8394c..471e671cd 100644 --- a/plugins/all-contributors/__tests__/all-contributors.test.ts +++ b/plugins/all-contributors/__tests__/all-contributors.test.ts @@ -1,40 +1,40 @@ -import * as Auto from '@auto-it/core'; -import { execSync } from 'child_process'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; -import makeCommitFromMsg from '@auto-it/core/dist/__tests__/make-commit-from-msg'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import fs from 'fs'; +import * as Auto from "@auto-it/core"; +import { execSync } from "child_process"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import fs from "fs"; -import AllContributors from '../src'; +import AllContributors from "../src"; const exec = jest.fn(); const gitShow = jest.fn(); const getLernaPackages = jest.fn(); getLernaPackages.mockReturnValue(Promise.resolve([])); -gitShow.mockReturnValue(Promise.resolve('')); -exec.mockReturnValue(''); +gitShow.mockReturnValue(Promise.resolve("")); +exec.mockReturnValue(""); -jest.mock('child_process'); +jest.mock("child_process"); // @ts-ignore execSync.mockImplementation(exec); // @ts-ignore -jest.spyOn(Auto, 'execPromise').mockImplementation(gitShow); -jest.spyOn(Auto, 'getLernaPackages').mockImplementation(getLernaPackages); -jest.spyOn(process, 'chdir').mockReturnValue(); +jest.spyOn(Auto, "execPromise").mockImplementation(gitShow); +jest.spyOn(Auto, "getLernaPackages").mockImplementation(getLernaPackages); +jest.spyOn(process, "chdir").mockReturnValue(); const mockRead = (result: string) => jest - .spyOn(fs, 'readFileSync') + .spyOn(fs, "readFileSync") // @ts-ignore .mockReturnValueOnce(result); -describe('All Contributors Plugin', () => { +describe("All Contributors Plugin", () => { afterEach(() => { jest.clearAllMocks(); }); - test('should do nothing for username-less commits', async () => { + test("should do nothing for username-less commits", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); mockRead('{ "contributors": [] }'); @@ -43,21 +43,21 @@ describe('All Contributors Plugin', () => { await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ - makeCommitFromMsg('Do the stuff'), - makeCommitFromMsg('Do the thing', { - files: ['src/index.ts'] - }) - ] + makeCommitFromMsg("Do the stuff"), + makeCommitFromMsg("Do the thing", { + files: ["src/index.ts"], + }), + ], }); expect(exec).not.toHaveBeenCalled(); }); - test('should find a single contribution', async () => { + test("should find a single contribution", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); mockRead('{ "contributors": [] }'); @@ -66,85 +66,85 @@ describe('All Contributors Plugin', () => { await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ { - subject: 'Do the thing', - hash: '123', + subject: "Do the thing", + hash: "123", labels: [], - files: ['src/index.ts'], - authors: [{ username: 'Jeff', hash: '123' }] - } - ] + files: ["src/index.ts"], + authors: [{ username: "Jeff", hash: "123" }], + }, + ], }); expect(exec.mock.calls[0][0]).toBe( - 'npx all-contributors-cli add Jeff code' + "npx all-contributors-cli add Jeff code" ); }); - test('should work for single package', async () => { + test("should work for single package", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); mockRead('{ "contributors": [] }'); - getLernaPackages.mockRejectedValueOnce(new Error('test')); + getLernaPackages.mockRejectedValueOnce(new Error("test")); releasedLabel.apply({ hooks: autoHooks, logger: dummyLog() } as Auto.Auto); await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ { - subject: 'Do the thing', - hash: '123', + subject: "Do the thing", + hash: "123", labels: [], - files: ['src/index.ts'], - authors: [{ username: 'Jeff', hash: '123' }] - } - ] + files: ["src/index.ts"], + authors: [{ username: "Jeff", hash: "123" }], + }, + ], }); expect(exec.mock.calls[0][0]).toBe( - 'npx all-contributors-cli add Jeff code' + "npx all-contributors-cli add Jeff code" ); }); - test('should find contributions from merge commit', async () => { + test("should find contributions from merge commit", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); mockRead('{ "contributors": [] }'); - gitShow.mockReturnValueOnce('src/index.ts'); + gitShow.mockReturnValueOnce("src/index.ts"); releasedLabel.apply({ hooks: autoHooks, logger: dummyLog() } as Auto.Auto); await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ { - subject: 'Do the thing', - hash: '123', + subject: "Do the thing", + hash: "123", labels: [], - files: ['src/index.ts'], - authors: [{ username: 'Jeff', hash: '123' }] - } - ] + files: ["src/index.ts"], + authors: [{ username: "Jeff", hash: "123" }], + }, + ], }); expect(exec.mock.calls[0][0]).toBe( - 'npx all-contributors-cli add Jeff code' + "npx all-contributors-cli add Jeff code" ); }); - test('should find a multiple contributions', async () => { + test("should find a multiple contributions", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); mockRead('{ "contributors": [] }'); @@ -153,49 +153,49 @@ describe('All Contributors Plugin', () => { await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ { - subject: 'Do the thing', - hash: '123', + subject: "Do the thing", + hash: "123", labels: [], - files: ['src/index.ts'], + files: ["src/index.ts"], - authors: [{ username: 'Jeff', hash: '123' }] + authors: [{ username: "Jeff", hash: "123" }], }, { - subject: 'Do other thing', - hash: '123', + subject: "Do other thing", + hash: "123", labels: [], - files: ['src/index.test.ts'], - authors: [{ username: 'Jeff', hash: '123' }] - } - ] + files: ["src/index.test.ts"], + authors: [{ username: "Jeff", hash: "123" }], + }, + ], }); expect(exec.mock.calls[0][0]).toBe( - 'npx all-contributors-cli add Jeff code,test' + "npx all-contributors-cli add Jeff code,test" ); }); - test('should update sub-packages', async () => { + test("should update sub-packages", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); getLernaPackages.mockReturnValueOnce( Promise.resolve([ { - path: 'packages/app', - name: '@foobar/app', - version: '1.2.3' + path: "packages/app", + name: "@foobar/app", + version: "1.2.3", }, { - path: 'packages/lib', - name: '@foobar/lib', - version: '1.2.3' - } + path: "packages/lib", + name: "@foobar/lib", + version: "1.2.3", + }, ]) ); @@ -203,40 +203,40 @@ describe('All Contributors Plugin', () => { await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ { - subject: 'Do the thing', - hash: '123', + subject: "Do the thing", + hash: "123", labels: [], - files: ['packages/app/src/index.ts'], - authors: [{ username: 'Jeff', hash: '123' }] + files: ["packages/app/src/index.ts"], + authors: [{ username: "Jeff", hash: "123" }], }, { - subject: 'Do the docs', - hash: '124', + subject: "Do the docs", + hash: "124", labels: [], - files: ['packages/lib/README.md'], - authors: [{ username: 'Jeff', hash: '124' }] - } - ] + files: ["packages/lib/README.md"], + authors: [{ username: "Jeff", hash: "124" }], + }, + ], }); // root contributors expect(exec.mock.calls[0][0]).toBe( - 'npx all-contributors-cli add Jeff code,doc' + "npx all-contributors-cli add Jeff code,doc" ); // app contributors expect(exec.mock.calls[1][0]).toBe( - 'npx all-contributors-cli add Jeff code' + "npx all-contributors-cli add Jeff code" ); // lib contributors - expect(exec.mock.calls[2][0]).toBe('npx all-contributors-cli add Jeff doc'); + expect(exec.mock.calls[2][0]).toBe("npx all-contributors-cli add Jeff doc"); }); - test('should update old contributors', async () => { + test("should update old contributors", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); mockRead( @@ -247,26 +247,26 @@ describe('All Contributors Plugin', () => { await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ { - subject: 'Do the thing', - hash: '123', + subject: "Do the thing", + hash: "123", labels: [], - files: ['src/index.ts'], - authors: [{ username: 'Jeff', hash: '123' }] - } - ] + files: ["src/index.ts"], + authors: [{ username: "Jeff", hash: "123" }], + }, + ], }); expect(exec.mock.calls[0][0]).toBe( - 'npx all-contributors-cli add Jeff infra,code' + "npx all-contributors-cli add Jeff infra,code" ); }); - test('should include sub-commit contributors', async () => { + test("should include sub-commit contributors", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); mockRead( @@ -277,32 +277,32 @@ describe('All Contributors Plugin', () => { await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ { - subject: 'Do the thing', - hash: '123', + subject: "Do the thing", + hash: "123", labels: [], - files: ['src/index.ts'], + files: ["src/index.ts"], authors: [ - { username: 'Jeff', hash: '123' }, - { username: 'Adam', hash: '456' } - ] - } - ] + { username: "Jeff", hash: "123" }, + { username: "Adam", hash: "456" }, + ], + }, + ], }); expect(exec.mock.calls[0][0]).toBe( - 'npx all-contributors-cli add Jeff infra,code' + "npx all-contributors-cli add Jeff infra,code" ); expect(exec.mock.calls[1][0]).toBe( - 'npx all-contributors-cli add Adam code' + "npx all-contributors-cli add Adam code" ); }); - test('should not update if no new contribution types', async () => { + test("should not update if no new contribution types", async () => { const releasedLabel = new AllContributors(); const autoHooks = makeHooks(); mockRead( @@ -313,15 +313,15 @@ describe('All Contributors Plugin', () => { await autoHooks.afterAddToChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.0', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.0", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ - makeCommitFromMsg('Do the thing', { - files: ['src/index.ts'], - username: 'Jeff' - }) - ] + makeCommitFromMsg("Do the thing", { + files: ["src/index.ts"], + username: "Jeff", + }), + ], }); expect(exec).not.toHaveBeenCalled(); diff --git a/plugins/all-contributors/src/index.ts b/plugins/all-contributors/src/index.ts index d2fcf6ec9..1cfb4f5f9 100644 --- a/plugins/all-contributors/src/index.ts +++ b/plugins/all-contributors/src/index.ts @@ -4,55 +4,55 @@ import { execPromise, getLernaPackages, inFolder, - validatePluginConfiguration -} from '@auto-it/core'; -import botList from '@auto-it/bot-list'; -import fs from 'fs'; -import path from 'path'; -import match from 'anymatch'; -import on from 'await-to-js'; -import { execSync } from 'child_process'; -import { IExtendedCommit } from '@auto-it/core/src/log-parse'; -import * as t from 'io-ts'; -import fromEntries from 'fromentries'; + validatePluginConfiguration, +} from "@auto-it/core"; +import botList from "@auto-it/bot-list"; +import fs from "fs"; +import path from "path"; +import match from "anymatch"; +import on from "await-to-js"; +import { execSync } from "child_process"; +import { IExtendedCommit } from "@auto-it/core/src/log-parse"; +import * as t from "io-ts"; +import fromEntries from "fromentries"; const contributionTypes = [ - 'blog', - 'bug', - 'business', - 'code', - 'content', - 'design', - 'doc', - 'eventOrganizing', - 'example', - 'financial', - 'fundingFinding', - 'ideas', - 'infra', - 'maintenance', - 'platform', - 'plugin', - 'projectManagement', - 'question', - 'review', - 'security', - 'talk', - 'test', - 'tool', - 'translation', - 'tutorial', - 'userTesting', - 'video' + "blog", + "bug", + "business", + "code", + "content", + "design", + "doc", + "eventOrganizing", + "example", + "financial", + "fundingFinding", + "ideas", + "infra", + "maintenance", + "platform", + "plugin", + "projectManagement", + "question", + "review", + "security", + "talk", + "test", + "tool", + "translation", + "tutorial", + "userTesting", + "video", ] as const; type Contribution = typeof contributionTypes[number]; /** Get an rc file if there is one. */ function getRcFile() { try { - const rcFile = path.join(process.cwd(), '.all-contributorsrc'); + const rcFile = path.join(process.cwd(), ".all-contributorsrc"); const config: AllContributorsRc = JSON.parse( - fs.readFileSync(rcFile, 'utf8') + fs.readFileSync(rcFile, "utf8") ); return config; @@ -65,11 +65,11 @@ const pluginOptions = t.partial({ exclude: t.array(t.string), /** Globs to detect change types by */ types: t.partial( - fromEntries(contributionTypes.map(c => [c, pattern])) as Record< + fromEntries(contributionTypes.map((c) => [c, pattern])) as Record< Contribution, typeof pattern > - ) + ), }); export type IAllContributorsPluginOptions = t.TypeOf; @@ -89,18 +89,18 @@ interface AllContributorsRc { const defaultOptions: IAllContributorsPluginOptions = { exclude: botList, types: { - doc: ['**/*.mdx', '**/*.md', '**/docs/**/*', '**/documentation/**/*'], - example: ['**/*.stories*', '**/*.story.*'], - infra: ['**/.circle/**/*', '**/.github/**/*', '**/travis.yml'], - test: ['**/*.test.*', '**/test/**', '**/__tests__/**'], - code: ['**/src/**/*', '**/lib/**/*', '**/package.json', '**/tsconfig.json'] - } + doc: ["**/*.mdx", "**/*.md", "**/docs/**/*", "**/documentation/**/*"], + example: ["**/*.stories*", "**/*.story.*"], + infra: ["**/.circle/**/*", "**/.github/**/*", "**/travis.yml"], + test: ["**/*.test.*", "**/test/**", "**/__tests__/**"], + code: ["**/src/**/*", "**/lib/**/*", "**/package.json", "**/tsconfig.json"], + }, }; /** Automatically add contributors as changelogs are produced. */ export default class AllContributorsPlugin implements IPlugin { /** The name of the plugin */ - name = 'all-contributors'; + name = "all-contributors"; /** The options of the plugin */ readonly options: Required; @@ -109,7 +109,7 @@ export default class AllContributorsPlugin implements IPlugin { constructor(options: IAllContributorsPluginOptions = {}) { this.options = { exclude: [...(defaultOptions.exclude || []), ...(options.exclude || [])], - types: { ...defaultOptions.types, ...options.types } + types: { ...defaultOptions.types, ...options.types }, }; } @@ -126,7 +126,7 @@ export default class AllContributorsPlugin implements IPlugin { async ({ commits }) => { const rootDir = process.cwd(); // Always do the root package - let packages = [{ path: rootDir, name: 'root-package' }]; + let packages = [{ path: rootDir, name: "root-package" }]; try { // Try to get sub-packages @@ -139,8 +139,8 @@ export default class AllContributorsPlugin implements IPlugin { auto.logger.verbose.info(`Updating contributors for: ${name}`); - const includedCommits = commits.filter(commit => - commit.files.some(file => inFolder(path, file)) + const includedCommits = commits.filter((commit) => + commit.files.some((file) => inFolder(path, file)) ); if (includedCommits.length > 0) { @@ -157,21 +157,21 @@ export default class AllContributorsPlugin implements IPlugin { }, Promise.resolve()); process.chdir(rootDir); - const changedFiles = await execPromise('git', [ - 'status', - '--porcelain' + const changedFiles = await execPromise("git", [ + "status", + "--porcelain", ]); if (changedFiles) { - await execPromise('git', ['add', 'README.md']); - await execPromise('git', ['add', '.all-contributorsrc']); - await on(execPromise('git', ['add', '**/README.md'])); - await on(execPromise('git', ['add', '**/.all-contributorsrc'])); - await execPromise('git', [ - 'commit', - '--no-verify', - '-m', - '"Update contributors [skip ci]"' + await execPromise("git", ["add", "README.md"]); + await execPromise("git", ["add", ".all-contributorsrc"]); + await on(execPromise("git", ["add", "**/README.md"])); + await on(execPromise("git", ["add", "**/.all-contributorsrc"])); + await execPromise("git", [ + "commit", + "--no-verify", + "-m", + '"Update contributors [skip ci]"', ]); } } @@ -190,24 +190,24 @@ export default class AllContributorsPlugin implements IPlugin { let didUpdate = false; const commitsWithAllChangedFiles = await Promise.all( - commits.map(async commit => { - const extra = await execPromise('git', [ - 'show', + commits.map(async (commit) => { + const extra = await execPromise("git", [ + "show", '--pretty=""', - '--name-only', - '--first-parent', - '-m', - commit.hash + "--name-only", + "--first-parent", + "-m", + commit.hash, ]); - commit.files = [...new Set([...commit.files, ...extra.split('\n')])]; + commit.files = [...new Set([...commit.files, ...extra.split("\n")])]; return commit; }) ); // 1. Find all the authors and their contribution types - commitsWithAllChangedFiles.forEach(commit => { + commitsWithAllChangedFiles.forEach((commit) => { const { authors } = commit; let { files } = commit; @@ -217,11 +217,11 @@ export default class AllContributorsPlugin implements IPlugin { const isType = (file: string) => match(this.options.types[type as Contribution] || [], file); const isMatch = files.some(isType); - files = files.filter(file => !isType(file)); + files = files.filter((file) => !isType(file)); return isMatch; }) - .forEach(contribution => { + .forEach((contribution) => { authors.forEach(({ username }) => { if (!username) { return; @@ -236,17 +236,17 @@ export default class AllContributorsPlugin implements IPlugin { }); }); - auto.logger.verbose.info('Found contributions:', authorContributions); + auto.logger.verbose.info("Found contributions:", authorContributions); // 2. Determine if contributor has update Object.entries(authorContributions).forEach(([username, contributions]) => { const { contributions: old = [] } = config.contributors.find( - contributor => + (contributor) => contributor.login.toLowerCase() === username.toLowerCase() ) || {}; const hasNew = [...contributions].find( - contribution => !old.includes(contribution) + (contribution) => !old.includes(contribution) ); if (hasNew && !this.options.exclude.includes(username)) { @@ -257,9 +257,9 @@ export default class AllContributorsPlugin implements IPlugin { execSync( `npx all-contributors-cli add ${username} ${[ - ...newContributions - ].join(',')}`, - { stdio: 'inherit' } + ...newContributions, + ].join(",")}`, + { stdio: "inherit" } ); } else { auto.logger.verbose.warn(`"${username}" had no new contributions...`); @@ -267,7 +267,7 @@ export default class AllContributorsPlugin implements IPlugin { }); if (didUpdate) { - auto.logger.log.success('Updated contributors!'); + auto.logger.log.success("Updated contributors!"); } } } diff --git a/plugins/chrome/src/index.ts b/plugins/chrome/src/index.ts index b3552e472..3ac903277 100644 --- a/plugins/chrome/src/index.ts +++ b/plugins/chrome/src/index.ts @@ -2,12 +2,12 @@ import { Auto, execPromise, IPlugin, - validatePluginConfiguration -} from '@auto-it/core'; -import * as fs from 'fs'; -import { inc, ReleaseType } from 'semver'; -import { promisify } from 'util'; -import * as t from 'io-ts'; + validatePluginConfiguration, +} from "@auto-it/core"; +import * as fs from "fs"; +import { inc, ReleaseType } from "semver"; +import { promisify } from "util"; +import * as t from "io-ts"; const readFile = promisify(fs.readFile); const writeFile = promisify(fs.writeFile); @@ -18,20 +18,20 @@ const pluginOptions = t.partial({ /** The path to the manifest of the chrome extension */ manifest: t.string, /** A path to the packaged extension */ - build: t.string + build: t.string, }); export type IChromeWebStoreConfig = t.TypeOf; /** Get the chrome plugin manifest. */ async function getManifest(path: string) { - return JSON.parse(await readFile(path, 'utf-8')); + return JSON.parse(await readFile(path, "utf-8")); } /** This plugin allows you to automate the publishing of chrome extensions */ export default class ChromeWebStorePlugin implements IPlugin { /** The name of the plugin */ - readonly name = 'chrome'; + readonly name = "chrome"; /** The options of the plugin */ private readonly options: Required; @@ -40,8 +40,8 @@ export default class ChromeWebStorePlugin implements IPlugin { constructor(config: IChromeWebStoreConfig) { this.options = { id: config.id || process.env.EXTENSION_ID!, - manifest: config.manifest || 'manifest.json', - build: config.build || process.env.EXTENSION_BUILD || 'extension.zip' + manifest: config.manifest || "manifest.json", + build: config.build || process.env.EXTENSION_BUILD || "extension.zip", }; } @@ -81,9 +81,9 @@ export default class ChromeWebStorePlugin implements IPlugin { } // Secrets - auto.checkEnv(this.name, 'CLIENT_ID'); - auto.checkEnv(this.name, 'CLIENT_SECRET'); - auto.checkEnv(this.name, 'REFRESH_TOKEN'); + auto.checkEnv(this.name, "CLIENT_ID"); + auto.checkEnv(this.name, "CLIENT_SECRET"); + auto.checkEnv(this.name, "REFRESH_TOKEN"); }); auto.hooks.beforeShipIt.tap(this.name, () => { @@ -143,7 +143,7 @@ export default class ChromeWebStorePlugin implements IPlugin { ); }); - auto.hooks.version.tapPromise(this.name, async version => { + auto.hooks.version.tapPromise(this.name, async (version) => { // increment version const manifest = await getManifest(this.options.manifest); manifest.version = inc(manifest.version, version as ReleaseType); @@ -153,40 +153,40 @@ export default class ChromeWebStorePlugin implements IPlugin { ); // commit new version - await execPromise('git', ['add', this.options.manifest]); - await execPromise('git', [ - 'commit', - '-m', + await execPromise("git", ["add", this.options.manifest]); + await execPromise("git", [ + "commit", + "-m", `"Bump version to ${manifest.version} [skip ci]"`, - '--no-verify' + "--no-verify", ]); - await execPromise('git', [ - 'tag', + await execPromise("git", [ + "tag", manifest.version, - '-m', - `"Update version to ${manifest.version}"` + "-m", + `"Update version to ${manifest.version}"`, ]); }); auto.hooks.publish.tapPromise(this.name, async () => { // publish extension - await execPromise('webstore', [ - 'upload', - '--extension-id', + await execPromise("webstore", [ + "upload", + "--extension-id", this.options.id, - '--source', + "--source", this.options.build, - '--auto-publish' + "--auto-publish", ]); // push to github - await execPromise('git', [ - 'push', - '--follow-tags', - '--set-upstream', + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", auto.remote, - auto.baseBranch + auto.baseBranch, ]); }); } diff --git a/plugins/cocoapods/CHANGELOG.md b/plugins/cocoapods/CHANGELOG.md index 0dd0e2285..312475ec6 100644 --- a/plugins/cocoapods/CHANGELOG.md +++ b/plugins/cocoapods/CHANGELOG.md @@ -11,4 +11,4 @@ Thank you, Harris Borawski ([@hborawski](https://github.com/hborawski)), for all #### Authors: 2 - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- Harris Borawski ([@hborawski](https://github.com/hborawski)) \ No newline at end of file +- Harris Borawski ([@hborawski](https://github.com/hborawski)) diff --git a/plugins/cocoapods/__tests__/cocoapods.test.ts b/plugins/cocoapods/__tests__/cocoapods.test.ts index ab7f6ff99..0bff8d0e8 100644 --- a/plugins/cocoapods/__tests__/cocoapods.test.ts +++ b/plugins/cocoapods/__tests__/cocoapods.test.ts @@ -1,13 +1,13 @@ -import * as Auto from '@auto-it/core'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; -import * as utilities from '../src/utilities'; +import * as Auto from "@auto-it/core"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import * as utilities from "../src/utilities"; import CocoapodsPlugin, { ICocoapodsPluginOptions, getParsedPodspecContents, getVersion, - updatePodspecVersion -} from '../src'; + updatePodspecVersion, +} from "../src"; const specWithVersion = (version: string) => ` Pod:: Spec.new do | s | @@ -43,17 +43,17 @@ const specWithVersion = (version: string) => ` `; const mockPodspec = (contents: string) => { - return jest.spyOn(utilities, 'getPodspecContents').mockReturnValue(contents); + return jest.spyOn(utilities, "getPodspecContents").mockReturnValue(contents); }; -describe('Cocoapods Plugin', () => { +describe("Cocoapods Plugin", () => { let hooks: Auto.IAutoHooks; const prefixRelease: (a: string) => string = (version: string) => { return `v${version}`; }; const options: ICocoapodsPluginOptions = { - podspecPath: './Test.podspec' + podspecPath: "./Test.podspec", }; beforeEach(() => { @@ -61,185 +61,185 @@ describe('Cocoapods Plugin', () => { const plugin = new CocoapodsPlugin(options); hooks = makeHooks(); plugin.apply({ hooks, logger: dummyLog(), prefixRelease } as Auto.Auto); - jest.spyOn(Auto, 'execPromise').mockReturnValue(Promise.resolve('')); + jest.spyOn(Auto, "execPromise").mockReturnValue(Promise.resolve("")); }); - describe('getParsedPodspecContents', () => { - test('should return null if contents cant be parsed with regex', () => { - mockPodspec('bad podspec'); + describe("getParsedPodspecContents", () => { + test("should return null if contents cant be parsed with regex", () => { + mockPodspec("bad podspec"); - expect(getParsedPodspecContents('./Test.podspec')).toBeNull(); + expect(getParsedPodspecContents("./Test.podspec")).toBeNull(); }); - test('should return parsed contents', () => { - mockPodspec(specWithVersion('0.0.1')); - const contents = getParsedPodspecContents('./Test.podspec'); - expect(contents).toHaveProperty('groups', { version: '0.0.1' }); + test("should return parsed contents", () => { + mockPodspec(specWithVersion("0.0.1")); + const contents = getParsedPodspecContents("./Test.podspec"); + expect(contents).toHaveProperty("groups", { version: "0.0.1" }); }); }); - describe('getVersion', () => { - test('should throw error if parsed podspec is returned as null', () => { - mockPodspec('bad podspec'); + describe("getVersion", () => { + test("should throw error if parsed podspec is returned as null", () => { + mockPodspec("bad podspec"); - expect(() => getVersion('./Test.podspec')).toThrow(); + expect(() => getVersion("./Test.podspec")).toThrow(); }); - test('should return version', () => { - mockPodspec(specWithVersion('0.0.1')); + test("should return version", () => { + mockPodspec(specWithVersion("0.0.1")); - expect(getVersion('./Test.podspec')).toBe('0.0.1'); + expect(getVersion("./Test.podspec")).toBe("0.0.1"); }); }); - describe('updatePodspecVersion', () => { - test('should throw error if there is an error writing file', async () => { - mockPodspec(specWithVersion('0.0.1')); + describe("updatePodspecVersion", () => { + test("should throw error if there is an error writing file", async () => { + mockPodspec(specWithVersion("0.0.1")); jest - .spyOn(utilities, 'writePodspecContents') + .spyOn(utilities, "writePodspecContents") .mockImplementationOnce(() => { - throw new Error('Filesystem Error'); + throw new Error("Filesystem Error"); }); await expect( - updatePodspecVersion('./Test.podspec', '0.0.2') + updatePodspecVersion("./Test.podspec", "0.0.2") ).rejects.toThrowError( - 'Error updating version in podspec: ./Test.podspec' + "Error updating version in podspec: ./Test.podspec" ); }); - test('should successfully write new version', async () => { - mockPodspec(specWithVersion('0.0.1')); + test("should successfully write new version", async () => { + mockPodspec(specWithVersion("0.0.1")); - const mock = jest.spyOn(utilities, 'writePodspecContents'); + const mock = jest.spyOn(utilities, "writePodspecContents"); - await updatePodspecVersion('./Test.podspec', '0.0.2'); - expect(mock).lastCalledWith(expect.any(String), specWithVersion('0.0.2')); + await updatePodspecVersion("./Test.podspec", "0.0.2"); + expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); }); }); - describe('modifyConfig hook', () => { - test('should set noVersionPrefix to true', () => { + describe("modifyConfig hook", () => { + test("should set noVersionPrefix to true", () => { const config = {}; expect(hooks.modifyConfig.call(config as any)).toStrictEqual({ - noVersionPrefix: true + noVersionPrefix: true, }); }); }); - describe('getPreviousVersion hook', () => { - test('should get previous version from podspec', async () => { - mockPodspec(specWithVersion('0.0.1')); + describe("getPreviousVersion hook", () => { + test("should get previous version from podspec", async () => { + mockPodspec(specWithVersion("0.0.1")); - expect(await hooks.getPreviousVersion.promise()).toBe('v0.0.1'); + expect(await hooks.getPreviousVersion.promise()).toBe("v0.0.1"); }); - test('should throw if no version found', async () => { - mockPodspec(specWithVersion('')); + test("should throw if no version found", async () => { + mockPodspec(specWithVersion("")); await expect(hooks.getPreviousVersion.promise()).rejects.toThrowError( - 'Version could not be found in podspec: ./Test.podspec' + "Version could not be found in podspec: ./Test.podspec" ); }); }); - describe('version hook', () => { - test('should version release - patch version', async () => { - mockPodspec(specWithVersion('0.0.1')); + describe("version hook", () => { + test("should version release - patch version", async () => { + mockPodspec(specWithVersion("0.0.1")); - const mock = jest.spyOn(utilities, 'writePodspecContents'); + const mock = jest.spyOn(utilities, "writePodspecContents"); await hooks.version.promise(Auto.SEMVER.patch); - expect(mock).lastCalledWith(expect.any(String), specWithVersion('0.0.2')); + expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); }); - test('should version release - minor version', async () => { - mockPodspec(specWithVersion('0.0.1')); + test("should version release - minor version", async () => { + mockPodspec(specWithVersion("0.0.1")); - const mock = jest.spyOn(utilities, 'writePodspecContents'); + const mock = jest.spyOn(utilities, "writePodspecContents"); await hooks.version.promise(Auto.SEMVER.minor); - expect(mock).lastCalledWith(expect.any(String), specWithVersion('0.1.0')); + expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.1.0")); }); - test('should version release - major version', async () => { - mockPodspec(specWithVersion('0.0.1')); + test("should version release - major version", async () => { + mockPodspec(specWithVersion("0.0.1")); - const mock = jest.spyOn(utilities, 'writePodspecContents'); + const mock = jest.spyOn(utilities, "writePodspecContents"); await hooks.version.promise(Auto.SEMVER.major); - expect(mock).lastCalledWith(expect.any(String), specWithVersion('1.0.0')); + expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); }); - test('should throw if there is an error writing new version', async () => { - mockPodspec(specWithVersion('0.0.1')); + test("should throw if there is an error writing new version", async () => { + mockPodspec(specWithVersion("0.0.1")); const mock = jest - .spyOn(utilities, 'writePodspecContents') + .spyOn(utilities, "writePodspecContents") .mockImplementationOnce(() => { - throw new Error('Filesystem Error'); + throw new Error("Filesystem Error"); }); await expect( hooks.version.promise(Auto.SEMVER.major) ).rejects.toThrowError( - 'Error updating version in podspec: ./Test.podspec' + "Error updating version in podspec: ./Test.podspec" ); - expect(mock).lastCalledWith(expect.any(String), specWithVersion('1.0.0')); + expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); }); }); - describe('publish hook', () => { - test('should push to trunk if no specsRepo in options', async () => { - mockPodspec(specWithVersion('0.0.1')); + describe("publish hook", () => { + test("should push to trunk if no specsRepo in options", async () => { + mockPodspec(specWithVersion("0.0.1")); const mock = jest - .spyOn(Auto, 'execPromise') - .mockReturnValue(Promise.resolve('')); + .spyOn(Auto, "execPromise") + .mockReturnValue(Promise.resolve("")); const plugin = new CocoapodsPlugin(options); const hook = makeHooks(); plugin.apply({ hooks: hook, logger: dummyLog(), - prefixRelease + prefixRelease, } as Auto.Auto); await hook.publish.promise(Auto.SEMVER.patch); expect(mock).toBeCalledTimes(2); - expect(mock).lastCalledWith('pod', ['trunk', 'push', './Test.podspec']); + expect(mock).lastCalledWith("pod", ["trunk", "push", "./Test.podspec"]); }); - test('should push to specs repo if specsRepo in options', async () => { - mockPodspec(specWithVersion('0.0.1')); + test("should push to specs repo if specsRepo in options", async () => { + mockPodspec(specWithVersion("0.0.1")); const mock = jest - .spyOn(Auto, 'execPromise') - .mockReturnValue(Promise.resolve('')); + .spyOn(Auto, "execPromise") + .mockReturnValue(Promise.resolve("")); const plugin = new CocoapodsPlugin({ ...options, - specsRepo: 'someSpecsRepo' + specsRepo: "someSpecsRepo", }); const hook = makeHooks(); plugin.apply({ hooks: hook, logger: dummyLog(), - prefixRelease + prefixRelease, } as Auto.Auto); await hook.publish.promise(Auto.SEMVER.patch); expect(mock).toBeCalledTimes(4); - expect(mock).toHaveBeenNthCalledWith(2, 'pod', [ - 'repo', - 'add', - 'autoPublishRepo', - 'someSpecsRepo' + expect(mock).toHaveBeenNthCalledWith(2, "pod", [ + "repo", + "add", + "autoPublishRepo", + "someSpecsRepo", ]); - expect(mock).toHaveBeenNthCalledWith(3, 'pod', [ - 'repo', - 'push', - 'autoPublishRepo', - './Test.podspec' + expect(mock).toHaveBeenNthCalledWith(3, "pod", [ + "repo", + "push", + "autoPublishRepo", + "./Test.podspec", ]); - expect(mock).toHaveBeenNthCalledWith(4, 'pod', [ - 'repo', - 'remove', - 'autoPublishRepo' + expect(mock).toHaveBeenNthCalledWith(4, "pod", [ + "repo", + "remove", + "autoPublishRepo", ]); }); }); diff --git a/plugins/cocoapods/src/index.ts b/plugins/cocoapods/src/index.ts index 6bb1f270f..482bd30e4 100644 --- a/plugins/cocoapods/src/index.ts +++ b/plugins/cocoapods/src/index.ts @@ -2,16 +2,16 @@ import { Auto, IPlugin, execPromise, - validatePluginConfiguration -} from '@auto-it/core'; + validatePluginConfiguration, +} from "@auto-it/core"; -import { inc, ReleaseType } from 'semver'; +import { inc, ReleaseType } from "semver"; -import * as t from 'io-ts'; +import * as t from "io-ts"; -import { getPodspecContents, writePodspecContents } from './utilities'; +import { getPodspecContents, writePodspecContents } from "./utilities"; -const logPrefix = '[Cocoapods-Plugin]'; +const logPrefix = "[Cocoapods-Plugin]"; /** Regex used to pull the version line from the spec */ const versionRegex = /\.version\s*=\s*['|"](?\d+\.\d+\.\d+)['|"]/; @@ -25,12 +25,12 @@ const logMessage = (msg: string): string => `${logPrefix} ${msg}`; const required = t.interface({ /** Relative path to podspec file */ - podspecPath: t.string + podspecPath: t.string, }); const optional = t.partial({ /** The Cocoapods repo to publish to */ - specsRepo: t.string + specsRepo: t.string, }); const pluginOptions = t.intersection([required, optional]); @@ -94,11 +94,11 @@ export async function updatePodspecVersion( writePodspecContents(podspecPath, newPodspec); - await execPromise('git', [ - 'commit', - '-am', + await execPromise("git", [ + "commit", + "-am", `"update version: ${version} [skip ci]"`, - '--no-verify' + "--no-verify", ]); } } catch (error) { @@ -109,7 +109,7 @@ export async function updatePodspecVersion( /** Use auto to version your cocoapod */ export default class CocoapodsPlugin implements IPlugin { /** The name of the plugin */ - name = 'cocoapods'; + name = "cocoapods"; /** The options of the plugin */ readonly options: ICocoapodsPluginOptions; @@ -127,16 +127,16 @@ export default class CocoapodsPlugin implements IPlugin { } }); - auto.hooks.modifyConfig.tap(this.name, config => ({ + auto.hooks.modifyConfig.tap(this.name, (config) => ({ ...config, - noVersionPrefix: true + noVersionPrefix: true, })); auto.hooks.getPreviousVersion.tapPromise(this.name, async () => auto.prefixRelease(getVersion(this.options.podspecPath)) ); - auto.hooks.version.tapPromise(this.name, async version => { + auto.hooks.version.tapPromise(this.name, async (version) => { const previousVersion = getVersion(this.options.podspecPath); const releaseVersion = inc(previousVersion, version as ReleaseType); @@ -147,46 +147,46 @@ export default class CocoapodsPlugin implements IPlugin { } await updatePodspecVersion(this.options.podspecPath, releaseVersion); - await execPromise('git', [ - 'tag', + await execPromise("git", [ + "tag", releaseVersion, - '-m', - `"Update version to ${releaseVersion}"` + "-m", + `"Update version to ${releaseVersion}"`, ]); }); auto.hooks.publish.tapPromise(this.name, async () => { - await execPromise('git', [ - 'push', - '--follow-tags', - '--set-upstream', + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", auto.remote, - auto.baseBranch + auto.baseBranch, ]); if (!this.options.specsRepo) { auto.logger.log.info(logMessage(`Pushing to Cocoapods trunk`)); - await execPromise('pod', ['trunk', 'push', this.options.podspecPath]); + await execPromise("pod", ["trunk", "push", this.options.podspecPath]); return; } try { - await execPromise('pod', [ - 'repo', - 'add', - 'autoPublishRepo', - this.options.specsRepo + await execPromise("pod", [ + "repo", + "add", + "autoPublishRepo", + this.options.specsRepo, ]); auto.logger.log.info( logMessage(`Pushing to specs repo: ${this.options.specsRepo}`) ); - await execPromise('pod', [ - 'repo', - 'push', - 'autoPublishRepo', - this.options.podspecPath + await execPromise("pod", [ + "repo", + "push", + "autoPublishRepo", + this.options.podspecPath, ]); } catch (error) { auto.logger.log.error( @@ -196,7 +196,7 @@ export default class CocoapodsPlugin implements IPlugin { ); process.exit(1); } finally { - await execPromise('pod', ['repo', 'remove', 'autoPublishRepo']); + await execPromise("pod", ["repo", "remove", "autoPublishRepo"]); } }); } diff --git a/plugins/cocoapods/src/utilities.ts b/plugins/cocoapods/src/utilities.ts index b2f52d1c9..90782b379 100644 --- a/plugins/cocoapods/src/utilities.ts +++ b/plugins/cocoapods/src/utilities.ts @@ -1,5 +1,5 @@ -import path from 'path'; -import fs from 'fs'; +import path from "path"; +import fs from "fs"; /** * Retrieves the contents of the podspec file diff --git a/plugins/conventional-commits/__tests__/conventional-commits.test.ts b/plugins/conventional-commits/__tests__/conventional-commits.test.ts index a6b6c9c11..c1f9d45da 100644 --- a/plugins/conventional-commits/__tests__/conventional-commits.test.ts +++ b/plugins/conventional-commits/__tests__/conventional-commits.test.ts @@ -1,87 +1,87 @@ -import Auto from '@auto-it/core'; -import makeCommitFromMsg from '@auto-it/core/dist/__tests__/make-commit-from-msg'; -import Git from '@auto-it/core/dist/git'; -import LogParse from '@auto-it/core/dist/log-parse'; +import Auto from "@auto-it/core"; +import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; +import Git from "@auto-it/core/dist/git"; +import LogParse from "@auto-it/core/dist/log-parse"; import Release, { defaultLabels, - getVersionMap -} from '@auto-it/core/dist/release'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; + getVersionMap, +} from "@auto-it/core/dist/release"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; import { makeHooks, - makeLogParseHooks -} from '@auto-it/core/dist/utils/make-hooks'; -import ConventionalCommitsPlugin from '../src'; + makeLogParseHooks, +} from "@auto-it/core/dist/utils/make-hooks"; +import ConventionalCommitsPlugin from "../src"; const versionLabels = getVersionMap(defaultLabels); -test('should do nothing when conventional commit message is not present', async () => { +test("should do nothing when conventional commit message is not present", async () => { const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const autoHooks = makeHooks(); conventionalCommitsPlugin.apply({ hooks: autoHooks, labels: defaultLabels, semVerLabels: versionLabels, - logger: dummyLog() + logger: dummyLog(), } as Auto); const logParseHooks = makeLogParseHooks(); autoHooks.onCreateLogParse.call({ - hooks: logParseHooks + hooks: logParseHooks, } as LogParse); - const commit = makeCommitFromMsg('normal commit with no bump'); + const commit = makeCommitFromMsg("normal commit with no bump"); expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual( commit ); }); -test('should add correct semver label to commit', async () => { +test("should add correct semver label to commit", async () => { const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const autoHooks = makeHooks(); conventionalCommitsPlugin.apply({ hooks: autoHooks, labels: defaultLabels, semVerLabels: versionLabels, - logger: dummyLog() + logger: dummyLog(), } as Auto); const logParseHooks = makeLogParseHooks(); autoHooks.onCreateLogParse.call({ - hooks: logParseHooks + hooks: logParseHooks, } as LogParse); - const commit = makeCommitFromMsg('fix: normal commit with no bump'); + const commit = makeCommitFromMsg("fix: normal commit with no bump"); expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual({ ...commit, - labels: ['patch'] + labels: ["patch"], }); }); -test('should add major semver label to commit', async () => { +test("should add major semver label to commit", async () => { const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const autoHooks = makeHooks(); conventionalCommitsPlugin.apply({ hooks: autoHooks, labels: defaultLabels, semVerLabels: versionLabels, - logger: dummyLog() + logger: dummyLog(), } as Auto); const logParseHooks = makeLogParseHooks(); autoHooks.onCreateLogParse.call({ - hooks: logParseHooks + hooks: logParseHooks, } as LogParse); - const commit = makeCommitFromMsg('BREAKING: normal commit with no bump'); + const commit = makeCommitFromMsg("BREAKING: normal commit with no bump"); expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual({ ...commit, - labels: ['major'] + labels: ["major"], }); }); -test('should not include label-less head commit if any other commit in PR has conventional commit message', async () => { - const commit = makeCommitFromMsg('Merge pull request #123 from some-pr\n\n'); +test("should not include label-less head commit if any other commit in PR has conventional commit message", async () => { + const commit = makeCommitFromMsg("Merge pull request #123 from some-pr\n\n"); const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const logParse = new LogParse(); const autoHooks = makeHooks(); @@ -92,7 +92,7 @@ test('should not include label-less head commit if any other commit in PR has co getFirstCommit: jest.fn(), getPr: jest.fn(), getCommitsForPR: () => - Promise.resolve([{ sha: '1', commit: { message: 'fix: child commit' } }]) + Promise.resolve([{ sha: "1", commit: { message: "fix: child commit" } }]), } as unknown) as Git; conventionalCommitsPlugin.apply({ hooks: autoHooks, @@ -100,7 +100,7 @@ test('should not include label-less head commit if any other commit in PR has co semVerLabels: versionLabels, logger: dummyLog(), git: mockGit, - release: new Release(mockGit) + release: new Release(mockGit), } as Auto); autoHooks.onCreateLogParse.call(logParse); @@ -109,9 +109,9 @@ test('should not include label-less head commit if any other commit in PR has co expect(result).toBeUndefined(); }); -test('should include labeled head commit', async () => { - const commit = makeCommitFromMsg('Merge pull request #123 from some-pr\n\n', { - labels: ['major'] +test("should include labeled head commit", async () => { + const commit = makeCommitFromMsg("Merge pull request #123 from some-pr\n\n", { + labels: ["major"], }); const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const logParse = new LogParse(); @@ -122,14 +122,14 @@ test('should include labeled head commit', async () => { getCommitDate: jest.fn(), getFirstCommit: jest.fn(), getPr: jest.fn(), - getLatestRelease: () => Promise.resolve('1.2.3'), + getLatestRelease: () => Promise.resolve("1.2.3"), getGitLog: () => Promise.resolve([ commit, - makeCommitFromMsg('fix: child commit', { hash: '1' }), - makeCommitFromMsg('unrelated', { hash: '2' }) + makeCommitFromMsg("fix: child commit", { hash: "1" }), + makeCommitFromMsg("unrelated", { hash: "2" }), ]), - getCommitsForPR: () => Promise.resolve([{ sha: '1' }]) + getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), } as unknown) as Git; conventionalCommitsPlugin.apply({ hooks: autoHooks, @@ -137,18 +137,18 @@ test('should include labeled head commit', async () => { semVerLabels: versionLabels, logger: dummyLog(), git: mockGit, - release: new Release(mockGit) + release: new Release(mockGit), } as Auto); autoHooks.onCreateLogParse.call(logParse); const result = await logParse.normalizeCommit(commit); - expect(result?.hash).toBe('foo'); + expect(result?.hash).toBe("foo"); }); -test('should respect PR label if SEMVER', async () => { - const commit = makeCommitFromMsg('fix: a test', { - labels: ['major'] +test("should respect PR label if SEMVER", async () => { + const commit = makeCommitFromMsg("fix: a test", { + labels: ["major"], }); const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const logParse = new LogParse(); @@ -159,9 +159,9 @@ test('should respect PR label if SEMVER', async () => { getCommitDate: jest.fn(), getFirstCommit: jest.fn(), getPr: jest.fn(), - getLatestRelease: () => Promise.resolve('1.2.3'), + getLatestRelease: () => Promise.resolve("1.2.3"), getGitLog: () => Promise.resolve([commit]), - getCommitsForPR: () => Promise.resolve([{ sha: '1' }]) + getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), } as unknown) as Git; conventionalCommitsPlugin.apply({ @@ -170,18 +170,18 @@ test('should respect PR label if SEMVER', async () => { semVerLabels: versionLabels, logger: dummyLog(), git: mockGit, - release: new Release(mockGit) + release: new Release(mockGit), } as Auto); autoHooks.onCreateLogParse.call(logParse); const result = await logParse.normalizeCommit(commit); - expect(result?.labels).toStrictEqual(['major']); + expect(result?.labels).toStrictEqual(["major"]); }); -test('should add conventional commit label if none/skip', async () => { - const commit = makeCommitFromMsg('fix: a test', { - labels: ['skip-release', 'internal'] +test("should add conventional commit label if none/skip", async () => { + const commit = makeCommitFromMsg("fix: a test", { + labels: ["skip-release", "internal"], }); const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const logParse = new LogParse(); @@ -192,9 +192,9 @@ test('should add conventional commit label if none/skip', async () => { getCommitDate: jest.fn(), getFirstCommit: jest.fn(), getPr: jest.fn(), - getLatestRelease: () => Promise.resolve('1.2.3'), + getLatestRelease: () => Promise.resolve("1.2.3"), getGitLog: () => Promise.resolve([commit]), - getCommitsForPR: () => Promise.resolve([{ sha: '1' }]) + getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), } as unknown) as Git; conventionalCommitsPlugin.apply({ @@ -203,11 +203,11 @@ test('should add conventional commit label if none/skip', async () => { semVerLabels: versionLabels, logger: dummyLog(), git: mockGit, - release: new Release(mockGit) + release: new Release(mockGit), } as Auto); autoHooks.onCreateLogParse.call(logParse); const result = await logParse.normalizeCommit(commit); - expect(result?.labels).toStrictEqual(['skip-release', 'internal', 'patch']); + expect(result?.labels).toStrictEqual(["skip-release", "internal", "patch"]); }); diff --git a/plugins/conventional-commits/src/index.ts b/plugins/conventional-commits/src/index.ts index 8274837e4..6292023a6 100644 --- a/plugins/conventional-commits/src/index.ts +++ b/plugins/conventional-commits/src/index.ts @@ -1,6 +1,6 @@ -import { applyPlugins, mappers, parse } from 'parse-commit-message'; +import { applyPlugins, mappers, parse } from "parse-commit-message"; -import { Auto, IPlugin, VersionLabel, SEMVER } from '@auto-it/core'; +import { Auto, IPlugin, VersionLabel, SEMVER } from "@auto-it/core"; /** * Parse conventional commit messages and use them to @@ -8,12 +8,12 @@ import { Auto, IPlugin, VersionLabel, SEMVER } from '@auto-it/core'; */ export default class ConventionalCommitsPlugin implements IPlugin { /** The name of the plugin */ - name = 'conventional-commits'; + name = "conventional-commits"; /** Tap into auto plugin points. */ apply(auto: Auto) { - auto.hooks.onCreateLogParse.tap(this.name, logParse => { - logParse.hooks.parseCommit.tap(this.name, commit => { + auto.hooks.onCreateLogParse.tap(this.name, (logParse) => { + logParse.hooks.parseCommit.tap(this.name, (commit) => { if (!auto.semVerLabels) { return commit; } @@ -29,7 +29,7 @@ export default class ConventionalCommitsPlugin implements IPlugin { const allSemVerLabels = [ auto.semVerLabels.get(SEMVER.major), auto.semVerLabels.get(SEMVER.minor), - auto.semVerLabels.get(SEMVER.patch) + auto.semVerLabels.get(SEMVER.patch), ].reduce( (acc, labels) => (labels ? [...acc, ...labels] : acc), [] @@ -38,7 +38,7 @@ export default class ConventionalCommitsPlugin implements IPlugin { if ( conventionalCommit.header && incrementLabel && - !commit.labels.some(l => allSemVerLabels.includes(l)) + !commit.labels.some((l) => allSemVerLabels.includes(l)) ) { commit.labels = [...commit.labels, ...incrementLabel]; } @@ -52,7 +52,7 @@ export default class ConventionalCommitsPlugin implements IPlugin { }); // should omit PR commit if there exists a commit with a CC commit message - logParse.hooks.omitCommit.tapPromise(this.name, async commit => { + logParse.hooks.omitCommit.tapPromise(this.name, async (commit) => { if ( auto.git && auto.release && @@ -65,7 +65,7 @@ export default class ConventionalCommitsPlugin implements IPlugin { // Omit the commit if one of the commits in the PR contains a CC message since it will already be counted return Boolean( - prCommits.find(c => { + prCommits.find((c) => { try { return Boolean(parse(c.commit.message)[0].header); } catch (error) { diff --git a/plugins/crates/__tests__/crates.test.ts b/plugins/crates/__tests__/crates.test.ts index 6e42475df..4c0339cc8 100644 --- a/plugins/crates/__tests__/crates.test.ts +++ b/plugins/crates/__tests__/crates.test.ts @@ -1,11 +1,11 @@ -import * as Auto from '@auto-it/core'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; +import * as Auto from "@auto-it/core"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; import CratesPlugin, { bumpVersion, checkForCreds, - getCargoConfig -} from '../src'; + getCargoConfig, +} from "../src"; const sampleCargoToml = `[package] name = "example" @@ -39,13 +39,13 @@ const existsSync = jest.fn(); const readFileSync = jest.fn(); const writeFile = jest.fn(); const writeFileSync = jest.fn(); -const readResult = '{}'; +const readResult = "{}"; // @ts-ignore -jest.spyOn(Auto, 'execPromise').mockImplementation(exec); +jest.spyOn(Auto, "execPromise").mockImplementation(exec); // @ts-ignore -jest.mock('env-ci', () => () => ({ isCi: false })); -jest.mock('fs', () => ({ +jest.mock("env-ci", () => () => ({ isCi: false })); +jest.mock("fs", () => ({ // @ts-ignore existsSync: (...args) => existsSync(...args), // @ts-ignore @@ -54,8 +54,8 @@ jest.mock('fs', () => ({ }, // @ts-ignore readFileSync: (...args) => readFileSync(...args), - ReadStream: function() {}, - WriteStream: function() {}, + ReadStream: function () {}, + WriteStream: function () {}, // @ts-ignore closeSync: () => undefined, // @ts-ignore @@ -65,17 +65,17 @@ jest.mock('fs', () => ({ // @ts-ignore writeFileSync: (file, data) => { writeFileSync(file, data); - } + }, })); afterEach(() => { jest.clearAllMocks(); }); -describe('CratesPlugin', () => { - describe('getCargoConfig', () => { - test('returns an error if the file does not exist', () => { - const error = new Error('file does not exist'); +describe("CratesPlugin", () => { + describe("getCargoConfig", () => { + test("returns an error if the file does not exist", () => { + const error = new Error("file does not exist"); readFileSync.mockImplementationOnce(() => { throw error; @@ -85,23 +85,23 @@ describe('CratesPlugin', () => { expect(readFileSync).toHaveBeenCalledTimes(1); }); - test('returns data if file exists', () => { + test("returns data if file exists", () => { readFileSync.mockReturnValueOnce(sampleCargoToml); const data = getCargoConfig(); - expect(data.dependencies.example_1.features[0]).toBe('example_ft'); + expect(data.dependencies.example_1.features[0]).toBe("example_ft"); expect(readFileSync).toHaveBeenCalledTimes(1); }); }); - describe('checkForCreds', () => { - test('returns false if file does not exist', () => { + describe("checkForCreds", () => { + test("returns false if file does not exist", () => { existsSync.mockReturnValueOnce(false); const res = checkForCreds(); expect(res).toBe(false); expect(existsSync).toHaveBeenCalledTimes(1); }); - test('returns true if the file exists', () => { + test("returns true if the file exists", () => { existsSync.mockReturnValueOnce(true); const res = checkForCreds(); expect(res).toBe(true); @@ -109,129 +109,129 @@ describe('CratesPlugin', () => { }); }); - describe('bumpVersion', () => { + describe("bumpVersion", () => { beforeEach(() => { readFileSync.mockReturnValueOnce(sampleCargoToml); }); - test('throws error if could not bump version', () => { + test("throws error if could not bump version", () => { // @ts-ignore - expect(() => bumpVersion('wrong')).toThrow( - 'Could not increment previous version' + expect(() => bumpVersion("wrong")).toThrow( + "Could not increment previous version" ); expect(readFileSync).toHaveBeenCalledTimes(1); }); - describe('bumps version and writes to file', () => { - test('patch', () => { + describe("bumps version and writes to file", () => { + test("patch", () => { const versionNew = bumpVersion(Auto.SEMVER.patch); - expect(versionNew).toBe('1.2.4'); + expect(versionNew).toBe("1.2.4"); expect(writeFileSync).toHaveBeenCalledTimes(1); }); - test('minor', () => { + test("minor", () => { const versionNew = bumpVersion(Auto.SEMVER.minor); - expect(versionNew).toBe('1.3.0'); + expect(versionNew).toBe("1.3.0"); expect(writeFileSync).toHaveBeenCalledTimes(1); }); - test('major', () => { + test("major", () => { const versionNew = bumpVersion(Auto.SEMVER.major); - expect(versionNew).toBe('2.0.0'); + expect(versionNew).toBe("2.0.0"); expect(writeFileSync).toHaveBeenCalledTimes(1); }); }); }); - describe('hooks', () => { - describe('beforeShipIt', () => { - test('throws error if no Cargo creds', async () => { + describe("hooks", () => { + describe("beforeShipIt", () => { + test("throws error if no Cargo creds", async () => { existsSync.mockReturnValueOnce(false); const plugin = new CratesPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, logger: dummyLog() } as Auto.Auto); await expect( - hooks.beforeShipIt.promise({ releaseType: 'canary' }) - ).rejects.toThrow('Cargo token is needed for the Crates plugin'); + hooks.beforeShipIt.promise({ releaseType: "canary" }) + ).rejects.toThrow("Cargo token is needed for the Crates plugin"); }); - test('does not throw error if Cargo creds', async () => { + test("does not throw error if Cargo creds", async () => { existsSync.mockReturnValueOnce(true); const plugin = new CratesPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, logger: dummyLog() } as Auto.Auto); await expect( - hooks.beforeShipIt.promise({ releaseType: 'canary' }) + hooks.beforeShipIt.promise({ releaseType: "canary" }) ).resolves.not.toThrow(); }); }); - describe('getAuthor', () => { - test('returns author', async () => { + describe("getAuthor", () => { + test("returns author", async () => { readFileSync.mockReturnValueOnce(sampleCargoToml); const plugin = new CratesPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, logger: dummyLog() } as Auto.Auto); expect(await hooks.getAuthor.promise()).toStrictEqual([ - 'example ' + "example ", ]); }); }); - describe('getPreviousVersion', () => { - test('returns version', async () => { + describe("getPreviousVersion", () => { + test("returns version", async () => { const prefixRelease = (str: string) => str; readFileSync.mockReturnValueOnce(sampleCargoToml); const plugin = new CratesPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, logger: dummyLog(), prefixRelease } as Auto.Auto); - expect(await hooks.getPreviousVersion.promise()).toStrictEqual('1.2.3'); + expect(await hooks.getPreviousVersion.promise()).toStrictEqual("1.2.3"); }); }); - describe('version', () => { - test('does versioning', async () => { + describe("version", () => { + test("does versioning", async () => { const plugin = new CratesPlugin(); readFileSync.mockReturnValueOnce(sampleCargoToml); const hooks = makeHooks(); plugin.apply({ hooks, logger: dummyLog() } as Auto.Auto); await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('cargo', ['build']); - expect(exec).toHaveBeenCalledWith('git', [ - 'add', - 'Cargo.toml', - 'Cargo.lock' + expect(exec).toHaveBeenCalledWith("cargo", ["build"]); + expect(exec).toHaveBeenCalledWith("git", [ + "add", + "Cargo.toml", + "Cargo.lock", ]); - expect(exec).toHaveBeenCalledWith('git', [ - 'commit', - '-m', + expect(exec).toHaveBeenCalledWith("git", [ + "commit", + "-m", `'Bump version to: 1.2.4 [skip ci]'`, - '--no-verify' + "--no-verify", ]); }); }); - describe('publish', () => { - test('does publishing', async () => { + describe("publish", () => { + test("does publishing", async () => { const plugin = new CratesPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, logger: dummyLog(), - remote: 'origin', - baseBranch: 'master' + remote: "origin", + baseBranch: "master", } as Auto.Auto); await hooks.publish.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('cargo', ['publish']); - expect(exec).toHaveBeenCalledWith('git', [ - 'push', - '--follow-tags', - '--set-upstream', - 'origin', - 'master' + expect(exec).toHaveBeenCalledWith("cargo", ["publish"]); + expect(exec).toHaveBeenCalledWith("git", [ + "push", + "--follow-tags", + "--set-upstream", + "origin", + "master", ]); }); }); diff --git a/plugins/crates/src/index.ts b/plugins/crates/src/index.ts index 6c519365c..673d0ec42 100644 --- a/plugins/crates/src/index.ts +++ b/plugins/crates/src/index.ts @@ -1,17 +1,17 @@ -import { Auto, execPromise, IPlugin, SEMVER } from '@auto-it/core'; -import envCi from 'env-ci'; -import fs from 'fs'; -import path from 'path'; -import { inc, ReleaseType } from 'semver'; -import toml from 'toml'; -import userHome from 'user-home'; +import { Auto, execPromise, IPlugin, SEMVER } from "@auto-it/core"; +import envCi from "env-ci"; +import fs from "fs"; +import path from "path"; +import { inc, ReleaseType } from "semver"; +import toml from "toml"; +import userHome from "user-home"; const { isCi } = envCi(); /** Get the parsed cargo.toml for the crate */ export const getCargoConfig = () => { const content = fs - .readFileSync(path.join(process.cwd(), 'Cargo.toml')) + .readFileSync(path.join(process.cwd(), "Cargo.toml")) .toString(); return toml.parse(content); }; @@ -22,13 +22,13 @@ export function checkForCreds() { return process.env.CARGO_REGISTRY_TOKEN; } - const credsFile = path.join(userHome, '.cargo', 'credentials'); + const credsFile = path.join(userHome, ".cargo", "credentials"); return fs.existsSync(credsFile); } /** Bump the version in cargo.toml */ export function bumpVersion(bumpBy: SEMVER) { - const filePath = path.join(process.cwd(), 'Cargo.toml'); + const filePath = path.join(process.cwd(), "Cargo.toml"); const content = fs.readFileSync(filePath).toString(); const config = toml.parse(content); const versionOld = config.package.version; @@ -48,13 +48,13 @@ export function bumpVersion(bumpBy: SEMVER) { /** Deploy Rust crates */ export default class CratesPlugin implements IPlugin { /** The name of the plugin */ - name = 'crates'; + name = "crates"; /** Tap into auto plugin points. */ apply(auto: Auto) { auto.hooks.beforeShipIt.tap(this.name, () => { if (!checkForCreds()) { - throw new Error('Cargo token is needed for the Crates plugin!'); + throw new Error("Cargo token is needed for the Crates plugin!"); } }); @@ -72,35 +72,35 @@ export default class CratesPlugin implements IPlugin { return version; }); - auto.hooks.version.tapPromise(this.name, async version => { + auto.hooks.version.tapPromise(this.name, async (version) => { const versionNew = bumpVersion(version); auto.logger.log.info(`Bumped version to: ${versionNew}`); - auto.logger.log.info('Building to update Cargo.lock'); - await execPromise('cargo', ['build']); - auto.logger.verbose.info('Committing files'); - await execPromise('git', ['add', 'Cargo.toml', 'Cargo.lock']); - await execPromise('git', [ - 'commit', - '-m', + auto.logger.log.info("Building to update Cargo.lock"); + await execPromise("cargo", ["build"]); + auto.logger.verbose.info("Committing files"); + await execPromise("git", ["add", "Cargo.toml", "Cargo.lock"]); + await execPromise("git", [ + "commit", + "-m", `'Bump version to: ${versionNew} [skip ci]'`, - '--no-verify' + "--no-verify", ]); - auto.logger.log.info('Create git commit for new version'); + auto.logger.log.info("Create git commit for new version"); }); auto.hooks.publish.tapPromise(this.name, async () => { - auto.logger.log.info('Publishing via cargo'); - await execPromise('cargo', ['publish']); - auto.logger.log.info('Publish complete'); - auto.logger.log.info('Pushing local git changes to origin remote'); - await execPromise('git', [ - 'push', - '--follow-tags', - '--set-upstream', + auto.logger.log.info("Publishing via cargo"); + await execPromise("cargo", ["publish"]); + auto.logger.log.info("Publish complete"); + auto.logger.log.info("Pushing local git changes to origin remote"); + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", auto.remote, - auto.baseBranch + auto.baseBranch, ]); - auto.logger.log.info('Push complete'); + auto.logger.log.info("Push complete"); }); } } diff --git a/plugins/exec/CHANGELOG.md b/plugins/exec/CHANGELOG.md index ec1d33d80..dc5ad7d27 100644 --- a/plugins/exec/CHANGELOG.md +++ b/plugins/exec/CHANGELOG.md @@ -63,4 +63,4 @@ Here is an example of a super light weight version of the `npm` and `gh-pages` p #### Authors: 1 -- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) \ No newline at end of file +- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) diff --git a/plugins/exec/__tests__/exec.test.ts b/plugins/exec/__tests__/exec.test.ts index 5c20e2d61..154df3e5c 100644 --- a/plugins/exec/__tests__/exec.test.ts +++ b/plugins/exec/__tests__/exec.test.ts @@ -1,25 +1,25 @@ -import Auto, { SEMVER } from '@auto-it/core'; +import Auto, { SEMVER } from "@auto-it/core"; import { makeHooks, makeReleaseHooks, makeLogParseHooks, - makeChangelogHooks -} from '@auto-it/core/dist/utils/make-hooks'; -import { execSync } from 'child_process'; + makeChangelogHooks, +} from "@auto-it/core/dist/utils/make-hooks"; +import { execSync } from "child_process"; -import Exec from '../src'; +import Exec from "../src"; const execSpy = execSync as jest.Mock; -jest.mock('child_process'); -execSpy.mockReturnValue(''); +jest.mock("child_process"); +execSpy.mockReturnValue(""); -describe('Exec Plugin', () => { +describe("Exec Plugin", () => { beforeEach(() => { execSpy.mockClear(); }); - test('should warn about invalid config', async () => { - const options = { notAHook: 'echo foo' }; + test("should warn about invalid config", async () => { + const options = { notAHook: "echo foo" }; // @ts-ignore const plugins = new Exec(options); const hooks = makeHooks(); @@ -27,48 +27,48 @@ describe('Exec Plugin', () => { plugins.apply({ hooks } as Auto); expect( - await hooks.validateConfig.promise('exec', options) + await hooks.validateConfig.promise("exec", options) ).toMatchSnapshot(); }); - test('should handle SyncHook hooks', () => { - const plugins = new Exec({ beforeRun: 'echo foo' }); + test("should handle SyncHook hooks", () => { + const plugins = new Exec({ beforeRun: "echo foo" }); const hooks = makeHooks(); plugins.apply({ hooks } as Auto); hooks.beforeRun.call({} as any); - expect(execSpy).toHaveBeenCalledWith('echo foo', expect.anything()); + expect(execSpy).toHaveBeenCalledWith("echo foo", expect.anything()); }); - test('should handle AsyncParallelHook hooks', async () => { - const plugins = new Exec({ version: 'echo bar' }); + test("should handle AsyncParallelHook hooks", async () => { + const plugins = new Exec({ version: "echo bar" }); const hooks = makeHooks(); plugins.apply({ hooks } as Auto); await hooks.version.promise(SEMVER.patch); - expect(execSpy).toHaveBeenCalledWith('echo bar', expect.anything()); + expect(execSpy).toHaveBeenCalledWith("echo bar", expect.anything()); }); - test('should handle AsyncSeriesHook hooks w/args', async () => { - const plugins = new Exec({ beforeShipIt: 'echo baz' }); + test("should handle AsyncSeriesHook hooks w/args", async () => { + const plugins = new Exec({ beforeShipIt: "echo baz" }); const hooks = makeHooks(); plugins.apply({ hooks } as Auto); - await hooks.beforeShipIt.promise({ releaseType: 'latest' }); + await hooks.beforeShipIt.promise({ releaseType: "latest" }); expect(execSpy).toHaveBeenCalledWith( - 'echo baz', + "echo baz", expect.objectContaining({ - env: expect.objectContaining({ ARG_0: '{"releaseType":"latest"}' }) + env: expect.objectContaining({ ARG_0: '{"releaseType":"latest"}' }), }) ); }); - test('should handle Bail hooks', async () => { + test("should handle Bail hooks", async () => { const plugins = new Exec({ - getRepository: `echo "{ "repo": "foo", "owner": "bar" }"` + getRepository: `echo "{ "repo": "foo", "owner": "bar" }"`, }); const hooks = makeHooks(); @@ -76,85 +76,85 @@ describe('Exec Plugin', () => { plugins.apply({ hooks } as Auto); expect(await hooks.getRepository.promise()).toStrictEqual({ - repo: 'foo', - owner: 'bar' + repo: "foo", + owner: "bar", }); }); - describe('canary', () => { - test('should handle string returns', async () => { - const canaryVersion = '0.1.0-canary'; + describe("canary", () => { + test("should handle string returns", async () => { + const canaryVersion = "0.1.0-canary"; const plugins = new Exec({ - canary: `echo ${canaryVersion}` + canary: `echo ${canaryVersion}`, }); const hooks = makeHooks(); execSpy.mockReturnValueOnce(canaryVersion); plugins.apply({ hooks } as Auto); - expect(await hooks.canary.promise(SEMVER.patch, '-canary')).toBe( + expect(await hooks.canary.promise(SEMVER.patch, "-canary")).toBe( canaryVersion ); }); - test('should handle object return values', async () => { - const canaryVersion = { newVersion: '0.1.0-canary', details: 'foo' }; + test("should handle object return values", async () => { + const canaryVersion = { newVersion: "0.1.0-canary", details: "foo" }; const plugins = new Exec({ - canary: `echo ${JSON.stringify(canaryVersion)}` + canary: `echo ${JSON.stringify(canaryVersion)}`, }); const hooks = makeHooks(); execSpy.mockReturnValueOnce(JSON.stringify(canaryVersion)); plugins.apply({ hooks } as Auto); - expect(await hooks.canary.promise(SEMVER.patch, '-canary')).toStrictEqual( + expect(await hooks.canary.promise(SEMVER.patch, "-canary")).toStrictEqual( canaryVersion ); }); }); - describe('onCreateRelease', () => { - test('should handle createChangelogTitle', async () => { + describe("onCreateRelease", () => { + test("should handle createChangelogTitle", async () => { const plugins = new Exec({ - onCreateRelease: { createChangelogTitle: 'echo here' } + onCreateRelease: { createChangelogTitle: "echo here" }, }); const hooks = makeHooks(); const releaseHooks = makeReleaseHooks(); - execSpy.mockReturnValueOnce('here'); + execSpy.mockReturnValueOnce("here"); plugins.apply({ hooks } as Auto); hooks.onCreateRelease.call({ hooks: releaseHooks } as any); - expect(await releaseHooks.createChangelogTitle.promise()).toBe('here'); + expect(await releaseHooks.createChangelogTitle.promise()).toBe("here"); }); }); - describe('onCreateLogParse', () => { - test('should omitCommit', async () => { + describe("onCreateLogParse", () => { + test("should omitCommit", async () => { const plugins = new Exec({ - onCreateLogParse: { omitCommit: 'echo true' } + onCreateLogParse: { omitCommit: "echo true" }, }); const hooks = makeHooks(); const logParsehooks = makeLogParseHooks(); - execSpy.mockReturnValueOnce('true'); + execSpy.mockReturnValueOnce("true"); plugins.apply({ hooks } as Auto); hooks.onCreateLogParse.call({ hooks: logParsehooks } as any); expect( await logParsehooks.omitCommit.promise({ - hash: '123', - subject: 'test', + hash: "123", + subject: "test", authors: [], files: [], - labels: [] + labels: [], }) ).toBe(true); }); - test('should not omitCommit', async () => { + test("should not omitCommit", async () => { const plugins = new Exec({ - onCreateLogParse: { omitCommit: 'echo' } + onCreateLogParse: { omitCommit: "echo" }, }); const hooks = makeHooks(); const logParsehooks = makeLogParseHooks(); @@ -164,25 +164,25 @@ describe('Exec Plugin', () => { expect( await logParsehooks.omitCommit.promise({ - hash: '123', - subject: 'test', + hash: "123", + subject: "test", authors: [], files: [], - labels: [] + labels: [], }) ).toBeUndefined(); }); }); - describe('onCreateChangelog', () => { - test('should omitReleaseNotes', async () => { + describe("onCreateChangelog", () => { + test("should omitReleaseNotes", async () => { const plugins = new Exec({ - onCreateChangelog: { omitReleaseNotes: 'echo true' } + onCreateChangelog: { omitReleaseNotes: "echo true" }, }); const hooks = makeHooks(); const changelogHooks = makeChangelogHooks(); - execSpy.mockReturnValueOnce('true'); + execSpy.mockReturnValueOnce("true"); plugins.apply({ hooks } as Auto); hooks.onCreateChangelog.call( { hooks: changelogHooks } as any, @@ -191,11 +191,11 @@ describe('Exec Plugin', () => { expect( await changelogHooks.omitReleaseNotes.promise({ - hash: '123', - subject: 'test', + hash: "123", + subject: "test", authors: [], files: [], - labels: [] + labels: [], }) ).toBe(true); }); diff --git a/plugins/exec/src/index.ts b/plugins/exec/src/index.ts index 50efe5dc3..82aea0b03 100644 --- a/plugins/exec/src/index.ts +++ b/plugins/exec/src/index.ts @@ -1,21 +1,21 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Auto, IPlugin, validatePluginConfiguration } from '@auto-it/core'; +import { Auto, IPlugin, validatePluginConfiguration } from "@auto-it/core"; import { makeHooks, makeReleaseHooks, makeLogParseHooks, - makeChangelogHooks -} from '@auto-it/core/dist/utils/make-hooks'; -import * as t from 'io-ts'; -import fromEntries from 'fromentries'; -import { execSync } from 'child_process'; + makeChangelogHooks, +} from "@auto-it/core/dist/utils/make-hooks"; +import * as t from "io-ts"; +import fromEntries from "fromentries"; +import { execSync } from "child_process"; type CommandMap = Record; /** Safely trim the value if it's a string */ function trim(val: string | undefined) { - if (typeof val === 'string') { + if (typeof val === "string") { return val.trim(); } } @@ -31,7 +31,7 @@ function makeHooksType( return t.partial( fromEntries( hooks - .map(name => [name, t.string] as const) + .map((name) => [name, t.string] as const) .filter(([hook]) => !exclude.includes(hook as any)) ) as Record, t.StringC> ); @@ -40,19 +40,19 @@ function makeHooksType( const onCreateChangelog = makeHooksType(makeChangelogHooks, []); const onCreateLogParse = makeHooksType(makeLogParseHooks, []); const onCreateRelease = makeHooksType(makeReleaseHooks, [ - 'onCreateChangelog', - 'onCreateLogParse' + "onCreateChangelog", + "onCreateLogParse", ]); const rootOptions = makeHooksType(makeHooks, [ - 'onCreateRelease', - 'onCreateChangelog', - 'onCreateLogParse', - 'validateConfig' + "onCreateRelease", + "onCreateChangelog", + "onCreateLogParse", + "validateConfig", ]); const pluginOptions = t.intersection([ rootOptions, - t.partial({ onCreateRelease, onCreateLogParse, onCreateChangelog }) + t.partial({ onCreateRelease, onCreateLogParse, onCreateChangelog }), ]); export type IExecPluginOptions = t.TypeOf; @@ -62,7 +62,7 @@ const createEnv = (args: any[]) => ({ ...process.env, ...fromEntries( args.map((arg, index) => [`ARG_${index}`, JSON.stringify(arg)]) - ) + ), }); /** Tap a hook if possible */ @@ -74,25 +74,25 @@ const tapHook = (name: string, hook: any, command: string) => { const hookType = hook.constructor.name; if ( - name === 'getRepository' || - name === 'getAuthor' || - name === 'makeRelease' || - name === 'modifyConfig' || - name === 'next' || - name === 'canary' || - name === 'parseCommit' || - name === 'addToBody' || - name === 'renderChangelogLine' || - name === 'renderChangelogTitle' || - name === 'renderChangelogAuthor' || - name === 'renderChangelogAuthorLine' + name === "getRepository" || + name === "getAuthor" || + name === "makeRelease" || + name === "modifyConfig" || + name === "next" || + name === "canary" || + name === "parseCommit" || + name === "addToBody" || + name === "renderChangelogLine" || + name === "renderChangelogTitle" || + name === "renderChangelogAuthor" || + name === "renderChangelogAuthorLine" ) { hook.tap(`exec ${name}`, (...args: any[]) => { const value = trim( execSync(command, { - stdio: ['ignore', 'pipe', 'inherit'], - encoding: 'utf8', - env: createEnv(args) + stdio: ["ignore", "pipe", "inherit"], + encoding: "utf8", + env: createEnv(args), }) ); @@ -100,7 +100,7 @@ const tapHook = (name: string, hook: any, command: string) => { return; } - if (name !== 'canary') { + if (name !== "canary") { return JSON.parse(value); } @@ -111,36 +111,36 @@ const tapHook = (name: string, hook: any, command: string) => { return value; } }); - } else if (name === 'omitCommit' || name === 'omitReleaseNotes') { + } else if (name === "omitCommit" || name === "omitReleaseNotes") { hook.tap(`exec ${name}`, (...args: any[]) => { const value = trim( execSync(command, { - stdio: ['ignore', 'pipe', 'inherit'], - encoding: 'utf8', - env: createEnv(args) + stdio: ["ignore", "pipe", "inherit"], + encoding: "utf8", + env: createEnv(args), }) ); - if (value === 'true') { + if (value === "true") { return true; } }); } else if ( - hookType === 'SyncHook' || - hookType === 'AsyncSeriesHook' || - hookType === 'AsyncParallelHook' || - name === 'createChangelogTitle' || - name === 'getPreviousVersion' + hookType === "SyncHook" || + hookType === "AsyncSeriesHook" || + hookType === "AsyncParallelHook" || + name === "createChangelogTitle" || + name === "getPreviousVersion" ) { hook.tap(`exec ${name}`, (...args: any[]) => trim( execSync(command, { - encoding: 'utf8', + encoding: "utf8", env: createEnv(args), stdio: - name === 'createChangelogTitle' || name === 'getPreviousVersion' - ? ['ignore', 'pipe', 'inherit'] - : 'inherit' + name === "createChangelogTitle" || name === "getPreviousVersion" + ? ["ignore", "pipe", "inherit"] + : "inherit", }) ) ); @@ -150,7 +150,7 @@ const tapHook = (name: string, hook: any, command: string) => { /** Tap into select hooks and run a command on the terminal */ export default class ExecPlugin implements IPlugin { /** The name of the plugin */ - name = 'exec'; + name = "exec"; /** The options of the plugin */ readonly options: IExecPluginOptions; @@ -178,8 +178,8 @@ export default class ExecPlugin implements IPlugin { private applyHook(auto: Auto, key: string, command: string | CommandMap) { const name = key as keyof IExecPluginOptions; - if (name === 'onCreateRelease') { - auto.hooks.onCreateRelease.tap(this.name, release => { + if (name === "onCreateRelease") { + auto.hooks.onCreateRelease.tap(this.name, (release) => { Object.entries(command as CommandMap).map( ([key, command]) => command && @@ -190,8 +190,8 @@ export default class ExecPlugin implements IPlugin { ) ); }); - } else if (name === 'onCreateChangelog') { - auto.hooks.onCreateChangelog.tap(this.name, changelog => { + } else if (name === "onCreateChangelog") { + auto.hooks.onCreateChangelog.tap(this.name, (changelog) => { Object.entries(command as CommandMap).map( ([key, command]) => command && @@ -202,8 +202,8 @@ export default class ExecPlugin implements IPlugin { ) ); }); - } else if (name === 'onCreateLogParse') { - auto.hooks.onCreateLogParse.tap(this.name, logParse => { + } else if (name === "onCreateLogParse") { + auto.hooks.onCreateLogParse.tap(this.name, (logParse) => { Object.entries(command as CommandMap).map( ([key, command]) => command && diff --git a/plugins/first-time-contributor/__tests__/first-time-contributor.test.ts b/plugins/first-time-contributor/__tests__/first-time-contributor.test.ts index ab823fc93..e4ae46887 100644 --- a/plugins/first-time-contributor/__tests__/first-time-contributor.test.ts +++ b/plugins/first-time-contributor/__tests__/first-time-contributor.test.ts @@ -1,19 +1,19 @@ -import * as Auto from '@auto-it/core'; -import makeCommitFromMsg from '@auto-it/core/dist/__tests__/make-commit-from-msg'; -import Changelog from '@auto-it/core/dist/changelog'; -import { Octokit } from '@octokit/rest'; +import * as Auto from "@auto-it/core"; +import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; +import Changelog from "@auto-it/core/dist/changelog"; +import { Octokit } from "@octokit/rest"; import { makeChangelogHooks, - makeHooks -} from '@auto-it/core/dist/utils/make-hooks'; + makeHooks, +} from "@auto-it/core/dist/utils/make-hooks"; -import FirstTimeContributor from '../src'; +import FirstTimeContributor from "../src"; const graphql = jest.fn(); const exec = jest.fn(); -exec.mockReturnValue(''); +exec.mockReturnValue(""); // @ts-ignore -jest.spyOn(Auto, 'execPromise').mockImplementation(exec); +jest.spyOn(Auto, "execPromise").mockImplementation(exec); const setup = ( contributors: Partial[] = [] @@ -26,18 +26,18 @@ const setup = ( hooks, git: { graphql, - options: { repo: 'repo', owner: 'test' }, + options: { repo: "repo", owner: "test" }, github: { repos: { - listContributors: () => Promise.resolve({ data: contributors }) - } - } - } as any + listContributors: () => Promise.resolve({ data: contributors }), + }, + }, + } as any, } as Auto.Auto); hooks.onCreateChangelog.call( { hooks: changelogHooks, - options: { baseUrl: 'https://github.com' } + options: { baseUrl: "https://github.com" }, } as Changelog, Auto.SEMVER.patch ); @@ -45,109 +45,109 @@ const setup = ( return changelogHooks; }; -describe('First Time Contributor Plugin', () => { +describe("First Time Contributor Plugin", () => { afterEach(() => { jest.clearAllMocks(); }); - test('should include all authors with no past contributors', async () => { + test("should include all authors with no past contributors", async () => { const hooks = setup(); const commits = [ - makeCommitFromMsg('foo', { name: 'Jeff', username: 'Jeff' }), - makeCommitFromMsg('foo', { name: 'Andrew', username: 'Andrew' }) + makeCommitFromMsg("foo", { name: "Jeff", username: "Jeff" }), + makeCommitFromMsg("foo", { name: "Andrew", username: "Andrew" }), ]; graphql.mockReturnValueOnce({ - search: { issueCount: 0 } + search: { issueCount: 0 }, }); graphql.mockReturnValueOnce({ - search: { issueCount: 0 } + search: { issueCount: 0 }, }); expect(await hooks.addToBody.promise([], commits)).toMatchSnapshot(); }); - test('should exclude a past contributor', async () => { + test("should exclude a past contributor", async () => { const hooks = setup(); const commits = [ - makeCommitFromMsg('foo', { name: 'Jeff', username: 'Jeff' }), - makeCommitFromMsg('foo', { name: 'Andrew', username: 'Andrew' }) + makeCommitFromMsg("foo", { name: "Jeff", username: "Jeff" }), + makeCommitFromMsg("foo", { name: "Andrew", username: "Andrew" }), ]; graphql.mockReturnValueOnce({ - search: { issueCount: 1 } + search: { issueCount: 1 }, }); expect(await hooks.addToBody.promise([], commits)).toMatchSnapshot(); }); - test('should exclude a past contributor by username', async () => { - const hooks = setup([{ login: 'Andrew420' }]); + test("should exclude a past contributor by username", async () => { + const hooks = setup([{ login: "Andrew420" }]); const commits = [ - makeCommitFromMsg('foo', { name: 'Jeff123', username: 'Jeff123' }), - makeCommitFromMsg('foo', { name: 'Andrew420', username: 'Andrew420' }) + makeCommitFromMsg("foo", { name: "Jeff123", username: "Jeff123" }), + makeCommitFromMsg("foo", { name: "Andrew420", username: "Andrew420" }), ]; graphql.mockReturnValueOnce({ - search: { issueCount: 1 } + search: { issueCount: 1 }, }); expect(await hooks.addToBody.promise([], commits)).toMatchSnapshot(); }); - test('should not include past contributors', async () => { + test("should not include past contributors", async () => { const hooks = setup(); const commits = [ - makeCommitFromMsg('foo', { name: 'Jeff', username: 'Jeff' }), - makeCommitFromMsg('foo', { name: 'Andrew', username: 'Andrew' }) + makeCommitFromMsg("foo", { name: "Jeff", username: "Jeff" }), + makeCommitFromMsg("foo", { name: "Andrew", username: "Andrew" }), ]; graphql.mockReturnValue({ - search: { issueCount: 2 } + search: { issueCount: 2 }, }); expect(await hooks.addToBody.promise([], commits)).toMatchSnapshot(); }); - test('should include a username if it exists', async () => { + test("should include a username if it exists", async () => { const hooks = setup(); const commits = [ - makeCommitFromMsg('foo', { name: 'Jeff', username: 'jeff-the-snake' }), - makeCommitFromMsg('foo', { name: 'Andrew', username: 'hipstersmoothie' }) + makeCommitFromMsg("foo", { name: "Jeff", username: "jeff-the-snake" }), + makeCommitFromMsg("foo", { name: "Andrew", username: "hipstersmoothie" }), ]; graphql.mockReturnValue({ - search: { issueCount: 1 } + search: { issueCount: 1 }, }); expect(await hooks.addToBody.promise([], commits)).toMatchSnapshot(); }); - test('should include only username if no name exists', async () => { + test("should include only username if no name exists", async () => { const hooks = setup(); const commits = [ - makeCommitFromMsg('foo', { username: 'jeff-the-snake', name: '' }) + makeCommitFromMsg("foo", { username: "jeff-the-snake", name: "" }), ]; graphql.mockReturnValue({ - search: { issueCount: 1 } + search: { issueCount: 1 }, }); expect(await hooks.addToBody.promise([], commits)).toMatchSnapshot(); }); - test('should exclude bots', async () => { + test("should exclude bots", async () => { const hooks = setup(); const commits = [ - makeCommitFromMsg('foo', { - username: 'jeff-the-snake', - name: 'jeff', - type: 'Bot' - }) + makeCommitFromMsg("foo", { + username: "jeff-the-snake", + name: "jeff", + type: "Bot", + }), ]; graphql.mockReturnValue({ - search: { issueCount: 1 } + search: { issueCount: 1 }, }); expect(await hooks.addToBody.promise([], commits)).toMatchSnapshot(); diff --git a/plugins/first-time-contributor/src/index.ts b/plugins/first-time-contributor/src/index.ts index 72c57a16a..4bbafd172 100644 --- a/plugins/first-time-contributor/src/index.ts +++ b/plugins/first-time-contributor/src/index.ts @@ -1,26 +1,26 @@ -import { Auto, IPlugin } from '@auto-it/core'; -import { ICommitAuthor } from '@auto-it/core/dist/log-parse'; -import flatMap from 'array.prototype.flatmap'; -import endent from 'endent'; -import urlJoin from 'url-join'; -import { URL } from 'url'; +import { Auto, IPlugin } from "@auto-it/core"; +import { ICommitAuthor } from "@auto-it/core/dist/log-parse"; +import flatMap from "array.prototype.flatmap"; +import endent from "endent"; +import urlJoin from "url-join"; +import { URL } from "url"; /** * Thank first time contributors for their work right in your release notes. */ export default class FirstTimeContributorPlugin implements IPlugin { /** The name of the plugin */ - name = 'first-time-contributor'; + name = "first-time-contributor"; /** Tap into auto plugin points. */ apply(auto: Auto) { - auto.hooks.onCreateChangelog.tap(this.name, changelog => { + auto.hooks.onCreateChangelog.tap(this.name, (changelog) => { const base = new URL(changelog.options.baseUrl).origin; /** Format a string for the contributor */ const renderContributor = ({ name, username }: ICommitAuthor) => { - const link = `[@${username}](${urlJoin(base, username || '')})`; - return `${name}${username ? (name ? ` (${link})` : link) : ''}`; + const link = `[@${username}](${urlJoin(base, username || "")})`; + return `${name}${username ? (name ? ` (${link})` : link) : ""}`; }; changelog.hooks.addToBody.tapPromise( @@ -28,8 +28,8 @@ export default class FirstTimeContributorPlugin implements IPlugin { async (notes, commits) => { const newContributors = ( await Promise.all( - flatMap(commits, c => c.authors).map(async author => { - if (!author.username || author.type === 'Bot') { + flatMap(commits, (c) => c.authors).map(async (author) => { + if (!author.username || author.type === "Bot") { return; } @@ -61,8 +61,8 @@ export default class FirstTimeContributorPlugin implements IPlugin { :tada: This release contains work from new contributors! :tada: Thanks for all your work!\n\n${[...lines] - .map(line => `:heart: ${line}`) - .join('\n\n')} + .map((line) => `:heart: ${line}`) + .join("\n\n")} `; } else { thankYou = endent` diff --git a/plugins/gh-pages/CHANGELOG.md b/plugins/gh-pages/CHANGELOG.md index d8a882cf2..3de183ee9 100644 --- a/plugins/gh-pages/CHANGELOG.md +++ b/plugins/gh-pages/CHANGELOG.md @@ -6,4 +6,4 @@ #### Authors: 1 -- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) \ No newline at end of file +- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) diff --git a/plugins/gh-pages/__tests__/gh-pages.test.ts b/plugins/gh-pages/__tests__/gh-pages.test.ts index 1e8e32704..4d83bdb30 100644 --- a/plugins/gh-pages/__tests__/gh-pages.test.ts +++ b/plugins/gh-pages/__tests__/gh-pages.test.ts @@ -1,14 +1,14 @@ -import { execPromise } from '@auto-it/core'; -import GhPages, { IGhPagesPluginOptions } from '../src'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import { execSync } from 'child_process'; +import { execPromise } from "@auto-it/core"; +import GhPages, { IGhPagesPluginOptions } from "../src"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import { execSync } from "child_process"; const execSyncSpy = execSync as jest.Mock; -jest.mock('child_process'); +jest.mock("child_process"); const execSpy = execPromise as jest.Mock; -jest.mock('@auto-it/core'); +jest.mock("@auto-it/core"); /** Create a test for gh-pages plugin */ function createTest( @@ -23,117 +23,117 @@ function createTest( logger: dummyLog(), setGitUser: () => undefined, ...autoOverrides, - git: autoOverrides.git || {} + git: autoOverrides.git || {}, } as any); return hooks; } -describe('Gh-Pages Plugin', () => { +describe("Gh-Pages Plugin", () => { beforeEach(() => { execSpy.mockReset(); }); - describe('beforeShipit', () => { - test('should not release on anything but a latest release', async () => { - const hooks = createTest({ dir: 'test' }); + describe("beforeShipit", () => { + test("should not release on anything but a latest release", async () => { + const hooks = createTest({ dir: "test" }); - await hooks.beforeShipIt.promise({ releaseType: 'canary' }); - await hooks.beforeShipIt.promise({ releaseType: 'next' }); - await hooks.beforeShipIt.promise({ releaseType: 'old' }); + await hooks.beforeShipIt.promise({ releaseType: "canary" }); + await hooks.beforeShipIt.promise({ releaseType: "next" }); + await hooks.beforeShipIt.promise({ releaseType: "old" }); expect(execSpy).not.toHaveBeenCalled(); }); - test('should not release if there is a version bump', async () => { - const hooks = createTest({ dir: 'test' }, { getVersion: () => 'patch' }); + test("should not release if there is a version bump", async () => { + const hooks = createTest({ dir: "test" }, { getVersion: () => "patch" }); - await hooks.beforeShipIt.promise({ releaseType: 'latest' }); + await hooks.beforeShipIt.promise({ releaseType: "latest" }); expect(execSpy).not.toHaveBeenCalled(); }); - test('should not release if there is no matching PR', async () => { + test("should not release if there is no matching PR", async () => { const hooks = createTest( - { dir: 'test' }, + { dir: "test" }, { - getVersion: () => '', - git: { getSha: () => '123', matchCommitToPr: () => undefined } + getVersion: () => "", + git: { getSha: () => "123", matchCommitToPr: () => undefined }, } ); - await hooks.beforeShipIt.promise({ releaseType: 'latest' }); + await hooks.beforeShipIt.promise({ releaseType: "latest" }); expect(execSpy).not.toHaveBeenCalled(); }); test("should not release if PR doesn't have documentation label", async () => { const hooks = createTest( - { dir: 'test' }, + { dir: "test" }, { - getVersion: () => '', - git: { getSha: () => '123', matchCommitToPr: () => ({ labels: [] }) } + getVersion: () => "", + git: { getSha: () => "123", matchCommitToPr: () => ({ labels: [] }) }, } ); - await hooks.beforeShipIt.promise({ releaseType: 'latest' }); + await hooks.beforeShipIt.promise({ releaseType: "latest" }); expect(execSpy).not.toHaveBeenCalled(); }); - test('should release if noVersion + documentation label', async () => { + test("should release if noVersion + documentation label", async () => { const hooks = createTest( - { dir: 'test' }, + { dir: "test" }, { - getVersion: () => '', + getVersion: () => "", git: { - getSha: () => '123', - matchCommitToPr: () => ({ labels: ['documentation'] }) - } + getSha: () => "123", + matchCommitToPr: () => ({ labels: ["documentation"] }), + }, } ); - await hooks.beforeShipIt.promise({ releaseType: 'latest' }); + await hooks.beforeShipIt.promise({ releaseType: "latest" }); expect(execSpy).toHaveBeenCalled(); }); }); - describe('afterRelease', () => { - test('should not release if there is not GitHub release', async () => { - const hooks = createTest({ dir: 'test' }); + describe("afterRelease", () => { + test("should not release if there is not GitHub release", async () => { + const hooks = createTest({ dir: "test" }); await hooks.afterRelease.promise({ response: undefined } as any); expect(execSpy).not.toHaveBeenCalled(); }); - test('should not release if it was pre-release', async () => { - const hooks = createTest({ dir: 'test' }); + test("should not release if it was pre-release", async () => { + const hooks = createTest({ dir: "test" }); await hooks.afterRelease.promise({ - response: { data: { prerelease: true } } + response: { data: { prerelease: true } }, } as any); expect(execSpy).not.toHaveBeenCalled(); }); - test('should run build command if present', async () => { - const buildCommand = 'dew it'; + test("should run build command if present", async () => { + const buildCommand = "dew it"; const hooks = createTest({ - dir: 'test', - buildCommand + dir: "test", + buildCommand, }); await hooks.afterRelease.promise({ - response: { data: { prerelease: false } } + response: { data: { prerelease: false } }, } as any); expect(execSyncSpy).toHaveBeenCalledWith(buildCommand); }); - test('should successfully deploy to gh-pages', async () => { - const hooks = createTest({ dir: 'test' }); + test("should successfully deploy to gh-pages", async () => { + const hooks = createTest({ dir: "test" }); await hooks.afterRelease.promise({ - response: { data: { prerelease: false } } + response: { data: { prerelease: false } }, } as any); - expect(execSpy).toHaveBeenCalledWith('npx', [ - 'push-dir', - '--cleanup', - '--remote=undefined', - '--dir=test', - '--branch=gh-pages', - '--message="Update docs [skip ci]"' + expect(execSpy).toHaveBeenCalledWith("npx", [ + "push-dir", + "--cleanup", + "--remote=undefined", + "--dir=test", + "--branch=gh-pages", + '--message="Update docs [skip ci]"', ]); }); }); diff --git a/plugins/gh-pages/src/index.ts b/plugins/gh-pages/src/index.ts index 88b6f711c..3482f5ea5 100644 --- a/plugins/gh-pages/src/index.ts +++ b/plugins/gh-pages/src/index.ts @@ -3,14 +3,14 @@ import { IPlugin, execPromise, validatePluginConfiguration, - SEMVER -} from '@auto-it/core'; -import { execSync } from 'child_process'; -import * as t from 'io-ts'; + SEMVER, +} from "@auto-it/core"; +import { execSync } from "child_process"; +import * as t from "io-ts"; const required = t.interface({ /** The directory to push to gh-pages */ - dir: t.string + dir: t.string, }); const optional = t.partial({ @@ -19,7 +19,7 @@ const optional = t.partial({ /** The branch to push to */ branch: t.string, /** A label to look for and always publish the docs */ - label: t.string + label: t.string, }); const pluginOptions = t.intersection([required, optional]); @@ -27,14 +27,14 @@ const pluginOptions = t.intersection([required, optional]); export type IGhPagesPluginOptions = t.TypeOf; const defaults = { - branch: 'gh-pages', - label: 'documentation' + branch: "gh-pages", + label: "documentation", }; /** Automate publishing to your gh-pages documentation website. */ export default class GhPagesPlugin implements IPlugin { /** The name of the plugin */ - name = 'gh-pages'; + name = "gh-pages"; /** The options of the plugin */ readonly options: IGhPagesPluginOptions & typeof defaults; @@ -53,7 +53,7 @@ export default class GhPagesPlugin implements IPlugin { }); auto.hooks.beforeShipIt.tapPromise(this.name, async ({ releaseType }) => { - if (releaseType !== 'latest' || !auto.git) { + if (releaseType !== "latest" || !auto.git) { return; } @@ -88,7 +88,7 @@ export default class GhPagesPlugin implements IPlugin { } const releases = Array.isArray(response) ? response : [response]; - const isPrerelease = releases.some(release => release.data.prerelease); + const isPrerelease = releases.some((release) => release.data.prerelease); if (isPrerelease) { return; @@ -105,21 +105,21 @@ export default class GhPagesPlugin implements IPlugin { } try { - await execPromise('npx', [ - 'push-dir', - '--cleanup', + await execPromise("npx", [ + "push-dir", + "--cleanup", `--remote=${auto.remote}`, `--dir=${this.options.dir}`, `--branch=${this.options.branch}`, - '--message="Update docs [skip ci]"' + '--message="Update docs [skip ci]"', ]); } catch (error) { auto.logger.log.error( - 'Oh no! It looks like there was trouble publishing to GitHub Pages 😢' + "Oh no! It looks like there was trouble publishing to GitHub Pages 😢" ); throw error; } - auto.logger.log.success('Successfully deployed to GitHub Pages!'); + auto.logger.log.success("Successfully deployed to GitHub Pages!"); } } diff --git a/plugins/git-tag/__tests__/git-tag.test.ts b/plugins/git-tag/__tests__/git-tag.test.ts index 657ae3da4..2ebf4a6ab 100644 --- a/plugins/git-tag/__tests__/git-tag.test.ts +++ b/plugins/git-tag/__tests__/git-tag.test.ts @@ -1,11 +1,11 @@ -import * as Auto from '@auto-it/core'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; +import * as Auto from "@auto-it/core"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; -import GitTag from '../src'; +import GitTag from "../src"; const exec = jest.fn(); -jest.spyOn(Auto, 'execPromise').mockImplementation(exec); +jest.spyOn(Auto, "execPromise").mockImplementation(exec); const setup = (mockGit?: any) => { const plugin = new GitTag(); @@ -14,93 +14,93 @@ const setup = (mockGit?: any) => { plugin.apply(({ hooks, git: mockGit, - remote: 'origin', + remote: "origin", logger: dummyLog(), prefixRelease: (r: string) => r, - config: { prereleaseBranches: ['next'] }, - getCurrentVersion: () => 'v1.0.0' + config: { prereleaseBranches: ["next"] }, + getCurrentVersion: () => "v1.0.0", } as unknown) as Auto.Auto); return hooks; }; -describe('Git Tag Plugin', () => { +describe("Git Tag Plugin", () => { beforeEach(() => { exec.mockClear(); }); - describe('getPreviousVersion', () => { - test('should error without git', async () => { + describe("getPreviousVersion", () => { + test("should error without git", async () => { const hooks = setup(); await expect(hooks.getPreviousVersion.promise()).rejects.toBeInstanceOf( Error ); }); - test('should get previous version', async () => { - const hooks = setup({ getLatestTagInBranch: () => 'v1.0.0' }); + test("should get previous version", async () => { + const hooks = setup({ getLatestTagInBranch: () => "v1.0.0" }); const previousVersion = await hooks.getPreviousVersion.promise(); - expect(previousVersion).toBe('v1.0.0'); + expect(previousVersion).toBe("v1.0.0"); }); - test('should default to 0.0.0 when no previous version', async () => { + test("should default to 0.0.0 when no previous version", async () => { const hooks = setup({ getLatestTagInBranch: () => { throw new Error(); - } + }, }); const previousVersion = await hooks.getPreviousVersion.promise(); - expect(previousVersion).toBe('0.0.0'); + expect(previousVersion).toBe("0.0.0"); }); }); - describe('version', () => { - test('should do nothing without git', async () => { + describe("version", () => { + test("should do nothing without git", async () => { const hooks = setup(); await hooks.version.promise(Auto.SEMVER.patch); expect(exec).not.toHaveBeenCalled(); }); - test('should do nothing with a bad version bump', async () => { - const hooks = setup({ getLatestTagInBranch: () => 'v1.0.0' }); - await hooks.version.promise('wrong' as Auto.SEMVER); + test("should do nothing with a bad version bump", async () => { + const hooks = setup({ getLatestTagInBranch: () => "v1.0.0" }); + await hooks.version.promise("wrong" as Auto.SEMVER); expect(exec).not.toHaveBeenCalled(); }); - test('should tag next version', async () => { - const hooks = setup({ getLatestTagInBranch: () => 'v1.0.0' }); + test("should tag next version", async () => { + const hooks = setup({ getLatestTagInBranch: () => "v1.0.0" }); await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('git', [ - 'tag', - '1.0.1', - '-m', - '"Update version to 1.0.1"' + expect(exec).toHaveBeenCalledWith("git", [ + "tag", + "1.0.1", + "-m", + '"Update version to 1.0.1"', ]); }); }); - describe('next', () => { - test('should do nothing without git', async () => { + describe("next", () => { + test("should do nothing without git", async () => { const hooks = setup(); await hooks.next.promise([], Auto.SEMVER.patch); expect(exec).not.toHaveBeenCalled(); }); - test('should tag next version', async () => { + test("should tag next version", async () => { const hooks = setup({ - getLatestRelease: () => 'v0.1.0', - getLastTagNotInBaseBranch: () => 'v1.0.0' + getLatestRelease: () => "v0.1.0", + getLastTagNotInBaseBranch: () => "v1.0.0", }); await hooks.next.promise([], Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('git', [ - 'tag', - '1.0.1-next.0', - '-m', - '"Tag pre-release: 1.0.1-next.0"' + expect(exec).toHaveBeenCalledWith("git", [ + "tag", + "1.0.1-next.0", + "-m", + '"Tag pre-release: 1.0.1-next.0"', ]); - expect(exec).toHaveBeenCalledWith('git', ['push', 'origin', '--tags']); + expect(exec).toHaveBeenCalledWith("git", ["push", "origin", "--tags"]); }); }); }); diff --git a/plugins/git-tag/src/index.ts b/plugins/git-tag/src/index.ts index 700a44450..50daab1a1 100644 --- a/plugins/git-tag/src/index.ts +++ b/plugins/git-tag/src/index.ts @@ -3,14 +3,14 @@ import { determineNextVersion, execPromise, IPlugin, - getCurrentBranch -} from '@auto-it/core'; -import { inc, ReleaseType } from 'semver'; + getCurrentBranch, +} from "@auto-it/core"; +import { inc, ReleaseType } from "semver"; /** Manage your projects version through just a git tag. */ export default class GitTagPlugin implements IPlugin { /** The name of the plugin */ - name = 'git-tag'; + name = "git-tag"; /** Tap into auto plugin points. */ apply(auto: Auto) { @@ -19,7 +19,7 @@ export default class GitTagPlugin implements IPlugin { try { return await auto.git!.getLatestTagInBranch(); } catch (error) { - return auto.prefixRelease('0.0.0'); + return auto.prefixRelease("0.0.0"); } } @@ -33,7 +33,7 @@ export default class GitTagPlugin implements IPlugin { return getTag(); }); - auto.hooks.version.tapPromise(this.name, async version => { + auto.hooks.version.tapPromise(this.name, async (version) => { if (!auto.git) { return; } @@ -42,18 +42,18 @@ export default class GitTagPlugin implements IPlugin { const newTag = inc(lastTag, version as ReleaseType); if (!newTag) { - auto.logger.log.info('No release found, doing nothing'); + auto.logger.log.info("No release found, doing nothing"); return; } const prefixedTag = auto.prefixRelease(newTag); auto.logger.log.info(`Tagging new tag: ${lastTag} => ${prefixedTag}`); - await execPromise('git', [ - 'tag', + await execPromise("git", [ + "tag", prefixedTag, - '-m', - `"Update version to ${prefixedTag}"` + "-m", + `"Update version to ${prefixedTag}"`, ]); }); @@ -63,7 +63,7 @@ export default class GitTagPlugin implements IPlugin { } const prereleaseBranches = auto.config?.prereleaseBranches!; - const branch = getCurrentBranch() || ''; + const branch = getCurrentBranch() || ""; const prereleaseBranch = prereleaseBranches.includes(branch) ? branch : prereleaseBranches[0]; @@ -78,27 +78,27 @@ export default class GitTagPlugin implements IPlugin { prereleaseBranch ); - await execPromise('git', [ - 'tag', + await execPromise("git", [ + "tag", prerelease, - '-m', - `"Tag pre-release: ${prerelease}"` + "-m", + `"Tag pre-release: ${prerelease}"`, ]); - await execPromise('git', ['push', auto.remote, '--tags']); + await execPromise("git", ["push", auto.remote, "--tags"]); return preReleaseVersions; }); auto.hooks.publish.tapPromise(this.name, async () => { - auto.logger.log.info('Pushing new tag to GitHub'); + auto.logger.log.info("Pushing new tag to GitHub"); - await execPromise('git', [ - 'push', - '--follow-tags', - '--set-upstream', + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", auto.remote, - getCurrentBranch() || auto.baseBranch + getCurrentBranch() || auto.baseBranch, ]); - }) + }); } } diff --git a/plugins/gradle/__tests__/gradle.test.ts b/plugins/gradle/__tests__/gradle.test.ts index a4294aff1..3da31872c 100644 --- a/plugins/gradle/__tests__/gradle.test.ts +++ b/plugins/gradle/__tests__/gradle.test.ts @@ -1,20 +1,20 @@ -import * as Auto from '@auto-it/core'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; +import * as Auto from "@auto-it/core"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; import GradleReleasePlugin, { IGradleReleasePluginPluginOptions, - getProperties -} from '../src'; + getProperties, +} from "../src"; const mockProperties = (properties: string) => jest - .spyOn(Auto, 'execPromise') + .spyOn(Auto, "execPromise") .mockReturnValueOnce(Promise.resolve(properties)); -describe('Gradle Plugin', () => { +describe("Gradle Plugin", () => { let hooks: Auto.IAutoHooks; const prefixRelease: (a: string) => string = jest.fn( - version => `v${version}` + (version) => `v${version}` ); const options: IGradleReleasePluginPluginOptions = {}; @@ -24,28 +24,28 @@ describe('Gradle Plugin', () => { plugin.apply({ hooks, logger: dummyLog(), prefixRelease } as Auto.Auto); }); - describe('getPreviousVersion', () => { - test('should get previous version from gradle properties', async () => { - mockProperties('version: 1.0.0'); - expect(await hooks.getPreviousVersion.promise()).toBe('v1.0.0'); + describe("getPreviousVersion", () => { + test("should get previous version from gradle properties", async () => { + mockProperties("version: 1.0.0"); + expect(await hooks.getPreviousVersion.promise()).toBe("v1.0.0"); }); - test('should get previous version snapshot from gradle properties', async () => { - mockProperties('version: 1.0.0-SNAPSHOT'); - expect(await hooks.getPreviousVersion.promise()).toBe('v1.0.0'); + test("should get previous version snapshot from gradle properties", async () => { + mockProperties("version: 1.0.0-SNAPSHOT"); + expect(await hooks.getPreviousVersion.promise()).toBe("v1.0.0"); }); - test('should throw when no version', async () => { - mockProperties(''); + test("should throw when no version", async () => { + mockProperties(""); await expect(hooks.getPreviousVersion.promise()).rejects.toThrowError( - 'No version was found in gradle properties.' + "No version was found in gradle properties." ); }); }); - describe('beforeRun & version', () => { - test('should version release - patch version', async () => { - const properties = 'version: 1.0.0'; + describe("beforeRun & version", () => { + test("should version release - patch version", async () => { + const properties = "version: 1.0.0"; mockProperties(properties); await hooks.beforeRun.promise({} as any); @@ -54,18 +54,18 @@ describe('Gradle Plugin', () => { await hooks.version.promise(Auto.SEMVER.patch); - expect(spy).toHaveBeenCalledWith(expect.stringMatching('gradle'), [ - 'release', - '-Prelease.useAutomaticVersion=true', + expect(spy).toHaveBeenCalledWith(expect.stringMatching("gradle"), [ + "release", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=1.0.1`, - '-x createReleaseTag', - '-x preTagCommit', - '-x commitNewVersion' + "-x createReleaseTag", + "-x preTagCommit", + "-x commitNewVersion", ]); }); - test('should version release - major version', async () => { - const properties = 'version: 1.0.0'; + test("should version release - major version", async () => { + const properties = "version: 1.0.0"; mockProperties(properties); await hooks.beforeRun.promise({} as any); @@ -74,17 +74,17 @@ describe('Gradle Plugin', () => { await hooks.version.promise(Auto.SEMVER.major); - expect(spy).toHaveBeenCalledWith(expect.stringMatching('gradle'), [ - 'release', - '-Prelease.useAutomaticVersion=true', + expect(spy).toHaveBeenCalledWith(expect.stringMatching("gradle"), [ + "release", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=2.0.0`, - '-x createReleaseTag', - '-x preTagCommit', - '-x commitNewVersion' + "-x createReleaseTag", + "-x preTagCommit", + "-x commitNewVersion", ]); }); - test('should version release - major version - w/build', async () => { + test("should version release - major version - w/build", async () => { const properties = `version: 1.0.0`; mockProperties(properties); @@ -95,18 +95,18 @@ describe('Gradle Plugin', () => { await hooks.version.promise(Auto.SEMVER.major); - expect(spy).toHaveBeenCalledWith(expect.stringMatching('gradle'), [ - 'release', - '-Prelease.useAutomaticVersion=true', + expect(spy).toHaveBeenCalledWith(expect.stringMatching("gradle"), [ + "release", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=2.0.0`, - '-x createReleaseTag', - '-x preTagCommit', - '-x commitNewVersion' + "-x createReleaseTag", + "-x preTagCommit", + "-x commitNewVersion", ]); }); - test('should version release - minor version', async () => { - const properties = 'version: 1.1.0'; + test("should version release - minor version", async () => { + const properties = "version: 1.1.0"; mockProperties(properties); await hooks.beforeRun.promise({} as any); @@ -115,18 +115,18 @@ describe('Gradle Plugin', () => { await hooks.version.promise(Auto.SEMVER.minor); - expect(spy).toHaveBeenCalledWith(expect.stringMatching('gradle'), [ - 'release', - '-Prelease.useAutomaticVersion=true', + expect(spy).toHaveBeenCalledWith(expect.stringMatching("gradle"), [ + "release", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=1.2.0`, - '-x createReleaseTag', - '-x preTagCommit', - '-x commitNewVersion' + "-x createReleaseTag", + "-x preTagCommit", + "-x commitNewVersion", ]); }); - test('should version release - patch w/ default snapshot', async () => { - const properties = 'version: 1.0.0-SNAPSHOT'; + test("should version release - patch w/ default snapshot", async () => { + const properties = "version: 1.0.0-SNAPSHOT"; mockProperties(properties); await hooks.beforeRun.promise({} as any); @@ -135,17 +135,17 @@ describe('Gradle Plugin', () => { await hooks.version.promise(Auto.SEMVER.patch); - expect(spy).toHaveBeenCalledWith(expect.stringMatching('gradle'), [ - 'release', - '-Prelease.useAutomaticVersion=true', + expect(spy).toHaveBeenCalledWith(expect.stringMatching("gradle"), [ + "release", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=1.0.0`, - '-x createReleaseTag', - '-x preTagCommit', - '-x commitNewVersion' + "-x createReleaseTag", + "-x preTagCommit", + "-x commitNewVersion", ]); }); - test('should version release - patch w/ custom snapshot', async () => { + test("should version release - patch w/ custom snapshot", async () => { const properties = ` version: 1.0.0.SNAP snapshotSuffix: .SNAP @@ -159,26 +159,26 @@ describe('Gradle Plugin', () => { await hooks.version.promise(Auto.SEMVER.patch); - expect(spy).toHaveBeenCalledWith(expect.stringMatching('gradle'), [ - 'release', - '-Prelease.useAutomaticVersion=true', + expect(spy).toHaveBeenCalledWith(expect.stringMatching("gradle"), [ + "release", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=1.0.0`, - '-x createReleaseTag', - '-x preTagCommit', - '-x commitNewVersion' + "-x createReleaseTag", + "-x preTagCommit", + "-x commitNewVersion", ]); }); }); }); -describe('Gradle Plugin - Custom Command', () => { +describe("Gradle Plugin - Custom Command", () => { let hooks: Auto.IAutoHooks; const prefixRelease: (a: string) => string = jest.fn( - version => `v${version}` + (version) => `v${version}` ); const options: IGradleReleasePluginPluginOptions = { - gradleCommand: './gradlew', - gradleOptions: ['-P prop=val'] + gradleCommand: "./gradlew", + gradleOptions: ["-P prop=val"], }; beforeEach(() => { @@ -187,40 +187,40 @@ describe('Gradle Plugin - Custom Command', () => { plugin.apply({ hooks, logger: dummyLog(), prefixRelease } as Auto.Auto); }); - describe('version', () => { - test('should version release - patch version - with custom gradle command', async () => { + describe("version", () => { + test("should version release - patch version - with custom gradle command", async () => { const spy = jest.fn(); - mockProperties('version: 1.0.0').mockImplementation(spy); + mockProperties("version: 1.0.0").mockImplementation(spy); await hooks.version.promise(Auto.SEMVER.patch); - expect(spy).toHaveBeenCalledWith(expect.stringMatching('gradlew'), [ - 'release', - '-Prelease.useAutomaticVersion=true', + expect(spy).toHaveBeenCalledWith(expect.stringMatching("gradlew"), [ + "release", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=1.0.1`, - '-x createReleaseTag', - '-x preTagCommit', - '-x commitNewVersion', - '-P prop=val' + "-x createReleaseTag", + "-x preTagCommit", + "-x commitNewVersion", + "-P prop=val", ]); }); }); }); -describe('getProperties', () => { - test('should read properties from file', async () => { +describe("getProperties", () => { + test("should read properties from file", async () => { mockProperties(` version: 1.0.0 snapshotSuffix: :SNAPSHOT `); - expect(await getProperties('')).toStrictEqual({ - version: '1.0.0', - snapshotSuffix: ':SNAPSHOT' + expect(await getProperties("")).toStrictEqual({ + version: "1.0.0", + snapshotSuffix: ":SNAPSHOT", }); }); - test('should read nothing from empty file', async () => { - mockProperties(''); - expect(await getProperties('')).toStrictEqual({}); + test("should read nothing from empty file", async () => { + mockProperties(""); + expect(await getProperties("")).toStrictEqual({}); }); }); diff --git a/plugins/gradle/src/index.ts b/plugins/gradle/src/index.ts index 286a65d6c..3de4ad01b 100644 --- a/plugins/gradle/src/index.ts +++ b/plugins/gradle/src/index.ts @@ -2,24 +2,24 @@ import { Auto, IPlugin, execPromise, - validatePluginConfiguration -} from '@auto-it/core'; -import { IExtendedCommit } from '@auto-it/core/dist/log-parse'; + validatePluginConfiguration, +} from "@auto-it/core"; +import { IExtendedCommit } from "@auto-it/core/dist/log-parse"; -import * as t from 'io-ts'; -import path from 'path'; -import { inc, ReleaseType } from 'semver'; +import * as t from "io-ts"; +import path from "path"; +import { inc, ReleaseType } from "semver"; /** Global functions for usage in module */ -const logPrefix = '[Gradle-Release-Plugin]'; -const defaultSnapshotSuffix = '-SNAPSHOT'; +const logPrefix = "[Gradle-Release-Plugin]"; +const defaultSnapshotSuffix = "-SNAPSHOT"; const pluginOptions = t.partial({ /** The gradle binary to release the project with */ gradleCommand: t.string, /** A list of gradle command customizations to pass to gradle */ - gradleOptions: t.array(t.string) + gradleOptions: t.array(t.string), }); export type IGradleReleasePluginPluginOptions = t.TypeOf; @@ -44,11 +44,11 @@ export interface IGradleProperties { export async function getProperties( gradleCommand: string ): Promise { - const properties = (await execPromise(gradleCommand, ['properties', '-q'])) - .split('\n') - .map(line => /([^:\s]+):\s?(.+)/.exec(line) || []) + const properties = (await execPromise(gradleCommand, ["properties", "-q"])) + .split("\n") + .map((line) => /([^:\s]+):\s?(.+)/.exec(line) || []) .map(([, key, value]) => key && value && { [key]: value }) - .filter(el => el); + .filter((el) => el); return Object.assign({}, ...properties); } @@ -62,20 +62,20 @@ export async function getProperties( async function getVersion(gradleCommand: string): Promise { const { version, - snapshotSuffix = defaultSnapshotSuffix + snapshotSuffix = defaultSnapshotSuffix, } = await getProperties(gradleCommand); if (version) { - return version.replace(snapshotSuffix, ''); + return version.replace(snapshotSuffix, ""); } - throw new Error('No version was found in gradle properties.'); + throw new Error("No version was found in gradle properties."); } /** A plugin to release java projects with gradle */ export default class GradleReleasePluginPlugin implements IPlugin { /** The name of the plugin */ - name = 'gradle'; + name = "gradle"; /** The options of the plugin */ readonly options: Required; @@ -91,8 +91,8 @@ export default class GradleReleasePluginPlugin implements IPlugin { this.options = { gradleCommand: options?.gradleCommand ? path.join(process.cwd(), options.gradleCommand) - : '/usr/bin/gradle', - gradleOptions: options.gradleOptions || [] + : "/usr/bin/gradle", + gradleOptions: options.gradleOptions || [], }; } @@ -105,28 +105,28 @@ export default class GradleReleasePluginPlugin implements IPlugin { if (buildFlag) { // don't create release, tag, or commit since auto will do this await execPromise(this.options.gradleCommand, [ - 'release', - '-Prelease.useAutomaticVersion=true', + "release", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=${version}`, - '-x createReleaseTag', - '-x preTagCommit', - '-x commitNewVersion', - ...this.options.gradleOptions + "-x createReleaseTag", + "-x preTagCommit", + "-x commitNewVersion", + ...this.options.gradleOptions, ]); } else { await execPromise(this.options.gradleCommand, [ - 'updateVersion', - '-Prelease.useAutomaticVersion=true', + "updateVersion", + "-Prelease.useAutomaticVersion=true", `-Prelease.newVersion=${version}`, - ...this.options.gradleOptions + ...this.options.gradleOptions, ]); } - await execPromise('git', [ - 'commit', - '-am', + await execPromise("git", [ + "commit", + "-am", `"${commitMsg || `update version: ${version} [skip ci]"`}"`, - '--no-verify' + "--no-verify", ]); }; @@ -143,17 +143,17 @@ export default class GradleReleasePluginPlugin implements IPlugin { this.properties = await getProperties(this.options.gradleCommand); const { - version = '', - snapshotSuffix = defaultSnapshotSuffix + version = "", + snapshotSuffix = defaultSnapshotSuffix, } = this.properties; if (version.endsWith(snapshotSuffix)) { this.snapshotRelease = true; } }); - auto.hooks.onCreateLogParse.tap(this.name, logParse => { + auto.hooks.onCreateLogParse.tap(this.name, (logParse) => { logParse.hooks.omitCommit.tap(this.name, (commit: IExtendedCommit) => { - if (commit.subject.includes('[Gradle Release Plugin]')) { + if (commit.subject.includes("[Gradle Release Plugin]")) { return true; } }); @@ -170,7 +170,7 @@ export default class GradleReleasePluginPlugin implements IPlugin { // After release we bump the version by a patch and add -SNAPSHOT // Given that we do not need to increment when versioning, since // it has already been done - this.snapshotRelease && version === 'patch' + this.snapshotRelease && version === "patch" ? previousVersion : inc(previousVersion, version as ReleaseType); @@ -188,11 +188,11 @@ export default class GradleReleasePluginPlugin implements IPlugin { const newVersion = auto.prefixRelease(releaseVersion); // Ensure tag is on this commit, changelog will be added automatically - await execPromise('git', [ - 'tag', + await execPromise("git", [ + "tag", newVersion, - '-m', - `"Update version to ${newVersion}"` + "-m", + `"Update version to ${newVersion}"`, ]); }); @@ -201,17 +201,17 @@ export default class GradleReleasePluginPlugin implements IPlugin { if (publish) { await execPromise(this.options.gradleCommand, [ - 'publish', - ...this.options.gradleOptions + "publish", + ...this.options.gradleOptions, ]); } - await execPromise('git', [ - 'push', - '--follow-tags', - '--set-upstream', + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", auto.remote, - auto.baseBranch + auto.baseBranch, ]); }); @@ -225,7 +225,7 @@ export default class GradleReleasePluginPlugin implements IPlugin { // snapshots precede releases, so if we had a minor/major release, // then we need to set up snapshots on the next version - const newVersion = `${inc(releaseVersion, 'patch')}${snapshotSuffix}`; + const newVersion = `${inc(releaseVersion, "patch")}${snapshotSuffix}`; await this.updateGradleVersion( newVersion, @@ -233,12 +233,12 @@ export default class GradleReleasePluginPlugin implements IPlugin { false ); - await execPromise('git', [ - 'push', - '--follow-tags', - '--set-upstream', + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", auto.remote, - auto.baseBranch + auto.baseBranch, ]); }); } diff --git a/plugins/jira/__tests__/jira.test.ts b/plugins/jira/__tests__/jira.test.ts index e3e01f058..de5d2baf9 100644 --- a/plugins/jira/__tests__/jira.test.ts +++ b/plugins/jira/__tests__/jira.test.ts @@ -1,78 +1,78 @@ -import Auto, { SEMVER } from '@auto-it/core'; -import makeCommitFromMsg from '@auto-it/core/dist/__tests__/make-commit-from-msg'; +import Auto, { SEMVER } from "@auto-it/core"; +import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; import Changelog, { - IGenerateReleaseNotesOptions -} from '@auto-it/core/dist/changelog'; -import LogParse from '@auto-it/core/dist/log-parse'; -import { defaultLabels } from '@auto-it/core/dist/release'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; + IGenerateReleaseNotesOptions, +} from "@auto-it/core/dist/changelog"; +import LogParse from "@auto-it/core/dist/log-parse"; +import { defaultLabels } from "@auto-it/core/dist/release"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; import { makeChangelogHooks, - makeHooks -} from '@auto-it/core/dist/utils/make-hooks'; -import JiraPlugin, { parseJira } from '../src'; + makeHooks, +} from "@auto-it/core/dist/utils/make-hooks"; +import JiraPlugin, { parseJira } from "../src"; -describe('parse jira', () => { - test('no story', () => { +describe("parse jira", () => { + test("no story", () => { const commit = { - ...makeCommitFromMsg('Add log') + ...makeCommitFromMsg("Add log"), }; expect(parseJira(commit)).toStrictEqual(commit); }); - test('story found', () => { + test("story found", () => { const jira = { - number: ['P-5052'] + number: ["P-5052"], }; - expect(parseJira(makeCommitFromMsg('P-5052: Add log')).jira).toStrictEqual( + expect(parseJira(makeCommitFromMsg("P-5052: Add log")).jira).toStrictEqual( jira ); expect( - parseJira(makeCommitFromMsg('[P-5052] - Add log')).jira + parseJira(makeCommitFromMsg("[P-5052] - Add log")).jira ).toStrictEqual(jira); - expect(parseJira(makeCommitFromMsg('[P-5052] Add log')).jira).toStrictEqual( + expect(parseJira(makeCommitFromMsg("[P-5052] Add log")).jira).toStrictEqual( jira ); }); - test('story found, pr no title', () => { + test("story found, pr no title", () => { const jira = { - number: ['PLAYA-5052'] + number: ["PLAYA-5052"], }; - expect(parseJira(makeCommitFromMsg('[PLAYA-5052]')).jira).toStrictEqual( + expect(parseJira(makeCommitFromMsg("[PLAYA-5052]")).jira).toStrictEqual( jira ); }); - test('story found multiple', () => { + test("story found multiple", () => { const jira = { - number: ['PLAYA-5052', 'PLAYA-6000'] + number: ["PLAYA-5052", "PLAYA-6000"], }; expect( - parseJira(makeCommitFromMsg('PLAYA-5052 PLAYA-6000: Add log')).jira + parseJira(makeCommitFromMsg("PLAYA-5052 PLAYA-6000: Add log")).jira ).toStrictEqual(jira); expect( - parseJira(makeCommitFromMsg('[PLAYA-5052][PLAYA-6000] - Add log')).jira + parseJira(makeCommitFromMsg("[PLAYA-5052][PLAYA-6000] - Add log")).jira ).toStrictEqual(jira); expect( - parseJira(makeCommitFromMsg('[PLAYA-5052] PLAYA-6000: Add log')).jira + parseJira(makeCommitFromMsg("[PLAYA-5052] PLAYA-6000: Add log")).jira ).toStrictEqual(jira); expect( - parseJira(makeCommitFromMsg('PLAYA-5052 [PLAYA-6000] - Add log')).jira + parseJira(makeCommitFromMsg("PLAYA-5052 [PLAYA-6000] - Add log")).jira ).toStrictEqual(jira); }); }); -describe('render jira', () => { - test('no jira number', async () => { - const plugin = new JiraPlugin('jira.com'); +describe("render jira", () => { + test("no jira number", async () => { + const plugin = new JiraPlugin("jira.com"); const hooks = makeHooks(); const changelogHooks = makeChangelogHooks(); - const commit = makeCommitFromMsg('Add log'); + const commit = makeCommitFromMsg("Add log"); plugin.apply({ hooks, logger: dummyLog() } as Auto); hooks.onCreateChangelog.promise( @@ -81,12 +81,12 @@ describe('render jira', () => { ); expect( - (await changelogHooks.renderChangelogLine.promise([commit, 'Add log']))[1] - ).toBe('Add log'); + (await changelogHooks.renderChangelogLine.promise([commit, "Add log"]))[1] + ).toBe("Add log"); }); - test('with jira number', async () => { - const plugin = new JiraPlugin({ url: 'jira.com' }); + test("with jira number", async () => { + const plugin = new JiraPlugin({ url: "jira.com" }); const hooks = makeHooks(); const changelogHooks = makeChangelogHooks(); @@ -97,29 +97,29 @@ describe('render jira', () => { ); const [, line] = await changelogHooks.renderChangelogLine.promise([ - makeCommitFromMsg('[PLAYA-5052] Add log'), - '[PLAYA-5052] Add log [author](link/to/author)' + makeCommitFromMsg("[PLAYA-5052] Add log"), + "[PLAYA-5052] Add log [author](link/to/author)", ]); expect(line).toBe( - '[PLAYA-5052](jira.com/PLAYA-5052): Add log [author](link/to/author)' + "[PLAYA-5052](jira.com/PLAYA-5052): Add log [author](link/to/author)" ); }); }); const testOptions = (): IGenerateReleaseNotesOptions => ({ - owner: 'foobar', - repo: 'auto', - baseUrl: 'https://github.custom.com/foobar/auto', + owner: "foobar", + repo: "auto", + baseUrl: "https://github.custom.com/foobar/auto", labels: defaultLabels, - baseBranch: 'master', - prereleaseBranches: ['next'] + baseBranch: "master", + prereleaseBranches: ["next"], }); const logParse = new LogParse(); -test('should create note for jira commits without PR title', async () => { +test("should create note for jira commits without PR title", async () => { const changelog = new Changelog(dummyLog(), testOptions()); - const plugin = new JiraPlugin({ url: 'https://jira.custom.com/browse/' }); + const plugin = new JiraPlugin({ url: "https://jira.custom.com/browse/" }); const autoHooks = makeHooks(); plugin.apply({ hooks: autoHooks } as Auto); @@ -127,15 +127,15 @@ test('should create note for jira commits without PR title', async () => { changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('[PLAYA-5052]') + makeCommitFromMsg("[PLAYA-5052]"), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); }); -test('should create note for JIRA commits', async () => { +test("should create note for JIRA commits", async () => { const changelog = new Changelog(dummyLog(), testOptions()); - const plugin = new JiraPlugin({ url: 'https://jira.custom.com/browse/' }); + const plugin = new JiraPlugin({ url: "https://jira.custom.com/browse/" }); const autoHooks = makeHooks(); plugin.apply({ hooks: autoHooks } as Auto); @@ -143,12 +143,12 @@ test('should create note for JIRA commits', async () => { changelog.loadDefaultHooks(); const normalized = await logParse.normalizeCommits([ - makeCommitFromMsg('[PLAYA-5052] - Some Feature (#12345)', { - labels: ['major'], - packages: [] + makeCommitFromMsg("[PLAYA-5052] - Some Feature (#12345)", { + labels: ["major"], + packages: [], }), - makeCommitFromMsg('Some Feature (#1234)', { labels: ['internal'] }), - makeCommitFromMsg('Third', { labels: ['patch'] }) + makeCommitFromMsg("Some Feature (#1234)", { labels: ["internal"] }), + makeCommitFromMsg("Third", { labels: ["patch"] }), ]); expect(await changelog.generateReleaseNotes(normalized)).toMatchSnapshot(); diff --git a/plugins/jira/src/index.ts b/plugins/jira/src/index.ts index 83b9413ec..0d91226db 100644 --- a/plugins/jira/src/index.ts +++ b/plugins/jira/src/index.ts @@ -1,23 +1,23 @@ -import join from 'url-join'; -import { prompt } from 'enquirer'; -import * as t from 'io-ts'; +import join from "url-join"; +import { prompt } from "enquirer"; +import * as t from "io-ts"; import { Auto, IPlugin, InteractiveInit, - validatePluginConfiguration -} from '@auto-it/core'; -import { IExtendedCommit } from '@auto-it/core/dist/log-parse'; + validatePluginConfiguration, +} from "@auto-it/core"; +import { IExtendedCommit } from "@auto-it/core/dist/log-parse"; const pluginOptions = t.interface({ /** Url to a hosted JIRA instance */ - url: t.string + url: t.string, }); export type IJiraPluginOptions = t.TypeOf; -interface InputResponse { +interface InputResponse { /** he value of the input prompt */ value: T; } @@ -57,19 +57,19 @@ export function parseJira(commit: IExtendedCommit): IJiraCommit { return { ...commit, jira: { - number: matches.map(match => match[1]) - } + number: matches.map((match) => match[1]), + }, }; } /** Convert shorthand options to noraml shape */ const normalizeOptions = (options: IJiraPluginOptions | string) => - typeof options === 'string' ? { url: options } : options; + typeof options === "string" ? { url: options } : options; /** Include Jira story information in your changelogs */ export default class JiraPlugin implements IPlugin { /** The name of the plugin */ - name = 'jira'; + name = "jira"; /** The options of the plugin */ readonly options: IJiraPluginOptions; @@ -81,16 +81,16 @@ export default class JiraPlugin implements IPlugin { /** Custom initialization for this plugin */ init(initializer: InteractiveInit) { - initializer.hooks.configurePlugin.tapPromise(this.name, async name => { - if (name === 'jira') { + initializer.hooks.configurePlugin.tapPromise(this.name, async (name) => { + if (name === "jira") { const url = await prompt({ - type: 'input', - name: 'value', - message: 'What is the root url of your Jira instance?', - required: true + type: "input", + name: "value", + message: "What is the root url of your Jira instance?", + required: true, }); - return ['jira', url.value]; + return ["jira", url.value]; } }); } @@ -107,7 +107,7 @@ export default class JiraPlugin implements IPlugin { } }); - auto.hooks.onCreateChangelog.tap(this.name, changelog => { + auto.hooks.onCreateChangelog.tap(this.name, (changelog) => { changelog.hooks.renderChangelogLine.tap( this.name, ([commit, currentRender]) => { @@ -120,7 +120,7 @@ export default class JiraPlugin implements IPlugin { line = line.replace( jira, - `[${jiraCommit.jira.number}](${link})${rest ? ':' : ''} $2` + `[${jiraCommit.jira.number}](${link})${rest ? ":" : ""} $2` ); } diff --git a/plugins/maven/README.md b/plugins/maven/README.md index a517703ed..22e9d8eca 100644 --- a/plugins/maven/README.md +++ b/plugins/maven/README.md @@ -59,9 +59,10 @@ You will also need all of the following configuration blocks for all parts of `a HEAD ``` - ::: message is-info - Don't forget to set enviornment variables `GH_USER`, `GH_TOKEN` - ::: + + ::: message is-info + Don't forget to set enviornment variables `GH_USER`, `GH_TOKEN` + ::: 3. Version diff --git a/plugins/maven/__tests__/maven.test.ts b/plugins/maven/__tests__/maven.test.ts index 51484665b..a45d211e4 100644 --- a/plugins/maven/__tests__/maven.test.ts +++ b/plugins/maven/__tests__/maven.test.ts @@ -1,17 +1,17 @@ -import fs from 'fs'; +import fs from "fs"; -import * as Auto from '@auto-it/core'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; -import MavenPlugin from '../src'; +import * as Auto from "@auto-it/core"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import MavenPlugin from "../src"; const mockRead = (result: string) => jest - .spyOn(fs, 'readFile') + .spyOn(fs, "readFile") // @ts-ignore .mockImplementationOnce((a, b, cb) => cb(undefined, result)); -describe('maven', () => { +describe("maven", () => { let hooks: Auto.IAutoHooks; beforeEach(() => { @@ -20,12 +20,12 @@ describe('maven', () => { plugin.apply({ hooks, logger: dummyLog(), - prefixRelease: r => `v${r}` + prefixRelease: (r) => `v${r}`, } as Auto.Auto); }); - describe('getAuthor', () => { - test('should get author from pom.xml', async () => { + describe("getAuthor", () => { + test("should get author from pom.xml", async () => { mockRead(` { expect(await hooks.getAuthor.promise()).toStrictEqual( expect.objectContaining({ - email: 'test@email.com', - name: 'Andrew Lisowski' + email: "test@email.com", + name: "Andrew Lisowski", }) ); }); - test('should support multiple developers', async () => { + test("should support multiple developers", async () => { mockRead(` { expect(await hooks.getAuthor.promise()).toStrictEqual( expect.objectContaining({ - email: 'test@email.com', - name: 'Andrew Lisowski' + email: "test@email.com", + name: "Andrew Lisowski", }) ); }); - test('should get author from pom.xml - simple', async () => { + test("should get author from pom.xml - simple", async () => { mockRead(` { }); }); - describe('getRepository', () => { - test('should get repo from pom.xml', async () => { + describe("getRepository", () => { + test("should get repo from pom.xml", async () => { mockRead(` { expect(await hooks.getRepository.promise()).toStrictEqual( expect.objectContaining({ - owner: 'Fuego-Tools', - repo: 'java-test-project' + owner: "Fuego-Tools", + repo: "java-test-project", }) ); }); - test('should throw if no repo found', async () => { + test("should throw if no repo found", async () => { mockRead(` { await expect(hooks.getRepository.promise()).rejects.toBeInstanceOf(Error); }); - test('should throw if cannot find github URL', async () => { + test("should throw if cannot find github URL", async () => { mockRead(` { await expect(hooks.getRepository.promise()).rejects.toBeInstanceOf(Error); }); - test('should throw if cannot parse github URL', async () => { + test("should throw if cannot parse github URL", async () => { mockRead(` { }); }); - describe('getPreviousVersion', () => { - test('should get previous version from pom.xml', async () => { + describe("getPreviousVersion", () => { + test("should get previous version from pom.xml", async () => { mockRead(` { `); - expect(await hooks.getPreviousVersion.promise()).toBe('v1.0.0'); + expect(await hooks.getPreviousVersion.promise()).toBe("v1.0.0"); }); - test('should throw when no version in pom.xml', async () => { - mockRead(''); + test("should throw when no version in pom.xml", async () => { + mockRead(""); await expect(hooks.getPreviousVersion.promise()).rejects.toBeInstanceOf( Error ); }); }); - describe('version', () => { - test('should version release - not versioned because of prepatched snapshot', async () => { + describe("version", () => { + test("should version release - not versioned because of prepatched snapshot", async () => { mockRead(` { `); const spy = jest.fn(); - jest.spyOn(Auto, 'execPromise').mockImplementation(spy); + jest.spyOn(Auto, "execPromise").mockImplementation(spy); await hooks.version.promise(Auto.SEMVER.patch); const call = spy.mock.calls[1][1]; - expect(call).toContain('-DreleaseVersion=1.0.0'); - expect(call).toContain('-Dtag=v1.0.0'); + expect(call).toContain("-DreleaseVersion=1.0.0"); + expect(call).toContain("-Dtag=v1.0.0"); }); - test('should error when failing to increment previous version', async () => { + test("should error when failing to increment previous version", async () => { mockRead(` { }); }); - describe('publish', () => { - test('should publish release', async () => { + describe("publish", () => { + test("should publish release", async () => { const spy = jest.fn(); - jest.spyOn(Auto, 'execPromise').mockImplementation(spy); + jest.spyOn(Auto, "execPromise").mockImplementation(spy); await hooks.publish.promise(Auto.SEMVER.patch); - expect(spy.mock.calls[1][1]).toContain('release:perform'); + expect(spy.mock.calls[1][1]).toContain("release:perform"); }); }); }); diff --git a/plugins/maven/src/index.ts b/plugins/maven/src/index.ts index 9fd7a6818..f50df07a9 100644 --- a/plugins/maven/src/index.ts +++ b/plugins/maven/src/index.ts @@ -1,10 +1,10 @@ -import { Auto, execPromise, IPlugin } from '@auto-it/core'; -import parseGitHubUrl from 'parse-github-url'; -import path from 'path'; -import { promisify } from 'util'; +import { Auto, execPromise, IPlugin } from "@auto-it/core"; +import parseGitHubUrl from "parse-github-url"; +import path from "path"; +import { promisify } from "util"; -import { parse } from 'pom-parser'; -import { inc, ReleaseType } from 'semver'; +import { parse } from "pom-parser"; +import { inc, ReleaseType } from "semver"; /** Ensure a value is an array */ const arrayify = (arg: T | T[]): T[] => (Array.isArray(arg) ? arg : [arg]); @@ -12,7 +12,7 @@ const parsePom = promisify(parse); /** Get the maven pom.xml for a project */ const getPom = async () => - parsePom({ filePath: path.join(process.cwd(), 'pom.xml') }); + parsePom({ filePath: path.join(process.cwd(), "pom.xml") }); /** Get the previous version from the pom.xml */ async function getPreviousVersion(auto: Auto): Promise { @@ -20,27 +20,27 @@ async function getPreviousVersion(auto: Auto): Promise { const previousVersion = pom.pomObject?.project.version; if (!previousVersion) { - throw new Error('Cannot read version from the pom.xml.'); + throw new Error("Cannot read version from the pom.xml."); } auto.logger.verbose.info( - 'Maven: Got previous version from pom.xml', + "Maven: Got previous version from pom.xml", previousVersion ); - return previousVersion.replace('-SNAPSHOT', ''); + return previousVersion.replace("-SNAPSHOT", ""); } /** Deploy project to maven */ export default class MavenPlugin implements IPlugin { /** The name of the plugin */ - name = 'maven'; + name = "maven"; /** Tap into auto plugin points. */ apply(auto: Auto) { - auto.hooks.onCreateLogParse.tap(this.name, logParse => { - logParse.hooks.omitCommit.tap(this.name, commit => { - if (commit.subject.includes('[maven-release-plugin]')) { + auto.hooks.onCreateLogParse.tap(this.name, (logParse) => { + logParse.hooks.omitCommit.tap(this.name, (commit) => { + if (commit.subject.includes("[maven-release-plugin]")) { return true; } }); @@ -51,36 +51,36 @@ export default class MavenPlugin implements IPlugin { if (!MAVEN_PASSWORD && !MAVEN_SETTINGS) { auto.logger.log.warn( - 'No password detected in the environment. You may need this to publish.' + "No password detected in the environment. You may need this to publish." ); } if (!MAVEN_USERNAME && !MAVEN_SETTINGS) { auto.logger.log.warn( - 'No username detected in the environment. You may need this to publish.' + "No username detected in the environment. You may need this to publish." ); } }); auto.hooks.getRepository.tapPromise(this.name, async () => { - auto.logger.verbose.info('Maven: getting repo information from pom.xml'); + auto.logger.verbose.info("Maven: getting repo information from pom.xml"); const pom = await getPom(); const { scm } = pom.pomObject.project; if (!scm) { throw new Error( - 'Could not find scm settings in pom.xml. Make sure you set one up that points to your project on GitHub or set owner+repo in your .autorc' + "Could not find scm settings in pom.xml. Make sure you set one up that points to your project on GitHub or set owner+repo in your .autorc" ); } - const github = arrayify(pom.pomObject.project.scm).find(remote => - Boolean(remote.url.includes('github')) + const github = arrayify(pom.pomObject.project.scm).find((remote) => + Boolean(remote.url.includes("github")) ); if (!github) { throw new Error( - 'Could not find GitHub scm settings in pom.xml. Make sure you set one up that points to your project on GitHub or set owner+repo in your .autorc' + "Could not find GitHub scm settings in pom.xml. Make sure you set one up that points to your project on GitHub or set owner+repo in your .autorc" ); } @@ -88,18 +88,18 @@ export default class MavenPlugin implements IPlugin { if (!repoInfo || !repoInfo.owner || !repoInfo.name) { throw new Error( - 'Cannot read owner and project name from GitHub URL in pom.xml' + "Cannot read owner and project name from GitHub URL in pom.xml" ); } return { owner: repoInfo.owner, - repo: repoInfo.name + repo: repoInfo.name, }; }); auto.hooks.getAuthor.tapPromise(this.name, async () => { - auto.logger.verbose.info('Maven: Getting repo information from pom.xml'); + auto.logger.verbose.info("Maven: Getting repo information from pom.xml"); const pom = await getPom(); const developers = pom.pomObject.project.developers?.developer; @@ -107,7 +107,7 @@ export default class MavenPlugin implements IPlugin { if (!developer || !developer.name || !developer.email) { throw new Error( - 'Cannot read author from the pom.xml. Please include at least 1 developer in the developers section.' + "Cannot read author from the pom.xml. Please include at least 1 developer in the developers section." ); } @@ -118,13 +118,13 @@ export default class MavenPlugin implements IPlugin { auto.prefixRelease(await getPreviousVersion(auto)) ); - auto.hooks.version.tapPromise(this.name, async version => { + auto.hooks.version.tapPromise(this.name, async (version) => { const previousVersion = await getPreviousVersion(auto); const newVersion = // After release we bump the version by a patch and add -SNAPSHOT // Given that we do not need to increment when versioning, since // it has already been done - version === 'patch' + version === "patch" ? previousVersion : inc(previousVersion, version as ReleaseType); @@ -134,47 +134,47 @@ export default class MavenPlugin implements IPlugin { ); } - await execPromise('mvn', ['clean']); - await execPromise('mvn', [ - '-B', - 'release:prepare', + await execPromise("mvn", ["clean"]); + await execPromise("mvn", [ + "-B", + "release:prepare", `-Dtag=${auto.prefixRelease(newVersion)}`, `-DreleaseVersion=${newVersion}`, - '-DpushChanges=false' + "-DpushChanges=false", ]); - await execPromise('git', ['checkout', '-b', 'dev-snapshot']); - await execPromise('git', ['checkout', 'master']); - await execPromise('git', ['reset', '--hard', 'HEAD~1']); + await execPromise("git", ["checkout", "-b", "dev-snapshot"]); + await execPromise("git", ["checkout", "master"]); + await execPromise("git", ["reset", "--hard", "HEAD~1"]); }); auto.hooks.publish.tapPromise(this.name, async () => { const { MAVEN_PASSWORD, MAVEN_USERNAME, MAVEN_SETTINGS } = process.env; - auto.logger.log.await('Performing maven release...'); + auto.logger.log.await("Performing maven release..."); - await execPromise('git', [ - 'push', - '--follow-tags', - '--set-upstream', + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", auto.remote, - auto.baseBranch + auto.baseBranch, ]); - await execPromise('mvn', [ + await execPromise("mvn", [ MAVEN_PASSWORD && `-Dpassword=${MAVEN_PASSWORD}`, MAVEN_USERNAME && `-Dusername=${MAVEN_USERNAME}`, MAVEN_SETTINGS && `-s=${MAVEN_SETTINGS}`, - 'release:perform' + "release:perform", ]); - auto.logger.log.success('Published code to maven!'); + auto.logger.log.success("Published code to maven!"); }); auto.hooks.afterShipIt.tapPromise(this.name, async () => { // prepare for next development iteration - await execPromise('git', ['reset', '--hard', 'dev-snapshot']); - await execPromise('git', ['branch', '-d', 'dev-snapshot']); - await execPromise('git', ['push', auto.remote, auto.baseBranch]); + await execPromise("git", ["reset", "--hard", "dev-snapshot"]); + await execPromise("git", ["branch", "-d", "dev-snapshot"]); + await execPromise("git", ["push", auto.remote, auto.baseBranch]); }); } } diff --git a/plugins/npm/__tests__/monorepo-log.test.ts b/plugins/npm/__tests__/monorepo-log.test.ts index 351b32a82..cfeacb7a1 100644 --- a/plugins/npm/__tests__/monorepo-log.test.ts +++ b/plugins/npm/__tests__/monorepo-log.test.ts @@ -1,100 +1,100 @@ -import * as Auto from '@auto-it/core'; -import makeCommitFromMsg from '@auto-it/core/dist/__tests__/make-commit-from-msg'; -import Changelog from '@auto-it/core/dist/changelog'; -import LogParse from '@auto-it/core/dist/log-parse'; -import { defaultLabels } from '@auto-it/core/dist/release'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; -import NpmPlugin from '../src'; +import * as Auto from "@auto-it/core"; +import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; +import Changelog from "@auto-it/core/dist/changelog"; +import LogParse from "@auto-it/core/dist/log-parse"; +import { defaultLabels } from "@auto-it/core/dist/release"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import NpmPlugin from "../src"; const exec = jest.fn(); const getLernaPackages = jest.fn(); const readFileSync = jest.fn(); -jest.spyOn(Auto, 'getLernaPackages').mockImplementation(getLernaPackages); -jest.spyOn(Auto, 'execPromise').mockImplementation(exec); -jest.mock('fs', () => ({ +jest.spyOn(Auto, "getLernaPackages").mockImplementation(getLernaPackages); +jest.spyOn(Auto, "execPromise").mockImplementation(exec); +jest.mock("fs", () => ({ // @ts-ignore existsSync: jest.fn().mockReturnValue(true), // @ts-ignore readFile: jest.fn(), // @ts-ignore readFileSync: () => readFileSync(), - ReadStream: function() {}, - WriteStream: function() {}, + ReadStream: function () {}, + WriteStream: function () {}, // @ts-ignore closeSync: () => undefined, // @ts-ignore - writeFile: jest.fn() + writeFile: jest.fn(), })); const logParse = new LogParse(); const commitsPromise = logParse.normalizeCommits([ - makeCommitFromMsg('[PLAYA-5052] - Some Feature (#12345)', { - labels: ['major'], - files: ['packages/@foobar/release/package.json'] + makeCommitFromMsg("[PLAYA-5052] - Some Feature (#12345)", { + labels: ["major"], + files: ["packages/@foobar/release/package.json"], }), - makeCommitFromMsg('[PLAYA-5052] - Some Feature - Revert (#12345)', { - labels: ['major'] + makeCommitFromMsg("[PLAYA-5052] - Some Feature - Revert (#12345)", { + labels: ["major"], }), - makeCommitFromMsg('woot (#12343)', { - labels: ['major'] + makeCommitFromMsg("woot (#12343)", { + labels: ["major"], + }), + makeCommitFromMsg("Another Feature (#1234)", { + labels: ["internal"], }), - makeCommitFromMsg('Another Feature (#1234)', { - labels: ['internal'] - }) ]); -test('should create sections for packages', async () => { +test("should create sections for packages", async () => { let changed = 0; getLernaPackages.mockImplementation(async () => Promise.resolve([ { - path: 'packages/@foobar/release', - name: '@foobar/release', - version: '1.0.0' + path: "packages/@foobar/release", + name: "@foobar/release", + version: "1.0.0", }, { - path: 'packages/@foobar/party', - name: '@foobar/party', - version: '1.0.0' - } + path: "packages/@foobar/party", + name: "@foobar/party", + version: "1.0.0", + }, ]) ); exec.mockImplementation(async () => { changed++; if (changed === 3) { - return Promise.resolve(''); + return Promise.resolve(""); } if (changed === 4) { - return Promise.resolve('packages/@foobar/release/README.md'); + return Promise.resolve("packages/@foobar/release/README.md"); } return Promise.resolve( - 'packages/@foobar/release/README.md\npackages/@foobar/party/package.jso' + "packages/@foobar/release/README.md\npackages/@foobar/party/package.jso" ); }); - readFileSync.mockReturnValue('{}'); + readFileSync.mockReturnValue("{}"); const plugin = new NpmPlugin(); const hooks = makeHooks(); const changelog = new Changelog(dummyLog(), { - owner: 'andrew', - repo: 'test', - baseUrl: 'https://github.custom.com/', + owner: "andrew", + repo: "test", + baseUrl: "https://github.custom.com/", labels: defaultLabels, - baseBranch: 'master', - prereleaseBranches: ['next'] + baseBranch: "master", + prereleaseBranches: ["next"], }); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - logger: dummyLog() + logger: dummyLog(), } as Auto.Auto); hooks.onCreateChangelog.call(changelog, Auto.SEMVER.patch); changelog.loadDefaultHooks(); @@ -103,36 +103,36 @@ test('should create sections for packages', async () => { expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); -test('should add versions for independent packages', async () => { +test("should add versions for independent packages", async () => { let changed = 0; getLernaPackages.mockImplementation(async () => Promise.resolve([ { - path: 'packages/@foobar/release', - name: '@foobar/release', - version: '1.0.0' + path: "packages/@foobar/release", + name: "@foobar/release", + version: "1.0.0", }, { - path: 'packages/@foobar/party', - name: '@foobar/party', - version: '1.0.2' - } + path: "packages/@foobar/party", + name: "@foobar/party", + version: "1.0.2", + }, ]) ); exec.mockImplementation(async () => { changed++; if (changed === 3) { - return Promise.resolve(''); + return Promise.resolve(""); } if (changed === 4) { - return Promise.resolve('packages/@foobar/release/README.md'); + return Promise.resolve("packages/@foobar/release/README.md"); } return Promise.resolve( - 'packages/@foobar/release/README.md\npackages/@foobar/party/package.jso' + "packages/@foobar/release/README.md\npackages/@foobar/party/package.jso" ); }); @@ -141,18 +141,18 @@ test('should add versions for independent packages', async () => { const plugin = new NpmPlugin(); const hooks = makeHooks(); const changelog = new Changelog(dummyLog(), { - owner: 'andrew', - repo: 'test', - baseUrl: 'https://github.custom.com/', + owner: "andrew", + repo: "test", + baseUrl: "https://github.custom.com/", labels: defaultLabels, - baseBranch: 'master', - prereleaseBranches: ['next'] + baseBranch: "master", + prereleaseBranches: ["next"], }); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - logger: dummyLog() + logger: dummyLog(), } as Auto.Auto); hooks.onCreateChangelog.call(changelog, Auto.SEMVER.patch); changelog.loadDefaultHooks(); @@ -161,27 +161,27 @@ test('should add versions for independent packages', async () => { expect(await changelog.generateReleaseNotes(commits)).toMatchSnapshot(); }); -test('should create extra change logs for sub-packages', async () => { +test("should create extra change logs for sub-packages", async () => { readFileSync.mockReturnValue('{ "version": "independent" }'); getLernaPackages.mockImplementation(async () => Promise.resolve([ { - path: 'packages/@foobar/release', - name: '@foobar/release', - version: '1.0.0' + path: "packages/@foobar/release", + name: "@foobar/release", + version: "1.0.0", }, { - path: 'packages/@foobar/party', - name: '@foobar/party', - version: '1.0.0' - } + path: "packages/@foobar/party", + name: "@foobar/party", + version: "1.0.0", + }, ]) ); exec.mockImplementation(async () => Promise.resolve( - 'packages/@foobar/release/README.md\npackages/@foobar/party/package.json' + "packages/@foobar/release/README.md\npackages/@foobar/party/package.json" ) ); @@ -190,36 +190,36 @@ test('should create extra change logs for sub-packages', async () => { const update = jest.fn(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, logger: dummyLog(), release: { updateChangelogFile: update, makeChangelog: () => { const t = new Changelog(dummyLog(), { - owner: 'andrew', - repo: 'test', - baseUrl: 'https://github.custom.com/', + owner: "andrew", + repo: "test", + baseUrl: "https://github.custom.com/", labels: defaultLabels, - baseBranch: 'master', - prereleaseBranches: ['next'] + baseBranch: "master", + prereleaseBranches: ["next"], }); - t.hooks.renderChangelogTitle.tap('test', label => label); + t.hooks.renderChangelogTitle.tap("test", (label) => label); return t; - } - } as any + }, + } as any, } as Auto.Auto); await hooks.beforeCommitChangelog.promise({ bump: Auto.SEMVER.patch, commits: await commitsPromise, - currentVersion: '1.0.0', - lastRelease: '0.1.0', - releaseNotes: '' + currentVersion: "1.0.0", + lastRelease: "0.1.0", + releaseNotes: "", }); expect(update).toHaveBeenCalledWith( - 'v1.0.1', - 'major\n- [PLAYA-5052] - Some Feature [#12345](https://github.custom.com/pull/12345)', - 'packages/@foobar/release/CHANGELOG.md' + "v1.0.1", + "major\n- [PLAYA-5052] - Some Feature [#12345](https://github.custom.com/pull/12345)", + "packages/@foobar/release/CHANGELOG.md" ); }); diff --git a/plugins/npm/__tests__/npm.test.ts b/plugins/npm/__tests__/npm.test.ts index ae106e08a..275bc956d 100644 --- a/plugins/npm/__tests__/npm.test.ts +++ b/plugins/npm/__tests__/npm.test.ts @@ -1,13 +1,13 @@ -import * as Auto from '@auto-it/core'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; +import * as Auto from "@auto-it/core"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; import NPMPlugin, { changedPackages, getMonorepoPackage, greaterRelease, - INpmConfig -} from '../src'; -import { IExtendedCommit } from '@auto-it/core/dist/log-parse'; + INpmConfig, +} from "../src"; +import { IExtendedCommit } from "@auto-it/core/dist/log-parse"; const exec = jest.fn(); const getLernaPackages = jest.fn(); @@ -16,17 +16,17 @@ const existsSync = jest.fn(); const readFileSync = jest.fn(); const writeSpy = jest.fn(); -jest.spyOn(Auto, 'getCurrentBranch').mockReturnValue('master'); +jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("master"); -let readResult = '{}'; -readFileSync.mockReturnValue('{}'); +let readResult = "{}"; +readFileSync.mockReturnValue("{}"); // @ts-ignore -jest.spyOn(Auto, 'execPromise').mockImplementation(exec); -jest.spyOn(Auto, 'getLernaPackages').mockImplementation(getLernaPackages); -jest.mock('env-ci', () => () => ({ isCi: false })); -jest.mock('get-monorepo-packages', () => () => monorepoPackages()); -jest.mock('fs', () => ({ +jest.spyOn(Auto, "execPromise").mockImplementation(exec); +jest.spyOn(Auto, "getLernaPackages").mockImplementation(getLernaPackages); +jest.mock("env-ci", () => () => ({ isCi: false })); +jest.mock("get-monorepo-packages", () => () => monorepoPackages()); +jest.mock("fs", () => ({ // @ts-ignore existsSync: (...args) => existsSync(...args), // @ts-ignore @@ -35,138 +35,138 @@ jest.mock('fs', () => ({ }, // @ts-ignore readFileSync: (...args) => readFileSync(...args), - ReadStream: function() {}, - WriteStream: function() {}, + ReadStream: function () {}, + WriteStream: function () {}, // @ts-ignore closeSync: () => undefined, // @ts-ignore writeFile: (file, data, cb) => { cb(undefined, writeSpy(file, data)); - } + }, })); const monorepoPackagesResult = [ - { path: 'packages/a', name: '@packages/a', package: { version: '0.1.1' } }, - { path: 'packages/b', name: '@packages/b', package: {} }, - { path: 'packages/c', name: '@packages/c', package: { version: '0.1.2' } }, - { path: 'packages/d', name: '@packages/d', package: { version: '0.1.1' } } + { path: "packages/a", name: "@packages/a", package: { version: "0.1.1" } }, + { path: "packages/b", name: "@packages/b", package: {} }, + { path: "packages/c", name: "@packages/c", package: { version: "0.1.2" } }, + { path: "packages/d", name: "@packages/d", package: { version: "0.1.1" } }, ]; -describe('changedPackages ', () => { - test('should return nothing without a package directory', async () => { +describe("changedPackages ", () => { + test("should return nothing without a package directory", async () => { exec.mockReturnValueOnce(`packages/README.md\npackage.json`); expect( await changedPackages({ - sha: 'sha', + sha: "sha", packages: [], lernaJson: {}, - logger: dummyLog() + logger: dummyLog(), }) ).toStrictEqual([]); }); - test('should match files in package directory', async () => { + test("should match files in package directory", async () => { exec.mockReturnValueOnce( `packages/foo/README.md\npackages/bar/package.json` ); expect( await changedPackages({ - sha: 'sha', + sha: "sha", packages: [ { - name: 'foo', - path: 'packages/foo', - version: '1.0.0' + name: "foo", + path: "packages/foo", + version: "1.0.0", }, { - name: 'bar', - path: 'packages/bar', - version: '1.0.0' - } + name: "bar", + path: "packages/bar", + version: "1.0.0", + }, ], lernaJson: {}, - logger: dummyLog() + logger: dummyLog(), }) - ).toStrictEqual(['foo', 'bar']); + ).toStrictEqual(["foo", "bar"]); }); - test('should match files in package directory with @scope/ names', async () => { + test("should match files in package directory with @scope/ names", async () => { exec.mockReturnValueOnce( `packages/@scope/foo/README.md\npackages/@scope/bar/package.json` ); expect( await changedPackages({ - sha: 'sha', + sha: "sha", packages: [ { - name: '@scope/foo', - path: 'packages/@scope/foo', - version: '1.0.0' + name: "@scope/foo", + path: "packages/@scope/foo", + version: "1.0.0", }, { - name: '@scope/bar', - path: 'packages/@scope/bar', - version: '1.0.0' - } + name: "@scope/bar", + path: "packages/@scope/bar", + version: "1.0.0", + }, ], lernaJson: {}, - logger: dummyLog() + logger: dummyLog(), }) - ).toStrictEqual(['@scope/foo', '@scope/bar']); + ).toStrictEqual(["@scope/foo", "@scope/bar"]); }); }); -describe('getMonorepoPackage', () => { - test('should default to 0.0.0', () => { +describe("getMonorepoPackage", () => { + test("should default to 0.0.0", () => { monorepoPackages.mockReturnValueOnce([]); expect(getMonorepoPackage()).toStrictEqual({}); }); - test('should find greatest package version', () => { + test("should find greatest package version", () => { monorepoPackages.mockReturnValueOnce(monorepoPackagesResult); - expect(getMonorepoPackage()).toStrictEqual({ version: '0.1.2' }); + expect(getMonorepoPackage()).toStrictEqual({ version: "0.1.2" }); }); }); const prefixRelease = (str: string) => str; -describe('greaterRelease', () => { - test('should default to packageVersion if not published', async () => { +describe("greaterRelease", () => { + test("should default to packageVersion if not published", async () => { exec.mockImplementationOnce(() => { - throw new Error('could not find name'); + throw new Error("could not find name"); }); expect( - await greaterRelease(prefixRelease, 'test-package-name', '1.0.0') - ).toBe('1.0.0'); + await greaterRelease(prefixRelease, "test-package-name", "1.0.0") + ).toBe("1.0.0"); }); - test('should default to packageVersion if greatest', async () => { - exec.mockReturnValueOnce('0.5.0'); + test("should default to packageVersion if greatest", async () => { + exec.mockReturnValueOnce("0.5.0"); expect( - await greaterRelease(prefixRelease, 'test-package-name', '1.0.0') - ).toBe('1.0.0'); + await greaterRelease(prefixRelease, "test-package-name", "1.0.0") + ).toBe("1.0.0"); }); - test('should default to publishedVersion if greatest', async () => { - exec.mockReturnValueOnce('1.0.1'); + test("should default to publishedVersion if greatest", async () => { + exec.mockReturnValueOnce("1.0.1"); expect( - await greaterRelease(prefixRelease, 'test-package-name', '1.0.0') - ).toBe('1.0.1'); + await greaterRelease(prefixRelease, "test-package-name", "1.0.0") + ).toBe("1.0.1"); }); }); -describe('getAuthor', () => { - test('should do nothing if no author', async () => { +describe("getAuthor", () => { + test("should do nothing if no author", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); readResult = ` @@ -177,16 +177,16 @@ describe('getAuthor', () => { expect(await hooks.getAuthor.promise()).toBeUndefined(); }); - test('should get author object from package.json', async () => { + test("should get author object from package.json", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); readResult = ` @@ -199,21 +199,21 @@ describe('getAuthor', () => { } `; expect(await hooks.getAuthor.promise()).toStrictEqual({ - name: 'Adam', - email: 'adam@email.com' + name: "Adam", + email: "adam@email.com", }); }); - test('should get parse author as string', async () => { + test("should get parse author as string", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); readResult = ` @@ -223,23 +223,23 @@ describe('getAuthor', () => { } `; expect(await hooks.getAuthor.promise()).toStrictEqual({ - name: 'Adam', - email: 'adam@email.com' + name: "Adam", + email: "adam@email.com", }); }); }); -describe('getRepository', () => { - test('should get package config', async () => { +describe("getRepository", () => { + test("should get package config", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); readResult = ` @@ -253,26 +253,26 @@ describe('getRepository', () => { } `; expect(await hooks.getRepository.promise()).toStrictEqual({ - owner: 'black-panther', - repo: 'operation-foo' + owner: "black-panther", + repo: "operation-foo", }); }); }); -describe('getPreviousVersion', () => { - test('should get use 0 if not version in package.json', async () => { +describe("getPreviousVersion", () => { + test("should get use 0 if not version in package.json", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); existsSync.mockReturnValueOnce(false); existsSync.mockReturnValueOnce(true); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - prefixRelease: str => str + prefixRelease: (str) => str, } as Auto.Auto); readResult = ` @@ -280,22 +280,22 @@ describe('getPreviousVersion', () => { "name": "test" } `; - expect(await hooks.getPreviousVersion.promise()).toBe('0.0.0'); + expect(await hooks.getPreviousVersion.promise()).toBe("0.0.0"); }); - test('should get version from the package.json', async () => { + test("should get version from the package.json", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); existsSync.mockReturnValueOnce(false); existsSync.mockReturnValueOnce(true); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - prefixRelease: str => str + prefixRelease: (str) => str, } as Auto.Auto); readResult = ` @@ -304,22 +304,22 @@ describe('getPreviousVersion', () => { "version": "1.0.0" } `; - expect(await hooks.getPreviousVersion.promise()).toBe('1.0.0'); + expect(await hooks.getPreviousVersion.promise()).toBe("1.0.0"); }); - test('should get version from the lerna.json', async () => { + test("should get version from the lerna.json", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); existsSync.mockReturnValueOnce(true); monorepoPackages.mockReturnValueOnce([]); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - prefixRelease: str => str + prefixRelease: (str) => str, } as Auto.Auto); readFileSync.mockReturnValueOnce(` @@ -328,10 +328,10 @@ describe('getPreviousVersion', () => { "version": "2.0.0" } `); - expect(await hooks.getPreviousVersion.promise()).toBe('2.0.0'); + expect(await hooks.getPreviousVersion.promise()).toBe("2.0.0"); }); - test('should get version greatest published monorepo package', async () => { + test("should get version greatest published monorepo package", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); @@ -345,50 +345,50 @@ describe('getPreviousVersion', () => { } `); // published version of test package - exec.mockReturnValueOnce('0.1.2'); + exec.mockReturnValueOnce("0.1.2"); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - prefixRelease: str => str + prefixRelease: (str) => str, } as Auto.Auto); - expect(await hooks.getPreviousVersion.promise()).toBe('0.1.2'); + expect(await hooks.getPreviousVersion.promise()).toBe("0.1.2"); }); }); -test('should use string semver if no published package', async () => { +test("should use string semver if no published package", async () => { const plugin = new NPMPlugin({ setRcToken: false }); expect(plugin).toStrictEqual( expect.objectContaining({ forcePublish: true, - name: 'npm', - setRcToken: false + name: "npm", + setRcToken: false, }) ); }); -describe('publish', () => { +describe("publish", () => { beforeEach(() => { exec.mockClear(); }); - test('should use silly logging in verbose mode', async () => { + test("should use silly logging in verbose mode", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); const logger = dummyLog(); - logger.logLevel = 'veryVerbose'; + logger.logLevel = "veryVerbose"; plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger + remote: "origin", + baseBranch: "master", + logger, } as Auto.Auto); readResult = ` @@ -398,27 +398,27 @@ describe('publish', () => { `; await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('npm', [ - 'version', + expect(exec).toHaveBeenCalledWith("npm", [ + "version", Auto.SEMVER.patch, - '--no-commit-hooks', - '-m', + "--no-commit-hooks", + "-m", '"Bump version to: %s [skip ci]"', - '--loglevel', - 'silly' + "--loglevel", + "silly", ]); }); - test('should use string semver if no published package', async () => { + test("should use string semver if no published package", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); readResult = ` @@ -428,25 +428,25 @@ describe('publish', () => { `; await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('npm', [ - 'version', + expect(exec).toHaveBeenCalledWith("npm", [ + "version", Auto.SEMVER.patch, - '--no-commit-hooks', - '-m', - '"Bump version to: %s [skip ci]"' + "--no-commit-hooks", + "-m", + '"Bump version to: %s [skip ci]"', ]); }); - test('monorepo - should use start version at 0 if no published package', async () => { + test("monorepo - should use start version at 0 if no published package", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); existsSync.mockReturnValueOnce(true); @@ -459,30 +459,30 @@ describe('publish', () => { `; await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('npx', [ - 'lerna', - 'version', - 'patch', - '--force-publish', - '--no-commit-hooks', - '--yes', - '--no-push', - '-m', + expect(exec).toHaveBeenCalledWith("npx", [ + "lerna", + "version", + "patch", + "--force-publish", + "--no-commit-hooks", + "--yes", + "--no-push", + "-m", '"Bump version to: %s [skip ci]"', - false + false, ]); }); - test('monorepo - should be able to not force publish all packages', async () => { + test("monorepo - should be able to not force publish all packages", async () => { const plugin = new NPMPlugin({ forcePublish: false }); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); existsSync.mockReturnValueOnce(true); @@ -495,30 +495,30 @@ describe('publish', () => { `; await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('npx', [ - 'lerna', - 'version', - 'patch', + expect(exec).toHaveBeenCalledWith("npx", [ + "lerna", + "version", + "patch", false, - '--no-commit-hooks', - '--yes', - '--no-push', - '-m', + "--no-commit-hooks", + "--yes", + "--no-push", + "-m", '"Bump version to: %s [skip ci]"', - false + false, ]); }); - test('monorepo - should be able to publish exact packages', async () => { + test("monorepo - should be able to publish exact packages", async () => { const plugin = new NPMPlugin({ exact: true }); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); existsSync.mockReturnValueOnce(true); @@ -531,56 +531,56 @@ describe('publish', () => { `; await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('npx', [ - 'lerna', - 'version', - 'patch', - '--force-publish', - '--no-commit-hooks', - '--yes', - '--no-push', - '-m', + expect(exec).toHaveBeenCalledWith("npx", [ + "lerna", + "version", + "patch", + "--force-publish", + "--no-commit-hooks", + "--yes", + "--no-push", + "-m", '"Bump version to: %s [skip ci]"', - '--exact' + "--exact", ]); }); - test('monorepo - should publish', async () => { + test("monorepo - should publish", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); existsSync.mockReturnValueOnce(true); await hooks.publish.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('npx', [ - 'lerna', - 'publish', - '--yes', - 'from-package' + expect(exec).toHaveBeenCalledWith("npx", [ + "lerna", + "publish", + "--yes", + "from-package", ]); }); - test('should bump published version', async () => { + test("should bump published version", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); - exec.mockReturnValueOnce('1.0.0'); + exec.mockReturnValueOnce("1.0.0"); readResult = ` { @@ -590,30 +590,30 @@ describe('publish', () => { `; await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('npm', [ - 'version', - '1.0.1', - '--no-commit-hooks', - '-m', - '"Bump version to: %s [skip ci]"' + expect(exec).toHaveBeenCalledWith("npm", [ + "version", + "1.0.1", + "--no-commit-hooks", + "-m", + '"Bump version to: %s [skip ci]"', ]); }); - test('monorepo - should bump published version', async () => { + test("monorepo - should bump published version", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); existsSync.mockReturnValueOnce(true); monorepoPackages.mockReturnValueOnce(monorepoPackagesResult); - exec.mockReturnValueOnce('1.0.0'); + exec.mockReturnValueOnce("1.0.0"); readResult = ` { @@ -622,30 +622,30 @@ describe('publish', () => { `; await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenNthCalledWith(2, 'npx', [ - 'lerna', - 'version', - '1.0.1', - '--force-publish', - '--no-commit-hooks', - '--yes', - '--no-push', - '-m', + expect(exec).toHaveBeenNthCalledWith(2, "npx", [ + "lerna", + "version", + "1.0.1", + "--force-publish", + "--no-commit-hooks", + "--yes", + "--no-push", + "-m", '"Bump version to: %s [skip ci]"', - false + false, ]); }); - test('should publish private scoped packages to private', async () => { + test("should publish private scoped packages to private", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); readResult = ` @@ -656,30 +656,30 @@ describe('publish', () => { `; await hooks.publish.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenCalledWith('npm', ['publish']); + expect(exec).toHaveBeenCalledWith("npm", ["publish"]); }); }); -describe('canary', () => { +describe("canary", () => { beforeEach(() => { exec.mockClear(); }); - test('use npm for normal package', async () => { + test("use npm for normal package", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply(({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - getCurrentVersion: () => '1.2.3', + getCurrentVersion: () => "1.2.3", git: { - getLatestRelease: () => '1.2.3', - getLatestTagInBranch: () => '1.2.3' - } + getLatestRelease: () => "1.2.3", + getLatestTagInBranch: () => "1.2.3", + }, } as unknown) as Auto.Auto); readResult = ` @@ -688,25 +688,25 @@ describe('canary', () => { } `; - await hooks.canary.promise(Auto.SEMVER.patch, '.123.1'); - expect(exec.mock.calls[0]).toContain('npm'); - expect(exec.mock.calls[0][1]).toContain('1.2.4-canary.123.1.0'); + await hooks.canary.promise(Auto.SEMVER.patch, ".123.1"); + expect(exec.mock.calls[0]).toContain("npm"); + expect(exec.mock.calls[0][1]).toContain("1.2.4-canary.123.1.0"); }); - test('use lerna for monorepo package', async () => { + test("use lerna for monorepo package", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), git: { - getLatestRelease: () => Promise.resolve('1.2.3'), - getLatestTagInBranch: () => '1.2.3' - } + getLatestRelease: () => Promise.resolve("1.2.3"), + getLatestTagInBranch: () => "1.2.3", + }, } as any); existsSync.mockReturnValueOnce(true); @@ -719,38 +719,38 @@ describe('canary', () => { getLernaPackages.mockImplementation(async () => Promise.resolve([ { - path: 'path/to/package', - name: '@foobar/app', - version: '1.2.3-canary.0+abcd' + path: "path/to/package", + name: "@foobar/app", + version: "1.2.3-canary.0+abcd", }, { - path: 'path/to/package', - name: '@foobar/lib', - version: '1.2.3-canary.0+abcd' - } + path: "path/to/package", + name: "@foobar/lib", + version: "1.2.3-canary.0+abcd", + }, ]) ); - const value = await hooks.canary.promise(Auto.SEMVER.patch, ''); - expect(exec.mock.calls[0][1]).toContain('lerna'); + const value = await hooks.canary.promise(Auto.SEMVER.patch, ""); + expect(exec.mock.calls[0][1]).toContain("lerna"); // @ts-ignore - expect(value.newVersion).toBe('1.2.3-canary.0'); + expect(value.newVersion).toBe("1.2.3-canary.0"); }); - test('error when no canary release found', async () => { + test("error when no canary release found", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), git: { - getLatestRelease: () => Promise.resolve('1.2.3'), - getLatestTagInBranch: () => '1.2.3' - } + getLatestRelease: () => Promise.resolve("1.2.3"), + getLatestTagInBranch: () => "1.2.3", + }, } as any); existsSync.mockReturnValueOnce(true); @@ -763,21 +763,21 @@ describe('canary', () => { getLernaPackages.mockImplementation(async () => Promise.resolve([ { - path: 'path/to/package', - name: '@foobar/app', - version: '1.2.3' + path: "path/to/package", + name: "@foobar/app", + version: "1.2.3", }, { - path: 'path/to/package', - name: '@foobar/lib', - version: '1.2.3' - } + path: "path/to/package", + name: "@foobar/lib", + version: "1.2.3", + }, ]) ); - const value = await hooks.canary.promise(Auto.SEMVER.patch, ''); + const value = await hooks.canary.promise(Auto.SEMVER.patch, ""); expect(value).toStrictEqual({ - error: 'No packages were changed. No canary published.' + error: "No packages were changed. No canary published.", }); }); @@ -786,11 +786,11 @@ describe('canary', () => { const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', - logger: dummyLog() + remote: "origin", + baseBranch: "master", + logger: dummyLog(), } as Auto.Auto); existsSync.mockReturnValueOnce(true); readFileSync.mockReturnValue('{ "version": "independent" }'); @@ -804,47 +804,47 @@ describe('canary', () => { getLernaPackages.mockImplementation(async () => Promise.resolve([ { - path: 'path/to/package', - name: '@foobar/app', - version: '1.2.4-canary.0' + path: "path/to/package", + name: "@foobar/app", + version: "1.2.4-canary.0", }, { - path: 'path/to/package', - name: '@foobar/lib', - version: '1.1.0-canary.0' - } + path: "path/to/package", + name: "@foobar/lib", + version: "1.1.0-canary.0", + }, ]) ); await hooks.version.promise(Auto.SEMVER.patch); - expect(exec).toHaveBeenNthCalledWith(1, 'npx', [ - 'lerna', - 'version', - 'patch', + expect(exec).toHaveBeenNthCalledWith(1, "npx", [ + "lerna", + "version", + "patch", false, - '--no-commit-hooks', - '--yes', - '--no-push', - '-m', + "--no-commit-hooks", + "--yes", + "--no-push", + "-m", '"Bump independent versions [skip ci]"', - false + false, ]); }); - test('dont force publish canaries', async () => { + test("dont force publish canaries", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), git: { - getLatestRelease: () => Promise.resolve('@foo/lib:1.1.0'), - getLatestTagInBranch: () => '1.2.3' - } + getLatestRelease: () => Promise.resolve("@foo/lib:1.1.0"), + getLatestTagInBranch: () => "1.2.3", + }, } as any); existsSync.mockReturnValueOnce(true); readFileSync.mockReturnValue('{ "version": "independent" }'); @@ -858,35 +858,35 @@ describe('canary', () => { getLernaPackages.mockImplementation(async () => Promise.resolve([ { - path: 'path/to/package', - name: '@foobar/app', - version: '1.2.4' + path: "path/to/package", + name: "@foobar/app", + version: "1.2.4", }, { - path: 'path/to/package', - name: '@foobar/lib', - version: '1.1.0.canary' - } + path: "path/to/package", + name: "@foobar/lib", + version: "1.1.0.canary", + }, ]) ); - expect(await hooks.canary.promise(Auto.SEMVER.patch, '')).toMatchSnapshot(); + expect(await hooks.canary.promise(Auto.SEMVER.patch, "")).toMatchSnapshot(); }); - test('error when no canary release found - independent', async () => { + test("error when no canary release found - independent", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), git: { - getLatestRelease: () => Promise.resolve('@foo/lib:1.1.0'), - getLatestTagInBranch: () => '1.2.3' - } + getLatestRelease: () => Promise.resolve("@foo/lib:1.1.0"), + getLatestTagInBranch: () => "1.2.3", + }, } as any); existsSync.mockReturnValueOnce(true); readFileSync.mockReturnValue('{ "version": "independent" }'); @@ -900,46 +900,46 @@ describe('canary', () => { getLernaPackages.mockImplementation(async () => Promise.resolve([ { - path: 'path/to/package', - name: '@foobar/app', - version: '1.2.4' + path: "path/to/package", + name: "@foobar/app", + version: "1.2.4", }, { - path: 'path/to/package', - name: '@foobar/lib', - version: '1.1.0' - } + path: "path/to/package", + name: "@foobar/lib", + version: "1.1.0", + }, ]) ); - const value = await hooks.canary.promise(Auto.SEMVER.patch, ''); + const value = await hooks.canary.promise(Auto.SEMVER.patch, ""); expect(value).toStrictEqual({ - error: 'No packages were changed. No canary published.' + error: "No packages were changed. No canary published.", }); }); }); -describe('next', () => { +describe("next", () => { beforeEach(() => { exec.mockClear(); }); - test('works in single package', async () => { + test("works in single package", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); plugin.apply(({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - getCurrentVersion: () => '1.2.3', + getCurrentVersion: () => "1.2.3", prefixRelease: (v: string) => v, git: { - getLatestRelease: () => '1.0.0', - getLastTagNotInBaseBranch: () => '1.2.3' - } + getLatestRelease: () => "1.0.0", + getLastTagNotInBaseBranch: () => "1.2.3", + }, } as unknown) as Auto.Auto); readResult = ` @@ -950,102 +950,102 @@ describe('next', () => { `; expect(await hooks.next.promise([], Auto.SEMVER.patch)).toStrictEqual([ - '1.2.4-next.0' + "1.2.4-next.0", ]); - expect(exec).toHaveBeenCalledWith('npm', [ - 'version', - '1.2.4-next.0', - '--no-git-tag-version' + expect(exec).toHaveBeenCalledWith("npm", [ + "version", + "1.2.4-next.0", + "--no-git-tag-version", ]); - expect(exec).toHaveBeenCalledWith('git', [ - 'tag', - '1.2.4-next.0', - '-m', - '"Update version to 1.2.4-next.0"' + expect(exec).toHaveBeenCalledWith("git", [ + "tag", + "1.2.4-next.0", + "-m", + '"Update version to 1.2.4-next.0"', ]); - expect(exec).toHaveBeenCalledWith('git', ['push', 'origin', '--tags']); - expect(exec).toHaveBeenCalledWith('npm', ['publish', '--tag', 'next']); + expect(exec).toHaveBeenCalledWith("git", ["push", "origin", "--tags"]); + expect(exec).toHaveBeenCalledWith("npm", ["publish", "--tag", "next"]); }); - test('works in monorepo', async () => { + test("works in monorepo", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); // isMonorepo existsSync.mockReturnValueOnce(true); readFileSync.mockReturnValue('{ "version": "1.2.3" }'); - exec.mockReturnValueOnce(''); - exec.mockReturnValueOnce('1.2.4-next.0'); + exec.mockReturnValueOnce(""); + exec.mockReturnValueOnce("1.2.4-next.0"); plugin.apply(({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - getCurrentVersion: () => '1.2.3', + getCurrentVersion: () => "1.2.3", prefixRelease: (v: string) => v, git: { - getLatestRelease: () => '1.0.0', - getLastTagNotInBaseBranch: () => '1.2.3' - } + getLatestRelease: () => "1.0.0", + getLastTagNotInBaseBranch: () => "1.2.3", + }, } as unknown) as Auto.Auto); expect(await hooks.next.promise([], Auto.SEMVER.patch)).toStrictEqual([ - '1.2.4-next.0' + "1.2.4-next.0", ]); expect(exec).toHaveBeenCalledWith( - 'npx', - expect.arrayContaining(['lerna', 'publish', '1.2.4-next.0']) + "npx", + expect.arrayContaining(["lerna", "publish", "1.2.4-next.0"]) ); - expect(exec).toHaveBeenCalledWith('git', ['push', 'origin', '--tags']); + expect(exec).toHaveBeenCalledWith("git", ["push", "origin", "--tags"]); }); - test('works in monorepo - independent', async () => { + test("works in monorepo - independent", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); // isMonorepo existsSync.mockReturnValueOnce(true); readFileSync.mockReturnValue('{ "version": "independent" }'); - exec.mockReturnValueOnce(''); - exec.mockReturnValueOnce('@foo/1@1.0.0-next.0\n@foo/2@2.0.0-next.0'); + exec.mockReturnValueOnce(""); + exec.mockReturnValueOnce("@foo/1@1.0.0-next.0\n@foo/2@2.0.0-next.0"); plugin.apply(({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), prefixRelease: (v: string) => v, git: { - getLatestRelease: () => '@foo/1@0.1.0', - getLastTagNotInBaseBranch: () => '@foo/1@1.0.0-next.0' - } + getLatestRelease: () => "@foo/1@0.1.0", + getLastTagNotInBaseBranch: () => "@foo/1@1.0.0-next.0", + }, } as unknown) as Auto.Auto); expect(await hooks.next.promise([], Auto.SEMVER.patch)).toStrictEqual([ - '@foo/1@1.0.0-next.0', - '@foo/2@2.0.0-next.0' + "@foo/1@1.0.0-next.0", + "@foo/2@2.0.0-next.0", ]); expect(exec).toHaveBeenCalledWith( - 'npx', - expect.arrayContaining(['lerna', 'publish', 'prerelease']) + "npx", + expect.arrayContaining(["lerna", "publish", "prerelease"]) ); - expect(exec).toHaveBeenCalledWith('git', ['push', 'origin', '--tags']); + expect(exec).toHaveBeenCalledWith("git", ["push", "origin", "--tags"]); }); }); -describe('makeRelease', () => { - test('publish release for each package', async () => { +describe("makeRelease", () => { + test("publish release for each package", async () => { const plugin = new NPMPlugin(); const hooks = makeHooks(); // isMonorepo - exec.mockReturnValue('@packages/a\n@packages/b'); + exec.mockReturnValue("@packages/a\n@packages/b"); getLernaPackages.mockReturnValueOnce(monorepoPackagesResult); readFileSync.mockReturnValue( '{ "name": "test", "version": "independent" }' @@ -1053,58 +1053,58 @@ describe('makeRelease', () => { const publish = jest.fn(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - prefixRelease: str => str, + prefixRelease: (str) => str, git: { publish } as any, release: { makeChangelog: () => ({ generateReleaseNotes: (commits: IExtendedCommit[]) => - Promise.resolve(commits.map(c => c.subject).join('\n')) - }) - } as any + Promise.resolve(commits.map((c) => c.subject).join("\n")), + }), + } as any, } as Auto.Auto); await hooks.makeRelease.promise({ - newVersion: '0.1.2', - from: '', + newVersion: "0.1.2", + from: "", isPrerelease: false, - fullReleaseNotes: '', + fullReleaseNotes: "", commits: [ { - subject: 'update package 1', - hash: '123', + subject: "update package 1", + hash: "123", labels: [], - files: ['packages/a/package.json'], - authors: [{ username: 'Jeff', hash: '123' }] + files: ["packages/a/package.json"], + authors: [{ username: "Jeff", hash: "123" }], }, { - subject: 'update package 2', - hash: '124', + subject: "update package 2", + hash: "124", labels: [], - files: ['packages/b/package.json'], - authors: [{ username: 'Andrew', hash: '124' }] - } - ] + files: ["packages/b/package.json"], + authors: [{ username: "Andrew", hash: "124" }], + }, + ], }); expect(publish).toHaveBeenCalledWith( - 'update package 1', - '@packages/a', + "update package 1", + "@packages/a", false ); expect(publish).toHaveBeenCalledWith( - 'update package 2', - '@packages/b', + "update package 2", + "@packages/b", false ); }); }); -describe('beforeCommitChangelog', () => { +describe("beforeCommitChangelog", () => { let updateChangelogFile: jest.Mock; async function subPackageChangelogTest(options: INpmConfig = {}) { @@ -1112,57 +1112,57 @@ describe('beforeCommitChangelog', () => { const hooks = makeHooks(); // isMonorepo - exec.mockReturnValue('@packages/a\n@packages/b'); + exec.mockReturnValue("@packages/a\n@packages/b"); existsSync.mockReturnValueOnce(true); getLernaPackages.mockReturnValueOnce(monorepoPackagesResult); updateChangelogFile = jest.fn(); plugin.apply({ - config: { prereleaseBranches: ['next'] }, + config: { prereleaseBranches: ["next"] }, hooks, - remote: 'origin', - baseBranch: 'master', + remote: "origin", + baseBranch: "master", logger: dummyLog(), - prefixRelease: str => str, + prefixRelease: (str) => str, release: { updateChangelogFile, makeChangelog: () => ({ generateReleaseNotes: (commits: IExtendedCommit[]) => - Promise.resolve(commits.map(c => c.subject).join('\n')) - }) - } as any + Promise.resolve(commits.map((c) => c.subject).join("\n")), + }), + } as any, } as Auto.Auto); await hooks.beforeCommitChangelog.promise({ bump: Auto.SEMVER.patch, - currentVersion: '0.0.1', - lastRelease: '0.0.0', - releaseNotes: '', + currentVersion: "0.0.1", + lastRelease: "0.0.0", + releaseNotes: "", commits: [ { - subject: 'update package 1', - hash: '123', + subject: "update package 1", + hash: "123", labels: [], - files: ['packages/a/package.json'], - authors: [{ username: 'Jeff', hash: '123' }] + files: ["packages/a/package.json"], + authors: [{ username: "Jeff", hash: "123" }], }, { - subject: 'update package 2', - hash: '124', + subject: "update package 2", + hash: "124", labels: [], - files: ['packages/b/package.json'], - authors: [{ username: 'Andrew', hash: '124' }] - } - ] + files: ["packages/b/package.json"], + authors: [{ username: "Andrew", hash: "124" }], + }, + ], }); } - test('should create sub-package changelogs ', async () => { + test("should create sub-package changelogs ", async () => { await subPackageChangelogTest(); expect(updateChangelogFile).toHaveBeenCalled(); }); - test('should not create sub-package changelogs ', async () => { + test("should not create sub-package changelogs ", async () => { await subPackageChangelogTest({ subPackageChangelogs: false }); expect(updateChangelogFile).not.toHaveBeenCalled(); }); diff --git a/plugins/npm/__tests__/package-config.test.ts b/plugins/npm/__tests__/package-config.test.ts index 9c280c418..7ef738042 100644 --- a/plugins/npm/__tests__/package-config.test.ts +++ b/plugins/npm/__tests__/package-config.test.ts @@ -1,50 +1,50 @@ -import packageConfig from '../src/package-config'; -import { loadPackageJson } from '../src/utils'; +import packageConfig from "../src/package-config"; +import { loadPackageJson } from "../src/utils"; const packageJsonSpy = loadPackageJson as jest.Mock; -jest.mock('../src/utils'); +jest.mock("../src/utils"); -test('should return nothing without a repo', async () => { +test("should return nothing without a repo", async () => { packageJsonSpy.mockReturnValueOnce({}); expect(await packageConfig()).toBeUndefined(); }); -test('should return nothing without an owner', async () => { +test("should return nothing without an owner", async () => { packageJsonSpy.mockReturnValueOnce({ - repository: { url: 'fake.com' } + repository: { url: "fake.com" }, }); expect(await packageConfig()).toBeUndefined(); }); -test('should return nothing without an package name', async () => { +test("should return nothing without an package name", async () => { packageJsonSpy.mockReturnValueOnce({ - repository: { url: 'fake.com' } + repository: { url: "fake.com" }, }); expect(await packageConfig()).toBeUndefined(); }); -test('should correctly parse package info', async () => { +test("should correctly parse package info", async () => { packageJsonSpy.mockReturnValueOnce({ - version: '1.0.0', - repository: { url: 'https://github.com/black-panther/operation-foo' } + version: "1.0.0", + repository: { url: "https://github.com/black-panther/operation-foo" }, }); expect(await packageConfig()).toStrictEqual({ - repo: 'operation-foo', - owner: 'black-panther' + repo: "operation-foo", + owner: "black-panther", }); }); -test('should correctly parse package info - string', async () => { +test("should correctly parse package info - string", async () => { packageJsonSpy.mockReturnValueOnce({ - version: '1.0.0', - repository: 'black-panther/operation-foo' + version: "1.0.0", + repository: "black-panther/operation-foo", }); expect(await packageConfig()).toStrictEqual({ - repo: 'operation-foo', - owner: 'black-panther' + repo: "operation-foo", + owner: "black-panther", }); }); diff --git a/plugins/npm/__tests__/set-npm-token.test.ts b/plugins/npm/__tests__/set-npm-token.test.ts index a6b3b22b9..62875b67e 100644 --- a/plugins/npm/__tests__/set-npm-token.test.ts +++ b/plugins/npm/__tests__/set-npm-token.test.ts @@ -1,74 +1,74 @@ /* eslint-disable no-template-curly-in-string */ -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import setNpmToken from '../src/set-npm-token'; -import * as utils from '../src/utils'; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import setNpmToken from "../src/set-npm-token"; +import * as utils from "../src/utils"; const loadPackageJson = utils.loadPackageJson as jest.Mock; const readFile = utils.readFile as jest.Mock; const writeFile = utils.writeFile as jest.Mock; -jest.mock('../src/utils.ts'); -jest.mock('env-ci', () => () => ({ - isCi: true +jest.mock("../src/utils.ts"); +jest.mock("env-ci", () => () => ({ + isCi: true, })); -jest.mock('registry-url', () => (scope?: string) => - scope ? 'foo.registry.com' : 'npm.registry.com' +jest.mock("registry-url", () => (scope?: string) => + scope ? "foo.registry.com" : "npm.registry.com" ); -jest.mock('user-home', () => '/User/name'); +jest.mock("user-home", () => "/User/name"); -describe('set npm token', () => { +describe("set npm token", () => { beforeEach(() => { writeFile.mockClear(); }); - test('should write a new npmrc', async () => { - loadPackageJson.mockReturnValueOnce({ name: 'test' }); + test("should write a new npmrc", async () => { + loadPackageJson.mockReturnValueOnce({ name: "test" }); await setNpmToken(dummyLog()); expect(writeFile).toHaveBeenCalledWith( - '/User/name/.npmrc', - 'npm.registry.com/:_authToken=${NPM_TOKEN}' + "/User/name/.npmrc", + "npm.registry.com/:_authToken=${NPM_TOKEN}" ); }); - test('should write a new npmrc w/o name', async () => { + test("should write a new npmrc w/o name", async () => { loadPackageJson.mockReturnValueOnce({}); await setNpmToken(dummyLog()); expect(writeFile).toHaveBeenCalledWith( - '/User/name/.npmrc', - 'npm.registry.com/:_authToken=${NPM_TOKEN}' + "/User/name/.npmrc", + "npm.registry.com/:_authToken=${NPM_TOKEN}" ); }); - test('should use registry from packageJson', async () => { + test("should use registry from packageJson", async () => { loadPackageJson.mockReturnValueOnce({ - name: 'test', - publishConfig: { registry: 'https://my-registry.com' } + name: "test", + publishConfig: { registry: "https://my-registry.com" }, }); await setNpmToken(dummyLog()); expect(writeFile).toHaveBeenCalledWith( - '/User/name/.npmrc', - '//my-registry.com/:_authToken=${NPM_TOKEN}' + "/User/name/.npmrc", + "//my-registry.com/:_authToken=${NPM_TOKEN}" ); }); - test('should use registry for scoped pacakged', async () => { + test("should use registry for scoped pacakged", async () => { loadPackageJson.mockReturnValueOnce({ - name: '@scope/test' + name: "@scope/test", }); await setNpmToken(dummyLog()); expect(writeFile).toHaveBeenCalledWith( - '/User/name/.npmrc', - 'foo.registry.com/:_authToken=${NPM_TOKEN}' + "/User/name/.npmrc", + "foo.registry.com/:_authToken=${NPM_TOKEN}" ); }); - test('should not edit npmrc if it already has the token', async () => { + test("should not edit npmrc if it already has the token", async () => { loadPackageJson.mockReturnValueOnce({ - name: 'test', - publishConfig: { registry: 'https://my-registry.com' } + name: "test", + publishConfig: { registry: "https://my-registry.com" }, }); - readFile.mockReturnValueOnce('//my-registry.com/:_authToken=${NPM_TOKEN}'); + readFile.mockReturnValueOnce("//my-registry.com/:_authToken=${NPM_TOKEN}"); await setNpmToken(dummyLog()); expect(writeFile).not.toHaveBeenCalled(); diff --git a/plugins/npm/src/index.ts b/plugins/npm/src/index.ts index f6e80286e..0bdd8816d 100644 --- a/plugins/npm/src/index.ts +++ b/plugins/npm/src/index.ts @@ -1,10 +1,10 @@ -import envCi from 'env-ci'; -import * as fs from 'fs'; -import parseAuthor from 'parse-author'; -import path from 'path'; -import { Memoize as memoize } from 'typescript-memoize'; -import { Octokit } from '@octokit/rest'; -import * as t from 'io-ts'; +import envCi from "env-ci"; +import * as fs from "fs"; +import parseAuthor from "parse-author"; +import path from "path"; +import { Memoize as memoize } from "typescript-memoize"; +import { Octokit } from "@octokit/rest"; +import * as t from "io-ts"; import { Auto, @@ -17,25 +17,25 @@ import { IPlugin, InteractiveInit, SEMVER, - validatePluginConfiguration -} from '@auto-it/core'; -import getPackages from 'get-monorepo-packages'; -import { gt, gte, inc, ReleaseType } from 'semver'; + validatePluginConfiguration, +} from "@auto-it/core"; +import getPackages from "get-monorepo-packages"; +import { gt, gte, inc, ReleaseType } from "semver"; -import getConfigFromPackageJson from './package-config'; -import setTokenOnCI from './set-npm-token'; -import { loadPackageJson, writeFile } from './utils'; +import getConfigFromPackageJson from "./package-config"; +import setTokenOnCI from "./set-npm-token"; +import { loadPackageJson, writeFile } from "./utils"; const { isCi } = envCi(); const VERSION_COMMIT_MESSAGE = '"Bump version to: %s [skip ci]"'; /** Check if the project is a monorepo */ -const isMonorepo = () => fs.existsSync('lerna.json'); +const isMonorepo = () => fs.existsSync("lerna.json"); /** Get the last published version for a npm package */ async function getPublishedVersion(name: string) { try { - return await execPromise('npm', ['view', name, 'version']); + return await execPromise("npm", ["view", name, "version"]); } catch (error) {} } @@ -59,7 +59,7 @@ export async function greaterRelease( // The branch (ex: next) is also the --preid const baseVersion = prereleaseBranch && packageVersion.includes(prereleaseBranch) - ? inc(packageVersion, 'patch') || packageVersion + ? inc(packageVersion, "patch") || packageVersion : packageVersion; return gte(baseVersion, publishedPrefixed) @@ -103,19 +103,19 @@ export async function changedPackages({ packages, lernaJson, logger, - version + version, }: ChangedPackagesArgs) { const changed = new Set(); - const changedFiles = await execPromise('git', [ - 'show', - '--first-parent', + const changedFiles = await execPromise("git", [ + "show", + "--first-parent", sha, - '--name-only', - '--pretty=' + "--name-only", + "--pretty=", ]); - changedFiles.split('\n').forEach(filePath => { - const monorepoPackage = packages.find(subPackage => + changedFiles.split("\n").forEach((filePath) => { + const monorepoPackage = packages.find((subPackage) => inFolder(subPackage.path, filePath) ); @@ -124,7 +124,7 @@ export async function changedPackages({ } changed.add( - lernaJson.version === 'independent' + lernaJson.version === "independent" ? `${monorepoPackage.name}@${inc( monorepoPackage.version, version as ReleaseType @@ -165,8 +165,8 @@ export function getMonorepoPackage() { /** Get all of the packages+version in the lerna monorepo */ async function getPackageList() { - return getLernaPackages().then(packages => - packages.map(p => `${p.name}@${p.version.split('+')[0]}`) + return getLernaPackages().then((packages) => + packages.map((p) => `${p.name}@${p.version.split("+")[0]}`) ); } @@ -180,13 +180,13 @@ async function bumpLatest( version: SEMVER ) { const latestVersion = localVersion - ? await greaterRelease(s => s, name, localVersion) + ? await greaterRelease((s) => s, name, localVersion) : undefined; return latestVersion ? inc(latestVersion, version as ReleaseType) : version; } -const verbose = ['--loglevel', 'silly']; +const verbose = ["--loglevel", "silly"]; const pluginOptions = t.partial({ /** Whether to create sub-package changelogs */ @@ -198,7 +198,7 @@ const pluginOptions = t.partial({ /** A scope to publish canary versions under */ canaryScope: t.string, /** Publish a monorepo with the lerna --exact flag */ - exact: t.boolean + exact: t.boolean, }); export type INpmConfig = t.TypeOf; @@ -206,7 +206,7 @@ export type INpmConfig = t.TypeOf; /** Parse the lerna.json file. */ const getLernaJson = () => { try { - return JSON.parse(fs.readFileSync('lerna.json', 'utf8')); + return JSON.parse(fs.readFileSync("lerna.json", "utf8")); } catch (error) { return {}; } @@ -214,23 +214,23 @@ const getLernaJson = () => { /** Render a list of string in markdown */ const markdownList = (lines: string[]) => - lines.map(line => `- \`${line}\``).join('\n'); + lines.map((line) => `- \`${line}\``).join("\n"); /** Get the previous version. Typically from a package distribution description file. */ async function getPreviousVersion(auto: Auto, prereleaseBranch: string) { - let previousVersion = ''; + let previousVersion = ""; if (isMonorepo()) { auto.logger.veryVerbose.info( - 'Using monorepo to calculate previous release' + "Using monorepo to calculate previous release" ); const monorepoVersion = getLernaJson().version; - if (monorepoVersion === 'independent') { + if (monorepoVersion === "independent") { previousVersion = - 'dryRun' in auto.options && auto.options.dryRun + "dryRun" in auto.options && auto.options.dryRun ? markdownList(await getPackageList()) - : ''; + : ""; } else { const releasedPackage = getMonorepoPackage(); @@ -245,9 +245,9 @@ async function getPreviousVersion(auto: Auto, prereleaseBranch: string) { ); } } - } else if (fs.existsSync('package.json')) { + } else if (fs.existsSync("package.json")) { auto.logger.veryVerbose.info( - 'Using package.json to calculate previous version' + "Using package.json to calculate previous version" ); const { version, name } = await loadPackageJson(); @@ -258,11 +258,11 @@ async function getPreviousVersion(auto: Auto, prereleaseBranch: string) { auto.prefixRelease(version), prereleaseBranch ) - : '0.0.0'; + : "0.0.0"; } auto.logger.verbose.info( - 'NPM: Got previous version from package.json', + "NPM: Got previous version from package.json", previousVersion ); @@ -270,7 +270,7 @@ async function getPreviousVersion(auto: Auto, prereleaseBranch: string) { } /** Remove the @ sign */ -const sanitizeScope = (canaryScope: string) => canaryScope.replace('@', ''); +const sanitizeScope = (canaryScope: string) => canaryScope.replace("@", ""); /** Add a npm scope to a package name. Can have leading @ or not. */ const addCanaryScope = (canaryScope: string, name: string) => @@ -279,7 +279,7 @@ const addCanaryScope = (canaryScope: string, name: string) => /** Change the scope of all the packages to the canary scope */ async function setCanaryScope(canaryScope: string, paths: string[]) { const packages = await Promise.all( - paths.map(async p => [p, await loadPackageJson(p)] as const) + paths.map(async (p) => [p, await loadPackageJson(p)] as const) ); const names = packages.map(([, p]) => p.name); @@ -287,15 +287,15 @@ async function setCanaryScope(canaryScope: string, paths: string[]) { packages.map(async ([p, packageJson]) => { const newJson = { ...packageJson }; const name = packageJson.name.match(/@\S+\/\S+/) - ? packageJson.name.split('/')[1] + ? packageJson.name.split("/")[1] : packageJson.name; newJson.name = addCanaryScope(canaryScope, name); if (newJson.dependencies) { - Object.keys(newJson.dependencies).forEach(d => { + Object.keys(newJson.dependencies).forEach((d) => { if (names.includes(d)) { - const depName = d.match(/@\S+\/\S+/) ? d.split('/')[1] : d; + const depName = d.match(/@\S+\/\S+/) ? d.split("/")[1] : d; newJson.dependencies![ addCanaryScope(canaryScope, depName) @@ -306,7 +306,7 @@ async function setCanaryScope(canaryScope: string, paths: string[]) { } await writeFile( - path.join(p, 'package.json'), + path.join(p, "package.json"), JSON.stringify(newJson, null, 2) ); }) @@ -315,24 +315,24 @@ async function setCanaryScope(canaryScope: string, paths: string[]) { /** Reset the scope changes of all the packages */ async function gitReset() { - await execPromise('git', ['reset', '--hard', 'HEAD']); + await execPromise("git", ["reset", "--hard", "HEAD"]); } /** Make install instructions for multiple repos */ const makeMonorepoInstallList = (packageList: string[]) => [ - ':sparkles: Test out this PR locally via:\n', - '```sh', - ...packageList.map(p => `npm install ${p}`), - '# or ', - ...packageList.map(p => `yarn add ${p}`), - '```' - ].join('\n'); + ":sparkles: Test out this PR locally via:\n", + "```sh", + ...packageList.map((p) => `npm install ${p}`), + "# or ", + ...packageList.map((p) => `yarn add ${p}`), + "```", + ].join("\n"); /** Publish to NPM. Works in both a monorepo setting and for a single package. */ export default class NPMPlugin implements IPlugin { /** The name of the plugin */ - name = 'npm'; + name = "npm"; /** Whether to render a changelog like a monorepo's */ private renderMonorepoChangelog: boolean; @@ -353,13 +353,13 @@ export default class NPMPlugin implements IPlugin { this.exact = Boolean(config.exact); this.renderMonorepoChangelog = true; this.subPackageChangelogs = - typeof config.subPackageChangelogs === 'boolean' + typeof config.subPackageChangelogs === "boolean" ? config.subPackageChangelogs : true; this.setRcToken = - typeof config.setRcToken === 'boolean' ? config.setRcToken : true; + typeof config.setRcToken === "boolean" ? config.setRcToken : true; this.forcePublish = - typeof config.forcePublish === 'boolean' ? config.forcePublish : true; + typeof config.forcePublish === "boolean" ? config.forcePublish : true; this.canaryScope = config.canaryScope || undefined; } @@ -371,12 +371,12 @@ export default class NPMPlugin implements IPlugin { /** Custom initialization for this plugin */ init(initializer: InteractiveInit) { - initializer.hooks.createEnv.tap(this.name, vars => [ + initializer.hooks.createEnv.tap(this.name, (vars) => [ ...vars, { - variable: 'NPM_TOKEN', - message: `Enter a npm token for publishing packages https://docs.npmjs.com/creating-and-viewing-authentication-tokens` - } + variable: "NPM_TOKEN", + message: `Enter a npm token for publishing packages https://docs.npmjs.com/creating-and-viewing-authentication-tokens`, + }, ]); initializer.hooks.getAuthor.tapPromise(this.name, async () => { @@ -389,7 +389,7 @@ export default class NPMPlugin implements IPlugin { const author = await initializer.getAuthorInformation(); const newPackageJson = { ...packageJson }; newPackageJson.author = `${author.name} <${author.email}>`; - await writeFile('package.json', JSON.stringify(newPackageJson, null, 2)); + await writeFile("package.json", JSON.stringify(newPackageJson, null, 2)); return true; }); @@ -404,29 +404,29 @@ export default class NPMPlugin implements IPlugin { const repository = await initializer.getRepoInformation(); const newPackageJson = { ...packageJson }; newPackageJson.repository = `${repository.owner}/${repository.repo}`; - await writeFile('package.json', JSON.stringify(newPackageJson, null, 2)); + await writeFile("package.json", JSON.stringify(newPackageJson, null, 2)); return true; }); - initializer.hooks.writeRcFile.tapPromise(this.name, async rc => { + initializer.hooks.writeRcFile.tapPromise(this.name, async (rc) => { const packageJson = await loadPackageJson(); // eslint-disable-next-line @typescript-eslint/no-explicit-any if ((packageJson as any).auto) { initializer.logger.log.note( - 'Would have wrote configuration:\n', + "Would have wrote configuration:\n", JSON.stringify(rc, null, 2) ); initializer.logger.log.warn( - 'Found auto configuration in package.json. Doing nothing.' + "Found auto configuration in package.json. Doing nothing." ); } else { await writeFile( - 'package.json', + "package.json", JSON.stringify({ ...packageJson, auto: rc }, null, 2) ); - initializer.logger.log.success('Wrote configuration to package.json'); + initializer.logger.log.success("Wrote configuration to package.json"); } return true; @@ -436,8 +436,8 @@ export default class NPMPlugin implements IPlugin { /** Tap into auto plugin points. */ apply(auto: Auto) { const isVerbose = - auto.logger.logLevel === 'verbose' || - auto.logger.logLevel === 'veryVerbose'; + auto.logger.logLevel === "verbose" || + auto.logger.logLevel === "veryVerbose"; const verboseArgs = isVerbose ? verbose : []; const prereleaseBranches = auto.config?.prereleaseBranches!; const branch = getCurrentBranch(); @@ -455,16 +455,16 @@ export default class NPMPlugin implements IPlugin { }); auto.hooks.beforeShipIt.tap(this.name, async () => { - const isIndependent = getLernaJson().version === 'independent'; + const isIndependent = getLernaJson().version === "independent"; // In independent mode it's possible that no changes to packages have been // made, so no release will be made. if (isIndependent) { try { - await execPromise('yarn', ['lerna', 'updated']); + await execPromise("yarn", ["lerna", "updated"]); } catch (error) { auto.logger.log.warn( - 'Lerna detected no changes in project. Aborting release since nothing would be published.' + "Lerna detected no changes in project. Aborting release since nothing would be published." ); process.exit(0); } @@ -474,12 +474,12 @@ export default class NPMPlugin implements IPlugin { return; } - auto.checkEnv(this.name, 'NPM_TOKEN'); + auto.checkEnv(this.name, "NPM_TOKEN"); }); auto.hooks.getAuthor.tapPromise(this.name, async () => { auto.logger.verbose.info( - 'NPM: Getting repo information from package.json' + "NPM: Getting repo information from package.json" ); const packageJson = await loadPackageJson(); @@ -489,7 +489,7 @@ export default class NPMPlugin implements IPlugin { const { author } = packageJson; - if (typeof author === 'string') { + if (typeof author === "string") { return parseAuthor(author); } @@ -502,7 +502,7 @@ export default class NPMPlugin implements IPlugin { auto.hooks.getRepository.tapPromise(this.name, async () => { auto.logger.verbose.info( - 'NPM: getting repo information from package.json' + "NPM: getting repo information from package.json" ); const repo = await getConfigFromPackageJson(); @@ -511,12 +511,12 @@ export default class NPMPlugin implements IPlugin { } }); - auto.hooks.onCreateRelease.tap(this.name, release => { + auto.hooks.onCreateRelease.tap(this.name, (release) => { release.hooks.createChangelogTitle.tap( `${this.name} - lerna independent`, () => { - if (isMonorepo() && getLernaJson().version === 'independent') { - return ''; + if (isMonorepo() && getLernaJson().version === "independent") { + return ""; } } ); @@ -526,7 +526,7 @@ export default class NPMPlugin implements IPlugin { this.name, (changelog, version = SEMVER.patch) => { changelog.hooks.renderChangelogLine.tapPromise( - 'NPM - Monorepo', + "NPM - Monorepo", async ([commit, line]) => { if (!isMonorepo() || !this.renderMonorepoChangelog) { return [commit, line]; @@ -539,18 +539,18 @@ export default class NPMPlugin implements IPlugin { packages: lernaPackages, lernaJson, logger: auto.logger, - version + version, }); const section = packages?.length - ? packages.map(p => `\`${p}\``).join(', ') - : 'monorepo'; + ? packages.map((p) => `\`${p}\``).join(", ") + : "monorepo"; - if (section === 'monorepo') { + if (section === "monorepo") { return [commit, line]; } - return [commit, [`- ${section}`, ` ${line}`].join('\n')]; + return [commit, [`- ${section}`, ` ${line}`].join("\n")]; } ); } @@ -576,8 +576,8 @@ export default class NPMPlugin implements IPlugin { `Updating changelog for: ${lernaPackage.name}` ); - const includedCommits = commits.filter(commit => - commit.files.some(file => inFolder(lernaPackage.path, file)) + const includedCommits = commits.filter((commit) => + commit.files.some((file) => inFolder(lernaPackage.path, file)) ); const title = `v${inc(lernaPackage.version, bump as ReleaseType)}`; const releaseNotes = await changelog.generateReleaseNotes( @@ -588,7 +588,7 @@ export default class NPMPlugin implements IPlugin { await auto.release!.updateChangelogFile( title, releaseNotes, - path.join(lernaPackage.path, 'CHANGELOG.md') + path.join(lernaPackage.path, "CHANGELOG.md") ); } }, Promise.resolve()); @@ -597,33 +597,33 @@ export default class NPMPlugin implements IPlugin { } ); - auto.hooks.version.tapPromise(this.name, async version => { + auto.hooks.version.tapPromise(this.name, async (version) => { const isBaseBranch = branch === auto.baseBranch; if (isMonorepo()) { - auto.logger.verbose.info('Detected monorepo, using lerna'); - const isIndependent = getLernaJson().version === 'independent'; + auto.logger.verbose.info("Detected monorepo, using lerna"); + const isIndependent = getLernaJson().version === "independent"; const monorepoBump = isIndependent || !isBaseBranch ? undefined : await bumpLatest(getMonorepoPackage(), version); - await execPromise('npx', [ - 'lerna', - 'version', + await execPromise("npx", [ + "lerna", + "version", monorepoBump || version, - !isIndependent && this.forcePublish && '--force-publish', - '--no-commit-hooks', - '--yes', - '--no-push', - '-m', + !isIndependent && this.forcePublish && "--force-publish", + "--no-commit-hooks", + "--yes", + "--no-push", + "-m", isIndependent ? '"Bump independent versions [skip ci]"' : VERSION_COMMIT_MESSAGE, - this.exact && '--exact', - ...verboseArgs + this.exact && "--exact", + ...verboseArgs, ]); - auto.logger.verbose.info('Successfully versioned repo'); + auto.logger.verbose.info("Successfully versioned repo"); return; } @@ -631,32 +631,32 @@ export default class NPMPlugin implements IPlugin { ? await bumpLatest(await loadPackageJson(), version) : version; - await execPromise('npm', [ - 'version', + await execPromise("npm", [ + "version", latestBump || version, - '--no-commit-hooks', - '-m', + "--no-commit-hooks", + "-m", VERSION_COMMIT_MESSAGE, - ...verboseArgs + ...verboseArgs, ]); - auto.logger.verbose.info('Successfully versioned repo'); + auto.logger.verbose.info("Successfully versioned repo"); }); auto.hooks.canary.tapPromise(this.name, async (bump, postFix) => { if (this.setRcToken) { await setTokenOnCI(auto.logger); - auto.logger.verbose.info('Set CI NPM_TOKEN'); + auto.logger.verbose.info("Set CI NPM_TOKEN"); } const lastRelease = await auto.git!.getLatestRelease(); const latestTag = (await auto.git?.getLatestTagInBranch()) || lastRelease; - const inPrerelease = prereleaseBranches.some(b => + const inPrerelease = prereleaseBranches.some((b) => latestTag.includes(`-${b}.`) ); if (isMonorepo()) { - const isIndependent = getLernaJson().version === 'independent'; - auto.logger.verbose.info('Detected monorepo, using lerna'); + const isIndependent = getLernaJson().version === "independent"; + auto.logger.verbose.info("Detected monorepo, using lerna"); const packagesBefore = await getLernaPackages(); const preid = `canary${postFix}`; @@ -672,51 +672,51 @@ export default class NPMPlugin implements IPlugin { if (this.canaryScope) { await setCanaryScope( this.canaryScope, - packagesBefore.map(p => p.path) + packagesBefore.map((p) => p.path) ); } - await execPromise('npx', [ - 'lerna', - 'publish', + await execPromise("npx", [ + "lerna", + "publish", next, - '--dist-tag', - 'canary', - !isIndependent && '--force-publish', - '--yes', // skip prompts, - '--no-git-reset', // so we can get the version that just published - '--no-git-tag-version', // no need to tag and commit, - '--exact', // do not add ^ to canary versions, this can result in `npm i` resolving the wrong canary version - ...(isIndependent ? ['--preid', preid] : []), - ...verboseArgs + "--dist-tag", + "canary", + !isIndependent && "--force-publish", + "--yes", // skip prompts, + "--no-git-reset", // so we can get the version that just published + "--no-git-tag-version", // no need to tag and commit, + "--exact", // do not add ^ to canary versions, this can result in `npm i` resolving the wrong canary version + ...(isIndependent ? ["--preid", preid] : []), + ...verboseArgs, ]); - auto.logger.verbose.info('Successfully published canary version'); + auto.logger.verbose.info("Successfully published canary version"); const packages = await getLernaPackages(); const packageList = await getPackageList(); // Reset after we read the packages from the system! await gitReset(); if (isIndependent) { - if (!packageList.some(p => p.includes('canary'))) { - return { error: 'No packages were changed. No canary published.' }; + if (!packageList.some((p) => p.includes("canary"))) { + return { error: "No packages were changed. No canary published." }; } return { - newVersion: 'Canary Versions', + newVersion: "Canary Versions", details: makeMonorepoInstallList( - packageList.filter(p => p.includes('canary')) - ) + packageList.filter((p) => p.includes("canary")) + ), }; } - const versioned = packages.find(p => p.version.includes('canary')); + const versioned = packages.find((p) => p.version.includes("canary")); if (!versioned) { - return { error: 'No packages were changed. No canary published.' }; + return { error: "No packages were changed. No canary published." }; } - const version = versioned.version.split('+')[0]; + const version = versioned.version.split("+")[0]; return { newVersion: this.canaryScope @@ -724,11 +724,11 @@ export default class NPMPlugin implements IPlugin { this.canaryScope )}@${version}` : version, - details: makeMonorepoInstallList(packageList) + details: makeMonorepoInstallList(packageList), }; } - auto.logger.verbose.info('Detected single npm package'); + auto.logger.verbose.info("Detected single npm package"); const current = await auto.getCurrentVersion(lastRelease); const canaryVersion = determineNextVersion( lastRelease, @@ -738,36 +738,36 @@ export default class NPMPlugin implements IPlugin { ); if (this.canaryScope) { - await setCanaryScope(this.canaryScope, ['./']); + await setCanaryScope(this.canaryScope, ["./"]); } - await execPromise('npm', [ - 'version', + await execPromise("npm", [ + "version", canaryVersion, - '--no-git-tag-version', - ...verboseArgs + "--no-git-tag-version", + ...verboseArgs, ]); - const publishArgs = ['--tag', 'canary']; - await execPromise('npm', ['publish', ...publishArgs, ...verboseArgs]); + const publishArgs = ["--tag", "canary"]; + await execPromise("npm", ["publish", ...publishArgs, ...verboseArgs]); if (this.canaryScope) { await gitReset(); } const { name } = await loadPackageJson(); - auto.logger.verbose.info('Successfully published canary version'); + auto.logger.verbose.info("Successfully published canary version"); return { newVersion: canaryVersion, - details: makeMonorepoInstallList([`${name}@${canaryVersion}`]) + details: makeMonorepoInstallList([`${name}@${canaryVersion}`]), }; }); auto.hooks.next.tapPromise(this.name, async (preReleaseVersions, bump) => { if (this.setRcToken) { await setTokenOnCI(auto.logger); - auto.logger.verbose.info('Set CI NPM_TOKEN'); + auto.logger.verbose.info("Set CI NPM_TOKEN"); } const lastRelease = await auto.git!.getLatestRelease(); @@ -776,12 +776,12 @@ export default class NPMPlugin implements IPlugin { (await getPreviousVersion(auto, prereleaseBranch)); if (isMonorepo()) { - auto.logger.verbose.info('Detected monorepo, using lerna'); - const isIndependent = getLernaJson().version === 'independent'; + auto.logger.verbose.info("Detected monorepo, using lerna"); + const isIndependent = getLernaJson().version === "independent"; // It's hard to accurately predict how we should bump independent versions. // So we just prerelease most of the time. (independent only) const next = isIndependent - ? 'prerelease' + ? "prerelease" : determineNextVersion( lastRelease, latestTag, @@ -794,25 +794,25 @@ export default class NPMPlugin implements IPlugin { latestTag, bump, prereleaseBranch, - next + next, }); - await execPromise('npx', [ - 'lerna', - 'publish', + await execPromise("npx", [ + "lerna", + "publish", next, - '--dist-tag', + "--dist-tag", prereleaseBranch, - '--preid', + "--preid", prereleaseBranch, - '--no-push', + "--no-push", // you always want a next version to publish - !isIndependent && '--force-publish', + !isIndependent && "--force-publish", // skip prompts - '--yes', + "--yes", // do not add ^ to next versions, this can result in `npm i` resolving the wrong next version - '--exact', - ...verboseArgs + "--exact", + ...verboseArgs, ]); // we do not want to commit the next version. this causes @@ -821,109 +821,109 @@ export default class NPMPlugin implements IPlugin { // commit+tag the new version and roll back all the tags to the // previous commit. const tags = ( - await execPromise('git', ['tag', '--points-at', 'HEAD']) - ).split('\n'); + await execPromise("git", ["tag", "--points-at", "HEAD"]) + ).split("\n"); await Promise.all( // Move tags back one commit - tags.map(tag => execPromise('git', ['tag', tag, '-f', 'HEAD^'])) + tags.map((tag) => execPromise("git", ["tag", tag, "-f", "HEAD^"])) ); // Move branch back one commit - await execPromise('git', ['reset', '--hard', 'HEAD~1']); + await execPromise("git", ["reset", "--hard", "HEAD~1"]); - auto.logger.verbose.info('Successfully published next version'); + auto.logger.verbose.info("Successfully published next version"); preReleaseVersions = [ ...preReleaseVersions, - ...tags.map(auto.prefixRelease) + ...tags.map(auto.prefixRelease), ]; } else { - auto.logger.verbose.info('Detected single npm package'); + auto.logger.verbose.info("Detected single npm package"); - await execPromise('npm', [ - 'version', + await execPromise("npm", [ + "version", determineNextVersion(lastRelease, latestTag, bump, prereleaseBranch), // we do not want to commit the next version. this causes // merge conflicts when merged into master - '--no-git-tag-version', - ...verboseArgs + "--no-git-tag-version", + ...verboseArgs, ]); const { version } = await loadPackageJson(); - await execPromise('git', [ - 'tag', + await execPromise("git", [ + "tag", auto.prefixRelease(version!), - '-m', - `"Update version to ${version}"` + "-m", + `"Update version to ${version}"`, ]); - await execPromise('npm', [ - 'publish', - '--tag', + await execPromise("npm", [ + "publish", + "--tag", prereleaseBranch, - ...verboseArgs + ...verboseArgs, ]); - auto.logger.verbose.info('Successfully published next version'); + auto.logger.verbose.info("Successfully published next version"); preReleaseVersions.push(auto.prefixRelease(version!)); } - await execPromise('git', ['push', auto.remote, '--tags']); + await execPromise("git", ["push", auto.remote, "--tags"]); return preReleaseVersions; }); auto.hooks.publish.tapPromise(this.name, async () => { - const status = await execPromise('git', ['status', '--porcelain']); + const status = await execPromise("git", ["status", "--porcelain"]); const isBaseBranch = branch === auto.baseBranch; // The only other time this hook is called is when creating a version // branch. So when on one of those branches publish to a tag of the same // name const tag = isBaseBranch ? [] - : [isMonorepo() ? '--dist-tag' : '--tag', branch]; + : [isMonorepo() ? "--dist-tag" : "--tag", branch]; if (isVerbose && status) { - auto.logger.log.error('Changed Files:\n', status); + auto.logger.log.error("Changed Files:\n", status); } if (this.setRcToken) { await setTokenOnCI(auto.logger); - auto.logger.verbose.info('Set CI NPM_TOKEN'); + auto.logger.verbose.info("Set CI NPM_TOKEN"); } if (isMonorepo()) { - auto.logger.verbose.info('Detected monorepo, using lerna'); + auto.logger.verbose.info("Detected monorepo, using lerna"); if (auto.options?.verbose) { - await execPromise('git', ['status', '--short']); + await execPromise("git", ["status", "--short"]); } - await execPromise('npx', [ - 'lerna', - 'publish', + await execPromise("npx", [ + "lerna", + "publish", ...tag, - '--yes', + "--yes", // Plugins can add as many commits as they want, lerna will still // publish the changed package versions. from-git broke when HEAD // didn't contain the tags - 'from-package', - ...verboseArgs + "from-package", + ...verboseArgs, ]); } else { - await execPromise('npm', ['publish', ...tag, ...verboseArgs]); + await execPromise("npm", ["publish", ...tag, ...verboseArgs]); } - await execPromise('git', [ - 'push', - '--follow-tags', - '--set-upstream', + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", auto.remote, - branch || auto.baseBranch + branch || auto.baseBranch, ]); - auto.logger.verbose.info('Successfully published repo'); + auto.logger.verbose.info("Successfully published repo"); }); - auto.hooks.makeRelease.tapPromise(this.name, async options => { - const isIndependent = getLernaJson().version === 'independent'; + auto.hooks.makeRelease.tapPromise(this.name, async (options) => { + const isIndependent = getLernaJson().version === "independent"; // Independent mode will create multiple releases on Github. // Each release will only contain the release notes for the @@ -935,22 +935,24 @@ export default class NPMPlugin implements IPlugin { const lernaPackages = await getLernaPackages(); // Go through each new tag: const newTags = ( - await execPromise('git', ['tag', '--points-at', 'HEAD']) - ).split('\n'); + await execPromise("git", ["tag", "--points-at", "HEAD"]) + ).split("\n"); this.renderMonorepoChangelog = false; - const packagePaths = lernaPackages.map(p => p.path); + const packagePaths = lernaPackages.map((p) => p.path); const commitsAtRoot = options.commits.filter( - commit => - !commit.files.some(file => - packagePaths.some(p => inFolder(p, file)) + (commit) => + !commit.files.some((file) => + packagePaths.some((p) => inFolder(p, file)) ) ); const releases = await Promise.all( - newTags.map(async tag => { - const lernaPackage = lernaPackages.find(p => tag.includes(p.name)); + newTags.map(async (tag) => { + const lernaPackage = lernaPackages.find((p) => + tag.includes(p.name) + ); if (!lernaPackage) { return; @@ -959,12 +961,12 @@ export default class NPMPlugin implements IPlugin { auto.logger.log.info(`Releasing ${tag}...`); // 1. generate release notes for just the commits for the package - const includedCommits = options.commits.filter(commit => - commit.files.some(file => inFolder(lernaPackage.path, file)) + const includedCommits = options.commits.filter((commit) => + commit.files.some((file) => inFolder(lernaPackage.path, file)) ); const releaseNotes = await changelog.generateReleaseNotes([ ...commitsAtRoot, - ...includedCommits + ...includedCommits, ]); auto.logger.log.info(`Using release notes:\n${releaseNotes}`); diff --git a/plugins/npm/src/package-config.ts b/plugins/npm/src/package-config.ts index caff7b9c9..2fb3d3292 100644 --- a/plugins/npm/src/package-config.ts +++ b/plugins/npm/src/package-config.ts @@ -1,5 +1,5 @@ -import parseGitHubUrl from 'parse-github-url'; -import { loadPackageJson } from './utils'; +import parseGitHubUrl from "parse-github-url"; +import { loadPackageJson } from "./utils"; export interface IRepoConfig { /** Owner of the repo (or GitHub user) */ @@ -20,7 +20,7 @@ export default async function getConfigFromPackageJson(): Promise< const { owner, name } = parseGitHubUrl( - typeof repository === 'string' ? repository : repository.url + typeof repository === "string" ? repository : repository.url ) || {}; if (!owner || !name) { @@ -29,6 +29,6 @@ export default async function getConfigFromPackageJson(): Promise< return { repo: name, - owner + owner, }; } diff --git a/plugins/npm/src/set-npm-token.ts b/plugins/npm/src/set-npm-token.ts index 8ce0b083c..e27711ad0 100644 --- a/plugins/npm/src/set-npm-token.ts +++ b/plugins/npm/src/set-npm-token.ts @@ -1,11 +1,11 @@ -import { ILogger } from '@auto-it/core'; -import envCi from 'env-ci'; -import path from 'path'; -import registryUrl from 'registry-url'; -import urlJoin from 'url-join'; -import userHome from 'user-home'; +import { ILogger } from "@auto-it/core"; +import envCi from "env-ci"; +import path from "path"; +import registryUrl from "registry-url"; +import urlJoin from "url-join"; +import userHome from "user-home"; -import { loadPackageJson, readFile, writeFile } from './utils'; +import { loadPackageJson, readFile, writeFile } from "./utils"; const { isCi } = envCi(); @@ -16,8 +16,8 @@ export default async function setTokenOnCI(logger: ILogger) { } const { publishConfig = {}, name } = await loadPackageJson(); - const rc = path.join(userHome, '.npmrc'); - let contents = ''; + const rc = path.join(userHome, ".npmrc"); + let contents = ""; try { contents = (await readFile(rc)).toString(); @@ -29,7 +29,7 @@ export default async function setTokenOnCI(logger: ILogger) { if (publishConfig.registry) { registry = publishConfig.registry; - } else if (name?.startsWith('@')) { + } else if (name?.startsWith("@")) { const scope = name.split(`/`)[0]; registry = registryUrl(scope); } else { @@ -40,7 +40,7 @@ export default async function setTokenOnCI(logger: ILogger) { const url = registry.replace(/^https?:/, ``); // eslint-disable-next-line no-template-curly-in-string - const authTokenString = urlJoin(url, ':_authToken=${NPM_TOKEN}'); + const authTokenString = urlJoin(url, ":_authToken=${NPM_TOKEN}"); logger.verbose.info(`Will set authentication token string in ${rc}`); diff --git a/plugins/npm/src/utils.ts b/plugins/npm/src/utils.ts index 3c0373974..680948fa0 100644 --- a/plugins/npm/src/utils.ts +++ b/plugins/npm/src/utils.ts @@ -1,11 +1,11 @@ -import * as fs from 'fs'; -import path from 'path'; -import { promisify } from 'util'; +import * as fs from "fs"; +import path from "path"; +import { promisify } from "util"; export const readFile = promisify(fs.readFile); export const writeFile = promisify(fs.writeFile); /** Load and parse the root package json for the project */ -export async function loadPackageJson(root = './'): Promise { - return JSON.parse(await readFile(path.join(root, 'package.json'), 'utf-8')); +export async function loadPackageJson(root = "./"): Promise { + return JSON.parse(await readFile(path.join(root, "package.json"), "utf-8")); } diff --git a/plugins/omit-commits/__tests__/omit-commits.test.ts b/plugins/omit-commits/__tests__/omit-commits.test.ts index b73fb9126..1a5b4426e 100644 --- a/plugins/omit-commits/__tests__/omit-commits.test.ts +++ b/plugins/omit-commits/__tests__/omit-commits.test.ts @@ -1,11 +1,11 @@ -import Auto from '@auto-it/core'; -import makeCommitFromMsg from '@auto-it/core/dist/__tests__/make-commit-from-msg'; -import LogParse from '@auto-it/core/dist/log-parse'; +import Auto from "@auto-it/core"; +import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; +import LogParse from "@auto-it/core/dist/log-parse"; import { makeHooks, - makeLogParseHooks -} from '@auto-it/core/dist/utils/make-hooks'; -import OmitCommits, { IOmitCommitsPluginOptions } from '../src'; + makeLogParseHooks, +} from "@auto-it/core/dist/utils/make-hooks"; +import OmitCommits, { IOmitCommitsPluginOptions } from "../src"; const setup = (options: IOmitCommitsPluginOptions) => { const plugin = new OmitCommits(options); @@ -18,40 +18,40 @@ const setup = (options: IOmitCommitsPluginOptions) => { return logParseHooks; }; -describe('Omit Commits Plugin', () => { - test('should not filter the commit', async () => { - const hooks = setup({ name: ['pdbf'] }); - const commit = makeCommitFromMsg('foo'); +describe("Omit Commits Plugin", () => { + test("should not filter the commit", async () => { + const hooks = setup({ name: ["pdbf"] }); + const commit = makeCommitFromMsg("foo"); expect(await hooks.omitCommit.promise(commit)).toBeUndefined(); }); - test('should filter the commit by name', async () => { - const hooks = setup({ name: ['pdbf'] }); - const commit = makeCommitFromMsg('foo', { name: 'pdbf' }); + test("should filter the commit by name", async () => { + const hooks = setup({ name: ["pdbf"] }); + const commit = makeCommitFromMsg("foo", { name: "pdbf" }); expect(await hooks.omitCommit.promise(commit)).toBe(true); }); - test('should filter the commit by username', async () => { - const hooks = setup({ username: ['pdbf'] }); - const commit = makeCommitFromMsg('foo', { username: 'pdbf' }); + test("should filter the commit by username", async () => { + const hooks = setup({ username: ["pdbf"] }); + const commit = makeCommitFromMsg("foo", { username: "pdbf" }); expect(await hooks.omitCommit.promise(commit)).toBe(true); }); - test('should filter the commit by email', async () => { - const hooks = setup({ email: ['foo@bar.com'] }); - const commit = makeCommitFromMsg('foo', { email: 'foo@bar.com' }); + test("should filter the commit by email", async () => { + const hooks = setup({ email: ["foo@bar.com"] }); + const commit = makeCommitFromMsg("foo", { email: "foo@bar.com" }); expect(await hooks.omitCommit.promise(commit)).toBe(true); }); - test('should filter the commit by label', async () => { - const hooks = setup({ labels: ['me'] }); - const commit = makeCommitFromMsg('foo', { labels: ['me'] }); + test("should filter the commit by label", async () => { + const hooks = setup({ labels: ["me"] }); + const commit = makeCommitFromMsg("foo", { labels: ["me"] }); expect(await hooks.omitCommit.promise(commit)).toBe(true); }); - test('should filter the commit by subject', async () => { - const hooks = setup({ subject: 'WIP' }); - const commit = makeCommitFromMsg('[WIP] foo'); + test("should filter the commit by subject", async () => { + const hooks = setup({ subject: "WIP" }); + const commit = makeCommitFromMsg("[WIP] foo"); expect(await hooks.omitCommit.promise(commit)).toBe(true); }); }); diff --git a/plugins/omit-commits/src/index.ts b/plugins/omit-commits/src/index.ts index 65e2e69f4..58f159ef4 100644 --- a/plugins/omit-commits/src/index.ts +++ b/plugins/omit-commits/src/index.ts @@ -1,5 +1,5 @@ -import { Auto, IPlugin, validatePluginConfiguration } from '@auto-it/core'; -import * as t from 'io-ts'; +import { Auto, IPlugin, validatePluginConfiguration } from "@auto-it/core"; +import * as t from "io-ts"; const pattern = t.union([t.string, t.array(t.string)]); const pluginOptions = t.partial({ @@ -12,7 +12,7 @@ const pluginOptions = t.partial({ /** Commit messages to omit */ subject: pattern, /** Labels to omit */ - labels: pattern + labels: pattern, }); export type IOmitCommitsPluginOptions = t.TypeOf; @@ -23,7 +23,7 @@ const arrayify = (arr: T | T[]): T[] => (Array.isArray(arr) ? arr : [arr]); /** Filter certain commits out of the changelog and version calculation. */ export default class OmitCommitsPlugin implements IPlugin { /** The name of the plugin */ - name = 'omit-commits'; + name = "omit-commits"; /** The options of the plugin */ readonly options: { @@ -46,7 +46,7 @@ export default class OmitCommitsPlugin implements IPlugin { email: options.email ? arrayify(options.email) : [], name: options.name ? arrayify(options.name) : [], subject: options.subject ? arrayify(options.subject) : [], - labels: options.labels ? arrayify(options.labels) : [] + labels: options.labels ? arrayify(options.labels) : [], }; } @@ -58,24 +58,24 @@ export default class OmitCommitsPlugin implements IPlugin { } }); - auto.hooks.onCreateLogParse.tap(this.name, logParse => { - logParse.hooks.omitCommit.tap(this.name, commit => { + auto.hooks.onCreateLogParse.tap(this.name, (logParse) => { + logParse.hooks.omitCommit.tap(this.name, (commit) => { if ( - commit.authors.find(author => + commit.authors.find((author) => Boolean(author.name && this.options.name.includes(author.name)) ) || - commit.authors.find(author => + commit.authors.find((author) => Boolean(author.email && this.options.email.includes(author.email)) ) || - commit.authors.find(author => + commit.authors.find((author) => Boolean( author.username && this.options.username.includes(author.username) ) ) || - commit.labels.find(label => + commit.labels.find((label) => Boolean(this.options.labels.includes(label)) ) || - this.options.subject.find(str => commit.subject.includes(str)) + this.options.subject.find((str) => commit.subject.includes(str)) ) { return true; } diff --git a/plugins/omit-release-notes/__tests__/omit-release-notes.test.ts b/plugins/omit-release-notes/__tests__/omit-release-notes.test.ts index 1056ff84a..01cefd10b 100644 --- a/plugins/omit-release-notes/__tests__/omit-release-notes.test.ts +++ b/plugins/omit-release-notes/__tests__/omit-release-notes.test.ts @@ -1,11 +1,11 @@ -import Auto, { SEMVER } from '@auto-it/core'; -import makeCommitFromMsg from '@auto-it/core/dist/__tests__/make-commit-from-msg'; -import Changelog from '@auto-it/core/dist/changelog'; +import Auto, { SEMVER } from "@auto-it/core"; +import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; +import Changelog from "@auto-it/core/dist/changelog"; import { makeChangelogHooks, - makeHooks -} from '@auto-it/core/dist/utils/make-hooks'; -import OmitReleaseNotesPlugin, { IReleaseNotesPluginOptions } from '../src'; + makeHooks, +} from "@auto-it/core/dist/utils/make-hooks"; +import OmitReleaseNotesPlugin, { IReleaseNotesPluginOptions } from "../src"; const setup = (options: IReleaseNotesPluginOptions) => { const plugin = new OmitReleaseNotesPlugin(options); @@ -21,34 +21,34 @@ const setup = (options: IReleaseNotesPluginOptions) => { return changelogHooks; }; -describe('Omit Release Notes Plugin', () => { - test('should not filter the commit', async () => { - const hooks = setup({ name: ['pdbf'] }); - const commit = makeCommitFromMsg('foo'); +describe("Omit Release Notes Plugin", () => { + test("should not filter the commit", async () => { + const hooks = setup({ name: ["pdbf"] }); + const commit = makeCommitFromMsg("foo"); expect(await hooks.omitReleaseNotes.promise(commit)).toBeUndefined(); }); - test('should filter the commit by name', async () => { - const hooks = setup({ name: ['pdbf'] }); - const commit = makeCommitFromMsg('foo', { name: 'pdbf' }); + test("should filter the commit by name", async () => { + const hooks = setup({ name: ["pdbf"] }); + const commit = makeCommitFromMsg("foo", { name: "pdbf" }); expect(await hooks.omitReleaseNotes.promise(commit)).toBe(true); }); - test('should filter the commit by username', async () => { - const hooks = setup({ username: ['pdbf'] }); - const commit = makeCommitFromMsg('foo', { username: 'pdbf' }); + test("should filter the commit by username", async () => { + const hooks = setup({ username: ["pdbf"] }); + const commit = makeCommitFromMsg("foo", { username: "pdbf" }); expect(await hooks.omitReleaseNotes.promise(commit)).toBe(true); }); - test('should filter the commit by email', async () => { - const hooks = setup({ email: ['foo@bar.com'] }); - const commit = makeCommitFromMsg('foo', { email: 'foo@bar.com' }); + test("should filter the commit by email", async () => { + const hooks = setup({ email: ["foo@bar.com"] }); + const commit = makeCommitFromMsg("foo", { email: "foo@bar.com" }); expect(await hooks.omitReleaseNotes.promise(commit)).toBe(true); }); - test('should filter the commit by label', async () => { - const hooks = setup({ labels: ['me'] }); - const commit = makeCommitFromMsg('foo', { labels: ['me'] }); + test("should filter the commit by label", async () => { + const hooks = setup({ labels: ["me"] }); + const commit = makeCommitFromMsg("foo", { labels: ["me"] }); expect(await hooks.omitReleaseNotes.promise(commit)).toBe(true); }); }); diff --git a/plugins/omit-release-notes/src/index.ts b/plugins/omit-release-notes/src/index.ts index 27df39d05..6111d3c54 100644 --- a/plugins/omit-release-notes/src/index.ts +++ b/plugins/omit-release-notes/src/index.ts @@ -1,5 +1,5 @@ -import { Auto, IPlugin, validatePluginConfiguration } from '@auto-it/core'; -import * as t from 'io-ts'; +import { Auto, IPlugin, validatePluginConfiguration } from "@auto-it/core"; +import * as t from "io-ts"; const pattern = t.union([t.string, t.array(t.string)]); const pluginOptions = t.partial({ @@ -10,7 +10,7 @@ const pluginOptions = t.partial({ /** Names to omit */ name: pattern, /** Labels to omit */ - labels: pattern + labels: pattern, }); export type IReleaseNotesPluginOptions = t.TypeOf; @@ -21,7 +21,7 @@ const arrayify = (arr: T | T[]): T[] => (Array.isArray(arr) ? arr : [arr]); /** Filter PRs with release notes that shouldn't make it into a release. */ export default class ReleaseNotesPlugin implements IPlugin { /** The name of the plugin */ - name = 'omit-release-notes'; + name = "omit-release-notes"; /** The options of the plugin */ readonly options: { @@ -41,7 +41,7 @@ export default class ReleaseNotesPlugin implements IPlugin { username: options.username ? arrayify(options.username) : [], email: options.email ? arrayify(options.email) : [], name: options.name ? arrayify(options.name) : [], - labels: options.labels ? arrayify(options.labels) : [] + labels: options.labels ? arrayify(options.labels) : [], }; } @@ -53,21 +53,21 @@ export default class ReleaseNotesPlugin implements IPlugin { } }); - auto.hooks.onCreateChangelog.tap(this.name, changelog => { - changelog.hooks.omitReleaseNotes.tap(this.name, commit => { + auto.hooks.onCreateChangelog.tap(this.name, (changelog) => { + changelog.hooks.omitReleaseNotes.tap(this.name, (commit) => { if ( - commit.authors.find(author => + commit.authors.find((author) => Boolean(author.name && this.options.name.includes(author.name)) ) || - commit.authors.find(author => + commit.authors.find((author) => Boolean(author.email && this.options.email.includes(author.email)) ) || - commit.authors.find(author => + commit.authors.find((author) => Boolean( author.username && this.options.username.includes(author.username) ) ) || - commit.labels.find(label => + commit.labels.find((label) => Boolean(this.options.labels.includes(label)) ) ) { diff --git a/plugins/released/__tests__/released-label.test.ts b/plugins/released/__tests__/released-label.test.ts index 2015205f5..4314bbf10 100644 --- a/plugins/released/__tests__/released-label.test.ts +++ b/plugins/released/__tests__/released-label.test.ts @@ -1,17 +1,17 @@ -import Auto from '@auto-it/core'; -import makeCommitFromMsg from '@auto-it/core/dist/__tests__/make-commit-from-msg'; -import Git from '@auto-it/core/dist/git'; -import LogParse from '@auto-it/core/dist/log-parse'; -import { defaultLabels } from '@auto-it/core/dist/release'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; +import Auto from "@auto-it/core"; +import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; +import Git from "@auto-it/core/dist/git"; +import LogParse from "@auto-it/core/dist/log-parse"; +import { defaultLabels } from "@auto-it/core/dist/release"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; import { makeHooks, - makeLogParseHooks -} from '@auto-it/core/dist/utils/make-hooks'; + makeLogParseHooks, +} from "@auto-it/core/dist/utils/make-hooks"; -import ReleasedLabelPlugin from '../src'; +import ReleasedLabelPlugin from "../src"; -const git = new Git({ owner: '1', repo: '2', baseBranch: 'master' }); +const git = new Git({ owner: "1", repo: "2", baseBranch: "master" }); const log = new LogParse(); const comment = jest.fn(); @@ -20,7 +20,7 @@ git.addLabelToPr = addLabelToPr; const getPr = jest.fn(); git.getPullRequest = getPr; -getPr.mockReturnValue({ data: { body: '', head: { ref: 'test' } } }); +getPr.mockReturnValue({ data: { body: "", head: { ref: "test" } } }); const commits = jest.fn(); git.getCommitsForPR = commits; @@ -34,7 +34,7 @@ const lockIssue = jest.fn(); git.lockIssue = lockIssue; lockIssue.mockReturnValue([]); -describe('release label plugin', () => { +describe("release label plugin", () => { beforeEach(() => { comment.mockClear(); addLabelToPr.mockClear(); @@ -42,7 +42,7 @@ describe('release label plugin', () => { lockIssue.mockClear(); }); - test('should init label', async () => { + test("should init label", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply({ hooks: autoHooks } as Auto); @@ -52,20 +52,20 @@ describe('release label plugin', () => { ).toStrictEqual({ labels: [ { - description: 'This issue/pull request has been released.', - name: 'released', - releaseType: 'none' + description: "This issue/pull request has been released.", + name: "released", + releaseType: "none", }, { - description: 'This change is available in a prerelease.', - name: 'prerelease', - releaseType: 'none' - } - ] + description: "This change is available in a prerelease.", + name: "prerelease", + releaseType: "none", + }, + ], }); }); - test('should not omit released PRs', async () => { + test("should not omit released PRs", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); const logParseHooks = makeLogParseHooks(); @@ -76,20 +76,20 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); autoHooks.onCreateLogParse.call({ hooks: logParseHooks } as LogParse); - const included = makeCommitFromMsg('normal commit with no bump'); + const included = makeCommitFromMsg("normal commit with no bump"); expect(await logParseHooks.omitCommit.promise(included)).not.toBe(true); - const omitted = makeCommitFromMsg('normal commit with no bump', { - labels: ['released'] + const omitted = makeCommitFromMsg("normal commit with no bump", { + labels: ["released"], }); expect(await logParseHooks.omitCommit.promise(omitted)).not.toBe(true); }); - test('should do nothing without PRs', async () => { + test("should do nothing without PRs", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -98,21 +98,21 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); - const commit = makeCommitFromMsg('normal commit with no bump'); + const commit = makeCommitFromMsg("normal commit with no bump"); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [commit], - releaseNotes: '' + releaseNotes: "", }); expect(comment).not.toHaveBeenCalled(); }); - test('should do nothing without new version', async () => { + test("should do nothing without new version", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -121,20 +121,20 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); - const commit = makeCommitFromMsg('normal commit with no bump'); + const commit = makeCommitFromMsg("normal commit with no bump"); await autoHooks.afterRelease.promise({ - lastRelease: '0.1.0', + lastRelease: "0.1.0", commits: [commit], - releaseNotes: '' + releaseNotes: "", }); expect(comment).not.toHaveBeenCalled(); }); - test('should do nothing without commits', async () => { + test("should do nothing without commits", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -143,20 +143,20 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '' + releaseNotes: "", }); expect(comment).not.toHaveBeenCalled(); }); - test('should do nothing with skip release label', async () => { + test("should do nothing with skip release label", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -165,23 +165,23 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); - const commit = makeCommitFromMsg('normal commit with no bump (#123)', { - labels: ['skip-release'] + const commit = makeCommitFromMsg("normal commit with no bump (#123)", { + labels: ["skip-release"], }); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: await log.normalizeCommits([commit]), - releaseNotes: '' + releaseNotes: "", }); expect(comment).not.toHaveBeenCalled(); }); - test('should comment and label PRs', async () => { + test("should comment and label PRs", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -190,28 +190,28 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); - const commit = makeCommitFromMsg('normal commit with no bump (#123)'); + const commit = makeCommitFromMsg("normal commit with no bump (#123)"); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: await log.normalizeCommits([commit]), - releaseNotes: '' + releaseNotes: "", }); expect(comment).toHaveBeenCalledWith( expect.objectContaining({ - message: ':rocket: PR was released in `1.0.0` :rocket:' + message: ":rocket: PR was released in `1.0.0` :rocket:", }) ); }); - test('should comment and label PRs with custom message', async () => { + test("should comment and label PRs with custom message", async () => { const releasedLabel = new ReleasedLabelPlugin({ message: - ':rocket: %TYPE is fixed. %TYPE was released in [%VERSION](https://github.com/intuit/auto/releases/tag/%VERSION) :rocket:' + ":rocket: %TYPE is fixed. %TYPE was released in [%VERSION](https://github.com/intuit/auto/releases/tag/%VERSION) :rocket:", }); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -220,26 +220,26 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); - const commit = makeCommitFromMsg('normal commit with no bump (#123)'); + const commit = makeCommitFromMsg("normal commit with no bump (#123)"); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: await log.normalizeCommits([commit]), - releaseNotes: '' + releaseNotes: "", }); expect(comment).toHaveBeenCalledWith( expect.objectContaining({ message: - ':rocket: PR is fixed. PR was released in [1.0.0](https://github.com/intuit/auto/releases/tag/1.0.0) :rocket:' + ":rocket: PR is fixed. PR was released in [1.0.0](https://github.com/intuit/auto/releases/tag/1.0.0) :rocket:", }) ); }); - test('should do nothing when label is already present', async () => { + test("should do nothing when label is already present", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); @@ -249,53 +249,53 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); - getLabels.mockReturnValueOnce(['released']); + getLabels.mockReturnValueOnce(["released"]); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: await log.normalizeCommits([ - makeCommitFromMsg('normal commit with no bump (#123)') + makeCommitFromMsg("normal commit with no bump (#123)"), ]), - releaseNotes: '' + releaseNotes: "", }); expect(addLabelToPr).not.toHaveBeenCalled(); }); - test('should do nothing on pre-release branches', async () => { + test("should do nothing on pre-release branches", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply(({ - config: { prereleaseBranches: ['next'], labels: [] }, + config: { prereleaseBranches: ["next"], labels: [] }, hooks: autoHooks, labels: defaultLabels, logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); - getPr.mockReturnValueOnce({ data: { body: '', head: { ref: 'next' } } }); - getLabels.mockReturnValueOnce(['released']); + getPr.mockReturnValueOnce({ data: { body: "", head: { ref: "next" } } }); + getLabels.mockReturnValueOnce(["released"]); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: await log.normalizeCommits([ - makeCommitFromMsg('normal commit with no bump (#123)') + makeCommitFromMsg("normal commit with no bump (#123)"), ]), - releaseNotes: '' + releaseNotes: "", }); expect(addLabelToPr).not.toHaveBeenCalled(); }); - test('should not add released label for canary releases', async () => { + test("should not add released label for canary releases", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); @@ -305,22 +305,22 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); await autoHooks.afterRelease.promise({ - lastRelease: '0.1.0', - newVersion: '1.0.0-canary', + lastRelease: "0.1.0", + newVersion: "1.0.0-canary", commits: await log.normalizeCommits([ - makeCommitFromMsg('normal commit with no bump (#123)') + makeCommitFromMsg("normal commit with no bump (#123)"), ]), - releaseNotes: '' + releaseNotes: "", }); expect(addLabelToPr).not.toHaveBeenCalled(); }); - test('should comment and lined Issues', async () => { + test("should comment and lined Issues", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -329,34 +329,34 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); commits.mockReturnValueOnce( - Promise.resolve([{ commit: { message: 'fixes #420' } }]) + Promise.resolve([{ commit: { message: "fixes #420" } }]) ); const commit = makeCommitFromMsg( - 'normal commit with no bump closes (#123)' + "normal commit with no bump closes (#123)" ); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: await log.normalizeCommits([commit]), - releaseNotes: '' + releaseNotes: "", }); expect(comment).toHaveBeenNthCalledWith( 2, expect.objectContaining({ - message: ':rocket: Issue was released in `1.0.0` :rocket:', + message: ":rocket: Issue was released in `1.0.0` :rocket:", pr: 420, - context: 'released' + context: "released", }) ); }); - test('should lock Issues', async () => { + test("should lock Issues", async () => { const releasedLabel = new ReleasedLabelPlugin({ lockIssues: true }); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -365,23 +365,23 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); const commit = makeCommitFromMsg( - 'normal commit with no bump (#123) closes #100' + "normal commit with no bump (#123) closes #100" ); await autoHooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: await log.normalizeCommits([commit]), - releaseNotes: '' + releaseNotes: "", }); expect(lockIssue).toHaveBeenCalled(); }); - test('should not lock Issues for canaries', async () => { + test("should not lock Issues for canaries", async () => { const releasedLabel = new ReleasedLabelPlugin(); const autoHooks = makeHooks(); releasedLabel.apply(({ @@ -390,17 +390,17 @@ describe('release label plugin', () => { logger: dummyLog(), options: {}, comment, - git + git, } as unknown) as Auto); const commit = makeCommitFromMsg( - 'normal commit with no bump (#123) closes #100' + "normal commit with no bump (#123) closes #100" ); await autoHooks.afterRelease.promise({ - lastRelease: '0.1.0', - newVersion: '1.0.0-canary', + lastRelease: "0.1.0", + newVersion: "1.0.0-canary", commits: await log.normalizeCommits([commit]), - releaseNotes: '' + releaseNotes: "", }); expect(lockIssue).not.toHaveBeenCalled(); diff --git a/plugins/released/src/index.ts b/plugins/released/src/index.ts index f93c2135d..8f190facb 100644 --- a/plugins/released/src/index.ts +++ b/plugins/released/src/index.ts @@ -1,7 +1,7 @@ -import { Auto, IPlugin, validatePluginConfiguration } from '@auto-it/core'; -import { IExtendedCommit } from '@auto-it/core/dist/log-parse'; -import merge from 'deepmerge'; -import * as t from 'io-ts'; +import { Auto, IPlugin, validatePluginConfiguration } from "@auto-it/core"; +import { IExtendedCommit } from "@auto-it/core/dist/log-parse"; +import merge from "deepmerge"; +import * as t from "io-ts"; const pluginOptions = t.partial({ /** Message to use when posting on issues and pull requests */ @@ -11,29 +11,29 @@ const pluginOptions = t.partial({ /** The label to add to issues and pull requests that are in a prerelease */ prereleaseLabel: t.string, /** Whether to lock the issue once the pull request has been released */ - lockIssues: t.boolean + lockIssues: t.boolean, }); export type IReleasedLabelPluginOptions = t.TypeOf; -const TYPE = '%TYPE'; -const VERSION = '%VERSION'; +const TYPE = "%TYPE"; +const VERSION = "%VERSION"; const defaultOptions: Required = { - label: 'released', - prereleaseLabel: 'prerelease', + label: "released", + prereleaseLabel: "prerelease", lockIssues: false, - message: `:rocket: ${TYPE} was released in \`${VERSION}\` :rocket:` + message: `:rocket: ${TYPE} was released in \`${VERSION}\` :rocket:`, }; const closeIssue = /(?:Close|Closes|Closed|Fix|Fixes|Fixed|Resolve|Resolves|Resolved)\s((?:#\d+(?:,\s)?)+)/gi; /** Determine if string is a canary version */ -const isCanary = (version: string) => version.match('canary'); +const isCanary = (version: string) => version.match("canary"); /** Comment on merged pull requests and issues with the new version */ export default class ReleasedLabelPlugin implements IPlugin { /** The name of the plugin */ - name = 'released'; + name = "released"; /** The options of the plugin */ readonly options: Required; @@ -47,20 +47,20 @@ export default class ReleasedLabelPlugin implements IPlugin { /** Tap into auto plugin points. */ apply(auto: Auto) { - auto.hooks.modifyConfig.tap(this.name, config => { - if (!config.labels.find(l => l.name === this.options.label)) { + auto.hooks.modifyConfig.tap(this.name, (config) => { + if (!config.labels.find((l) => l.name === this.options.label)) { config.labels.push({ name: this.options.label, - description: 'This issue/pull request has been released.', - releaseType: 'none' + description: "This issue/pull request has been released.", + releaseType: "none", }); } - if (!config.labels.find(l => l.name === this.options.prereleaseLabel)) { + if (!config.labels.find((l) => l.name === this.options.prereleaseLabel)) { config.labels.push({ name: this.options.prereleaseLabel, - description: 'This change is available in a prerelease.', - releaseType: 'none' + description: "This change is available in a prerelease.", + releaseType: "none", }); } @@ -87,9 +87,9 @@ export default class ReleasedLabelPlugin implements IPlugin { } const skipReleaseLabels = ( - auto.config?.labels.filter(l => l.releaseType === 'skip') || [] - ).map(l => l.name); - const isSkipped = head.labels.find(label => + auto.config?.labels.filter((l) => l.releaseType === "skip") || [] + ).map((l) => l.name); + const isSkipped = head.labels.find((label) => skipReleaseLabels.includes(label) ); @@ -98,11 +98,11 @@ export default class ReleasedLabelPlugin implements IPlugin { } const isPrerelease = Array.isArray(response) - ? response.some(r => r?.data.prerelease) + ? response.some((r) => r?.data.prerelease) : response?.data.prerelease; await Promise.all( - commits.map(async commit => + commits.map(async (commit) => this.addReleased(auto, commit, newVersion, isPrerelease) ) ); @@ -131,34 +131,34 @@ export default class ReleasedLabelPlugin implements IPlugin { auto, newVersion, prOrIssue: commit.pullRequest.number, - isPrerelease + isPrerelease, }); const pr = await auto.git!.getPullRequest(commit.pullRequest.number); - pr.data.body.split('\n').map(line => messages.push(line)); + pr.data.body.split("\n").map((line) => messages.push(line)); const commitsInPr = await auto.git!.getCommitsForPR( commit.pullRequest.number ); - commitsInPr.map(c => messages.push(c.commit.message)); + commitsInPr.map((c) => messages.push(c.commit.message)); } const issues = messages - .map(message => message.match(closeIssue)) + .map((message) => message.match(closeIssue)) .filter((r): r is string[] => Boolean(r)) .reduce((all, arr) => [...all, ...arr], []) - .map(issue => issue.match(/#(\d+)/i)) + .map((issue) => issue.match(/#(\d+)/i)) .filter((r: RegExpMatchArray | null): r is RegExpMatchArray => Boolean(r)) - .map(match => Number(match[1])); + .map((match) => Number(match[1])); await Promise.all( - issues.map(async issue => { + issues.map(async (issue) => { await this.addCommentAndLabel({ auto, newVersion, prOrIssue: issue, isIssue: true, - isPrerelease + isPrerelease, }); if (this.options.lockIssues && !isCanary(newVersion) && !isPrerelease) { @@ -174,7 +174,7 @@ export default class ReleasedLabelPlugin implements IPlugin { newVersion, prOrIssue, isIssue = false, - isPrerelease = false + isPrerelease = false, }: { /** Reference to auto instance */ auto: Auto; @@ -189,7 +189,7 @@ export default class ReleasedLabelPlugin implements IPlugin { }) { // leave a comment with the new version const message = this.createReleasedComment(isIssue, newVersion); - await auto.comment({ message, pr: prOrIssue, context: 'released' }); + await auto.comment({ message, pr: prOrIssue, context: "released" }); // Do not add released to issue/label for canary versions if (isCanary(newVersion)) { @@ -215,7 +215,7 @@ export default class ReleasedLabelPlugin implements IPlugin { /** Create a comment that fits the context (pr of issue) */ private createReleasedComment(isIssue: boolean, version: string) { return this.options.message - .replace(new RegExp(TYPE, 'g'), isIssue ? 'Issue' : 'PR') - .replace(new RegExp(VERSION, 'g'), version); + .replace(new RegExp(TYPE, "g"), isIssue ? "Issue" : "PR") + .replace(new RegExp(VERSION, "g"), version); } } diff --git a/plugins/s3/__tests__/s3.test.ts b/plugins/s3/__tests__/s3.test.ts index a37c85df7..91dac4ebd 100644 --- a/plugins/s3/__tests__/s3.test.ts +++ b/plugins/s3/__tests__/s3.test.ts @@ -1,17 +1,17 @@ -import Auto from '@auto-it/core'; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; +import Auto from "@auto-it/core"; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; -import S3, { IUploadAssetsPluginOptions } from '../src'; +import S3, { IUploadAssetsPluginOptions } from "../src"; -jest.mock('aws-cli-js'); +jest.mock("aws-cli-js"); -describe('S3 Plugin', () => { - test('should warn about keys', () => { +describe("S3 Plugin", () => { + test("should warn about keys", () => { const options: IUploadAssetsPluginOptions = { - bucket: 'BUCKET_NAME', - region: 'us-west-2', - files: [['test-files', 'andrew-test']] + bucket: "BUCKET_NAME", + region: "us-west-2", + files: [["test-files", "andrew-test"]], }; const plugin = new S3(options); const hooks = makeHooks(); @@ -23,11 +23,11 @@ describe('S3 Plugin', () => { expect(checkEnv).toHaveBeenCalledTimes(3); }); - test('should accept a options object', () => { + test("should accept a options object", () => { const options: IUploadAssetsPluginOptions = { - bucket: 'BUCKET_NAME', - region: 'us-west-2', - files: [['test-files', 'andrew-test']] + bucket: "BUCKET_NAME", + region: "us-west-2", + files: [["test-files", "andrew-test"]], }; const plugin = new S3(options); @@ -35,18 +35,18 @@ describe('S3 Plugin', () => { expect(plugin.options).toStrictEqual([options]); }); - test('should accept an array options objects', () => { + test("should accept an array options objects", () => { const options: IUploadAssetsPluginOptions[] = [ { - bucket: 'BUCKET_NAME', - region: 'us-west-2', - files: [['test-files', 'andrew-test']] + bucket: "BUCKET_NAME", + region: "us-west-2", + files: [["test-files", "andrew-test"]], }, { - bucket: 'BUCKET_NAME', - region: 'us-west-2', - files: [[__dirname, 'andrew-test']] - } + bucket: "BUCKET_NAME", + region: "us-west-2", + files: [[__dirname, "andrew-test"]], + }, ]; const plugin = new S3(options); @@ -54,90 +54,90 @@ describe('S3 Plugin', () => { expect(plugin.options).toStrictEqual(options); }); - test('should try to write a file to s3 using aws CLI', async () => { - process.env.AWS_ACCESS_KEY = 'AWS_ACCESS_KEY'; - process.env.AWS_SECRET_KEY = 'AWS_SECRET_KEY'; - process.env.AWS_SESSION_TOKEN = 'AWS_SESSION_TOKEN'; + test("should try to write a file to s3 using aws CLI", async () => { + process.env.AWS_ACCESS_KEY = "AWS_ACCESS_KEY"; + process.env.AWS_SECRET_KEY = "AWS_SECRET_KEY"; + process.env.AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN"; const hooks = makeHooks(); const plugin = new S3({ - bucket: 'BUCKET_NAME', - region: 'REGION_NAME', - files: [['test-files', 'andrew-test']] + bucket: "BUCKET_NAME", + region: "REGION_NAME", + files: [["test-files", "andrew-test"]], }); plugin.apply({ hooks, - logger: dummyLog() + logger: dummyLog(), } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '' + releaseNotes: "", }); // @ts-ignore expect(plugin.aws.command).toHaveBeenCalled(); }); - test('should replace version', async () => { - process.env.AWS_ACCESS_KEY = 'AWS_ACCESS_KEY'; - process.env.AWS_SECRET_KEY = 'AWS_SECRET_KEY'; - process.env.AWS_SESSION_TOKEN = 'AWS_SESSION_TOKEN'; + test("should replace version", async () => { + process.env.AWS_ACCESS_KEY = "AWS_ACCESS_KEY"; + process.env.AWS_SECRET_KEY = "AWS_SECRET_KEY"; + process.env.AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN"; const hooks = makeHooks(); const plugin = new S3({ - bucket: 'BUCKET_NAME', - region: 'REGION_NAME', - files: [['test-files', '$VERSION/andrew-test']] + bucket: "BUCKET_NAME", + region: "REGION_NAME", + files: [["test-files", "$VERSION/andrew-test"]], }); plugin.apply({ hooks, - logger: dummyLog() + logger: dummyLog(), } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '' + releaseNotes: "", }); // @ts-ignore expect(plugin.aws.command).toHaveBeenCalledWith( - 's3 sync test-files s3://BUCKET_NAME/1.0.0/andrew-test --region REGION_NAME' + "s3 sync test-files s3://BUCKET_NAME/1.0.0/andrew-test --region REGION_NAME" ); }); test('should not overwrite when "overwrite" is set to false', async () => { - process.env.AWS_ACCESS_KEY = 'AWS_ACCESS_KEY'; - process.env.AWS_SECRET_KEY = 'AWS_SECRET_KEY'; - process.env.AWS_SESSION_TOKEN = 'AWS_SESSION_TOKEN'; + process.env.AWS_ACCESS_KEY = "AWS_ACCESS_KEY"; + process.env.AWS_SECRET_KEY = "AWS_SECRET_KEY"; + process.env.AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN"; const hooks = makeHooks(); const plugin = new S3({ - bucket: 'BUCKET_NAME', - region: 'REGION_NAME', + bucket: "BUCKET_NAME", + region: "REGION_NAME", overwrite: false, - files: [['test-files', 'andrew-test']] + files: [["test-files", "andrew-test"]], }); plugin.apply({ hooks, - logger: dummyLog() + logger: dummyLog(), } as Auto); // @ts-ignore plugin.aws.command.mockReturnValueOnce(true); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '' + releaseNotes: "", }); // @ts-ignore diff --git a/plugins/s3/src/index.ts b/plugins/s3/src/index.ts index 4a59d7b98..3c7092bbb 100644 --- a/plugins/s3/src/index.ts +++ b/plugins/s3/src/index.ts @@ -1,6 +1,6 @@ -import { Auto, IPlugin, validatePluginConfiguration } from '@auto-it/core'; -import { Aws, Options } from 'aws-cli-js'; -import * as t from 'io-ts'; +import { Auto, IPlugin, validatePluginConfiguration } from "@auto-it/core"; +import { Aws, Options } from "aws-cli-js"; +import * as t from "io-ts"; const pluginOptions = t.intersection([ t.interface({ @@ -9,12 +9,12 @@ const pluginOptions = t.intersection([ /** S3 Region to post the asset to */ region: t.string, /** Paths to the files to upload to S3 */ - files: t.array(t.tuple([t.string, t.string])) + files: t.array(t.tuple([t.string, t.string])), }), t.partial({ /** Whether this plugin should overwrite files on S3 */ - overwrite: t.boolean - }) + overwrite: t.boolean, + }), ]); export type IUploadAssetsPluginOptions = t.TypeOf; @@ -25,7 +25,7 @@ export type ConfigOptions = t.TypeOf; /** Post your built artifacts to s3 during `auto release` */ export default class S3Plugin implements IPlugin { /** The name of the plugin */ - name = 's3'; + name = "s3"; /** The options of the plugin */ private readonly options: IUploadAssetsPluginOptions[]; @@ -52,7 +52,7 @@ export default class S3Plugin implements IPlugin { if (name === this.name || name === `@auto-it/${this.name}`) { if (Array.isArray(options)) { const errors = await Promise.all( - options.map(o => + options.map((o) => validatePluginConfiguration(this.name, pluginOptions, o) ) ); @@ -65,14 +65,16 @@ export default class S3Plugin implements IPlugin { }); auto.hooks.beforeRun.tap(this.name, () => { - auto.checkEnv(this.name, 'AWS_ACCESS_KEY'); - auto.checkEnv(this.name, 'AWS_SECRET_KEY'); - auto.checkEnv(this.name, 'AWS_SESSION_TOKEN'); + auto.checkEnv(this.name, "AWS_ACCESS_KEY"); + auto.checkEnv(this.name, "AWS_SECRET_KEY"); + auto.checkEnv(this.name, "AWS_SESSION_TOKEN"); }); auto.hooks.afterRelease.tapPromise(this.name, async ({ newVersion }) => { await Promise.all( - this.options.map(option => this.processBucket(auto, newVersion, option)) + this.options.map((option) => + this.processBucket(auto, newVersion, option) + ) ); }); } @@ -85,7 +87,7 @@ export default class S3Plugin implements IPlugin { /** Upload files to a bucket */ private async processBucket( auto: Auto, - newVersion = '', + newVersion = "", options: IUploadAssetsPluginOptions ) { const { bucket, region, files, overwrite = true } = options; diff --git a/plugins/slack/__tests__/slack.test.ts b/plugins/slack/__tests__/slack.test.ts index bb57b383a..6f60a7000 100644 --- a/plugins/slack/__tests__/slack.test.ts +++ b/plugins/slack/__tests__/slack.test.ts @@ -1,15 +1,15 @@ -import Auto from '@auto-it/core'; -import makeCommitFromMsg from '@auto-it/core/src/__tests__/make-commit-from-msg'; -import { dummyLog } from '@auto-it/core/src/utils/logger'; -import { makeHooks } from '@auto-it/core/src/utils/make-hooks'; -import { defaultLabels } from '@auto-it/core/dist/release'; -import { execSync } from 'child_process'; +import Auto from "@auto-it/core"; +import makeCommitFromMsg from "@auto-it/core/src/__tests__/make-commit-from-msg"; +import { dummyLog } from "@auto-it/core/src/utils/logger"; +import { makeHooks } from "@auto-it/core/src/utils/make-hooks"; +import { defaultLabels } from "@auto-it/core/dist/release"; +import { execSync } from "child_process"; -import SlackPlugin from '../src'; +import SlackPlugin from "../src"; const fetchSpy = jest.fn(); // @ts-ignore -jest.mock('node-fetch', () => (...args) => { +jest.mock("node-fetch", () => (...args) => { fetchSpy(...args); }); @@ -18,77 +18,77 @@ beforeEach(() => { }); // For the purpose of this test, we use the current branch as the "prerelease" branch to fake being on a "next" branch -const nextBranch = execSync('git rev-parse --abbrev-ref HEAD', { - encoding: 'utf8' +const nextBranch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", }).trim(); const mockGit = { options: { - owner: 'adierkens', - repo: 'test' + owner: "adierkens", + repo: "test", }, getProject: () => ({ - html_url: 'https://github.custom.com/adierkens/test' - }) + html_url: "https://github.custom.com/adierkens/test", + }), }; const mockAuto = ({ git: mockGit, - logger: dummyLog() + logger: dummyLog(), } as unknown) as Auto; -describe('postToSlack', () => { +describe("postToSlack", () => { test("doesn't post with no new version", async () => { - const plugin = new SlackPlugin('https://custom-slack-url'); + const plugin = new SlackPlugin("https://custom-slack-url"); const hooks = makeHooks(); - jest.spyOn(plugin, 'postToSlack').mockImplementation(); + jest.spyOn(plugin, "postToSlack").mockImplementation(); // @ts-ignore plugin.apply({ hooks } as Auto); await hooks.afterRelease.promise({ - lastRelease: '0.1.0', + lastRelease: "0.1.0", commits: [], - releaseNotes: '# My Notes' + releaseNotes: "# My Notes", }); expect(plugin.postToSlack).not.toHaveBeenCalled(); }); test("doesn't post with no commits", async () => { - const plugin = new SlackPlugin('https://custom-slack-url'); + const plugin = new SlackPlugin("https://custom-slack-url"); const hooks = makeHooks(); - jest.spyOn(plugin, 'postToSlack').mockImplementation(); + jest.spyOn(plugin, "postToSlack").mockImplementation(); // @ts-ignore plugin.apply({ hooks, options: {} } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '# My Notes' + releaseNotes: "# My Notes", }); expect(plugin.postToSlack).not.toHaveBeenCalled(); }); test("doesn't post with skip release label", async () => { - const plugin = new SlackPlugin('https://custom-slack-url'); + const plugin = new SlackPlugin("https://custom-slack-url"); const hooks = makeHooks(); - jest.spyOn(plugin, 'postToSlack').mockImplementation(); + jest.spyOn(plugin, "postToSlack").mockImplementation(); // @ts-ignore plugin.apply({ hooks, options: {}, - config: { labels: defaultLabels } + config: { labels: defaultLabels }, } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', - commits: [makeCommitFromMsg('skipped', { labels: ['skip-release'] })], - releaseNotes: '# My Notes' + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [makeCommitFromMsg("skipped", { labels: ["skip-release"] })], + releaseNotes: "# My Notes", }); expect(plugin.postToSlack).not.toHaveBeenCalled(); @@ -99,16 +99,16 @@ describe('postToSlack', () => { const plugin = new SlackPlugin({ url: undefined }); const hooks = makeHooks(); - jest.spyOn(plugin, 'postToSlack').mockImplementation(); + jest.spyOn(plugin, "postToSlack").mockImplementation(); // @ts-ignore plugin.apply({ hooks, options: {} } as Auto); await expect( hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', - commits: [makeCommitFromMsg('a patch')], - releaseNotes: '# My Notes' + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [makeCommitFromMsg("a patch")], + releaseNotes: "# My Notes", }) ).rejects.toBeInstanceOf(Error); }); @@ -116,11 +116,11 @@ describe('postToSlack', () => { test("doesn't post when prelease branch and using default prereleasePublish setting", async () => { // @ts-ignore const plugin = new SlackPlugin({ - url: 'https://custom-slack-url' + url: "https://custom-slack-url", }); const hooks = makeHooks(); - jest.spyOn(plugin, 'postToSlack').mockImplementation(); + jest.spyOn(plugin, "postToSlack").mockImplementation(); // @ts-ignore plugin.apply({ ...mockAuto, @@ -128,15 +128,15 @@ describe('postToSlack', () => { options: {}, config: { prereleaseBranches: [nextBranch], - labels: defaultLabels - } + labels: defaultLabels, + }, } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', - commits: [makeCommitFromMsg('a patch')], - releaseNotes: '# My Notes' + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [makeCommitFromMsg("a patch")], + releaseNotes: "# My Notes", }); expect(fetchSpy).not.toHaveBeenCalled(); }); @@ -144,12 +144,12 @@ describe('postToSlack', () => { test("doesn't post when prelease branch setting is false", async () => { // @ts-ignore const plugin = new SlackPlugin({ - url: 'https://custom-slack-url', - publishPreRelease: false + url: "https://custom-slack-url", + publishPreRelease: false, }); const hooks = makeHooks(); - jest.spyOn(plugin, 'postToSlack').mockImplementation(); + jest.spyOn(plugin, "postToSlack").mockImplementation(); // @ts-ignore plugin.apply({ ...mockAuto, @@ -157,137 +157,137 @@ describe('postToSlack', () => { options: {}, config: { prereleaseBranches: [nextBranch], - labels: defaultLabels - } + labels: defaultLabels, + }, } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', - commits: [makeCommitFromMsg('a patch')], - releaseNotes: '# My Notes' + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [makeCommitFromMsg("a patch")], + releaseNotes: "# My Notes", }); expect(fetchSpy).not.toHaveBeenCalled(); }); - test('posts when prelease branch setting is true', async () => { + test("posts when prelease branch setting is true", async () => { // @ts-ignore const plugin = new SlackPlugin({ - url: 'https://custom-slack-url', - publishPreRelease: true + url: "https://custom-slack-url", + publishPreRelease: true, }); const hooks = makeHooks(); - jest.spyOn(plugin, 'postToSlack').mockImplementation(); + jest.spyOn(plugin, "postToSlack").mockImplementation(); // @ts-ignore plugin.apply({ ...mockAuto, hooks, options: {}, - config: { prereleaseBranches: ['next'], labels: defaultLabels } + config: { prereleaseBranches: ["next"], labels: defaultLabels }, } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', - commits: [makeCommitFromMsg('a patch')], - releaseNotes: '# My Notes' + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [makeCommitFromMsg("a patch")], + releaseNotes: "# My Notes", }); expect(plugin.postToSlack).toHaveBeenCalledTimes(1); }); - test('should warn when no token', async () => { - const plugin = new SlackPlugin('https://custom-slack-url'); + test("should warn when no token", async () => { + const plugin = new SlackPlugin("https://custom-slack-url"); const logger = dummyLog(); - jest.spyOn(logger.verbose, 'warn').mockImplementation(); - process.env.SLACK_TOKEN = ''; + jest.spyOn(logger.verbose, "warn").mockImplementation(); + process.env.SLACK_TOKEN = ""; await plugin.postToSlack( { ...mockAuto, logger } as Auto, - '1.0.0', - '# My Notes\n- PR [some link](google.com)' + "1.0.0", + "# My Notes\n- PR [some link](google.com)" ); expect(logger.verbose.warn).toHaveBeenCalled(); }); - test('should call slack api with minimal config', async () => { - const plugin = new SlackPlugin('https://custom-slack-url'); - process.env.SLACK_TOKEN = 'MY_TOKEN'; + test("should call slack api with minimal config", async () => { + const plugin = new SlackPlugin("https://custom-slack-url"); + process.env.SLACK_TOKEN = "MY_TOKEN"; await plugin.postToSlack( mockAuto, - '1.0.0', - '# My Notes\n- PR [some link](google.com)' + "1.0.0", + "# My Notes\n- PR [some link](google.com)" ); expect(fetchSpy).toHaveBeenCalled(); expect(fetchSpy.mock.calls[0][0]).toBe( - 'https://custom-slack-url?token=MY_TOKEN' + "https://custom-slack-url?token=MY_TOKEN" ); expect(fetchSpy.mock.calls[0][1].body).toMatchSnapshot(); }); - test('should call slack api', async () => { - const plugin = new SlackPlugin({ url: 'https://custom-slack-url' }); + test("should call slack api", async () => { + const plugin = new SlackPlugin({ url: "https://custom-slack-url" }); const hooks = makeHooks(); - process.env.SLACK_TOKEN = 'MY_TOKEN'; + process.env.SLACK_TOKEN = "MY_TOKEN"; plugin.apply({ hooks, options: {}, ...mockAuto } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', - commits: [makeCommitFromMsg('a patch')], - releaseNotes: '# My Notes\n- PR [some link](google.com)' + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [makeCommitFromMsg("a patch")], + releaseNotes: "# My Notes\n- PR [some link](google.com)", }); expect(fetchSpy).toHaveBeenCalled(); expect(fetchSpy.mock.calls[0][0]).toBe( - 'https://custom-slack-url?token=MY_TOKEN' + "https://custom-slack-url?token=MY_TOKEN" ); expect(fetchSpy.mock.calls[0][1].body).toMatchSnapshot(); }); - test('should call slack api with custom atTarget', async () => { + test("should call slack api with custom atTarget", async () => { const plugin = new SlackPlugin({ - url: 'https://custom-slack-url', - atTarget: 'here' + url: "https://custom-slack-url", + atTarget: "here", }); const hooks = makeHooks(); - process.env.SLACK_TOKEN = 'MY_TOKEN'; + process.env.SLACK_TOKEN = "MY_TOKEN"; plugin.apply({ hooks, options: {}, ...mockAuto } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', - commits: [makeCommitFromMsg('a patch')], - releaseNotes: '# My Notes\n- PR [some link](google.com)' + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [makeCommitFromMsg("a patch")], + releaseNotes: "# My Notes\n- PR [some link](google.com)", }); expect(fetchSpy).toHaveBeenCalled(); expect(fetchSpy.mock.calls[0][0]).toBe( - 'https://custom-slack-url?token=MY_TOKEN' + "https://custom-slack-url?token=MY_TOKEN" ); - expect(fetchSpy.mock.calls[0][1].body.includes('@here')).toBe(true); + expect(fetchSpy.mock.calls[0][1].body.includes("@here")).toBe(true); expect(fetchSpy.mock.calls[0][1].body).toMatchSnapshot(); }); - test('should call slack api in env var', async () => { - process.env.SLACK_WEBHOOK_URL = 'https://foo.bar'; + test("should call slack api in env var", async () => { + process.env.SLACK_WEBHOOK_URL = "https://foo.bar"; const plugin = new SlackPlugin(); const hooks = makeHooks(); - process.env.SLACK_TOKEN = 'MY_TOKEN'; + process.env.SLACK_TOKEN = "MY_TOKEN"; plugin.apply({ hooks, options: {}, ...mockAuto } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', - commits: [makeCommitFromMsg('a patch')], - releaseNotes: '# My Notes\n- PR [some link](google.com)' + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [makeCommitFromMsg("a patch")], + releaseNotes: "# My Notes\n- PR [some link](google.com)", }); expect(fetchSpy).toHaveBeenCalled(); - expect(fetchSpy.mock.calls[0][0]).toBe('https://foo.bar?token=MY_TOKEN'); + expect(fetchSpy.mock.calls[0][0]).toBe("https://foo.bar?token=MY_TOKEN"); expect(fetchSpy.mock.calls[0][1].body).toMatchSnapshot(); }); }); diff --git a/plugins/slack/src/index.ts b/plugins/slack/src/index.ts index dd86a3246..9fc5044ec 100644 --- a/plugins/slack/src/index.ts +++ b/plugins/slack/src/index.ts @@ -1,28 +1,28 @@ -import { githubToSlack } from '@atomist/slack-messages'; +import { githubToSlack } from "@atomist/slack-messages"; import { Auto, IPlugin, getCurrentBranch, InteractiveInit, - validatePluginConfiguration -} from '@auto-it/core'; -import fetch from 'node-fetch'; -import join from 'url-join'; -import * as t from 'io-ts'; + validatePluginConfiguration, +} from "@auto-it/core"; +import fetch from "node-fetch"; +import join from "url-join"; +import * as t from "io-ts"; /** Transform markdown into slack friendly text */ const sanitizeMarkdown = (markdown: string) => githubToSlack(markdown) - .split('\n') - .map(line => { + .split("\n") + .map((line) => { // Strip out the ### prefix and replace it with ** to make it bold - if (line.startsWith('#')) { - return `*${line.replace(/^[#]+/, '')}*`; + if (line.startsWith("#")) { + return `*${line.replace(/^[#]+/, "")}*`; } return line; }) - .join('\n'); + .join("\n"); const pluginOptions = t.partial({ /** URL of the slack to post to */ @@ -30,7 +30,7 @@ const pluginOptions = t.partial({ /** Who to bother when posting to the channel */ atTarget: t.string, /** Allow users to opt into having prereleases posted to slack */ - publishPreRelease: t.boolean + publishPreRelease: t.boolean, }); export type ISlackPluginOptions = t.TypeOf; @@ -38,34 +38,34 @@ export type ISlackPluginOptions = t.TypeOf; /** Post your release notes to Slack during `auto release` */ export default class SlackPlugin implements IPlugin { /** The name of the plugin */ - name = 'slack'; + name = "slack"; /** The options of the plugin */ readonly options: ISlackPluginOptions; /** Initialize the plugin with it's options */ constructor(options: ISlackPluginOptions | string = {}) { - if (typeof options === 'string') { - this.options = { url: options, atTarget: 'channel' }; + if (typeof options === "string") { + this.options = { url: options, atTarget: "channel" }; } else { this.options = { - url: process.env.SLACK_WEBHOOK_URL || options.url || '', - atTarget: options.atTarget ? options.atTarget : 'channel', + url: process.env.SLACK_WEBHOOK_URL || options.url || "", + atTarget: options.atTarget ? options.atTarget : "channel", publishPreRelease: options.publishPreRelease ? options.publishPreRelease - : false + : false, }; } } /** Custom initialization for this plugin */ init(initializer: InteractiveInit) { - initializer.hooks.createEnv.tapPromise(this.name, async vars => [ + initializer.hooks.createEnv.tapPromise(this.name, async (vars) => [ ...vars, { - variable: 'SLACK_WEBHOOK_URL', - message: 'What is the root url of your slack hook? ()' - } + variable: "SLACK_WEBHOOK_URL", + message: "What is the root url of your slack hook? ()", + }, ]); } @@ -73,7 +73,7 @@ export default class SlackPlugin implements IPlugin { apply(auto: Auto) { auto.hooks.validateConfig.tapPromise(this.name, async (name, options) => { // If it's a string thats valid config - if (name === this.name && typeof options !== 'string') { + if (name === this.name && typeof options !== "string") { return validatePluginConfiguration(this.name, pluginOptions, options); } }); @@ -102,9 +102,9 @@ export default class SlackPlugin implements IPlugin { } const skipReleaseLabels = ( - auto.config?.labels.filter(l => l.releaseType === 'skip') || [] - ).map(l => l.name); - const isSkipped = head.labels.find(label => + auto.config?.labels.filter((l) => l.releaseType === "skip") || [] + ).map((l) => l.name); + const isSkipped = head.labels.find((label) => skipReleaseLabels.includes(label) ); @@ -113,7 +113,7 @@ export default class SlackPlugin implements IPlugin { } if (!this.options.url) { - throw new Error('Slack url must be set to post a message to slack.'); + throw new Error("Slack url must be set to post a message to slack."); } await this.postToSlack(auto, newVersion, releaseNotes); @@ -127,30 +127,30 @@ export default class SlackPlugin implements IPlugin { return; } - auto.logger.verbose.info('Posting release notes to slack.'); + auto.logger.verbose.info("Posting release notes to slack."); const project = await auto.git.getProject(); const body = sanitizeMarkdown(releaseNotes); const token = process.env.SLACK_TOKEN; - const releaseUrl = join(project.html_url, 'releases/tag', newVersion); + const releaseUrl = join(project.html_url, "releases/tag", newVersion); const atTarget = this.options.atTarget; if (!token) { - auto.logger.verbose.warn('Slack may need a token to send a message'); + auto.logger.verbose.warn("Slack may need a token to send a message"); } - await fetch(`${this.options.url}${token ? `?token=${token}` : ''}`, { - method: 'POST', + await fetch(`${this.options.url}${token ? `?token=${token}` : ""}`, { + method: "POST", body: JSON.stringify({ text: [ `@${atTarget}: New release *<${releaseUrl}|${newVersion}>*`, - body - ].join('\n'), - link_names: 1 + body, + ].join("\n"), + link_names: 1, }), - headers: { 'Content-Type': 'application/json' } + headers: { "Content-Type": "application/json" }, }); - auto.logger.verbose.info('Posted release notes to slack.'); + auto.logger.verbose.info("Posted release notes to slack."); } } diff --git a/plugins/twitter/__tests__/twitter.test.ts b/plugins/twitter/__tests__/twitter.test.ts index 881dec807..025a76290 100644 --- a/plugins/twitter/__tests__/twitter.test.ts +++ b/plugins/twitter/__tests__/twitter.test.ts @@ -1,47 +1,47 @@ -import Auto, { SEMVER } from '@auto-it/core'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; -import endent from 'endent'; -import TwitterPlugin from '../src'; +import Auto, { SEMVER } from "@auto-it/core"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import endent from "endent"; +import TwitterPlugin from "../src"; const tweet = jest.fn(); // @ts-ignore -jest.mock('tweet-tweet', () => () => (m, cb) => tweet(m, cb())); +jest.mock("tweet-tweet", () => () => (m, cb) => tweet(m, cb())); const mockResponse = { - data: { html_url: 'https://foo.com' } + data: { html_url: "https://foo.com" }, } as any; const mockGit = { - options: { repo: 'Test-Repo' } + options: { repo: "Test-Repo" }, } as any; -test('Twitter Plugin should throw without proper tokens set', async () => { +test("Twitter Plugin should throw without proper tokens set", async () => { expect(() => new TwitterPlugin()).toThrow(); }); -describe('Twitter Plugin', () => { +describe("Twitter Plugin", () => { beforeAll(() => { - process.env.TWITTER_ACCESS_TOKEN = '1'; - process.env.TWITTER_ACCESS_TOKEN_SECRET = '2'; - process.env.TWITTER_CONSUMER_KEY = '3'; - process.env.TWITTER_CONSUMER_KEY_SECRET = '4'; + process.env.TWITTER_ACCESS_TOKEN = "1"; + process.env.TWITTER_ACCESS_TOKEN_SECRET = "2"; + process.env.TWITTER_CONSUMER_KEY = "3"; + process.env.TWITTER_CONSUMER_KEY_SECRET = "4"; }); beforeEach(() => { tweet.mockReset(); }); - test('should not throw with env variables set', async () => { + test("should not throw with env variables set", async () => { expect(() => new TwitterPlugin()).not.toThrow(); }); - test('should do nothing without a new version', async () => { + test("should do nothing without a new version", async () => { const plugin = new TwitterPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, git: mockGit } as Auto); await hooks.afterRelease.promise({ commits: [], - releaseNotes: '', - lastRelease: '1.0.0' + releaseNotes: "", + lastRelease: "1.0.0", }); expect(tweet).not.toHaveBeenCalled(); @@ -53,27 +53,27 @@ describe('Twitter Plugin', () => { plugin.apply({ hooks, git: mockGit } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.1', + newVersion: "1.0.1", commits: [], - releaseNotes: '', - lastRelease: '1.0.0', - response: mockResponse + releaseNotes: "", + lastRelease: "1.0.0", + response: mockResponse, }); expect(tweet).not.toHaveBeenCalled(); }); - test('should post message if threshold met', async () => { + test("should post message if threshold met", async () => { const plugin = new TwitterPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, git: mockGit } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.1.0', + newVersion: "1.1.0", commits: [], - releaseNotes: '', - lastRelease: '1.0.0', - response: mockResponse + releaseNotes: "", + lastRelease: "1.0.0", + response: mockResponse, }); expect(tweet).toHaveBeenCalled(); @@ -85,61 +85,61 @@ describe('Twitter Plugin', () => { plugin.apply({ hooks, git: mockGit } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', + newVersion: "1.0.0", commits: [], - releaseNotes: '', - lastRelease: '1.0.0', - response: mockResponse + releaseNotes: "", + lastRelease: "1.0.0", + response: mockResponse, }); expect(tweet).not.toHaveBeenCalled(); }); - test('should be able to configure threshold', async () => { + test("should be able to configure threshold", async () => { const plugin = new TwitterPlugin({ threshold: SEMVER.major }); const hooks = makeHooks(); plugin.apply({ hooks, git: mockGit } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.1.0', + newVersion: "1.1.0", commits: [], - releaseNotes: '', - lastRelease: '1.0.0', - response: mockResponse + releaseNotes: "", + lastRelease: "1.0.0", + response: mockResponse, }); expect(tweet).not.toHaveBeenCalled(); }); - test('should be able to configure message', async () => { + test("should be able to configure message", async () => { const plugin = new TwitterPlugin({ message: endent` v%version of %package was released! %link - ` + `, }); const hooks = makeHooks(); plugin.apply({ hooks, git: mockGit } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.1.0', + newVersion: "1.1.0", commits: [], - releaseNotes: '', - lastRelease: '1.0.0', - response: mockResponse + releaseNotes: "", + lastRelease: "1.0.0", + response: mockResponse, }); expect(tweet.mock.calls[0][0]).toMatchSnapshot(); }); - test('should post correct message', async () => { + test("should post correct message", async () => { const plugin = new TwitterPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, git: mockGit } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.1.0', + newVersion: "1.1.0", commits: [], releaseNotes: endent` #### 🐛 Bug Fix @@ -154,20 +154,20 @@ describe('Twitter Plugin', () => { - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) `, - lastRelease: '1.0.0', - response: mockResponse + lastRelease: "1.0.0", + response: mockResponse, }); expect(tweet.mock.calls[0][0]).toMatchSnapshot(); }); - test('should handle long release notes', async () => { + test("should handle long release notes", async () => { const plugin = new TwitterPlugin(); const hooks = makeHooks(); plugin.apply({ hooks, git: mockGit } as Auto); await hooks.afterRelease.promise({ - newVersion: '1.1.0', + newVersion: "1.1.0", commits: [], releaseNotes: endent` #### 🐛 Bug Fix @@ -187,8 +187,8 @@ describe('Twitter Plugin', () => { - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) `, - lastRelease: '1.0.0', - response: mockResponse + lastRelease: "1.0.0", + response: mockResponse, }); expect(tweet.mock.calls[0][0]).toMatchSnapshot(); diff --git a/plugins/twitter/src/index.ts b/plugins/twitter/src/index.ts index 4b1aa727c..09eff9725 100644 --- a/plugins/twitter/src/index.ts +++ b/plugins/twitter/src/index.ts @@ -2,14 +2,14 @@ import { Auto, IPlugin, SEMVER, - validatePluginConfiguration -} from '@auto-it/core'; -import endent from 'endent'; -import { diff, ReleaseType } from 'semver'; -import twitter from 'tweet-tweet'; -import tweetValidation from 'twitter-text'; -import { promisify } from 'util'; -import * as t from 'io-ts'; + validatePluginConfiguration, +} from "@auto-it/core"; +import endent from "endent"; +import { diff, ReleaseType } from "semver"; +import twitter from "tweet-tweet"; +import tweetValidation from "twitter-text"; +import { promisify } from "util"; +import * as t from "io-ts"; const pluginOptions = t.partial({ /** The message template to use to post to Twitter */ @@ -18,8 +18,8 @@ const pluginOptions = t.partial({ threshold: t.keyof({ patch: null, minor: null, - major: null - }) + major: null, + }), }); export type ITwitterPluginOptions = t.TypeOf; @@ -32,10 +32,10 @@ const defaults: Required = { %notes %link - ` + `, }; -const RELEASE_PRECEDENCE: ReleaseType[] = ['patch', 'minor', 'major']; +const RELEASE_PRECEDENCE: ReleaseType[] = ["patch", "minor", "major"]; /** Determine the release with the biggest semver change */ const isGreaterThan = (a: ReleaseType, b: ReleaseType) => @@ -43,11 +43,7 @@ const isGreaterThan = (a: ReleaseType, b: ReleaseType) => /** Remove the last line of text from a multiline string */ const removeLastLine = (text: string) => - text - .split('\n') - .slice(0, -1) - .join('\n') - .trim(); + text.split("\n").slice(0, -1).join("\n").trim(); interface MakeTweetArgs { /** The generated release notes for the release */ @@ -71,21 +67,21 @@ const makeTweet = ({ versionBump, newVersion, repo, - url + url, }: MakeTweetArgs) => { /** Replace all the variables in the message */ const build = (notes: string) => message - .replace('%release', versionBump) - .replace('%package', repo) - .replace('%version', newVersion) - .replace('%notes', notes) - .replace('%link', url); + .replace("%release", versionBump) + .replace("%package", repo) + .replace("%version", newVersion) + .replace("%notes", notes) + .replace("%link", url); let truncatedNotes = releaseNotes - .split('#### Authors')[0] - .replace(/#### /gm, '') - .replace(/\(?\[\S+\]\(\S+\)/gm, '') + .split("#### Authors")[0] + .replace(/#### /gm, "") + .replace(/\(?\[\S+\]\(\S+\)/gm, "") .trim(); let tweet = build(truncatedNotes); @@ -103,7 +99,7 @@ const makeTweet = ({ /** Post your release notes to twitter during `auto release` */ export default class TwitterPlugin implements IPlugin { /** The name of the plugin */ - name = 'twitter'; + name = "twitter"; /** The options of the plugin */ readonly options: Required; @@ -122,7 +118,7 @@ export default class TwitterPlugin implements IPlugin { !process.env.TWITTER_CONSUMER_KEY_SECRET ) { throw new Error( - 'Need all of the following secrets available on the environment: TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET, TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_KEY_SECRET' + "Need all of the following secrets available on the environment: TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET, TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_KEY_SECRET" ); } @@ -131,7 +127,7 @@ export default class TwitterPlugin implements IPlugin { accessToken: process.env.TWITTER_ACCESS_TOKEN, accessTokenSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET, consumerKey: process.env.TWITTER_CONSUMER_KEY, - consumerSecret: process.env.TWITTER_CONSUMER_KEY_SECRET + consumerSecret: process.env.TWITTER_CONSUMER_KEY_SECRET, }) ); } @@ -151,14 +147,14 @@ export default class TwitterPlugin implements IPlugin { return; } - const versionBump = diff(newVersion, lastRelease) || 'patch'; + const versionBump = diff(newVersion, lastRelease) || "patch"; if (isGreaterThan(this.options.threshold as ReleaseType, versionBump)) { return; } const url = Array.isArray(response) - ? response.map(r => `- ${r.data.html_url}`).join('\n') + ? response.map((r) => `- ${r.data.html_url}`).join("\n") : response.data.html_url; await this.tweet( @@ -168,7 +164,7 @@ export default class TwitterPlugin implements IPlugin { versionBump, newVersion, repo: auto.git.options.repo, - url + url, }) ); } diff --git a/plugins/upload-assets/__tests__/upload-assets.test.ts b/plugins/upload-assets/__tests__/upload-assets.test.ts index 73c15d191..2c7dfdcd5 100644 --- a/plugins/upload-assets/__tests__/upload-assets.test.ts +++ b/plugins/upload-assets/__tests__/upload-assets.test.ts @@ -1,15 +1,15 @@ -import Auto from '@auto-it/core'; -import { makeHooks } from '@auto-it/core/dist/utils/make-hooks'; -import { Octokit } from '@octokit/rest'; -import path from 'path'; +import Auto from "@auto-it/core"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import { Octokit } from "@octokit/rest"; +import path from "path"; -import { dummyLog } from '@auto-it/core/dist/utils/logger'; -import UploadAssets from '../src'; +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import UploadAssets from "../src"; -describe('Upload Assets Plugin', () => { - test('should do nothing without a response', async () => { +describe("Upload Assets Plugin", () => { + test("should do nothing without a response", async () => { const plugin = new UploadAssets([ - path.join(__dirname, './test-assets/macos') + path.join(__dirname, "./test-assets/macos"), ]); const hooks = makeHooks(); const uploadReleaseAsset = jest.fn(); @@ -17,22 +17,22 @@ describe('Upload Assets Plugin', () => { plugin.apply(({ hooks, logger: dummyLog(), - git: { github: { repos: { uploadReleaseAsset } } } + git: { github: { repos: { uploadReleaseAsset } } }, } as unknown) as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '' + releaseNotes: "", }); expect(uploadReleaseAsset).not.toHaveBeenCalled(); }); - test('should upload a single asset', async () => { + test("should upload a single asset", async () => { const plugin = new UploadAssets([ - path.join(__dirname, './test-assets/macos') + path.join(__dirname, "./test-assets/macos"), ]); const hooks = makeHooks(); const uploadReleaseAsset = jest.fn(); @@ -40,37 +40,37 @@ describe('Upload Assets Plugin', () => { plugin.apply(({ hooks, logger: dummyLog(), - git: { github: { repos: { uploadReleaseAsset } } } + git: { github: { repos: { uploadReleaseAsset } } }, } as unknown) as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '', + releaseNotes: "", response: { - data: { upload_url: 'https://foo.com' } - } as Octokit.Response + data: { upload_url: "https://foo.com" }, + } as Octokit.Response, }); expect(uploadReleaseAsset).toHaveBeenCalledWith( expect.objectContaining({ headers: { - 'content-length': 14044, - 'content-type': 'application/octet-stream' + "content-length": 14044, + "content-type": "application/octet-stream", }, - name: 'macos', - url: 'https://foo.com' + name: "macos", + url: "https://foo.com", }) ); }); - test('should upload multiple assets', async () => { + test("should upload multiple assets", async () => { const plugin = new UploadAssets({ assets: [ - path.join(__dirname, './test-assets/macos'), - path.join(__dirname, './test-assets/test.txt') - ] + path.join(__dirname, "./test-assets/macos"), + path.join(__dirname, "./test-assets/test.txt"), + ], }); const hooks = makeHooks(); const uploadReleaseAsset = jest.fn(); @@ -78,25 +78,25 @@ describe('Upload Assets Plugin', () => { plugin.apply(({ hooks, logger: dummyLog(), - git: { github: { repos: { uploadReleaseAsset } } } + git: { github: { repos: { uploadReleaseAsset } } }, } as unknown) as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '', + releaseNotes: "", response: { - data: { upload_url: 'https://foo.com' } - } as Octokit.Response + data: { upload_url: "https://foo.com" }, + } as Octokit.Response, }); expect(uploadReleaseAsset).toHaveBeenCalledTimes(2); }); - test('should upload multiple assets using a glob', async () => { + test("should upload multiple assets using a glob", async () => { const plugin = new UploadAssets({ - assets: [path.join(__dirname, './test-assets/test*.txt')] + assets: [path.join(__dirname, "./test-assets/test*.txt")], }); const hooks = makeHooks(); const uploadReleaseAsset = jest.fn(); @@ -104,25 +104,25 @@ describe('Upload Assets Plugin', () => { plugin.apply(({ hooks, logger: dummyLog(), - git: { github: { repos: { uploadReleaseAsset } } } + git: { github: { repos: { uploadReleaseAsset } } }, } as unknown) as Auto); await hooks.afterRelease.promise({ - newVersion: '1.0.0', - lastRelease: '0.1.0', + newVersion: "1.0.0", + lastRelease: "0.1.0", commits: [], - releaseNotes: '', + releaseNotes: "", response: { - data: { upload_url: 'https://foo.com' } - } as Octokit.Response + data: { upload_url: "https://foo.com" }, + } as Octokit.Response, }); expect(uploadReleaseAsset).toHaveBeenCalledTimes(2); expect(uploadReleaseAsset).toHaveBeenCalledWith( - expect.objectContaining({ name: 'test-2.txt' }) + expect.objectContaining({ name: "test-2.txt" }) ); expect(uploadReleaseAsset).toHaveBeenCalledWith( - expect.objectContaining({ name: 'test.txt' }) + expect.objectContaining({ name: "test.txt" }) ); }); }); diff --git a/plugins/upload-assets/src/index.ts b/plugins/upload-assets/src/index.ts index d1f9dc816..29c757b24 100644 --- a/plugins/upload-assets/src/index.ts +++ b/plugins/upload-assets/src/index.ts @@ -1,18 +1,18 @@ -import { Auto, IPlugin, validatePluginConfiguration } from '@auto-it/core'; -import endent from 'endent'; -import FileType from 'file-type'; -import fs from 'fs'; -import glob from 'fast-glob'; -import path from 'path'; -import { promisify } from 'util'; -import * as t from 'io-ts'; +import { Auto, IPlugin, validatePluginConfiguration } from "@auto-it/core"; +import endent from "endent"; +import FileType from "file-type"; +import fs from "fs"; +import glob from "fast-glob"; +import path from "path"; +import { promisify } from "util"; +import * as t from "io-ts"; const stat = promisify(fs.stat); const readFile = promisify(fs.readFile); const pluginOptions = t.interface({ /** Paths to assets to upload */ - assets: t.array(t.string) + assets: t.array(t.string), }); /** Convert shorthand options to noraml shape */ @@ -24,7 +24,7 @@ export type IUploadAssetsPluginOptions = t.TypeOf; /** Attach extra assets to a GitHub Release */ export default class UploadAssetsPlugin implements IPlugin { /** The name of the plugin */ - name = 'upload-assets'; + name = "upload-assets"; /** The options of the plugin */ readonly options: IUploadAssetsPluginOptions; @@ -52,12 +52,12 @@ export default class UploadAssetsPlugin implements IPlugin { auto.logger.log.info(endent` Uploading: - ${assets.map(asset => `\t- ${asset}`).join('\n')} + ${assets.map((asset) => `\t- ${asset}`).join("\n")} `); await Promise.all( - assets.map(async asset => { + assets.map(async (asset) => { if (!auto.git || !response) { return; } @@ -70,25 +70,25 @@ export default class UploadAssetsPlugin implements IPlugin { file, name: path.basename(asset), headers: { - 'content-length': stats.size, - 'content-type': type ? type.mime : 'application/octet-stream' - } + "content-length": stats.size, + "content-type": type ? type.mime : "application/octet-stream", + }, }; // Multiple releases were made if (Array.isArray(response)) { await Promise.all( - response.map(r => + response.map((r) => auto.git!.github.repos.uploadReleaseAsset({ ...options, - url: r.data.upload_url + url: r.data.upload_url, }) ) ); } else { await auto.git.github.repos.uploadReleaseAsset({ ...options, - url: response.data.upload_url + url: response.data.upload_url, }); }