Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
2822 lines (2599 loc) · 173 KB
/
Copy pathMakefile
File metadata and controls
2822 lines (2599 loc) · 173 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
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.PHONY: \
help \
version-print \
version-check \
test-version-contract \
version-sync \
set-version \
build \
build-seed \
build-check \
run \
docs-build \
docs-dev \
verify-contract \
conformance-mod-download \
build-conformance \
lint-conformance \
test-conformance-testresult \
test-integration-mutant-manifest \
test-conformance-imports \
test-conformance-contract \
test-conformance-drivers \
update-conformance-catalog \
check-conformance-catalog \
test-conformance-scenarios \
test-vectors \
test-conformance-faults \
test-invariants \
test-conformance-invariants \
soak \
soak-replay \
test-local-postgres \
test-blackbox-harness \
test-blackbox-components \
test-blackbox-wal \
test-blackbox-configured-bounds \
test-blackbox-mutation-control \
test-r1-benchmark-units \
record-r1-benchmark \
test-r1-benchmark \
_run-r1-benchmark \
characterize-dataset \
test-conformance-dataset \
parse-testresult \
conformance-adapter-artifact \
conformance-seed-artifact \
conformance-pg18-extension-artifact \
conformance-pg18-extension-test-artifact \
conformance-update-baseline-extension-artifact \
release-stage-server \
release-stage-packages \
release-stage \
release-verify \
release-consumer-artifacts \
release-test-upgrade \
release-run-support-cell \
test-conformance \
test-blackbox \
test-release-artifacts \
test-release-publish \
test-server-consumer-helper \
test-consumer-server \
test-consumer-go \
lint-go \
audit-rn \
lint-rn \
lint-rust-core \
lint-rust-pg \
lint-rust \
test \
ci-source-quality \
ci-candidate-server \
ci-candidate-swift \
ci-candidate-kotlin \
ci-candidate-rn-ios \
ci-candidate-rn-android \
test-rust-core \
test-rust-mutants \
test-rust-mutants-broad \
test-integration-mutants \
test-integration-mutants-broad \
test-integration-mutant \
test-rust-pg \
test-rust-pg-all \
test-adapter \
benchmark-adapter \
local-postgres-start \
local-postgres-stop \
build-local-postgres \
ext-build \
ext-install \
ext-test \
ext-seed \
build-swift-native-runner \
build-kotlin-library \
build-kotlin-conformance-app \
test-swift-unit \
test-client-schema-identity \
_test-client-schema-identity \
test-swift-warm-connect \
test-swift-scenarios \
test-swift \
test-kotlin-unit \
test-kotlin-warm-connect \
test-kotlin-scenarios \
test-kotlin-instrumentation \
test-kotlin \
test-kotlin-integration \
test-kotlin-jvm-integration \
test-swift-upgrade \
test-kotlin-upgrade \
test-rn-upgrade-ios \
upgrade-fixture \
build-rn-upgrade-android \
test-rn-upgrade-android \
test-rn-unit \
test-rn-android-parity \
test-rn-ios-parity \
test-rn-native-parity \
test-rn-warm-connect-control \
test-rn-warm-connect-ios \
test-rn-performance-ios \
test-rn-performance-android \
test-rn-pending-cycle-ios \
test-rn-pending-cycle-android \
test-rn-provenance-android \
test-rn-provenance-ios \
test-rn-push-android \
test-rn-push-ios \
test-rn-retention-android \
test-rn-retention-ios \
test-rn-scope-empty-pull-android \
test-rn-scope-empty-pull-ios \
test-rn-check-android \
test-rn-check-ios \
test-rn-requests-android \
test-rn-requests-ios \
test-rn-forged-android \
test-rn-forged-ios \
test-rn-sqm-android \
test-rn-sqm-ios \
test-rn-cardinality-android \
test-rn-cardinality-ios \
test-rn-queue-replay-ios \
test-rn-queue-replay-android \
test-rn-seeded-empty-startup-ios \
test-rn-seeded-empty-startup-android \
test-rn-warm-connect-android \
verify-rn-seed \
refresh-rn-seed \
rn-seed-asset \
rn-e2e-server-seed \
rn-watchman-reset \
rn-ios-pods \
android-emulator-prepare \
test-android-emulator-prepare \
test-rn-e2e-ios-build \
test-rn-e2e-ios-run \
test-rn-e2e-ios \
test-rn-e2e-android-build \
test-rn-e2e-android-run \
test-rn-e2e-android \
test-rn \
synchrod-pg-test-start \
synchrod-pg-test-stop \
synchrod-pg-test-restart \
release-pods-check \
release-kotlin-local \
release-npm-dry-run \
client-consumer-apple-artifact \
client-consumer-kotlin-artifact \
client-consumer-rn-artifact \
client-consumer-artifacts \
local-consumer-artifacts \
test-consumer-swift \
test-consumer-swift-ios \
test-consumer-kotlin \
test-consumer-kotlin-device \
test-consumer-kotlin-device-smoke \
test-consumer-rn-ios \
test-consumer-rn-android \
test-consumer-rn-ios-smoke \
test-consumer-rn-android-smoke \
test-client-platforms \
test-packaged-smoke \
test-packaged-smoke-structure \
test-packaged-consumers \
generate-pg-sql \
check-pg-sql \
check-released-update-scripts \
clean
ANDROID_HOME ?= /opt/homebrew/share/android-commandlinetools
ANDROID_JAVA_HOME ?= $(shell \
if [ -d /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home ]; then \
echo /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home; \
elif [ -x /usr/libexec/java_home ]; then \
CANDIDATE="$$(/usr/libexec/java_home -v 17 2>/dev/null || true)"; \
if [ -n "$$CANDIDATE" ] && "$$CANDIDATE/bin/java" -version 2>&1 | grep -q '"17\.'; then \
echo "$$CANDIDATE"; \
fi; \
fi)
KOTLIN_ANDROID_SERIAL ?= $(ANDROID_SERIAL)
ifneq "$(ANDROID_SERIAL)" ""
ifneq "$(KOTLIN_ANDROID_SERIAL)" ""
ifneq "$(ANDROID_SERIAL)" "$(KOTLIN_ANDROID_SERIAL)"
$(error Original ANDROID_SERIAL and KOTLIN_ANDROID_SERIAL values disagree)
endif
endif
endif
# adb and Detox select the device through ANDROID_SERIAL. Export the one
# resolved serial, so a Detox run uses only that booted device.
ifneq ($(KOTLIN_ANDROID_SERIAL),)
override ANDROID_SERIAL := $(KOTLIN_ANDROID_SERIAL)
export ANDROID_SERIAL
endif
RN_IOS_TEST_DESTINATION ?= platform=iOS Simulator,name=iPhone SE (3rd generation)
RN_IOS_BUILD_ARGS ?=
RN_IOS_TEST_DIAGNOSTICS ?= on-failure
RN_POD_UPDATE ?=
# AGP selects connected devices through ANDROID_SERIAL. Without one serial it
# uses every online device, so a device gate requires exactly one serial.
REQUIRE_ONE_ANDROID_SERIAL = case "$(KOTLIN_ANDROID_SERIAL)" in ''|*[[:space:],]*) echo "Set KOTLIN_ANDROID_SERIAL to exactly one booted Android device." >&2; exit 1 ;; esac
RN_ANDROID_DETOX_CONFIG ?= android.emu.release
# One Maven repository receives the Kotlin SDK publication and serves every React Native Android
# build, including the Detox APK builds. A stale same-version SDK in the default Maven local
# repository then cannot enter a build. It is repository configuration, not a test selector.
SYNCHRO_MAVEN_REPO ?= $(CURDIR)/clients/kotlin/build/maven-repository
override SYNCHRO_MAVEN_REPO := $(abspath $(SYNCHRO_MAVEN_REPO))
export SYNCHRO_MAVEN_REPO
PGRX_PG ?= pg18
PGRX_PG_CONFIG ?= $(shell awk -F'"' '/^$(PGRX_PG)[[:space:]]*=/ { print $$2 }' $(HOME)/.pgrx/config.toml)
PGRX_PG_BIN_DIR ?= $(dir $(PGRX_PG_CONFIG))
PGRX_TARGET_DIR ?= $(CURDIR)/.pgrx-target
MUTATION_CONTROL_TEST ?=
MUTATION_CONTROL_EXPECT ?= target_pass
INTEGRATION_MUTANT_ID ?=
SOAK_SEED ?= 1
# SOAK_OPERATIONS is the explicit seeded-stress operation budget.
SOAK_OPERATIONS ?= 7
SOAK_TIMEOUT ?= 35m
# Each run creates a new seed directory here for its journals and failure wire bodies.
SOAK_ARTIFACT_DIR ?= $(CURDIR)/.ignore/soak-evidence
SOAK_REPLAY_JOURNAL ?=
TESTRESULT_TEST_NAME ?=
CONFORMANCE_ADAPTER_ARTIFACT_DIR ?= $(CURDIR)/dist/conformance/synchrod-pg-adapter
CONFORMANCE_SEED_ARTIFACT ?= $(CURDIR)/dist/conformance/synchro-seed
CONFORMANCE_EXTENSION_ARTIFACT ?= $(CURDIR)/dist/conformance/synchro-pg-pg18
CONFORMANCE_UPDATE_BASELINE_EXTENSION_ARTIFACT ?= $(CURDIR)/dist/conformance/synchro-pg-pg18-update-baseline
CONFORMANCE_UPDATE_ORIGIN_EXTENSION_ARTIFACTS ?= $(CURDIR)/dist/conformance/synchro-pg-pg18-update-origins
ADAPTER_TEST_URL ?=
REPLICATION_URL = $(ADAPTER_TEST_URL)
override R1_BENCHMARK_BASELINE := $(CURDIR)/conformance/blackbox/integration/testdata/r1-benchmark-baseline.json
R1_BENCHMARK_EXTENSION_TARGET ?= conformance-pg18-extension-artifact
SYNCHROD_PG_PORT ?= 8091
SYNCHRO_TEST_HOST ?= localhost
SYNCHRO_TEST_PORT ?= $(SYNCHROD_PG_PORT)
SYNCHRO_TEST_URL ?= http://$(SYNCHRO_TEST_HOST):$(SYNCHRO_TEST_PORT)
SYNCHRO_TEST_JWT_SECRET ?= test-secret-for-integration-tests
MIN_CLIENT_VERSION ?= 1.0.0
SYNCHROD_PG_PID_FILE ?= .synchrod-pg-test.pid
SYNCHROD_PG_LOG_FILE ?= .synchrod-pg-test.log
LOCAL_POSTGRES_STATE_DIR ?= $(CURDIR)/.ignore/r2/tmp/local-postgres
LOCAL_POSTGRES_PID_FILE ?= $(LOCAL_POSTGRES_STATE_DIR)/postgres.pid
LOCAL_POSTGRES_LOG_FILE ?= $(LOCAL_POSTGRES_STATE_DIR)/postgres.log
LOCAL_POSTGRES_URL_FILE ?= $(LOCAL_POSTGRES_STATE_DIR)/postgres.url
LOCAL_POSTGRES_ATTACH_ENV_FILE ?= $(LOCAL_POSTGRES_STATE_DIR)/attach.env
LOCAL_POSTGRES_LISTEN ?= 127.0.0.1
LOCAL_POSTGRES_BINARY ?= $(CURDIR)/bin/synchro-local-postgres
LOCAL_POSTGRES_BINARY_SHA256 ?=
# One warm-connect gate run consumes one freshly started provisioner
# instance. Restart local-postgres-start before each run.
WARM_CONNECT_ENV_FILE ?=
WARM_CONNECT_ENV = if [ -n "$(WARM_CONNECT_ENV_FILE)" ]; then \
test -r "$(WARM_CONNECT_ENV_FILE)"; \
SYNCHRO_ATTACH_DIR="$$(cd "$$(dirname "$(WARM_CONNECT_ENV_FILE)")" && pwd)"; \
export SYNCHRO_ATTACH_DIR; \
set -a; . "$(WARM_CONNECT_ENV_FILE)"; set +a; \
if [ -z "$${SYNCHRO_CONFORMANCE_ADAPTER_ARTIFACT:-}" ]; then \
test -x "$(CONFORMANCE_ADAPTER_ARTIFACT_DIR)/synchrod-pg" || $(MAKE) conformance-adapter-artifact; \
SYNCHRO_CONFORMANCE_ADAPTER_ARTIFACT="$(CONFORMANCE_ADAPTER_ARTIFACT_DIR)/synchrod-pg"; \
export SYNCHRO_CONFORMANCE_ADAPTER_ARTIFACT; \
fi; \
fi;
BINARY ?= bin/synchrod-pg
SEED_BINARY ?= bin/synchro-seed
RN_PINNED_SEED ?= clients/react-native/example/seed.db
RN_CONSUMER_SEED ?= clients/react-native/example/verification/seed.db
RN_ANDROID_SEED_ASSET ?= clients/react-native/example/android/app/src/main/assets/seed.db
CLIENT_INTEGRATION_SEED ?= $(CURDIR)/.ignore/client-integration/seed.db
# CLIENT_DATASET=1 also prepares the synthetic training dataset (conformance/dataset).
CLIENT_DATASET ?= 0
REFRESH_RN_SEED_OUTPUT ?= $(CURDIR)/clients/react-native/example/seed.db
# A required gate runs its declared selection. A result stream cannot show that
# a caller selector omitted tests, so a required gate rejects a changed selector.
# PARTIAL=1 permits the selector and labels the run as partial diagnostic output.
PARTIAL ?=
DECLARED_GO_TEST_ARGS := -v -count=1 -p 1
DECLARED_GO_TEST_PKGS := ./...
DECLARED_GRADLE_TEST_ARGS := --rerun-tasks
DECLARED_BLACKBOX_TEST_COUNT := 1
DECLARED_SWIFT_TEST_ARGS :=
DECLARED_DETOX_ARGS :=
GO_TEST_ARGS ?= $(DECLARED_GO_TEST_ARGS)
GO_TEST_PKGS ?= $(DECLARED_GO_TEST_PKGS)
GRADLE_TEST_ARGS ?= $(DECLARED_GRADLE_TEST_ARGS)
BLACKBOX_TEST_COUNT ?= $(DECLARED_BLACKBOX_TEST_COUNT)
SWIFT_TEST_ARGS ?= $(DECLARED_SWIFT_TEST_ARGS)
DETOX_ARGS ?= $(DECLARED_DETOX_ARGS)
# A timeout bounds a run but cannot omit a test, so it is not a selector.
# Each integration test retains its own deadline.
# This outer package timeout bounds the complete run.
BLACKBOX_TIMEOUT ?= 120m
SWIFT_SCENARIOS_TIMEOUT ?= 30m
changed_selectors = $(strip $(foreach name,$(1),$(if $(subst x$(DECLARED_$(name)),,x$($(name)))$(subst x$($(name)),,x$(DECLARED_$(name))),$(name))))
declared_selection = @case "$(PARTIAL)" in \
'') test -z "$(call changed_selectors,$(1))" || { echo "$@ is a required gate. The caller changed $(call changed_selectors,$(1)) from its declared selection. Set PARTIAL=1 for a partial diagnostic run." >&2; exit 1; } ;; \
1) echo "PARTIAL: $@ runs a diagnostic selection. Its result is not required-gate evidence." >&2 ;; \
*) echo "PARTIAL must be empty or 1" >&2; exit 1 ;; \
esac
CLIENT_ARTIFACT_DIR ?= $(CURDIR)/dist/local-consumer
LOCAL_CONSUMER_DIR ?= $(CLIENT_ARTIFACT_DIR)
CURRENT_VERSION := $(shell cat VERSION 2>/dev/null)
SWIFTPM_GIT_ENV := GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.bareRepository GIT_CONFIG_VALUE_0=all
SWIFT_BUILD_ARGS ?=
PACKAGED_SMOKE_EVIDENCE ?= $(CURDIR)/dist/verification/packaged-smoke-summary.json
PACKAGED_SMOKE_CELL_DIR ?= $(CURDIR)/dist/verification/packaged-smoke-cells
PACKAGED_SMOKE_TMP_ROOT ?= $(CURDIR)/.ignore/r2/tmp
# Installed-client cells check server rows through ADAPTER_TEST_URL with this client.
PACKAGED_SMOKE_PSQL ?= $(if $(PGRX_PG_BIN_DIR),$(PGRX_PG_BIN_DIR)psql,psql)
RELEASE_DIR ?=
RELEASE_SERVER_DIR ?= $(CURDIR)/dist/release-components/server
RELEASE_PACKAGE_DIR ?= $(CURDIR)/dist/release-components/packages
RELEASE_CONSUMER_DIR ?= $(CURDIR)/dist/release-consumer
RELEASE_SBOM ?=
RELEASE_SUPPORT_ENVIRONMENTS ?=
RELEASE_EVIDENCE_DIR ?= $(CURDIR)/dist/verification/release-evidence
RELEASE_PG18_BIN_DIR ?=
RELEASE_PROVISIONER ?=
RELEASE_SERVER_LISTEN_URL ?= http://127.0.0.1:8091
RELEASE_CANDIDATE_CI_RUN_ID ?=
RELEASE_CANDIDATE_CI_RUN_ATTEMPT ?=
RELEASE_BUILD_RUN_ID ?= $(GITHUB_RUN_ID)
RELEASE_BUILD_RUN_ATTEMPT ?= $(GITHUB_RUN_ATTEMPT)
RELEASE_STAGED_ARTIFACTS ?= 0
CLIENT_ARTIFACTS_PREPARED ?= 0
# The published release that native upgrade tests install first.
UPGRADE_PREDECESSOR_VERSION ?= 0.3.1
# The commit of the published v0.3.1 tag. A moved tag fails the Swift upgrade build.
UPGRADE_PREDECESSOR_SWIFT_REVISION ?= 234d18d0f8f1751927ca58544863688ebc2fb70a
UPGRADE_WORK_DIR ?= $(CURDIR)/.ignore/upgrade
# React Native builds this address into its bundle, so the port is fixed.
UPGRADE_CONTROL_ADDRESS ?= 127.0.0.1:8095
UPGRADE_ANDROID_PACKAGE := com.trainstar.synchro.upgrade
RELEASE_INVENTORY := $(CURDIR)/conformance/artifacts/inventory.json
RELEASE_SUPPORT_MATRIX := $(CURDIR)/conformance/support-matrix.json
TEST_ENV = \
TEST_DATABASE_URL="$(ADAPTER_TEST_URL)" \
TEST_REPLICATION_URL="$(REPLICATION_URL)" \
SYNCHRO_TEST_URL="$(SYNCHRO_TEST_URL)" \
SYNCHRO_TEST_JWT_SECRET="$(SYNCHRO_TEST_JWT_SECRET)" \
SYNCHRO_TEST_SEED_PATH="$(CURDIR)/clients/react-native/example/seed.db"
help:
@echo "Available targets:"
@echo " version-print - Print the canonical repo version from VERSION"
@echo " version-check - Verify every public release surface matches VERSION"
@echo " test-version-contract - Validate next-release metadata with docs dependencies"
@echo " version-sync - Sync versioned metadata from VERSION"
@echo " set-version - Set VERSION=X.Y.Z and sync public metadata"
@echo " build - Build the synchrod-pg adapter binary"
@echo " build-seed - Build the seed database generator binary"
@echo " test-client-schema-identity - Verify Go seed DDL converges with Swift and Kotlin"
@echo " build-check - Build the Go adapter module"
@echo " run - Run synchrod-pg locally with current env"
@echo " docs-build - Verify the contract and build the docs site"
@echo " docs-dev - Run the docs site locally"
@echo " verify-contract - Validate the JavaScript-authored release contract"
@echo " conformance-mod-download - Download standalone conformance dependencies"
@echo " build-conformance - Build every standalone conformance package"
@echo " lint-conformance - Format and vet the standalone conformance module"
@echo " test-conformance-testresult - Test the structured Go test-result parser"
@echo " test-integration-mutant-manifest - Validate integration mutant bindings"
@echo " test-conformance-imports - Test standalone conformance import policy"
@echo " test-conformance-contract - Test strict contract loading and snapshots"
@echo " test-conformance-drivers - Test the plain Swift and Kotlin process drivers"
@echo " update-conformance-catalog - Write the deterministic scenario catalog"
@echo " check-conformance-catalog - Check the deterministic scenario catalog"
@echo " test-conformance-scenarios - Test strict scenario loading and catalog generation"
@echo " test-vectors - Test canonical protocol 3 vectors"
@echo " test-conformance-invariants - Test the invariant engine and soak driver"
@echo " soak - Run bounded seeded stress: SOAK_SEED, SOAK_OPERATIONS, SOAK_ARTIFACT_DIR"
@echo " soak-replay - Replay one retained soak journal: SOAK_REPLAY_JOURNAL"
@echo " test-conformance - Run the independent protocol conformance suite"
@echo " test-blackbox - Run the packaged server black-box suite"
@echo " test-blackbox-configured-bounds - Run the real configured-limit measurement proof"
@echo " test-blackbox-mutation-control - Run one structured real mutation control"
@echo " record-r1-benchmark - Record one R1 benchmark candidate"
@echo " test-r1-benchmark - Compare R1 benchmark results with the tracked baseline"
@echo " characterize-dataset - Record complete-work samples for DATASET_SEED and DATASET_SIZE"
@echo " release-stage-server - Build Linux x64 server release components"
@echo " release-stage-packages - Build signed Maven and npm release components"
@echo " release-stage - Assemble and seal already built release components"
@echo " release-verify - Verify one sealed release without building"
@echo " release-consumer-artifacts - Prepare sealed payloads for package consumers"
@echo " release-test-upgrade - Run one current client upgrade against sealed package inputs"
@echo " test-release-publish - Test publication identity and recovery state"
@echo " test-server-consumer-helper - Run server packaged-consumer helper unit tests"
@echo " test-consumer-server - Run the SUP-PG-LINUX-X64-001 lifecycle with locally built server artifacts"
@echo " test-consumer-go - Resolve and compile the public Go module consumer"
@echo " lint-go - Run Go formatting checks and go vet"
@echo " audit-rn - Run the full recursive React Native workspace audit"
@echo " lint-rn - Run React Native typecheck and ESLint"
@echo " lint-rust-core - Run Rust fmt and clippy for the shared core"
@echo " lint-rust-pg - Run Rust fmt and clippy for the PostgreSQL extension"
@echo " lint-rust - Run all Rust fmt and clippy checks"
@echo " test - Run the default local validation set"
@echo " ci-source-quality - Run the tests of the CI source-quality job"
@echo " ci-candidate-server - Run the tests of the CI candidate-server job (SOAK_ARTIFACT_DIR)"
@echo " ci-candidate-swift - Run the tests of the CI candidate-swift job (ADAPTER_TEST_URL, WARM_CONNECT_ENV_FILE)"
@echo " ci-candidate-kotlin - Run the tests of the CI candidate-kotlin job on KOTLIN_ANDROID_SERIAL (ADAPTER_TEST_URL)"
@echo " ci-candidate-rn-ios - Run the tests of the CI candidate-rn-ios job (ADAPTER_TEST_URL, WARM_CONNECT_ENV_FILE)"
@echo " ci-candidate-rn-android - Run the device tests of the CI candidate-rn-android job on KOTLIN_ANDROID_SERIAL"
@echo " test-rust-core - Run synchro-core unit tests"
@echo " test-rust-mutants - Run targeted synchro-core mutation tests"
@echo " test-rust-mutants-broad - Run broad synchro-core mutation search"
@echo " test-integration-mutants - Run curated production integration mutants"
@echo " test-integration-mutants-broad - Run every production integration mutant"
@echo " test-integration-mutant - Run one manifest mutant with INTEGRATION_MUTANT_ID"
@echo " test-rust-pg - Run pgrx integration tests on PG 18"
@echo " test-rust-pg-all - Run pgrx tests on PG 14 through PG 18"
@echo " test-adapter - Run Go adapter integration tests (PARTIAL=1 permits GO_TEST_PKGS or GO_TEST_ARGS)"
@echo " benchmark-adapter - Run Go adapter tests and benchmarks (override GO_TEST_PKGS to focus)"
@echo " Set ADAPTER_TEST_URL to the one test PostgreSQL database URL"
@echo " local-postgres-start - Start an isolated PostgreSQL 18 through the Go provisioner"
@echo " local-postgres-stop - Stop the isolated PostgreSQL 18 provisioner"
@echo " build-swift-native-runner - Build the macOS native conformance process"
@echo " build-kotlin-library - Build the Kotlin client library"
@echo " build-kotlin-conformance-app - Build the Android native conformance test APK"
@echo " test-swift-unit - Run Swift unit tests"
@echo " test-swift-warm-connect - Run the direct Swift warm-connect scenario"
@echo " test-swift-scenarios - Run the direct Swift correctness scenarios"
@echo " test-swift - Run Swift integration tests against the local adapter"
@echo " test-kotlin-unit - Run Kotlin unit tests"
@echo " test-kotlin-scenarios - Run the direct Kotlin correctness scenarios"
@echo " test-kotlin-instrumentation - Run Android instrumentation on KOTLIN_ANDROID_SERIAL"
@echo " test-kotlin - Run Kotlin integration tests against the local adapter"
@echo " test-kotlin-jvm-integration - Run only the Kotlin JVM integration tests against the local adapter"
@echo " test-swift-upgrade - Upgrade Swift intent from the published predecessor to the candidate package"
@echo " test-kotlin-upgrade - Upgrade Kotlin intent from the published predecessor APK on KOTLIN_ANDROID_SERIAL"
@echo " test-rn-upgrade-ios - Upgrade React Native intent from the published predecessor on the booted simulator"
@echo " build-rn-upgrade-android - Build both React Native Android upgrade package sides without a device"
@echo " test-rn-upgrade-android - Upgrade React Native intent from the published predecessor on KOTLIN_ANDROID_SERIAL"
@echo " test-rn-unit - Run React Native Jest tests"
@echo " test-rn-android-parity - Regenerate the TurboModule spec and compile the Android implementation"
@echo " test-rn-ios-parity - Compile the iOS implementation against the generated TurboModule spec"
@echo " test-rn-native-parity - Compile both native implementations against one TurboModule spec"
@echo " test-rn-bridge-transactions - Run the native bridge transaction tests on iOS and KOTLIN_ANDROID_SERIAL"
@echo " build-rn-bridge-transactions-android - Build the Android bridge transaction test APK without a device"
@echo " build-rn-bridge-transactions-ios - Build the iOS bridge transaction test target for the host simulator architecture"
@echo " test-rn-warm-connect-control - Run the exact React Native warm-connect negative control"
@echo " rn-ios-build - Build the iOS conformance app without starting a server"
@echo " rn-ios-bundle - Rebundle JavaScript-only changes in an existing iOS test app"
@echo " test-rn-warm-connect-ios - Run direct React Native warm-connect through the iOS bridge"
@echo " test-rn-performance-android - Run direct React Native steady-pull through the Android bridge"
@echo " test-rn-pending-cycle-ios - Run direct React Native pending-cycle through the iOS bridge"
@echo " test-rn-pending-cycle-android - Run direct React Native pending-cycle through the Android bridge"
@echo " test-rn-provenance-android - Run direct React Native multi-scope provenance through the Android bridge"
@echo " test-rn-provenance-ios - Run direct React Native multi-scope provenance through the iOS bridge"
@echo " test-rn-retention-ios - Run retention reconnect through the iOS bridge"
@echo " test-rn-retention-android - Run retention reconnect through the Android bridge"
@echo " test-rn-scope-empty-pull-ios - Run the empty scope set pull through the iOS bridge"
@echo " test-rn-scope-empty-pull-android - Run the empty scope set pull through the Android bridge"
@echo " test-rn-queue-replay-ios - Run direct React Native queue-replay through the iOS bridge"
@echo " test-rn-queue-replay-android - Run direct React Native queue-replay through the Android bridge"
@echo " test-rn-seeded-empty-startup-ios - Run seeded and empty startup through the iOS bridge"
@echo " test-rn-seeded-empty-startup-android - Run seeded and empty startup through the Android bridge"
@echo " test-rn-warm-connect-android - Run direct React Native warm-connect through the Android bridge"
@echo " verify-rn-seed - Verify the pinned React Native seed digest"
@echo " refresh-rn-seed - Regenerate and pin the React Native seed"
@echo " test-rn-e2e-ios - Run React Native Detox tests on iOS"
@echo " test-rn-e2e-android - Run React Native Detox tests on Android ($(RN_ANDROID_DETOX_CONFIG))"
@echo " test-rn - Run React Native Detox tests on both platforms"
@echo " android-emulator-prepare - Keep the booted Android test device awake and focused"
@echo " test-android-emulator-prepare - Test Android preparation without a device"
@echo " synchrod-pg-test-start - Start the extension-backed test adapter for ADAPTER_TEST_URL"
@echo " synchrod-pg-test-stop - Stop the extension-backed test adapter"
@echo " synchrod-pg-test-restart - Restart the extension-backed test adapter"
@echo " release-pods-check - Validate Apple package metadata surfaces"
@echo " release-kotlin-local - Publish Kotlin SDK to SYNCHRO_MAVEN_REPO"
@echo " release-npm-dry-run - Dry-run npm pack for the React Native package"
@echo " client-consumer-artifacts - Stage Apple, Kotlin, and React Native consumer artifacts"
@echo " local-consumer-artifacts - Build local-consumer artifacts for RN, Kotlin, and Apple"
@echo " test-consumer-swift - Run the packaged Swift consumer"
@echo " test-consumer-swift-ios - Run the packaged Swift consumer on an iOS simulator"
@echo " test-consumer-kotlin - Build the packaged Kotlin app and instrumentation APK"
@echo " test-consumer-kotlin-device - Run the packaged Kotlin consumer on KOTLIN_ANDROID_SERIAL"
@echo " test-consumer-rn-ios - Build an isolated RN iOS consumer from packaged artifacts"
@echo " test-consumer-rn-android - Build an isolated RN Android consumer from packaged artifacts"
@echo " test-client-platforms - Run one packaged client support cell (SUPPORT_CELL_ID required)"
@echo " test-packaged-smoke - Validate five terminal checks for every non-excluded support cell"
@echo " test-packaged-smoke-structure - Run packaged smoke summary failure controls"
@echo " test-packaged-consumers - Run all packaged consumer checks"
@echo " check-pg-sql - Verify tracked SQL matches pgrx generation"
@echo " check-released-update-scripts - Verify released update scripts match their release tags"
@echo " clean - Remove local build and server artifacts"
version-print:
@cd api/go && GOWORK=off go run ./cmd/synchro-version print
version-check:
@cd api/go && GOWORK=off go run ./cmd/synchro-version check $(if $(EXPECTED_TAG),--expected-tag "$(EXPECTED_TAG)")
test-version-contract:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -dir ../api/go -- go test -tags releasecontract -json -count=1 -run '^TestNextVersionPassesSupportPolicyAndRequirementsSchema$$' ./internal/releaseversion
version-sync:
@cd api/go && GOWORK=off go run ./cmd/synchro-version sync
set-version:
@test -n "$(VERSION)" || (echo "Provide VERSION=X.Y.Z"; exit 1)
cd api/go && GOWORK=off go run ./cmd/synchro-version set "$(VERSION)"
# The adapter owner fingerprint hashes this binary. VCS stamping would change
# the hash when a later step dirties a tracked file, such as Podfile.lock.
build:
@python3 scripts/ci/download_go_modules.py --module-dir api/go
@mkdir -p "$(dir $(BINARY))"
cd api/go && GOPROXY=off GOWORK=off go build -buildvcs=false -o "$(abspath $(BINARY))" ./cmd/synchrod-pg
build-seed:
@python3 scripts/ci/download_go_modules.py --module-dir api/go
@mkdir -p "$(dir $(SEED_BINARY))"
cd api/go && GOPROXY=off GOWORK=off go build -o "$(abspath $(SEED_BINARY))" ./cmd/synchro-seed
build-check:
@python3 scripts/ci/download_go_modules.py --module-dir api/go
cd api/go && GOPROXY=off GOWORK=off go build ./...
run:
cd api/go && GOWORK=off go run ./cmd/synchrod-pg
docs-build: verify-contract test-docs-links
cd docs && npm run build
python3 docs/scripts/check_links.py docs/dist
.PHONY: test-docs-links
test-docs-links: test-python-runner
@PYTHONPYCACHEPREFIX="$(PACKAGED_SMOKE_TMP_ROOT)/python-cache" python3 -m scripts.ci.run_python_tests docs.scripts.test_check_links
docs-dev:
cd docs && npm run dev
audit-docs:
cd docs && npm audit --json --audit-level=info
# This target validates the JavaScript-authored contract.
verify-contract:
cd docs && npm ci
$(MAKE) --no-print-directory audit-docs
$(MAKE) --no-print-directory test-docs-postcss-nested
$(MAKE) --no-print-directory test-docs-contract
.PHONY: test-docs-postcss-nested
test-docs-postcss-nested: test-python-runner
@PYTHONPYCACHEPREFIX="$(PACKAGED_SMOKE_TMP_ROOT)/python-cache" python3 -m scripts.ci.run_python_tests docs.scripts.test_postcss_nested
.PHONY: test-docs-contract
test-docs-contract:
cd docs && npm run verify:contract
conformance-mod-download:
@python3 scripts/ci/download_go_modules.py --module-dir conformance
build-conformance: conformance-mod-download
cd conformance && GOFLAGS= GOPROXY=off GOWORK=off go build ./...
lint-conformance: conformance-mod-download
@test -z "$$(find conformance -name '*.go' -print0 | xargs -0 gofmt -l)"
cd conformance && GOFLAGS= GOWORK=off go vet ./...
test-conformance-testresult:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./cmd/testresult -count=1
@cd conformance && if GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./cmd/testresult -count=1 -run '^TestDoesNotExist$$'; then \
echo "testresult accepted a zero-match run" >&2; \
exit 1; \
fi
test-integration-mutant-manifest: conformance-mod-download
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./mutants -count=1
test-conformance-imports:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./internal/importguard -count=1
test-conformance-contract:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./internal/jsonstrict ./internal/schemavalidator ./internal/contract -count=1
test-conformance-drivers:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./swift ./kotlin ./reactnative -count=1
update-conformance-catalog:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/synchro-conformance catalog --repo-root .. --write
check-conformance-catalog:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/synchro-conformance catalog --repo-root .. --check
test-conformance-scenarios:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./scenarios/... ./cmd/synchro-conformance -count=1
test-vectors:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./vectors -count=1
test-conformance-faults:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./barriers ./faults -count=1
test-invariants:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./invariants -count=1
test-conformance-dataset:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./dataset -count=1
test-conformance-invariants: test-invariants
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./soak -count=1
soak:
@$(WARM_CONNECT_ENV) \
test -n "$${SYNCHRO_CONFORMANCE_ADAPTER_ARTIFACT:-}" || { echo "the black-box environment is required for soak: set WARM_CONNECT_ENV_FILE or export SYNCHRO_CONFORMANCE_* variables" >&2; exit 1; }; \
mkdir -p "$(abspath $(SOAK_ARTIFACT_DIR))"; \
cd conformance && GOFLAGS= GOWORK=off \
SOAK_SEED="$(SOAK_SEED)" SOAK_OPERATIONS="$(SOAK_OPERATIONS)" SOAK_ARTIFACT_DIR="$(abspath $(SOAK_ARTIFACT_DIR))" SOAK_REPLAY_JOURNAL= \
go run ./cmd/testresult suite -- go test -json ./blackbox/integration -count=1 -timeout=$(SOAK_TIMEOUT) \
-run '^TestSoak$$' -args --provision --install
# Replay reads only the retained journal and rebuilds its harness in a new cluster.
soak-replay:
@test -f "$(SOAK_REPLAY_JOURNAL)" || { echo "SOAK_REPLAY_JOURNAL must name a retained soak journal" >&2; exit 1; }
@$(WARM_CONNECT_ENV) \
test -n "$${SYNCHRO_CONFORMANCE_ADAPTER_ARTIFACT:-}" || { echo "the black-box environment is required for soak-replay: set WARM_CONNECT_ENV_FILE or export SYNCHRO_CONFORMANCE_* variables" >&2; exit 1; }; \
cd conformance && GOFLAGS= GOWORK=off \
SOAK_ARTIFACT_DIR="$(abspath $(SOAK_ARTIFACT_DIR))" SOAK_REPLAY_JOURNAL="$(abspath $(SOAK_REPLAY_JOURNAL))" \
go run ./cmd/testresult suite -- go test -json ./blackbox/integration -count=1 -timeout=$(SOAK_TIMEOUT) \
-run '^TestSoak$$' -args --provision --install
test-local-postgres:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./cmd/synchro-local-postgres -count=1
test-blackbox-harness:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./blackbox -count=1
# The Linux vet builds the pidfd implementation on every host. It does not execute it.
.PHONY: test-owned-crash-safety
test-owned-crash-safety: conformance-mod-download
cd conformance && GOFLAGS= GOWORK=off GOOS=linux GOARCH=amd64 go vet ./blackbox
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./blackbox -run '^TestOwnedBackendCrash' -count=1
.PHONY: test-soak-controls
test-soak-controls:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./blackbox/integration -run '^TestSoak(Harness|Fault|WAL|Wire)' -count=1
test-blackbox-components:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./observer -count=1
test-blackbox-wal: conformance-mod-download test-blackbox-harness
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -json ./blackbox/integration -run '^TestRealWALPipeline$$' -count=1 -args --provision --install
test-blackbox-configured-bounds: conformance-mod-download test-blackbox-harness
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite \
-- go test -json ./blackbox/integration -count=1 -timeout=20m \
-run '^TestRealConfiguredBoundsMeasurement$$' -args --provision --install
test-blackbox-mutation-control:
@test -n "$(MUTATION_CONTROL_TEST)" || { echo "MUTATION_CONTROL_TEST is required" >&2; exit 1; }
@case "$(MUTATION_CONTROL_EXPECT)" in \
target_pass|target_semantic_test_failure) ;; \
*) echo "MUTATION_CONTROL_EXPECT is invalid" >&2; exit 1 ;; \
esac
@target='$(MUTATION_CONTROL_TEST)'; test_name=$${target%%/*}; assertion=$${target#*/}; \
if [ "$$assertion" = "$$target" ]; then assertion=assertion; fi; \
case "$$test_name" in \
TestRealMutationControlCursorAdvancement|TestRealMutationControlWALAcknowledgement|TestRealMutationControlMutationConservation|TestRealMutationControlChecksumCorrectness|TestRealMutationControlScopeIsolation|TestRealMutationControlProgressOrder|TestRealS02DivergentPullPaginationIsStarvationFree|\
TestRealIssue49ConnectRejectsFreshReuseAndInvalidEnvelopeValues|TestRealIssue49SemanticVersionPrecedence|TestRealIssue49PortableIntegerBoundariesAndCounterOverflow|TestRealIssue49MutationLifecycleVersionsVocabularyAndCrossBatchReplay|TestRealIssue49PortableSeedScopeContinuationAndTokenBindings|TestRealIssue49ConcurrentUpdateDeletePreservesOneAuthoritativeWinner|TestRealIssue49RebuildReplayEpochAndMonotonicCursor|TestRealIssue49PublishedSchemaIdentityIsImmutable|\
TestRealIssue49SecurityAdapterAuthorityAndScopeBoundary|TestRealIssue49SecurityRegistryIdentityAndKeys|TestRealRegistryAcceptsOnlyKeyTypesWithOneTextForm|TestRealRegistryRejectsDeferrablePrimaryKey|TestRealRegistryRejectsDatabaseGeneratedPushKey|TestRealIssue49SecurityCaptureHealthFailsClosed|TestRealIssue49SecurityDatabaseAuthority|TestRealIssue49SecurityOperationalRedaction|TestRealIssue49SecurityInstallationAuthority|\
TestRealIssue49WALIsTheOnlyAtomicPublicationPath|TestRealIssue49ResetLifecycleAndFenceCoverage|TestRealIssue49FenceCorrelationAndCapturePending|TestRealWALCorrelatesTriggerDMLPerRowIdentity|TestRealCaptureFenceRejectsOutOfOrderRowWrites|TestRealIssue49CompletePullVisibleWALRepresentation|TestRealIssue49CaptureReadinessRequiresEveryCheck|TestRealIssue49FenceCorrelatesOldRecordIdentity|TestRealIssue49FenceCorrelatesCaptureKeys|TestRealIssue49ResetCoversEveryFenceOperation|TestRealIssue49MembershipBackfillRetainsContinuationAcrossWorkerLoss|\
TestRealIssue49RemainingSemantics|TestRealExtensionUpdateFromBaseline|TestRealTransactionMembershipUsesFinalProjectionAcrossActivations|TestRealScopeAssignmentPullReconciliation|TestRealScopeAssignmentBoundFailsClosed|TestRealAtomicGroupAppliesAllOrNone|TestRealPushUnitConstraintBoundary|TestRealReadinessStaysReadyUnderWriteLoad) ;; \
*) echo "MUTATION_CONTROL_TEST is not a supported mutation control" >&2; exit 1 ;; \
esac; \
case "$$assertion" in assertion|assertion\#[0-9][0-9]) ;; *) echo "MUTATION_CONTROL_TEST does not name a supported assertion" >&2; exit 1 ;; esac; \
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult exact \
-test "$$target" \
-expect "$(MUTATION_CONTROL_EXPECT)" \
-- go test -json ./blackbox/integration -count=1 -run "^$$test_name$$/^$$assertion$$" -args --provision --install
test-r1-benchmark-units:
cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult suite -- go test -tags r1benchmark -json ./blackbox/integration -count=1 -run '^TestR1Benchmark(StrictParser|ThresholdLogic|ResultPathSafety)$$'
record-r1-benchmark: test-r1-benchmark-units
@$(MAKE) --no-print-directory _run-r1-benchmark R1_BENCHMARK_RUN_MODE=record
test-r1-benchmark: test-r1-benchmark-units
@$(MAKE) --no-print-directory _run-r1-benchmark R1_BENCHMARK_RUN_MODE=compare
_run-r1-benchmark:
@case "$(R1_BENCHMARK_RUN_MODE)" in record|compare) ;; *) echo "R1 benchmark run mode is invalid" >&2; exit 1 ;; esac
@test -n "$(R1_BENCHMARK_RESULT)" || { echo "R1_BENCHMARK_RESULT is required" >&2; exit 1; }
@test "$(abspath $(R1_BENCHMARK_RESULT))" != "$(R1_BENCHMARK_BASELINE)" || { echo "R1_BENCHMARK_RESULT must not replace the baseline" >&2; exit 1; }
@result="$(abspath $(R1_BENCHMARK_RESULT))"; repo="$(CURDIR)"; \
case "$$result" in "$$repo"|"$$repo"/*) echo "R1_BENCHMARK_RESULT must be outside the repository" >&2; exit 1 ;; esac
@test -z "$$(git status --porcelain --untracked-files=normal)" || { echo "R1 benchmark requires a clean worktree" >&2; exit 1; }
@git ls-files --error-unmatch -- "conformance/blackbox/integration/real_r1_benchmark_test.go" >/dev/null 2>&1 || { echo "R1 benchmark definition is not tracked" >&2; exit 1; }
@if [ "$(R1_BENCHMARK_RUN_MODE)" = compare ]; then \
test -f "$(R1_BENCHMARK_BASELINE)" || { echo "tracked R1 benchmark baseline is missing" >&2; exit 1; }; \
git ls-files --error-unmatch -- "conformance/blackbox/integration/testdata/r1-benchmark-baseline.json" >/dev/null 2>&1 || { echo "R1 benchmark baseline is not tracked" >&2; exit 1; }; \
fi
@set -eu; \
revision="$$(git rev-parse --verify HEAD)"; \
test "$${#revision}" -eq 40; \
repo="$$(pwd -P)"; \
temp_parent="$${TMPDIR:-/tmp}"; \
temp_parent="$$(cd "$$temp_parent" && pwd -P)"; \
case "$$temp_parent" in "$$repo"|"$$repo"/*) echo "R1 benchmark temporary directory must be outside the repository" >&2; exit 1 ;; esac; \
artifact_root="$$(mktemp -d "$$temp_parent/synchro-r1-$$revision.XXXXXX")"; \
cleanup() { rm -rf "$$artifact_root"; }; \
trap cleanup EXIT HUP INT TERM; \
adapter_bundle="$$artifact_root/adapter"; \
extension_bundle="$$artifact_root/extension"; \
secrets_dir="$$artifact_root/secrets"; \
mkdir "$$secrets_dir"; \
umask 077; \
for name in admin adapter observer worker operator jwt; do openssl rand -hex 32 > "$$secrets_dir/$$name-password"; done; \
pg_config="$(PGRX_PG_CONFIG)"; \
test -x "$$pg_config" || { echo "pgrx PostgreSQL 18 configuration is unavailable" >&2; exit 1; }; \
pg_bindir="$$(dirname "$$pg_config")"; \
$(MAKE) --no-print-directory conformance-adapter-artifact CONFORMANCE_ADAPTER_ARTIFACT_DIR="$$adapter_bundle"; \
$(MAKE) --no-print-directory $(R1_BENCHMARK_EXTENSION_TARGET) CONFORMANCE_EXTENSION_ARTIFACT="$$extension_bundle" PGRX_TARGET_DIR="$$artifact_root/cargo-target"; \
test -z "$$(git status --porcelain --untracked-files=normal)" || { echo "R1 artifact packaging changed the worktree" >&2; exit 1; }; \
test "$$(git rev-parse --verify HEAD)" = "$$revision" || { echo "R1 benchmark revision changed during packaging" >&2; exit 1; }; \
cd conformance; \
SYNCHRO_CONFORMANCE_ADAPTER_ARTIFACT="$$adapter_bundle/synchrod-pg" \
SYNCHRO_CONFORMANCE_EXTENSION_ARTIFACT="$$extension_bundle" \
SYNCHRO_CONFORMANCE_PG18_BINDIR="$$pg_bindir" \
SYNCHRO_CONFORMANCE_ADMIN_USER="synchro_cf_admin" \
SYNCHRO_CONFORMANCE_ADMIN_PASSWORD_FILE="$$secrets_dir/admin-password" \
SYNCHRO_CONFORMANCE_ADAPTER_USER="synchro_cf_adapter" \
SYNCHRO_CONFORMANCE_ADAPTER_PASSWORD_FILE="$$secrets_dir/adapter-password" \
SYNCHRO_CONFORMANCE_OBSERVER_USER="synchro_cf_observer" \
SYNCHRO_CONFORMANCE_OBSERVER_PASSWORD_FILE="$$secrets_dir/observer-password" \
SYNCHRO_CONFORMANCE_WORKER_USER="synchro_cf_worker" \
SYNCHRO_CONFORMANCE_WORKER_PASSWORD_FILE="$$secrets_dir/worker-password" \
SYNCHRO_CONFORMANCE_OPERATOR_USER="synchro_cf_operator" \
SYNCHRO_CONFORMANCE_OPERATOR_PASSWORD_FILE="$$secrets_dir/operator-password" \
SYNCHRO_CONFORMANCE_JWT_SECRET_FILE="$$secrets_dir/jwt-password" \
SYNCHRO_CONFORMANCE_INSTALL_LOCK="$$artifact_root/install.lock" \
R1_BENCHMARK_MODE="$(R1_BENCHMARK_RUN_MODE)" \
R1_BENCHMARK_REVISION="$$revision" \
R1_BENCHMARK_RESULT="$(abspath $(R1_BENCHMARK_RESULT))" \
GOFLAGS= GOWORK=off go run ./cmd/testresult exact \
-test TestRealR1PerformanceBenchmark \
-expect target_pass \
-- go test -tags r1benchmark -json ./blackbox/integration -count=1 -timeout=20m \
-run '^TestRealR1PerformanceBenchmark$$' -args --provision --install
# Characterize complete correct work for one seeded dataset. The run records
# samples and has no numerical pass or fail rule (D-06). It uses the black-box
# SYNCHRO_CONFORMANCE_* environment of test-blackbox.
characterize-dataset: conformance-mod-download
@case "$(DATASET_SEED)" in ''|*[!0-9]*) echo "DATASET_SEED must be an unsigned integer" >&2; exit 1 ;; esac
@case "$(DATASET_SIZE)" in s|m|l) ;; *) echo "DATASET_SIZE must be s, m, or l" >&2; exit 1 ;; esac
@test -n "$(DATASET_CHARACTERIZATION_RESULT)" || { echo "DATASET_CHARACTERIZATION_RESULT is required" >&2; exit 1; }
@result="$(abspath $(DATASET_CHARACTERIZATION_RESULT))"; repo="$(CURDIR)"; \
case "$$result" in "$$repo"|"$$repo"/*) echo "DATASET_CHARACTERIZATION_RESULT must be outside the repository" >&2; exit 1 ;; esac
@test -z "$$(git status --porcelain --untracked-files=normal)" || { echo "dataset characterization requires a clean worktree" >&2; exit 1; }
cd conformance && DATASET_REVISION="$$(git rev-parse --verify HEAD)" DATASET_SEED="$(DATASET_SEED)" DATASET_SIZE="$(DATASET_SIZE)" \
DATASET_CHARACTERIZATION_RESULT="$(abspath $(DATASET_CHARACTERIZATION_RESULT))" \
GOFLAGS= GOWORK=off go run ./cmd/testresult exact -test TestRealDatasetCharacterization -expect target_pass \
-- go test -tags datasetcharacterization -json ./blackbox/integration -count=1 -timeout=180m \
-run '^TestRealDatasetCharacterization$$' -args --provision --install
parse-testresult:
@test -n "$(TESTRESULT_TEST_NAME)" || { echo "TESTRESULT_TEST_NAME is required" >&2; exit 1; }
@cd conformance && GOFLAGS= GOWORK=off go run ./cmd/testresult --test "$(TESTRESULT_TEST_NAME)"
conformance-adapter-artifact:
@python3 scripts/ci/download_go_modules.py --module-dir api/go
@set -eu; \
final="$(CONFORMANCE_ADAPTER_ARTIFACT_DIR)"; \
parent="$$(dirname "$$final")"; \
stage="$$final.tmp.$$$$"; \
lock="$$final.publish-lock"; \
mkdir -p "$$parent"; \
mkdir "$$lock" || { echo "adapter artifact publication is locked" >&2; exit 1; }; \
cleanup() { rm -rf "$$stage"; rmdir "$$lock" 2>/dev/null || true; }; \
trap cleanup EXIT HUP INT TERM; \
test ! -e "$$final" || { echo "$$final already exists" >&2; exit 1; }; \
mkdir "$$stage"; \
(cd api/go && GOPROXY=off GOWORK=off go build -o "$$stage/synchrod-pg" ./cmd/synchrod-pg); \
test -x "$$stage/synchrod-pg"; \
digest="$$(shasum -a 256 "$$stage/synchrod-pg" | cut -d ' ' -f 1)"; \
test -n "$$digest"; \
printf '%s\n' "$$digest" > "$$stage/synchrod-pg.sha256.tmp"; \
mv "$$stage/synchrod-pg.sha256.tmp" "$$stage/synchrod-pg.sha256"; \
mv "$$stage" "$$final"; \
rmdir "$$lock"; \
trap - EXIT HUP INT TERM
conformance-seed-artifact:
@python3 scripts/ci/download_go_modules.py --module-dir api/go
@set -eu; \
final="$(CONFORMANCE_SEED_ARTIFACT)"; \
parent="$$(dirname "$$final")"; \
stage="$$final.tmp.$$$$"; \
lock="$$final.publish-lock"; \
mkdir -p "$$parent"; \
mkdir "$$lock" || { echo "seed artifact publication is locked" >&2; exit 1; }; \
cleanup() { rm -f "$$stage" "$$stage.sha256.tmp"; rmdir "$$lock" 2>/dev/null || true; }; \
trap cleanup EXIT HUP INT TERM; \
test ! -e "$$final" || { echo "$$final already exists" >&2; exit 1; }; \
(cd api/go && GOPROXY=off GOWORK=off go build -o "$$stage" ./cmd/synchro-seed); \
test -x "$$stage"; \
digest="$$(shasum -a 256 "$$stage" | cut -d ' ' -f 1)"; \
test -n "$$digest"; \
printf '%s\n' "$$digest" > "$$stage.sha256.tmp"; \
mv "$$stage" "$$final"; \
mv "$$stage.sha256.tmp" "$$final.sha256"; \
rmdir "$$lock"; \
trap - EXIT HUP INT TERM
conformance-pg18-extension-artifact: override CONFORMANCE_PG18_EXTENSION_ARTIFACT_POLICY := certified
conformance-pg18-extension-test-artifact: override CONFORMANCE_PG18_EXTENSION_ARTIFACT_POLICY := runtime
conformance-pg18-extension-artifact conformance-pg18-extension-test-artifact:
@set -eu; \
export LC_ALL=C; \
test -n "$(PGRX_PG_CONFIG)" || { echo "PGRX_PG_CONFIG is required" >&2; exit 1; }; \
postgresql_version="$$($(PGRX_PG_CONFIG) --version | awk '{print $$2}')"; \
case "$(CONFORMANCE_PG18_EXTENSION_ARTIFACT_POLICY)" in \
certified) test "$$postgresql_version" = "18.3" || { echo "PGRX_PG_CONFIG must select PostgreSQL 18.3, found: $$($(PGRX_PG_CONFIG) --version)" >&2; exit 1; } ;; \
runtime) printf '%s\n' "$$postgresql_version" | awk 'NR == 1 && $$0 ~ /^18\.[0-9]+$$/ { valid = 1 } END { exit valid && NR == 1 ? 0 : 1 }' || { echo "PGRX_PG_CONFIG must select PostgreSQL 18.x, found: $$($(PGRX_PG_CONFIG) --version)" >&2; exit 1; } ;; \
*) echo "extension artifact PostgreSQL version policy is invalid" >&2; exit 1 ;; \
esac; \
final="$(CONFORMANCE_EXTENSION_ARTIFACT)"; \
parent="$$(dirname "$$final")"; \
out="$$final.tmp.$$$$"; \
lock="$$final.publish-lock"; \
mkdir -p "$$parent"; \
mkdir "$$lock" || { echo "extension artifact publication is locked" >&2; exit 1; }; \
cleanup() { rm -rf "$$out"; rmdir "$$lock" 2>/dev/null || true; }; \
trap cleanup EXIT HUP INT TERM; \
test ! -e "$$final" || { echo "$$final already exists" >&2; exit 1; }; \
(cd extensions/synchro-pg && CARGO_TARGET_DIR="$(PGRX_TARGET_DIR)" cargo pgrx package --pg-config "$(PGRX_PG_CONFIG)" --out-dir "$$out"); \
pkglibdir="$$($(PGRX_PG_CONFIG) --pkglibdir)"; \
sharedir="$$($(PGRX_PG_CONFIG) --sharedir)"; \
case "$$(uname -s)" in Darwin) suffix=dylib ;; *) suffix=so ;; esac; \
library="$$out$$pkglibdir/synchro_pg.$$suffix"; \
control="$$out$$sharedir/extension/synchro_pg.control"; \
sql="$$out$$sharedir/extension/synchro_pg--$(CURRENT_VERSION).sql"; \
test -f "$$library" && test -f "$$control" && test -f "$$sql" || { echo "pgrx package omitted a required extension file" >&2; exit 1; }; \
perl -pi -e 's/[ \t]+$$//' "$$sql"; \
perl -0pi -e 's/\n+\z/\n/' "$$sql"; \
cmp -s extensions/synchro-pg/sql/synchro_pg--$(CURRENT_VERSION).sql "$$sql" || { echo "packaged PostgreSQL SQL differs from the tracked artifact. Run make generate-pg-sql" >&2; exit 1; }; \
cmp -s extensions/synchro-pg/synchro_pg.control "$$control" || { echo "packaged PostgreSQL control file differs from the tracked artifact" >&2; exit 1; }; \
update_records=""; \
for tracked in extensions/synchro-pg/sql/synchro_pg--*--*.sql; do \
test -e "$$tracked" || continue; \
name="$${tracked##*/}"; \
update="$$out$$sharedir/extension/$$name"; \
test -f "$$update" && cmp -s "$$tracked" "$$update" || { echo "packaged PostgreSQL update SQL differs from the tracked artifact: $$name" >&2; exit 1; }; \
update_path="$${update#"$$out"/}"; \
update_hash="$$(shasum -a 256 "$$update" | cut -d ' ' -f 1)"; \
test -n "$$update_hash"; \
update_records="$$update_records$$(printf ',\n {"path": "%s", "destination": "sharedir/extension/%s", "sha256": "%s"}' "$$update_path" "$$name" "$$update_hash")"; \
done; \
for packaged in "$$out$$sharedir"/extension/synchro_pg--*.sql; do \
name="$${packaged##*/}"; \
case "$$name" in \
"synchro_pg--$(CURRENT_VERSION).sql") ;; \
synchro_pg--*--*.sql) test -f "extensions/synchro-pg/sql/$$name" || { echo "pgrx package contains an untracked extension SQL file: $$name" >&2; exit 1; } ;; \
*) echo "pgrx package contains an untracked extension SQL file: $$name" >&2; exit 1 ;; \
esac; \
done; \
library_path="$${library#"$$out"/}"; \
control_path="$${control#"$$out"/}"; \
sql_path="$${sql#"$$out"/}"; \
library_hash="$$(shasum -a 256 "$$library" | cut -d ' ' -f 1)"; \
control_hash="$$(shasum -a 256 "$$control" | cut -d ' ' -f 1)"; \
sql_hash="$$(shasum -a 256 "$$sql" | cut -d ' ' -f 1)"; \
printf '%s\n' \
'{' \
' "format": "synchro-pg18-extension-bundle-v1",' \
' "postgresql_major": 18,' \
" \"postgresql_version\": \"$$postgresql_version\"," \
' "files": [' \
" {\"path\": \"$$library_path\", \"destination\": \"pkglibdir/synchro_pg.$$suffix\", \"sha256\": \"$$library_hash\"}," \
" {\"path\": \"$$control_path\", \"destination\": \"sharedir/extension/synchro_pg.control\", \"sha256\": \"$$control_hash\"}," \
" {\"path\": \"$$sql_path\", \"destination\": \"sharedir/extension/synchro_pg--$(CURRENT_VERSION).sql\", \"sha256\": \"$$sql_hash\"}$$update_records" \
' ]' \
'}' > "$$out/artifact-manifest.json.tmp"; \
mv "$$out/artifact-manifest.json.tmp" "$$out/artifact-manifest.json"; \
manifest_digest="$$(shasum -a 256 "$$out/artifact-manifest.json" | cut -d ' ' -f 1)"; \
test -n "$$manifest_digest"; \
printf '%s\n' "$$manifest_digest" > "$$out/artifact-manifest.json.sha256.tmp"; \
mv "$$out/artifact-manifest.json.sha256.tmp" "$$out/artifact-manifest.json.sha256"; \
mv "$$out" "$$final"; \
rmdir "$$lock"; \
trap - EXIT HUP INT TERM
# The baseline is the first update origin. Each later released origin is a
# pinned published bundle in CONFORMANCE_UPDATE_ORIGIN_EXTENSION_ARTIFACTS/<version>.
conformance-update-baseline-extension-artifact:
@set -eu; \
export LC_ALL=C; \
final="$(CONFORMANCE_UPDATE_BASELINE_EXTENSION_ARTIFACT)"; \
origins="$(CONFORMANCE_UPDATE_ORIGIN_EXTENSION_ARTIFACTS)"; \
test ! -e "$$final" || { echo "$$final already exists" >&2; exit 1; }; \
test ! -e "$$origins" || { echo "$$origins already exists" >&2; exit 1; }; \
artifact="$$(cd api/go && GOWORK=off go run ./cmd/synchro-version update-baseline-artifact)"; \
set -- $$artifact; \
test "$$#" -eq 2 || { echo "update baseline artifact must have one URL and one SHA-256 digest" >&2; exit 1; }; \
baseline_url="$$1"; \
baseline_digest="$$2"; \
later="$$(python3 scripts/update-origins.py extensions/synchro-pg/update-origins.json extensions/synchro-pg/update-baseline.json)"; \
work="$$final.tmp.$$$$"; \
mkdir -p "$$(dirname "$$final")"; \
cleanup() { rm -rf "$$work"; }; \
trap cleanup EXIT HUP INT TERM; \
mkdir "$$work" "$$work/origins"; \
fetch() { \
mkdir "$$work/extract"; \
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --output "$$work/archive.tar.gz" "$$1"; \
test "$$(shasum -a 256 "$$work/archive.tar.gz" | cut -d ' ' -f 1)" = "$$2" || { echo "update origin archive SHA-256 differs from the pinned digest" >&2; exit 1; }; \
tar -xzf "$$work/archive.tar.gz" -C "$$work/extract"; \
manifest="$$work/extract/extension/artifact-manifest.json"; \
test -f "$$manifest" && test -f "$$manifest.sha256" || { echo "update origin archive omitted the extension manifest or its digest" >&2; exit 1; }; \
test "$$(shasum -a 256 "$$manifest" | cut -d ' ' -f 1)" = "$$(cat "$$manifest.sha256")" || { echo "update origin extension manifest differs from its digest" >&2; exit 1; }; \
mv "$$work/extract/extension" "$$3"; \
rm -rf "$$work/extract" "$$work/archive.tar.gz"; \
}; \
fetch "$$baseline_url" "$$baseline_digest" "$$work/baseline"; \
printf '%s\n' "$$later" | while read -r version url digest; do \
test -n "$$version" || continue; \
fetch "$$url" "$$digest" "$$work/origins/$$version"; \
done; \
mkdir -p "$$(dirname "$$origins")"; \
mv "$$work/origins" "$$origins"; \
mv "$$work/baseline" "$$final"; \
rm -rf "$$work"; \
trap - EXIT HUP INT TERM
test-blackbox: conformance-mod-download test-blackbox-harness test-blackbox-components
$(call declared_selection,GO_TEST_ARGS BLACKBOX_TEST_COUNT)
cd conformance && GOFLAGS= GOWORK=off SOAK_SEED="$(SOAK_SEED)" SOAK_OPERATIONS="$(SOAK_OPERATIONS)" SOAK_ARTIFACT_DIR="$(abspath $(SOAK_ARTIFACT_DIR))" SOAK_REPLAY_JOURNAL= \
go run ./cmd/testresult suite -- go test $(GO_TEST_ARGS) -json ./blackbox/integration -count=$(BLACKBOX_TEST_COUNT) -timeout=$(BLACKBOX_TIMEOUT) -args --provision --install
test-conformance: conformance-mod-download test-conformance-testresult test-conformance-imports test-conformance-contract test-conformance-drivers test-conformance-scenarios test-conformance-dataset check-conformance-catalog test-vectors test-conformance-faults test-invariants test-conformance-invariants test-blackbox-harness
release-stage-server: version-check
@test -n "$(VERSION)" && test "$(VERSION)" = "$(CURRENT_VERSION)" || { echo "VERSION=$(CURRENT_VERSION) is required" >&2; exit 1; }
@set -eu; \
test "$$(uname -s)" = Linux && test "$$(uname -m)" = x86_64 || { echo "release server components require Linux x64" >&2; exit 1; }; \
test -z "$$(git status --porcelain --untracked-files=normal)" || { echo "release staging requires a clean worktree" >&2; exit 1; }; \
revision="$$(git rev-parse --verify HEAD)"; \
final="$(abspath $(RELEASE_SERVER_DIR))"; \
stage="$$final.tmp.$$$$"; \
trap 'rm -rf "$$stage"' EXIT HUP INT TERM; \
test ! -e "$$final" || { echo "server component staging already exists: $$final" >&2; exit 1; }; \
mkdir -p "$$stage/extension" "$$stage/adapter" "$$stage/seed"; \
$(MAKE) --no-print-directory conformance-pg18-extension-artifact CONFORMANCE_EXTENSION_ARTIFACT="$$stage/extension-bundle"; \
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(MAKE) --no-print-directory build BINARY="$$stage/adapter/synchrod-pg-linux-x64-$(VERSION)"; \
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(MAKE) --no-print-directory build-seed SEED_BINARY="$$stage/seed/synchro-seed-linux-x64-$(VERSION)"; \
python3 scripts/release-artifacts.py archive-extension --source "$$stage/extension-bundle" --output "$$stage/extension/synchro-pg-pg18-ubuntu24.04-linux-x64-$(VERSION).tar.gz"; \
rm -rf "$$stage/extension-bundle"; \
python3 scripts/release-artifacts.py server-metadata --output "$$stage/server-metadata.json" --source-commit "$$revision" \
--binary "adapter/synchrod-pg-linux-x64-$(VERSION)=$$stage/adapter/synchrod-pg-linux-x64-$(VERSION)" \
--binary "seed/synchro-seed-linux-x64-$(VERSION)=$$stage/seed/synchro-seed-linux-x64-$(VERSION)"; \
test -z "$$(git status --porcelain --untracked-files=normal)" && test "$$(git rev-parse --verify HEAD)" = "$$revision" || { echo "source changed during release server staging" >&2; exit 1; }; \
mkdir -p "$$(dirname "$$final")"; \
mv "$$stage" "$$final"; \
trap - EXIT HUP INT TERM
release-stage-packages: version-check
@test -n "$(VERSION)" && test "$(VERSION)" = "$(CURRENT_VERSION)" || { echo "VERSION=$(CURRENT_VERSION) is required" >&2; exit 1; }
@test -n "$(ANDROID_JAVA_HOME)" || { echo "ANDROID_JAVA_HOME is required" >&2; exit 1; }
@test -d "$(ANDROID_HOME)" || { echo "ANDROID_HOME is required" >&2; exit 1; }
@set -eu; \
test -z "$$(git status --porcelain --untracked-files=normal)" || { echo "release staging requires a clean worktree" >&2; exit 1; }; \
revision="$$(git rev-parse --verify HEAD)"; \
final="$(abspath $(RELEASE_PACKAGE_DIR))"; \
stage="$$final.tmp.$$$$"; \
trap 'rm -rf "$$stage"' EXIT HUP INT TERM; \
test ! -e "$$final" || { echo "package component staging already exists: $$final" >&2; exit 1; }; \
mkdir -p "$$stage/maven" "$$stage/npm" "$$stage/maven-repository"; \
(cd clients/kotlin && ANDROID_HOME="$(ANDROID_HOME)" ANDROID_SDK_ROOT="$(ANDROID_HOME)" \
JAVA_HOME="$(ANDROID_JAVA_HOME)" PATH="$(ANDROID_JAVA_HOME)/bin:$$PATH" \
SYNCHRO_RELEASE_MAVEN_REPOSITORY="$$stage/maven-repository" \
./gradlew -Pversion="$(VERSION)" :synchro:releaseBundle); \
python3 scripts/release-artifacts.py prepare-maven --repository "$$stage/maven-repository" --version "$(VERSION)"; \
python3 scripts/release-artifacts.py archive-maven --source "$$stage/maven-repository" --output "$$stage/maven/synchro-maven-$(VERSION).zip" --version "$(VERSION)"; \
rm -rf "$$stage/maven-repository"; \
(cd clients/react-native && corepack enable >/dev/null 2>&1 && yarn install --immutable && yarn prepare && npm pack --ignore-scripts --silent --pack-destination "$$stage/npm"); \