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