@thisismanta/semantic-version 1.0.4 → 2.0.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,35 +1,123 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
2
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
+ const semver_1 = __importDefault(require("semver"));
3
30
  const execa_1 = require("execa");
31
+ const github = __importStar(require("@actions/github"));
4
32
  const index_1 = require("./index");
33
+ const debug_1 = require("./debug");
5
34
  main();
6
35
  async function main() {
7
- const message = await run('git log -1 HEAD --format=%s');
8
- console.log('message »', message);
9
- const { type, breaking } = (0, index_1.checkConventionalMessage)(message, console);
10
- const releaseType = (() => {
11
- if (breaking) {
36
+ const lastVersion = semver_1.default.valid(await run('git describe --tags --abbrev=0') ||
37
+ await run('npm pkg get version'));
38
+ (0, debug_1.debug)('lastVersion »', JSON.stringify(lastVersion));
39
+ if (!lastVersion) {
40
+ throw new Error('Expect to have a last version on Git tag or package.json `version` field.');
41
+ }
42
+ const commits = (await run(`git log v${lastVersion}..HEAD --format="%H %s"`))
43
+ .trim()
44
+ .split('\n')
45
+ .map(line => {
46
+ const hash = line.substring(0, line.indexOf(' '));
47
+ const message = line.substring(line.indexOf(' ') + 1);
48
+ const { type, breaking, subject } = (0, index_1.checkConventionalMessage)(message, { debug: debug_1.debug });
49
+ return {
50
+ hash,
51
+ type,
52
+ breaking,
53
+ subject,
54
+ };
55
+ });
56
+ console.log(`Found ${commits.length} commits since ${lastVersion}`);
57
+ (0, debug_1.debug)('commits »', JSON.stringify(commits, null, 2));
58
+ const releaseType = commits.reduce((releaseType, { type, breaking }) => {
59
+ if (releaseType === 'major' || breaking) {
12
60
  return 'major';
13
61
  }
14
- else if (type === 'feat') {
15
- return 'minor';
62
+ if (releaseType === 'minor' || type === 'feat') {
63
+ return releaseType;
16
64
  }
17
- else if (type === 'fix') {
65
+ if (type === 'fix') {
18
66
  return 'patch';
19
67
  }
20
- // Indicate no version bump
21
- return '';
22
- })();
23
- console.log('releaseType »', releaseType);
68
+ return releaseType;
69
+ }, null);
70
+ (0, debug_1.debug)('releaseType »', JSON.stringify(releaseType));
24
71
  if (!releaseType) {
72
+ console.log('Exited without releasing a new version');
25
73
  return;
26
74
  }
27
- const newVersion = await run(`npm version --json --no-commit-hooks ${releaseType}`);
28
- await run(`git push origin refs/tags/${newVersion}`);
75
+ const nextVersion = await run(`npm version --json --no-commit-hooks ${releaseType}`);
76
+ (0, debug_1.debug)('nextVersion »', nextVersion);
77
+ console.log(`Created version ${releaseType}`);
78
+ await run(`git push --follow-tags origin master`);
79
+ console.log(`Pushed Git tags`);
80
+ if (semver_1.default.valid(nextVersion) && process.env.GITHUB_TOKEN) {
81
+ const commitGroups = {
82
+ 'BREAKING CHANGES': [],
83
+ 'Features': [],
84
+ 'Bug Fixes': [],
85
+ 'Others': [],
86
+ };
87
+ for (const commit of commits) {
88
+ if (commit.breaking) {
89
+ commitGroups['BREAKING CHANGES'].push(commit);
90
+ }
91
+ else if (commit.type === 'feat') {
92
+ commitGroups['Features'].push(commit);
93
+ }
94
+ else if (commit.type === 'bug') {
95
+ commitGroups['Bug Fixes'].push(commit);
96
+ }
97
+ else {
98
+ commitGroups['Others'].push(commit);
99
+ }
100
+ }
101
+ const releaseNote = Object.entries(commitGroups)
102
+ .filter(([title, commits]) => commits.length > 0)
103
+ .map(([title, commits]) => `### ${title}\n\n` +
104
+ commits.map(({ subject, hash }) => `- ${subject} (${hash})`).join('\n'))
105
+ .join('\n\n');
106
+ (0, debug_1.debug)('releaseNote »', releaseNote);
107
+ const octokit = github.getOctokit(process.env.GITHUB_TOKEN);
108
+ // See https://octokit.github.io/rest.js/v19#repos-create-release
109
+ const octokitRespond = await octokit.rest.repos.createRelease({
110
+ ...github.context.repo,
111
+ tag_name: nextVersion,
112
+ body: releaseNote,
113
+ });
114
+ (0, debug_1.debug)('octokitRespond »', JSON.stringify(octokitRespond, null, 2));
115
+ console.log('Created a new release on GitHub');
116
+ }
29
117
  }
30
118
  async function run(command) {
31
- console.log(command);
119
+ (0, debug_1.debug)(command);
32
120
  const { stdout } = await (0, execa_1.execaCommand)(command);
33
- console.log(stdout);
121
+ (0, debug_1.debug)(stdout);
34
122
  return stdout;
35
123
  }
package/lib/debug.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function debug(...args: Array<any>): void;
package/lib/debug.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.debug = void 0;
4
+ function debug(...args) {
5
+ if (process.env.DEBUG) {
6
+ console.log(...args);
7
+ }
8
+ }
9
+ exports.debug = debug;
package/lib/index.d.ts CHANGED
@@ -2,6 +2,6 @@ export declare const allowedTypes: string[];
2
2
  export declare function checkConventionalMessage(message: string, { debug }: Pick<Console, 'debug'>): {
3
3
  type: string | undefined;
4
4
  breaking: boolean;
5
- subject: string | undefined;
5
+ subject: string;
6
6
  errors: string[];
7
7
  };
package/lib/index.js CHANGED
@@ -5,8 +5,11 @@ const titlePattern = /^(?<type>\w+)(?<scope>\(.*?\))?(?<breaking>\!)?:(?<subject
5
5
  exports.allowedTypes = ['feat', 'fix', 'test', 'refactor', 'chore'];
6
6
  function checkConventionalMessage(message, { debug }) {
7
7
  const pattern = (message.match(titlePattern)?.groups || {});
8
- debug(JSON.stringify(pattern, null, 2));
9
8
  const { type, scope, breaking, subject } = pattern;
9
+ debug('type »', type);
10
+ debug('scope »', scope);
11
+ debug('breaking »', breaking);
12
+ debug('subject »', subject);
10
13
  const errors = [
11
14
  !type &&
12
15
  'The pull request title must match the pattern of "<type>[!]: <subject>" which is a reduced set of https://www.conventionalcommits.org/en/v1.0.0/',
@@ -22,11 +25,15 @@ function checkConventionalMessage(message, { debug }) {
22
25
  'A single space must be after ":" symbol.',
23
26
  typeof subject === 'string' && /^[a-z]/.test(subject.trim()) === false &&
24
27
  'The subject must start with a lower case latin alphabet.',
28
+ typeof subject === 'string' && /[\s\.]+$/.test(subject) && /\.{3}$/.test(subject.trim()) === false &&
29
+ 'The subject must not end with a period or a space.',
25
30
  ].filter((error) => typeof error === 'string');
26
31
  return {
27
32
  type,
28
33
  breaking: !!breaking,
29
- subject: typeof subject === 'string' ? subject.trim() : subject,
34
+ subject: typeof subject === 'string'
35
+ ? subject.trim().replace(/[\s\.]+$/, '') + (/\.{3}$/.test(subject.trim()) ? '...' : '')
36
+ : message,
30
37
  errors
31
38
  };
32
39
  }
package/lib/index.test.js CHANGED
@@ -68,3 +68,17 @@ it('returns the error, given the first word in non-lower-case for the subject',
68
68
  ]
69
69
  });
70
70
  });
71
+ it('returns the error, given a period after the subject', () => {
72
+ expect((0, index_1.checkConventionalMessage)('chore: xxx.', { debug })).toMatchObject({
73
+ subject: 'xxx',
74
+ errors: [
75
+ 'The subject must not end with a period or a space.'
76
+ ]
77
+ });
78
+ expect((0, index_1.checkConventionalMessage)('chore: xxx ...', { debug })).toEqual({
79
+ type: 'chore',
80
+ breaking: false,
81
+ subject: 'xxx...',
82
+ errors: []
83
+ });
84
+ });
@@ -26,28 +26,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  const fs = __importStar(require("fs/promises"));
27
27
  const fp = __importStar(require("path"));
28
28
  const execa_1 = require("execa");
29
+ const debug_1 = require("./debug");
29
30
  main();
31
+ const packageName = require('../package.json').name;
30
32
  async function main() {
31
33
  const currentDirectoryPath = process.cwd();
32
- console.debug('currentDirectoryPath »', currentDirectoryPath);
34
+ (0, debug_1.debug)('currentDirectoryPath »', currentDirectoryPath);
33
35
  const gitDirectoryPath = await findGitDirectoryPath(currentDirectoryPath);
34
- console.debug('gitDirectoryPath »', gitDirectoryPath);
36
+ (0, debug_1.debug)('gitDirectoryPath »', gitDirectoryPath);
35
37
  if (!gitDirectoryPath) {
36
38
  throw new Error('Could not find a Git directory.');
37
39
  }
38
40
  const packageJSON = JSON.parse(await fs.readFile(fp.join(gitDirectoryPath, 'package.json'), 'utf-8'));
39
- if (packageJSON.name === '@thisismanta/semantic-version') {
41
+ if (packageJSON.name === packageName) {
40
42
  console.warn('Skip installing Git hooks as it is supposed to be done on a consumer repository.');
41
43
  return;
42
44
  }
43
45
  console.log('Installing Husky');
44
46
  await (0, execa_1.execaCommand)('npx husky install', { cwd: gitDirectoryPath });
45
- // Up 3 levels because of `node_modules/@thisismanta/semantic-version`
46
- const huskyDirectoryPath = fp.resolve(currentDirectoryPath, '../../..', '.husky');
47
+ const huskyDirectoryPath = fp.resolve(gitDirectoryPath, '.husky');
48
+ (0, debug_1.debug)('huskyDirectoryPath »', huskyDirectoryPath);
47
49
  await fs.access(huskyDirectoryPath);
48
- console.log('Found', huskyDirectoryPath);
49
50
  await upsert(fp.join(huskyDirectoryPath, 'commit-msg'), 'npx lint-commit-message ${1}');
50
- await upsert(fp.join(huskyDirectoryPath, 'post-commit'), 'npx auto-npm-version');
51
51
  console.log('Done adding Git hooks.');
52
52
  }
53
53
  async function findGitDirectoryPath(path) {
@@ -71,11 +71,12 @@ async function findGitDirectoryPath(path) {
71
71
  async function upsert(filePath, text) {
72
72
  try {
73
73
  await fs.access(filePath);
74
+ console.log('Found', filePath);
74
75
  }
75
76
  catch (error) {
76
- console.log('Created', filePath);
77
77
  await fs.writeFile(filePath, '#!/usr/bin/env sh' + '\n' +
78
78
  '. "$(dirname -- "$0")/_/husky.sh"' + '\n', 'utf-8');
79
+ console.log('Created', filePath);
79
80
  }
80
81
  const fileText = await fs.readFile(filePath, 'utf-8');
81
82
  const lines = fileText.trim().split('\n');
@@ -25,12 +25,18 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  const fs = __importStar(require("fs/promises"));
27
27
  const index_1 = require("./index");
28
+ const debug_1 = require("./debug");
28
29
  main();
29
30
  async function main() {
30
31
  const [messageFilePath] = process.argv.slice(2);
32
+ (0, debug_1.debug)('messageFilePath »', messageFilePath);
31
33
  const message = (await fs.readFile(messageFilePath, 'utf-8')).trim();
32
- const { errors } = (0, index_1.checkConventionalMessage)(message, console);
34
+ (0, debug_1.debug)('message »', message);
35
+ const { errors } = (0, index_1.checkConventionalMessage)(message, { debug: debug_1.debug });
33
36
  if (errors.length > 0) {
34
- throw new Error(errors[0]);
37
+ for (const error of errors) {
38
+ console.error(error);
39
+ }
40
+ process.exit(1);
35
41
  }
36
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thisismanta/semantic-version",
3
- "version": "1.0.4",
3
+ "version": "2.0.0-0",
4
4
  "author": "Anantachai Saothong <thisismanta@gmail.com>",
5
5
  "license": "ISC",
6
6
  "engines": {
@@ -18,22 +18,25 @@
18
18
  ],
19
19
  "scripts": {
20
20
  "test": "jest",
21
- "build": "tsc",
22
- "preversion": "npm run test && npm run build && git add lib/*",
21
+ "build": "rm -rf lib && tsc",
22
+ "preversion": "npm run test && npm run build",
23
23
  "version": "npm publish --access public",
24
- "postversion": "git push --tags origin master",
24
+ "postversion": "git push --follow-tags origin master",
25
25
  "postinstall": "node ./lib/install-git-hooks.js"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/jest": "^29.4.0",
29
29
  "@types/node": "^16.0.0",
30
+ "@types/semver": "^7.3.13",
30
31
  "jest": "^29.4.3",
31
32
  "ts-jest": "^29.0.5",
32
33
  "typescript": "^4.9.5"
33
34
  },
34
35
  "dependencies": {
36
+ "@actions/github": "^5.1.1",
35
37
  "execa": "npm:@esm2cjs/execa@^6.1.1-cjs.1",
36
- "husky": "^8.0.3"
38
+ "husky": "^8.0.3",
39
+ "semver": "^7.3.8"
37
40
  },
38
41
  "jest": {
39
42
  "preset": "ts-jest",