合并 master 并保留外部生成 worker 模式

合入 master 的拼消消、微信能力、OpenSSL 3.2 和 SpacetimeDB 2.4.1 更新
保留外部内容生成 queue/inline、worker lease 与动态扩缩容口径
补齐拼图后台图片生成队列轮询和运行态返回恢复
同步容器、生产运维和 Hermes 共享记忆中的 worker 文档
This commit is contained in:
2026-06-09 16:55:32 +08:00
497 changed files with 66318 additions and 13329 deletions

View File

@@ -20,7 +20,7 @@ const UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD';
function usage() {
console.log(`用法:
npm run database:backup:oss -- [--data-dir <path>] [--work-dir <path>] [--bucket <bucket>] [--object-prefix <prefix>] [--keep-local]
node scripts/database-backup-to-oss.mjs [--stop-service spacetimedb.service] [--defer-upload]
node scripts/database-backup-to-oss.mjs [--stop-service spacetimedb.service] [--restart-service-after genarrative-api.service] [--defer-upload]
node scripts/database-backup-to-oss.mjs --upload-archive <path>
说明:
@@ -100,6 +100,7 @@ function parseArgs(argv) {
envFiles: [],
keepLocal: false,
stopService: '',
restartServicesAfter: [],
database: '',
dryRun: false,
deferUpload: false,
@@ -159,6 +160,9 @@ function parseArgs(argv) {
case '--stop-service':
options.stopService = readValue();
break;
case '--restart-service-after':
options.restartServicesAfter.push(readValue());
break;
case '--keep-local':
options.keepLocal = true;
break;
@@ -266,6 +270,16 @@ function startServiceIfNeeded(serviceName, wasStopped) {
runCommand('systemctl', ['start', serviceName], {stdio: 'inherit'});
}
function restartServicesAfterBackup(serviceNames) {
for (const serviceName of serviceNames) {
if (!serviceName) {
continue;
}
console.log(`[database-backup] 冷备份后重启依赖服务: ${serviceName}`);
runCommand('systemctl', ['restart', serviceName], {stdio: 'inherit'});
}
}
function createArchive({dataDir, workDir, fileName}) {
if (!existsSync(dataDir)) {
throw new Error(`数据库数据目录不存在: ${dataDir}`);
@@ -510,6 +524,13 @@ async function main() {
} finally {
startServiceIfNeeded(args.stopService || firstNonEmpty(env.GENARRATIVE_DATABASE_BACKUP_STOP_SERVICE), serviceStopped);
}
restartServicesAfterBackup([
...String(env.GENARRATIVE_DATABASE_BACKUP_RESTART_SERVICE_AFTER ?? '')
.split(',')
.map((value) => value.trim())
.filter(Boolean),
...args.restartServicesAfter,
]);
const manifestPath = `${archivePath}.manifest.json`;
writeManifest({

View File

@@ -435,14 +435,20 @@ mkdir -p "${RELEASE_DIR}"
cp "${SOURCE_DIR}/api-server" "${RELEASE_DIR}/api-server"
chmod +x "${RELEASE_DIR}/api-server"
SCRIPT_SOURCE_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)/scripts"
BACKUP_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/database-backup-to-oss.mjs"
WORKSPACE_BACKUP_SCRIPT_SOURCE="$(cd "${SCRIPT_DIR}/../.." && pwd)/scripts/database-backup-to-oss.mjs"
mkdir -p "${RELEASE_DIR}/scripts"
if [[ -f "${SCRIPT_SOURCE_DIR}/database-backup-to-oss.mjs" ]]; then
cp "${SCRIPT_SOURCE_DIR}/database-backup-to-oss.mjs" "${RELEASE_DIR}/scripts/database-backup-to-oss.mjs"
chmod 0644 "${RELEASE_DIR}/scripts/database-backup-to-oss.mjs"
else
echo "[production-api-deploy] 未找到数据库备份脚本release 目录不会包含 scripts/database-backup-to-oss.mjs" >&2
if [[ ! -f "${BACKUP_SCRIPT_SOURCE}" ]]; then
if [[ -f "${WORKSPACE_BACKUP_SCRIPT_SOURCE}" ]]; then
echo "[production-api-deploy] 发布产物缺少 scripts/database-backup-to-oss.mjs回退使用部署工作区脚本请重新触发包含该脚本的 API 构建。" >&2
BACKUP_SCRIPT_SOURCE="${WORKSPACE_BACKUP_SCRIPT_SOURCE}"
else
echo "[production-api-deploy] 缺少数据库备份脚本: ${SOURCE_DIR}/scripts/database-backup-to-oss.mjs" >&2
exit 1
fi
fi
cp "${BACKUP_SCRIPT_SOURCE}" "${RELEASE_DIR}/scripts/database-backup-to-oss.mjs"
chmod 0644 "${RELEASE_DIR}/scripts/database-backup-to-oss.mjs"
if [[ -f "${SOURCE_DIR}/release-manifest.json" ]]; then
cp "${SOURCE_DIR}/release-manifest.json" "${RELEASE_DIR}/release-manifest.api-server.json"

View File

@@ -179,6 +179,7 @@ prepare_async_backup() {
--data-dir "${SPACETIME_ROOT_DIR}" \
--database "${DATABASE}" \
--stop-service spacetimedb.service \
--restart-service-after genarrative-api.service \
--defer-upload \
--result-file "${ASYNC_BACKUP_STATUS_FILE}"
}
@@ -257,7 +258,8 @@ case "${BACKUP_MODE}" in
--env-file /etc/genarrative/api-server.env \
--data-dir "${SPACETIME_ROOT_DIR}" \
--database "${DATABASE}" \
--stop-service spacetimedb.service
--stop-service spacetimedb.service \
--restart-service-after genarrative-api.service
;;
skip)
echo "[production-stdb-publish] 已按参数跳过 publish 前数据库备份"

View File

@@ -88,6 +88,29 @@ describe('dev utils env merge', () => {
);
});
test('本地短信 smoke 可以用 mock 验证码覆盖真实短信 provider 口径', () => {
withTempEnvFiles(
{
'.env.local': [
'SMS_AUTH_ENABLED=true',
'SMS_AUTH_PROVIDER=mock',
'SMS_AUTH_MOCK_VERIFY_CODE=123456',
].join('\n'),
},
(_env, tempDir) => {
const env = mergeApiServerEnv(tempDir, {
SMS_AUTH_ENABLED: 'true',
SMS_AUTH_PROVIDER: 'aliyun',
SMS_AUTH_MOCK_VERIFY_CODE: '654321',
});
expect(env.SMS_AUTH_ENABLED).toBe('true');
expect(env.SMS_AUTH_PROVIDER).toBe('mock');
expect(env.SMS_AUTH_MOCK_VERIFY_CODE).toBe('123456');
},
);
});
test('空外层 shell 变量不会遮蔽本地私密配置', () => {
withTempEnvFiles(
{

View File

@@ -5,6 +5,7 @@ import {
existsSync,
readdirSync,
readFileSync,
realpathSync,
statSync,
watch,
writeFileSync,
@@ -407,7 +408,11 @@ function readWorkspaceSpacetimeVersion() {
if (!version) {
throw new Error('无法从 server-rs/Cargo.toml 读取 spacetimedb 版本');
}
return version;
return normalizeCargoVersionRequirement(version);
}
function normalizeCargoVersionRequirement(version) {
return version.replace(/^=/u, '');
}
function parseSpacetimeToolVersion(output) {
@@ -2046,6 +2051,36 @@ function normalizePath(path) {
return path.replace(/\\/gu, '/');
}
function normalizeDirectExecutionPath(path) {
return normalizePath(path).replace(/^\/([A-Za-z]:\/)/u, '$1');
}
function safeRealpath(pathValue) {
try {
return realpathSync(pathValue);
} catch {
return resolve(pathValue);
}
}
function isDirectModuleExecution(argv1, moduleUrl, resolvePath = safeRealpath) {
if (!argv1) {
return false;
}
try {
return (
normalizeDirectExecutionPath(resolvePath(argv1)) ===
normalizeDirectExecutionPath(resolvePath(fileURLToPath(moduleUrl)))
);
} catch {
return (
normalizeDirectExecutionPath(resolve(argv1)) ===
normalizeDirectExecutionPath(fileURLToPath(moduleUrl))
);
}
}
function buildSpacetimePublishArgs({database, server, preserveDatabase}) {
const args = [
'publish',
@@ -2098,6 +2133,8 @@ export {
createDevServerSpawnOptions,
createWatchConfigs,
isSpacetimePublishPermissionError,
isDirectModuleExecution,
normalizeCargoVersionRequirement,
parseSpacetimeToolVersion,
parseArgs,
resolveDevStackStatePath,
@@ -2129,6 +2166,6 @@ async function main() {
}
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
if (isDirectModuleExecution(process.argv[1], import.meta.url)) {
void main();
}

View File

@@ -13,7 +13,9 @@ import {
buildSpacetimePublishArgs,
createDevServerSpawnOptions,
createWatchConfigs,
isDirectModuleExecution,
isSpacetimePublishPermissionError,
normalizeCargoVersionRequirement,
parseSpacetimeToolVersion,
parseArgs,
resolveDevStackStatePath,
@@ -33,12 +35,25 @@ function workspaceSpacetimeVersionForTest() {
if (!match) {
throw new Error('无法读取测试用 SpacetimeDB 版本');
}
return match[1];
return normalizeCargoVersionRequirement(match[1]);
}
describe('dev scheduler argument routing', () => {
const linuxTest = process.platform === 'linux' ? test : test.skip;
test('Windows junction 路径下的直接执行入口也能识别为当前模块', () => {
const moduleUrl =
'file:///F:/DevWorktrees/codex/worktrees/f584/Genarrative/scripts/dev.mjs';
const argv1 =
'C:\\Users\\wuxiangwanzi\\.codex\\worktrees\\f584\\Genarrative\\scripts\\dev.mjs';
const resolvePath = (value) =>
value.startsWith('C:\\Users\\')
? 'F:\\DevWorktrees\\codex\\worktrees\\f584\\Genarrative\\scripts\\dev.mjs'
: value;
expect(isDirectModuleExecution(argv1, moduleUrl, resolvePath)).toBe(true);
});
test('完整 dev 栈覆盖前端代理到本次解析出的 api-server 地址', () => {
const {command, explicitOptions, options} = parseArgs([], {
GENARRATIVE_API_PORT: '8090',
@@ -388,20 +403,25 @@ describe('dev scheduler watch routing', () => {
});
describe('dev scheduler spacetime refresh', () => {
test('解析 Cargo 精确版本要求时用于 CLI 校验的版本号不带等号', () => {
expect(normalizeCargoVersionRequirement('=2.4.1')).toBe('2.4.1');
expect(normalizeCargoVersionRequirement('2.4.1')).toBe('2.4.1');
});
test('解析 spacetime --version 输出里的 tool version', () => {
const version = parseSpacetimeToolVersion(`
A new version of SpacetimeDB is available: v2.3.0 (current: v2.2.0)
spacetimedb tool version 2.3.0; spacetimedb-lib version 2.3.0;
A new version of SpacetimeDB is available: v2.4.1 (current: v2.4.0)
spacetimedb tool version 2.4.1; spacetimedb-lib version 2.4.1;
`);
expect(version).toBe('2.3.0');
expect(version).toBe('2.4.1');
});
test('本机 spacetime 版本和 workspace 锁定版本不一致时直接报清楚', () => {
expect(() =>
assertSpacetimeToolVersionMatchesWorkspace({
toolVersion: '2.1.0',
workspaceVersion: '2.3.0',
workspaceVersion: '2.4.1',
}),
).toThrow('procedure 返回值 BSATN 反序列化失败');
});

View File

@@ -51,11 +51,71 @@ fetch_source_branch() {
fi
echo "[jenkins-checkout-source] 尝试 Git 远端: ${remote_url:-origin}"
if [[ -z "${COMMIT_HASH}" ]]; then
git fetch --no-tags --prune --depth=1 origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
git fetch --no-tags --prune --depth=1 origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
}
is_shallow_repository() {
[[ "$(git rev-parse --is-shallow-repository 2>/dev/null || echo false)" == "true" ]]
}
resolve_requested_commit_if_on_branch() {
local requested_commit="$1"
local resolved_commit
if ! git cat-file -e "${requested_commit}^{commit}" 2>/dev/null; then
return 1
fi
resolved_commit="$(git rev-parse "${requested_commit}^{commit}")"
if ! git merge-base --is-ancestor "${resolved_commit}" "refs/remotes/origin/${SOURCE_BRANCH}" 2>/dev/null; then
return 1
fi
printf "%s\n" "${resolved_commit}"
}
resolve_requested_commit_with_deepen() {
local requested_commit="$1"
local deepen_steps_raw="${GENARRATIVE_JENKINS_CHECKOUT_DEEPEN_STEPS:-50 200 1000 5000}"
local deepen_steps=()
local deepen_depth
local resolved_commit
# 中文注释:上游构建 commit 通常就是分支 HEAD先吃浅克隆确实不是浅历史内提交时再逐步加深。
if resolved_commit="$(resolve_requested_commit_if_on_branch "${requested_commit}")"; then
printf "%s\n" "${resolved_commit}"
return 0
fi
read -r -a deepen_steps <<<"${deepen_steps_raw}"
for deepen_depth in "${deepen_steps[@]}"; do
if [[ ! "${deepen_depth}" =~ ^[0-9]+$ || "${deepen_depth}" -le 1 ]]; then
echo "[jenkins-checkout-source] 忽略无效加深深度: ${deepen_depth}" >&2
continue
fi
echo "[jenkins-checkout-source] 浅历史未命中 commit=${requested_commit},加深到 depth=${deepen_depth}" >&2
if is_shallow_repository; then
git fetch --no-tags --prune --depth="${deepen_depth}" origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
else
git fetch --no-tags --prune origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
fi
if resolved_commit="$(resolve_requested_commit_if_on_branch "${requested_commit}")"; then
printf "%s\n" "${resolved_commit}"
return 0
fi
done
if is_shallow_repository; then
echo "[jenkins-checkout-source] 逐步加深仍未命中 commit=${requested_commit},最后尝试展开完整历史" >&2
git fetch --unshallow --no-tags origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}" || \
git fetch --no-tags --prune origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
else
git fetch --no-tags --prune origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
fi
resolve_requested_commit_if_on_branch "${requested_commit}"
}
add_git_remote_candidate "${GIT_REMOTE_URL}"
@@ -80,17 +140,11 @@ else
fi
fi
if [[ -n "${COMMIT_HASH}" && "$(git rev-parse --is-shallow-repository 2>/dev/null || echo false)" == "true" ]]; then
git fetch --unshallow --no-tags origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}" || true
fi
git cat-file -e "refs/remotes/origin/${SOURCE_BRANCH}^{commit}"
if [[ -n "${COMMIT_HASH}" ]]; then
git cat-file -e "${COMMIT_HASH}^{commit}"
RESOLVED_COMMIT="$(git rev-parse "${COMMIT_HASH}^{commit}")"
if ! git merge-base --is-ancestor "${RESOLVED_COMMIT}" "refs/remotes/origin/${SOURCE_BRANCH}"; then
echo "[jenkins-checkout-source] 指定 commit 不属于 origin/${SOURCE_BRANCH}: ${RESOLVED_COMMIT}" >&2
if ! RESOLVED_COMMIT="$(resolve_requested_commit_with_deepen "${COMMIT_HASH}")"; then
echo "[jenkins-checkout-source] 指定 commit 不属于 origin/${SOURCE_BRANCH}: ${COMMIT_HASH}" >&2
exit 1
fi
else

View File

@@ -5,6 +5,10 @@ PROVISION_TOOLS_DIR="${PROVISION_TOOLS_DIR:-provision-tools}"
SPACETIME_BIN_SOURCE="${SPACETIME_BIN_SOURCE:-${PROVISION_TOOLS_DIR}/spacetime/spacetime}"
OTELCOL_BIN_SOURCE="${OTELCOL_BIN_SOURCE:-${PROVISION_TOOLS_DIR}/otelcol-contrib}"
WORKER_ENV_FILE="${WORKER_ENV_FILE:-/etc/genarrative/external-generation-worker.env}"
GENARRATIVE_OPENSSL_VERSION="${GENARRATIVE_OPENSSL_VERSION:-3.2.0}"
GENARRATIVE_OPENSSL_PREFIX="${GENARRATIVE_OPENSSL_PREFIX:-/opt/genarrative/openssl-3.2.0}"
GENARRATIVE_OPENSSL_SOURCE_URL="${GENARRATIVE_OPENSSL_SOURCE_URL:-https://github.com/openssl/openssl/releases/download/openssl-${GENARRATIVE_OPENSSL_VERSION}/openssl-${GENARRATIVE_OPENSSL_VERSION}.tar.gz}"
GENARRATIVE_OPENSSL_SOURCE_SHA256="${GENARRATIVE_OPENSSL_SOURCE_SHA256:-14c826f07c7e433706fb5c69fa9e25dab95684844b4c962a2cf1bf183eb4690e}"
require_non_root_relative_path() {
local label="$1"
@@ -28,6 +32,14 @@ require_path() {
fi
}
require_cmd() {
local name="$1"
if ! command -v "${name}" >/dev/null 2>&1; then
echo "[server-provision] 缺少命令: ${name}" >&2
exit 1
fi
}
normalize_server_aliases() {
printf "%s" "${SERVER_ALIASES:-}" | tr ',' ' ' | xargs
}
@@ -88,6 +100,113 @@ install_nginx_brotli_modules() {
fi
}
download_file() {
local url="$1"
local output="$2"
if command -v curl >/dev/null 2>&1; then
curl -fsSL --retry 3 --retry-delay 2 "${url}" -o "${output}"
elif command -v wget >/dev/null 2>&1; then
wget -O "${output}" "${url}"
else
echo "[server-provision] 需要 curl 或 wget 下载: ${url}" >&2
exit 1
fi
}
openssl_lib_dir_candidates() {
printf "%s\n" \
"${GENARRATIVE_OPENSSL_PREFIX}/lib64" \
"${GENARRATIVE_OPENSSL_PREFIX}/lib"
}
find_genarrative_openssl_lib_dir() {
local lib_dir
while IFS= read -r lib_dir; do
if [[ -f "${lib_dir}/libssl.so.3" && -f "${lib_dir}/libcrypto.so.3" ]]; then
printf "%s" "${lib_dir}"
return 0
fi
done < <(openssl_lib_dir_candidates)
return 1
}
genarrative_openssl_has_required_symbol() {
local lib_dir
lib_dir="$(find_genarrative_openssl_lib_dir 2>/dev/null || true)"
if [[ -z "${lib_dir}" ]]; then
return 1
fi
grep -a -q "OPENSSL_${GENARRATIVE_OPENSSL_VERSION}" "${lib_dir}/libssl.so.3"
}
verify_genarrative_openssl_install() {
local lib_dir
lib_dir="$(find_genarrative_openssl_lib_dir 2>/dev/null || true)"
if [[ -z "${lib_dir}" ]]; then
echo "[server-provision] OpenSSL ${GENARRATIVE_OPENSSL_VERSION} 安装后缺少 libssl.so.3/libcrypto.so.3: ${GENARRATIVE_OPENSSL_PREFIX}" >&2
exit 1
fi
if ! grep -a -q "OPENSSL_${GENARRATIVE_OPENSSL_VERSION}" "${lib_dir}/libssl.so.3"; then
echo "[server-provision] OpenSSL 动态库缺少 OPENSSL_${GENARRATIVE_OPENSSL_VERSION} 符号: ${lib_dir}/libssl.so.3" >&2
exit 1
fi
if ! env "LD_LIBRARY_PATH=${lib_dir}" "${GENARRATIVE_OPENSSL_PREFIX}/bin/openssl" version | grep -q "OpenSSL ${GENARRATIVE_OPENSSL_VERSION}"; then
echo "[server-provision] OpenSSL ${GENARRATIVE_OPENSSL_VERSION} 安装后命令验证失败: ${GENARRATIVE_OPENSSL_PREFIX}/bin/openssl" >&2
exit 1
fi
echo "[server-provision] OpenSSL ${GENARRATIVE_OPENSSL_VERSION} 已就绪: ${lib_dir}"
}
install_genarrative_openssl_runtime() {
local tmp_dir archive source_dir jobs lib_dir
echo "[server-provision] 检查 api-server/libcurl 运行时 OpenSSL ${GENARRATIVE_OPENSSL_VERSION}"
if [[ "${DRY_RUN}" == "true" ]]; then
echo "+ install OpenSSL ${GENARRATIVE_OPENSSL_VERSION} into ${GENARRATIVE_OPENSSL_PREFIX}"
echo "+ verify OPENSSL_${GENARRATIVE_OPENSSL_VERSION} symbol for api-server/libcurl"
return
fi
if genarrative_openssl_has_required_symbol; then
verify_genarrative_openssl_install
return
fi
if command -v apt-get >/dev/null 2>&1; then
run_cmd apt-get install -y build-essential ca-certificates curl perl tar
else
echo "[server-provision] 当前系统未使用 apt无法自动构建 OpenSSL ${GENARRATIVE_OPENSSL_VERSION};请手动安装到 ${GENARRATIVE_OPENSSL_PREFIX}" >&2
exit 1
fi
require_cmd sha256sum
require_cmd tar
tmp_dir="$(mktemp -d)"
archive="${tmp_dir}/openssl-${GENARRATIVE_OPENSSL_VERSION}.tar.gz"
echo "[server-provision] 下载 OpenSSL ${GENARRATIVE_OPENSSL_VERSION}: ${GENARRATIVE_OPENSSL_SOURCE_URL}"
download_file "${GENARRATIVE_OPENSSL_SOURCE_URL}" "${archive}"
printf "%s %s\n" "${GENARRATIVE_OPENSSL_SOURCE_SHA256}" "${archive}" | sha256sum -c -
tar -xzf "${archive}" -C "${tmp_dir}"
source_dir="${tmp_dir}/openssl-${GENARRATIVE_OPENSSL_VERSION}"
jobs="$(nproc 2>/dev/null || echo 2)"
(
cd "${source_dir}"
./config --prefix="${GENARRATIVE_OPENSSL_PREFIX}" --openssldir="${GENARRATIVE_OPENSSL_PREFIX}/ssl" shared
make -j "${jobs}"
make install_sw
)
rm -rf "${tmp_dir}"
lib_dir="$(find_genarrative_openssl_lib_dir 2>/dev/null || true)"
if [[ -n "${lib_dir}" ]]; then
chmod 0755 "${GENARRATIVE_OPENSSL_PREFIX}" "${lib_dir}" || true
chmod 0644 "${lib_dir}/libssl.so.3" "${lib_dir}/libcrypto.so.3" || true
fi
verify_genarrative_openssl_install
}
sync_otelcol_install() {
local target_bin="/usr/local/bin/otelcol-contrib"
local source_bin="${OTELCOL_BIN_SOURCE}"
@@ -340,6 +459,7 @@ ensure_spacetime_owner_client_token() {
echo "[server-provision] 已生成 SpacetimeDB client identity 并写入 GENARRATIVE_SPACETIME_TOKEN: ${identity_preview}..."
fi
# 中文注释:这里是 provision 内部为 spacetimedb 运行用户隔离 CLI 登录态的受控用法,不作为人工 spacetime 命令示例。
if ! login_output="$(runuser -u spacetimedb -- "${cli_path}" --root-dir "${SPACETIME_ROOT}" login --token "${token}" 2>&1)"; then
echo "[server-provision] 使用 GENARRATIVE_SPACETIME_TOKEN 登录 SpacetimeDB CLI 失败。" >&2
printf "%s\\n" "${login_output}" | sed -E "s/[A-Za-z0-9_.=-]{24,}/[REDACTED]/g" >&2
@@ -670,6 +790,7 @@ fi
run_cmd chown -R spacetimedb:spacetimedb "${SPACETIME_ROOT}"
run_cmd chown -R genarrative:genarrative /opt/genarrative /var/lib/genarrative /srv/genarrative
install_genarrative_openssl_runtime
if [[ ! -x "${SPACETIME_BIN_SOURCE}" ]]; then
echo "[server-provision] spacetime CLI 不存在或不可执行: ${SPACETIME_BIN_SOURCE}" >&2

View File

@@ -8,7 +8,7 @@ PREPARE_OTELCOL="${PREPARE_OTELCOL:-${ENABLE_OTELCOL:-true}}"
OTELCOL_DOWNLOAD_ROOT="${OTELCOL_DOWNLOAD_ROOT:-https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download}"
OTELCOL_ARCHIVE_PATH="${OTELCOL_ARCHIVE_PATH:-}"
SPACETIME_INSTALLER_URL="${SPACETIME_INSTALLER_URL:-https://install.spacetimedb.com}"
SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/latest/download}"
SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/download/v2.4.1}"
SPACETIME_TARGET_HOST="${SPACETIME_TARGET_HOST:-x86_64-unknown-linux-gnu}"
SPACETIME_ARCHIVE_PATH="${SPACETIME_ARCHIVE_PATH:-}"
SPACETIME_INSTALLER_PATH="${SPACETIME_INSTALLER_PATH:-}"