Skip to content

Commit 776ced6

Browse files
committed
fix(deployer): ship the proof-server compose file
testkit-js boots the `auto` container from `<proofServer.path>/<fileName>` and defaults that path to `process.cwd()`, so every `auto` deploy failed with `open <cwd>/proof-server.yml: no such file`. The package now ships that compose file and points testkit at it. A compose file in the cwd still wins, as the override for a different image. The tag is pinned so `auto` cannot move under a deploy. The healthcheck is disabled because the image is distroless: every probe form fails, and testkit waits on listening ports anyway.
1 parent 2170b16 commit 776ced6

6 files changed

Lines changed: 135 additions & 4 deletions

File tree

packages/deployer/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ and this package adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Initial package. `Deployer` and `runDeploy` build and submit a Compact deploy transaction from a `compact.toml` profile, resolving the artifact, constructor args, initial private state, and contract signing key, then record the result in `<deployments_dir>/<network>.json` (#86)
1414
- Wallet support: seed-file / `MN_DEPLOYER_SEED` / keystore / prefunded-local seed resolution, a per-seed on-disk sync cache under `.states/`, and a `proof_server = "auto"` mode that boots a testkit-js proof-server container for the run (#86)
1515
- Supported deploy stack is `@midnight-ntwrk/compact-runtime` 0.16.0 and `@midnight-ntwrk/ledger-v8` 8.1.2; artifacts must be compiled with `compact compile +0.31.1`. See the README's "Supported stack" section (#86)
16+
- A packaged `proof-server.yml`, pinning `midnightntwrk/proof-server:8.0.3`, is what `proof_server = "auto"` boots. `auto` no longer depends on a compose file in the working directory, though one there still wins (#165)

packages/deployer/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ signing_key_file = "./deploy/Vault.signingkey"
190190

191191
`proof_server`: a URL pins the server; `"auto"` spawns a `testcontainers`-managed proof-server container for the duration of the deploy; omitting it falls back to the env var `PROOF_SERVER_PORT` then to `http://127.0.0.1:6300`.
192192

193+
`"auto"` needs Docker and boots the `proof-server.yml` shipped in this package, which pins `midnightntwrk/proof-server:8.0.3` and publishes port 6300 on a free host port. To boot a different image, put your own `proof-server.yml` in the directory you run `compact-deploy` from; a compose file there wins over the packaged one.
194+
193195
## Keystore format
194196

195197
`compact-deploy` reads/writes a JSON keystore with the Ethereum V3 shape (scrypt + AES-128-CTR) but with `version: "midnight-1"` so other tooling does not silently mis-read it as an Ethereum key. The encrypted secret is a 32-byte Midnight wallet seed (hex).

packages/deployer/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
},
8585
"files": [
8686
"dist",
87+
"proof-server.yml",
8788
"README.md",
8889
"CHANGELOG.md",
8990
"LICENSE"

packages/deployer/proof-server.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Compose file backing `proof_server = "auto"`. Ships in the package.
2+
#
3+
# The service name, `container_name` and `TESTCONTAINERS_UID` are testkit's
4+
# contract, not free choice: it looks the container up as `proof-server_<uid>`
5+
# and reads the port back via `getMappedPort(6300)`.
6+
services:
7+
proof-server:
8+
# Pinned to a release, not `:latest`, so `auto` cannot move under a deploy.
9+
image: 'midnightntwrk/proof-server:8.0.3'
10+
container_name: 'proof-server_$TESTCONTAINERS_UID'
11+
command: ['midnight-proof-server -v']
12+
# Host port 0: Docker picks a free one, so concurrent deploys cannot collide.
13+
ports:
14+
- '0:6300'
15+
environment:
16+
RUST_BACKTRACE: 'full'

packages/deployer/src/providers/proof-server.test.ts

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,35 @@
1+
import { existsSync, mkdtempSync, writeFileSync } from 'node:fs';
2+
import { tmpdir } from 'node:os';
3+
import { join } from 'node:path';
14
import type { Logger } from 'pino';
25
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
36
import type { NetworkConfig } from '../config/schema.ts';
47
import { ConfigError } from '../errors.ts';
58

9+
/**
10+
* Stand-in for testkit's container config. Kept global and mutable like the
11+
* real one, so a start observes whatever the last write left behind.
12+
*/
13+
let containersConfig = {
14+
proofServer: {
15+
path: process.cwd(),
16+
fileName: 'proof-server.yml',
17+
container: { name: 'proof-server', port: 6300, waitStrategy: {} },
18+
},
19+
};
20+
21+
/** Directories `DynamicProofServerContainer.start` was pointed at, in order. */
22+
const composeDirs: string[] = [];
23+
624
vi.mock('@midnight-ntwrk/testkit-js', () => ({
725
DynamicProofServerContainer: {
8-
start: vi.fn(async () => ({
9-
getUrl: () => 'http://dynamic-container:6300',
10-
stop: vi.fn(async () => undefined),
11-
})),
26+
start: vi.fn(async () => {
27+
composeDirs.push(containersConfig.proofServer.path);
28+
return {
29+
getUrl: () => 'http://dynamic-container:6300',
30+
stop: vi.fn(async () => undefined),
31+
};
32+
}),
1233
},
1334
StaticProofServerContainer: vi.fn(function StaticProofServerContainer(
1435
this: { getUrl: () => string; stop: () => Promise<void> },
@@ -17,6 +38,10 @@ vi.mock('@midnight-ntwrk/testkit-js', () => ({
1738
this.getUrl = () => `http://127.0.0.1:${port}`;
1839
this.stop = vi.fn(async () => undefined);
1940
}),
41+
getContainersConfiguration: vi.fn(() => containersConfig),
42+
setContainersConfiguration: vi.fn((next: typeof containersConfig) => {
43+
containersConfig = next;
44+
}),
2045
}));
2146

2247
const { DynamicProofServerContainer, StaticProofServerContainer } =
@@ -205,3 +230,54 @@ describe('ProofServer — disposal', () => {
205230
expect(logger.warn).toHaveBeenCalled();
206231
});
207232
});
233+
234+
describe('ProofServer.start — the "auto" compose file', () => {
235+
const FILE_NAME = 'proof-server.yml';
236+
237+
const emptyDir = (): string => mkdtempSync(join(tmpdir(), 'compose-'));
238+
239+
const dirWithComposeFile = (): string => {
240+
const dir = emptyDir();
241+
writeFileSync(join(dir, FILE_NAME), 'services: {}\n');
242+
return dir;
243+
};
244+
245+
/** Starts `auto` from `cwd`; answers with the directory testkit booted from. */
246+
const composeDirFrom = async (cwd: string): Promise<string> => {
247+
vi.spyOn(process, 'cwd').mockReturnValue(cwd);
248+
composeDirs.length = 0;
249+
await ProofServer.start({
250+
network: { ...baseNetwork, proof_server: 'auto' },
251+
logger: makeLogger(),
252+
});
253+
expect(composeDirs).toHaveLength(1);
254+
return composeDirs[0];
255+
};
256+
257+
afterEach(() => {
258+
vi.mocked(process.cwd).mockRestore();
259+
});
260+
261+
// Also guards `files` in package.json: dropping the yaml there would break
262+
// `auto` for npm installs only, never in CI, which runs from the worktree.
263+
it('should boot from the packaged file when the cwd has no compose file', async () => {
264+
const dir = await composeDirFrom(emptyDir());
265+
266+
expect(existsSync(join(dir, FILE_NAME))).toBe(true);
267+
});
268+
269+
it('should prefer a compose file in the cwd over the packaged one', async () => {
270+
const cwd = dirWithComposeFile();
271+
272+
expect(await composeDirFrom(cwd)).toBe(cwd);
273+
});
274+
275+
it('should re-resolve per start rather than reuse the last path', async () => {
276+
const overriding = dirWithComposeFile();
277+
expect(await composeDirFrom(overriding)).toBe(overriding);
278+
279+
const packaged = await composeDirFrom(emptyDir());
280+
expect(packaged).not.toBe(overriding);
281+
expect(existsSync(join(packaged, FILE_NAME))).toBe(true);
282+
});
283+
});

packages/deployer/src/providers/proof-server.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import { existsSync } from 'node:fs';
2+
import { dirname, resolve } from 'node:path';
3+
import { fileURLToPath } from 'node:url';
14
import {
25
DynamicProofServerContainer,
6+
getContainersConfiguration,
37
StaticProofServerContainer,
8+
setContainersConfiguration,
49
} from '@midnight-ntwrk/testkit-js';
510
import type { Logger } from 'pino';
611
import type { NetworkConfig } from '../config/schema.ts';
@@ -13,6 +18,35 @@ export interface ProofServerOptions {
1318
logger: Logger;
1419
}
1520

21+
/** Holds the packaged `proof-server.yml`. Two levels up from `providers/` in
22+
* both `src/` and the built `dist/`. */
23+
const PACKAGE_ROOT = resolve(
24+
dirname(fileURLToPath(import.meta.url)),
25+
'..',
26+
'..',
27+
);
28+
29+
/**
30+
* Point testkit at a compose file it can read.
31+
*
32+
* testkit boots the `auto` container from `<proofServer.path>/<fileName>` and
33+
* defaults that path to `process.cwd()`. A compose file in the cwd still wins,
34+
* as the escape hatch for a different image; otherwise the packaged one. The
35+
* choice is remade on every start because testkit's config is global.
36+
*/
37+
function useComposeFile(logger: Logger): void {
38+
const current = getContainersConfiguration();
39+
const { fileName } = current.proofServer;
40+
const cwd = process.cwd();
41+
const path = existsSync(resolve(cwd, fileName)) ? cwd : PACKAGE_ROOT;
42+
43+
logger.debug(`Proof-server compose file: ${resolve(path, fileName)}`);
44+
setContainersConfiguration({
45+
...current,
46+
proofServer: { ...current.proofServer, path },
47+
});
48+
}
49+
1650
/**
1751
* Proof-server handle with a resolved URL + lifecycle. Always acquired via
1852
* {@link ProofServer.start}; {@link dispose} is a no-op for static URLs
@@ -50,6 +84,7 @@ export class ProofServer {
5084

5185
if (explicit === 'auto') {
5286
logger.info('Starting proof-server container (auto)…');
87+
useComposeFile(logger);
5388
const container = await DynamicProofServerContainer.start(
5489
logger,
5590
undefined,

0 commit comments

Comments
 (0)