feat(setup-install): add new setup-install action (#237)

This commit is contained in:
Damien Retzinger
2026-05-05 16:06:59 -04:00
committed by GitHub
parent 198bc1072a
commit e31f6f656a
13 changed files with 615 additions and 7 deletions
+176
View File
@@ -0,0 +1,176 @@
import { buildInstallArgs, buildMysqlPrepArgs, Services } from './build-command';
const BASE_ARGS = [
'--base-url=http://localhost/',
'--admin-user=admin',
'--admin-password=admin123',
'--admin-email=admin@example.com',
'--admin-firstname=Admin',
'--admin-lastname=User',
'--backend-frontname=admin',
];
const MYSQL_SERVICE = {
image: 'mysql:8.4',
env: {
MYSQL_DATABASE: 'magento_integration_tests',
MYSQL_USER: 'user',
MYSQL_PASSWORD: 'password',
MYSQL_ROOT_PASSWORD: 'rootpassword',
},
ports: ['3306:3306'],
options: '--health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3',
};
const MYSQL_ARGS = [
'--db-host=127.0.0.1:3306',
'--db-name=magento_integration_tests',
'--db-user=user',
'--db-password=password',
];
const OPENSEARCH_ARGS = [
'--search-engine=opensearch',
'--opensearch-host=localhost',
'--opensearch-port=9200',
];
const RABBITMQ_ARGS = [
'--amqp-host=localhost',
'--amqp-port=5672',
'--amqp-user=guest',
'--amqp-password=guest',
];
const CACHE_ARGS = [
'--session-save=redis',
'--session-save-redis-host=localhost',
'--session-save-redis-port=6379',
'--cache-backend=redis',
'--cache-backend-redis-server=localhost',
'--cache-backend-redis-port=6379',
];
describe('buildInstallArgs', () => {
describe('base args', () => {
it('returns only base args when services is null', () => {
expect(buildInstallArgs(null)).toEqual(BASE_ARGS);
});
it('returns only base args when services is empty', () => {
expect(buildInstallArgs({})).toEqual(BASE_ARGS);
});
});
describe('mysql', () => {
it('adds db flags when mysql service is present', () => {
const services: Services = { mysql: MYSQL_SERVICE };
expect(buildInstallArgs(services)).toEqual([...BASE_ARGS, ...MYSQL_ARGS]);
});
});
describe('search engine', () => {
it('adds opensearch flags when opensearch service is present', () => {
const services: Services = { opensearch: { image: 'opensearchproject/opensearch:2.19.1' } };
expect(buildInstallArgs(services)).toEqual([...BASE_ARGS, ...OPENSEARCH_ARGS]);
});
it('adds elasticsearch7 flags for an elasticsearch 7.x image', () => {
const services: Services = { elasticsearch: { image: 'elasticsearch:7.17.0' } };
expect(buildInstallArgs(services)).toEqual([
...BASE_ARGS,
'--search-engine=elasticsearch7',
'--elasticsearch-host=localhost',
'--elasticsearch-port=9200',
]);
});
it('adds elasticsearch8 flags for an elasticsearch 8.x image', () => {
const services: Services = { elasticsearch: { image: 'elasticsearch:8.11.4' } };
expect(buildInstallArgs(services)).toEqual([
...BASE_ARGS,
'--search-engine=elasticsearch8',
'--elasticsearch-host=localhost',
'--elasticsearch-port=9200',
]);
});
it('prefers opensearch over elasticsearch when both are present', () => {
const services: Services = {
opensearch: { image: 'opensearchproject/opensearch:2.19.1' },
elasticsearch: { image: 'elasticsearch:8.11.4' },
};
const args = buildInstallArgs(services);
expect(args).toContain('--search-engine=opensearch');
expect(args.some(a => a.startsWith('--search-engine=elasticsearch'))).toBe(false);
});
});
describe('rabbitmq', () => {
it('adds amqp flags when rabbitmq service is present', () => {
const services: Services = { rabbitmq: { image: 'rabbitmq:4.0-management' } };
expect(buildInstallArgs(services)).toEqual([...BASE_ARGS, ...RABBITMQ_ARGS]);
});
});
describe('cache / session', () => {
it('adds redis cache flags when redis service is present', () => {
const services: Services = { redis: { image: 'redis:7.2' } };
expect(buildInstallArgs(services)).toEqual([...BASE_ARGS, ...CACHE_ARGS]);
});
it('adds redis cache flags when valkey service is present', () => {
const services: Services = { valkey: { image: 'valkey:8.0' } };
expect(buildInstallArgs(services)).toEqual([...BASE_ARGS, ...CACHE_ARGS]);
});
it('adds cache flags once when both valkey and redis are present', () => {
const services: Services = {
valkey: { image: 'valkey:8.0' },
redis: { image: 'redis:7.2' },
};
const args = buildInstallArgs(services);
expect(args.filter(a => a === '--session-save=redis')).toHaveLength(1);
});
});
describe('buildMysqlPrepArgs', () => {
it('uses root password and port from service config', () => {
expect(buildMysqlPrepArgs(MYSQL_SERVICE)).toEqual([
'-h127.0.0.1',
'--port=3306',
'-uroot',
'-prootpassword',
'-e', 'SET GLOBAL log_bin_trust_function_creators = 1;',
]);
});
it('falls back to default port when ports is absent', () => {
const args = buildMysqlPrepArgs({ image: 'mysql:8.4' });
expect(args).toContain('--port=3306');
});
it('falls back to default root password when env is absent', () => {
const args = buildMysqlPrepArgs({ image: 'mysql:8.4' });
expect(args).toContain('-prootpassword');
});
});
describe('all services', () => {
it('adds all flags when all services are present', () => {
const services: Services = {
mysql: MYSQL_SERVICE,
opensearch: { image: 'opensearchproject/opensearch:2.19.1' },
rabbitmq: { image: 'rabbitmq:4.0-management' },
valkey: { image: 'valkey:8.0' },
};
expect(buildInstallArgs(services)).toEqual([
...BASE_ARGS,
...MYSQL_ARGS,
...OPENSEARCH_ARGS,
...RABBITMQ_ARGS,
...CACHE_ARGS,
]);
});
});
});
+84
View File
@@ -0,0 +1,84 @@
export interface ServiceConfig {
image: string;
env?: Record<string, string>;
ports?: string[];
options?: string;
}
export interface Services {
mysql?: ServiceConfig;
opensearch?: ServiceConfig;
elasticsearch?: ServiceConfig;
rabbitmq?: ServiceConfig;
redis?: ServiceConfig;
valkey?: ServiceConfig;
}
const BASE_ARGS = [
'--base-url=http://localhost/',
'--admin-user=admin',
'--admin-password=admin123',
'--admin-email=admin@example.com',
'--admin-firstname=Admin',
'--admin-lastname=User',
'--backend-frontname=admin',
];
export const buildMysqlPrepArgs = (mysql: ServiceConfig): string[] => {
const rootPassword = mysql.env?.MYSQL_ROOT_PASSWORD ?? 'rootpassword';
const port = mysql.ports?.[0]?.split(':')[0] ?? '3306';
return ['-h127.0.0.1', `--port=${port}`, '-uroot', `-p${rootPassword}`, '-e', 'SET GLOBAL log_bin_trust_function_creators = 1;'];
};
export const buildInstallArgs = (services: Services | null): string[] => {
const args = [...BASE_ARGS];
if (!services) return args;
if (services.mysql) {
const dbPort = services.mysql.ports?.[0]?.split(':')[0] ?? '3306';
args.push(
`--db-host=127.0.0.1:${dbPort}`,
`--db-name=${services.mysql.env?.MYSQL_DATABASE ?? 'magento'}`,
`--db-user=${services.mysql.env?.MYSQL_USER ?? 'magento'}`,
`--db-password=${services.mysql.env?.MYSQL_PASSWORD ?? 'magento'}`,
);
}
if (services.opensearch) {
args.push(
'--search-engine=opensearch',
'--opensearch-host=localhost',
'--opensearch-port=9200',
);
} else if (services.elasticsearch) {
const majorVersion = services.elasticsearch.image.split(':')[1]?.split('.')[0];
args.push(
`--search-engine=elasticsearch${majorVersion}`,
'--elasticsearch-host=localhost',
'--elasticsearch-port=9200',
);
}
if (services.rabbitmq) {
args.push(
'--amqp-host=localhost',
'--amqp-port=5672',
'--amqp-user=guest',
'--amqp-password=guest',
);
}
if (services.valkey || services.redis) {
args.push(
'--session-save=redis',
'--session-save-redis-host=localhost',
'--session-save-redis-port=6379',
'--cache-backend=redis',
'--cache-backend-redis-server=localhost',
'--cache-backend-redis-port=6379',
);
}
return args;
};
+36
View File
@@ -0,0 +1,36 @@
import * as core from '@actions/core';
import * as exec from '@actions/exec';
import { buildInstallArgs, buildMysqlPrepArgs, Services } from './build-command';
export async function run(): Promise<void> {
try {
const servicesInput = core.getInput('services');
const path = core.getInput('path') || '.';
const extraArgs = core.getInput('extra_args').trim();
let services: Services | null = null;
if (servicesInput && servicesInput !== 'null') {
services = JSON.parse(servicesInput) as Services;
}
// setup:install creates MySQL triggers, which requires log_bin_trust_function_creators=1
// when binary logging is enabled.
if (services?.mysql) {
await exec.exec('mysql', buildMysqlPrepArgs(services.mysql));
}
const args = buildInstallArgs(services);
if (extraArgs) {
args.push(...extraArgs.split(/\s+/));
}
core.setOutput('command', `php bin/magento setup:install ${args.join(' ')}`);
await exec.exec('php', ['bin/magento', 'setup:install', ...args], { cwd: path });
} catch (error) {
core.setFailed((error as Error).message);
}
}
run();