From 46085e6ffc26cbcdd720d6c335d8e9c13e3047ae Mon Sep 17 00:00:00 2001 From: Dmitriy Mozgovoy Date: Tue, 20 Dec 2022 20:45:24 +0200 Subject: [PATCH] chore(ci): fix release action; * chore(ci): Add release-it script; * fix(utils): redesigned logic for obtaining the global link;; * chore(git): updated .gitignore; chore(npm): updated .npmignore; * chore(git): fix husky prepare script; * chore(github): reworked npm release action step; * chore(ci): add CHANGELOG.md contributors section generator; * chore(deps): add `auto-changelog` to package.json; --- .github/workflows/release.yml | 6 ++- .gitignore | 2 +- .npmignore | 30 +++++------- bin/check-build-version.js | 13 ++++- bin/contributors.js | 89 +++++++++++++++++++++++++++++++++++ bin/injectContributorsList.js | 47 ++++++++++++++++++ lib/utils.js | 6 ++- package-lock.json | 88 +++++++++++++++++++++++++++++++++- package.json | 11 +++-- templates/contributors.hbs | 13 +++++ 10 files changed, 275 insertions(+), 30 deletions(-) create mode 100644 bin/contributors.js create mode 100644 bin/injectContributorsList.js create mode 100644 templates/contributors.hbs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4f1be09d3b..d761811e30 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,6 +50,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TYPE_ARG: ${{ fromJSON('{"auto":"", "patch":"--patch", "minor":"--minor", "major":"--major"}')[github.event.inputs.type] }} BETA_ARG: ${{ github.event.inputs.beta == 'true' && '--preRelease=beta' || '' }} - NPM_ARG: ${{ github.event.inputs.npm == 'false' && '--no-npm' || '' }} DRY_ARG: ${{ github.event.inputs.dry == 'true' && '--dry-run' || '' }} - run: npm run release -- --ci --verbose $NPM_ARG $TYPE_ARG $BETA_ARG $DRY_ARG + run: npm run release -- --ci --verbose $TYPE_ARG $BETA_ARG $DRY_ARG + - name: npm-release + if: ${{ github.event.inputs.dry == 'false' && github.event.inputs.npm == 'true' }} + run: npm publish diff --git a/.gitignore b/.gitignore index 8d9c5a919e..2bfebdaa4a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,6 @@ test/typescript/axios.js* sauce_connect.log test/module/**/package-lock.json backup/ -./.husky/ +/.husky/ .npmrc .env diff --git a/.npmignore b/.npmignore index f94390d8c9..49010d7e38 100644 --- a/.npmignore +++ b/.npmignore @@ -1,19 +1,11 @@ -**/.* -*.iml -coverage/ -examples/ -node_modules/ -typings/ -sandbox/ -test/ -bower.json -CODE_OF_CONDUCT.md -COLLABORATOR_GUIDE.md -CONTRIBUTING.md -COOKBOOK.md -ECOSYSTEM.md -Gruntfile.js -karma.conf.js -webpack.*.js -sauce_connect.log -backup/ +**/* +!/dist/** +!/lib/** +!index.js +!index.d.ts +!index.d.cts +!CHANGELOG.md +!LICENSE +!MIGRATION_GUIDE.md +!README.md +!SECURITY.md diff --git a/bin/check-build-version.js b/bin/check-build-version.js index fd00d37ec7..e1d5b607d5 100644 --- a/bin/check-build-version.js +++ b/bin/check-build-version.js @@ -12,8 +12,17 @@ console.log(`Axios version: v${axios.VERSION}`); console.log(`Axios build version: v${axiosBuild.VERSION}`); console.log(`----------------------------`); -assert.strictEqual(version, axios.VERSION, `Version mismatch between package and Axios`); -assert.strictEqual(version, axiosBuild.VERSION, `Version mismatch between package and build`); +assert.strictEqual( + version, + axios.VERSION, + `Version mismatch between package and Axios ${version} != ${axios.VERSION}` +); + +assert.strictEqual( + version, + axiosBuild.VERSION, + `Version mismatch between package and build ${version} != ${axiosBuild.VERSION}` +); console.log('✔️ PASSED\n'); diff --git a/bin/contributors.js b/bin/contributors.js new file mode 100644 index 0000000000..3b7650c6f9 --- /dev/null +++ b/bin/contributors.js @@ -0,0 +1,89 @@ +import axios from "../index.js"; +import util from "util"; +import cp from "child_process"; +import Handlebars from "handlebars"; +import fs from "fs/promises"; + +const exec = util.promisify(cp.exec); + +const removeExtraLineBreaks = (str) => str.replace(/(?:\r\n|\r|\n){3,}/gm, '\r\n\r\n'); + +const cleanTemplate = template => template + .replace(/\n +/g, '\n') + .replace(/^ +/, '') + .replace(/\n\n\n+/g, '\n\n') + .replace(/\n\n$/, '\n'); + +const getUserInfo = ((userCache) => async (email) => { + if (userCache[email] !== undefined) { + return userCache[email]; + } + try { + const {data: {items: [user]}} = await axios.get(`https://api.github.com/search/users?q=${email}`); + + return (userCache[email] = user ? { + ...user, + avatar_url_sm: user.avatar_url + '&s=16' + } : null); + } catch (err) { + console.warn(err); + return {}; + } +})({}); + + +const getReleaseInfo = async (version, useGithub) => { + version = 'v' + version.replace(/^v/, ''); + + const releases = JSON.parse((await exec( + `npx auto-changelog ${ + version ? '--starting-version ' + version + ' --ending-version ' + version: '' + } --stdout --commit-limit false --template json`)).stdout + ); + + for(const release of releases) { + const authors = {}; + + const commits = [ + ...release.commits, + ...release.fixes.map(fix => fix.commit), + ...release.merges.map(fix => fix.commit) + ].filter(Boolean); + + for(const {author, email, insertions, deletions} of commits) { + const user = Object.assign({ + name: author, + email + }, useGithub ? await getUserInfo(email) : null); + + const entry = authors[author] = (authors[author] || { + insertions: 0, deletions: 0, ...user + }); + + entry.github = entry.login ? `https://github.com/${encodeURIComponent(entry.login)}` : ''; + + entry.insertions += insertions; + entry.deletions += deletions; + entry.points = entry.insertions + entry.deletions; + } + + release.authors = Object.values(authors).sort((a, b) => b.points - a.points); + release.allCommits = commits; + } + + return releases; +} + +const renderContributorsList = async (version, useGithub = false, template) => { + const release = (await getReleaseInfo(version, useGithub))[0]; + + const compile = Handlebars.compile(String(await fs.readFile(template))) + + const content = compile(release); + + return removeExtraLineBreaks(cleanTemplate(content)); +} + +export { + renderContributorsList +} diff --git a/bin/injectContributorsList.js b/bin/injectContributorsList.js new file mode 100644 index 0000000000..182c0aeb18 --- /dev/null +++ b/bin/injectContributorsList.js @@ -0,0 +1,47 @@ +import fs from 'fs/promises'; +import path from 'path'; +import {renderContributorsList} from './contributors.js'; +import asyncReplace from 'string-replace-async'; +import {fileURLToPath} from "url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const injectContributors = async (infile, injector) => { + console.log(`Checking contributors sections in ${infile}`); + + infile = path.resolve(__dirname, infile); + + const content = String(await fs.readFile(infile)); + const headerRE = /^##\s+\[([-_\d.\w]+)]\s+-.+/mig; + const contributorsRE = /^\s*### Contributors/mi; + + let tag; + let index = 0; + + const newContent = await asyncReplace(content, headerRE, async (match, nextTag, offset) => { + const releaseContent = content.slice(index, offset); + + const hasContributorsSection = contributorsRE.test(releaseContent); + + const currentTag = tag; + + tag = nextTag; + index = offset + match.length; + + if(currentTag && !hasContributorsSection) { + console.log(`Adding contributors for ${currentTag}`); + + return (await injector(currentTag)) + match; + } + + return match; + }); + + await fs.writeFile(infile, newContent); +} + + +await injectContributors( + '../CHANGELOG.md', + (tag) => renderContributorsList(tag, true, path.resolve(__dirname, '../templates/contributors.hbs') + )); diff --git a/lib/utils.js b/lib/utils.js index 2267723967..68d791102b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -277,7 +277,11 @@ function findKey(obj, key) { return null; } -const _global = typeof self === "undefined" ? typeof global === "undefined" ? this : global : self; +const _global = (() => { + /*eslint no-undef:0*/ + if (typeof globalThis !== "undefined") return globalThis; + return typeof self !== "undefined" ? self : (typeof window !== 'undefined' ? window : global) +})(); const isContextDefined = (context) => !isUndefined(context) && context !== _global; diff --git a/package-lock.json b/package-lock.json index 3df717e406..222092dcd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@rollup/plugin-multi-entry": "^4.0.0", "@rollup/plugin-node-resolve": "^9.0.0", "abortcontroller-polyfill": "^1.7.3", + "auto-changelog": "^2.4.0", "body-parser": "^1.20.0", "coveralls": "^3.1.1", "cross-env": "^7.0.3", @@ -37,6 +38,7 @@ "fs-extra": "^10.1.0", "get-stream": "^3.0.0", "gulp": "^4.0.2", + "handlebars": "^4.7.7", "husky": "^8.0.2", "istanbul-instrumenter-loader": "^3.0.1", "jasmine-core": "^2.4.1", @@ -60,10 +62,10 @@ "rollup-plugin-terser": "^7.0.2", "sinon": "^4.5.0", "stream-throttle": "^0.1.3", + "string-replace-async": "^3.0.2", "terser-webpack-plugin": "^4.2.3", "typescript": "^4.8.4", - "url-search-params": "^0.10.0", - "yargs": "^17.6.2" + "url-search-params": "^0.10.0" } }, "node_modules/@ampproject/remapping": { @@ -4557,6 +4559,34 @@ "node": ">= 4.5.0" } }, + "node_modules/auto-changelog": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.4.0.tgz", + "integrity": "sha512-vh17hko1c0ItsEcw6m7qPRf3m45u+XK5QyCrrBFViElZ8jnKrPC1roSznrd1fIB/0vR/zawdECCRJtTuqIXaJw==", + "dev": true, + "dependencies": { + "commander": "^7.2.0", + "handlebars": "^4.7.7", + "node-fetch": "^2.6.1", + "parse-github-url": "^1.0.2", + "semver": "^7.3.5" + }, + "bin": { + "auto-changelog": "src/index.js" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/auto-changelog/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -17433,6 +17463,18 @@ "node": ">=0.8" } }, + "node_modules/parse-github-url": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", + "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", + "dev": true, + "bin": { + "parse-github-url": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -20914,6 +20956,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-replace-async": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/string-replace-async/-/string-replace-async-3.0.2.tgz", + "integrity": "sha512-s6hDtXJ7FKyRap/amefqrOMpkEQvxUDueyvJygQeHxCK5Za90dOMgdibCCrPdfdAYAkr8imrZ1PPXW7DOf0RzQ==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -27386,6 +27437,27 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "auto-changelog": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.4.0.tgz", + "integrity": "sha512-vh17hko1c0ItsEcw6m7qPRf3m45u+XK5QyCrrBFViElZ8jnKrPC1roSznrd1fIB/0vR/zawdECCRJtTuqIXaJw==", + "dev": true, + "requires": { + "commander": "^7.2.0", + "handlebars": "^4.7.7", + "node-fetch": "^2.6.1", + "parse-github-url": "^1.0.2", + "semver": "^7.3.5" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -37676,6 +37748,12 @@ "path-root": "^0.1.1" } }, + "parse-github-url": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", + "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", + "dev": true + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -40436,6 +40514,12 @@ "safe-buffer": "~5.2.0" } }, + "string-replace-async": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/string-replace-async/-/string-replace-async-3.0.2.tgz", + "integrity": "sha512-s6hDtXJ7FKyRap/amefqrOMpkEQvxUDueyvJygQeHxCK5Za90dOMgdibCCrPdfdAYAkr8imrZ1PPXW7DOf0RzQ==", + "dev": true + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", diff --git a/package.json b/package.json index 7e0d5f9252..aeb1d89d14 100644 --- a/package.json +++ b/package.json @@ -41,12 +41,13 @@ "coveralls": "cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", "fix": "eslint --fix lib/**/*.js", "prepare": "husky install && npm run prepare:hooks", - "prepare:hooks": "npx husky add .husky/commit-msg \"npx commitlint --edit $1\"", + "prepare:hooks": "npx husky set .husky/commit-msg \"npx commitlint --edit $1\"", "release:dry": "release-it --dry-run --no-npm", "release:info": "release-it --release-version", "release:beta:no-npm": "release-it --preRelease=beta --no-npm", "release:beta": "release-it --preRelease=beta", "release:no-npm": "release-it --no-npm", + "release:changelog:fix": "node ./bin/injectContributorsList.js", "release": "release-it" }, "repository": { @@ -78,6 +79,7 @@ "@rollup/plugin-multi-entry": "^4.0.0", "@rollup/plugin-node-resolve": "^9.0.0", "abortcontroller-polyfill": "^1.7.3", + "auto-changelog": "^2.4.0", "body-parser": "^1.20.0", "coveralls": "^3.1.1", "cross-env": "^7.0.3", @@ -90,6 +92,7 @@ "fs-extra": "^10.1.0", "get-stream": "^3.0.0", "gulp": "^4.0.2", + "handlebars": "^4.7.7", "husky": "^8.0.2", "istanbul-instrumenter-loader": "^3.0.1", "jasmine-core": "^2.4.1", @@ -113,6 +116,7 @@ "rollup-plugin-terser": "^7.0.2", "sinon": "^4.5.0", "stream-throttle": "^0.1.3", + "string-replace-async": "^3.0.2", "terser-webpack-plugin": "^4.2.3", "typescript": "^4.8.4", "url-search-params": "^0.10.0" @@ -160,7 +164,7 @@ "release": true }, "npm": { - "publish": true, + "publish": false, "ignoreVersion": false }, "plugins": { @@ -171,7 +175,8 @@ }, "hooks": { "before:init": "npm test", - "after:bump": "gulp version --bump ${version} && npm run build && npm run test:build:version", + "after:bump": "gulp version --bump ${version} && npm run build && npm run test:build:version && git add ./dist", + "before:release": "npm run release:changelog:fix && git add CHANGELOG.md", "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}." } }, diff --git a/templates/contributors.hbs b/templates/contributors.hbs new file mode 100644 index 0000000000..c31fb6e8aa --- /dev/null +++ b/templates/contributors.hbs @@ -0,0 +1,13 @@ +{{#if authors}} + + ### Contributors 🤵 + + {{#each authors}} + {{#if github}} + - {{#if avatar_url}}![avatar]({{avatar_url_sm}}){{/if}} [{{name}}]({{github}}) ({{insertions}}++ / {{deletions}}--) + {{else}} + - {{name}} ({{insertions}}++ / {{deletions}}--) + {{/if}} + {{/each}} + +{{/if}}