@le-space/rootfs 0.4.4 → 0.5.2
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/index.js +1 -1
- package/package.json +9 -1
- package/reference/orbitdb-relay-pinner/rootfs/build-rootfs.sh +13 -79
- package/reference/orbitdb-relay-pinner/rootfs/orbitdb-relay-pinner-bootstrap-refresh.py +1 -1
- package/reference/orbitdb-relay-pinner/rootfs/publish-store-message.mjs +73 -0
- package/reference/orbitdb-relay-pinner/rootfs/read-rootfs-contract.mjs +68 -0
- package/reference/uc-go-peer/rootfs/build-rootfs.sh +1 -1
- package/reference/uc-go-peer/rootfs/uc-go-peer-bootstrap-deregister.py +1 -1
- package/reference/uc-go-peer/rootfs/uc-go-peer-bootstrap-refresh.py +1 -1
- package/reference/uc-go-peer/rootfs/uc-go-peer-setup-server.py +1 -1
- package/reference/orbitdb-relay-pinner/rootfs/read-rootfs-contract.py +0 -72
package/index.js
CHANGED
|
@@ -222,7 +222,7 @@ function createRootfsBuildPlan(contract, options) {
|
|
|
222
222
|
skipBuild: options.skipBuild === true,
|
|
223
223
|
ipfsAddUrl: options.ipfsAddUrl?.trim() || "https://ipfs.aleph.cloud/api/v0/add",
|
|
224
224
|
ipfsGatewayUrl: options.ipfsGatewayUrl?.trim() || "https://ipfs.aleph.cloud/ipfs",
|
|
225
|
-
alephApiHost: options.alephApiHost?.trim() || "https://
|
|
225
|
+
alephApiHost: options.alephApiHost?.trim() || "https://api.aleph.im",
|
|
226
226
|
alephMessageWaitAttempts: positiveInteger(options.alephMessageWaitAttempts, 60),
|
|
227
227
|
alephMessageWaitDelaySeconds: positiveInteger(options.alephMessageWaitDelaySeconds, 5),
|
|
228
228
|
alephPinAttempts: positiveInteger(options.alephPinAttempts, 4),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/rootfs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Shared rootfs contract parsing, reference profile assets, and build helpers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,5 +14,13 @@
|
|
|
14
14
|
},
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/NiKrause/relay-button"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/NiKrause/relay-button",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/NiKrause/relay-button/issues"
|
|
17
25
|
}
|
|
18
26
|
}
|
|
@@ -31,12 +31,12 @@ die() {
|
|
|
31
31
|
|
|
32
32
|
load_rootfs_contract() {
|
|
33
33
|
[ -n "${ROOTFS_CONTRACT_FILE}" ] || return 0
|
|
34
|
-
require
|
|
34
|
+
require node
|
|
35
35
|
[ -f "${ROOTFS_CONTRACT_FILE}" ] || die "Rootfs contract does not exist: ${ROOTFS_CONTRACT_FILE}"
|
|
36
36
|
|
|
37
37
|
# The contract is relay-owned metadata. We load it once here so users can
|
|
38
38
|
# select a standardized profile without repeating profile/install-mode flags.
|
|
39
|
-
eval "$(
|
|
39
|
+
eval "$(node "${SCRIPT_DIR}/read-rootfs-contract.mjs" "${ROOTFS_CONTRACT_FILE}")"
|
|
40
40
|
|
|
41
41
|
if [ -n "${ROOTFS_PROFILE}" ] && [ "${ROOTFS_PROFILE}" != "${ROOTFS_CONTRACT_PROFILE}" ]; then
|
|
42
42
|
die "ROOTFS_PROFILE=${ROOTFS_PROFILE} conflicts with contract profile ${ROOTFS_CONTRACT_PROFILE}"
|
|
@@ -119,25 +119,11 @@ resolve_rootfs_version() {
|
|
|
119
119
|
|
|
120
120
|
if [ "${ROOTFS_PROFILE}" = "orbitdb-relay-pinner" ] && [ -n "${ORBITDB_RELAY_PINNER_DIR}" ]; then
|
|
121
121
|
local orbitdb_package_json="${ORBITDB_RELAY_PINNER_DIR}/package.json"
|
|
122
|
-
if [ -f "${orbitdb_package_json}" ] && command -v
|
|
122
|
+
if [ -f "${orbitdb_package_json}" ] && command -v node >/dev/null 2>&1; then
|
|
123
123
|
local orbitdb_version
|
|
124
|
-
orbitdb_version="$(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
from pathlib import Path
|
|
128
|
-
|
|
129
|
-
try:
|
|
130
|
-
payload = json.loads(Path(sys.argv[1]).read_text())
|
|
131
|
-
except Exception:
|
|
132
|
-
raise SystemExit(1)
|
|
133
|
-
|
|
134
|
-
version = payload.get("version")
|
|
135
|
-
if not isinstance(version, str) or not version.strip():
|
|
136
|
-
raise SystemExit(1)
|
|
137
|
-
|
|
138
|
-
print(f"orbitdb-relay-pinner-v{version.strip().lstrip('v')}")
|
|
139
|
-
PY
|
|
140
|
-
)" || orbitdb_version=""
|
|
124
|
+
orbitdb_version="$(
|
|
125
|
+
node -e "const fs=require('node:fs');const payload=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));const version=typeof payload.version==='string'?payload.version.trim():'';if(!version)process.exit(1);console.log('orbitdb-relay-pinner-v'+version.replace(/^v/,''));" "${orbitdb_package_json}"
|
|
126
|
+
)" || orbitdb_version=""
|
|
141
127
|
|
|
142
128
|
if [ -n "${orbitdb_version}" ]; then
|
|
143
129
|
printf '%s\n' "${orbitdb_version}"
|
|
@@ -172,27 +158,6 @@ PY
|
|
|
172
158
|
|
|
173
159
|
ROOTFS_VERSION="$(resolve_rootfs_version)"
|
|
174
160
|
|
|
175
|
-
resolve_aleph_bin() {
|
|
176
|
-
if [ -n "${ALEPH_BIN:-}" ]; then
|
|
177
|
-
printf '%s\n' "${ALEPH_BIN}"
|
|
178
|
-
return
|
|
179
|
-
fi
|
|
180
|
-
|
|
181
|
-
local local_aleph="${REPO_DIR}/aleph-client/.venv/bin/aleph"
|
|
182
|
-
if [ -x "${local_aleph}" ]; then
|
|
183
|
-
printf '%s\n' "${local_aleph}"
|
|
184
|
-
return
|
|
185
|
-
fi
|
|
186
|
-
|
|
187
|
-
if command -v aleph >/dev/null 2>&1; then
|
|
188
|
-
command -v aleph
|
|
189
|
-
return
|
|
190
|
-
fi
|
|
191
|
-
|
|
192
|
-
echo "Missing aleph CLI. Set ALEPH_BIN=/path/to/aleph or install aleph-client." >&2
|
|
193
|
-
exit 1
|
|
194
|
-
}
|
|
195
|
-
|
|
196
161
|
build_with_host_tools() {
|
|
197
162
|
echo "Using host virt-customize/qemu-img toolchain."
|
|
198
163
|
"${SCRIPT_DIR}/build-rootfs-image.sh"
|
|
@@ -412,10 +377,7 @@ EOF
|
|
|
412
377
|
}
|
|
413
378
|
|
|
414
379
|
upload_image() {
|
|
415
|
-
|
|
416
|
-
aleph_bin="$(resolve_aleph_bin)"
|
|
417
|
-
|
|
418
|
-
require python3
|
|
380
|
+
require node
|
|
419
381
|
require curl
|
|
420
382
|
[ -f "${IMAGE}" ] || die "Rootfs image does not exist: ${IMAGE}"
|
|
421
383
|
|
|
@@ -430,48 +392,20 @@ upload_image() {
|
|
|
430
392
|
fi
|
|
431
393
|
|
|
432
394
|
local cid
|
|
433
|
-
if ! cid="$(
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
from pathlib import Path
|
|
437
|
-
|
|
438
|
-
lines = [line for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]
|
|
439
|
-
if not lines:
|
|
440
|
-
raise SystemExit("No response received from the IPFS add endpoint")
|
|
441
|
-
|
|
442
|
-
payload = json.loads(lines[-1])
|
|
443
|
-
cid = payload.get("Hash")
|
|
444
|
-
if not cid:
|
|
445
|
-
raise SystemExit(f"IPFS add response did not include a Hash: {payload}")
|
|
446
|
-
|
|
447
|
-
print(cid)
|
|
448
|
-
PY
|
|
449
|
-
)"; then
|
|
395
|
+
if ! cid="$(
|
|
396
|
+
node -e "const fs=require('node:fs');const lines=fs.readFileSync(process.argv[1],'utf8').split(/\\r?\\n/).map((line)=>line.trim()).filter(Boolean);if(!lines.length){console.error('No response received from the IPFS add endpoint');process.exit(1)}const payload=JSON.parse(lines.at(-1));if(typeof payload.Hash!=='string'||!payload.Hash.trim()){console.error('IPFS add response did not include a Hash');process.exit(1)}console.log(payload.Hash.trim());" "${OUT_DIR}/ipfs-add-response.jsonl"
|
|
397
|
+
)"; then
|
|
450
398
|
die "Failed to extract CID from ${OUT_DIR}/ipfs-add-response.jsonl"
|
|
451
399
|
fi
|
|
452
400
|
[ -n "${cid}" ] || die "IPFS upload returned an empty CID"
|
|
453
401
|
|
|
454
|
-
echo "Pinning CID ${cid} on Aleph Cloud
|
|
402
|
+
echo "Pinning CID ${cid} on Aleph Cloud via direct API call..." >&2
|
|
455
403
|
: > "${OUT_DIR}/store-message.json"
|
|
456
|
-
if ! "${
|
|
457
|
-
--channel "${CHANNEL}" \
|
|
458
|
-
> "${OUT_DIR}/store-message.json"; then
|
|
404
|
+
if ! node "${SCRIPT_DIR}/publish-store-message.mjs" "${cid}" > "${OUT_DIR}/store-message.json"; then
|
|
459
405
|
die "Aleph pin failed for CID ${cid}"
|
|
460
406
|
fi
|
|
461
407
|
|
|
462
|
-
if !
|
|
463
|
-
import json
|
|
464
|
-
import sys
|
|
465
|
-
from pathlib import Path
|
|
466
|
-
|
|
467
|
-
content = Path(sys.argv[1]).read_text().strip()
|
|
468
|
-
if not content:
|
|
469
|
-
raise SystemExit("Aleph pin returned an empty response")
|
|
470
|
-
|
|
471
|
-
payload = json.loads(content)
|
|
472
|
-
print(payload["item_hash"])
|
|
473
|
-
PY
|
|
474
|
-
then
|
|
408
|
+
if ! node -e "const fs=require('node:fs');const content=fs.readFileSync(process.argv[1],'utf8').trim();if(!content){console.error('Aleph pin returned an empty response');process.exit(1)}const payload=JSON.parse(content);if(typeof payload.item_hash!=='string'||!payload.item_hash.trim()){console.error('Failed to extract item_hash');process.exit(1)}console.log(payload.item_hash.trim());" "${OUT_DIR}/store-message.json"; then
|
|
475
409
|
die "Failed to extract item_hash from ${OUT_DIR}/store-message.json"
|
|
476
410
|
fi
|
|
477
411
|
}
|
|
@@ -24,7 +24,7 @@ ENV_FILE = os.environ.get("ENV_FILE", "/etc/default/orbitdb-relay-pinner")
|
|
|
24
24
|
DESCRIBE_SCRIPT = os.environ.get(
|
|
25
25
|
"DESCRIBE_SCRIPT", "/usr/local/sbin/orbitdb-relay-pinner-describe.py"
|
|
26
26
|
)
|
|
27
|
-
DEFAULT_API_HOST = os.environ.get("ALEPH_BOOTSTRAP_API_HOST", "https://
|
|
27
|
+
DEFAULT_API_HOST = os.environ.get("ALEPH_BOOTSTRAP_API_HOST", "https://api.aleph.im")
|
|
28
28
|
DEFAULT_CHANNEL = os.environ.get("ALEPH_BOOTSTRAP_CHANNEL", "simple-todo")
|
|
29
29
|
DEFAULT_REF = os.environ.get("ALEPH_BOOTSTRAP_REF", "simple-todo-bootstrap")
|
|
30
30
|
DEFAULT_POST_TYPE = os.environ.get("ALEPH_BOOTSTRAP_POST_TYPE", "relay-bootstrap")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createHash } from 'node:crypto'
|
|
4
|
+
|
|
5
|
+
const [{ Wallet }] = await Promise.all([
|
|
6
|
+
import('ethers'),
|
|
7
|
+
])
|
|
8
|
+
|
|
9
|
+
function requiredEnv(name) {
|
|
10
|
+
const value = process.env[name]?.trim()
|
|
11
|
+
if (!value) {
|
|
12
|
+
throw new Error(`Missing required environment variable ${name}`)
|
|
13
|
+
}
|
|
14
|
+
return value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeApiHost(value) {
|
|
18
|
+
return value.replace(/\/+$/u, '')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const cid = process.argv[2]?.trim()
|
|
22
|
+
if (!cid) {
|
|
23
|
+
throw new Error('Usage: publish-store-message.mjs <cid>')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const wallet = new Wallet(requiredEnv('ALEPH_PRIVATE_KEY'))
|
|
27
|
+
const apiHost = normalizeApiHost(process.env.ALEPH_API_HOST?.trim() || 'https://api.aleph.im')
|
|
28
|
+
const channel = process.env.CHANNEL?.trim() || 'ALEPH-CLOUDSOLUTIONS'
|
|
29
|
+
const ref = process.env.ALEPH_ROOTFS_REF?.trim() || undefined
|
|
30
|
+
const time = Date.now() / 1000
|
|
31
|
+
|
|
32
|
+
const content = {
|
|
33
|
+
address: wallet.address,
|
|
34
|
+
time,
|
|
35
|
+
item_type: 'ipfs',
|
|
36
|
+
item_hash: cid,
|
|
37
|
+
...(ref ? { ref } : {}),
|
|
38
|
+
}
|
|
39
|
+
const itemContent = JSON.stringify(content)
|
|
40
|
+
const itemHash = createHash('sha256').update(itemContent).digest('hex')
|
|
41
|
+
const message = {
|
|
42
|
+
sender: wallet.address,
|
|
43
|
+
chain: 'ETH',
|
|
44
|
+
type: 'STORE',
|
|
45
|
+
item_hash: itemHash,
|
|
46
|
+
item_type: 'inline',
|
|
47
|
+
item_content: itemContent,
|
|
48
|
+
time,
|
|
49
|
+
channel,
|
|
50
|
+
}
|
|
51
|
+
const signaturePayload = [message.chain, message.sender, message.type, message.item_hash].join('\n')
|
|
52
|
+
const signature = await wallet.signMessage(signaturePayload)
|
|
53
|
+
|
|
54
|
+
const response = await fetch(`${apiHost}/api/v0/messages`, {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: {
|
|
57
|
+
'content-type': 'application/json',
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify({
|
|
60
|
+
sync: true,
|
|
61
|
+
message: {
|
|
62
|
+
...message,
|
|
63
|
+
signature,
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const payload = await response.json().catch(() => ({}))
|
|
69
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`)
|
|
70
|
+
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
process.exit(1)
|
|
73
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
function shellQuote(value) {
|
|
7
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function shellAssign(name, value) {
|
|
11
|
+
return `${name}=${shellQuote(value)}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const contractPath = process.argv[2]
|
|
15
|
+
if (!contractPath) {
|
|
16
|
+
console.error('Usage: read-rootfs-contract.mjs /path/to/contract.json')
|
|
17
|
+
process.exit(1)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let payload
|
|
21
|
+
try {
|
|
22
|
+
payload = JSON.parse(readFileSync(contractPath, 'utf8'))
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(`Failed to read rootfs contract ${contractPath}: ${error instanceof Error ? error.message : String(error)}`)
|
|
25
|
+
process.exit(1)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const rootfs = payload.rootfs ?? {}
|
|
29
|
+
const services = payload.services ?? {}
|
|
30
|
+
const source = payload.source ?? {}
|
|
31
|
+
const manifest = payload.manifest ?? {}
|
|
32
|
+
const ports = Array.isArray(payload.ports) ? payload.ports : null
|
|
33
|
+
const profile = typeof rootfs.profile === 'string' ? rootfs.profile.trim() : ''
|
|
34
|
+
const installMode = typeof rootfs.installMode === 'string' ? rootfs.installMode.trim() : ''
|
|
35
|
+
|
|
36
|
+
if (!profile) {
|
|
37
|
+
console.error('Rootfs contract is missing rootfs.profile')
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
if (!installMode) {
|
|
41
|
+
console.error('Rootfs contract is missing rootfs.installMode')
|
|
42
|
+
process.exit(1)
|
|
43
|
+
}
|
|
44
|
+
if (!ports) {
|
|
45
|
+
console.error('Rootfs contract field ports must be a list')
|
|
46
|
+
process.exit(1)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const resolvedContractPath = path.resolve(contractPath)
|
|
50
|
+
const lines = [
|
|
51
|
+
shellAssign('ROOTFS_CONTRACT_PATH', resolvedContractPath),
|
|
52
|
+
shellAssign('ROOTFS_CONTRACT_ID', payload.id ?? ''),
|
|
53
|
+
shellAssign('ROOTFS_CONTRACT_PROFILE', profile),
|
|
54
|
+
shellAssign('ROOTFS_CONTRACT_INSTALL_MODE', installMode),
|
|
55
|
+
shellAssign('ROOTFS_CONTRACT_SOURCE_SUBDIRECTORY', source.subdirectory ?? ''),
|
|
56
|
+
shellAssign('ROOTFS_CONTRACT_INSTALL_DIR', rootfs.installDir ?? ''),
|
|
57
|
+
shellAssign('ROOTFS_CONTRACT_BINARY_PATH', rootfs.binaryPath ?? '/usr/local/bin/universal-chat-go'),
|
|
58
|
+
shellAssign('ROOTFS_CONTRACT_DATA_DIR', rootfs.dataDir ?? ''),
|
|
59
|
+
shellAssign('ROOTFS_CONTRACT_ENV_FILE', rootfs.envFile ?? ''),
|
|
60
|
+
shellAssign('ROOTFS_CONTRACT_MAIN_SERVICE', services.main ?? ''),
|
|
61
|
+
shellAssign('ROOTFS_CONTRACT_BOOTSTRAP_SERVICE', services.bootstrap ?? ''),
|
|
62
|
+
shellAssign('ROOTFS_CONTRACT_AUTOTLS_SERVICE', services.autotlsRefresh ?? ''),
|
|
63
|
+
shellAssign('ROOTFS_CONTRACT_MANIFEST_COPY_TARGET', manifest.copyTarget ?? ''),
|
|
64
|
+
shellAssign('ROOTFS_CONTRACT_MANIFEST_NOTES', manifest.notes ?? ''),
|
|
65
|
+
shellAssign('ROOTFS_CONTRACT_PORT_FORWARDS_JSON', JSON.stringify(ports)),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
process.stdout.write(`${lines.join('\n')}\n`)
|
|
@@ -15,7 +15,7 @@ SKIP_UPLOAD="${SKIP_UPLOAD:-0}"
|
|
|
15
15
|
SKIP_BUILD="${SKIP_BUILD:-0}"
|
|
16
16
|
IPFS_ADD_URL="${IPFS_ADD_URL:-https://ipfs.aleph.cloud/api/v0/add}"
|
|
17
17
|
IPFS_GATEWAY_URL="${IPFS_GATEWAY_URL:-https://ipfs.aleph.cloud/ipfs}"
|
|
18
|
-
ALEPH_API_HOST="${ALEPH_API_HOST:-https://
|
|
18
|
+
ALEPH_API_HOST="${ALEPH_API_HOST:-https://api.aleph.im}"
|
|
19
19
|
IPFS_GATEWAY_WAIT_ATTEMPTS="${IPFS_GATEWAY_WAIT_ATTEMPTS:-30}"
|
|
20
20
|
IPFS_GATEWAY_WAIT_DELAY_SECONDS="${IPFS_GATEWAY_WAIT_DELAY_SECONDS:-10}"
|
|
21
21
|
ROOTFS_CID=""
|
|
@@ -20,7 +20,7 @@ except ImportError as error: # pragma: no cover - runtime dependency
|
|
|
20
20
|
|
|
21
21
|
ENV_FILE = os.environ.get("ENV_FILE", "/etc/default/uc-go-peer")
|
|
22
22
|
DESCRIBE_SCRIPT = os.environ.get("DESCRIBE_SCRIPT", "/usr/local/sbin/uc-go-peer-describe.py")
|
|
23
|
-
DEFAULT_API_HOST = os.environ.get("ALEPH_BOOTSTRAP_API_HOST", "https://
|
|
23
|
+
DEFAULT_API_HOST = os.environ.get("ALEPH_BOOTSTRAP_API_HOST", "https://api.aleph.im")
|
|
24
24
|
DEFAULT_CHANNEL = os.environ.get("ALEPH_BOOTSTRAP_CHANNEL", "simple-todo")
|
|
25
25
|
DEFAULT_REF = os.environ.get("ALEPH_BOOTSTRAP_REF", "simple-todo-bootstrap")
|
|
26
26
|
DEFAULT_POST_TYPE = os.environ.get("ALEPH_BOOTSTRAP_POST_TYPE", "relay-bootstrap")
|
|
@@ -22,7 +22,7 @@ except ImportError as error: # pragma: no cover - runtime dependency
|
|
|
22
22
|
|
|
23
23
|
ENV_FILE = os.environ.get("ENV_FILE", "/etc/default/uc-go-peer")
|
|
24
24
|
DESCRIBE_SCRIPT = os.environ.get("DESCRIBE_SCRIPT", "/usr/local/sbin/uc-go-peer-describe.py")
|
|
25
|
-
DEFAULT_API_HOST = os.environ.get("ALEPH_BOOTSTRAP_API_HOST", "https://
|
|
25
|
+
DEFAULT_API_HOST = os.environ.get("ALEPH_BOOTSTRAP_API_HOST", "https://api.aleph.im")
|
|
26
26
|
DEFAULT_CHANNEL = os.environ.get("ALEPH_BOOTSTRAP_CHANNEL", "simple-todo")
|
|
27
27
|
DEFAULT_REF = os.environ.get("ALEPH_BOOTSTRAP_REF", "simple-todo-bootstrap")
|
|
28
28
|
DEFAULT_POST_TYPE = os.environ.get("ALEPH_BOOTSTRAP_POST_TYPE", "relay-bootstrap")
|
|
@@ -24,7 +24,7 @@ BOOTSTRAP_SERVICE = os.environ.get("BOOTSTRAP_SERVICE", "uc-go-peer-bootstrap.se
|
|
|
24
24
|
METADATA_FILE = os.environ.get("METADATA_FILE", "/run/uc-go-peer-setup-metadata.json")
|
|
25
25
|
METADATA_ERROR_FILE = os.environ.get("METADATA_ERROR_FILE", "/run/uc-go-peer-setup-metadata.error")
|
|
26
26
|
AUTHORIZED_KEYS_FILE = os.environ.get("AUTHORIZED_KEYS_FILE", "/root/.ssh/authorized_keys")
|
|
27
|
-
ALEPH_API_HOST = os.environ.get("ALEPH_API_HOST", "https://
|
|
27
|
+
ALEPH_API_HOST = os.environ.get("ALEPH_API_HOST", "https://api.aleph.im")
|
|
28
28
|
BOOTSTRAP_CONFIG_KEY = os.environ.get("BOOTSTRAP_CONFIG_KEY", "vm-bootstrap-config")
|
|
29
29
|
BOOTSTRAP_CONFIG_POLL_SECONDS = float(os.environ.get("BOOTSTRAP_CONFIG_POLL_SECONDS", "5"))
|
|
30
30
|
BOOTSTRAP_CONFIG_SIGNAL_REF = os.environ.get("BOOTSTRAP_CONFIG_SIGNAL_REF", "vm-bootstrap-config")
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import argparse
|
|
6
|
-
import json
|
|
7
|
-
import shlex
|
|
8
|
-
import sys
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def shell_assign(name: str, value: str) -> str:
|
|
13
|
-
return f"{name}={shlex.quote(value)}"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def main() -> int:
|
|
17
|
-
parser = argparse.ArgumentParser(description="Read a relay rootfs contract and emit shell exports.")
|
|
18
|
-
parser.add_argument("contract", type=Path, help="Path to the rootfs contract JSON file")
|
|
19
|
-
args = parser.parse_args()
|
|
20
|
-
|
|
21
|
-
try:
|
|
22
|
-
payload = json.loads(args.contract.read_text())
|
|
23
|
-
except FileNotFoundError:
|
|
24
|
-
print(f"Missing rootfs contract: {args.contract}", file=sys.stderr)
|
|
25
|
-
return 1
|
|
26
|
-
except json.JSONDecodeError as exc:
|
|
27
|
-
print(f"Invalid rootfs contract JSON in {args.contract}: {exc}", file=sys.stderr)
|
|
28
|
-
return 1
|
|
29
|
-
|
|
30
|
-
rootfs = payload.get("rootfs") or {}
|
|
31
|
-
services = payload.get("services") or {}
|
|
32
|
-
source = payload.get("source") or {}
|
|
33
|
-
manifest = payload.get("manifest") or {}
|
|
34
|
-
ports = payload.get("ports") or []
|
|
35
|
-
|
|
36
|
-
profile = rootfs.get("profile")
|
|
37
|
-
install_mode = rootfs.get("installMode")
|
|
38
|
-
|
|
39
|
-
if not isinstance(profile, str) or not profile.strip():
|
|
40
|
-
print("Rootfs contract is missing rootfs.profile", file=sys.stderr)
|
|
41
|
-
return 1
|
|
42
|
-
if not isinstance(install_mode, str) or not install_mode.strip():
|
|
43
|
-
print("Rootfs contract is missing rootfs.installMode", file=sys.stderr)
|
|
44
|
-
return 1
|
|
45
|
-
if not isinstance(ports, list):
|
|
46
|
-
print("Rootfs contract field ports must be a list", file=sys.stderr)
|
|
47
|
-
return 1
|
|
48
|
-
|
|
49
|
-
lines = [
|
|
50
|
-
shell_assign("ROOTFS_CONTRACT_PATH", str(args.contract.resolve())),
|
|
51
|
-
shell_assign("ROOTFS_CONTRACT_ID", str(payload.get("id", ""))),
|
|
52
|
-
shell_assign("ROOTFS_CONTRACT_PROFILE", profile.strip()),
|
|
53
|
-
shell_assign("ROOTFS_CONTRACT_INSTALL_MODE", install_mode.strip()),
|
|
54
|
-
shell_assign("ROOTFS_CONTRACT_SOURCE_SUBDIRECTORY", str(source.get("subdirectory", ""))),
|
|
55
|
-
shell_assign("ROOTFS_CONTRACT_INSTALL_DIR", str(rootfs.get("installDir", ""))),
|
|
56
|
-
shell_assign("ROOTFS_CONTRACT_BINARY_PATH", str(rootfs.get("binaryPath", "/usr/local/bin/universal-chat-go"))),
|
|
57
|
-
shell_assign("ROOTFS_CONTRACT_DATA_DIR", str(rootfs.get("dataDir", ""))),
|
|
58
|
-
shell_assign("ROOTFS_CONTRACT_ENV_FILE", str(rootfs.get("envFile", ""))),
|
|
59
|
-
shell_assign("ROOTFS_CONTRACT_MAIN_SERVICE", str(services.get("main", ""))),
|
|
60
|
-
shell_assign("ROOTFS_CONTRACT_BOOTSTRAP_SERVICE", str(services.get("bootstrap", ""))),
|
|
61
|
-
shell_assign("ROOTFS_CONTRACT_AUTOTLS_SERVICE", str(services.get("autotlsRefresh", ""))),
|
|
62
|
-
shell_assign("ROOTFS_CONTRACT_MANIFEST_COPY_TARGET", str(manifest.get("copyTarget", ""))),
|
|
63
|
-
shell_assign("ROOTFS_CONTRACT_MANIFEST_NOTES", str(manifest.get("notes", ""))),
|
|
64
|
-
shell_assign("ROOTFS_CONTRACT_PORT_FORWARDS_JSON", json.dumps(ports, separators=(",", ":"))),
|
|
65
|
-
]
|
|
66
|
-
|
|
67
|
-
print("\n".join(lines))
|
|
68
|
-
return 0
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if __name__ == "__main__":
|
|
72
|
-
raise SystemExit(main())
|