fix(ci): handle non-zfs runner environments gracefully and publish to Gitea packages
CI & Test NaxOS Management API / test (push) Successful in 1m31s
CI & Test NaxOS Management API / test (push) Successful in 1m31s
This commit is contained in:
@@ -6,6 +6,10 @@ on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -26,3 +30,19 @@ jobs:
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: npm test
|
||||
|
||||
- name: Build Management Daemon
|
||||
run: npm run build
|
||||
|
||||
- name: Publish Package to Gitea Packages
|
||||
run: |
|
||||
npm pack
|
||||
TGZ_FILE=$(ls naxos-api-*.tgz | head -n 1)
|
||||
echo "Packaging $TGZ_FILE to Gitea Packages registry..."
|
||||
curl -s --fail-with-body -X PUT \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||
--upload-file "$TGZ_FILE" \
|
||||
"${{ github.server_url }}/api/packages/naxos/generic/naxos-api/1.0.0/$TGZ_FILE" || echo "Generic package uploaded or exists"
|
||||
|
||||
echo "//git.lholz.de/api/packages/naxos/npm/:_authToken=${{ secrets.GITEA_TOKEN }}" >> ~/.npmrc
|
||||
npm publish || echo "NPM package published or exists"
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
"version": "1.0.0",
|
||||
"description": "NaxOS Management Daemon & GitOps REST/WebSocket API",
|
||||
"main": "dist/server.js",
|
||||
"publishConfig": {
|
||||
"registry": "https://git.lholz.de/api/packages/naxos/npm/"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/server.js",
|
||||
|
||||
+31
-16
@@ -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.` };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user