Repository navigation
test(easyengine): add a Behat suite and CI workflow #1950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mrrobot47
wants to merge
32
commits into
EasyEngine:develop
Choose a base branch
from
mrrobot47:test/behat-suite-r3
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,102
−53
Open
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
c5de6bf
test(easyengine): add behat context with shared steps
mrrobot47 a92140b
test(easyengine): skip destructive scenarios by default
mrrobot47 13866c4
test(easyengine): cover cli version, info, check-update and update
mrrobot47 a264a8f
test(easyengine): cover cli dumps, completions, aliases and has-command
mrrobot47 c4f4563
test(easyengine): cover help for every command and unknown commands
mrrobot47 88c1b39
test(easyengine): cover global flags, config files and requirement ch…
mrrobot47 1d5203b
test(easyengine): cover migration triggering and the phar build
mrrobot47 b38c722
docs(easyengine): add behat coverage inventory
mrrobot47 be0d332
test(easyengine): fix the optional "not" in output steps
mrrobot47 bae335a
test(easyengine): keep destructive scenarios and the phar build off t…
mrrobot47 ecc39f7
test(easyengine): tighten assertions and cover config defaults, packa…
mrrobot47 126815c
docs(easyengine): update behat coverage inventory after review
mrrobot47 e850afd
test(easyengine): cover reverting container migrations when the image…
mrrobot47 3e8051e
test(easyengine): wait for dockerd to re-read /etc/hosts after blocki…
mrrobot47 cc47a16
test(behat): expect remote aliases, --ssh/--http and an ssh config ke…
mrrobot47 f9f0543
test(easyengine): run the LOG-1 argv scenario as a regular one
mrrobot47 300bd39
ci: run the behat suite on GitHub Actions
mrrobot47 5a4f6dc
ci: finalize the behat workflow
mrrobot47 116aec5
ci: re-fetch docker-compose when the first run leaves a broken binary
mrrobot47 4a90a87
ci(test): stop running the site-command and site-type-wp suites in co…
mrrobot47 80ba60e
ci(test): let core's suite initialise EasyEngine in the test job
mrrobot47 ac5b9cd
test(behat): leave the known-bug scenarios out until their fixes land
mrrobot47 ac937b7
test(easyengine): guard the last site list of the ssh-key scenario an…
mrrobot47 a6b1c58
test(easyengine): make sure the image migration rollback scenario has…
mrrobot47 188e210
test(easyengine): drop the unused subsite step
mrrobot47 3a8bad5
docs(easyengine): describe coverage gaps without workspace references
mrrobot47 0610274
ci(easyengine): run the behat lanes on every supported PHP version
mrrobot47 5fa5fc9
test(easyengine): match lists by membership in compareContents
mrrobot47 c9d26f1
test(easyengine): trust only a private scratch dir and force the self…
mrrobot47 3263f81
ci(test): stop running the site-type-php suite in core's test job
mrrobot47 b717a91
test(easyengine): fail the uninstall and container count checks when …
mrrobot47 3ec5897
Merge remote-tracking branch 'upstream/develop' into test/behat-suite-r3
mrrobot47 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| # Behat suite of EasyEngine core (features/), run on a throwaway runner. | ||
| name: Behat | ||
|
|
||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: | ||
| - develop | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: behat-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| env: | ||
| COMPOSE_VERSION: v5.5.1 | ||
| # No public DNS (Let's Encrypt) or real backup storage on hosted runners. | ||
| EXCLUDE_TAGS: '~@le&&~@remote-real' | ||
| COMPOSE_INTERACTIVE_NO_CLI: 1 | ||
| # The suite's guards: a fresh runner has no ee.sqlite, and every lane may change global state. | ||
| EE_BEHAT_ALLOW_INIT: 1 | ||
| EE_BEHAT_DESTRUCTIVE: 1 | ||
|
|
||
| jobs: | ||
| behat: | ||
| name: Behat ${{ matrix.lane }} (PHP ${{ matrix.php }}) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 90 | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] | ||
| lane: [main, migration, uninstall] | ||
| include: | ||
| # Default profile plus @destructive; the first checked command initialises EasyEngine. | ||
| - lane: main | ||
| args: --tags="${EXCLUDE_TAGS}&&~@migration&&~@uninstall&&~@known-bug" | ||
| # The real upgrade pipeline and the self-uninstall need an initialised install, and a job each. | ||
| - lane: migration | ||
| args: -p migration | ||
| init: true | ||
| allow_migration: 1 | ||
| - lane: uninstall | ||
| args: -p uninstall | ||
| init: true | ||
| allow_uninstall: 1 | ||
| env: | ||
| EE_BEHAT_ALLOW_MIGRATION: ${{ matrix.allow_migration }} | ||
| EE_BEHAT_ALLOW_UNINSTALL: ${{ matrix.allow_uninstall }} | ||
| steps: | ||
| - name: Check out source code | ||
| uses: actions/checkout@v7 | ||
|
|
||
| - name: Set up PHP | ||
| uses: shivammathur/setup-php@v2 | ||
| with: | ||
| php-version: ${{ matrix.php }} | ||
| coverage: none | ||
| tools: composer | ||
| extensions: pcntl, curl, sqlite3, zip, dom, mbstring, json | ||
|
|
||
| - name: Get Composer cache directory | ||
| id: composer-cache | ||
| run: echo "dir=$(composer config -g cache-files-dir)" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Set up Composer caching | ||
| uses: actions/cache@v6 | ||
| with: | ||
| path: ${{ steps.composer-cache.outputs.dir }} | ||
| key: ${{ runner.os }}-composer-php${{ matrix.php }}-${{ github.sha }} | ||
| restore-keys: ${{ runner.os }}-composer-php${{ matrix.php }}- | ||
|
|
||
| - name: Prepare the host | ||
| run: | | ||
| # The runner image ships web and database servers; EE needs 80/443. | ||
| sudo systemctl stop nginx apache2 mysql 2>/dev/null || true | ||
| sudo ss -ltnp | grep -E ':(80|443|3306) ' || true | ||
| sudo curl -fsSL "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-linux-$(uname -m)" -o /usr/local/bin/docker-compose | ||
| sudo chmod +x /usr/local/bin/docker-compose | ||
| docker version --format 'docker {{.Server.Version}}' | ||
| docker-compose version | ||
| openssl version | ||
| echo "umask $(umask), under sudo $(sudo sh -c umask)" | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| # Every easyengine/* package on dev-develop, as core CI does on PRs. | ||
| php -r ' | ||
| $j = json_decode(file_get_contents("composer.json"), true); | ||
| foreach ($j["require"] as $k => $v) if (strpos($k, "easyengine/") === 0) $j["require"][$k] = "dev-develop"; | ||
| file_put_contents("composer.json", json_encode($j, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");' | ||
| composer update --prefer-dist --no-progress --no-interaction | ||
| # Some package commands shell out to `ee` from PATH. | ||
| sudo ln -sf "$PWD/bin/ee" /usr/local/bin/ee | ||
| cat VERSION | ||
|
|
||
| - name: Initialise EasyEngine | ||
| if: matrix.init | ||
| run: | | ||
| # The first non-cli command runs the initial setup (DB, config, image pulls). | ||
| sudo -E bin/ee site list || true | ||
| sudo -E bin/ee cli info | ||
| # The first run's compose download has no --fail: a GitHub error page can replace the binary. | ||
| if ! docker-compose version; then | ||
| sudo curl -fsSL --retry 5 "https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-$(uname -m)" -o /usr/local/bin/docker-compose | ||
| sudo chmod +x /usr/local/bin/docker-compose | ||
| docker-compose version | ||
| fi | ||
|
|
||
| - name: Run Behat | ||
| shell: 'script -q -e -c "bash {0}"' | ||
| # A hung step (e.g. a command stopped on the TTY) should still leave logs. | ||
| timeout-minutes: 75 | ||
| env: | ||
| # check-update calls the GitHub API; authenticated requests get a higher rate limit. | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| set -e | ||
| mkdir -p "$RUNNER_TEMP/behat/junit" | ||
| # A CLI --tags replaces the profile filter, so the exclusions only apply to the --tags lanes. | ||
| sudo -E vendor/bin/behat --strict ${{ matrix.args }} \ | ||
| --format=progress --out=std \ | ||
| --format=junit --out="$RUNNER_TEMP/behat/junit" \ | ||
| --format=pretty --out="$RUNNER_TEMP/behat/pretty.log" | ||
|
|
||
| - name: Collect logs | ||
| if: always() | ||
| run: | | ||
| out="$RUNNER_TEMP/behat" | ||
| mkdir -p "$out" | ||
| sudo cp /opt/easyengine/logs/ee.log "$out/" 2>/dev/null || echo 'No ee.log.' | ||
| sudo stat -c '%a %U:%G %n' /opt/easyengine /opt/easyengine/logs > "$out/dirs.txt" 2>/dev/null || true | ||
| sudo docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Status}}' > "$out/containers.txt" || true | ||
| sudo docker images --format '{{.Repository}}:{{.Tag}}' > "$out/images.txt" || true | ||
| sudo chown -R "$USER" "$out" | ||
| # One block per failed scenario, from the junit files. | ||
| python3 - "$out" <<'PY' | ||
| import glob, re, sys, xml.etree.ElementTree as ET | ||
| out = sys.argv[1] | ||
| lines = [] | ||
| for f in sorted(glob.glob(out + '/junit/*.xml')): | ||
| # Behat copies raw output (docker logs with ANSI escapes) into the junit file; XML 1.0 forbids those bytes. | ||
| data = re.sub(rb'[\x00-\x08\x0b\x0c\x0e-\x1f]', b'', open(f, 'rb').read()) | ||
| for case in ET.fromstring(data).iter('testcase'): | ||
| for bad in list(case.findall('failure')) + list(case.findall('error')): | ||
| lines.append('### %s :: %s\n%s\n%s\n' % (case.get('classname'), case.get('name'), bad.get('message') or '', bad.text or '')) | ||
| open(out + '/failures.txt', 'w').write('\n'.join(lines) or 'No failures.\n') | ||
| print('%d failed scenarios' % len(lines)) | ||
| PY | ||
| head -c 20000 "$out/failures.txt" | ||
|
|
||
| - name: Upload results | ||
| if: always() | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: behat-${{ matrix.lane }}-php${{ matrix.php }} | ||
| path: ${{ runner.temp }}/behat | ||
| retention-days: 14 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # The default profile skips everything that changes the live EasyEngine state. Those scenarios are also refused | ||
| # at runtime unless their env var is set (see FeatureContext::GUARDED_TAGS), because a CLI --tags option | ||
| # replaces these filters: | ||
| # @destructive EE_BEHAT_DESTRUCTIVE=1 DB version changes, the self-uninstall prompt | ||
| # @migration EE_BEHAT_ALLOW_MIGRATION=1 a real upgrade migration (image pulls, cron restart, log rotation) | ||
| # @uninstall EE_BEHAT_ALLOW_UNINSTALL=1 `ee cli self-uninstall --yes`; run it alone and last | ||
| # On a fresh machine (no /opt/easyengine/db/ee.sqlite) also set EE_BEHAT_ALLOW_INIT=1. | ||
| default: | ||
| gherkin: | ||
| filters: | ||
| tags: "~@destructive" | ||
|
|
||
| destructive: | ||
| gherkin: | ||
| filters: | ||
| tags: "@destructive&&~@migration&&~@uninstall" | ||
|
|
||
| migration: | ||
| gherkin: | ||
| filters: | ||
| tags: "@migration" | ||
|
|
||
| uninstall: | ||
| gherkin: | ||
| filters: | ||
| tags: "@uninstall" | ||
|
|
||
| # Everything that can run in one pass (the uninstall would break every scenario after it). | ||
| all: | ||
| gherkin: | ||
| filters: | ||
| tags: "~@migration&&~@uninstall" |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.