Skip to content

Commit 9ec1fe0

Browse files
committed
Fixed PHPUnit to run only in the first CI container + docs.
1 parent daea0fb commit 9ec1fe0

28 files changed

Lines changed: 692 additions & 443 deletions

File tree

.circleci/config.yml

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -376,40 +376,19 @@ jobs:
376376
#;< TOOL_PHPUNIT
377377
- run:
378378
name: Test with PHPUnit
379-
command: docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ]
380-
#;> TOOL_PHPUNIT
381-
382-
#;< TOOL_BEHAT
383-
- run:
384-
name: Test with Behat
385379
command: |
386-
if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi
387-
echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile"
388-
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \
389-
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \
390-
[ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ]
391-
no_output_timeout: 30m
392-
#;> TOOL_BEHAT
380+
[ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0
381+
docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ]
393382
394383
- run:
395-
name: Process test logs and artifacts
384+
name: Process PHPUnit logs and coverage
396385
command: |
397-
mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}"
386+
[ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0
387+
mkdir -p "${VORTEX_CI_ARTIFACTS}"
398388
if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then
399389
docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/"
400-
if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then
401-
docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/"
402-
fi
403390
fi
404-
when: always
405-
406-
- store_test_results:
407-
path: *test_results
408391
409-
- store_artifacts:
410-
path: *artifacts
411-
412-
#;< TOOL_PHPUNIT
413392
- run:
414393
name: Check code coverage threshold
415394
command: |
@@ -439,14 +418,45 @@ jobs:
439418
-d "$(jq -n --arg body "\`\`\`
440419
${COVERAGE_CONTENT}
441420
\`\`\`" '{body: $body}')"
442-
#;> TOOL_PHPUNIT
443421
444422
- run:
445423
name: Upload code coverage reports to Codecov
446424
command: |
425+
[ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0
447426
if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then
448427
codecov -Z -s /tmp/artifacts/coverage;
449428
fi
429+
#;> TOOL_PHPUNIT
430+
431+
#;< TOOL_BEHAT
432+
- run:
433+
name: Test with Behat
434+
command: |
435+
if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi
436+
echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile"
437+
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \
438+
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \
439+
[ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ]
440+
no_output_timeout: 30m
441+
#;> TOOL_BEHAT
442+
443+
- run:
444+
name: Process test logs and artifacts
445+
command: |
446+
mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}"
447+
if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then
448+
docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/"
449+
if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then
450+
docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/"
451+
fi
452+
fi
453+
when: always
454+
455+
- store_test_results:
456+
path: *test_results
457+
458+
- store_artifacts:
459+
path: *artifacts
450460

451461
- persist_to_workspace:
452462
root: /tmp/workspace

.github/workflows/build-test-deploy.yml

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -357,42 +357,18 @@ jobs:
357357

358358
#;< TOOL_PHPUNIT
359359
- name: Test with PHPUnit
360+
if: ${{ matrix.instance == 0 || strategy.job-total == 1 }}
360361
run: docker compose exec -T cli vendor/bin/phpunit
361362
continue-on-error: ${{ vars.VORTEX_CI_PHPUNIT_IGNORE_FAILURE == '1' }}
362-
#;> TOOL_PHPUNIT
363-
364-
#;< TOOL_BEHAT
365-
- name: Test with Behat
366-
run: |
367-
# shellcheck disable=SC2170
368-
if [ ${{ strategy.job-total }} -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${{ strategy.job-index }}}"; fi
369-
echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile"
370-
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \
371-
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}"
372-
env:
373-
VORTEX_CI_BEHAT_PROFILE: ${{ vars.VORTEX_CI_BEHAT_PROFILE }}
374-
continue-on-error: ${{ vars.VORTEX_CI_BEHAT_IGNORE_FAILURE == '1' }}
375-
timeout-minutes: 30
376-
#;> TOOL_BEHAT
377363

378-
- name: Process test logs and artifacts
379-
if: always()
364+
- name: Process PHPUnit logs and coverage
365+
if: ${{ matrix.instance == 0 || strategy.job-total == 1 }}
380366
run: |
381367
mkdir -p ".logs"
382368
if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then
383369
docker compose cp cli:/app/.logs/. ".logs/"
384370
fi
385371
386-
- name: Upload test artifacts
387-
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
388-
if: always()
389-
with:
390-
name: test-artifacts-${{ matrix.instance }}
391-
path: .logs
392-
include-hidden-files: true
393-
if-no-files-found: error
394-
395-
#;< TOOL_PHPUNIT
396372
- name: Check code coverage threshold
397373
if: ${{ matrix.instance == 0 || strategy.job-total == 1 }}
398374
run: |
@@ -419,7 +395,7 @@ jobs:
419395

420396
- name: Upload coverage report to Codecov
421397
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
422-
if: ${{ env.CODECOV_TOKEN != '' }}
398+
if: ${{ (matrix.instance == 0 || strategy.job-total == 1) && env.CODECOV_TOKEN != '' }}
423399
with:
424400
directory: .logs/coverage
425401
fail_ci_if_error: true
@@ -428,6 +404,37 @@ jobs:
428404
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
429405
#;> TOOL_PHPUNIT
430406

407+
#;< TOOL_BEHAT
408+
- name: Test with Behat
409+
run: |
410+
# shellcheck disable=SC2170
411+
if [ ${{ strategy.job-total }} -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${{ strategy.job-index }}}"; fi
412+
echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile"
413+
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \
414+
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}"
415+
env:
416+
VORTEX_CI_BEHAT_PROFILE: ${{ vars.VORTEX_CI_BEHAT_PROFILE }}
417+
continue-on-error: ${{ vars.VORTEX_CI_BEHAT_IGNORE_FAILURE == '1' }}
418+
timeout-minutes: 30
419+
#;> TOOL_BEHAT
420+
421+
- name: Process test logs and artifacts
422+
if: always()
423+
run: |
424+
mkdir -p ".logs"
425+
if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then
426+
docker compose cp cli:/app/.logs/. ".logs/"
427+
fi
428+
429+
- name: Upload test artifacts
430+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
431+
if: always()
432+
with:
433+
name: test-artifacts-${{ matrix.instance }}
434+
path: .logs
435+
include-hidden-files: true
436+
if-no-files-found: error
437+
431438
- name: Upload exported codebase as an artifact
432439
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
433440
if: ${{ matrix.instance == 0 && !startsWith(github.head_ref || github.ref_name, 'deps/') && contains(env.VORTEX_DEPLOY_TYPES, 'artifact') }}

.vortex/docs/content/continuous-integration/README.mdx

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ import CodeLifecycle from '../_code-lifecycle.mdx';
3535
- Validates Composer lock and audits dependencies
3636
- Assembles the codebase by installing dependencies
3737
- Provisions a website
38-
- Lints code (only on the first instance)
39-
- Runs unit tests
40-
- Runs BDD tests
41-
- Generates code coverage reports
38+
- Lints code (first instance only)
39+
- Runs PHPUnit tests (first instance only)
40+
- Checks code coverage and posts PR comment (first instance only)
41+
- Runs BDD tests (distributed across all instances)
4242
- Collects and stores test results and artifacts
4343

4444
### 3. Deployment
@@ -102,6 +102,64 @@ The continuous integration pipeline is triggered by:
102102
- **Tags** matching semantic version (`1.2.3`, `1.2.3-rc.1`) or date-based (`2023-04-17`) patterns
103103
- **Scheduled runs** for automatic database caching
104104

105+
## Test parallelism
106+
107+
The build job runs across multiple parallel containers (2 by default) to speed
108+
up test execution. Since each container runs the full build and provision
109+
steps, the test workload is distributed to make the best use of each container.
110+
111+
### What runs where
112+
113+
| Task | First container | Other containers |
114+
|------|:---:|:---:|
115+
| Code linting (PHPCS, PHPStan, Rector, etc.) |||
116+
| PHPUnit tests |||
117+
| Code coverage check and PR comment |||
118+
| Behat tests | ✓ (profile `p0`) | ✓ (profile `p1`, `p2`, ...) |
119+
120+
Linting, PHPUnit, and coverage reporting run exclusively on the first container
121+
to avoid duplicate work. Behat tests run on all containers using profile-based
122+
distribution.
123+
124+
### Balancing Behat tests
125+
126+
Because the first container handles linting, PHPUnit, and coverage in addition
127+
to Behat tests, it has more work to do than the other containers. To keep
128+
overall build time low, assign more Behat scenarios to the non-first containers.
129+
130+
Behat scenarios are assigned to containers using profile tags. Tag a scenario
131+
with `@p0` to run it on the first container or `@p1` to run it on the second:
132+
133+
```gherkin
134+
@p0
135+
Scenario: Quick smoke test
136+
Given I go to the homepage
137+
Then I should see "Welcome"
138+
139+
@p1
140+
Scenario: Full content workflow
141+
Given I am logged in as a content editor
142+
...
143+
```
144+
145+
Scenarios without a profile tag default to the first container. When only one
146+
container is available, all scenarios run regardless of tags.
147+
148+
:::tip
149+
150+
As a rule of thumb, keep lightweight or smoke-test scenarios on the first
151+
container (`@p0`) and move heavier or more numerous scenarios to additional
152+
containers (`@p1`, `@p2`, etc.). This keeps the total build time closer to the
153+
duration of the longest single container rather than the sum of all tests.
154+
155+
:::
156+
157+
See the provider-specific pages for how to change the number of parallel
158+
containers:
159+
160+
- [CircleCI — Change test parallelism](/docs/continuous-integration/circleci#change-test-parallelism)
161+
- [GitHub Actions — Change test parallelism](/docs/continuous-integration/github-actions#change-test-parallelism)
162+
105163
## Maintenance
106164

107165
### Enable debug mode

.vortex/docs/content/continuous-integration/circleci.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@ build:
146146
parallelism: 4 # Run tests across 4 containers
147147
```
148148

149+
When adding more containers, distribute Behat scenarios across them using
150+
profile tags (`@p0`, `@p1`, `@p2`, etc.) in your feature files. Since the first
151+
container also runs linting, PHPUnit, and coverage, assign more Behat scenarios
152+
to the additional containers to keep build times balanced.
153+
154+
See [Test parallelism](/docs/continuous-integration#test-parallelism) for details
155+
on how tests are distributed across containers.
156+
149157
### SSH access for debugging
150158

151159
CircleCI provides built-in SSH access to debug failing builds. Click the

.vortex/docs/content/continuous-integration/github-actions.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ strategy:
141141
instance: [0, 1, 2, 3] # Run tests across 4 containers
142142
```
143143

144+
When adding more containers, distribute Behat scenarios across them using
145+
profile tags (`@p0`, `@p1`, `@p2`, etc.) in your feature files. Since the first
146+
container also runs linting, PHPUnit, and coverage, assign more Behat scenarios
147+
to the additional containers to keep build times balanced.
148+
149+
See [Test parallelism](/docs/continuous-integration#test-parallelism) for details
150+
on how tests are distributed across containers.
151+
144152
### Terminal access for debugging
145153

146154
The workflow includes a `tmate` session for debugging. Trigger a manual workflow

.vortex/installer/tests/Fixtures/handler_process/_baseline/.github/workflows/build-test-deploy.yml

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -315,38 +315,18 @@ jobs:
315315
timeout-minutes: 30
316316

317317
- name: Test with PHPUnit
318+
if: ${{ matrix.instance == 0 || strategy.job-total == 1 }}
318319
run: docker compose exec -T cli vendor/bin/phpunit
319320
continue-on-error: ${{ vars.VORTEX_CI_PHPUNIT_IGNORE_FAILURE == '1' }}
320321

321-
- name: Test with Behat
322-
run: |
323-
# shellcheck disable=SC2170
324-
if [ ${{ strategy.job-total }} -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${{ strategy.job-index }}}"; fi
325-
echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile"
326-
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \
327-
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}"
328-
env:
329-
VORTEX_CI_BEHAT_PROFILE: ${{ vars.VORTEX_CI_BEHAT_PROFILE }}
330-
continue-on-error: ${{ vars.VORTEX_CI_BEHAT_IGNORE_FAILURE == '1' }}
331-
timeout-minutes: 30
332-
333-
- name: Process test logs and artifacts
334-
if: always()
322+
- name: Process PHPUnit logs and coverage
323+
if: ${{ matrix.instance == 0 || strategy.job-total == 1 }}
335324
run: |
336325
mkdir -p ".logs"
337326
if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then
338327
docker compose cp cli:/app/.logs/. ".logs/"
339328
fi
340329
341-
- name: Upload test artifacts
342-
uses: actions/upload-artifact@__HASH__ # __VERSION__
343-
if: always()
344-
with:
345-
name: test-artifacts-${{ matrix.instance }}
346-
path: .logs
347-
include-hidden-files: true
348-
if-no-files-found: error
349-
350330
- name: Check code coverage threshold
351331
if: ${{ matrix.instance == 0 || strategy.job-total == 1 }}
352332
run: |
@@ -373,14 +353,43 @@ jobs:
373353

374354
- name: Upload coverage report to Codecov
375355
uses: codecov/codecov-action@__HASH__ # __VERSION__
376-
if: ${{ env.CODECOV_TOKEN != '' }}
356+
if: ${{ (matrix.instance == 0 || strategy.job-total == 1) && env.CODECOV_TOKEN != '' }}
377357
with:
378358
directory: .logs/coverage
379359
fail_ci_if_error: true
380360
token: ${{ secrets.CODECOV_TOKEN }}
381361
env:
382362
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
383363

364+
- name: Test with Behat
365+
run: |
366+
# shellcheck disable=SC2170
367+
if [ ${{ strategy.job-total }} -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${{ strategy.job-index }}}"; fi
368+
echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile"
369+
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \
370+
docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}"
371+
env:
372+
VORTEX_CI_BEHAT_PROFILE: ${{ vars.VORTEX_CI_BEHAT_PROFILE }}
373+
continue-on-error: ${{ vars.VORTEX_CI_BEHAT_IGNORE_FAILURE == '1' }}
374+
timeout-minutes: 30
375+
376+
- name: Process test logs and artifacts
377+
if: always()
378+
run: |
379+
mkdir -p ".logs"
380+
if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then
381+
docker compose cp cli:/app/.logs/. ".logs/"
382+
fi
383+
384+
- name: Upload test artifacts
385+
uses: actions/upload-artifact@__HASH__ # __VERSION__
386+
if: always()
387+
with:
388+
name: test-artifacts-${{ matrix.instance }}
389+
path: .logs
390+
include-hidden-files: true
391+
if-no-files-found: error
392+
384393
- name: Upload exported codebase as an artifact
385394
uses: actions/upload-artifact@__HASH__ # __VERSION__
386395
if: ${{ matrix.instance == 0 && !startsWith(github.head_ref || github.ref_name, 'deps/') && contains(env.VORTEX_DEPLOY_TYPES, 'artifact') }}

0 commit comments

Comments
 (0)