@catladder/pipeline 3.13.1 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bash/bashEscape.d.ts +2 -0
- package/dist/bash/bashEscape.js +9 -1
- package/dist/constants.js +1 -1
- package/dist/pipeline/agent/createAgentContext.d.ts +6 -0
- package/dist/pipeline/agent/createAgentContext.js +141 -0
- package/dist/pipeline/agent/createAgentEventJob.d.ts +2 -0
- package/dist/pipeline/agent/createAgentEventJob.js +74 -0
- package/dist/pipeline/agent/createAgentReviewJob.d.ts +2 -0
- package/dist/pipeline/agent/createAgentReviewJob.js +69 -0
- package/dist/pipeline/agent/createJobsForAgentContext.d.ts +2 -0
- package/dist/pipeline/agent/createJobsForAgentContext.js +12 -0
- package/dist/pipeline/agent/prompts.d.ts +10 -0
- package/dist/pipeline/agent/prompts.js +66 -0
- package/dist/pipeline/agent/shared.d.ts +8 -0
- package/dist/pipeline/agent/shared.js +29 -0
- package/dist/pipeline/agent/utils.d.ts +3 -0
- package/dist/pipeline/agent/utils.js +16 -0
- package/dist/pipeline/createAllJobs.d.ts +5 -1
- package/dist/pipeline/createAllJobs.js +32 -6
- package/dist/pipeline/createMainPipeline.js +10 -5
- package/dist/pipeline/gitlab/createGitlabJobs.d.ts +3 -3
- package/dist/pipeline/gitlab/createGitlabJobs.js +13 -11
- package/dist/pipeline/gitlab/createGitlabPipeline.js +6 -0
- package/dist/rules/index.d.ts +1 -0
- package/dist/rules/index.js +8 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/agent.d.ts +7 -0
- package/dist/types/agent.js +5 -0
- package/dist/types/config.d.ts +2 -0
- package/dist/types/context.d.ts +7 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +2 -1
- package/dist/types/jobs.d.ts +5 -1
- package/dist/types/jobs.js +1 -1
- package/examples/__snapshots__/cloud-run-health-check-defaults.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-health-check-only-startup.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-health-check.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-http2.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-llama.test.ts.snap +29 -0
- package/examples/__snapshots__/cloud-run-memory-limit.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-meteor-with-worker.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-nextjs.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-no-cpu-throttling.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-no-service.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-non-public.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-post-stop-job.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-service-custom-vpc-connector.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-service-custom-vpc.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-service-gen2.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-service-increase-timout.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-service-with-volumes.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-storybook.test.ts.snap +53 -0
- package/examples/__snapshots__/cloud-run-with-agents.test.ts.snap +1576 -0
- package/examples/__snapshots__/cloud-run-with-gpu.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-with-ngnix.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-with-sql-legacy-jobs.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-with-sql-multiple-dbs.test.ts.snap +169 -0
- package/examples/__snapshots__/cloud-run-with-sql-reuse-db.test.ts.snap +117 -0
- package/examples/__snapshots__/cloud-run-with-sql.test.ts.snap +65 -0
- package/examples/__snapshots__/cloud-run-with-worker.test.ts.snap +65 -0
- package/examples/__snapshots__/custom-build-job-with-tests.test.ts.snap +65 -0
- package/examples/__snapshots__/custom-build-job.test.ts.snap +53 -0
- package/examples/__snapshots__/custom-deploy.test.ts.snap +59 -0
- package/examples/__snapshots__/custom-envs.test.ts.snap +63 -0
- package/examples/__snapshots__/custom-sbom-java.test.ts.snap +53 -0
- package/examples/__snapshots__/custom-verify-job.test.ts.snap +73 -0
- package/examples/__snapshots__/git-submodule.test.ts.snap +65 -0
- package/examples/__snapshots__/kubernetes-application-customization.test.ts.snap +73 -0
- package/examples/__snapshots__/kubernetes-with-cloud-sql.test.ts.snap +73 -0
- package/examples/__snapshots__/kubernetes-with-jobs.test.ts.snap +133 -0
- package/examples/__snapshots__/kubernetes-with-mongodb.test.ts.snap +73 -0
- package/examples/__snapshots__/local-dot-env.test.ts.snap +65 -0
- package/examples/__snapshots__/meteor-kubernetes.test.ts.snap +73 -0
- package/examples/__snapshots__/multiline-var.test.ts.snap +177 -0
- package/examples/__snapshots__/native-app.test.ts.snap +101 -0
- package/examples/__snapshots__/node-build-with-custom-image.test.ts.snap +65 -0
- package/examples/__snapshots__/node-build-with-docker-additions.test.ts.snap +65 -0
- package/examples/__snapshots__/override-secrets.test.ts.snap +65 -0
- package/examples/__snapshots__/rails-k8s-with-worker-dockerfile.test.ts.snap +65 -0
- package/examples/__snapshots__/rails-k8s-with-worker.test.ts.snap +65 -0
- package/examples/__snapshots__/referencing-other-vars.test.ts.snap +177 -0
- package/examples/__snapshots__/wait-for-other-deploy.test.ts.snap +85 -0
- package/examples/__snapshots__/workspace-api-www-turbo-cache.test.ts.snap +97 -0
- package/examples/__snapshots__/workspace-api-www.test.ts.snap +97 -0
- package/examples/cloud-run-with-agents.test.ts +11 -0
- package/examples/cloud-run-with-agents.ts +36 -0
- package/package.json +1 -1
- package/src/bash/bashEscape.ts +6 -0
- package/src/pipeline/__tests__/__snapshots__/getPipelineStages.test.ts.snap +9 -0
- package/src/pipeline/agent/createAgentContext.ts +19 -0
- package/src/pipeline/agent/createAgentEventJob.ts +35 -0
- package/src/pipeline/agent/createAgentReviewJob.ts +33 -0
- package/src/pipeline/agent/createJobsForAgentContext.ts +7 -0
- package/src/pipeline/agent/prompts.ts +233 -0
- package/src/pipeline/agent/shared.ts +36 -0
- package/src/pipeline/agent/utils.ts +9 -0
- package/src/pipeline/createAllJobs.ts +20 -1
- package/src/pipeline/createJobsForComponent.ts +1 -0
- package/src/pipeline/createMainPipeline.ts +19 -4
- package/src/pipeline/gitlab/createGitlabJobs.ts +39 -30
- package/src/pipeline/gitlab/createGitlabPipeline.ts +8 -0
- package/src/rules/index.ts +7 -0
- package/src/types/agent.ts +7 -0
- package/src/types/config.ts +3 -0
- package/src/types/context.ts +8 -0
- package/src/types/index.ts +1 -0
- package/src/types/jobs.ts +6 -0
|
@@ -0,0 +1,1576 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`matches snapshot for cloud-run-with-agents local pipeline YAML 1`] = `
|
|
4
|
+
"image: path/to/docker/jobs-default:the-version
|
|
5
|
+
stages:
|
|
6
|
+
- setup
|
|
7
|
+
- setup dev
|
|
8
|
+
- setup review
|
|
9
|
+
- setup stage
|
|
10
|
+
- setup prod
|
|
11
|
+
- test
|
|
12
|
+
- test dev
|
|
13
|
+
- test review
|
|
14
|
+
- test stage
|
|
15
|
+
- test prod
|
|
16
|
+
- build
|
|
17
|
+
- build dev
|
|
18
|
+
- build review
|
|
19
|
+
- build stage
|
|
20
|
+
- build prod
|
|
21
|
+
- deploy
|
|
22
|
+
- deploy dev
|
|
23
|
+
- deploy review
|
|
24
|
+
- deploy stage
|
|
25
|
+
- deploy prod
|
|
26
|
+
- verify
|
|
27
|
+
- verify dev
|
|
28
|
+
- verify review
|
|
29
|
+
- verify stage
|
|
30
|
+
- verify prod
|
|
31
|
+
- agents
|
|
32
|
+
- agents dev
|
|
33
|
+
- agents review
|
|
34
|
+
- agents stage
|
|
35
|
+
- agents prod
|
|
36
|
+
- rollback
|
|
37
|
+
- rollback dev
|
|
38
|
+
- rollback review
|
|
39
|
+
- rollback stage
|
|
40
|
+
- rollback prod
|
|
41
|
+
- stop
|
|
42
|
+
- stop dev
|
|
43
|
+
- stop review
|
|
44
|
+
- stop stage
|
|
45
|
+
- stop prod
|
|
46
|
+
- release
|
|
47
|
+
variables:
|
|
48
|
+
FF_USE_FASTZIP: 'true'
|
|
49
|
+
ARTIFACT_COMPRESSION_LEVEL: fast
|
|
50
|
+
CACHE_COMPRESSION_LEVEL: fast
|
|
51
|
+
TRANSFER_METER_FREQUENCY: 5s
|
|
52
|
+
GIT_DEPTH: '1'
|
|
53
|
+
workflow:
|
|
54
|
+
name: $PIPELINE_ICON $PIPELINE_NAME
|
|
55
|
+
rules:
|
|
56
|
+
- if: $CI_PIPELINE_SOURCE == "trigger"
|
|
57
|
+
variables:
|
|
58
|
+
PIPELINE_ICON: 🤖
|
|
59
|
+
PIPELINE_NAME: Thinking...
|
|
60
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
61
|
+
variables:
|
|
62
|
+
PIPELINE_ICON: 🐱🔨
|
|
63
|
+
PIPELINE_NAME: Merge Request $CI_MERGE_REQUEST_IID
|
|
64
|
+
- if: $CI_COMMIT_TAG
|
|
65
|
+
variables:
|
|
66
|
+
PIPELINE_ICON: 🐱📦
|
|
67
|
+
PIPELINE_NAME: Release $CI_COMMIT_TAG
|
|
68
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
69
|
+
variables:
|
|
70
|
+
PIPELINE_ICON: 🐱🔨
|
|
71
|
+
PIPELINE_NAME: Main
|
|
72
|
+
- when: always
|
|
73
|
+
variables:
|
|
74
|
+
PIPELINE_ICON: 🐱❓
|
|
75
|
+
PIPELINE_NAME: Default
|
|
76
|
+
before_script:
|
|
77
|
+
- |-
|
|
78
|
+
function escapeForDotEnv () {
|
|
79
|
+
input="\${1:-$(cat)}"
|
|
80
|
+
input="\${input//$'\\n'/\\\\n}"
|
|
81
|
+
if [[ "$input" == *\\\\n* ]]; then
|
|
82
|
+
if [[ "$input" == *\\"* && "$input" == *\\'* && "$input" == *\\\`* ]]; then
|
|
83
|
+
printf "\\"%s\\"\\n" "$input"
|
|
84
|
+
elif [[ "$input" == *\\"* && "$input" == *\\'* ]]; then
|
|
85
|
+
printf "\`%s\`\\n" "$input"
|
|
86
|
+
elif [[ "$input" == *\\"* ]]; then
|
|
87
|
+
printf "'%s'\\n" "$input"
|
|
88
|
+
else
|
|
89
|
+
printf "\\"%s\\"\\n" "$input"
|
|
90
|
+
fi
|
|
91
|
+
else
|
|
92
|
+
printf "%s\\n" "$input"
|
|
93
|
+
fi
|
|
94
|
+
}
|
|
95
|
+
- |-
|
|
96
|
+
function collapseable_section_start () {
|
|
97
|
+
local section_title="\${1}"
|
|
98
|
+
local section_description="\${2:-$section_title}"
|
|
99
|
+
echo -e "section_start:\`date +%s\`:\${section_title}[collapsed=true]\\r\\e[0K\${section_description}"
|
|
100
|
+
}
|
|
101
|
+
- |-
|
|
102
|
+
function collapseable_section_end () {
|
|
103
|
+
local section_title="\${1}"
|
|
104
|
+
echo -e "section_end:\`date +%s\`:\${section_title}\\r\\e[0K"
|
|
105
|
+
}
|
|
106
|
+
www 🛡 audit:
|
|
107
|
+
stage: test
|
|
108
|
+
image: path/to/docker/jobs-default:the-version
|
|
109
|
+
variables:
|
|
110
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
111
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
112
|
+
KUBERNETES_MEMORY_LIMIT: 4Gi
|
|
113
|
+
script:
|
|
114
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
115
|
+
- export APP_PATH="www"
|
|
116
|
+
- collapseable_section_end "injectvars"
|
|
117
|
+
- cd www
|
|
118
|
+
- yarn npm audit --environment production
|
|
119
|
+
rules:
|
|
120
|
+
- when: never
|
|
121
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
122
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
123
|
+
- when: never
|
|
124
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
125
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
126
|
+
needs: []
|
|
127
|
+
retry: &a1
|
|
128
|
+
max: 2
|
|
129
|
+
when:
|
|
130
|
+
- runner_system_failure
|
|
131
|
+
- stuck_or_timeout_failure
|
|
132
|
+
interruptible: true
|
|
133
|
+
allow_failure: true
|
|
134
|
+
www 👮 lint:
|
|
135
|
+
stage: test
|
|
136
|
+
image: path/to/docker/jobs-default:the-version
|
|
137
|
+
variables:
|
|
138
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
139
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
140
|
+
KUBERNETES_MEMORY_LIMIT: 4Gi
|
|
141
|
+
script:
|
|
142
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
143
|
+
- export APP_PATH="www"
|
|
144
|
+
- collapseable_section_end "injectvars"
|
|
145
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
146
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
147
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
148
|
+
- collapseable_section_end "nodeinstall"
|
|
149
|
+
- cd www
|
|
150
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
151
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
152
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
153
|
+
- collapseable_section_end "nodeinstall"
|
|
154
|
+
- collapseable_section_start "yarninstall" "Yarn install"
|
|
155
|
+
- yarn install --immutable
|
|
156
|
+
- collapseable_section_end "yarninstall"
|
|
157
|
+
- yarn lint
|
|
158
|
+
cache:
|
|
159
|
+
- key: www-yarn
|
|
160
|
+
policy: pull-push
|
|
161
|
+
paths:
|
|
162
|
+
- www/.yarn
|
|
163
|
+
- key: www-node-modules
|
|
164
|
+
policy: pull-push
|
|
165
|
+
paths:
|
|
166
|
+
- www/node_modules
|
|
167
|
+
rules:
|
|
168
|
+
- when: never
|
|
169
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
170
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
171
|
+
- when: never
|
|
172
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
173
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
174
|
+
needs: []
|
|
175
|
+
retry: *a1
|
|
176
|
+
interruptible: true
|
|
177
|
+
www 🧪 test:
|
|
178
|
+
stage: test
|
|
179
|
+
image: path/to/docker/jobs-testing-chrome:the-version
|
|
180
|
+
variables:
|
|
181
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
182
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
183
|
+
KUBERNETES_MEMORY_LIMIT: 4Gi
|
|
184
|
+
script:
|
|
185
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
186
|
+
- export APP_PATH="www"
|
|
187
|
+
- collapseable_section_end "injectvars"
|
|
188
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
189
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
190
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
191
|
+
- collapseable_section_end "nodeinstall"
|
|
192
|
+
- cd www
|
|
193
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
194
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
195
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
196
|
+
- collapseable_section_end "nodeinstall"
|
|
197
|
+
- collapseable_section_start "yarninstall" "Yarn install"
|
|
198
|
+
- yarn install --immutable
|
|
199
|
+
- collapseable_section_end "yarninstall"
|
|
200
|
+
- yarn test
|
|
201
|
+
cache:
|
|
202
|
+
- key: www-yarn
|
|
203
|
+
policy: pull-push
|
|
204
|
+
paths:
|
|
205
|
+
- www/.yarn
|
|
206
|
+
- key: www-node-modules
|
|
207
|
+
policy: pull-push
|
|
208
|
+
paths:
|
|
209
|
+
- www/node_modules
|
|
210
|
+
rules:
|
|
211
|
+
- when: never
|
|
212
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
213
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
214
|
+
- when: never
|
|
215
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
216
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
217
|
+
needs: []
|
|
218
|
+
retry: *a1
|
|
219
|
+
interruptible: true
|
|
220
|
+
'www 🔨 app | dev ':
|
|
221
|
+
stage: build
|
|
222
|
+
image: path/to/docker/jobs-default:the-version
|
|
223
|
+
variables:
|
|
224
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
225
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
226
|
+
KUBERNETES_MEMORY_LIMIT: 4Gi
|
|
227
|
+
script:
|
|
228
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
229
|
+
- export ENV_SHORT="dev"
|
|
230
|
+
- export APP_DIR="www"
|
|
231
|
+
- export ENV_TYPE="dev"
|
|
232
|
+
- export BUILD_INFO_BUILD_ID="$(git describe --tags 2>/dev/null || git rev-parse HEAD)"
|
|
233
|
+
- export BUILD_INFO_BUILD_TIME="$CI_JOB_STARTED_AT"
|
|
234
|
+
- export BUILD_INFO_CURRENT_VERSION="$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")"
|
|
235
|
+
- export HOSTNAME="$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
236
|
+
- export ROOT_URL="https://$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
237
|
+
- export HOSTNAME_INTERNAL="$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
238
|
+
- export ROOT_URL_INTERNAL="https://$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
239
|
+
- export DEPLOY_CLOUD_RUN_PROJECT_ID="google-project-id"
|
|
240
|
+
- export DEPLOY_CLOUD_RUN_REGION="europe-west6"
|
|
241
|
+
- export GCLOUD_DEPLOY_credentialsKey="$CL_dev_www_GCLOUD_DEPLOY_credentialsKey"
|
|
242
|
+
- export GCLOUD_RUN_canonicalHostSuffix="$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix"
|
|
243
|
+
- export _ALL_ENV_VAR_KEYS="[\\"ENV_SHORT\\",\\"APP_DIR\\",\\"ENV_TYPE\\",\\"BUILD_INFO_BUILD_ID\\",\\"BUILD_INFO_BUILD_TIME\\",\\"BUILD_INFO_CURRENT_VERSION\\",\\"HOSTNAME\\",\\"ROOT_URL\\",\\"HOSTNAME_INTERNAL\\",\\"ROOT_URL_INTERNAL\\",\\"DEPLOY_CLOUD_RUN_PROJECT_ID\\",\\"DEPLOY_CLOUD_RUN_REGION\\",\\"GCLOUD_DEPLOY_credentialsKey\\",\\"GCLOUD_RUN_canonicalHostSuffix\\"]"
|
|
244
|
+
- collapseable_section_end "injectvars"
|
|
245
|
+
- collapseable_section_start "write-dotenv-www" "write dot env for www"
|
|
246
|
+
- |-
|
|
247
|
+
cat <<EOF > www/.env
|
|
248
|
+
ENV_SHORT=dev
|
|
249
|
+
APP_DIR=www
|
|
250
|
+
ENV_TYPE=dev
|
|
251
|
+
HOSTNAME=$(printf %s "$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
252
|
+
ROOT_URL=$(printf %s "https://$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
253
|
+
HOSTNAME_INTERNAL=$(printf %s "$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
254
|
+
ROOT_URL_INTERNAL=$(printf %s "https://$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
255
|
+
DEPLOY_CLOUD_RUN_PROJECT_ID=google-project-id
|
|
256
|
+
DEPLOY_CLOUD_RUN_REGION=europe-west6
|
|
257
|
+
GCLOUD_DEPLOY_credentialsKey=$(printf %s "$CL_dev_www_GCLOUD_DEPLOY_credentialsKey" | escapeForDotEnv)
|
|
258
|
+
GCLOUD_RUN_canonicalHostSuffix=$(printf %s "$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | escapeForDotEnv)
|
|
259
|
+
_ALL_ENV_VAR_KEYS=["ENV_SHORT","APP_DIR","ENV_TYPE","BUILD_INFO_BUILD_ID","BUILD_INFO_BUILD_TIME","BUILD_INFO_CURRENT_VERSION","HOSTNAME","ROOT_URL","HOSTNAME_INTERNAL","ROOT_URL_INTERNAL","DEPLOY_CLOUD_RUN_PROJECT_ID","DEPLOY_CLOUD_RUN_REGION","GCLOUD_DEPLOY_credentialsKey","GCLOUD_RUN_canonicalHostSuffix"]
|
|
260
|
+
EOF
|
|
261
|
+
- collapseable_section_end "write-dotenv-www"
|
|
262
|
+
- echo '{"id":"$(git describe --tags 2>/dev/null || git rev-parse HEAD)","time":"$CI_JOB_STARTED_AT"}' > www/__build_info.json
|
|
263
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
264
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
265
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
266
|
+
- collapseable_section_end "nodeinstall"
|
|
267
|
+
- cd www
|
|
268
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
269
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
270
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
271
|
+
- collapseable_section_end "nodeinstall"
|
|
272
|
+
- collapseable_section_start "yarninstall" "Yarn install"
|
|
273
|
+
- yarn install --immutable
|
|
274
|
+
- collapseable_section_end "yarninstall"
|
|
275
|
+
- yarn build
|
|
276
|
+
cache:
|
|
277
|
+
- key: www-yarn
|
|
278
|
+
policy: pull-push
|
|
279
|
+
paths:
|
|
280
|
+
- www/.yarn
|
|
281
|
+
- key: www-node-modules
|
|
282
|
+
policy: pull-push
|
|
283
|
+
paths:
|
|
284
|
+
- www/node_modules
|
|
285
|
+
- key: www-default
|
|
286
|
+
policy: pull-push
|
|
287
|
+
paths:
|
|
288
|
+
- www/.next/cache
|
|
289
|
+
artifacts:
|
|
290
|
+
paths:
|
|
291
|
+
- www/__build_info.json
|
|
292
|
+
- www/.next
|
|
293
|
+
- www/dist
|
|
294
|
+
exclude:
|
|
295
|
+
- www/.env
|
|
296
|
+
expire_in: 1 day
|
|
297
|
+
when: always
|
|
298
|
+
reports: {}
|
|
299
|
+
rules:
|
|
300
|
+
- when: never
|
|
301
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
302
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
303
|
+
needs: []
|
|
304
|
+
retry: *a1
|
|
305
|
+
interruptible: true
|
|
306
|
+
'www 🔨 docker | dev ':
|
|
307
|
+
stage: build
|
|
308
|
+
image: path/to/docker/docker-build:the-version
|
|
309
|
+
services:
|
|
310
|
+
- name: docker:24.0.6-dind
|
|
311
|
+
command:
|
|
312
|
+
- --tls=false
|
|
313
|
+
- --registry-mirror=https://mirror.gcr.io
|
|
314
|
+
variables:
|
|
315
|
+
DOCKER_HOST: tcp://docker:2375
|
|
316
|
+
DOCKER_TLS_CERTDIR: ''
|
|
317
|
+
DOCKER_DRIVER: overlay2
|
|
318
|
+
DOCKER_BUILDKIT: '1'
|
|
319
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
320
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
321
|
+
KUBERNETES_MEMORY_LIMIT: 2Gi
|
|
322
|
+
script:
|
|
323
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
324
|
+
- export APP_DIR="www"
|
|
325
|
+
- export DOCKER_BUILD_CONTEXT="."
|
|
326
|
+
- export DOCKER_REGISTRY="europe-west6-docker.pkg.dev"
|
|
327
|
+
- export DOCKER_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/dev/www"
|
|
328
|
+
- export DOCKER_CACHE_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www"
|
|
329
|
+
- export DOCKER_IMAGE_TAG="$CI_COMMIT_SHA"
|
|
330
|
+
- |-
|
|
331
|
+
export DOCKER_COPY_AND_INSTALL_APP="COPY --chown=node:node $APP_DIR .
|
|
332
|
+
RUN yarn plugin import workspace-tools
|
|
333
|
+
RUN yarn workspaces focus --production && yarn rebuild"
|
|
334
|
+
- |-
|
|
335
|
+
export DOCKER_COPY_WORKSPACE_FILES="COPY --chown=node:node www/package.json /app/www/package.json
|
|
336
|
+
COPY --chown=node:node www/yarn.lock /app/www/yarn.lock
|
|
337
|
+
COPY --chown=node:node .yarnrc.yml /app/.yarnrc.yml
|
|
338
|
+
COPY --chown=node:node .yarn /app/.yarn"
|
|
339
|
+
- collapseable_section_end "injectvars"
|
|
340
|
+
- ensureNodeDockerfile
|
|
341
|
+
- collapseable_section_start "docker-login" "Docker Login"
|
|
342
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_dev_www_GCLOUD_DEPLOY_credentialsKey")
|
|
343
|
+
- gcloud auth configure-docker europe-west6-docker.pkg.dev
|
|
344
|
+
- collapseable_section_end "docker-login"
|
|
345
|
+
- collapseable_section_start "docker-build" "Docker build"
|
|
346
|
+
- docker build --network host --cache-from $DOCKER_CACHE_IMAGE --tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG -f $APP_DIR/Dockerfile $DOCKER_BUILD_CONTEXT --build-arg BUILDKIT_INLINE_CACHE=1
|
|
347
|
+
- collapseable_section_end "docker-build"
|
|
348
|
+
- collapseable_section_start "docker-push" "Docker push and tag"
|
|
349
|
+
- docker push $DOCKER_IMAGE:$DOCKER_IMAGE_TAG
|
|
350
|
+
- docker tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG $DOCKER_CACHE_IMAGE
|
|
351
|
+
- docker push $DOCKER_CACHE_IMAGE
|
|
352
|
+
- collapseable_section_end "docker-push"
|
|
353
|
+
cache:
|
|
354
|
+
- key: www-yarn
|
|
355
|
+
policy: pull
|
|
356
|
+
paths:
|
|
357
|
+
- www/.yarn
|
|
358
|
+
rules:
|
|
359
|
+
- when: never
|
|
360
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
361
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
362
|
+
needs:
|
|
363
|
+
- 'www 🔨 app | dev '
|
|
364
|
+
retry: *a1
|
|
365
|
+
interruptible: true
|
|
366
|
+
'www 🧾 sbom | dev ':
|
|
367
|
+
stage: build
|
|
368
|
+
image:
|
|
369
|
+
name: aquasec/trivy:0.58.2
|
|
370
|
+
entrypoint:
|
|
371
|
+
- ''
|
|
372
|
+
variables: {}
|
|
373
|
+
script:
|
|
374
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
375
|
+
- collapseable_section_end "injectvars"
|
|
376
|
+
- trivy fs --quiet --format cyclonedx --output "__sbom.json" www
|
|
377
|
+
artifacts:
|
|
378
|
+
paths:
|
|
379
|
+
- __sbom.json
|
|
380
|
+
rules:
|
|
381
|
+
- when: never
|
|
382
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
383
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
384
|
+
needs: []
|
|
385
|
+
retry: *a1
|
|
386
|
+
interruptible: true
|
|
387
|
+
allow_failure: true
|
|
388
|
+
'www 🚀 Deploy | dev ':
|
|
389
|
+
stage: deploy dev
|
|
390
|
+
image: path/to/docker/gcloud:the-version
|
|
391
|
+
variables:
|
|
392
|
+
KUBERNETES_CPU_REQUEST: '0.22'
|
|
393
|
+
KUBERNETES_MEMORY_REQUEST: 200Mi
|
|
394
|
+
KUBERNETES_MEMORY_LIMIT: 400Mi
|
|
395
|
+
script:
|
|
396
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
397
|
+
- export ENV_SHORT="dev"
|
|
398
|
+
- export APP_DIR="www"
|
|
399
|
+
- export ENV_TYPE="dev"
|
|
400
|
+
- export BUILD_INFO_BUILD_ID="$(git describe --tags 2>/dev/null || git rev-parse HEAD)"
|
|
401
|
+
- export BUILD_INFO_BUILD_TIME="$CI_JOB_STARTED_AT"
|
|
402
|
+
- export BUILD_INFO_CURRENT_VERSION="$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")"
|
|
403
|
+
- export HOSTNAME="$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
404
|
+
- export ROOT_URL="https://$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
405
|
+
- export HOSTNAME_INTERNAL="$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
406
|
+
- export ROOT_URL_INTERNAL="https://$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
407
|
+
- export DEPLOY_CLOUD_RUN_PROJECT_ID="google-project-id"
|
|
408
|
+
- export DEPLOY_CLOUD_RUN_REGION="europe-west6"
|
|
409
|
+
- export GCLOUD_DEPLOY_credentialsKey="$CL_dev_www_GCLOUD_DEPLOY_credentialsKey"
|
|
410
|
+
- export GCLOUD_RUN_canonicalHostSuffix="$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix"
|
|
411
|
+
- export _ALL_ENV_VAR_KEYS="[\\"ENV_SHORT\\",\\"APP_DIR\\",\\"ENV_TYPE\\",\\"BUILD_INFO_BUILD_ID\\",\\"BUILD_INFO_BUILD_TIME\\",\\"BUILD_INFO_CURRENT_VERSION\\",\\"HOSTNAME\\",\\"ROOT_URL\\",\\"HOSTNAME_INTERNAL\\",\\"ROOT_URL_INTERNAL\\",\\"DEPLOY_CLOUD_RUN_PROJECT_ID\\",\\"DEPLOY_CLOUD_RUN_REGION\\",\\"GCLOUD_DEPLOY_credentialsKey\\",\\"GCLOUD_RUN_canonicalHostSuffix\\"]"
|
|
412
|
+
- export DOCKER_REGISTRY="europe-west6-docker.pkg.dev"
|
|
413
|
+
- export DOCKER_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/dev/www"
|
|
414
|
+
- export DOCKER_CACHE_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www"
|
|
415
|
+
- export DOCKER_IMAGE_TAG="$CI_COMMIT_SHA"
|
|
416
|
+
- export CLOUDSDK_CORE_DISABLE_PROMPTS="1"
|
|
417
|
+
- collapseable_section_end "injectvars"
|
|
418
|
+
- collapseable_section_start "prepare" "Prepare..."
|
|
419
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_dev_www_GCLOUD_DEPLOY_credentialsKey")
|
|
420
|
+
- export GCLOUD_PROJECT_NUMBER=$(gcloud projects describe google-project-id --format="value(projectNumber)")
|
|
421
|
+
- 'echo "GCLOUD_PROJECT_NUMBER: $GCLOUD_PROJECT_NUMBER"'
|
|
422
|
+
- collapseable_section_end "prepare"
|
|
423
|
+
- collapseable_section_start "writeenvvars" "Write env vars to file"
|
|
424
|
+
- |
|
|
425
|
+
cat > ____envvars.yaml <<EOF
|
|
426
|
+
ENV_SHORT: |-
|
|
427
|
+
dev
|
|
428
|
+
APP_DIR: |-
|
|
429
|
+
www
|
|
430
|
+
ENV_TYPE: |-
|
|
431
|
+
dev
|
|
432
|
+
BUILD_INFO_BUILD_ID: |-
|
|
433
|
+
$(printf %s "$(git describe --tags 2>/dev/null || git rev-parse HEAD)" | sed '1!s/^/ /')
|
|
434
|
+
BUILD_INFO_BUILD_TIME: |-
|
|
435
|
+
$(printf %s "$CI_JOB_STARTED_AT" | sed '1!s/^/ /')
|
|
436
|
+
BUILD_INFO_CURRENT_VERSION: |-
|
|
437
|
+
$(printf %s "$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")" | sed '1!s/^/ /')
|
|
438
|
+
HOSTNAME: |-
|
|
439
|
+
$(printf %s "$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
440
|
+
ROOT_URL: |-
|
|
441
|
+
$(printf %s "https://$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
442
|
+
HOSTNAME_INTERNAL: |-
|
|
443
|
+
$(printf %s "$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
444
|
+
ROOT_URL_INTERNAL: |-
|
|
445
|
+
$(printf %s "https://$(printf %s "pan-agent-example-app-dev-www-$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
446
|
+
DEPLOY_CLOUD_RUN_PROJECT_ID: |-
|
|
447
|
+
google-project-id
|
|
448
|
+
DEPLOY_CLOUD_RUN_REGION: |-
|
|
449
|
+
europe-west6
|
|
450
|
+
GCLOUD_RUN_canonicalHostSuffix: |-
|
|
451
|
+
$(printf %s "$CL_dev_www_GCLOUD_RUN_canonicalHostSuffix" | sed '1!s/^/ /')
|
|
452
|
+
_ALL_ENV_VAR_KEYS: |-
|
|
453
|
+
["ENV_SHORT","APP_DIR","ENV_TYPE","BUILD_INFO_BUILD_ID","BUILD_INFO_BUILD_TIME","BUILD_INFO_CURRENT_VERSION","HOSTNAME","ROOT_URL","HOSTNAME_INTERNAL","ROOT_URL_INTERNAL","DEPLOY_CLOUD_RUN_PROJECT_ID","DEPLOY_CLOUD_RUN_REGION","GCLOUD_DEPLOY_credentialsKey","GCLOUD_RUN_canonicalHostSuffix"]
|
|
454
|
+
|
|
455
|
+
EOF
|
|
456
|
+
- collapseable_section_end "writeenvvars"
|
|
457
|
+
- collapseable_section_start "deploy" "Deploy to cloud run"
|
|
458
|
+
- gcloud run deploy pan-agent-example-app-dev-www --command="yarn,start" --image=europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/dev/www:$DOCKER_IMAGE_TAG --project=google-project-id --region=europe-west6 --labels=customer-name=pan,component-name=www,app-name=agent-example-app,env-type=dev,env-name=dev,build-type=node,cloud-run-service-name=pan-agent-example-app-dev-www --env-vars-file=____envvars.yaml --min-instances=0 --max-instances=100 --cpu-throttling --allow-unauthenticated --ingress=all --cpu-boost
|
|
459
|
+
- collapseable_section_end "deploy"
|
|
460
|
+
- collapseable_section_start "cleanup" "Cleanup"
|
|
461
|
+
- set +e
|
|
462
|
+
- gcloud run revisions list --project=google-project-id --region=europe-west6 --service=pan-agent-example-app-dev-www --limit=unlimited --sort-by=metadata.creationTimestamp --format="value(name)" --filter='(status.conditions.status=False OR status.conditions.status=Unknown)' | while read -r revisionname; do gcloud run revisions delete --project=google-project-id --region=europe-west6 --quiet $revisionname ; done
|
|
463
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/dev/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/dev/www@$version --quiet --delete-tags; done
|
|
464
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www@$version --quiet --delete-tags; done
|
|
465
|
+
- set -e
|
|
466
|
+
- collapseable_section_end "cleanup"
|
|
467
|
+
- echo 'Uploading SBOM to Dependency Track'
|
|
468
|
+
- /dtrackuploader https://dep.panter.swiss/ "$DT_KEY_PROD" upload "pan-agent-example-app/www" "$ROOT_URL" "__sbom.json" vex.json || true
|
|
469
|
+
- echo "CL_GITLAB_ENVIRONMENT_URL=$ROOT_URL" >> gitlab_environment.env
|
|
470
|
+
environment:
|
|
471
|
+
name: dev/www
|
|
472
|
+
url: $CL_GITLAB_ENVIRONMENT_URL
|
|
473
|
+
on_stop: 'www 🛑 Stop ⚠️ | dev '
|
|
474
|
+
auto_stop_in: 4 weeks
|
|
475
|
+
artifacts:
|
|
476
|
+
reports:
|
|
477
|
+
dotenv: gitlab_environment.env
|
|
478
|
+
rules:
|
|
479
|
+
- when: never
|
|
480
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
481
|
+
- when: on_success
|
|
482
|
+
if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
483
|
+
needs:
|
|
484
|
+
- job: www 👮 lint
|
|
485
|
+
artifacts: false
|
|
486
|
+
- job: 'www 🔨 app | dev '
|
|
487
|
+
artifacts: false
|
|
488
|
+
- job: 'www 🔨 docker | dev '
|
|
489
|
+
artifacts: false
|
|
490
|
+
- job: www 🧪 test
|
|
491
|
+
artifacts: false
|
|
492
|
+
- job: 'www 🧾 sbom | dev '
|
|
493
|
+
artifacts: true
|
|
494
|
+
- job: www 🛡 audit
|
|
495
|
+
artifacts: false
|
|
496
|
+
retry: *a1
|
|
497
|
+
interruptible: true
|
|
498
|
+
allow_failure: false
|
|
499
|
+
'www 🛑 Stop ⚠️ | dev ':
|
|
500
|
+
stage: stop dev
|
|
501
|
+
image: path/to/docker/gcloud:the-version
|
|
502
|
+
variables:
|
|
503
|
+
KUBERNETES_CPU_REQUEST: '0.22'
|
|
504
|
+
KUBERNETES_MEMORY_REQUEST: 200Mi
|
|
505
|
+
KUBERNETES_MEMORY_LIMIT: 400Mi
|
|
506
|
+
GIT_STRATEGY: none
|
|
507
|
+
script:
|
|
508
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
509
|
+
- export CLOUDSDK_CORE_DISABLE_PROMPTS="1"
|
|
510
|
+
- collapseable_section_end "injectvars"
|
|
511
|
+
- set +e
|
|
512
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_dev_www_GCLOUD_DEPLOY_credentialsKey")
|
|
513
|
+
- gcloud run services delete pan-agent-example-app-dev-www --project=google-project-id --region=europe-west6
|
|
514
|
+
- gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/dev/www --quiet --delete-tags
|
|
515
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www@$version --quiet --delete-tags; done
|
|
516
|
+
- echo 'Disabling component in Dependency Track'
|
|
517
|
+
- /dtrackuploader https://dep.panter.swiss/ "$DT_KEY_PROD" disable "pan-agent-example-app/www" "$CI_ENVIRONMENT_URL" || true
|
|
518
|
+
- set -e
|
|
519
|
+
environment:
|
|
520
|
+
name: dev/www
|
|
521
|
+
action: stop
|
|
522
|
+
rules:
|
|
523
|
+
- if: $CI_COMMIT_BRANCH =~ /^[0-9]+\\.([0-9]+|x)\\.x$/
|
|
524
|
+
when: on_success
|
|
525
|
+
- when: never
|
|
526
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
527
|
+
- when: manual
|
|
528
|
+
if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
529
|
+
needs: []
|
|
530
|
+
retry: *a1
|
|
531
|
+
interruptible: true
|
|
532
|
+
allow_failure: true
|
|
533
|
+
'www 🔨 app | review ':
|
|
534
|
+
stage: build
|
|
535
|
+
image: path/to/docker/jobs-default:the-version
|
|
536
|
+
variables:
|
|
537
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
538
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
539
|
+
KUBERNETES_MEMORY_LIMIT: 4Gi
|
|
540
|
+
script:
|
|
541
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
542
|
+
- export ENV_SHORT="review"
|
|
543
|
+
- export APP_DIR="www"
|
|
544
|
+
- export ENV_TYPE="review"
|
|
545
|
+
- export BUILD_INFO_BUILD_ID="$(git describe --tags 2>/dev/null || git rev-parse HEAD)"
|
|
546
|
+
- export BUILD_INFO_BUILD_TIME="$CI_JOB_STARTED_AT"
|
|
547
|
+
- export BUILD_INFO_CURRENT_VERSION="$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")"
|
|
548
|
+
- export HOSTNAME="$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
549
|
+
- export ROOT_URL="https://$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
550
|
+
- export HOSTNAME_INTERNAL="$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
551
|
+
- export ROOT_URL_INTERNAL="https://$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
552
|
+
- export DEPLOY_CLOUD_RUN_PROJECT_ID="google-project-id"
|
|
553
|
+
- export DEPLOY_CLOUD_RUN_REGION="europe-west6"
|
|
554
|
+
- export GCLOUD_DEPLOY_credentialsKey="$CL_review_www_GCLOUD_DEPLOY_credentialsKey"
|
|
555
|
+
- export GCLOUD_RUN_canonicalHostSuffix="$CL_review_www_GCLOUD_RUN_canonicalHostSuffix"
|
|
556
|
+
- export _ALL_ENV_VAR_KEYS="[\\"ENV_SHORT\\",\\"APP_DIR\\",\\"ENV_TYPE\\",\\"BUILD_INFO_BUILD_ID\\",\\"BUILD_INFO_BUILD_TIME\\",\\"BUILD_INFO_CURRENT_VERSION\\",\\"HOSTNAME\\",\\"ROOT_URL\\",\\"HOSTNAME_INTERNAL\\",\\"ROOT_URL_INTERNAL\\",\\"DEPLOY_CLOUD_RUN_PROJECT_ID\\",\\"DEPLOY_CLOUD_RUN_REGION\\",\\"GCLOUD_DEPLOY_credentialsKey\\",\\"GCLOUD_RUN_canonicalHostSuffix\\"]"
|
|
557
|
+
- collapseable_section_end "injectvars"
|
|
558
|
+
- collapseable_section_start "write-dotenv-www" "write dot env for www"
|
|
559
|
+
- |-
|
|
560
|
+
cat <<EOF > www/.env
|
|
561
|
+
ENV_SHORT=review
|
|
562
|
+
APP_DIR=www
|
|
563
|
+
ENV_TYPE=review
|
|
564
|
+
HOSTNAME=$(printf %s "$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
565
|
+
ROOT_URL=$(printf %s "https://$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
566
|
+
HOSTNAME_INTERNAL=$(printf %s "$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
567
|
+
ROOT_URL_INTERNAL=$(printf %s "https://$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
568
|
+
DEPLOY_CLOUD_RUN_PROJECT_ID=google-project-id
|
|
569
|
+
DEPLOY_CLOUD_RUN_REGION=europe-west6
|
|
570
|
+
GCLOUD_DEPLOY_credentialsKey=$(printf %s "$CL_review_www_GCLOUD_DEPLOY_credentialsKey" | escapeForDotEnv)
|
|
571
|
+
GCLOUD_RUN_canonicalHostSuffix=$(printf %s "$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | escapeForDotEnv)
|
|
572
|
+
_ALL_ENV_VAR_KEYS=["ENV_SHORT","APP_DIR","ENV_TYPE","BUILD_INFO_BUILD_ID","BUILD_INFO_BUILD_TIME","BUILD_INFO_CURRENT_VERSION","HOSTNAME","ROOT_URL","HOSTNAME_INTERNAL","ROOT_URL_INTERNAL","DEPLOY_CLOUD_RUN_PROJECT_ID","DEPLOY_CLOUD_RUN_REGION","GCLOUD_DEPLOY_credentialsKey","GCLOUD_RUN_canonicalHostSuffix"]
|
|
573
|
+
EOF
|
|
574
|
+
- collapseable_section_end "write-dotenv-www"
|
|
575
|
+
- echo '{"id":"$(git describe --tags 2>/dev/null || git rev-parse HEAD)","time":"$CI_JOB_STARTED_AT"}' > www/__build_info.json
|
|
576
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
577
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
578
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
579
|
+
- collapseable_section_end "nodeinstall"
|
|
580
|
+
- cd www
|
|
581
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
582
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
583
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
584
|
+
- collapseable_section_end "nodeinstall"
|
|
585
|
+
- collapseable_section_start "yarninstall" "Yarn install"
|
|
586
|
+
- yarn install --immutable
|
|
587
|
+
- collapseable_section_end "yarninstall"
|
|
588
|
+
- yarn build
|
|
589
|
+
cache:
|
|
590
|
+
- key: www-yarn
|
|
591
|
+
policy: pull-push
|
|
592
|
+
paths:
|
|
593
|
+
- www/.yarn
|
|
594
|
+
- key: www-node-modules
|
|
595
|
+
policy: pull-push
|
|
596
|
+
paths:
|
|
597
|
+
- www/node_modules
|
|
598
|
+
- key: www-default
|
|
599
|
+
policy: pull-push
|
|
600
|
+
paths:
|
|
601
|
+
- www/.next/cache
|
|
602
|
+
artifacts:
|
|
603
|
+
paths:
|
|
604
|
+
- www/__build_info.json
|
|
605
|
+
- www/.next
|
|
606
|
+
- www/dist
|
|
607
|
+
exclude:
|
|
608
|
+
- www/.env
|
|
609
|
+
expire_in: 1 day
|
|
610
|
+
when: always
|
|
611
|
+
reports: {}
|
|
612
|
+
rules:
|
|
613
|
+
- when: never
|
|
614
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
615
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
616
|
+
needs: []
|
|
617
|
+
retry: *a1
|
|
618
|
+
interruptible: true
|
|
619
|
+
'www 🔨 docker | review ':
|
|
620
|
+
stage: build
|
|
621
|
+
image: path/to/docker/docker-build:the-version
|
|
622
|
+
services:
|
|
623
|
+
- name: docker:24.0.6-dind
|
|
624
|
+
command:
|
|
625
|
+
- --tls=false
|
|
626
|
+
- --registry-mirror=https://mirror.gcr.io
|
|
627
|
+
variables:
|
|
628
|
+
DOCKER_HOST: tcp://docker:2375
|
|
629
|
+
DOCKER_TLS_CERTDIR: ''
|
|
630
|
+
DOCKER_DRIVER: overlay2
|
|
631
|
+
DOCKER_BUILDKIT: '1'
|
|
632
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
633
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
634
|
+
KUBERNETES_MEMORY_LIMIT: 2Gi
|
|
635
|
+
script:
|
|
636
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
637
|
+
- export APP_DIR="www"
|
|
638
|
+
- export DOCKER_BUILD_CONTEXT="."
|
|
639
|
+
- export DOCKER_REGISTRY="europe-west6-docker.pkg.dev"
|
|
640
|
+
- export DOCKER_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/review/www/$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })"
|
|
641
|
+
- export DOCKER_CACHE_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www"
|
|
642
|
+
- export DOCKER_IMAGE_TAG="$CI_COMMIT_SHA"
|
|
643
|
+
- |-
|
|
644
|
+
export DOCKER_COPY_AND_INSTALL_APP="COPY --chown=node:node $APP_DIR .
|
|
645
|
+
RUN yarn plugin import workspace-tools
|
|
646
|
+
RUN yarn workspaces focus --production && yarn rebuild"
|
|
647
|
+
- |-
|
|
648
|
+
export DOCKER_COPY_WORKSPACE_FILES="COPY --chown=node:node www/package.json /app/www/package.json
|
|
649
|
+
COPY --chown=node:node www/yarn.lock /app/www/yarn.lock
|
|
650
|
+
COPY --chown=node:node .yarnrc.yml /app/.yarnrc.yml
|
|
651
|
+
COPY --chown=node:node .yarn /app/.yarn"
|
|
652
|
+
- collapseable_section_end "injectvars"
|
|
653
|
+
- ensureNodeDockerfile
|
|
654
|
+
- collapseable_section_start "docker-login" "Docker Login"
|
|
655
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_review_www_GCLOUD_DEPLOY_credentialsKey")
|
|
656
|
+
- gcloud auth configure-docker europe-west6-docker.pkg.dev
|
|
657
|
+
- collapseable_section_end "docker-login"
|
|
658
|
+
- collapseable_section_start "docker-build" "Docker build"
|
|
659
|
+
- docker build --network host --cache-from $DOCKER_CACHE_IMAGE --tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG -f $APP_DIR/Dockerfile $DOCKER_BUILD_CONTEXT --build-arg BUILDKIT_INLINE_CACHE=1
|
|
660
|
+
- collapseable_section_end "docker-build"
|
|
661
|
+
- collapseable_section_start "docker-push" "Docker push and tag"
|
|
662
|
+
- docker push $DOCKER_IMAGE:$DOCKER_IMAGE_TAG
|
|
663
|
+
- docker tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG $DOCKER_CACHE_IMAGE
|
|
664
|
+
- docker push $DOCKER_CACHE_IMAGE
|
|
665
|
+
- collapseable_section_end "docker-push"
|
|
666
|
+
cache:
|
|
667
|
+
- key: www-yarn
|
|
668
|
+
policy: pull
|
|
669
|
+
paths:
|
|
670
|
+
- www/.yarn
|
|
671
|
+
rules:
|
|
672
|
+
- when: never
|
|
673
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
674
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
675
|
+
needs:
|
|
676
|
+
- 'www 🔨 app | review '
|
|
677
|
+
retry: *a1
|
|
678
|
+
interruptible: true
|
|
679
|
+
'www 🧾 sbom | review ':
|
|
680
|
+
stage: build
|
|
681
|
+
image:
|
|
682
|
+
name: aquasec/trivy:0.58.2
|
|
683
|
+
entrypoint:
|
|
684
|
+
- ''
|
|
685
|
+
variables: {}
|
|
686
|
+
script:
|
|
687
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
688
|
+
- collapseable_section_end "injectvars"
|
|
689
|
+
- trivy fs --quiet --format cyclonedx --output "__sbom.json" www
|
|
690
|
+
artifacts:
|
|
691
|
+
paths:
|
|
692
|
+
- __sbom.json
|
|
693
|
+
rules:
|
|
694
|
+
- when: never
|
|
695
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
696
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
697
|
+
needs: []
|
|
698
|
+
retry: *a1
|
|
699
|
+
interruptible: true
|
|
700
|
+
allow_failure: true
|
|
701
|
+
'www 🚀 Deploy | review ':
|
|
702
|
+
stage: deploy review
|
|
703
|
+
image: path/to/docker/gcloud:the-version
|
|
704
|
+
variables:
|
|
705
|
+
KUBERNETES_CPU_REQUEST: '0.22'
|
|
706
|
+
KUBERNETES_MEMORY_REQUEST: 200Mi
|
|
707
|
+
KUBERNETES_MEMORY_LIMIT: 400Mi
|
|
708
|
+
script:
|
|
709
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
710
|
+
- export ENV_SHORT="review"
|
|
711
|
+
- export APP_DIR="www"
|
|
712
|
+
- export ENV_TYPE="review"
|
|
713
|
+
- export BUILD_INFO_BUILD_ID="$(git describe --tags 2>/dev/null || git rev-parse HEAD)"
|
|
714
|
+
- export BUILD_INFO_BUILD_TIME="$CI_JOB_STARTED_AT"
|
|
715
|
+
- export BUILD_INFO_CURRENT_VERSION="$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")"
|
|
716
|
+
- export HOSTNAME="$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
717
|
+
- export ROOT_URL="https://$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
718
|
+
- export HOSTNAME_INTERNAL="$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
719
|
+
- export ROOT_URL_INTERNAL="https://$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
720
|
+
- export DEPLOY_CLOUD_RUN_PROJECT_ID="google-project-id"
|
|
721
|
+
- export DEPLOY_CLOUD_RUN_REGION="europe-west6"
|
|
722
|
+
- export GCLOUD_DEPLOY_credentialsKey="$CL_review_www_GCLOUD_DEPLOY_credentialsKey"
|
|
723
|
+
- export GCLOUD_RUN_canonicalHostSuffix="$CL_review_www_GCLOUD_RUN_canonicalHostSuffix"
|
|
724
|
+
- export _ALL_ENV_VAR_KEYS="[\\"ENV_SHORT\\",\\"APP_DIR\\",\\"ENV_TYPE\\",\\"BUILD_INFO_BUILD_ID\\",\\"BUILD_INFO_BUILD_TIME\\",\\"BUILD_INFO_CURRENT_VERSION\\",\\"HOSTNAME\\",\\"ROOT_URL\\",\\"HOSTNAME_INTERNAL\\",\\"ROOT_URL_INTERNAL\\",\\"DEPLOY_CLOUD_RUN_PROJECT_ID\\",\\"DEPLOY_CLOUD_RUN_REGION\\",\\"GCLOUD_DEPLOY_credentialsKey\\",\\"GCLOUD_RUN_canonicalHostSuffix\\"]"
|
|
725
|
+
- export DOCKER_REGISTRY="europe-west6-docker.pkg.dev"
|
|
726
|
+
- export DOCKER_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/review/www/$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })"
|
|
727
|
+
- export DOCKER_CACHE_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www"
|
|
728
|
+
- export DOCKER_IMAGE_TAG="$CI_COMMIT_SHA"
|
|
729
|
+
- export CLOUDSDK_CORE_DISABLE_PROMPTS="1"
|
|
730
|
+
- collapseable_section_end "injectvars"
|
|
731
|
+
- collapseable_section_start "prepare" "Prepare..."
|
|
732
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_review_www_GCLOUD_DEPLOY_credentialsKey")
|
|
733
|
+
- export GCLOUD_PROJECT_NUMBER=$(gcloud projects describe google-project-id --format="value(projectNumber)")
|
|
734
|
+
- 'echo "GCLOUD_PROJECT_NUMBER: $GCLOUD_PROJECT_NUMBER"'
|
|
735
|
+
- collapseable_section_end "prepare"
|
|
736
|
+
- collapseable_section_start "writeenvvars" "Write env vars to file"
|
|
737
|
+
- |
|
|
738
|
+
cat > ____envvars.yaml <<EOF
|
|
739
|
+
ENV_SHORT: |-
|
|
740
|
+
review
|
|
741
|
+
APP_DIR: |-
|
|
742
|
+
www
|
|
743
|
+
ENV_TYPE: |-
|
|
744
|
+
review
|
|
745
|
+
BUILD_INFO_BUILD_ID: |-
|
|
746
|
+
$(printf %s "$(git describe --tags 2>/dev/null || git rev-parse HEAD)" | sed '1!s/^/ /')
|
|
747
|
+
BUILD_INFO_BUILD_TIME: |-
|
|
748
|
+
$(printf %s "$CI_JOB_STARTED_AT" | sed '1!s/^/ /')
|
|
749
|
+
BUILD_INFO_CURRENT_VERSION: |-
|
|
750
|
+
$(printf %s "$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")" | sed '1!s/^/ /')
|
|
751
|
+
HOSTNAME: |-
|
|
752
|
+
$(printf %s "$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
753
|
+
ROOT_URL: |-
|
|
754
|
+
$(printf %s "https://$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
755
|
+
HOSTNAME_INTERNAL: |-
|
|
756
|
+
$(printf %s "$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
757
|
+
ROOT_URL_INTERNAL: |-
|
|
758
|
+
$(printf %s "https://$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www-$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
759
|
+
DEPLOY_CLOUD_RUN_PROJECT_ID: |-
|
|
760
|
+
google-project-id
|
|
761
|
+
DEPLOY_CLOUD_RUN_REGION: |-
|
|
762
|
+
europe-west6
|
|
763
|
+
GCLOUD_RUN_canonicalHostSuffix: |-
|
|
764
|
+
$(printf %s "$CL_review_www_GCLOUD_RUN_canonicalHostSuffix" | sed '1!s/^/ /')
|
|
765
|
+
_ALL_ENV_VAR_KEYS: |-
|
|
766
|
+
["ENV_SHORT","APP_DIR","ENV_TYPE","BUILD_INFO_BUILD_ID","BUILD_INFO_BUILD_TIME","BUILD_INFO_CURRENT_VERSION","HOSTNAME","ROOT_URL","HOSTNAME_INTERNAL","ROOT_URL_INTERNAL","DEPLOY_CLOUD_RUN_PROJECT_ID","DEPLOY_CLOUD_RUN_REGION","GCLOUD_DEPLOY_credentialsKey","GCLOUD_RUN_canonicalHostSuffix"]
|
|
767
|
+
|
|
768
|
+
EOF
|
|
769
|
+
- collapseable_section_end "writeenvvars"
|
|
770
|
+
- collapseable_section_start "deploy" "Deploy to cloud run"
|
|
771
|
+
- gcloud run deploy $(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www" | awk '{print tolower($0)}') --command="yarn,start" --image=europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/review/www/$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; }):$DOCKER_IMAGE_TAG --project=google-project-id --region=europe-west6 --labels=customer-name=pan,component-name=www,app-name=agent-example-app,env-type=review,env-name=review,build-type=node,cloud-run-service-name=$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www" | awk '{print tolower($0)}') --env-vars-file=____envvars.yaml --min-instances=0 --max-instances=100 --cpu-throttling --allow-unauthenticated --ingress=all --cpu-boost
|
|
772
|
+
- collapseable_section_end "deploy"
|
|
773
|
+
- collapseable_section_start "cleanup" "Cleanup"
|
|
774
|
+
- set +e
|
|
775
|
+
- gcloud run revisions list --project=google-project-id --region=europe-west6 --service=$(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www" | awk '{print tolower($0)}') --limit=unlimited --sort-by=metadata.creationTimestamp --format="value(name)" --filter='(status.conditions.status=False OR status.conditions.status=Unknown)' | while read -r revisionname; do gcloud run revisions delete --project=google-project-id --region=europe-west6 --quiet $revisionname ; done
|
|
776
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/review/www/$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; }) --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/review/www/$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })@$version --quiet --delete-tags; done
|
|
777
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www@$version --quiet --delete-tags; done
|
|
778
|
+
- set +e
|
|
779
|
+
- gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/review/www --quiet --delete-tags
|
|
780
|
+
- set -e
|
|
781
|
+
- set -e
|
|
782
|
+
- collapseable_section_end "cleanup"
|
|
783
|
+
- echo 'Uploading SBOM to Dependency Track'
|
|
784
|
+
- /dtrackuploader https://dep.panter.swiss/ "$DT_KEY_PROD" upload "pan-agent-example-app/www" "$ROOT_URL" "__sbom.json" vex.json || true
|
|
785
|
+
- echo "CL_GITLAB_ENVIRONMENT_URL=$ROOT_URL" >> gitlab_environment.env
|
|
786
|
+
environment:
|
|
787
|
+
name: review/$CI_COMMIT_REF_NAME/www
|
|
788
|
+
url: $CL_GITLAB_ENVIRONMENT_URL
|
|
789
|
+
on_stop: 'www 🛑 Stop ⚠️ | review '
|
|
790
|
+
auto_stop_in: 1 week
|
|
791
|
+
artifacts:
|
|
792
|
+
reports:
|
|
793
|
+
dotenv: gitlab_environment.env
|
|
794
|
+
rules:
|
|
795
|
+
- when: never
|
|
796
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
797
|
+
- when: on_success
|
|
798
|
+
if: $CI_MERGE_REQUEST_ID
|
|
799
|
+
needs:
|
|
800
|
+
- job: www 👮 lint
|
|
801
|
+
artifacts: false
|
|
802
|
+
- job: 'www 🔨 app | review '
|
|
803
|
+
artifacts: false
|
|
804
|
+
- job: 'www 🔨 docker | review '
|
|
805
|
+
artifacts: false
|
|
806
|
+
- job: www 🧪 test
|
|
807
|
+
artifacts: false
|
|
808
|
+
- job: 'www 🧾 sbom | review '
|
|
809
|
+
artifacts: true
|
|
810
|
+
- job: www 🛡 audit
|
|
811
|
+
artifacts: false
|
|
812
|
+
retry: *a1
|
|
813
|
+
interruptible: true
|
|
814
|
+
allow_failure: false
|
|
815
|
+
'www 🛑 Stop ⚠️ | review ':
|
|
816
|
+
stage: stop review
|
|
817
|
+
image: path/to/docker/gcloud:the-version
|
|
818
|
+
variables:
|
|
819
|
+
KUBERNETES_CPU_REQUEST: '0.22'
|
|
820
|
+
KUBERNETES_MEMORY_REQUEST: 200Mi
|
|
821
|
+
KUBERNETES_MEMORY_LIMIT: 400Mi
|
|
822
|
+
GIT_STRATEGY: none
|
|
823
|
+
script:
|
|
824
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
825
|
+
- export CLOUDSDK_CORE_DISABLE_PROMPTS="1"
|
|
826
|
+
- collapseable_section_end "injectvars"
|
|
827
|
+
- set +e
|
|
828
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_review_www_GCLOUD_DEPLOY_credentialsKey")
|
|
829
|
+
- gcloud run services delete $(printf %s "pan-agent-example-app-review-$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })-www" | awk '{print tolower($0)}') --project=google-project-id --region=europe-west6
|
|
830
|
+
- gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/review/www/$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; }) --quiet --delete-tags
|
|
831
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www@$version --quiet --delete-tags; done
|
|
832
|
+
- set +e
|
|
833
|
+
- gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/review/www --quiet --delete-tags
|
|
834
|
+
- set -e
|
|
835
|
+
- echo 'Disabling component in Dependency Track'
|
|
836
|
+
- /dtrackuploader https://dep.panter.swiss/ "$DT_KEY_PROD" disable "pan-agent-example-app/www" "$CI_ENVIRONMENT_URL" || true
|
|
837
|
+
- set -e
|
|
838
|
+
environment:
|
|
839
|
+
name: review/$CI_COMMIT_REF_NAME/www
|
|
840
|
+
action: stop
|
|
841
|
+
rules:
|
|
842
|
+
- if: $CI_COMMIT_BRANCH =~ /^[0-9]+\\.([0-9]+|x)\\.x$/
|
|
843
|
+
when: on_success
|
|
844
|
+
- when: never
|
|
845
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
846
|
+
- when: manual
|
|
847
|
+
if: $CI_MERGE_REQUEST_ID
|
|
848
|
+
needs: []
|
|
849
|
+
retry: *a1
|
|
850
|
+
interruptible: true
|
|
851
|
+
allow_failure: true
|
|
852
|
+
'www 🔨 app | stage ':
|
|
853
|
+
stage: build
|
|
854
|
+
image: path/to/docker/jobs-default:the-version
|
|
855
|
+
variables:
|
|
856
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
857
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
858
|
+
KUBERNETES_MEMORY_LIMIT: 4Gi
|
|
859
|
+
script:
|
|
860
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
861
|
+
- export ENV_SHORT="stage"
|
|
862
|
+
- export APP_DIR="www"
|
|
863
|
+
- export ENV_TYPE="stage"
|
|
864
|
+
- export BUILD_INFO_BUILD_ID="$(git describe --tags 2>/dev/null || git rev-parse HEAD)"
|
|
865
|
+
- export BUILD_INFO_BUILD_TIME="$CI_JOB_STARTED_AT"
|
|
866
|
+
- export BUILD_INFO_CURRENT_VERSION="$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")"
|
|
867
|
+
- export HOSTNAME="$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
868
|
+
- export ROOT_URL="https://$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
869
|
+
- export HOSTNAME_INTERNAL="$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
870
|
+
- export ROOT_URL_INTERNAL="https://$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
871
|
+
- export DEPLOY_CLOUD_RUN_PROJECT_ID="google-project-id"
|
|
872
|
+
- export DEPLOY_CLOUD_RUN_REGION="europe-west6"
|
|
873
|
+
- export GCLOUD_DEPLOY_credentialsKey="$CL_stage_www_GCLOUD_DEPLOY_credentialsKey"
|
|
874
|
+
- export GCLOUD_RUN_canonicalHostSuffix="$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix"
|
|
875
|
+
- export _ALL_ENV_VAR_KEYS="[\\"ENV_SHORT\\",\\"APP_DIR\\",\\"ENV_TYPE\\",\\"BUILD_INFO_BUILD_ID\\",\\"BUILD_INFO_BUILD_TIME\\",\\"BUILD_INFO_CURRENT_VERSION\\",\\"HOSTNAME\\",\\"ROOT_URL\\",\\"HOSTNAME_INTERNAL\\",\\"ROOT_URL_INTERNAL\\",\\"DEPLOY_CLOUD_RUN_PROJECT_ID\\",\\"DEPLOY_CLOUD_RUN_REGION\\",\\"GCLOUD_DEPLOY_credentialsKey\\",\\"GCLOUD_RUN_canonicalHostSuffix\\"]"
|
|
876
|
+
- collapseable_section_end "injectvars"
|
|
877
|
+
- collapseable_section_start "write-dotenv-www" "write dot env for www"
|
|
878
|
+
- |-
|
|
879
|
+
cat <<EOF > www/.env
|
|
880
|
+
ENV_SHORT=stage
|
|
881
|
+
APP_DIR=www
|
|
882
|
+
ENV_TYPE=stage
|
|
883
|
+
HOSTNAME=$(printf %s "$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
884
|
+
ROOT_URL=$(printf %s "https://$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
885
|
+
HOSTNAME_INTERNAL=$(printf %s "$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
886
|
+
ROOT_URL_INTERNAL=$(printf %s "https://$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
887
|
+
DEPLOY_CLOUD_RUN_PROJECT_ID=google-project-id
|
|
888
|
+
DEPLOY_CLOUD_RUN_REGION=europe-west6
|
|
889
|
+
GCLOUD_DEPLOY_credentialsKey=$(printf %s "$CL_stage_www_GCLOUD_DEPLOY_credentialsKey" | escapeForDotEnv)
|
|
890
|
+
GCLOUD_RUN_canonicalHostSuffix=$(printf %s "$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | escapeForDotEnv)
|
|
891
|
+
_ALL_ENV_VAR_KEYS=["ENV_SHORT","APP_DIR","ENV_TYPE","BUILD_INFO_BUILD_ID","BUILD_INFO_BUILD_TIME","BUILD_INFO_CURRENT_VERSION","HOSTNAME","ROOT_URL","HOSTNAME_INTERNAL","ROOT_URL_INTERNAL","DEPLOY_CLOUD_RUN_PROJECT_ID","DEPLOY_CLOUD_RUN_REGION","GCLOUD_DEPLOY_credentialsKey","GCLOUD_RUN_canonicalHostSuffix"]
|
|
892
|
+
EOF
|
|
893
|
+
- collapseable_section_end "write-dotenv-www"
|
|
894
|
+
- echo '{"id":"$(git describe --tags 2>/dev/null || git rev-parse HEAD)","time":"$CI_JOB_STARTED_AT"}' > www/__build_info.json
|
|
895
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
896
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
897
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
898
|
+
- collapseable_section_end "nodeinstall"
|
|
899
|
+
- cd www
|
|
900
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
901
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
902
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
903
|
+
- collapseable_section_end "nodeinstall"
|
|
904
|
+
- collapseable_section_start "yarninstall" "Yarn install"
|
|
905
|
+
- yarn install --immutable
|
|
906
|
+
- collapseable_section_end "yarninstall"
|
|
907
|
+
- yarn build
|
|
908
|
+
cache:
|
|
909
|
+
- key: www-yarn
|
|
910
|
+
policy: pull-push
|
|
911
|
+
paths:
|
|
912
|
+
- www/.yarn
|
|
913
|
+
- key: www-node-modules
|
|
914
|
+
policy: pull-push
|
|
915
|
+
paths:
|
|
916
|
+
- www/node_modules
|
|
917
|
+
- key: www-default
|
|
918
|
+
policy: pull-push
|
|
919
|
+
paths:
|
|
920
|
+
- www/.next/cache
|
|
921
|
+
artifacts:
|
|
922
|
+
paths:
|
|
923
|
+
- www/__build_info.json
|
|
924
|
+
- www/.next
|
|
925
|
+
- www/dist
|
|
926
|
+
exclude:
|
|
927
|
+
- www/.env
|
|
928
|
+
expire_in: 1 day
|
|
929
|
+
when: always
|
|
930
|
+
reports: {}
|
|
931
|
+
rules:
|
|
932
|
+
- when: never
|
|
933
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
934
|
+
- if: $CI_COMMIT_TAG
|
|
935
|
+
needs: []
|
|
936
|
+
retry: *a1
|
|
937
|
+
interruptible: true
|
|
938
|
+
'www 🔨 docker | stage ':
|
|
939
|
+
stage: build
|
|
940
|
+
image: path/to/docker/docker-build:the-version
|
|
941
|
+
services:
|
|
942
|
+
- name: docker:24.0.6-dind
|
|
943
|
+
command:
|
|
944
|
+
- --tls=false
|
|
945
|
+
- --registry-mirror=https://mirror.gcr.io
|
|
946
|
+
variables:
|
|
947
|
+
DOCKER_HOST: tcp://docker:2375
|
|
948
|
+
DOCKER_TLS_CERTDIR: ''
|
|
949
|
+
DOCKER_DRIVER: overlay2
|
|
950
|
+
DOCKER_BUILDKIT: '1'
|
|
951
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
952
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
953
|
+
KUBERNETES_MEMORY_LIMIT: 2Gi
|
|
954
|
+
script:
|
|
955
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
956
|
+
- export APP_DIR="www"
|
|
957
|
+
- export DOCKER_BUILD_CONTEXT="."
|
|
958
|
+
- export DOCKER_REGISTRY="europe-west6-docker.pkg.dev"
|
|
959
|
+
- export DOCKER_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/stage/www"
|
|
960
|
+
- export DOCKER_CACHE_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www"
|
|
961
|
+
- export DOCKER_IMAGE_TAG="$CI_COMMIT_SHA"
|
|
962
|
+
- |-
|
|
963
|
+
export DOCKER_COPY_AND_INSTALL_APP="COPY --chown=node:node $APP_DIR .
|
|
964
|
+
RUN yarn plugin import workspace-tools
|
|
965
|
+
RUN yarn workspaces focus --production && yarn rebuild"
|
|
966
|
+
- |-
|
|
967
|
+
export DOCKER_COPY_WORKSPACE_FILES="COPY --chown=node:node www/package.json /app/www/package.json
|
|
968
|
+
COPY --chown=node:node www/yarn.lock /app/www/yarn.lock
|
|
969
|
+
COPY --chown=node:node .yarnrc.yml /app/.yarnrc.yml
|
|
970
|
+
COPY --chown=node:node .yarn /app/.yarn"
|
|
971
|
+
- collapseable_section_end "injectvars"
|
|
972
|
+
- ensureNodeDockerfile
|
|
973
|
+
- collapseable_section_start "docker-login" "Docker Login"
|
|
974
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_stage_www_GCLOUD_DEPLOY_credentialsKey")
|
|
975
|
+
- gcloud auth configure-docker europe-west6-docker.pkg.dev
|
|
976
|
+
- collapseable_section_end "docker-login"
|
|
977
|
+
- collapseable_section_start "docker-build" "Docker build"
|
|
978
|
+
- docker build --network host --cache-from $DOCKER_CACHE_IMAGE --tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG -f $APP_DIR/Dockerfile $DOCKER_BUILD_CONTEXT --build-arg BUILDKIT_INLINE_CACHE=1
|
|
979
|
+
- collapseable_section_end "docker-build"
|
|
980
|
+
- collapseable_section_start "docker-push" "Docker push and tag"
|
|
981
|
+
- docker push $DOCKER_IMAGE:$DOCKER_IMAGE_TAG
|
|
982
|
+
- docker tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG $DOCKER_CACHE_IMAGE
|
|
983
|
+
- docker push $DOCKER_CACHE_IMAGE
|
|
984
|
+
- collapseable_section_end "docker-push"
|
|
985
|
+
cache:
|
|
986
|
+
- key: www-yarn
|
|
987
|
+
policy: pull
|
|
988
|
+
paths:
|
|
989
|
+
- www/.yarn
|
|
990
|
+
rules:
|
|
991
|
+
- when: never
|
|
992
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
993
|
+
- if: $CI_COMMIT_TAG
|
|
994
|
+
needs:
|
|
995
|
+
- 'www 🔨 app | stage '
|
|
996
|
+
retry: *a1
|
|
997
|
+
interruptible: true
|
|
998
|
+
'www 🧾 sbom | stage ':
|
|
999
|
+
stage: build
|
|
1000
|
+
image:
|
|
1001
|
+
name: aquasec/trivy:0.58.2
|
|
1002
|
+
entrypoint:
|
|
1003
|
+
- ''
|
|
1004
|
+
variables: {}
|
|
1005
|
+
script:
|
|
1006
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1007
|
+
- collapseable_section_end "injectvars"
|
|
1008
|
+
- trivy fs --quiet --format cyclonedx --output "__sbom.json" www
|
|
1009
|
+
artifacts:
|
|
1010
|
+
paths:
|
|
1011
|
+
- __sbom.json
|
|
1012
|
+
rules:
|
|
1013
|
+
- when: never
|
|
1014
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1015
|
+
- if: $CI_COMMIT_TAG
|
|
1016
|
+
needs: []
|
|
1017
|
+
retry: *a1
|
|
1018
|
+
interruptible: true
|
|
1019
|
+
allow_failure: true
|
|
1020
|
+
'www 🚀 Deploy | stage ':
|
|
1021
|
+
stage: deploy stage
|
|
1022
|
+
image: path/to/docker/gcloud:the-version
|
|
1023
|
+
variables:
|
|
1024
|
+
KUBERNETES_CPU_REQUEST: '0.22'
|
|
1025
|
+
KUBERNETES_MEMORY_REQUEST: 200Mi
|
|
1026
|
+
KUBERNETES_MEMORY_LIMIT: 400Mi
|
|
1027
|
+
script:
|
|
1028
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1029
|
+
- export ENV_SHORT="stage"
|
|
1030
|
+
- export APP_DIR="www"
|
|
1031
|
+
- export ENV_TYPE="stage"
|
|
1032
|
+
- export BUILD_INFO_BUILD_ID="$(git describe --tags 2>/dev/null || git rev-parse HEAD)"
|
|
1033
|
+
- export BUILD_INFO_BUILD_TIME="$CI_JOB_STARTED_AT"
|
|
1034
|
+
- export BUILD_INFO_CURRENT_VERSION="$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")"
|
|
1035
|
+
- export HOSTNAME="$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1036
|
+
- export ROOT_URL="https://$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1037
|
+
- export HOSTNAME_INTERNAL="$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1038
|
+
- export ROOT_URL_INTERNAL="https://$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1039
|
+
- export DEPLOY_CLOUD_RUN_PROJECT_ID="google-project-id"
|
|
1040
|
+
- export DEPLOY_CLOUD_RUN_REGION="europe-west6"
|
|
1041
|
+
- export GCLOUD_DEPLOY_credentialsKey="$CL_stage_www_GCLOUD_DEPLOY_credentialsKey"
|
|
1042
|
+
- export GCLOUD_RUN_canonicalHostSuffix="$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix"
|
|
1043
|
+
- export _ALL_ENV_VAR_KEYS="[\\"ENV_SHORT\\",\\"APP_DIR\\",\\"ENV_TYPE\\",\\"BUILD_INFO_BUILD_ID\\",\\"BUILD_INFO_BUILD_TIME\\",\\"BUILD_INFO_CURRENT_VERSION\\",\\"HOSTNAME\\",\\"ROOT_URL\\",\\"HOSTNAME_INTERNAL\\",\\"ROOT_URL_INTERNAL\\",\\"DEPLOY_CLOUD_RUN_PROJECT_ID\\",\\"DEPLOY_CLOUD_RUN_REGION\\",\\"GCLOUD_DEPLOY_credentialsKey\\",\\"GCLOUD_RUN_canonicalHostSuffix\\"]"
|
|
1044
|
+
- export DOCKER_REGISTRY="europe-west6-docker.pkg.dev"
|
|
1045
|
+
- export DOCKER_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/stage/www"
|
|
1046
|
+
- export DOCKER_CACHE_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www"
|
|
1047
|
+
- export DOCKER_IMAGE_TAG="$CI_COMMIT_SHA"
|
|
1048
|
+
- export CLOUDSDK_CORE_DISABLE_PROMPTS="1"
|
|
1049
|
+
- collapseable_section_end "injectvars"
|
|
1050
|
+
- collapseable_section_start "prepare" "Prepare..."
|
|
1051
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_stage_www_GCLOUD_DEPLOY_credentialsKey")
|
|
1052
|
+
- export GCLOUD_PROJECT_NUMBER=$(gcloud projects describe google-project-id --format="value(projectNumber)")
|
|
1053
|
+
- 'echo "GCLOUD_PROJECT_NUMBER: $GCLOUD_PROJECT_NUMBER"'
|
|
1054
|
+
- collapseable_section_end "prepare"
|
|
1055
|
+
- collapseable_section_start "writeenvvars" "Write env vars to file"
|
|
1056
|
+
- |
|
|
1057
|
+
cat > ____envvars.yaml <<EOF
|
|
1058
|
+
ENV_SHORT: |-
|
|
1059
|
+
stage
|
|
1060
|
+
APP_DIR: |-
|
|
1061
|
+
www
|
|
1062
|
+
ENV_TYPE: |-
|
|
1063
|
+
stage
|
|
1064
|
+
BUILD_INFO_BUILD_ID: |-
|
|
1065
|
+
$(printf %s "$(git describe --tags 2>/dev/null || git rev-parse HEAD)" | sed '1!s/^/ /')
|
|
1066
|
+
BUILD_INFO_BUILD_TIME: |-
|
|
1067
|
+
$(printf %s "$CI_JOB_STARTED_AT" | sed '1!s/^/ /')
|
|
1068
|
+
BUILD_INFO_CURRENT_VERSION: |-
|
|
1069
|
+
$(printf %s "$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")" | sed '1!s/^/ /')
|
|
1070
|
+
HOSTNAME: |-
|
|
1071
|
+
$(printf %s "$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
1072
|
+
ROOT_URL: |-
|
|
1073
|
+
$(printf %s "https://$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
1074
|
+
HOSTNAME_INTERNAL: |-
|
|
1075
|
+
$(printf %s "$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
1076
|
+
ROOT_URL_INTERNAL: |-
|
|
1077
|
+
$(printf %s "https://$(printf %s "pan-agent-example-app-stage-www-$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
1078
|
+
DEPLOY_CLOUD_RUN_PROJECT_ID: |-
|
|
1079
|
+
google-project-id
|
|
1080
|
+
DEPLOY_CLOUD_RUN_REGION: |-
|
|
1081
|
+
europe-west6
|
|
1082
|
+
GCLOUD_RUN_canonicalHostSuffix: |-
|
|
1083
|
+
$(printf %s "$CL_stage_www_GCLOUD_RUN_canonicalHostSuffix" | sed '1!s/^/ /')
|
|
1084
|
+
_ALL_ENV_VAR_KEYS: |-
|
|
1085
|
+
["ENV_SHORT","APP_DIR","ENV_TYPE","BUILD_INFO_BUILD_ID","BUILD_INFO_BUILD_TIME","BUILD_INFO_CURRENT_VERSION","HOSTNAME","ROOT_URL","HOSTNAME_INTERNAL","ROOT_URL_INTERNAL","DEPLOY_CLOUD_RUN_PROJECT_ID","DEPLOY_CLOUD_RUN_REGION","GCLOUD_DEPLOY_credentialsKey","GCLOUD_RUN_canonicalHostSuffix"]
|
|
1086
|
+
|
|
1087
|
+
EOF
|
|
1088
|
+
- collapseable_section_end "writeenvvars"
|
|
1089
|
+
- collapseable_section_start "deploy" "Deploy to cloud run"
|
|
1090
|
+
- gcloud run deploy pan-agent-example-app-stage-www --command="yarn,start" --image=europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/stage/www:$DOCKER_IMAGE_TAG --project=google-project-id --region=europe-west6 --labels=customer-name=pan,component-name=www,app-name=agent-example-app,env-type=stage,env-name=stage,build-type=node,cloud-run-service-name=pan-agent-example-app-stage-www --env-vars-file=____envvars.yaml --min-instances=0 --max-instances=100 --cpu-throttling --allow-unauthenticated --ingress=all --cpu-boost
|
|
1091
|
+
- collapseable_section_end "deploy"
|
|
1092
|
+
- collapseable_section_start "cleanup" "Cleanup"
|
|
1093
|
+
- set +e
|
|
1094
|
+
- gcloud run revisions list --project=google-project-id --region=europe-west6 --service=pan-agent-example-app-stage-www --limit=unlimited --sort-by=metadata.creationTimestamp --format="value(name)" --filter='(status.conditions.status=False OR status.conditions.status=Unknown)' | while read -r revisionname; do gcloud run revisions delete --project=google-project-id --region=europe-west6 --quiet $revisionname ; done
|
|
1095
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/stage/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/stage/www@$version --quiet --delete-tags; done
|
|
1096
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www@$version --quiet --delete-tags; done
|
|
1097
|
+
- set -e
|
|
1098
|
+
- collapseable_section_end "cleanup"
|
|
1099
|
+
- echo 'Uploading SBOM to Dependency Track'
|
|
1100
|
+
- /dtrackuploader https://dep.panter.swiss/ "$DT_KEY_PROD" upload "pan-agent-example-app/www" "$ROOT_URL" "__sbom.json" vex.json || true
|
|
1101
|
+
- echo "CL_GITLAB_ENVIRONMENT_URL=$ROOT_URL" >> gitlab_environment.env
|
|
1102
|
+
environment:
|
|
1103
|
+
name: stage/www
|
|
1104
|
+
url: $CL_GITLAB_ENVIRONMENT_URL
|
|
1105
|
+
on_stop: 'www 🛑 Stop ⚠️ | stage '
|
|
1106
|
+
artifacts:
|
|
1107
|
+
reports:
|
|
1108
|
+
dotenv: gitlab_environment.env
|
|
1109
|
+
rules:
|
|
1110
|
+
- when: never
|
|
1111
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1112
|
+
- when: on_success
|
|
1113
|
+
if: $CI_COMMIT_TAG
|
|
1114
|
+
needs:
|
|
1115
|
+
- job: 'www 🔨 app | stage '
|
|
1116
|
+
artifacts: false
|
|
1117
|
+
- job: 'www 🔨 docker | stage '
|
|
1118
|
+
artifacts: false
|
|
1119
|
+
- job: 'www 🧾 sbom | stage '
|
|
1120
|
+
artifacts: true
|
|
1121
|
+
retry: *a1
|
|
1122
|
+
interruptible: true
|
|
1123
|
+
allow_failure: false
|
|
1124
|
+
'www 🛑 Stop ⚠️ | stage ':
|
|
1125
|
+
stage: stop stage
|
|
1126
|
+
image: path/to/docker/gcloud:the-version
|
|
1127
|
+
variables:
|
|
1128
|
+
KUBERNETES_CPU_REQUEST: '0.22'
|
|
1129
|
+
KUBERNETES_MEMORY_REQUEST: 200Mi
|
|
1130
|
+
KUBERNETES_MEMORY_LIMIT: 400Mi
|
|
1131
|
+
GIT_STRATEGY: none
|
|
1132
|
+
script:
|
|
1133
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1134
|
+
- export CLOUDSDK_CORE_DISABLE_PROMPTS="1"
|
|
1135
|
+
- collapseable_section_end "injectvars"
|
|
1136
|
+
- set +e
|
|
1137
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_stage_www_GCLOUD_DEPLOY_credentialsKey")
|
|
1138
|
+
- gcloud run services delete pan-agent-example-app-stage-www --project=google-project-id --region=europe-west6
|
|
1139
|
+
- gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/stage/www --quiet --delete-tags
|
|
1140
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www@$version --quiet --delete-tags; done
|
|
1141
|
+
- echo 'Disabling component in Dependency Track'
|
|
1142
|
+
- /dtrackuploader https://dep.panter.swiss/ "$DT_KEY_PROD" disable "pan-agent-example-app/www" "$CI_ENVIRONMENT_URL" || true
|
|
1143
|
+
- set -e
|
|
1144
|
+
environment:
|
|
1145
|
+
name: stage/www
|
|
1146
|
+
action: stop
|
|
1147
|
+
rules:
|
|
1148
|
+
- if: $CI_COMMIT_BRANCH =~ /^[0-9]+\\.([0-9]+|x)\\.x$/
|
|
1149
|
+
when: on_success
|
|
1150
|
+
- when: never
|
|
1151
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1152
|
+
- when: manual
|
|
1153
|
+
if: $CI_COMMIT_TAG
|
|
1154
|
+
needs: []
|
|
1155
|
+
retry: *a1
|
|
1156
|
+
interruptible: true
|
|
1157
|
+
allow_failure: true
|
|
1158
|
+
'www 🔨 app | prod ':
|
|
1159
|
+
stage: build
|
|
1160
|
+
image: path/to/docker/jobs-default:the-version
|
|
1161
|
+
variables:
|
|
1162
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
1163
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
1164
|
+
KUBERNETES_MEMORY_LIMIT: 4Gi
|
|
1165
|
+
script:
|
|
1166
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1167
|
+
- export ENV_SHORT="prod"
|
|
1168
|
+
- export APP_DIR="www"
|
|
1169
|
+
- export ENV_TYPE="prod"
|
|
1170
|
+
- export BUILD_INFO_BUILD_ID="$(git describe --tags 2>/dev/null || git rev-parse HEAD)"
|
|
1171
|
+
- export BUILD_INFO_BUILD_TIME="$CI_JOB_STARTED_AT"
|
|
1172
|
+
- export BUILD_INFO_CURRENT_VERSION="$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")"
|
|
1173
|
+
- export HOSTNAME="$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1174
|
+
- export ROOT_URL="https://$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1175
|
+
- export HOSTNAME_INTERNAL="$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1176
|
+
- export ROOT_URL_INTERNAL="https://$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1177
|
+
- export DEPLOY_CLOUD_RUN_PROJECT_ID="google-project-id"
|
|
1178
|
+
- export DEPLOY_CLOUD_RUN_REGION="europe-west6"
|
|
1179
|
+
- export GCLOUD_DEPLOY_credentialsKey="$CL_prod_www_GCLOUD_DEPLOY_credentialsKey"
|
|
1180
|
+
- export GCLOUD_RUN_canonicalHostSuffix="$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix"
|
|
1181
|
+
- export _ALL_ENV_VAR_KEYS="[\\"ENV_SHORT\\",\\"APP_DIR\\",\\"ENV_TYPE\\",\\"BUILD_INFO_BUILD_ID\\",\\"BUILD_INFO_BUILD_TIME\\",\\"BUILD_INFO_CURRENT_VERSION\\",\\"HOSTNAME\\",\\"ROOT_URL\\",\\"HOSTNAME_INTERNAL\\",\\"ROOT_URL_INTERNAL\\",\\"DEPLOY_CLOUD_RUN_PROJECT_ID\\",\\"DEPLOY_CLOUD_RUN_REGION\\",\\"GCLOUD_DEPLOY_credentialsKey\\",\\"GCLOUD_RUN_canonicalHostSuffix\\"]"
|
|
1182
|
+
- collapseable_section_end "injectvars"
|
|
1183
|
+
- collapseable_section_start "write-dotenv-www" "write dot env for www"
|
|
1184
|
+
- |-
|
|
1185
|
+
cat <<EOF > www/.env
|
|
1186
|
+
ENV_SHORT=prod
|
|
1187
|
+
APP_DIR=www
|
|
1188
|
+
ENV_TYPE=prod
|
|
1189
|
+
HOSTNAME=$(printf %s "$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
1190
|
+
ROOT_URL=$(printf %s "https://$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
1191
|
+
HOSTNAME_INTERNAL=$(printf %s "$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
1192
|
+
ROOT_URL_INTERNAL=$(printf %s "https://$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | escapeForDotEnv)
|
|
1193
|
+
DEPLOY_CLOUD_RUN_PROJECT_ID=google-project-id
|
|
1194
|
+
DEPLOY_CLOUD_RUN_REGION=europe-west6
|
|
1195
|
+
GCLOUD_DEPLOY_credentialsKey=$(printf %s "$CL_prod_www_GCLOUD_DEPLOY_credentialsKey" | escapeForDotEnv)
|
|
1196
|
+
GCLOUD_RUN_canonicalHostSuffix=$(printf %s "$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | escapeForDotEnv)
|
|
1197
|
+
_ALL_ENV_VAR_KEYS=["ENV_SHORT","APP_DIR","ENV_TYPE","BUILD_INFO_BUILD_ID","BUILD_INFO_BUILD_TIME","BUILD_INFO_CURRENT_VERSION","HOSTNAME","ROOT_URL","HOSTNAME_INTERNAL","ROOT_URL_INTERNAL","DEPLOY_CLOUD_RUN_PROJECT_ID","DEPLOY_CLOUD_RUN_REGION","GCLOUD_DEPLOY_credentialsKey","GCLOUD_RUN_canonicalHostSuffix"]
|
|
1198
|
+
EOF
|
|
1199
|
+
- collapseable_section_end "write-dotenv-www"
|
|
1200
|
+
- echo '{"id":"$(git describe --tags 2>/dev/null || git rev-parse HEAD)","time":"$CI_JOB_STARTED_AT"}' > www/__build_info.json
|
|
1201
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
1202
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
1203
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
1204
|
+
- collapseable_section_end "nodeinstall"
|
|
1205
|
+
- cd www
|
|
1206
|
+
- collapseable_section_start "nodeinstall" "Ensure node version"
|
|
1207
|
+
- if [ -f ~/.nvm/nvm.sh ]; then source ~/.nvm/nvm.sh; fi
|
|
1208
|
+
- if command -v nvm &> /dev/null && [ -f ./.nvmrc ]; then nvm install; fi
|
|
1209
|
+
- collapseable_section_end "nodeinstall"
|
|
1210
|
+
- collapseable_section_start "yarninstall" "Yarn install"
|
|
1211
|
+
- yarn install --immutable
|
|
1212
|
+
- collapseable_section_end "yarninstall"
|
|
1213
|
+
- yarn build
|
|
1214
|
+
cache:
|
|
1215
|
+
- key: www-yarn
|
|
1216
|
+
policy: pull-push
|
|
1217
|
+
paths:
|
|
1218
|
+
- www/.yarn
|
|
1219
|
+
- key: www-node-modules
|
|
1220
|
+
policy: pull-push
|
|
1221
|
+
paths:
|
|
1222
|
+
- www/node_modules
|
|
1223
|
+
- key: www-default
|
|
1224
|
+
policy: pull-push
|
|
1225
|
+
paths:
|
|
1226
|
+
- www/.next/cache
|
|
1227
|
+
artifacts:
|
|
1228
|
+
paths:
|
|
1229
|
+
- www/__build_info.json
|
|
1230
|
+
- www/.next
|
|
1231
|
+
- www/dist
|
|
1232
|
+
exclude:
|
|
1233
|
+
- www/.env
|
|
1234
|
+
expire_in: 1 day
|
|
1235
|
+
when: always
|
|
1236
|
+
reports: {}
|
|
1237
|
+
rules:
|
|
1238
|
+
- when: never
|
|
1239
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1240
|
+
- if: $CI_COMMIT_TAG
|
|
1241
|
+
needs: []
|
|
1242
|
+
retry: *a1
|
|
1243
|
+
interruptible: true
|
|
1244
|
+
'www 🔨 docker | prod ':
|
|
1245
|
+
stage: build
|
|
1246
|
+
image: path/to/docker/docker-build:the-version
|
|
1247
|
+
services:
|
|
1248
|
+
- name: docker:24.0.6-dind
|
|
1249
|
+
command:
|
|
1250
|
+
- --tls=false
|
|
1251
|
+
- --registry-mirror=https://mirror.gcr.io
|
|
1252
|
+
variables:
|
|
1253
|
+
DOCKER_HOST: tcp://docker:2375
|
|
1254
|
+
DOCKER_TLS_CERTDIR: ''
|
|
1255
|
+
DOCKER_DRIVER: overlay2
|
|
1256
|
+
DOCKER_BUILDKIT: '1'
|
|
1257
|
+
KUBERNETES_CPU_REQUEST: '0.45'
|
|
1258
|
+
KUBERNETES_MEMORY_REQUEST: 1Gi
|
|
1259
|
+
KUBERNETES_MEMORY_LIMIT: 2Gi
|
|
1260
|
+
script:
|
|
1261
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1262
|
+
- export APP_DIR="www"
|
|
1263
|
+
- export DOCKER_BUILD_CONTEXT="."
|
|
1264
|
+
- export DOCKER_REGISTRY="europe-west6-docker.pkg.dev"
|
|
1265
|
+
- export DOCKER_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/prod/www"
|
|
1266
|
+
- export DOCKER_CACHE_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www"
|
|
1267
|
+
- export DOCKER_IMAGE_TAG="$CI_COMMIT_SHA"
|
|
1268
|
+
- |-
|
|
1269
|
+
export DOCKER_COPY_AND_INSTALL_APP="COPY --chown=node:node $APP_DIR .
|
|
1270
|
+
RUN yarn plugin import workspace-tools
|
|
1271
|
+
RUN yarn workspaces focus --production && yarn rebuild"
|
|
1272
|
+
- |-
|
|
1273
|
+
export DOCKER_COPY_WORKSPACE_FILES="COPY --chown=node:node www/package.json /app/www/package.json
|
|
1274
|
+
COPY --chown=node:node www/yarn.lock /app/www/yarn.lock
|
|
1275
|
+
COPY --chown=node:node .yarnrc.yml /app/.yarnrc.yml
|
|
1276
|
+
COPY --chown=node:node .yarn /app/.yarn"
|
|
1277
|
+
- collapseable_section_end "injectvars"
|
|
1278
|
+
- ensureNodeDockerfile
|
|
1279
|
+
- collapseable_section_start "docker-login" "Docker Login"
|
|
1280
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_prod_www_GCLOUD_DEPLOY_credentialsKey")
|
|
1281
|
+
- gcloud auth configure-docker europe-west6-docker.pkg.dev
|
|
1282
|
+
- collapseable_section_end "docker-login"
|
|
1283
|
+
- collapseable_section_start "docker-build" "Docker build"
|
|
1284
|
+
- docker build --network host --cache-from $DOCKER_CACHE_IMAGE --tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG -f $APP_DIR/Dockerfile $DOCKER_BUILD_CONTEXT --build-arg BUILDKIT_INLINE_CACHE=1
|
|
1285
|
+
- collapseable_section_end "docker-build"
|
|
1286
|
+
- collapseable_section_start "docker-push" "Docker push and tag"
|
|
1287
|
+
- docker push $DOCKER_IMAGE:$DOCKER_IMAGE_TAG
|
|
1288
|
+
- docker tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG $DOCKER_CACHE_IMAGE
|
|
1289
|
+
- docker push $DOCKER_CACHE_IMAGE
|
|
1290
|
+
- collapseable_section_end "docker-push"
|
|
1291
|
+
cache:
|
|
1292
|
+
- key: www-yarn
|
|
1293
|
+
policy: pull
|
|
1294
|
+
paths:
|
|
1295
|
+
- www/.yarn
|
|
1296
|
+
rules:
|
|
1297
|
+
- when: never
|
|
1298
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1299
|
+
- if: $CI_COMMIT_TAG
|
|
1300
|
+
needs:
|
|
1301
|
+
- 'www 🔨 app | prod '
|
|
1302
|
+
retry: *a1
|
|
1303
|
+
interruptible: true
|
|
1304
|
+
'www 🧾 sbom | prod ':
|
|
1305
|
+
stage: build
|
|
1306
|
+
image:
|
|
1307
|
+
name: aquasec/trivy:0.58.2
|
|
1308
|
+
entrypoint:
|
|
1309
|
+
- ''
|
|
1310
|
+
variables: {}
|
|
1311
|
+
script:
|
|
1312
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1313
|
+
- collapseable_section_end "injectvars"
|
|
1314
|
+
- trivy fs --quiet --format cyclonedx --output "__sbom.json" www
|
|
1315
|
+
artifacts:
|
|
1316
|
+
paths:
|
|
1317
|
+
- __sbom.json
|
|
1318
|
+
rules:
|
|
1319
|
+
- when: never
|
|
1320
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1321
|
+
- if: $CI_COMMIT_TAG
|
|
1322
|
+
needs: []
|
|
1323
|
+
retry: *a1
|
|
1324
|
+
interruptible: true
|
|
1325
|
+
allow_failure: true
|
|
1326
|
+
'www 🚀 Deploy | prod ':
|
|
1327
|
+
stage: deploy prod
|
|
1328
|
+
image: path/to/docker/gcloud:the-version
|
|
1329
|
+
variables:
|
|
1330
|
+
KUBERNETES_CPU_REQUEST: '0.22'
|
|
1331
|
+
KUBERNETES_MEMORY_REQUEST: 200Mi
|
|
1332
|
+
KUBERNETES_MEMORY_LIMIT: 400Mi
|
|
1333
|
+
script:
|
|
1334
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1335
|
+
- export ENV_SHORT="prod"
|
|
1336
|
+
- export APP_DIR="www"
|
|
1337
|
+
- export ENV_TYPE="prod"
|
|
1338
|
+
- export BUILD_INFO_BUILD_ID="$(git describe --tags 2>/dev/null || git rev-parse HEAD)"
|
|
1339
|
+
- export BUILD_INFO_BUILD_TIME="$CI_JOB_STARTED_AT"
|
|
1340
|
+
- export BUILD_INFO_CURRENT_VERSION="$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")"
|
|
1341
|
+
- export HOSTNAME="$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1342
|
+
- export ROOT_URL="https://$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1343
|
+
- export HOSTNAME_INTERNAL="$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1344
|
+
- export ROOT_URL_INTERNAL="https://$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')"
|
|
1345
|
+
- export DEPLOY_CLOUD_RUN_PROJECT_ID="google-project-id"
|
|
1346
|
+
- export DEPLOY_CLOUD_RUN_REGION="europe-west6"
|
|
1347
|
+
- export GCLOUD_DEPLOY_credentialsKey="$CL_prod_www_GCLOUD_DEPLOY_credentialsKey"
|
|
1348
|
+
- export GCLOUD_RUN_canonicalHostSuffix="$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix"
|
|
1349
|
+
- export _ALL_ENV_VAR_KEYS="[\\"ENV_SHORT\\",\\"APP_DIR\\",\\"ENV_TYPE\\",\\"BUILD_INFO_BUILD_ID\\",\\"BUILD_INFO_BUILD_TIME\\",\\"BUILD_INFO_CURRENT_VERSION\\",\\"HOSTNAME\\",\\"ROOT_URL\\",\\"HOSTNAME_INTERNAL\\",\\"ROOT_URL_INTERNAL\\",\\"DEPLOY_CLOUD_RUN_PROJECT_ID\\",\\"DEPLOY_CLOUD_RUN_REGION\\",\\"GCLOUD_DEPLOY_credentialsKey\\",\\"GCLOUD_RUN_canonicalHostSuffix\\"]"
|
|
1350
|
+
- export DOCKER_REGISTRY="europe-west6-docker.pkg.dev"
|
|
1351
|
+
- export DOCKER_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/prod/www"
|
|
1352
|
+
- export DOCKER_CACHE_IMAGE="europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www"
|
|
1353
|
+
- export DOCKER_IMAGE_TAG="$CI_COMMIT_SHA"
|
|
1354
|
+
- export CLOUDSDK_CORE_DISABLE_PROMPTS="1"
|
|
1355
|
+
- collapseable_section_end "injectvars"
|
|
1356
|
+
- collapseable_section_start "prepare" "Prepare..."
|
|
1357
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_prod_www_GCLOUD_DEPLOY_credentialsKey")
|
|
1358
|
+
- export GCLOUD_PROJECT_NUMBER=$(gcloud projects describe google-project-id --format="value(projectNumber)")
|
|
1359
|
+
- 'echo "GCLOUD_PROJECT_NUMBER: $GCLOUD_PROJECT_NUMBER"'
|
|
1360
|
+
- collapseable_section_end "prepare"
|
|
1361
|
+
- collapseable_section_start "writeenvvars" "Write env vars to file"
|
|
1362
|
+
- |
|
|
1363
|
+
cat > ____envvars.yaml <<EOF
|
|
1364
|
+
ENV_SHORT: |-
|
|
1365
|
+
prod
|
|
1366
|
+
APP_DIR: |-
|
|
1367
|
+
www
|
|
1368
|
+
ENV_TYPE: |-
|
|
1369
|
+
prod
|
|
1370
|
+
BUILD_INFO_BUILD_ID: |-
|
|
1371
|
+
$(printf %s "$(git describe --tags 2>/dev/null || git rev-parse HEAD)" | sed '1!s/^/ /')
|
|
1372
|
+
BUILD_INFO_BUILD_TIME: |-
|
|
1373
|
+
$(printf %s "$CI_JOB_STARTED_AT" | sed '1!s/^/ /')
|
|
1374
|
+
BUILD_INFO_CURRENT_VERSION: |-
|
|
1375
|
+
$(printf %s "$(tag=$(git ls-remote origin "refs/tags/v*[0-9]" 2>/dev/null | cut -f 2- | sort -V | tail -1 | sed 's/refs\\/tags\\/v//'); [ -z "$tag" ] && echo "0.0.0" || echo "$tag")" | sed '1!s/^/ /')
|
|
1376
|
+
HOSTNAME: |-
|
|
1377
|
+
$(printf %s "$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
1378
|
+
ROOT_URL: |-
|
|
1379
|
+
$(printf %s "https://$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
1380
|
+
HOSTNAME_INTERNAL: |-
|
|
1381
|
+
$(printf %s "$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
1382
|
+
ROOT_URL_INTERNAL: |-
|
|
1383
|
+
$(printf %s "https://$(printf %s "pan-agent-example-app-prod-www-$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | awk '{print tolower($0)}')" | sed '1!s/^/ /')
|
|
1384
|
+
DEPLOY_CLOUD_RUN_PROJECT_ID: |-
|
|
1385
|
+
google-project-id
|
|
1386
|
+
DEPLOY_CLOUD_RUN_REGION: |-
|
|
1387
|
+
europe-west6
|
|
1388
|
+
GCLOUD_RUN_canonicalHostSuffix: |-
|
|
1389
|
+
$(printf %s "$CL_prod_www_GCLOUD_RUN_canonicalHostSuffix" | sed '1!s/^/ /')
|
|
1390
|
+
_ALL_ENV_VAR_KEYS: |-
|
|
1391
|
+
["ENV_SHORT","APP_DIR","ENV_TYPE","BUILD_INFO_BUILD_ID","BUILD_INFO_BUILD_TIME","BUILD_INFO_CURRENT_VERSION","HOSTNAME","ROOT_URL","HOSTNAME_INTERNAL","ROOT_URL_INTERNAL","DEPLOY_CLOUD_RUN_PROJECT_ID","DEPLOY_CLOUD_RUN_REGION","GCLOUD_DEPLOY_credentialsKey","GCLOUD_RUN_canonicalHostSuffix"]
|
|
1392
|
+
|
|
1393
|
+
EOF
|
|
1394
|
+
- collapseable_section_end "writeenvvars"
|
|
1395
|
+
- collapseable_section_start "deploy" "Deploy to cloud run"
|
|
1396
|
+
- gcloud run deploy pan-agent-example-app-prod-www --command="yarn,start" --image=europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/prod/www:$DOCKER_IMAGE_TAG --project=google-project-id --region=europe-west6 --labels=customer-name=pan,component-name=www,app-name=agent-example-app,env-type=prod,env-name=prod,build-type=node,cloud-run-service-name=pan-agent-example-app-prod-www --env-vars-file=____envvars.yaml --min-instances=0 --max-instances=100 --cpu-throttling --allow-unauthenticated --ingress=all --cpu-boost
|
|
1397
|
+
- collapseable_section_end "deploy"
|
|
1398
|
+
- collapseable_section_start "cleanup" "Cleanup"
|
|
1399
|
+
- set +e
|
|
1400
|
+
- gcloud run revisions list --project=google-project-id --region=europe-west6 --service=pan-agent-example-app-prod-www --limit=unlimited --sort-by=metadata.creationTimestamp --format="value(name)" --filter='(status.conditions.status=False OR status.conditions.status=Unknown)' | tail -n +6 | while read -r revisionname; do gcloud run revisions delete --project=google-project-id --region=europe-west6 --quiet $revisionname ; done
|
|
1401
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/prod/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +7 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/prod/www@$version --quiet --delete-tags; done
|
|
1402
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www@$version --quiet --delete-tags; done
|
|
1403
|
+
- set -e
|
|
1404
|
+
- collapseable_section_end "cleanup"
|
|
1405
|
+
- echo 'Uploading SBOM to Dependency Track'
|
|
1406
|
+
- /dtrackuploader https://dep.panter.swiss/ "$DT_KEY_PROD" upload "pan-agent-example-app/www" "$ROOT_URL" "__sbom.json" vex.json || true
|
|
1407
|
+
- echo "CL_GITLAB_ENVIRONMENT_URL=$ROOT_URL" >> gitlab_environment.env
|
|
1408
|
+
environment:
|
|
1409
|
+
name: prod/www
|
|
1410
|
+
url: $CL_GITLAB_ENVIRONMENT_URL
|
|
1411
|
+
on_stop: 'www 🛑 Stop ⚠️ | prod '
|
|
1412
|
+
artifacts:
|
|
1413
|
+
reports:
|
|
1414
|
+
dotenv: gitlab_environment.env
|
|
1415
|
+
rules:
|
|
1416
|
+
- when: never
|
|
1417
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1418
|
+
- when: manual
|
|
1419
|
+
if: $CI_COMMIT_TAG
|
|
1420
|
+
needs:
|
|
1421
|
+
- job: 'www 🔨 app | prod '
|
|
1422
|
+
artifacts: false
|
|
1423
|
+
- job: 'www 🔨 docker | prod '
|
|
1424
|
+
artifacts: false
|
|
1425
|
+
- job: 'www 🧾 sbom | prod '
|
|
1426
|
+
artifacts: true
|
|
1427
|
+
retry: *a1
|
|
1428
|
+
interruptible: true
|
|
1429
|
+
allow_failure: true
|
|
1430
|
+
'www 🛑 Stop ⚠️ | prod ':
|
|
1431
|
+
stage: stop prod
|
|
1432
|
+
image: path/to/docker/gcloud:the-version
|
|
1433
|
+
variables:
|
|
1434
|
+
KUBERNETES_CPU_REQUEST: '0.22'
|
|
1435
|
+
KUBERNETES_MEMORY_REQUEST: 200Mi
|
|
1436
|
+
KUBERNETES_MEMORY_LIMIT: 400Mi
|
|
1437
|
+
GIT_STRATEGY: none
|
|
1438
|
+
script:
|
|
1439
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1440
|
+
- export CLOUDSDK_CORE_DISABLE_PROMPTS="1"
|
|
1441
|
+
- collapseable_section_end "injectvars"
|
|
1442
|
+
- set +e
|
|
1443
|
+
- gcloud auth activate-service-account --key-file=<(echo "$CL_prod_www_GCLOUD_DEPLOY_credentialsKey")
|
|
1444
|
+
- gcloud run services delete pan-agent-example-app-prod-www --project=google-project-id --region=europe-west6
|
|
1445
|
+
- gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/prod/www --quiet --delete-tags
|
|
1446
|
+
- gcloud artifacts docker images list europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www --sort-by=~CREATE_TIME --format="value(version)" | tail -n +2 | while read -r version; do gcloud artifacts docker images delete europe-west6-docker.pkg.dev/google-project-id/catladder-deploy/pan-agent-example-app/caches/www@$version --quiet --delete-tags; done
|
|
1447
|
+
- echo 'Disabling component in Dependency Track'
|
|
1448
|
+
- /dtrackuploader https://dep.panter.swiss/ "$DT_KEY_PROD" disable "pan-agent-example-app/www" "$CI_ENVIRONMENT_URL" || true
|
|
1449
|
+
- set -e
|
|
1450
|
+
environment:
|
|
1451
|
+
name: prod/www
|
|
1452
|
+
action: stop
|
|
1453
|
+
rules:
|
|
1454
|
+
- if: $CI_COMMIT_BRANCH =~ /^[0-9]+\\.([0-9]+|x)\\.x$/
|
|
1455
|
+
when: on_success
|
|
1456
|
+
- when: never
|
|
1457
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1458
|
+
- when: manual
|
|
1459
|
+
if: $CI_COMMIT_TAG
|
|
1460
|
+
needs: []
|
|
1461
|
+
retry: *a1
|
|
1462
|
+
interruptible: true
|
|
1463
|
+
allow_failure: true
|
|
1464
|
+
claude claude-agent-event:
|
|
1465
|
+
stage: agents
|
|
1466
|
+
image: node:24-alpine3.21
|
|
1467
|
+
variables: {}
|
|
1468
|
+
script:
|
|
1469
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1470
|
+
- export MAX_MCP_OUTPUT_TOKENS="75000"
|
|
1471
|
+
- export GITLAB_PERSONAL_ACCESS_TOKEN="$AGENT_GITLAB_PERSONAL_ACCESS_TOKEN"
|
|
1472
|
+
- export GITLAB_API_URL="$CI_API_V4_URL"
|
|
1473
|
+
- collapseable_section_end "injectvars"
|
|
1474
|
+
- apk update
|
|
1475
|
+
- apk add --no-cache git curl bash
|
|
1476
|
+
- npm install -g @anthropic-ai/claude-code
|
|
1477
|
+
- claude mcp add gitlab --env GITLAB_PERSONAL_ACCESS_TOKEN=$GITLAB_PERSONAL_ACCESS_TOKEN --env GITLAB_API_URL=$GITLAB_API_URL -- npx -y @zereight/mcp-gitlab
|
|
1478
|
+
- 'export PROMPT="\\nYou are a GitLab assistant bot. You receive ONE raw GitLab webhook JSON payload.\\n\\n\\nProject ID: $CI_PROJECT_ID\\nGitLab Host: $CI_SERVER_URL\\n\\n---\\nevent_json:\\n$(cat $TRIGGER_PAYLOAD)\\n---\\n\\n\\n## Identity\\n- Your GitLab username is \\"agent.claude\\".\\n\\n\\n## Golden Rules\\n- Use the \\\`gitlab-mcp\\\` tool for ALL GitLab actions. If a needed action is missing, use GitLab REST/GraphQL API directly as a fallback.\\n- NEVER mention yourself (\\"@agent.claude\\").\\n- NEVER push to main/default or any protected branch. Always create a new branch and open a Merge Request (MR).\\n- Do not create an MR for a **closed** issue.\\n- Keep actions minimal and idempotent. Avoid duplicate comments or duplicate MRs.\\n- Use ONE stable \\\`source_branch\\\` per run; do not regenerate its name later.\\n\\n\\n## Self-Parse the Raw Payload (no preprocessing available)\\nFrom \\\`event_json\\\`, extract:\\n- kind: \\"issue\\" | \\"merge_request\\" | \\"note\\"\\n- target + iid from URL:\\n - \\\`/-/issues/<n>\\\` → target=\\"issue\\", iid=<n>\\n - \\\`/-/merge_requests/<n>\\\` → target=\\"mr\\", iid=<n>\\n- note_id if present (\\\`#note_<id>\\\`)\\n- description/body text, state, author \\\`user_username\\\`, timestamps\\n- project id/path; detect default branch via tool/API when needed\\n\\nIf any key is missing, choose the safest minimal action or briefly explain via a comment.\\n\\n\\n## High-Reliability Workflow (sequence + postconditions)\\nFollow this order for any change work:\\n\\n1) **Acknowledge** with a short comment on the issue/MR thread.\\n2) **Discover default branch** (e.g., \\"main\\") via MCP or API.\\n3) **Create a working branch** from default (stable name, e.g., \\\`fix/issue-<iid>-<slug>\\\` or \\\`feat/issue-<iid>-<slug>\\\`).\\n4) **Write changes → commit → push to remote branch.**\\n5) **Verify push landed**:\\n - Fetch latest commit on \\\`source_branch\\\`; record its short SHA.\\n - Compare default vs \\\`source_branch\\\` and ensure \\\`diffs.length > 0\\\`.\\n6) **Create or update MR** ONLY if there is a non-empty diff.\\n - Include \\\`Closes #<issue_iid>\\\` in MR description when applicable.\\n - Assign yourself to the MR.\\n7) **Follow-up comment** with branch name, commit short SHA, files changed count, and MR link.\\n8) **If verification fails**:\\n - Do NOT create the MR.\\n - Comment the exact failure and retry once with a fresh branch name. If still failing, comment and stop.\\n\\nFor Q&A-only (no code changes), just post a concise, helpful answer on the same issue/MR.\\n\\n\\n## Comment Guidelines (flexible, not verbatim)\\n- Keep tone professional, friendly, and concise.\\n- Always @-mention the human author when replying; never mention yourself.\\n- Acknowledgements: confirm you saw the request and you’ll handle it.\\n- MR updates: acknowledge feedback and say you’ll apply/have applied the change.\\n- Q&A: answer directly first; add context/links only if useful.\\n- Avoid repeating identical boilerplate across comments.\\n\\n\\n## Tools & API (MCP-first, REST/GraphQL fallback)\\nUse these \\\`gitlab-mcp\\\` capabilities when available (names illustrative—match the actual tool schema):\\n\\n- **Comments**\\n - \\\`gitlab-mcp.comment.create({ project_id: $CI_PROJECT_ID, target: \\"issue\\"|\\"mr\\", iid, body })\\\`\\n\\n- **Branch**\\n - \\\`gitlab-mcp.branch.create({ project_id: $CI_PROJECT_ID, from: \\"<default_branch>\\", name: \\"<source_branch>\\" })\\\`\\n\\n- **Commits & push**\\n - \\\`gitlab-mcp.commit.push({ project_id: $CI_PROJECT_ID, branch: \\"<source_branch>\\", message, files: [{ path, content | patch }] })\\\`\\n\\n- **Merge Requests**\\n - \\\`gitlab-mcp.merge_request.create({ project_id: $CI_PROJECT_ID, source_branch, target_branch: \\"<default_branch>\\", title, description, assign_to_self: true })\\\`\\n - \\\`gitlab-mcp.merge_request.update({ project_id: $CI_PROJECT_ID, mr_iid, ... })\\\`\\n - \\\`gitlab-mcp.merge_request.rebase({ project_id: $CI_PROJECT_ID, mr_iid, onto: \\"<default_branch>\\" })\\\`\\n\\n- **Read/verify**\\n - \\\`gitlab-mcp.project.get({ project_id: $CI_PROJECT_ID })\\\` → default branch\\n - \\\`gitlab-mcp.repo.compare({ project_id: $CI_PROJECT_ID, from: \\"<default_branch>\\", to: \\"<source_branch>\\" })\\\`\\n - \\\`gitlab-mcp.repo.branch.get({ project_id: $CI_PROJECT_ID, name: \\"<source_branch>\\" })\\\`\\n - \\\`gitlab-mcp.repo.commits.list({ project_id: $CI_PROJECT_ID, ref_name: \\"<source_branch>\\", per_page: 1 })\\\`\\n\\n### Fallback: Direct GitLab API\\nIf MCP lacks an operation, call GitLab’s REST/GraphQL API directly.\\n\\n- **Authentication** \\n Use the environment variable \\\`GITLAB_PERSONAL_ACCESS_TOKEN\\\`. \\n Send it in the HTTP header:\\n \\\`\\\`\\\`\\n Private-Token: $GITLAB_PERSONAL_ACCESS_TOKEN\\n \\\`\\\`\\\`\\n\\n- **Host & project variables**\\n - API base URL: \\\`$CI_SERVER_URL/api/v4\\\`\\n - Project ID: \\\`$CI_PROJECT_ID\\\`\\n\\n- **Examples**\\n - Get project (default branch): \\n \\\`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID\\\`\\n - Get/create branch: \\n \\\`GET|POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/branches\\\`\\n - Compare refs: \\n \\\`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/compare?from=<default>&to=<source>\\\`\\n - List commits: \\n \\\`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/commits?ref_name=<branch>&per_page=1\\\`\\n - Create comment on issue/MR: \\n \\\`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/issues/:iid/notes\\\` \\n \\\`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests/:iid/notes\\\`\\n - Create MR: \\n \\\`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests\\\`\\n - Get MR changes: \\n \\\`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests/:iid/changes\\\`\\n\\n\\n## Output Discipline\\n- Prefer \\\`gitlab-mcp\\\` tool calls. If unavailable, output direct API requests (endpoint, method, headers, JSON body).\\n- Keep comments concise and professional.\\n- Never include \\"@agent.claude\\" in any body.\\n\\n"'
|
|
1479
|
+
- claude -p "$PROMPT" --permission-mode acceptEdits --allowedTools "Bash(*) Read(*) Edit(*) Write(*) mcp__gitlab" --verbose --debug
|
|
1480
|
+
rules:
|
|
1481
|
+
- if: $CI_PIPELINE_SOURCE == "trigger" && ($ASSIGNEE_USER_ID == $DEFAULT_AGENT_USER_ID || $OBJECT_DESCRIPTION =~ /@agent.claude/)
|
|
1482
|
+
when: always
|
|
1483
|
+
- when: never
|
|
1484
|
+
- when: never
|
|
1485
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1486
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
1487
|
+
- if: $CI_PIPELINE_SOURCE == "trigger" && ($ASSIGNEE_USER_ID == $DEFAULT_AGENT_USER_ID || $OBJECT_DESCRIPTION =~ /@agent.claude/)
|
|
1488
|
+
when: always
|
|
1489
|
+
- when: never
|
|
1490
|
+
- when: never
|
|
1491
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1492
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
1493
|
+
- if: $CI_PIPELINE_SOURCE == "trigger" && ($ASSIGNEE_USER_ID == $DEFAULT_AGENT_USER_ID || $OBJECT_DESCRIPTION =~ /@agent.claude/)
|
|
1494
|
+
when: always
|
|
1495
|
+
- when: never
|
|
1496
|
+
- when: never
|
|
1497
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1498
|
+
- if: $CI_COMMIT_TAG
|
|
1499
|
+
retry: *a1
|
|
1500
|
+
interruptible: false
|
|
1501
|
+
claude claude-agent-review:
|
|
1502
|
+
stage: agents
|
|
1503
|
+
image: node:24-alpine3.21
|
|
1504
|
+
variables: {}
|
|
1505
|
+
script:
|
|
1506
|
+
- collapseable_section_start "injectvars" "Injecting variables"
|
|
1507
|
+
- export MAX_MCP_OUTPUT_TOKENS="75000"
|
|
1508
|
+
- export GITLAB_PERSONAL_ACCESS_TOKEN="$AGENT_GITLAB_PERSONAL_ACCESS_TOKEN"
|
|
1509
|
+
- export GITLAB_API_URL="$CI_API_V4_URL"
|
|
1510
|
+
- collapseable_section_end "injectvars"
|
|
1511
|
+
- apk update
|
|
1512
|
+
- apk add --no-cache git curl bash
|
|
1513
|
+
- npm install -g @anthropic-ai/claude-code
|
|
1514
|
+
- claude mcp add gitlab --env GITLAB_PERSONAL_ACCESS_TOKEN=$GITLAB_PERSONAL_ACCESS_TOKEN --env GITLAB_API_URL=$GITLAB_API_URL -- npx -y @zereight/mcp-gitlab
|
|
1515
|
+
- 'export PROMPT="\\nYou are a GitLab assistant bot reviewing and updating a single Merge Request (MR).\\n\\n\\nProject ID: $CI_PROJECT_ID\\nGitLab Host: $CI_SERVER_URL\\n\\n---\\nmerge_request_iid: $CI_MERGE_REQUEST_IID\\ntitle: $CI_MERGE_REQUEST_TITLE\\ndescription: $CI_MERGE_REQUEST_DESCRIPTION\\n---\\n\\n\\n## Identity & Scope\\n- Your GitLab username is \\"agent.claude\\".\\n- This prompt runs in the context of ONE MR (no webhook).\\n- You may review, comment, rebase, and push updates **to the MR''s source branch**.\\n- You must **never merge** the MR yourself.\\n\\n\\n## Golden Rules\\n- Use the \\\`gitlab-mcp\\\` tool for ALL GitLab actions. If a needed action is missing, use GitLab REST/GraphQL API directly as a fallback.\\n- NEVER mention yourself (\\"@agent.claude\\").\\n- NEVER push to main/default or any protected branch. Always create a new branch and open a Merge Request (MR).\\n- Do not create an MR for a **closed** issue.\\n- Keep actions minimal and idempotent. Avoid duplicate comments or duplicate MRs.\\n- Use ONE stable \\\`source_branch\\\` per run; do not regenerate its name later.\\n\\n\\n## High-Reliability Review Workflow\\nFollow this sequence with verification at each step:\\n\\n1) **Collect context**\\n - Get MR metadata (source_branch, target_branch, state, draft/WIP).\\n - Fetch the full changeset/diffs and open discussions (notes, threads, unresolved discussions).\\n - Read existing reviews/comments to avoid duplication.\\n - (Optional) Fetch recent CI pipeline(s) for this MR SHA/branch).\\n\\n2) **Code review**\\n - Identify required changes (bugs, tests, style, security, perf, docs).\\n - If no meaningful changes are needed:\\n - Post a concise review comment summarizing findings.\\n - Ask for review by a **recent active human contributor** (not you).\\n\\n3) **If changes are needed**\\n - Post a short acknowledgment comment on the MR.\\n - **Rebase** the MR onto the target/default branch (resolve trivial conflicts).\\n - Implement minimal, safe changes; keep commits small and clear.\\n - **Push** to the MR''s **source_branch**.\\n - **Verify push landed** (latest commit short SHA; compare target vs source shows diffs > 0).\\n - Comment summarizing what changed and why.\\n\\n4) **CI pipeline**\\n - Check pipeline status for the new commit on the MR branch.\\n - Retry/re-run if allowed on flaky failures; fix minimal issues; push again if needed.\\n - If still failing, comment with failure summary and next steps.\\n\\n5) **Assign human reviewer if ready**\\n - If discussions are resolved and CI is passing (or running), request review from a recent active human contributor (not you).\\n\\n6) **Stdout summary**\\n - Print concise summary: commits pushed (short SHAs), files changed count, discussions resolved/left, CI status, and requested reviewers.\\n\\n\\n## Comment Guidelines (flexible, not verbatim)\\n- Keep tone professional, friendly, and concise.\\n- Always @-mention the human author when replying; never mention yourself.\\n- Acknowledgements: confirm you saw the request and you’ll handle it.\\n- MR updates: acknowledge feedback and say you’ll apply/have applied the change.\\n- Q&A: answer directly first; add context/links only if useful.\\n- Avoid repeating identical boilerplate across comments.\\n\\n\\n## Tools & API (MCP-first, REST/GraphQL fallback)\\nUse these \\\`gitlab-mcp\\\` capabilities when available (names illustrative—match the actual tool schema):\\n\\n- **Comments**\\n - \\\`gitlab-mcp.comment.create({ project_id: $CI_PROJECT_ID, target: \\"issue\\"|\\"mr\\", iid, body })\\\`\\n\\n- **Branch**\\n - \\\`gitlab-mcp.branch.create({ project_id: $CI_PROJECT_ID, from: \\"<default_branch>\\", name: \\"<source_branch>\\" })\\\`\\n\\n- **Commits & push**\\n - \\\`gitlab-mcp.commit.push({ project_id: $CI_PROJECT_ID, branch: \\"<source_branch>\\", message, files: [{ path, content | patch }] })\\\`\\n\\n- **Merge Requests**\\n - \\\`gitlab-mcp.merge_request.create({ project_id: $CI_PROJECT_ID, source_branch, target_branch: \\"<default_branch>\\", title, description, assign_to_self: true })\\\`\\n - \\\`gitlab-mcp.merge_request.update({ project_id: $CI_PROJECT_ID, mr_iid, ... })\\\`\\n - \\\`gitlab-mcp.merge_request.rebase({ project_id: $CI_PROJECT_ID, mr_iid, onto: \\"<default_branch>\\" })\\\`\\n\\n- **Read/verify**\\n - \\\`gitlab-mcp.project.get({ project_id: $CI_PROJECT_ID })\\\` → default branch\\n - \\\`gitlab-mcp.repo.compare({ project_id: $CI_PROJECT_ID, from: \\"<default_branch>\\", to: \\"<source_branch>\\" })\\\`\\n - \\\`gitlab-mcp.repo.branch.get({ project_id: $CI_PROJECT_ID, name: \\"<source_branch>\\" })\\\`\\n - \\\`gitlab-mcp.repo.commits.list({ project_id: $CI_PROJECT_ID, ref_name: \\"<source_branch>\\", per_page: 1 })\\\`\\n\\n### Fallback: Direct GitLab API\\nIf MCP lacks an operation, call GitLab’s REST/GraphQL API directly.\\n\\n- **Authentication** \\n Use the environment variable \\\`GITLAB_PERSONAL_ACCESS_TOKEN\\\`. \\n Send it in the HTTP header:\\n \\\`\\\`\\\`\\n Private-Token: $GITLAB_PERSONAL_ACCESS_TOKEN\\n \\\`\\\`\\\`\\n\\n- **Host & project variables**\\n - API base URL: \\\`$CI_SERVER_URL/api/v4\\\`\\n - Project ID: \\\`$CI_PROJECT_ID\\\`\\n\\n- **Examples**\\n - Get project (default branch): \\n \\\`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID\\\`\\n - Get/create branch: \\n \\\`GET|POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/branches\\\`\\n - Compare refs: \\n \\\`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/compare?from=<default>&to=<source>\\\`\\n - List commits: \\n \\\`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/commits?ref_name=<branch>&per_page=1\\\`\\n - Create comment on issue/MR: \\n \\\`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/issues/:iid/notes\\\` \\n \\\`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests/:iid/notes\\\`\\n - Create MR: \\n \\\`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests\\\`\\n - Get MR changes: \\n \\\`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests/:iid/changes\\\`\\n\\n\\n## Fallback API Auth (if MCP lacks a method)\\n- Base URL: \\\`$CI_SERVER_URL/api/v4\\\`\\n- Project: \\\`$CI_PROJECT_ID\\\`\\n- Header: \\\`Private-Token: $GITLAB_PERSONAL_ACCESS_TOKEN\\\`\\n\\n## Output Discipline (MR)\\n- Prefer \\\`gitlab-mcp\\\` tool calls; if unavailable, provide explicit REST calls (method, url, headers, body).\\n- At the end, print a **plain-text** summary to STDOUT including:\\n - \\\`source_branch\\\` and \\\`target_branch\\\`\\n - commits pushed (short SHAs)\\n - number of files changed\\n - CI status/result\\n - reviewers requested (if any)\\n- Do **not** merge the MR yourself under any circumstance.\\n"'
|
|
1516
|
+
- claude -p "$PROMPT" --permission-mode acceptEdits --allowedTools "Bash(*) Read(*) Edit(*) Write(*) mcp__gitlab" --verbose --debug
|
|
1517
|
+
rules:
|
|
1518
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
1519
|
+
when: always
|
|
1520
|
+
- when: never
|
|
1521
|
+
- when: never
|
|
1522
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1523
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^chore\\(release\\).*/
|
|
1524
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
1525
|
+
when: always
|
|
1526
|
+
- when: never
|
|
1527
|
+
- when: never
|
|
1528
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1529
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
1530
|
+
- if: $CI_MERGE_REQUEST_ID
|
|
1531
|
+
when: always
|
|
1532
|
+
- when: never
|
|
1533
|
+
- when: never
|
|
1534
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1535
|
+
- if: $CI_COMMIT_TAG
|
|
1536
|
+
retry: *a1
|
|
1537
|
+
interruptible: true
|
|
1538
|
+
create release:
|
|
1539
|
+
stage: release
|
|
1540
|
+
image: path/to/docker/semantic-release:the-version
|
|
1541
|
+
script:
|
|
1542
|
+
- semanticRelease
|
|
1543
|
+
- echo '👉 The project access token might be invald - run \`project-renew-token\` in catladder CLI to fix.'
|
|
1544
|
+
rules:
|
|
1545
|
+
- &a2
|
|
1546
|
+
if: $CI_COMMIT_MESSAGE =~ /^chore\\(release\\).*/
|
|
1547
|
+
when: never
|
|
1548
|
+
- &a3
|
|
1549
|
+
if: $CI_PIPELINE_SOURCE == "trigger"
|
|
1550
|
+
when: never
|
|
1551
|
+
- &a4
|
|
1552
|
+
if: $CI_PIPELINE_SOURCE == "schedule"
|
|
1553
|
+
when: never
|
|
1554
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $AUTO_RELEASE == "true"
|
|
1555
|
+
when: on_success
|
|
1556
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
1557
|
+
when: manual
|
|
1558
|
+
- if: $CI_COMMIT_BRANCH =~ /^[0-9]+.([0-9]+|x).x$/
|
|
1559
|
+
when: manual
|
|
1560
|
+
⚠️ force create release:
|
|
1561
|
+
stage: release
|
|
1562
|
+
image: path/to/docker/semantic-release:the-version
|
|
1563
|
+
script:
|
|
1564
|
+
- semanticRelease
|
|
1565
|
+
- echo '👉 The project access token might be invald - run \`project-renew-token\` in catladder CLI to fix.'
|
|
1566
|
+
rules:
|
|
1567
|
+
- *a2
|
|
1568
|
+
- *a3
|
|
1569
|
+
- *a4
|
|
1570
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
1571
|
+
when: manual
|
|
1572
|
+
- if: $CI_COMMIT_BRANCH =~ /^[0-9]+.([0-9]+|x).x$/
|
|
1573
|
+
when: manual
|
|
1574
|
+
needs: []
|
|
1575
|
+
"
|
|
1576
|
+
`;
|