Skip to content

Commit 3d1c260

Browse files
yeager-erennikaaru
authored andcommitted
chore(ci): improve release production flow
1 parent 5517c14 commit 3d1c260

14 files changed

Lines changed: 182 additions & 129 deletions

File tree

.github/PUBLISH_TEMPLATE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Checklist
2+
3+
TBA

.github/workflows/publish.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ on:
33
push:
44
branches:
55
- main
6-
tags:
7-
- '!*'
86
pull_request:
97
types:
108
- closed
@@ -43,3 +41,11 @@ jobs:
4341
REF: ${{ github.ref }}
4442
BASE_REF: ${{ github.event.pull_request.base.ref }}
4543
GH_TOKEN: ${{ github.token }}
44+
45+
- name: Creating PR on next
46+
if: ${{ github.ref == 'refs/heads/main' }}
47+
run: yarn run post-release-prod
48+
env:
49+
REF: ${{ github.ref }}
50+
BASE_REF: ${{ github.event.pull_request.base.ref }}
51+
GH_TOKEN: ${{ github.token }}

CONTRIBUTING.md

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -29,52 +29,6 @@ Here is the structure:
2929
`/widget/playground`: This directory offers a playground environment where you can test and obtain configurations for our widget.
3030
`/widget/iframe`: This directory contains a JavaScript class that simplifies the process of adding our iframe-based widget to dApps.
3131

32-
33-
## Release workflow
34-
35-
A release can be a lib or an app/client release. We are publishing our libs to `npm` and deploying our apps (client) on `vercel`.
36-
37-
If a package is app, you need to add the package name to `scripts/deploy/config.mjs` and then after getting a `PROJECT_ID` from Vercel, you need to set it as enviroment variable as well.
38-
39-
There are main commands:
40-
41-
`yarn run publish` for publishing our NPM packages.
42-
`yarn run deploy` to deploy apps on Vercel.
43-
44-
### Publish
45-
46-
#### Prerelase
47-
48-
Our publish script will do these steps:
49-
50-
1. Get the last release (by using git tags) and calculate changes since then.
51-
2. Bump the version for changed packages.
52-
3. Create changelog, git tags and github release, and publish them to NPM.
53-
4. Make a publish commit and push the updated packages (version) and tags to origin.
54-
55-
Note:
56-
Libs will be published under `next` tag on npm, which means you need to use `yarn add @rango/test-package@next` to install the published version whenever you need.
57-
58-
#### Production relase
59-
60-
Release should be triggered manually and then it will automatically published. You only need to run this command on you local machine to release the production:
61-
62-
`yarn run release-prod`
63-
64-
After release (Green pipleline), make sure you will merge `main` into `next` as well.
65-
66-
`yarn run post-release-prod`
67-
68-
### Deploy
69-
70-
You should manually trigger the `deploy` workflow.
71-
72-
By running `yarn run deploy`, it will build all the apps/clients then will try to deploy them on vercel.
73-
74-
If the workflow is running on `next` branch, it will be deployed as Vercel's `preview`. If not, it's production release.
75-
76-
All the apps published by `prerelase` workflow will be published under the Vercel's `preview` enviroment.
77-
7832
## Translation
7933

8034
First we need to extract the message from our source code using `yarn i18n:extract` and then we should run `yarn i18n:compile` to make a wrapper arround the translation file `.po` to be used inside our app.
@@ -115,7 +69,7 @@ Additionally, there is the option to manually trigger the crowdin workflow if ne
11569

11670
### How we handle urls in embedded?
11771

