|
1 | | -# Full workspace install (clone + Docker image + rosdep + colcon + yarn) plus a |
2 | | -# non-interactive launch smoke test, on both x86_64 and ARM64 GitHub-hosted runners. |
3 | | -# |
4 | | -# - DEV=false -> install.sh uses `url_https` from config/repos.json (sub-repos are public). |
5 | | -# - CI=true -> install.sh skips the host `xhost` check (no GUI on runners). |
6 | | -# - variant=arm runs on `ubuntu-24.04-arm` with `./install.sh --arm`; the built image |
7 | | -# is then verified to be linux/arm64 (linux/amd64 for the default variant). |
8 | | -# - Runner OS (22.04 or 24.04) is only the CI host. ROS Humble always runs inside |
9 | | -# Jammy-based images (`osrf/ros:humble-desktop` / `ros:humble-ros-base-jammy`). |
10 | | -# - `ubuntu-24.04-arm` is GitHub's hosted ARM64 runner label; there is no 22.04-arm |
11 | | -# standard runner. The container OS stays Jammy regardless. |
12 | | -# - The launch smoke test uses `./launch_lucy.sh --headless <command>`, which runs a |
13 | | -# single command inside the container (no control panel, no auto Gazebo/RViz). |
14 | | - |
15 | | -name: Install, Launch & Release |
| 1 | +# - DEV=false -> install.py uses url_https from config/repos.json. |
| 2 | +# - python3 install.py --skip-build clones sub-repos; colcon build runs via pixi run build. |
| 3 | + |
| 4 | +name: Install, Build & Test |
16 | 5 |
|
17 | 6 | on: |
18 | 7 | workflow_dispatch: |
19 | 8 | push: |
20 | 9 | branches: [master, dev] |
21 | 10 | tags: |
22 | | - - 'v*' # Run on version tags like v1.0, v2.3.4 |
| 11 | + - 'v*' |
23 | 12 | pull_request: |
24 | 13 | branches: [master, dev] |
25 | 14 |
|
26 | 15 | permissions: |
27 | 16 | contents: read |
28 | 17 |
|
29 | 18 | jobs: |
30 | | - install-and-launch: |
| 19 | + pixi-install-build-test: |
| 20 | + name: Pixi install & build (${{ matrix.runner }}) |
31 | 21 | concurrency: |
32 | | - group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.variant }} |
| 22 | + group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.runner }} |
33 | 23 | cancel-in-progress: true |
34 | 24 | runs-on: ${{ matrix.runner }} |
35 | 25 | timeout-minutes: 120 |
36 | 26 | strategy: |
37 | 27 | fail-fast: false |
38 | 28 | matrix: |
39 | 29 | include: |
40 | | - - variant: default |
41 | | - runner: ubuntu-latest |
42 | | - use_arm_install: false |
43 | | - expected_arch: linux/amd64 |
44 | | - - variant: arm |
45 | | - runner: ubuntu-24.04-arm |
46 | | - use_arm_install: true |
47 | | - expected_arch: linux/arm64 |
| 30 | + - runner: ubuntu-latest |
| 31 | + pixi_platform: linux-64 |
| 32 | + - runner: ubuntu-24.04-arm |
| 33 | + pixi_platform: linux-aarch64 |
| 34 | + - runner: macos-latest |
| 35 | + pixi_platform: osx-arm64 |
| 36 | + - runner: windows-latest |
| 37 | + pixi_platform: win-64 |
48 | 38 | env: |
49 | 39 | DEV: "false" |
50 | 40 | CI: "true" |
51 | 41 | steps: |
52 | | - - uses: actions/checkout@v4 |
53 | | - |
54 | | - # Make sure no committed/local .env can flip DEV back to true at install time. |
55 | | - - name: Drop workspace .env if present |
56 | | - run: rm -f .env .env.local || true |
57 | | - |
58 | | - # Docker Hub metadata/pull flakes (i/o timeout) fail `docker build` before |
59 | | - # any Dockerfile layer runs. Pre-pull with retries so the build reuses cache. |
60 | | - - name: Pre-pull base images (retry on Hub flakes) |
61 | | - run: | |
62 | | - pull_with_retry() { |
63 | | - image="$1" |
64 | | - for attempt in 1 2 3 4 5; do |
65 | | - if docker pull "$image"; then |
66 | | - return 0 |
67 | | - fi |
68 | | - echo "docker pull ${image} failed (attempt ${attempt}/5), retrying..." |
69 | | - sleep $((attempt * 15)) |
70 | | - done |
71 | | - return 1 |
72 | | - } |
73 | | - if [ "${{ matrix.use_arm_install }}" = true ]; then |
74 | | - pull_with_retry ros:humble-ros-base-jammy |
75 | | - else |
76 | | - pull_with_retry osrf/ros:humble-desktop |
77 | | - fi |
78 | | -
|
79 | | - - name: Install (clone + Docker image + rosdep + colcon + yarn) |
80 | | - run: | |
81 | | - chmod +x install.sh launch_lucy.sh |
82 | | - if [ "${{ matrix.use_arm_install }}" = true ]; then |
83 | | - ./install.sh --arm |
84 | | - else |
85 | | - ./install.sh |
86 | | - fi |
87 | | -
|
88 | | - - name: Verify Lucy image architecture |
89 | | - run: | |
90 | | - osarch=$(docker image inspect lucy_ros2:humble --format '{{.Os}}/{{.Architecture}}') |
91 | | - echo "lucy_ros2:humble -> ${osarch}" |
92 | | - if [ "${osarch}" != "${{ matrix.expected_arch }}" ]; then |
93 | | - echo "::error::Expected ${{ matrix.expected_arch }} after ./install.sh on ${{ matrix.runner }}, got ${osarch}" |
94 | | - exit 1 |
95 | | - fi |
| 42 | + - uses: actions/checkout@v5 |
96 | 43 |
|
97 | | - # Single command in the container -> bypasses the control-panel / Gazebo / RViz auto-launch. |
98 | | - - name: Launch smoke test (headless, no TTY) |
99 | | - run: ./launch_lucy.sh --headless ros2 doctor --report |
100 | | - |
101 | | - # Windows-specific CI. NOTE: GitHub-hosted Windows runners cannot run Linux |
102 | | - # containers (no Hyper-V/nested virtualization, no WSL2), so the heavy install |
103 | | - # step (docker build of the ROS image + colcon) CANNOT run here — that is |
104 | | - # covered for both amd64/arm64 by the Linux `install-and-launch` job above. |
105 | | - # This job verifies the Windows-only pieces on real x64 AND arm64 hardware: |
106 | | - # - host CPU -> Docker platform detection (install_ops.host_container_platform) |
107 | | - # - Lucy.exe builds (PyInstaller) and its bundled --cli imports work |
108 | | - # - the NSIS installer compiles |
109 | | - windows-build-test: |
110 | | - name: Windows build & installer test (${{ matrix.arch }}) |
111 | | - strategy: |
112 | | - fail-fast: false |
113 | | - matrix: |
114 | | - include: |
115 | | - - arch: x64 |
116 | | - runner: windows-latest |
117 | | - expected_platform: linux/amd64 |
118 | | - - arch: arm64 |
119 | | - runner: windows-11-arm |
120 | | - expected_platform: linux/arm64 |
121 | | - runs-on: ${{ matrix.runner }} |
122 | | - timeout-minutes: 30 |
123 | | - steps: |
124 | | - - name: Check out repository |
125 | | - uses: actions/checkout@v4 |
126 | | - |
127 | | - - name: Set up Python |
128 | | - uses: actions/setup-python@v5 |
| 44 | + - uses: prefix-dev/setup-pixi@v0.10.0 |
129 | 45 | with: |
130 | | - python-version: '3.x' |
| 46 | + pixi-version: latest |
| 47 | + cache: ${{ matrix.pixi_platform != 'win-64' }} |
| 48 | + post-cleanup: false |
131 | 49 |
|
132 | | - - name: Generate releases manifest |
133 | | - run: python windows/generate_releases.py |
| 50 | + - name: Workspace unit tests |
| 51 | + shell: bash |
| 52 | + run: pixi run workspace-test |
134 | 53 |
|
135 | | - - name: Verify host architecture detection |
136 | | - shell: pwsh |
137 | | - run: | |
138 | | - $expected = "${{ matrix.expected_platform }}" |
139 | | - $got = (python -c "import sys; sys.path.insert(0,'windows'); import install_ops; print(install_ops.host_container_platform())").Trim() |
140 | | - Write-Host "host_container_platform() -> $got (expected $expected)" |
141 | | - if ($got -ne $expected) { |
142 | | - Write-Error "Arch detection mismatch: got '$got', expected '$expected'" |
143 | | - exit 1 |
144 | | - } |
145 | | -
|
146 | | - - name: Install PyInstaller |
147 | | - run: python -m pip install pyinstaller |
| 54 | + - name: Drop workspace .env if present |
| 55 | + shell: bash |
| 56 | + run: rm -f .env .env.local || true |
148 | 57 |
|
149 | | - - name: Build Lucy.exe |
150 | | - shell: pwsh |
151 | | - run: | |
152 | | - python -m PyInstaller --noconfirm --onefile --name Lucy ` |
153 | | - --icon windows/assets/lucy-icon.ico ` |
154 | | - --hidden-import install_ops ` |
155 | | - --hidden-import install_runner ` |
156 | | - --paths windows ` |
157 | | - windows/Lucy.py |
158 | | - if (-not (Test-Path "dist/Lucy.exe")) { Write-Error "Lucy.exe not produced"; exit 1 } |
159 | | -
|
160 | | - - name: Smoke-test Lucy.exe CLI (bundled imports + prereq report) |
161 | | - shell: pwsh |
| 58 | + - name: Clone sub-repositories (skip colcon) |
| 59 | + shell: bash |
162 | 60 | run: | |
163 | | - dist\Lucy.exe --cli check-prereqs |
164 | | - $code = $LASTEXITCODE |
165 | | - Write-Host "check-prereqs exit code: $code" |
166 | | - # 0 (all present) or 1 (a prereq missing, e.g. no Docker on CI) both mean |
167 | | - # the exe ran fine; >1 = crash. Reset the propagated exit code explicitly. |
168 | | - if ($code -gt 1) { Write-Error "Lucy.exe --cli check-prereqs crashed (exit $code)"; exit 1 } |
169 | | - exit 0 |
170 | | -
|
171 | | - - name: Install NSIS |
172 | | - run: choco install nsis -y --no-progress |
173 | | - |
174 | | - - name: Build Lucy-Setup.exe (NSIS) |
175 | | - shell: pwsh |
176 | | - run: | |
177 | | - $makensis = "${env:ProgramFiles(x86)}\NSIS\makensis.exe" |
178 | | - & $makensis "/DMyAppVersion=0.0.0-ci" windows/installer/Lucy.nsi |
179 | | - if (-not (Test-Path "dist/Lucy-Setup-0.0.0-ci.exe")) { Write-Error "Installer not produced"; exit 1 } |
| 61 | + chmod +x launch_lucy.sh |
| 62 | + python3 install.py --skip-build |
180 | 63 |
|
181 | | - - name: Upload Windows artifacts |
182 | | - uses: actions/upload-artifact@v4 |
183 | | - with: |
184 | | - name: lucy-windows-${{ matrix.arch }} |
185 | | - path: | |
186 | | - dist/Lucy.exe |
187 | | - dist/Lucy-Setup-0.0.0-ci.exe |
188 | | - if-no-files-found: error |
189 | | - |
190 | | - build-and-release-windows-exe: |
191 | | - name: Build and Release Windows Executable |
192 | | - # Only run this job when a new tag is pushed |
193 | | - if: startsWith(github.ref, 'refs/tags/') |
194 | | - runs-on: windows-latest |
195 | | - needs: install-and-launch |
196 | | - permissions: |
197 | | - contents: write # Required to create a release and upload assets |
198 | | - steps: |
199 | | - - name: Check out repository |
200 | | - uses: actions/checkout@v4 |
| 64 | + - name: Build workspace (colcon) |
| 65 | + shell: bash |
| 66 | + run: pixi run build |
201 | 67 |
|
202 | | - - name: Set up Python |
203 | | - uses: actions/setup-python@v5 |
204 | | - with: |
205 | | - python-version: '3.x' |
| 68 | + - name: Install control panel (yarn) |
| 69 | + shell: bash |
| 70 | + run: pixi run panel-install |
206 | 71 |
|
207 | | - - name: Generate releases manifest |
208 | | - run: python windows/generate_releases.py |
| 72 | + - name: Run colcon tests |
| 73 | + shell: bash |
| 74 | + run: pixi run test-only |
209 | 75 |
|
210 | | - - name: Install PyInstaller |
211 | | - run: python -m pip install pyinstaller |
| 76 | + - name: ROS doctor |
| 77 | + shell: bash |
| 78 | + run: pixi run doctor |
212 | 79 |
|
213 | | - - name: Build Lucy.exe |
214 | | - shell: pwsh |
| 80 | + - name: Launch smoke test (headless) |
| 81 | + shell: bash |
| 82 | + run: ./launch_lucy.sh --headless python scripts/ros_doctor.py |
| 83 | + |
| 84 | + - name: Launcher tmux smoke (Linux) |
| 85 | + if: matrix.pixi_platform == 'linux-64' |
| 86 | + shell: bash |
215 | 87 | run: | |
216 | | - python -m PyInstaller --noconfirm --onefile --name Lucy ` |
217 | | - --icon windows/assets/lucy-icon.ico ` |
218 | | - --hidden-import install_ops ` |
219 | | - --hidden-import install_runner ` |
220 | | - --paths windows ` |
221 | | - windows/Lucy.py |
222 | | -
|
223 | | - - name: Install NSIS |
224 | | - run: choco install nsis -y |
225 | | - |
226 | | - - name: Build Lucy-Setup.exe |
227 | | - shell: pwsh |
| 88 | + chmod +x scripts/ci_tmux_launcher_smoke.sh |
| 89 | + LUCY_WS_ROOT="$PWD" ./scripts/ci_tmux_launcher_smoke.sh |
| 90 | +
|
| 91 | + # The smoke above answers "did the processes start". This asks whether the |
| 92 | + # graph they formed is one the control panel and RViz can actually use. |
| 93 | + - name: Core bringup graph checks (Linux) |
| 94 | + if: matrix.pixi_platform == 'linux-64' |
| 95 | + shell: bash |
| 96 | + run: pixi run core-bringup-test |
| 97 | + |
| 98 | + - name: Verify Pixi platform |
| 99 | + shell: bash |
228 | 100 | run: | |
229 | | - $version = "${{ github.ref_name }}" -replace '^v','' |
230 | | - if (-not $version) { $version = "0.0.0" } |
231 | | - $makensis = "${env:ProgramFiles(x86)}\NSIS\makensis.exe" |
232 | | - & $makensis "/DMyAppVersion=$version" windows/installer/Lucy.nsi |
| 101 | + got=$(pixi info --json | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('platform', ''))") |
| 102 | + echo "pixi platform -> ${got} (expected ${{ matrix.pixi_platform }})" |
| 103 | + if [ "${got}" != "${{ matrix.pixi_platform }}" ]; then |
| 104 | + echo "::error::Pixi platform mismatch on ${{ matrix.runner }}" |
| 105 | + exit 1 |
| 106 | + fi |
233 | 107 |
|
234 | | - - name: Create Release and Upload Assets |
235 | | - uses: softprops/action-gh-release@v2 |
236 | | - with: |
237 | | - files: | |
238 | | - dist/Lucy.exe |
239 | | - dist/Lucy-Setup-*.exe |
240 | | - draft: true |
0 commit comments