chore(ci): optimize (#5403)

* chore(ci): optimize

* fix: bug

* fix: bug

* chore: update

* fix: url
This commit is contained in:
YANG QIA 2024-10-13 11:30:54 +08:00 committed by GitHub
parent cd7467feb3
commit 8f726979a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 163 additions and 113 deletions

View File

@ -1,4 +1,4 @@
name: auto-merge
name: Auto merge main -> next
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -13,17 +13,29 @@ jobs:
push-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: nocobase,pro-plugins,${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ',') }},${{ join(fromJSON(vars.CUSTOM_PRO_PLUGIN_REPOS), ',') }}
skip-token-revoke: true
- name: Get GitHub App User ID
id: get-user-id
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
- name: Checkout
uses: actions/checkout@v4
with:
repository: nocobase/nocobase
ssh-key: ${{ secrets.NOCOBASE_DEPLOY_KEY }}
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
fetch-depth: 0
- name: main -> next(nocobase)
run: |
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions Bot"
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
git checkout main
git pull origin main
git checkout next

View File

@ -1,4 +1,4 @@
name: Build Docker Image
name: Build docker image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@ -1,4 +1,4 @@
name: Build Pro Image
name: Build pro image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@ -70,7 +70,7 @@ jobs:
- name: Run script
shell: bash
run: |
node scripts/release/changelogAndRelease.js --ver ${{ inputs.version }}
node scripts/release/changelogAndRelease.js --ver ${{ inputs.version }} --cmsURL ${{ secrets.CMS_URL }} --cmsToken ${{ secrets.CMS_TOKEN }}
env:
PRO_PLUGIN_REPOS: ${{ vars.PRO_PLUGIN_REPOS }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}

View File

@ -1,4 +1,4 @@
name: deploy client docs
name: Deploy client docs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -50,5 +50,5 @@ jobs:
username: ${{ secrets.CN_CLIENT_USERNAME }}
key: ${{ secrets.CN_CLIENT_KEY }}
port: ${{ secrets.CN_CLIENT_PORT }}
source: "packages/core/client/dist/*"
source: 'packages/core/client/dist/*'
target: ${{ secrets.CN_CLIENT_TARGET }}/${{ steps.set-tags.outputs.tags }}

View File

@ -1,39 +0,0 @@
name: Get nocobase app github token
on:
workflow_call:
outputs:
token:
value: ${{ jobs.get-app-token.outputs.token }}
user-id:
value: ${{ jobs.get-app-token.outputs.user-id }}
app-slug:
value: ${{ jobs.get-app-token.outputs.app-slug }}
jobs:
get-app-token:
runs-on: ubuntu-latest
outputs:
token: ${{ steps.encrypt-token.outputs.token }}
app-slug: ${{ steps.app-token.outputs.app-slug }}
user-id: ${{ steps.get-user-id.outputs.user-id }}
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.NOCOBASE_APP_ID }}
private-key: ${{ secrets.NOCOBASE_APP_PRIVATE_KEY }}
repositories: nocobase,pro-plugins,${{ join(fromJSON(vars.PRO_PLUGIN_REPOS), ',') }}
skip-token-revoke: true
- name: Encrypt token
id: encrypt-token
shell: bash
run: |
APP_TOKEN=${{ steps.app-token.outputs.token }};
ENCRYPTED_SECRET=$(echo -n "$APP_TOKEN" | openssl enc -aes-256-cbc -pbkdf2 -salt -k "${{ secrets.APP_TOKEN_ENCRYPTION_PASSWORD }}" | base64 -w 0);
echo "token=$ENCRYPTED_SECRET" >> $GITHUB_OUTPUT
- name: Get GitHub App User ID
id: get-user-id
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}

View File

@ -1,4 +1,4 @@
name: manual-build-pr-docker-image
name: Manual build pr docker image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@ -1,4 +1,4 @@
name: manual-build-pro-image
name: Manual build pro image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@ -1,4 +1,4 @@
name: Build Pro Plugin Docker Image
name: Build pro plugin docker image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@ -1,4 +1,4 @@
name: manual-e2e
name: Manual e2e
on:
workflow_dispatch:

