1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tslib_1 = require("tslib");
|
4 | const dotenv_1 = tslib_1.__importDefault(require("dotenv"));
|
5 | const env_ci_1 = tslib_1.__importDefault(require("env-ci"));
|
6 | const fs_1 = tslib_1.__importDefault(require("fs"));
|
7 | const path_1 = tslib_1.__importDefault(require("path"));
|
8 | const terminal_link_1 = tslib_1.__importDefault(require("terminal-link"));
|
9 | const log_symbols_1 = tslib_1.__importDefault(require("log-symbols"));
|
10 | const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
11 | const semver_1 = require("semver");
|
12 | const endent_1 = tslib_1.__importDefault(require("endent"));
|
13 | const url_1 = require("url");
|
14 | const await_to_js_1 = tslib_1.__importDefault(require("await-to-js"));
|
15 | const https_proxy_agent_1 = tslib_1.__importDefault(require("https-proxy-agent"));
|
16 | const semver_2 = require("./semver");
|
17 | const config_1 = tslib_1.__importDefault(require("./config"));
|
18 | const git_1 = tslib_1.__importDefault(require("./git"));
|
19 | const init_1 = tslib_1.__importDefault(require("./init"));
|
20 | const release_1 = tslib_1.__importStar(require("./release"));
|
21 | const semver_3 = tslib_1.__importStar(require("./semver"));
|
22 | const exec_promise_1 = tslib_1.__importDefault(require("./utils/exec-promise"));
|
23 | const load_plugins_1 = require("./utils/load-plugins");
|
24 | const logger_1 = tslib_1.__importStar(require("./utils/logger"));
|
25 | const make_hooks_1 = require("./utils/make-hooks");
|
26 | const get_current_branch_1 = require("./utils/get-current-branch");
|
27 | const match_sha_to_pr_1 = require("./match-sha-to-pr");
|
28 | const get_repository_1 = tslib_1.__importDefault(require("./utils/get-repository"));
|
29 | const validate_config_1 = require("./validate-config");
|
30 | const omit_1 = require("./utils/omit");
|
31 | const child_process_1 = require("child_process");
|
32 | const is_binary_1 = tslib_1.__importDefault(require("./utils/is-binary"));
|
33 | const git_reset_1 = require("./utils/git-reset");
|
34 | const proxyUrl = process.env.https_proxy || process.env.http_proxy;
|
35 | const env = env_ci_1.default();
|
36 |
|
37 | const makeDetail = (summary, body) => endent_1.default `
|
38 | <details>
|
39 | <summary>${summary}</summary>
|
40 | <br />
|
41 |
|
42 | ${body}
|
43 | </details>
|
44 | `;
|
45 |
|
46 | const loadEnv = () => {
|
47 | const envFile = path_1.default.resolve(process.cwd(), ".env");
|
48 | if (!fs_1.default.existsSync(envFile)) {
|
49 | return;
|
50 | }
|
51 | const envConfig = dotenv_1.default.parse(fs_1.default.readFileSync(envFile));
|
52 | Object.entries(envConfig).forEach(([key, value]) => {
|
53 | process.env[key] = value;
|
54 | });
|
55 | };
|
56 |
|
57 | function getPrNumberFromEnv(pr) {
|
58 | const envPr = "pr" in env && Number(env.pr);
|
59 | const prNumber = pr || envPr;
|
60 | return prNumber;
|
61 | }
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | function determineNextVersion(lastVersion, currentVersion, bump, tag) {
|
71 | const next = semver_1.inc(lastVersion, `pre${bump}`, tag);
|
72 | return !next || semver_1.lte(next, currentVersion)
|
73 | ? semver_1.inc(currentVersion, "prerelease", tag) || "prerelease"
|
74 | : next;
|
75 | }
|
76 | exports.determineNextVersion = determineNextVersion;
|
77 |
|
78 | function getAutoVersion() {
|
79 | const packagePath = path_1.default.join(__dirname, "../package.json");
|
80 | const packageJson = JSON.parse(fs_1.default.readFileSync(packagePath, "utf8"));
|
81 | return packageJson.version;
|
82 | }
|
83 | exports.getAutoVersion = getAutoVersion;
|
84 |
|
85 | function escapeRegExp(str) {
|
86 | return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
87 | }
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | class Auto {
|
93 |
|
94 | constructor(options = {}) {
|
95 |
|
96 | this.checkClean = async () => {
|
97 | const status = await exec_promise_1.default("git", ["status", "--porcelain"]);
|
98 | if (!status) {
|
99 | return;
|
100 | }
|
101 | this.logger.log.error("Changed Files:\n", status);
|
102 | throw new Error("Working directory is not clean, make sure all files are committed");
|
103 | };
|
104 |
|
105 | this.prefixRelease = (release) => {
|
106 | var _a;
|
107 | if (!this.release) {
|
108 | throw this.createErrorMessage();
|
109 | }
|
110 | return ((_a = this.config) === null || _a === void 0 ? void 0 : _a.noVersionPrefix) || release.startsWith("v")
|
111 | ? release
|
112 | : `v${release}`;
|
113 | };
|
114 | this.options = options;
|
115 | this.baseBranch = options.baseBranch || "master";
|
116 | logger_1.setLogLevel("quiet" in options && options.quiet
|
117 | ? "quiet"
|
118 | : Array.isArray(options.verbose) && options.verbose.length > 1
|
119 | ? "veryVerbose"
|
120 | : options.verbose
|
121 | ? "verbose"
|
122 | : undefined);
|
123 | this.logger = logger_1.default();
|
124 | this.hooks = make_hooks_1.makeHooks();
|
125 | this.hooks.getRepository.tapPromise("Get repo info from origin", get_repository_1.default);
|
126 | this.hooks.onCreateRelease.tap("Link onCreateChangelog", (release) => {
|
127 | release.hooks.onCreateChangelog.tap("Link onCreateChangelog", (changelog, version) => {
|
128 | this.hooks.onCreateChangelog.call(changelog, version);
|
129 | });
|
130 | });
|
131 | this.hooks.onCreateRelease.tap("Link onCreateLogParse", (release) => {
|
132 | release.hooks.onCreateLogParse.tap("Link onCreateLogParse", (logParse) => {
|
133 | this.hooks.onCreateLogParse.call(logParse);
|
134 | });
|
135 | });
|
136 | this.hooks.beforeCommitChangelog.tapPromise("Old Version Branches", async ({ bump }) => {
|
137 | var _a, _b;
|
138 | if (bump === semver_3.default.major && ((_a = this.config) === null || _a === void 0 ? void 0 : _a.versionBranches)) {
|
139 | const branch = `${this.config.versionBranches}${semver_1.major(await this.hooks.getPreviousVersion.promise())}`;
|
140 | await exec_promise_1.default("git", [
|
141 | "branch",
|
142 | await ((_b = this.git) === null || _b === void 0 ? void 0 : _b.getLatestTagInBranch()),
|
143 | ]);
|
144 | await exec_promise_1.default("git", ["push", this.remote, branch]);
|
145 | }
|
146 | });
|
147 | |
148 |
|
149 |
|
150 |
|
151 | this.hooks.afterVersion.tapPromise("Check remote for commits", async () => {
|
152 |
|
153 |
|
154 | try {
|
155 | const currentBranch = get_current_branch_1.getCurrentBranch();
|
156 | const heads = await exec_promise_1.default("git", [
|
157 | "ls-remote",
|
158 | "--heads",
|
159 | this.remote,
|
160 | currentBranch,
|
161 | ]);
|
162 | this.logger.verbose.info("Branch:", currentBranch);
|
163 | this.logger.verbose.info("HEADs:", heads);
|
164 | const [, remoteHead] = heads.match(/^(\w+)?/) || [];
|
165 | if (remoteHead) {
|
166 |
|
167 | child_process_1.execSync(`git merge-base --is-ancestor ${remoteHead} HEAD`, {
|
168 | stdio: "ignore",
|
169 | });
|
170 | }
|
171 | this.logger.verbose.info("Current branch is up to date, proceeding with release");
|
172 | }
|
173 | catch (error) {
|
174 |
|
175 | this.logger.log.warn("Current commit is behind, skipping the release to avoid collisions.");
|
176 | this.logger.verbose.warn(error);
|
177 | process.exit(0);
|
178 | }
|
179 | });
|
180 | loadEnv();
|
181 | this.logger.verbose.info("ENV:", env);
|
182 | }
|
183 |
|
184 | async listPlugins() {
|
185 | await load_plugins_1.listPlugins(this.config, this.logger, this.getExtendedLocation(this.config));
|
186 | }
|
187 | |
188 |
|
189 |
|
190 |
|
191 | loadDefaultBehavior() {
|
192 | this.hooks.makeRelease.tapPromise("Default", async (options) => {
|
193 | if (options.dryRun) {
|
194 | const bump = await this.getVersion({ from: options.from });
|
195 | this.logger.log.info(`Would have created a release on GitHub for version: ${semver_1.inc(options.newVersion, bump)}`);
|
196 | this.logger.log.note('The above version would only get released if ran with "shipit" or a custom script that bumps the version using the "version" command');
|
197 | }
|
198 | else {
|
199 | this.logger.log.info(`Releasing ${options.newVersion} to GitHub.`);
|
200 | const release = await this.git.publish(options.fullReleaseNotes, options.newVersion, options.isPrerelease);
|
201 | this.logger.log.info(release.data.html_url);
|
202 | return release;
|
203 | }
|
204 | });
|
205 | }
|
206 | |
207 |
|
208 |
|
209 |
|
210 | async loadConfig() {
|
211 | const configLoader = new config_1.default(this.logger);
|
212 | const userConfig = await configLoader.loadConfig();
|
213 | this.logger.verbose.success("Loaded `auto` with config:", userConfig);
|
214 |
|
215 | this.config = Object.assign(Object.assign({}, userConfig), { plugins: this.options.plugins || userConfig.plugins });
|
216 | this.loadPlugins(this.config);
|
217 | this.loadDefaultBehavior();
|
218 | this.config = this.hooks.modifyConfig.call(this.config);
|
219 | this.labels = this.config.labels;
|
220 | this.semVerLabels = release_1.getVersionMap(this.config.labels);
|
221 | this.hooks.beforeRun.call(this.config);
|
222 | const errors = [
|
223 | ...(await validate_config_1.validateAutoRc(this.config)),
|
224 | ...(await validate_config_1.validatePlugins(this.hooks.validateConfig, this.config)),
|
225 | ];
|
226 | if (errors.length) {
|
227 | this.logger.log.error(endent_1.default `
|
228 | Found configuration errors:
|
229 |
|
230 | ${errors.map(validate_config_1.formatError).join("\n")}
|
231 | `, "\n");
|
232 | this.logger.log.warn("These errors are for the fully loaded configuration (this is why some paths might seem off).");
|
233 | if (this.config.extends) {
|
234 | this.logger.log.warn("Some errors might originate from an extend config.");
|
235 | }
|
236 | process.exit(1);
|
237 | }
|
238 | const config = Object.assign(Object.assign(Object.assign({}, this.config), omit_1.omit(this.options, ["_command", "_all", "main"])), { baseBranch: this.baseBranch });
|
239 | this.config = config;
|
240 | const repository = await this.getRepo(config);
|
241 | const token = (repository === null || repository === void 0 ? void 0 : repository.token) || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
242 | if (!token || token === "undefined") {
|
243 | this.logger.log.error("No GitHub was found. Make sure it is available on process.env.GH_TOKEN.");
|
244 | throw new Error("GitHub token not found!");
|
245 | }
|
246 | const githubOptions = Object.assign(Object.assign({ owner: config.owner, repo: config.repo }, repository), { token, agent: proxyUrl ? https_proxy_agent_1.default(proxyUrl) : undefined, baseUrl: config.githubApi || "https://api.github.com", graphqlBaseUrl: config.githubGraphqlApi || config.githubApi || "https://api.github.com" });
|
247 | this.git = this.startGit(githubOptions);
|
248 | this.release = new release_1.default(this.git, config, this.logger);
|
249 | this.remote = await this.getRemote();
|
250 | this.logger.verbose.info(`Using remote: ${this.remote.replace(token, `****${token.substring(0, 4)}`)}`);
|
251 | this.hooks.onCreateRelease.call(this.release);
|
252 | return config;
|
253 | }
|
254 |
|
255 | async getRemote() {
|
256 | const [, configuredRemote = "origin"] = await await_to_js_1.default(exec_promise_1.default("git", ["remote", "get-url", "origin"]));
|
257 | if (!this.git) {
|
258 | return configuredRemote;
|
259 | }
|
260 | const { html_url } = (await this.git.getProject()) || { html_url: "" };
|
261 | const GIT_TOKENS = {
|
262 |
|
263 |
|
264 | GITHUB_TOKEN: process.env.GITHUB_ACTION
|
265 | ? `x-access-token:${process.env.GITHUB_TOKEN}`
|
266 | : undefined,
|
267 | };
|
268 | const envVar = Object.keys(GIT_TOKENS).find((v) => process.env[v]) || "";
|
269 | const gitCredentials = GIT_TOKENS[envVar] || process.env.GH_TOKEN;
|
270 | if (gitCredentials) {
|
271 | const _a = url_1.parse(html_url), { port, hostname } = _a, parsed = tslib_1.__rest(_a, ["port", "hostname"]);
|
272 | const urlWithAuth = url_1.format(Object.assign(Object.assign({}, parsed), { auth: gitCredentials, host: `${hostname}${port ? `:${port}` : ""}` }));
|
273 | if (await this.git.verifyAuth(urlWithAuth)) {
|
274 | this.logger.veryVerbose.note("Using token + html URL as remote");
|
275 | return urlWithAuth;
|
276 | }
|
277 | }
|
278 | if (html_url && (await this.git.verifyAuth(html_url))) {
|
279 | this.logger.veryVerbose.note("Using bare html URL as remote");
|
280 | return html_url;
|
281 | }
|
282 | this.logger.veryVerbose.note("Using remote set in environment");
|
283 | return configuredRemote;
|
284 | }
|
285 |
|
286 | async init() {
|
287 | const init = new init_1.default(this);
|
288 | await init.run();
|
289 | }
|
290 |
|
291 | async info(args) {
|
292 | var _a, _b;
|
293 | if (!this.git) {
|
294 | return { hasError: false };
|
295 | }
|
296 | const [, gitVersion = ""] = await await_to_js_1.default(exec_promise_1.default("git", ["--version"]));
|
297 | const [noProject, project] = await await_to_js_1.default(this.git.getProject());
|
298 | const repo = (await this.getRepo(this.config)) || {};
|
299 | const repoLink = terminal_link_1.default(`${repo.owner}/${repo.repo}`, project === null || project === void 0 ? void 0 : project.html_url);
|
300 | const author = (await this.getGitUser()) || {};
|
301 | const [, lastRelease = "0.0.0"] = await await_to_js_1.default(this.git.getLatestRelease());
|
302 | const version = await this.getCurrentVersion(lastRelease);
|
303 | const [err, latestRelease] = await await_to_js_1.default(this.git.getLatestReleaseInfo());
|
304 | const latestReleaseLink = latestRelease
|
305 | ? terminal_link_1.default(latestRelease.tag_name, latestRelease.html_url)
|
306 | : "";
|
307 | const { headers } = await this.git.github.request("HEAD /");
|
308 | const access = headers;
|
309 | const rateLimitRefresh = new Date(Number(access["x-ratelimit-reset"]) * 1000);
|
310 | const token = this.git.options.token || "";
|
311 | const tokenRefresh = `${rateLimitRefresh.toLocaleTimeString()} ${rateLimitRefresh.toLocaleDateString("en-us")}`;
|
312 | const projectLabels = await this.git.getProjectLabels();
|
313 | const hasLabels = (_a = this.config) === null || _a === void 0 ? void 0 : _a.labels.reduce((acc, label) => {
|
314 | var _a, _b;
|
315 | if (label.name === "release" &&
|
316 | !((_a = this.config) === null || _a === void 0 ? void 0 : _a.onlyPublishWithReleaseLabel)) {
|
317 | return acc;
|
318 | }
|
319 | if (label.name === "skip-release" && ((_b = this.config) === null || _b === void 0 ? void 0 : _b.onlyPublishWithReleaseLabel)) {
|
320 | return acc;
|
321 | }
|
322 | return acc && projectLabels.includes(label.name);
|
323 | }, true);
|
324 | const { permission, user } = (await this.git.getTokenPermissionLevel()) || {};
|
325 | let hasError = false;
|
326 |
|
327 | const logSuccess = (err) => {
|
328 | if (err) {
|
329 | hasError = true;
|
330 | return log_symbols_1.default.error;
|
331 | }
|
332 | return log_symbols_1.default.success;
|
333 | };
|
334 | console.log("");
|
335 |
|
336 | console.log(endent_1.default `
|
337 | ${chalk_1.default.underline.white('Environment Information:')}
|
338 |
|
339 | "auto" version: v${getAutoVersion()}
|
340 | "git" version: v${gitVersion.replace('git version ', '')}
|
341 | "node" version: ${process.version.trim()}${access['x-github-enterprise-version']
|
342 | ? `GHE version: v${access['x-github-enterprise-version']}\n`
|
343 | : '\n'}
|
344 | ${chalk_1.default.underline.white('Project Information:')}
|
345 |
|
346 | ${logSuccess(noProject)} Repository: ${repoLink}
|
347 | ${logSuccess(!author.name)} Author Name: ${author.name}
|
348 | ${logSuccess(!author.email)} Author Email: ${author.email}
|
349 | ${logSuccess(!version)} Current Version: ${this.prefixRelease(version)}
|
350 | ${logSuccess(err)} Latest Release: ${latestReleaseLink}
|
351 |
|
352 | ${logSuccess(!hasLabels)} Labels configured on GitHub project ${hasLabels ? '' : '(Try running "auto create-labels")'}
|
353 |
|
354 | ${chalk_1.default.underline.white('GitHub Token Information:')}
|
355 |
|
356 | ${logSuccess(!token)} Token: ${`[Token starting with ${token.substring(0, 4)}]`}
|
357 | ${logSuccess(!(permission === 'admin' || permission === 'write'))} Repo Permission: ${permission}
|
358 | ${logSuccess(!(user === null || user === void 0 ? void 0 : user.login))} User: ${user === null || user === void 0 ? void 0 : user.login}
|
359 | ${logSuccess()} API: ${terminal_link_1.default(this.git.options.baseUrl, this.git.options.baseUrl)}
|
360 | ${logSuccess(!((_b = access['x-oauth-scopes']) === null || _b === void 0 ? void 0 : _b.includes('repo')))} Enabled Scopes: ${access['x-oauth-scopes']}
|
361 | ${logSuccess(Number(access['x-ratelimit-remaining']) === 0)} Rate Limit: ${access['x-ratelimit-remaining'] || '∞'}/${access['x-ratelimit-limit'] || '∞'} ${access['ratelimit-reset'] ? `(Renews @ ${tokenRefresh})` : ''}
|
362 | `);
|
363 | console.log("");
|
364 | if (args.listPlugins) {
|
365 | await this.listPlugins();
|
366 | }
|
367 | return { hasError };
|
368 | }
|
369 |
|
370 | inPrereleaseBranch() {
|
371 | var _a;
|
372 | const branch = get_current_branch_1.getCurrentBranch();
|
373 | const prereleaseBranches = (_a = this.config) === null || _a === void 0 ? void 0 : _a.prereleaseBranches;
|
374 | return Boolean(branch && prereleaseBranches.includes(branch));
|
375 | }
|
376 |
|
377 | inOldVersionBranch() {
|
378 | var _a;
|
379 | const branch = get_current_branch_1.getCurrentBranch();
|
380 | const prereleaseBranchPrefix = (_a = this.config) === null || _a === void 0 ? void 0 : _a.versionBranches;
|
381 | return Boolean(prereleaseBranchPrefix &&
|
382 | branch &&
|
383 | new RegExp(`^${escapeRegExp(prereleaseBranchPrefix)}`).test(branch));
|
384 | }
|
385 | |
386 |
|
387 |
|
388 | async createLabels(options = {}) {
|
389 | if (!this.release || !this.labels) {
|
390 | throw this.createErrorMessage();
|
391 | }
|
392 | await this.release.addLabelsToProject(this.labels, options);
|
393 | }
|
394 | |
395 |
|
396 |
|
397 | async label({ pr } = {}) {
|
398 | if (!this.git) {
|
399 | throw this.createErrorMessage();
|
400 | }
|
401 | this.logger.verbose.info("Using command: 'label'");
|
402 | const number = getPrNumberFromEnv(pr);
|
403 | let labels = [];
|
404 | if (number) {
|
405 | labels = await this.git.getLabels(number);
|
406 | }
|
407 | else {
|
408 | const pulls = await this.git.getPullRequests({
|
409 | state: "closed",
|
410 | });
|
411 | const lastMerged = pulls
|
412 | .sort((a, b) => new Date(b.merged_at).getTime() - new Date(a.merged_at).getTime())
|
413 | .find((pull) => pull.merged_at);
|
414 | if (lastMerged) {
|
415 | labels = lastMerged.labels.map((label) => label.name);
|
416 | }
|
417 | }
|
418 | if (labels.length) {
|
419 | console.log(labels.join("\n"));
|
420 | }
|
421 | }
|
422 | |
423 |
|
424 |
|
425 | async prStatus(_a) {
|
426 | var { dryRun, pr, url } = _a, options = tslib_1.__rest(_a, ["dryRun", "pr", "url"]);
|
427 | if (!this.git) {
|
428 | throw this.createErrorMessage();
|
429 | }
|
430 | let { sha } = options;
|
431 | let prNumber;
|
432 | try {
|
433 | prNumber = this.getPrNumber("pr", pr);
|
434 | }
|
435 | catch (error) {
|
436 |
|
437 | }
|
438 | this.logger.verbose.info("Using command: 'pr-status'");
|
439 | if (!sha && prNumber) {
|
440 | this.logger.verbose.info("Getting commit SHA from PR.");
|
441 | const res = await this.git.getPullRequest(prNumber);
|
442 | sha = res.data.head.sha;
|
443 | }
|
444 | else if (!sha) {
|
445 | this.logger.verbose.info("No PR found, getting commit SHA from HEAD.");
|
446 | sha = await this.git.getSha();
|
447 | }
|
448 | this.logger.verbose.info("Found PR SHA:", sha);
|
449 | const target_url = url;
|
450 | if (dryRun) {
|
451 | this.logger.verbose.info("`pr` dry run complete.");
|
452 | }
|
453 | else {
|
454 | try {
|
455 | await this.git.createStatus(Object.assign(Object.assign({}, options), { sha,
|
456 | target_url }));
|
457 | }
|
458 | catch (error) {
|
459 | throw new Error(`Failed to post status to Pull Request with error code ${error.status}`);
|
460 | }
|
461 | this.logger.log.success("Posted status to Pull Request.");
|
462 | }
|
463 | this.logger.verbose.success("Finished `pr` command");
|
464 | }
|
465 | |
466 |
|
467 |
|
468 | async prCheck(_a) {
|
469 | var _b, _c;
|
470 | var { dryRun, pr, url } = _a, options = tslib_1.__rest(_a, ["dryRun", "pr", "url"]);
|
471 | if (!this.git || !this.release || !this.semVerLabels) {
|
472 | throw this.createErrorMessage();
|
473 | }
|
474 | this.logger.verbose.info(`Using command: 'pr-check' for '${url}'`);
|
475 | const target_url = url;
|
476 | const prNumber = this.getPrNumber("prCheck", pr);
|
477 | let msg;
|
478 | let sha;
|
479 | try {
|
480 | const res = await this.git.getPullRequest(prNumber);
|
481 | sha = res.data.head.sha;
|
482 | const labels = await this.git.getLabels(prNumber);
|
483 | const labelValues = [...this.semVerLabels.values()];
|
484 | const releaseTag = labels.find((l) => l === "release");
|
485 | const skipReleaseLabels = (((_b = this.config) === null || _b === void 0 ? void 0 : _b.labels.filter((l) => l.releaseType === "skip")) || []).map((l) => l.name);
|
486 | const skipReleaseTag = labels.find((l) => skipReleaseLabels.includes(l));
|
487 | const semverTag = labels.find((l) => labelValues.some((labelValue) => labelValue.includes(l)) &&
|
488 | !skipReleaseLabels.includes(l) &&
|
489 | l !== "release");
|
490 | const branch = get_current_branch_1.getCurrentBranch();
|
491 | if (branch && ((_c = this.config) === null || _c === void 0 ? void 0 : _c.prereleaseBranches.includes(branch))) {
|
492 | msg = {
|
493 | description: "PR will graduate prerelease once merged",
|
494 | state: "success",
|
495 | };
|
496 | }
|
497 | else if (semverTag === undefined && !skipReleaseTag) {
|
498 | throw new Error("No semver label!");
|
499 | }
|
500 | else {
|
501 | this.logger.log.success(`PR is using label: ${semverTag || skipReleaseTag}`);
|
502 | let description;
|
503 | if (skipReleaseTag) {
|
504 | description = "PR will not create a release";
|
505 | }
|
506 | else if (releaseTag) {
|
507 | description = `PR will create release once merged - ${semverTag}`;
|
508 | }
|
509 | else {
|
510 | description = `CI - ${semverTag}`;
|
511 | }
|
512 | msg = {
|
513 | description,
|
514 | state: "success",
|
515 | };
|
516 | }
|
517 | }
|
518 | catch (error) {
|
519 | msg = {
|
520 | description: error.message,
|
521 | state: "error",
|
522 | };
|
523 | }
|
524 | this.logger.verbose.info("Posting status to GitHub\n", msg);
|
525 | if (dryRun) {
|
526 | this.logger.verbose.info("`pr-check` dry run complete.");
|
527 | }
|
528 | else {
|
529 | try {
|
530 | await this.git.createStatus(Object.assign(Object.assign(Object.assign({}, options), msg), { target_url,
|
531 | sha }));
|
532 | this.logger.log.success("Posted status to Pull Request.");
|
533 | }
|
534 | catch (error) {
|
535 | throw new Error(`Failed to post status to Pull Request with error code ${error.status}`);
|
536 | }
|
537 | }
|
538 | this.logger.verbose.success("Finished `pr-check` command");
|
539 | }
|
540 | |
541 |
|
542 |
|
543 |
|
544 | async comment(args) {
|
545 | const options = Object.assign(Object.assign({}, this.getCommandDefault("comment")), args);
|
546 | const { message, pr, context = "default", dryRun, delete: deleteFlag, edit: editFlag, } = options;
|
547 | if (!this.git) {
|
548 | throw this.createErrorMessage();
|
549 | }
|
550 | this.logger.verbose.info("Using command: 'comment'");
|
551 | const prNumber = this.getPrNumber("comment", pr);
|
552 | if (dryRun) {
|
553 | if (deleteFlag) {
|
554 | this.logger.log.info(`Would have deleted comment on ${prNumber} under "${context}" context`);
|
555 | }
|
556 | else if (editFlag) {
|
557 | this.logger.log.info(`Would have edited the comment on ${prNumber} under "${context}" context.\n\nNew message: ${message}`);
|
558 | }
|
559 | else {
|
560 | this.logger.log.info(`Would have commented on ${prNumber} under "${context}" context:\n\n${message}`);
|
561 | }
|
562 | }
|
563 | else if (editFlag && message) {
|
564 | await this.git.editComment(message, prNumber, context);
|
565 | this.logger.log.success(`Edited comment on PR #${prNumber} under context "${context}"`);
|
566 | }
|
567 | else {
|
568 | if (deleteFlag) {
|
569 | await this.git.deleteComment(prNumber, context);
|
570 | this.logger.log.success(`Deleted comment on PR #${prNumber} under context "${context}"`);
|
571 | }
|
572 | if (message) {
|
573 | await this.git.createComment(message, prNumber, context);
|
574 | this.logger.log.success(`Commented on PR #${prNumber}`);
|
575 | }
|
576 | }
|
577 | }
|
578 | |
579 |
|
580 |
|
581 |
|
582 |
|
583 | async prBody(options) {
|
584 | const { message, pr, context = "default", dryRun, delete: deleteFlag, } = options;
|
585 | if (!this.git) {
|
586 | throw this.createErrorMessage();
|
587 | }
|
588 | this.logger.verbose.info("Using command: 'pr-body'");
|
589 | const prNumber = this.getPrNumber("pr-body", pr);
|
590 | if (dryRun) {
|
591 | if (deleteFlag) {
|
592 | this.logger.log.info(`Would have deleted PR body on ${prNumber} under "${context}" context`);
|
593 | }
|
594 | else {
|
595 | this.logger.log.info(`Would have appended to PR body on ${prNumber} under "${context}" context:\n\n${message}`);
|
596 | }
|
597 | }
|
598 | else {
|
599 | if (deleteFlag) {
|
600 | await this.git.addToPrBody("", prNumber, context);
|
601 | }
|
602 | if (message) {
|
603 | await this.git.addToPrBody(message, prNumber, context);
|
604 | }
|
605 | this.logger.log.success(`Updated body on PR #${prNumber}`);
|
606 | }
|
607 | }
|
608 | |
609 |
|
610 |
|
611 | async version(options = {}) {
|
612 | this.logger.verbose.info("Using command: 'version'");
|
613 | const bump = await this.getVersion(options);
|
614 | console.log(bump);
|
615 | }
|
616 | |
617 |
|
618 |
|
619 | async changelog(options) {
|
620 | this.logger.verbose.info("Using command: 'changelog'");
|
621 | await this.makeChangelog(options);
|
622 | }
|
623 | |
624 |
|
625 |
|
626 | async runRelease(options = {}) {
|
627 | this.logger.verbose.info("Using command: 'release'");
|
628 | await this.makeRelease(options);
|
629 | }
|
630 |
|
631 |
|
632 | async canary(args = {}) {
|
633 | const options = Object.assign(Object.assign({}, this.getCommandDefault("canary")), args);
|
634 | if (!this.git || !this.release) {
|
635 | throw this.createErrorMessage();
|
636 | }
|
637 | if (!this.hooks.canary.isUsed()) {
|
638 | this.logger.log.warn(endent_1.default `
|
639 | None of the plugins that you are using implement the \`canary\` command!
|
640 |
|
641 | "canary" releases are versions that are used solely to test changes. They make sense on some platforms (ex: npm) but not all!
|
642 |
|
643 | If you think your package manager has the ability to support canaries please file an issue or submit a pull request,
|
644 | `);
|
645 | process.exit(0);
|
646 | }
|
647 | if (!args.dryRun) {
|
648 | await this.checkClean();
|
649 | }
|
650 | let { pr, build } = await this.getPrEnvInfo();
|
651 | pr = options.pr ? String(options.pr) : pr;
|
652 | build = options.build ? String(options.build) : build;
|
653 | this.logger.verbose.info("Canary info found:", { pr, build });
|
654 | const from = (await this.git.shaExists("HEAD^")) ? "HEAD^" : "HEAD";
|
655 | const head = await this.release.getCommitsInRelease(from);
|
656 | const labels = head.map((commit) => commit.labels);
|
657 | const version = semver_3.calculateSemVerBump(labels, this.semVerLabels, this.config);
|
658 | if (version === semver_3.default.noVersion && !options.force) {
|
659 | this.logger.log.info("Skipping canary release due to PR being specifying no release. Use `auto canary --force` to override this setting");
|
660 | return;
|
661 | }
|
662 | let canaryVersion = "";
|
663 | let newVersion = "";
|
664 | if (pr) {
|
665 | canaryVersion = `${canaryVersion}.${pr}`;
|
666 | }
|
667 | if (build) {
|
668 | canaryVersion = `${canaryVersion}.${build}`;
|
669 | }
|
670 | if (!pr || !build) {
|
671 | canaryVersion = `${canaryVersion}.${await this.git.getSha(true)}`;
|
672 | }
|
673 | canaryVersion = `canary${canaryVersion}`;
|
674 | if (options.dryRun) {
|
675 | const lastRelease = await this.git.getLatestRelease();
|
676 | const current = await this.getCurrentVersion(lastRelease);
|
677 | if (semver_1.parse(current)) {
|
678 | const next = determineNextVersion(lastRelease, current, version, canaryVersion);
|
679 | if (options.quiet) {
|
680 | console.log(next);
|
681 | }
|
682 | else {
|
683 | this.logger.log.warn(`Published canary identifier would be: ${next}`);
|
684 | }
|
685 | }
|
686 | else if (options.quiet) {
|
687 | console.log(`-${canaryVersion}`);
|
688 | }
|
689 | else {
|
690 | this.logger.log.warn(`Published canary identifier would be: "-${canaryVersion}"`);
|
691 | }
|
692 | }
|
693 | else {
|
694 | this.logger.verbose.info("Calling canary hook");
|
695 | const result = await this.hooks.canary.promise(version, canaryVersion);
|
696 | if (typeof result === "object" && "error" in result) {
|
697 | this.logger.log.warn(result.error);
|
698 | return;
|
699 | }
|
700 | if (!result) {
|
701 | return;
|
702 | }
|
703 | newVersion = typeof result === "string" ? result : result.newVersion;
|
704 | const messageHeader = (options.message || "📦 Published PR as canary version: %v").replace("%v", !newVersion || newVersion.includes("\n")
|
705 | ? newVersion
|
706 | : `<code>${newVersion}</code>`);
|
707 | if (options.message !== "false" && pr) {
|
708 | const message = typeof result === "string"
|
709 | ? messageHeader
|
710 | : makeDetail(messageHeader, result.details);
|
711 | await this.prBody({
|
712 | pr: Number(pr),
|
713 | context: "canary-version",
|
714 | message,
|
715 | });
|
716 | }
|
717 | this.logger.log.success(`Published canary version${newVersion ? `: ${newVersion}` : ""}`);
|
718 | if (args.quiet) {
|
719 | console.log(newVersion);
|
720 | }
|
721 | await git_reset_1.gitReset();
|
722 | }
|
723 | let latestTag;
|
724 | try {
|
725 | latestTag = await this.git.getLatestTagInBranch();
|
726 | }
|
727 | catch (error) {
|
728 | latestTag = await this.git.getFirstCommit();
|
729 | }
|
730 | const commitsInRelease = await this.release.getCommits(latestTag);
|
731 | return { newVersion, commitsInRelease, context: "canary" };
|
732 | }
|
733 | |
734 |
|
735 |
|
736 |
|
737 | async next(args) {
|
738 | var _a;
|
739 | const options = Object.assign(Object.assign({}, this.getCommandDefault("next")), args);
|
740 | if (!this.git || !this.release) {
|
741 | throw this.createErrorMessage();
|
742 | }
|
743 | if (!this.hooks.next.isUsed()) {
|
744 | this.logger.log.warn(endent_1.default `
|
745 | None of the plugins that you are using implement the \`next\` command!
|
746 |
|
747 | "next" releases are pre-releases such as betas or alphas. They make sense on some platforms (ex: npm) but not all!
|
748 |
|
749 | If you think your package manager has the ability to support "next" releases please file an issue or submit a pull request,
|
750 | `);
|
751 | process.exit(0);
|
752 | }
|
753 | if (!args.dryRun) {
|
754 | await this.checkClean();
|
755 | }
|
756 | await this.setGitUser();
|
757 | this.hooks.onCreateLogParse.tap("Omit merges from master", (logParse) => {
|
758 | logParse.hooks.omitCommit.tap("Omit merges from master", (commit) => {
|
759 | const shouldOmit = commit.subject.match(/^Merge (?:\S+\/)*master/);
|
760 | this.logger.verbose.info(`Omit merges from master?" ${shouldOmit}: ${commit.subject}`);
|
761 | if (shouldOmit) {
|
762 | return true;
|
763 | }
|
764 | });
|
765 | });
|
766 | const currentBranch = get_current_branch_1.getCurrentBranch();
|
767 | const initialForkCommit = ((await exec_promise_1.default("git", [
|
768 | "rev-list",
|
769 | "--boundary",
|
770 | `${currentBranch}...origin/${this.baseBranch}`,
|
771 | "--left-only",
|
772 | ]))
|
773 | .split("\n")
|
774 | .filter((line) => line.startsWith("-"))[0] || "").slice(1);
|
775 | const lastRelease = initialForkCommit || (await this.git.getLatestRelease());
|
776 | const lastTag = await this.git.getLastTagNotInBaseBranch(currentBranch);
|
777 | const fullReleaseNotes = await this.release.generateReleaseNotes(lastRelease);
|
778 | const commits = await this.release.getCommitsInRelease(lastTag);
|
779 | const releaseNotes = await this.release.generateReleaseNotes(lastTag);
|
780 | const labels = commits.map((commit) => commit.labels);
|
781 | const bump = semver_3.calculateSemVerBump(labels, this.semVerLabels, this.config) ||
|
782 | semver_3.default.patch;
|
783 | if (!args.quiet) {
|
784 | this.logger.log.info("Full Release notes for next release:");
|
785 | console.log(fullReleaseNotes);
|
786 | if (releaseNotes) {
|
787 | this.logger.log.info("Release notes for last change in next release");
|
788 | console.log(releaseNotes);
|
789 | }
|
790 | }
|
791 | if (options.dryRun) {
|
792 | const lastRelease = await this.git.getLatestRelease();
|
793 | const current = await this.getCurrentVersion(lastRelease);
|
794 | if (semver_1.parse(current)) {
|
795 | const prereleaseBranches = (_a = this.config) === null || _a === void 0 ? void 0 : _a.prereleaseBranches;
|
796 | const branch = get_current_branch_1.getCurrentBranch() || "";
|
797 | const prereleaseBranch = prereleaseBranches.includes(branch)
|
798 | ? branch
|
799 | : prereleaseBranches[0];
|
800 | const prerelease = determineNextVersion(lastRelease, current, bump, prereleaseBranch);
|
801 | if (options.quiet) {
|
802 | console.log(prerelease);
|
803 | }
|
804 | else {
|
805 | this.logger.log.success(`Would have created prerelease version: ${prerelease}`);
|
806 | }
|
807 | }
|
808 | else if (options.quiet) {
|
809 |
|
810 | console.log(`${bump} on ${lastTag}`);
|
811 | }
|
812 | else {
|
813 | this.logger.log.success(`Would have created prerelease version with: ${bump} on ${lastTag}`);
|
814 | }
|
815 | return { newVersion: "", commitsInRelease: commits, context: "next" };
|
816 | }
|
817 | this.logger.verbose.info(`Calling "next" hook with: ${bump}`);
|
818 | const result = await this.hooks.next.promise([], bump);
|
819 | const newVersion = result.join(", ");
|
820 | await Promise.all(result.map(async (prerelease) => {
|
821 | var _a;
|
822 | const release = await ((_a = this.git) === null || _a === void 0 ? void 0 : _a.publish(releaseNotes, prerelease, true));
|
823 | this.logger.verbose.info(release);
|
824 | await this.hooks.afterRelease.promise({
|
825 | lastRelease: lastTag,
|
826 | newVersion: prerelease,
|
827 | commits,
|
828 | releaseNotes,
|
829 | response: release,
|
830 | });
|
831 | }));
|
832 | this.logger.log.success(`Published next version${result.length > 1 ? `s` : ""}: ${newVersion}`);
|
833 | const { pr } = await this.getPrEnvInfo();
|
834 | if (pr) {
|
835 | const message = options.message || "Published prerelease version: %v";
|
836 | if (pr) {
|
837 | await this.prBody({
|
838 | pr: Number(pr),
|
839 | context: "prerelease-version",
|
840 | message: endent_1.default `
|
841 | # Version
|
842 |
|
843 | ${message.replace("%v", result.map((r) => `\`${r}\``).join("\n"))}
|
844 |
|
845 | <details>
|
846 | <summary>Changelog</summary>
|
847 |
|
848 | ${fullReleaseNotes}
|
849 | </details>
|
850 | `,
|
851 | });
|
852 | }
|
853 | }
|
854 | if (options.quiet) {
|
855 | console.log(newVersion);
|
856 | }
|
857 | await git_reset_1.gitReset();
|
858 | return { newVersion, commitsInRelease: commits, context: "next" };
|
859 | }
|
860 |
|
861 | async latest(args = {}) {
|
862 | const options = Object.assign(Object.assign({}, this.getCommandDefault("latest")), args);
|
863 | return this.publishFullRelease(options);
|
864 | }
|
865 | |
866 |
|
867 |
|
868 |
|
869 |
|
870 |
|
871 |
|
872 |
|
873 | async shipit(args = {}) {
|
874 | var _a, _b;
|
875 | const options = Object.assign(Object.assign({}, this.getCommandDefault("shipit")), args);
|
876 | if (!this.git || !this.release) {
|
877 | throw this.createErrorMessage();
|
878 | }
|
879 | this.logger.verbose.info("Using command: 'shipit'");
|
880 | const isPR = "isPr" in env && env.isPr;
|
881 | const from = (await this.git.shaExists("HEAD^")) ? "HEAD^" : "HEAD";
|
882 | const head = await this.release.getCommitsInRelease(from);
|
883 |
|
884 |
|
885 | const currentBranch = get_current_branch_1.getCurrentBranch();
|
886 | const isBaseBranch = !isPR && currentBranch === this.baseBranch;
|
887 | const shouldGraduate = !options.onlyGraduateWithReleaseLabel ||
|
888 | (options.onlyGraduateWithReleaseLabel &&
|
889 | head[0].labels.some((l) => { var _a, _b; return (_b = (_a = this.semVerLabels) === null || _a === void 0 ? void 0 : _a.get("release")) === null || _b === void 0 ? void 0 : _b.includes(l); }));
|
890 | const isPrereleaseBranch = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.prereleaseBranches) === null || _b === void 0 ? void 0 : _b.some((branch) => currentBranch === branch);
|
891 | const publishPrerelease = isPrereleaseBranch ||
|
892 | (currentBranch === this.baseBranch &&
|
893 | options.onlyGraduateWithReleaseLabel);
|
894 | this.logger.veryVerbose.info({
|
895 | currentBranch,
|
896 | isBaseBranch,
|
897 | isPR,
|
898 | shouldGraduate,
|
899 | isPrereleaseBranch,
|
900 | publishPrerelease,
|
901 | });
|
902 | let publishInfo;
|
903 | let releaseType = "canary";
|
904 | if (isBaseBranch && shouldGraduate) {
|
905 | releaseType = "latest";
|
906 | }
|
907 | else if (this.inOldVersionBranch()) {
|
908 | releaseType = "old";
|
909 | }
|
910 | else if (publishPrerelease) {
|
911 | releaseType = "next";
|
912 | }
|
913 | await this.hooks.beforeShipIt.promise({ releaseType });
|
914 | if (releaseType === "latest") {
|
915 | publishInfo = await this.latest(options);
|
916 | }
|
917 | else if (releaseType === "old") {
|
918 | publishInfo = await this.oldRelease(options);
|
919 | }
|
920 | else if (releaseType === "next") {
|
921 | publishInfo = await this.next(options);
|
922 | }
|
923 | else {
|
924 | publishInfo = await this.canary(options);
|
925 | if (options.dryRun && !options.quiet) {
|
926 | this.logger.log.success("Below is what would happen upon merge of the current branch into master");
|
927 | await this.publishFullRelease(options);
|
928 | }
|
929 | }
|
930 | if (!publishInfo) {
|
931 | return;
|
932 | }
|
933 | const { newVersion, commitsInRelease, context } = publishInfo;
|
934 | await this.hooks.afterShipIt.promise(newVersion, commitsInRelease, {
|
935 | context,
|
936 | });
|
937 | }
|
938 |
|
939 | async getCurrentVersion(lastRelease) {
|
940 | this.hooks.getPreviousVersion.tap("None", () => {
|
941 | this.logger.veryVerbose.info("No previous release found, using 0.0.0 as previous version.");
|
942 | return this.prefixRelease("0.0.0");
|
943 | });
|
944 | const lastVersion = await this.hooks.getPreviousVersion.promise();
|
945 | if (semver_1.parse(lastRelease) &&
|
946 | semver_1.parse(lastVersion) &&
|
947 | semver_1.gt(lastRelease, lastVersion)) {
|
948 | this.logger.veryVerbose.info("Using latest release as previous version");
|
949 | return lastRelease;
|
950 | }
|
951 | return lastVersion;
|
952 | }
|
953 | |
954 |
|
955 |
|
956 | checkEnv(pluginName, key) {
|
957 | if (!process.env[key]) {
|
958 | this.logger.log.warn(`${pluginName}: No "${key}" found in environment`);
|
959 | }
|
960 | }
|
961 |
|
962 | async oldRelease(options) {
|
963 | var _a;
|
964 | const latestTag = await ((_a = this.git) === null || _a === void 0 ? void 0 : _a.getLatestTagInBranch());
|
965 | const result = await this.publishFullRelease(Object.assign(Object.assign({}, options), { from: latestTag }));
|
966 | if (result) {
|
967 | result.context = "old";
|
968 | }
|
969 | return result;
|
970 | }
|
971 |
|
972 | async publishFullRelease(options) {
|
973 | if (!this.git || !this.release) {
|
974 | throw this.createErrorMessage();
|
975 | }
|
976 | const version = await this.getVersion(options);
|
977 | this.logger.log.success(`Calculated version bump: ${version || "none"}`);
|
978 | if (version === "") {
|
979 | this.logger.log.info("No version published.");
|
980 | return;
|
981 | }
|
982 | const lastRelease = options.from || (await this.git.getLatestRelease());
|
983 | const commitsInRelease = await this.release.getCommitsInRelease(lastRelease);
|
984 | await this.makeChangelog(Object.assign(Object.assign({}, options), { quiet: undefined, noCommit: options.noChangelog }));
|
985 | if (!options.dryRun) {
|
986 | await this.checkClean();
|
987 | this.logger.verbose.info("Calling version hook");
|
988 | await this.hooks.version.promise(version);
|
989 | this.logger.verbose.info("Calling after version hook");
|
990 | await this.hooks.afterVersion.promise();
|
991 | this.logger.verbose.info("Calling publish hook");
|
992 | await this.hooks.publish.promise(version);
|
993 | this.logger.verbose.info("Calling after publish hook");
|
994 | await this.hooks.afterPublish.promise();
|
995 | }
|
996 | const newVersion = await this.makeRelease(options);
|
997 | if (options.dryRun) {
|
998 | const current = await this.getCurrentVersion(lastRelease);
|
999 | if (semver_1.parse(current)) {
|
1000 | const next = semver_1.inc(current, version);
|
1001 | if (options.quiet) {
|
1002 | console.log(next);
|
1003 | }
|
1004 | else {
|
1005 | this.logger.log.warn(`Published version would be: ${next}`);
|
1006 | }
|
1007 | }
|
1008 | }
|
1009 | else if (options.quiet) {
|
1010 | console.log(newVersion);
|
1011 | }
|
1012 | return { newVersion, commitsInRelease, context: "latest" };
|
1013 | }
|
1014 |
|
1015 | getPrNumber(command, pr) {
|
1016 | const prNumber = getPrNumberFromEnv(pr);
|
1017 | if (!prNumber) {
|
1018 | this.logger.log.error(endent_1.default `
|
1019 | Could not detect PR number. ${command} must be run from either a PR or have the PR number supplied via the --pr flag.
|
1020 |
|
1021 | In some CIs your branch might be built before you open a PR and posting the canary version will fail. In this case subsequent builds should succeed.
|
1022 | `);
|
1023 | process.exit(1);
|
1024 | }
|
1025 | return prNumber;
|
1026 | }
|
1027 |
|
1028 | startGit(gitOptions) {
|
1029 | if (!gitOptions.owner || !gitOptions.repo || !gitOptions.token) {
|
1030 | throw new Error("Must set owner, repo, and GitHub token.");
|
1031 | }
|
1032 | this.logger.verbose.info("Options contain repo information.");
|
1033 |
|
1034 | const tokenlessArgs = Object.assign(Object.assign({}, gitOptions), { token: `[Token starting with ${gitOptions.token.substring(0, 4)}]` });
|
1035 | this.logger.verbose.info("Initializing GitHub API with:\n", tokenlessArgs);
|
1036 | return new git_1.default({
|
1037 | owner: gitOptions.owner,
|
1038 | repo: gitOptions.repo,
|
1039 | token: gitOptions.token,
|
1040 | baseUrl: gitOptions.baseUrl,
|
1041 | baseBranch: this.baseBranch,
|
1042 | graphqlBaseUrl: gitOptions.graphqlBaseUrl,
|
1043 | agent: gitOptions.agent,
|
1044 | }, this.logger);
|
1045 | }
|
1046 |
|
1047 | async getVersion({ from } = {}) {
|
1048 | if (!this.git || !this.release) {
|
1049 | throw this.createErrorMessage();
|
1050 | }
|
1051 | const isPrerelease = this.inPrereleaseBranch();
|
1052 | const lastRelease = from ||
|
1053 | (isPrerelease && (await this.git.getLatestTagInBranch())) ||
|
1054 | (await this.git.getLatestRelease());
|
1055 | const calculatedBump = await this.release.getSemverBump(lastRelease);
|
1056 | const bump = (isPrerelease && semver_2.preVersionMap.get(calculatedBump)) || calculatedBump;
|
1057 | this.versionBump = bump;
|
1058 | return bump;
|
1059 | }
|
1060 |
|
1061 | async makeChangelog(args = {}) {
|
1062 | const options = Object.assign(Object.assign({}, this.getCommandDefault("changelog")), args);
|
1063 | const { dryRun, from, to, title, message = "Update CHANGELOG.md [skip ci]", noCommit, } = options;
|
1064 | if (!this.release || !this.git) {
|
1065 | throw this.createErrorMessage();
|
1066 | }
|
1067 | await this.setGitUser();
|
1068 | if (title) {
|
1069 | this.release.hooks.createChangelogTitle.tap("Changelog Flag", () => title);
|
1070 | }
|
1071 | const lastRelease = from || (await this.git.getLatestRelease());
|
1072 | const bump = await this.release.getSemverBump(lastRelease, to);
|
1073 | const releaseNotes = await this.release.generateReleaseNotes(lastRelease, to, this.versionBump);
|
1074 | if (dryRun) {
|
1075 | this.logger.log.info("Potential Changelog Addition:\n", releaseNotes);
|
1076 | this.logger.verbose.info("`changelog` dry run complete.");
|
1077 | return;
|
1078 | }
|
1079 | if (args.quiet) {
|
1080 | console.log(releaseNotes);
|
1081 | }
|
1082 | else {
|
1083 | this.logger.log.info("New Release Notes\n", releaseNotes);
|
1084 | }
|
1085 | const currentVersion = await this.getCurrentVersion(lastRelease);
|
1086 | const context = {
|
1087 | bump,
|
1088 | commits: await this.release.getCommits(lastRelease, to || undefined),
|
1089 | releaseNotes,
|
1090 | lastRelease,
|
1091 | currentVersion,
|
1092 | };
|
1093 | if (!noCommit) {
|
1094 | await this.release.addToChangelog(releaseNotes, lastRelease, currentVersion);
|
1095 | await this.hooks.beforeCommitChangelog.promise(context);
|
1096 | await exec_promise_1.default("git", ["commit", "-m", `"${message}"`, "--no-verify"]);
|
1097 | this.logger.verbose.info("Committed new changelog.");
|
1098 | }
|
1099 | await this.hooks.afterAddToChangelog.promise(context);
|
1100 | }
|
1101 |
|
1102 | async makeRelease(args = {}) {
|
1103 | const options = Object.assign(Object.assign({}, this.getCommandDefault("release")), args);
|
1104 | const { dryRun, from, useVersion, prerelease = false } = options;
|
1105 | if (!this.release || !this.git) {
|
1106 | throw this.createErrorMessage();
|
1107 | }
|
1108 |
|
1109 | const [err, latestTag] = await await_to_js_1.default(this.git.getLatestTagInBranch());
|
1110 |
|
1111 |
|
1112 | if ((err === null || err === void 0 ? void 0 : err.message.includes("No names found")) && !args.dryRun) {
|
1113 | this.logger.log.error(endent_1.default `
|
1114 | Could not find any tags in the local repository. Exiting early.
|
1115 |
|
1116 | The "release" command creates GitHub releases for tags that have already been created in your repo.
|
1117 |
|
1118 | 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.
|
1119 | `, "\n");
|
1120 | this.logger.verbose.error(err);
|
1121 | return process.exit(1);
|
1122 | }
|
1123 | const isPrerelease = prerelease || this.inPrereleaseBranch();
|
1124 | let lastRelease = from ||
|
1125 | (isPrerelease && (await this.git.getPreviousTagInBranch())) ||
|
1126 | (await this.git.getLatestRelease());
|
1127 |
|
1128 | this.logger.veryVerbose.info(`Using ${lastRelease} as previous release.`);
|
1129 | if (lastRelease.match(/^\d+\.\d+\.\d+/)) {
|
1130 | lastRelease = this.prefixRelease(lastRelease);
|
1131 | }
|
1132 | this.logger.log.info('Current "Latest Release" on Github:', lastRelease);
|
1133 | const commitsInRelease = await this.release.getCommitsInRelease(lastRelease);
|
1134 | const releaseNotes = await this.release.generateReleaseNotes(lastRelease, undefined, this.versionBump);
|
1135 | this.logger.log.info(`Using release notes:\n${releaseNotes}`);
|
1136 | const rawVersion = useVersion ||
|
1137 | (isPrerelease && latestTag) ||
|
1138 | (await this.getCurrentVersion(lastRelease)) ||
|
1139 | latestTag;
|
1140 | if (!rawVersion) {
|
1141 | this.logger.log.error("Could not calculate next version from last tag.");
|
1142 | return;
|
1143 | }
|
1144 | const newVersion = semver_1.parse(rawVersion)
|
1145 | ? this.prefixRelease(rawVersion)
|
1146 | : rawVersion;
|
1147 | if (!dryRun &&
|
1148 | semver_1.parse(newVersion) &&
|
1149 | semver_1.parse(lastRelease) &&
|
1150 | semver_1.eq(newVersion, lastRelease)) {
|
1151 | this.logger.log.warn(`Nothing released to Github. Version to be released is the same as the latest release on Github: ${newVersion}`);
|
1152 | return;
|
1153 | }
|
1154 | const release = await this.hooks.makeRelease.promise({
|
1155 | dryRun,
|
1156 | from: lastRelease,
|
1157 | isPrerelease,
|
1158 | newVersion,
|
1159 | fullReleaseNotes: releaseNotes,
|
1160 | commits: commitsInRelease,
|
1161 | });
|
1162 | if (release) {
|
1163 | await this.hooks.afterRelease.promise({
|
1164 | lastRelease,
|
1165 | newVersion,
|
1166 | commits: commitsInRelease,
|
1167 | releaseNotes,
|
1168 | response: release,
|
1169 | });
|
1170 | }
|
1171 | return newVersion;
|
1172 | }
|
1173 |
|
1174 | createErrorMessage() {
|
1175 | return new Error(`Auto is not initialized! Make sure the have run Auto.loadConfig`);
|
1176 | }
|
1177 |
|
1178 | async getGitUser() {
|
1179 | try {
|
1180 | return {
|
1181 |
|
1182 | system: true,
|
1183 | email: await exec_promise_1.default("git", ["config", "user.email"]),
|
1184 | name: await exec_promise_1.default("git", ["config", "user.name"]),
|
1185 | };
|
1186 | }
|
1187 | catch (error) {
|
1188 | this.logger.verbose.warn("Could not find git user or email configured in git config");
|
1189 | if (!this.release) {
|
1190 | return;
|
1191 | }
|
1192 | let { email, name } = this.release.config;
|
1193 | this.logger.verbose.warn(`Got author from options: email: ${email}, name ${name}`);
|
1194 | const packageAuthor = await this.hooks.getAuthor.promise();
|
1195 | email = !email && packageAuthor ? packageAuthor.email : email;
|
1196 | name = !name && packageAuthor ? packageAuthor.name : name;
|
1197 | this.logger.verbose.warn(`Using author: ${name} <${email}>`);
|
1198 | return { email, name };
|
1199 | }
|
1200 | }
|
1201 | |
1202 |
|
1203 |
|
1204 | async setGitUser() {
|
1205 | const user = await this.getGitUser();
|
1206 | if (user && !user.system) {
|
1207 | if (!env.isCi) {
|
1208 | this.logger.log.note(endent_1.default `
|
1209 | Detected local environment, will not set git user. This happens automatically in a CI environment.
|
1210 |
|
1211 | If a command fails manually run:
|
1212 |
|
1213 | - git config user.email your@email.com
|
1214 | - git config user.name "Your Name"
|
1215 | `);
|
1216 | return;
|
1217 | }
|
1218 | if (user.email) {
|
1219 | await exec_promise_1.default("git", ["config", "user.email", `"${user.email}"`]);
|
1220 | this.logger.verbose.warn(`Set git email to ${user.email}`);
|
1221 | }
|
1222 | if (user.name) {
|
1223 | await exec_promise_1.default("git", ["config", "user.name", `"${user.name}"`]);
|
1224 | this.logger.verbose.warn(`Set git name to ${user.name}`);
|
1225 | }
|
1226 | }
|
1227 | }
|
1228 |
|
1229 | async getRepo(config) {
|
1230 | if (config.owner && config.repo) {
|
1231 | return config;
|
1232 | }
|
1233 | const author = await this.hooks.getRepository.promise();
|
1234 | if (!author || !author.owner || !author.repo) {
|
1235 | this.logger.log.error(endent_1.default `
|
1236 | Cannot find project owner and repository name!
|
1237 |
|
1238 | You must do one of the following:
|
1239 |
|
1240 | - configure the repo for your package manager (ex: set "repository" in package.json)
|
1241 | - configure your git remote 'origin' to point to your project on GitHub.
|
1242 | `, "");
|
1243 | process.exit(1);
|
1244 | }
|
1245 | return author;
|
1246 | }
|
1247 |
|
1248 | getExtendedLocation(config) {
|
1249 | let extendedLocation;
|
1250 | try {
|
1251 | if (config.extends) {
|
1252 | extendedLocation = require.resolve(config.extends);
|
1253 | }
|
1254 | }
|
1255 | catch (error) {
|
1256 | this.logger.veryVerbose.error(error);
|
1257 | }
|
1258 | return extendedLocation;
|
1259 | }
|
1260 | |
1261 |
|
1262 |
|
1263 | loadPlugins(config) {
|
1264 | config.plugins = config.plugins || [is_binary_1.default() ? "git-tag" : "npm"];
|
1265 | const extendedLocation = this.getExtendedLocation(config);
|
1266 | const pluginsPaths = [
|
1267 | require.resolve("./plugins/filter-non-pull-request"),
|
1268 | ...config.plugins,
|
1269 | ];
|
1270 | pluginsPaths
|
1271 | .map((plugin) =>
|
1272 |
|
1273 | typeof plugin === "string" ? [plugin, {}] : plugin)
|
1274 | .map((plugin) => load_plugins_1.loadPlugin(plugin, this.logger, extendedLocation))
|
1275 | .filter((plugin) => Boolean(plugin))
|
1276 | .forEach((plugin) => {
|
1277 | this.logger.verbose.info(`Using ${plugin.name} Plugin...`);
|
1278 | plugin.apply(this);
|
1279 | });
|
1280 | }
|
1281 |
|
1282 | async getPrEnvInfo() {
|
1283 | var _a, _b, _c, _d, _e, _f;
|
1284 |
|
1285 | let pr;
|
1286 | let build;
|
1287 | if ("pr" in env && "build" in env) {
|
1288 | ({ pr } = env);
|
1289 | ({ build } = env);
|
1290 | }
|
1291 | else if ("pr" in env && "commit" in env) {
|
1292 | ({ pr } = env);
|
1293 | build = env.commit;
|
1294 | }
|
1295 |
|
1296 |
|
1297 | if (env.isCi && !pr && ((_a = this.git) === null || _a === void 0 ? void 0 : _a.options.owner) && ((_b = this.git) === null || _b === void 0 ? void 0 : _b.options.repo)) {
|
1298 | const commit = await this.git.getSha();
|
1299 | const query = match_sha_to_pr_1.buildSearchQuery((_c = this.git) === null || _c === void 0 ? void 0 : _c.options.owner, (_d = this.git) === null || _d === void 0 ? void 0 : _d.options.repo, [commit]);
|
1300 | if (query) {
|
1301 | const result = await this.git.graphql(query);
|
1302 | if (result === null || result === void 0 ? void 0 : result[`hash_${commit}`]) {
|
1303 | const number = (_f = (_e = result[`hash_${commit}`].edges[0]) === null || _e === void 0 ? void 0 : _e.node) === null || _f === void 0 ? void 0 : _f.number;
|
1304 | if (number) {
|
1305 | pr = String(number);
|
1306 | }
|
1307 | }
|
1308 | }
|
1309 | }
|
1310 | return { pr, build };
|
1311 | }
|
1312 |
|
1313 | getCommandDefault(name) {
|
1314 | if (!this.config) {
|
1315 | return {};
|
1316 | }
|
1317 | const commandConfig = this.config[name];
|
1318 | return typeof commandConfig === "object" ? commandConfig : {};
|
1319 | }
|
1320 | }
|
1321 | exports.default = Auto;
|
1322 | var init_2 = require("./init");
|
1323 | exports.InteractiveInit = init_2.default;
|
1324 | var get_current_branch_2 = require("./utils/get-current-branch");
|
1325 | exports.getCurrentBranch = get_current_branch_2.getCurrentBranch;
|
1326 | var validate_config_2 = require("./validate-config");
|
1327 | exports.validatePluginConfiguration = validate_config_2.validatePluginConfiguration;
|
1328 | var auto_1 = require("./auto");
|
1329 | exports.Auto = auto_1.default;
|
1330 | var semver_4 = require("./semver");
|
1331 | exports.SEMVER = semver_4.default;
|
1332 | var exec_promise_2 = require("./utils/exec-promise");
|
1333 | exports.execPromise = exec_promise_2.default;
|
1334 | var get_lerna_packages_1 = require("./utils/get-lerna-packages");
|
1335 | exports.getLernaPackages = get_lerna_packages_1.default;
|
1336 | var in_folder_1 = require("./utils/in-folder");
|
1337 | exports.inFolder = in_folder_1.default;
|
1338 |
|
\ | No newline at end of file |