Skip to content

Commit bdbc92c

Browse files
ci: PLT-690: Add release pipeline
1 parent 845aa20 commit bdbc92c

File tree

3 files changed

+328
-0
lines changed

3 files changed

+328
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: "Release: Cut off release branch"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: 'Release version'
8+
required: true
9+
type: string
10+
ref:
11+
description: 'Commit SHA or ref name or tag'
12+
required: true
13+
default: 'master'
14+
type: string
15+
16+
env:
17+
RELEASE_BRANCH_PREFIX: "release/"
18+
19+
jobs:
20+
draft-new-release:
21+
name: "Cut off release branch"
22+
runs-on: ubuntu-latest
23+
outputs:
24+
next_develop_version: ${{ steps.calculate_branch_name_and_version.outputs.next_develop_version }}
25+
release_version: ${{ steps.calculate_branch_name_and_version.outputs.release_version }}
26+
release_branch: ${{ steps.calculate_branch_name_and_version.outputs.release_branch }}
27+
permissions:
28+
contents: write
29+
pull-requests: write
30+
steps:
31+
- uses: hmarr/[email protected]
32+
33+
- name: Checkout
34+
uses: actions/checkout@v4
35+
with:
36+
token: ${{ secrets.GIT_PAT }}
37+
ref: ${{ inputs.ref }}
38+
submodules: 'recursive'
39+
fetch-depth: 0
40+
41+
- name: Configure git
42+
shell: bash
43+
run: |
44+
set -xeuo pipefail
45+
git config --global user.name 'robot-ci-heartex'
46+
git config --global user.email '[email protected]'
47+
48+
- name: Calculate branch name and version
49+
id: calculate_branch_name_and_version
50+
shell: bash
51+
run: |
52+
set -xeuo pipefail
53+
54+
regexp='^[v]?([0-9]+)\.([0-9]+)\.([0-9]+)$';
55+
56+
if [[ "${{ inputs.version }}" =~ $regexp ]]; then
57+
first="${BASH_REMATCH[1]}"
58+
second="${BASH_REMATCH[2]}"
59+
third="${BASH_REMATCH[3]}"
60+
else
61+
echo "${{ inputs.version }} does not mach the regexp ${regexp}"
62+
exit 1
63+
fi
64+
65+
release_version="${first}.${second}.${third}"
66+
release_branch="${{ env.RELEASE_BRANCH_PREFIX }}${first}.${second}.${third}"
67+
next_develop_version="${first}.${second}.$(($third + 1)).dev"
68+
69+
echo "release_branch=${release_branch}" >> $GITHUB_OUTPUT
70+
echo "release_version=${release_version}" >> $GITHUB_OUTPUT
71+
echo "next_develop_version=${next_develop_version}" >> $GITHUB_OUTPUT
72+
73+
- name: Cut release branch
74+
shell: bash
75+
run: |
76+
set -xeuo pipefail
77+
78+
git checkout -b "${{ steps.calculate_branch_name_and_version.outputs.release_branch }}"
79+
echo "commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
80+
git push origin HEAD:refs/heads/${{ steps.calculate_branch_name_and_version.outputs.release_branch }}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: "Release: Pipeline"
2+
3+
on:
4+
release:
5+
types:
6+
- released
7+
8+
concurrency:
9+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.pull_request.head.ref || github.ref }}
10+
cancel-in-progress: true
11+
12+
jobs:
13+
build-image:
14+
name: Build image
15+
if: needs.calculate_matrix.outputs.matrix-include != '[]'
16+
needs: calculate_matrix
17+
runs-on: ${{ matrix.runs_on || 'ubuntu-latest' }}
18+
timeout-minutes: 45
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
include: ${{ fromJson(needs.calculate_matrix.outputs.matrix-include) }}
23+
env:
24+
IMAGE_NAME: heartexlabs/label-studio-ml-backend
25+
backend_dir_name: ${{ matrix.backend_dir_name }}
26+
backend_tag_prefix: ${{ matrix.backend_tag_prefix }}
27+
steps:
28+
- uses: hmarr/[email protected]
29+
30+
- uses: actions/checkout@v4
31+
with:
32+
ref: "${{ env.GITHUB_SHA }}"
33+
34+
- name: Set up Docker Buildx
35+
uses: docker/[email protected]
36+
37+
- name: Login to DockerHub
38+
if: ${{ !github.event.pull_request.head.repo.fork }}
39+
uses: docker/[email protected]
40+
with:
41+
username: ${{ vars.DOCKERHUB_USERNAME }}
42+
password: ${{ secrets.DOCKERHUB_TOKEN }}
43+
44+
- name: Push Docker image
45+
uses: docker/[email protected]
46+
id: docker_build_and_push
47+
env:
48+
DOCKER_BUILD_CHECKS_ANNOTATIONS: "false"
49+
DOCKER_BUILD_SUMMARY: "false"
50+
DOCKER_BUILD_RECORD_UPLOAD: "false"
51+
with:
52+
context: "${{ env.DOCKER_EXAMPLES_DIRECTORY }}/${{ env.backend_dir_name }}"
53+
tags: "${{ matrix.backend_tag_prefix }}${{ github.ref_name }}"
54+
cache-from: type=gha
55+
cache-to: type=gha,mode=max
56+
57+
calculate_matrix:
58+
name: "Calculate build matrix"
59+
runs-on: ubuntu-latest
60+
outputs:
61+
matrix-include: ${{ steps.matrix.outputs.matrix-include }}
62+
steps:
63+
- uses: hmarr/[email protected]
64+
65+
- run: npm install js-yaml
66+
67+
- name: Build matrix
68+
id: matrix
69+
uses: actions/github-script@v7
70+
with:
71+
script: |
72+
const yaml = require('js-yaml');
73+
74+
const {owner, repo} = context.repo;
75+
76+
const default_branch = process.env.DOCKER_BUILD_CONFIG_BRANCH;
77+
const docker_build_config_path = process.env.DOCKER_BUILD_CONFIG_PATH;
78+
79+
const docker_build_config_blob = await github.rest.repos.getContent({
80+
owner: owner,
81+
repo: repo,
82+
ref: default_branch,
83+
path: docker_build_config_path,
84+
});
85+
const docker_build_config_content = Buffer.from(docker_build_config_blob.data.content, docker_build_config_blob.data.encoding).toString("utf8");
86+
const docker_build_config = yaml.load(docker_build_config_content);
87+
88+
core.setOutput("matrix-include", docker_build_config);
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
name: "Update Draft Release"
2+
3+
on:
4+
push:
5+
branches:
6+
- 'release/**'
7+
8+
concurrency:
9+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.pull_request.head.ref || github.ref }}
10+
cancel-in-progress: true
11+
12+
env:
13+
RELEASE_BRANCH_PREFIX: "release/"
14+
15+
jobs:
16+
draft-release:
17+
name: "Draft Release"
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: write
21+
outputs:
22+
id: ${{ steps.create-draft-release.outputs.id }}
23+
rc-version: ${{ steps.create-draft-release.outputs.rc-version }}
24+
steps:
25+
- name: Create release draft
26+
uses: actions/github-script@v7
27+
id: create-draft-release
28+
env:
29+
TARGET_COMMITISH: "${{ github.ref_name }}"
30+
RELEASE_BRANCH_PREFIX: "${{ env.RELEASE_BRANCH_PREFIX }}"
31+
DEFAULT_BRANCH: "${{ github.event.repository.default_branch }}"
32+
with:
33+
script: |
34+
const { repo, owner } = context.repo;
35+
const target_commitish = process.env.TARGET_COMMITISH;
36+
const default_branch = process.env.DEFAULT_BRANCH;
37+
let version = target_commitish.replace(process.env.RELEASE_BRANCH_PREFIX, '')
38+
const regexp = '^[v]?([0-9]+)\.([0-9]+)\.([0-9]+)(\.post([0-9]+))?$';
39+
40+
const {data: compare} = await github.rest.repos.compareCommits({
41+
owner,
42+
repo,
43+
base: default_branch,
44+
head: target_commitish,
45+
});
46+
const rc_version = `${version}rc${ compare.ahead_by }`
47+
console.log(`rc-version: ${rc_version}`)
48+
core.setOutput("rc-version", rc_version);
49+
50+
function compareVersions(a, b) {
51+
if (a[1] === b[1])
52+
if (a[2] === b[2])
53+
if (a[3] === b[3])
54+
return (+a[5] || -1) - (+b[5] || -1)
55+
else
56+
return +a[3] - b[3]
57+
else
58+
return +a[2] - b[2]
59+
else
60+
return +a[1] - b[1]
61+
}
62+
63+
const versionMatch = version.match(regexp)
64+
if (!versionMatch) {
65+
core.setFailed(`Version "${version}" from branch "${target_commitish}" does not match the regexp ${regexp}`)
66+
process.exit()
67+
}
68+
69+
const tags = await github.paginate(
70+
github.rest.repos.listTags,
71+
{
72+
owner,
73+
repo,
74+
per_page: 100
75+
},
76+
(response) => response.data
77+
);
78+
console.log(`Tags:`)
79+
console.log(tags.map(e => e.name))
80+
const matchedTags = tags.filter(e => e.name.indexOf(version) !== -1)
81+
console.log(`Tags for ${version}:`)
82+
console.log(matchedTags.map(e => e.name))
83+
if (matchedTags.length !== 0) {
84+
let newHotfixNumber = 0
85+
for (let matchedTag of matchedTags) {
86+
const matchVersion = matchedTag.name.match('^[v]?([0-9]+)\.([0-9]+)\.([0-9]+)(.post([0-9]+))?$')
87+
if (matchVersion && matchVersion[5]) {
88+
const hotfixNumber = parseInt(matchVersion[5])
89+
if (newHotfixNumber <= hotfixNumber) {
90+
newHotfixNumber = hotfixNumber + 1
91+
}
92+
}
93+
}
94+
version = `${version}.post${newHotfixNumber}`
95+
}
96+
console.log(`New version: ${version}`)
97+
98+
const rawTags = tags.map(e => e.name)
99+
const tagsWithNew = [...rawTags, version]
100+
const sortedTags = tagsWithNew
101+
.filter(e => e.match(regexp))
102+
.map((e => e.match(regexp)))
103+
.sort(compareVersions)
104+
.reverse()
105+
.map(e => e[0])
106+
const previousTag = sortedTags[sortedTags.indexOf(version)+1]
107+
console.log(`Previous version: ${previousTag}`)
108+
109+
console.log('Find or Create a Draft release')
110+
const releases = await github.paginate(
111+
github.rest.repos.listReleases,
112+
{
113+
owner,
114+
repo,
115+
per_page: 100
116+
},
117+
(response) => response.data
118+
);
119+
let release = releases.find(e => target_commitish.endsWith(e.target_commitish) && e.draft)
120+
if (release) {
121+
console.log(`Draft release already exist ${release.html_url}`)
122+
} else {
123+
console.log(`Draft release is not found creating a new one`)
124+
const {data: newDraftRelease} = await github.rest.repos.createRelease({
125+
owner,
126+
repo,
127+
draft: true,
128+
prerelease: false,
129+
name: version,
130+
tag_name: version,
131+
target_commitish: target_commitish,
132+
});
133+
console.log(`Draft release is created ${newDraftRelease.html_url}`)
134+
release = newDraftRelease;
135+
core.setOutput("created", true);
136+
}
137+
138+
console.log('Updating release with new release notes')
139+
const {data: release_notes} = await github.rest.repos.generateReleaseNotes({
140+
owner,
141+
repo,
142+
tag_name: version,
143+
previous_tag_name: previousTag,
144+
target_commitish: target_commitish,
145+
});
146+
const {data: updated_release} = await github.rest.repos.updateRelease({
147+
owner,
148+
repo,
149+
release_id: release.id,
150+
draft: true,
151+
prerelease: false,
152+
name: version,
153+
tag_name: version,
154+
target_commitish: target_commitish,
155+
body: release_notes.body
156+
});
157+
console.log(`Draft release is updated ${updated_release.html_url}`)
158+
159+
core.setOutput("id", updated_release.id);
160+
core.setOutput("tag_name", release.tag_name);

0 commit comments

Comments
 (0)