Skip to content

Commit 6334ddd

Browse files
committed
chore: Improve Windows build time
1 parent 07d3e04 commit 6334ddd

8 files changed

Lines changed: 393 additions & 81 deletions

File tree

.circleci/config.yml

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ parameters:
3737
windows_bash_env_script:
3838
type: string
3939
default: 'snyk-env.sh'
40+
# TEMPORARY (CLI-1633): set true to run only the minimal Windows/Linux build workflow.
41+
# Revert to false before merging to main.
42+
minimal_ci_test:
43+
type: boolean
44+
default: false
4045

4146
orbs:
4247
prodsec: snyk/prodsec-orb@1
@@ -264,6 +269,53 @@ commands:
264269
$bashLine = 'export PATH="' + $bashPath + ':$PATH"'
265270
[System.IO.File]::AppendAllText($bashEnvScript, $bashLine + "`n", (New-Object System.Text.UTF8Encoding $false))
266271
272+
# Minimal Windows toolchain for COMPILING the CLI: Node, Python, Make only.
273+
# Excludes .NET/Maven/Gradle (needed only by acceptance tests), which keeps
274+
# this cache small and fast to restore compared to the full tools cache.
275+
install-deps-windows-native-build-min:
276+
steps:
277+
- run:
278+
name: Ensure tools cache directory
279+
shell: powershell
280+
command: |
281+
$cacheDir = '<< pipeline.parameters.windows_cache_dir >>'
282+
if (-not (Test-Path $cacheDir)) {
283+
New-Item -ItemType Directory -Path $cacheDir | Out-Null
284+
}
285+
- restore_cache:
286+
name: Restoring Windows build tools cache
287+
keys:
288+
- windows-build-tools-cache-v1-{{ arch }}-{{ checksum ".nvmrc" }}
289+
- run:
290+
name: Install Node.js (native)
291+
shell: powershell
292+
command: |
293+
.\scripts\windows\install-node.ps1
294+
- run:
295+
name: Install Python 3.12.8 (native)
296+
shell: powershell
297+
command: |
298+
.\scripts\windows\install-python3.ps1
299+
- run:
300+
name: Install GNU Make 4.4.1 (native)
301+
shell: powershell
302+
command: |
303+
.\scripts\windows\install-make.ps1
304+
- run:
305+
name: Ensure Python and uv
306+
shell: powershell
307+
command: |
308+
if (Test-Path '<< pipeline.parameters.windows_cache_dir >>/<< pipeline.parameters.windows_env_script >>') { . '<< pipeline.parameters.windows_cache_dir >>/<< pipeline.parameters.windows_env_script >>' }
309+
.\scripts\windows\ensure-python-uv.ps1
310+
- save_cache:
311+
name: Saving Windows build tools cache
312+
key: windows-build-tools-cache-v1-{{ arch }}-{{ checksum ".nvmrc" }}
313+
paths:
314+
- << pipeline.parameters.windows_cache_dir >>
315+
- C:\ProgramData\nvm
316+
- C:\tools\make
317+
- C:\Program Files\Python312
318+
267319
install-deps-windows-native-build:
268320
steps:
269321
- run:
@@ -277,6 +329,7 @@ commands:
277329
- restore_cache:
278330
name: Restoring Windows tools cache
279331
keys:
332+
- windows-tools-cache-v6-{{ arch }}-{{ checksum ".nvmrc" }}
280333
- windows-tools-cache-v5-{{ arch }}-{{ checksum ".nvmrc" }}
281334
- run:
282335
name: Install Node.js (native)
@@ -316,10 +369,14 @@ commands:
316369
.\scripts\windows\ensure-python-uv.ps1
317370
- save_cache:
318371
name: Saving Windows tools cache
319-
key: windows-tools-cache-v5-{{ arch }}-{{ checksum ".nvmrc" }}
372+
key: windows-tools-cache-v6-{{ arch }}-{{ checksum ".nvmrc" }}
320373
paths:
321374
- << pipeline.parameters.windows_cache_dir >>
322375
- C:\ProgramData\nvm
376+
- C:\tools\maven
377+
- C:\tools\make
378+
- C:\Program Files\dotnet
379+
- C:\Program Files\Python312
323380

324381
install-deps-windows-native-full-signing:
325382
steps:
@@ -380,6 +437,22 @@ commands:
380437
name: No dependencies to install
381438
command: echo all done!
382439

440+
setup-go-build-cache:
441+
description: >-
442+
Pin GOMODCACHE and GOCACHE to workspace paths so CircleCI cache
443+
restore/save works on Linux, macOS, and Windows executors.
444+
steps:
445+
- run:
446+
name: Configure Go module and build cache paths
447+
shell: bash
448+
command: |
449+
if [ -f "/c/tools-cache/<< pipeline.parameters.windows_bash_env_script >>" ]; then
450+
source "/c/tools-cache/<< pipeline.parameters.windows_bash_env_script >>"
451+
fi
452+
mkdir -p .go-cache/mod .go-cache/build
453+
echo "export GOMODCACHE=\"$(pwd)/.go-cache/mod\"" >> "$BASH_ENV"
454+
echo "export GOCACHE=\"$(pwd)/.go-cache/build\"" >> "$BASH_ENV"
455+
383456
setup-go-private-modules:
384457
description: >-
385458
Configure git and Go to fetch private snyk modules using the
@@ -551,7 +624,74 @@ commands:
551624
####################################################################################################
552625

553626
workflows:
627+
# TEMPORARY (CLI-1633): isolated build perf test — prepare-build + linux amd64 + windows x2.
628+
# Toggle pipeline parameter minimal_ci_test=false to restore full CI.
629+
test_windows_build_perf:
630+
when: << pipeline.parameters.minimal_ci_test >>
631+
jobs:
632+
- prepare-build:
633+
context:
634+
- devex_cli_docker_hub
635+
- go-private-modules
636+
637+
- build-artifact:
638+
name: build linux amd64
639+
context:
640+
- iac-cli
641+
- go-private-modules
642+
- devex_cli_docker_hub
643+
go_target_os: linux
644+
go_os: linux
645+
go_arch: amd64
646+
static_binary: true
647+
go_download_base_url: << pipeline.parameters.go_download_base_url >>
648+
executor: docker-amd64-xl
649+
requires:
650+
- prepare-build
651+
652+
- build-artifact:
653+
name: build windows amd64
654+
go_target_os: windows
655+
go_os: windows
656+
go_arch: amd64
657+
go_download_base_url: << pipeline.parameters.fips_go_download_base_url >>
658+
make_target: build build-fips verify-fips-artifacts BUILD_MODE=private
659+
install_deps_extension: windows-native-build-min
660+
install_path: 'C:\'
661+
executor: win-server2022-amd64
662+
context:
663+
- iac-cli
664+
- go-private-modules
665+
requires:
666+
- prepare-build
667+
668+
- acceptance-tests:
669+
name: acceptance-tests windows amd64
670+
go_target_os: windows
671+
go_os: windows
672+
go_arch: amd64
673+
go_download_base_url: << pipeline.parameters.go_download_base_url >>
674+
context:
675+
- nodejs-install
676+
- team_hammerhead-cli
677+
- team-cli-workflow-context
678+
requires:
679+
- build windows amd64
680+
executor: win-server2022-amd64
681+
test_snyk_command: binary-releases\\snyk-win.exe
682+
install_deps_extension: windows-native-build
683+
dont_skip_tests: 0
684+
shards: 8
685+
pre_test_cmds: |
686+
if (Test-Path '<< pipeline.parameters.windows_cache_dir >>/<< pipeline.parameters.windows_env_script >>') { . '<< pipeline.parameters.windows_cache_dir >>/<< pipeline.parameters.windows_env_script >>' }
687+
test_cmd: |
688+
Get-ChildItem -Recurse -Path test/jest/acceptance,ts-binary-wrapper/test/acceptance -Filter *.spec.ts |
689+
ForEach-Object { (Resolve-Path -Relative $_.FullName) -replace '\\', '/' -replace '^\./', '' } |
690+
circleci tests run --command "xargs npx jest --maxWorkers=1 --selectProjects coreCli --reporters=jest-junit --reporters=default --forceExit --" --split-by=timings --timings-type=file
691+
554692
test_and_release:
693+
when:
694+
not: << pipeline.parameters.minimal_ci_test >>
555695
jobs:
556696
- prodsec/secrets-scan:
557697
name: secrets-scan
@@ -817,12 +957,11 @@ workflows:
817957
go_os: windows
818958
go_arch: amd64
819959
go_download_base_url: << pipeline.parameters.fips_go_download_base_url >>
820-
make_target: build clean-golang build-fips BUILD_MODE=private
821-
install_deps_extension: windows-native-full-signing
960+
make_target: build build-fips verify-fips-artifacts BUILD_MODE=private
961+
install_deps_extension: windows-native-build-min
822962
install_path: 'C:\'
823963
executor: win-server2022-amd64
824964
context:
825-
- snyk-windows-signing
826965
- iac-cli
827966
- go-private-modules
828967
requires:
@@ -1681,17 +1820,25 @@ jobs:
16811820
base_url: << parameters.go_download_base_url >>
16821821
extraction_path: << parameters.install_path >>
16831822
- setup-go-private-modules
1823+
- setup-go-build-cache
16841824
- restore_cache:
1685-
key: go-build-{{ arch }}-{{ checksum "cliv2/go.sum" }}
1825+
name: Restoring Go module and build cache
1826+
keys:
1827+
- go-build-v2-{{ arch }}-{{ checksum "cliv2/go.sum" }}
1828+
- go-build-v2-{{ arch }}-
1829+
- go-build-{{ arch }}-{{ checksum "cliv2/go.sum" }}
16861830
- make-binary:
16871831
static: << parameters.static_binary >>
16881832
go_arch: << parameters.go_arch >>
16891833
go_target_os: << parameters.go_target_os >>
16901834
make_target: << parameters.make_target >>
16911835
c_compiler: << parameters.c_compiler >>
16921836
- save_cache:
1693-
key: go-build-{{ arch }}-{{ checksum "cliv2/go.sum" }}
1694-
paths: [/home/circleci/go/pkg/mod]
1837+
name: Saving Go module and build cache
1838+
key: go-build-v2-{{ arch }}-{{ checksum "cliv2/go.sum" }}
1839+
paths:
1840+
- .go-cache/mod
1841+
- .go-cache/build
16951842
- store_artifacts:
16961843
path: binary-releases
16971844
- persist_to_workspace:

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,14 @@ build-fips: pre-build $(BINARY_OUTPUT_FOLDER)/fips/version
254254
@cd $(EXTENSIBLE_CLI_DIR); $(MAKE) fips build-full install bindir=$(WORKING_DIR)/$(BINARY_OUTPUT_FOLDER)/fips USE_LEGACY_EXECUTABLE_NAME=1 BUILD_MODE=$(BUILD_MODE)
255255
@$(MAKE) clean-package-files
256256

