Skip to content

Commit 471b123

Browse files
committed
Refactor TypeScript configuration in package.json to streamline script execution
1 parent 17a844f commit 471b123

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

.github/workflows/tests.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
unit-tests:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
- name: Use Node.js
16+
uses: actions/setup-node@v4
17+
with:
18+
node-version: '18'
19+
20+
- name: Install Yarn
21+
run: npm install -g yarn
22+
23+
- name: Install dependencies
24+
run: yarn --frozen-lockfile
25+
26+
- name: Run unit tests
27+
run: yarn test
28+
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import { execSync } from 'child_process';
2+
import fs from 'fs';
3+
import os from 'os';
4+
import path from 'path';
5+
6+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
7+
8+
function run(cmd: string, opts?: { cwd?: string }) {
9+
execSync(cmd, {
10+
stdio: 'inherit',
11+
cwd: opts?.cwd,
12+
env: process.env,
13+
timeout: 1000 * 60 * 20, // 20 minutes per command
14+
});
15+
}
16+
17+
function dockerExec(container: string, cmd: string) {
18+
execSync(`docker exec ${container} bash -lc ${JSON.stringify(cmd)}`, {
19+
stdio: 'pipe',
20+
env: process.env,
21+
timeout: 1000 * 60 * 5,
22+
});
23+
}
24+
25+
function dockerExecCapture(container: string, cmd: string) {
26+
return execSync(`docker exec ${container} bash -lc ${JSON.stringify(cmd)}`, {
27+
stdio: 'pipe',
28+
env: process.env,
29+
timeout: 1000 * 60 * 5,
30+
}).toString('utf8');
31+
}
32+
33+
const shouldRun = process.env.RUN_DOCKER_DEB_INSTALL_START_TEST === '1';
34+
const itOrSkip = shouldRun ? it : it.skip;
35+
36+
describe('Docker Debian DEB smoke', () => {
37+
jest.setTimeout(1000 * 60 * 30); // 30 minutes
38+
39+
itOrSkip('build deb, install and start sesame-daemon on Debian', async () => {
40+
const repoRoot = path.resolve(__dirname, '../..');
41+
const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
42+
43+
run('docker version');
44+
45+
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sesame-daemon-deb-test-'));
46+
const containerName = `sesame-daemon-deb-smoke-${process.pid}-${Date.now()}`;
47+
48+
try {
49+
const binPath = path.join(workDir, 'sesame-daemon-linux');
50+
51+
// Build the Linux binary (pkg) in a throwaway container.
52+
run(
53+
[
54+
'docker run --rm',
55+
`-v ${JSON.stringify(repoRoot)}:/repo`,
56+
`-v ${JSON.stringify(workDir)}:/output`,
57+
'-w /repo',
58+
'node:18-bookworm-slim bash -lc',
59+
JSON.stringify([
60+
'set -eux',
61+
'yarn install --prefer-offline --frozen-lockfile --non-interactive',
62+
'yarn run build',
63+
'npm i -g pkg',
64+
`rm -f /output/sesame-daemon-linux`,
65+
`pkg dist/main.js -o /output/sesame-daemon-linux --targets node18-linux-x64 --config package.json`,
66+
'chmod +x /output/sesame-daemon-linux',
67+
].join(' && ')),
68+
].join(' '),
69+
);
70+
71+
// Prepare deb package root.
72+
const debRoot = path.join(workDir, '.debpkg');
73+
fs.mkdirSync(path.join(debRoot, 'DEBIAN'), { recursive: true });
74+
75+
// Minimal control file (we avoid postinst/postrm because systemd is fragile in containers).
76+
const control = [
77+
'Package: sesame-daemon',
78+
`Version: ${pkg.version}`,
79+
'Section: utils',
80+
'Priority: optional',
81+
'Architecture: amd64',
82+
'Maintainer: Libertech-FR <noreply@example.com>',
83+
'Description: Sesame Daemon',
84+
'',
85+
].join('\n');
86+
fs.writeFileSync(path.join(debRoot, 'DEBIAN', 'control'), control, 'utf8');
87+
88+
fs.mkdirSync(path.join(debRoot, 'usr/bin'), { recursive: true });
89+
fs.copyFileSync(binPath, path.join(debRoot, 'usr/bin/sesame-daemon'));
90+
fs.chmodSync(path.join(debRoot, 'usr/bin/sesame-daemon'), 0o755);
91+
92+
// Data directory + backends + package.json for runtime reads.
93+
fs.mkdirSync(path.join(debRoot, 'var/lib/sesame-daemon'), { recursive: true });
94+
fs.copyFileSync(path.join(repoRoot, 'package.json'), path.join(debRoot, 'var/lib/sesame-daemon/package.json'));
95+
fs.mkdirSync(path.join(debRoot, 'var/lib/sesame-daemon/backends'), { recursive: true });
96+
for (const entry of fs.readdirSync(path.join(repoRoot, 'backends.example'))) {
97+
const from = path.join(repoRoot, 'backends.example', entry);
98+
const to = path.join(debRoot, 'var/lib/sesame-daemon/backends', entry);
99+
fs.cpSync(from, to, { recursive: true });
100+
}
101+
102+
// Service file + defaults (even if we don't use systemd in this test).
103+
fs.mkdirSync(path.join(debRoot, 'usr/share/sesame-daemon'), { recursive: true });
104+
fs.copyFileSync(
105+
path.join(repoRoot, '.debpkg/usr/share/sesame-daemon/sesame-daemon.service'),
106+
path.join(debRoot, 'usr/share/sesame-daemon/sesame-daemon.service'),
107+
);
108+
fs.mkdirSync(path.join(debRoot, 'etc/default'), { recursive: true });
109+
fs.copyFileSync(
110+
path.join(repoRoot, '.debpkg/etc/default/sesame-daemon'),
111+
path.join(debRoot, 'etc/default/sesame-daemon'),
112+
);
113+
114+
// Build the .deb inside a Debian container (ensures dpkg-deb availability).
115+
const debFileName = `sesame-daemon_${pkg.version}_amd64.deb`;
116+
run(
117+
[
118+
'docker run --rm --platform linux/amd64',
119+
`-v ${JSON.stringify(workDir)}:/work`,
120+
'-w /work',
121+
'debian:bookworm-slim bash -lc',
122+
JSON.stringify(['set -eux', 'apt-get update', 'apt-get install -y dpkg-dev', `dpkg-deb --build .debpkg ${debFileName}`].join(' && ')),
123+
].join(' '),
124+
);
125+
126+
const debPathOnHost = path.join(workDir, debFileName);
127+
if (!fs.existsSync(debPathOnHost)) {
128+
throw new Error(`.deb not found: ${debPathOnHost}`);
129+
}
130+
131+
const debPathInContainer = path.join('/tmp/payload', debFileName);
132+
133+
// Start Debian runtime container.
134+
run(
135+
[
136+
'docker run -d --rm --platform linux/amd64',
137+
'--name ' + containerName,
138+
'-v ' + JSON.stringify(workDir) + ':/tmp/payload:ro',
139+
'debian:bookworm-slim bash -lc "while true; do sleep 3600; done"',
140+
].join(' '),
141+
);
142+
143+
// Install runtime dependencies + tools.
144+
dockerExec(
145+
containerName,
146+
'apt-get update && apt-get install -y bash ca-certificates procps redis-server',
147+
);
148+
dockerExec(containerName, 'redis-server --daemonize yes');
149+
150+
// Install .deb.
151+
try {
152+
dockerExec(containerName, `dpkg -i ${debPathInContainer}`);
153+
} catch {
154+
dockerExec(containerName, 'apt-get -f install -y');
155+
dockerExec(containerName, `dpkg -i ${debPathInContainer}`);
156+
}
157+
158+
// Start the daemon directly (systemd is often unavailable in container tests).
159+
dockerExecCapture(
160+
containerName,
161+
'cd /var/lib/sesame-daemon; set -a; . /etc/default/sesame-daemon; set +a; ' +
162+
'nohup /usr/bin/sesame-daemon </dev/null > /tmp/sesame-daemon.log 2>&1 &',
163+
);
164+
165+
// Wait for process to show up and stay up briefly.
166+
let running = false;
167+
for (let i = 0; i < 30; i++) {
168+
const pids = dockerExecCapture(containerName, "pgrep -f sesame-daemon || true").trim();
169+
if (pids) {
170+
running = true;
171+
break;
172+
}
173+
// eslint-disable-next-line no-await-in-loop
174+
await sleep(1000);
175+
}
176+
177+
if (!running) {
178+
const logsHead = dockerExecCapture(containerName, "sed -n '1,400p' /tmp/sesame-daemon.log 2>/dev/null || true");
179+
const logsTail = dockerExecCapture(containerName, "tail -n 200 /tmp/sesame-daemon.log 2>/dev/null || true");
180+
const ps = dockerExecCapture(containerName, "ps -ef | grep -v grep | grep -E 'sesame-daemon|node' || true");
181+
const redisPing = dockerExecCapture(containerName, 'redis-cli ping || true');
182+
throw new Error(
183+
'sesame-daemon process is not running.' +
184+
'\nredis-cli ping: ' +
185+
redisPing +
186+
'\nps:\n' +
187+
ps +
188+
'\nlogs(head):\n' +
189+
logsHead +
190+
'\nlogs(tail):\n' +
191+
logsTail,
192+
);
193+
}
194+
} finally {
195+
try {
196+
run(`docker rm -f ${containerName}`);
197+
} catch {
198+
// ignore
199+
}
200+
}
201+
});
202+
});
203+

0 commit comments

Comments
 (0)