Skip to content

Commit 50b517c

Browse files
committed
implement GH release workflow - phase 1
1 parent 949f0e6 commit 50b517c

11 files changed

+621
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: Prepare Release
2+
3+
on:
4+
workflow_dispatch:
5+
branches: ["master"]
6+
7+
jobs:
8+
build:
9+
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: tibdex/github-app-token@v1
15+
id: generate-token
16+
with:
17+
app_id: ${{ secrets.APP_ID }}
18+
private_key: ${{ secrets.APP_PRIVATE_KEY }}
19+
- name: Set up Python 3.10
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: '3.10'
23+
- name: Set up Java 11
24+
uses: actions/setup-java@v4
25+
with:
26+
java-version: 11
27+
distribution: temurin
28+
server-id: central
29+
server-username: MAVEN_USERNAME
30+
server-password: MAVEN_PASSWORD
31+
- name: Cache local Maven repository
32+
uses: actions/cache@v4
33+
with:
34+
path: ~/.m2/repository
35+
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
36+
restore-keys: |
37+
${{ runner.os }}-maven-
38+
- name: Run prepare release script
39+
id: prepare-release
40+
run: |
41+
export MY_POM_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${projects.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec`
42+
if [[ $MY_POM_VERSION =~ ^.*SNAPSHOT$ ]];
43+
then
44+
. ./CI/prepare-release.sh
45+
echo "PREPARE_RELEASE_OK=yes" >> $GITHUB_ENV
46+
else
47+
echo "not preparing release for release version: " ${MY_POM_VERSION}
48+
echo "PREPARE_RELEASE_OK=no" >> $GITHUB_ENV
49+
fi
50+
echo "SC_VERSION=$SC_VERSION" >> $GITHUB_ENV
51+
echo "SC_NEXT_VERSION=$SC_NEXT_VERSION" >> $GITHUB_ENV
52+
- name: Create Prepare Release Pull Request
53+
uses: peter-evans/create-pull-request@v4
54+
if: env.PREPARE_RELEASE_OK == 'yes'
55+
with:
56+
token: ${{ steps.generate-token.outputs.token }}
57+
commit-message: prepare release ${{ env.SC_VERSION }}
58+
title: 'prepare release ${{ env.SC_VERSION }}'
59+
branch: prepare-release-${{ env.SC_VERSION }}
60+
env:
61+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
62+
MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
63+
MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
64+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
65+
SC_VERSION:
66+
SC_NEXT_VERSION:
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
name: Release
2+
3+
on:
4+
workflow_dispatch:
5+
branches: ["master"]
6+
7+
jobs:
8+
build:
9+
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: tibdex/github-app-token@v1
15+
id: generate-token
16+
with:
17+
app_id: ${{ secrets.APP_ID }}
18+
private_key: ${{ secrets.APP_PRIVATE_KEY }}
19+
- name: Set up Python 3.10
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: '3.10'
23+
- name: Set up Java 11
24+
uses: actions/setup-java@v4
25+
with:
26+
java-version: 11
27+
distribution: temurin
28+
server-id: central
29+
server-username: MAVEN_USERNAME
30+
server-password: MAVEN_PASSWORD
31+
gpg-private-key: ${{ secrets.OSSRH_GPG_PRIVATE_KEY }}
32+
- name: Cache local Maven repository
33+
uses: actions/cache@v4
34+
with:
35+
path: ~/.m2/repository
36+
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
37+
restore-keys: |
38+
${{ runner.os }}-maven-
39+
- name: Run pre release script
40+
id: preRelease
41+
run: |
42+
# export GPG_TTY=$(tty)
43+
export MY_POM_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${projects.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec`
44+
if [[ $MY_POM_VERSION =~ ^.*SNAPSHOT$ ]];
45+
then
46+
echo "not releasing snapshot version: " ${MY_POM_VERSION}
47+
echo "RELEASE_OK=no" >> $GITHUB_ENV
48+
else
49+
. ./CI/pre-release.sh
50+
echo "RELEASE_OK=yes" >> $GITHUB_ENV
51+
fi
52+
echo "SC_VERSION=$SC_VERSION" >> $GITHUB_ENV
53+
echo "SC_NEXT_VERSION=$SC_NEXT_VERSION" >> $GITHUB_ENV
54+
echo "SC_LAST_RELEASE=$SC_LAST_RELEASE" >> $GITHUB_ENV
55+
echo "SC_RELEASE_TAG=v$SC_VERSION" >> $GITHUB_ENV
56+
- name: configure git user email
57+
run: |
58+
git config --global user.email "[email protected]"
59+
git config --global user.name "GitHub Action"
60+
git config --global hub.protocol https
61+
git remote set-url origin https://\${{ secrets.GITHUB_TOKEN }}:[email protected]/''' + 'swagger-api/swagger-codegen' + '''.git
62+
- name: Run maven deploy/release
63+
if: env.RELEASE_OK == 'yes'
64+
run: |
65+
mvn --no-transfer-progress -B -Prelease deploy
66+
- name: Set up QEMU
67+
uses: docker/setup-qemu-action@v3
68+
- name: Set up Docker Buildx
69+
uses: docker/setup-buildx-action@v3
70+
- name: docker login
71+
run: |
72+
docker login --username=${{ secrets.DOCKERHUB_SB_USERNAME }} --password=${{ secrets.DOCKERHUB_SB_PASSWORD }}
73+
set -e
74+
- name: Build generator image and push
75+
uses: docker/build-push-action@v5
76+
with:
77+
context: ./modules/swagger-generator
78+
push: true
79+
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x
80+
provenance: false
81+
tags: swaggerapi/swagger-generator:${{ env.SC_RELEASE_TAG }},swaggerapi/swagger-generator:latest
82+
- name: Build CLI image and push
83+
uses: docker/build-push-action@v5
84+
with:
85+
context: ./modules/swagger-codegen-cli
86+
push: true
87+
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x
88+
provenance: false
89+
tags: swaggerapi/swagger-codegen-cli:${{ env.SC_RELEASE_TAG }},swaggerapi/swagger-codegen-cli:latest
90+
- name: Run post release script
91+
id: postRelease
92+
if: env.RELEASE_OK == 'yes'
93+
run: |
94+
. ./CI/post-release.sh
95+
- name: Create Next Snapshot Pull Request
96+
uses: peter-evans/create-pull-request@v4
97+
if: env.RELEASE_OK == 'yes'
98+
with:
99+
token: ${{ steps.generate-token.outputs.token }}
100+
commit-message: bump snapshot ${{ env.SC_NEXT_VERSION }}-SNAPSHOT
101+
title: 'bump snapshot ${{ env.SC_NEXT_VERSION }}-SNAPSHOT'
102+
branch: bump-snap-${{ env.SC_NEXT_VERSION }}-SNAPSHOT
103+
- name: deploy
104+
run: |
105+
echo "${{ env.SC_RELEASE_TAG }}"
106+
107+
TOKEN="${{ secrets.RANCHER2_BEARER_TOKEN }}"
108+
RANCHER_HOST="rancher.tools.swagger.io"
109+
CLUSTER_ID="c-n8zp2"
110+
NAMESPACE_NAME="swagger-oss"
111+
K8S_OBJECT_TYPE="daemonsets"
112+
K8S_OBJECT_NAME="swagger-generator"
113+
DEPLOY_IMAGE="swaggerapi/swagger-generator:${{ env.SC_RELEASE_TAG }}"
114+
115+
workloadStatus=""
116+
getStatus() {
117+
echo "Getting update status..."
118+
if ! workloadStatus="$(curl -s -X GET \
119+
-H "Authorization: Bearer ${TOKEN}" \
120+
-H 'Content-Type: application/json' \
121+
"https://${RANCHER_HOST}/k8s/clusters/${CLUSTER_ID}/apis/apps/v1/namespaces/${NAMESPACE_NAME}/${K8S_OBJECT_TYPE}/${K8S_OBJECT_NAME}/status")"
122+
then
123+
echo 'ERROR - get status k8s API call failed!'
124+
echo "Exiting build"...
125+
exit 1
126+
fi
127+
}
128+
129+
# $1 = image to deploy
130+
updateObject() {
131+
local image="${1}"
132+
echo "Updating image value..."
133+
134+
if ! curl -s -X PATCH \
135+
-H "Authorization: Bearer ${TOKEN}" \
136+
-H 'Content-Type: application/json-patch+json' \
137+
"https://${RANCHER_HOST}/k8s/clusters/${CLUSTER_ID}/apis/apps/v1/namespaces/${NAMESPACE_NAME}/${K8S_OBJECT_TYPE}/${K8S_OBJECT_NAME}" \
138+
-d "[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/image\", \"value\": \"${image}\"}]"
139+
then
140+
echo 'ERROR - image update k8s API call failed!'
141+
echo "Exiting build..."
142+
exit 1
143+
fi
144+
}
145+
146+
147+
# Check that the TAG is valid
148+
if [[ ${{ env.SC_RELEASE_TAG }} =~ ^[vV]?[0-9]*\.[0-9]*\.[0-9]*$ ]]; then
149+
echo ""
150+
echo "This is a Valid TAG..."
151+
152+
# Get current image/tag in case we need to rollback
153+
getStatus
154+
ROLLBACK_IMAGE="$(echo "${workloadStatus}" | jq -r '.spec.template.spec.containers[0].image')"
155+
echo ""
156+
echo "Current image: ${ROLLBACK_IMAGE}"
157+
158+
# Update image and validate response
159+
echo ""
160+
updateObject "${DEPLOY_IMAGE}"
161+
echo ""
162+
163+
echo ""
164+
echo "Waiting for pods to start..."
165+
echo ""
166+
sleep 60s
167+
168+
# Get state of the k8s object. If numberReady == desiredNumberScheduled, consider the upgrade successful. Else raise error
169+
getStatus
170+
status="$(echo "${workloadStatus}" | jq '.status')"
171+
echo ""
172+
echo "${status}"
173+
echo ""
174+
175+
numberDesired="$(echo "${status}" | jq -r '.desiredNumberScheduled')"
176+
numberReady="$(echo "${status}" | jq -r '.numberReady')"
177+
178+
if (( numberReady == numberDesired )); then
179+
echo "${K8S_OBJECT_NAME} has been upgraded to ${DEPLOY_IMAGE}"
180+
181+
# If pods are not starting, rollback the upgrade and exit the build with error
182+
else
183+
echo "state = error...rolling back upgrade"
184+
updateObject "${ROLLBACK_IMAGE}"
185+
echo ""
186+
187+
echo ""
188+
echo "Waiting for rollback pods to start..."
189+
echo ""
190+
sleep 60s
191+
192+
getStatus
193+
status="$(echo "${workloadStatus}" | jq '.status')"
194+
echo ""
195+
echo "${status}"
196+
echo ""
197+
198+
numberDesired="$(echo "${status}" | jq -r '.desiredNumberScheduled')"
199+
numberReady="$(echo "${status}" | jq -r '.numberReady')"
200+
201+
if (( numberReady == numberDesired )); then
202+
echo "Rollback to ${ROLLBACK_IMAGE} completed."
203+
else
204+
echo "FATAL - rollback failed"
205+
fi
206+
echo "Exiting Build..."
207+
exit 1
208+
fi
209+
210+
else
211+
echo "This TAG is not in a valid format..."
212+
echo "Exiting Build..."
213+
exit 0
214+
fi
215+
echo "Exiting Build..."
216+
exit 0
217+
env:
218+
SC_RELEASE_TAG:
219+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
220+
MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
221+
MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
222+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
223+
SC_VERSION:
224+
SC_NEXT_VERSION:
225+
GPG_PRIVATE_KEY: ${{ secrets.OSSRH_GPG_PRIVATE_KEY }}
226+
GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_PRIVATE_PASSPHRASE }}
227+
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
228+
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}

