mirror of
https://github.com/strapi/strapi.git
synced 2026-06-02 16:27:47 +00:00
Add hc-m2m fixture checks to validate-migration.js (counts, draft/publish pairing, API relation parity for v4-origin runs). Harden morph and nested component validation. Prefer Docker for compose on GitHub Actions and set STRAPI_BENCH_RUNTIME on migration_v5 so postgres/mysql/mariadb matrix legs start containers reliably. Co-authored-by: Cursor <cursoragent@cursor.com>
154 lines
4.7 KiB
JavaScript
154 lines
4.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/* eslint-disable no-console */
|
|
|
|
/**
|
|
* Runtime auto-detection for docker/podman + compose CLIs.
|
|
*
|
|
* Callers should not know whether the user has docker or podman installed;
|
|
* they just call runCompose(args) / runContainer(args) and this module picks
|
|
* the right binary. Preference order:
|
|
*
|
|
* Compose: podman compose > podman-compose > docker compose > docker-compose
|
|
* Container: podman > docker
|
|
*
|
|
* Override via env var STRAPI_BENCH_RUNTIME=podman|docker if auto-detection
|
|
* picks the wrong one on a mixed-install system.
|
|
*/
|
|
|
|
const { execFileSync, spawnSync } = require('child_process');
|
|
|
|
function probe(exe, args = ['--version']) {
|
|
const result = spawnSync(exe, args, { stdio: 'ignore' });
|
|
return result.status === 0;
|
|
}
|
|
|
|
function probeSubcommand(exe, sub) {
|
|
// Some binaries (podman/docker) have their compose functionality as a subcommand.
|
|
// `<exe> compose version` exits 0 when the subcommand is available, nonzero otherwise.
|
|
const result = spawnSync(exe, [sub, 'version'], { stdio: 'ignore' });
|
|
return result.status === 0;
|
|
}
|
|
|
|
let detectedCompose = null;
|
|
let detectedContainer = null;
|
|
|
|
function detectCompose() {
|
|
if (detectedCompose) return detectedCompose;
|
|
|
|
const override = process.env.STRAPI_BENCH_RUNTIME;
|
|
const tryOrder = [];
|
|
|
|
if (override === 'podman') {
|
|
tryOrder.push(['podman', ['compose'], 'podman compose']);
|
|
tryOrder.push(['podman-compose', [], 'podman-compose']);
|
|
} else if (override === 'docker') {
|
|
tryOrder.push(['docker', ['compose'], 'docker compose']);
|
|
tryOrder.push(['docker-compose', [], 'docker-compose']);
|
|
} else if (process.env.GITHUB_ACTIONS === 'true') {
|
|
// GitHub Actions: use Docker (daemon is available; podman is often installed but not running).
|
|
tryOrder.push(['docker', ['compose'], 'docker compose']);
|
|
tryOrder.push(['docker-compose', [], 'docker-compose']);
|
|
tryOrder.push(['podman', ['compose'], 'podman compose']);
|
|
tryOrder.push(['podman-compose', [], 'podman-compose']);
|
|
} else {
|
|
// Auto: prefer podman first (user preference)
|
|
tryOrder.push(['podman', ['compose'], 'podman compose']);
|
|
tryOrder.push(['podman-compose', [], 'podman-compose']);
|
|
tryOrder.push(['docker', ['compose'], 'docker compose']);
|
|
tryOrder.push(['docker-compose', [], 'docker-compose']);
|
|
}
|
|
|
|
for (const [exe, prefix, label] of tryOrder) {
|
|
const available = prefix.length > 0 ? probeSubcommand(exe, prefix[0]) : probe(exe);
|
|
if (available) {
|
|
detectedCompose = { exe, prefixArgs: prefix, label };
|
|
return detectedCompose;
|
|
}
|
|
}
|
|
|
|
throw new Error(
|
|
'No compose runtime found. Install one of: podman (with podman-compose or podman v4+ built-in compose), docker compose, or docker-compose.'
|
|
);
|
|
}
|
|
|
|
function detectContainer() {
|
|
if (detectedContainer) return detectedContainer;
|
|
|
|
const override = process.env.STRAPI_BENCH_RUNTIME;
|
|
const tryOrder =
|
|
override === 'docker'
|
|
? ['docker', 'podman']
|
|
: override === 'podman'
|
|
? ['podman', 'docker']
|
|
: process.env.GITHUB_ACTIONS === 'true'
|
|
? ['docker', 'podman']
|
|
: ['podman', 'docker'];
|
|
|
|
for (const exe of tryOrder) {
|
|
if (probe(exe)) {
|
|
detectedContainer = exe;
|
|
return detectedContainer;
|
|
}
|
|
}
|
|
|
|
throw new Error('No container runtime found. Install podman or docker.');
|
|
}
|
|
|
|
/**
|
|
* Run a compose command. Args are appended after the detected prefix.
|
|
*
|
|
* runCompose(['-f', 'docker-compose.dev.yml', 'up', '-d', 'postgres'], {cwd, env})
|
|
*
|
|
* Returns stdout (utf8). Pass opts.stdio='inherit' to stream to terminal.
|
|
*/
|
|
function runCompose(args, opts = {}) {
|
|
const { exe, prefixArgs } = detectCompose();
|
|
const finalArgs = [...prefixArgs, ...args];
|
|
return execFileSync(exe, finalArgs, {
|
|
encoding: 'utf8',
|
|
stdio: 'pipe',
|
|
...opts,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Run a container command (exec, inspect, ps, etc.) against the detected runtime.
|
|
*
|
|
* runContainer(['exec', containerId, 'pg_isready'])
|
|
* runContainer(['inspect', '--format={{.State.Running}}', containerId])
|
|
*/
|
|
function runContainer(args, opts = {}) {
|
|
const exe = detectContainer();
|
|
return execFileSync(exe, args, {
|
|
encoding: 'utf8',
|
|
stdio: 'pipe',
|
|
...opts,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Lower-level accessor — use when you need to build the command yourself
|
|
* (e.g., passing to spawnSync for silent probes).
|
|
*/
|
|
function getComposeCommand() {
|
|
return detectCompose();
|
|
}
|
|
|
|
function getContainerCommand() {
|
|
return detectContainer();
|
|
}
|
|
|
|
function describeRuntime() {
|
|
const compose = detectCompose();
|
|
const container = detectContainer();
|
|
return `compose: ${compose.label}, container: ${container}`;
|
|
}
|
|
|
|
module.exports = {
|
|
runCompose,
|
|
runContainer,
|
|
getComposeCommand,
|
|
getContainerCommand,
|
|
describeRuntime,
|
|
};
|