Skip to content

Commit 1c81ce5

Browse files
authored
Merge pull request #234 from GitGab19/fix-update-errors
make generated service configs update-safe
2 parents 8dbf95a + 9d781ed commit 1c81ce5

26 files changed

Lines changed: 1557 additions & 357 deletions

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ on:
44
pull_request:
55
branches:
66
- main
7+
push:
8+
branches:
9+
- 'release/v*'
710

811
jobs:
912
lint:
@@ -87,3 +90,22 @@ jobs:
8790

8891
- name: Build Docker image
8992
run: docker build -f Dockerfile .
93+
94+
sv2-app-config:
95+
name: SV2 Apps Config Compatibility
96+
runs-on: ubuntu-latest
97+
needs: test
98+
steps:
99+
- uses: actions/checkout@v4
100+
101+
- name: Setup Node.js
102+
uses: actions/setup-node@v4
103+
with:
104+
node-version: '24'
105+
cache: 'npm'
106+
107+
- name: Install dependencies
108+
run: npm ci
109+
110+
- name: Smoke-test generated configs against selected sv2-apps images
111+
run: npm run check:sv2-app-config

.github/workflows/docker-release.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@ jobs:
1515
steps:
1616
- name: Checkout code
1717
uses: actions/checkout@v4
18+
with:
19+
ref: ${{ github.ref }}
20+
21+
# On a release event this checks the tagged release commit, which comes
22+
# from the release/vX branch and contains its pinned sv2-apps images.
23+
- name: Set up Node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: '24'
27+
cache: 'npm'
28+
29+
- name: Install dependencies
30+
run: npm ci
31+
32+
- name: Smoke-test generated configs against selected sv2-apps images
33+
run: npm run check:sv2-app-config
1834

1935
- name: Set up QEMU
2036
uses: docker/setup-qemu-action@v3

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ FROM node:24-alpine AS builder
33

44
WORKDIR /app
55

6-
# Copy package files
6+
# Copy workspace package manifests so dependency installs are cached correctly
77
COPY package.json package-lock.json ./
88
COPY server/package.json server/
9+
COPY shared/package.json shared/
910

1011
# Install all dependencies
1112
RUN npm ci

package-lock.json

Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"lint": "eslint . --max-warnings 0",
2020
"test": "node --test --import tsx src/**/*.test.ts",
2121
"test:server": "npm run test --prefix server",
22+
"check:sv2-app-config": "node --import tsx server/src/sv2-app-config-compatibility.ts",
2223
"test:all": "npm run test && npm run test:server",
2324
"typecheck": "tsc --noEmit",
2425
"generate:types": "orval"
@@ -29,7 +30,6 @@
2930
"@radix-ui/react-tooltip": "^1.2.8",
3031
"@tanstack/react-query": "^5.62.0",
3132
"@sv2-ui/shared": "^0.1.0",
32-
"bitcoinjs-lib": "^7.0.1",
3333
"bs58check": "^4.0.0",
3434
"class-variance-authority": "^0.7.1",
3535
"clsx": "^2.1.1",
@@ -38,7 +38,6 @@
3838
"react-dom": "^18.3.1",
3939
"recharts": "^2.14.1",
4040
"tailwind-merge": "^2.6.0",
41-
"tiny-secp256k1": "^2.2.4",
4241
"wouter": "^3.3.5"
4342
},
4443
"devDependencies": {

server/src/atomic-write.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { randomUUID } from 'node:crypto';
2+
import fs from 'node:fs/promises';
3+
import path from 'node:path';
4+
5+
/**
6+
* Write a file using a same-directory temporary file and rename. A reader
7+
* therefore sees either the previous complete file or the new complete file,
8+
* never a partially-written one.
9+
*/
10+
export async function writeFileAtomically(filePath: string, contents: string): Promise<void> {
11+
const temporaryPath = path.join(
12+
path.dirname(filePath),
13+
`.${path.basename(filePath)}.${randomUUID()}.tmp`,
14+
);
15+
16+
let handle: fs.FileHandle | null = null;
17+
try {
18+
handle = await fs.open(temporaryPath, 'w');
19+
await handle.writeFile(contents, 'utf8');
20+
await handle.sync();
21+
await handle.close();
22+
handle = null;
23+
24+
await fs.rename(temporaryPath, filePath);
25+
} finally {
26+
await handle?.close();
27+
await fs.rm(temporaryPath, { force: true });
28+
}
29+
}

server/src/config-generator.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ export function normalizeSetupData(data: SetupData): SetupData {
177177
export function generateTranslatorConfig(data: SetupData): string {
178178
const normalizedData = normalizeSetupData(data);
179179
const { pool, translator, mode, jdc } = normalizedData;
180+
if (normalizedData.miningMode !== 'solo' && normalizedData.miningMode !== 'pool') {
181+
throw new Error('Mining mode is required');
182+
}
183+
if (mode !== 'jd' && mode !== 'no-jd') {
184+
throw new Error('Template mode is required');
185+
}
186+
180187
const isJdMode = mode === 'jd';
181188
const isSovereignSolo = normalizedData.miningMode === 'solo' && isJdMode;
182189
const isSoloPool = normalizedData.miningMode === 'solo' && mode === 'no-jd';

server/src/docker.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,8 +683,8 @@ async function connectSv2UiToNetwork(): Promise<void> {
683683
}
684684

685685
/**
686-
* Pull the latest version of an image from Docker Hub.
687-
* Only pulls if the image doesn't exist locally.
686+
* Ensure the selected image exists locally. This avoids registry checks on
687+
* every start/retry once the image has already been pulled.
688688
*/
689689
async function pullImage(imageName: string): Promise<void> {
690690
try {
@@ -881,6 +881,11 @@ export async function startStack(
881881
await startJdc(`${configDir}/jdc.toml`, socketPath, data.bitcoin.network, imageSelection.jdc);
882882
console.log('Waiting for JDC to initialize...');
883883
await new Promise(resolve => setTimeout(resolve, 3000));
884+
885+
const jdcStatus = await getContainerStatus(JDC_CONTAINER);
886+
if (!jdcStatus || jdcStatus.status === 'stopped') {
887+
throw new Error('Mining could not start. Review your setup and try again; check the logs if the problem continues.');
888+
}
884889
}
885890

886891
// Start Translator

0 commit comments

Comments
 (0)