118-
`embedded` is importing in different enviroments (iframe, import as react component and a separate `app`). When it's being used as a react component we let the dApp to use it inside its router and they can load the widget in a separate route (not root `/`, e.g. `/swaps`). In this context we can not use absolute paths because we don't know the basename set by user, so we need to always use relative paths. Relative paths has been defined in [URL spec](https://url.spec.whatwg.org/#urls) so we can be sure it's a long term solution and will work in future or by changing our router librart (e.g. `react-router`).
72+
`embedded` is importing in different environments (iframe, import as react component and a separate `app`). When it's being used as a react component we let the dApp to use it inside its router and they can load the widget in a separate route (not root `/`, e.g. `/swaps`). In this context we can not use absolute paths because we don't know the basename set by user, so we need to always use relative paths. Relative paths has been defined in [URL spec](https://url.spec.whatwg.org/#urls) so we can be sure it's a long term solution and will work in future or by changing our router librart (e.g. `react-router`).
11973

12074
### HMR
12175

docs/fork.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Fork
2+
3+
## How you can test release flow?
4+
5+
You need to add your [Github Token](https://github.com/settings/tokens) and set it as `PAT` in your fork's secrets.
6+
7+
for NPM:
8+
9+
- Create an NPM account and an organization. (I created @yeager-dev for example)
10+
- Get a token from NPM and set it as `NPM_TOKEN` in our repo.
11+
- Enable Github Actions on your fork.
12+
13+
_Note 1_: If you are a Rango developer, you can ask NPM token for `@yeager-dev` org.
14+
15+
For Crowdin:
16+
17+
After creating a Crowdin project, it has an ID (you can find it in right sidebar) and for accessing to API you will [need a token](https://crowdin.com/settings#api-key) as well.
18+
Then, you need to set `CROWDIN_PERSONAL_TOKEN` and `CROWDIN_PROJECT_ID` in your secrets.

docs/readme.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Docs
2+
3+
Here, we are documenting some of our internal processes.

docs/release.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Release
2+
3+
## How we are releasing a new version?
4+
5+
A release can be a lib or an app/client release. We are publishing our libs to `npm` and deploying our apps (client) on `vercel`.
6+
7+
If a package is app, you need to add the package name to `scripts/deploy/config.mjs` and then after getting a `PROJECT_ID` from Vercel, you need to set it as enviroment variable as well.
8+
9+
There are main commands:
10+
11+
`yarn run publish` for publishing our NPM packages.
12+
`yarn run deploy` to deploy apps on Vercel.
13+
14+
### Publish flow
15+
16+
#### Prerelase
17+
18+
Our publish script will do these steps:
19+
20+
1. Get the last release (by using git tags) and calculate changes since then.
21+
2. Bump the version for changed packages.
22+
3. Create changelog, git tags and github release, and publish them to NPM.
23+
4. Make a publish commit and push the updated packages (version) and tags to origin.
24+
25+
Note:
26+
Libs will be published under `next` tag on npm, which means you need to use `yarn add @rango/test-package@next` to install the published version whenever you need.
27+
28+
#### Production relase
29+
30+
Release should be triggered manually and then it will automatically published. You only need to run this command on you local machine to release the production:
31+
32+
`yarn run release-prod`
33+
34+
### Deploy flow
35+
36+
You should manually trigger the `deploy` workflow.
37+
38+
By running `yarn run deploy`, it will build all the apps/clients then will try to deploy them on vercel.
39+
40+
If the workflow is running on `next` branch, it will be deployed as Vercel's `preview`. If not, it's production release.
41+
42+
All the apps published by `prerelase` workflow will be published under the Vercel's `preview` enviroment.
43+
44+
## How you can release a new version?
45+
46+
### Next (Staging)
47+
48+
A publish only will be triggered when a **Pull Request** has been merged. If you try to commit directly into the `next` branch it wouldn't be triggered.
49+
50+
First it tries to extracting translations (if any) and push them onto Crowdin, then releasing libraries will be started.
51+
_Note 1_: Syncing translations (first workflow) is an optional step which means if it fails we will do the publish anyway.
52+
53+
### Production
54+
55+
For releasing production, you need to run `yarn release-prod` it will checkout to `next` branch and pull the latest changes then it tries to merge the `next` into `main`.
56+
_Note 1_: Make sure you are having permission for `push` on `main`.
57+
58+
In production, we don't run localization workflow (crowdin) since we assume our `/translation` folder is in sync with Crowdin. if you think there is new translation, you can run `crowdin` workflow manually and then try to release.
59+
60+
At the end, a PR will be created to merge `main` into `next` after publishing the libraries. You need to check the PR description and make sure you are considering/doing them.

scripts/common/constants.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
/** A fixed subject for using in publish commit. */
22
export const PUBLISH_COMMIT_SUBJECT = 'chore(release): publish';
3-
export const NPM_ORG_NAME = '@rango-dev';

scripts/common/errors.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ export class GithubCreateReleaseFailedError extends Error {
1919
}
2020
}
2121

22+
export class GithubCommandError extends Error {
23+
name = 'GithubCommandError';
24+
constructor(msg) {
25+
super(msg);
26+
}
27+
}
28+
2229
export class NpmPackageNotFoundError extends Error {
2330
name = 'NpmPackageNotFoundError';
2431
constructor(packageName) {

scripts/common/git.mjs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -147,21 +147,26 @@ export async function getChangedPackagesFor(channel) {
147147
*
148148
*/
149149
export async function publishCommitAndTags(pkgs) {
150-
const isTaggingSkipped = detectChannel() !== 'prod';
150+
const channel = detectChannel();
151+
const isTaggingSkipped = channel !== 'prod';
151152
const subject = `${PUBLISH_COMMIT_SUBJECT}\n\n`;
152153
const tags = pkgs.map(generateTagName);
153154

154155
const list = tags.map((tag) => `- ${tag}`).join('\n');
155156
const message = subject + list;
157+
let body = `Affected packages: ${tags.join(',')}`;
158+
159+
/*
160+
When we are pushing a publish commit into main, it triggers a redundant workflow run,
161+
To avoid this, by adding a [skip ci] the workflow run will be skipped.
162+
We don't need it on `next` since the next workflow is running on `pullrequest.closed` event.
163+
*/
164+
if (channel === 'prod') {
165+
body += '\n[skip ci]';
166+
}
156167

157168
// Making a publish commit
158-
await execa('git', [
159-
'commit',
160-
'-m',
161-
message,
162-
'-m',
163-
`Affected packages: ${tags.join(',')}`,
164-
]).catch((error) => {
169+
await execa('git', ['commit', '-m', message, '-m', body]).catch((error) => {
165170
throw new GitError(`git commit failed. \n ${error.stderr}`);
166171
});
167172

@@ -194,14 +199,23 @@ export async function addFileToStage(path) {
194199
});
195200
}
196201

197-
export async function push(remote = 'origin') {
198-
const output = await execa('git', [
199-
'push',
200-
remote,
201-
'--follow-tags',
202-
'--no-verify',
203-
'--atomic',
204-
])
202+
export async function push(options) {
203+
const { setupRemote, branch, remote = 'origin' } = options || {};
204+
205+
let pushOptions = [];
206+
if (setupRemote) {
207+
if (!branch) {
208+
throw new CustomScriptError(
209+
`You should also pass branch name as parameter to push. \n ${error.stderr}`
210+
);
211+
}
212+
213+
pushOptions = ['--set-upstream', remote, branch];
214+
} else {
215+
pushOptions = [remote, '--follow-tags', '--no-verify', '--atomic'];
216+
}
217+
218+
const output = await execa('git', ['push', ...pushOptions])
205219
.then(({ stdout }) => stdout)
206220
.catch((error) => {
207221
throw new GitError(`git push failed. \n ${error.stderr}`);

scripts/common/github.mjs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { execa } from 'execa';
22
import { generateChangelog } from './changelog.mjs';
33
import {
4+
GithubCommandError,
45
GithubCreateReleaseFailedError,
56
GithubGetReleaseError,
67
GithubReleaseNotFoundError,
@@ -82,6 +83,48 @@ export async function githubReleaseFor(pkg) {
8283
}
8384
}
8485

86+
/**
87+
*
88+
* @param {PullRequestInfo} pr
89+
*
90+
* @typedef {Object} PullRequestInfo
91+
* @property {string} title PR title
92+
* @property {string} branch your current branch
93+
* @property {string} baseBranch PR will be merge into base branch.
94+
* @property {string} templatePath template path for PR
95+
*
96+
*/
97+
export async function createPullRequest(pr) {
98+
const { title, baseBranch, branch, templatePath } = pr;
99+
100+
if (!title || !baseBranch || !branch || !templatePath) {
101+
throw new GithubCommandError(
102+
'Creating pull request can not be proceed without required parameters. \n',
103+
JSON.stringify({ title, baseBranch, branch, templatePath })
104+
);
105+
}
106+
107+
const ghCreateParams = [
108+
'--title',
109+
title,
110+
'--base',
111+
baseBranch,
112+
'--head',
113+
branch,
114+
'--body-file',
115+
templatePath,
116+
];
117+
const output = await execa('gh', ['pr', 'create', ...ghCreateParams])
118+
.then(({ stdout }) => stdout)
119+
.catch((err) => {
120+
throw new GithubCommandError(
121+
`gh pr command failed. \n ${err.stdout || err} \n`
122+
);
123+
});
124+
125+
return output;
126+
}
127+
85128
export function checkEnvironments() {
86129
const envs = {
87130
NPM_TOKEN: !!process.env.NPM_TOKEN,

0 commit comments

Comments
 (0)