diff --git a/.github/workflows/bring-it.yaml b/.github/workflows/bring-it.yaml index fc25b03..ef5fc91 100644 --- a/.github/workflows/bring-it.yaml +++ b/.github/workflows/bring-it.yaml @@ -36,7 +36,7 @@ jobs: uses: airkro/bring-it@actions with: npm-token: ${{ secrets.NPM_TOKEN }} - publish-command: npx @bring-it/npm@0.3.9 npm + publish-command: node packages/cli/dist/cli.mjs npm custom-command: | cd packages mkdir -p cli/dist diff --git a/package.json b/package.json index 4a3f9aa..045867a 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,12 @@ "@nice-move/prettier-config": "^0.10.0", "best-shot": "^0.5.0", "eslint": "^8.56.0", - "garou": "^0.6.20", + "garou": "^0.6.22", "prettier": "^3.1.1" }, + "peerDependencies": { + "@bring-it/utils": "workspace:*" + }, "engines": { "node": "^18.0.0 || ^20.0.0", "npm": ">=9.4.0" @@ -39,7 +42,7 @@ "packageManager": "pnpm@8.12.1", "pnpm": { "overrides": { - "cpu-features": "npm:garou@0.0.0" + "cpu-features": "npm:cheetor@0.0.0" } }, "eslintConfig": { diff --git a/packages/notify/lib/utils.mjs b/packages/notify/lib/utils.mjs index 93508b7..c02d1ef 100644 --- a/packages/notify/lib/utils.mjs +++ b/packages/notify/lib/utils.mjs @@ -1,3 +1,10 @@ +import { createRequire } from 'node:module'; + +import { sync } from 'conventional-commits-parser'; +import { clean } from 'fast-clean'; +import gitlog from 'gitlog'; +import groupBy from 'lodash/groupBy.js'; +import sortBy from 'lodash/sortBy.js'; import { toMarkdown } from 'mdast-util-to-markdown'; import semverPrerelease from 'semver/functions/prerelease.js'; @@ -10,6 +17,7 @@ const { DEPOT_NAME, GIT_COMMIT_SHORT, GIT_COMMIT, + GIT_PREVIOUS_COMMIT, GIT_HTTP_URL, JOB_ID, npm_package_version = '未知', @@ -17,6 +25,18 @@ const { PROJECT_WEB_URL, } = process.env; +function getVersion({ isLatest, version }) { + let name = npm_package_version; + + if (version !== true) { + try { + name = createRequire(import.meta.url)(`${version}/package.json`).version; + } catch {} + } + + return isLatest ? ['latest', name].join(' / ') : name; +} + export function dingtalk({ markdown, title, token }) { console.log({ title }); @@ -31,9 +51,14 @@ export function dingtalk({ markdown, title, token }) { markdown: { title, text: markdown - .trim() - // eslint-disable-next-line unicorn/prefer-string-replace-all - .replace(/\n\n/g, '\n\n
\n\n'), + .split('更新历史:') + .map((text, i) => + i === 0 + ? text.replaceAll('\n\n', '\n\n
\n\n') + : text.replaceAll('\n\n##', '\n\n
\n\n##'), + ) + .join('更新历史:') + .trim(), }, }, }); @@ -43,6 +68,126 @@ function isTest() { return BRANCH_NAME !== 'master' || semverPrerelease(npm_package_version); } +const configs = { + feat: { + label: '功能变化', + level: 0, + }, + fix: { + label: '功能修复', + level: 1, + }, + perf: { + label: '性能优化', + level: 2, + }, + refactor: { + label: '重构', + level: 3, + }, + revert: { + label: '回滚', + level: 4, + }, + chore: { + label: '杂项', + level: 5, + }, + style: { + label: '代码风格', + level: 6, + }, + test: { + label: '测试', + level: 7, + }, + build: { + label: '编译配置', + level: 8, + }, + ci: { + label: '自动化', + level: 9, + }, + docs: { + label: '补充文档', + level: 10, + }, +}; + +function getCommits() { + const io = + GIT_COMMIT === GIT_PREVIOUS_COMMIT + ? [] + : sortBy( + Object.entries( + groupBy( + gitlog + .default({ + repo: '.', + since: GIT_PREVIOUS_COMMIT, + until: GIT_COMMIT, + fields: ['hash', 'abbrevHash', 'subject'], + }) + .map(({ abbrevHash, hash, subject }) => ({ + hash, + abbrevHash, + message: sync(subject), + subject, + })), + ({ message }) => message.type, + ), + ), + ([type]) => configs[type].level, + ).flatMap(([type, list]) => [ + { + type: 'heading', + depth: 4, + children: [ + { + type: 'text', + value: configs[type]?.label || type, + }, + ], + }, + { + type: 'list', + spread: false, + children: list.map(({ hash, subject }) => ({ + type: 'listItem', + children: [ + { + type: 'link', + url: `${PROJECT_WEB_URL}/d/${DEPOT_NAME}/git/commit/${hash}`, + children: [ + { + type: 'text', + value: subject, + }, + ], + }, + ], + })), + }, + ]); + + return io.length > 0 + ? [ + { + type: 'heading', + depth: 3, + children: [ + { + type: 'text', + value: '更新历史:', + }, + ], + }, + ...io, + ] + : io; +} + export function createContent({ project = '未命名项目', type, @@ -50,10 +195,11 @@ export function createContent({ banner, isLatest = false, image, + version = true, }) { return toMarkdown({ type: 'root', - children: [ + children: clean([ banner ? { type: 'paragraph', @@ -67,7 +213,7 @@ export function createContent({ : undefined, { type: 'heading', - depth: 3, + depth: 2, children: [ { type: 'text', @@ -122,8 +268,13 @@ export function createContent({ type: 'listItem', children: [ { - type: 'text', - value: `发布类型:${type}`, + type: 'paragraph', + children: [ + { + type: 'text', + value: `发布类型:${type}`, + }, + ], }, ], } @@ -139,25 +290,23 @@ export function createContent({ ], } : undefined, - { - type: 'listItem', - children: [ - { - type: 'paragraph', + version + ? { + type: 'listItem', children: [ { - type: 'text', - value: `版本编号:${ - isLatest - ? ['latest', npm_package_version].join(' / ') - : npm_package_version - }`, + type: 'paragraph', + children: [ + { + type: 'text', + value: `版本编号:${getVersion({ isLatest, version })}`, + }, + ], }, ], - }, - ], - }, - ].filter(Boolean), + } + : undefined, + ], }, { type: 'paragraph', @@ -254,7 +403,13 @@ export function createContent({ children: [ { type: 'text', - value: GIT_COMMIT_SHORT, + value: + GIT_COMMIT === GIT_PREVIOUS_COMMIT + ? GIT_COMMIT_SHORT + : [ + GIT_PREVIOUS_COMMIT.slice(0, 7), + GIT_COMMIT_SHORT, + ].join('...'), }, ], }, @@ -289,8 +444,9 @@ export function createContent({ ], } : undefined, - ].filter(Boolean), + ], }, - ].filter(Boolean), + ...getCommits(), + ]), }); } diff --git a/packages/notify/package.json b/packages/notify/package.json index 5b74c71..4918c21 100644 --- a/packages/notify/package.json +++ b/packages/notify/package.json @@ -1,6 +1,6 @@ { "name": "@bring-it/notify", - "version": "0.2.0", + "version": "0.2.1", "description": "Send releases notifications", "license": "MIT", "author": { @@ -37,6 +37,10 @@ "prepublishOnly": "pnpm run build" }, "devDependencies": { + "conventional-commits-parser": "^5.0.0", + "fast-clean": "^1.3.2", + "gitlog": "^4.0.8", + "lodash": "^4.17.21", "mdast-util-to-markdown": "^2.1.0", "semver": "^7.5.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 054450a..ae4fc99 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,11 +5,15 @@ settings: excludeLinksFromLockfile: false overrides: - cpu-features: npm:garou@0.0.0 + cpu-features: npm:cheetor@0.0.0 importers: .: + dependencies: + '@bring-it/utils': + specifier: workspace:* + version: link:packages/utils devDependencies: '@nice-move/cli': specifier: ^0.11.3 @@ -27,8 +31,8 @@ importers: specifier: ^8.56.0 version: 8.56.0 garou: - specifier: ^0.6.20 - version: 0.6.20(eslint@8.56.0)(typescript@5.3.3) + specifier: ^0.6.22 + version: 0.6.22(eslint@8.56.0)(typescript@5.3.3) prettier: specifier: ^3.1.1 version: 3.1.1 @@ -52,6 +56,18 @@ importers: specifier: workspace:^ version: link:../cli devDependencies: + conventional-commits-parser: + specifier: ^5.0.0 + version: 5.0.0 + fast-clean: + specifier: ^1.3.2 + version: 1.3.2 + gitlog: + specifier: ^4.0.8 + version: 4.0.8 + lodash: + specifier: ^4.17.21 + version: 4.17.21 mdast-util-to-markdown: specifier: ^2.1.0 version: 2.1.0 @@ -933,6 +949,14 @@ packages: resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} dev: true + /JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + dev: true + /acorn-import-assertions@1.9.0(acorn@8.11.2): resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} peerDependencies: @@ -1208,6 +1232,12 @@ packages: resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} dev: true + /cheetor@0.0.0: + resolution: {integrity: sha512-dFpFG4lIUa6GISraG69KSFiSE+bzatlow4oG3VqIoMG6MQdl1O8MpxOe4MSS31rOpO+VhJdRAzMoV5wun0soHA==} + requiresBuild: true + dev: true + optional: true + /cheetor@0.13.0: resolution: {integrity: sha512-1QADgHQtO2oZksh/y6MK8yth40dpeWtlg0qSKMkvdpsWT2UHCT5uAw/yCDgi4vCFChMsmhgOlUsZ+O3kjKhDgg==} engines: {node: ^14.17.0 || >=16.13.0} @@ -1277,6 +1307,17 @@ packages: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} dev: true + /conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + dev: true + /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true @@ -1873,6 +1914,10 @@ packages: strip-final-newline: 3.0.0 dev: true + /fast-clean@1.3.2: + resolution: {integrity: sha512-MnlLEHYSfVtvbk9zAZS8DWyF9ZHE/0cvh+pI6I9WNN13k7Asi6OZHpPeuCl9kFiNk7WnZJKvnTsFe/lB+//OSA==} + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -1970,20 +2015,13 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /garou@0.0.0: - resolution: {integrity: sha512-jxk51p6LStuG4TuejYyNxlZCRT31KwIN5tcwDHtRWS43Pq99KeVoCVdyKkAK+/jUBlLVEjX18aXgdJyBJWhdVQ==} - deprecated: Use latest version - requiresBuild: true - dev: true - optional: true - - /garou@0.6.20(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-WDB47SrWQbBJoYDPfrMJ/bMfw0IFB+2sgHkwXkRhFzfcfGjEMLQi/LZSM3dV6Fs9BKte/1hlhGV5UYDQ07tHwA==} + /garou@0.6.22(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-p/hDC+7HjlAstuAAP5gWwojJyfDrsOPhEoXHqKq74nLu5mnppR9SoV1bDeODu8xbYtZAfR2AJnp47LYk6odUig==} engines: {node: '>=20.0.0 || ^16.15.0 || ^18.12.0'} hasBin: true peerDependencies: - eslint: ^8.55.0 - typescript: ^5.2.2 + eslint: ^8.56.0 + typescript: ^5.3.3 peerDependenciesMeta: typescript: optional: true @@ -2040,6 +2078,16 @@ packages: resolve-pkg-maps: 1.0.0 dev: true + /gitlog@4.0.8: + resolution: {integrity: sha512-FcTLP7Rc0H1vWXD+J/aj5JS1uiCEBblcYXlcacRAT73N26OMYFFzrBXYmDozmWlV2K7zwK5PrH16/nuRNhqSlQ==} + engines: {node: '>= 10.x'} + dependencies: + debug: 4.3.4 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2383,6 +2431,13 @@ packages: has-symbols: 1.0.3 dev: true + /is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + dependencies: + text-extensions: 2.4.0 + dev: true + /is-typed-array@1.1.12: resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} engines: {node: '>= 0.4'} @@ -2488,6 +2543,11 @@ packages: hasBin: true dev: true + /jsonparse@1.3.1: + resolution: {integrity: sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=} + engines: {'0': node >= 0.2.0} + dev: true + /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: @@ -2616,6 +2676,11 @@ packages: '@types/mdast': 4.0.3 dev: true + /meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + dev: true + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true @@ -3286,6 +3351,11 @@ packages: resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} dev: true + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: true + /ssh-config@4.4.1: resolution: {integrity: sha512-3VKB9wiwWbwVGjM8T5/nkIrijenIYhKXOHrcCH4cOlAX6d+hD4lMFJtJp3UF1KaUDIMtccg1MmwqoTCX7CoVzw==} dev: true @@ -3298,7 +3368,7 @@ packages: asn1: 0.2.6 bcrypt-pbkdf: 1.0.2 optionalDependencies: - cpu-features: /garou@0.0.0 + cpu-features: /cheetor@0.0.0 nan: 2.18.0 dev: true @@ -3437,10 +3507,19 @@ packages: source-map-support: 0.5.21 dev: true + /text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + dev: true + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /through@2.3.8: + resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=} + dev: true + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -3470,6 +3549,10 @@ packages: strip-bom: 3.0.0 dev: true + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true + /tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} dev: true