Skip to content

Commit f4d79e4

Browse files
committed
Add E2E for Ubuntu 24.04 and Windows Server 2025
1 parent 7328f4b commit f4d79e4

8 files changed

Lines changed: 617 additions & 0 deletions

File tree

‎.dockerignore‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.git
2+
.gitignore
3+
.idea
4+
.github
5+
*.md
6+
LICENSE
7+
.tool-versions
8+
Injection.lua

‎.github/workflows/e2e.yml‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: E2E
2+
3+
on:
4+
pull_request:
5+
6+
permissions:
7+
contents: read
8+
9+
concurrency:
10+
group: e2e-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
env:
14+
IMAGE: vfox-flutter-e2e:${{ github.run_id }}
15+
FLUTTER_VERSION: '3.44.0'
16+
17+
jobs:
18+
e2e-linux:
19+
name: Linux container
20+
runs-on: ubuntu-latest
21+
timeout-minutes: 60
22+
steps:
23+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
24+
with:
25+
persist-credentials: false
26+
27+
- name: Build the Linux toolchain image
28+
run: docker build --pull -f tests/e2e/Dockerfile -t "$IMAGE" .
29+
30+
- name: Run the e2e suite (vfox latest)
31+
run: |
32+
docker run --rm \
33+
-e VFOX_VERSION=latest \
34+
-e FLUTTER_VERSION="${FLUTTER_VERSION}" \
35+
-e STRICT_SESSION=false \
36+
"$IMAGE"
37+
38+
- name: Run the e2e suite (vfox main)
39+
run: |
40+
docker run --rm \
41+
-e VFOX_VERSION=main \
42+
-e FLUTTER_VERSION="${FLUTTER_VERSION}" \
43+
-e STRICT_SESSION=true \
44+
"$IMAGE"
45+
46+
e2e-windows:
47+
name: Windows container
48+
runs-on: windows-2025
49+
timeout-minutes: 90
50+
steps:
51+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
52+
with:
53+
persist-credentials: false
54+
55+
- name: Switch Docker to Windows containers
56+
shell: bash
57+
run: |
58+
"/c/Program Files/Docker/Docker/DockerCli.exe" -SwitchWindowsEngine || true
59+
docker version
60+
61+
- name: Build the Windows toolchain image
62+
shell: bash
63+
run: docker build --pull -f tests/e2e/Dockerfile.windows -t "$IMAGE" .
64+
65+
- name: Run the e2e suite (vfox latest)
66+
shell: bash
67+
run: |
68+
docker run --rm -e VFOX_VERSION=latest -e FLUTTER_VERSION="${FLUTTER_VERSION}" -e STRICT_SESSION=false "$IMAGE"
69+
70+
- name: Run the e2e suite (vfox main)
71+
shell: bash
72+
run: |
73+
docker run --rm -e VFOX_VERSION=main -e FLUTTER_VERSION="${FLUTTER_VERSION}" -e STRICT_SESSION=true "$IMAGE"

‎README.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ Common mirror values:
7070

7171
For an up-to-date list of available mirrors, refer to the MirrorZ Help site: https://help.mirrors.cernet.edu.cn/flutter/ .
7272

