Skip to content

Commit 9c1dbc7

Browse files
authored
feat(supported-version): add optional services output for each matrix entry (#206)
1 parent ab5dc9f commit 9c1dbc7

File tree

9 files changed

+489
-41
lines changed

9 files changed

+489
-41
lines changed

supported-version/action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ inputs:
2222
default: "2y"
2323
description: "The time frame (from today). Only used in `recent` kind. String that defines a time duration using a combination of years (y), months (m), and days (d). Each unit is optional and can appear in any order, separated by spaces. For example `2y 2m 2d`. "
2424

25+
include_services:
26+
required: false
27+
default: "false"
28+
description: "Whether to include a `services` key in each matrix entry with GitHub Actions service configurations."
29+
2530
outputs:
2631
matrix:
2732
description: "The Github Actions matrix of software technologies required to run Magento."

supported-version/dist/index.js

Lines changed: 32 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

supported-version/src/index.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,35 @@ import * as core from '@actions/core';
22
import { validateKind } from './kind/validate-kinds';
33
import { getMatrixForKind } from './matrix/get-matrix-for-kind';
44
import { validateProject } from "./project/validate-projects";
5+
import { buildServicesForEntry } from "./services/build-services";
56

67

78
export async function run(): Promise<void> {
8-
try {
9+
try {
910
const kind = core.getInput("kind");
1011
const customVersions = core.getInput("custom_versions");
1112
const project = core.getInput("project");
1213
const recent_time_frame = core.getInput("recent_time_frame");
13-
14+
const include_services = core.getInput("include_services") === "true";
15+
1416
validateProject(<any>project)
1517

1618
validateKind(<any>kind, customVersions ? customVersions.split(',') : undefined);
1719

18-
core.setOutput('matrix', getMatrixForKind(kind, project, customVersions, recent_time_frame));
19-
}
20+
let matrix = getMatrixForKind(kind, project, customVersions, recent_time_frame);
21+
22+
if (include_services) {
23+
matrix = {
24+
magento: matrix.magento,
25+
include: matrix.include.map((entry) => ({
26+
...entry,
27+
services: buildServicesForEntry(entry)
28+
}))
29+
};
30+
}
31+
32+
core.setOutput('matrix', matrix);
33+
}
2034
catch (error) {
2135
core.setFailed(error.message);
2236
}

supported-version/src/matrix/matrix-type.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
export interface ServiceConfig {
2+
image: string;
3+
env?: Record<string, string>;
4+
ports?: string[];
5+
options?: string;
6+
}
7+
8+
export interface Services {
9+
[serviceName: string]: ServiceConfig;
10+
}
11+
112
export interface PackageMatrixVersion {
213
magento: string,
314
php: string | number,
@@ -12,7 +23,8 @@ export interface PackageMatrixVersion {
1223
nginx: string,
1324
os: string,
1425
release: string,
15-
eol: string
26+
eol: string,
27+
services?: Services
1628
}
1729

1830
export interface GithubActionsMatrix {
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import { buildServicesForEntry } from './build-services';
2+
import { PackageMatrixVersion } from '../matrix/matrix-type';
3+
4+
const createTestEntry = (overrides: Partial<PackageMatrixVersion> = {}): PackageMatrixVersion => ({
5+
magento: 'magento/project-community-edition:2.4.7',
6+
php: '8.3',
7+
composer: '2.7.4',
8+
mysql: 'mysql:8.4',
9+
elasticsearch: 'elasticsearch:8.11.4',
10+
opensearch: 'opensearchproject/opensearch:2.19.1',
11+
rabbitmq: 'rabbitmq:4.0-management',
12+
redis: 'redis:7.2',
13+
varnish: 'varnish:7.5',
14+
valkey: 'valkey:8.0',
15+
nginx: 'nginx:1.26',
16+
os: 'ubuntu-latest',
17+
release: '2024-04-09T00:00:00+0000',
18+
eol: '2027-04-09T00:00:00+0000',
19+
...overrides
20+
});
21+
22+
describe('buildServicesForEntry', () => {
23+
describe('search engine selection', () => {
24+
it('should prefer opensearch when both are available', () => {
25+
const entry = createTestEntry();
26+
const services = buildServicesForEntry(entry);
27+
28+
expect(services.opensearch).toBeDefined();
29+
expect(services.opensearch.image).toBe('opensearchproject/opensearch:2.19.1');
30+
expect(services.elasticsearch).toBeUndefined();
31+
});
32+
33+
it('should fall back to elasticsearch when opensearch is empty', () => {
34+
const entry = createTestEntry({ opensearch: '' });
35+
const services = buildServicesForEntry(entry);
36+
37+
expect(services.elasticsearch).toBeDefined();
38+
expect(services.elasticsearch.image).toBe('elasticsearch:8.11.4');
39+
expect(services.opensearch).toBeUndefined();
40+
});
41+
42+
it('should not include search engine when neither is available', () => {
43+
const entry = createTestEntry({ opensearch: '', elasticsearch: '' });
44+
const services = buildServicesForEntry(entry);
45+
46+
expect(services.opensearch).toBeUndefined();
47+
expect(services.elasticsearch).toBeUndefined();
48+
});
49+
});
50+
51+
describe('cache selection', () => {
52+
it('should prefer valkey when both are available', () => {
53+
const entry = createTestEntry();
54+
const services = buildServicesForEntry(entry);
55+
56+
expect(services.valkey).toBeDefined();
57+
expect(services.valkey.image).toBe('valkey:8.0');
58+
expect(services.redis).toBeUndefined();
59+
});
60+
61+
it('should fall back to redis when valkey is empty', () => {
62+
const entry = createTestEntry({ valkey: '' });
63+
const services = buildServicesForEntry(entry);
64+
65+
expect(services.redis).toBeDefined();
66+
expect(services.redis.image).toBe('redis:7.2');
67+
expect(services.valkey).toBeUndefined();
68+
});
69+
70+
it('should not include cache when neither is available', () => {
71+
const entry = createTestEntry({ valkey: '', redis: '' });
72+
const services = buildServicesForEntry(entry);
73+
74+
expect(services.valkey).toBeUndefined();
75+
expect(services.redis).toBeUndefined();
76+
});
77+
});
78+
79+
describe('mysql configuration', () => {
80+
it('should include mysql when available', () => {
81+
const entry = createTestEntry();
82+
const services = buildServicesForEntry(entry);
83+
84+
expect(services.mysql).toBeDefined();
85+
expect(services.mysql.image).toBe('mysql:8.4');
86+
});
87+
88+
it('should not include mysql when empty', () => {
89+
const entry = createTestEntry({ mysql: '' });
90+
const services = buildServicesForEntry(entry);
91+
92+
expect(services.mysql).toBeUndefined();
93+
});
94+
95+
it('should include correct mysql env configuration', () => {
96+
const entry = createTestEntry();
97+
const services = buildServicesForEntry(entry);
98+
99+
expect(services.mysql.env).toEqual({
100+
MYSQL_DATABASE: 'magento_integration_tests',
101+
MYSQL_USER: 'user',
102+
MYSQL_PASSWORD: 'password',
103+
MYSQL_ROOT_PASSWORD: 'rootpassword'
104+
});
105+
});
106+
107+
it('should include correct mysql ports', () => {
108+
const entry = createTestEntry();
109+
const services = buildServicesForEntry(entry);
110+
111+
expect(services.mysql.ports).toEqual(['3306:3306']);
112+
});
113+
114+
it('should include mysql health check options', () => {
115+
const entry = createTestEntry();
116+
const services = buildServicesForEntry(entry);
117+
118+
expect(services.mysql.options).toContain('--health-cmd');
119+
expect(services.mysql.options).toContain('mysqladmin ping');
120+
});
121+
});
122+
123+
describe('rabbitmq configuration', () => {
124+
it('should include rabbitmq when available', () => {
125+
const entry = createTestEntry();
126+
const services = buildServicesForEntry(entry);
127+
128+
expect(services.rabbitmq).toBeDefined();
129+
expect(services.rabbitmq.image).toBe('rabbitmq:4.0-management');
130+
});
131+
132+
it('should not include rabbitmq when empty', () => {
133+
const entry = createTestEntry({ rabbitmq: '' });
134+
const services = buildServicesForEntry(entry);
135+
136+
expect(services.rabbitmq).toBeUndefined();
137+
});
138+
139+
it('should include correct rabbitmq env configuration', () => {
140+
const entry = createTestEntry();
141+
const services = buildServicesForEntry(entry);
142+
143+
expect(services.rabbitmq.env).toEqual({
144+
RABBITMQ_DEFAULT_USER: 'guest',
145+
RABBITMQ_DEFAULT_PASS: 'guest'
146+
});
147+
});
148+
149+
it('should include correct rabbitmq ports', () => {
150+
const entry = createTestEntry();
151+
const services = buildServicesForEntry(entry);
152+
153+
expect(services.rabbitmq.ports).toEqual(['5672:5672']);
154+
});
155+
});
156+
157+
describe('opensearch configuration', () => {
158+
it('should include correct opensearch env configuration', () => {
159+
const entry = createTestEntry();
160+
const services = buildServicesForEntry(entry);
161+
162+
expect(services.opensearch.env).toEqual({
163+
'discovery.type': 'single-node',
164+
'DISABLE_INSTALL_DEMO_CONFIG': 'true',
165+
'DISABLE_SECURITY_PLUGIN': 'true'
166+
});
167+
});
168+
169+
it('should include correct opensearch ports', () => {
170+
const entry = createTestEntry();
171+
const services = buildServicesForEntry(entry);
172+
173+
expect(services.opensearch.ports).toEqual(['9200:9200']);
174+
});
175+
176+
it('should include opensearch health check options', () => {
177+
const entry = createTestEntry();
178+
const services = buildServicesForEntry(entry);
179+
180+
expect(services.opensearch.options).toContain('--health-cmd');
181+
expect(services.opensearch.options).toContain('curl');
182+
});
183+
});
184+
185+
describe('elasticsearch configuration', () => {
186+
it('should include correct elasticsearch env configuration', () => {
187+
const entry = createTestEntry({ opensearch: '' });
188+
const services = buildServicesForEntry(entry);
189+
190+
expect(services.elasticsearch.env).toEqual({
191+
'discovery.type': 'single-node',
192+
'xpack.security.enabled': 'false',
193+
'xpack.security.http.ssl.enabled': 'false',
194+
'xpack.security.transport.ssl.enabled': 'false'
195+
});
196+
});
197+
198+
it('should include correct elasticsearch ports', () => {
199+
const entry = createTestEntry({ opensearch: '' });
200+
const services = buildServicesForEntry(entry);
201+
202+
expect(services.elasticsearch.ports).toEqual(['9200:9200']);
203+
});
204+
});
205+
206+
describe('cache configuration', () => {
207+
it('should include correct valkey ports', () => {
208+
const entry = createTestEntry();
209+
const services = buildServicesForEntry(entry);
210+
211+
expect(services.valkey.ports).toEqual(['6379:6379']);
212+
});
213+
214+
it('should include correct redis ports', () => {
215+
const entry = createTestEntry({ valkey: '' });
216+
const services = buildServicesForEntry(entry);
217+
218+
expect(services.redis.ports).toEqual(['6379:6379']);
219+
});
220+
});
221+
222+
describe('complete service output', () => {
223+
it('should build all services when all are available', () => {
224+
const entry = createTestEntry();
225+
const services = buildServicesForEntry(entry);
226+
227+
expect(Object.keys(services)).toHaveLength(4);
228+
expect(services.mysql).toBeDefined();
229+
expect(services.opensearch).toBeDefined();
230+
expect(services.rabbitmq).toBeDefined();
231+
expect(services.valkey).toBeDefined();
232+
});
233+
234+
it('should handle entry with minimal services', () => {
235+
const entry = createTestEntry({
236+
mysql: '',
237+
elasticsearch: '',
238+
opensearch: '',
239+
rabbitmq: '',
240+
redis: '',
241+
valkey: ''
242+
});
243+
const services = buildServicesForEntry(entry);
244+
245+
expect(Object.keys(services)).toHaveLength(0);
246+
});
247+
});
248+
});

0 commit comments

Comments
 (0)