@arthovlabs/liber 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -268,10 +268,15 @@ function auditTerminal(entry) {
|
|
|
268
268
|
function containerPolicy() {
|
|
269
269
|
const term = policy.terminalConfig();
|
|
270
270
|
const requested = term.backend === 'docker';
|
|
271
|
+
const image = term.image || 'alpine:latest';
|
|
272
|
+
const usable = requested ? containerUsable(false, image) : { ok: false, error: null };
|
|
271
273
|
return {
|
|
272
|
-
|
|
274
|
+
// A daemon that answers but cannot start a container would make every
|
|
275
|
+
// command fail; treat "usable" as the condition, and say why otherwise.
|
|
276
|
+
on: requested && usable.ok,
|
|
273
277
|
requested,
|
|
274
|
-
|
|
278
|
+
fallback_reason: requested && !usable.ok ? (usable.error || 'container runtime not usable') : null,
|
|
279
|
+
image,
|
|
275
280
|
network: term.allow_network === true,
|
|
276
281
|
memory: term.memory || '512m',
|
|
277
282
|
pids: Number(term.pids) || 128
|
|
@@ -292,12 +297,45 @@ function containerAvailable(force = false) {
|
|
|
292
297
|
return ok;
|
|
293
298
|
}
|
|
294
299
|
|
|
300
|
+
let _runCache = null;
|
|
301
|
+
/**
|
|
302
|
+
* Can this machine actually START a container with the configured image?
|
|
303
|
+
* A reachable daemon is not the same thing: a CI runner can have the CLI and a
|
|
304
|
+
* server version and still refuse to run (no image, no permissions). Checking by
|
|
305
|
+
* running is the only honest answer, and it is cached because it costs ~1s.
|
|
306
|
+
*/
|
|
307
|
+
function containerUsable(force = false, image = 'alpine:latest') {
|
|
308
|
+
if (!force && _runCache && _runCache.image === image && Date.now() - _runCache.at < 300_000) return _runCache;
|
|
309
|
+
if (!containerAvailable(force)) {
|
|
310
|
+
_runCache = { ok: false, image, at: Date.now(), error: 'docker daemon not reachable' };
|
|
311
|
+
return _runCache;
|
|
312
|
+
}
|
|
313
|
+
let ok = false;
|
|
314
|
+
let error = null;
|
|
315
|
+
try {
|
|
316
|
+
require('child_process').execFileSync('docker', ['run', '--rm', '--network', 'none', image, 'true'],
|
|
317
|
+
{ timeout: 60000, windowsHide: true, stdio: ['ignore', 'ignore', 'pipe'] });
|
|
318
|
+
ok = true;
|
|
319
|
+
} catch (e) {
|
|
320
|
+
error = String(e.stderr || e.message || 'container run failed').slice(0, 200);
|
|
321
|
+
}
|
|
322
|
+
_runCache = { ok, image, at: Date.now(), error };
|
|
323
|
+
return _runCache;
|
|
324
|
+
}
|
|
325
|
+
|
|
295
326
|
function runContainer(command, { cwd, timeout, profile }) {
|
|
296
327
|
const { execFile } = require('child_process');
|
|
297
328
|
const mount = `${path.resolve(process.cwd())}:/work:rw`;
|
|
329
|
+
// On POSIX hosts run as the invoking user: files the container writes land
|
|
330
|
+
// owned by them instead of root, which is both safer and the only way a write
|
|
331
|
+
// into the mount works under a rootless daemon.
|
|
332
|
+
const asUser = process.platform !== 'win32' && typeof process.getuid === 'function'
|
|
333
|
+
? ['--user', `${process.getuid()}:${process.getgid()}`]
|
|
334
|
+
: [];
|
|
298
335
|
const args = [
|
|
299
336
|
'run', '--rm', '--interactive',
|
|
300
337
|
...(profile.network ? ['--network', 'bridge'] : ['--network', 'none']),
|
|
338
|
+
...asUser,
|
|
301
339
|
'--read-only',
|
|
302
340
|
'--tmpfs', '/tmp:rw,noexec,nosuid,size=64m',
|
|
303
341
|
'--memory', profile.memory,
|
|
@@ -445,5 +483,6 @@ module.exports = {
|
|
|
445
483
|
sandboxPolicy,
|
|
446
484
|
sandboxEnv,
|
|
447
485
|
containerPolicy,
|
|
448
|
-
containerAvailable
|
|
486
|
+
containerAvailable,
|
|
487
|
+
containerUsable
|
|
449
488
|
};
|
|
@@ -26,8 +26,11 @@ function isGitRepo() {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function branch() {
|
|
29
|
+
// `rev-parse --abbrev-ref HEAD` prints the literal string "HEAD" on a detached
|
|
30
|
+
// checkout (CI tag builds do this), which is not a branch name — reporting it
|
|
31
|
+
// as one made the UI show a branch called HEAD and marked nothing current.
|
|
29
32
|
const b = sh('git', ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
30
|
-
if (b && b.trim()) return b.trim();
|
|
33
|
+
if (b && b.trim() && b.trim() !== 'HEAD') return b.trim();
|
|
31
34
|
const detached = sh('git', ['rev-parse', '--short', 'HEAD']);
|
|
32
35
|
return detached ? `detached@${detached.trim()}` : null;
|
|
33
36
|
}
|
|
@@ -158,6 +161,9 @@ function gitState() {
|
|
|
158
161
|
is_git: true,
|
|
159
162
|
root: git(['rev-parse', '--show-toplevel']).out || process.cwd(),
|
|
160
163
|
branch: current,
|
|
164
|
+
// CI checks out a commit, not a branch: say so instead of pretending a
|
|
165
|
+
// branch is current and letting the UI mark nothing.
|
|
166
|
+
detached: String(current || '').startsWith('detached@'),
|
|
161
167
|
branches: list.map(name => ({ name, current: name === current })),
|
|
162
168
|
remote: remoteUrl ? { name: 'origin', url: remoteUrl } : null,
|
|
163
169
|
ahead, behind, dirty
|