55 branches : [main]
66 pull_request :
77 paths :
8- - " packages/ats/**"
9- - " apps/ats/**"
8+ - " packages/ats/contracts/**"
109 - " package.json"
1110 - " .github/workflows/*ats*.yaml"
1211 workflow_dispatch :
@@ -20,34 +19,206 @@ permissions:
2019
2120jobs :
2221 test-ats :
23- name : testing
22+ name : contracts scripts tests
2423 runs-on : token-studio-linux-large
2524 timeout-minutes : 45
2625 env :
2726 CONTRACT_SIZER_RUN_ON_COMPILE : " false"
2827 REPORT_GAS : " false"
29- CLIENT_PRIVATE_KEY_ECDSA_1 : ${{ secrets.CLIENT_PRIVATE_KEY_ECDSA_1 }}
30- CLIENT_PUBLIC_KEY_ECDSA_1 : ${{ secrets.CLIENT_PUBLIC_KEY_ECDSA_1 }}
31- CLIENT_ACCOUNT_ID_ECDSA_1 : " 0.0.1328"
32- CLIENT_EVM_ADDRESS_ECDSA_1_CORRECT : " 0x97C50bb12E1C6284cF2855cdba95c5D60AEE44CF"
33- CLIENT_EVM_ADDRESS_ECDSA_1 : " 0x0000000000000000000000000000000000000530"
34- CLIENT_PRIVATE_KEY_ECDSA_2 : ${{ secrets.CLIENT_PRIVATE_KEY_ECDSA_2 }}
35- CLIENT_PUBLIC_KEY_ECDSA_2 : ${{ secrets.CLIENT_PUBLIC_KEY_ECDSA_2 }}
36- CLIENT_ACCOUNT_ID_ECDSA_2 : " 0.0.2168740"
37- CLIENT_EVM_ADDRESS_ECDSA_2 : " 0x00000000000000000000000000000000002117A4"
38- FACTORY_ADDRESS : " 0.0.5480051"
39- RESOLVER_ADDRESS : " 0.0.5479997"
40- FIREBLOCKS_HEDERA_ACCOUNT_ID : " 0.0.2168740"
41- FIREBLOCKS_HEDERA_PUBLIC_KEY : ${{ secrets.CLIENT_PUBLIC_KEY_ECDSA_2 }}
42- DFNS_HEDERA_ACCOUNT_ID : " 0.0.2168740"
43- DFNS_WALLET_PUBLIC_KEY : ${{ secrets.CLIENT_PUBLIC_KEY_ECDSA_2 }}
44- AWS_KMS_HEDERA_ACCOUNT_ID : " 0.0.4394946"
45- AWS_KMS_HEDERA_PUBLIC_KEY : " 302d300706052b8104000a03220003ee815bb9b5e53f5dbe7264a77e586127dfcb75da8c1246f5aa6ededdb13e6c21"
46- REACT_APP_MIRROR_NODE : " https://testnet.mirrornode.hedera.com/api/v1/"
47- REACT_APP_RPC_NODE : " https://testnet.hashio.io/api"
48- REACT_APP_RESOLVER : " 0.0.5479997"
49- REACT_APP_FACTORY : " 0.0.5480051"
50- REACT_APP_SHOW_DISCLAIMER : " true"
28+
29+ steps :
30+ - name : Harden Runner
31+ uses : step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
32+ with :
33+ egress-policy : audit
34+
35+ - name : Checkout repository
36+ uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
37+
38+ - name : Setup NodeJS Environment
39+ uses : actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
40+ with :
41+ node-version-file : .nvmrc
42+ cache : " npm"
43+
44+ # Shared dependency cache across every job in this workflow (and matched by the other ATS
45+ # workflows on the same key), so node_modules is restored instead of reinstalled. On a hit
46+ # the install below is skipped entirely.
47+ - name : Restore dependencies from cache
48+ id : deps-cache
49+ uses : actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
50+ with :
51+ path : |
52+ node_modules
53+ */*/node_modules
54+ */*/*/node_modules
55+ ~/.npm
56+ key : ${{ runner.os }}-deps-${{ hashFiles('package-lock.json') }}
57+
58+ # --ignore-scripts skips the contracts `prepare` hook (a *production* `hardhat compile`).
59+ # Every job here recompiles in test/coverage mode anyway and regenerates the gitignored
60+ # resolver registry as part of that compile, so the prepare compile is pure duplicated work.
61+ - name : Install dependencies
62+ if : steps.deps-cache.outputs.cache-hit != 'true'
63+ run : npm ci --ignore-scripts
64+
65+ # Contracts integration tests run under coverage in the parallel `coverage-ats` matrix below
66+ # (which doubles as their gate), so this job only runs the scripts/* suite that coverage does
67+ # not exercise. SDK and web are deprecated and no longer tested here.
68+ #
69+ # The scripts suite deploys the system and exercises test-only facets (EvmAccessors,
70+ # MockDiamondCut, …), so it needs a TEST-MODE compile — which also regenerates the test-mode
71+ # resolver registry — before the run. `--force` is mandatory: without it Hardhat sees no
72+ # source change and skips, leaving a registry with no key for EvmAccessorsFacet.
73+ - name : Run contracts scripts tests
74+ working-directory : packages/ats/contracts
75+ env :
76+ ATS_TEST_MODE : " true"
77+ run : |
78+ npm run compile:force
79+ npm run test:scripts
80+
81+ # Contracts coverage is sharded across parallel runners: solidity-coverage re-instruments and
82+ # recompiles per shard (unavoidable on Hardhat 2), but each shard runs on its own runner so the
83+ # ~3 min compile overlaps instead of serializing. Each shard runs the mega-asset entry once for
84+ # its `i % SHARD_TOTAL` slice plus a round-robined share of the standalone suites, computed by
85+ # scripts/tools/coverage-shard/planShard.ts. Shards upload their lcov as artifacts; the
86+ # merge-coverage job below combines them into a single report and uploads it once to Codecov.
87+ coverage-ats :
88+ name : coverage (shard ${{ matrix.shard }})
89+ runs-on : token-studio-linux-large
90+ timeout-minutes : 45
91+ strategy :
92+ fail-fast : false
93+ matrix :
94+ # Keep this list's length equal to SHARD_TOTAL below (indices 0..N-1).
95+ shard : [0, 1, 2, 3]
96+ env :
97+ CONTRACT_SIZER_RUN_ON_COMPILE : " false"
98+ REPORT_GAS : " false"
99+ SHARD_INDEX : ${{ matrix.shard }}
100+ SHARD_TOTAL : " 4"
101+
102+ steps :
103+ - name : Harden Runner
104+ uses : step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
105+ with :
106+ egress-policy : audit
107+
108+ - name : Checkout repository
109+ uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
110+
111+ - name : Setup NodeJS Environment
112+ uses : actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
113+ with :
114+ node-version-file : .nvmrc
115+ cache : " npm"
116+
117+ - name : Restore dependencies from cache
118+ id : deps-cache
119+ uses : actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
120+ with :
121+ path : |
122+ node_modules
123+ */*/node_modules
124+ */*/*/node_modules
125+ ~/.npm
126+ key : ${{ runner.os }}-deps-${{ hashFiles('package-lock.json') }}
127+
128+ - name : Install dependencies
129+ if : steps.deps-cache.outputs.cache-hit != 'true'
130+ run : npm ci --ignore-scripts
131+
132+ - name : Run contracts coverage shard
133+ run : |
134+ npm run test:coverage:shard --workspace=packages/ats/contracts 2>&1 | tee shard.log
135+ # Record this shard's passing-test count (ANSI-stripped) for the merge-time reconciliation
136+ # that proves the sharded runs executed every test — see the merge-coverage job.
137+ sed -E 's/\x1b\[[0-9;]*m//g' shard.log | grep -oE '[0-9]+ passing' | head -1 | grep -oE '^[0-9]+' \
138+ > packages/ats/contracts/coverage/shard-count.txt
139+ echo "shard ${SHARD_INDEX} passing: $(cat packages/ats/contracts/coverage/shard-count.txt)"
140+
141+ - name : Upload coverage shard artifact
142+ if : ${{ !cancelled() }}
143+ uses : actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
144+ with :
145+ name : coverage-shard-${{ matrix.shard }}
146+ path : |
147+ packages/ats/contracts/coverage/lcov.info
148+ packages/ats/contracts/coverage/shard-count.txt
149+ if-no-files-found : error
150+ retention-days : 1
151+
152+ # Sequential oracle for the trust check: run the integration suite in ONE pass and record its
153+ # passing-test count. The merge job compares this against the sum of the coverage shards' counts to
154+ # prove the sharding/balancing dropped no test. Integration-only (NOT the scripts suite, which the
155+ # coverage shards don't run), and excludes the local-parallel `ats.shard.*` files. Runs in parallel
156+ # with the coverage matrix, so it adds no critical-path time.
157+ verify-test-count :
158+ name : verify test count
159+ runs-on : token-studio-linux-large
160+ timeout-minutes : 45
161+ env :
162+ CONTRACT_SIZER_RUN_ON_COMPILE : " false"
163+ REPORT_GAS : " false"
164+
165+ steps :
166+ - name : Harden Runner
167+ uses : step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
168+ with :
169+ egress-policy : audit
170+
171+ - name : Checkout repository
172+ uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
173+
174+ - name : Setup NodeJS Environment
175+ uses : actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
176+ with :
177+ node-version-file : .nvmrc
178+ cache : " npm"
179+
180+ - name : Restore dependencies from cache
181+ id : deps-cache
182+ uses : actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
183+ with :
184+ path : |
185+ node_modules
186+ */*/node_modules
187+ */*/*/node_modules
188+ ~/.npm
189+ key : ${{ runner.os }}-deps-${{ hashFiles('package-lock.json') }}
190+
191+ - name : Install dependencies
192+ if : steps.deps-cache.outputs.cache-hit != 'true'
193+ run : npm ci --ignore-scripts
194+
195+ - name : Run the integration suite sequentially (oracle)
196+ working-directory : packages/ats/contracts
197+ run : |
198+ npm run test:integration 2>&1 | tee oracle.log
199+ sed -E 's/\x1b\[[0-9;]*m//g' oracle.log | grep -oE '[0-9]+ passing' | head -1 | grep -oE '^[0-9]+' \
200+ > oracle-count.txt
201+ echo "sequential integration passing: $(cat oracle-count.txt)"
202+
203+ - name : Upload oracle count
204+ if : ${{ !cancelled() }}
205+ uses : actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
206+ with :
207+ name : oracle-count
208+ path : packages/ats/contracts/oracle-count.txt
209+ if-no-files-found : error
210+ retention-days : 1
211+
212+ # Merge the per-shard lcov reports into a single report and upload it once to Codecov — matching
213+ # the local `npm run test:coverage` single report, instead of uploading partial per-shard pieces.
214+ # Also reconciles the sharded test count against the sequential oracle. Runs only when every shard
215+ # and the oracle succeeded, so the merged report is complete.
216+ merge-coverage :
217+ name : merge coverage + upload
218+ needs : [coverage-ats, verify-test-count]
219+ runs-on : token-studio-linux-large
220+ timeout-minutes : 15
221+ env :
51222 CODECOV_TOKEN : ${{ secrets.CODECOV_TOKEN }}
52223
53224 steps :
@@ -65,33 +236,58 @@ jobs:
65236 node-version-file : .nvmrc
66237 cache : " npm"
67238
239+ - name : Restore dependencies from cache
240+ id : deps-cache
241+ uses : actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
242+ with :
243+ path : |
244+ node_modules
245+ */*/node_modules
246+ */*/*/node_modules
247+ ~/.npm
248+ key : ${{ runner.os }}-deps-${{ hashFiles('package-lock.json') }}
249+
68250 - name : Install dependencies
69- run : npm run ats:install
70-
71- # BBND-1766: `atsRegistry.generated.ts` is gitignored and regenerated
72- # by the contracts package's `prepare` script during `npm ci`. This
73- # step is the clean-checkout guard for that contract — if the file is
74- # missing after install, `prepare` regressed and every downstream
75- # build / test step would fail with a confusing module-not-found.
76- - name : Verify auto-generated registry produced by prepare hook
251+ if : steps.deps-cache.outputs.cache-hit != 'true'
252+ run : npm ci --ignore-scripts
253+
254+ - name : Download all coverage shard artifacts
255+ uses : actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
256+ with :
257+ pattern : coverage-shard-*
258+ path : coverage-shards
259+
260+ - name : Download sequential oracle count
261+ uses : actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
262+ with :
263+ name : oracle-count
264+ path : oracle
265+
266+ # Trust check: the sum of the coverage shards' passing-test counts must equal the sequential
267+ # single-pass count, proving the sharding/balancing ran every test (none dropped or doubled).
268+ - name : Reconcile sharded vs sequential test count
77269 run : |
78- REGISTRY_PATH="packages/ats/contracts/scripts/domain/atsRegistry.generated.ts"
79- if [[ ! -f "${REGISTRY_PATH}" ]]; then
80- echo "::error::${REGISTRY_PATH} missing after npm ci. The contracts package's 'prepare' script (hardhat compile) likely regressed."
81- exit 1
82- fi
83- if ! grep -q "^export const FACET_REGISTRY" "${REGISTRY_PATH}"; then
84- echo "::error::${REGISTRY_PATH} missing FACET_REGISTRY export — generator produced a malformed file."
270+ oracle=$(cat oracle/oracle-count.txt)
271+ sharded=$(find coverage-shards -name shard-count.txt -exec cat {} + | awk '{s += $1} END {print s + 0}')
272+ echo "sequential integration : ${oracle} passing"
273+ echo "Σ coverage shards : ${sharded} passing"
274+ if [[ "${oracle}" != "${sharded}" ]]; then
275+ echo "::error::Test-count mismatch — sequential ${oracle} vs sharded ${sharded}. A suite may be dropped or double-run by the sharding."
85276 exit 1
86277 fi
87- echo "✓ ${REGISTRY_PATH} auto-regenerated by prepare hook "
278+ echo "✅ all tests accounted for (${oracle}) "
88279
89- - name : Build ATS packages
90- run : npm run ats:build
280+ # Sum line/function/branch hits across the disjoint shards into one lcov, written to the same
281+ # path a local single-run coverage produces, so the Codecov upload is identical to local. Uses
282+ # the in-repo merger (preserves function coverage, which lcov-result-merger drops; no
283+ # runtime-fetched dependency).
284+ - name : Merge shard lcov reports
285+ working-directory : packages/ats/contracts
286+ run : npm run coverage:merge -- coverage/lcov.info "${GITHUB_WORKSPACE}/coverage-shards"
91287
92- - name : Run ATS tests
93- run : npm run ats:test:ci
94-
95- - name : Upload coverage report
96- if : ${{ !cancelled() && always() }}
288+ - name : Upload merged coverage to Codecov
97289 uses : codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v5.4.0
290+ with :
291+ token : ${{ secrets.CODECOV_TOKEN }}
292+ files : packages/ats/contracts/coverage/lcov.info
293+ disable_search : true
0 commit comments