-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·360 lines (308 loc) · 12.4 KB
/
build.sh
File metadata and controls
executable file
·360 lines (308 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
#!/bin/bash
# Build script for SonarSource Poetry projects.
# Supports building, testing, SonarQube analysis, and JFrog Artifactory deployment.
#
# Required inputs (must be explicitly provided):
# - BUILD_NUMBER: Build number for versioning
# - ARTIFACTORY_URL: URL to Artifactory repository
# - ARTIFACTORY_PYPI_REPO: Repository to install dependencies from
# - ARTIFACTORY_ACCESS_TOKEN: Access token to read Repox repositories
# - ARTIFACTORY_DEPLOY_REPO: Deployment repository name
# - ARTIFACTORY_DEPLOY_ACCESS_TOKEN: Access token to deploy to the repository
# - DEFAULT_BRANCH: Default branch name (e.g. main)
# - PULL_REQUEST: Pull request number (e.g. 1234) or empty string
# - SONAR_PLATFORM: SonarQube primary platform (next, sqc-eu, or sqc-us)
# - NEXT_URL: URL of SonarQube server for next platform
# - NEXT_TOKEN: Access token to send analysis reports to SonarQube for next platform
# - SQC_US_URL: URL of SonarQube server for sqc-us platform
# - SQC_US_TOKEN: Access token to send analysis reports to SonarQube for sqc-us platform
# - SQC_EU_URL: URL of SonarQube server for sqc-eu platform
# - SQC_EU_TOKEN: Access token to send analysis reports to SonarQube for sqc-eu platform
# - RUN_SHADOW_SCANS: If true, run sonar scanner on all 3 platforms. If false, run on the platform provided by SONAR_PLATFORM.
#
# GitHub Actions auto-provided:
# - GITHUB_REF_NAME: Git branch name
# - GITHUB_SHA: Git commit SHA
# - GITHUB_REPOSITORY: Repository name (e.g. sonarsource/sonar-dummy-poetry)
# - GITHUB_RUN_ID: GitHub Actions run ID
# - GITHUB_EVENT_NAME: Event name (e.g. push, pull_request)
# - GITHUB_EVENT_PATH: Path to the event webhook payload file
# - GITHUB_ENV: Path to GitHub Actions environment file
# - GITHUB_OUTPUT: Path to GitHub Actions output file
# - GITHUB_BASE_REF: Base branch for pull requests (only during pull_request events)
#
# Optional user customization:
# - DEPLOY_PULL_REQUEST: Whether to deploy pull request artifacts (default: false)
#
# Auto-derived by script:
# - PROJECT: Project name derived from GITHUB_REPOSITORY
# shellcheck source-path=SCRIPTDIR
set -euo pipefail
# shellcheck source=../shared/common-functions.sh
source "$(dirname "${BASH_SOURCE[0]}")/../shared/common-functions.sh"
: "${ARTIFACTORY_URL:?}"
: "${ARTIFACTORY_PYPI_REPO:?}" "${ARTIFACTORY_ACCESS_TOKEN:?}" "${ARTIFACTORY_DEPLOY_REPO:?}" "${ARTIFACTORY_DEPLOY_ACCESS_TOKEN:?}"
: "${GITHUB_REF_NAME:?}" "${BUILD_NUMBER:?}" "${GITHUB_REPOSITORY:?}" "${GITHUB_EVENT_NAME:?}" "${GITHUB_EVENT_PATH:?}"
: "${PULL_REQUEST?}" "${DEFAULT_BRANCH:?}"
: "${GITHUB_ENV:?}" "${GITHUB_OUTPUT:?}" "${GITHUB_SHA:?}" "${GITHUB_RUN_ID:?}"
# Only validate sonar credentials if platform is not 'none'
if [[ "${SONAR_PLATFORM:?}" != "none" ]]; then
: "${NEXT_URL:?}" "${NEXT_TOKEN:?}" "${SQC_US_URL:?}" "${SQC_US_TOKEN:?}" "${SQC_EU_URL:?}" "${SQC_EU_TOKEN:?}"
fi
: "${RUN_SHADOW_SCANS:?}"
: "${DEPLOY_PULL_REQUEST:=false}"
export ARTIFACTORY_URL DEPLOY_PULL_REQUEST
# Unshallow and fetch all commit history for SonarQube analysis and issue assignment
git_fetch_unshallow() {
if [ "$SONAR_PLATFORM" = "none" ]; then
echo "Skipping git fetch (sonar analysis disabled)"
return 0
fi
if git rev-parse --is-shallow-repository --quiet >/dev/null 2>&1; then
echo "Fetch Git references for SonarQube analysis..."
git fetch --unshallow || true # Ignore errors like "fatal: --unshallow on a complete repository does not make sense"
elif [ -n "${GITHUB_BASE_REF:-}" ]; then
echo "Fetch ${GITHUB_BASE_REF} for SonarQube analysis..."
git fetch origin "${GITHUB_BASE_REF}"
fi
}
set_sonar_platform_vars() {
local platform="$1"
# TODO: The SONAR_REGION variable can be removed once SCANPY-203 is fixed
case "$platform" in
"next")
export SONAR_HOST_URL="$NEXT_URL"
export SONAR_TOKEN="$NEXT_TOKEN"
export SONAR_REGION=""
;;
"sqc-us")
export SONAR_HOST_URL="$SQC_US_URL"
export SONAR_TOKEN="$SQC_US_TOKEN"
export SONAR_REGION="us"
;;
"sqc-eu")
export SONAR_HOST_URL="$SQC_EU_URL"
export SONAR_TOKEN="$SQC_EU_TOKEN"
export SONAR_REGION=""
;;
"none")
echo "Sonar analysis disabled (platform: none)"
return 0
;;
*)
echo "ERROR: Unknown sonar platform '$platform'. Expected: next, sqc-us, sqc-eu, or none" >&2
return 1
;;
esac
echo "Using Sonar platform: $platform (URL: $SONAR_HOST_URL)"
}
run_sonar_scanner() {
local additional_params=("$@")
# Install pysonar into Poetry's virtual environment without modifying project files
poetry run pip install pysonar
echo "Poetry command: poetry run pysonar ..." \
"-Dsonar.host.url=${SONAR_HOST_URL}" \
"-Dsonar.analysis.buildNumber=${BUILD_NUMBER}" \
"-Dsonar.analysis.pipeline=${GITHUB_RUN_ID}" \
"-Dsonar.analysis.repository=${GITHUB_REPOSITORY}" \
"${additional_params[@]+${additional_params[@]}}"
poetry run pysonar \
-Dsonar.host.url="${SONAR_HOST_URL}" \
-Dsonar.token="${SONAR_TOKEN}" \
-Dsonar.analysis.buildNumber="${BUILD_NUMBER}" \
-Dsonar.analysis.pipeline="${GITHUB_RUN_ID}" \
-Dsonar.analysis.repository="${GITHUB_REPOSITORY}" \
"${additional_params[@]+${additional_params[@]}}"
}
run_sonar_analysis() {
local sonar_args=("$@")
echo "run_sonar_analysis()"
if [ "${RUN_SHADOW_SCANS}" = "true" ]; then
echo "=== Running Sonar analysis on all platforms (shadow scan enabled) ==="
local platforms=("next" "sqc-us" "sqc-eu")
for platform in "${platforms[@]}"; do
echo "::group::Sonar analysis on $platform"
echo "--- ORCHESTRATOR: Analyzing with platform: $platform ---"
set_sonar_platform_vars "$platform"
run_sonar_scanner "${sonar_args[@]+${sonar_args[@]}}"
echo "::endgroup::"
done
echo "=== Completed Sonar analysis on all platforms ==="
else
if [ "$SONAR_PLATFORM" = "none" ]; then
echo "=== Sonar platform set to 'none'. Skipping Sonar analysis."
return 0
fi
echo "=== Running Sonar analysis on selected platform: $SONAR_PLATFORM ==="
echo "::group::Sonar analysis on $SONAR_PLATFORM"
set_sonar_platform_vars "$SONAR_PLATFORM"
run_sonar_scanner "${sonar_args[@]+${sonar_args[@]}}"
echo "::endgroup::"
fi
}
# FIXME BUILD-8337? this is similar to source github-env <BUILD|BUILD-PRIVATE>
set_build_env() {
export PROJECT=${GITHUB_REPOSITORY#*/}
echo "PROJECT: $PROJECT"
git_fetch_unshallow
}
set_project_version() {
local current_version release_version digit_count
if ! current_version=$(poetry version -s); then
echo "Could not get version from Poetry project ('poetry version -s')" >&2
echo "$current_version" >&2
return 1
fi
export CURRENT_VERSION=$current_version
release_version=${current_version%".dev"*}
# In case of 2 digits, we need to add a '0' as the 3rd digit.
digit_count=$(echo "${release_version//./ }" | wc -w)
if [[ "$digit_count" -lt 3 ]]; then
release_version="$release_version.0"
fi
if [[ "$digit_count" -gt 3 && $release_version =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
release_version="${BASH_REMATCH[0]}"
echo "WARN: version was truncated to $release_version because it had more than 3 digits"
fi
release_version="$release_version.${BUILD_NUMBER}"
echo "Replacing version $current_version with $release_version"
poetry version "$release_version"
echo "project-version=$release_version" >> "$GITHUB_OUTPUT"
echo "PROJECT_VERSION=$release_version" >> "$GITHUB_ENV"
echo "PROJECT_VERSION=$release_version"
export PROJECT_VERSION=$release_version
}
# Determine build configuration based on branch type
# TODO BUILD-10586: this function does not support a DEPLOY env var to override deployment (unlike build-maven and build-gradle).
# Should add a DEPLOY=${DEPLOY:=true} check consistent with those build scripts.
# Note: unlike build-maven and build-gradle, long-lived feature branches (feature/long/*) do not deploy here.
get_build_config() {
local enable_sonar enable_deploy
local sonar_args=()
if is_default_branch && ! is_pull_request; then
echo "======= Building main branch ======="
enable_sonar=true
enable_deploy=true
elif is_maintenance_branch && ! is_pull_request; then
echo "======= Building maintenance branch ======="
enable_sonar=true
enable_deploy=true
sonar_args=("-Dsonar.branch.name=${GITHUB_REF_NAME}")
elif is_pull_request; then
echo "======= Building pull request ======="
enable_sonar=true
sonar_args=("-Dsonar.analysis.prNumber=${PULL_REQUEST}")
if [ "${DEPLOY_PULL_REQUEST:-false}" == "true" ]; then
echo "======= with deploy ======="
enable_deploy=true
else
echo "======= no deploy ======="
enable_deploy=false
fi
elif is_dogfood_branch && ! is_pull_request; then
echo "======= Build dogfood branch ======="
enable_sonar=false
enable_deploy=true
elif is_long_lived_feature_branch && ! is_pull_request; then
echo "======= Build long-lived feature branch ======="
enable_sonar=true
enable_deploy=false
sonar_args=("-Dsonar.branch.name=${GITHUB_REF_NAME}")
else
echo "======= Build other branch ======="
enable_sonar=false
enable_deploy=false
fi
# Export the configuration for use by build_poetry
export BUILD_ENABLE_SONAR="$enable_sonar"
export BUILD_ENABLE_DEPLOY="$enable_deploy"
if [[ "$enable_deploy" = "true" ]]; then
echo "deployed=true" >> "$GITHUB_OUTPUT"
fi
export BUILD_SONAR_ARGS="${sonar_args[*]:-}"
}
jfrog_poetry_install() {
jf config add repox --artifactory-url "$ARTIFACTORY_URL" --access-token "$ARTIFACTORY_ACCESS_TOKEN"
jf poetry-config --server-id-resolve repox --repo-resolve "$ARTIFACTORY_PYPI_REPO"
jf poetry install --build-name="$PROJECT" --build-number="$BUILD_NUMBER"
}
jfrog_poetry_publish() {
jf config remove repox
jf config add repox --artifactory-url "$ARTIFACTORY_URL" --access-token "$ARTIFACTORY_DEPLOY_ACCESS_TOKEN"
project_name=$(poetry version | awk '{print $1}')
pushd dist
jf rt upload ./ "$ARTIFACTORY_DEPLOY_REPO/$project_name/$PROJECT_VERSION/" --module="$project_name:$PROJECT_VERSION" \
--build-name="$PROJECT" --build-number="$BUILD_NUMBER"
popd
jf rt build-collect-env "$PROJECT" "$BUILD_NUMBER"
jf rt build-publish "$PROJECT" "$BUILD_NUMBER" \
--env-include 'PROJECT;GIT_*;*VERSION*;BUILD_*;GITHUB_*;*BRANCH*;*ID;PULL_REQUEST*;ARTIFACTORY*' \
--env-exclude "*login*;*pass*;*psw*;*pwd*;*secret*;*key*;*token*;*auth*" \
--overwrite # avoid duplicate builds on re-runs
}
build_poetry() {
echo "=== Poetry Build, Deploy, and Analyze ==="
echo "Branch: ${GITHUB_REF_NAME}"
echo "Pull Request: ${PULL_REQUEST}"
echo "Deploy Pull Request: ${DEPLOY_PULL_REQUEST}"
echo "::group::Set project version"
set_project_version
echo "::endgroup::"
get_build_config
echo "::group::Install dependencies"
echo "Installing dependencies..."
jfrog_poetry_install
echo "::endgroup::"
echo "::group::Build project"
echo "Building project..."
poetry build
echo "::endgroup::"
if [ "${BUILD_ENABLE_SONAR}" = "true" ]; then
read -ra sonar_args <<< "$BUILD_SONAR_ARGS"
# run_sonar_analysis emits its own groups
run_sonar_analysis "${sonar_args[@]+${sonar_args[@]}}"
fi
if [ "${BUILD_ENABLE_DEPLOY}" = "true" ]; then
echo "::group::Publish to Artifactory"
jfrog_poetry_publish
echo "::endgroup::"
export_built_artifacts
fi
echo "=== Build completed successfully ==="
}
export_built_artifacts() {
local deployed
deployed=$(grep "deployed=" "$GITHUB_OUTPUT" 2>/dev/null | cut -d= -f2)
[[ "$deployed" != "true" ]] && return 0
echo "::group::Capturing built artifacts for attestation"
local artifacts
artifacts=$(/usr/bin/find dist -type f \( -name '*.tar.gz' -o -name '*.whl' -o -name '*.json' \) 2>/dev/null || true)
if [[ -z "$artifacts" ]]; then
echo "::warning title=No artifacts found::No artifacts found for attestation in build output directories"
echo "::endgroup::"
return 0
fi
echo "Found artifacts for attestation:"
echo "$artifacts"
{
echo "artifact-paths<<EOF"
echo "$artifacts"
echo "EOF"
} >> "$GITHUB_OUTPUT"
echo "::endgroup::"
}
main() {
echo "::group::Check tools"
check_tool jq --version
check_tool python --version
check_tool poetry --version
check_tool jf --version
echo "::endgroup::"
echo "::group::Configure build environment"
set_build_env
echo "::endgroup::"
build_poetry
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi