Compare commits

..

28 Commits

Author SHA1 Message Date
dependabot[bot] e27fd68401 build(deps-dev): bump esbuild from 0.28.0 to 0.28.1
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.28.0 to 0.28.1.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.28.0...v0.28.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-13 01:09:04 +00:00
GrayBot 7e9e4ae0df chore: restore internal action refs to @main (#291) 2026-06-11 16:00:43 -04:00
GrayBot 7b29aa3652 chore: release 8.6.0 (#286) 2026-06-11 15:59:53 -04:00
Damien Retzinger b7c6614ac3 fix(check-extension, integration): prevent crash on docker pull failure 2026-06-10 09:01:17 -04:00
Damien Retzinger 8f695ed7f8 fix(check-extension,integration): remove quotes from version names when upladoing sandbox data
fixes: https://github.com/graycoreio/github-actions-magento2/issues/287
2026-06-09 18:10:14 -04:00
Damien Retzinger a54cad7d46 chore: realign package-lock.json after dep updates 2026-06-09 18:06:52 -04:00
Damien Retzinger 6db07e2737 build(deps): upgrade to typescript 6
TypeScript 6 enables `strict` by default and changes default @types
inclusion.
2026-06-09 18:04:26 -04:00
Damien Retzinger 9d3601a1e3 build(deps): declare semver dependency
semver is imported directly by supported-version/src/kind/get-usable.ts
but was never declared in package.json.
2026-06-09 18:04:14 -04:00
Damien Retzinger c236c55738 build(deps): ignore @types/node in dependabot 2026-06-09 17:52:19 -04:00
Damien Retzinger ed78b5307f build(deps): upgrade to eslint 10 2026-06-09 17:50:47 -04:00
Damien Retzinger cfbf870a80 build(deps): upgrade to node 24.16.0 2026-06-09 17:46:56 -04:00
Damien Retzinger 0702fc3984 build(deps-dev): bump esbuild from 0.25.12 to 0.28.0 2026-06-09 17:44:36 -04:00
Damien Retzinger 8cbdeb780d docs: document resolve-check-config 2026-06-09 17:44:05 -04:00
Damien Retzinger c43cf1503b ci: prevent dependabot from bumping placeholder packages 2026-06-09 17:40:41 -04:00
Damien Retzinger 14a0e38d64 chore: migrate eslint to flat config
eslint 9 ignores .eslintrc.* by default, so replace .eslintrc.cjs with a
flat eslint.config.mjs (in the style of graycoreio/daffodil)
2026-06-09 17:33:57 -04:00
Damien Retzinger 8a6a886d7e chore(deps): upgrade jest to v30
Bump jest ^29.5.0 -> ^30.4.2, @types/jest ^29.5.14 -> ^30.0.0, and
ts-jest ^29.4.6 -> ^29.4.11.

@types/jest@30 removes the deprecated `toThrowError` matcher type, so
migrate every `.toThrowError()` assertion to `.toThrow()`.
2026-06-09 17:33:49 -04:00
Damien Retzinger 0a33911be4 chore(deps): upgrade @actions/core to v3 2026-06-09 17:33:42 -04:00
Damien Retzinger 7f6945e30b fix(tsconfig): set ES2022 target for jest typechecking
resolve-check-config and setup-install set `types: ["jest"]`, which
excludes @types/node and the lib references it pulls in. With no explicit
`target`, ts-jest typechecks against the default ES5 lib, so modern
globals like `Object.entries` fail to resolve (TS2550) and the
resolve-check-config suite fails to compile.
2026-06-09 17:33:10 -04:00
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
GrayBot 3c51e99538 chore: release 8.5.0 (#284) 2026-05-27 16:18:40 -04:00
Damien Retzinger 32a5fd2bad feat(setup-install): run with --no-interaction 2026-05-27 15:39:46 -04:00
GrayBot 91bd008e62 chore: restore internal action refs to @main (#283) 2026-05-25 16:15:12 -04:00
GrayBot 36953b919c chore: release 8.4.0 (#282) 2026-05-25 16:14:12 -04:00
Damien Retzinger 83f9433da0 feat: remove rabbitmq from supported-version for mage-os/minimal 2026-05-25 16:09:31 -04:00
dependabot[bot] a7d48f567e build(deps): bump googleapis/release-please-action from 4 to 5 (#273)
Bumps [googleapis/release-please-action](https://github.com/googleapis/release-please-action) from 4 to 5.
- [Release notes](https://github.com/googleapis/release-please-action/releases)
- [Changelog](https://github.com/googleapis/release-please-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/release-please-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: googleapis/release-please-action
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-25 09:34:12 -04:00
dependabot[bot] 76eb9064ff build(deps): bump actions/upload-artifact from 6 to 7 (#272)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-25 09:34:00 -04:00
GrayBot 147245e120 chore: restore internal action refs to @main (#281) 2026-05-25 09:33:37 -04:00
68 changed files with 2678 additions and 1738 deletions
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
root: true,
};
+11
View File
@@ -9,3 +9,14 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
# TS subpackages pin deps to 0.0.0-PLACEHOLDER; real versions are managed
# in the root package.json Keep Dependabot out of them so it stops
# "bumping" the placeholder (see PR #267).
exclude-paths:
- "resolve-check-config/**"
- "setup-install/**"
- "supported-version/**"
# @types/node tracks the Node runtime version, not the latest release;
# bumping it ahead of the pinned Node version is noise.
ignore:
- dependency-name: "@types/node"
+15 -15
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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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 }}
@@ -301,9 +301,9 @@ jobs:
run: ../../../vendor/bin/phpunit -c phpunit.xml.dist --testsuite Extension_Integration_Tests run: ../../../vendor/bin/phpunit -c phpunit.xml.dist --testsuite Extension_Integration_Tests
- name: Upload test sandbox dir - name: Upload test sandbox dir
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
if: failure() if: failure() && steps.magento-version.outputs.version != ''
with: with:
name: sandbox-data-${{ steps.magento-version.outputs.version }} name: sandbox-data-${{ fromJSON(steps.magento-version.outputs.version) }}
path: ${{ steps.setup-magento.outputs.path }}/dev/tests/integration/tmp/sandbox-* path: ${{ steps.setup-magento.outputs.path }}/dev/tests/integration/tmp/sandbox-*
retention-days: 3 retention-days: 3
+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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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
+4 -4
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.3.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 }}
@@ -182,10 +182,10 @@ jobs:
name: Run Integration Tests name: Run Integration Tests
- name: Upload test sandbox dir - name: Upload test sandbox dir
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
if: failure() if: failure() && steps.magento-version.outputs.version != ''
with: with:
name: sandbox-data-${{ steps.magento-version.outputs.version }} name: sandbox-data-${{ fromJSON(steps.magento-version.outputs.version) }}
path: /home/runner/work/infrastructure/magento2/dev/tests/integration/tmp/sandbox-* path: /home/runner/work/infrastructure/magento2/dev/tests/integration/tmp/sandbox-*
retention-days: 3 retention-days: 3
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
releases_created: ${{ steps.release.outputs.releases_created }} releases_created: ${{ steps.release.outputs.releases_created }}
steps: steps:
- id: release - id: release
uses: googleapis/release-please-action@v4 uses: googleapis/release-please-action@v5
with: with:
token: ${{ secrets.GRAYCORE_GITHUB_TOKEN }} token: ${{ secrets.GRAYCORE_GITHUB_TOKEN }}
config-file: ${{ inputs.release-mode == 'rc' && 'release-please-config.rc.json' || (inputs.release-mode == 'graduate' && 'release-please-config.graduate.json' || 'release-please-config.json') }} config-file: ${{ inputs.release-mode == 'rc' && 'release-please-config.rc.json' || (inputs.release-mode == 'graduate' && 'release-please-config.graduate.json' || 'release-please-config.json') }}
+1 -1
View File
@@ -1 +1 @@
v24.12.0 v24.16.0
+1 -1
View File
@@ -1 +1 @@
{".":"8.3.0"} {".":"8.6.0"}
+3 -2
View File
@@ -13,7 +13,7 @@ docs/ # General documentation
other-root-level-folders # Individual GitHub Actions (all are external/public) other-root-level-folders # Individual GitHub Actions (all are external/public)
``` ```
Most actions are **composite** (bash scripts in `action.yml`). Two are **TypeScript bundled**: `supported-version` and `setup-install`. Most actions are **composite** (bash scripts in `action.yml`). Three are **TypeScript bundled**: `supported-version`, `setup-install`, and `resolve-check-config`.
## Commands ## Commands
@@ -28,6 +28,7 @@ cd actionName && npm test
# Build a TypeScript action (must be committed after source changes) # Build a TypeScript action (must be committed after source changes)
cd supported-version && npm run build cd supported-version && npm run build
cd setup-install && npm run build cd setup-install && npm run build
cd resolve-check-config && npm run build
``` ```
Build uses `esbuild` and outputs `dist/index.js`. The `dist/` file **must be committed** — GitHub Actions runs the bundled output directly. Build uses `esbuild` and outputs `dist/index.js`. The `dist/` file **must be committed** — GitHub Actions runs the bundled output directly.
@@ -50,7 +51,7 @@ Build uses `esbuild` and outputs `dist/index.js`. The `dist/` file **must be com
## @test-agent ## @test-agent
Writes and updates Jest specs for TypeScript actions (`supported-version`, `setup-install`). Scope is limited to `**/*.spec.ts` files. Writes and updates Jest specs for TypeScript actions (`supported-version`, `setup-install`, `resolve-check-config`). Scope is limited to `**/*.spec.ts` files.
### Style ### Style
+28
View File
@@ -2,6 +2,34 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [8.6.0](https://github.com/graycoreio/github-actions-magento2/compare/v8.5.0...v8.6.0) (2026-06-10)
### Features
* **resolve-check-config:** graphql smoke test opt-out by default ([a097371](https://github.com/graycoreio/github-actions-magento2/commit/a097371e379bb481e0a077fa7c673c5004e948b8))
### Bug Fixes
* **check-extension, integration:** prevent crash on docker pull failure ([b7c6614](https://github.com/graycoreio/github-actions-magento2/commit/b7c6614ac37ec3d5c4d6b3797e13f8c249e13e6a))
* **check-extension,integration:** remove quotes from version names when upladoing sandbox data ([8f695ed](https://github.com/graycoreio/github-actions-magento2/commit/8f695ed7f8513df196dfad1bd6804dd87e502909))
* **tsconfig:** set ES2022 target for jest typechecking ([7f6945e](https://github.com/graycoreio/github-actions-magento2/commit/7f6945e30be5d7610a2ab64e9d7147be0cbeaf20))
## [8.5.0](https://github.com/graycoreio/github-actions-magento2/compare/v8.4.0...v8.5.0) (2026-05-27)
### Features
* **setup-install:** run with --no-interaction ([32a5fd2](https://github.com/graycoreio/github-actions-magento2/commit/32a5fd2badfe558e7dced9606765d0d44632c6f0))
## [8.4.0](https://github.com/graycoreio/github-actions-magento2/compare/v8.3.0...v8.4.0) (2026-05-25)
### Features
* remove rabbitmq from supported-version for mage-os/minimal ([83f9433](https://github.com/graycoreio/github-actions-magento2/commit/83f9433da0d7f20efbf090fd8ed75a0a39000797))
## [8.3.0](https://github.com/graycoreio/github-actions-magento2/compare/v8.2.0...v8.3.0) (2026-05-25) ## [8.3.0](https://github.com/graycoreio/github-actions-magento2/compare/v8.2.0...v8.3.0) (2026-05-25)
+4 -4
View File
@@ -33,7 +33,7 @@ The `composer.lock` hash is derived from `working-directory/composer.lock` using
### Extension (download cache only) ### Extension (download cache only)
```yml ```yml
- uses: graycoreio/github-actions-magento2/cache-magento@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/cache-magento@v8.6.0 # x-release-please-version
with: with:
composer_cache_key: ${{ inputs.composer_cache_key }} composer_cache_key: ${{ inputs.composer_cache_key }}
``` ```
@@ -41,13 +41,13 @@ The `composer.lock` hash is derived from `working-directory/composer.lock` using
### Extension or store (download + vendor stamp cache) ### Extension or store (download + vendor stamp cache)
```yml ```yml
- uses: graycoreio/github-actions-magento2/setup-magento@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/setup-magento@v8.6.0 # x-release-please-version
id: setup-magento id: setup-magento
with: with:
mode: extension # or store mode: extension # or store
# ... # ...
- uses: graycoreio/github-actions-magento2/cache-magento@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/cache-magento@v8.6.0 # x-release-please-version
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 }}
@@ -69,7 +69,7 @@ As such, use `stamp: true` when `composer.lock` is stable across most runs — a
> **Dependabot / Renovate:** Each time a Dependabot or Renovate PR is merged, the remaining open PRs rebase and each produces a new `composer.lock`. This cascades into a large number of unique cache entries, inflating storage costs without delivering proportional compute savings — because automated PRs are not waiting on fast feedback. The fix is to disable stamp caching for automated dependency PRs entirely: > **Dependabot / Renovate:** Each time a Dependabot or Renovate PR is merged, the remaining open PRs rebase and each produces a new `composer.lock`. This cascades into a large number of unique cache entries, inflating storage costs without delivering proportional compute savings — because automated PRs are not waiting on fast feedback. The fix is to disable stamp caching for automated dependency PRs entirely:
> >
> ```yml > ```yml
> - uses: graycoreio/github-actions-magento2/cache-magento@v8.3.0 # x-release-please-version > - uses: graycoreio/github-actions-magento2/cache-magento@v8.6.0 # x-release-please-version
> with: > with:
> stamp: ${{ github.actor != 'dependabot[bot]' }} > stamp: ${{ github.actor != 'dependabot[bot]' }}
> ``` > ```
+1 -1
View File
@@ -62,7 +62,7 @@ runs:
exit 1 exit 1
fi fi
- uses: graycoreio/github-actions-magento2/get-magento-version@v8.3.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 }}
+1 -1
View File
@@ -36,7 +36,7 @@ jobs:
tools: composer:v2 tools: composer:v2
coverage: none coverage: none
- uses: graycoreio/github-actions-magento2/coding-standard@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/coding-standard@v8.6.0 # x-release-please-version
with: with:
path: app/code # Optional, defaults to . path: app/code # Optional, defaults to .
version: 25 # Optional, will use the latest if omitted. version: 25 # Optional, will use the latest if omitted.
+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.3.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.3.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:
+3 -3
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
{ {
@@ -55,12 +55,12 @@ jobs:
matrix: ${{ steps.supported-version.outputs.matrix }} matrix: ${{ steps.supported-version.outputs.matrix }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/supported-version@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/supported-version@v8.6.0 # x-release-please-version
id: supported-version id: supported-version
- run: echo ${{ steps.supported-version.outputs.matrix }} - run: echo ${{ steps.supported-version.outputs.matrix }}
check-extension: check-extension:
needs: compute_matrix needs: compute_matrix
uses: graycoreio/github-actions-magento2/.github/workflows/check-extension.yaml@v8.3.0 # x-release-please-version uses: graycoreio/github-actions-magento2/.github/workflows/check-extension.yaml@v8.6.0 # x-release-please-version
with: with:
matrix: ${{ needs.compute_matrix.outputs.matrix }} matrix: ${{ needs.compute_matrix.outputs.matrix }}
``` ```
+3 -3
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
{ {
@@ -58,7 +58,7 @@ on:
jobs: jobs:
check-store: check-store:
uses: graycoreio/github-actions-magento2/.github/workflows/check-store.yaml@v8.3.0 # x-release-please-version uses: graycoreio/github-actions-magento2/.github/workflows/check-store.yaml@v8.6.0 # x-release-please-version
secrets: secrets:
composer_auth: ${{ secrets.COMPOSER_AUTH }} composer_auth: ${{ secrets.COMPOSER_AUTH }}
``` ```
@@ -70,7 +70,7 @@ If your pipeline builds or prepares the store in a prior job and uploads it as a
```yml ```yml
jobs: jobs:
check-store: check-store:
uses: graycoreio/github-actions-magento2/.github/workflows/check-store.yaml@v8.3.0 # x-release-please-version uses: graycoreio/github-actions-magento2/.github/workflows/check-store.yaml@v8.6.0 # x-release-please-version
secrets: secrets:
composer_auth: ${{ secrets.COMPOSER_AUTH }} composer_auth: ${{ secrets.COMPOSER_AUTH }}
``` ```
+2 -2
View File
@@ -50,13 +50,13 @@ jobs:
matrix: ${{ steps.supported-version.outputs.matrix }} matrix: ${{ steps.supported-version.outputs.matrix }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/supported-version@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/supported-version@v8.6.0 # x-release-please-version
with: with:
include_services: true include_services: true
id: supported-version id: supported-version
integration-workflow: integration-workflow:
needs: compute_matrix needs: compute_matrix
uses: graycoreio/github-actions-magento2/.github/workflows/integration.yaml@v8.3.0 # x-release-please-version uses: graycoreio/github-actions-magento2/.github/workflows/integration.yaml@v8.6.0 # x-release-please-version
with: with:
package_name: my-vendor/package package_name: my-vendor/package
matrix: ${{ needs.compute_matrix.outputs.matrix }} matrix: ${{ needs.compute_matrix.outputs.matrix }}
+29
View File
@@ -0,0 +1,29 @@
import { defineConfig, globalIgnores } from "eslint/config";
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
export default defineConfig([
globalIgnores([
"**/dist",
"**/node_modules",
"**/vendor",
"_test",
]),
{
files: ["**/*.ts"],
extends: [
eslint.configs.recommended,
tseslint.configs.recommended,
],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
},
],
},
},
]);
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/fix-magento-install@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/fix-magento-install@v8.6.0 # x-release-please-version
with: with:
magento_directory: path/to/magento magento_directory: path/to/magento
``` ```
+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.3.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 }}
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
name: A job to compute an installed Composer version. name: A job to compute an installed Composer version.
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/get-composer-version@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/get-composer-version@v8.6.0 # x-release-please-version
id: get-composer-version id: get-composer-version
- run: echo version ${{ steps.get-composer-version.outputs.version }} - run: echo version ${{ steps.get-composer-version.outputs.version }}
shell: bash shell: bash
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
name: A job to compute an installed Magento version. name: A job to compute an installed Magento version.
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/get-magento-version@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/get-magento-version@v8.6.0 # x-release-please-version
id: get-magento-version id: get-magento-version
- run: echo version ${{ steps.get-magento-version.outputs.version }} - run: echo version ${{ steps.get-magento-version.outputs.version }}
shell: bash shell: bash
+2122 -1415
View File
File diff suppressed because it is too large Load Diff
+13 -11
View File
@@ -1,6 +1,6 @@
{ {
"name": "@graycoreio/github-actions-magento2", "name": "@graycoreio/github-actions-magento2",
"version": "8.3.0", "version": "8.6.0",
"description": "Github Actions for Magento 2", "description": "Github Actions for Magento 2",
"scripts": { "scripts": {
"test": "cd supported-version && npm run test && cd - && cd setup-install && npm run test && cd -", "test": "cd supported-version && npm run test && cd - && cd setup-install && npm run test && cd -",
@@ -18,18 +18,20 @@
}, },
"homepage": "https://github.com/graycoreio/github-actions-magento2#readme", "homepage": "https://github.com/graycoreio/github-actions-magento2#readme",
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^3.0.1",
"@actions/exec": "^3.0.0" "@actions/exec": "^3.0.0",
"@eslint/js": "^10.0.1",
"semver": "^7.8.3"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.14", "@types/jest": "^30.0.0",
"@types/node": "^24.10.4", "@types/node": "^24.10.4",
"@typescript-eslint/eslint-plugin": "^8.49.0", "@types/semver": "^7.7.1",
"@typescript-eslint/parser": "^8.49.0", "esbuild": "^0.28.1",
"esbuild": "^0.25.12", "eslint": "^10.4.1",
"eslint": "^9.39.2", "jest": "^30.4.2",
"jest": "^29.5.0", "ts-jest": "^29.4.11",
"ts-jest": "^29.4.6", "typescript": "^6.0.3",
"typescript": "^5.9.3" "typescript-eslint": "^8.61.0"
} }
} }
+9 -2
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 |
@@ -21,12 +28,12 @@ jobs:
outputs: outputs:
resolved: ${{ steps.resolve.outputs.resolved }} resolved: ${{ steps.resolve.outputs.resolved }}
steps: steps:
- uses: graycoreio/github-actions-magento2/supported-version@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/supported-version@v8.6.0 # x-release-please-version
id: supported-version id: supported-version
with: with:
kind: currently-supported kind: currently-supported
- uses: graycoreio/github-actions-magento2/resolve-check-config@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/resolve-check-config@v8.6.0 # x-release-please-version
id: resolve id: resolve
with: with:
kind: store kind: store
+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
}
]
} }
} }
} }
+34 -49
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -13,14 +13,14 @@ describe('isKind / assertKind', () => {
it('rejects other strings', () => { it('rejects other strings', () => {
expect(isKind('taco')).toBe(false); expect(isKind('taco')).toBe(false);
expect(() => assertKind('taco')).toThrowError(/`kind` must be 'store' or 'extension'/); expect(() => assertKind('taco')).toThrow(/`kind` must be 'store' or 'extension'/);
}); });
it('rejects empty input', () => { it('rejects empty input', () => {
expect(() => assertKind('')).toThrowError(/`kind` must be 'store' or 'extension'/); expect(() => assertKind('')).toThrow(/`kind` must be 'store' or 'extension'/);
}); });
it('rejects non-string input', () => { it('rejects non-string input', () => {
expect(() => assertKind(undefined)).toThrowError(/`kind` must be 'store' or 'extension'/); expect(() => assertKind(undefined)).toThrow(/`kind` must be 'store' or 'extension'/);
}); });
}); });
@@ -88,13 +88,13 @@ describe('resolveExtensionConfig', () => {
}); });
it('throws on a typo in the job name', () => { it('throws on a typo in the job name', () => {
expect(() => resolveExtensionConfig({ jobs: { 'inteegration_test': false } }, MATRIX)).toThrowError( expect(() => resolveExtensionConfig({ jobs: { 'inteegration_test': false } }, MATRIX)).toThrow(
/unknown job "inteegration_test" for kind "extension"/ /unknown job "inteegration_test" for kind "extension"/
); );
}); });
it('throws when a store-only job name is used', () => { it('throws when a store-only job name is used', () => {
expect(() => resolveExtensionConfig({ jobs: { 'smoke-test': false } }, MATRIX)).toThrowError( expect(() => resolveExtensionConfig({ jobs: { 'smoke-test': false } }, MATRIX)).toThrow(
/unknown job "smoke-test" for kind "extension"/ /unknown job "smoke-test" for kind "extension"/
); );
}); });
+31 -2
View File
@@ -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,14 +83,39 @@ 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)).toThrow(
/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)).toThrow(
/unknown job "smkoe-test" for kind "store"/ /unknown job "smkoe-test" for kind "store"/
); );
}); });
it('throws when an extension-only job name is used', () => { it('throws when an extension-only job name is used', () => {
expect(() => resolveStoreConfig({ jobs: { 'unit-test-extension': false } }, MATRIX)).toThrowError( expect(() => resolveStoreConfig({ jobs: { 'unit-test-extension': false } }, MATRIX)).toThrow(
/unknown job "unit-test-extension" for kind "store"/ /unknown job "unit-test-extension" 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'],
}, },
}; };
+77 -15
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', () => {
@@ -76,26 +78,86 @@ describe('normalizeJobEntry', () => {
}); });
it('throws when entry is a non-array primitive other than boolean', () => { it('throws when entry is a non-array primitive other than boolean', () => {
expect(() => normalizeJobEntry('unit-test', 'true' as never, noDefaults)).toThrowError( expect(() => normalizeJobEntry('unit-test', 'true' as never, noDefaults)).toThrow(
/must be a boolean or an object/ /must be a boolean or an object/
); );
}); });
it('throws when entry is an array', () => { it('throws when entry is an array', () => {
expect(() => normalizeJobEntry('unit-test', [] as never, noDefaults)).toThrowError(/got array/); expect(() => normalizeJobEntry('unit-test', [] as never, noDefaults)).toThrow(/got array/);
}); });
it('throws when services is not an array', () => { it('throws when services is not an array', () => {
expect(() => normalizeJobEntry('smoke-test', { services: 'search' } as never, smokeDefaults)).toThrowError( expect(() => normalizeJobEntry('smoke-test', { services: 'search' } as never, smokeDefaults)).toThrow(
/services must be an array of tier names/ /services must be an array of tier names/
); );
}); });
it('throws when services contains an unknown tier', () => { it('throws when services contains an unknown tier', () => {
expect(() => normalizeJobEntry('smoke-test', { services: ['llm'] } as never, smokeDefaults)).toThrowError( expect(() => normalizeJobEntry('smoke-test', { services: ['llm'] } as never, smokeDefaults)).toThrow(
/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)).toThrow(
/job "unit-test" does not support "probes"/
);
});
it('throws when probes is not an array', () => {
expect(() => normalizeProbes('smoke-test', 'page', ['page'])).toThrow(
/probes must be an array of probe names/
);
});
it('throws when probes contains an unknown probe', () => {
expect(() => normalizeProbes('smoke-test', ['rest'], ['page'])).toThrow(
/probes contains unknown probe "rest"/
);
});
it('accepts an empty probes array', () => {
expect(normalizeProbes('smoke-test', [], ['page'])).toEqual([]);
});
}); });
describe('mergeRequiredTiers', () => { describe('mergeRequiredTiers', () => {
@@ -233,13 +295,13 @@ describe('resolveJobs', () => {
}); });
it('throws on unknown job names with the kind in the message', () => { it('throws on unknown job names with the kind in the message', () => {
expect(() => resolveJobs({ jobs: { taco: false } }, 'store', jobs, MATRIX)).toThrowError( expect(() => resolveJobs({ jobs: { taco: false } }, 'store', jobs, MATRIX)).toThrow(
/unknown job "taco" for kind "store"/ /unknown job "taco" for kind "store"/
); );
}); });
it('throws when `jobs` is not an object', () => { it('throws when `jobs` is not an object', () => {
expect(() => resolveJobs({ jobs: 'oops' } as never, 'store', jobs, MATRIX)).toThrowError( expect(() => resolveJobs({ jobs: 'oops' } as never, 'store', jobs, MATRIX)).toThrow(
/`jobs` must be an object/ /`jobs` must be an object/
); );
}); });
@@ -261,19 +323,19 @@ describe('parseRawConfig', () => {
}); });
it('throws on syntactically invalid JSON', () => { it('throws on syntactically invalid JSON', () => {
expect(() => parseRawConfig('{not json}')).toThrowError(/failed to parse JSON/); expect(() => parseRawConfig('{not json}')).toThrow(/failed to parse JSON/);
}); });
it('throws when top level is an array', () => { it('throws when top level is an array', () => {
expect(() => parseRawConfig('[]')).toThrowError(/top-level value must be an object/); expect(() => parseRawConfig('[]')).toThrow(/top-level value must be an object/);
}); });
it('throws when top level is a primitive', () => { it('throws when top level is a primitive', () => {
expect(() => parseRawConfig('"hello"')).toThrowError(/top-level value must be an object/); expect(() => parseRawConfig('"hello"')).toThrow(/top-level value must be an object/);
}); });
it('throws when top level is null', () => { it('throws when top level is null', () => {
expect(() => parseRawConfig('null')).toThrowError(/top-level value must be an object/); expect(() => parseRawConfig('null')).toThrow(/top-level value must be an object/);
}); });
}); });
@@ -284,22 +346,22 @@ describe('parseMatrixInput', () => {
}); });
it('throws on empty input', () => { it('throws on empty input', () => {
expect(() => parseMatrixInput('')).toThrowError(/`matrix` input is required/); expect(() => parseMatrixInput('')).toThrow(/`matrix` input is required/);
}); });
it('throws on invalid JSON', () => { it('throws on invalid JSON', () => {
expect(() => parseMatrixInput('{nope}')).toThrowError(/failed to parse `matrix` input/); expect(() => parseMatrixInput('{nope}')).toThrow(/failed to parse `matrix` input/);
}); });
it('throws when top level is an array', () => { it('throws when top level is an array', () => {
expect(() => parseMatrixInput('[]')).toThrowError(/`matrix` must be a JSON object/); expect(() => parseMatrixInput('[]')).toThrow(/`matrix` must be a JSON object/);
}); });
it('throws when include is missing', () => { it('throws when include is missing', () => {
expect(() => parseMatrixInput('{}')).toThrowError(/`matrix.include` must be an array/); expect(() => parseMatrixInput('{}')).toThrow(/`matrix.include` must be an array/);
}); });
it('throws when include is not an array', () => { it('throws when include is not an array', () => {
expect(() => parseMatrixInput('{"include": "nope"}')).toThrowError(/`matrix.include` must be an array/); expect(() => parseMatrixInput('{"include": "nope"}')).toThrow(/`matrix.include` must be an array/);
}); });
}); });
+53 -16
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;
} }
@@ -139,7 +176,7 @@ export const parseRawConfig = (jsonText: string): RawConfig => {
try { try {
parsed = JSON.parse(trimmed); parsed = JSON.parse(trimmed);
} catch (e) { } catch (e) {
throw new Error(`check-config: failed to parse JSON: ${(e as Error).message}`); throw new Error(`check-config: failed to parse JSON: ${(e as Error).message}`, { cause: e });
} }
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) { if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`check-config: top-level value must be an object`); throw new Error(`check-config: top-level value must be an object`);
@@ -161,7 +198,7 @@ export const parseMatrixInput = (jsonText: string): Matrix => {
try { try {
parsed = JSON.parse(trimmed); parsed = JSON.parse(trimmed);
} catch (e) { } catch (e) {
throw new Error(`check-config: failed to parse \`matrix\` input as JSON: ${(e as Error).message}`); throw new Error(`check-config: failed to parse \`matrix\` input as JSON: ${(e as Error).message}`, { cause: e });
} }
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) { if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('check-config: `matrix` must be a JSON object'); throw new Error('check-config: `matrix` must be a JSON object');
+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);
+2 -2
View File
@@ -29,10 +29,10 @@ describe('resolveConfig', () => {
}); });
it('rejects a job name from the other kind', () => { it('rejects a job name from the other kind', () => {
expect(() => resolveConfig({ jobs: { 'smoke-test': false } }, 'extension', MATRIX)).toThrowError( expect(() => resolveConfig({ jobs: { 'smoke-test': false } }, 'extension', MATRIX)).toThrow(
/unknown job "smoke-test" for kind "extension"/ /unknown job "smoke-test" for kind "extension"/
); );
expect(() => resolveConfig({ jobs: { 'unit-test-extension': false } }, 'store', MATRIX)).toThrowError( expect(() => resolveConfig({ jobs: { 'unit-test-extension': false } }, 'store', MATRIX)).toThrow(
/unknown job "unit-test-extension" for kind "store"/ /unknown job "unit-test-extension" for kind "store"/
); );
}); });
+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
+2 -1
View File
@@ -3,6 +3,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,
"typeRoots": ["../node_modules/@types"], "typeRoots": ["../node_modules/@types"],
"types": ["jest"] "types": ["jest"],
"target": "ES2022"
} }
} }
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/sansec-ecomscan@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/sansec-ecomscan@v8.6.0 # x-release-please-version
with: with:
license: ${{ secrets.SANSEC_LICENSE_KEY }} license: ${{ secrets.SANSEC_LICENSE_KEY }}
``` ```
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
name: A job to semantically compare two versions name: A job to semantically compare two versions
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/semver-compare@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/semver-compare@v8.6.0 # x-release-please-version
with: with:
version: 2.1.0 version: 2.1.0
compare_against: 2.2.3 compare_against: 2.2.3
+4 -4
View File
@@ -28,7 +28,7 @@ jobs:
matrix: ${{ steps.supported-version.outputs.matrix }} matrix: ${{ steps.supported-version.outputs.matrix }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/supported-version@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/supported-version@v8.6.0 # x-release-please-version
id: supported-version id: supported-version
compile: compile:
@@ -40,19 +40,19 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/setup-magento@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/setup-magento@v8.6.0 # x-release-please-version
id: setup-magento id: setup-magento
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
tools: composer:v${{ matrix.composer }} tools: composer:v${{ matrix.composer }}
- uses: graycoreio/github-actions-magento2/cache-magento@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/cache-magento@v8.6.0 # x-release-please-version
- run: composer install - run: composer install
env: env:
COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }} COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}
- uses: graycoreio/github-actions-magento2/setup-di-compile@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/setup-di-compile@v8.6.0 # x-release-please-version
with: with:
path: ${{ steps.setup-magento.outputs.path }} path: ${{ steps.setup-magento.outputs.path }}
``` ```
+3 -3
View File
@@ -36,7 +36,7 @@ jobs:
matrix: ${{ steps.supported-version.outputs.matrix }} matrix: ${{ steps.supported-version.outputs.matrix }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/supported-version@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/supported-version@v8.6.0 # x-release-please-version
id: supported-version id: supported-version
with: with:
include_services: "true" include_services: "true"
@@ -51,7 +51,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/setup-magento@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/setup-magento@v8.6.0 # x-release-please-version
id: setup-magento id: setup-magento
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
@@ -64,7 +64,7 @@ jobs:
env: env:
COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }} COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}
- uses: graycoreio/github-actions-magento2/setup-install@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/setup-install@v8.6.0 # x-release-please-version
with: with:
services: ${{ toJSON(matrix.services) }} services: ${{ toJSON(matrix.services) }}
path: ${{ steps.setup-magento.outputs.path }} path: ${{ steps.setup-magento.outputs.path }}
+35 -50
View File
File diff suppressed because one or more lines are too long
+5
View File
@@ -8,6 +8,7 @@ const BASE_ARGS = [
'--admin-firstname=Admin', '--admin-firstname=Admin',
'--admin-lastname=User', '--admin-lastname=User',
'--backend-frontname=admin', '--backend-frontname=admin',
'--no-interaction',
]; ];
const MYSQL_SERVICE = { const MYSQL_SERVICE = {
@@ -60,6 +61,10 @@ describe('buildInstallArgs', () => {
it('returns only base args when services is empty', () => { it('returns only base args when services is empty', () => {
expect(buildInstallArgs({})).toEqual(BASE_ARGS); expect(buildInstallArgs({})).toEqual(BASE_ARGS);
}); });
it('runs non-interactively', () => {
expect(buildInstallArgs(null)).toContain('--no-interaction');
});
}); });
describe('mysql', () => { describe('mysql', () => {
+1
View File
@@ -24,6 +24,7 @@ const BASE_ARGS = [
'--admin-firstname=Admin', '--admin-firstname=Admin',
'--admin-lastname=User', '--admin-lastname=User',
'--backend-frontname=admin', '--backend-frontname=admin',
'--no-interaction',
]; ];
const parsePort = (svc: ServiceConfig | undefined, index: 0 | 1, fallback: string): string => { const parsePort = (svc: ServiceConfig | undefined, index: 0 | 1, fallback: string): string => {
+2 -1
View File
@@ -3,6 +3,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,
"typeRoots": ["../node_modules/@types"], "typeRoots": ["../node_modules/@types"],
"types": ["jest"] "types": ["jest"],
"target": "ES2022"
} }
} }
+2 -2
View File
@@ -51,7 +51,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/setup-magento@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/setup-magento@v8.6.0 # x-release-please-version
id: setup-magento id: setup-magento
with: with:
php-version: "8.3" php-version: "8.3"
@@ -89,7 +89,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: graycoreio/github-actions-magento2/setup-magento@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/setup-magento@v8.6.0 # x-release-please-version
id: setup-magento id: setup-magento
with: with:
php-version: "8.3" php-version: "8.3"
+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.3.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 }}
+2 -2
View File
@@ -68,7 +68,7 @@ with:
### Example ### Example
```yml ```yml
- uses: graycoreio/github-actions-magento2/supported-version@v8.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/supported-version@v8.6.0 # x-release-please-version
id: supported-version id: supported-version
with: with:
kind: currently-supported kind: currently-supported
@@ -94,7 +94,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.3.0 # x-release-please-version - uses: graycoreio/github-actions-magento2/supported-version@v8.6.0 # x-release-please-version
id: supported-version id: supported-version
- run: echo ${{ steps.supported-version.outputs.matrix }} - run: echo ${{ steps.supported-version.outputs.matrix }}
``` ```
+33 -48
View File
File diff suppressed because one or more lines are too long
@@ -4,10 +4,10 @@ import { getIndividualVersionsForProject } from "../versions/get-versions-for-pr
export const getCurrentlySupportedVersions = (project: string, date: Date): string[] => { export const getCurrentlySupportedVersions = (project: string, date: Date): string[] => {
const allVersions = getIndividualVersionsForProject(project) const allVersions = getIndividualVersionsForProject(project)
return Object.entries(<Record<string,PackageMatrixVersion>>allVersions) return Object.entries(<Record<string,PackageMatrixVersion>>allVersions)
.filter(([key, value]) => { .filter(([, value]) => {
const dayOfRelease = new Date(value.release); const dayOfRelease = new Date(value.release);
dayOfRelease.setSeconds(dayOfRelease.getSeconds() + 1); dayOfRelease.setSeconds(dayOfRelease.getSeconds() + 1);
return date >= dayOfRelease && new Date(value.eol) >= date; return date >= dayOfRelease && new Date(value.eol) >= date;
}) })
.map(([key, value]) => key); .map(([key]) => key);
} }
+1 -1
View File
@@ -42,5 +42,5 @@ export const getUsableVersions = (project: string): string[] => {
return true; return true;
}) })
.map(([key, value]) => key); .map(([key]) => key);
} }
+2 -2
View File
@@ -15,7 +15,7 @@ export const getRecentVersions = (project: string, date: Date, durationStr: stri
const allVersions = getIndividualVersionsForProject(project) const allVersions = getIndividualVersionsForProject(project)
return Object.entries(<Record<string,PackageMatrixVersion>>allVersions) return Object.entries(<Record<string,PackageMatrixVersion>>allVersions)
.filter(([key, value]) => { .filter(([, value]) => {
const dayOfRelease = new Date(value.release); const dayOfRelease = new Date(value.release);
dayOfRelease.setSeconds(dayOfRelease.getSeconds() + 1); dayOfRelease.setSeconds(dayOfRelease.getSeconds() + 1);
const dateAfterRelease = new Date(value.release); const dateAfterRelease = new Date(value.release);
@@ -27,5 +27,5 @@ export const getRecentVersions = (project: string, date: Date, durationStr: stri
return date >= dayOfRelease && date <= dateAfterRelease; return date >= dayOfRelease && date <= dateAfterRelease;
}) })
.map(([key, value]) => key); .map(([key]) => key);
} }
@@ -6,11 +6,11 @@ describe('validateKind', () => {
}); });
it('throws a helpful exception if its an invalid kind', () => { it('throws a helpful exception if its an invalid kind', () => {
expect(() => validateKind(<any>"taco")).toThrowError(); expect(() => validateKind(<any>"taco")).toThrow();
}) })
it('throws a helpful exception if custom versions are provided with the wrong kind', () => { it('throws a helpful exception if custom versions are provided with the wrong kind', () => {
expect(() => validateKind(<any>"latest", [])).toThrowError(); expect(() => validateKind(<any>"latest", [])).toThrow();
}) })
it('returns `true` for kind `custom` with a custom versions', () => { it('returns `true` for kind `custom` with a custom versions', () => {
+1 -1
View File
@@ -2,7 +2,7 @@ import { customVersionsValidator } from "./validations/custom-versions-validator
import { isKnownKind } from "./validations/is-known-kind"; import { isKnownKind } from "./validations/is-known-kind";
import { KindValidator } from "./validator"; import { KindValidator } from "./validator";
export const validateKind: KindValidator = (kind, custom_versions = null): boolean => { export const validateKind: KindValidator = (kind, custom_versions): boolean => {
return validators.reduce((acc, el) => el(kind, custom_versions), true); return validators.reduce((acc, el) => el(kind, custom_versions), true);
} }
@@ -62,7 +62,7 @@ describe('getMatrixForKind for mage-os', () => {
}); });
it('errors for invalid `custom``', () => { it('errors for invalid `custom``', () => {
expect(() => getMatrixForKind("custom", project)).toThrowError(); expect(() => getMatrixForKind("custom", project)).toThrow();
}); });
}) })
@@ -133,6 +133,6 @@ describe('getMatrixForKind for magento-open-source', () => {
}); });
it('errors for invalid `custom``', () => { it('errors for invalid `custom``', () => {
expect(() => getMatrixForKind("custom", project)).toThrowError(); expect(() => getMatrixForKind("custom", project)).toThrow();
}); });
}) })
@@ -12,13 +12,13 @@ export const getMatrixForKind = (kind: string, project: string, versions = "", r
switch(kind){ switch(kind){
case 'latest': case 'latest':
return getMatrixForVersions(project, latestJson[project]); return getMatrixForVersions(project, latestJson[project as keyof typeof latestJson]);
case 'currently-supported': case 'currently-supported':
return getMatrixForVersions(project, getCurrentlySupportedVersions(project, new Date())); return getMatrixForVersions(project, getCurrentlySupportedVersions(project, new Date()));
case 'usable': case 'usable':
return getMatrixForVersions(project, getUsableVersions(project)); return getMatrixForVersions(project, getUsableVersions(project));
case 'nightly': case 'nightly':
return amendMatrixForNext(getMatrixForVersions(project, nightlyJson[project]), 'https://upstream-nightly.mage-os.org', getDayBefore()); return amendMatrixForNext(getMatrixForVersions(project, nightlyJson[project as keyof typeof nightlyJson]), 'https://upstream-nightly.mage-os.org', getDayBefore());
case 'all': case 'all':
return getMatrixForVersions(project, Object.keys(getIndividualVersionsForProject(project))); return getMatrixForVersions(project, Object.keys(getIndividualVersionsForProject(project)));
case 'custom': case 'custom':
@@ -3,7 +3,7 @@ import { Repository } from "./repository";
/** /**
* Get the next version of Magento, as determined by the repository. * Get the next version of Magento, as determined by the repository.
*/ */
export const getNextVersion = (repository: Repository, date: Date) => { export const getNextVersion = (repository: Repository, _date: Date) => {
switch(repository){ switch(repository){
case "https://nightly.mage-os.org": case "https://nightly.mage-os.org":
case "https://upstream-nightly.mage-os.org": case "https://upstream-nightly.mage-os.org":
+1 -1
View File
@@ -1,4 +1,4 @@
const KNOWN_REPOSITORIES = { export const KNOWN_REPOSITORIES = {
"https://repo.mage-os.org": true, "https://repo.mage-os.org": true,
"https://nightly.mage-os.org": true, "https://nightly.mage-os.org": true,
"https://upstream-mirror.mage-os.org": true, "https://upstream-mirror.mage-os.org": true,
@@ -8,6 +8,6 @@ describe('validateProject', () => {
}); });
it('throws a helpful exception if it is an invalid project', () => { it('throws a helpful exception if it is an invalid project', () => {
expect(() => validateProject(<any>"quark")).toThrowError(); expect(() => validateProject(<any>"quark")).toThrow();
}) })
}) })
@@ -9,6 +9,6 @@ describe('isKnownProject', () => {
}); });
it('throws a message if for unknown projects', () => { it('throws a message if for unknown projects', () => {
expect(() => isKnownProject(<Project>"bingo")).toThrowError() expect(() => isKnownProject(<Project>"bingo")).toThrow()
}); });
}) })
@@ -1,5 +1,7 @@
import { buildServicesForEntry } from './build-services'; import { buildServicesForEntry } from './build-services';
import { PackageMatrixVersion } from '../matrix/matrix-type'; import { PackageMatrixVersion } from '../matrix/matrix-type';
import mageOsMinimalIndividual from '../versions/mage-os-minimal/individual.json';
import mageOsMinimalComposite from '../versions/mage-os-minimal/composite.json';
const createTestEntry = (overrides: Partial<PackageMatrixVersion> = {}): PackageMatrixVersion => ({ const createTestEntry = (overrides: Partial<PackageMatrixVersion> = {}): PackageMatrixVersion => ({
magento: 'magento/project-community-edition:2.4.7', magento: 'magento/project-community-edition:2.4.7',
@@ -362,4 +364,17 @@ describe('buildServicesForEntry', () => {
expect(withPref).toEqual(withoutPref); expect(withPref).toEqual(withoutPref);
}); });
}); });
describe('mage-os-minimal', () => {
const minimalEntries: [string, PackageMatrixVersion][] = [
...Object.entries(mageOsMinimalIndividual as unknown as Record<string, PackageMatrixVersion>),
...Object.entries(mageOsMinimalComposite as unknown as Record<string, PackageMatrixVersion>)
];
it.each(minimalEntries)('omits rabbitmq from services for %s', (_key, entry) => {
const services = buildServicesForEntry(entry);
expect(services.rabbitmq).toBeUndefined();
});
});
}); });
@@ -35,16 +35,16 @@ describe('parseServicePreferences', () => {
}); });
it('throws on unknown service name', () => { it('throws on unknown service name', () => {
expect(() => parseServicePreferences('foobar')).toThrowError(/unknown service "foobar"/); expect(() => parseServicePreferences('foobar')).toThrow(/unknown service "foobar"/);
}); });
it('throws on a collision in the search tier', () => { it('throws on a collision in the search tier', () => {
expect(() => parseServicePreferences('elasticsearch,opensearch')).toThrowError( expect(() => parseServicePreferences('elasticsearch,opensearch')).toThrow(
/collision in tier "search"/ /collision in tier "search"/
); );
}); });
it('throws on a collision in the cache tier', () => { it('throws on a collision in the cache tier', () => {
expect(() => parseServicePreferences('redis,valkey')).toThrowError(/collision in tier "cache"/); expect(() => parseServicePreferences('redis,valkey')).toThrow(/collision in tier "cache"/);
}); });
}); });
@@ -31,7 +31,7 @@ describe('validatePreferencesAgainstMatrix', () => {
baseEntry({ version: '2.4.7' }), baseEntry({ version: '2.4.7' }),
baseEntry({ version: '2.4.5', opensearch: '' }), baseEntry({ version: '2.4.5', opensearch: '' }),
]; ];
expect(() => validatePreferencesAgainstMatrix({ search: 'opensearch' }, entries)).toThrowError( expect(() => validatePreferencesAgainstMatrix({ search: 'opensearch' }, entries)).toThrow(
/not satisfied for:\n\s+- magento 2\.4\.5 \(supported: elasticsearch\)/ /not satisfied for:\n\s+- magento 2\.4\.5 \(supported: elasticsearch\)/
); );
}); });
@@ -41,14 +41,14 @@ describe('validatePreferencesAgainstMatrix', () => {
baseEntry({ version: '2.4.5', opensearch: '' }), baseEntry({ version: '2.4.5', opensearch: '' }),
baseEntry({ version: '2.4.4', opensearch: '' }), baseEntry({ version: '2.4.4', opensearch: '' }),
]; ];
expect(() => validatePreferencesAgainstMatrix({ search: 'opensearch' }, entries)).toThrowError( expect(() => validatePreferencesAgainstMatrix({ search: 'opensearch' }, entries)).toThrow(
/magento 2\.4\.5[\s\S]*magento 2\.4\.4/ /magento 2\.4\.5[\s\S]*magento 2\.4\.4/
); );
}); });
it('reports "<none>" when the entry supports nothing in the tier', () => { it('reports "<none>" when the entry supports nothing in the tier', () => {
const entries = [baseEntry({ version: '2.4.0', opensearch: '', elasticsearch: '' })]; const entries = [baseEntry({ version: '2.4.0', opensearch: '', elasticsearch: '' })];
expect(() => validatePreferencesAgainstMatrix({ search: 'opensearch' }, entries)).toThrowError( expect(() => validatePreferencesAgainstMatrix({ search: 'opensearch' }, entries)).toThrow(
/magento 2\.4\.0 \(supported: <none>\)/ /magento 2\.4\.0 \(supported: <none>\)/
); );
}); });
@@ -9,7 +9,7 @@ describe('getIndividialVersionsForProject', () => {
}) })
it('throws error if no individual versions are specified for given project', () => { it('throws error if no individual versions are specified for given project', () => {
expect(() => getIndividualVersionsForProject(<Project>"ahsoka")).toThrowError() expect(() => getIndividualVersionsForProject(<Project>"ahsoka")).toThrow()
}) })
}) })
@@ -21,6 +21,6 @@ describe('getCompositeVersionsForProject', () => {
}) })
it('throws error if no composite versions are specified for given project', () => { it('throws error if no composite versions are specified for given project', () => {
expect(() => getCompositeVersionsForProject(<Project>"spock")).toThrowError() expect(() => getCompositeVersionsForProject(<Project>"spock")).toThrow()
}) })
}) })
@@ -1,16 +1,22 @@
import { validateProject } from "../project/validate-projects"; import { validateProject } from "../project/validate-projects";
import { PackageMatrixVersion } from "../matrix/matrix-type"; import { PackageMatrixVersion } from "../matrix/matrix-type";
import mageOsIndividual from './mage-os/individual.json';
import mageOsMinimalIndividual from './mage-os-minimal/individual.json';
import magentoOpenSourceIndividual from './magento-open-source/individual.json';
import mageOsComposite from './mage-os/composite.json';
import mageOsMinimalComposite from './mage-os-minimal/composite.json';
import magentoOpenSourceComposite from './magento-open-source/composite.json';
const individual = { const individual = <Record<string, Record<string, PackageMatrixVersion>>><unknown>{
'mage-os': require('./mage-os/individual.json'), 'mage-os': mageOsIndividual,
'mage-os-minimal': require('./mage-os-minimal/individual.json'), 'mage-os-minimal': mageOsMinimalIndividual,
'magento-open-source': require('./magento-open-source/individual.json') 'magento-open-source': magentoOpenSourceIndividual
} }
const composite = { const composite = <Record<string, Record<string, PackageMatrixVersion>>><unknown>{
'mage-os': require('./mage-os/composite.json'), 'mage-os': mageOsComposite,
'mage-os-minimal': require('./mage-os-minimal/composite.json'), 'mage-os-minimal': mageOsMinimalComposite,
'magento-open-source': require('./magento-open-source/composite.json') 'magento-open-source': magentoOpenSourceComposite
} }
export const getIndividualVersionsForProject = (project: string): Record<string, PackageMatrixVersion> => { export const getIndividualVersionsForProject = (project: string): Record<string, PackageMatrixVersion> => {
@@ -5,7 +5,6 @@
"composer": "2.9.8", "composer": "2.9.8",
"mysql": "mysql:8.4", "mysql": "mysql:8.4",
"opensearch": "opensearchproject/opensearch:3", "opensearch": "opensearchproject/opensearch:3",
"rabbitmq": "rabbitmq:4.2-management",
"valkey": "valkey/valkey:9", "valkey": "valkey/valkey:9",
"varnish": "varnish:8", "varnish": "varnish:8",
"nginx": "nginx:1.28", "nginx": "nginx:1.28",
@@ -19,7 +18,6 @@
"composer": "2.9.8", "composer": "2.9.8",
"mysql": "mysql:8.4", "mysql": "mysql:8.4",
"opensearch": "opensearchproject/opensearch:3", "opensearch": "opensearchproject/opensearch:3",
"rabbitmq": "rabbitmq:4.2-management",
"valkey": "valkey/valkey:9", "valkey": "valkey/valkey:9",
"varnish": "varnish:8", "varnish": "varnish:8",
"nginx": "nginx:1.28", "nginx": "nginx:1.28",
@@ -33,7 +31,6 @@
"composer": "2.9.8", "composer": "2.9.8",
"mysql": "mysql:8.4", "mysql": "mysql:8.4",
"opensearch": "opensearchproject/opensearch:3", "opensearch": "opensearchproject/opensearch:3",
"rabbitmq": "rabbitmq:4.2-management",
"valkey": "valkey/valkey:9", "valkey": "valkey/valkey:9",
"varnish": "varnish:8", "varnish": "varnish:8",
"nginx": "nginx:1.28", "nginx": "nginx:1.28",
@@ -6,7 +6,6 @@
"composer": "2.9.8", "composer": "2.9.8",
"mysql": "mysql:8.4", "mysql": "mysql:8.4",
"opensearch": "opensearchproject/opensearch:3", "opensearch": "opensearchproject/opensearch:3",
"rabbitmq": "rabbitmq:4.2-management",
"valkey": "valkey/valkey:9", "valkey": "valkey/valkey:9",
"varnish": "varnish:8", "varnish": "varnish:8",
"nginx": "nginx:1.28", "nginx": "nginx:1.28",
+3
View File
@@ -2,5 +2,8 @@
"compilerOptions": { "compilerOptions": {
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,
"typeRoots": ["../node_modules/@types"],
"types": ["jest"],
"target": "ES2022"
} }
} }