mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 02:29:55 +02:00
Merge pull request #19 from sudosylabs/feat/linux-release
ci: build Linux release packages
This commit is contained in:
344
.github/workflows/linux-packages.yml
vendored
Normal file
344
.github/workflows/linux-packages.yml
vendored
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
name: Linux packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/linux-packages.yml"
|
||||||
|
- "packaging/linux/**"
|
||||||
|
- "assets/linux/**"
|
||||||
|
- "desktopApp/**"
|
||||||
|
- "shared/**"
|
||||||
|
- "crates/vnidrop/**"
|
||||||
|
- "Cargo.toml"
|
||||||
|
- "Cargo.lock"
|
||||||
|
- "LICENSE"
|
||||||
|
- "build.gradle.kts"
|
||||||
|
- "settings.gradle.kts"
|
||||||
|
- "gradle.properties"
|
||||||
|
- "gradle/**"
|
||||||
|
- "gradlew"
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Release version in MAJOR.MINOR.PATCH form
|
||||||
|
required: true
|
||||||
|
default: "1.0.0"
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: linux-packages-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-deb:
|
||||||
|
name: Build Debian package (x64)
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 90
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Install packaging tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --yes fakeroot unzip
|
||||||
|
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: "21.0.11+10.0.LTS"
|
||||||
|
|
||||||
|
- name: Set up Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
|
||||||
|
|
||||||
|
- name: Set up Rust 1.91
|
||||||
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # v1
|
||||||
|
with:
|
||||||
|
toolchain: "1.91.0"
|
||||||
|
|
||||||
|
- name: Cache Cargo
|
||||||
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: linux-deb-x64-cargo-1.91.0-${{ hashFiles('Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
linux-deb-x64-cargo-1.91.0-
|
||||||
|
|
||||||
|
- name: Resolve version
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
REQUESTED_VERSION: ${{ inputs.version || '1.0.0' }}
|
||||||
|
run: |
|
||||||
|
version=$(packaging/linux/resolve-version.sh "$REQUESTED_VERSION")
|
||||||
|
echo "app=$version" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Test and build Debian package
|
||||||
|
run: |
|
||||||
|
./gradlew \
|
||||||
|
:shared:jvmTest \
|
||||||
|
:desktopApp:packageReleaseDeb \
|
||||||
|
-Pvnidrop.version=${{ steps.version.outputs.app }} \
|
||||||
|
-Pvnidrop.desktop.rustVariant=release \
|
||||||
|
-Pvnidrop.diagnostics.included=false \
|
||||||
|
--no-daemon \
|
||||||
|
--no-configuration-cache \
|
||||||
|
--stacktrace
|
||||||
|
|
||||||
|
- name: Validate Debian package
|
||||||
|
id: package
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.version.outputs.app }}
|
||||||
|
run: |
|
||||||
|
mapfile -t packages < <(find desktopApp/build/compose/binaries/main-release/deb -maxdepth 1 -type f -name '*.deb')
|
||||||
|
if (( ${#packages[@]} != 1 )); then
|
||||||
|
echo "Expected exactly one Debian package, found ${#packages[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
output_directory=build/release/linux/deb
|
||||||
|
output_name="vnidrop_${VERSION}-1_amd64.deb"
|
||||||
|
mkdir -p "$output_directory"
|
||||||
|
cp "${packages[0]}" "$output_directory/$output_name"
|
||||||
|
packaging/linux/verify-package.sh deb "$VERSION" "$output_directory/$output_name"
|
||||||
|
(
|
||||||
|
cd "$output_directory"
|
||||||
|
sha256sum "$output_name" > "$output_name.sha256"
|
||||||
|
)
|
||||||
|
|
||||||
|
- name: Upload Debian artifact
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: vnidrop-${{ steps.version.outputs.app }}-linux-deb-x64
|
||||||
|
path: build/release/linux/deb/
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 14
|
||||||
|
compression-level: 0
|
||||||
|
|
||||||
|
- name: Summarize Debian package
|
||||||
|
run: |
|
||||||
|
echo "### Debian package" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo "- Version: ${{ steps.version.outputs.app }}-1" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo "- Architecture: amd64" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo "- Build baseline: Ubuntu 22.04" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
|
build-rpm:
|
||||||
|
name: Build RPM package (x64)
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
container:
|
||||||
|
image: registry.fedoraproject.org/fedora:43
|
||||||
|
volumes:
|
||||||
|
- /usr/local/lib/android/sdk:/usr/local/lib/android/sdk
|
||||||
|
timeout-minutes: 90
|
||||||
|
env:
|
||||||
|
ANDROID_HOME: /usr/local/lib/android/sdk
|
||||||
|
ANDROID_SDK_ROOT: /usr/local/lib/android/sdk
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install build and packaging tools
|
||||||
|
run: |
|
||||||
|
dnf install --assumeyes \
|
||||||
|
alsa-lib \
|
||||||
|
cpio \
|
||||||
|
curl \
|
||||||
|
cups-libs \
|
||||||
|
desktop-file-utils \
|
||||||
|
findutils \
|
||||||
|
fontconfig \
|
||||||
|
freetype \
|
||||||
|
gcc \
|
||||||
|
gcc-c++ \
|
||||||
|
git \
|
||||||
|
gzip \
|
||||||
|
gtk3 \
|
||||||
|
libX11 \
|
||||||
|
libXext \
|
||||||
|
libXi \
|
||||||
|
libXrandr \
|
||||||
|
libXrender \
|
||||||
|
libXtst \
|
||||||
|
make \
|
||||||
|
mesa-libGL \
|
||||||
|
rpm-build \
|
||||||
|
tar \
|
||||||
|
unzip \
|
||||||
|
which \
|
||||||
|
xz \
|
||||||
|
zstd
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: "21.0.11+10.0.LTS"
|
||||||
|
|
||||||
|
- name: Set up Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
|
||||||
|
|
||||||
|
- name: Set up Rust 1.91
|
||||||
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # v1
|
||||||
|
with:
|
||||||
|
toolchain: "1.91.0"
|
||||||
|
|
||||||
|
- name: Cache Cargo
|
||||||
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: linux-rpm-x64-cargo-1.91.0-${{ hashFiles('Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
linux-rpm-x64-cargo-1.91.0-
|
||||||
|
|
||||||
|
- name: Resolve version
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
REQUESTED_VERSION: ${{ inputs.version || '1.0.0' }}
|
||||||
|
run: |
|
||||||
|
version=$(packaging/linux/resolve-version.sh "$REQUESTED_VERSION")
|
||||||
|
echo "app=$version" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Build RPM package
|
||||||
|
run: |
|
||||||
|
./gradlew \
|
||||||
|
:desktopApp:packageReleaseRpm \
|
||||||
|
-Pvnidrop.version=${{ steps.version.outputs.app }} \
|
||||||
|
-Pvnidrop.desktop.rustVariant=release \
|
||||||
|
-Pvnidrop.diagnostics.included=false \
|
||||||
|
--no-daemon \
|
||||||
|
--no-configuration-cache \
|
||||||
|
--stacktrace
|
||||||
|
|
||||||
|
- name: Validate RPM package
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.version.outputs.app }}
|
||||||
|
run: |
|
||||||
|
mapfile -t packages < <(find desktopApp/build/compose/binaries/main-release/rpm -maxdepth 1 -type f -name '*.rpm')
|
||||||
|
if (( ${#packages[@]} != 1 )); then
|
||||||
|
echo "Expected exactly one RPM package, found ${#packages[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
output_directory=build/release/linux/rpm
|
||||||
|
output_name="vnidrop-${VERSION}-1.x86_64.rpm"
|
||||||
|
mkdir -p "$output_directory"
|
||||||
|
cp "${packages[0]}" "$output_directory/$output_name"
|
||||||
|
packaging/linux/verify-package.sh rpm "$VERSION" "$output_directory/$output_name"
|
||||||
|
(
|
||||||
|
cd "$output_directory"
|
||||||
|
sha256sum "$output_name" > "$output_name.sha256"
|
||||||
|
)
|
||||||
|
|
||||||
|
- name: Upload RPM artifact
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: vnidrop-${{ steps.version.outputs.app }}-linux-rpm-x64
|
||||||
|
path: build/release/linux/rpm/
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 14
|
||||||
|
compression-level: 0
|
||||||
|
|
||||||
|
- name: Summarize RPM package
|
||||||
|
run: |
|
||||||
|
echo "### RPM package" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo "- Version: ${{ steps.version.outputs.app }}-1" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo "- Architecture: x86_64" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo "- Build environment: Fedora 43" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
|
publish-release:
|
||||||
|
name: Publish GitHub Release assets
|
||||||
|
if: github.event_name == 'push' && github.ref_type == 'tag'
|
||||||
|
needs:
|
||||||
|
- build-deb
|
||||||
|
- build-rpm
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 15
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout release history
|
||||||
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Verify tag is on master
|
||||||
|
run: |
|
||||||
|
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/master; then
|
||||||
|
echo "Release tags must point to a commit on master" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Download Linux artifacts
|
||||||
|
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||||
|
with:
|
||||||
|
pattern: vnidrop-*-linux-*-x64
|
||||||
|
path: build/release/linux
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Verify artifacts and checksums
|
||||||
|
run: |
|
||||||
|
cd build/release/linux
|
||||||
|
shopt -s nullglob
|
||||||
|
deb_packages=(*.deb)
|
||||||
|
rpm_packages=(*.rpm)
|
||||||
|
checksum_files=(*.sha256)
|
||||||
|
if (( ${#deb_packages[@]} != 1 || ${#rpm_packages[@]} != 1 || ${#checksum_files[@]} != 2 )); then
|
||||||
|
echo "Expected one DEB, one RPM, and two checksum sidecars" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
version=${GITHUB_REF_NAME#v}
|
||||||
|
if [[ ${deb_packages[0]} != "vnidrop_${version}-1_amd64.deb" || ${rpm_packages[0]} != "vnidrop-${version}-1.x86_64.rpm" ]]; then
|
||||||
|
echo "Downloaded package names do not match tag $GITHUB_REF_NAME" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sha256sum --check "${checksum_files[@]}"
|
||||||
|
sha256sum "${deb_packages[@]}" "${rpm_packages[@]}" > SHA256SUMS
|
||||||
|
rm -- "${checksum_files[@]}"
|
||||||
|
|
||||||
|
- name: Publish GitHub Release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
GH_REPO: ${{ github.repository }}
|
||||||
|
run: |
|
||||||
|
tag=${GITHUB_REF_NAME}
|
||||||
|
version=${tag#v}
|
||||||
|
if gh release view "$tag" >/dev/null 2>&1; then
|
||||||
|
echo "GitHub Release $tag already exists; refusing to replace its assets" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
gh release create "$tag" \
|
||||||
|
build/release/linux/*.deb \
|
||||||
|
build/release/linux/*.rpm \
|
||||||
|
build/release/linux/SHA256SUMS \
|
||||||
|
--verify-tag \
|
||||||
|
--title "VniDrop $version" \
|
||||||
|
--generate-notes
|
||||||
@@ -37,11 +37,12 @@ compose.desktop {
|
|||||||
buildTypes.release.proguard.isEnabled.set(false)
|
buildTypes.release.proguard.isEnabled.set(false)
|
||||||
|
|
||||||
nativeDistributions {
|
nativeDistributions {
|
||||||
targetFormats(TargetFormat.Dmg, TargetFormat.Deb)
|
targetFormats(TargetFormat.Dmg, TargetFormat.Deb, TargetFormat.Rpm)
|
||||||
packageName = "VniDrop"
|
packageName = "VniDrop"
|
||||||
packageVersion = appVersion
|
packageVersion = appVersion
|
||||||
description = "Send files directly across your devices"
|
description = "Send files directly across your devices"
|
||||||
vendor = "Sudosy Labs"
|
vendor = "Sudosy Labs"
|
||||||
|
licenseFile.set(project.file("../LICENSE"))
|
||||||
macOS {
|
macOS {
|
||||||
bundleID = "com.vnidrop.app"
|
bundleID = "com.vnidrop.app"
|
||||||
iconFile.set(project.file("../assets/macos/app-icon.icns"))
|
iconFile.set(project.file("../assets/macos/app-icon.icns"))
|
||||||
@@ -52,6 +53,9 @@ compose.desktop {
|
|||||||
linux {
|
linux {
|
||||||
packageName = "vnidrop"
|
packageName = "vnidrop"
|
||||||
iconFile.set(project.file("../assets/linux/app-icon.png"))
|
iconFile.set(project.file("../assets/linux/app-icon.png"))
|
||||||
|
debMaintainer = "support@sudosy.fr"
|
||||||
|
appRelease = "1"
|
||||||
|
rpmLicenseType = "Apache-2.0"
|
||||||
}
|
}
|
||||||
fileAssociation(
|
fileAssociation(
|
||||||
mimeType = "application/vnd.vnidrop.transfer",
|
mimeType = "application/vnd.vnidrop.transfer",
|
||||||
|
|||||||
84
packaging/linux/README.md
Normal file
84
packaging/linux/README.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Linux packaging
|
||||||
|
|
||||||
|
VniDrop ships native x64 packages for the two common Linux package families:
|
||||||
|
|
||||||
|
- Debian/Ubuntu: `.deb`
|
||||||
|
- Current Fedora systems: `.rpm`
|
||||||
|
|
||||||
|
Both packages contain the application, its release Rust library, and a private
|
||||||
|
Java runtime. Users do not need to install Java separately. The packages are
|
||||||
|
currently distributed as direct GitHub Release downloads, so they use SHA-256
|
||||||
|
checksums rather than a Linux repository signing key. A future APT or RPM
|
||||||
|
repository should add repository metadata signing and its own update channel.
|
||||||
|
|
||||||
|
## GitHub Actions
|
||||||
|
|
||||||
|
The Linux packages workflow runs for relevant pull requests, release tags
|
||||||
|
matching `vMAJOR.MINOR.PATCH`, and manual dispatches. Each native package is
|
||||||
|
built and validated on its matching distribution family:
|
||||||
|
|
||||||
|
- `.deb` on Ubuntu 22.04 for a conservative glibc baseline
|
||||||
|
- `.rpm` inside Fedora 43 so `jpackage` can discover normal RPM dependencies
|
||||||
|
|
||||||
|
The shared JVM suite runs inside the Debian build job. Package construction and
|
||||||
|
payload validation happen in both build jobs, so there is no separate test
|
||||||
|
runner. Pull requests build and verify both packages but do not retain
|
||||||
|
artifacts. Manual runs retain build artifacts for 14 days. A pushed version tag
|
||||||
|
whose commit is on `master` creates the matching GitHub Release with the `.deb`,
|
||||||
|
`.rpm`, and a combined `SHA256SUMS`.
|
||||||
|
|
||||||
|
The existing `v1.0.0` tag predates this workflow and will not run it
|
||||||
|
retroactively. Use the next version tag after this configuration reaches
|
||||||
|
`master`.
|
||||||
|
|
||||||
|
## Install a downloaded package
|
||||||
|
|
||||||
|
Verify downloads from the directory containing all three release files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sha256sum -c SHA256SUMS
|
||||||
|
```
|
||||||
|
|
||||||
|
On Debian or Ubuntu:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install ./vnidrop_VERSION-1_amd64.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
On Fedora:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install ./vnidrop-VERSION-1.x86_64.rpm
|
||||||
|
```
|
||||||
|
|
||||||
|
The package manager installs declared system-library dependencies and creates
|
||||||
|
the VniDrop desktop launcher. Uninstall with `sudo apt remove vnidrop` or
|
||||||
|
`sudo dnf remove vnidrop`.
|
||||||
|
|
||||||
|
## Manual native builds
|
||||||
|
|
||||||
|
Use JDK 21 and Rust 1.91. Build DEB packages on Debian/Ubuntu with `dpkg` and
|
||||||
|
`fakeroot`; build RPM packages on Fedora with `rpm-build`. Building an RPM on
|
||||||
|
Ubuntu prevents `jpackage` from discovering normal RPM dependencies.
|
||||||
|
|
||||||
|
From the repository root on the matching Linux family, run one of:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :shared:jvmTest :desktopApp:packageReleaseDeb \
|
||||||
|
-Pvnidrop.version=1.0.0 \
|
||||||
|
-Pvnidrop.desktop.rustVariant=release \
|
||||||
|
-Pvnidrop.diagnostics.included=false \
|
||||||
|
--no-daemon --no-configuration-cache --stacktrace
|
||||||
|
|
||||||
|
./gradlew :shared:jvmTest :desktopApp:packageReleaseRpm \
|
||||||
|
-Pvnidrop.version=1.0.0 \
|
||||||
|
-Pvnidrop.desktop.rustVariant=release \
|
||||||
|
-Pvnidrop.diagnostics.included=false \
|
||||||
|
--no-daemon --no-configuration-cache --stacktrace
|
||||||
|
```
|
||||||
|
|
||||||
|
Compose writes the packages under
|
||||||
|
`desktopApp/build/compose/binaries/main-release/deb/` and
|
||||||
|
`desktopApp/build/compose/binaries/main-release/rpm/`. The workflow then
|
||||||
|
validates package identity, version, architecture, dependencies, bundled JVM,
|
||||||
|
and release Rust payload before publishing anything.
|
||||||
39
packaging/linux/resolve-version.sh
Executable file
39
packaging/linux/resolve-version.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
version=${1:-1.0.0}
|
||||||
|
|
||||||
|
if [[ ${GITHUB_REF_TYPE:-} == "tag" ]]; then
|
||||||
|
if [[ ! ${GITHUB_REF_NAME:-} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "Linux release tags must use vMAJOR.MINOR.PATCH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
version=${GITHUB_REF_NAME#v}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "Version must use MAJOR.MINOR.PATCH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=. read -r major minor patch <<< "$version"
|
||||||
|
parts=("$major" "$minor" "$patch")
|
||||||
|
|
||||||
|
for index in "${!parts[@]}"; do
|
||||||
|
part=${parts[$index]}
|
||||||
|
if [[ $part != "0" && $part == 0* ]]; then
|
||||||
|
echo "Version components must be canonical integers without leading zeroes" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if (( ${#part} > 5 )) || (( 10#$part > 65535 )); then
|
||||||
|
echo "Version components must be between 0 and 65535" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if (( index == 0 && 10#$part == 0 )); then
|
||||||
|
echo "The major version must be non-zero" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s\n' "$version"
|
||||||
96
packaging/linux/verify-package.sh
Executable file
96
packaging/linux/verify-package.sh
Executable file
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if (( $# != 3 )); then
|
||||||
|
echo "Usage: $0 <deb|rpm> <MAJOR.MINOR.PATCH> <package-path>" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
format=$1
|
||||||
|
version=$2
|
||||||
|
package_path=$3
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "Package verification failed: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
require_command() {
|
||||||
|
command -v "$1" >/dev/null 2>&1 || fail "required command '$1' is not installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ -f $package_path ]] || fail "package not found: $package_path"
|
||||||
|
require_command find
|
||||||
|
require_command unzip
|
||||||
|
|
||||||
|
extract_root=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$extract_root"' EXIT
|
||||||
|
|
||||||
|
case $format in
|
||||||
|
deb)
|
||||||
|
require_command dpkg-deb
|
||||||
|
[[ $(dpkg-deb -f "$package_path" Package) == "vnidrop" ]] || fail "unexpected Debian package name"
|
||||||
|
[[ $(dpkg-deb -f "$package_path" Version) == "$version-1" ]] || fail "unexpected Debian package version"
|
||||||
|
[[ $(dpkg-deb -f "$package_path" Architecture) == "amd64" ]] || fail "unexpected Debian architecture"
|
||||||
|
[[ $(dpkg-deb -f "$package_path" Maintainer) == *"support@sudosy.fr"* ]] || fail "unexpected Debian maintainer"
|
||||||
|
deb_dependencies=$(dpkg-deb -f "$package_path" Depends)
|
||||||
|
deb_libc_pattern='(^|,[[:space:]])libc6([[:space:](]|,|$)'
|
||||||
|
deb_xdg_pattern='(^|,[[:space:]])xdg-utils([[:space:](]|,|$)'
|
||||||
|
[[ $deb_dependencies =~ $deb_libc_pattern ]] || fail "Debian package does not declare libc6"
|
||||||
|
[[ $deb_dependencies =~ $deb_xdg_pattern ]] || fail "Debian package does not declare xdg-utils"
|
||||||
|
dpkg-deb -x "$package_path" "$extract_root"
|
||||||
|
;;
|
||||||
|
rpm)
|
||||||
|
require_command rpm
|
||||||
|
require_command rpm2cpio
|
||||||
|
require_command cpio
|
||||||
|
metadata=$(rpm -qp --queryformat '%{NAME}\n%{VERSION}\n%{RELEASE}\n%{ARCH}\n%{LICENSE}\n' "$package_path")
|
||||||
|
mapfile -t fields <<< "$metadata"
|
||||||
|
[[ ${fields[0]:-} == "vnidrop" ]] || fail "unexpected RPM package name"
|
||||||
|
[[ ${fields[1]:-} == "$version" ]] || fail "unexpected RPM package version"
|
||||||
|
[[ ${fields[2]:-} == "1" ]] || fail "unexpected RPM release"
|
||||||
|
[[ ${fields[3]:-} == "x86_64" ]] || fail "unexpected RPM architecture"
|
||||||
|
[[ ${fields[4]:-} == "Apache-2.0" ]] || fail "unexpected RPM license"
|
||||||
|
mapfile -t runtime_requirements < <(rpm -qpR "$package_path" | grep -Ev '^(rpmlib\(|/bin/sh$)' || true)
|
||||||
|
printf '%s\n' "${runtime_requirements[@]}" | grep -Eq '^(glibc($|[[:space:]])|libc\.so\.6)' || fail "RPM package does not declare glibc"
|
||||||
|
printf '%s\n' "${runtime_requirements[@]}" | grep -Fxq 'xdg-utils' || fail "RPM package does not declare xdg-utils"
|
||||||
|
rpm2cpio "$package_path" | (
|
||||||
|
cd "$extract_root"
|
||||||
|
cpio -idm --quiet
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
fail "unsupported package format: $format"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
mapfile -d '' -t launchers < <(find "$extract_root" -type f -iname 'vnidrop' -perm /111 -print0)
|
||||||
|
(( ${#launchers[@]} == 1 )) || fail "expected exactly one executable VniDrop launcher"
|
||||||
|
|
||||||
|
mapfile -d '' -t desktop_entries < <(find "$extract_root" -type f -name '*.desktop' -print0)
|
||||||
|
(( ${#desktop_entries[@]} == 1 )) || fail "expected exactly one desktop entry"
|
||||||
|
grep -Eiq '^Exec=.*/VniDrop([[:space:]]|$)' "${desktop_entries[0]}" || fail "desktop entry does not launch VniDrop"
|
||||||
|
grep -Eq '^MimeType=.*application/vnd\.vnidrop\.transfer(;|$)' "${desktop_entries[0]}" || fail "desktop entry does not register VniDrop invitations"
|
||||||
|
|
||||||
|
mapfile -d '' -t bundled_jvms < <(find "$extract_root" -type f -path '*/lib/runtime/lib/server/libjvm.so' -print0)
|
||||||
|
(( ${#bundled_jvms[@]} == 1 )) || fail "expected exactly one bundled JVM"
|
||||||
|
|
||||||
|
mapfile -d '' -t debug_rust_jars < <(find "$extract_root" -type f -name 'shared-linux-x86-64-debug-*.jar' -print0)
|
||||||
|
(( ${#debug_rust_jars[@]} == 0 )) || fail "package contains a debug Rust runtime JAR"
|
||||||
|
|
||||||
|
mapfile -d '' -t rust_jars < <(
|
||||||
|
find "$extract_root" -type f -name 'shared-linux-x86-64-*.jar' ! -name 'shared-linux-x86-64-debug-*.jar' -print0
|
||||||
|
)
|
||||||
|
(( ${#rust_jars[@]} == 1 )) || fail "expected exactly one release Rust runtime JAR"
|
||||||
|
|
||||||
|
native_entry_size=$(unzip -p "${rust_jars[0]}" 'linux-x86-64/libvnidrop.so' | wc -c)
|
||||||
|
[[ $native_entry_size =~ ^[0-9]+$ ]] || fail "release Rust runtime JAR does not contain libvnidrop.so"
|
||||||
|
(( native_entry_size > 0 )) || fail "release Rust runtime JAR contains an empty libvnidrop.so"
|
||||||
|
|
||||||
|
mapfile -d '' -t shared_jars < <(find "$extract_root" -type f -name 'shared-jvm-*.jar' -print0)
|
||||||
|
(( ${#shared_jars[@]} == 1 )) || fail "expected exactly one shared JVM JAR"
|
||||||
|
unzip -p "${shared_jars[0]}" META-INF/MANIFEST.MF | tr -d '\r' |
|
||||||
|
grep -Fxq "Implementation-Version: $version" || fail "packaged app version does not match $version"
|
||||||
|
|
||||||
|
printf 'Verified %s package: %s\n' "$format" "$package_path"
|
||||||
Reference in New Issue
Block a user