Skip to content

Fix ty type-checking warnings #579

Fix ty type-checking warnings

Fix ty type-checking warnings #579

Workflow file for this run

name: CI
on:
push:
branches: [master]
tags: ["v*"]
pull_request:
branches: [master]
jobs:
changes:
name: Detect CI-relevant changes
runs-on: ubuntu-24.04
outputs:
run_ci: ${{ steps.filter.outputs.run_ci }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Decide whether CI is required
id: filter
env:
EVENT_NAME: ${{ github.event_name }}
EVENT_BEFORE: ${{ github.event.before }}
EVENT_SHA: ${{ github.sha }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
REF_TYPE: ${{ github.ref_type }}
run: |
set -euo pipefail
if [ "${REF_TYPE}" = "tag" ]; then
echo "Tag push detected; CI will run."
echo "run_ci=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "${EVENT_NAME}" = "pull_request" ]; then
base_sha="${PR_BASE_SHA}"
head_sha="${PR_HEAD_SHA}"
else
base_sha="${EVENT_BEFORE}"
head_sha="${EVENT_SHA}"
fi
if [ -z "${base_sha}" ] || [ "${base_sha}" = "0000000000000000000000000000000000000000" ]; then
echo "No comparable base commit; CI will run."
echo "run_ci=true" >> "$GITHUB_OUTPUT"
exit 0
fi
mapfile -t changed_files < <(git diff --name-only "${base_sha}" "${head_sha}")
if [ "${#changed_files[@]}" -eq 0 ]; then
echo "No changed files detected; CI will run."
echo "run_ci=true" >> "$GITHUB_OUTPUT"
exit 0
fi
printf 'Changed files:\n'
printf ' - %s\n' "${changed_files[@]}"
for path in "${changed_files[@]}"; do
case "${path}" in
docs/**|website/**|images/**)
;;
*)
echo "CI-relevant change detected: ${path}"
echo "run_ci=true" >> "$GITHUB_OUTPUT"
exit 0
;;
esac
done
echo "Only docs/ website/ and images/ changed; skipping CI."
echo "run_ci=false" >> "$GITHUB_OUTPUT"
quality:
name: Quality (ruff + ty)
needs: [changes]
if: needs.changes.outputs.run_ci == 'true'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install i18n tooling
run: |
sudo apt-get update
sudo apt-get install -y gettext
- name: Cache pip downloads
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-quality-pip-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-quality-pip-
- name: Install project
run: |
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
- name: Verify POT is up to date
run: bash tools/i18n.sh --check-pot-sync
- name: Validate translation catalogs
run: bash tools/i18n.sh --check-catalogs --allow-incomplete
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Check package-data sync
run: python3 tools/check_package_data_sync.py
- name: Check translation packaging
run: python3 tools/check_translation_packaging.py
- name: Lint (ruff check)
run: .venv/bin/python -m ruff check docking/ tests/
- name: Format (ruff format)
run: .venv/bin/python -m ruff format --check docking/ tests/
- name: Type check (ty)
run: .venv/bin/python -m ty check docking/
bdd-visual:
name: BDD + Visual Regression
needs: [changes, quality]
if: needs.changes.outputs.run_ci == 'true'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Cache pip downloads
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-bdd-visual-pip-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-bdd-visual-pip-
- name: Install system dependencies
run: |
sudo apt-get update
icon_theme_pkg="adwaita-icon-theme"
if apt-cache show adwaita-icon-theme-full >/dev/null 2>&1; then
icon_theme_pkg="adwaita-icon-theme-full"
fi
sudo apt-get install -y \
python3-venv \
xvfb xauth \
gir1.2-gtk-3.0 gir1.2-gdk-3.0 \
gir1.2-gdkpixbuf-2.0 gir1.2-wnck-3.0 gir1.2-pango-1.0 \
gir1.2-nm-1.0 gir1.2-gstreamer-1.0 \
libcairo2-dev libgirepository1.0-dev libglib2.0-dev \
meson ninja-build pkg-config \
python3-cairo python3-gi python3-gi-cairo \
librsvg2-common hicolor-icon-theme "$icon_theme_pkg"
- name: Install project
run: |
python3 -m venv --system-site-packages .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
- name: Screenshot regression tests
env:
GSETTINGS_BACKEND: memory
GTK_THEME: Adwaita
run: |
xvfb-run -a .venv/bin/python -m pytest tests/visual -q -m visual -o addopts=
- name: Behave interaction scenarios
env:
GSETTINGS_BACKEND: memory
GTK_THEME: Adwaita
run: |
xvfb-run -a .venv/bin/behave -q
- name: Upload visual artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: visual-output
path: tests/visual/output/
if-no-files-found: ignore
test-ubuntu:
name: Test (Python ${{ matrix.python }}, Ubuntu ${{ matrix.ubuntu }})
needs: [changes, quality]
if: needs.changes.outputs.run_ci == 'true'
runs-on: ubuntu-${{ matrix.ubuntu }}
strategy:
matrix:
include:
- ubuntu: "22.04"
python: "3.10"
- ubuntu: "24.04"
python: "3.12"
- ubuntu: "24.04"
python: "3.14"
steps:
- uses: actions/checkout@v4
- name: Set up Python
if: matrix.python == '3.14'
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Cache pip downloads
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-ubuntu-${{ matrix.ubuntu }}-py${{ matrix.python }}-pip-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-ubuntu-${{ matrix.ubuntu }}-py${{ matrix.python }}-pip-
${{ runner.os }}-ubuntu-${{ matrix.ubuntu }}-pip-
- name: Install system dependencies
run: |
sudo apt-get update
icon_theme_pkg="adwaita-icon-theme"
if apt-cache show adwaita-icon-theme-full >/dev/null 2>&1; then
icon_theme_pkg="adwaita-icon-theme-full"
fi
sudo apt-get install -y \
python3-venv \
xvfb xauth \
gir1.2-gtk-3.0 gir1.2-gdk-3.0 \
gir1.2-gdkpixbuf-2.0 gir1.2-wnck-3.0 gir1.2-pango-1.0 \
gir1.2-nm-1.0 gir1.2-gstreamer-1.0 \
libcairo2-dev libgirepository1.0-dev libglib2.0-dev \
meson ninja-build pkg-config \
python3-cairo python3-gi python3-gi-cairo \
librsvg2-common hicolor-icon-theme "$icon_theme_pkg"
if command -v gdk-pixbuf-query-loaders >/dev/null 2>&1; then
gdk-pixbuf-query-loaders | grep -qi librsvg
fi
- name: Install project with system GI bindings
if: matrix.python != '3.14'
run: |
python3 -m venv --system-site-packages .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
- name: Install project without GI bindings
if: matrix.python == '3.14'
run: |
python -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip setuptools wheel
python -m pip install pycairo
python -m pip install -e ".[dev]"
- name: Full tests
if: matrix.python != '3.14'
env:
GSETTINGS_BACKEND: memory
GTK_THEME: Adwaita
COVERAGE_MIN: "55"
run: |
xvfb-run -a .venv/bin/python -m pytest tests/ -v \
--cov=docking \
--cov-report=term-missing \
--cov-report=xml:coverage.xml \
--cov-report=html:htmlcov \
--cov-fail-under=${COVERAGE_MIN}
- name: Python 3.14 smoke tests
if: matrix.python == '3.14'
run: .venv/bin/python -m pytest -q tests/smoke/test_python314_smoke.py
- name: Upload coverage artifact
if: matrix.python != '3.14'
uses: actions/upload-artifact@v4
with:
name: coverage-ubuntu-${{ matrix.ubuntu }}-py${{ matrix.python }}
path: |
coverage.xml
htmlcov/
- name: Upload coverage to Codecov
if: matrix.ubuntu == '24.04' && matrix.python == '3.12' && env.CODECOV_TOKEN != ''
uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
token: ${{ env.CODECOV_TOKEN }}
files: coverage.xml
flags: ubuntu-24.04-py3.12
fail_ci_if_error: false
test-debian:
name: Test (Python ${{ matrix.python }}, Debian ${{ matrix.debian }})
needs: [changes, quality]
if: needs.changes.outputs.run_ci == 'true'
runs-on: ubuntu-22.04
strategy:
matrix:
include:
- debian: "11"
python: "3.10"
image: python:3.10-bullseye
- debian: "12"
python: "3.12"
image: python:3.12-bookworm
- debian: "12"
python: "3.14"
image: python:3.14-bookworm
container:
image: ${{ matrix.image }}
steps:
- uses: actions/checkout@v4
- name: Cache pip downloads
uses: actions/cache@v4
with:
path: /root/.cache/pip
key: ${{ runner.os }}-debian-${{ matrix.debian }}-py${{ matrix.python }}-pip-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-debian-${{ matrix.debian }}-py${{ matrix.python }}-pip-
${{ runner.os }}-debian-${{ matrix.debian }}-pip-
- name: Install system dependencies
run: |
apt-get update
apt-get install -y \
build-essential \
ca-certificates curl git \
xvfb xauth pkg-config gobject-introspection libcairo2-dev libgirepository1.0-dev \
gir1.2-gtk-3.0 gir1.2-gdk-3.0 gir1.2-gdkpixbuf-2.0 gir1.2-wnck-3.0 \
gir1.2-pango-1.0 gir1.2-nm-1.0 gir1.2-gstreamer-1.0
- name: Install project with GI bindings
if: matrix.python != '3.14'
run: |
python -m pip install --upgrade pip
python -m pip install pycairo "PyGObject==3.42.2"
python -m pip install -e ".[dev]"
- name: Install project without GI bindings
if: matrix.python == '3.14'
run: |
python -m pip install --upgrade pip
python -m pip install pycairo
python -m pip install -e ".[dev]"
- name: Full tests
if: matrix.python != '3.14'
env:
GSETTINGS_BACKEND: memory
GTK_THEME: Adwaita
run: xvfb-run -a python -m pytest tests/ -v
- name: Python 3.14 smoke tests
if: matrix.python == '3.14'
run: python -m pytest -q tests/smoke/test_python314_smoke.py
test-arm64:
name: Test (Python 3.12, Ubuntu 24.04 ARM64)
needs: [changes, quality]
if: needs.changes.outputs.run_ci == 'true'
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Cache pip downloads
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-ubuntu-24.04-arm-py3.12-pip-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-ubuntu-24.04-arm-py3.12-pip-
${{ runner.os }}-ubuntu-24.04-arm-pip-
- name: Install system dependencies
run: |
sudo apt-get update
icon_theme_pkg="adwaita-icon-theme"
if apt-cache show adwaita-icon-theme-full >/dev/null 2>&1; then
icon_theme_pkg="adwaita-icon-theme-full"
fi
sudo apt-get install -y \
python3-venv \
xvfb xauth \
gir1.2-gtk-3.0 gir1.2-gdk-3.0 \
gir1.2-gdkpixbuf-2.0 gir1.2-wnck-3.0 gir1.2-pango-1.0 \
gir1.2-nm-1.0 gir1.2-gstreamer-1.0 \
libcairo2-dev libgirepository1.0-dev libglib2.0-dev \
meson ninja-build pkg-config \
python3-cairo python3-gi python3-gi-cairo \
librsvg2-common hicolor-icon-theme "$icon_theme_pkg"
if command -v gdk-pixbuf-query-loaders >/dev/null 2>&1; then
gdk-pixbuf-query-loaders | grep -qi librsvg
fi
- name: Install project with system GI bindings
run: |
python3 -m venv --system-site-packages .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
- name: Full tests
env:
GSETTINGS_BACKEND: memory
GTK_THEME: Adwaita
run: |
xvfb-run -a .venv/bin/python -m pytest tests/ -v
test-fedora-smoke:
name: Smoke (Fedora 41)
needs: [changes, quality]
if: needs.changes.outputs.run_ci == 'true'
runs-on: ubuntu-24.04
container:
image: fedora:41
steps:
- uses: actions/checkout@v4
- name: Install system dependencies
run: |
dnf install -y \
git \
python3 python3-pip \
cairo-devel cairo-gobject-devel gobject-introspection-devel \
gtk3 gdk-pixbuf2 libwnck3 pango NetworkManager-libnm gstreamer1 \
python3-cairo python3-gobject \
xorg-x11-server-Xvfb xorg-x11-xauth \
adwaita-icon-theme hicolor-icon-theme librsvg2
- name: Install project
run: |
python3 -m venv --system-site-packages .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
- name: Smoke tests
env:
GSETTINGS_BACKEND: memory
GTK_THEME: Adwaita
run: |
xvfb-run -a .venv/bin/python -m pytest -q \
tests/core/test_config.py \
tests/ui/test_about.py \
tests/ui/test_menu_integration.py \
tests/applets/test_registry.py \
tests/applets/test_trivia.py
test-opensuse-smoke:
name: Smoke (openSUSE Tumbleweed)
needs: [changes, quality]
if: needs.changes.outputs.run_ci == 'true'
runs-on: ubuntu-24.04
container:
image: opensuse/tumbleweed:latest
steps:
- uses: actions/checkout@v4
- name: Install system dependencies
run: |
zypper --non-interactive refresh
choose_pkg() {
for pkg in "$@"; do
if zypper --non-interactive info "$pkg" >/dev/null 2>&1; then
printf '%s\n' "$pkg"
return 0
fi
done
return 1
}
PYTHON_PKG="$(choose_pkg python313 python312 python311 python3)"
PYTHON_DEV_PKG="$(choose_pkg python313-devel python312-devel python311-devel python3-devel)"
PYTHON_PIP_PKG="$(choose_pkg python313-pip python312-pip python311-pip python3-pip)"
PANGOCAIRO_TYPELIB_PKG="$(choose_pkg typelib-1_0-PangoCairo-1_0 || true)"
zypper --non-interactive install --no-recommends \
git \
gcc \
meson \
ninja \
"$PYTHON_PKG" \
"$PYTHON_DEV_PKG" \
"$PYTHON_PIP_PKG" \
cairo-devel glib2-devel gobject-introspection-devel pkgconf \
typelib-1_0-GdkPixbuf-2_0 \
typelib-1_0-Gtk-3_0 \
typelib-1_0-Pango-1_0 \
typelib-1_0-Wnck-3_0 \
gdk-pixbuf-loader-rsvg librsvg \
xauth xorg-x11-server-Xvfb \
adwaita-icon-theme hicolor-icon-theme \
${PANGOCAIRO_TYPELIB_PKG:+$PANGOCAIRO_TYPELIB_PKG}
- name: Install project
run: |
PYTHON="$(command -v python3 || command -v python3.13 || command -v python3.12 || command -v python3.11)"
"$PYTHON" -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip setuptools wheel
python -m pip install pycairo PyGObject
python -m pip install -e ".[dev]"
- name: Smoke tests
env:
GSETTINGS_BACKEND: memory
GTK_THEME: Adwaita
run: |
Xvfb :99 -screen 0 1280x800x24 &
XVFB_PID=$!
trap 'kill $XVFB_PID' EXIT
export DISPLAY=:99
.venv/bin/python -m pytest -q \
tests/core/test_config.py \
tests/ui/test_about.py \
tests/ui/test_menu_integration.py \
tests/applets/test_trivia.py
build-deb:
name: Build .deb
runs-on: ubuntu-22.04
needs: [changes, test-ubuntu, test-debian, test-fedora-smoke, test-opensuse-smoke]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential debhelper dh-python python3-setuptools python3-wheel python3-pip \
python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-wnck-3.0 \
pybuild-plugin-pyproject python3-all gettext
- name: Build .deb
run: |
bash packaging/deb/build.sh
mkdir -p artifacts
cp ../docking_*.deb artifacts/
ls -lh artifacts/docking_*.deb
- name: Validate .deb install
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y ./artifacts/docking_*.deb
echo "[check] dpkg status"
dpkg -s docking | grep -F "Status: install ok installed"
echo "[check] executable exists: /usr/bin/docking"
if [ -x /usr/bin/docking ]; then
ls -l /usr/bin/docking
else
echo "missing or non-executable: /usr/bin/docking"
dpkg -L docking | sed -n '1,200p'
exit 1
fi
echo "[check] desktop file exists"
if [ -f /usr/share/applications/org.docking.Docking.desktop ]; then
ls -l /usr/share/applications/org.docking.Docking.desktop
else
echo "missing: /usr/share/applications/org.docking.Docking.desktop"
dpkg -L docking | sed -n '1,200p'
exit 1
fi
echo "[check] private application directory exists"
if [ -d /usr/lib/docking/python ]; then
ls -ld /usr/lib/docking/python
else
echo "missing: /usr/lib/docking/python"
dpkg -L docking | sed -n '1,200p'
exit 1
fi
echo "[check] vendored dependencies directory exists"
if [ -d /usr/lib/docking/vendor ]; then
ls -ld /usr/lib/docking/vendor
else
echo "missing: /usr/lib/docking/vendor"
dpkg -L docking | sed -n '1,200p'
exit 1
fi
echo "[check] all install validations passed"
- name: Upload .deb artifact
uses: actions/upload-artifact@v4
with:
name: docking-deb
path: artifacts/docking_*.deb
build-deb-arm64:
name: Build .deb (ARM64)
runs-on: ubuntu-24.04-arm
needs: [changes, test-arm64]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential debhelper dh-python python3-setuptools python3-wheel python3-pip \
python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-wnck-3.0 \
pybuild-plugin-pyproject python3-all gettext
- name: Build .deb
run: |
bash packaging/deb/build.sh
mkdir -p artifacts
cp ../docking_*.deb artifacts/
ls -lh artifacts/docking_*.deb
- name: Validate .deb install on ARM64
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y ./artifacts/docking_*.deb
echo "[check] dpkg status"
dpkg -s docking | grep -F "Status: install ok installed"
echo "[check] architecture"
dpkg --print-architecture | grep -Fx "arm64"
echo "[check] executable exists: /usr/bin/docking"
if [ -x /usr/bin/docking ]; then
ls -l /usr/bin/docking
else
echo "missing or non-executable: /usr/bin/docking"
dpkg -L docking | sed -n '1,200p'
exit 1
fi
echo "[check] desktop file exists"
if [ -f /usr/share/applications/org.docking.Docking.desktop ]; then
ls -l /usr/share/applications/org.docking.Docking.desktop
else
echo "missing: /usr/share/applications/org.docking.Docking.desktop"
dpkg -L docking | sed -n '1,200p'
exit 1
fi
echo "[check] private application directory exists"
if [ -d /usr/lib/docking/python ]; then
ls -ld /usr/lib/docking/python
else
echo "missing: /usr/lib/docking/python"
dpkg -L docking | sed -n '1,200p'
exit 1
fi
echo "[check] vendored dependencies directory exists"
if [ -d /usr/lib/docking/vendor ]; then
ls -ld /usr/lib/docking/vendor
else
echo "missing: /usr/lib/docking/vendor"
dpkg -L docking | sed -n '1,200p'
exit 1
fi
echo "[check] all ARM64 install validations passed"
- name: Upload ARM64 .deb artifact
uses: actions/upload-artifact@v4
with:
name: docking-deb-arm64
path: artifacts/docking_*.deb
build-rpm-arm64:
name: Build RPM (ARM64)
runs-on: ubuntu-24.04-arm
needs: [changes, test-arm64]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install RPM tooling
run: |
sudo apt-get update
sudo apt-get install -y rpm python3-pip gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build RPM package
run: |
mkdir -p artifacts
./packaging/rpm/build.sh
- name: Upload ARM64 RPM artifact
uses: actions/upload-artifact@v4
with:
name: docking-rpm-arm64
path: artifacts/*.rpm
build-rpm:
name: Build RPM
runs-on: ubuntu-22.04
needs: [changes, test-ubuntu, test-debian, test-fedora-smoke, test-opensuse-smoke]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install RPM tooling
run: |
sudo apt-get update
sudo apt-get install -y rpm python3-pip gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build RPM package
run: |
mkdir -p artifacts
./packaging/rpm/build.sh
- name: Upload RPM artifact
uses: actions/upload-artifact@v4
with:
name: docking-rpm
path: artifacts/*.rpm
build-flatpak:
name: Build Flatpak
runs-on: ubuntu-22.04
needs: [changes, test-ubuntu, test-debian, test-fedora-smoke, test-opensuse-smoke]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install Flatpak tooling
run: |
sudo apt-get update
sudo apt-get install -y flatpak flatpak-builder gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build Flatpak bundle
run: |
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install --user -y flathub org.gnome.Platform//46 org.gnome.Sdk//46
flatpak-builder --force-clean --repo=flatpak-repo flatpak-build packaging/flatpak/org.docking.Docking.json
mkdir -p artifacts
flatpak build-bundle flatpak-repo artifacts/org.docking.Docking.flatpak org.docking.Docking
ls -lh artifacts/org.docking.Docking.flatpak
- name: Upload Flatpak artifact
uses: actions/upload-artifact@v4
with:
name: docking-flatpak
path: artifacts/org.docking.Docking.flatpak
build-flatpak-arm64:
name: Build Flatpak (ARM64)
runs-on: ubuntu-24.04-arm
needs: [changes, test-arm64]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install Flatpak tooling
run: |
sudo apt-get update
sudo apt-get install -y flatpak flatpak-builder gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build Flatpak bundle
run: |
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install --user -y flathub org.gnome.Platform//46 org.gnome.Sdk//46
flatpak-builder --force-clean --repo=flatpak-repo flatpak-build packaging/flatpak/org.docking.Docking.json
mkdir -p artifacts
flatpak build-bundle flatpak-repo artifacts/org.docking.Docking.flatpak org.docking.Docking
ls -lh artifacts/org.docking.Docking.flatpak
- name: Upload ARM64 Flatpak artifact
uses: actions/upload-artifact@v4
with:
name: docking-flatpak-arm64
path: artifacts/org.docking.Docking.flatpak
build-snap:
name: Build Snap
runs-on: ubuntu-22.04
needs: [changes, test-ubuntu, test-debian, test-fedora-smoke, test-opensuse-smoke]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install Snapcraft
run: |
sudo apt-get update
sudo apt-get install -y snapcraft gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build Snap package
run: |
mkdir -p artifacts
(
cd packaging/snap
sudo snapcraft pack --destructive-mode --output "$GITHUB_WORKSPACE/artifacts/docking.snap"
)
sudo chown "$USER:$USER" artifacts/docking.snap
ls -lh artifacts/*.snap
- name: Upload Snap artifact
uses: actions/upload-artifact@v4
with:
name: docking-snap
path: artifacts/*.snap
build-snap-arm64:
name: Build Snap (ARM64)
runs-on: ubuntu-24.04-arm
needs: [changes, test-arm64]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install Snapcraft
run: |
sudo apt-get update
sudo apt-get install -y snapd gettext
sudo systemctl start snapd.socket snapd.service
sudo snap install snapcraft --classic
echo "/snap/bin" >> "$GITHUB_PATH"
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build Snap package
run: |
mkdir -p artifacts
(
cd packaging/snap
sudo snapcraft pack --destructive-mode --output "$GITHUB_WORKSPACE/artifacts/docking.snap"
)
sudo chown "$USER:$USER" artifacts/docking.snap
ls -lh artifacts/*.snap
- name: Upload ARM64 Snap artifact
uses: actions/upload-artifact@v4
with:
name: docking-snap-arm64
path: artifacts/*.snap
build-appimage:
name: Build AppImage
runs-on: ubuntu-22.04
needs: [changes, test-ubuntu, test-debian, test-fedora-smoke, test-opensuse-smoke]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install AppImage tooling
run: |
sudo apt-get update
sudo apt-get install -y python3-pip libfuse2 gettext
python3 -m pip install --upgrade pip
python3 -m pip install appimage-builder
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build AppImage
run: |
mkdir -p artifacts
./packaging/appimage/build.sh
- name: Upload AppImage artifact
uses: actions/upload-artifact@v4
with:
name: docking-appimage
path: artifacts/*.AppImage
build-appimage-arm64:
name: Build AppImage (ARM64)
runs-on: ubuntu-24.04-arm
needs: [changes, test-arm64]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install AppImage tooling
run: |
sudo apt-get update
sudo apt-get install -y python3-pip libfuse2 gettext
python3 -m pip install --upgrade pip
python3 -m pip install appimage-builder
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build AppImage
run: |
mkdir -p artifacts
./packaging/appimage/build.sh
- name: Upload ARM64 AppImage artifact
uses: actions/upload-artifact@v4
with:
name: docking-appimage-arm64
path: artifacts/*.AppImage
build-arch:
name: Build Arch package
runs-on: ubuntu-22.04
needs: [changes, test-ubuntu, test-debian, test-fedora-smoke, test-opensuse-smoke]
if: needs.changes.outputs.run_ci == 'true'
container:
image: archlinux:latest
steps:
- uses: actions/checkout@v4
- name: Install Arch build tooling
run: |
pacman -Sy --noconfirm archlinux-keyring
pacman -Syu --noconfirm
pacman -S --noconfirm --needed base-devel git python python-pip gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build Arch package
run: |
useradd -m builder
chown -R builder:builder "$GITHUB_WORKSPACE"
su - builder -c "cd '$GITHUB_WORKSPACE' && ./packaging/arch/build.sh"
ls -lh artifacts/*.pkg.tar.*
- name: Upload Arch artifact
uses: actions/upload-artifact@v4
with:
name: docking-arch
path: artifacts/*.pkg.tar.*
build-arch-arm64:
name: Build Arch package (ARM64)
runs-on: ubuntu-24.04-arm
needs: [changes, test-arm64]
if: needs.changes.outputs.run_ci == 'true'
container:
image: adyranov/archlinux:latest
steps:
- uses: actions/checkout@v4
- name: Install Arch build tooling
run: |
pacman -Sy --noconfirm archlinux-keyring
pacman -Syu --noconfirm
pacman -S --noconfirm --needed base-devel git python python-pip gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build Arch package
run: |
useradd -m builder
chown -R builder:builder "$GITHUB_WORKSPACE"
su - builder -c "cd '$GITHUB_WORKSPACE' && ./packaging/arch/build.sh"
ls -lh artifacts/*.pkg.tar.*
- name: Upload ARM64 Arch artifact
uses: actions/upload-artifact@v4
with:
name: docking-arch-arm64
path: artifacts/*.pkg.tar.*
build-nix:
name: Build Nix package
runs-on: ubuntu-22.04
needs: [changes, test-ubuntu, test-debian, test-fedora-smoke, test-opensuse-smoke]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-24.11
- name: Install i18n tooling
run: |
sudo apt-get update
sudo apt-get install -y gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build Nix package
run: |
mkdir -p artifacts
./packaging/nix/build.sh
- name: Upload Nix artifact
uses: actions/upload-artifact@v4
with:
name: docking-nix
path: |
artifacts/docking-nix-output.tar.gz
artifacts/docking-nix-store-path.txt
build-nix-arm64:
name: Build Nix package (ARM64)
runs-on: ubuntu-24.04-arm
needs: [changes, test-arm64]
if: needs.changes.outputs.run_ci == 'true'
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-24.11
- name: Install i18n tooling
run: |
sudo apt-get update
sudo apt-get install -y gettext
- name: Compile translations
run: bash tools/i18n.sh --compile
- name: Build Nix package
run: |
mkdir -p artifacts
./packaging/nix/build.sh
- name: Upload ARM64 Nix artifact
uses: actions/upload-artifact@v4
with:
name: docking-nix-arm64
path: |
artifacts/docking-nix-output.tar.gz
artifacts/docking-nix-store-path.txt
release:
name: Release
runs-on: ubuntu-22.04
needs: [changes, build-deb, build-deb-arm64, build-rpm, build-rpm-arm64, build-flatpak, build-flatpak-arm64, build-snap, build-snap-arm64, build-appimage, build-appimage-arm64, build-arch, build-arch-arm64, build-nix, build-nix-arm64]
if: github.ref == 'refs/heads/master' && needs.changes.outputs.run_ci == 'true'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Get version from pyproject.toml
id: version
run: |
VERSION=$(
awk -F ' *= *' '
$0 == "[project]" { in_project = 1; next }
/^\[/ { in_project = 0 }
in_project && $1 == "version" {
gsub(/"/, "", $2)
print $2
exit
}
' pyproject.toml
)
if [ -z "${VERSION}" ]; then
echo "Failed to read [project].version from pyproject.toml"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
- name: Decide if release is needed
id: release_check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
current_version="${{ steps.version.outputs.version }}"
api_url="https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest"
status_code=$(
curl -sS -o /tmp/latest-release.json -w "%{http_code}" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"${api_url}"
)
if [ "${status_code}" = "404" ]; then
echo "latest_tag=" >> "$GITHUB_OUTPUT"
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "reason=no_existing_release" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "${status_code}" != "200" ]; then
echo "GitHub API error (${status_code}) while reading latest release"
cat /tmp/latest-release.json
exit 1
fi
latest_tag=$(
python3 -c 'import json, pathlib; print(json.loads(pathlib.Path("/tmp/latest-release.json").read_text()).get("tag_name", ""))'
)
latest_version="${latest_tag#v}"
echo "latest_tag=${latest_tag}" >> "$GITHUB_OUTPUT"
if [ -z "${latest_version}" ]; then
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "reason=latest_release_without_tag" >> "$GITHUB_OUTPUT"
elif [ "${current_version}" = "${latest_version}" ]; then
echo "should_release=false" >> "$GITHUB_OUTPUT"
echo "reason=already_released" >> "$GITHUB_OUTPUT"
elif [ "$(printf '%s\n%s\n' "${latest_version}" "${current_version}" | sort -V | tail -n1)" = "${current_version}" ]; then
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "reason=version_is_newer" >> "$GITHUB_OUTPUT"
else
echo "should_release=false" >> "$GITHUB_OUTPUT"
echo "reason=version_not_newer" >> "$GITHUB_OUTPUT"
fi
- name: Create tag
if: steps.release_check.outputs.should_release == 'true'
run: |
set -euo pipefail
tag="${{ steps.version.outputs.tag }}"
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then
echo "Tag ${tag} already exists on origin, skipping tag creation"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag "${tag}"
git push origin "${tag}"
- name: Download .deb artifact
uses: actions/download-artifact@v4
with:
name: docking-deb
path: artifacts/deb-amd64
- name: Download ARM64 .deb artifact
uses: actions/download-artifact@v4
with:
name: docking-deb-arm64
path: artifacts/deb-arm64
- name: Download RPM artifact
uses: actions/download-artifact@v4
with:
name: docking-rpm
path: artifacts/rpm-amd64
- name: Download ARM64 RPM artifact
uses: actions/download-artifact@v4
with:
name: docking-rpm-arm64
path: artifacts/rpm-arm64
- name: Download Flatpak artifact
uses: actions/download-artifact@v4
with:
name: docking-flatpak
path: artifacts/flatpak-amd64
- name: Download ARM64 Flatpak artifact
uses: actions/download-artifact@v4
with:
name: docking-flatpak-arm64
path: artifacts/flatpak-arm64
- name: Download Snap artifact
uses: actions/download-artifact@v4
with:
name: docking-snap
path: artifacts/snap-amd64
- name: Download ARM64 Snap artifact
uses: actions/download-artifact@v4
with:
name: docking-snap-arm64
path: artifacts/snap-arm64
- name: Download AppImage artifact
uses: actions/download-artifact@v4
with:
name: docking-appimage
path: artifacts/appimage-amd64
- name: Download ARM64 AppImage artifact
uses: actions/download-artifact@v4
with:
name: docking-appimage-arm64
path: artifacts/appimage-arm64
- name: Download Arch artifact
uses: actions/download-artifact@v4
with:
name: docking-arch
path: artifacts/arch-amd64
- name: Download ARM64 Arch artifact
uses: actions/download-artifact@v4
with:
name: docking-arch-arm64
path: artifacts/arch-arm64
- name: Download Nix artifact
uses: actions/download-artifact@v4
with:
name: docking-nix
path: artifacts/nix-amd64
- name: Download ARM64 Nix artifact
uses: actions/download-artifact@v4
with:
name: docking-nix-arm64
path: artifacts/nix-arm64
- name: Normalize release asset names
if: steps.release_check.outputs.should_release == 'true'
run: |
set -euo pipefail
shopt -s nullglob
version="${{ steps.version.outputs.version }}"
host_arch="$(uname -m)"
source_dir="artifacts"
release_dir="${source_dir}/release"
mkdir -p "${release_dir}"
echo "Downloaded artifacts:"
find "${source_dir}" -maxdepth 2 -type f | sed 's#^# - #'
normalize_arch() {
case "$1" in
amd64)
echo "x86_64"
;;
arm64)
echo "aarch64"
;;
noarch)
echo "all"
;;
*)
echo "$1"
;;
esac
}
pick_candidate() {
local label="$1"
shift
if [ "$#" -eq 0 ]; then
echo "Missing ${label} artifact" >&2
exit 1
fi
if [ "$#" -gt 1 ]; then
echo "Multiple ${label} artifacts found ($#), selecting first:" >&2
printf ' - %s\n' "$@"
fi
printf '%s\n' "$1"
}
copy_release_asset() {
local src="$1"
local versioned_name="$2"
local latest_name="$3"
cp -f "${src}" "${release_dir}/${versioned_name}"
cp -f "${src}" "${release_dir}/${latest_name}"
}
deb_candidates=("${source_dir}"/deb-amd64/docking_*.deb)
deb_file="$(pick_candidate "deb" "${deb_candidates[@]}")"
deb_arch="$(echo "${deb_file##*/}" | sed -E 's/.*_([[:alnum:]_]+)\.deb$/\1/')"
deb_arch="$(normalize_arch "${deb_arch}")"
copy_release_asset \
"${deb_file}" \
"docking-${version}-linux-${deb_arch}.deb" \
"docking-latest-linux-${deb_arch}.deb"
for rpm_dir in "${source_dir}"/rpm-*; do
[ -d "${rpm_dir}" ] || continue
rpm_candidates=("${rpm_dir}"/*.rpm)
rpm_primary=()
for f in "${rpm_candidates[@]}"; do
if [[ "${f}" != *.src.rpm ]]; then
rpm_primary+=("${f}")
fi
done
rpm_file="$(pick_candidate "rpm" "${rpm_primary[@]}")"
rpm_arch="$(echo "${rpm_file##*/}" | sed -E 's/.*\.([^.]+)\.rpm$/\1/')"
rpm_arch="$(normalize_arch "${rpm_arch}")"
copy_release_asset \
"${rpm_file}" \
"docking-${version}-linux-${rpm_arch}.rpm" \
"docking-latest-linux-${rpm_arch}.rpm"
done
for flatpak_dir in "${source_dir}"/flatpak-*; do
[ -d "${flatpak_dir}" ] || continue
flatpak_candidates=("${flatpak_dir}"/*.flatpak)
flatpak_file="$(pick_candidate "flatpak" "${flatpak_candidates[@]}")"
flatpak_arch="${flatpak_dir##*-}"
flatpak_arch="$(normalize_arch "${flatpak_arch}")"
copy_release_asset \
"${flatpak_file}" \
"docking-${version}-linux-${flatpak_arch}.flatpak" \
"docking-latest-linux-${flatpak_arch}.flatpak"
done
for snap_dir in "${source_dir}"/snap-*; do
[ -d "${snap_dir}" ] || continue
snap_candidates=("${snap_dir}"/*.snap)
snap_file="$(pick_candidate "snap" "${snap_candidates[@]}")"
snap_arch="${snap_dir##*-}"
snap_arch="$(normalize_arch "${snap_arch}")"
copy_release_asset \
"${snap_file}" \
"docking-${version}-linux-${snap_arch}.snap" \
"docking-latest-linux-${snap_arch}.snap"
done
for appimage_dir in "${source_dir}"/appimage-*; do
[ -d "${appimage_dir}" ] || continue
appimage_candidates=("${appimage_dir}"/*.AppImage)
appimage_file="$(pick_candidate "appimage" "${appimage_candidates[@]}")"
appimage_arch="$(echo "${appimage_file##*/}" | sed -nE 's/.*-([[:alnum:]_]+)\.AppImage$/\1/p')"
if [ -z "${appimage_arch}" ]; then
appimage_arch="${appimage_dir##*-}"
fi
appimage_arch="$(normalize_arch "${appimage_arch}")"
copy_release_asset \
"${appimage_file}" \
"docking-${version}-linux-${appimage_arch}.AppImage" \
"docking-latest-linux-${appimage_arch}.AppImage"
done
for arch_dir in "${source_dir}"/arch-*; do
[ -d "${arch_dir}" ] || continue
arch_candidates=("${arch_dir}"/*.pkg.tar.*)
arch_primary=()
for f in "${arch_candidates[@]}"; do
if [[ "${f}" != *.sig && "${f##*/}" != docking-debug-* ]]; then
arch_primary+=("${f}")
fi
done
arch_file="$(pick_candidate "arch" "${arch_primary[@]}")"
arch_arch="$(echo "${arch_file##*/}" | sed -E 's/.*-([^-]+)\.pkg\.tar\..+$/\1/')"
arch_arch="$(normalize_arch "${arch_arch}")"
arch_ext="${arch_file##*.pkg.tar.}"
copy_release_asset \
"${arch_file}" \
"docking-${version}-linux-${arch_arch}.pkg.tar.${arch_ext}" \
"docking-latest-linux-${arch_arch}.pkg.tar.${arch_ext}"
done
for nix_dir in "${source_dir}"/nix-*; do
[ -d "${nix_dir}" ] || continue
nix_arch="${nix_dir##*-}"
nix_arch="$(normalize_arch "${nix_arch}")"
nix_output_candidates=("${nix_dir}"/docking-nix-output.tar.gz)
nix_output_file="$(pick_candidate "nix output tarball" "${nix_output_candidates[@]}")"
copy_release_asset \
"${nix_output_file}" \
"docking-${version}-linux-${nix_arch}-nix-output.tar.gz" \
"docking-latest-linux-${nix_arch}-nix-output.tar.gz"
nix_path_candidates=("${nix_dir}"/docking-nix-store-path.txt)
nix_path_file="$(pick_candidate "nix store path" "${nix_path_candidates[@]}")"
copy_release_asset \
"${nix_path_file}" \
"docking-${version}-linux-${nix_arch}-nix-store-path.txt" \
"docking-latest-linux-${nix_arch}-nix-store-path.txt"
done
echo "Standardized release assets:"
ls -lh "${release_dir}"
- name: Create GitHub Release
if: steps.release_check.outputs.should_release == 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
name: Docking ${{ steps.version.outputs.version }}
files: |
artifacts/release/*
generate_release_notes: true
- name: Skip release summary
if: steps.release_check.outputs.should_release != 'true'
run: |
echo "Release skipped: ${{ steps.release_check.outputs.reason }}"
echo "Latest release tag: ${{ steps.release_check.outputs.latest_tag }}"
echo "Current pyproject version: ${{ steps.version.outputs.version }}"