Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6da9717
Add the Release design spec (Windows zip and Linux AppImage via GitHu…
adrobi Oct 31, 2026
ac28dc4
Add the Release implementation plan (8 tasks)
adrobi Oct 31, 2026
b898ed7
Add a Linux build recipe for PhyML and PhyML-MPI
adrobi Oct 31, 2026
2d964fc
Make the app portable to Linux: tool names, data dir, mpirun, open fo…
adrobi Oct 31, 2026
baa19e4
Make the test suite pass on Linux and document Linux use
adrobi Oct 31, 2026
ea60daa
Describe third-party tools per platform and add build steps
adrobi Oct 31, 2026
377331b
Package the Windows zip with PyInstaller and smoke-test it
adrobi Oct 31, 2026
d39660c
Build and smoke-test the Linux AppImage
adrobi Oct 31, 2026
d7007e1
Add the RAxML-NG Windows vs Linux parity check
adrobi Oct 31, 2026
865c856
Add the release workflow: tools, packages, smoke tests, draft release
adrobi Oct 31, 2026
f191b49
Fix the release review findings: serial PhyML on Linux, pytest-free r…
adrobi Oct 31, 2026
983cbd3
Temporarily run the release workflow on pushes to the release branch
adrobi Oct 31, 2026
02bc59a
Make the fake tool write bytes so Cyrillic paths survive a cp1252 pipe
adrobi Oct 31, 2026
4aef719
TEMPORARY: find which test file crashes Python 3.11 on Linux at exit
adrobi Oct 31, 2026
fbee051
TEMPORARY: narrow down the Python 3.11 exit crash on Linux
adrobi Oct 31, 2026
cfe01fa
TEMPORARY: per-file and per-test runs for the Python 3.11 exit crash
adrobi Oct 31, 2026
a8a5621
TEMPORARY: measure psutil True/False refcounts on Python 3.11
adrobi Oct 31, 2026
caef407
TEMPORARY: measure psutil wait refcounts on Python 3.11
adrobi Oct 31, 2026
7acd77a
TEMPORARY: fix the refcount probe indentation
adrobi Oct 31, 2026
312ca14
TEMPORARY: per-test True/False refcount probe on Python 3.11
adrobi Oct 31, 2026
6cfe48c
Remove the temporary Python 3.11 crash diagnostics
adrobi Oct 31, 2026
0c2035b
Test Linux on Python 3.12 and 3.13; document Python 3.12+ for Linux s…
adrobi Oct 31, 2026
4f24bc6
Mark suffixed versions as pre-releases; drop the temporary branch tri…
adrobi Oct 31, 2026
fde5d99
README in the style of the other projects, CHANGELOG and bilingual re…
adrobi Oct 31, 2026
bfcb8ae
Build Linux PhyML for x86-64-v2 instead of -march=native
adrobi Oct 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# patches are applied to upstream LF sources: never convert their line endings
*.patch text eol=lf
*.sh text eol=lf
packaging/appimage/AppRun text eol=lf
*.desktop text eol=lf
270 changes: 270 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
name: release

# A tag vX.Y.Z[-suffix] builds both packages and creates a DRAFT release (never
# published automatically). A manual run builds and tests everything but uploads
# nothing to Releases: the packages stay as workflow artifacts.
on:
push:
tags: ['v*']
workflow_dispatch:

permissions:
contents: read

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

env:
PYTHON_VERSION: '3.13'

jobs:
version:
runs-on: ubuntu-22.04
timeout-minutes: 5
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Check the tag and derive the version
id: version
run: |
version=$(python3 packaging/release_meta.py version "$GITHUB_REF")
echo "version=$version" >> "$GITHUB_OUTPUT"

tests:
needs: version
uses: ./.github/workflows/tests.yml

tools-windows:
needs: version
runs-on: windows-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- uses: msys2/setup-msys2@v2
id: msys2
with:
msystem: UCRT64
update: true
install: >-
git unzip
mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja
- name: Cache Windows programs
id: cache
uses: actions/cache@v4
with:
path: bin
key: release-bin-windows-${{ hashFiles('packaging/third_party.json', 'packaging/build_raxml_windows.sh', 'packaging/patches/**') }}
- name: Fetch programs and build RAxML-NG
if: steps.cache.outputs.cache-hit != 'true'
shell: pwsh
run: |
$env:PHYMLGUI_BASH = Join-Path '${{ steps.msys2.outputs.msys2-location }}' 'usr\bin\bash.exe'
python scripts/fetch_third_party.py --platform windows --dest bin
- name: Install dependencies
run: pip install -r requirements.txt
- name: RAxML-NG parity run (Windows build)
run: python packaging/raxml_parity.py run --exe bin/raxml-ng.exe --out parity-windows
- uses: actions/upload-artifact@v4
with:
name: bin-windows
path: bin
- uses: actions/upload-artifact@v4
with:
name: parity-windows
path: parity-windows

tools-linux:
needs: version
runs-on: ubuntu-22.04
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install build tools and OpenMPI
run: >
sudo apt-get update && sudo apt-get install -y
openmpi-bin libopenmpi-dev build-essential autoconf automake libtool pkg-config
- name: Cache Linux programs
id: cache
uses: actions/cache@v4
with:
path: bin
key: release-bin-linux-${{ hashFiles('packaging/third_party.json', 'packaging/build_phyml_linux.sh') }}
- name: Fetch programs and build PhyML / PhyML-MPI
if: steps.cache.outputs.cache-hit != 'true'
run: python scripts/fetch_third_party.py --platform linux --dest bin
- name: Install dependencies
run: pip install -r requirements.txt
- name: RAxML-NG parity run (official Linux build)
run: python packaging/raxml_parity.py run --exe bin/raxml-ng --out parity-linux
# tar keeps the executable bits that upload-artifact would drop
- name: Pack programs
run: tar -czf bin-linux.tar.gz bin
- uses: actions/upload-artifact@v4
with:
name: bin-linux
path: bin-linux.tar.gz
- uses: actions/upload-artifact@v4
with:
name: parity-linux
path: parity-linux

raxml-parity:
needs: [tools-windows, tools-linux]
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- run: pip install biopython
- uses: actions/download-artifact@v4
with:
name: parity-windows
path: parity-windows
- uses: actions/download-artifact@v4
with:
name: parity-linux
path: parity-linux
- name: Compare the Windows build with the official Linux build
run: python packaging/raxml_parity.py compare parity-windows parity-linux

package-windows:
needs: [version, tests, raxml-parity]
runs-on: windows-latest
timeout-minutes: 40
env:
VERSION: ${{ needs.version.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- run: pip install -r requirements.txt pyinstaller
- uses: actions/download-artifact@v4
with:
name: bin-windows
path: bin
- name: Install Microsoft MPI (the bundled phyml-mpi.exe needs msmpi.dll)
run: |
python scripts/fetch_third_party.py --platform windows_runtime --dest mpi
Start-Process mpi/msmpisetup.exe -ArgumentList '-unattend' -Wait
- name: Build the zip
run: python packaging/package.py windows --version "$env:VERSION" --bin bin --out dist
- name: Smoke test
run: python packaging/smoke_test.py --package "dist/phymlGUI-$env:VERSION-windows-x64.zip" --version "$env:VERSION"
- uses: actions/upload-artifact@v4
with:
name: package-windows
path: dist/phymlGUI-*-windows-x64.zip

package-linux:
needs: [version, tests, raxml-parity]
runs-on: ubuntu-22.04
timeout-minutes: 40
env:
VERSION: ${{ needs.version.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Qt runtime libraries and OpenMPI
run: >
sudo apt-get update && sudo apt-get install -y
libegl1 libgl1 libxkbcommon0 libxkbcommon-x11-0 libdbus-1-3 libfontconfig1 libxcb-cursor0
openmpi-bin
- run: pip install -r requirements.txt pyinstaller
- uses: actions/download-artifact@v4
with:
name: bin-linux
- run: tar -xzf bin-linux.tar.gz
- name: Fetch the pinned appimagetool and runtime
run: python scripts/fetch_third_party.py --platform build_tools --dest tools
- name: Build the AppImage
run: >
python packaging/package.py linux --version "$VERSION" --bin bin --out dist
--appimagetool tools/appimagetool --runtime tools/runtime-x86_64
- name: Smoke test (with OpenMPI)
run: python packaging/smoke_test.py --package "dist/phymlGUI-$VERSION-x86_64.AppImage" --version "$VERSION" --mpi
- name: Run in a clean ubuntu:22.04 container
run: |
mkdir check
cp "dist/phymlGUI-$VERSION-x86_64.AppImage" check/phymlgui.AppImage
python - <<'EOF'
import json, sys
sys.path.insert(0, 'tests/e2e')
from dataset_gen import make_dataset, write_fasta
names, _, unaligned = make_dataset()
write_fasta('check/unaligned.fasta', names, unaligned)
json.dump({'engine': 'phyml', 'datatype': 'nt', 'model': 'HKY85',
'support_method': 'bootstrap', 'bootstrap_replicates': '4',
'use_mpi': False}, open('check/config.json', 'w'))
EOF
docker run --rm -v "$PWD/check:/check" -w /check ubuntu:22.04 sh -c '
set -e
./phymlgui.AppImage --appimage-extract-and-run --version
./phymlgui.AppImage --appimage-extract-and-run --file unaligned.fasta --config config.json
ls *_phyml_tree.txt'
- uses: actions/upload-artifact@v4
with:
name: package-linux
path: dist/phymlGUI-*-x86_64.AppImage

release:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: [version, package-windows, package-linux]
runs-on: ubuntu-22.04
timeout-minutes: 15
permissions:
contents: write
env:
VERSION: ${{ needs.version.outputs.version }}
TAG: ${{ github.ref_name }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
pattern: package-*
path: out
merge-multiple: true
- name: RAxML-NG sources and the Windows patch (AGPL-3.0)
run: |
url=$(python3 -c "import json; print(json.load(open('packaging/third_party.json'))['windows']['raxml']['url'])")
sha=$(python3 -c "import json; print(json.load(open('packaging/third_party.json'))['windows']['raxml']['sha256'])")
mkdir src && curl -fsSL -o src/raxml-ng_v2.0.3_source.zip "$url"
echo "$sha src/raxml-ng_v2.0.3_source.zip" | sha256sum -c -
python3 packaging/release_meta.py raxml-source src/raxml-ng_v2.0.3_source.zip \
packaging/patches/raxml-ng-2.0.3-windows.patch out/raxml-ng-2.0.3-source+patch.tar.gz
- name: Checksums and release notes
run: |
python3 packaging/release_meta.py sha256sums out/SHA256SUMS \
out/phymlGUI-*-windows-x64.zip out/phymlGUI-*-x86_64.AppImage out/raxml-ng-2.0.3-source+patch.tar.gz
python3 packaging/release_meta.py notes "$VERSION" notes.md
- name: Create or update the draft release
run: |
# vX.Y.Z-suffix (rc, beta) is a pre-release
prerelease=()
case "$VERSION" in *-*) prerelease=(--prerelease) ;; esac
files=(out/phymlGUI-*-windows-x64.zip out/phymlGUI-*-x86_64.AppImage
out/raxml-ng-2.0.3-source+patch.tar.gz out/SHA256SUMS)
if draft=$(gh release view "$TAG" --json isDraft -q .isDraft 2>/dev/null); then
if [ "$draft" != "true" ]; then
echo "::error::release $TAG is already published; not touching it"
exit 1
fi
gh release upload "$TAG" "${files[@]}" --clobber
if [ ${#prerelease[@]} -gt 0 ]; then gh release edit "$TAG" "${prerelease[@]}"; fi
else
gh release create "$TAG" --draft "${prerelease[@]}" --verify-tag --title "phymlGUI $VERSION" \
--notes-file notes.md "${files[@]}"
fi
32 changes: 24 additions & 8 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,48 @@ on:
push:
branches: [master]
pull_request:
workflow_call:

jobs:
test:
runs-on: windows-latest
timeout-minutes: 30
runs-on: ${{ matrix.os }}
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
python-version: ['3.11', '3.13']
# Linux starts at 3.12: on 3.11 a C extension's True/False over-decref aborts
# the interpreter at exit (bool_dealloc); 3.12+ makes those objects immortal
include:
- {os: windows-latest, python-version: '3.11'}
- {os: windows-latest, python-version: '3.13'}
- {os: ubuntu-22.04, python-version: '3.12'}
- {os: ubuntu-22.04, python-version: '3.13'}
env:
QT_QPA_PLATFORM: offscreen
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Qt runtime libraries, OpenMPI and build tools (Linux)
if: runner.os == 'Linux'
run: >
sudo apt-get update && sudo apt-get install -y
libegl1 libgl1 libxkbcommon0 libxkbcommon-x11-0 libdbus-1-3 libfontconfig1 libxcb-cursor0
openmpi-bin libopenmpi-dev build-essential autoconf automake libtool pkg-config
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Cache pinned IQ-TREE and MUSCLE
- name: Cache pinned third-party programs
id: third-party
uses: actions/cache@v4
with:
path: bin
key: third-party-${{ hashFiles('packaging/third_party.json') }}
- name: Fetch pinned IQ-TREE and MUSCLE
if: steps.third-party.outputs.cache-hit != 'true'
run: python scripts/fetch_third_party.py --dest bin
key: third-party-${{ runner.os }}-${{ hashFiles('packaging/third_party.json', 'packaging/build_phyml_linux.sh') }}
- name: Fetch pinned IQ-TREE and MUSCLE (Windows)
if: runner.os == 'Windows' && steps.third-party.outputs.cache-hit != 'true'
run: python scripts/fetch_third_party.py --platform windows --only iqtree,muscle --dest bin
- name: Fetch IQ-TREE, MUSCLE and build PhyML (Linux)
if: runner.os == 'Linux' && steps.third-party.outputs.cache-hit != 'true'
run: python scripts/fetch_third_party.py --platform linux --only iqtree,muscle,phyml --dest bin
- name: Tests
run: python -m pytest -q -rs
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
phyml-mpi.exe
/build
/dist
phymlgui.spec
/phymlgui.spec
__pycache__/
phyml_config.json
phymlgui v1.1/
Expand All @@ -14,3 +14,5 @@ release.json
release_resp.json
asset_resp.json
/bin/
/build-bin/
/build-parity-*/
Loading
Loading