@aircall/ci-scripts
Advanced tools
Comparing version 1.4.0 to 2.0.0
@@ -20,4 +20,8 @@ #!/usr/bin/env node | ||
createDraft, | ||
createFullRelease, | ||
createPreRelease, | ||
updateDraft, | ||
markDraftAsReleased | ||
markDraftAsReleased, | ||
markDraftAsPreReleased, | ||
markPreReleaseAsReleased | ||
} = require('./commands/releases.js'); | ||
@@ -27,2 +31,3 @@ const { generateChangelog } = require('./commands/changelog.js'); | ||
const { startBuild } = require('./commands/e2e-tests.js'); | ||
const { notifySlack } = require('./commands/notify-slack.js'); | ||
@@ -42,2 +47,5 @@ // Process arguments | ||
} | ||
if (usefullArguments.includes('--from-prerelease')) { | ||
options.fromPreRelease = true; | ||
} | ||
@@ -70,3 +78,3 @@ /** | ||
const toCommit = !!latestReleasedTag ? await getToCommit() : await getLatestCommit(); | ||
const toCommit = await getToCommit(); | ||
const toDate = commitDate(toCommit); | ||
@@ -84,4 +92,79 @@ | ||
/** | ||
* Exectutes the logic to generate a prerelease or a release | ||
* @param {boolean} isRelease Is is a prerelease or a relelase | ||
*/ | ||
async function handleReleaseFlow(isRelease = true) { | ||
const { CIRCLE_SHA1 } = checkEnvVariables(['CIRCLE_SHA1']); | ||
// changelog from last prelease/release | ||
const releaseData = await diffData(); | ||
const releaseChangelog = await generateChangelog(releaseData.filteredPRs); | ||
// latest release | ||
const latestRelease = await getLatestRelease(); | ||
// if it is a draft | ||
if (!!latestRelease && !!latestRelease.draft) { | ||
// we update draft as prerelease/release | ||
if (isRelease) { | ||
await markDraftAsReleased( | ||
latestRelease.id, | ||
releaseData.newTag, | ||
releaseData.toCommit.sha, | ||
releaseChangelog | ||
); | ||
} else { | ||
await markDraftAsPreReleased( | ||
latestRelease.id, | ||
releaseData.newTag, | ||
releaseData.toCommit.sha, | ||
releaseChangelog | ||
); | ||
} | ||
// if we are not in the latest commit, we create a new draft with the diff between current commit and latest | ||
const latestCommit = await getLatestCommit(); | ||
if (CIRCLE_SHA1 !== latestCommit.sha) { | ||
// info for the draft | ||
const toDate = commitDate(latestCommit); | ||
const fromDate = commitDate(await getCommit(CIRCLE_SHA1)); | ||
const pullRequests = await getMergedPullRequests(); | ||
const filteredPRs = filterPullRequests(pullRequests, fromDate, toDate); | ||
const newTag = nextVersion( | ||
releaseVersion({ tag: latestRelease.tag_name }), | ||
checkIfOnlyFixes(filteredPRs) | ||
); | ||
const draftChangelog = await generateChangelog(filteredPRs); | ||
// creating the draft | ||
console.log( | ||
`Creating new draft with changelog between the current release and the HEAD: ${newTag}` | ||
); | ||
await createDraft(newTag, latestCommit.sha, draftChangelog); | ||
} else { | ||
// nothing to do | ||
} | ||
} else { | ||
if (latestRelease.sha === CIRCLE_SHA1) { | ||
// latest Release is on same commit. aborting | ||
console.log(`Already on release on this commit. Aborting.`); | ||
return; | ||
} | ||
const newTag = options.fromCircleCITag ? process.env['CIRCLE_TAG'] : releaseData.newTag; | ||
console.log( | ||
`Creating a ${isRelease ? 'release' : 'prerelease'}, starting on last release up to commit ${ | ||
releaseData.toCommit.sha | ||
}${options.toLatestCommit ? ' (latest)' : ''}.` | ||
); | ||
// we create a new release with changelog | ||
if (isRelease) { | ||
await createFullRelease(newTag, releaseData.toCommit.sha, releaseChangelog); | ||
} else { | ||
await createPreRelease(newTag, releaseData.toCommit.sha, releaseChangelog); | ||
} | ||
} | ||
} | ||
async function main() { | ||
switch(options.action) { | ||
switch (options.action) { | ||
case 'next_version': | ||
@@ -103,36 +186,44 @@ const nextVersionData = await diffData(); | ||
// This step should be use to generate a Draft release | ||
// No tag will be assigned to it | ||
case 'prerelease': | ||
const prereleaseData = await diffData(); | ||
const prereleaseChangelog = await generateChangelog(prereleaseData.filteredPRs); | ||
const newTag = options.fromCircleCITag ? process.env['CIRCLE_TAG'] : prereleaseData.newTag; | ||
case 'draft': | ||
// changelog from last prelease/release | ||
const draftData = await diffData(); | ||
const draftChangelog = await generateChangelog(draftData.filteredPRs); | ||
// new tag for draft | ||
const newTag = options.fromCircleCITag ? process.env['CIRCLE_TAG'] : draftData.newTag; | ||
// latest release | ||
const latestRelease = await getLatestRelease(); | ||
// if it is a draft | ||
if (!!latestRelease && !!latestRelease.draft) { | ||
console.log( | ||
`Updating latest drafted release, starting on last release up to commit ${prereleaseData.toCommit.sha}${ | ||
options.toLatestCommit ? ' (latest)' : '' | ||
}.` | ||
`Updating latest drafted release, starting on last release up to commit ${ | ||
draftData.toCommit.sha | ||
}${options.toLatestCommit ? ' (latest)' : ''}.` | ||
); | ||
await updateDraft( | ||
latestRelease.id, | ||
newTag, | ||
prereleaseData.toCommit.sha, | ||
prereleaseChangelog | ||
); | ||
// we update existing draft with updated changelog | ||
await updateDraft(latestRelease.id, newTag, draftData.toCommit.sha, draftChangelog); | ||
} else { | ||
console.log( | ||
`Creating a drafted release, starting on last release up to commit ${prereleaseData.toCommit.sha}${ | ||
options.toLatestCommit ? ' (latest)' : '' | ||
}.` | ||
`Creating a drafted release, starting on last release up to commit ${ | ||
draftData.toCommit.sha | ||
}${options.toLatestCommit ? ' (latest)' : ''}.` | ||
); | ||
await createDraft(newTag, prereleaseData.toCommit.sha, prereleaseChangelog); | ||
// we create a new tag with changelog | ||
await createDraft(newTag, draftData.toCommit.sha, draftChangelog); | ||
} | ||
break; | ||
// This will mark the latestest draft as a release | ||
case 'prerelease': | ||
await handleReleaseFlow(false); | ||
break; | ||
case 'release': | ||
await markDraftAsReleased(); | ||
// with prerelease settings, we convert from last prerelease | ||
if (options.fromPreRelease) { | ||
await markPreReleaseAsReleased(); | ||
break; | ||
} | ||
// normal settings, we convert from draft or create a release | ||
await handleReleaseFlow(true); | ||
break; | ||
@@ -144,2 +235,10 @@ | ||
case 'notify-slack': | ||
const [env, channel] = usefullArguments.slice(1,3); | ||
if (!(env && channel)) { | ||
throw new Error('notify-slack command needs arguments <env> and <channel>'); | ||
} | ||
await notifySlack(env, channel); | ||
break; | ||
case 'e2e-tests': | ||
@@ -146,0 +245,0 @@ await startBuild(); |
const { checkEnvVariables } = require('../helpers.js'); | ||
const { JIRA_BASE_URL } = checkEnvVariables(['JIRA_BASE_URL']); | ||
const categories = { | ||
config: ['## Config:'], | ||
feat: ['## Features:'], | ||
fix: ['## Fixes:'], | ||
impr: ['## Improvements:'], | ||
others: ['## Others:'], | ||
revert: ['## Reverts:'], | ||
tech: ['## Tech:'] | ||
}; | ||
// Match Dependabot title pattern (ie, 'Bump gem_name from 0.1.0 to 0.2.0') | ||
@@ -50,2 +40,12 @@ const bumpTitle = title => { | ||
const categories = { | ||
config: ['## Config:'], | ||
feat: ['## Features:'], | ||
fix: ['## Fixes:'], | ||
impr: ['## Improvements:'], | ||
others: ['## Others:'], | ||
revert: ['## Reverts:'], | ||
tech: ['## Tech:'] | ||
}; | ||
console.log(`Generating changelog for ${pullRequests.length} PRs.`); | ||
@@ -52,0 +52,0 @@ |
@@ -1,22 +0,50 @@ | ||
const { createRelease, updateRelease, getLatestRelease } = require('../github.js'); | ||
const { | ||
createRelease, | ||
updateRelease, | ||
getLatestRelease, | ||
getLatestPreRelease | ||
} = require('../github.js'); | ||
// Release a Draft, and associate it with a tag | ||
const markDraftAsReleased = async () => { | ||
const latestRelease = await getLatestRelease(); | ||
if (!latestRelease) { | ||
console.error('No release exists on this repo yet.') | ||
// update a prerelease as release | ||
const markPreReleaseAsReleased = async () => { | ||
const latestPreRelease = await getLatestPreRelease(); | ||
if (!latestPreRelease) { | ||
console.log(`No prerelease found. Aborting.`); | ||
return; | ||
} | ||
console.log(`Updating Release ${latestRelease.tag_name}`); | ||
await updateRelease(latestRelease.id, { | ||
name: latestRelease.tag_name, | ||
tag_name: latestRelease.tag_name, | ||
draft: false | ||
console.log(`Updating Release ${latestPreRelease.tag_name}`); | ||
await updateRelease(latestPreRelease.id, { | ||
prerelease: false | ||
}); | ||
}; | ||
// Release a Draft, and associate it with a tag | ||
const markDraftAsReleased = async (releaseId, tag, targetCommitish, changelog) => { | ||
console.log(`Updating latest draft as Release`); | ||
await updateRelease(releaseId, { | ||
name: tag, | ||
tag_name: tag, | ||
draft: false, | ||
target_commitish: targetCommitish, | ||
body: changelog, | ||
prerelease: false | ||
}); | ||
}; | ||
// Release a Draft, and associate it with a tag | ||
const markDraftAsPreReleased = async (releaseId, tag, targetCommitish, changelog) => { | ||
console.log(`Updating latest draft as Prerelease`); | ||
await updateRelease(releaseId, { | ||
name: tag, | ||
tag_name: tag, | ||
draft: false, | ||
target_commitish: targetCommitish, | ||
body: changelog, | ||
prerelease: true | ||
}); | ||
}; | ||
// Create a Drafted release | ||
const createDraft = async (tag, targetCommitish, changelog) => { | ||
console.log(`Creating Draft ${tag}`); | ||
await createRelease({ | ||
@@ -42,6 +70,35 @@ name: `${tag}-staging`, | ||
// Create a release, no draft or prerelease | ||
const createFullRelease = async (tag, targetCommitish, changelog) => { | ||
console.log(`Creating Release ${tag}`); | ||
await createRelease({ | ||
name: `${tag}`, | ||
tag_name: tag, | ||
target_commitish: targetCommitish, | ||
body: changelog | ||
}); | ||
}; | ||
// Create a prerelease, no draft | ||
const createPreRelease = async (tag, targetCommitish, changelog) => { | ||
console.log(`Creating PreRelease ${tag}`); | ||
await createRelease({ | ||
name: `${tag}`, | ||
tag_name: tag, | ||
target_commitish: targetCommitish, | ||
body: changelog, | ||
prerelease: true | ||
}); | ||
}; | ||
module.exports = { | ||
createDraft, | ||
createFullRelease, | ||
createPreRelease, | ||
updateDraft, | ||
markDraftAsReleased | ||
markDraftAsReleased, | ||
markDraftAsPreReleased, | ||
markPreReleaseAsReleased | ||
}; |
@@ -23,2 +23,5 @@ const { get, patch, post } = require('./request.js'); | ||
// Used to Memoize Releases | ||
let gitReleases; | ||
const createRelease = async attributes => { | ||
@@ -56,8 +59,28 @@ await post(`${githubApiUrl}/releases`, { | ||
// Memoize releases | ||
const getReleases = async () => { | ||
if (!gitReleases) { | ||
const { data } = await get(`${githubApiUrl}/releases`, { headers: apiRequestHeaders }); | ||
gitReleases = data; | ||
} | ||
return gitReleases; | ||
} | ||
const getLatestRelease = async () => { | ||
const releases = await getReleases(); | ||
return releases[0]; | ||
}; | ||
const getPreviousRelease = async () => { | ||
const releases = await getReleases(); | ||
return releases[1]; | ||
}; | ||
const getLatestPreRelease = async () => { | ||
const { data } = await get(`${githubApiUrl}/releases`, { headers: apiRequestHeaders }); | ||
return data[0]; | ||
const latestPreRelease = data.find(r => !r.draft && r.prerelease); | ||
return latestPreRelease; | ||
}; | ||
const executeGraphQLRequest = async (query) => { | ||
const executeGraphQLRequest = async query => { | ||
const { data } = await post(githubGraphQLUrl, { | ||
@@ -105,3 +128,3 @@ body: { | ||
// Find the latest published tag | ||
const latestRelease = pullRequestsdata.data.repository.releases.edges.find((r) => !r.node.isDraft); | ||
const latestRelease = pullRequestsdata.data.repository.releases.edges.find(r => !r.node.isDraft); | ||
if (!latestRelease || !latestRelease.node) { | ||
@@ -113,5 +136,5 @@ return null; | ||
tag: latestRelease.node.tag.name, | ||
tagCommitDate: new Date(latestRelease.node.tag.target.authoredDate), | ||
tagCommitDate: new Date(latestRelease.node.tag.target.authoredDate) | ||
}; | ||
} | ||
}; | ||
@@ -154,4 +177,6 @@ const getMergedPullRequests = async () => { | ||
getLatestRelease, | ||
getLatestPreRelease, | ||
getLatestReleasedTag, | ||
getMergedPullRequests | ||
getMergedPullRequests, | ||
getPreviousRelease | ||
}; |
{ | ||
"name": "@aircall/ci-scripts", | ||
"version": "1.4.0", | ||
"version": "2.0.0", | ||
"dependencies": {}, | ||
@@ -5,0 +5,0 @@ "main": "ci-scripts.js", |
@@ -57,3 +57,3 @@ # CI Scripts | ||
name: Convert pre-release to release | ||
command: npx @aircall/ci-scripts release | ||
command: npx @aircall/ci-scripts release --from-prerelease | ||
``` | ||
@@ -91,10 +91,26 @@ | ||
### Create a GitHub pre-release | ||
### Create a GitHub draft release | ||
Create a pre-release with a generated changelog based on merged Pull Requests from the last release to the CircleCI Workflow current commit. | ||
Create a release draft with a generated changelog based on merged Pull Requests from the last release to the CircleCI Workflow current commit. | ||
```js | ||
npx ci-scripts draft | ||
``` | ||
### Create a GitHub prerelease or release | ||
If there is no draft: | ||
Create a prerelease/release with a generated changelog based on merged Pull Requests from the last release to the CircleCI Workflow current commit. | ||
If there is a draft: | ||
Convert the draft into a prerelease/release. If the draft is on a different target commit that the release, The draft will be converted with only the needed changelog for the prerelease/release, and another draft will be created with the changelog diff. | ||
```js | ||
npx ci-scripts prerelease | ||
``` | ||
```js | ||
npx ci-scripts release | ||
``` | ||
Same as above, but the changelog will be generated from the last release to the last merged Pull Request | ||
@@ -106,4 +122,8 @@ | ||
Create the prerelease on an existing tag, based on `CIRCLE_TAG` | ||
```js | ||
npx ci-scripts release --to-last-commit | ||
``` | ||
Create the prerelease/release on an existing tag, based on `CIRCLE_TAG` | ||
```js | ||
@@ -113,6 +133,10 @@ npx ci-scripts prerelease --from-circle-ci-tag | ||
```js | ||
npx ci-scripts release --from-circle-ci-tag | ||
``` | ||
### Convert a GitHub pre-release as a release | ||
```js | ||
npx ci-scripts release | ||
npx ci-scripts release --from-prerelease | ||
``` | ||
@@ -131,1 +155,28 @@ | ||
``` | ||
### Post a Slack Deployment Notification | ||
```yml | ||
jobs: | ||
slack-notify: | ||
executor: node | ||
parameters: | ||
env: | ||
type: string | ||
channel: | ||
type: string | ||
steps: | ||
- *restore-workspace | ||
- run: npx @aircall/ci-scripts notify-slack << parameters.env >> << parameters.channel >> | ||
workflows: | ||
version: 2 | ||
build-deploy: | ||
jobs: | ||
- checkout-install-dependencies | ||
- slack-notify: | ||
requires: [checkout-install-dependencies] | ||
filters: *branch-filter | ||
env: staging | ||
channel: aircall-xyz | ||
``` |
31950
12
840
178
3