CI/CI.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
## Continuous integration
2+
3+
### Build, test and deploy
4+
Swagger Codegen uses Github actions to run jobs/checks building, testing and deploying snapshots on push and PR events.
5+
6+
These github actions are configured in `.github/workflows`:
7+
8+
* maven-master.yml : Build Test Deploy master
9+
* maven-master-pulls.yml Build Test PR
10+
11+
12+
These actions use available actions in combination with short bash scripts.
13+
14+
### Release
15+
16+
Releases are semi-automated and consist in 2 actions using available public actions in combination with bash and python scripts.
17+
**TODO**: Python code is used for historical reasons to execute GitHub APIs calls, in general a more consistent environment would
18+
be more maintainable e.g. implementing a custom JavaScript or Docker Container GitHub Action and/or a bash only script(s).
19+
20+
#### Workflow summary
21+
22+
1. execute `prepare-release-master.yml` / `Prepare Release Master` for `master` branch
23+
1. check and merge the Prepare Release PR pushed by previous step. Delete the branch
24+
1. execute `release-master.yml` / `Release Master` for `master` branch
25+
1. check and merge the next snaphot PR pushed by previous step. Delete the branch
26+
27+
#### Prepare Release
28+
29+
The first action to execute is `prepare-release-master.yml` / `Prepare Release Master` for master branch.
30+
31+
This is triggered by manually executing the action, selecting `Actions` in project GitHub UI, then `Prepare Release Master` workflow
32+
and clicking `Run Workflow`
33+
34+
`Prepare Release Master` takes care of:
35+
36+
* create release notes out of merged PRs
37+
* Draft a release with related tag
38+
* bump versions to release, and update all affected files
39+
* build and test maven
40+
* push a Pull Request with the changes for human check.
41+
42+
After the PR checks complete, the PR can me merged, and the second phase `Release Master` started.
43+
44+
#### Release
45+
46+
Once prepare release PR has been merged, the second phase is provided by `release-master.yml` / `Release Master` actions for master branch.
47+
48+
This is triggered by manually executing the action, selecting `Actions` in project GitHub UI, then `Release Master` workflow
49+
and clicking `Run Workflow`
50+
51+
`Release Master` takes care of:
52+
53+
* build and test maven
54+
* deploy/publish to maven central
55+
* publish the previously prepared GitHub release / tag
56+
* build and push docker image
57+
* deploy/publish docker image to docker hub
58+
* push PR for next snapshot
59+
60+
61+
62+
### Secrets
63+
64+
GitHub Actions make use of `Secrets` which can be configured either with Repo or Organization scope; the needed secrets are the following:
65+
66+
* `APP_ID` and APP_PRIVATE_KEY`: these are the values provided by an account configured GitHub App, allowing to obtain a GitHub token
67+
different from the default used in GitHub Actions (which does not allow to "chain" actions).Actions
68+
69+
The GitHub App must be configured as detailed in [this doc](https://github.com/peter-evans/create-pull-request/blob/master/docs/concepts-guidelines.md#authenticating-with-github-app-generated-tokens).
70+
71+
See also [here](https://github.com/peter-evans/create-pull-request/blob/master/docs/concepts-guidelines.md#triggering-further-workflow-runs)
72+
73+
* `OSSRH_GPG_PRIVATE_KEY` and `OSSRH_GPG_PRIVATE_PASSPHRASE` : gpg key and passphrase to be used for sonatype releases
74+
GPG private key and passphrase defined to be used for sonatype deployments, as detailed in
75+
https://central.sonatype.org/pages/working-with-pgp-signatures.html (I'd say with email matching the one of the sonatype account of point 1
76+
77+
* `MAVEN_CENTRAL_USERNAME` and `MAVEN_CENTRAL_PASSWORD`: sonatype user/token
78+
79+
* `GRADLE_PUBLISH_KEY` and `GRADLE_PUBLISH_SECRET`: credentials for https://plugins.gradle.org/
80+
81+
82+
83+
84+
85+
86+
87+
88+

0 commit comments

Comments
 (0)