@fairmint/canton-node-sdk 0.0.223 → 0.0.224
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/canton-localnet +182 -0
- package/package.json +14 -8
- package/scripts/localnet-cloud.sh +816 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PACKAGE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
LOCALNET_SCRIPT="${PACKAGE_ROOT}/scripts/localnet-cloud.sh"
|
|
6
|
+
DEFAULT_QUICKSTART_REPO="https://github.com/digital-asset/cn-quickstart.git"
|
|
7
|
+
DEFAULT_QUICKSTART_REF="46201cd27ee16c99f78ab97b7e6513c56f28e004"
|
|
8
|
+
|
|
9
|
+
log() {
|
|
10
|
+
printf '[canton-localnet] %s\n' "$*"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
usage() {
|
|
14
|
+
cat <<'USAGE'
|
|
15
|
+
Usage: canton-localnet <command>
|
|
16
|
+
|
|
17
|
+
Commands:
|
|
18
|
+
setup Prepare LocalNet prerequisites
|
|
19
|
+
start Start lean LocalNet + Keycloak and wait for ready endpoints
|
|
20
|
+
stop Stop LocalNet services
|
|
21
|
+
logs Show LocalNet diagnostic logs
|
|
22
|
+
status Show docker + endpoint status
|
|
23
|
+
smoke Run endpoint smoke checks
|
|
24
|
+
test Run project integration tests when configured
|
|
25
|
+
verify Run setup + start + smoke + test
|
|
26
|
+
|
|
27
|
+
One-liners:
|
|
28
|
+
npx @fairmint/canton-node-sdk start
|
|
29
|
+
npx @fairmint/canton-node-sdk verify
|
|
30
|
+
|
|
31
|
+
Environment:
|
|
32
|
+
CANTON_LOCALNET_QUICKSTART_DIR Use an existing cn-quickstart/quickstart directory
|
|
33
|
+
CANTON_LOCALNET_CACHE_DIR Cache root for fetched cn-quickstart assets
|
|
34
|
+
CANTON_LOCALNET_QUICKSTART_REF cn-quickstart git ref to fetch
|
|
35
|
+
CANTON_LOCALNET_INFRA_ONLY Defaults to true for this package binary
|
|
36
|
+
USAGE
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
require_command() {
|
|
40
|
+
local cmd="$1"
|
|
41
|
+
if ! command -v "${cmd}" >/dev/null 2>&1; then
|
|
42
|
+
log "Missing required command: ${cmd}"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
cache_root() {
|
|
48
|
+
if [[ -n "${CANTON_LOCALNET_CACHE_DIR:-}" ]]; then
|
|
49
|
+
printf '%s' "${CANTON_LOCALNET_CACHE_DIR}"
|
|
50
|
+
return
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if [[ -n "${XDG_CACHE_HOME:-}" ]]; then
|
|
54
|
+
printf '%s' "${XDG_CACHE_HOME}/fairmint/canton-localnet"
|
|
55
|
+
return
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
printf '%s' "${HOME}/.cache/fairmint/canton-localnet"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
quickstart_ref() {
|
|
62
|
+
printf '%s' "${CANTON_LOCALNET_QUICKSTART_REF:-${DEFAULT_QUICKSTART_REF}}"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
quickstart_repo() {
|
|
66
|
+
printf '%s' "${CANTON_LOCALNET_QUICKSTART_REPO:-${DEFAULT_QUICKSTART_REPO}}"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
default_quickstart_root() {
|
|
70
|
+
printf '%s/cn-quickstart-%s' "$(cache_root)" "$(quickstart_ref)"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
repo_quickstart_dir() {
|
|
74
|
+
printf '%s/libs/cn-quickstart/quickstart' "${PACKAGE_ROOT}"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
use_repo_quickstart_default() {
|
|
78
|
+
[[ -z "${CANTON_LOCALNET_QUICKSTART_DIR:-}" \
|
|
79
|
+
&& -f "${PACKAGE_ROOT}/.gitmodules" \
|
|
80
|
+
&& ( -d "${PACKAGE_ROOT}/.git" || -f "${PACKAGE_ROOT}/.git" ) ]]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
quickstart_dir() {
|
|
84
|
+
if [[ -n "${CANTON_LOCALNET_QUICKSTART_DIR:-}" ]]; then
|
|
85
|
+
printf '%s' "${CANTON_LOCALNET_QUICKSTART_DIR}"
|
|
86
|
+
return
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if use_repo_quickstart_default; then
|
|
90
|
+
repo_quickstart_dir
|
|
91
|
+
return
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
printf '%s/quickstart' "$(default_quickstart_root)"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
ensure_quickstart_checkout() {
|
|
98
|
+
local dir=""
|
|
99
|
+
local root=""
|
|
100
|
+
local tmp=""
|
|
101
|
+
|
|
102
|
+
if use_repo_quickstart_default; then
|
|
103
|
+
return
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
dir="$(quickstart_dir)"
|
|
107
|
+
if [[ -d "${dir}/docker/modules/localnet" ]]; then
|
|
108
|
+
return
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
if [[ -n "${CANTON_LOCALNET_QUICKSTART_DIR:-}" ]]; then
|
|
112
|
+
log "CANTON_LOCALNET_QUICKSTART_DIR is not a cn-quickstart quickstart directory: ${dir}"
|
|
113
|
+
exit 1
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
require_command git
|
|
117
|
+
|
|
118
|
+
root="$(default_quickstart_root)"
|
|
119
|
+
if [[ -e "${root}" ]]; then
|
|
120
|
+
log "Cached cn-quickstart directory exists but is incomplete: ${root}"
|
|
121
|
+
log "Remove it or set CANTON_LOCALNET_CACHE_DIR to use a fresh cache."
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
mkdir -p "$(cache_root)"
|
|
126
|
+
tmp="$(mktemp -d "$(cache_root)/cn-quickstart.XXXXXX")"
|
|
127
|
+
trap 'rm -rf "${tmp}"' RETURN
|
|
128
|
+
|
|
129
|
+
log "Fetching cn-quickstart $(quickstart_ref)..."
|
|
130
|
+
git -C "${tmp}" init -q
|
|
131
|
+
git -C "${tmp}" remote add origin "$(quickstart_repo)"
|
|
132
|
+
if git -C "${tmp}" fetch --depth 1 origin "$(quickstart_ref)"; then
|
|
133
|
+
git -C "${tmp}" checkout --detach FETCH_HEAD -q
|
|
134
|
+
else
|
|
135
|
+
log "Direct ref fetch failed; retrying with a blobless repository fetch."
|
|
136
|
+
git -C "${tmp}" fetch --filter=blob:none origin
|
|
137
|
+
git -C "${tmp}" checkout --detach "$(quickstart_ref)" -q
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if [[ ! -d "${tmp}/quickstart/docker/modules/localnet" ]]; then
|
|
141
|
+
log "Fetched cn-quickstart does not contain quickstart/docker/modules/localnet."
|
|
142
|
+
exit 1
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
mv "${tmp}" "${root}"
|
|
146
|
+
trap - RETURN
|
|
147
|
+
log "Cached cn-quickstart at ${root}."
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
main() {
|
|
151
|
+
local command="${1:-}"
|
|
152
|
+
local dir=""
|
|
153
|
+
|
|
154
|
+
case "${command}" in
|
|
155
|
+
"" | -h | --help | help)
|
|
156
|
+
usage
|
|
157
|
+
if [[ -z "${command}" ]]; then
|
|
158
|
+
exit 1
|
|
159
|
+
fi
|
|
160
|
+
exit 0
|
|
161
|
+
;;
|
|
162
|
+
setup | start | verify)
|
|
163
|
+
ensure_quickstart_checkout
|
|
164
|
+
;;
|
|
165
|
+
logs | status | stop | smoke | test)
|
|
166
|
+
;;
|
|
167
|
+
*)
|
|
168
|
+
usage
|
|
169
|
+
exit 1
|
|
170
|
+
;;
|
|
171
|
+
esac
|
|
172
|
+
|
|
173
|
+
dir="$(quickstart_dir)"
|
|
174
|
+
if ! use_repo_quickstart_default; then
|
|
175
|
+
export CANTON_LOCALNET_QUICKSTART_DIR="${dir}"
|
|
176
|
+
fi
|
|
177
|
+
export CANTON_LOCALNET_INFRA_ONLY="${CANTON_LOCALNET_INFRA_ONLY:-true}"
|
|
178
|
+
|
|
179
|
+
exec bash "${LOCALNET_SCRIPT}" "$@"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main "$@"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fairmint/canton-node-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.224",
|
|
4
4
|
"description": "Canton Node SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canton",
|
|
@@ -21,7 +21,12 @@
|
|
|
21
21
|
"author": "Fairmint",
|
|
22
22
|
"main": "build/src/index.js",
|
|
23
23
|
"types": "build/src/index.d.ts",
|
|
24
|
+
"bin": {
|
|
25
|
+
"canton-localnet": "bin/canton-localnet"
|
|
26
|
+
},
|
|
24
27
|
"files": [
|
|
28
|
+
"bin/canton-localnet",
|
|
29
|
+
"scripts/localnet-cloud.sh",
|
|
25
30
|
"build/**"
|
|
26
31
|
],
|
|
27
32
|
"scripts": {
|
|
@@ -42,13 +47,14 @@
|
|
|
42
47
|
"lint:fix": "eslint . --fix",
|
|
43
48
|
"lint:npm": "npmPkgJsonLint . --fix",
|
|
44
49
|
"lint:npm:check": "npmPkgJsonLint .",
|
|
45
|
-
"localnet:quickstart": "bash ./
|
|
46
|
-
"localnet:
|
|
47
|
-
"localnet:
|
|
48
|
-
"localnet:
|
|
49
|
-
"localnet:
|
|
50
|
-
"localnet:
|
|
51
|
-
"localnet:
|
|
50
|
+
"localnet:quickstart": "bash ./bin/canton-localnet setup",
|
|
51
|
+
"localnet:logs": "bash ./bin/canton-localnet logs",
|
|
52
|
+
"localnet:setup": "bash ./bin/canton-localnet setup",
|
|
53
|
+
"localnet:smoke": "bash ./bin/canton-localnet smoke",
|
|
54
|
+
"localnet:start": "bash ./bin/canton-localnet start",
|
|
55
|
+
"localnet:status": "bash ./bin/canton-localnet status",
|
|
56
|
+
"localnet:stop": "bash ./bin/canton-localnet stop",
|
|
57
|
+
"localnet:verify": "bash ./bin/canton-localnet verify",
|
|
52
58
|
"prepack": "npm run clean && npm run build:core",
|
|
53
59
|
"prepare-release": "tsx scripts/prepare-release.ts",
|
|
54
60
|
"prepublishOnly": "npm run prepack",
|
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
6
|
+
PROJECT_ROOT="${CANTON_LOCALNET_PROJECT_ROOT:-$(pwd)}"
|
|
7
|
+
QUICKSTART_DIR="${CANTON_LOCALNET_QUICKSTART_DIR:-${REPO_ROOT}/libs/cn-quickstart/quickstart}"
|
|
8
|
+
DOCKERD_PID_FILE="/tmp/localnet-dockerd.pid"
|
|
9
|
+
DOCKERD_LOG_FILE="/tmp/localnet-dockerd.log"
|
|
10
|
+
HOSTS_ENTRY="127.0.0.1 scan.localhost sv.localhost wallet.localhost"
|
|
11
|
+
CURL_CONNECT_TIMEOUT=2
|
|
12
|
+
CURL_MAX_TIME=5
|
|
13
|
+
|
|
14
|
+
log() {
|
|
15
|
+
printf '[localnet] %s\n' "$*"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
is_truthy() {
|
|
19
|
+
case "${1,,}" in
|
|
20
|
+
1 | true | yes | on)
|
|
21
|
+
return 0
|
|
22
|
+
;;
|
|
23
|
+
*)
|
|
24
|
+
return 1
|
|
25
|
+
;;
|
|
26
|
+
esac
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
require_command() {
|
|
30
|
+
local cmd="$1"
|
|
31
|
+
if ! command -v "${cmd}" >/dev/null 2>&1; then
|
|
32
|
+
log "Missing required command: ${cmd}"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ensure_sudo() {
|
|
38
|
+
if ! sudo_noninteractive_available; then
|
|
39
|
+
log "Passwordless sudo is required in this cloud environment."
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
sudo_noninteractive_available() {
|
|
45
|
+
command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ensure_docker_packages() {
|
|
49
|
+
if command -v docker >/dev/null 2>&1 \
|
|
50
|
+
&& docker compose version >/dev/null 2>&1; then
|
|
51
|
+
return
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if ! sudo_noninteractive_available || ! command -v apt-get >/dev/null 2>&1; then
|
|
55
|
+
log "Docker with Compose is required. Install Docker and start it, then retry."
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
log "Installing Docker packages..."
|
|
60
|
+
sudo apt-get update
|
|
61
|
+
sudo apt-get install -y docker.io docker-compose docker-compose-v2
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ensure_legacy_iptables() {
|
|
65
|
+
if ! command -v update-alternatives >/dev/null 2>&1 \
|
|
66
|
+
|| [[ ! -x /usr/sbin/iptables-legacy || ! -x /usr/sbin/ip6tables-legacy ]]; then
|
|
67
|
+
log "iptables legacy binaries unavailable; skipping backend switch."
|
|
68
|
+
return
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if ! iptables --version 2>/dev/null | grep -q 'legacy'; then
|
|
72
|
+
log "Switching iptables to legacy backend..."
|
|
73
|
+
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
if ! ip6tables --version 2>/dev/null | grep -q 'legacy'; then
|
|
77
|
+
log "Switching ip6tables to legacy backend..."
|
|
78
|
+
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
docker_ready() {
|
|
83
|
+
docker info >/dev/null 2>&1 || (sudo_noninteractive_available && sudo docker info >/dev/null 2>&1)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
configure_docker_socket_permissions() {
|
|
87
|
+
if [[ ! -S /var/run/docker.sock ]]; then
|
|
88
|
+
return
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
if ! sudo_noninteractive_available; then
|
|
92
|
+
return
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
sudo groupadd -f docker >/dev/null 2>&1 || true
|
|
96
|
+
sudo chown root:docker /var/run/docker.sock >/dev/null 2>&1 || true
|
|
97
|
+
sudo chmod 660 /var/run/docker.sock || true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
run_docker() {
|
|
101
|
+
if docker info >/dev/null 2>&1; then
|
|
102
|
+
docker "$@"
|
|
103
|
+
return
|
|
104
|
+
fi
|
|
105
|
+
if ! sudo_noninteractive_available || ! sudo docker info >/dev/null 2>&1; then
|
|
106
|
+
log "Docker daemon is not available."
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
sudo docker "$@"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
resolve_quickstart_image_tag() {
|
|
113
|
+
local env_file=""
|
|
114
|
+
local splice_version=""
|
|
115
|
+
local parsed_value=""
|
|
116
|
+
|
|
117
|
+
for env_file in "${QUICKSTART_DIR}/.env" "${QUICKSTART_DIR}/.env.local"; do
|
|
118
|
+
if [[ ! -f "${env_file}" ]]; then
|
|
119
|
+
continue
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
parsed_value="$(awk -F= -v key="SPLICE_VERSION" '
|
|
123
|
+
$0 ~ /^[[:space:]]*#/ { next }
|
|
124
|
+
$1 == key {
|
|
125
|
+
value = substr($0, index($0, "=") + 1)
|
|
126
|
+
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
|
127
|
+
gsub(/^"|"$/, "", value)
|
|
128
|
+
gsub(/^'\''|'\''$/, "", value)
|
|
129
|
+
parsed = value
|
|
130
|
+
}
|
|
131
|
+
END {
|
|
132
|
+
if (parsed != "") {
|
|
133
|
+
print parsed
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
' "${env_file}")"
|
|
137
|
+
|
|
138
|
+
if [[ -n "${parsed_value}" ]]; then
|
|
139
|
+
splice_version="${parsed_value}"
|
|
140
|
+
fi
|
|
141
|
+
done
|
|
142
|
+
|
|
143
|
+
printf '%s' "${splice_version}"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
run_quickstart_command() {
|
|
147
|
+
local command="$1"
|
|
148
|
+
local docker_shim_dir=""
|
|
149
|
+
local quickstart_image_tag=""
|
|
150
|
+
local status=0
|
|
151
|
+
|
|
152
|
+
quickstart_image_tag="$(resolve_quickstart_image_tag)"
|
|
153
|
+
|
|
154
|
+
if ! docker info >/dev/null 2>&1 && sudo docker info >/dev/null 2>&1; then
|
|
155
|
+
docker_shim_dir="$(mktemp -d "/tmp/canton-localnet-docker-shim.XXXXXX")"
|
|
156
|
+
cat >"${docker_shim_dir}/docker" <<'EOF'
|
|
157
|
+
#!/usr/bin/env bash
|
|
158
|
+
exec sudo -E docker "$@"
|
|
159
|
+
EOF
|
|
160
|
+
chmod +x "${docker_shim_dir}/docker"
|
|
161
|
+
log "Using sudo docker shim for quickstart command."
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
set +e
|
|
165
|
+
if [[ -n "${docker_shim_dir}" ]]; then
|
|
166
|
+
(
|
|
167
|
+
cd "${QUICKSTART_DIR}"
|
|
168
|
+
MODULES_DIR="${QUICKSTART_DIR}/docker/modules" \
|
|
169
|
+
LOCALNET_DIR="${QUICKSTART_DIR}/docker/modules/localnet" \
|
|
170
|
+
IMAGE_TAG="${quickstart_image_tag}" \
|
|
171
|
+
PATH="${docker_shim_dir}:${HOME}/.daml/bin:${PATH}" \
|
|
172
|
+
bash -lc "${command}"
|
|
173
|
+
)
|
|
174
|
+
status=$?
|
|
175
|
+
else
|
|
176
|
+
(
|
|
177
|
+
cd "${QUICKSTART_DIR}"
|
|
178
|
+
MODULES_DIR="${QUICKSTART_DIR}/docker/modules" \
|
|
179
|
+
LOCALNET_DIR="${QUICKSTART_DIR}/docker/modules/localnet" \
|
|
180
|
+
IMAGE_TAG="${quickstart_image_tag}" \
|
|
181
|
+
PATH="${HOME}/.daml/bin:${PATH}" \
|
|
182
|
+
bash -lc "${command}"
|
|
183
|
+
)
|
|
184
|
+
status=$?
|
|
185
|
+
fi
|
|
186
|
+
set -e
|
|
187
|
+
|
|
188
|
+
if [[ -n "${docker_shim_dir}" ]]; then
|
|
189
|
+
rm -rf "${docker_shim_dir}"
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
return "${status}"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
run_quickstart_make() {
|
|
196
|
+
local target="$1"
|
|
197
|
+
run_quickstart_command "make ${target}"
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
run_infra_compose() {
|
|
201
|
+
local compose_args="$1"
|
|
202
|
+
|
|
203
|
+
run_quickstart_command "docker compose \
|
|
204
|
+
-f \"\${LOCALNET_DIR}/compose.yaml\" \
|
|
205
|
+
-f \"\${MODULES_DIR}/keycloak/compose.yaml\" \
|
|
206
|
+
--env-file .env \
|
|
207
|
+
--env-file .env.local \
|
|
208
|
+
--env-file \"\${LOCALNET_DIR}/compose.env\" \
|
|
209
|
+
--env-file \"\${LOCALNET_DIR}/env/common.env\" \
|
|
210
|
+
--env-file \"\${MODULES_DIR}/keycloak/compose.env\" \
|
|
211
|
+
--profile app-provider \
|
|
212
|
+
--profile app-user \
|
|
213
|
+
--profile sv \
|
|
214
|
+
--profile keycloak \
|
|
215
|
+
${compose_args}"
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
start_docker_daemon() {
|
|
219
|
+
local dockerd_pid=""
|
|
220
|
+
|
|
221
|
+
if docker_ready; then
|
|
222
|
+
configure_docker_socket_permissions
|
|
223
|
+
return
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
if ! sudo_noninteractive_available; then
|
|
227
|
+
log "Docker daemon is not running. Start Docker and retry."
|
|
228
|
+
exit 1
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
ensure_legacy_iptables
|
|
232
|
+
|
|
233
|
+
log "Starting Docker daemon with vfs storage driver..."
|
|
234
|
+
sudo nohup dockerd --host=unix:///var/run/docker.sock --pidfile="${DOCKERD_PID_FILE}" --storage-driver=vfs >"${DOCKERD_LOG_FILE}" 2>&1 &
|
|
235
|
+
|
|
236
|
+
for _ in $(seq 1 60); do
|
|
237
|
+
if sudo docker info >/dev/null 2>&1; then
|
|
238
|
+
configure_docker_socket_permissions
|
|
239
|
+
return
|
|
240
|
+
fi
|
|
241
|
+
if [[ -f "${DOCKERD_PID_FILE}" ]]; then
|
|
242
|
+
dockerd_pid="$(cat "${DOCKERD_PID_FILE}" 2>/dev/null || true)"
|
|
243
|
+
if [[ -n "${dockerd_pid}" ]] && ! ps -p "${dockerd_pid}" >/dev/null 2>&1; then
|
|
244
|
+
log "Docker daemon exited before becoming ready."
|
|
245
|
+
log "Inspect ${DOCKERD_LOG_FILE}."
|
|
246
|
+
exit 1
|
|
247
|
+
fi
|
|
248
|
+
fi
|
|
249
|
+
sleep 1
|
|
250
|
+
done
|
|
251
|
+
|
|
252
|
+
log "Docker failed to start. Inspect ${DOCKERD_LOG_FILE}."
|
|
253
|
+
exit 1
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
ensure_submodules() {
|
|
257
|
+
if [[ -n "${CANTON_LOCALNET_QUICKSTART_DIR:-}" ]]; then
|
|
258
|
+
if [[ -d "${QUICKSTART_DIR}" ]]; then
|
|
259
|
+
return
|
|
260
|
+
fi
|
|
261
|
+
log "Configured CANTON_LOCALNET_QUICKSTART_DIR does not exist: ${QUICKSTART_DIR}"
|
|
262
|
+
exit 1
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
if [[ -d "${REPO_ROOT}/libs/splice" && -d "${QUICKSTART_DIR}" ]]; then
|
|
266
|
+
return
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
require_command git
|
|
270
|
+
|
|
271
|
+
if ! git -C "${REPO_ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
272
|
+
log "Missing localnet assets and not in a git checkout. Install from npm with bundled assets or clone SDK with submodules."
|
|
273
|
+
exit 1
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
if [[ ! -d "${REPO_ROOT}/libs/splice" ]]; then
|
|
277
|
+
log "Initializing libs/splice submodule..."
|
|
278
|
+
git -C "${REPO_ROOT}" submodule update --init --depth 1 libs/splice
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
if [[ ! -d "${REPO_ROOT}/libs/cn-quickstart" ]]; then
|
|
282
|
+
log "Initializing libs/cn-quickstart submodule..."
|
|
283
|
+
git -C "${REPO_ROOT}" submodule update --init --recursive libs/cn-quickstart
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
if [[ ! -d "${QUICKSTART_DIR}" ]]; then
|
|
287
|
+
log "cn-quickstart directory not found after submodule init."
|
|
288
|
+
exit 1
|
|
289
|
+
fi
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
ensure_hosts_entries() {
|
|
293
|
+
if ! grep -Eq '(^|[[:space:]])scan\.localhost([[:space:]]|$)' /etc/hosts \
|
|
294
|
+
|| ! grep -Eq '(^|[[:space:]])sv\.localhost([[:space:]]|$)' /etc/hosts \
|
|
295
|
+
|| ! grep -Eq '(^|[[:space:]])wallet\.localhost([[:space:]]|$)' /etc/hosts; then
|
|
296
|
+
log "Adding localnet host aliases to /etc/hosts..."
|
|
297
|
+
if sudo_noninteractive_available; then
|
|
298
|
+
echo "${HOSTS_ENTRY}" | sudo tee -a /etc/hosts >/dev/null
|
|
299
|
+
return
|
|
300
|
+
fi
|
|
301
|
+
if [[ -t 0 ]] && command -v sudo >/dev/null 2>&1; then
|
|
302
|
+
echo "${HOSTS_ENTRY}" | sudo tee -a /etc/hosts >/dev/null
|
|
303
|
+
return
|
|
304
|
+
fi
|
|
305
|
+
log "Missing localnet host aliases. Add this line to /etc/hosts, then retry:"
|
|
306
|
+
log "${HOSTS_ENTRY}"
|
|
307
|
+
exit 1
|
|
308
|
+
fi
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
quickstart_setup() {
|
|
312
|
+
if [[ ! -f "${QUICKSTART_DIR}/.env.local" ]]; then
|
|
313
|
+
if quickstart_infra_only_enabled; then
|
|
314
|
+
write_quickstart_env_local
|
|
315
|
+
else
|
|
316
|
+
log "Running cn-quickstart setup (OAuth2 enabled)..."
|
|
317
|
+
(
|
|
318
|
+
cd "${QUICKSTART_DIR}"
|
|
319
|
+
# Match CI behavior first, then fall back to prompt-based answers for newer setup flows.
|
|
320
|
+
echo "2" | make setup || true
|
|
321
|
+
if ! grep -Eq '^AUTH_MODE=oauth2$' "${QUICKSTART_DIR}/.env.local" 2>/dev/null; then
|
|
322
|
+
printf 'y\ny\n\nn\n' | make setup
|
|
323
|
+
fi
|
|
324
|
+
)
|
|
325
|
+
fi
|
|
326
|
+
else
|
|
327
|
+
log "Reusing existing ${QUICKSTART_DIR}/.env.local."
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
if quickstart_infra_only_enabled; then
|
|
331
|
+
log "Skipping Daml SDK install for infrastructure-only localnet."
|
|
332
|
+
return
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
if [[ ! -x "${HOME}/.daml/bin/daml" ]]; then
|
|
336
|
+
log "Installing Daml SDK..."
|
|
337
|
+
(
|
|
338
|
+
cd "${QUICKSTART_DIR}"
|
|
339
|
+
make install-daml-sdk
|
|
340
|
+
)
|
|
341
|
+
else
|
|
342
|
+
log "Reusing existing Daml SDK at ${HOME}/.daml/bin/daml."
|
|
343
|
+
fi
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
default_party_hint() {
|
|
347
|
+
local clean_user=""
|
|
348
|
+
local raw_user="${USER:-runner}"
|
|
349
|
+
|
|
350
|
+
clean_user="$(printf '%s' "${raw_user}" | tr -cd '[:alpha:]')"
|
|
351
|
+
if [[ -z "${clean_user}" ]]; then
|
|
352
|
+
clean_user="runner"
|
|
353
|
+
fi
|
|
354
|
+
|
|
355
|
+
printf 'quickstart-%s-1' "${clean_user,,}"
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
write_quickstart_env_local() {
|
|
359
|
+
local auth_mode="${CANTON_LOCALNET_AUTH_MODE:-oauth2}"
|
|
360
|
+
local observability_enabled="${CANTON_LOCALNET_OBSERVABILITY_ENABLED:-false}"
|
|
361
|
+
local party_hint="${CANTON_LOCALNET_PARTY_HINT:-}"
|
|
362
|
+
local test_mode="${CANTON_LOCALNET_TEST_MODE:-off}"
|
|
363
|
+
|
|
364
|
+
if [[ -z "${party_hint}" ]]; then
|
|
365
|
+
party_hint="$(default_party_hint)"
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
log "Writing non-interactive cn-quickstart localnet config."
|
|
369
|
+
cat >"${QUICKSTART_DIR}/.env.local" <<EOF
|
|
370
|
+
OBSERVABILITY_ENABLED=${observability_enabled}
|
|
371
|
+
AUTH_MODE=${auth_mode}
|
|
372
|
+
PARTY_HINT=${party_hint}
|
|
373
|
+
TEST_MODE=${test_mode}
|
|
374
|
+
EOF
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
quickstart_fast_start_enabled() {
|
|
378
|
+
is_truthy "${CANTON_LOCALNET_FAST_START:-true}"
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
quickstart_force_full_start() {
|
|
382
|
+
is_truthy "${CANTON_LOCALNET_FORCE_FULL_START:-false}"
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
quickstart_infra_only_enabled() {
|
|
386
|
+
is_truthy "${CANTON_LOCALNET_INFRA_ONLY:-false}"
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
quickstart_build_artifacts_ready() {
|
|
390
|
+
local missing_paths=()
|
|
391
|
+
|
|
392
|
+
if [[ ! -f "${QUICKSTART_DIR}/backend/build/distributions/backend.tar" ]]; then
|
|
393
|
+
missing_paths+=("backend/build/distributions/backend.tar")
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
if [[ ! -d "${QUICKSTART_DIR}/frontend/dist" ]]; then
|
|
397
|
+
missing_paths+=("frontend/dist")
|
|
398
|
+
fi
|
|
399
|
+
|
|
400
|
+
if ! compgen -G "${QUICKSTART_DIR}/daml/licensing/.daml/dist/*.dar" >/dev/null; then
|
|
401
|
+
missing_paths+=("daml/licensing/.daml/dist/*.dar")
|
|
402
|
+
fi
|
|
403
|
+
|
|
404
|
+
if ! compgen -G "${QUICKSTART_DIR}/backend/build/otel-agent/opentelemetry-javaagent-*.jar" >/dev/null; then
|
|
405
|
+
missing_paths+=("backend/build/otel-agent/opentelemetry-javaagent-*.jar")
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
if [[ ${#missing_paths[@]} -gt 0 ]]; then
|
|
409
|
+
log "Fast start unavailable; missing quickstart build artifacts: ${missing_paths[*]}"
|
|
410
|
+
return 1
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
return 0
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
extract_quickstart_compose_up_command() {
|
|
417
|
+
(
|
|
418
|
+
cd "${QUICKSTART_DIR}"
|
|
419
|
+
make -n start 2>/dev/null | awk '/docker compose .* up -d --no-recreate/{line=$0} END{print line}'
|
|
420
|
+
)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
try_fast_start_localnet() {
|
|
424
|
+
local compose_up_command=""
|
|
425
|
+
|
|
426
|
+
compose_up_command="$(extract_quickstart_compose_up_command)"
|
|
427
|
+
if [[ -z "${compose_up_command}" ]]; then
|
|
428
|
+
log "Unable to determine quickstart compose startup command."
|
|
429
|
+
return 1
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
log "Starting cn-quickstart with fast path (skip quickstart rebuild)..."
|
|
433
|
+
run_quickstart_command "${compose_up_command}"
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
start_infra_only_localnet() {
|
|
437
|
+
log "Starting cn-quickstart LocalNet infrastructure only (skip quickstart app, PQS, and onboarding)."
|
|
438
|
+
run_infra_compose 'up -d --no-recreate'
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
stop_infra_only_localnet() {
|
|
442
|
+
if [[ ! -f "${QUICKSTART_DIR}/.env.local" ]]; then
|
|
443
|
+
return
|
|
444
|
+
fi
|
|
445
|
+
|
|
446
|
+
log "Stopping cn-quickstart LocalNet infrastructure-only stack..."
|
|
447
|
+
run_infra_compose down || true
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
wait_for_services() {
|
|
451
|
+
log "Waiting for Keycloak..."
|
|
452
|
+
for _ in $(seq 1 30); do
|
|
453
|
+
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
454
|
+
break
|
|
455
|
+
fi
|
|
456
|
+
sleep 2
|
|
457
|
+
done
|
|
458
|
+
if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
459
|
+
log "Keycloak did not become ready."
|
|
460
|
+
exit 1
|
|
461
|
+
fi
|
|
462
|
+
|
|
463
|
+
log "Waiting for Validator API..."
|
|
464
|
+
for _ in $(seq 1 30); do
|
|
465
|
+
code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)"
|
|
466
|
+
if [[ "${code}" == "200" || "${code}" == "401" ]]; then
|
|
467
|
+
break
|
|
468
|
+
fi
|
|
469
|
+
sleep 2
|
|
470
|
+
done
|
|
471
|
+
code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)"
|
|
472
|
+
if [[ "${code}" != "200" && "${code}" != "401" ]]; then
|
|
473
|
+
log "Validator API did not become ready."
|
|
474
|
+
exit 1
|
|
475
|
+
fi
|
|
476
|
+
|
|
477
|
+
log "Waiting for Scan API..."
|
|
478
|
+
for _ in $(seq 1 30); do
|
|
479
|
+
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then
|
|
480
|
+
break
|
|
481
|
+
fi
|
|
482
|
+
sleep 2
|
|
483
|
+
done
|
|
484
|
+
if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then
|
|
485
|
+
log "Scan API did not become ready."
|
|
486
|
+
exit 1
|
|
487
|
+
fi
|
|
488
|
+
|
|
489
|
+
log "All localnet services are ready."
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
start_localnet() {
|
|
493
|
+
if quickstart_force_full_start; then
|
|
494
|
+
log "Forcing full cn-quickstart start (CANTON_LOCALNET_FORCE_FULL_START=true)."
|
|
495
|
+
run_quickstart_make start
|
|
496
|
+
wait_for_services
|
|
497
|
+
return
|
|
498
|
+
fi
|
|
499
|
+
|
|
500
|
+
if quickstart_infra_only_enabled; then
|
|
501
|
+
start_infra_only_localnet
|
|
502
|
+
wait_for_services
|
|
503
|
+
return
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
if quickstart_fast_start_enabled && quickstart_build_artifacts_ready; then
|
|
507
|
+
if try_fast_start_localnet; then
|
|
508
|
+
wait_for_services
|
|
509
|
+
return
|
|
510
|
+
fi
|
|
511
|
+
log "Fast start failed; falling back to full cn-quickstart start."
|
|
512
|
+
fi
|
|
513
|
+
|
|
514
|
+
log "Starting cn-quickstart with full build..."
|
|
515
|
+
run_quickstart_make start
|
|
516
|
+
wait_for_services
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
stop_localnet() {
|
|
520
|
+
if [[ ! -d "${QUICKSTART_DIR}" ]]; then
|
|
521
|
+
log "cn-quickstart directory not found; nothing to stop."
|
|
522
|
+
stop_managed_dockerd
|
|
523
|
+
return
|
|
524
|
+
fi
|
|
525
|
+
|
|
526
|
+
log "Stopping cn-quickstart..."
|
|
527
|
+
stop_infra_only_localnet
|
|
528
|
+
if [[ -f "${QUICKSTART_DIR}/Makefile" ]]; then
|
|
529
|
+
run_quickstart_make stop || true
|
|
530
|
+
else
|
|
531
|
+
log "Quickstart Makefile not found; skipping quickstart stop target."
|
|
532
|
+
fi
|
|
533
|
+
stop_managed_dockerd
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
stop_managed_dockerd() {
|
|
537
|
+
local pid=""
|
|
538
|
+
local cmd=""
|
|
539
|
+
|
|
540
|
+
if [[ ! -f "${DOCKERD_PID_FILE}" ]]; then
|
|
541
|
+
return
|
|
542
|
+
fi
|
|
543
|
+
|
|
544
|
+
pid="$(cat "${DOCKERD_PID_FILE}" 2>/dev/null || true)"
|
|
545
|
+
if [[ -z "${pid}" ]]; then
|
|
546
|
+
rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}"
|
|
547
|
+
return
|
|
548
|
+
fi
|
|
549
|
+
|
|
550
|
+
if ! ps -p "${pid}" >/dev/null 2>&1; then
|
|
551
|
+
rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}"
|
|
552
|
+
return
|
|
553
|
+
fi
|
|
554
|
+
|
|
555
|
+
cmd="$(ps -p "${pid}" -o comm= 2>/dev/null | tr -d '[:space:]')"
|
|
556
|
+
if [[ "${cmd}" != "dockerd" ]]; then
|
|
557
|
+
log "PID ${pid} is not dockerd; skipping daemon cleanup."
|
|
558
|
+
rm -f "${DOCKERD_PID_FILE}"
|
|
559
|
+
return
|
|
560
|
+
fi
|
|
561
|
+
|
|
562
|
+
if ! sudo -n true >/dev/null 2>&1; then
|
|
563
|
+
log "Cannot stop managed dockerd without passwordless sudo."
|
|
564
|
+
return
|
|
565
|
+
fi
|
|
566
|
+
|
|
567
|
+
log "Stopping managed dockerd (pid ${pid})..."
|
|
568
|
+
sudo kill -TERM "${pid}" >/dev/null 2>&1 || true
|
|
569
|
+
for _ in $(seq 1 10); do
|
|
570
|
+
if ! ps -p "${pid}" >/dev/null 2>&1; then
|
|
571
|
+
break
|
|
572
|
+
fi
|
|
573
|
+
sleep 1
|
|
574
|
+
done
|
|
575
|
+
if ps -p "${pid}" >/dev/null 2>&1; then
|
|
576
|
+
sudo kill -KILL "${pid}" >/dev/null 2>&1 || true
|
|
577
|
+
fi
|
|
578
|
+
rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}"
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
status_localnet() {
|
|
582
|
+
if docker_ready; then
|
|
583
|
+
log "Docker daemon is running."
|
|
584
|
+
else
|
|
585
|
+
log "Docker daemon is not running."
|
|
586
|
+
exit 1
|
|
587
|
+
fi
|
|
588
|
+
|
|
589
|
+
run_docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
|
|
590
|
+
echo
|
|
591
|
+
|
|
592
|
+
keycloak_ok="no"
|
|
593
|
+
validator_ok="no"
|
|
594
|
+
scan_ok="no"
|
|
595
|
+
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
596
|
+
keycloak_ok="yes"
|
|
597
|
+
fi
|
|
598
|
+
validator_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)"
|
|
599
|
+
if [[ "${validator_code}" == "200" || "${validator_code}" == "401" ]]; then
|
|
600
|
+
validator_ok="yes"
|
|
601
|
+
fi
|
|
602
|
+
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then
|
|
603
|
+
scan_ok="yes"
|
|
604
|
+
fi
|
|
605
|
+
|
|
606
|
+
printf 'Keycloak ready: %s\n' "${keycloak_ok}"
|
|
607
|
+
printf 'Validator ready: %s (HTTP %s)\n' "${validator_ok}" "${validator_code:-n/a}"
|
|
608
|
+
printf 'Scan ready: %s\n' "${scan_ok}"
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
show_localnet_logs() {
|
|
612
|
+
log "==================== Docker Containers ===================="
|
|
613
|
+
if docker_ready; then
|
|
614
|
+
run_docker ps -a || true
|
|
615
|
+
else
|
|
616
|
+
log "Docker daemon is not running."
|
|
617
|
+
fi
|
|
618
|
+
|
|
619
|
+
echo
|
|
620
|
+
log "==================== Docker Compose Logs ===================="
|
|
621
|
+
if [[ -f "${QUICKSTART_DIR}/.env.local" ]]; then
|
|
622
|
+
run_infra_compose 'logs --tail=100' || true
|
|
623
|
+
elif [[ -d "${QUICKSTART_DIR}" ]]; then
|
|
624
|
+
log "Quickstart local env not found; skipping compose logs: ${QUICKSTART_DIR}/.env.local"
|
|
625
|
+
else
|
|
626
|
+
log "cn-quickstart directory not found: ${QUICKSTART_DIR}"
|
|
627
|
+
fi
|
|
628
|
+
|
|
629
|
+
echo
|
|
630
|
+
log "==================== Canton Logs ===================="
|
|
631
|
+
if [[ -f "${QUICKSTART_DIR}/logs/canton.log" ]]; then
|
|
632
|
+
tail -100 "${QUICKSTART_DIR}/logs/canton.log" || true
|
|
633
|
+
else
|
|
634
|
+
log "Canton log not found: ${QUICKSTART_DIR}/logs/canton.log"
|
|
635
|
+
fi
|
|
636
|
+
|
|
637
|
+
if [[ -f "${DOCKERD_LOG_FILE}" ]]; then
|
|
638
|
+
echo
|
|
639
|
+
log "==================== Managed Docker Daemon Logs ===================="
|
|
640
|
+
tail -100 "${DOCKERD_LOG_FILE}" || true
|
|
641
|
+
fi
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
run_smoke() {
|
|
645
|
+
local validator_code=""
|
|
646
|
+
|
|
647
|
+
log "Running localnet smoke checks..."
|
|
648
|
+
|
|
649
|
+
if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
650
|
+
log "Keycloak is not reachable."
|
|
651
|
+
exit 1
|
|
652
|
+
fi
|
|
653
|
+
|
|
654
|
+
validator_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)"
|
|
655
|
+
if [[ "${validator_code}" != "200" && "${validator_code}" != "401" ]]; then
|
|
656
|
+
log "Validator API is not reachable (HTTP ${validator_code})."
|
|
657
|
+
exit 1
|
|
658
|
+
fi
|
|
659
|
+
|
|
660
|
+
if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then
|
|
661
|
+
log "Scan API is not reachable."
|
|
662
|
+
exit 1
|
|
663
|
+
fi
|
|
664
|
+
|
|
665
|
+
log "Smoke checks passed."
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
read_npm_script() {
|
|
669
|
+
local target_dir="$1"
|
|
670
|
+
local script_name="$2"
|
|
671
|
+
|
|
672
|
+
if [[ ! -f "${target_dir}/package.json" ]]; then
|
|
673
|
+
return
|
|
674
|
+
fi
|
|
675
|
+
|
|
676
|
+
node -e 'const fs=require("fs"); const pkgPath=process.argv[1]; const scriptName=process.argv[2]; try { const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); const script = pkg?.scripts?.[scriptName]; if (typeof script === "string") process.stdout.write(script); } catch {}' "${target_dir}/package.json" "${script_name}"
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
script_is_recursive_localnet_test() {
|
|
680
|
+
local script_value="$1"
|
|
681
|
+
echo "${script_value}" | grep -Eq '(^|[[:space:]])(canton-localnet[[:space:]]+test|[^[:space:]]*localnet-cloud\.sh[[:space:]]+test)([[:space:]]|$)'
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
run_integration_tests() {
|
|
685
|
+
local script_value=""
|
|
686
|
+
|
|
687
|
+
if [[ -n "${CANTON_LOCALNET_TEST_CMD:-}" ]]; then
|
|
688
|
+
log "Running custom integration command from CANTON_LOCALNET_TEST_CMD..."
|
|
689
|
+
(
|
|
690
|
+
cd "${PROJECT_ROOT}"
|
|
691
|
+
bash -lc "${CANTON_LOCALNET_TEST_CMD}"
|
|
692
|
+
)
|
|
693
|
+
return
|
|
694
|
+
fi
|
|
695
|
+
|
|
696
|
+
script_value="$(read_npm_script "${PROJECT_ROOT}" "test:integration")"
|
|
697
|
+
if [[ -n "${script_value}" ]]; then
|
|
698
|
+
if script_is_recursive_localnet_test "${script_value}"; then
|
|
699
|
+
log "Skipping recursive test:integration script."
|
|
700
|
+
else
|
|
701
|
+
log "Running project integration tests: npm run test:integration"
|
|
702
|
+
(
|
|
703
|
+
cd "${PROJECT_ROOT}"
|
|
704
|
+
npm run test:integration
|
|
705
|
+
)
|
|
706
|
+
return
|
|
707
|
+
fi
|
|
708
|
+
fi
|
|
709
|
+
|
|
710
|
+
script_value="$(read_npm_script "${PROJECT_ROOT}" "test:localnet")"
|
|
711
|
+
if [[ -n "${script_value}" ]]; then
|
|
712
|
+
if script_is_recursive_localnet_test "${script_value}"; then
|
|
713
|
+
log "Skipping recursive test:localnet script."
|
|
714
|
+
else
|
|
715
|
+
log "Running project integration tests: npm run test:localnet"
|
|
716
|
+
(
|
|
717
|
+
cd "${PROJECT_ROOT}"
|
|
718
|
+
npm run test:localnet
|
|
719
|
+
)
|
|
720
|
+
return
|
|
721
|
+
fi
|
|
722
|
+
fi
|
|
723
|
+
|
|
724
|
+
if [[ "${PROJECT_ROOT}" == "${REPO_ROOT}" && -d "${REPO_ROOT}/test/integration/localnet" && -d "${REPO_ROOT}/node_modules" ]]; then
|
|
725
|
+
log "Running bundled SDK localnet integration tests..."
|
|
726
|
+
(
|
|
727
|
+
cd "${REPO_ROOT}"
|
|
728
|
+
npm test -- test/integration/localnet
|
|
729
|
+
)
|
|
730
|
+
return
|
|
731
|
+
fi
|
|
732
|
+
|
|
733
|
+
log "No integration test command configured; skipping test step."
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
usage() {
|
|
737
|
+
cat <<'USAGE'
|
|
738
|
+
Usage: scripts/localnet/localnet-cloud.sh <command>
|
|
739
|
+
|
|
740
|
+
Commands:
|
|
741
|
+
setup Install prerequisites, init submodules, configure quickstart
|
|
742
|
+
start Start localnet and wait for ready endpoints
|
|
743
|
+
stop Stop localnet services
|
|
744
|
+
logs Show localnet diagnostic logs
|
|
745
|
+
status Show docker + endpoint status
|
|
746
|
+
smoke Run endpoint smoke checks
|
|
747
|
+
test Run project integration tests (if configured)
|
|
748
|
+
verify Run setup + start + smoke + test
|
|
749
|
+
|
|
750
|
+
Environment:
|
|
751
|
+
CANTON_LOCALNET_FAST_START=true|false Enable fast startup path (default: true)
|
|
752
|
+
CANTON_LOCALNET_FORCE_FULL_START=true|false Force full startup with rebuild
|
|
753
|
+
CANTON_LOCALNET_INFRA_ONLY=true|false Start only LocalNet + Keycloak infrastructure
|
|
754
|
+
CANTON_LOCALNET_QUICKSTART_DIR=<path> Use an existing cn-quickstart/quickstart directory
|
|
755
|
+
USAGE
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
main() {
|
|
759
|
+
if [[ "${1:-}" == "" ]]; then
|
|
760
|
+
usage
|
|
761
|
+
exit 1
|
|
762
|
+
fi
|
|
763
|
+
|
|
764
|
+
case "$1" in
|
|
765
|
+
setup)
|
|
766
|
+
ensure_docker_packages
|
|
767
|
+
start_docker_daemon
|
|
768
|
+
ensure_submodules
|
|
769
|
+
ensure_hosts_entries
|
|
770
|
+
quickstart_setup
|
|
771
|
+
;;
|
|
772
|
+
start)
|
|
773
|
+
require_command curl
|
|
774
|
+
ensure_docker_packages
|
|
775
|
+
start_docker_daemon
|
|
776
|
+
ensure_submodules
|
|
777
|
+
ensure_hosts_entries
|
|
778
|
+
quickstart_setup
|
|
779
|
+
start_localnet
|
|
780
|
+
;;
|
|
781
|
+
stop)
|
|
782
|
+
stop_localnet
|
|
783
|
+
;;
|
|
784
|
+
logs)
|
|
785
|
+
show_localnet_logs
|
|
786
|
+
;;
|
|
787
|
+
status)
|
|
788
|
+
require_command curl
|
|
789
|
+
status_localnet
|
|
790
|
+
;;
|
|
791
|
+
smoke)
|
|
792
|
+
require_command curl
|
|
793
|
+
run_smoke
|
|
794
|
+
;;
|
|
795
|
+
test)
|
|
796
|
+
run_integration_tests
|
|
797
|
+
;;
|
|
798
|
+
verify)
|
|
799
|
+
require_command curl
|
|
800
|
+
ensure_docker_packages
|
|
801
|
+
start_docker_daemon
|
|
802
|
+
ensure_submodules
|
|
803
|
+
ensure_hosts_entries
|
|
804
|
+
quickstart_setup
|
|
805
|
+
start_localnet
|
|
806
|
+
run_smoke
|
|
807
|
+
run_integration_tests
|
|
808
|
+
;;
|
|
809
|
+
*)
|
|
810
|
+
usage
|
|
811
|
+
exit 1
|
|
812
|
+
;;
|
|
813
|
+
esac
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
main "$@"
|