Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"

[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
71 changes: 65 additions & 6 deletions .github/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
FROM ubuntu:24.04

ARG RUST_VERSION=1.94.0

ENV DEBIAN_FRONTEND=noninteractive
ENV RUSTUP_HOME=/usr/local/rustup
ENV CARGO_HOME=/usr/local/cargo
ENV PATH="/usr/local/cargo/bin:${PATH}"

# System dependencies
# Configure multi-arch package sources based on host architecture.
# On amd64: add arm64 + armhf foreign arches via ports.ubuntu.com
# On arm64: add armhf foreign arch only (arm64 packages are native)
RUN set -eux; \
NATIVE_ARCH=$(dpkg --print-architecture); \
if [ "$NATIVE_ARCH" = "amd64" ]; then \
dpkg --add-architecture arm64; \
dpkg --add-architecture armhf; \
. /etc/os-release; \
echo "deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports ${VERSION_CODENAME} main restricted universe multiverse" > /etc/apt/sources.list.d/arm-ports.list; \
echo "deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports ${VERSION_CODENAME}-updates main restricted universe multiverse" >> /etc/apt/sources.list.d/arm-ports.list; \
echo "deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports ${VERSION_CODENAME}-security main restricted universe multiverse" >> /etc/apt/sources.list.d/arm-ports.list; \
if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then \
sed -i '/^Architectures:/d; /^Types:/a Architectures: amd64' /etc/apt/sources.list.d/ubuntu.sources; \
elif [ -f /etc/apt/sources.list ]; then \
sed -i 's/^deb http/deb [arch=amd64] http/' /etc/apt/sources.list; \
fi; \
elif [ "$NATIVE_ARCH" = "arm64" ]; then \
dpkg --add-architecture armhf; \
fi

# System dependencies (native)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
curl \
Expand All @@ -20,10 +43,46 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
git \
&& rm -rf /var/lib/apt/lists/*

# Rust stable
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --default-toolchain stable --profile minimal && \
rustup component add clippy rustfmt
# Cross-compilers (architecture-dependent)
# amd64: cross-compile to both arm64 and armhf
# arm64: cross-compile to armhf only (arm64 is native)
RUN set -eux; \
NATIVE_ARCH=$(dpkg --print-architecture); \
apt-get update; \
if [ "$NATIVE_ARCH" = "amd64" ]; then \
apt-get install -y --no-install-recommends \
gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf; \
elif [ "$NATIVE_ARCH" = "arm64" ]; then \
apt-get install -y --no-install-recommends \
gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf; \
fi; \
rm -rf /var/lib/apt/lists/*

# Multi-arch dev libraries for cross-compilation targets
RUN set -eux; \
NATIVE_ARCH=$(dpkg --print-architecture); \
apt-get update; \
if [ "$NATIVE_ARCH" = "amd64" ]; then \
apt-get install -y --no-install-recommends \
libfuse-dev:arm64 libsqlite3-dev:arm64 libssl-dev:arm64 zlib1g-dev:arm64 libudev-dev:arm64 \
libfuse-dev:armhf libsqlite3-dev:armhf libssl-dev:armhf zlib1g-dev:armhf libudev-dev:armhf; \
elif [ "$NATIVE_ARCH" = "arm64" ]; then \
apt-get install -y --no-install-recommends \
libfuse-dev:armhf libsqlite3-dev:armhf libssl-dev:armhf zlib1g-dev:armhf libudev-dev:armhf; \
fi; \
rm -rf /var/lib/apt/lists/*

# Rust stable with cross-compilation targets
RUN set -eux; \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain "${RUST_VERSION}" --profile minimal; \
rustup component add clippy rustfmt; \
NATIVE_ARCH=$(dpkg --print-architecture); \
if [ "$NATIVE_ARCH" = "amd64" ]; then \
rustup target add aarch64-unknown-linux-gnu armv7-unknown-linux-gnueabihf; \
elif [ "$NATIVE_ARCH" = "arm64" ]; then \
rustup target add armv7-unknown-linux-gnueabihf; \
fi

# Packaging tools
RUN cargo install cargo-deb cargo-generate-rpm
RUN cargo install cargo-deb cargo-generate-rpm
206 changes: 173 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ env:
RELEASE_BUILD: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }}

jobs:
# 1. New setup job to create the lowercase repository variable
setup:
runs-on: ubuntu-latest
outputs:
Expand All @@ -28,7 +27,7 @@ jobs:
lint:
needs: setup
runs-on: ubuntu-latest
container: ghcr.io/${{ needs.setup.outputs.repo_lc }}/ci:1.0
container: ghcr.io/${{ needs.setup.outputs.repo_lc }}/ci:2.0
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -54,10 +53,31 @@ jobs:
run: cargo check

build-and-test:
needs: setup # 2. Require the setup job to finish first
runs-on: ubuntu-latest
# 3. Use the output from the setup job
container: ghcr.io/${{ needs.setup.outputs.repo_lc }}/ci:1.0
needs: setup
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
runner: ubuntu-latest
use-container: true
artifact-name: linux-amd64
strip-cmd: strip
- target: aarch64-unknown-linux-gnu
runner: ubuntu-24.04-arm
use-container: true
artifact-name: linux-arm64
strip-cmd: strip
- target: armv7-unknown-linux-gnueabihf
runner: ubuntu-24.04-arm
use-container: true
artifact-name: linux-armhf
strip-cmd: arm-linux-gnueabihf-strip
cc: arm-linux-gnueabihf-gcc
pkg-config-path: /usr/lib/arm-linux-gnueabihf/pkgconfig
bindgen-target: arm-linux-gnueabihf
runs-on: ${{ matrix.runner }}
container: ${{ matrix.use-container && format('ghcr.io/{0}/ci:2.0', needs.setup.outputs.repo_lc) || '' }}
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -70,86 +90,206 @@ jobs:
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'Cargo.toml') }}
restore-keys: cargo-${{ runner.os }}-
key: cargo-${{ matrix.target }}-${{ hashFiles('Cargo.lock', 'Cargo.toml') }}
restore-keys: cargo-${{ matrix.target }}-

- name: Build
run: |
FEATURES=""
if [ "$RELEASE_BUILD" = "true" ]; then
FEATURES="--features crash-reporting"
fi
cargo build --release $FEATURES
cargo build --release --target ${{ matrix.target }} $FEATURES
env:
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
CC: ${{ matrix.cc || '' }}
PKG_CONFIG_PATH: ${{ matrix.pkg-config-path || env.PKG_CONFIG_PATH || '' }}
PKG_CONFIG_ALLOW_CROSS: ${{ matrix.cc && '1' || '' }}
BINDGEN_EXTRA_CLANG_ARGS: ${{ matrix.bindgen-target && format('--target={0} --sysroot=/usr/{0}', matrix.bindgen-target) || '' }}

- name: Test
run: cargo test --release
run: cargo test --release --target ${{ matrix.target }}
env:
CC: ${{ matrix.cc || '' }}
PKG_CONFIG_PATH: ${{ matrix.pkg-config-path || env.PKG_CONFIG_PATH || '' }}
PKG_CONFIG_ALLOW_CROSS: ${{ matrix.cc && '1' || '' }}
BINDGEN_EXTRA_CLANG_ARGS: ${{ matrix.bindgen-target && format('--target={0} --sysroot=/usr/{0}', matrix.bindgen-target) || '' }}

- name: Upload binary
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v'))
uses: actions/upload-artifact@v4
with:
name: binary
path: target/release/pcloud
name: binary-${{ matrix.artifact-name }}
path: target/${{ matrix.target }}/release/pcloud
retention-days: 5

build-macos:
needs: setup
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install native (arm64) dependencies
run: |
brew install macfuse sqlite openssl llvm pkg-config
echo "LIBCLANG_PATH=$(brew --prefix llvm)/lib" >> $GITHUB_ENV

- name: Install x86_64 dependencies via Rosetta
run: |
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
arch -x86_64 /usr/local/bin/brew install openssl@3 sqlite

- name: Add x86_64 target
run: rustup target add x86_64-apple-darwin

- uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-macos-universal-${{ hashFiles('Cargo.lock', 'Cargo.toml') }}
restore-keys: cargo-macos-universal-

- name: Build aarch64
run: |
FEATURES=""
if [ "$RELEASE_BUILD" = "true" ]; then
FEATURES="--features crash-reporting"
fi
cargo build --release --target aarch64-apple-darwin $FEATURES
env:
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
PKG_CONFIG_PATH: /opt/homebrew/opt/openssl@3/lib/pkgconfig:/opt/homebrew/opt/sqlite/lib/pkgconfig

- name: Build x86_64
run: |
FEATURES=""
if [ "$RELEASE_BUILD" = "true" ]; then
FEATURES="--features crash-reporting"
fi
cargo build --release --target x86_64-apple-darwin $FEATURES
env:
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
PKG_CONFIG_PATH: /usr/local/opt/openssl@3/lib/pkgconfig:/usr/local/opt/sqlite/lib/pkgconfig

- name: Test (native aarch64 only)
run: cargo test --release --target aarch64-apple-darwin

- name: Create universal binary
run: |
lipo -create -output pcloud-macos-universal \
target/aarch64-apple-darwin/release/pcloud \
target/x86_64-apple-darwin/release/pcloud
lipo -info pcloud-macos-universal
strip pcloud-macos-universal

- name: Upload universal binary
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v'))
uses: actions/upload-artifact@v4
with:
name: binary-macos-universal
path: pcloud-macos-universal
retention-days: 5

package:
runs-on: ubuntu-latest
# 4. Include setup in the needs array to access its outputs here
needs: [setup, build-and-test]
needs: [setup, lint, build-and-test]
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v'))
# 5. Use the output from the setup job
container: ghcr.io/${{ needs.setup.outputs.repo_lc }}/ci:latest
container: ghcr.io/${{ needs.setup.outputs.repo_lc }}/ci:2.0
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
artifact-name: linux-amd64
strip-cmd: strip
deb-arch: amd64
rpm-arch: x86_64
- target: aarch64-unknown-linux-gnu
artifact-name: linux-arm64
strip-cmd: aarch64-linux-gnu-strip
deb-arch: arm64
rpm-arch: aarch64
- target: armv7-unknown-linux-gnueabihf
artifact-name: linux-armhf
strip-cmd: arm-linux-gnueabihf-strip
deb-arch: armhf
rpm-arch: armv7hl
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: actions/download-artifact@v4
with:
name: binary
path: target/release/
name: binary-${{ matrix.artifact-name }}
path: target/${{ matrix.target }}/release/

- name: Ensure binary is executable
run: chmod +x target/release/pcloud
run: chmod +x target/${{ matrix.target }}/release/pcloud

- name: Build .deb
run: cargo deb --no-build
run: cargo deb --no-build --target ${{ matrix.target }}

- name: Build .rpm
run: cargo generate-rpm
run: cargo generate-rpm --target ${{ matrix.target }}

- name: Strip binary
run: strip target/release/pcloud
run: ${{ matrix.strip-cmd }} target/${{ matrix.target }}/release/pcloud

- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: packages
name: packages-${{ matrix.artifact-name }}
retention-days: 30
path: |
target/debian/*.deb
target/generate-rpm/*.rpm
target/release/pcloud
target/${{ matrix.target }}/debian/*.deb
target/${{ matrix.target }}/generate-rpm/*.rpm
target/${{ matrix.target }}/release/pcloud

release:
runs-on: ubuntu-latest
needs: package
needs: [package, build-macos]
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v4
with:
name: packages
path: artifacts/

- name: Prepare release assets
run: |
mkdir -p release

# Linux binaries and packages
for arch in linux-amd64 linux-arm64 linux-armhf; do
if [ -d "artifacts/packages-${arch}" ]; then
# Rename binary with arch suffix
cp "artifacts/packages-${arch}/release/pcloud" "release/pcloud-${arch}" 2>/dev/null || \
cp "artifacts/packages-${arch}/"*/release/pcloud "release/pcloud-${arch}" 2>/dev/null || true

# Copy .deb packages
find "artifacts/packages-${arch}" -name '*.deb' -exec cp {} release/ \; 2>/dev/null || true

# Copy .rpm packages
find "artifacts/packages-${arch}" -name '*.rpm' -exec cp {} release/ \; 2>/dev/null || true
fi
done

# macOS universal binary
if [ -d "artifacts/binary-macos-universal" ]; then
cp "artifacts/binary-macos-universal/pcloud-macos-universal" "release/pcloud-macos-universal" 2>/dev/null || true
fi

ls -la release/

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: ${{ github.ref_name }}
generate_release_notes: true
files: |
debian/*.deb
generate-rpm/*.rpm
release/pcloud
files: release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Loading
Loading