Compare commits
No commits in common. "098993f81c596ec452fe2b4f613077a731029c90" and "9d1c0ef9c6a30405b7e6869344b58ce92f5b06a5" have entirely different histories.
098993f81c
...
9d1c0ef9c6
|
|
@ -1,18 +1,14 @@
|
||||||
name: Build Avida-ED Native Apps
|
name: Build Avida-ED Onefile
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
ed_versions:
|
ed_versions:
|
||||||
description: "Comma-separated versions to build, for example v3,v4"
|
description: "Comma-separated versions (v3,v4)"
|
||||||
default: "v3,v4"
|
default: "v3,v4"
|
||||||
required: true
|
required: true
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.ver }} on ${{ matrix.os }}
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -22,97 +18,63 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Skip unrequested versions
|
|
||||||
id: version_filter
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
requested=",${{ github.event.inputs.ed_versions || 'v3,v4' }},"
|
|
||||||
requested="${requested// /}"
|
|
||||||
if [[ "$requested" == *",${{ matrix.ver }},"* ]]; then
|
|
||||||
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "enabled=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Set up Rust
|
- name: Set up Rust
|
||||||
if: steps.version_filter.outputs.enabled == 'true'
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
- name: Install Linux packaging dependencies
|
- name: Fetch assets via docker compose (using URL fallback on mac/win if docker unavailable)
|
||||||
if: steps.version_filter.outputs.enabled == 'true' && runner.os == 'Linux'
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
if command -v docker >/dev/null 2>&1; then
|
||||||
sudo apt-get install -y \
|
docker compose run --rm fetch-${{ matrix.ver }}
|
||||||
pkg-config \
|
else
|
||||||
libgtk-3-dev \
|
MODE=url URL=https://avida-ed.msu.edu/app4/ OUTDIR=./apps/${{ matrix.ver }} bash tools/fetch_assets.sh
|
||||||
libwebkit2gtk-4.1-dev \
|
fi
|
||||||
imagemagick \
|
|
||||||
file \
|
|
||||||
desktop-file-utils
|
|
||||||
curl -L -o appimagetool \
|
|
||||||
https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
|
||||||
chmod +x appimagetool
|
|
||||||
sudo mv appimagetool /usr/local/bin/appimagetool
|
|
||||||
|
|
||||||
- name: Install Windows packaging dependencies
|
|
||||||
if: steps.version_filter.outputs.enabled == 'true' && runner.os == 'Windows'
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
choco install 7zip -y --no-progress
|
|
||||||
|
|
||||||
- name: Fetch canonical Avida-ED assets
|
|
||||||
if: steps.version_filter.outputs.enabled == 'true'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
MODE=github ED_VER=${{ matrix.ver }} OUTDIR=apps/${{ matrix.ver }} bash tools/fetch_assets.sh
|
|
||||||
|
|
||||||
- name: Inject webroot
|
- name: Inject webroot
|
||||||
if: steps.version_filter.outputs.enabled == 'true'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
bash tools/inject_webroot.sh ${{ matrix.ver }}
|
rm -rf server-ui/webroot
|
||||||
|
mkdir -p server-ui/webroot
|
||||||
|
rsync -a apps/${{ matrix.ver }}/Avida-ED-Eco/ server-ui/webroot/Avida-ED-Eco/
|
||||||
|
|
||||||
- name: Build binary
|
- name: Build binary
|
||||||
if: steps.version_filter.outputs.enabled == 'true'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
case "${{ matrix.ver }}" in
|
|
||||||
v3) default_path="/Avida-ED/index.html" ;;
|
|
||||||
v4) default_path="/Avida-ED-Eco/index.html" ;;
|
|
||||||
esac
|
|
||||||
cd server-ui
|
cd server-ui
|
||||||
AVIDA_ED_DEFAULT_PATH="$default_path" cargo build --release
|
cargo build --release
|
||||||
|
|
||||||
- name: Package Linux AppImage
|
- name: Package (Linux AppImage)
|
||||||
if: steps.version_filter.outputs.enabled == 'true' && runner.os == 'Linux'
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
|
sudo apt-get update && sudo apt-get install -y imagemagick || true
|
||||||
bash packaging/linux/make_appimage.sh ${{ matrix.ver }} server-ui/target/release/avidaed_onefile
|
bash packaging/linux/make_appimage.sh ${{ matrix.ver }} server-ui/target/release/avidaed_onefile
|
||||||
|
mv Avida-ED-${{ matrix.ver }}-Linux-x86_64.AppImage Avida-ED-${{ matrix.ver }}-Linux-x86_64.AppImage
|
||||||
|
|
||||||
- name: Package Windows app
|
- name: Package (Windows EXE)
|
||||||
if: steps.version_filter.outputs.enabled == 'true' && runner.os == 'Windows'
|
if: startsWith(matrix.os, 'windows')
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
|
$WV2 = "$env:RUNNER_TEMP\WebView2Fixed" # pre-cached in your org, or downloaded here
|
||||||
|
# TODO: fetch/unzip WebView2 Fixed into $WV2 if not cached
|
||||||
powershell -ExecutionPolicy Bypass -File packaging/windows/make_windows_sfx.ps1 `
|
powershell -ExecutionPolicy Bypass -File packaging/windows/make_windows_sfx.ps1 `
|
||||||
-Version ${{ matrix.ver }} `
|
-Version ${{ matrix.ver }} `
|
||||||
-BinPath server-ui/target/release/avidaed_onefile.exe `
|
-BinPath server-ui/target/release/avidaed_onefile.exe `
|
||||||
-OutputDir .
|
-WV2Fixed $WV2
|
||||||
|
Move-Item packaging\windows\Avida-ED-${{ matrix.ver }}-Windows.exe .
|
||||||
|
|
||||||
- name: Package macOS app
|
- name: Package (macOS .app)
|
||||||
if: steps.version_filter.outputs.enabled == 'true' && runner.os == 'macOS'
|
if: startsWith(matrix.os, 'macos')
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
bash packaging/mac/make_macos_bundle.sh ${{ matrix.ver }} server-ui/target/release/avidaed_onefile .
|
bash packaging/mac/make_macos_bundle.sh ${{ matrix.ver }} server-ui/target/release/avidaed_onefile
|
||||||
|
# Optionally make a DMG
|
||||||
|
# brew install create-dmg
|
||||||
|
# create-dmg "Avida-ED-${{ matrix.ver }}.app"
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: steps.version_filter.outputs.enabled == 'true'
|
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Avida-ED-${{ matrix.ver }}-${{ matrix.os }}
|
name: Avida-ED-${{ matrix.ver }}-${{ matrix.os }}
|
||||||
path: |
|
path: |
|
||||||
Avida-ED-${{ matrix.ver }}-Linux-*.AppImage
|
*.AppImage
|
||||||
Avida-ED-${{ matrix.ver }}-Windows-*.zip
|
*.exe
|
||||||
Avida-ED-${{ matrix.ver }}-Windows-*.exe
|
Avida-ED-${{ matrix.ver }}.app
|
||||||
Avida-ED-${{ matrix.ver }}-macOS.zip
|
if-no-files-found: ignore
|
||||||
if-no-files-found: error
|
|
||||||
|
|
|
||||||
|
|
@ -211,15 +211,3 @@ cython_debug/
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
# Avida-ED-App-Builder generated artifacts
|
|
||||||
/apps/
|
|
||||||
/server-ui/webroot/
|
|
||||||
/AvidaED-*.AppDir/
|
|
||||||
/squashfs-root/
|
|
||||||
/Avida-ED-*-Linux-*.AppImage
|
|
||||||
/Avida-ED-*-Windows-*.zip
|
|
||||||
/Avida-ED-*-Windows-*.exe
|
|
||||||
/Avida-ED-*.app/
|
|
||||||
/Avida-ED-*-macOS.zip
|
|
||||||
/packaging/windows/payload_*/
|
|
||||||
/packaging/windows/payload_*.7z
|
|
||||||
|
|
|
||||||
48
Makefile
48
Makefile
|
|
@ -1,44 +1,26 @@
|
||||||
VER ?= v4
|
VER ?= v4 # or v3
|
||||||
APP_DIR_v3 := Avida-ED
|
|
||||||
APP_DIR_v4 := Avida-ED-Eco
|
|
||||||
APP_DIR := $(APP_DIR_$(VER))
|
|
||||||
DEFAULT_PATH := /$(APP_DIR)/index.html
|
|
||||||
|
|
||||||
.PHONY: fetch-v3 fetch-v4 fetch-v3-url fetch-v4-url check-ver check-assets check-linux-deps inject-$(VER) build-linux build-mac build-win appimage winexe macapp all
|
.PHONY: fetch-$(VER) inject-$(VER) build-linux build-mac build-win appimage winexe macapp all
|
||||||
|
|
||||||
fetch-v3:
|
fetch-v3:
|
||||||
MODE=github ED_VER=v3 OUTDIR=apps/v3 bash tools/fetch_assets.sh
|
docker compose run --rm fetch-v3
|
||||||
fetch-v4:
|
fetch-v4:
|
||||||
MODE=github ED_VER=v4 OUTDIR=apps/v4 bash tools/fetch_assets.sh
|
docker compose run --rm fetch-v4
|
||||||
fetch-v3-url:
|
|
||||||
MODE=url ED_VER=v3 URL=https://avida-ed.msu.edu/app/ OUTDIR=apps/v3 bash tools/fetch_assets.sh
|
|
||||||
fetch-v4-url:
|
|
||||||
MODE=url ED_VER=v4 URL=https://avida-ed.github.io/Avida-ED-Eco/ OUTDIR=apps/v4 bash tools/fetch_assets.sh
|
|
||||||
|
|
||||||
check-ver:
|
inject-$(VER):
|
||||||
@test -n "$(APP_DIR)" || { echo "Unsupported VER=$(VER). Use VER=v3 or VER=v4."; exit 2; }
|
rm -rf server-ui/webroot
|
||||||
|
mkdir -p server-ui/webroot
|
||||||
check-assets: check-ver
|
rsync -a apps/$(VER)/Avida-ED-Eco/ server-ui/webroot/Avida-ED-Eco/
|
||||||
@test -f "apps/$(VER)/$(APP_DIR)/index.html" || { echo "Missing apps/$(VER)/$(APP_DIR)/index.html. Run make fetch-$(VER) first."; exit 2; }
|
|
||||||
|
|
||||||
check-linux-deps:
|
|
||||||
@command -v cargo >/dev/null || { echo "Missing cargo. Install Rust 1.75+ with rustup."; exit 127; }
|
|
||||||
@command -v pkg-config >/dev/null || { echo "Missing pkg-config. Install pkg-config plus GTK/WebKitGTK development packages."; exit 127; }
|
|
||||||
@pkg-config --exists glib-2.0 gtk+-3.0 || { echo "Missing GLib/GTK development packages visible to pkg-config."; exit 127; }
|
|
||||||
@pkg-config --exists webkit2gtk-4.1 || pkg-config --exists webkit2gtk-4.0 || { echo "Missing WebKitGTK development package visible to pkg-config."; exit 127; }
|
|
||||||
|
|
||||||
inject-$(VER): check-assets
|
|
||||||
bash tools/inject_webroot.sh $(VER)
|
|
||||||
|
|
||||||
# Build binaries natively on each OS runner
|
# Build binaries natively on each OS runner
|
||||||
build-linux: check-ver check-linux-deps inject-$(VER)
|
build-linux: inject-$(VER)
|
||||||
cd server-ui && AVIDA_ED_DEFAULT_PATH="$(DEFAULT_PATH)" cargo build --release
|
cd server-ui && cargo build --release
|
||||||
|
|
||||||
build-mac: inject-$(VER)
|
build-mac: inject-$(VER)
|
||||||
cd server-ui && AVIDA_ED_DEFAULT_PATH="$(DEFAULT_PATH)" cargo build --release
|
cd server-ui && cargo build --release
|
||||||
|
|
||||||
build-win: inject-$(VER)
|
build-win: inject-$(VER)
|
||||||
cd server-ui && AVIDA_ED_DEFAULT_PATH="$(DEFAULT_PATH)" cargo build --release
|
cd server-ui && cargo build --release
|
||||||
|
|
||||||
# Package
|
# Package
|
||||||
appimage: build-linux
|
appimage: build-linux
|
||||||
|
|
@ -49,14 +31,14 @@ winexe: build-win
|
||||||
powershell -ExecutionPolicy Bypass -File packaging/windows/make_windows_sfx.ps1 \
|
powershell -ExecutionPolicy Bypass -File packaging/windows/make_windows_sfx.ps1 \
|
||||||
-Version $(VER) \
|
-Version $(VER) \
|
||||||
-BinPath $(CURDIR)/server-ui/target/release/avidaed_onefile.exe \
|
-BinPath $(CURDIR)/server-ui/target/release/avidaed_onefile.exe \
|
||||||
-WV2Fixed "C:\SDKs\WebView2.FixedRuntime" \
|
-WV2Fixed "C:\SDKs\WebView2.FixedRuntime"
|
||||||
-OutputDir $(CURDIR)
|
|
||||||
|
|
||||||
macapp: build-mac
|
macapp: build-mac
|
||||||
bash packaging/mac/make_macos_bundle.sh $(VER) server-ui/target/release/avidaed_onefile $(CURDIR)
|
bash packaging/mac/make_macos_bundle.sh $(VER) server-ui/target/release/avidaed_onefile
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@echo "Targets:"
|
@echo "Targets:"
|
||||||
@echo " make fetch-v3 | fetch-v4"
|
@echo " make fetch-v3 | fetch-v4"
|
||||||
@echo " make build-linux | build-mac | build-win"
|
@echo " make build-linux | build-mac | build-win"
|
||||||
@echo " make appimage | winexe | macapp"
|
@echo " make appimage | winexe | macapp"
|
||||||
|
|
||||||
|
|
|
||||||
68
README.md
68
README.md
|
|
@ -58,7 +58,7 @@ avidaed-onefile/
|
||||||
|
|
||||||
| OS | Additional Requirements |
|
| OS | Additional Requirements |
|
||||||
| ----------- | ----------------------------------------------------------------- |
|
| ----------- | ----------------------------------------------------------------- |
|
||||||
| **Linux** | `pkg-config`, `libgtk-3-dev`, `libwebkit2gtk-4.1-dev` or `libwebkit2gtk-4.0-dev`, `appimagetool` for AppImage packaging |
|
| **Linux** | `libgtk-3-dev`, `libwebkit2gtk-4.0-dev`, `appimagetool` |
|
||||||
| **Windows** | PowerShell 5+, 7-Zip installed in `C:\Program Files\7-Zip\7z.exe` |
|
| **Windows** | PowerShell 5+, 7-Zip installed in `C:\Program Files\7-Zip\7z.exe` |
|
||||||
| **macOS** | Xcode command-line tools, optionally `create-dmg` |
|
| **macOS** | Xcode command-line tools, optionally `create-dmg` |
|
||||||
|
|
||||||
|
|
@ -66,26 +66,24 @@ avidaed-onefile/
|
||||||
|
|
||||||
## 🧱 Step 1. Fetch the Avida-ED Web Assets
|
## 🧱 Step 1. Fetch the Avida-ED Web Assets
|
||||||
|
|
||||||
Avida-ED web assets are pulled from the canonical GitHub repositories:
|
Avida-ED web builds are pulled either:
|
||||||
|
|
||||||
* v3: `https://github.com/Avida-ED/Avida-ED3`
|
* from **a known-good Docker image** (`welsberr/aed-docker`), or
|
||||||
* v4: `https://github.com/Avida-ED/Avida-ED4`
|
* from a **public URL** (fallback).
|
||||||
|
|
||||||
### Option A — Using GitHub (canonical)
|
### Option A — Using Docker (preferred)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Pull version 3 and version 4 assets
|
# Pull version 3 and version 4 assets
|
||||||
make fetch-v3
|
docker compose run --rm fetch-v3
|
||||||
make fetch-v4
|
docker compose run --rm fetch-v4
|
||||||
```
|
```
|
||||||
|
|
||||||
This clones the selected canonical source repository and normalizes the app root into:
|
This:
|
||||||
|
|
||||||
```
|
* spins up the minimal Alpine container in `tools/Dockerfile.fetch`,
|
||||||
apps/
|
* extracts the Avida-ED webroot from the container’s Nginx image, and
|
||||||
├── v3/Avida-ED/index.html
|
* deposits the result under `apps/v3/` and `apps/v4/`.
|
||||||
└── v4/Avida-ED-Eco/index.html
|
|
||||||
```
|
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
|
|
@ -95,12 +93,12 @@ apps/
|
||||||
└── v4/Avida-ED-Eco/index.html
|
└── v4/Avida-ED-Eco/index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
### Option B — Fetch via Public Site (fallback)
|
### Option B — Fetch via HTTPS (fallback)
|
||||||
|
|
||||||
If GitHub is unavailable but the public app site is reachable:
|
If Docker is unavailable:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make fetch-v4-url
|
MODE=url URL=https://avida-ed.msu.edu/app4/ OUTDIR=./apps/v4 bash tools/fetch_assets.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -129,8 +127,6 @@ The binary (`avidaed_onefile`) runs both the HTTP server and browser window.
|
||||||
make VER=v4 build-linux # or build-mac
|
make VER=v4 build-linux # or build-mac
|
||||||
```
|
```
|
||||||
|
|
||||||
On Linux, `build-linux` checks for Rust, `pkg-config`, GTK, and WebKitGTK development packages before compiling. Building the binary does not require `appimagetool`; only `make VER=v4 appimage` does.
|
|
||||||
|
|
||||||
### Windows (PowerShell)
|
### Windows (PowerShell)
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|
@ -178,15 +174,15 @@ chmod +x Avida-ED-v4-Linux-x86_64.AppImage
|
||||||
|
|
||||||
### 🪟 Windows — Self-Extracting EXE
|
### 🪟 Windows — Self-Extracting EXE
|
||||||
|
|
||||||
**Purpose:** a native Windows package containing:
|
**Purpose:** one `.exe` that includes:
|
||||||
|
|
||||||
* the app binary,
|
* the app binary,
|
||||||
* an optional WebView2 Fixed Runtime,
|
* the WebView2 Fixed Runtime,
|
||||||
* a batch launcher.
|
* a batch launcher.
|
||||||
|
|
||||||
Steps:
|
Steps:
|
||||||
|
|
||||||
1. Optional: download the **WebView2 Fixed Version Runtime** (x64) from Microsoft:
|
1. Download the **WebView2 Fixed Version Runtime** (x64) from Microsoft:
|
||||||
[https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section)
|
[https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section)
|
||||||
2. Extract it somewhere, e.g. `C:\SDKs\WebView2.FixedRuntime`.
|
2. Extract it somewhere, e.g. `C:\SDKs\WebView2.FixedRuntime`.
|
||||||
3. Run:
|
3. Run:
|
||||||
|
|
@ -207,15 +203,14 @@ Steps:
|
||||||
Result:
|
Result:
|
||||||
|
|
||||||
```
|
```
|
||||||
Avida-ED-v4-Windows-x64.zip
|
Avida-ED-v4-Windows.exe
|
||||||
Avida-ED-v4-Windows-x64.exe # when 7-Zip's SFX module is available
|
|
||||||
```
|
```
|
||||||
|
|
||||||
When run:
|
When run:
|
||||||
|
|
||||||
* `run.bat` sets `WEBVIEW2_BROWSER_EXECUTABLE_FOLDER` when a fixed runtime is bundled.
|
* Extracts to `%TEMP%\Avida-ED-*`,
|
||||||
* If no fixed runtime is bundled, the system WebView2 Runtime is used.
|
* Sets `WEBVIEW2_BROWSER_EXECUTABLE_FOLDER`,
|
||||||
* The SFX executable self-extracts and launches `run.bat`.
|
* Launches immediately.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -243,12 +238,6 @@ brew install create-dmg
|
||||||
create-dmg Avida-ED-v4.app
|
create-dmg Avida-ED-v4.app
|
||||||
```
|
```
|
||||||
|
|
||||||
The bundle script also writes:
|
|
||||||
|
|
||||||
```
|
|
||||||
Avida-ED-v4-macOS.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🧪 Step 5. Test All Artifacts
|
## 🧪 Step 5. Test All Artifacts
|
||||||
|
|
@ -277,22 +266,11 @@ make all
|
||||||
|
|
||||||
On CI (GitHub Actions), artifacts are built via matrix:
|
On CI (GitHub Actions), artifacts are built via matrix:
|
||||||
|
|
||||||
* OS: `ubuntu-22.04`, `windows-2022`, `macos-13`
|
* OS: `ubuntu`, `windows`, `macos`
|
||||||
* Version: `v3`, `v4`
|
* Version: `v3`, `v4`
|
||||||
|
|
||||||
Resulting artifacts are automatically uploaded.
|
Resulting artifacts are automatically uploaded.
|
||||||
|
|
||||||
The workflow is `.github/workflows/release.yml`. It:
|
|
||||||
|
|
||||||
* fetches canonical assets from `Avida-ED/Avida-ED3` and `Avida-ED/Avida-ED4`,
|
|
||||||
* injects the correct app root for each version,
|
|
||||||
* builds the Rust wrapper on the native runner,
|
|
||||||
* packages Windows as a zip plus optional SFX executable,
|
|
||||||
* packages macOS as a `.app` bundle inside a zip,
|
|
||||||
* packages Linux as an AppImage.
|
|
||||||
|
|
||||||
Run it manually from GitHub Actions with the default `v3,v4` input, or enter a single version such as `v4`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔁 Updating Avida-ED Versions
|
## 🔁 Updating Avida-ED Versions
|
||||||
|
|
@ -396,3 +374,5 @@ This repository's content was largely derived from prompting
|
||||||
OpenAI's ChatGPT with GPT 5.
|
OpenAI's ChatGPT with GPT 5.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
64
TEST_PLAN.md
64
TEST_PLAN.md
|
|
@ -1,64 +0,0 @@
|
||||||
# Avida-ED App Test Plan
|
|
||||||
|
|
||||||
This builder packages the browser apps, so tests should cover two separate layers:
|
|
||||||
|
|
||||||
1. Browser app behavior in the canonical repositories.
|
|
||||||
2. Packaged executable behavior in this builder.
|
|
||||||
|
|
||||||
## Canonical App Tests
|
|
||||||
|
|
||||||
The canonical v3 and v4 repositories should own browser-level behavior tests. Current coverage:
|
|
||||||
|
|
||||||
- Load the app through Playwright with `?avidaTest=1`.
|
|
||||||
- Wait for the Avida worker to start.
|
|
||||||
- Import a minimal default experiment.
|
|
||||||
- Verify `webGridData` and `webPopulationStats` messages arrive.
|
|
||||||
- Assert no browser errors were captured.
|
|
||||||
- Exercise the missing-parent-series population-stats regression directly.
|
|
||||||
|
|
||||||
Next browser tests to add:
|
|
||||||
|
|
||||||
- Inject an ancestor sequence, step several updates, and verify population count changes.
|
|
||||||
- Trace an organism with `webOrgTraceBySequence` and verify snapshots are produced.
|
|
||||||
- Simulate the freezer offspring delete/rename path that previously caused stale DOM removal.
|
|
||||||
- Import a saved workspace containing multiple ancestors and verify all injection responses are handled.
|
|
||||||
- Export CSV from empty and populated page states.
|
|
||||||
|
|
||||||
## Packaged App Tests
|
|
||||||
|
|
||||||
The packaged executable should be validated without depending on visual inspection. The Rust wrapper already logs:
|
|
||||||
|
|
||||||
- Local HTTP server URL.
|
|
||||||
- HTTP 200/404 responses.
|
|
||||||
- WebView page-load start/finish.
|
|
||||||
- JavaScript errors, unhandled rejections, and proxied `console.error` output.
|
|
||||||
|
|
||||||
Use `tests/smoke_appimage.sh` to run a built AppImage briefly, capture those signals, and fail if the app never loads or JavaScript errors appear.
|
|
||||||
|
|
||||||
Recommended Linux smoke matrix:
|
|
||||||
|
|
||||||
- `Avida-ED-v3-Linux-x86_64.AppImage`
|
|
||||||
- `Avida-ED-v4-Linux-x86_64.AppImage`
|
|
||||||
- Run under `xvfb-run` in CI when no display is available.
|
|
||||||
- Run once with software GL environment variables for older GPUs:
|
|
||||||
`LIBGL_ALWAYS_SOFTWARE=1 WEBKIT_DISABLE_COMPOSITING_MODE=1`.
|
|
||||||
|
|
||||||
The AppImage smoke test intentionally treats timeout exit as success after the app has loaded, because the app is expected to keep running until the window is closed.
|
|
||||||
|
|
||||||
Recommended Windows smoke matrix:
|
|
||||||
|
|
||||||
- `Avida-ED-v3-Windows-x64.zip`
|
|
||||||
- `Avida-ED-v4-Windows-x64.zip`
|
|
||||||
- Verify `run.bat`, `avidaed_onefile.exe`, and `README.txt` are present.
|
|
||||||
- Launch `run.bat` on a Windows VM with WebView2 Runtime installed.
|
|
||||||
- Repeat with a bundled WebView2 Fixed Runtime when available.
|
|
||||||
|
|
||||||
Recommended macOS smoke matrix:
|
|
||||||
|
|
||||||
- `Avida-ED-v3-macOS.zip`
|
|
||||||
- `Avida-ED-v4-macOS.zip`
|
|
||||||
- Verify the zip contains `Avida-ED-v*.app/Contents/MacOS/Avida-ED`.
|
|
||||||
- Launch with `open Avida-ED-v*.app` on a clean macOS VM.
|
|
||||||
- Check Console or stderr for `Avida-ED page-load finished` and no proxied JavaScript errors.
|
|
||||||
|
|
||||||
The native GitHub Actions workflow builds and uploads these artifacts, but it does not replace VM launch testing. Use the uploaded artifacts as the input to manual or future automated OS-level smoke tests.
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
HERE="$(dirname "$(readlink -f "$0")")"
|
HERE="$(dirname "$(readlink -f "$0")")"
|
||||||
exec "$HERE/usr/bin/avidaed_onefile"
|
exec "$HERE/usr/bin/avidaed_onefile"
|
||||||
|
|
||||||
|
|
@ -1,23 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
VER="${1:?version folder, e.g., v4}"
|
VER="${1:?version folder, e.g., v4}"
|
||||||
BINPATH="${2:-server-ui/target/release/avidaed_onefile}"
|
BINPATH="${2:-../..//server-ui/target/release/avidaed_onefile}"
|
||||||
case "$VER" in
|
|
||||||
v3) APP_ASSET_DIR="apps/v3/Avida-ED" ;;
|
|
||||||
v4) APP_ASSET_DIR="apps/v4/Avida-ED-Eco" ;;
|
|
||||||
*) echo "error: unsupported version: $VER" >&2; exit 2 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if ! command -v appimagetool >/dev/null 2>&1; then
|
|
||||||
echo "error: appimagetool is required to build the Linux AppImage" >&2
|
|
||||||
echo "Install appimagetool or run only 'make VER=${VER} build-linux' to build the Linux binary." >&2
|
|
||||||
exit 127
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -x "$BINPATH" ]; then
|
|
||||||
echo "error: Linux binary not found or not executable: $BINPATH" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
APPDIR="AvidaED-${VER}.AppDir"
|
APPDIR="AvidaED-${VER}.AppDir"
|
||||||
rm -rf "$APPDIR"; mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/share/icons/hicolor/256x256/apps"
|
rm -rf "$APPDIR"; mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/share/icons/hicolor/256x256/apps"
|
||||||
|
|
@ -26,19 +10,9 @@ cp "$(dirname "$0")/AppRun" "$APPDIR/AppRun"
|
||||||
chmod +x "$APPDIR/AppRun"
|
chmod +x "$APPDIR/AppRun"
|
||||||
cp "$(dirname "$0")/desktop/avidaed.desktop" "$APPDIR/avidaed.desktop"
|
cp "$(dirname "$0")/desktop/avidaed.desktop" "$APPDIR/avidaed.desktop"
|
||||||
|
|
||||||
if [ -f "$APP_ASSET_DIR/avida-ed-icon.png" ]; then
|
# placeholder icon; replace with your PNG
|
||||||
cp "$APP_ASSET_DIR/avida-ed-icon.png" "$APPDIR/avidaed.png"
|
convert -size 256x256 xc:white "$APPDIR/usr/share/icons/hicolor/256x256/apps/avidaed.png" 2>/dev/null || true
|
||||||
else
|
|
||||||
convert -size 256x256 xc:white "$APPDIR/avidaed.png" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
cp "$APPDIR/avidaed.png" "$APPDIR/usr/share/icons/hicolor/256x256/apps/avidaed.png"
|
|
||||||
|
|
||||||
APPIMAGETOOL_ARGS=()
|
appimagetool "$APPDIR" "Avida-ED-${VER}-Linux-x86_64.AppImage"
|
||||||
if [ -n "${APPIMAGETOOL_RUNTIME_FILE:-}" ]; then
|
echo "Wrote Avida-ED-${VER}-Linux-x86_64.AppImage"
|
||||||
APPIMAGETOOL_ARGS+=(--runtime-file "$APPIMAGETOOL_RUNTIME_FILE")
|
|
||||||
fi
|
|
||||||
|
|
||||||
OUTFILE="Avida-ED-${VER}-Linux-x86_64.AppImage"
|
|
||||||
rm -f "$OUTFILE"
|
|
||||||
appimagetool "${APPIMAGETOOL_ARGS[@]}" "$APPDIR" "$OUTFILE"
|
|
||||||
echo "Wrote $OUTFILE"
|
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,14 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
VER="${1:?v3|v4}"
|
VER="${1:?v3|v4}"
|
||||||
BINPATH="${2:-../../server-ui/target/release/avidaed_onefile}"
|
BINPATH="${2:-../../server-ui/target/release/avidaed_onefile}"
|
||||||
OUTDIR="${3:-.}"
|
|
||||||
APP="Avida-ED-${VER}.app"
|
APP="Avida-ED-${VER}.app"
|
||||||
ZIP="Avida-ED-${VER}-macOS.zip"
|
|
||||||
rm -rf "$APP"
|
rm -rf "$APP"
|
||||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
||||||
cp "$BINPATH" "$APP/Contents/MacOS/Avida-ED"
|
cp "$BINPATH" "$APP/Contents/MacOS/Avida-ED"
|
||||||
chmod +x "$APP/Contents/MacOS/Avida-ED"
|
chmod +x "$APP/Contents/MacOS/Avida-ED"
|
||||||
# Info.plist
|
# Info.plist
|
||||||
sed "s/{{VER}}/${VER}/g" "$(dirname "$0")/info.plist.tmpl" > "$APP/Contents/Info.plist"
|
sed "s/{{VER}}/${VER}/g" "$(dirname "$0")/Info.plist.tmpl" > "$APP/Contents/Info.plist"
|
||||||
# Icon placeholder
|
# Icon placeholder
|
||||||
sips -s format icns /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericApplicationIcon.icns --out "$APP/Contents/Resources/AppIcon.icns" >/dev/null 2>&1 || true
|
sips -s format icns /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericApplicationIcon.icns --out "$APP/Contents/Resources/AppIcon.icns" >/dev/null 2>&1 || true
|
||||||
if command -v codesign >/dev/null 2>&1; then
|
echo "Wrote $APP (unsigned)."
|
||||||
codesign --force --deep --sign - "$APP" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
mkdir -p "$OUTDIR"
|
|
||||||
if command -v ditto >/dev/null 2>&1; then
|
|
||||||
ditto -c -k --keepParent "$APP" "$OUTDIR/$ZIP"
|
|
||||||
else
|
|
||||||
zip -qry "$OUTDIR/$ZIP" "$APP"
|
|
||||||
fi
|
|
||||||
echo "Wrote $APP and $OUTDIR/$ZIP"
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,32 @@
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory=$true)][string]$Version,
|
[Parameter(Mandatory=$true)][string]$Version, # v3 or v4
|
||||||
[Parameter(Mandatory=$true)][string]$BinPath,
|
[Parameter(Mandatory=$true)][string]$BinPath, # path to target\release\avidaed_onefile.exe
|
||||||
[string]$WV2Fixed = "",
|
[Parameter(Mandatory=$true)][string]$WV2Fixed # path to WebView2 Fixed Runtime folder
|
||||||
[string]$SevenZip = "C:\Program Files\7-Zip\7z.exe",
|
|
||||||
[string]$SfxModule = "C:\Program Files\7-Zip\7z.sfx",
|
|
||||||
[string]$OutputDir = "."
|
|
||||||
)
|
)
|
||||||
|
$Work = Join-Path $PSScriptRoot "payload_$Version"
|
||||||
|
Remove-Item $Work -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
New-Item -ItemType Directory -Force -Path $Work | Out-Null
|
||||||
|
Copy-Item $BinPath -Destination (Join-Path $Work "avidaed_onefile.exe")
|
||||||
|
Copy-Item $WV2Fixed -Destination (Join-Path $Work "WebView2Fixed") -Recurse
|
||||||
|
|
||||||
|
# launcher
|
||||||
|
$runbat = @"
|
||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
set WEBVIEW2_BROWSER_EXECUTABLE_FOLDER=%~dp0WebView2Fixed
|
||||||
|
start "" "%~dp0avidaed_onefile.exe"
|
||||||
|
"@
|
||||||
|
$runbat | Out-File -Encoding ASCII (Join-Path $Work "run.bat")
|
||||||
|
|
||||||
|
# create 7z archive
|
||||||
|
$SevenZip = "C:\Program Files\7-Zip\7z.exe"
|
||||||
|
& $SevenZip a -t7z (Join-Path $PSScriptRoot "payload_$Version.7z") "$Work\*"
|
||||||
|
|
||||||
|
# concatenate SFX + config + archive -> one EXE
|
||||||
|
$Sfx = Join-Path $PSScriptRoot "7z.sfx"
|
||||||
|
$Cfg = Join-Path $PSScriptRoot "config.txt"
|
||||||
|
$Out = Join-Path $PSScriptRoot ("Avida-ED-$Version-Windows.exe")
|
||||||
|
Get-Content $Sfx -Encoding Byte, $Cfg -Encoding Byte, (Join-Path $PSScriptRoot "payload_$Version.7z") -Encoding Byte |
|
||||||
|
Set-Content $Out -Encoding Byte
|
||||||
|
Write-Host "Wrote $Out"
|
||||||
|
|
||||||
& (Join-Path $PSScriptRoot "make_windows_sfx.ps1") `
|
|
||||||
-Version $Version `
|
|
||||||
-BinPath $BinPath `
|
|
||||||
-WV2Fixed $WV2Fixed `
|
|
||||||
-SevenZip $SevenZip `
|
|
||||||
-SfxModule $SfxModule `
|
|
||||||
-OutputDir $OutputDir
|
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
param(
|
|
||||||
[Parameter(Mandatory=$true)][string]$Version,
|
|
||||||
[Parameter(Mandatory=$true)][string]$BinPath,
|
|
||||||
[string]$WV2Fixed = "",
|
|
||||||
[string]$SevenZip = "C:\Program Files\7-Zip\7z.exe",
|
|
||||||
[string]$SfxModule = "C:\Program Files\7-Zip\7z.sfx",
|
|
||||||
[string]$OutputDir = "."
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
if ($Version -notin @("v3", "v4")) {
|
|
||||||
throw "Unsupported Version '$Version'. Use v3 or v4."
|
|
||||||
}
|
|
||||||
if (!(Test-Path $BinPath)) {
|
|
||||||
throw "Missing binary: $BinPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..")
|
|
||||||
$OutRoot = Resolve-Path $OutputDir
|
|
||||||
$Work = Join-Path $PSScriptRoot "payload_$Version"
|
|
||||||
$Archive = Join-Path $PSScriptRoot "payload_$Version.7z"
|
|
||||||
$ZipOut = Join-Path $OutRoot "Avida-ED-$Version-Windows-x64.zip"
|
|
||||||
$ExeOut = Join-Path $OutRoot "Avida-ED-$Version-Windows-x64.exe"
|
|
||||||
|
|
||||||
Remove-Item $Work -Recurse -Force -ErrorAction SilentlyContinue
|
|
||||||
Remove-Item $Archive -Force -ErrorAction SilentlyContinue
|
|
||||||
New-Item -ItemType Directory -Force -Path $Work | Out-Null
|
|
||||||
|
|
||||||
Copy-Item $BinPath -Destination (Join-Path $Work "avidaed_onefile.exe")
|
|
||||||
|
|
||||||
$HasFixedRuntime = $false
|
|
||||||
if ($WV2Fixed -and (Test-Path $WV2Fixed)) {
|
|
||||||
Copy-Item $WV2Fixed -Destination (Join-Path $Work "WebView2Fixed") -Recurse
|
|
||||||
$HasFixedRuntime = $true
|
|
||||||
}
|
|
||||||
|
|
||||||
$runbat = @"
|
|
||||||
@echo off
|
|
||||||
setlocal
|
|
||||||
set HERE=%~dp0
|
|
||||||
if exist "%HERE%WebView2Fixed\msedgewebview2.exe" set WEBVIEW2_BROWSER_EXECUTABLE_FOLDER=%HERE%WebView2Fixed
|
|
||||||
start "" "%HERE%avidaed_onefile.exe"
|
|
||||||
"@
|
|
||||||
$runbat | Out-File -Encoding ASCII (Join-Path $Work "run.bat")
|
|
||||||
|
|
||||||
$readme = @"
|
|
||||||
Avida-ED $Version for Windows
|
|
||||||
|
|
||||||
Run run.bat to start Avida-ED.
|
|
||||||
|
|
||||||
This package includes a WebView2 Fixed Runtime: $HasFixedRuntime
|
|
||||||
If no fixed runtime is bundled, Microsoft Edge WebView2 Runtime must already be installed.
|
|
||||||
"@
|
|
||||||
$readme | Out-File -Encoding UTF8 (Join-Path $Work "README.txt")
|
|
||||||
|
|
||||||
if (!(Test-Path $SevenZip)) {
|
|
||||||
throw "Missing 7-Zip at $SevenZip"
|
|
||||||
}
|
|
||||||
|
|
||||||
& $SevenZip a -tzip $ZipOut "$Work\*" | Write-Host
|
|
||||||
Write-Host "Wrote $ZipOut"
|
|
||||||
|
|
||||||
if (Test-Path $SfxModule) {
|
|
||||||
& $SevenZip a -t7z $Archive "$Work\*" | Write-Host
|
|
||||||
$Cfg = Join-Path $PSScriptRoot "config.txt"
|
|
||||||
$OutStream = [System.IO.File]::Create($ExeOut)
|
|
||||||
try {
|
|
||||||
foreach ($Path in @($SfxModule, $Cfg, $Archive)) {
|
|
||||||
$Bytes = [System.IO.File]::ReadAllBytes($Path)
|
|
||||||
$OutStream.Write($Bytes, 0, $Bytes.Length)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
$OutStream.Close()
|
|
||||||
}
|
|
||||||
Write-Host "Wrote $ExeOut"
|
|
||||||
} else {
|
|
||||||
Write-Warning "SFX module not found at $SfxModule; skipped EXE creation."
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,13 +5,14 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wry = "0.39" # WebView wrapper: WebView2/WKWebView/WebKitGTK
|
wry = "0.39" # WebView wrapper: WebView2/WKWebView/WebKitGTK
|
||||||
tao = "0.28" # Window/event loop used by wry
|
tiny-http = "0.12" # tiny static server
|
||||||
tiny_http = "0.12" # tiny static server
|
|
||||||
include_dir = "0.7" # embed webroot into binary
|
include_dir = "0.7" # embed webroot into binary
|
||||||
mime_guess = "2.0"
|
mime_guess = "2.0"
|
||||||
|
once_cell = "1.19"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
strip = "symbols"
|
strip = "symbols"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,3 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
use std::{fs, path::Path};
|
|
||||||
|
|
||||||
let webroot = Path::new("webroot");
|
|
||||||
if !webroot.exists() {
|
|
||||||
fs::create_dir_all(webroot).expect("create fallback webroot");
|
|
||||||
fs::write(
|
|
||||||
webroot.join("index.html"),
|
|
||||||
"<!doctype html><title>Avida-ED assets not injected</title><p>Run make fetch-v4 build-linux.</p>\n",
|
|
||||||
)
|
|
||||||
.expect("write fallback webroot index");
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=webroot");
|
println!("cargo:rerun-if-changed=webroot");
|
||||||
print_rerun_files(webroot);
|
|
||||||
println!("cargo:rerun-if-env-changed=AVIDA_ED_DEFAULT_PATH");
|
|
||||||
let default_path = std::env::var("AVIDA_ED_DEFAULT_PATH")
|
|
||||||
.unwrap_or_else(|_| "/index.html".to_string());
|
|
||||||
println!("cargo:rustc-env=AVIDA_ED_DEFAULT_PATH={default_path}");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_rerun_files(path: &std::path::Path) {
|
|
||||||
let Ok(entries) = std::fs::read_dir(path) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
for entry in entries.flatten() {
|
|
||||||
let path = entry.path();
|
|
||||||
if path.is_dir() {
|
|
||||||
print_rerun_files(&path);
|
|
||||||
} else {
|
|
||||||
println!("cargo:rerun-if-changed={}", path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
use include_dir::{include_dir, Dir};
|
use include_dir::{include_dir, Dir};
|
||||||
use tiny_http::{Request, Response, Method};
|
use tiny_http::{Request, Response, Method};
|
||||||
use tao::{
|
use wry::{
|
||||||
event::{Event, StartCause, WindowEvent},
|
application::event::{Event, StartCause, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoop},
|
application::event_loop::{ControlFlow, EventLoop},
|
||||||
window::WindowBuilder,
|
application::window::WindowBuilder,
|
||||||
|
webview::WebViewBuilder
|
||||||
};
|
};
|
||||||
use wry::{PageLoadEvent, WebViewBuilder};
|
use std::{net::TcpListener, thread, time::Duration};
|
||||||
use std::{net::TcpListener, time::Duration};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
static WEBROOT: Dir = include_dir!("$CARGO_MANIFEST_DIR/webroot");
|
static WEBROOT: Dir = include_dir!("$CARGO_MANIFEST_DIR/webroot");
|
||||||
static DEFAULT_PATH: &str = env!("AVIDA_ED_DEFAULT_PATH");
|
static DEFAULT_PATH: &str = "/Avida-ED-Eco/index.html";
|
||||||
|
|
||||||
fn mime(p: &str)->&'static str{
|
fn mime(p: &str)->&'static str{
|
||||||
if p.ends_with(".wasm"){return "application/wasm";}
|
if p.ends_with(".wasm"){return "application/wasm";}
|
||||||
|
|
@ -19,7 +19,7 @@ fn mime(p: &str)->&'static str{
|
||||||
if p.ends_with(".html") || p.ends_with(".htm"){return "text/html; charset=utf-8";}
|
if p.ends_with(".html") || p.ends_with(".htm"){return "text/html; charset=utf-8";}
|
||||||
mime_guess::from_path(p).first_raw().unwrap_or("application/octet-stream")
|
mime_guess::from_path(p).first_raw().unwrap_or("application/octet-stream")
|
||||||
}
|
}
|
||||||
fn serve(req:Request){
|
fn serve(mut req:Request){
|
||||||
if req.method()!=&Method::Get && req.method()!=&Method::Head{
|
if req.method()!=&Method::Get && req.method()!=&Method::Head{
|
||||||
let _=req.respond(Response::from_string("Method Not Allowed").with_status_code(405));return;
|
let _=req.respond(Response::from_string("Method Not Allowed").with_status_code(405));return;
|
||||||
}
|
}
|
||||||
|
|
@ -28,21 +28,17 @@ fn serve(req:Request){
|
||||||
if path=="/"{path=DEFAULT_PATH.to_string();}
|
if path=="/"{path=DEFAULT_PATH.to_string();}
|
||||||
let fpath=path.trim_start_matches('/');
|
let fpath=path.trim_start_matches('/');
|
||||||
if let Some(f)=WEBROOT.get_file(fpath){
|
if let Some(f)=WEBROOT.get_file(fpath){
|
||||||
eprintln!("Avida-ED HTTP 200 {path}");
|
|
||||||
let mut resp=Response::from_data(f.contents().to_vec());
|
let mut resp=Response::from_data(f.contents().to_vec());
|
||||||
let _=resp.add_header(tiny_http::Header::from_bytes(b"Content-Type", mime(&path)).unwrap());
|
let _=resp.add_header(tiny_http::Header::from_bytes(b"Content-Type", mime(&path)).unwrap());
|
||||||
let _=resp.add_header(tiny_http::Header::from_bytes(b"Cache-Control", b"no-store").unwrap());
|
let _=resp.add_header(tiny_http::Header::from_bytes(b"Cache-Control", b"no-store").unwrap());
|
||||||
let _=req.respond(resp);
|
let _=req.respond(resp);
|
||||||
}else{
|
}else{
|
||||||
eprintln!("Avida-ED HTTP 404 {path}");
|
|
||||||
let _=req.respond(Response::from_string("Not Found").with_status_code(404));
|
let _=req.respond(Response::from_string("Not Found").with_status_code(404));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn main()->Result<()>{
|
fn main()->Result<()>{
|
||||||
let listener=TcpListener::bind(("127.0.0.1",0))?;
|
let listener=TcpListener::bind(("127.0.0.1",0))?; listener.set_nonblocking(true)?;
|
||||||
let port=listener.local_addr()?.port();
|
let port=listener.local_addr()?.port(); let srv=tiny_http::Server::from_tcp(listener)?;
|
||||||
let srv=tiny_http::Server::from_listener(listener, None)
|
|
||||||
.map_err(|e| anyhow::anyhow!("failed to create HTTP server: {e}"))?;
|
|
||||||
std::thread::spawn(move||{
|
std::thread::spawn(move||{
|
||||||
loop{
|
loop{
|
||||||
match srv.recv_timeout(Duration::from_millis(200)){
|
match srv.recv_timeout(Duration::from_millis(200)){
|
||||||
|
|
@ -51,71 +47,17 @@ fn main()->Result<()>{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let url=format!("http://127.0.0.1:{port}{DEFAULT_PATH}");
|
let url=format!("http://127.0.0.1:{port}{DEFAULT_PATH}");
|
||||||
let event_loop=EventLoop::new();
|
let event_loop=EventLoop::new()?;
|
||||||
let window=WindowBuilder::new()
|
let window=WindowBuilder::new()
|
||||||
.with_title("Avida-ED")
|
.with_title("Avida-ED")
|
||||||
.with_inner_size(tao::dpi::LogicalSize::new(1280.0,800.0))
|
.with_inner_size(wry::application::dpi::LogicalSize::new(1280.0,800.0))
|
||||||
.build(&event_loop)?;
|
.build(&event_loop)?;
|
||||||
eprintln!("Avida-ED loading {url}");
|
let _wv=WebViewBuilder::new(&window)?.with_url(&url)?.build()?;
|
||||||
|
|
||||||
#[cfg(any(
|
|
||||||
target_os = "windows",
|
|
||||||
target_os = "macos",
|
|
||||||
target_os = "ios",
|
|
||||||
target_os = "android"
|
|
||||||
))]
|
|
||||||
let builder = WebViewBuilder::new(&window);
|
|
||||||
|
|
||||||
#[cfg(not(any(
|
|
||||||
target_os = "windows",
|
|
||||||
target_os = "macos",
|
|
||||||
target_os = "ios",
|
|
||||||
target_os = "android"
|
|
||||||
)))]
|
|
||||||
let builder = {
|
|
||||||
use tao::platform::unix::WindowExtUnix;
|
|
||||||
use wry::WebViewBuilderExtUnix;
|
|
||||||
let vbox = window.default_vbox().ok_or_else(|| anyhow::anyhow!("failed to get GTK window container"))?;
|
|
||||||
WebViewBuilder::new_gtk(vbox)
|
|
||||||
};
|
|
||||||
|
|
||||||
let _wv=builder
|
|
||||||
.with_initialization_script(
|
|
||||||
r#"
|
|
||||||
(() => {
|
|
||||||
const send = (message) => {
|
|
||||||
try {
|
|
||||||
if (window.ipc && window.ipc.postMessage) window.ipc.postMessage(String(message));
|
|
||||||
} catch (_) {}
|
|
||||||
};
|
|
||||||
window.addEventListener('error', (event) => {
|
|
||||||
send(`js-error:${event.message} at ${event.filename}:${event.lineno}:${event.colno}`);
|
|
||||||
});
|
|
||||||
window.addEventListener('unhandledrejection', (event) => {
|
|
||||||
send(`js-rejection:${event.reason}`);
|
|
||||||
});
|
|
||||||
console.error = new Proxy(console.error, {
|
|
||||||
apply(target, thisArg, args) {
|
|
||||||
send(`console-error:${args.map(String).join(' ')}`);
|
|
||||||
return Reflect.apply(target, thisArg, args);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.with_ipc_handler(|req| eprintln!("Avida-ED WebView {}", req.body()))
|
|
||||||
.with_on_page_load_handler(|event, url| {
|
|
||||||
let event_name = match event {
|
|
||||||
PageLoadEvent::Started => "started",
|
|
||||||
PageLoadEvent::Finished => "finished",
|
|
||||||
};
|
|
||||||
eprintln!("Avida-ED page-load {event_name} {url}");
|
|
||||||
})
|
|
||||||
.with_url(&url)
|
|
||||||
.build()?;
|
|
||||||
event_loop.run(move|e,_,cf|{
|
event_loop.run(move|e,_,cf|{
|
||||||
*cf=ControlFlow::Wait;
|
*cf=wry::application::event_loop::ControlFlow::Wait;
|
||||||
if let Event::WindowEvent{event:WindowEvent::CloseRequested,..}=e{*cf=ControlFlow::Exit;}
|
if let Event::WindowEvent{event:WindowEvent::CloseRequested,..}=e{*cf=wry::application::event_loop::ControlFlow::Exit;}
|
||||||
if let Event::NewEvents(StartCause::Init)=e{}
|
if let Event::NewEvents(StartCause::Init)=e{}
|
||||||
});
|
})?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
cat <<'USAGE'
|
|
||||||
Usage: tests/smoke_appimage.sh PATH_TO_APPIMAGE [SECONDS]
|
|
||||||
|
|
||||||
Runs an Avida-ED AppImage long enough to verify wrapper startup, embedded HTTP
|
|
||||||
serving, WebView page load, and absence of proxied JavaScript errors.
|
|
||||||
|
|
||||||
The app normally keeps running until its window closes, so timeout exit code 124
|
|
||||||
is accepted after the expected load signals appear.
|
|
||||||
USAGE
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
|
||||||
usage
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
appimage="${1:?missing AppImage path}"
|
|
||||||
seconds="${2:-20}"
|
|
||||||
|
|
||||||
if [[ ! -x "$appimage" ]]; then
|
|
||||||
echo "error: AppImage is missing or not executable: $appimage" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
tmpdir="$(mktemp -d)"
|
|
||||||
logfile="$tmpdir/appimage.log"
|
|
||||||
cleanup() {
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
cmd=(
|
|
||||||
env
|
|
||||||
LIBGL_ALWAYS_SOFTWARE="${LIBGL_ALWAYS_SOFTWARE:-1}"
|
|
||||||
WEBKIT_DISABLE_COMPOSITING_MODE="${WEBKIT_DISABLE_COMPOSITING_MODE:-1}"
|
|
||||||
"$appimage"
|
|
||||||
)
|
|
||||||
|
|
||||||
if [[ -z "${DISPLAY:-}" ]] && command -v xvfb-run >/dev/null 2>&1; then
|
|
||||||
cmd=(xvfb-run -a "${cmd[@]}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
set +e
|
|
||||||
timeout "$seconds" "${cmd[@]}" >"$logfile" 2>&1
|
|
||||||
status=$?
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cat "$logfile"
|
|
||||||
|
|
||||||
if [[ "$status" -ne 0 && "$status" -ne 124 ]]; then
|
|
||||||
echo "error: AppImage exited before smoke window completed; status=$status" >&2
|
|
||||||
exit "$status"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! grep -q "Avida-ED loading http://127.0.0.1:" "$logfile"; then
|
|
||||||
echo "error: wrapper did not log local HTTP load URL" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! grep -q "Avida-ED HTTP 200" "$logfile"; then
|
|
||||||
echo "error: embedded HTTP server did not serve any successful response" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! grep -q "Avida-ED page-load finished" "$logfile"; then
|
|
||||||
echo "error: WebView did not finish loading the app page" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if grep -E "Avida-ED WebView (js-error|js-rejection|console-error):" "$logfile"; then
|
|
||||||
echo "error: WebView reported JavaScript errors" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "AppImage smoke passed: $appimage"
|
|
||||||
|
|
@ -2,5 +2,4 @@ FROM alpine:3.20
|
||||||
RUN apk add --no-cache ca-certificates curl bash rsync
|
RUN apk add --no-cache ca-certificates curl bash rsync
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
COPY fetch_assets.sh /work/fetch_assets.sh
|
COPY fetch_assets.sh /work/fetch_assets.sh
|
||||||
RUN chmod +x /work/fetch_assets.sh
|
|
||||||
ENTRYPOINT ["/work/fetch_assets.sh"]
|
ENTRYPOINT ["/work/fetch_assets.sh"]
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,29 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
MODE="${MODE:-github}" # github|docker|url
|
MODE="${MODE:-docker}" # docker|url
|
||||||
ED_VER="${ED_VER:-v4}" # v3|v4
|
ED_VER="${ED_VER:-v4}" # v3|v4
|
||||||
OUTDIR="${OUTDIR:-/out}"
|
OUTDIR="${OUTDIR:-/out}"
|
||||||
case "$ED_VER" in
|
|
||||||
v3) APP_DIR="Avida-ED"; GITHUB_REPO="https://github.com/Avida-ED/Avida-ED3.git" ;;
|
|
||||||
v4) APP_DIR="Avida-ED-Eco"; GITHUB_REPO="https://github.com/Avida-ED/Avida-ED4.git" ;;
|
|
||||||
*) echo "Unknown ED_VER=$ED_VER" >&2; exit 2 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
mkdir -p "$OUTDIR"
|
mkdir -p "$OUTDIR"
|
||||||
case "$MODE" in
|
case "$MODE" in
|
||||||
github)
|
|
||||||
TMPDIR="$(mktemp -d)"
|
|
||||||
trap 'rm -rf "$TMPDIR"' EXIT
|
|
||||||
git clone --depth 1 "$GITHUB_REPO" "$TMPDIR/source"
|
|
||||||
rm -rf "$OUTDIR/$APP_DIR"
|
|
||||||
mkdir -p "$OUTDIR/$APP_DIR"
|
|
||||||
if command -v rsync >/dev/null 2>&1; then
|
|
||||||
rsync -a --exclude .git "$TMPDIR/source"/ "$OUTDIR/$APP_DIR"/
|
|
||||||
else
|
|
||||||
(cd "$TMPDIR/source" && tar --exclude .git -cf - .) | (cd "$OUTDIR/$APP_DIR" && tar -xf -)
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
docker)
|
docker)
|
||||||
# 1) Start your aed-docker container and copy its served webroot
|
# 1) Start your aed-docker container and copy its served webroot
|
||||||
# Expectation: container serves Avida-ED-Eco at /usr/share/nginx/html/Avida-ED-Eco
|
# Expectation: container serves Avida-ED-Eco at /usr/share/nginx/html/Avida-ED-Eco
|
||||||
if ! CID="$(docker create --name aedtmp_$ED_VER welsberr/aed-docker:$ED_VER)"; then
|
CID="$(docker create --name aedtmp_$ED_VER welsberr/aed-docker:$ED_VER)"
|
||||||
echo "Could not create welsberr/aed-docker:$ED_VER. The canonical source is GitHub; try make fetch-$ED_VER." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
trap 'docker rm -f aedtmp_'"$ED_VER"' >/dev/null 2>&1 || true' EXIT
|
trap 'docker rm -f aedtmp_'"$ED_VER"' >/dev/null 2>&1 || true' EXIT
|
||||||
docker cp "aedtmp_$ED_VER:/usr/share/nginx/html/." "$OUTDIR/"
|
docker cp "aedtmp_$ED_VER:/usr/share/nginx/html/." "$OUTDIR/"
|
||||||
;;
|
;;
|
||||||
url)
|
url)
|
||||||
URL="${URL:-https://avida-ed.msu.edu/app4/}"
|
URL="${URL:-https://avida-ed.msu.edu/app4/}"
|
||||||
apk add --no-cache wget >/dev/null 2>&1 || true
|
apk add --no-cache wget >/dev/null 2>&1 || true
|
||||||
WGET_STATUS=0
|
|
||||||
wget --recursive --no-parent --page-requisites --adjust-extension \
|
wget --recursive --no-parent --page-requisites --adjust-extension \
|
||||||
--compression=auto --convert-links --timestamping \
|
--compression=auto --convert-links --timestamping \
|
||||||
--directory-prefix "$OUTDIR" \
|
--directory-prefix "$OUTDIR" \
|
||||||
"$URL" || WGET_STATUS=$?
|
"$URL"
|
||||||
# normalize into the directory shape expected by the Rust injection step
|
# normalize into $OUTDIR/Avida-ED-Eco as needed
|
||||||
if [ ! -d "$OUTDIR/$APP_DIR" ]; then
|
if [ ! -d "$OUTDIR/Avida-ED-Eco" ]; then
|
||||||
SUB="$(find "$OUTDIR" -type f -name index.html | head -n1)"
|
SUB="$(find "$OUTDIR" -type f -name index.html | head -n1)"
|
||||||
if [ -n "$SUB" ]; then
|
[ -n "$SUB" ] && rsync -a "$(dirname "$SUB")"/ "$OUTDIR/Avida-ED-Eco"/
|
||||||
if command -v rsync >/dev/null 2>&1; then
|
|
||||||
rsync -a "$(dirname "$SUB")"/ "$OUTDIR/$APP_DIR"/
|
|
||||||
else
|
|
||||||
cp -R "$(dirname "$SUB")"/. "$OUTDIR/$APP_DIR"/
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ ! -f "$OUTDIR/$APP_DIR/index.html" ]; then
|
|
||||||
echo "URL fetch failed: no normalized $OUTDIR/$APP_DIR/index.html found. wget exit status: $WGET_STATUS" >&2
|
|
||||||
exit "${WGET_STATUS:-1}"
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*) echo "Unknown MODE=$MODE" >&2; exit 1;;
|
*) echo "Unknown MODE=$MODE" >&2; exit 1;;
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
VER="${1:-${VER:-v4}}"
|
|
||||||
|
|
||||||
case "$VER" in
|
|
||||||
v3) APP_DIR="Avida-ED" ;;
|
|
||||||
v4) APP_DIR="Avida-ED-Eco" ;;
|
|
||||||
*) echo "Unsupported VER=$VER. Use v3 or v4." >&2; exit 2 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
SRC="apps/$VER/$APP_DIR"
|
|
||||||
DEST="server-ui/webroot/$APP_DIR"
|
|
||||||
|
|
||||||
if [ ! -f "$SRC/index.html" ]; then
|
|
||||||
echo "Missing $SRC/index.html. Run make fetch-$VER first." >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -rf server-ui/webroot
|
|
||||||
mkdir -p server-ui/webroot
|
|
||||||
|
|
||||||
if command -v rsync >/dev/null 2>&1; then
|
|
||||||
rsync -a "$SRC/" "$DEST/"
|
|
||||||
else
|
|
||||||
mkdir -p "$DEST"
|
|
||||||
cp -R "$SRC"/. "$DEST"/
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Injected $SRC into $DEST"
|
|
||||||
Loading…
Reference in New Issue