Initial import
This commit is contained in:
138
etc/servers/work-server/src/services/work-server-build-service.ts
Executable file
138
etc/servers/work-server/src/services/work-server-build-service.ts
Executable file
@@ -0,0 +1,138 @@
|
||||
import fs from 'node:fs';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export type WorkServerBuildInfo = {
|
||||
version: string;
|
||||
buildId: string;
|
||||
builtAt: string;
|
||||
};
|
||||
|
||||
export type WorkServerSourceChangeInfo = {
|
||||
changedAt: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
const MODULE_DIR_PATH = path.dirname(fileURLToPath(import.meta.url));
|
||||
const WORK_SERVER_ROOT_PATH = path.resolve(MODULE_DIR_PATH, '..', '..');
|
||||
const BUILD_INFO_FILE_PATH = path.join(WORK_SERVER_ROOT_PATH, 'dist', 'build-info.json');
|
||||
const SOURCE_TARGET_PATHS = [
|
||||
path.join(WORK_SERVER_ROOT_PATH, 'src'),
|
||||
path.join(WORK_SERVER_ROOT_PATH, 'scripts'),
|
||||
path.join(WORK_SERVER_ROOT_PATH, 'package.json'),
|
||||
path.join(WORK_SERVER_ROOT_PATH, 'tsconfig.json'),
|
||||
] as const;
|
||||
|
||||
function normalizeBuildInfo(value: unknown): WorkServerBuildInfo | null {
|
||||
if (!value || typeof value !== 'object') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const candidate = value as Partial<Record<keyof WorkServerBuildInfo, unknown>>;
|
||||
|
||||
if (
|
||||
typeof candidate.version !== 'string' ||
|
||||
typeof candidate.buildId !== 'string' ||
|
||||
typeof candidate.builtAt !== 'string'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const builtAt = new Date(candidate.builtAt);
|
||||
|
||||
if (Number.isNaN(builtAt.getTime())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
version: candidate.version,
|
||||
buildId: candidate.buildId,
|
||||
builtAt: builtAt.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
function readBuildInfoFromDiskSync(filePath: string) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalizeBuildInfo(JSON.parse(fs.readFileSync(filePath, 'utf8')));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function readLatestWorkServerBuildInfo() {
|
||||
try {
|
||||
return normalizeBuildInfo(JSON.parse(await readFile(BUILD_INFO_FILE_PATH, 'utf8')));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const runtimeWorkServerBuildInfo = readBuildInfoFromDiskSync(BUILD_INFO_FILE_PATH);
|
||||
|
||||
export function getRuntimeWorkServerBuildInfo() {
|
||||
return runtimeWorkServerBuildInfo;
|
||||
}
|
||||
|
||||
async function findLatestSourceChangeInPath(targetPath: string): Promise<WorkServerSourceChangeInfo | null> {
|
||||
try {
|
||||
const stats = await fs.promises.stat(targetPath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
return {
|
||||
changedAt: stats.mtime.toISOString(),
|
||||
path: path.relative(WORK_SERVER_ROOT_PATH, targetPath) || path.basename(targetPath),
|
||||
};
|
||||
}
|
||||
|
||||
if (!stats.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const entries = await fs.promises.readdir(targetPath, { withFileTypes: true });
|
||||
let latest: WorkServerSourceChangeInfo | null = null;
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.docker') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const childPath = path.join(targetPath, entry.name);
|
||||
const childLatest = await findLatestSourceChangeInPath(childPath);
|
||||
|
||||
if (!childLatest) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!latest || childLatest.changedAt > latest.changedAt) {
|
||||
latest = childLatest;
|
||||
}
|
||||
}
|
||||
|
||||
return latest;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function readLatestWorkServerSourceChange() {
|
||||
let latest: WorkServerSourceChangeInfo | null = null;
|
||||
|
||||
for (const targetPath of SOURCE_TARGET_PATHS) {
|
||||
const candidate = await findLatestSourceChangeInPath(targetPath);
|
||||
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!latest || candidate.changedAt > latest.changedAt) {
|
||||
latest = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return latest;
|
||||
}
|
||||
Reference in New Issue
Block a user