Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
pull_request:
branches:
- main
push:
branches:
- 'release/v*'

jobs:
lint:
Expand Down Expand Up @@ -87,3 +90,22 @@ jobs:

- name: Build Docker image
run: docker build -f Dockerfile .

sv2-app-config:
name: SV2 Apps Config Compatibility
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Smoke-test generated configs against selected sv2-apps images
run: npm run check:sv2-app-config
16 changes: 16 additions & 0 deletions .github/workflows/docker-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}

# On a release event this checks the tagged release commit, which comes
# from the release/vX branch and contains its pinned sv2-apps images.
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Smoke-test generated configs against selected sv2-apps images
run: npm run check:sv2-app-config

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ FROM node:24-alpine AS builder

WORKDIR /app

# Copy package files
# Copy workspace package manifests so dependency installs are cached correctly
COPY package.json package-lock.json ./
COPY server/package.json server/
COPY shared/package.json shared/

# Install all dependencies
RUN npm ci
Expand Down
8 changes: 5 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"lint": "eslint . --max-warnings 0",
"test": "node --test --import tsx src/**/*.test.ts",
"test:server": "npm run test --prefix server",
"check:sv2-app-config": "node --import tsx server/src/sv2-app-config-compatibility.ts",
"test:all": "npm run test && npm run test:server",
"typecheck": "tsc --noEmit",
"generate:types": "orval"
Expand All @@ -29,7 +30,6 @@
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-query": "^5.62.0",
"@sv2-ui/shared": "^0.1.0",
"bitcoinjs-lib": "^7.0.1",
"bs58check": "^4.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand All @@ -38,7 +38,6 @@
"react-dom": "^18.3.1",
"recharts": "^2.14.1",
"tailwind-merge": "^2.6.0",
"tiny-secp256k1": "^2.2.4",
"wouter": "^3.3.5"
},
"devDependencies": {
Expand Down
29 changes: 29 additions & 0 deletions server/src/atomic-write.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { randomUUID } from 'node:crypto';
import fs from 'node:fs/promises';
import path from 'node:path';

/**
* Write a file using a same-directory temporary file and rename. A reader
* therefore sees either the previous complete file or the new complete file,
* never a partially-written one.
*/
export async function writeFileAtomically(filePath: string, contents: string): Promise<void> {
const temporaryPath = path.join(
path.dirname(filePath),
`.${path.basename(filePath)}.${randomUUID()}.tmp`,
);

let handle: fs.FileHandle | null = null;
try {
handle = await fs.open(temporaryPath, 'w');
await handle.writeFile(contents, 'utf8');
await handle.sync();
await handle.close();
handle = null;

await fs.rename(temporaryPath, filePath);
} finally {
await handle?.close();
await fs.rm(temporaryPath, { force: true });
}
}
7 changes: 7 additions & 0 deletions server/src/config-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ export function normalizeSetupData(data: SetupData): SetupData {
export function generateTranslatorConfig(data: SetupData): string {
const normalizedData = normalizeSetupData(data);
const { pool, translator, mode, jdc } = normalizedData;
if (normalizedData.miningMode !== 'solo' && normalizedData.miningMode !== 'pool') {
throw new Error('Mining mode is required');
}
if (mode !== 'jd' && mode !== 'no-jd') {
throw new Error('Template mode is required');
}

const isJdMode = mode === 'jd';
const isSovereignSolo = normalizedData.miningMode === 'solo' && isJdMode;
const isSoloPool = normalizedData.miningMode === 'solo' && mode === 'no-jd';
Expand Down
9 changes: 7 additions & 2 deletions server/src/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,8 +683,8 @@ async function connectSv2UiToNetwork(): Promise<void> {
}

/**
* Pull the latest version of an image from Docker Hub.
* Only pulls if the image doesn't exist locally.
* Ensure the selected image exists locally. This avoids registry checks on
* every start/retry once the image has already been pulled.
*/
async function pullImage(imageName: string): Promise<void> {
try {
Expand Down Expand Up @@ -881,6 +881,11 @@ export async function startStack(
await startJdc(`${configDir}/jdc.toml`, socketPath, data.bitcoin.network, imageSelection.jdc);
console.log('Waiting for JDC to initialize...');
await new Promise(resolve => setTimeout(resolve, 3000));

const jdcStatus = await getContainerStatus(JDC_CONTAINER);
if (!jdcStatus || jdcStatus.status === 'stopped') {
throw new Error('Mining could not start. Review your setup and try again; check the logs if the problem continues.');
}
}

// Start Translator
Expand Down
Loading
Loading