-
Notifications
You must be signed in to change notification settings - Fork 2
/
.gitlab-ci.yml
387 lines (351 loc) · 14.3 KB
/
.gitlab-ci.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
default:
image: gradle:8.5.0-jdk21-alpine
cache:
key:
files:
- gradle/wrapper/gradle-wrapper.properties
prefix: gradle-cache-
paths:
- cache/.gradle/wrapper
- cache/.gradle/caches
- cache/.gradle/notifications
variables:
GITLAB_CLUSTER_SOLVER_CONTEXT: solver-team/solver-engine:solver-v2
GITLAB_CLUSTER_PRODUCTION_CONTEXT: solver-team/k8s-clusters-management:prod-cluster-agent
GITLAB_CLUSTER_DEVELOPMENT_CONTEXT: solver-team/k8s-clusters-management:dev-cluster-agent
GGBTRANS_SOLVER_IMPORT_URL: https://dev.geogebra.org/ggbtrans/props/api/solver_import
GRADLE: './gradlew --no-daemon --stacktrace --build-cache --gradle-user-home cache/.gradle'
SOLVER_PRODUCTION_DOMAIN: solver-api.geogebra.org
SOLVER_DEVELOPMENT_DOMAIN: solver.geogebra.net
RELEASE_BRANCH: release
# The gitlab ID of the repository where our container images are uploaded
REPOSITORY_ID: 4
SDK_BUCKET: solver-software-development-kit
stages:
- checks
- build
- publish
- deploy
# Here we define all the variables that configure whether and how the jobs are run.
#
# The strategy is to:
# - deploy SDK and engine in development branches and staging
# - deploy only the SDK when committing to $RELEASE_BRANCH
# - deploy only the engine when a release-x.y.z tag is created
#
# This means that to deploy a new release the steps are as follows
# 1. Create a "release-x.y.0" tag on the staging branch. This will deploy the engine version "x.y".
# 2. Merge staging into $RELEASE_BRANCH. This should trigger an SDK release with the SDK pointing to the x.y API URL.
# 3. Merge $RELEASE_BRANCH back into main. This is to keep semantic-release happy. (See https://semantic-release.gitbook.io/semantic-release/usage/workflow-configuration)
#
# Once we have a release "x.y.z" we can
# - deploy a patch release by creating a new "release-x.y.(z + 1)" tag on the release branch (this will overwrite the
# existing x.y release)
# - release a new SDK simply by adding commits to $RELEASE_BRANCH that contain "fix:" or "feat:" commit messages
#
# This strategy means that several minor release versions can be deployed simultaneously. So it is up to us
# to delete deployments of old releases.
#
# To deploy the engine, the following variables must be set
# - SOLVER_VERSION: something like 1.2 for release versions, plut-652 for development branch, main for the main branch
# - SOLVER_IMAGE_TAG: the container image tag for the solver image, must be different for distinct deployments
# - SOLVER_DEPLOYMENT_NAME: a name used to create a k8s namespace and a gitlab environment. Reuse the same name to
# update a deployment (e.g. when releasing a patch version, or for any development branch)
# - SOLVER_DEPLOYMENT_DOMAIN: the DNS domain where the solver deployment will be available
# - SOLVER_DEPLOYMENT_CLUSTER: the cluster where the solver deployment will be available
#
# These variables are required but computed from the ones above
# - SOLVER_CONTEXT_PATH: the path at which the solver API root is accessible (e.g. /main, /1.2, /plut-654)
# - SOLVER_BASE_URL: the base URL for accessing the solver deployment
# - POKER_URL: The URL where the poker can be reached (it extends $SOLVER_BASE_URL)
#
# To deploy the SDK, the following variables must be set
# - SOLVER_VERSION: something like 1.2 for release versions, plut-652 for development branch, main for the main branch
# - SOLVER_DEPLOYMENT_NAME: a name used to create a k8s namespace and a gitlab environment. Reuse the same name to
#
# The following variables are required but computed from the ones above
# - API_URL: the URL where the solver API can be accessed (it extends $SOLVER_BASE_URL)
#
before_script:
# Define a bunch of variables depending on the commit or branch that triggered the pipeline
- |
if [[ $CI_COMMIT_TAG =~ ^release-[0-9]*\.[0-9]*.*$ ]]; then
# This pipeline is triggered by a release tag: we deploy only the engine, not the SDK, to production.
# release-1.2.1 ==> 1.2
export SOLVER_VERSION=`echo "$CI_COMMIT_TAG" | sed 's/^release-\([0-9]*\.[0-9]*\).*$/\1/'`
# release-1-2-1 so kubernetes doesn't complain
export SOLVER_IMAGE_TAG="$CI_COMMIT_REF_SLUG"
# release-1-2 to only have one deployment per minor version
export SOLVER_DEPLOYMENT_NAME=$(echo "release-$SOLVER_VERSION" | tr "." "-")
export SOLVER_DEPLOYMENT_DOMAIN="$SOLVER_PRODUCTION_DOMAIN"
# The cluster where the solver-engine is deployed
export SOLVER_DEPLOYMENT_CLUSTER_CONTEXT="$GITLAB_CLUSTER_PRODUCTION_CONTEXT"
elif [[ $CI_COMMIT_BRANCH == $RELEASE_BRANCH ]]; then
# This pipeline is triggered by committing to the release branch: we deploy only the SDK, not the engine.
# on the release branch, we only do SDK releases on commit, based on the latest tag
RELEASE_TAG=`git describe --tags --match 'release-*' --abbrev=0`
export SOLVER_VERSION=`echo $RELEASE_TAG | sed 's/^release-\([0-9]*\.[0-9]*\).*$/\1/'`
export SOLVER_DEPLOYMENT_DOMAIN="$SOLVER_PRODUCTION_DOMAIN"
else
# This is a development commit - we want to deploy both engine and SDK
# If the branch name starts with a JIRA tiket number (PLUT-123) use that, else use the whole branch name.
export SOLVER_VERSION=`echo $CI_COMMIT_REF_SLUG | sed 's/^\([a-z][a-z]*\-[0-9][0-9]*\).*/\1/'`
# The tag for the container image of the solver backend that will be published to the container registry
export SOLVER_IMAGE_TAG="$SOLVER_VERSION"
# The name of the Helm deployment - will also be used as the base for naming the kubernetes entities (see helm templates)
export SOLVER_DEPLOYMENT_NAME="$SOLVER_VERSION"
# The domain where the deployed solver backend will be available
export SOLVER_DEPLOYMENT_DOMAIN="$SOLVER_DEVELOPMENT_DOMAIN"
# The cluster where the solver-engine is deployed
export SOLVER_DEPLOYMENT_CLUSTER_CONTEXT="$GITLAB_CLUSTER_SOLVER_CONTEXT"
fi
# For now, the solver context path is always /$SOLVER_VERSION
- export SOLVER_CONTEXT_PATH="/$SOLVER_VERSION"
# The base URL for reaching the deployment
- export SOLVER_BASE_URL="https://$SOLVER_DEPLOYMENT_DOMAIN$SOLVER_CONTEXT_PATH"
# The URL where the solver API can be reached
- export API_URL="$SOLVER_BASE_URL/api/v1"
# The URL where the poker can be reached
- export POKER_URL="$SOLVER_BASE_URL/poker/index.html"
- export REPO_FULL_NAME=`echo $CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME`
# This encodes the conditions when to perform the jobs that are needed only for releasing the SDK
.deploy-sdk-job:
rules:
- if: $CI_COMMIT_BRANCH
# This encodes the conditions when to perform the jobs that are needed only for deploying the engine
.deploy-engine-job:
rules:
- if: $CI_COMMIT_TAG =~ /^release-[0-9]*\.[0-9]*.*$/
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $RELEASE_BRANCH
check-circular-dependencies-in-methods:
stage: checks
cache: []
script:
- ./scripts/check_method_dependencies.sh
print-solver-vars:
stage: checks
script:
- echo RELEASE_BRANCH=$RELEASE_BRANCH
- echo CI_COMMIT_TAG=$CI_COMMIT_TAG
- echo CI_COMMIT_REF_SLUG=$CI_COMMIT_REF_SLUG
- echo SOLVER_VERSION=$SOLVER_VERSION
- echo SOLVER_IMAGE_TAG=$SOLVER_IMAGE_TAG
- echo SOLVER_DEPLOYMENT_NAME=$SOLVER_DEPLOYMENT_NAME
- echo SOLVER_DEPLOYMENT_DOMAIN=$SOLVER_DEPLOYMENT_DOMAIN
- echo SOLVER_DEPLOYMENT_CLUSTER_CONTEXT=$SOLVER_DEPLOYMENT_CLUSTER_CONTEXT
- echo SOLVER_CONTEXT_PATH=$SOLVER_CONTEXT_PATH
- echo SOLVER_BASE_URL=$SOLVER_BASE_URL
- echo API_URL=$API_URL
- echo POKER_URL=$POKER_URL
- echo REPO_FULL_NAME=$REPO_FULL_NAME
- echo REPOSITORY_ID=$REPOSITORY_ID
- echo SDK_BUCKET=$SDK_BUCKET
# Build all artifacts from SDK for the poker and for possible release
build-sdk-and-poker:
stage: build
needs: []
image: node:18
tags:
- docker
cache:
key:
files:
- package-lock.json
prefix: npm-cache-
paths:
- .npm/
script:
- npm ci --cache .npm --prefer-offline
- npm exec -c "prettier . -c"
artifacts:
paths:
- solver-sdk/lib
- solver-sdk/dist
- api/src/main/resources/static/poker
expire_in: 1 day
# The gradle 'build' includes running detekt, ktlint, runnings tests.
build-engine:
stage: build
needs:
- build-sdk-and-poker
tags:
- docker
script:
- $GRADLE build
artifacts:
paths:
- api/build/libs/*.jar
- build/reports/ktlint
- build/reports/detekt
- methods/build/generated/ksp/main/resources/TranslationKeys.json
- methods/build/generated/ksp/main/resources/Method.TranslationKeys.json
- engine/build/generated/ksp/main/resources/TranslationKeys.json
# Used by SDK tests
- methods/build/test-results/gmActionTests.json
- api/src/main/resources/static/poker
expire_in: 1 day
# Test SDK and poker
test-sdk-and-poker:
stage: build
needs:
- build-engine
image: node:18
tags:
- docker
script:
- npm ci --cache .npm --prefer-offline
- npm test -ws
# Package the engine into a docker image
.package-engine:
stage: publish
image: docker
needs:
- build-engine
script:
- export VERSION=$(sed -n 's/version = "\(.*\)"/\1/p' build.gradle.kts)
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build --build-arg VERSION=${VERSION} --tag $CI_REGISTRY_IMAGE/api:$SOLVER_IMAGE_TAG --push .
# With AMD architecture for non-release deployments
package-engine-amd:
extends: .package-engine
tags:
- docker
rules:
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $RELEASE_BRANCH
# With ARM architecture for release tags
package-engine-arm:
extends: .package-engine
tags:
- docker-arm
rules:
- if: $CI_COMMIT_TAG =~ /^release-[0-9]*\.[0-9]*.*$/
export-translation-keys:
stage: publish
image: curlimages/curl
needs:
- build-engine
tags:
- docker
cache: []
script:
# Translation keys from the methods module (most keys)
- |
curl -X POST "$GGBTRANS_SOLVER_IMPORT_URL/?override_test=1" \
--fail-with-body \
--header "X-Token: $GGBTRANS_API_TOKEN" \
--data @methods/build/generated/ksp/main/resources/TranslationKeys.json
# Public method titles
- |
curl -X POST "$GGBTRANS_SOLVER_IMPORT_URL/?override_test=1" \
--fail-with-body \
--header "X-Token: $GGBTRANS_API_TOKEN" \
--data @methods/build/generated/ksp/main/resources/Method.TranslationKeys.json
# Generic translation keys from the engine module
- |
curl -X POST "$GGBTRANS_SOLVER_IMPORT_URL/?override_test=1" \
--fail-with-body \
--header "X-Token: $GGBTRANS_API_TOKEN" \
--data @engine/build/generated/ksp/main/resources/TranslationKeys.json
when: manual
# Deploy the engine to k8s
deploy-engine:
extends: .deploy-engine-job
stage: deploy
image: alpine/k8s:1.23.7
environment:
name: solver/$CI_COMMIT_REF_SLUG
url: $DYNAMIC_ENVIRONMENT_URL
on_stop: undeploy-engine
tags:
- docker
cache: []
artifacts:
reports:
dotenv: deploy.env
script:
- kubectl config get-contexts
- kubectl config use-context $SOLVER_DEPLOYMENT_CLUSTER_CONTEXT
- helm package helm/ --version "6.6.6"
- |
helm upgrade --install $SOLVER_DEPLOYMENT_NAME solver-6.6.6.tgz \
--set deploymentDomain=$SOLVER_DEPLOYMENT_DOMAIN \
--set contextPath=$SOLVER_CONTEXT_PATH \
--set deploymentName=$SOLVER_DEPLOYMENT_NAME \
--set solverImageTag=$SOLVER_IMAGE_TAG \
--set commitSha=$CI_COMMIT_SHA \
--set imageCredentials.username=$CI_REGISTRY_USER \
--set imageCredentials.password=$CI_REGISTRY_PASSWORD \
--set springProfile=production
- echo "DYNAMIC_ENVIRONMENT_URL=$POKER_URL" >> deploy.env
# Make an SDK release
sdk-semantic-release:
extends: .deploy-sdk-job
stage: deploy
needs:
- build-sdk-and-poker
- test-sdk-and-poker
# We would normally use "node:18-alpine" instead of "node:18" because it is smaller, but
# we need git installed in order for semantic-release to work.
image: node:18
tags:
- docker
cache:
key:
files:
- package-lock.json
prefix: npm-cache-
paths:
- .npm/
variables:
NPM_TOKEN: ${CI_JOB_TOKEN}
# The default (fetch) strategy does not remove deleted tags, so they can be out of date
# when running semantic release causing all kinds of issues
GIT_STRATEGY: clone
script:
- node ./solver-sdk/set-base-url.mjs $API_URL
- npm ci --cache .npm --prefer-offline
- npm config set "@geogebra:registry" https://git.geogebra.org/api/v4/projects/126/packages/npm/
- npm config set "//git.geogebra.org/api/v4/projects/126/packages/npm/:_authToken" ${CI_JOB_TOKEN}
- npm run semantic-release -w solver-sdk
artifacts:
paths:
- solver-sdk/dist
- solver-sdk/version.env
expire_in: 1 day
# Make an SDK release to S3 bucket
sdk-semantic-release-to-s3:
extends: .deploy-sdk-job
stage: deploy
needs:
- sdk-semantic-release
script:
- aws s3 sync ./solver-sdk/dist s3://${SDK_BUCKET}/solver-sdk/$(cat ./solver-sdk/version.env)/ --exclude="*" --include="solver-sdk.*.js" || true
# Remove kubernetes deployment. This job is automatically triggered when the feature branch is deleted.
undeploy-engine:
stage: deploy
dependencies: []
image: alpine/k8s:1.23.7
variables:
# As the branch is deleted at this point we can't fetch the repo (and we don't need to)
GIT_STRATEGY: none
environment:
name: solver/$CI_COMMIT_REF_SLUG
action: stop
tags:
- docker
cache: []
script:
- echo "Attempting do delete tag $SOLVER_IMAGE_TAG of deployment $SOLVER_DEPLOYMENT_NAME..."
# Call the gitlab API to delete the image tag as it is no longer needed. This will allow the image
# to be garbage-collected, saving space on the container registry.
- |
curl --request DELETE \
--header "PRIVATE-TOKEN: $GITLAB_API_PRIVATE_TOKEN" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/registry/repositories/$REPOSITORY_ID/tags/$SOLVER_IMAGE_TAG"
- kubectl config get-contexts
- kubectl config use-context $SOLVER_DEPLOYMENT_CLUSTER_CONTEXT
- helm uninstall $SOLVER_DEPLOYMENT_NAME
when: manual
rules:
# we only want to let people undeploy when it's not main or $RELEASE_BRANCH and it's a branch pipeline.
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != $RELEASE_BRANCH