View File

@ -1,4 +1,4 @@
name: manual-release
name: Manual release
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@ -1,4 +1,4 @@
name: NocoBase Backend Test
name: NocoBase backend test
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@ -1,4 +1,4 @@
name: NocoBase FrontEnd Test
name: NocoBase frontEnd test
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@ -1,4 +1,4 @@
name: Release Next
name: Release next
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -148,7 +148,7 @@ jobs:
shell: bash
run: |
git fetch
node scripts/release/changelogAndRelease.js --ver alpha
node scripts/release/changelogAndRelease.js --ver alpha --cmsURL ${{ secrets.CMS_URL }} --cmsToken ${{ secrets.CMS_TOKEN }}
env:
PRO_PLUGIN_REPOS: ${{ vars.NEXT_PRO_PLUGIN_REPOS }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}

View File

@ -3,8 +3,15 @@ const fs = require('fs/promises');
const path = require('path');
const { Command } = require('commander');
const program = new Command();
const axios = require('axios');
program.option('-f, --from [from]').option('-t, --to [to]').option('-v, --ver [ver]', '', 'beta').option('--test');
program
.option('-f, --from [from]')
.option('-t, --to [to]')
.option('-v, --ver [ver]', '', 'beta')
.option('--test')
.option('--cmsURL [url]')
.option('--cmsToken [token]');
program.parse(process.argv);
const header = {
@ -99,7 +106,9 @@ async function parsePR(number, pkgType, cwd, pkg, retries = 10) {
// gh pr view 5112 --json author,body,files
let res;
try {
const { stdout } = await execa('gh', ['pr', 'view', number, '--json', 'author,body,files,baseRefName'], { cwd });
const { stdout } = await execa('gh', ['pr', 'view', number, '--json', 'author,body,files,baseRefName,url'], {
cwd,
});
res = stdout;
} catch (error) {
console.error(`Get PR #${number} failed, error: ${error.message}`);
@ -110,7 +119,7 @@ async function parsePR(number, pkgType, cwd, pkg, retries = 10) {
}
return { number };
}
const { author, body, files, baseRefName } = JSON.parse(res);
const { author, body, files, baseRefName, url } = JSON.parse(res);
if (ver === 'alpha' && baseRefName !== 'next') {
return { number };
}
@ -130,6 +139,7 @@ async function parsePR(number, pkgType, cwd, pkg, retries = 10) {
author: author.login,
moduleType: name?.includes('plugin-') ? 'plugin' : 'core',
module: name,
url,
en: {
module: displayName || pkgName,
description,
@ -187,17 +197,7 @@ function arrangeChangelogs(changelogs) {
return result;
}
async function collect() {
let { from, to, ver = 'beta' } = program.opts();
if (!from || !to) {
// git tag -l --sort=version:refname | grep "v*-ver" | tail -2
const tagPattern = `v*-${ver}`;
const { stdout: tags } = await execa(`git tag -l --sort=version:refname | grep "${tagPattern}" | tail -2`, {
shell: true,
});
[from, to] = tags.split('\n');
}
console.log(`From: ${from}, To: ${to}`);
async function collect(from, to) {
const changelogs = [];
const get = async (changelogs, pkgType, cwd, pkg) => {
const prs = await getPRList(from, to, cwd);
@ -231,11 +231,11 @@ async function collect() {
}
}
}
return { changelogs: arrangeChangelogs(changelogs), from, to };
return { changelogs: arrangeChangelogs(changelogs) };
}
async function generateChangelog() {
const { changelogs, from, to } = await collect();
async function generateChangelog(changelogs) {
const { test } = program.opts();
const prTypeLocale = {
'New feature': {
en: '🎉 New Features',
@ -275,13 +275,13 @@ async function generateChangelog() {
const moduleResults = [];
const lists = [];
for (const changelog of moduleChangelogs) {
const { number, author, pro } = changelog;
const { number, author, pro, url } = changelog;
const { description, docTitle, docLink } = changelog[lang];
if (!description) {
console.warn(`PR #${number} has no ${lang} changelog`);
continue;
}
const pr = pro ? '' : ` ([#${number}](https://github.com/nocobase/nocobase/pull/${number}))`;
const pr = pro && !test ? '' : ` ([#${number}](${url}))`;
const doc = docTitle && docLink ? `${referenceLocale[lang]}[${docTitle}](${docLink})` : '';
lists.push(`${description}${pr} by @${author}\n${doc}`);
}
@ -314,7 +314,7 @@ async function generateChangelog() {
const cn = generate(changelogs, 'cn');
const en = generate(changelogs, 'en');
return { cn, en, from, to };
return { cn, en };
}
async function writeChangelog(cn, en, from, to) {
@ -335,6 +335,13 @@ async function writeChangelog(cn, en, from, to) {
}
async function createRelease(cn, en, to) {
const { stdout } = await execa('gh', ['release', 'list', '--json', 'tagName']);
const releases = JSON.parse(stdout);
const tags = releases.map((release) => release.tagName);
if (tags.includes(to)) {
console.log(`Release ${to} already exists`);
return;
}
let { ver = 'beta' } = program.opts();
// gh release create -t title -n note
if (ver === 'alpha') {
@ -344,21 +351,91 @@ async function createRelease(cn, en, to) {
await execa('gh', ['release', 'create', to, '-t', to, '-n', `${en}\n---\n${cn}`]);
}
async function writeChangelogAndCreateRelease() {
let { ver = 'beta', test } = program.opts();
const { cn, en, from, to } = await generateChangelog();
if (!cn && !en) {
throw new Error('No changelog generated');
async function getExistsChangelog(from, to) {
const get = async (lang) => {
const file = lang === 'cn' ? 'CHANGELOG.zh-CN.md' : 'CHANGELOG.md';
const oldChangelog = await fs.readFile(path.join(__dirname, `../../${file}`), 'utf8');
if (!oldChangelog.includes(`## [${to}]`)) {
return null;
}
if (test) {
console.log(en);
console.log(cn);
const fromIndex = oldChangelog.indexOf(`## [${from}]`);
const toIndex = oldChangelog.indexOf(`## [${to}]`);
return oldChangelog.slice(toIndex, fromIndex);
};
const cn = await get('cn');
const en = await get('en');
return { cn, en };
}
async function getVersion() {
let { from, to, ver = 'beta' } = program.opts();
if (!from || !to) {
// git tag -l --sort=version:refname | grep "v*-ver" | tail -2
const tagPattern = `v*-${ver}`;
const { stdout: tags } = await execa(`git tag -l --sort=version:refname | grep "${tagPattern}" | tail -2`, {
shell: true,
});
[from, to] = tags.split('\n');
}
console.log(`From: ${from}, To: ${to}`);
return { from, to };
}
async function postCMS(title, content, contentCN) {
const { cmsToken, cmsURL } = program.opts();
if (!cmsToken || !cmsURL) {
console.error('No cmsToken or cmsURL provided');
return;
}
if (ver === 'beta') {
await axios.request({
method: 'post',
url: `${cmsURL}/api/articles:updateOrCreate`,
headers: {
Authorization: `Bearer ${cmsToken}`,
},
params: {
filterKeys: ['title'],
},
data: {
title,
title_cn: title,
content,
content_cn: contentCN,
tags: [4],
status: 'drafted',
author: 'nocobase [bot]',
},
});
}
async function writeChangelogAndCreateRelease() {
let { ver = 'beta', test } = program.opts();
const { from, to } = await getVersion();
let { cn, en } = await getExistsChangelog(from, to);
let exists = false;
if (cn || en) {
exists = true;
console.log('Changelog already exists');
} else {
const { changelogs } = await collect(from, to);
const c = await generateChangelog(changelogs);
cn = c.cn;
en = c.en;
if (!cn && !en) {
console.error('No changelog generated');
return;
}
}
console.log(en);
console.log(cn);
if (test) {
return;
}
if (ver === 'beta' && !exists) {
await writeChangelog(cn, en, from, to);
}
await createRelease(cn, en, to);
await postCMS(to, en, cn);
}
writeChangelogAndCreateRelease();