73+
## Testing
74+
75+
- Offline Lua hook tests (`tests/hooks_test.lua`) run on every push/PR via the
76+
**Test Plugin** workflow.
77+
- A containerized end-to-end suite validates the main-branch plugin against both the
78+
latest vfox release and the vfox `main` branch on `ubuntu-latest` (Linux container)
79+
and `windows-latest` (Windows container), reproducing the `vfox activate` +
80+
`vfox use --global` flow from [version-fox/vfox#680](https://github.com/version-fox/vfox/issues/680).
81+
See [docs/e2e.md](docs/e2e.md).
82+
7383
## Releasing this plugin
7484

7585
Maintainers can publish from **Actions → Plugin → Run workflow** on the default

‎docs/e2e.md‎

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# End-to-end (E2E) compatibility test
2+
3+
This document explains the E2E CI added for the Flutter plugin.
4+
5+
## Why
6+
7+
Issue #680 reported that after adding `Invoke-Expression "$(vfox activate pwsh)"` to
8+
the PowerShell profile, a subsequent `vfox use --global flutter@<version>` stopped
9+
putting `flutter` on `PATH`. That is a *runtime* interaction between vfox's shell
10+
hook and a plugin's `EnvKeys`/`PreInstall` output, so no amount of offline Lua unit
11+
testing (see `tests/hooks_test.lua`) could catch it. The E2E suite therefore drives a
12+
real vfox binary, loads the real plugin from the working tree, installs a real Flutter
13+
SDK, and reproduces the failing flow in a fresh shell.
14+
15+
## What runs
16+
17+
The workflow [`.github/workflows/e2e.yml`](../.github/workflows/e2e.yml) builds one
18+
container image per OS and, inside it, exercises both vfox variants:
19+
20+
| OS runner | Container | Script | vfox variants |
21+
|-----------|-----------|--------|---------------|
22+
| `ubuntu-latest` (`ubuntu:26.04`) | Linux container | `tests/e2e/run.sh` (bash) | `latest` + `main` |
23+
| `windows-2025` (`servercore:ltsc2025`) | Windows container | `tests/e2e/run.ps1` (PowerShell 7) | `latest` + `main` |
24+
25+
The image is a **pinned toolchain only**:
26+
- `Dockerfile` — `ubuntu:26.04` + `curl`, `git`, `zip`, `unzip`, `xz-utils`.
27+
- `Dockerfile.windows` — `mcr.microsoft.com/windows/servercore:ltsc2025`, plus a build-time
28+
install of PowerShell 7 (`PowerShell-<ver>-win-x64.zip`, pinned via `ARG PWSH_VERSION`,
29+
default `7.6.6`) and a minimal Git for Windows (`MinGit`, resolved from the
30+
git-for-windows `releases/latest` API — its asset names carry a patch suffix that is not
31+
derivable, so the API `browser_download_url` is used rather than a hand-built URL).
32+
33+
**Go is intentionally *not* in either image.** The scripts install it on demand, and only
34+
for the `vfox main` leg (`ensure_go` / `Ensure-Go`): they resolve the current stable from
35+
`https://go.dev/VERSION?m=text` (falling back to a pinned `GO_FALLBACK`/`$GoFallback`) and
36+
extract it into the scratch dir. The `latest` leg never needs a compiler, so it stays lean.
37+
38+
vfox itself, the plugin, and the Flutter SDK are all installed at **runtime** by the
39+
scripts, so the `latest`/`main` pair shares one image and always reflects the working-tree
40+
plugin.
41+
42+
`vfox latest` is the newest published release binary; `vfox main` is built from the
43+
tip of <https://github.com/version-fox/vfox> so the plugin is also validated against
44+
unreleased vfox changes.
45+
46+
The workflow runs on every `pull_request` (no path filter, no scheduled or manual
47+
trigger). Because it downloads a real Flutter SDK and builds a container image per OS,
48+
each leg is a few minutes and roughly a gigabyte; `concurrency` cancels superseded runs
49+
on the same PR. `actions/checkout` is pinned to a full commit SHA
50+
(`3d3c42e5aac5ba805825da76410c181273ba90b1` = v7), matching `test.yml`.
51+
52+
## Output and failure handling
53+
54+
The scripts are **silent on success**: every external command (vfox, `git`, `curl`, the
55+
Flutter download, and each fresh-shell child) has its stdout/stderr appended to a scratch
56+
`e2e.log`, and nothing is written to the console unless a check fails. On the first failure
57+
the run aborts immediately and prints the failing command plus the last 200 lines of
58+
`e2e.log`, which keeps a real error legible without drowning the run in progress bars. The
59+
one intentional non-fatal line is the `WARN:` emitted when the #680 ordering check fails on
60+
a non-strict (latest) leg — see below.
61+
62+
## The two checks
63+
64+
Each script provisions the plugin and a concrete Flutter version, then runs two
65+
activated-shell checks.
66+
67+
1. **`verify_session_ordering` — the literal #680 flow.** A fresh shell runs
68+
`vfox activate` *first* (global scope is still empty), then `vfox use --global`
69+
*in that same session*, then a PATH refresh, and finally requires `flutter`/`dart`
70+
to resolve and run. If vfox regresses so that `use --global` does not take effect
71+
after activation, this check catches it.
72+
73+
2. **`verify_persistence` — the "day-2" flow.** Global selection is already set, so a
74+
brand-new shell that only runs `vfox activate` must expose the SDK with no further
75+
commands. This is the plain plugin↔vfox `EnvKeys`/global-scope compatibility gate
76+
and is enforced on every leg.
77+
78+
Ordering matters: the ordering check runs first, while global state is clean, so the
79+
"not present before `use`" transition is observable; it then leaves the global
80+
selection in place for the persistence check.
81+
82+
### `STRICT_SESSION`
83+
84+
The same-session refresh bug from #680 was reported against **PowerShell on Windows**;
85+
the released vfox on Linux reproduces it as a pass. To keep the job meaningful without
86+
being permanently red on a vfox-side issue that is not yet released, the scripts treat
87+
the ordering check as:
88+
89+
- a **hard failure** when `STRICT_SESSION=true` (used for the `vfox main` leg, where
90+
any fix must land), and
91+
- a **warning** otherwise (the `vfox latest` leg), so it reports the state of the
92+
released binary without blocking the plugin.
93+
94+
`verify_persistence` is always a hard gate regardless.
95+
96+
## How the reproduction stays deterministic
97+
98+
Two vfox behaviors make a naïve script flaky in CI; both are handled explicitly:
99+
100+
- **`vfox use` requires the activate hook.** vfox 1.x refuses `vfox use` unless the
101+
current shell has run `vfox activate` (it prints
102+
"vfox requires hook support..."). So every `use` happens *inside* an activated
103+
child shell — never in the harness process itself.
104+
- **The hook refreshes PATH from the interactive prompt.** `vfox activate` installs a
105+
`PROMPT_COMMAND`/`prompt` hook that calls `vfox env -s <shell>` on each prompt. A
106+
non-interactive CI shell never displays a prompt, so the scripts reproduce exactly
107+
one refresh by manually running `eval "$("vfox" env -s bash)"`
108+
(`Invoke-Expression (& vfox env -s pwsh)`) after `use --global`. This is the same
109+
command the next interactive prompt would run.
110+
111+
## Environment variables
112+
113+
The scripts are parameterised through env vars (consumed by the workflow):
114+
115+
| Variable | Values | Default | Meaning |
116+
|----------|--------|---------|---------|
117+
| `VFOX_VERSION` | `latest` \| `main` | `latest` | Which vfox runtime to install. |
118+
| `FLUTTER_VERSION` | concrete release, e.g. `3.44.0` | `3.44.0` | Exact Flutter SDK to install (a concrete version, not a channel, so `use` and the installed dir match). |
119+
| `STRICT_SESSION` | `true` \| `false` | `false` | Promote the #680 ordering check to a hard failure. |
120+
| `E2E_WORK_DIR` | path | temp dir | Scratch dir for the vfox binary, plugin zip and caches. |
121+
122+
`FLUTTER_VERSION` defaults to a real stable release referenced in `README.md`; bump the
123+
`FLUTTER_VERSION` env in `.github/workflows/e2e.yml` if that version is ever retired.
124+
125+
## Running locally
126+
127+
Linux container:
128+
129+
```bash
130+
docker build -f tests/e2e/Dockerfile -t vfox-flutter-e2e:linux .
131+
docker run --rm -e VFOX_VERSION=main -e STRICT_SESSION=true vfox-flutter-e2e:linux
132+
```
133+
134+
Windows container (PowerShell, on a Windows host with the Docker daemon in Windows-container mode):
135+
136+
```powershell
137+
docker build -f tests/e2e/Dockerfile.windows -t vfox-flutter-e2e:windows .
138+
docker run --rm -e VFOX_VERSION=main -e STRICT_SESSION=true vfox-flutter-e2e:windows
139+
```
140+
141+
Each run downloads a full Flutter SDK and lets `flutter --version` bootstrap the Dart
142+
tool, so expect a few minutes and roughly a gigabyte of download per leg.
143+
144+
## Windows container caveats
145+
146+
- The base image and the runner's OS must match, so the job uses `windows-2025` and
147+
`Dockerfile.windows` pins `mcr.microsoft.com/windows/servercore:ltsc2025`. (Microsoft
148+
publishes no `powershell` image for `ltsc2025`, hence PowerShell 7 is layered on top of
149+
the plain server-core base at build time.)
150+
- PowerShell 7 is required (the script uses `Invoke-WebRequest -SkipHttpErrorCheck` and
151+
runs its fresh-shell children with `pwsh`); the build-time install supplies `pwsh.exe`.
152+
- The runner's Docker daemon is switched to Windows containers by a best-effort step; on a
153+
native Windows-container engine that step is a harmless no-op.
154+
- This leg is validated only by a real `windows-2025` run; the Linux leg is fully
155+
reproducible locally with Docker.

‎tests/e2e/Dockerfile‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM ubuntu:26.04
2+
3+
RUN apt-get update \
4+
&& DEBIAN_FRONTEND=noninteractive apt-get install --assume-yes --no-install-recommends \
5+
ca-certificates \
6+
curl \
7+
git \
8+
unzip \
9+
xz-utils \
10+
zip \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
ENV HOME=/root
14+
WORKDIR /e2e
15+
16+
COPY metadata.lua ./metadata.lua
17+
COPY hooks ./hooks
18+
COPY lib ./lib
19+
COPY tests/e2e ./tests/e2e
20+
21+
ENTRYPOINT ["bash", "/e2e/tests/e2e/run.sh"]

‎tests/e2e/Dockerfile.windows‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# escape=`
2+
# syntax=docker/dockerfile:1
3+
FROM mcr.microsoft.com/windows/servercore:ltsc2025
4+
5+
SHELL ["powershell", "-NoProfile", "-Command"]
6+
7+
ARG PWSH_VERSION=7.6.6
8+
9+
RUN <<'PWSH'
10+
$ErrorActionPreference = 'Stop'
11+
[Net.ServicePointManager]::SecurityProtocol = 'Tls12'
12+
13+
$psZip = 'C:\ps.zip'
14+
Invoke-WebRequest "https://github.com/PowerShell/PowerShell/releases/download/v${env:PWSH_VERSION}/PowerShell-${env:PWSH_VERSION}-win-x64.zip" -OutFile $psZip
15+
Expand-Archive $psZip -DestinationPath 'C:\PowerShell-7'
16+
Remove-Item $psZip
17+
& 'C:\PowerShell-7\pwsh.exe' -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'
18+
19+
$release = Invoke-WebRequest -UseBasicParsing 'https://api.github.com/repos/git-for-windows/git/releases/latest' | ConvertFrom-Json
20+
$minGitUrl = ($release.assets | Where-Object { $_.name -match '^MinGit-.*-64-bit\.zip$' } | Select-Object -First 1).browser_download_url
21+
$gitZip = 'C:\git.zip'
22+
Invoke-WebRequest $minGitUrl -OutFile $gitZip
23+
Expand-Archive $gitZip -DestinationPath 'C:\MinGit'
24+
Remove-Item $gitZip
25+
PWSH
26+
27+
ENV PATH="C:\PowerShell-7;C:\MinGit\cmd;${PATH}"
28+
29+
WORKDIR C:\e2e
30+
31+
COPY metadata.lua C:\e2e\metadata.lua
32+
COPY hooks C:\e2e\hooks
33+
COPY lib C:\e2e\lib
34+
COPY tests/e2e C:\e2e\tests\e2e
35+
36+
ENTRYPOINT ["pwsh", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\e2e\\tests\\e2e\\run.ps1"]

0 commit comments

Comments
 (0)