feat: add community publishing and news syndication
Validate and deploy production / validate (push) Successful in 23s
Validate and deploy staging / validate (push) Successful in 50s
Validate and deploy production / deploy (push) Failing after 40s
Validate and deploy staging / deploy (push) Failing after 7s

This commit is contained in:
jb
2026-08-01 21:42:09 -04:00
parent b47e2588e9
commit dd84dff055
288 changed files with 83254 additions and 38351 deletions
+67 -36
View File
@@ -5,50 +5,81 @@ import process from 'node:process';
const target = process.argv[2];
if (!['staging', 'production'].includes(target)) {
console.error('Usage: node scripts/environment-bootstrap-export.mjs <staging|production> [output.json]');
process.exit(2);
console.error(
'Usage: node scripts/environment-bootstrap-export.mjs <staging|production> [output.json]',
);
process.exit(2);
}
const outputArgument = process.argv[3];
const timestamp = new Date().toISOString().replaceAll(':', '-').replace(/\.\d{3}Z$/, 'Z');
const outputPath = resolve(outputArgument || `./data/backups/shd-${target}-bootstrap-${timestamp}.json`);
const timestamp = new Date()
.toISOString()
.replaceAll(':', '-')
.replace(/\.\d{3}Z$/, 'Z');
const outputPath = resolve(
outputArgument || `./data/backups/shd-${target}-bootstrap-${timestamp}.json`,
);
const temporaryBackupPath = `${outputPath}.database.json`;
const databaseBackup = spawnSync(process.execPath, ['scripts/db-backup.mjs', temporaryBackupPath], {
cwd: process.cwd(),
stdio: 'inherit',
env: process.env
});
const databaseBackup = spawnSync(
process.execPath,
['scripts/db-backup.mjs', temporaryBackupPath],
{
cwd: process.cwd(),
stdio: 'inherit',
env: process.env,
},
);
if (databaseBackup.error) throw databaseBackup.error;
if (databaseBackup.status !== 0) throw new Error(`Database backup failed; no ${target} bootstrap bundle was created.`);
if (databaseBackup.status !== 0)
throw new Error(
`Database backup failed; no ${target} bootstrap bundle was created.`,
);
async function collectFiles(directory, pattern) {
try {
const names = (await readdir(directory)).filter((name) => pattern.test(name)).sort();
return await Promise.all(names.map(async (name) => ({
name,
contentBase64: (await readFile(join(directory, name))).toString('base64')
})));
} catch (error) {
if (error?.code === 'ENOENT') return [];
throw error;
}
try {
const names = (await readdir(directory))
.filter((name) => pattern.test(name))
.sort();
return await Promise.all(
names.map(async (name) => ({
name,
contentBase64: (await readFile(join(directory, name))).toString(
'base64',
),
})),
);
} catch (error) {
if (error?.code === 'ENOENT') return [];
throw error;
}
}
try {
const database = JSON.parse(await readFile(temporaryBackupPath, 'utf8'));
const avatars = await collectFiles(resolve(process.env.TDR_AVATAR_STORAGE_PATH || './data/avatars'), /^[0-9a-f-]{36}\.webp$/i);
const gameIcons = await collectFiles(resolve(process.env.TDR_GAME_ICON_STORAGE_PATH || './data/game-icons'), /^[a-z_]+-[0-9a-f-]{36}\.webp$/i);
const bundle = {
format: 'shd-build-environment-bootstrap',
version: 1,
target,
createdAt: new Date().toISOString(),
database,
files: { avatars, gameIcons }
};
await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, `${JSON.stringify(bundle, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
console.log(`${target} bootstrap bundle includes ${avatars.length} avatars and ${gameIcons.length} managed image assets.`);
console.log(`Bootstrap bundle: ${outputPath}`);
const database = JSON.parse(await readFile(temporaryBackupPath, 'utf8'));
const avatars = await collectFiles(
resolve(process.env.TDR_AVATAR_STORAGE_PATH || './data/avatars'),
/^[0-9a-f-]{36}\.webp$/i,
);
const gameIcons = await collectFiles(
resolve(process.env.TDR_GAME_ICON_STORAGE_PATH || './data/game-icons'),
/^[a-z_]+-[0-9a-f-]{36}\.webp$/i,
);
const bundle = {
format: 'shd-build-environment-bootstrap',
version: 1,
target,
createdAt: new Date().toISOString(),
database,
files: { avatars, gameIcons },
};
await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, `${JSON.stringify(bundle, null, 2)}\n`, {
encoding: 'utf8',
flag: 'wx',
});
console.log(
`${target} bootstrap bundle includes ${avatars.length} avatars and ${gameIcons.length} managed image assets.`,
);
console.log(`Bootstrap bundle: ${outputPath}`);
} finally {
await rm(temporaryBackupPath, { force: true });
await rm(temporaryBackupPath, { force: true });
}