Automated the release process

no issue
This commit is contained in:
Daniel Lockyer 2020-03-31 18:25:02 +01:00
parent 634b5e83a2
commit 02bf8773b9
4 changed files with 197 additions and 2 deletions

26
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Release
on:
push:
tags:
- '*'
jobs:
automate:
runs-on: ubuntu-latest
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
FORCE_COLOR: 1
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
submodules: true
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- uses: actions/setup-node@v1
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: yarn
- run: grunt automated-release
#- run: npm publish
# env:
# NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -17,6 +17,8 @@ const KnexMigrator = require('knex-migrator');
const knexMigrator = new KnexMigrator({
knexMigratorFilePath: config.get('paths:appRoot')
});
const releaseUtils = require('@tryghost/release-utils');
const semver = require('semver');
const path = require('path');
const escapeChar = process.platform.match(/^win/) ? '^' : '\\';
@ -32,6 +34,10 @@ const logBuildingClient = function (grunt) {
}
};
const ORGNAME = 'TryGhost';
const githubRepoPathGhost = `https://github.com/${ORGNAME}/Ghost`;
let gistUrl, githubUploadURL;
// ## Grunt configuration
const configureGrunt = function (grunt) {
// #### Load all grunt tasks
@ -579,6 +585,143 @@ const configureGrunt = function (grunt) {
['shell:master', 'subgrunt:init']
);
grunt.registerTask('changelog', 'Generate changelog since version (:<version>)', function (version) {
let ghostPackageInfo = grunt.file.readJSON(path.join(process.cwd(), 'package.json'));
const done = this.async();
releaseUtils
.releases
.get({
userAgent: 'ghost-release',
uri: `https://api.github.com/repos/${ORGNAME}/Ghost/releases`
})
.then((response) => {
const sameMajorReleaseTags = [], otherReleaseTags = [];
response.forEach((release) => {
let lastVersion = release.tag_name || release.name;
// only compare to versions smaller than the new one
if (semver.gt(ghostPackageInfo.version, lastVersion)) {
// check if the majors are the same
if (semver.major(lastVersion) === semver.major(ghostPackageInfo.version)) {
sameMajorReleaseTags.push(lastVersion);
} else {
otherReleaseTags.push(lastVersion);
}
}
});
return (sameMajorReleaseTags.length !== 0) ? sameMajorReleaseTags[0] : otherReleaseTags[0];
})
.then((previousVersion) => {
let versionToUse = version || previousVersion;
const changelog = new releaseUtils.Changelog({
changelogPath: path.join(process.cwd(), '.', 'changelog.md'),
folder: process.cwd()
});
changelog
.write({
githubRepoPath: githubRepoPathGhost,
lastVersion: versionToUse
})
.write({
githubRepoPath: `https://github.com/${ORGNAME}/Ghost-Admin`,
lastVersion: versionToUse,
append: true,
folder: path.join(process.cwd(), 'core', 'client')
})
.sort()
.clean();
grunt.log.writeln('changelog.md generated'.cyan);
done();
})
.catch(done);
});
grunt.registerTask('gist', 'Generate a gist with the changelog', function () {
let ghostPackageInfo = grunt.file.readJSON(path.join(process.cwd(), 'package.json'));
const done = this.async();
releaseUtils
.gist
.create({
userAgent: 'ghost-release',
gistName: 'changelog-' + ghostPackageInfo.version + '.md',
gistDescription: 'Changelog ' + ghostPackageInfo.version,
changelogPath: path.join(process.cwd(), 'changelog.md'),
github: {
token: process.env.RELEASE_TOKEN
},
isPublic: true
}).then((response) => {
gistUrl = response.gistUrl;
grunt.log.writeln(`Gist generated: ${gistUrl}`.cyan);
done();
}).catch(done);
});
grunt.registerTask('draftRelease', 'Publish a draft release on GitHub', function () {
let ghostPackageInfo = grunt.file.readJSON(path.join(process.cwd(), 'package.json'));
let changelogPaths = [{changelogPath: path.join(process.cwd(), 'changelog.md')}];
const done = this.async();
/*if (hasCasperChanged) {
changelogPaths.push({
changelogPath: path.join(process.cwd(), releaseDir, casperDir, 'changelog.md'),
content: [`\n\nCasper (the default theme) has been upgraded to ${casperNewVersion}:`]
});
}*/
releaseUtils
.releases
.create({
draft: true,
preRelease: false,
tagName: ghostPackageInfo.version,
releaseName: ghostPackageInfo.version,
userAgent: 'ghost-release',
uri: `https://api.github.com/repos/${ORGNAME}/Ghost/releases`,
github: {
token: process.env.RELEASE_TOKEN
},
changelogPath: changelogPaths,
gistUrl: gistUrl
})
.then((response) => {
githubUploadURL = response.uploadUrl;
grunt.log.writeln(`Release draft generated: ${response.releaseUrl}`.cyan);
done();
}).catch(done);
});
grunt.registerTask('uploadGitHub', 'Upload Ghost .zip to GitHub', function () {
const done = this.async();
const ghostPackageInfo = grunt.file.readJSON(path.join(process.cwd(), 'package.json'));
const zipName = `Ghost-${ghostPackageInfo.version}.zip`;
releaseUtils
.releases
.uploadZip({
github: {
token: process.env.RELEASE_TOKEN
},
zipPath: path.join(process.cwd(), '.dist', 'release', zipName),
uri: `${githubUploadURL.substring(0, githubUploadURL.indexOf('{'))}?name=${zipName}`,
userAgent: 'ghost-release'
})
.then(done)
.catch(done);
});
grunt.registerTask('automated-release', function () {
grunt.option('skip-tests', true);
grunt.task.run(['release', 'changelog', 'gist', 'draftRelease', 'uploadGitHub']);
});
// ### Release
// Run `grunt release` to create a Ghost release zip file.
// Uses the files specified by `.npmignore` to know what should and should not be included.

View File

@ -135,6 +135,7 @@
},
"devDependencies": {
"@lodder/grunt-postcss": "^2.0.1",
"@tryghost/release-utils": "0.5.0",
"cssnano": "^4.1.10",
"eslint": "6.8.0",
"eslint-plugin-ghost": "1.0.1",

View File

@ -436,6 +436,18 @@
chalk "^2.4.1"
sywac "^1.2.1"
"@tryghost/release-utils@0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@tryghost/release-utils/-/release-utils-0.5.0.tgz#39ec5d0095bc26f7aba7fa461047a393a196f3f8"
integrity sha512-HJoXlaTDFfem6ivnHMRhdpdXat9DLl4gQ22/OwseZ0ItrTDb4BNb0wes/hCafktfACPeqB5IOT0tmy6rAystgA==
dependencies:
bluebird "^3.5.3"
emoji-regex "^8.0.0"
execa "^1.0.0"
lodash "^4.17.11"
request "^2.88.0"
request-promise "^4.2.4"
"@tryghost/social-urls@0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@tryghost/social-urls/-/social-urls-0.1.7.tgz#a62b008c16e2e1f6d7519a9f36f3b2966be2bad8"
@ -2022,7 +2034,7 @@ cross-spawn@^5.0.1:
shebang-command "^1.2.0"
which "^1.2.9"
cross-spawn@^6.0.5:
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
@ -2941,6 +2953,19 @@ execa@^0.8.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
execa@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
dependencies:
cross-spawn "^6.0.0"
get-stream "^4.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
exit@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@ -3515,7 +3540,7 @@ get-stream@^3.0.0:
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
get-stream@^4.1.0:
get-stream@^4.0.0, get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==