zupt/.github/workflows/ci.yml
Cristian Cezar Moisés 33eb90454d
Some checks failed
CI / Source-only, license, shell and secret policy (push) Has been cancelled
CI / Build and full tests (clang) (push) Has been cancelled
CI / Build and full tests (gcc) (push) Has been cancelled
CI / Strict warnings (clang) (push) Has been cancelled
CI / Strict warnings (gcc) (push) Has been cancelled
CI / ASan, LSan and UBSan (push) Has been cancelled
CI / GCC static analyzer (push) Has been cancelled
CI / Reproducible audited source archive (push) Has been cancelled
CI / Debian/Ubuntu source-built package (push) Has been cancelled
CI / openSUSE Tumbleweed x86_64 RPM gate (push) Has been cancelled
CI / Fedora noarch GUI RPM and SRPM gate (push) Has been cancelled
CI / Linux x86_64 notice-bearing CLI tar.xz gate (push) Has been cancelled
CI / Source-only GUI portable ZIP gate (push) Has been cancelled
CI / Windows and macOS release gates (push) Has been cancelled
CI / Windows x86_64 package and smoke test (push) Has been cancelled
CI / macOS native DMG and installed-image test (push) Has been cancelled
packaging: validate genuine source RPM metadata
2026-08-31 22:12:31 -03:00

640 lines
27 KiB
YAML

# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
name: CI
on:
push:
branches:
- master
- 'codex/**'
tags:
- 'v*'
pull_request:
branches:
- master
workflow_dispatch:
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' && !startsWith(github.ref, 'refs/tags/') }}
permissions:
contents: read
jobs:
source-policy:
name: Source-only, license, shell and secret policy
runs-on: ubuntu-24.04
steps:
- name: Check out all refs without LFS or submodules
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
lfs: false
submodules: false
- name: Install audit tools
run: |
sudo apt-get update
sudo apt-get install -y \
dpkg-dev file git-lfs libarchive-tools libxml2-utils make python3 ruby \
shellcheck unzip
- name: Audit tracked files, worktree and HEAD archive
run: bash scripts/check-source-only.sh
- name: Exercise positive and negative scanner fixtures
run: bash tests/test_source_only.sh
- name: Audit license headers
run: make WITH_SDK=0 WITH_PQBOX=0 audit-licenses
- name: Validate release packaging metadata
run: bash tests/test_packaging_syntax.sh
- name: ShellCheck release and source-policy scripts
run: |
shellcheck \
packaging/build-deb.sh \
packaging/build-rpm.sh \
packaging/build-appimage.sh \
packaging/build-dmg.sh \
packaging/build-gui-appimage.sh \
packaging/build-gui-deb.sh \
packaging/build-gui-rpm.sh \
packaging/opensuse/source-audit.sh \
scripts/check-source-only.sh \
scripts/export-opensuse-package.sh \
scripts/test-installed-zupt.sh \
tests/test_atomic_archive_output.sh \
tests/test_authenticated_dedup_reorder.sh \
tests/test_benchmark_temp_safety.sh \
tests/test_block_type_confusion.sh \
tests/test_disk_device_capacity.sh \
tests/test_f09_preface.sh \
tests/test_key_files.sh \
tests/test_legacy_disk_5_2_1.sh \
tests/test_path_traversal.sh \
tests/test_pqbox.sh \
tests/test_sdk.sh \
tests/test_source_only.sh
- name: Credential material audit (paths only)
shell: bash
run: |
set -Eeuo pipefail
findings=$(git grep -Il -E -- \
"-----BEGIN (RSA |OPENSSH |EC |DSA )?PRIVATE KEY-----|https?://[^/@[:space:]]+:[A-Za-z0-9_+=.-]{20,}@|gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{20,}|AKIA[A-Z0-9]{16}|xox[baprs]-[A-Za-z0-9-]{20,}|(FORGEJO_TOKEN|SECURITYOPS_TOKEN|GITHUB_TOKEN|CODEBERG_TOKEN)[[:space:]]*[:=][[:space:]]*['\\\"]?[A-Za-z0-9_+=./-]{20,}" \
-- . || true)
if [[ -n $findings ]]; then
printf '%s\n' "$findings" >&2
echo 'credential-like material found in tracked files' >&2
exit 1
fi
echo 'No private-key block, named token assignment, or credential-bearing URL found.'
build-and-test:
name: Build and full tests (${{ matrix.cc }})
needs: source-policy
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
cc: [gcc, clang]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
lfs: false
submodules: false
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y build-essential clang file libarchive-tools python3 unzip
- name: Clean source-only build
run: |
make clean
make -j"$(nproc)" CC=${{ matrix.cc }} V=1 WITH_SDK=0 WITH_PQBOX=0
- name: Distribution checks
run: make CC=${{ matrix.cc }} V=1 WITH_SDK=0 WITH_PQBOX=0 check
- name: Extended upstream tests
run: make CC=${{ matrix.cc }} V=1 WITH_SDK=0 WITH_PQBOX=0 test-all
- name: In-tree SDK atomic key-save regression
run: make CC=${{ matrix.cc }} V=1 sdk-test
- name: Functional test of the built CLI
run: bash scripts/test-installed-zupt.sh "$PWD/zupt"
strict-warnings:
name: Strict warnings (${{ matrix.cc }})
needs: source-policy
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- cc: gcc
flags: >-
-O2 -g -std=c11 -Wall -Wextra -Wpedantic -Wshadow
-Wcast-align -Wstrict-prototypes -Wmissing-prototypes
-Wnull-dereference -Wformat=2 -Werror
- cc: clang
flags: >-
-O2 -g -std=c11 -Wall -Wextra -Wpedantic -Wshadow
-Wcast-align -Wstrict-prototypes -Wmissing-prototypes
-Wnull-dereference -Wformat=2 -Werror
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install compilers
run: |
sudo apt-get update
sudo apt-get install -y build-essential clang
- name: Compile with warnings as errors
run: |
make clean
make -j"$(nproc)" CC=${{ matrix.cc }} V=1 WITH_SDK=0 WITH_PQBOX=0 \
CFLAGS="${{ matrix.flags }}"
sanitizers:
name: ASan, LSan and UBSan
needs: source-policy
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install compiler and test tools
run: |
sudo apt-get update
sudo apt-get install -y build-essential file python3
- name: Instrumented functional tests
env:
ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: make V=1 WITH_SDK=0 WITH_PQBOX=0 test-asan-run
- name: Mutation smoke under sanitizers
env:
ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: make V=1 WITH_SDK=0 WITH_PQBOX=0 fuzz-format-run
static-analysis:
name: GCC static analyzer
needs: source-policy
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install GCC
run: |
sudo apt-get update
sudo apt-get install -y build-essential
- name: Analyze every source translation unit
run: |
make clean
make -j"$(nproc)" CC=gcc V=1 WITH_SDK=0 WITH_PQBOX=0 \
CFLAGS="-O1 -g -std=c11 -Wall -Wextra -Werror -fanalyzer"
source-archive:
name: Reproducible audited source archive
needs: source-policy
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
lfs: false
submodules: false
- name: Install archive audit tools
run: |
sudo apt-get update
sudo apt-get install -y file libarchive-tools python3 unzip
- name: Build the source archive twice
run: |
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
mkdir -p "$RUNNER_TEMP/dist-one" "$RUNNER_TEMP/dist-two" \
"$RUNNER_TEMP/release-source"
make DIST_TARBALL="$RUNNER_TEMP/dist-one/zupt-$version.tar.gz" dist
make DIST_TARBALL="$RUNNER_TEMP/dist-two/zupt-$version.tar.gz" dist
cmp "$RUNNER_TEMP/dist-one/zupt-$version.tar.gz" \
"$RUNNER_TEMP/dist-two/zupt-$version.tar.gz"
cp "$RUNNER_TEMP/dist-one/zupt-$version.tar.gz" \
"$RUNNER_TEMP/release-source/"
(cd "$RUNNER_TEMP/release-source" && sha256sum "zupt-$version.tar.gz" > \
"zupt-$version.tar.gz.sha256")
bash scripts/check-source-only.sh --archive \
"$RUNNER_TEMP/release-source/zupt-$version.tar.gz"
- name: Match downstream recipe checksums to the tagged source archive
if: startsWith(github.ref, 'refs/tags/v')
run: |
set -Eeuo pipefail
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
source_tar="$RUNNER_TEMP/release-source/zupt-$version.tar.gz"
actual_sha=$(sha256sum "$source_tar" | awk '{print $1}')
aur_sha=$(awk -F"'" '/^sha256sums=/ { print $2; exit }' packaging/aur/PKGBUILD)
homebrew_sha=$(awk -F'"' '/^[[:space:]]*sha256 / { print $2; exit }' packaging/homebrew/zupt.rb)
guix_base32=$(sed -n 's/^[[:space:]]*(base32 "\([^"]*\)").*/\1/p' \
packaging/guix/zupt.scm | head -n 1)
actual_base32=$(python3 - "$source_tar" <<'PY'
import hashlib
import pathlib
import sys
alphabet = "0123456789abcdfghijklmnpqrsvwxyz"
digest = hashlib.sha256(pathlib.Path(sys.argv[1]).read_bytes()).digest()
value = int.from_bytes(digest, "little")
length = (len(digest) * 8 + 4) // 5
print("".join(alphabet[(value >> (5 * index)) & 31]
for index in range(length - 1, -1, -1)))
PY
)
[[ $aur_sha == "$actual_sha" && $homebrew_sha == "$actual_sha" ]] || {
echo 'AUR or Homebrew checksum does not match the source archive' >&2
exit 1
}
[[ $guix_base32 == "$actual_base32" ]] || {
echo 'Guix checksum does not match the source archive' >&2
exit 1
}
- name: Upload source and checksum
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-source
path: ${{ runner.temp }}/release-source/*
if-no-files-found: error
retention-days: 7
debian-package:
name: Debian/Ubuntu source-built package
needs: [source-policy, build-and-test]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
- name: Install Debian package tools
run: |
sudo apt-get update
sudo apt-get install -y build-essential binutils dpkg-dev file git libarchive-tools python3 python3-pyqt6 unzip
- name: Build and extract-test the DEB
run: |
mkdir -p "$RUNNER_TEMP/release-deb"
DIST_DIR="$RUNNER_TEMP/release-deb" RUN_CHECKS=1 bash packaging/build-deb.sh
- name: Build and content-test the GUI DEB
run: |
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
test -n "$version"
mkdir -p "$RUNNER_TEMP/release-gui-deb"
DIST_DIR="$RUNNER_TEMP/release-gui-deb" bash packaging/build-gui-deb.sh
gui_deb="$RUNNER_TEMP/release-gui-deb/zupt-gui_${version}_all.deb"
test -s "$gui_deb"
test "$(dpkg-deb -f "$gui_deb" Package)" = zupt-gui
test "$(dpkg-deb -f "$gui_deb" Version)" = "$version"
test "$(dpkg-deb -f "$gui_deb" Architecture)" = all
- name: Install, functionally test and uninstall the DEBs
run: |
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
test -n "$version"
deb=$(find "$RUNNER_TEMP/release-deb" -maxdepth 1 -type f -name '*.deb' -print -quit)
gui_deb="$RUNNER_TEMP/release-gui-deb/zupt-gui_${version}_all.deb"
test -n "$deb" && test -s "$gui_deb"
sudo apt-get install -y "$deb" "$gui_deb"
bash scripts/test-installed-zupt.sh /usr/bin/zupt
QT_QPA_PLATFORM=offscreen zupt-gui --version | grep -Fx "zupt-gui $version"
test ! -e /usr/bin/vaptvupt
sudo apt-get purge -y zupt-gui zupt
test ! -e /usr/bin/zupt-gui
test ! -e /usr/bin/zupt
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-deb
path: ${{ runner.temp }}/release-deb/*.deb
if-no-files-found: error
retention-days: 7
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-gui-deb
path: ${{ runner.temp }}/release-gui-deb/*.deb
if-no-files-found: error
retention-days: 7
tumbleweed-rpm:
name: openSUSE Tumbleweed x86_64 RPM gate
needs: [source-policy, build-and-test]
runs-on: ubuntu-24.04
container: opensuse/tumbleweed:latest
defaults:
run:
shell: bash
steps:
- name: Bootstrap Git before checkout
run: |
zypper --non-interactive --gpg-auto-import-keys refresh
zypper --non-interactive install --no-recommends \
bash ca-certificates git-core
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
lfs: false
submodules: false
- name: Trust the exact checked-out workspace
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Install native openSUSE tooling
run: |
if rpm -q busybox-gawk >/dev/null 2>&1; then
zypper --non-interactive remove busybox-gawk
fi
zypper --non-interactive install --no-recommends \
bash binutils cpio coreutils diffutils file findutils gawk gcc git-core grep gzip \
libxml2-tools make osc obs-service-obs_scm obs-service-recompress \
obs-service-tar python3-base rpm-build rpmlint sed \
shadow spec-cleaner tar unzip util-linux
- name: Confirm the Factory architecture gate
run: test "$(uname -m)" = x86_64
- name: Validate OBS service and spec syntax
run: |
xmllint --noout packaging/opensuse/_service
test -x /usr/lib/obs/service/obs_scm
test -x /usr/lib/obs/service/tar
test -x /usr/lib/obs/service/recompress
rpmspec -P packaging/opensuse/zupt.spec >/dev/null
spec-cleaner --no-copyright packaging/opensuse/zupt.spec \
> "$RUNNER_TEMP/zupt.spec.cleaned"
diff -u packaging/opensuse/zupt.spec \
"$RUNNER_TEMP/zupt.spec.cleaned"
- name: Exercise pinned OBS source service chain on release tags
if: startsWith(github.ref, 'refs/tags/v')
run: |
service_dir=$RUNNER_TEMP/obs-service
mkdir -p "$service_dir"
cp packaging/opensuse/_service "$service_dir/"
# `osc service runall` additionally requires OBS working-copy metadata.
# Use osc's installed service executor to validate this standalone,
# repository-owned _service file with the exact same local services.
python3 - "$service_dir" <<'PY'
import os
import sys
from xml.etree import ElementTree
from osc.obs_scm.serviceinfo import Serviceinfo
service_dir = sys.argv[1]
os.chdir(service_dir)
service_info = Serviceinfo()
service_info.read(ElementTree.parse(f"{service_dir}/_service").getroot())
raise SystemExit(service_info.execute(service_dir, "all", verbose=True))
PY
mapfile -t service_archives < <(find "$service_dir" -maxdepth 1 \
-type f -name 'zupt-*.tar.gz' -print)
test "${#service_archives[@]}" -eq 1
bash scripts/check-source-only.sh --archive "${service_archives[0]}"
- name: Build source and binary RPMs with real checks
run: |
mkdir -p "$RUNNER_TEMP/release-rpm"
DIST_DIR="$RUNNER_TEMP/release-rpm" bash packaging/build-rpm.sh
- name: Run rpmlint without suppressions
shell: bash
run: |
set -Eeuo pipefail
rpmlint "$RUNNER_TEMP"/release-rpm/*.rpm 2>&1 \
| tee "$RUNNER_TEMP/rpmlint.log"
if grep -Eq ': E:' "$RUNNER_TEMP/rpmlint.log"; then
echo 'rpmlint reported one or more errors' >&2
exit 1
fi
- name: Install, functionally test and uninstall the RPM
run: |
rpm_file=$(find "$RUNNER_TEMP/release-rpm" -maxdepth 1 -type f \
-name '*.rpm' ! -name '*.src.rpm' -print -quit)
test -n "$rpm_file"
zypper --non-interactive install --allow-unsigned-rpm "$rpm_file"
test_home=/tmp/zupt-ci-user
useradd --create-home --home-dir "$test_home" --shell /bin/bash zupt-ci
runuser -u zupt-ci -- env HOME="$test_home" TMPDIR="$test_home" \
bash "$GITHUB_WORKSPACE/scripts/test-installed-zupt.sh" \
/usr/bin/zupt
test ! -e /usr/bin/vaptvupt
zypper --non-interactive remove zupt
test ! -e /usr/bin/zupt
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-rpm
path: ${{ runner.temp }}/release-rpm/*.rpm
if-no-files-found: error
retention-days: 7
gui-rpm-package:
name: Fedora noarch GUI RPM and SRPM gate
needs: [source-policy, build-and-test]
runs-on: ubuntu-24.04
container: fedora:latest
defaults:
run:
shell: bash
steps:
- name: Bootstrap checkout dependencies
run: dnf install -y ca-certificates git
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
lfs: false
submodules: false
- name: Trust the exact checked-out workspace
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Install native build, package, audit and GUI runtime tools
run: |
dnf install -y \
binutils cpio file findutils gcc git-core gzip libarchive make \
python3 python3-pyside6 rpm-build rpmdevtools tar unzip
- name: Build and content-test the GUI RPM and source RPM
run: |
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
test -n "$version"
mkdir -p "$RUNNER_TEMP/release-gui-rpm"
DIST_DIR="$RUNNER_TEMP/release-gui-rpm" bash packaging/build-gui-rpm.sh
gui_rpm="$RUNNER_TEMP/release-gui-rpm/zupt-gui-$version-1.noarch.rpm"
gui_srpm="$RUNNER_TEMP/release-gui-rpm/zupt-gui-$version-1.src.rpm"
test -s "$gui_rpm"
test -s "$gui_srpm"
test "$(rpm -qp --qf '%{NAME}' "$gui_rpm")" = zupt-gui
test "$(rpm -qp --qf '%{VERSION}-%{RELEASE}' "$gui_rpm")" = "$version-1"
test "$(rpm -qp --qf '%{ARCH}' "$gui_rpm")" = noarch
rpm -qp --requires "$gui_rpm" | grep -Fx "zupt >= $version"
test "$(rpm -qp --qf '%{NAME}' "$gui_srpm")" = zupt-gui
test "$(rpm -qp --qf '%{VERSION}-%{RELEASE}' "$gui_srpm")" = "$version-1"
test "$(rpm -qp --qf '%{SOURCEPACKAGE}' "$gui_srpm")" = 1
test "$(rpm -qp --qf '%{SOURCERPM}' "$gui_srpm")" = '(none)'
test "$(rpm -qpl "$gui_srpm" | wc -l)" -eq 2
rpm -qpl "$gui_srpm" | grep -Fx "zupt-gui-$version.tar.gz"
rpm -qpl "$gui_srpm" | grep -Fx zupt-gui.spec
- name: Build the matching Fedora CLI RPM
run: |
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
core_top="$RUNNER_TEMP/core-rpmbuild"
mkdir -p "$core_top"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
make DIST_TARBALL="$core_top/SOURCES/zupt-$version.tar.gz" dist
rpmbuild --define "_topdir $core_top" -ba packaging/rpm/zupt.spec
- name: Install and functionally test the GUI with the packaged CLI
run: |
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
test -n "$version"
core_rpm=$(find "$RUNNER_TEMP/core-rpmbuild/RPMS" -type f \
-name "zupt-$version-1.*.rpm" ! -name '*-debuginfo-*' \
! -name '*-debugsource-*' -print -quit)
gui_rpm="$RUNNER_TEMP/release-gui-rpm/zupt-gui-$version-1.noarch.rpm"
test -n "$core_rpm" && test -s "$gui_rpm"
dnf install -y "$core_rpm" "$gui_rpm"
bash scripts/test-installed-zupt.sh /usr/bin/zupt
QT_QPA_PLATFORM=offscreen zupt-gui --version | grep -Fx "zupt-gui $version"
test ! -e /usr/bin/vaptvupt
dnf remove -y zupt-gui zupt
test ! -e /usr/bin/zupt-gui
test ! -e /usr/bin/zupt
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-gui-rpm
path: ${{ runner.temp }}/release-gui-rpm/*.rpm
if-no-files-found: error
retention-days: 7
linux-portable:
name: Linux x86_64 notice-bearing CLI tar.xz gate
needs: [source-policy, build-and-test]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
lfs: false
submodules: false
- name: Install build and archive tools
run: |
sudo apt-get update
sudo apt-get install -y build-essential binutils file python3 xz-utils
- name: Build and audit the native executable
run: |
test "$(uname -m)" = x86_64
make clean
make -j"$(nproc)" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
make V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
bash scripts/test-installed-zupt.sh "$PWD/zupt"
if readelf -d zupt | grep -Eq '(RPATH|RUNPATH)'; then
echo 'Linux portable binary contains RPATH/RUNPATH' >&2
exit 1
fi
mapfile -t needed < <(readelf -d zupt | sed -n 's/.*Shared library: \[\([^]]*\)\].*/\1/p')
((${#needed[@]} > 0))
for library in "${needed[@]}"; do
case $library in
libc.so.6|libm.so.6|libpthread.so.0) ;;
*) echo "unexpected Linux runtime dependency: $library" >&2; exit 1 ;;
esac
done
if ldd zupt | grep -Fq 'not found'; then
echo 'Linux portable binary has an unresolved runtime dependency' >&2
exit 1
fi
- name: Assemble and extracted-package-test the tar.xz
run: |
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
epoch=$(<.source-date-epoch)
root="$RUNNER_TEMP/linux-work/zupt-$version-linux-x86_64"
output="$RUNNER_TEMP/release-linux-x86_64/zupt-$version-linux-x86_64.tar.xz"
mkdir -p "$root" "$(dirname "$output")"
install -m 0755 zupt "$root/zupt"
install -m 0644 README.md CHANGELOG.md SECURITY.md THREAT_MODEL.md \
LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause \
LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE \
THIRD-PARTY-NOTICES.md "$root/"
tar --sort=name --mtime="@$epoch" --owner=0 --group=0 --numeric-owner \
-C "$(dirname "$root")" -cJf "$output" "$(basename "$root")"
extract=$(mktemp -d)
tar -xJf "$output" -C "$extract"
bash scripts/test-installed-zupt.sh \
"$extract/$(basename "$root")/zupt"
test "$(find "$extract/$(basename "$root")" -maxdepth 1 -type f | wc -l)" -eq 13
sha256sum "$output"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-linux-x86_64
path: ${{ runner.temp }}/release-linux-x86_64/*.tar.xz
if-no-files-found: error
retention-days: 7
gui-portable:
name: Source-only GUI portable ZIP gate
needs: [source-policy, build-and-test]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
lfs: false
submodules: false
- name: Install GUI smoke-test and archive tools
run: |
sudo apt-get update
sudo apt-get install -y build-essential file python3 python3-pyqt6 unzip zip
- name: Assemble, audit and execute the portable GUI source bundle
run: |
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
make -j"$(nproc)" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
root="$RUNNER_TEMP/gui-work/zupt-gui-$version-portable"
output="$RUNNER_TEMP/release-gui-portable/zupt-gui-$version-portable.zip"
mkdir -p "$root/assets" "$(dirname "$output")"
install -m 0644 gui/src/zupt_gui.py "$root/zupt_gui.py"
install -m 0755 packaging/portable/zupt-gui.sh \
packaging/portable/zupt-gui.command "$root/"
install -m 0644 packaging/portable/zupt-gui.bat "$root/"
install -m 0644 packaging/portable/README.txt "$root/README.txt"
install -m 0644 gui/assets/zupt-icon.png gui/assets/zupt.ico "$root/assets/"
install -m 0644 LICENSE-AGPL-3.0 gui/LICENSE-GUI CHANGELOG.md "$root/"
install -m 0644 gui/assets/README.md "$root/ASSET-PROVENANCE.md"
bash scripts/check-source-only.sh --tree "$root"
QT_QPA_PLATFORM=offscreen PATH="$PWD:$PATH" \
"$root/zupt-gui.sh" --version | grep -Fx "zupt-gui $version"
epoch=$(<.source-date-epoch)
find "$root" -exec touch -d "@$epoch" {} +
(cd "$(dirname "$root")" && zip -X -9 -r "$output" "$(basename "$root")")
extract=$(mktemp -d)
unzip -q "$output" -d "$extract"
bash scripts/check-source-only.sh --tree "$extract/$(basename "$root")"
QT_QPA_PLATFORM=offscreen PATH="$PWD:$PATH" \
"$extract/$(basename "$root")/zupt-gui.sh" --version | \
grep -Fx "zupt-gui $version"
sha256sum "$output"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-gui-portable
path: ${{ runner.temp }}/release-gui-portable/*.zip
if-no-files-found: error
retention-days: 7
target-packages:
name: Windows and macOS release gates
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
needs:
- source-policy
- build-and-test
- strict-warnings
- sanitizers
- static-analysis
- source-archive
- debian-package
- tumbleweed-rpm
- gui-rpm-package
- linux-portable
- gui-portable
uses: ./.github/workflows/cross-platform.yml
permissions:
contents: read