From 3a874db088f445d03e71f08f9125ac5ce9cbc4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Beye?= Date: Thu, 26 May 2022 13:04:00 +0200 Subject: [PATCH] feat(updater): Add changelog to nightly update provider --- .github/workflows/nightly_build.yml | 6 +- .../GithubValetudoNightlyUpdateProvider.js | 43 ++++-- package-lock.json | 134 ++++++++++++++++++ package.json | 5 +- util/build_release_manifest.js | 10 ++ util/res/auto_changelog_handlebars_helpers.js | 41 ++++++ util/res/auto_changelog_template.hbs | 26 ++++ 7 files changed, 251 insertions(+), 14 deletions(-) create mode 100644 util/res/auto_changelog_handlebars_helpers.js create mode 100644 util/res/auto_changelog_template.hbs diff --git a/.github/workflows/nightly_build.yml b/.github/workflows/nightly_build.yml index 5dbf3719d2..371dbe0894 100644 --- a/.github/workflows/nightly_build.yml +++ b/.github/workflows/nightly_build.yml @@ -40,9 +40,13 @@ 'name': 'UPX-compress valetudo binaries', 'run': 'npm run upx', }, + { + 'name': 'Generate changelog', + 'run': 'npm run generate_nightly_changelog', + }, { 'name': 'Build manifest', - 'run': 'npm run build_release_manifest', + 'run': 'npm run build_release_manifest nightly', }, { 'name': 'Push binaries to nightly repo', diff --git a/backend/lib/updater/update_provider/GithubValetudoNightlyUpdateProvider.js b/backend/lib/updater/update_provider/GithubValetudoNightlyUpdateProvider.js index 1c22fbb268..54a16b9f9f 100644 --- a/backend/lib/updater/update_provider/GithubValetudoNightlyUpdateProvider.js +++ b/backend/lib/updater/update_provider/GithubValetudoNightlyUpdateProvider.js @@ -21,11 +21,24 @@ class GithubValetudoNightlyUpdateProvider extends ValetudoUpdateProvider { throw new Error("GithubValetudoNightlyUpdateProvider: Received invalid branch response"); } + let changelog = rawBranchResponse.data.commit.commit.message; + let manifest; + + try { + manifest = await this.fetchManifest(); + + if (typeof manifest?.changelog === "string") { + changelog = manifest.changelog; + } + } catch (e) { + // intentional + } + return [ new ValetudoRelease({ version: rawBranchResponse.data.commit.sha, releaseTimestamp: new Date(rawBranchResponse.data.commit.commit.committer.date), - changelog: rawBranchResponse.data.commit.commit.message, + changelog: changelog, }) ]; } @@ -35,18 +48,10 @@ class GithubValetudoNightlyUpdateProvider extends ValetudoUpdateProvider { * @return {Promise>} */ async fetchBinariesForRelease(release) { - let releaseBinaries = []; - let rawManifestResponse = await axios.get(`${GithubValetudoNightlyUpdateProvider.ASSET_BASE_URL}${GithubValetudoNightlyUpdateProvider.MANIFEST_NAME}`); - - // @ts-ignore - if (!rawManifestResponse.data) { - throw new Error(`GithubValetudoNightlyUpdateProvider: Invalid ${GithubValetudoNightlyUpdateProvider.MANIFEST_NAME}`); - } - - const manifest = rawManifestResponse.data; + const manifest = await this.fetchManifest(); // @ts-ignore - releaseBinaries = Object.keys(manifest.sha256sums).map(name => { + return Object.keys(manifest.sha256sums).map(name => { return new ValetudoReleaseBinary({ name: name, // @ts-ignore @@ -54,8 +59,22 @@ class GithubValetudoNightlyUpdateProvider extends ValetudoUpdateProvider { downloadUrl: `${GithubValetudoNightlyUpdateProvider.ASSET_BASE_URL}${GithubValetudoNightlyUpdateProvider.BINARY_NAMES[name]}` }); }); + } + + + /** + * @private + * @return {Promise} + */ + async fetchManifest() { + let rawManifestResponse = await axios.get(`${GithubValetudoNightlyUpdateProvider.ASSET_BASE_URL}${GithubValetudoNightlyUpdateProvider.MANIFEST_NAME}`); + + // @ts-ignore + if (!rawManifestResponse.data) { + throw new Error(`GithubValetudoNightlyUpdateProvider: Invalid ${GithubValetudoNightlyUpdateProvider.MANIFEST_NAME}`); + } - return releaseBinaries; + return rawManifestResponse.data; } } diff --git a/package-lock.json b/package-lock.json index dda7f58286..5f8ea10a46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@typescript-eslint/eslint-plugin": "5.25.0", "@typescript-eslint/experimental-utils": "5.25.0", "@typescript-eslint/parser": "5.25.0", + "auto-changelog": "2.4.0", "eslint": "8.16.0", "eslint-plugin-jsdoc": "39.3.0", "eslint-plugin-node": "11.1.0", @@ -5257,6 +5258,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/autoprefixer": { "version": "10.4.7", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", @@ -9497,6 +9526,27 @@ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -14025,6 +14075,18 @@ "node": ">=6" } }, + "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": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -18426,6 +18488,19 @@ "node": ">=4.2.0" } }, + "node_modules/uglify-js": { + "version": "3.15.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.5.tgz", + "integrity": "sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -19319,6 +19394,12 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, "node_modules/workbox-background-sync": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.3.tgz", @@ -23379,6 +23460,27 @@ "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==" }, + "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 + } + } + }, "autoprefixer": { "version": "10.4.7", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", @@ -26488,6 +26590,19 @@ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, "hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -29737,6 +29852,12 @@ "callsites": "^3.0.0" } }, + "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": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -32890,6 +33011,13 @@ "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", "dev": true }, + "uglify-js": { + "version": "3.15.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.5.tgz", + "integrity": "sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==", + "dev": true, + "optional": true + }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -33604,6 +33732,12 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, "workbox-background-sync": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.3.tgz", diff --git a/package.json b/package.json index ae6ceff40f..45ed80adcc 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,15 @@ "upx": "node ./util/upx_compress_valetudo.js", "build_openapi_schema": "node util/build_openapi_schema.mjs", "build_release_manifest": "node util/build_release_manifest.js", - "check_dependencies_for_update": "npx check-outdated --ignore-pre-releases --ignore-packages react-router-dom" + "check_dependencies_for_update": "npx check-outdated --ignore-pre-releases --ignore-packages react-router-dom", + "generate_changelog": "npx auto-changelog --commit-limit false --tag-pattern .+ --handlebars-setup ./util/res/auto_changelog_handlebars_helpers.js --template ./util/res/auto_changelog_template.hbs --output ./build/changelog.md", + "generate_nightly_changelog": "npx auto-changelog --commit-limit false --tag-pattern .+ --unreleased-only --handlebars-setup ./util/res/auto_changelog_handlebars_helpers.js --template ./util/res/auto_changelog_template.hbs --output ./build/changelog_nightly.md" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "5.25.0", "@typescript-eslint/experimental-utils": "5.25.0", "@typescript-eslint/parser": "5.25.0", + "auto-changelog": "2.4.0", "eslint": "8.16.0", "eslint-plugin-jsdoc": "39.3.0", "eslint-plugin-node": "11.1.0", diff --git a/util/build_release_manifest.js b/util/build_release_manifest.js index 1cc2a03cf4..51ef0ee108 100644 --- a/util/build_release_manifest.js +++ b/util/build_release_manifest.js @@ -38,4 +38,14 @@ Object.values(binaries).forEach((path, i) => { } }) +if (process.argv.length > 2 && process.argv[2] === "nightly") { + manifest.version = "nightly"; + + try { + manifest.changelog = fs.readFileSync("./build/changelog_nightly.md").toString(); + } catch(e) { + //intentional + } +} + fs.writeFileSync("./build/valetudo_release_manifest.json", JSON.stringify(manifest, null, 2)) diff --git a/util/res/auto_changelog_handlebars_helpers.js b/util/res/auto_changelog_handlebars_helpers.js new file mode 100644 index 0000000000..a94c76f462 --- /dev/null +++ b/util/res/auto_changelog_handlebars_helpers.js @@ -0,0 +1,41 @@ +function mergeAndSortCategories(merges, fixes, commits) { + const allCommits = [ + ...merges.map(merge => merge.commit), + ...fixes.map(merge => merge.commit), + ...commits + ]; + + allCommits.forEach(commit => { + commit.breaking = /^[A-Za-z0-9.]+!(?:\(.*\))?:/.test(commit.subject); + }) + + return allCommits.sort((a, b) => { + return new Date(b.date) - new Date(a.date); + }); +} + +module.exports = function (Handlebars) { + Handlebars.registerHelper('get-all-non-breaking-commits', function (merges, fixes, commits) { + return mergeAndSortCategories(merges, fixes, commits).filter(c => c.breaking === false); + }) + + Handlebars.registerHelper('get-all-breaking-commits', function (merges, fixes, commits) { + return mergeAndSortCategories(merges, fixes, commits).filter(c => c.breaking === true); + }) + + Handlebars.registerHelper("render-ccm", function(subject) { + const match = /^(?[A-Za-z0-9.]+)(?!)?(?:\((?[A-Za-z0-9.]+)\))?: (?.*)$/.exec(subject); + + if (typeof match?.groups?.type === "string" && typeof match?.groups?.message === "string") { + let output = match.groups.message; + + if (typeof match.groups.scope === "string") { + output = `**${match.groups.scope}**: ${output}`; + } + + return output; + } else { + return subject; + } + }) +} \ No newline at end of file diff --git a/util/res/auto_changelog_template.hbs b/util/res/auto_changelog_template.hbs new file mode 100644 index 0000000000..7ed57e7c4e --- /dev/null +++ b/util/res/auto_changelog_template.hbs @@ -0,0 +1,26 @@ +{{#with releases.[0]}} + {{#if tag}} + ## Valetudo {{tag}} ({{date}}) + {{else}} + ## Valetudo nightly ({{date}}) + {{/if}} + {{#commit-list (get-all-breaking-commits merges fixes commits) heading='### Breaking Changes'}} + - {{render-ccm subject}} [`{{shorthash}}`]({{href}}) + {{/commit-list}} + + {{#commit-list (get-all-non-breaking-commits merges fixes commits) heading='### Features' message='^feat!?(?:\(.*\))?:'}} + - {{render-ccm subject}} [`{{shorthash}}`]({{href}}) + {{/commit-list}} + + {{#commit-list (get-all-non-breaking-commits merges fixes commits) heading='### Fixes' message='^fix!?(?:\(.*\))?:'}} + - {{render-ccm subject}} [`{{shorthash}}`]({{href}}) + {{/commit-list}} + + {{#commit-list (get-all-non-breaking-commits merges fixes commits) heading='### Refactoring' message='^refactor!?(?:\(.*\))?:'}} + - {{render-ccm subject}} [`{{shorthash}}`]({{href}}) + {{/commit-list}} + + {{#commit-list (get-all-non-breaking-commits merges fixes commits) heading='### Chores' message='^chore!?(?:\(.*\))?:'}} + - {{render-ccm subject}} [`{{shorthash}}`]({{href}}) + {{/commit-list}} +{{/with}} \ No newline at end of file