Skip to content

Stabilize loss estimates and add CONTROL autonomy #8

Stabilize loss estimates and add CONTROL autonomy

Stabilize loss estimates and add CONTROL autonomy #8

name: Cross-platform automatic release
on:
push:
branches: [main]
paths:
- 'cross_platform/**'
- '.github/scripts/prepare_cross_platform_release.py'
- '.github/workflows/cross-platform-release.yml'
workflow_dispatch:
permissions:
contents: write
actions: write
concurrency:
group: pace-controller-cross-platform-release
cancel-in-progress: false
jobs:
screenshot:
name: Render real PySide6 screenshot
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
- uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: pip
cache-dependency-path: cross_platform/requirements.txt
- name: Install Qt runtime libraries
run: |
sudo apt-get update
sudo apt-get install -y libegl1 libgl1 libxkbcommon0 libxkbcommon-x11-0 libdbus-1-3 libfontconfig1 libxcb-cursor0 libxcb-xkb1 libxcb-image0 libxcb-icccm4 libxcb-render-util0 libxcb-keysyms1 libxcb-shape0 libxcb-randr0 libxcb-xfixes0
- name: Render and validate the current real interface
working-directory: cross_platform
env:
QT_QPA_PLATFORM: offscreen
PACE_CONTROLLER_DATA_DIR: ${{ runner.temp }}/pace-controller-data
run: |
python -m pip install .
python -m pace_controller --language en --screenshot docs/pace_controller_gui.png
python -c "from PySide6.QtGui import QImage; i=QImage('docs/pace_controller_gui.png'); assert not i.isNull() and i.width() >= 1000 and i.height() >= 700, (i.width(), i.height())"
- name: Commit the real screenshot
shell: bash
run: |
set -euo pipefail
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add cross_platform/docs/pace_controller_gui.png
if ! git diff --cached --quiet; then
git commit -m 'Update real cross-platform interface screenshot [skip ci]'
git fetch origin main
git rebase origin/main
git push origin HEAD:main
fi
prepare:
if: github.actor != 'github-actions[bot]' || github.event_name == 'workflow_dispatch'
needs: screenshot
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.prepare.outputs.version }}
commit_sha: ${{ steps.commit.outputs.commit_sha }}
steps:
- uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
- uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: pip
cache-dependency-path: cross_platform/requirements-dev.txt
- name: Install test and manual dependencies
run: |
sudo apt-get update
sudo apt-get install -y pandoc texlive-xetex texlive-latex-extra texlive-fonts-recommended libegl1 libgl1 libxkbcommon0 libxkbcommon-x11-0 libdbus-1-3 libfontconfig1 libxcb-cursor0 libxcb-xkb1 libxcb-image0 libxcb-icccm4 libxcb-render-util0 libxcb-keysyms1 libxcb-shape0 libxcb-randr0 libxcb-xfixes0
python -m pip install -r cross_platform/requirements-dev.txt
- name: Validate source before release
env:
QT_QPA_PLATFORM: offscreen
PACE_CONTROLLER_DATA_DIR: ${{ runner.temp }}/pace-controller-data
run: |
python tests/test_project.py
python -m pytest -q cross_platform/tests
python -m compileall -q cross_platform/src
python -c "from pathlib import Path; import hashlib; data=Path('PACE_Controller.ps1').read_bytes().replace(b'\r\n', b'\n'); assert hashlib.sha256(data).hexdigest() == 'aa6ffe5431dfab7d2ea998f9b59e8ac5163b0e3478e84a3c15e2e826fb356b8e'"
- name: Prepare semantic version and metadata
id: prepare
run: |
VERSION="$(python3 .github/scripts/prepare_cross_platform_release.py)"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Preparing PACE Controller v$VERSION"
- name: Build versioned PDF manual
run: |
pandoc cross_platform/docs/PACE_Controller_Manual.md \
--from markdown \
--resource-path=.:cross_platform:cross_platform/docs \
--pdf-engine=xelatex \
--output cross_platform/docs/PACE_Controller_Manual.pdf
test -s cross_platform/docs/PACE_Controller_Manual.pdf
- name: Validate prepared release
run: |
VERSION="${{ steps.prepare.outputs.version }}"
python tests/test_project.py
python -m pytest -q cross_platform/tests
python - <<'PY'
from pathlib import Path
import re
version = "${{ steps.prepare.outputs.version }}"
init = Path("cross_platform/src/pace_controller/__init__.py").read_text(encoding="utf-8")
pyproject = Path("cross_platform/pyproject.toml").read_text(encoding="utf-8")
assert f'__version__ = "{version}"' in init
assert f'version = "{version}"' in pyproject
for path in ["README.md", "cross_platform/README.md", "cross_platform/docs/PACE_Controller_Manual.md", "CITATION.cff"]:
assert version in Path(path).read_text(encoding="utf-8"), path
assert Path("cross_platform/docs/PACE_Controller_Manual.pdf").stat().st_size > 10_000
PY
- name: Commit prepared release
id: commit
run: |
set -euo pipefail
VERSION="${{ steps.prepare.outputs.version }}"
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add README.md CITATION.cff CHANGELOG.md cross_platform
if ! git diff --cached --quiet; then
git commit -m "Prepare cross-platform v${VERSION} release"
git fetch origin main
git rebase origin/main
git push origin HEAD:main
fi
echo "commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Create immutable release tag
env:
VERSION: ${{ steps.prepare.outputs.version }}
run: |
set -euo pipefail
if git ls-remote --exit-code --tags origin "refs/tags/v${VERSION}" >/dev/null 2>&1; then
echo "Tag v${VERSION} already exists; refusing to overwrite it."
exit 1
fi
git tag "v${VERSION}" "${{ steps.commit.outputs.commit_sha }}"
git push origin "v${VERSION}"
windows-exe:
name: Windows executable (x86_64)
needs: prepare
runs-on: windows-2022
steps:
- uses: actions/checkout@v6
with:
ref: v${{ needs.prepare.outputs.version }}
- uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: pip
cache-dependency-path: cross_platform/requirements-dev.txt
- name: Build standalone executable
shell: powershell
run: cross_platform/scripts/build_windows.ps1 -Version '${{ needs.prepare.outputs.version }}'
- name: Validate Windows PE file
shell: powershell
run: |
$path = 'cross_platform/dist/PACE-Controller-v${{ needs.prepare.outputs.version }}-Windows-x86_64.exe'
$bytes = [System.IO.File]::ReadAllBytes((Resolve-Path $path))
if ($bytes.Length -lt 2 -or $bytes[0] -ne 0x4D -or $bytes[1] -ne 0x5A) { throw 'Invalid Windows PE header.' }
Get-Item $path | Format-List Name,Length
- uses: actions/upload-artifact@v7
with:
name: pace-controller-windows
path: cross_platform/dist/PACE-Controller-v${{ needs.prepare.outputs.version }}-Windows-x86_64.exe
if-no-files-found: error
linux-exe:
name: Linux executable (x86_64)
needs: prepare
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
with:
ref: v${{ needs.prepare.outputs.version }}
- uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: pip
cache-dependency-path: cross_platform/requirements-dev.txt
- name: Install Linux build libraries
run: |
sudo apt-get update
sudo apt-get install -y libegl1 libgl1 libxkbcommon0 libxkbcommon-x11-0 libdbus-1-3 libfontconfig1 libxcb-cursor0 libxcb-xkb1 libxcb-image0 libxcb-icccm4 libxcb-render-util0 libxcb-keysyms1 libxcb-shape0 libxcb-randr0 libxcb-xfixes0
- name: Build standalone archive
run: bash cross_platform/scripts/build_linux.sh '${{ needs.prepare.outputs.version }}'
- name: Smoke-test packaged Linux GUI
env:
QT_QPA_PLATFORM: offscreen
PACE_CONTROLLER_DATA_DIR: ${{ runner.temp }}/pace-controller-data
run: |
cross_platform/dist/PACE-Controller-v${{ needs.prepare.outputs.version }}-Linux-x86_64 --language en --screenshot "${{ runner.temp }}/packaged-ui.png"
python -c "from PySide6.QtGui import QImage; import sys; i=QImage(sys.argv[1]); assert not i.isNull() and i.width() >= 1000 and i.height() >= 700" "${{ runner.temp }}/packaged-ui.png"
- uses: actions/upload-artifact@v7
with:
name: pace-controller-linux
path: cross_platform/dist/PACE-Controller-v${{ needs.prepare.outputs.version }}-Linux-x86_64.tar.gz
if-no-files-found: error
source-package:
name: Source archive and manual
needs: prepare
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with:
ref: v${{ needs.prepare.outputs.version }}
- name: Create complete offline source package
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
set -euo pipefail
PACKAGE="PACE-Controller-v${VERSION}-source"
mkdir -p "$PACKAGE"
cp -a cross_platform/. "$PACKAGE/"
cp LICENSE "$PACKAGE/"
find "$PACKAGE" -type d \( -name __pycache__ -o -name .pytest_cache \) -prune -exec rm -rf {} +
rm -rf "$PACKAGE/build" "$PACKAGE/dist"
zip -r "${PACKAGE}.zip" "$PACKAGE"
test -s "${PACKAGE}.zip"
cp cross_platform/docs/PACE_Controller_Manual.pdf PACE_Controller_Manual.pdf
- uses: actions/upload-artifact@v7
with:
name: pace-controller-source
path: |
PACE-Controller-v${{ needs.prepare.outputs.version }}-source.zip
PACE_Controller_Manual.pdf
if-no-files-found: error
publish:
name: Publish GitHub release
needs: [prepare, windows-exe, linux-exe, source-package]
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@v7
with:
pattern: pace-controller-*
path: release-assets
merge-multiple: true
- name: Generate checksums
run: |
cd release-assets
sha256sum PACE-Controller-* PACE_Controller_Manual.pdf > SHA256SUMS.txt
cat SHA256SUMS.txt
- name: Publish validated release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.prepare.outputs.version }}
run: |
cat > /tmp/release-notes.md <<EOF
## PACE Controller v${VERSION}
Cross-platform release for classic Druck PACE 5000/6000:
- Ethernet TCP/SCPI and RS-232 on Windows and Linux;
- standalone unsigned Windows and Linux executables;
- complete source archive, illustrated PDF manual, and SHA-256 checksums;
- frozen legacy PowerShell/WinForms v0.3.1 retained unchanged.
**Safety:** validate the software at low pressure on an unloaded setup. Software interlocks do not replace hardware pressure protections or operator supervision.
**Zenodo DOI:** pending archival or repository integration.
EOF
gh release create "v${VERSION}" release-assets/* \
--repo "$GITHUB_REPOSITORY" \
--target "${{ needs.prepare.outputs.commit_sha }}" \
--title "PACE Controller v${VERSION}" \
--notes-file /tmp/release-notes.md \
--verify-tag
- name: Dispatch Zenodo DOI sync
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.prepare.outputs.version }}
run: |
gh workflow run sync-zenodo-doi.yml \
--repo "$GITHUB_REPOSITORY" \
--ref main \
-f "version=${VERSION}"