@stll/oxlint-config 0.12.2 → 0.15.0
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/README.md +261 -1
- package/dist/cloud-setup-cli.cjs +6 -4
- package/dist/cloud-setup-cli.cjs.map +1 -1
- package/dist/cloud-setup-cli.mjs +3 -1
- package/dist/cloud-setup-cli.mjs.map +1 -1
- package/dist/compiler-packages.cjs +28 -0
- package/dist/compiler-packages.cjs.map +1 -0
- package/dist/compiler-packages.mjs +17 -0
- package/dist/compiler-packages.mjs.map +1 -0
- package/dist/consumer-compat-cli.cjs +1091 -0
- package/dist/consumer-compat-cli.cjs.map +1 -0
- package/dist/consumer-compat-cli.d.cts +1 -0
- package/dist/consumer-compat-cli.d.mts +1 -0
- package/dist/consumer-compat-cli.mjs +1089 -0
- package/dist/consumer-compat-cli.mjs.map +1 -0
- package/dist/index.cjs +50 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -41
- package/dist/index.d.mts +46 -41
- package/dist/index.mjs +50 -41
- package/dist/index.mjs.map +1 -1
- package/dist/publish-build-target-nuxt-helper.cjs +71 -0
- package/dist/publish-build-target-nuxt-helper.cjs.map +1 -0
- package/dist/publish-build-target-nuxt-helper.d.cts +10 -0
- package/dist/publish-build-target-nuxt-helper.d.mts +10 -0
- package/dist/publish-build-target-nuxt-helper.mjs +68 -0
- package/dist/publish-build-target-nuxt-helper.mjs.map +1 -0
- package/dist/publish-build-target-vite-dts-helper.cjs +3 -0
- package/dist/publish-build-target-vite-dts-helper.d.cts +24 -0
- package/dist/publish-build-target-vite-dts-helper.d.mts +24 -0
- package/dist/publish-build-target-vite-dts-helper.mjs +2 -0
- package/dist/publish-build-target-vite-dts-helper2.cjs +372 -0
- package/dist/publish-build-target-vite-dts-helper2.cjs.map +1 -0
- package/dist/publish-build-target-vite-dts-helper2.mjs +359 -0
- package/dist/publish-build-target-vite-dts-helper2.mjs.map +1 -0
- package/dist/publish-contract-cli.cjs +983 -0
- package/dist/publish-contract-cli.cjs.map +1 -0
- package/dist/publish-contract-cli.d.cts +1 -0
- package/dist/publish-contract-cli.d.mts +1 -0
- package/dist/publish-contract-cli.mjs +981 -0
- package/dist/publish-contract-cli.mjs.map +1 -0
- package/dist/publish-contract.cjs +431 -0
- package/dist/publish-contract.cjs.map +1 -0
- package/dist/publish-contract.mjs +396 -0
- package/dist/publish-contract.mjs.map +1 -0
- package/dist/rolldown-runtime.cjs +28 -0
- package/dist/toolchain-changed-cli.cjs +34 -0
- package/dist/toolchain-changed-cli.cjs.map +1 -0
- package/dist/toolchain-changed-cli.d.cts +1 -0
- package/dist/toolchain-changed-cli.d.mts +1 -0
- package/dist/toolchain-changed-cli.mjs +34 -0
- package/dist/toolchain-changed-cli.mjs.map +1 -0
- package/dist/toolchain-changed.cjs +1470 -0
- package/dist/toolchain-changed.cjs.map +1 -0
- package/dist/toolchain-changed.mjs +1463 -0
- package/dist/toolchain-changed.mjs.map +1 -0
- package/dist/toolchain-check-cli.cjs +2 -1
- package/dist/toolchain-check-cli.cjs.map +1 -1
- package/dist/toolchain-check-cli.mjs +2 -1
- package/dist/toolchain-check-cli.mjs.map +1 -1
- package/dist/toolchain-guard.cjs +360 -1161
- package/dist/toolchain-guard.cjs.map +1 -1
- package/dist/toolchain-guard.mjs +338 -1100
- package/dist/toolchain-guard.mjs.map +1 -1
- package/dist/toolchain-inputs.cjs +926 -0
- package/dist/toolchain-inputs.cjs.map +1 -0
- package/dist/toolchain-inputs.mjs +816 -0
- package/dist/toolchain-inputs.mjs.map +1 -0
- package/dist/toolchain-schema.cjs +702 -0
- package/dist/toolchain-schema.cjs.map +1 -0
- package/dist/toolchain-schema.mjs +580 -0
- package/dist/toolchain-schema.mjs.map +1 -0
- package/dist/typecheck-parity-cli.cjs +38 -898
- package/dist/typecheck-parity-cli.cjs.map +1 -1
- package/dist/typecheck-parity-cli.mjs +39 -899
- package/dist/typecheck-parity-cli.mjs.map +1 -1
- package/dist/typecheck-parity.cjs +905 -0
- package/dist/typecheck-parity.cjs.map +1 -0
- package/dist/typecheck-parity.mjs +894 -0
- package/dist/typecheck-parity.mjs.map +1 -0
- package/dist/typecheck-probe-cli.cjs +176 -0
- package/dist/typecheck-probe-cli.cjs.map +1 -0
- package/dist/typecheck-probe-cli.d.cts +1 -0
- package/dist/typecheck-probe-cli.d.mts +1 -0
- package/dist/typecheck-probe-cli.mjs +174 -0
- package/dist/typecheck-probe-cli.mjs.map +1 -0
- package/package.json +43 -5
- package/toolchain.json +9 -1
|
@@ -0,0 +1,926 @@
|
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime.cjs");
|
|
2
|
+
let node_path = require("node:path");
|
|
3
|
+
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
4
|
+
let semver = require("semver");
|
|
5
|
+
let picomatch = require("picomatch");
|
|
6
|
+
picomatch = require_rolldown_runtime.__toESM(picomatch, 1);
|
|
7
|
+
//#region src/cloud-setup-install.ts
|
|
8
|
+
const packageInstall = String.raw`
|
|
9
|
+
# Prevent package maintainer scripts from starting unmanaged system services.
|
|
10
|
+
apt_install() (
|
|
11
|
+
# EXIT runs after function locals expire; this subshell owns the trap state.
|
|
12
|
+
backup=''
|
|
13
|
+
policy_state=untouched
|
|
14
|
+
[[ ! -L /var/lock/stll-cloud-apt.lock ]] || fail 'Invalid package install lock'
|
|
15
|
+
exec 8>/var/lock/stll-cloud-apt.lock
|
|
16
|
+
flock -x -w 120 8 || fail 'Another package installation is active'
|
|
17
|
+
[[ ! -L /usr/sbin/policy-rc.d ]] || fail 'Unsupported service-start policy symlink'
|
|
18
|
+
[[ ! -e /usr/sbin/policy-rc.d || -f /usr/sbin/policy-rc.d ]] || fail 'Unsupported service-start policy file'
|
|
19
|
+
backup="$(mktemp -d)"
|
|
20
|
+
cleanup_package_policy() {
|
|
21
|
+
local status=$?
|
|
22
|
+
trap - EXIT HUP INT TERM
|
|
23
|
+
if [[ "$policy_state" == installed ]]; then
|
|
24
|
+
if [[ -f "$backup/original" ]]; then
|
|
25
|
+
if ! cp -p "$backup/original" /usr/sbin/policy-rc.d; then status=1; fi
|
|
26
|
+
else
|
|
27
|
+
if ! rm -f /usr/sbin/policy-rc.d; then status=1; fi
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
rm -rf -- "$backup"
|
|
31
|
+
exit "$status"
|
|
32
|
+
}
|
|
33
|
+
trap cleanup_package_policy EXIT
|
|
34
|
+
trap 'exit 129' HUP
|
|
35
|
+
trap 'exit 130' INT
|
|
36
|
+
trap 'exit 143' TERM
|
|
37
|
+
if [[ -e /usr/sbin/policy-rc.d ]]; then cp -p /usr/sbin/policy-rc.d "$backup/original"; fi
|
|
38
|
+
policy_state=installed
|
|
39
|
+
printf '%s\n' '#!/bin/sh' 'exit 101' > /usr/sbin/policy-rc.d
|
|
40
|
+
chmod 755 /usr/sbin/policy-rc.d
|
|
41
|
+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$@"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
`;
|
|
45
|
+
const postgresSourceLine = "deb [signed-by=/usr/share/keyrings/stll-pgdg.asc] https://apt.postgresql.org/pub/repos/apt noble-pgdg main";
|
|
46
|
+
const postgresSigningFingerprint = "B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8";
|
|
47
|
+
const postgresSources = String.raw`
|
|
48
|
+
pgdg_key_fingerprint() {
|
|
49
|
+
gpg --batch --show-keys --with-colons "$1" | awk -F: '$1 == "pub" { primary = 1 } $1 == "sub" { primary = 0 } $1 == "fpr" && primary { print toupper($10); primary = 0 }'
|
|
50
|
+
}
|
|
51
|
+
pgdg_sources_ready() {
|
|
52
|
+
local managed="$1" file mode records suite keyring override fingerprint selected_keyring=''
|
|
53
|
+
shift
|
|
54
|
+
PGDG_SOURCE_STATE=missing
|
|
55
|
+
[[ ! -L "$managed" ]] || fail "Managed PGDG source must not be a symlink: $managed"
|
|
56
|
+
for file in "$@"; do
|
|
57
|
+
[[ -e "$file" ]] || continue
|
|
58
|
+
[[ -f "$file" ]] || fail "APT source must be a regular file: $file"
|
|
59
|
+
case "$file" in *.sources) mode=sources ;; *) mode=list ;; esac
|
|
60
|
+
records="$(awk -v mode="$mode" '
|
|
61
|
+
function trim(value) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); return value }
|
|
62
|
+
function pgdg_uri(uri) {
|
|
63
|
+
uri = tolower(uri)
|
|
64
|
+
if (uri !~ /^[a-z][a-z0-9+.-]*:\/\//) return 0
|
|
65
|
+
sub(/^[a-z][a-z0-9+.-]*:\/\//, "", uri)
|
|
66
|
+
sub(/[\/?#].*$/, "", uri)
|
|
67
|
+
sub(/^.*@/, "", uri)
|
|
68
|
+
sub(/:.*$/, "", uri)
|
|
69
|
+
return uri == "apt.postgresql.org" || uri == "apt.postgresql.org."
|
|
70
|
+
}
|
|
71
|
+
function contains_pgdg(value, tokens, count, token_index) {
|
|
72
|
+
count = split(value, tokens, /[[:space:]]+/)
|
|
73
|
+
for (token_index = 1; token_index <= count; token_index++) if (pgdg_uri(tokens[token_index])) return 1
|
|
74
|
+
return 0
|
|
75
|
+
}
|
|
76
|
+
function unsafe_field(name) {
|
|
77
|
+
return name ~ /^(trusted|allow-insecure|allow-weak|allow-downgrade-to-insecure)$/
|
|
78
|
+
}
|
|
79
|
+
function disabled_override(value) {
|
|
80
|
+
value = tolower(trim(value))
|
|
81
|
+
gsub(/^["\047]|["\047]$/, "", value)
|
|
82
|
+
return value ~ /^(no|false|without|off|disable|0)$/
|
|
83
|
+
}
|
|
84
|
+
function stanza(override_index) {
|
|
85
|
+
override = 0
|
|
86
|
+
for (override_index = 1; override_index <= override_count; override_index++) {
|
|
87
|
+
if (!disabled_override(override_values[override_index])) override = 1
|
|
88
|
+
delete override_values[override_index]
|
|
89
|
+
}
|
|
90
|
+
if (tolower(trim(enabled)) != "no" && pgdg) print (trim(suites) == "" ? "-" : trim(suites)) "\t" (trim(keyring) == "" ? "-" : trim(keyring)) "\t" (override ? "unsafe" : "safe")
|
|
91
|
+
enabled = ""; pgdg = 0; field = ""; suites = ""; keyring = ""; override = 0; override_count = 0
|
|
92
|
+
}
|
|
93
|
+
/^[[:space:]]*#/ { if (mode == "list" || $0 ~ /^#/) next }
|
|
94
|
+
{
|
|
95
|
+
line = $0
|
|
96
|
+
if (mode == "list") {
|
|
97
|
+
sub(/[[:space:]]*#.*$/, "", line)
|
|
98
|
+
if (line !~ /^[[:space:]]*deb(-src)?[[:space:]]/ || !contains_pgdg(line)) next
|
|
99
|
+
keyring = ""; override = 0
|
|
100
|
+
if (match(line, /\[[^]]*\]/)) {
|
|
101
|
+
options = substr(line, RSTART + 1, RLENGTH - 2)
|
|
102
|
+
count = split(options, tokens, /[[:space:]]+/)
|
|
103
|
+
for (token_index = 1; token_index <= count; token_index++) {
|
|
104
|
+
if (tolower(tokens[token_index]) ~ /^signed-by=/) { keyring = tokens[token_index]; sub(/^[^=]*=/, "", keyring) }
|
|
105
|
+
option_name = tolower(tokens[token_index]); sub(/=.*/, "", option_name)
|
|
106
|
+
option_value = tokens[token_index]; sub(/^[^=]*=/, "", option_value)
|
|
107
|
+
if (unsafe_field(option_name) && !disabled_override(option_value)) override = 1
|
|
108
|
+
}
|
|
109
|
+
sub(/\[[^]]*\]/, "", line)
|
|
110
|
+
}
|
|
111
|
+
count = split(line, tokens, /[[:space:]]+/)
|
|
112
|
+
for (token_index = 1; token_index <= count; token_index++) if (pgdg_uri(tokens[token_index])) print (tokens[token_index + 1] == "" ? "-" : tokens[token_index + 1]) "\t" (keyring == "" ? "-" : keyring) "\t" (override ? "unsafe" : "safe")
|
|
113
|
+
next
|
|
114
|
+
}
|
|
115
|
+
if (line ~ /^[[:space:]]*$/) { stanza(); next }
|
|
116
|
+
if (line ~ /^[[:space:]]/) {
|
|
117
|
+
if (field == "uris" && contains_pgdg(line)) pgdg = 1
|
|
118
|
+
if (field == "enabled") enabled = enabled " " line
|
|
119
|
+
if (field == "suites") suites = suites " " line
|
|
120
|
+
if (field == "signed-by") keyring = keyring " " line
|
|
121
|
+
if (unsafe_field(field)) override_values[override_count] = override_values[override_count] " " line
|
|
122
|
+
next
|
|
123
|
+
}
|
|
124
|
+
field = tolower(line)
|
|
125
|
+
sub(/:.*/, "", field)
|
|
126
|
+
value = line
|
|
127
|
+
sub(/^[^:]*:[[:space:]]*/, "", value)
|
|
128
|
+
if (field == "uris" && contains_pgdg(value)) pgdg = 1
|
|
129
|
+
if (field == "enabled") enabled = value
|
|
130
|
+
if (field == "suites") suites = value
|
|
131
|
+
if (field == "signed-by") keyring = value
|
|
132
|
+
if (unsafe_field(field)) override_values[++override_count] = value
|
|
133
|
+
}
|
|
134
|
+
END { if (mode == "sources") stanza() }
|
|
135
|
+
' "$file")" || fail "Cannot inspect APT source: $file"
|
|
136
|
+
if [[ "$file" == "$managed" && -z "$records" ]]; then
|
|
137
|
+
fail "Managed PGDG source has no active PGDG entry: $file"
|
|
138
|
+
fi
|
|
139
|
+
while IFS=$'\t' read -r suite keyring override; do
|
|
140
|
+
[[ -n "$suite" ]] || continue
|
|
141
|
+
[[ "$override" == safe ]] || fail "PGDG source enables an unsafe trust override: $file"
|
|
142
|
+
[[ "$suite" == noble-pgdg && "$keyring" == /* && ! "$keyring" =~ [[:space:],] ]] || fail "PGDG source requires noble-pgdg and one Signed-By keyring: $file"
|
|
143
|
+
[[ -z "$selected_keyring" || "$selected_keyring" == "$keyring" ]] || fail "Conflicting PGDG Signed-By keyrings: $file"
|
|
144
|
+
fingerprint="$(pgdg_key_fingerprint "$keyring")" || fail "Cannot verify PGDG Signed-By keyring: $file"
|
|
145
|
+
[[ "$fingerprint" == @PGDG_FINGERPRINT@ ]] || fail "PGDG Signed-By fingerprint mismatch: $file"
|
|
146
|
+
selected_keyring="$keyring"
|
|
147
|
+
PGDG_SOURCE_STATE=reuse
|
|
148
|
+
done <<< "$records"
|
|
149
|
+
done
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
const postgresInstall = String.raw`
|
|
153
|
+
postgres_ready() {
|
|
154
|
+
local binary
|
|
155
|
+
for binary in postgres initdb pg_ctl psql createdb; do
|
|
156
|
+
[[ -x "/usr/lib/postgresql/@POSTGRES@/bin/$binary" ]] || return 1
|
|
157
|
+
done
|
|
158
|
+
/usr/lib/postgresql/@POSTGRES@/bin/postgres --version | grep -Eq '^postgres \(PostgreSQL\) @POSTGRES@\.'
|
|
159
|
+
}
|
|
160
|
+
if ! postgres_ready; then
|
|
161
|
+
pgdg_sources_ready /etc/apt/sources.list.d/stll-pgdg.list /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources
|
|
162
|
+
if [[ "$PGDG_SOURCE_STATE" == missing ]]; then
|
|
163
|
+
local key key_fingerprint
|
|
164
|
+
key="$(mktemp)"
|
|
165
|
+
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 https://www.postgresql.org/media/keys/ACCC4CF8.asc -o "$key"
|
|
166
|
+
key_fingerprint="$(pgdg_key_fingerprint "$key")"
|
|
167
|
+
[[ "$key_fingerprint" == @PGDG_FINGERPRINT@ ]] || fail 'PostgreSQL signing key mismatch'
|
|
168
|
+
install -d -m 755 /usr/share/keyrings
|
|
169
|
+
install -m 644 "$key" /usr/share/keyrings/stll-pgdg.asc
|
|
170
|
+
rm -- "$key"
|
|
171
|
+
printf '%s\n' '@PGDG_SOURCE@' > /etc/apt/sources.list.d/stll-pgdg.list
|
|
172
|
+
fi
|
|
173
|
+
apt-get update
|
|
174
|
+
apt_install postgresql-@POSTGRES@ postgresql-client-@POSTGRES@
|
|
175
|
+
fi
|
|
176
|
+
postgres_ready || fail 'PostgreSQL toolchain is incomplete or differs from the policy major'
|
|
177
|
+
`;
|
|
178
|
+
const valkeyInstall = String.raw`
|
|
179
|
+
if ! command -v valkey-server >/dev/null || [[ "$(valkey-server --version | sed -nE 's/.*v=([0-9.]+).*/\1/p')" != '@VALKEY@' ]] || ! command -v valkey-cli >/dev/null || [[ "$(valkey-cli --version)" != 'valkey-cli @VALKEY@' ]]; then
|
|
180
|
+
local scratch server_url server_sha tools_url tools_sha package
|
|
181
|
+
scratch="$(mktemp -d)"
|
|
182
|
+
chmod 755 "$scratch"
|
|
183
|
+
case "$ARCH" in
|
|
184
|
+
amd64) server_url='@AMD64_SERVER_URL@'; server_sha='@AMD64_SERVER_SHA@'; tools_url='@AMD64_TOOLS_URL@'; tools_sha='@AMD64_TOOLS_SHA@' ;;
|
|
185
|
+
arm64) server_url='@ARM64_SERVER_URL@'; server_sha='@ARM64_SERVER_SHA@'; tools_url='@ARM64_TOOLS_URL@'; tools_sha='@ARM64_TOOLS_SHA@' ;;
|
|
186
|
+
*) fail 'Unsupported Valkey architecture' ;;
|
|
187
|
+
esac
|
|
188
|
+
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "$server_url" -o "$scratch/valkey-server.deb"
|
|
189
|
+
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "$tools_url" -o "$scratch/valkey-tools.deb"
|
|
190
|
+
(cd "$scratch"; printf '%s %s\n' "$server_sha" valkey-server.deb "$tools_sha" valkey-tools.deb | sha256sum --strict -c -)
|
|
191
|
+
for package in valkey-server valkey-tools; do
|
|
192
|
+
[[ "$(dpkg-deb -f "$scratch/$package.deb" Package)" == "$package" ]] || fail 'Valkey artifact package mismatch'
|
|
193
|
+
[[ "$(dpkg-deb -f "$scratch/$package.deb" Version)" == '@VALKEY@-1.noble' ]] || fail 'Valkey artifact version mismatch'
|
|
194
|
+
[[ "$(dpkg-deb -f "$scratch/$package.deb" Architecture)" == "$ARCH" ]] || fail 'Valkey artifact architecture mismatch'
|
|
195
|
+
done
|
|
196
|
+
apt-get update
|
|
197
|
+
apt_install "$scratch/valkey-server.deb" "$scratch/valkey-tools.deb"
|
|
198
|
+
rm -rf -- "$scratch"
|
|
199
|
+
fi
|
|
200
|
+
[[ "$(valkey-server --version | sed -nE 's/.*v=([0-9.]+).*/\1/p')" == '@VALKEY@' ]] || fail 'Valkey version differs from policy'
|
|
201
|
+
[[ "$(valkey-cli --version)" == 'valkey-cli @VALKEY@' ]] || fail 'Valkey client version differs from policy'
|
|
202
|
+
`;
|
|
203
|
+
const state = String.raw`
|
|
204
|
+
service_account_ready() {
|
|
205
|
+
local uid gid shell
|
|
206
|
+
uid="$(id -u stll-cloud)" || fail 'Service user is unavailable; run install'
|
|
207
|
+
gid="$(id -g stll-cloud)"
|
|
208
|
+
shell="$(getent passwd stll-cloud | cut -d: -f7)"
|
|
209
|
+
[[ "$uid" =~ ^[1-9][0-9]*$ && "$gid" =~ ^[1-9][0-9]*$ && "$shell" == /usr/sbin/nologin ]] || fail 'Service user must be unprivileged with no login shell'
|
|
210
|
+
}
|
|
211
|
+
prepare_state() {
|
|
212
|
+
service_account_ready
|
|
213
|
+
[[ ! -L /var/lib/stll-cloud ]] || fail 'Service state root must not be a symlink'
|
|
214
|
+
if [[ -e /var/lib/stll-cloud ]]; then
|
|
215
|
+
[[ -d /var/lib/stll-cloud && "$(stat -c '%u:%a' /var/lib/stll-cloud)" == 0:755 ]] || fail 'Invalid service state root'
|
|
216
|
+
else
|
|
217
|
+
install -d -m 755 -o root -g root /var/lib/stll-cloud
|
|
218
|
+
fi
|
|
219
|
+
STATE="/var/lib/stll-cloud/$(printf '%s' "$REPO_ROOT" | sha256sum | cut -d' ' -f1)"
|
|
220
|
+
[[ ! -L "$STATE" ]] || fail 'Service state must not be a symlink'
|
|
221
|
+
if [[ -e "$STATE" ]]; then
|
|
222
|
+
[[ -d "$STATE" && "$(stat -c '%u:%a' "$STATE")" == 0:710 && "$(stat -c '%g' "$STATE")" == "$(id -g stll-cloud)" ]] || fail 'Invalid service state ownership'
|
|
223
|
+
else
|
|
224
|
+
install -d -m 710 -o root -g stll-cloud "$STATE"
|
|
225
|
+
fi
|
|
226
|
+
[[ ! -L "$STATE/lock" ]] || fail 'Service lock must not be a symlink'
|
|
227
|
+
exec 9>"$STATE/lock"
|
|
228
|
+
flock -x -w 10 9 || fail 'Another setup process owns the service state'
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
const renderCloudServiceInstall = ({ services, postgres, valkey, valkeyArtifacts }) => {
|
|
232
|
+
const parts = services.map((service) => {
|
|
233
|
+
switch (service) {
|
|
234
|
+
case "postgres": return postgresInstall.replaceAll("@POSTGRES@", postgres).replaceAll("@PGDG_SOURCE@", postgresSourceLine).replaceAll("@PGDG_FINGERPRINT@", postgresSigningFingerprint);
|
|
235
|
+
case "valkey": {
|
|
236
|
+
const replacements = /* @__PURE__ */ new Map([
|
|
237
|
+
["@VALKEY@", valkey],
|
|
238
|
+
["@AMD64_SERVER_URL@", valkeyArtifacts.amd64.server.url],
|
|
239
|
+
["@AMD64_SERVER_SHA@", valkeyArtifacts.amd64.server.sha256],
|
|
240
|
+
["@AMD64_TOOLS_URL@", valkeyArtifacts.amd64.tools.url],
|
|
241
|
+
["@AMD64_TOOLS_SHA@", valkeyArtifacts.amd64.tools.sha256],
|
|
242
|
+
["@ARM64_SERVER_URL@", valkeyArtifacts.arm64.server.url],
|
|
243
|
+
["@ARM64_SERVER_SHA@", valkeyArtifacts.arm64.server.sha256],
|
|
244
|
+
["@ARM64_TOOLS_URL@", valkeyArtifacts.arm64.tools.url],
|
|
245
|
+
["@ARM64_TOOLS_SHA@", valkeyArtifacts.arm64.tools.sha256]
|
|
246
|
+
]);
|
|
247
|
+
return valkeyInstall.replace(/@[A-Z0-9_]+@/g, (token) => {
|
|
248
|
+
const value = replacements.get(token);
|
|
249
|
+
if (value === void 0) throw new Error(`Missing Valkey artifact field: ${token}`);
|
|
250
|
+
return value;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
default: throw new Error(`Unsupported service: ${String(service)}`);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
const user = services.length === 0 ? "" : " if ! id stll-cloud >/dev/null 2>&1; then useradd --system --user-group --no-create-home --shell /usr/sbin/nologin stll-cloud; fi\n service_account_ready\n";
|
|
257
|
+
return `${packageInstall}\n${services.includes("postgres") ? postgresSources.replaceAll("@PGDG_FINGERPRINT@", postgresSigningFingerprint) : ""}\n${services.length === 0 ? "prepare_state() { :; }\n" : state}\ninstall_services() {\n${user}${parts.join("")}\n :\n}\n`;
|
|
258
|
+
};
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/cloud-setup-schema.ts
|
|
261
|
+
const cloudServices = ["postgres", "valkey"];
|
|
262
|
+
const cloudInstallCommand = "bun install --frozen-lockfile";
|
|
263
|
+
const cloudService = (value) => cloudServices.some((service) => value === service);
|
|
264
|
+
/** Parse an explicit declaration without assuming services for undeclared repositories. */
|
|
265
|
+
const parseCloudSetup = (input) => {
|
|
266
|
+
if (input === void 0) return void 0;
|
|
267
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new Error("cloud must be an object");
|
|
268
|
+
for (const key of Object.keys(input)) if (key !== "services" && key !== "install" && key !== "envFile") throw new Error(`unsupported cloud key: ${key}`);
|
|
269
|
+
const selected = "services" in input ? input.services : [];
|
|
270
|
+
if (!Array.isArray(selected)) throw new Error("cloud.services must be an array when provided");
|
|
271
|
+
if (!("install" in input) || input.install !== "bun install --frozen-lockfile") throw new Error(`cloud.install must be exactly ${cloudInstallCommand}`);
|
|
272
|
+
const seen = /* @__PURE__ */ new Set();
|
|
273
|
+
const services = Array.from(selected, (service) => {
|
|
274
|
+
if (!cloudService(service)) throw new Error("cloud.services contains an unsupported service");
|
|
275
|
+
if (seen.has(service)) throw new Error(`cloud.services contains duplicate service: ${service}`);
|
|
276
|
+
seen.add(service);
|
|
277
|
+
return service;
|
|
278
|
+
});
|
|
279
|
+
if (services.length === 0) {
|
|
280
|
+
if ("envFile" in input) throw new Error("cloud.envFile is not allowed without services");
|
|
281
|
+
return {
|
|
282
|
+
services,
|
|
283
|
+
install: cloudInstallCommand
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (!("envFile" in input) || typeof input.envFile !== "string") throw new Error("cloud.envFile must be a canonical repository-relative path");
|
|
287
|
+
const envFile = input.envFile;
|
|
288
|
+
if (envFile.startsWith("/") || /^[A-Za-z]:/.test(envFile) || envFile.includes("\\") || /\p{Cc}/u.test(envFile) || envFile.split("/").some((part) => part === "" || part === "." || part === "..")) throw new Error("cloud.envFile must be a canonical repository-relative path");
|
|
289
|
+
return {
|
|
290
|
+
services,
|
|
291
|
+
install: cloudInstallCommand,
|
|
292
|
+
envFile
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
//#endregion
|
|
296
|
+
//#region src/cloud-setup-services.ts
|
|
297
|
+
const common = String.raw`
|
|
298
|
+
service_file() {
|
|
299
|
+
local file="$1" owner="$2"
|
|
300
|
+
[[ ! -L "$file" ]] || fail "Service state must not contain symbolic links"
|
|
301
|
+
if [[ -e "$file" ]]; then
|
|
302
|
+
[[ -f "$file" && "$(stat -c '%u:%a' "$file")" == "$owner:600" ]] || fail "Invalid service state file"
|
|
303
|
+
fi
|
|
304
|
+
}
|
|
305
|
+
service_directory() {
|
|
306
|
+
local directory="$1"
|
|
307
|
+
[[ ! -L "$directory" ]] || fail "Service directory must not be a symbolic link"
|
|
308
|
+
if [[ -e "$directory" ]]; then
|
|
309
|
+
[[ -d "$directory" && "$(stat -c '%u:%a' "$directory")" == "$SERVICE_UID:700" ]] || fail "Invalid service directory"
|
|
310
|
+
else
|
|
311
|
+
root install -d -m 700 -o stll-cloud -g stll-cloud "$directory"
|
|
312
|
+
fi
|
|
313
|
+
}
|
|
314
|
+
service_password() {
|
|
315
|
+
local file="$1"
|
|
316
|
+
service_file "$file" 0
|
|
317
|
+
if [[ ! -e "$file" ]]; then
|
|
318
|
+
(umask 077; openssl rand -hex 32 > "$file")
|
|
319
|
+
root chown root:root "$file"
|
|
320
|
+
fi
|
|
321
|
+
[[ "$(cat "$file")" =~ ^[0-9a-f]{64}$ ]] || fail "Invalid service credential state"
|
|
322
|
+
}
|
|
323
|
+
service_process_matches() {
|
|
324
|
+
local pid="$1" executable="$2" owner actual expected
|
|
325
|
+
[[ "$pid" =~ ^[1-9][0-9]*$ && -d "/proc/$pid" ]] || return 1
|
|
326
|
+
owner="$(stat -c '%u' "/proc/$pid" 2>/dev/null)" || return 2
|
|
327
|
+
[[ "$owner" =~ ^[0-9]+$ ]] || return 2
|
|
328
|
+
[[ "$owner" == "$SERVICE_UID" ]] || return 1
|
|
329
|
+
actual="$(readlink -f "/proc/$pid/exe" 2>/dev/null)" || return 2
|
|
330
|
+
expected="$(readlink -f "$executable")" || return 2
|
|
331
|
+
[[ -n "$actual" && -n "$expected" && "$actual" != *' (deleted)' ]] || return 2
|
|
332
|
+
[[ "$actual" == "$expected" ]]
|
|
333
|
+
}
|
|
334
|
+
service_process() {
|
|
335
|
+
service_process_matches "$1" "$2" || fail "Managed service process identity differs from the recorded state"
|
|
336
|
+
}
|
|
337
|
+
service_prune_pid() {
|
|
338
|
+
local file="$1" executable="$2" service="$3" pid identity
|
|
339
|
+
[[ -e "$file" ]] || return 0
|
|
340
|
+
pid="$(head -n 1 "$file")"
|
|
341
|
+
# PostgreSQL encodes a standalone backend with a negative PID.
|
|
342
|
+
if [[ "$service" == postgres ]]; then pid="$(printf '%s' "$pid" | sed 's/^-//')"; fi
|
|
343
|
+
if service_process_matches "$pid" "$executable"; then return 0; else identity=$?; fi
|
|
344
|
+
[[ "$identity" != 2 ]] || fail "Cannot verify recorded live service process; retaining its PID file"
|
|
345
|
+
# Let PostgreSQL check orphan shared memory before removing a dead-PID lock.
|
|
346
|
+
if [[ "$service" == postgres && "$pid" =~ ^[1-9][0-9]*$ && ! -d "/proc/$pid" ]]; then return 0; fi
|
|
347
|
+
if [[ "$service" == postgres ]]; then
|
|
348
|
+
[[ "$pid" =~ ^[1-9][0-9]*$ && "$SERVICE_UID" != 0 ]] || fail "Cannot safely recover PostgreSQL PID state"
|
|
349
|
+
# The root controller is not our nonroot postgres. Preserve the remaining
|
|
350
|
+
# lock rows so PostgreSQL's EPERM/ancestor path still checks orphan memory.
|
|
351
|
+
root sed -i "1s/.*/$$/" "$file"
|
|
352
|
+
return 0
|
|
353
|
+
fi
|
|
354
|
+
# Restored state can name an unrelated live process; never signal that PID.
|
|
355
|
+
root rm -- "$file"
|
|
356
|
+
}
|
|
357
|
+
`;
|
|
358
|
+
const postgresStart = String.raw`
|
|
359
|
+
local pg_bin='/usr/lib/postgresql/@POSTGRES@/bin' pg_data="$STATE/postgres" pg_password pg_details pg_pid pg_version
|
|
360
|
+
service_password "$STATE/postgres.password"
|
|
361
|
+
pg_password="$(cat "$STATE/postgres.password")"
|
|
362
|
+
service_directory "$pg_data"
|
|
363
|
+
for file in PG_VERSION postmaster.pid postgresql.conf pg_hba.conf pg_ident.conf postgresql.auto.conf; do
|
|
364
|
+
service_file "$pg_data/$file" "$SERVICE_UID"
|
|
365
|
+
done
|
|
366
|
+
if [[ ! -e "$pg_data/PG_VERSION" ]]; then
|
|
367
|
+
[[ -z "$(ls -A "$pg_data")" ]] || fail "PostgreSQL data directory is not initialized"
|
|
368
|
+
service_file "$STATE/postgres.init-password" "$SERVICE_UID"
|
|
369
|
+
(umask 077; printf '%s\n' "$pg_password" > "$STATE/postgres.init-password")
|
|
370
|
+
root chown stll-cloud:stll-cloud "$STATE/postgres.init-password"
|
|
371
|
+
service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/initdb" -D "$pg_data" --username=stll_cloud --auth-host=scram-sha-256 --auth-local=scram-sha-256 --pwfile="$STATE/postgres.init-password" --encoding=UTF8 --locale=C --set=unix_socket_directories= >/dev/null || fail "PostgreSQL initialization failed"
|
|
372
|
+
root rm "$STATE/postgres.init-password"
|
|
373
|
+
fi
|
|
374
|
+
[[ -f "$pg_data/PG_VERSION" && "$(cat "$pg_data/PG_VERSION")" == '@POSTGRES@' ]] || fail "PostgreSQL data has a different major version"
|
|
375
|
+
service_prune_pid "$pg_data/postmaster.pid" "$pg_bin/postgres" postgres
|
|
376
|
+
if ! service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/pg_ctl" -D "$pg_data" status >/dev/null 2>&1; then
|
|
377
|
+
service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/pg_ctl" -D "$pg_data" -l "$pg_data/cloud.log" -o "-h 127.0.0.1 -p 55432 -c unix_socket_directories=" -t 10 -w start >/dev/null || fail "PostgreSQL start failed; port 55432 must be available"
|
|
378
|
+
fi
|
|
379
|
+
pg_details="$(env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/psql" -X -h 127.0.0.1 -p 55432 -U stll_cloud -d postgres -At -v ON_ERROR_STOP=1 -c "SELECT current_setting('data_directory'), current_setting('listen_addresses'), current_setting('port'), current_setting('server_version_num'), current_setting('unix_socket_directories')")" || fail "PostgreSQL authenticated readiness failed"
|
|
380
|
+
pg_version="$(printf '%s\n' "$pg_details" | cut -d '|' -f4)"
|
|
381
|
+
[[ "$pg_details" == "$pg_data|127.0.0.1|55432|$pg_version|" && "$pg_version" =~ ^[0-9]{6}$ ]] || fail "PostgreSQL endpoint does not match managed data and listener"
|
|
382
|
+
(( pg_version / 10000 == @POSTGRES@ )) || fail "PostgreSQL endpoint has a different major version"
|
|
383
|
+
pg_pid="$(head -n 1 "$pg_data/postmaster.pid")"
|
|
384
|
+
service_process "$pg_pid" "$pg_bin/postgres"
|
|
385
|
+
[[ "$(env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/psql" -X -h 127.0.0.1 -p 55432 -U stll_cloud -d postgres -At -v ON_ERROR_STOP=1 -c "SELECT count(*) FROM pg_database WHERE datname = 'cloud_test'")" == 1 ]] || env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/createdb" -h 127.0.0.1 -p 55432 -U stll_cloud cloud_test || fail "PostgreSQL test database creation failed"
|
|
386
|
+
DATABASE_URL="postgresql://stll_cloud:$pg_password@127.0.0.1:55432/cloud_test"
|
|
387
|
+
`;
|
|
388
|
+
const valkeyStart = String.raw`
|
|
389
|
+
local vk_data="$STATE/valkey" vk_password vk_config vk_info vk_pid vk_expected ready=0
|
|
390
|
+
service_password "$STATE/valkey.password"
|
|
391
|
+
vk_password="$(cat "$STATE/valkey.password")"
|
|
392
|
+
service_directory "$vk_data"
|
|
393
|
+
vk_config="$vk_data/valkey.conf"
|
|
394
|
+
service_file "$vk_config" "$SERVICE_UID"
|
|
395
|
+
for file in valkey.pid valkey.log dump.rdb; do
|
|
396
|
+
[[ ! -L "$vk_data/$file" ]] || fail "Valkey state must not contain symbolic links"
|
|
397
|
+
if [[ -e "$vk_data/$file" ]]; then
|
|
398
|
+
[[ -f "$vk_data/$file" && "$(stat -c '%u' "$vk_data/$file")" == "$SERVICE_UID" ]] || fail "Invalid Valkey state file"
|
|
399
|
+
fi
|
|
400
|
+
done
|
|
401
|
+
vk_expected="$(printf '%s\n' 'bind 127.0.0.1' 'port 56379' 'protected-mode yes' 'daemonize yes' "requirepass $vk_password" "dir $vk_data" "pidfile $vk_data/valkey.pid" "logfile $vk_data/valkey.log" 'save ""' 'appendonly no')"
|
|
402
|
+
if [[ ! -e "$vk_config" ]]; then
|
|
403
|
+
(umask 077; printf '%s\n' "$vk_expected" > "$vk_config")
|
|
404
|
+
root chown stll-cloud:stll-cloud "$vk_config"
|
|
405
|
+
fi
|
|
406
|
+
[[ "$(cat "$vk_config")" == "$vk_expected" ]] || fail "Valkey configuration differs from managed settings"
|
|
407
|
+
service_prune_pid "$vk_data/valkey.pid" "$(command -v valkey-server)" valkey
|
|
408
|
+
if [[ ! -e "$vk_data/valkey.pid" ]]; then
|
|
409
|
+
service_run valkey-server "$vk_config" || fail "Valkey start failed; port 56379 must be available"
|
|
410
|
+
fi
|
|
411
|
+
for ((attempt=0; attempt<20; attempt++)); do
|
|
412
|
+
if vk_info="$(VALKEYCLI_AUTH="$vk_password" timeout 0.2 valkey-cli -h 127.0.0.1 -p 56379 --raw INFO server 2>/dev/null)"; then
|
|
413
|
+
if [[ "$vk_info" == *$'valkey_version:@VALKEY@\r\n'* && "$vk_info" == *"config_file:$vk_config"$'\r\n'* ]]; then ready=1; break; fi
|
|
414
|
+
fi
|
|
415
|
+
sleep 0.25
|
|
416
|
+
done
|
|
417
|
+
[[ "$ready" == 1 ]] || fail "Valkey authenticated readiness or managed version/configuration check failed"
|
|
418
|
+
[[ -f "$vk_data/valkey.pid" ]] || fail "Valkey managed process identifier is unavailable"
|
|
419
|
+
vk_pid="$(cat "$vk_data/valkey.pid")"
|
|
420
|
+
service_process "$vk_pid" "$(command -v valkey-server)"
|
|
421
|
+
[[ "$vk_info" == *"process_id:$vk_pid"$'\r\n'* ]] || fail "Valkey endpoint has a different process identifier"
|
|
422
|
+
[[ "$(VALKEYCLI_AUTH="$vk_password" timeout 2 valkey-cli -h 127.0.0.1 -p 56379 --raw CONFIG GET bind)" == $'bind\n127.0.0.1' ]] || fail "Valkey endpoint must bind only loopback"
|
|
423
|
+
[[ "$(VALKEYCLI_AUTH="$vk_password" timeout 2 valkey-cli -h 127.0.0.1 -p 56379 --raw CONFIG GET dir)" == "$(printf 'dir\n%s' "$vk_data")" ]] || fail "Valkey endpoint has a different data directory"
|
|
424
|
+
REDIS_URL="redis://:$vk_password@127.0.0.1:56379/0"
|
|
425
|
+
`;
|
|
426
|
+
/** Render only declared services; installation and environment-file ownership stay with the caller. */
|
|
427
|
+
const renderCloudServiceStart = ({ services, postgres, valkey }) => {
|
|
428
|
+
if (!/^[1-9][0-9]*$/.test(postgres) || !/^\d+\.\d+\.\d+$/.test(valkey)) throw new Error("Invalid cloud service runtime policy");
|
|
429
|
+
if (services.length === 0) return "start_services() { :; }\n";
|
|
430
|
+
const bodies = services.map((service) => {
|
|
431
|
+
switch (service) {
|
|
432
|
+
case "postgres": return postgresStart.replaceAll("@POSTGRES@", postgres);
|
|
433
|
+
case "valkey": return valkeyStart.replaceAll("@VALKEY@", valkey);
|
|
434
|
+
default: throw new Error(`Unsupported cloud service: ${String(service)}`);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
return `${common}\nstart_services() {\n local SERVICE_UID file attempt\n SERVICE_UID="$(id -u stll-cloud)"\n [[ -d "$STATE" && ! -L "$STATE" && "$(stat -c '%u' "$STATE")" == 0 ]] || fail "Invalid cloud service state directory"\n${bodies.join("")}\n}\n`;
|
|
438
|
+
};
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/toolchain-node.ts
|
|
441
|
+
const numericComponent = "(?:0|[1-9]\\d*)";
|
|
442
|
+
const policyPattern = new RegExp(`^${numericComponent}\\.x$`);
|
|
443
|
+
const selectorPattern = new RegExp(`^(${numericComponent})(?:\\.(?:x|${numericComponent}(?:\\.${numericComponent})?))?$`);
|
|
444
|
+
/** A Node policy selects the complete stable release series of one major. */
|
|
445
|
+
const nodePolicyValid = (value) => policyPattern.test(value) && (0, semver.validRange)(value) !== null;
|
|
446
|
+
/** Static selectors may narrow that series, but cannot float across majors. */
|
|
447
|
+
const nodeSelectorMatches = (value, policy) => {
|
|
448
|
+
if (!nodePolicyValid(policy) || typeof value !== "string") return false;
|
|
449
|
+
const major = selectorPattern.exec(value)?.[1];
|
|
450
|
+
return major !== void 0 && major === policy.slice(0, -2) && (0, semver.validRange)(value) !== null;
|
|
451
|
+
};
|
|
452
|
+
/** Engines must support every stable release in the approved major series. */
|
|
453
|
+
const nodeSupportRangeMatches = (policy, range) => {
|
|
454
|
+
if (!nodePolicyValid(policy)) return false;
|
|
455
|
+
try {
|
|
456
|
+
return (0, semver.subset)(policy, range);
|
|
457
|
+
} catch {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
//#endregion
|
|
462
|
+
//#region src/cloud-setup.ts
|
|
463
|
+
const cloudSetupPath = ".agents/cloud-setup.sh";
|
|
464
|
+
const cloudEnvironmentMarker = "# Generated by stll-cloud-setup; disposable local test services.";
|
|
465
|
+
const script = String.raw`#!/usr/bin/env bash
|
|
466
|
+
# Generated by stll-cloud-setup. Regenerate with stll-cloud-setup.
|
|
467
|
+
# Networked installation and offline service startup are separate lifecycle steps.
|
|
468
|
+
# Cached filesystems may not preserve processes; run start at every session start.
|
|
469
|
+
# Supported platform: Ubuntu 24.04 (noble), amd64/arm64, root or passwordless sudo.
|
|
470
|
+
set -euo pipefail
|
|
471
|
+
|
|
472
|
+
usage() { echo 'Usage: cloud-setup.sh install|start' >&2; exit 2; }
|
|
473
|
+
[[ "$#" == 1 ]] || usage
|
|
474
|
+
case "$1" in install|start) MODE="$1" ;; *) usage ;; esac
|
|
475
|
+
fail() { echo "cloud-setup: $*" >&2; exit 1; }
|
|
476
|
+
trap 'echo "cloud-setup: command failed at line $LINENO" >&2' ERR
|
|
477
|
+
[[ -f /etc/os-release ]] || fail 'Ubuntu 24.04 is required'
|
|
478
|
+
. /etc/os-release
|
|
479
|
+
[[ "$ID" == ubuntu && "$VERSION_ID" == 24.04 && "$VERSION_CODENAME" == noble ]] || fail 'Only Ubuntu 24.04 (noble) is supported'
|
|
480
|
+
SCRIPT="$(realpath -- "$0")"
|
|
481
|
+
REPO_ROOT="$(cd -- "$(dirname -- "$SCRIPT")/.." && pwd -P)"
|
|
482
|
+
[[ "$SCRIPT" == "$REPO_ROOT/.agents/cloud-setup.sh" ]] || fail 'Run the generated repository script'
|
|
483
|
+
if [[ "$(id -u)" != 0 ]]; then
|
|
484
|
+
command -v sudo >/dev/null && sudo -n true || fail 'Root or passwordless sudo is required'
|
|
485
|
+
exec sudo -n -- bash "$SCRIPT" "$MODE"
|
|
486
|
+
fi
|
|
487
|
+
OUTPUT_UID="$(printenv SUDO_UID || id -u)"
|
|
488
|
+
[[ "$OUTPUT_UID" =~ ^[0-9]+$ ]] || fail 'Invalid invoking user'
|
|
489
|
+
OUTPUT_GID="$(getent passwd "$OUTPUT_UID" | cut -d: -f4)"
|
|
490
|
+
[[ "$OUTPUT_GID" =~ ^[0-9]+$ ]] || fail 'Invoking user is unavailable'
|
|
491
|
+
OUTPUT_USER="$(getent passwd "$OUTPUT_UID" | cut -d: -f1)"
|
|
492
|
+
OUTPUT_HOME="$(getent passwd "$OUTPUT_UID" | cut -d: -f6)"
|
|
493
|
+
[[ -n "$OUTPUT_USER" && "$OUTPUT_HOME" == /* && -d "$OUTPUT_HOME" ]] || fail 'Invoking user requires an existing home directory'
|
|
494
|
+
root() { "$@"; }
|
|
495
|
+
service_run() { runuser -u stll-cloud -- "$@" 9>&-; }
|
|
496
|
+
BUN_VERSION='@BUN@'
|
|
497
|
+
NODE_VERSION='@NODE@'
|
|
498
|
+
NODE_DIR="/opt/stll-cloud/node/$NODE_VERSION"
|
|
499
|
+
BUN_DIR="/opt/stll-cloud/bun/$BUN_VERSION"
|
|
500
|
+
export PATH="$NODE_DIR/bin:$BUN_DIR:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
501
|
+
BUN_CACHE="/var/cache/stll-cloud/bun/$OUTPUT_UID"
|
|
502
|
+
@ENV_ASSIGNMENTS@
|
|
503
|
+
[[ -f "$REPO_ROOT/.node-version" && ! -L "$REPO_ROOT/.node-version" ]] || fail 'A regular root .node-version is required'
|
|
504
|
+
[[ "$(awk 'NF { if (++count > 1 || NF != 1) exit 1; value=$1 } END { if (count != 1) exit 1; print value }' "$REPO_ROOT/.node-version")" == "$NODE_VERSION" ]] || fail 'Node selector changed; regenerate cloud-setup.sh'
|
|
505
|
+
cd -- "$REPO_ROOT"
|
|
506
|
+
case "$(uname -m)" in
|
|
507
|
+
x86_64) ARCH=amd64; NODE_ARCH=x64; BUN_ARCH=x64 ;;
|
|
508
|
+
aarch64|arm64) ARCH=arm64; NODE_ARCH=arm64; BUN_ARCH=aarch64 ;;
|
|
509
|
+
*) fail 'Only amd64 and arm64 are supported' ;;
|
|
510
|
+
esac
|
|
511
|
+
|
|
512
|
+
runtime_ready() {
|
|
513
|
+
[[ -x "$NODE_DIR/bin/node" && "$($NODE_DIR/bin/node --version)" == "v$NODE_VERSION" ]] || fail 'Pinned Node is unavailable; run install'
|
|
514
|
+
[[ -x "$BUN_DIR/bun" && "$($BUN_DIR/bun --version)" == "$BUN_VERSION" ]] || fail 'Pinned Bun is unavailable; run install'
|
|
515
|
+
}
|
|
516
|
+
install_runtimes() {
|
|
517
|
+
if ! command -v curl >/dev/null || ! command -v unzip >/dev/null || ! command -v xz >/dev/null || ! command -v openssl >/dev/null || ! command -v gpg >/dev/null; then
|
|
518
|
+
apt-get update
|
|
519
|
+
DEBIAN_FRONTEND=noninteractive apt_install ca-certificates curl unzip xz-utils openssl gnupg util-linux
|
|
520
|
+
fi
|
|
521
|
+
local scratch node_archive bun_archive bun_folder
|
|
522
|
+
scratch="$(mktemp -d)"
|
|
523
|
+
trap 'rm -rf -- "$scratch"' RETURN
|
|
524
|
+
if [[ ! -x "$NODE_DIR/bin/node" ]] || [[ "$($NODE_DIR/bin/node --version)" != "v$NODE_VERSION" ]]; then
|
|
525
|
+
node_archive="node-v$NODE_VERSION-linux-$NODE_ARCH.tar.xz"
|
|
526
|
+
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://nodejs.org/dist/v$NODE_VERSION/$node_archive" -o "$scratch/$node_archive"
|
|
527
|
+
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt" -o "$scratch/node-sums"
|
|
528
|
+
(cd "$scratch"; awk -v name="$node_archive" '$2 == name { print; count++ } END { if (count != 1) exit 1 }' node-sums | sha256sum --strict -c -)
|
|
529
|
+
install -d -m 755 "$NODE_DIR"
|
|
530
|
+
tar -xJf "$scratch/$node_archive" --strip-components=1 -C "$NODE_DIR"
|
|
531
|
+
fi
|
|
532
|
+
if [[ ! -x "$BUN_DIR/bun" ]] || [[ "$($BUN_DIR/bun --version)" != "$BUN_VERSION" ]]; then
|
|
533
|
+
bun_folder="bun-linux-$BUN_ARCH"
|
|
534
|
+
bun_archive="$bun_folder.zip"
|
|
535
|
+
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://github.com/oven-sh/bun/releases/download/bun-v$BUN_VERSION/$bun_archive" -o "$scratch/$bun_archive"
|
|
536
|
+
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://github.com/oven-sh/bun/releases/download/bun-v$BUN_VERSION/SHASUMS256.txt" -o "$scratch/bun-sums"
|
|
537
|
+
(cd "$scratch"; awk -v name="$bun_archive" '$2 == name || $2 == "*" name { print $1 " " name; count++ } END { if (count != 1) exit 1 }' bun-sums | sha256sum --strict -c -)
|
|
538
|
+
unzip -q "$scratch/$bun_archive" -d "$scratch/bun"
|
|
539
|
+
install -d -m 755 "$BUN_DIR"
|
|
540
|
+
install -m 755 "$scratch/bun/$bun_folder/bun" "$BUN_DIR/bun"
|
|
541
|
+
fi
|
|
542
|
+
runtime_ready
|
|
543
|
+
ln -sfnT -- "$NODE_DIR/bin/node" /usr/local/bin/node
|
|
544
|
+
ln -sfnT -- "$NODE_DIR/bin/npm" /usr/local/bin/npm
|
|
545
|
+
ln -sfnT -- "$NODE_DIR/bin/npx" /usr/local/bin/npx
|
|
546
|
+
ln -sfnT -- "$BUN_DIR/bun" /usr/local/bin/bun
|
|
547
|
+
rm -rf -- "$scratch"
|
|
548
|
+
trap - RETURN
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
dependency_install() {
|
|
552
|
+
local directory
|
|
553
|
+
for directory in /var/cache/stll-cloud /var/cache/stll-cloud/bun; do
|
|
554
|
+
[[ ! -L "$directory" ]] || fail 'Dependency cache parent must not be a symlink'
|
|
555
|
+
if [[ -e "$directory" ]]; then
|
|
556
|
+
[[ -d "$directory" && "$(stat -c '%u' "$directory")" == 0 ]] || fail 'Dependency cache parent must be root-owned'
|
|
557
|
+
fi
|
|
558
|
+
install -d -m 755 -o root -g root "$directory"
|
|
559
|
+
done
|
|
560
|
+
[[ ! -L "$BUN_CACHE" ]] || fail 'Dependency cache must not be a symlink'
|
|
561
|
+
if [[ -e "$BUN_CACHE" ]]; then
|
|
562
|
+
[[ -d "$BUN_CACHE" && "$(stat -c '%u' "$BUN_CACHE")" == "$OUTPUT_UID" ]] || fail 'Dependency cache ownership differs from the invoking user'
|
|
563
|
+
fi
|
|
564
|
+
install -d -m 700 -o "$OUTPUT_UID" -g "$OUTPUT_GID" "$BUN_CACHE"
|
|
565
|
+
runuser -u "$OUTPUT_USER" -- env HOME="$OUTPUT_HOME" BUN_INSTALL_CACHE_DIR="$BUN_CACHE" PATH="$PATH" "$BUN_DIR/bun" install --frozen-lockfile
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
@SERVICE_INSTALL@
|
|
569
|
+
@SERVICE_START@
|
|
570
|
+
|
|
571
|
+
@ENV_FUNCTIONS@
|
|
572
|
+
|
|
573
|
+
if [[ "$MODE" == install ]]; then
|
|
574
|
+
install_runtimes
|
|
575
|
+
install_services
|
|
576
|
+
dependency_install
|
|
577
|
+
exit 0
|
|
578
|
+
fi
|
|
579
|
+
runtime_ready
|
|
580
|
+
@ENV_VALIDATE@
|
|
581
|
+
prepare_state
|
|
582
|
+
start_services
|
|
583
|
+
@ENV_WRITE@
|
|
584
|
+
`;
|
|
585
|
+
const environmentFunctions = String.raw`
|
|
586
|
+
validate_environment() {
|
|
587
|
+
local part target="$REPO_ROOT" env_path="$REPO_ROOT/$ENV_FILE" first_line temporary
|
|
588
|
+
while IFS= read -r part; do
|
|
589
|
+
target="$target/$part"
|
|
590
|
+
[[ ! -L "$target" ]] || fail 'Environment path must not contain symbolic links'
|
|
591
|
+
done < <(printf '%s\n' "$ENV_FILE" | tr '/' '\n')
|
|
592
|
+
[[ -d "$(dirname -- "$env_path")" ]] || fail 'Environment parent directory is missing'
|
|
593
|
+
if [[ -e "$env_path" ]]; then
|
|
594
|
+
[[ -f "$env_path" ]] || fail 'Environment target must be a regular file'
|
|
595
|
+
IFS= read -r first_line < "$env_path" || fail 'Refusing to overwrite an unowned environment file'
|
|
596
|
+
[[ "$first_line" == "$ENV_MARKER" ]] || fail 'Refusing to overwrite an unowned environment file'
|
|
597
|
+
fi
|
|
598
|
+
}
|
|
599
|
+
write_environment() {
|
|
600
|
+
validate_environment
|
|
601
|
+
local env_path="$REPO_ROOT/$ENV_FILE" temporary
|
|
602
|
+
temporary="$(mktemp "$(dirname -- "$env_path")/.stll-cloud-env.XXXXXX")"
|
|
603
|
+
chmod 600 "$temporary"
|
|
604
|
+
{
|
|
605
|
+
printf '%s\n' "$ENV_MARKER" 'NODE_ENV=test'
|
|
606
|
+
if [[ -n "$DATABASE_URL" ]]; then printf 'DATABASE_URL=%s\n' "$DATABASE_URL"; fi
|
|
607
|
+
if [[ -n "$REDIS_URL" ]]; then printf 'REDIS_URL=%s\n' "$REDIS_URL"; fi
|
|
608
|
+
} > "$temporary"
|
|
609
|
+
chown "$OUTPUT_UID:$OUTPUT_GID" "$temporary"
|
|
610
|
+
mv -f -- "$temporary" "$env_path"
|
|
611
|
+
}
|
|
612
|
+
`;
|
|
613
|
+
const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
614
|
+
const generateCloudSetup = ({ policy, cloud, nodeVersion }) => {
|
|
615
|
+
const selected = parseCloudSetup(cloud);
|
|
616
|
+
if (selected === void 0) throw new Error("cloud declaration is required");
|
|
617
|
+
const exactNode = nodeVersion.trim();
|
|
618
|
+
if ((0, semver.valid)(exactNode) !== exactNode || !nodeSelectorMatches(exactNode, policy.node)) throw new Error("cloud requires a tracked exact .node-version within the shared Node series");
|
|
619
|
+
const services = cloudServices.filter((service) => selected.services.includes(service));
|
|
620
|
+
const replacements = /* @__PURE__ */ new Map([
|
|
621
|
+
["@BUN@", policy.bun],
|
|
622
|
+
["@NODE@", exactNode],
|
|
623
|
+
["@ENV_ASSIGNMENTS@", selected.envFile === void 0 ? "" : `ENV_FILE=${shellQuote(selected.envFile)}\nENV_MARKER=${shellQuote(cloudEnvironmentMarker)}`],
|
|
624
|
+
["@ENV_FUNCTIONS@", selected.envFile === void 0 ? "" : environmentFunctions],
|
|
625
|
+
["@ENV_VALIDATE@", selected.envFile === void 0 ? "" : "DATABASE_URL=''\nREDIS_URL=''\nvalidate_environment"],
|
|
626
|
+
["@ENV_WRITE@", selected.envFile === void 0 ? "" : "write_environment"],
|
|
627
|
+
["@SERVICE_INSTALL@", renderCloudServiceInstall({
|
|
628
|
+
services,
|
|
629
|
+
postgres: policy.postgres,
|
|
630
|
+
valkey: policy.valkey,
|
|
631
|
+
valkeyArtifacts: policy.valkeyArtifacts
|
|
632
|
+
})],
|
|
633
|
+
["@SERVICE_START@", renderCloudServiceStart({
|
|
634
|
+
services,
|
|
635
|
+
postgres: policy.postgres,
|
|
636
|
+
valkey: policy.valkey
|
|
637
|
+
})]
|
|
638
|
+
]);
|
|
639
|
+
return script.replace(/@[A-Z0-9_]+@/g, (token) => {
|
|
640
|
+
const value = replacements.get(token);
|
|
641
|
+
if (value === void 0) throw new Error(`Missing cloud setup field: ${token}`);
|
|
642
|
+
return value;
|
|
643
|
+
});
|
|
644
|
+
};
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region src/toolchain-workspaces.ts
|
|
647
|
+
/** Match manifest directories with the same positive and negative workspace patterns. */
|
|
648
|
+
const workspaceContains = ({ directory, file, patterns, rootMembership }) => {
|
|
649
|
+
const relative = node_path.default.posix.relative(directory, node_path.default.posix.dirname(file));
|
|
650
|
+
if (relative === "" && rootMembership === "implicit") return true;
|
|
651
|
+
if (relative === ".." || relative.startsWith("../")) return false;
|
|
652
|
+
if (!Array.isArray(patterns)) return false;
|
|
653
|
+
const normalize = (pattern) => node_path.default.posix.normalize(pattern);
|
|
654
|
+
const selected = patterns.filter((value) => typeof value === "string");
|
|
655
|
+
return (0, picomatch.default)(selected.filter((pattern) => !pattern.startsWith("!")).map(normalize))(relative) && !(0, picomatch.default)(selected.filter((pattern) => pattern.startsWith("!")).map((pattern) => normalize(pattern.slice(1))))(relative);
|
|
656
|
+
};
|
|
657
|
+
//#endregion
|
|
658
|
+
//#region src/toolchain-container-inputs.ts
|
|
659
|
+
const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
660
|
+
/** Filename class recognized by Dependabot's Docker Compose fetcher. */
|
|
661
|
+
const isComposeDefinitionPath = (file) => /^\.?(?:docker-)?compose(?:-[\w]+)?(?:\.[\w-]+)?\.ya?ml$/i.test(file.split("/").at(-1) ?? "");
|
|
662
|
+
/** Candidate YAML files are classified by their document shape after reading. */
|
|
663
|
+
const isKubernetesDefinitionPath = (file) => /^[^.].*\.ya?ml$/i.test(file.split("/").at(-1) ?? "");
|
|
664
|
+
const kubernetesPodSpecPaths = {
|
|
665
|
+
Pod: ["spec"],
|
|
666
|
+
Deployment: [
|
|
667
|
+
"spec",
|
|
668
|
+
"template",
|
|
669
|
+
"spec"
|
|
670
|
+
],
|
|
671
|
+
DaemonSet: [
|
|
672
|
+
"spec",
|
|
673
|
+
"template",
|
|
674
|
+
"spec"
|
|
675
|
+
],
|
|
676
|
+
StatefulSet: [
|
|
677
|
+
"spec",
|
|
678
|
+
"template",
|
|
679
|
+
"spec"
|
|
680
|
+
],
|
|
681
|
+
ReplicaSet: [
|
|
682
|
+
"spec",
|
|
683
|
+
"template",
|
|
684
|
+
"spec"
|
|
685
|
+
],
|
|
686
|
+
ReplicationController: [
|
|
687
|
+
"spec",
|
|
688
|
+
"template",
|
|
689
|
+
"spec"
|
|
690
|
+
],
|
|
691
|
+
Job: [
|
|
692
|
+
"spec",
|
|
693
|
+
"template",
|
|
694
|
+
"spec"
|
|
695
|
+
],
|
|
696
|
+
CronJob: [
|
|
697
|
+
"spec",
|
|
698
|
+
"jobTemplate",
|
|
699
|
+
"spec",
|
|
700
|
+
"template",
|
|
701
|
+
"spec"
|
|
702
|
+
]
|
|
703
|
+
};
|
|
704
|
+
/** Shared semantic image enumeration for runtime checks and update-root generation. */
|
|
705
|
+
const containerDocumentImages = (document, file) => {
|
|
706
|
+
if (!record(document)) return void 0;
|
|
707
|
+
const services = document["services"];
|
|
708
|
+
if (isComposeDefinitionPath(file) && record(services)) {
|
|
709
|
+
const images = [];
|
|
710
|
+
for (const [name, service] of Object.entries(services)) if (record(service) && service["image"] !== void 0) images.push({
|
|
711
|
+
image: service["image"],
|
|
712
|
+
path: [
|
|
713
|
+
"services",
|
|
714
|
+
name,
|
|
715
|
+
"image"
|
|
716
|
+
]
|
|
717
|
+
});
|
|
718
|
+
return {
|
|
719
|
+
ecosystem: "docker-compose",
|
|
720
|
+
images
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
if (typeof document["apiVersion"] !== "string" || typeof document["kind"] !== "string") return void 0;
|
|
724
|
+
const images = [];
|
|
725
|
+
const visited = /* @__PURE__ */ new Set();
|
|
726
|
+
const walkResource = (resource, prefix) => {
|
|
727
|
+
if (visited.has(resource)) return;
|
|
728
|
+
visited.add(resource);
|
|
729
|
+
if (typeof resource["apiVersion"] !== "string" || typeof resource["kind"] !== "string") return;
|
|
730
|
+
if (resource["kind"] === "List" && Array.isArray(resource["items"])) {
|
|
731
|
+
for (const [index, item] of resource["items"].entries()) if (record(item)) walkResource(item, [
|
|
732
|
+
...prefix,
|
|
733
|
+
"items",
|
|
734
|
+
index
|
|
735
|
+
]);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
const specPath = Object.entries(kubernetesPodSpecPaths).find(([kind]) => kind === resource["kind"])?.[1];
|
|
739
|
+
if (specPath === void 0) return;
|
|
740
|
+
let spec = resource;
|
|
741
|
+
for (const key of specPath) spec = record(spec) ? spec[key] : void 0;
|
|
742
|
+
if (!record(spec)) return;
|
|
743
|
+
for (const key of [
|
|
744
|
+
"containers",
|
|
745
|
+
"initContainers",
|
|
746
|
+
"ephemeralContainers"
|
|
747
|
+
]) {
|
|
748
|
+
const containers = spec[key];
|
|
749
|
+
if (!Array.isArray(containers)) continue;
|
|
750
|
+
for (const [index, container] of containers.entries()) if (record(container) && container["image"] !== void 0) images.push({
|
|
751
|
+
image: container["image"],
|
|
752
|
+
path: [
|
|
753
|
+
...prefix,
|
|
754
|
+
...specPath,
|
|
755
|
+
key,
|
|
756
|
+
index,
|
|
757
|
+
"image"
|
|
758
|
+
]
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
walkResource(document, []);
|
|
763
|
+
return images.length === 0 ? void 0 : {
|
|
764
|
+
ecosystem: "docker",
|
|
765
|
+
images
|
|
766
|
+
};
|
|
767
|
+
};
|
|
768
|
+
//#endregion
|
|
769
|
+
//#region src/toolchain-inputs.ts
|
|
770
|
+
const excludedInputPath = (file) => file === "" || file.split("/").some((part) => part === "node_modules" || part === "vendor");
|
|
771
|
+
const javascriptDependencyLockfiles = [
|
|
772
|
+
"bun.lock",
|
|
773
|
+
"package-lock.json",
|
|
774
|
+
"yarn.lock",
|
|
775
|
+
"pnpm-lock.yaml"
|
|
776
|
+
];
|
|
777
|
+
/** Match project, environment, local, and fragment filenames recognized by mise. */
|
|
778
|
+
const isMiseConfigPath = (file) => !excludedInputPath(file) && (/(?:^|\/)\.?mise(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/config(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/conf\.d\/[^./][^/]*\.toml$/.test(file));
|
|
779
|
+
/** GitHub action metadata may live at the repository root or in any directory. */
|
|
780
|
+
const githubAutomationFileKind = (file) => {
|
|
781
|
+
if (file === "") return void 0;
|
|
782
|
+
if (/^\.github\/workflows\/[^/]+\.ya?ml$/.test(file)) return "workflow";
|
|
783
|
+
if (/(?:^|\/)action\.ya?ml$/.test(file)) return "action";
|
|
784
|
+
};
|
|
785
|
+
/** Dependabot updates workflows and repository-root action metadata. */
|
|
786
|
+
const isDependabotGithubActionsPath = (file) => {
|
|
787
|
+
const kind = githubAutomationFileKind(file);
|
|
788
|
+
return kind === "workflow" || kind === "action" && !file.includes("/");
|
|
789
|
+
};
|
|
790
|
+
/** Use one filename discriminator for reader selection and Python ecosystem decisions. */
|
|
791
|
+
const pythonDependencyManifestKind = (file) => {
|
|
792
|
+
if (excludedInputPath(file)) return void 0;
|
|
793
|
+
const name = file.split("/").at(-1);
|
|
794
|
+
if (name === "pyproject.toml") return "project";
|
|
795
|
+
if (name === "Pipfile") return "pipfile";
|
|
796
|
+
if (name === "Pipfile.lock") return "pipfile-lock";
|
|
797
|
+
if (name === "setup.py" || name === "setup.cfg") return "setup";
|
|
798
|
+
if (name !== void 0 && /^requirements[^/]*\.(?:txt|in)$/.test(name)) return "requirements";
|
|
799
|
+
};
|
|
800
|
+
const isPythonDependencyManifest = (file) => pythonDependencyManifestKind(file) !== void 0;
|
|
801
|
+
/** Docker definition names use dot-delimited variants, excluding source and documentation. */
|
|
802
|
+
const isDockerDefinitionPath = (file) => {
|
|
803
|
+
if (excludedInputPath(file)) return false;
|
|
804
|
+
const name = file.split("/").at(-1) ?? "";
|
|
805
|
+
if (/\.(?:[cm]?[jt]sx?|mdx?|rst|txt|html?|py|rs|go|java|sh|c|cc|cpp|cxx|h|hpp|cs|rb|php|swift|kt|css|scss)$/i.test(name)) return false;
|
|
806
|
+
return /^(?:[^./]+\.)*(?:dockerfile|containerfile)(?:\.[^./]+)*$/i.test(name);
|
|
807
|
+
};
|
|
808
|
+
/** Lockfiles establish ecosystem presence without reading their dependency graphs. */
|
|
809
|
+
const toolchainInputKind = (file) => {
|
|
810
|
+
if (file === ".agents/cloud-setup.sh") return "config";
|
|
811
|
+
if (githubAutomationFileKind(file) !== void 0) return "config";
|
|
812
|
+
if (excludedInputPath(file)) return void 0;
|
|
813
|
+
if (pythonDependencyManifestKind(file) === "pipfile-lock" || javascriptDependencyLockfiles.some((name) => file.split("/").at(-1) === name) || /(?:^|\/)uv\.lock$/.test(file)) return "presence";
|
|
814
|
+
if (githubAutomationFileKind(file) !== void 0 || isMiseConfigPath(file) || isPythonDependencyManifest(file) || isDockerDefinitionPath(file) || isComposeDefinitionPath(file) || isKubernetesDefinitionPath(file) || /(?:^|\/)(?:package\.json|consumer-compat\.json|pnpm-workspace\.yaml|stll-toolchain\.json|\.bun-version|\.node-version|\.nvmrc|\.python-version|rust-toolchain(?:\.toml)?|\.tool-versions|\.?mise\.toml|uv\.toml|\.uv\.toml|\.github\/dependabot\.ya?ml|Cargo\.toml)$/.test(file)) return "config";
|
|
815
|
+
};
|
|
816
|
+
//#endregion
|
|
817
|
+
Object.defineProperty(exports, "cloudSetupPath", {
|
|
818
|
+
enumerable: true,
|
|
819
|
+
get: function() {
|
|
820
|
+
return cloudSetupPath;
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
Object.defineProperty(exports, "containerDocumentImages", {
|
|
824
|
+
enumerable: true,
|
|
825
|
+
get: function() {
|
|
826
|
+
return containerDocumentImages;
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
Object.defineProperty(exports, "generateCloudSetup", {
|
|
830
|
+
enumerable: true,
|
|
831
|
+
get: function() {
|
|
832
|
+
return generateCloudSetup;
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
Object.defineProperty(exports, "githubAutomationFileKind", {
|
|
836
|
+
enumerable: true,
|
|
837
|
+
get: function() {
|
|
838
|
+
return githubAutomationFileKind;
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
Object.defineProperty(exports, "isComposeDefinitionPath", {
|
|
842
|
+
enumerable: true,
|
|
843
|
+
get: function() {
|
|
844
|
+
return isComposeDefinitionPath;
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
Object.defineProperty(exports, "isDependabotGithubActionsPath", {
|
|
848
|
+
enumerable: true,
|
|
849
|
+
get: function() {
|
|
850
|
+
return isDependabotGithubActionsPath;
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
Object.defineProperty(exports, "isDockerDefinitionPath", {
|
|
854
|
+
enumerable: true,
|
|
855
|
+
get: function() {
|
|
856
|
+
return isDockerDefinitionPath;
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
Object.defineProperty(exports, "isKubernetesDefinitionPath", {
|
|
860
|
+
enumerable: true,
|
|
861
|
+
get: function() {
|
|
862
|
+
return isKubernetesDefinitionPath;
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
Object.defineProperty(exports, "isMiseConfigPath", {
|
|
866
|
+
enumerable: true,
|
|
867
|
+
get: function() {
|
|
868
|
+
return isMiseConfigPath;
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
Object.defineProperty(exports, "isPythonDependencyManifest", {
|
|
872
|
+
enumerable: true,
|
|
873
|
+
get: function() {
|
|
874
|
+
return isPythonDependencyManifest;
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
Object.defineProperty(exports, "javascriptDependencyLockfiles", {
|
|
878
|
+
enumerable: true,
|
|
879
|
+
get: function() {
|
|
880
|
+
return javascriptDependencyLockfiles;
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
Object.defineProperty(exports, "nodePolicyValid", {
|
|
884
|
+
enumerable: true,
|
|
885
|
+
get: function() {
|
|
886
|
+
return nodePolicyValid;
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
Object.defineProperty(exports, "nodeSelectorMatches", {
|
|
890
|
+
enumerable: true,
|
|
891
|
+
get: function() {
|
|
892
|
+
return nodeSelectorMatches;
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
Object.defineProperty(exports, "nodeSupportRangeMatches", {
|
|
896
|
+
enumerable: true,
|
|
897
|
+
get: function() {
|
|
898
|
+
return nodeSupportRangeMatches;
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
Object.defineProperty(exports, "parseCloudSetup", {
|
|
902
|
+
enumerable: true,
|
|
903
|
+
get: function() {
|
|
904
|
+
return parseCloudSetup;
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
Object.defineProperty(exports, "pythonDependencyManifestKind", {
|
|
908
|
+
enumerable: true,
|
|
909
|
+
get: function() {
|
|
910
|
+
return pythonDependencyManifestKind;
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
Object.defineProperty(exports, "toolchainInputKind", {
|
|
914
|
+
enumerable: true,
|
|
915
|
+
get: function() {
|
|
916
|
+
return toolchainInputKind;
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
Object.defineProperty(exports, "workspaceContains", {
|
|
920
|
+
enumerable: true,
|
|
921
|
+
get: function() {
|
|
922
|
+
return workspaceContains;
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
//# sourceMappingURL=toolchain-inputs.cjs.map
|