Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/integration-README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ See the [integration.yaml](./integration.yaml)

| Input | Description | Required | Default |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------- |
| matrix | JSON string of [version matrix for Magento](./#matrix-format) | true | NULL |
| matrix | JSON string of [version matrix for Magento](./#matrix-format). Must include a `services` key (pass `include_services: true` to the supported-version action). | true | NULL |
| fail-fast | Same as Github's [fail-fast](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategyfail-fast) | false | true |
| package_name | The name of the package | true | NULL |
| source_folder | The source folder of the package | false | $GITHUB_WORKSPACE |
Expand Down
1 change: 1 addition & 0 deletions supported-version/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ See the [action.yml](./action.yml)
| project | The project to return the supported versions for. Allowed values are `mage-os` and `magento-open-source` | false | 'magento-open-source' |
| custom_versions | The versions you want to support, as a comma-separated string, i.e. 'magento/project-community-edition:2.3.7-p3, magento/project-community-edition:2.4.2-p2' | false | '' |
| recent_time_frame | The time frame (from today) used when `kind` is `recent`. Combination of years (y), months (m), and days (d), e.g. `2y 2m 2d`. | false | '2y' |
| include_services | Whether to include a `services` key in each matrix entry with GitHub Actions service container configurations for MySQL, search engine, RabbitMQ, and cache. | false | 'true' |

## Kinds
- `currently-supported` - The currently supported Magento Open Source versions by Adobe.
Expand Down
4 changes: 4 additions & 0 deletions supported-version/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ inputs:
required: false
default: "2y"
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`. "
include_services:
required: false
default: "true"
description: "Whether to include a `services` key in each matrix entry with GitHub Actions service configurations."

outputs:
matrix:
Expand Down
70 changes: 35 additions & 35 deletions supported-version/dist/index.js

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions supported-version/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as core from '@actions/core';
import { validateKind } from './kind/validate-kinds';
import { getMatrixForKind } from './matrix/get-matrix-for-kind';
import { validateProject } from "./project/validate-projects";
import { buildServicesForEntry } from "./services/build-services";


export async function run(): Promise<void> {
Expand All @@ -10,12 +11,26 @@ export async function run(): Promise<void> {
const customVersions = core.getInput("custom_versions");
const project = core.getInput("project");
const recent_time_frame = core.getInput("recent_time_frame");
const include_services = core.getInput("include_services") === "true";

validateProject(<any>project)

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

core.setOutput('matrix', getMatrixForKind(kind, project, customVersions, recent_time_frame));
let matrix = getMatrixForKind(kind, project, customVersions);

if (include_services) {
matrix = {
magento: matrix.magento,
include: matrix.include.map((entry) => ({
...entry,
services: buildServicesForEntry(entry)
}))
};
}

core.setOutput('matrix', matrix);
}
catch (error) {
core.setFailed(error.message);
Expand Down
14 changes: 13 additions & 1 deletion supported-version/src/matrix/matrix-type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
export interface ServiceConfig {
image: string;
env?: Record<string, string>;
ports?: string[];
options?: string;
}

export interface Services {
[serviceName: string]: ServiceConfig;
}

export interface PackageMatrixVersion {
magento: string,
php: string | number,
Expand All @@ -12,7 +23,8 @@ export interface PackageMatrixVersion {
nginx: string,
os: string,
release: string,
eol: string
eol: string,
services?: Services
}

export interface GithubActionsMatrix {
Expand Down
248 changes: 248 additions & 0 deletions supported-version/src/services/build-services.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import { buildServicesForEntry } from './build-services';
import { PackageMatrixVersion } from '../matrix/matrix-type';

const createTestEntry = (overrides: Partial<PackageMatrixVersion> = {}): PackageMatrixVersion => ({
magento: 'magento/project-community-edition:2.4.7',
php: '8.3',
composer: '2.7.4',
mysql: 'mysql:8.4',
elasticsearch: 'elasticsearch:8.11.4',
opensearch: 'opensearchproject/opensearch:2.19.1',
rabbitmq: 'rabbitmq:4.0-management',
redis: 'redis:7.2',
varnish: 'varnish:7.5',
valkey: 'valkey:8.0',
nginx: 'nginx:1.26',
os: 'ubuntu-latest',
release: '2024-04-09T00:00:00+0000',
eol: '2027-04-09T00:00:00+0000',
...overrides
});

describe('buildServicesForEntry', () => {
describe('search engine selection', () => {
it('should prefer opensearch when both are available', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.opensearch).toBeDefined();
expect(services.opensearch.image).toBe('opensearchproject/opensearch:2.19.1');
expect(services.elasticsearch).toBeUndefined();
});

it('should fall back to elasticsearch when opensearch is empty', () => {
const entry = createTestEntry({ opensearch: '' });
const services = buildServicesForEntry(entry);

expect(services.elasticsearch).toBeDefined();
expect(services.elasticsearch.image).toBe('elasticsearch:8.11.4');
expect(services.opensearch).toBeUndefined();
});

it('should not include search engine when neither is available', () => {
const entry = createTestEntry({ opensearch: '', elasticsearch: '' });
const services = buildServicesForEntry(entry);

expect(services.opensearch).toBeUndefined();
expect(services.elasticsearch).toBeUndefined();
});
});

describe('cache selection', () => {
it('should prefer valkey when both are available', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.valkey).toBeDefined();
expect(services.valkey.image).toBe('valkey:8.0');
expect(services.redis).toBeUndefined();
});

it('should fall back to redis when valkey is empty', () => {
const entry = createTestEntry({ valkey: '' });
const services = buildServicesForEntry(entry);

expect(services.redis).toBeDefined();
expect(services.redis.image).toBe('redis:7.2');
expect(services.valkey).toBeUndefined();
});

it('should not include cache when neither is available', () => {
const entry = createTestEntry({ valkey: '', redis: '' });
const services = buildServicesForEntry(entry);

expect(services.valkey).toBeUndefined();
expect(services.redis).toBeUndefined();
});
});

describe('mysql configuration', () => {
it('should include mysql when available', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.mysql).toBeDefined();
expect(services.mysql.image).toBe('mysql:8.4');
});

it('should not include mysql when empty', () => {
const entry = createTestEntry({ mysql: '' });
const services = buildServicesForEntry(entry);

expect(services.mysql).toBeUndefined();
});

it('should include correct mysql env configuration', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.mysql.env).toEqual({
MYSQL_DATABASE: 'magento_integration_tests',
MYSQL_USER: 'user',
MYSQL_PASSWORD: 'password',
MYSQL_ROOT_PASSWORD: 'rootpassword'
});
});

it('should include correct mysql ports', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.mysql.ports).toEqual(['3306:3306']);
});

it('should include mysql health check options', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.mysql.options).toContain('--health-cmd');
expect(services.mysql.options).toContain('mysqladmin ping');
});
});

describe('rabbitmq configuration', () => {
it('should include rabbitmq when available', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.rabbitmq).toBeDefined();
expect(services.rabbitmq.image).toBe('rabbitmq:4.0-management');
});

it('should not include rabbitmq when empty', () => {
const entry = createTestEntry({ rabbitmq: '' });
const services = buildServicesForEntry(entry);

expect(services.rabbitmq).toBeUndefined();
});

it('should include correct rabbitmq env configuration', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.rabbitmq.env).toEqual({
RABBITMQ_DEFAULT_USER: 'guest',
RABBITMQ_DEFAULT_PASS: 'guest'
});
});

it('should include correct rabbitmq ports', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.rabbitmq.ports).toEqual(['5672:5672']);
});
});

describe('opensearch configuration', () => {
it('should include correct opensearch env configuration', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.opensearch.env).toEqual({
'discovery.type': 'single-node',
'DISABLE_INSTALL_DEMO_CONFIG': 'true',
'DISABLE_SECURITY_PLUGIN': 'true'
});
});

it('should include correct opensearch ports', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.opensearch.ports).toEqual(['9200:9200']);
});

it('should include opensearch health check options', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.opensearch.options).toContain('--health-cmd');
expect(services.opensearch.options).toContain('curl');
});
});

describe('elasticsearch configuration', () => {
it('should include correct elasticsearch env configuration', () => {
const entry = createTestEntry({ opensearch: '' });
const services = buildServicesForEntry(entry);

expect(services.elasticsearch.env).toEqual({
'discovery.type': 'single-node',
'xpack.security.enabled': 'false',
'xpack.security.http.ssl.enabled': 'false',
'xpack.security.transport.ssl.enabled': 'false'
});
});

it('should include correct elasticsearch ports', () => {
const entry = createTestEntry({ opensearch: '' });
const services = buildServicesForEntry(entry);

expect(services.elasticsearch.ports).toEqual(['9200:9200']);
});
});

describe('cache configuration', () => {
it('should include correct valkey ports', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(services.valkey.ports).toEqual(['6379:6379']);
});

it('should include correct redis ports', () => {
const entry = createTestEntry({ valkey: '' });
const services = buildServicesForEntry(entry);

expect(services.redis.ports).toEqual(['6379:6379']);
});
});

describe('complete service output', () => {
it('should build all services when all are available', () => {
const entry = createTestEntry();
const services = buildServicesForEntry(entry);

expect(Object.keys(services)).toHaveLength(4);
expect(services.mysql).toBeDefined();
expect(services.opensearch).toBeDefined();
expect(services.rabbitmq).toBeDefined();
expect(services.valkey).toBeDefined();
});

it('should handle entry with minimal services', () => {
const entry = createTestEntry({
mysql: '',
elasticsearch: '',
opensearch: '',
rabbitmq: '',
redis: '',
valkey: ''
});
const services = buildServicesForEntry(entry);

expect(Object.keys(services)).toHaveLength(0);
});
});
});
Loading
Loading