Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
111 lines
3.5 KiB
JavaScript
111 lines
3.5 KiB
JavaScript
#!/usr/bin/env node
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { spawnSync } from 'node:child_process';
|
|
|
|
const DEFAULT_METADATA_FILE = '/var/lib/alga-appliance/maintenance-metadata.json';
|
|
|
|
function nowIso() {
|
|
return new Date().toISOString();
|
|
}
|
|
|
|
function readOsRelease(file = '/etc/os-release') {
|
|
if (!fs.existsSync(file)) {
|
|
return null;
|
|
}
|
|
const content = fs.readFileSync(file, 'utf8');
|
|
const lines = content.split(/\r?\n/).filter(Boolean);
|
|
const map = {};
|
|
for (const line of lines) {
|
|
const idx = line.indexOf('=');
|
|
if (idx <= 0) continue;
|
|
const key = line.slice(0, idx);
|
|
const raw = line.slice(idx + 1).trim();
|
|
map[key] = raw.replace(/^"|"$/g, '');
|
|
}
|
|
return {
|
|
id: map.ID || null,
|
|
versionId: map.VERSION_ID || null,
|
|
prettyName: map.PRETTY_NAME || null
|
|
};
|
|
}
|
|
|
|
function commandOutput(command) {
|
|
const result = spawnSync('sh', ['-c', command], { env: process.env, encoding: 'utf8' });
|
|
if (result.status !== 0) {
|
|
return null;
|
|
}
|
|
return (result.stdout || '').trim() || null;
|
|
}
|
|
|
|
function writeSecureJsonFile(targetFile, value) {
|
|
const dir = path.dirname(targetFile);
|
|
fs.mkdirSync(dir, { recursive: true, mode: 0o750 });
|
|
fs.writeFileSync(targetFile, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
fs.chmodSync(dir, 0o750);
|
|
fs.chmodSync(targetFile, 0o600);
|
|
}
|
|
|
|
function readJsonFile(file) {
|
|
if (!fs.existsSync(file)) {
|
|
return null;
|
|
}
|
|
try {
|
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function persistMaintenanceMetadata(options = {}) {
|
|
const metadataFile = options.metadataFile || DEFAULT_METADATA_FILE;
|
|
const releaseSelectionFile = options.releaseSelectionFile || '/etc/alga-appliance/release-selection.json';
|
|
const installStateFile = options.installStateFile || '/var/lib/alga-appliance/install-state.json';
|
|
const osReleaseFile = options.osReleaseFile || '/etc/os-release';
|
|
const k3sVersionCommand = options.k3sVersionCommand || 'k3s --version | head -n1';
|
|
|
|
const existing = readJsonFile(metadataFile) || {};
|
|
const releaseSelection = readJsonFile(releaseSelectionFile) || {};
|
|
const installState = readJsonFile(installStateFile) || {};
|
|
const osInfo = readOsRelease(osReleaseFile);
|
|
const k3sVersion = commandOutput(k3sVersionCommand);
|
|
|
|
const payload = {
|
|
updatedAt: nowIso(),
|
|
host: {
|
|
os: osInfo,
|
|
updatePolicy: 'manual-support-run-v1',
|
|
cveLiabilityNote: 'Ubuntu package and k3s updates are manual in v1; managed maintenance is planned for v2.'
|
|
},
|
|
k3s: {
|
|
version: k3sVersion,
|
|
updatePolicy: 'manual-support-run-v1'
|
|
},
|
|
app: {
|
|
selectedChannel: releaseSelection.selectedChannel || releaseSelection.channel || null,
|
|
selectedReleaseVersion: releaseSelection.selectedReleaseVersion || null,
|
|
registryHost: releaseSelection.registryHost || null,
|
|
repository: releaseSelection.repository || null,
|
|
manifestDigest: releaseSelection.manifestDigest || null,
|
|
lastKnownInstallStatus: installState.status || null,
|
|
lastKnownPhase: installState.phase || null,
|
|
lastAppUpdateAt: existing.app?.lastAppUpdateAt || null
|
|
},
|
|
maintenance: {
|
|
v2Direction: [
|
|
'add managed maintenance windows',
|
|
'preflight backup/snapshot checks',
|
|
'validated k3s upgrade paths',
|
|
'maintenance history and rollback guidance'
|
|
]
|
|
}
|
|
};
|
|
|
|
if ((installState.status || '').startsWith('update-')) {
|
|
payload.app.lastAppUpdateAt = nowIso();
|
|
}
|
|
|
|
writeSecureJsonFile(metadataFile, payload);
|
|
return { ok: true, metadataFile, metadata: payload };
|
|
}
|