Skip to content

Commit

Permalink
ci: automate package release
Browse files Browse the repository at this point in the history
Closes FEPLT-1675

chore: bump lerna to v7

chore: add post-changelog script

chore: replace SLACK_API_POST_RELEASE_MESSAGE env var with variable

docs: add publish releases section to contributing

ci: fix lerna.json nesting
  • Loading branch information
mvidalgarcia authored and mainframev committed Sep 15, 2023
1 parent f234800 commit 65adbd9
Show file tree
Hide file tree
Showing 11 changed files with 1,173 additions and 1,704 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
GH_TOKEN=
GATSBY_GOOGLE_CLIENT_ID=
FIGMA_TOKEN=
NPM_TOKEN=
SLACK_TOKEN=
SLACK_API_POST_RELEASE_MESSAGE=
SLACK_API_READ_PLZ_ORBIT=
GATSBY_ORBIT_STORAGE_PATH=
ORBIT_STORAGE_PATH=
7 changes: 7 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- [Props naming convention](#props-naming-convention)
- [Component design](#component-design)
- [Commits](#commits)
- [Publish releases](#publish-releases)
- [Versioning](#versioning)

---
Expand Down Expand Up @@ -212,6 +213,12 @@ Check [this section](contribution/component-design.md) for a complete descriptio

Please read our [guidelines for authoring commits](contribution/commits.md).

## Publish releases

We use Lerna to publish new versions of Orbit packages. To publish a new version we use a GitHub Action workflow which is triggered **manually** from the GitHub UI. The workflow is called `Publish` and it's located in the `Actions` tab of the repository, see [here](https://github.com/kiwicom/orbit/actions/workflows/publish.yml). To trigger the workflow, click on the `Run workflow` button, leave the `master` branch selected and click on the `Run workflow` button again. The workflow will then run and publish the new versions of the packages to NPM as well as create GitHub releases for each package.

If you want to have a preview of the changes that Lerna will make, you can select the `Dry run` checkbox. This will run the workflow in a dry run mode and it will not publish anything. Instead, it will print out the versions that will be published and the diffs of the changelogs. This is useful if you want to check what will be published before actually publishing it.

## Versioning

We release orbit-components under Semantic Versioning 2.0. [See here](https://semver.org/).
47 changes: 44 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,51 @@
name: Publish

on: workflow_dispatch
on:
workflow_dispatch:
inputs:
dryrun:
description: "Dry run"
required: false
default: false
type: boolean

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write # create commits and releases
steps:
- name: Publish dummy job
run: echo "Hello publish dummy job!"
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0 # fetch tags

- name: Node
uses: ./.github/actions/node

- name: Setup git config
run: |
git config user.name "GitHub Actions Bot"
git config user.email "<>"
- name: Dry run
if: ${{ github.event.inputs.dryrun == 'true' }}
run: |
yarn lerna version --no-private --no-push --no-git-tag-version --yes
git diff
yarn zx scripts/post-changelog.mjs --dry
- name: Publish
if: ${{ github.event.inputs.dryrun == 'false' }}
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # must be of type Automation to create releases
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "scope=@kiwicom" > ~/.npmrc
echo "access=public" >> ~/.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
echo "//registry.npmjs.org/:always-auth=true" >> ~/.npmrc
yarn lerna publish --no-private --conventional-commits --create-release github --yes
yarn docs changelog
git add docs/src/data/log.md && git commit -m "docs: update changelog" && git push
yarn zx scripts/post-changelog.mjs
2 changes: 0 additions & 2 deletions .npmrc

This file was deleted.

14 changes: 8 additions & 6 deletions lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
"$schema": "http://json.schemastore.org/lerna",
"version": "independent",
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/*",
"docs"
],
"packages": ["packages/*", "docs"],
"command": {
"version": {
"message": "chore: publish"
"allowBranch": "master",
"message": "chore: publish",
"conventionalCommits": true
},
"publish": {
"allowBranch": "master",
"conventionalCommits": true
}
}
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,12 @@
"fast-glob": "^3.2.12",
"filedirname": "^2.7.0",
"flow-bin": "^0.187.1",
"gitdiff-parser": "^0.3.1",
"husky": "^6.0.0",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"jest-matchmedia-mock": "^1.1.0",
"lerna": "5.2.0",
"lerna": "7.2.0",
"lint-staged": "^12.3.2",
"lodash": "^4.17.21",
"markdown-magic": "^2.6.0",
Expand All @@ -132,6 +133,7 @@
"remark-preset-lint-recommended": "^5.0.0",
"remark-preset-prettier": "^0.4.0",
"remark-validate-links": "^10.0.2",
"simple-git": "^3.19.1",
"size-limit": "^8.0.0",
"slackify-markdown": "^4.3.1",
"styled-components": "^5.3.1",
Expand Down
2 changes: 0 additions & 2 deletions packages/eslint-plugin-orbit-components/.npmrc

This file was deleted.

2 changes: 0 additions & 2 deletions packages/orbit-tracking/.npmrc

This file was deleted.

161 changes: 161 additions & 0 deletions scripts/post-changelog.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { $, fetch, argv } from "zx";
import dotenv from "dotenv-safe";
import conventionalChangelog from "conventional-changelog";
import { getPackages } from "@lerna/project";
import slackify from "slackify-markdown";
import { simpleGit } from "simple-git";
import gitDiffParser from "gitdiff-parser";

/* eslint-disable no-console */

const CHANNEL = `orbit-react`;
const COLOR_CORE = `#00A58E`;
const PACKAGES = ["orbit-components", "orbit-tailwind-preset"];
const PACKAGE_PREFIX = "@kiwicom";
const SLACK_API_POST_RELEASE_MESSAGE = "https://slack.com/api/chat.postMessage";

function getTitle(pkg) {
return `New ${pkg} release 🚀`;
}

const apiRequest = async ({ method = "GET", url, body }) =>
fetch(url, {
method,
body,
headers: {
"Content-Type": "application/json; charset=utf-8",
Authorization: `Bearer ${process.env.SLACK_TOKEN}`,
Accept: "application/json",
},
});

function adjustChangelog(str) {
const output = str
.replace("Bug Fixes", "Bug Fixes 🐛")
.replace("Features", "Features 🆕")
.replace("BREAKING CHANGES", "BREAKING CHANGES 🚨")
.replace("Reverts", "Reverts 🔄");

return output;
}

function getDiff(tag, path) {
return simpleGit().show([tag, path]);
}

function changelogPath(package_) {
return `packages/${package_}/CHANGELOG.md`;
}

async function postSlackNotification(changelog, package_) {
try {
$.verbose = false;
const res = await apiRequest({
url: SLACK_API_POST_RELEASE_MESSAGE,
method: "POST",
body: JSON.stringify({
channel: CHANNEL,
attachments: [
{
title: getTitle(package_),
text: changelog,
color: COLOR_CORE,
},
],
}),
});
return res;
} catch (err) {
console.log("Error posting to Slack");
console.error(err);
}

return undefined;
}

async function configureSlackToken() {
try {
dotenv.config({
allowEmptyValues: true,
example: ".env.example",
});
} catch (err) {
if (/SLACK_TOKEN/g.test(err.message)) {
throw new Error(
"Slack token is missing in the .env file, please add it.\nLearn how to create one: https://slack.com/intl/en-cz/help/articles/215770388-Create-and-regenerate-API-tokens",
);
}
}
}

async function getChangelogMessage(package_) {
const packages = await getPackages();

return new Promise((resolve, reject) => {
let changelog = "";
const pkg = packages.find(p => p.name === package_);

const stream = conventionalChangelog(
{
lernaPackage: pkg.name,
preset: "angular",
},
{
host: "https://github.com",
title: package_,
owner: "kiwicom",
repository: "orbit",
linkCompare: true,
version: pkg.version,
},
{ path: pkg.location },
);

stream.on("data", data => {
changelog += data.toString();
});

stream.on("end", () => {
changelog = slackify(adjustChangelog(changelog));
resolve(changelog);
});

stream.on("error", err => {
reject(err);
});
});
}

async function publishChangelog(package_) {
try {
const changelog = await getChangelogMessage(`${PACKAGE_PREFIX}/${package_}`);

await simpleGit().fetch(["origin", "master", "--tags"]);
const tags = await simpleGit().tags();
const diff = await getDiff(tags.latest ?? "", changelogPath(package_));
const files = gitDiffParser.parse(diff);
if (files.length === 0) {
console.log(`No changes in ${package_}`);
return;
}

if (argv.dry) {
console.info(changelog);
} else {
await configureSlackToken();
await postSlackNotification(changelog, package_);
}
} catch (err) {
console.error(err);
process.exit(1);
}
}

(async () => {
await Promise.all(
PACKAGES.map(async package_ => {
await publishChangelog(package_);
}),
);
process.exit(0);
})();
3 changes: 2 additions & 1 deletion scripts/publish.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const TITLE = `New orbit release 🚀`;
const CHANNEL = `orbit-react`;
const COLOR_CORE = `#00A58E`;
const COLOR_PING = `#0172CB`;
const SLACK_API_POST_RELEASE_MESSAGE = "https://slack.com/api/chat.postMessage";

const octokit = new Octokit({
auth: process.env.GH_TOKEN,
Expand Down Expand Up @@ -67,7 +68,7 @@ async function postSlackNotification(changelog, names) {
try {
$.verbose = false;
const res = await apiRequest({
url: process.env.SLACK_API_POST_RELEASE_MESSAGE,
url: SLACK_API_POST_RELEASE_MESSAGE,
method: "POST",
body: JSON.stringify({
channel: CHANNEL,
Expand Down
Loading

0 comments on commit 65adbd9

Please sign in to comment.