257+
# Confirms the FIPS binary is genuinely distinct from the normal binary. This is
258+
# the safety net that lets us build normal + FIPS in one job without clean-golang:
259+
# if the FIPS build were ever skipped, the two binaries would be identical and
260+
# this target fails. Requires both `build` and `build-fips` to have run first.
261+
.PHONY: verify-fips-artifacts
262+
verify-fips-artifacts:
263+
@bash ./scripts/verify-fips-build.sh $(BINARY_OUTPUT_FOLDER) $(BINARY_OUTPUT_FOLDER)/fips
264+
257265
.PHONY: build-experimental
258266
build-experimental: pre-build $(BINARY_OUTPUT_FOLDER)/experimental/version
259267
@cd $(EXTENSIBLE_CLI_DIR); $(MAKE) experimental build-full install bindir=$(WORKING_DIR)/$(BINARY_OUTPUT_FOLDER)/experimental USE_LEGACY_EXECUTABLE_NAME=1 BUILD_MODE=$(BUILD_MODE)

scripts/verify-fips-build.sh

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env bash
2+
# Verifies that the FIPS build actually produced a binary distinct from the
3+
# normal build.
4+
#
5+
# Why this exists: the normal and FIPS builds compile to the same intermediate
6+
# output path and are only distinguished by the crypto backend. If a regression
7+
# (e.g. removing `clean-golang` combined with a stale build output) caused the
8+
# FIPS build to be skipped, the FIPS and normal binaries would be byte-identical.
9+
# This script fails loudly in that case, and logs the Go build settings of both
10+
# binaries so a human can confirm the FIPS crypto backend is present.
11+
#
12+
# Usage: verify-fips-build.sh <normal-dir> <fips-dir>
13+
set -euo pipefail
14+
15+
normal_dir="${1:?normal binary directory required}"
16+
fips_dir="${2:?fips binary directory required}"
17+
18+
# Load Windows env (PATH for go) if running on the Windows executor.
19+
if [ -f "/c/tools-cache/snyk-env.sh" ]; then
20+
# shellcheck disable=SC1091
21+
source "/c/tools-cache/snyk-env.sh"
22+
fi
23+
24+
find_binary() {
25+
local dir="$1"
26+
local f
27+
shopt -s nullglob
28+
for f in "$dir"/snyk-*; do
29+
case "$f" in
30+
*.sha256 | *.sha256.asc | *.asc) continue ;;
31+
esac
32+
[ -f "$f" ] || continue
33+
echo "$f"
34+
return 0
35+
done
36+
return 1
37+
}
38+
39+
hash_file() {
40+
if command -v sha256sum >/dev/null 2>&1; then
41+
sha256sum "$1" | awk '{print $1}'
42+
elif command -v shasum >/dev/null 2>&1; then
43+
shasum -a 256 "$1" | awk '{print $1}'
44+
else
45+
echo "NOHASH"
46+
fi
47+
}
48+
49+
log_build_settings() {
50+
local label="$1"
51+
local bin="$2"
52+
echo "[verify-fips] Build settings ($label): $bin"
53+
go version -m "$bin" 2>/dev/null | grep -E '^[[:space:]]*build[[:space:]]' || echo " (no build info available)"
54+
}
55+
56+
normal_bin="$(find_binary "$normal_dir" || true)"
57+
fips_bin="$(find_binary "$fips_dir" || true)"
58+
59+
if [ -z "$normal_bin" ] || [ -z "$fips_bin" ]; then
60+
echo "[verify-fips] ERROR: could not locate binaries (normal='$normal_bin', fips='$fips_bin')"
61+
exit 1
62+
fi
63+
64+
echo "=================================================================="
65+
echo "[verify-fips] Normal binary: $normal_bin"
66+
echo "[verify-fips] FIPS binary: $fips_bin"
67+
echo "------------------------------------------------------------------"
68+
log_build_settings "normal" "$normal_bin"
69+
log_build_settings "fips" "$fips_bin"
70+
echo "------------------------------------------------------------------"
71+
72+
# Informational: Microsoft's Go toolchain records GOEXPERIMENT=systemcrypto for
73+
# FIPS builds. Absence is not treated as fatal (toolchain behaviour may vary),
74+
# but it is surfaced clearly.
75+
if go version -m "$fips_bin" 2>/dev/null | grep -qi 'systemcrypto'; then
76+
echo "[verify-fips] FIPS indicator: 'systemcrypto' FOUND in FIPS binary build info."
77+
else
78+
echo "[verify-fips] WARNING: 'systemcrypto' NOT found in FIPS binary build info."
79+
fi
80+
81+
# Authoritative gate: the FIPS and normal binaries must differ. Identical bytes
82+
# mean the FIPS build did not take effect (e.g. it was skipped).
83+
normal_hash="$(hash_file "$normal_bin")"
84+
fips_hash="$(hash_file "$fips_bin")"
85+
echo "[verify-fips] normal sha256: $normal_hash"
86+
echo "[verify-fips] fips sha256: $fips_hash"
87+
88+
if [ "$normal_hash" = "NOHASH" ]; then
89+
echo "[verify-fips] WARNING: no sha256 tool available; skipping byte-difference gate."
90+
echo "[verify-fips] Review the build settings above to confirm the FIPS backend."
91+
exit 0
92+
fi
93+
94+
if [ "$normal_hash" = "$fips_hash" ]; then
95+
echo "=================================================================="
96+
echo "[verify-fips] FAILED: FIPS and normal binaries are IDENTICAL."
97+
echo "[verify-fips] The FIPS build did not take effect (was it skipped?)."
98+
exit 1
99+
fi
100+
101+
echo "=================================================================="
102+
echo "[verify-fips] PASSED: FIPS binary is distinct from the normal binary."

scripts/windows/ensure-python-uv.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ Param()
33
$ErrorActionPreference = 'Stop'
44

55
try {
6+
if (Get-Command uv -ErrorAction SilentlyContinue) {
7+
Write-Host "[uv-cache] HIT: uv already installed; skipping pip install."
8+
return
9+
}
10+
611
$python = Get-Command python -ErrorAction SilentlyContinue
712
if (-not $python) {
813
$python = Get-Command python3 -ErrorAction SilentlyContinue

0 commit comments

Comments
 (0)