Compare commits

...

2 Commits

Author SHA1 Message Date
Damien Retzinger a097371e37 feat(resolve-check-config): graphql smoke test opt-out by default 2026-05-31 21:26:17 -04:00
GrayBot 5ee0768610 chore: restore internal action refs to @main (#285) 2026-05-27 16:20:58 -04:00
18 changed files with 256 additions and 71 deletions
+12 -12
View File
@@ -47,7 +47,7 @@ jobs:
outputs: outputs:
resolved: ${{ steps.resolve.outputs.resolved }} resolved: ${{ steps.resolve.outputs.resolved }}
steps: steps:
- uses: graycoreio/github-actions-magento2/resolve-check-config@v8.5.0 - uses: graycoreio/github-actions-magento2/resolve-check-config@main
id: resolve id: resolve
with: with:
kind: extension kind: extension
@@ -63,7 +63,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/setup-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/setup-magento@main
id: setup-magento id: setup-magento
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
@@ -92,7 +92,7 @@ jobs:
env: env:
COMPOSER_AUTH: ${{ secrets.composer_auth }} COMPOSER_AUTH: ${{ secrets.composer_auth }}
- uses: graycoreio/github-actions-magento2/cache-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/cache-magento@main
with: with:
composer_cache_key: ${{ inputs.composer_cache_key && format('{0} | {1}', inputs.composer_cache_key, matrix.magento) || matrix.magento }} composer_cache_key: ${{ inputs.composer_cache_key && format('{0} | {1}', inputs.composer_cache_key, matrix.magento) || matrix.magento }}
working-directory: ${{ steps.setup-magento.outputs.path }} working-directory: ${{ steps.setup-magento.outputs.path }}
@@ -134,7 +134,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/setup-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/setup-magento@main
id: setup-magento id: setup-magento
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
@@ -163,7 +163,7 @@ jobs:
env: env:
COMPOSER_AUTH: ${{ secrets.composer_auth }} COMPOSER_AUTH: ${{ secrets.composer_auth }}
- uses: graycoreio/github-actions-magento2/cache-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/cache-magento@main
with: with:
composer_cache_key: ${{ inputs.composer_cache_key && format('{0} | {1}', inputs.composer_cache_key, matrix.magento) || matrix.magento }} composer_cache_key: ${{ inputs.composer_cache_key && format('{0} | {1}', inputs.composer_cache_key, matrix.magento) || matrix.magento }}
working-directory: ${{ steps.setup-magento.outputs.path }} working-directory: ${{ steps.setup-magento.outputs.path }}
@@ -176,7 +176,7 @@ jobs:
COMPOSER_AUTH: ${{ secrets.composer_auth }} COMPOSER_AUTH: ${{ secrets.composer_auth }}
COMPOSER_MIRROR_PATH_REPOS: 1 COMPOSER_MIRROR_PATH_REPOS: 1
- uses: graycoreio/github-actions-magento2/setup-di-compile@v8.5.0 - uses: graycoreio/github-actions-magento2/setup-di-compile@main
with: with:
path: ${{ steps.setup-magento.outputs.path }} path: ${{ steps.setup-magento.outputs.path }}
@@ -185,7 +185,7 @@ jobs:
outputs: outputs:
matrix: ${{ steps.supported-version.outputs.matrix }} matrix: ${{ steps.supported-version.outputs.matrix }}
steps: steps:
- uses: graycoreio/github-actions-magento2/supported-version@v8.5.0 - uses: graycoreio/github-actions-magento2/supported-version@main
id: supported-version id: supported-version
with: with:
kind: latest kind: latest
@@ -205,11 +205,11 @@ jobs:
tools: composer:v${{ matrix.composer }} tools: composer:v${{ matrix.composer }}
coverage: none coverage: none
- uses: graycoreio/github-actions-magento2/cache-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/cache-magento@main
with: with:
composer_cache_key: ${{ inputs.composer_cache_key && format('{0} | {1}', inputs.composer_cache_key, matrix.magento) || matrix.magento }} composer_cache_key: ${{ inputs.composer_cache_key && format('{0} | {1}', inputs.composer_cache_key, matrix.magento) || matrix.magento }}
- uses: graycoreio/github-actions-magento2/coding-standard@v8.5.0 - uses: graycoreio/github-actions-magento2/coding-standard@main
with: with:
path: ${{ inputs.path }} path: ${{ inputs.path }}
composer_auth: ${{ secrets.composer_auth }} composer_auth: ${{ secrets.composer_auth }}
@@ -225,7 +225,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/setup-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/setup-magento@main
id: setup-magento id: setup-magento
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
@@ -254,7 +254,7 @@ jobs:
env: env:
COMPOSER_AUTH: ${{ secrets.composer_auth }} COMPOSER_AUTH: ${{ secrets.composer_auth }}
- uses: graycoreio/github-actions-magento2/cache-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/cache-magento@main
with: with:
composer_cache_key: ${{ inputs.composer_cache_key && format('{0} | {1}', inputs.composer_cache_key, matrix.magento) || matrix.magento }} composer_cache_key: ${{ inputs.composer_cache_key && format('{0} | {1}', inputs.composer_cache_key, matrix.magento) || matrix.magento }}
working-directory: ${{ steps.setup-magento.outputs.path }} working-directory: ${{ steps.setup-magento.outputs.path }}
@@ -267,7 +267,7 @@ jobs:
COMPOSER_AUTH: ${{ secrets.composer_auth }} COMPOSER_AUTH: ${{ secrets.composer_auth }}
COMPOSER_MIRROR_PATH_REPOS: 1 COMPOSER_MIRROR_PATH_REPOS: 1
- uses: graycoreio/github-actions-magento2/get-magento-version@v8.5.0 - uses: graycoreio/github-actions-magento2/get-magento-version@main
id: magento-version id: magento-version
with: with:
working-directory: ${{ steps.setup-magento.outputs.path }} working-directory: ${{ steps.setup-magento.outputs.path }}
+19 -14
View File
@@ -46,19 +46,19 @@ jobs:
name: ${{ inputs.store_artifact_name }} name: ${{ inputs.store_artifact_name }}
path: ${{ inputs.path }} path: ${{ inputs.path }}
- uses: graycoreio/github-actions-magento2/get-magento-version@v8.5.0 - uses: graycoreio/github-actions-magento2/get-magento-version@main
id: get-magento-version id: get-magento-version
with: with:
working-directory: ${{ inputs.path }} working-directory: ${{ inputs.path }}
- uses: graycoreio/github-actions-magento2/supported-version@v8.5.0 - uses: graycoreio/github-actions-magento2/supported-version@main
id: supported-version id: supported-version
with: with:
project: ${{ steps.get-magento-version.outputs.supported_version_project }} project: ${{ steps.get-magento-version.outputs.supported_version_project }}
kind: custom kind: custom
custom_versions: ${{ steps.get-magento-version.outputs.project }}:${{ fromJSON(steps.get-magento-version.outputs.version) }} custom_versions: ${{ steps.get-magento-version.outputs.project }}:${{ fromJSON(steps.get-magento-version.outputs.version) }}
- uses: graycoreio/github-actions-magento2/resolve-check-config@v8.5.0 - uses: graycoreio/github-actions-magento2/resolve-check-config@main
id: resolve id: resolve
with: with:
kind: store kind: store
@@ -81,7 +81,7 @@ jobs:
name: ${{ inputs.store_artifact_name }} name: ${{ inputs.store_artifact_name }}
path: ${{ inputs.path }} path: ${{ inputs.path }}
- uses: graycoreio/github-actions-magento2/setup-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/setup-magento@main
id: setup-magento id: setup-magento
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
@@ -90,7 +90,7 @@ jobs:
working-directory: ${{ inputs.path }} working-directory: ${{ inputs.path }}
composer_auth: ${{ secrets.composer_auth }} composer_auth: ${{ secrets.composer_auth }}
- uses: graycoreio/github-actions-magento2/cache-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/cache-magento@main
with: with:
composer_cache_key: ${{ inputs.composer_cache_key }} composer_cache_key: ${{ inputs.composer_cache_key }}
working-directory: ${{ steps.setup-magento.outputs.path }} working-directory: ${{ steps.setup-magento.outputs.path }}
@@ -144,7 +144,7 @@ jobs:
name: ${{ inputs.store_artifact_name }} name: ${{ inputs.store_artifact_name }}
path: ${{ inputs.path }} path: ${{ inputs.path }}
- uses: graycoreio/github-actions-magento2/setup-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/setup-magento@main
id: setup-magento id: setup-magento
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
@@ -153,7 +153,7 @@ jobs:
working-directory: ${{ inputs.path }} working-directory: ${{ inputs.path }}
composer_auth: ${{ secrets.composer_auth }} composer_auth: ${{ secrets.composer_auth }}
- uses: graycoreio/github-actions-magento2/cache-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/cache-magento@main
with: with:
composer_cache_key: ${{ inputs.composer_cache_key }} composer_cache_key: ${{ inputs.composer_cache_key }}
working-directory: ${{ steps.setup-magento.outputs.path }} working-directory: ${{ steps.setup-magento.outputs.path }}
@@ -178,7 +178,7 @@ jobs:
EOF EOF
fi fi
- uses: graycoreio/github-actions-magento2/coding-standard@v8.5.0 - uses: graycoreio/github-actions-magento2/coding-standard@main
with: with:
path: ${{ steps.setup-magento.outputs.path }} path: ${{ steps.setup-magento.outputs.path }}
composer_auth: ${{ secrets.composer_auth }} composer_auth: ${{ secrets.composer_auth }}
@@ -201,7 +201,7 @@ jobs:
name: ${{ inputs.store_artifact_name }} name: ${{ inputs.store_artifact_name }}
path: ${{ inputs.path }} path: ${{ inputs.path }}
- uses: graycoreio/github-actions-magento2/setup-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/setup-magento@main
id: setup-magento id: setup-magento
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
@@ -210,7 +210,7 @@ jobs:
working-directory: ${{ inputs.path }} working-directory: ${{ inputs.path }}
composer_auth: ${{ secrets.composer_auth }} composer_auth: ${{ secrets.composer_auth }}
- uses: graycoreio/github-actions-magento2/cache-magento@v8.5.0 - uses: graycoreio/github-actions-magento2/cache-magento@main
with: with:
composer_cache_key: ${{ inputs.composer_cache_key }} composer_cache_key: ${{ inputs.composer_cache_key }}
working-directory: ${{ steps.setup-magento.outputs.path }} working-directory: ${{ steps.setup-magento.outputs.path }}
@@ -222,7 +222,7 @@ jobs:
env: env:
COMPOSER_AUTH: ${{ secrets.composer_auth }} COMPOSER_AUTH: ${{ secrets.composer_auth }}
- uses: graycoreio/github-actions-magento2/setup-install@v8.5.0 - uses: graycoreio/github-actions-magento2/setup-install@main
id: setup-install id: setup-install
with: with:
services: ${{ toJSON(matrix.services) }} services: ${{ toJSON(matrix.services) }}
@@ -230,15 +230,20 @@ jobs:
container_id: ${{ job.services['php-fpm'].id }} container_id: ${{ job.services['php-fpm'].id }}
extra_args: --magento-init-params=MAGE_MODE=developer extra_args: --magento-init-params=MAGE_MODE=developer
- uses: graycoreio/github-actions-magento2/configure-service-nginx@v8.5.0 - uses: graycoreio/github-actions-magento2/configure-service-nginx@main
with: with:
container_id: ${{ job.services.nginx.id }} container_id: ${{ job.services.nginx.id }}
magento_path: ${{ inputs.path }} magento_path: ${{ inputs.path }}
- uses: graycoreio/github-actions-magento2/smoke-test@v8.5.0 - uses: graycoreio/github-actions-magento2/smoke-test@main
if: contains(fromJSON(needs.compute_matrix.outputs.resolved)['smoke-test'].probes, 'page')
with: with:
kind: page kind: page
- uses: graycoreio/github-actions-magento2/smoke-test@v8.5.0 ## graphql is opt-in: editions without GraphQL modules (e.g. mage-os
## minimal) have no /graphql endpoint. Enable it per store via
## `.github/check-store.json` -> jobs.smoke-test.probes: ["page", "graphql"].
- uses: graycoreio/github-actions-magento2/smoke-test@main
if: contains(fromJSON(needs.compute_matrix.outputs.resolved)['smoke-test'].probes, 'graphql')
with: with:
kind: graphql kind: graphql
+1 -1
View File
@@ -82,7 +82,7 @@ jobs:
COMPOSER_AUTH: ${{ secrets.composer_auth }} COMPOSER_AUTH: ${{ secrets.composer_auth }}
name: Create Magento ${{ matrix.magento }} Project name: Create Magento ${{ matrix.magento }} Project
- uses: graycoreio/github-actions-magento2/get-magento-version@v8.5.0 - uses: graycoreio/github-actions-magento2/get-magento-version@main
id: magento-version id: magento-version
with: with:
working-directory: ${{ inputs.magento_directory }} working-directory: ${{ inputs.magento_directory }}
+1 -1
View File
@@ -62,7 +62,7 @@ runs:
exit 1 exit 1
fi fi
- uses: graycoreio/github-actions-magento2/get-magento-version@v8.5.0 - uses: graycoreio/github-actions-magento2/get-magento-version@main
id: cache-magento-get-magento-version id: cache-magento-get-magento-version
with: with:
working-directory: ${{ inputs.working-directory }} working-directory: ${{ inputs.working-directory }}
+2 -2
View File
@@ -52,12 +52,12 @@ runs:
fi fi
- name: Get Composer Version - name: Get Composer Version
uses: graycoreio/github-actions-magento2/get-composer-version@v8.5.0 uses: graycoreio/github-actions-magento2/get-composer-version@main
id: get-composer-version id: get-composer-version
if: steps.check-installed.outputs.installed != 'true' if: steps.check-installed.outputs.installed != 'true'
- name: Check if allow-plugins option is available for this version of composer - name: Check if allow-plugins option is available for this version of composer
uses: graycoreio/github-actions-magento2/semver-compare@v8.5.0 uses: graycoreio/github-actions-magento2/semver-compare@main
id: is-allow-plugins-available id: is-allow-plugins-available
if: steps.check-installed.outputs.installed != 'true' if: steps.check-installed.outputs.installed != 'true'
with: with:
+1 -1
View File
@@ -24,7 +24,7 @@ Each check can be toggled on/off through an optional `.github/check-extension.js
You can learn more about this file here in the [`resolve-check-config` action.](../../resolve-check-config/README.md): You can learn more about this file here in the [`resolve-check-config` action.](../../resolve-check-config/README.md):
Reference the published JSON Schema with `$schema` to get autocompletion and inline validation in editors that support it: Reference the published JSON Schema with `$schema` to get autocompletion and inline validation in editors that support it — see [`check-extension.schema.json`](../../resolve-check-config/check-extension.schema.json):
```json ```json
{ {
+1 -1
View File
@@ -32,7 +32,7 @@ Each check can be toggled on/off through an optional `.github/check-store.json`
You can learn more about this file here in the [`resolve-check-config` action.](../../resolve-check-config/README.md): You can learn more about this file here in the [`resolve-check-config` action.](../../resolve-check-config/README.md):
Reference the published JSON Schema with `$schema` to get autocompletion and inline validation in editors that support it: Reference the published JSON Schema with `$schema` to get autocompletion and inline validation in editors that support it — see [`check-store.schema.json`](../../resolve-check-config/check-store.schema.json):
```json ```json
{ {
+1 -1
View File
@@ -9,7 +9,7 @@ inputs:
runs: runs:
using: "composite" using: "composite"
steps: steps:
- uses: graycoreio/github-actions-magento2/get-magento-version@v8.5.0 - uses: graycoreio/github-actions-magento2/get-magento-version@main
id: init-magento-get-magento-version id: init-magento-get-magento-version
with: with:
working-directory: ${{ inputs.magento_directory }} working-directory: ${{ inputs.magento_directory }}
+7
View File
@@ -4,6 +4,13 @@ Reads `.github/check-<kind>.json` (or a path you specify), validates job names a
A missing config file is fine — every known job is emitted with its default tier list. A missing config file is fine — every known job is emitted with its default tier list.
## Schemas
Reference the published JSON Schema from your config's `$schema` key for autocompletion and inline validation in editors that support it:
- [`check-store.schema.json`](./check-store.schema.json) — config for the [MageCheck Store](../docs/workflows/check-store.md) workflow
- [`check-extension.schema.json`](./check-extension.schema.json) — config for the [MageCheck Extension](../docs/workflows/check-extension.md) workflow
## Inputs ## Inputs
| Input | Description | Required | Default | | Input | Description | Required | Default |
+24 -1
View File
@@ -11,7 +11,7 @@
"properties": { "properties": {
"unit-test": { "$ref": "#/$defs/jobConfig" }, "unit-test": { "$ref": "#/$defs/jobConfig" },
"coding-standard": { "$ref": "#/$defs/jobConfig" }, "coding-standard": { "$ref": "#/$defs/jobConfig" },
"smoke-test": { "$ref": "#/$defs/jobConfig" } "smoke-test": { "$ref": "#/$defs/smokeJobConfig" }
}, },
"additionalProperties": false "additionalProperties": false
} }
@@ -34,6 +34,29 @@
"additionalProperties": true "additionalProperties": true
} }
] ]
},
"smokeJobConfig": {
"description": "How the smoke-test job should be configured. Boolean form is shorthand for { enabled: <bool> }; object form adds a `probes` list on top of `enabled`.",
"oneOf": [
{ "type": "boolean" },
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether the job should run. Defaults to true when the key is present.",
"default": true
},
"probes": {
"type": "array",
"description": "Which smoke-test probes to run. Defaults to [\"page\"]. Add \"graphql\" to also probe the GraphQL endpoint — only for editions that ship GraphQL modules (the mage-os minimal edition does not, so /graphql 404s there).",
"items": { "enum": ["page", "graphql"] },
"default": ["page"]
}
},
"additionalProperties": true
}
]
} }
} }
} }
+20 -20
View File
File diff suppressed because one or more lines are too long
@@ -31,6 +31,10 @@ describe('STORE_JOBS', () => {
]); ]);
}); });
it('defaults smoke-test to the page probe only (graphql is opt-in)', () => {
expect(STORE_JOBS['smoke-test'].probes).toEqual(['page']);
});
it('exposes empty service defaults for unit-test and coding-standard', () => { it('exposes empty service defaults for unit-test and coding-standard', () => {
expect(STORE_JOBS['unit-test'].services).toEqual([]); expect(STORE_JOBS['unit-test'].services).toEqual([]);
expect(STORE_JOBS['coding-standard'].services).toEqual([]); expect(STORE_JOBS['coding-standard'].services).toEqual([]);
@@ -79,6 +83,31 @@ describe('resolveStoreConfig', () => {
expect(resolved['smoke-test'].enabled).toBe(false); expect(resolved['smoke-test'].enabled).toBe(false);
}); });
it('emits the default page-only probe list for smoke-test', () => {
const resolved = resolveStoreConfig({}, MATRIX);
expect(resolved['smoke-test'].probes).toEqual(['page']);
});
it('honors a smoke-test probes override', () => {
const resolved = resolveStoreConfig(
{ jobs: { 'smoke-test': { probes: ['page', 'graphql'] } } },
MATRIX,
);
expect(resolved['smoke-test'].probes).toEqual(['page', 'graphql']);
});
it('does not emit probes on jobs without a probe concept', () => {
const resolved = resolveStoreConfig({}, MATRIX);
expect(resolved['unit-test'].probes).toBeUndefined();
expect(resolved['coding-standard'].probes).toBeUndefined();
});
it('rejects probes on a job that does not support it', () => {
expect(() => resolveStoreConfig({ jobs: { 'unit-test': { probes: ['page'] } } }, MATRIX)).toThrowError(
/job "unit-test" does not support "probes"/
);
});
it('throws on a typo in the job name', () => { it('throws on a typo in the job name', () => {
expect(() => resolveStoreConfig({ jobs: { 'smkoe-test': false } }, MATRIX)).toThrowError( expect(() => resolveStoreConfig({ jobs: { 'smkoe-test': false } }, MATRIX)).toThrowError(
/unknown job "smkoe-test" for kind "store"/ /unknown job "smkoe-test" for kind "store"/
+1
View File
@@ -14,6 +14,7 @@ export const STORE_JOBS: Record<string, JobDefaults> = {
'smoke-test': { 'smoke-test': {
services: [], services: [],
requiredServices: ['db', 'search', 'queue', 'cache', 'web'], requiredServices: ['db', 'search', 'queue', 'cache', 'web'],
probes: ['page'],
}, },
}; };
+62
View File
@@ -3,6 +3,7 @@ import {
filterMatrixForJob, filterMatrixForJob,
mergeRequiredTiers, mergeRequiredTiers,
normalizeJobEntry, normalizeJobEntry,
normalizeProbes,
parseMatrixInput, parseMatrixInput,
parseRawConfig, parseRawConfig,
resolveJobs, resolveJobs,
@@ -24,6 +25,7 @@ const MATRIX: Matrix = {
const noDefaults: JobDefaults = { services: [] }; const noDefaults: JobDefaults = { services: [] };
const smokeDefaults: JobDefaults = { services: ['search', 'queue', 'cache', 'web'] }; const smokeDefaults: JobDefaults = { services: ['search', 'queue', 'cache', 'web'] };
const probeDefaults: JobDefaults = { services: [], probes: ['page'] };
describe('normalizeJobEntry', () => { describe('normalizeJobEntry', () => {
it('defaults enabled=true and uses the default tiers when entry is undefined', () => { it('defaults enabled=true and uses the default tiers when entry is undefined', () => {
@@ -96,6 +98,66 @@ describe('normalizeJobEntry', () => {
/services contains unknown tier "llm"/ /services contains unknown tier "llm"/
); );
}); });
it('carries the default probes when the entry omits them', () => {
expect(normalizeJobEntry('smoke-test', { services: [] }, probeDefaults)).toEqual({
enabled: true,
tiers: [],
probes: ['page'],
});
});
it('carries the default probes for the boolean shorthand', () => {
expect(normalizeJobEntry('smoke-test', true, probeDefaults)).toEqual({
enabled: true,
tiers: [],
probes: ['page'],
});
});
it('overrides the default probes when probes is set', () => {
expect(normalizeJobEntry('smoke-test', { probes: ['page', 'graphql'] }, probeDefaults)).toEqual({
enabled: true,
tiers: [],
probes: ['page', 'graphql'],
});
});
it('omits probes for a job that declares no probe defaults', () => {
expect(normalizeJobEntry('unit-test', { services: [] }, noDefaults).probes).toBeUndefined();
});
});
describe('normalizeProbes', () => {
it('returns the defaults when probes is omitted', () => {
expect(normalizeProbes('smoke-test', undefined, ['page'])).toEqual(['page']);
});
it('returns undefined for a job with no probe defaults when omitted', () => {
expect(normalizeProbes('unit-test', undefined, undefined)).toBeUndefined();
});
it('throws when probes is set on a job that does not support it', () => {
expect(() => normalizeProbes('unit-test', ['page'], undefined)).toThrowError(
/job "unit-test" does not support "probes"/
);
});
it('throws when probes is not an array', () => {
expect(() => normalizeProbes('smoke-test', 'page', ['page'])).toThrowError(
/probes must be an array of probe names/
);
});
it('throws when probes contains an unknown probe', () => {
expect(() => normalizeProbes('smoke-test', ['rest'], ['page'])).toThrowError(
/probes contains unknown probe "rest"/
);
});
it('accepts an empty probes array', () => {
expect(normalizeProbes('smoke-test', [], ['page'])).toEqual([]);
});
}); });
describe('mergeRequiredTiers', () => { describe('mergeRequiredTiers', () => {
+51 -14
View File
@@ -1,34 +1,67 @@
import { JobDefaults, Kind, Matrix, MatrixEntry, RawConfig, RawJobConfig, ResolvedConfig, ResolvedJobConfig, Services } from './types'; import { JobDefaults, Kind, Matrix, MatrixEntry, RawConfig, RawJobConfig, ResolvedConfig, ResolvedJobConfig, Services } from './types';
import { isTier, servicesForTiers, Tier } from './tier-map'; import { isTier, servicesForTiers, Tier } from './tier-map';
import { isProbe, Probe } from './probe';
/** /**
* Normalizes a single raw job entry to (enabled, tiers). Accepts * Normalizes the `probes` value from a job entry. Returns the
* the boolean shorthand and the object form. Validates the shape * caller's list when present (validated), the job's default probe
* and the `services` tier list; throws on unexpected input. The * list when omitted, or `undefined` for jobs that have no probe
* caller supplies the per-job default tiers, used when `services` * concept. Throws if a job without probe defaults is given `probes`.
* is omitted from the entry. */
export const normalizeProbes = (
jobName: string,
raw: unknown,
defaults: readonly Probe[] | undefined,
): readonly Probe[] | undefined => {
if (raw === undefined) {
return defaults;
}
if (defaults === undefined) {
throw new Error(`check-config: job "${jobName}" does not support "probes"`);
}
if (!Array.isArray(raw)) {
throw new Error(`check-config: job "${jobName}".probes must be an array of probe names`);
}
const probes: Probe[] = [];
for (const value of raw) {
if (!isProbe(value)) {
throw new Error(`check-config: job "${jobName}".probes contains unknown probe "${String(value)}"`);
}
probes.push(value);
}
return probes;
}
/**
* Normalizes a single raw job entry to (enabled, tiers, probes).
* Accepts the boolean shorthand and the object form. Validates the
* shape, the `services` tier list, and the `probes` list; throws on
* unexpected input. The caller supplies the per-job defaults, used
* when `services`/`probes` are omitted from the entry. `probes` is
* `undefined` for jobs that declare no probe defaults.
*/ */
export const normalizeJobEntry = ( export const normalizeJobEntry = (
jobName: string, jobName: string,
raw: RawJobConfig | undefined, raw: RawJobConfig | undefined,
defaults: JobDefaults, defaults: JobDefaults,
): { enabled: boolean; tiers: readonly Tier[] } => { ): { enabled: boolean; tiers: readonly Tier[]; probes?: readonly Probe[] } => {
if (raw === undefined) { if (raw === undefined) {
return { enabled: true, tiers: defaults.services }; return { enabled: true, tiers: defaults.services, probes: defaults.probes };
} }
if (typeof raw === 'boolean') { if (typeof raw === 'boolean') {
return { enabled: raw, tiers: defaults.services }; return { enabled: raw, tiers: defaults.services, probes: defaults.probes };
} }
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) { if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
throw new Error( throw new Error(
`check-config: job "${jobName}" must be a boolean or an object (got ${Array.isArray(raw) ? 'array' : typeof raw})` `check-config: job "${jobName}" must be a boolean or an object (got ${Array.isArray(raw) ? 'array' : typeof raw})`
); );
} }
const { enabled, services } = raw as { enabled?: unknown; services?: unknown }; const { enabled, services, probes } = raw as { enabled?: unknown; services?: unknown; probes?: unknown };
const enabledValue = enabled === undefined ? true : Boolean(enabled); const enabledValue = enabled === undefined ? true : Boolean(enabled);
const resolvedProbes = normalizeProbes(jobName, probes, defaults.probes);
if (services === undefined) { if (services === undefined) {
return { enabled: enabledValue, tiers: defaults.services }; return { enabled: enabledValue, tiers: defaults.services, probes: resolvedProbes };
} }
if (!Array.isArray(services)) { if (!Array.isArray(services)) {
throw new Error(`check-config: job "${jobName}".services must be an array of tier names`); throw new Error(`check-config: job "${jobName}".services must be an array of tier names`);
@@ -40,7 +73,7 @@ export const normalizeJobEntry = (
} }
tiers.push(value); tiers.push(value);
} }
return { enabled: enabledValue, tiers }; return { enabled: enabledValue, tiers, probes: resolvedProbes };
} }
/** /**
@@ -117,12 +150,16 @@ export const resolveJobs = (
const resolved: ResolvedConfig = {}; const resolved: ResolvedConfig = {};
for (const [name, defaults] of Object.entries(jobs)) { for (const [name, defaults] of Object.entries(jobs)) {
const entry = (rawJobs as Record<string, RawJobConfig>)[name]; const entry = (rawJobs as Record<string, RawJobConfig>)[name];
const { enabled, tiers } = normalizeJobEntry(name, entry, defaults); const { enabled, tiers, probes } = normalizeJobEntry(name, entry, defaults);
const finalTiers = mergeRequiredTiers(tiers, defaults.requiredServices); const finalTiers = mergeRequiredTiers(tiers, defaults.requiredServices);
resolved[name] = { const resolvedEntry: ResolvedJobConfig = {
enabled, enabled,
matrix: filterMatrixForJob(matrix, finalTiers), matrix: filterMatrixForJob(matrix, finalTiers),
} as ResolvedJobConfig; };
if (probes !== undefined) {
resolvedEntry.probes = [...probes];
}
resolved[name] = resolvedEntry;
} }
return resolved; return resolved;
} }
+13
View File
@@ -0,0 +1,13 @@
/**
* A smoke-test probe the check-store workflow can run against a
* running store. `page` does a GET / and asserts a non-empty title;
* `graphql` POSTs a storeConfig query to /graphql. Probes are opt-in
* per job because not every edition exposes every surface (e.g. the
* mage-os minimal edition ships no GraphQL modules, so /graphql 404s).
*/
export const PROBES = ['page', 'graphql'] as const;
export type Probe = (typeof PROBES)[number];
export const isProbe = (value: unknown): value is Probe =>
typeof value === 'string' && (PROBES as readonly string[]).includes(value);
+10 -2
View File
@@ -1,4 +1,5 @@
import { Tier } from './tier-map'; import { Tier } from './tier-map';
import { Probe } from './probe';
/** /**
* Which reusable workflow a config belongs to. Selects the known-job * Which reusable workflow a config belongs to. Selects the known-job
@@ -53,10 +54,15 @@ export interface Matrix {
* regardless of caller overrides. Use it for tiers a job structurally * regardless of caller overrides. Use it for tiers a job structurally
* cannot run without (e.g. mysql for a running store smoke-test) and * cannot run without (e.g. mysql for a running store smoke-test) and
* which therefore should not appear in the user-facing schema enum. * which therefore should not appear in the user-facing schema enum.
*
* `probes` is the default smoke-test probe list used when the caller
* does not override it. Only jobs that declare it support the
* `probes` config key; omit it for jobs that have no probe concept.
*/ */
export interface JobDefaults { export interface JobDefaults {
services: readonly Tier[]; services: readonly Tier[];
requiredServices?: readonly Tier[]; requiredServices?: readonly Tier[];
probes?: readonly Probe[];
} }
/** /**
@@ -67,6 +73,7 @@ export interface JobDefaults {
export interface ResolvedJobConfig { export interface ResolvedJobConfig {
enabled: boolean; enabled: boolean;
matrix: Matrix; matrix: Matrix;
probes?: Probe[];
[key: string]: unknown; [key: string]: unknown;
} }
@@ -83,11 +90,12 @@ export interface ResolvedConfig {
* Shape of a single per-job entry in the user's JSON config file. * Shape of a single per-job entry in the user's JSON config file.
* - `true` / `false`: shorthand for `{ enabled: true|false }` * - `true` / `false`: shorthand for `{ enabled: true|false }`
* - object: explicit enabled flag plus an optional tier list under * - object: explicit enabled flag plus an optional tier list under
* `services` (validated against the per-kind schema). * `services` and an optional probe list under `probes` (both
* validated against the per-kind schema).
*/ */
export type RawJobConfig = export type RawJobConfig =
| boolean | boolean
| { enabled?: boolean; services?: string[]; [key: string]: unknown }; | { enabled?: boolean; services?: string[]; probes?: string[]; [key: string]: unknown };
/** /**
* Top-level shape of the user's JSON config file. Job toggles live * Top-level shape of the user's JSON config file. Job toggles live
+1 -1
View File
@@ -111,7 +111,7 @@ runs:
fi fi
printf '%s\n' "$line" >> .gitattributes printf '%s\n' "$line" >> .gitattributes
- uses: graycoreio/github-actions-magento2/fix-magento-install@v8.5.0 - uses: graycoreio/github-actions-magento2/fix-magento-install@main
name: Fix Magento Out of Box Install Issues name: Fix Magento Out of Box Install Issues
with: with:
magento_directory: ${{ steps.setup-magento-compute-directory.outputs.MAGENTO_DIRECTORY }} magento_directory: ${{ steps.setup-magento-compute-directory.outputs.MAGENTO_DIRECTORY }}