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 semver_1 = require("semver");
|
9 | const endent_1 = tslib_1.__importDefault(require("endent"));
|
10 | const https_proxy_agent_1 = tslib_1.__importDefault(require("https-proxy-agent"));
|
11 | const config_1 = tslib_1.__importDefault(require("./config"));
|
12 | const git_1 = tslib_1.__importDefault(require("./git"));
|
13 | const init_1 = tslib_1.__importDefault(require("./init"));
|
14 | const release_1 = tslib_1.__importStar(require("./release"));
|
15 | const semver_2 = tslib_1.__importStar(require("./semver"));
|
16 | const exec_promise_1 = tslib_1.__importDefault(require("./utils/exec-promise"));
|
17 | const load_plugins_1 = tslib_1.__importDefault(require("./utils/load-plugins"));
|
18 | const logger_1 = tslib_1.__importDefault(require("./utils/logger"));
|
19 | const make_hooks_1 = require("./utils/make-hooks");
|
20 | const proxyUrl = process.env.https_proxy || process.env.http_proxy;
|
21 | const env = env_ci_1.default();
|
22 |
|
23 | const loadEnv = () => {
|
24 | const envFile = path_1.default.resolve(process.cwd(), '.env');
|
25 | if (!fs_1.default.existsSync(envFile)) {
|
26 | return;
|
27 | }
|
28 | const envConfig = dotenv_1.default.parse(fs_1.default.readFileSync(envFile));
|
29 | Object.entries(envConfig).forEach(([key, value]) => {
|
30 | process.env[key] = value;
|
31 | });
|
32 | };
|
33 |
|
34 | function getPrNumberFromEnv(pr) {
|
35 | const envPr = 'pr' in env && Number(env.pr);
|
36 | const prNumber = pr || envPr;
|
37 | return prNumber;
|
38 | }
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | function determineNextVersion(lastVersion, currentVersion, bump, tag) {
|
48 | const next = semver_1.inc(lastVersion, `pre${bump}`, tag) || 'prerelease';
|
49 | return semver_1.lte(next, currentVersion)
|
50 | ? semver_1.inc(currentVersion, 'prerelease', tag) || 'prerelease'
|
51 | : next;
|
52 | }
|
53 | exports.determineNextVersion = determineNextVersion;
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | class Auto {
|
59 |
|
60 | constructor(options = {}) {
|
61 |
|
62 | this.checkClean = async () => {
|
63 | const status = await exec_promise_1.default('git', ['status', '--porcelain']);
|
64 | if (!status) {
|
65 | return;
|
66 | }
|
67 | this.logger.log.error('Changed Files:\n', status);
|
68 | throw new Error('Working direction is not clean, make sure all files are commited');
|
69 | };
|
70 |
|
71 | this.prefixRelease = (release) => {
|
72 | var _a;
|
73 | if (!this.release) {
|
74 | throw this.createErrorMessage();
|
75 | }
|
76 | return ((_a = this.config) === null || _a === void 0 ? void 0 : _a.noVersionPrefix) || release.startsWith('v')
|
77 | ? release
|
78 | : `v${release}`;
|
79 | };
|
80 | this.options = options;
|
81 | this.baseBranch = options.baseBranch || 'master';
|
82 | this.logger = logger_1.default(Array.isArray(options.verbose) && options.verbose.length > 1
|
83 | ? 'veryVerbose'
|
84 | : options.verbose
|
85 | ? 'verbose'
|
86 | : undefined);
|
87 | this.hooks = make_hooks_1.makeHooks();
|
88 | this.hooks.onCreateRelease.tap('Link onCreateChangelog', release => {
|
89 | release.hooks.onCreateChangelog.tap('Link onCreateChangelog', (changelog, version) => {
|
90 | this.hooks.onCreateChangelog.call(changelog, version);
|
91 | });
|
92 | });
|
93 | this.hooks.onCreateRelease.tap('Link onCreateLogParse', release => {
|
94 | release.hooks.onCreateLogParse.tap('Link onCreateLogParse', logParse => {
|
95 | this.hooks.onCreateLogParse.call(logParse);
|
96 | });
|
97 | });
|
98 | loadEnv();
|
99 | this.logger.verbose.info('ENV:', env);
|
100 | }
|
101 | |
102 |
|
103 |
|
104 |
|
105 | async loadConfig() {
|
106 | const configLoader = new config_1.default(this.logger);
|
107 | const config = Object.assign(Object.assign({}, (await configLoader.loadConfig(this.options))), { baseBranch: this.baseBranch });
|
108 | this.logger.verbose.success('Loaded `auto` with config:', config);
|
109 | this.config = config;
|
110 | this.labels = config.labels;
|
111 | this.semVerLabels = release_1.getVersionMap(config.labels);
|
112 | this.loadPlugins(config);
|
113 | this.config = this.hooks.modifyConfig.call(config);
|
114 | this.hooks.beforeRun.call(config);
|
115 | const repository = await this.getRepo(config);
|
116 | const token = (repository && repository.token) || process.env.GH_TOKEN;
|
117 | if (!token || token === 'undefined') {
|
118 | this.logger.log.error('No GitHub was found. Make sure it is available on process.env.GH_TOKEN.');
|
119 | throw new Error('GitHub token not found!');
|
120 | }
|
121 | const githubOptions = Object.assign(Object.assign({ owner: config.owner, repo: config.repo }, repository), { token, agent: proxyUrl ? new https_proxy_agent_1.default(proxyUrl) : undefined, baseUrl: config.githubApi || 'https://api.github.com', graphqlBaseUrl: config.githubGraphqlApi || config.githubApi || 'https://api.github.com' });
|
122 | this.git = this.startGit(githubOptions);
|
123 | this.release = new release_1.default(this.git, config, this.logger);
|
124 | this.hooks.onCreateRelease.call(this.release);
|
125 | }
|
126 | |
127 |
|
128 |
|
129 | async init(options = {}) {
|
130 | await init_1.default(options, this.logger);
|
131 | }
|
132 | |
133 |
|
134 |
|
135 |
|
136 |
|
137 | async createLabels(options = {}) {
|
138 | if (!this.release || !this.labels) {
|
139 | throw this.createErrorMessage();
|
140 | }
|
141 | await this.release.addLabelsToProject(this.labels, options);
|
142 | }
|
143 | |
144 |
|
145 |
|
146 |
|
147 |
|
148 | async label({ pr } = {}) {
|
149 | if (!this.git) {
|
150 | throw this.createErrorMessage();
|
151 | }
|
152 | this.logger.verbose.info("Using command: 'label'");
|
153 | const number = getPrNumberFromEnv(pr);
|
154 | let labels = [];
|
155 | if (number) {
|
156 | labels = await this.git.getLabels(number);
|
157 | }
|
158 | else {
|
159 | const pulls = await this.git.getPullRequests({
|
160 | state: 'closed'
|
161 | });
|
162 | const lastMerged = pulls
|
163 | .sort((a, b) => new Date(b.merged_at).getTime() - new Date(a.merged_at).getTime())
|
164 | .find(pull => pull.merged_at);
|
165 | if (lastMerged) {
|
166 | labels = lastMerged.labels.map(label => label.name);
|
167 | }
|
168 | }
|
169 | if (labels.length) {
|
170 | console.log(labels.join('\n'));
|
171 | }
|
172 | }
|
173 | |
174 |
|
175 |
|
176 |
|
177 |
|
178 | async prStatus(_a) {
|
179 | var { dryRun, pr, url } = _a, options = tslib_1.__rest(_a, ["dryRun", "pr", "url"]);
|
180 | if (!this.git) {
|
181 | throw this.createErrorMessage();
|
182 | }
|
183 | let { sha } = options;
|
184 | let prNumber;
|
185 | try {
|
186 | prNumber = this.getPrNumber('pr', pr);
|
187 | }
|
188 | catch (error) {
|
189 |
|
190 | }
|
191 | this.logger.verbose.info("Using command: 'pr-status'");
|
192 | if (!sha && prNumber) {
|
193 | this.logger.verbose.info('Getting commit SHA from PR.');
|
194 | const res = await this.git.getPullRequest(prNumber);
|
195 | sha = res.data.head.sha;
|
196 | }
|
197 | else if (!sha) {
|
198 | this.logger.verbose.info('No PR found, getting commit SHA from HEAD.');
|
199 | sha = await this.git.getSha();
|
200 | }
|
201 | this.logger.verbose.info('Found PR SHA:', sha);
|
202 | const target_url = url;
|
203 | if (dryRun) {
|
204 | this.logger.verbose.info('`pr` dry run complete.');
|
205 | }
|
206 | else {
|
207 | try {
|
208 | await this.git.createStatus(Object.assign(Object.assign({}, options), { sha,
|
209 | target_url }));
|
210 | }
|
211 | catch (error) {
|
212 | throw new Error(`Failed to post status to Pull Request with error code ${error.status}`);
|
213 | }
|
214 | this.logger.log.success('Posted status to Pull Request.');
|
215 | }
|
216 | this.logger.verbose.success('Finished `pr` command');
|
217 | }
|
218 | |
219 |
|
220 |
|
221 |
|
222 |
|
223 | async prCheck(_a) {
|
224 | var _b;
|
225 | var { dryRun, pr, url } = _a, options = tslib_1.__rest(_a, ["dryRun", "pr", "url"]);
|
226 | if (!this.git || !this.release || !this.semVerLabels) {
|
227 | throw this.createErrorMessage();
|
228 | }
|
229 | this.logger.verbose.info(`Using command: 'pr-check' for '${url}'`);
|
230 | const target_url = url;
|
231 | const prNumber = this.getPrNumber('prCheck', pr);
|
232 | let msg;
|
233 | let sha;
|
234 | try {
|
235 | const res = await this.git.getPullRequest(prNumber);
|
236 | sha = res.data.head.sha;
|
237 | const labels = await this.git.getLabels(prNumber);
|
238 | const labelValues = [...this.semVerLabels.values()];
|
239 | const releaseTag = labels.find(l => l === 'release');
|
240 | const skipReleaseLabels = (((_b = this.config) === null || _b === void 0 ? void 0 : _b.labels.filter(l => l.releaseType === 'skip')) || []).map(l => l.name);
|
241 | const skipReleaseTag = labels.find(l => skipReleaseLabels.includes(l));
|
242 | const semverTag = labels.find(l => labelValues.some(labelValue => labelValue.includes(l)) &&
|
243 | !skipReleaseLabels.includes(l) &&
|
244 | l !== 'release');
|
245 | if (semverTag === undefined && !skipReleaseTag) {
|
246 | throw new Error('No semver label!');
|
247 | }
|
248 | this.logger.log.success(`PR is using label: ${semverTag || skipReleaseTag}`);
|
249 | let description;
|
250 | if (skipReleaseTag) {
|
251 | description = 'PR will not create a release';
|
252 | }
|
253 | else if (releaseTag) {
|
254 | description = `PR will create release once merged - ${semverTag}`;
|
255 | }
|
256 | else {
|
257 | description = `CI - ${semverTag}`;
|
258 | }
|
259 | msg = {
|
260 | description,
|
261 | state: 'success'
|
262 | };
|
263 | }
|
264 | catch (error) {
|
265 | msg = {
|
266 | description: error.message,
|
267 | state: 'error'
|
268 | };
|
269 | }
|
270 | this.logger.verbose.info('Posting status to GitHub\n', msg);
|
271 | if (dryRun) {
|
272 | this.logger.verbose.info('`pr-check` dry run complete.');
|
273 | }
|
274 | else {
|
275 | try {
|
276 | await this.git.createStatus(Object.assign(Object.assign(Object.assign({}, options), msg), { target_url,
|
277 | sha }));
|
278 | this.logger.log.success('Posted status to Pull Request.');
|
279 | }
|
280 | catch (error) {
|
281 | throw new Error(`Failed to post status to Pull Request with error code ${error.status}`);
|
282 | }
|
283 | }
|
284 | this.logger.verbose.success('Finished `pr-check` command');
|
285 | }
|
286 | |
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 | async comment(options) {
|
293 | const { message, pr, context = 'default', dryRun, delete: deleteFlag, edit: editFlag } = options;
|
294 | if (!this.git) {
|
295 | throw this.createErrorMessage();
|
296 | }
|
297 | this.logger.verbose.info("Using command: 'comment'");
|
298 | const prNumber = this.getPrNumber('comment', pr);
|
299 | if (dryRun) {
|
300 | if (deleteFlag) {
|
301 | this.logger.log.info(`Would have deleted comment on ${prNumber} under "${context}" context`);
|
302 | }
|
303 | else if (editFlag) {
|
304 | this.logger.log.info(`Would have edited the comment on ${prNumber} under "${context}" context.\n\nNew message: ${message}`);
|
305 | }
|
306 | else {
|
307 | this.logger.log.info(`Would have commented on ${prNumber} under "${context}" context:\n\n${message}`);
|
308 | }
|
309 | }
|
310 | else if (editFlag && message) {
|
311 | await this.git.editComment(message, prNumber, context);
|
312 | this.logger.log.success(`Edited comment on PR #${prNumber} under context "${context}"`);
|
313 | }
|
314 | else {
|
315 | if (deleteFlag) {
|
316 | await this.git.deleteComment(prNumber, context);
|
317 | this.logger.log.success(`Deleted comment on PR #${prNumber} under context "${context}"`);
|
318 | }
|
319 | if (message) {
|
320 | await this.git.createComment(message, prNumber, context);
|
321 | this.logger.log.success(`Commented on PR #${prNumber}`);
|
322 | }
|
323 | }
|
324 | }
|
325 | |
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 | async prBody(options) {
|
333 | const { message, pr, context = 'default', dryRun, delete: deleteFlag } = options;
|
334 | if (!this.git) {
|
335 | throw this.createErrorMessage();
|
336 | }
|
337 | this.logger.verbose.info("Using command: 'pr-body'");
|
338 | const prNumber = this.getPrNumber('pr-body', pr);
|
339 | if (dryRun) {
|
340 | if (deleteFlag) {
|
341 | this.logger.log.info(`Would have deleted PR body on ${prNumber} under "${context}" context`);
|
342 | }
|
343 | else {
|
344 | this.logger.log.info(`Would have appended to PR body on ${prNumber} under "${context}" context:\n\n${message}`);
|
345 | }
|
346 | }
|
347 | else {
|
348 | if (deleteFlag) {
|
349 | await this.git.addToPrBody('', prNumber, context);
|
350 | }
|
351 | if (message) {
|
352 | await this.git.addToPrBody(message, prNumber, context);
|
353 | }
|
354 | this.logger.log.success(`Updated body on PR #${prNumber}`);
|
355 | }
|
356 | }
|
357 | |
358 |
|
359 |
|
360 | async version(options = {}) {
|
361 | this.logger.verbose.info("Using command: 'version'");
|
362 | const bump = await this.getVersion(options);
|
363 | console.log(bump);
|
364 | }
|
365 | |
366 |
|
367 |
|
368 | async changelog(options) {
|
369 | this.logger.verbose.info("Using command: 'changelog'");
|
370 | await this.makeChangelog(options);
|
371 | }
|
372 | |
373 |
|
374 |
|
375 | async runRelease(options = {}) {
|
376 | this.logger.verbose.info("Using command: 'release'");
|
377 | await this.makeRelease(options);
|
378 | }
|
379 |
|
380 | async canary(options = {}) {
|
381 | if (!this.git || !this.release) {
|
382 | throw this.createErrorMessage();
|
383 | }
|
384 | if (!this.hooks.canary.isUsed()) {
|
385 | this.logger.log.error(endent_1.default `
|
386 | None of the plugins that you are using implement the \`canary\` command!
|
387 |
|
388 | "canary" releases are versions that are used solely to test changes. They make sense on some platforms (ex: npm) but not all!
|
389 |
|
390 | If you think your package manager has the ability to support canaries please file an issue or submit a pull request,
|
391 | `);
|
392 | process.exit(1);
|
393 | }
|
394 | await this.checkClean();
|
395 |
|
396 | let pr;
|
397 | let build;
|
398 | if ('pr' in env && 'build' in env) {
|
399 | ({ pr } = env);
|
400 | ({ build } = env);
|
401 | }
|
402 | else if ('pr' in env && 'commit' in env) {
|
403 | ({ pr } = env);
|
404 | build = env.commit;
|
405 | }
|
406 | pr = options.pr ? String(options.pr) : pr;
|
407 | build = options.build ? String(options.build) : build;
|
408 | this.logger.verbose.info('Canary info found:', { pr, build });
|
409 | const head = await this.release.getCommitsInRelease('HEAD^');
|
410 | const labels = head.map(commit => commit.labels);
|
411 | const version = semver_2.calculateSemVerBump(labels, this.semVerLabels, this.config) ||
|
412 | semver_2.default.patch;
|
413 | let canaryVersion = '';
|
414 | if (pr) {
|
415 | canaryVersion = `${canaryVersion}.${pr}`;
|
416 | }
|
417 | if (build) {
|
418 | canaryVersion = `${canaryVersion}.${build}`;
|
419 | }
|
420 | if (!pr || !build) {
|
421 | canaryVersion = `${canaryVersion}.${await this.git.getSha(true)}`;
|
422 | }
|
423 | let newVersion = '';
|
424 | if (options.dryRun) {
|
425 | this.logger.log.warn(`Published canary identifier would be: "-canary${canaryVersion}"`);
|
426 | }
|
427 | else {
|
428 | this.logger.verbose.info('Calling canary hook');
|
429 | const result = await this.hooks.canary.promise(version, canaryVersion);
|
430 | if (typeof result === 'object') {
|
431 | this.logger.log.warn(result.error);
|
432 | return;
|
433 | }
|
434 | newVersion = result;
|
435 | const message = options.message || 'Published PR with canary version: %v';
|
436 | if (message !== 'false' && pr) {
|
437 | await this.prBody({
|
438 | pr: Number(pr),
|
439 | context: 'canary-version',
|
440 | message: message.replace('%v', !newVersion || newVersion.includes('\n')
|
441 | ? newVersion
|
442 | : `\`${newVersion}\``)
|
443 | });
|
444 | }
|
445 | this.logger.log.success(`Published canary version${newVersion ? `: ${newVersion}` : ''}`);
|
446 | }
|
447 | let latestTag;
|
448 | try {
|
449 | latestTag = await this.git.getLatestTagInBranch();
|
450 | }
|
451 | catch (error) {
|
452 | latestTag = await this.git.getFirstCommit();
|
453 | }
|
454 | const commitsInRelease = await this.release.getCommits(latestTag);
|
455 | return { newVersion, commitsInRelease };
|
456 | }
|
457 | |
458 |
|
459 |
|
460 |
|
461 | async next(options) {
|
462 | if (!this.git || !this.release) {
|
463 | throw this.createErrorMessage();
|
464 | }
|
465 | if (!this.hooks.next.isUsed()) {
|
466 | this.logger.log.error(endent_1.default `
|
467 | None of the plugins that you are using implement the \`next\` command!
|
468 |
|
469 | "next" releases are pre-releases such as betas or alphas. They make sense on some platforms (ex: npm) but not all!
|
470 |
|
471 | If you think your package manager has the ability to support "next" releases please file an issue or submit a pull request,
|
472 | `);
|
473 | process.exit(1);
|
474 | }
|
475 | await this.checkClean();
|
476 | await this.setGitUser();
|
477 | const lastRelease = await this.git.getLatestRelease();
|
478 | const lastTag = await this.git.getLatestTagInBranch();
|
479 | const commits = await this.release.getCommitsInRelease(lastTag);
|
480 | const releaseNotes = await this.release.generateReleaseNotes(lastTag);
|
481 | const labels = commits.map(commit => commit.labels);
|
482 | const bump = semver_2.calculateSemVerBump(labels, this.semVerLabels, this.config) ||
|
483 | semver_2.default.patch;
|
484 | if (options.dryRun) {
|
485 | this.logger.log.success(`Would have created prerelease version with: ${bump}`);
|
486 | return { newVersion: '', commitsInRelease: commits };
|
487 | }
|
488 | this.logger.verbose.info(`Calling "next" hook with: ${bump}`);
|
489 | const result = await this.hooks.next.promise([], bump);
|
490 | const newVersion = result.join(', ');
|
491 | await Promise.all(result.map(async (prerelease) => {
|
492 | var _a;
|
493 | const release = await ((_a = this.git) === null || _a === void 0 ? void 0 : _a.publish(releaseNotes, prerelease, true));
|
494 | this.logger.verbose.info(release);
|
495 | await this.hooks.afterRelease.promise({
|
496 | lastRelease: lastTag,
|
497 | newVersion: prerelease,
|
498 | commits,
|
499 | releaseNotes,
|
500 | response: release
|
501 | });
|
502 | }));
|
503 | this.logger.log.success(`Published next version${result.length > 1 ? `s` : ''}: ${newVersion}`);
|
504 | if ('isPr' in env && env.isPr) {
|
505 | const message = options.message || 'Published prerelease version: %v';
|
506 | const pr = 'pr' in env && env.pr;
|
507 | if (pr) {
|
508 | await this.prBody({
|
509 | pr: Number(pr),
|
510 | context: 'prerelease-version',
|
511 | message: endent_1.default `
|
512 | # Version
|
513 |
|
514 | ${message.replace('%v', result.map(r => `\`${r}\``).join('\n'))}
|
515 |
|
516 | <details>
|
517 | <summary>Changelog</summary>
|
518 | ${await this.release.generateReleaseNotes(lastRelease)}
|
519 | </details>
|
520 | `
|
521 | });
|
522 | }
|
523 | }
|
524 | return { newVersion, commitsInRelease: commits };
|
525 | }
|
526 | |
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 |
|
533 |
|
534 | async shipit(options = {}) {
|
535 | var _a, _b;
|
536 | if (!this.git || !this.release) {
|
537 | throw this.createErrorMessage();
|
538 | }
|
539 | this.logger.verbose.info("Using command: 'shipit'");
|
540 | this.hooks.beforeShipIt.call();
|
541 | const isPR = 'isPr' in env && env.isPr;
|
542 | const head = await this.release.getCommitsInRelease('HEAD^');
|
543 |
|
544 |
|
545 | const currentBranch = isPR
|
546 | ? 'prBranch' in env && env.prBranch
|
547 | : 'branch' in env && env.branch;
|
548 | const isBaseBrach = !isPR && currentBranch === this.baseBranch;
|
549 | const shouldGraduate = !options.onlyGraduateWithReleaseLabel ||
|
550 | (options.onlyGraduateWithReleaseLabel &&
|
551 | 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); }));
|
552 | 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);
|
553 | const publishPrerelease = isPrereleaseBranch ||
|
554 | (currentBranch === this.baseBranch &&
|
555 | options.onlyGraduateWithReleaseLabel);
|
556 | this.logger.veryVerbose.info({
|
557 | currentBranch,
|
558 | isBaseBrach,
|
559 | isPR,
|
560 | shouldGraduate,
|
561 | isPrereleaseBranch,
|
562 | publishPrerelease
|
563 | });
|
564 | const publishInfo = isBaseBrach && shouldGraduate
|
565 | ? await this.publishLatest(options)
|
566 | : publishPrerelease
|
567 | ? await this.next(options)
|
568 | : await this.canary(options);
|
569 | if (!publishInfo) {
|
570 | return;
|
571 | }
|
572 | const { newVersion, commitsInRelease } = publishInfo;
|
573 | await this.hooks.afterShipIt.promise(newVersion, commitsInRelease);
|
574 | }
|
575 |
|
576 | async getCurrentVersion(lastRelease) {
|
577 | this.hooks.getPreviousVersion.tap('None', () => {
|
578 | this.logger.veryVerbose.info('No previous release found, using 0.0.0 as previous version.');
|
579 | return this.prefixRelease('0.0.0');
|
580 | });
|
581 | const lastVersion = await this.hooks.getPreviousVersion.promise();
|
582 | if (semver_1.parse(lastRelease) &&
|
583 | semver_1.parse(lastVersion) &&
|
584 | semver_1.gt(lastRelease, lastVersion)) {
|
585 | this.logger.veryVerbose.info('Using latest release as previous version');
|
586 | return lastRelease;
|
587 | }
|
588 | return lastVersion;
|
589 | }
|
590 | |
591 |
|
592 |
|
593 | checkEnv(pluginName, key) {
|
594 | if (!process.env[key]) {
|
595 | this.logger.log.warn(`${pluginName}: No "${key}" found in environment`);
|
596 | }
|
597 | }
|
598 |
|
599 | async publishLatest(options) {
|
600 | if (!this.git || !this.release) {
|
601 | throw this.createErrorMessage();
|
602 | }
|
603 | const version = await this.getVersion();
|
604 | if (version === '') {
|
605 | this.logger.log.info('No version published.');
|
606 | return;
|
607 | }
|
608 | const lastRelease = await this.git.getLatestRelease();
|
609 | const commitsInRelease = await this.release.getCommitsInRelease(lastRelease);
|
610 | await this.makeChangelog(options);
|
611 | if (!options.dryRun) {
|
612 | await this.checkClean();
|
613 | this.logger.verbose.info('Calling version hook');
|
614 | await this.hooks.version.promise(version);
|
615 | this.logger.verbose.info('Calling after version hook');
|
616 | await this.hooks.afterVersion.promise();
|
617 | this.logger.verbose.info('Calling publish hook');
|
618 | await this.hooks.publish.promise(version);
|
619 | this.logger.verbose.info('Calling after publish hook');
|
620 | await this.hooks.afterPublish.promise();
|
621 | }
|
622 | const newVersion = await this.makeRelease(options);
|
623 | if (options.dryRun) {
|
624 | this.logger.log.warn("The version reported in the line above hasn't been incremented during `dry-run`");
|
625 | const current = await this.getCurrentVersion(lastRelease);
|
626 | if (semver_1.parse(current)) {
|
627 | this.logger.log.warn(`Published version would be: ${semver_1.inc(current, version)}`);
|
628 | }
|
629 | }
|
630 | return { newVersion, commitsInRelease };
|
631 | }
|
632 |
|
633 | getPrNumber(command, pr) {
|
634 | const prNumber = getPrNumberFromEnv(pr);
|
635 | if (!prNumber) {
|
636 | throw new Error(`Could not detect PR number. ${command} must be run from either a PR or have the PR number supplied via the --pr flag.`);
|
637 | }
|
638 | return prNumber;
|
639 | }
|
640 |
|
641 | startGit(gitOptions) {
|
642 | if (!gitOptions.owner || !gitOptions.repo || !gitOptions.token) {
|
643 | throw new Error('Must set owner, repo, and GitHub token.');
|
644 | }
|
645 | this.logger.verbose.info('Options contain repo information.');
|
646 |
|
647 | const tokenlessArgs = Object.assign(Object.assign({}, gitOptions), { token: `[Token starting with ${gitOptions.token.substring(0, 4)}]` });
|
648 | this.logger.verbose.info('Initializing GitHub API with:\n', tokenlessArgs);
|
649 | return new git_1.default({
|
650 | owner: gitOptions.owner,
|
651 | repo: gitOptions.repo,
|
652 | token: gitOptions.token,
|
653 | baseUrl: gitOptions.baseUrl,
|
654 | graphqlBaseUrl: gitOptions.graphqlBaseUrl,
|
655 | agent: gitOptions.agent
|
656 | }, this.logger);
|
657 | }
|
658 |
|
659 | async getVersion({ from } = {}) {
|
660 | if (!this.git || !this.release) {
|
661 | throw this.createErrorMessage();
|
662 | }
|
663 | const lastRelease = from || (await this.git.getLatestRelease());
|
664 | const bump = await this.release.getSemverBump(lastRelease);
|
665 | this.versionBump = bump;
|
666 | return bump;
|
667 | }
|
668 |
|
669 | async makeChangelog({ dryRun, from, to, message = 'Update CHANGELOG.md [skip ci]' } = {}) {
|
670 | if (!this.release || !this.git) {
|
671 | throw this.createErrorMessage();
|
672 | }
|
673 | await this.setGitUser();
|
674 | const lastRelease = from || (await this.git.getLatestRelease());
|
675 | const bump = await this.release.getSemverBump(lastRelease, to);
|
676 | const releaseNotes = await this.release.generateReleaseNotes(lastRelease, to || undefined, this.versionBump);
|
677 | this.logger.log.info('New Release Notes\n', releaseNotes);
|
678 | if (dryRun) {
|
679 | this.logger.verbose.info('`changelog` dry run complete.');
|
680 | return;
|
681 | }
|
682 | const currentVersion = await this.getCurrentVersion(lastRelease);
|
683 | await this.release.addToChangelog(releaseNotes, lastRelease, currentVersion);
|
684 | const options = {
|
685 | bump,
|
686 | commits: await this.release.getCommits(lastRelease, to || undefined),
|
687 | releaseNotes,
|
688 | lastRelease,
|
689 | currentVersion
|
690 | };
|
691 | await this.hooks.beforeCommitChangelog.promise(options);
|
692 | await exec_promise_1.default('git', ['commit', '-m', `"${message}"`, '--no-verify']);
|
693 | this.logger.verbose.info('Committed new changelog.');
|
694 | await this.hooks.afterAddToChangelog.promise(options);
|
695 | }
|
696 |
|
697 | async makeRelease({ dryRun, from, useVersion, prerelease = false } = {}) {
|
698 | if (!this.release || !this.git) {
|
699 | throw this.createErrorMessage();
|
700 | }
|
701 | let lastRelease = from || (await this.git.getLatestRelease());
|
702 |
|
703 | this.logger.veryVerbose.info(`Using ${lastRelease} as previous release.`);
|
704 | if (lastRelease.match(/^\d+\.\d+\.\d+/)) {
|
705 | lastRelease = this.prefixRelease(lastRelease);
|
706 | }
|
707 | this.logger.log.info('Last used release:', lastRelease);
|
708 | const commitsInRelease = await this.release.getCommitsInRelease(lastRelease);
|
709 | const releaseNotes = await this.release.generateReleaseNotes(lastRelease, undefined, this.versionBump);
|
710 | this.logger.log.info(`Using release notes:\n${releaseNotes}`);
|
711 | const rawVersion = useVersion ||
|
712 | (await this.getCurrentVersion(lastRelease)) ||
|
713 | (await this.git.getLatestTagInBranch());
|
714 | if (!rawVersion) {
|
715 | this.logger.log.error('Could not calculate next version from last tag.');
|
716 | return;
|
717 | }
|
718 | const newVersion = semver_1.parse(rawVersion)
|
719 | ? this.prefixRelease(rawVersion)
|
720 | : rawVersion;
|
721 | if (!dryRun &&
|
722 | semver_1.parse(newVersion) &&
|
723 | semver_1.parse(lastRelease) &&
|
724 | semver_1.eq(newVersion, lastRelease)) {
|
725 | this.logger.log.warn(`Nothing released to Github. Version to be released is the same as the latest release on Github: ${newVersion}`);
|
726 | return;
|
727 | }
|
728 | let release;
|
729 | if (dryRun) {
|
730 | this.logger.log.info(`Would have released (unless ran with "shipit"): ${newVersion}`);
|
731 | }
|
732 | else {
|
733 | this.logger.log.info(`Releasing ${newVersion} to GitHub.`);
|
734 | release = await this.git.publish(releaseNotes, newVersion, prerelease);
|
735 | await this.hooks.afterRelease.promise({
|
736 | lastRelease,
|
737 | newVersion,
|
738 | commits: commitsInRelease,
|
739 | releaseNotes,
|
740 | response: release
|
741 | });
|
742 | }
|
743 | return newVersion;
|
744 | }
|
745 |
|
746 | createErrorMessage() {
|
747 | return new Error(`Auto is not initialized! Make sure the have run Auto.loadConfig`);
|
748 | }
|
749 | |
750 |
|
751 |
|
752 | async setGitUser() {
|
753 | try {
|
754 |
|
755 | await exec_promise_1.default('git', ['config', 'user.email']);
|
756 | await exec_promise_1.default('git', ['config', 'user.name']);
|
757 | }
|
758 | catch (error) {
|
759 | this.logger.verbose.warn('Could not find git user or email configured in environment');
|
760 | if (!env.isCi) {
|
761 | this.logger.log.note(`Detected local environment, will not set git user. This happens automatically in a CI environment.
|
762 |
|
763 | If a command fails manually run:
|
764 |
|
765 | - git config user.email your@email.com
|
766 | - git config user.name "Your Name"`);
|
767 | return;
|
768 | }
|
769 | if (!this.release) {
|
770 | return;
|
771 | }
|
772 | let { email, name } = this.release.config;
|
773 | this.logger.verbose.warn(`Got author from options: email: ${email}, name ${name}`);
|
774 | const packageAuthor = await this.hooks.getAuthor.promise();
|
775 | this.logger.verbose.warn(`Got author: ${JSON.stringify(packageAuthor, undefined, 2)}`);
|
776 | email = !email && packageAuthor ? packageAuthor.email : email;
|
777 | name = !name && packageAuthor ? packageAuthor.name : name;
|
778 | if (email) {
|
779 | await exec_promise_1.default('git', ['config', 'user.email', `"${email}"`]);
|
780 | this.logger.verbose.warn(`Set git email to ${email}`);
|
781 | }
|
782 | if (name) {
|
783 | await exec_promise_1.default('git', ['config', 'user.name', `"${name}"`]);
|
784 | this.logger.verbose.warn(`Set git name to ${name}`);
|
785 | }
|
786 | }
|
787 | }
|
788 |
|
789 | async getRepo(config) {
|
790 | if (config.owner && config.repo) {
|
791 | return config;
|
792 | }
|
793 | return this.hooks.getRepository.promise();
|
794 | }
|
795 | |
796 |
|
797 |
|
798 | loadPlugins(config) {
|
799 |
|
800 | const defaultPlugins = [process.pkg ? 'git-tag' : 'npm'];
|
801 | const pluginsPaths = [
|
802 | require.resolve('./plugins/filter-non-pull-request'),
|
803 | ...(config.plugins || defaultPlugins)
|
804 | ];
|
805 | pluginsPaths
|
806 | .map(plugin =>
|
807 |
|
808 | typeof plugin === 'string' ? [plugin, {}] : plugin)
|
809 | .map(plugin => load_plugins_1.default(plugin, this.logger))
|
810 | .filter((plugin) => Boolean(plugin))
|
811 | .forEach(plugin => {
|
812 | this.logger.verbose.info(`Using ${plugin.name} Plugin...`);
|
813 | plugin.apply(this);
|
814 | });
|
815 | }
|
816 | }
|
817 | exports.default = Auto;
|
818 | var auto_1 = require("./auto");
|
819 | exports.Auto = auto_1.default;
|
820 | var semver_3 = require("./semver");
|
821 | exports.SEMVER = semver_3.default;
|
822 | var exec_promise_2 = require("./utils/exec-promise");
|
823 | exports.execPromise = exec_promise_2.default;
|
824 |
|
\ | No newline at end of file |