fix(ci): handle non-zfs runner environments gracefully and publish to Gitea packages
CI & Test NaxOS Management API / test (push) Successful in 1m31s

This commit is contained in:
Lukas Holzner
2026-09-04 07:30:38 +02:00
parent a283feedbd
commit 4d1becd781
3 changed files with 54 additions and 16 deletions
+31 -16
View File
@@ -1,4 +1,4 @@
import { execFile } from 'child_process';
import { execFile, execFileSync } from 'child_process';
import { promisify } from 'util';
import { config } from '../config/index.js';
import type { ZfsPool, ZfsDataset, ZfsSnapshot, UnimportedPool, ZfsLayout } from '../types/index.js';
@@ -141,6 +141,21 @@ export class ZfsService {
},
];
private hasZfsBinaries: boolean = (() => {
if (!config.isLinux) return false;
try {
execFileSync('which', ['zpool'], { stdio: 'ignore' });
execFileSync('which', ['zfs'], { stdio: 'ignore' });
return true;
} catch {
return false;
}
})();
private get useMock(): boolean {
return !this.hasZfsBinaries;
}
private async runCommand(cmd: string, args: string[]): Promise<string> {
try {
const { stdout } = await execFileAsync(cmd, args);
@@ -151,7 +166,7 @@ export class ZfsService {
}
async listPools(): Promise<ZfsPool[]> {
if (!config.isLinux) {
if (this.useMock) {
return this.mockPools;
}
try {
@@ -189,7 +204,7 @@ export class ZfsService {
ashift?: number;
}): Promise<{ success: boolean; message: string }> {
const ashift = params.ashift || 12;
if (!config.isLinux) {
if (this.useMock) {
const newPool: ZfsPool = {
name: params.name,
size: '19.0T',
@@ -230,7 +245,7 @@ export class ZfsService {
}
async scanUnimportedPools(): Promise<UnimportedPool[]> {
if (!config.isLinux) {
if (this.useMock) {
return this.mockUnimportedPools;
}
try {
@@ -279,7 +294,7 @@ export class ZfsService {
altroot?: string;
noMount?: boolean;
}): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
const found = this.mockUnimportedPools.find((p) => p.name === params.poolName);
if (found) {
this.mockUnimportedPools = this.mockUnimportedPools.filter((p) => p.name !== params.poolName);
@@ -310,7 +325,7 @@ export class ZfsService {
}
async exportPool(poolName: string): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
this.mockPools = this.mockPools.filter((p) => p.name !== poolName);
return { success: true, message: `Pool '${poolName}' exported.` };
}
@@ -319,7 +334,7 @@ export class ZfsService {
}
async scrub(poolName: string, action: 'start' | 'stop' | 'pause'): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
return { success: true, message: `Scrub ${action} requested for pool '${poolName}'.` };
}
const flag = action === 'stop' ? '-s' : action === 'pause' ? '-p' : '';
@@ -331,7 +346,7 @@ export class ZfsService {
}
async trim(poolName: string): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
return { success: true, message: `TRIM initiated for pool '${poolName}'.` };
}
await this.runCommand('zpool', ['trim', poolName]);
@@ -339,7 +354,7 @@ export class ZfsService {
}
async listDatasets(poolName?: string): Promise<ZfsDataset[]> {
if (!config.isLinux) {
if (this.useMock) {
if (poolName) {
return this.mockDatasets.filter((d) => d.pool === poolName);
}
@@ -378,7 +393,7 @@ export class ZfsService {
quota?: string;
mountpoint?: string;
}): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
const pool = params.name.split('/')[0];
const newDs: ZfsDataset = {
name: params.name,
@@ -407,7 +422,7 @@ export class ZfsService {
}
async destroyDataset(name: string, recursive = false): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
this.mockDatasets = this.mockDatasets.filter((d) => d.name !== name && (!recursive || !d.name.startsWith(`${name}/`)));
return { success: true, message: `Dataset '${name}' destroyed.` };
}
@@ -419,7 +434,7 @@ export class ZfsService {
}
async setDatasetProperty(name: string, property: string, value: string): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
const ds = this.mockDatasets.find((d) => d.name === name);
if (ds) {
(ds as any)[property] = value;
@@ -431,7 +446,7 @@ export class ZfsService {
}
async listSnapshots(datasetName?: string): Promise<ZfsSnapshot[]> {
if (!config.isLinux) {
if (this.useMock) {
if (datasetName) {
return this.mockSnapshots.filter((s) => s.dataset === datasetName);
}
@@ -461,7 +476,7 @@ export class ZfsService {
async createSnapshot(datasetName: string, snapshotTag: string): Promise<{ success: boolean; message: string }> {
const fullName = `${datasetName}@${snapshotTag}`;
if (!config.isLinux) {
if (this.useMock) {
this.mockSnapshots.unshift({
name: fullName,
dataset: datasetName,
@@ -477,7 +492,7 @@ export class ZfsService {
}
async rollbackSnapshot(snapshotName: string): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
return { success: true, message: `Dataset rolled back to '${snapshotName}'.` };
}
await this.runCommand('zfs', ['rollback', '-r', snapshotName]);
@@ -485,7 +500,7 @@ export class ZfsService {
}
async destroySnapshot(snapshotName: string): Promise<{ success: boolean; message: string }> {
if (!config.isLinux) {
if (this.useMock) {
this.mockSnapshots = this.mockSnapshots.filter((s) => s.name !== snapshotName);
return { success: true, message: `Snapshot '${snapshotName}' destroyed.` };
}