Skip to content
Closed
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
56 changes: 43 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust 1.91
uses: dtolnay/rust-toolchain@1.91.0
- name: Install Rust 1.99
uses: dtolnay/rust-toolchain@1.99.0
with:
components: rustfmt
- name: Check formatting
Expand All @@ -34,35 +34,46 @@ jobs:
uses: actions/checkout@v4
- name: Install Linux build tools
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends build-essential pkg-config
- name: Install Rust 1.91
uses: dtolnay/rust-toolchain@1.91.0
- name: Install Rust 1.99
uses: dtolnay/rust-toolchain@1.99.0
with:
components: clippy
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
cache-bin: false
- name: Run Clippy
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
run: cargo clippy --locked --workspace --all-targets --all-features -- -D warnings

strict-clippy:
name: Strict Clippy
name: Strict Clippy (${{ matrix.rust }})
strategy:
fail-fast: false
matrix:
rust: ["1.99.0", "stable"]
runs-on: ubuntu-24.04
env:
RUSTUP_TOOLCHAIN: ${{ matrix.rust }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Linux build tools
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends build-essential pkg-config
- name: Install Rust 1.91
uses: dtolnay/rust-toolchain@1.91.0
- name: Install selected Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
components: clippy
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
cache-bin: false
- name: Run strict Clippy
run: cargo clippy --workspace --all-targets --all-features -- -D warnings -D clippy::all -D clippy::pedantic -D clippy::nursery -D clippy::cargo
run: cargo clippy --locked --workspace --all-targets --all-features -- -D warnings -D clippy::all -D clippy::pedantic -D clippy::nursery -D clippy::cargo

- name: Test latest stable
if: matrix.rust == 'stable'
run: cargo test --locked --workspace --all-features

test:
name: Test (${{ matrix.name }})
Expand All @@ -85,13 +96,32 @@ jobs:
- name: Install Linux build tools
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends build-essential pkg-config
- name: Install Rust 1.91
uses: dtolnay/rust-toolchain@1.91.0
- name: Install Rust 1.99
uses: dtolnay/rust-toolchain@1.99.0
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
cache-bin: false
- name: Run tests
run: cargo test --workspace --all-features
run: cargo test --locked --workspace --all-features
- name: Build CLI
run: cargo build --workspace --all-features --bin rustpost-cli
run: cargo build --locked --workspace --all-features --bin rustpost-cli

release-tools:
name: Signed update packaging tests
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Validate release signatures and rejection paths
run: python3 tools/test-release-tools.py

msrv:
name: Rust 1.91 compatibility
env:
RUSTUP_TOOLCHAIN: "1.91.0"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.91.0
- name: Check minimum supported Rust
run: cargo check --locked --workspace --all-features
36 changes: 26 additions & 10 deletions .github/workflows/container.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
name: Container

on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
tag:
description: Published source tag to build (for example v1.0.0)
required: true
type: string

env:
RUSTPOST_RELEASE_TAG: ${{ inputs.tag || github.ref_name }}

permissions:
contents: read
Expand All @@ -21,10 +31,14 @@ jobs:
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Verify v1.0.0 application source
with:
ref: ${{ env.RUSTPOST_RELEASE_TAG }}
- name: Resolve and validate version
id: version
run: |
git fetch --depth=1 origin tag v1.0.0
git diff --quiet v1.0.0 -- Cargo.toml Cargo.lock src
version="${RUSTPOST_RELEASE_TAG#v}"
test "$(python3 -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["package"]["version"])')" = "$version"
echo "value=$version" >> "$GITHUB_OUTPUT"
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
Expand All @@ -36,7 +50,8 @@ jobs:
context: .
platforms: linux/${{ matrix.arch }}
push: true
tags: ghcr.io/csd113/rustpost:1.0.0-${{ matrix.arch }}
tags: ghcr.io/csd113/rustpost:${{ steps.version.outputs.value }}-${{ matrix.arch }}
build-args: RUSTPOST_VERSION=${{ steps.version.outputs.value }}
provenance: false

publish:
Expand All @@ -52,10 +67,11 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create multi-platform tags
run: |
docker buildx imagetools create \
--tag ghcr.io/csd113/rustpost:1.0.0 \
--tag ghcr.io/csd113/rustpost:latest \
ghcr.io/csd113/rustpost:1.0.0-amd64 \
ghcr.io/csd113/rustpost:1.0.0-arm64
version="${RUSTPOST_RELEASE_TAG#v}"
tags=(--tag "ghcr.io/csd113/rustpost:$version")
if [[ "$version" != *-* ]]; then tags+=(--tag ghcr.io/csd113/rustpost:latest); fi
docker buildx imagetools create "${tags[@]}" \
"ghcr.io/csd113/rustpost:$version-amd64" \
"ghcr.io/csd113/rustpost:$version-arm64"
- name: Inspect published image
run: docker buildx imagetools inspect ghcr.io/csd113/rustpost:1.0.0
run: docker buildx imagetools inspect "ghcr.io/csd113/rustpost:${RUSTPOST_RELEASE_TAG#v}"
85 changes: 70 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,43 +37,48 @@ jobs:
- name: Install Linux build tools
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends build-essential pkg-config
- name: Install Rust 1.91
uses: dtolnay/rust-toolchain@1.91.0
- name: Install Rust 1.99
uses: dtolnay/rust-toolchain@1.99.0
with:
targets: ${{ matrix.target }}
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
cache-bin: false
- name: Build release binary
run: cargo build --release --locked --target ${{ matrix.target }} --bin rustpost-cli
run: cargo build --release --locked --target ${{ matrix.target }} --bins
- name: Package archive
run: |
set -euo pipefail
staging="dist/${{ matrix.name }}/rustpost"
mkdir -p "$staging"
test "$(target/${{ matrix.target }}/release/rustpost-cli --version)" = "rustpost-cli ${GITHUB_REF_NAME#v}"
cp "target/${{ matrix.target }}/release/rustpost-cli" "$staging/"
cp "target/${{ matrix.target }}/release/rustpost-updater" "$staging/"
if [ "${{ runner.os }}" = "Linux" ] && [[ "$GITHUB_REF_NAME" != *-* ]]; then
python3 tools/package-update.py --binary "target/${{ matrix.target }}/release/rustpost-cli" --target "${{ matrix.target }}" --version "$GITHUB_REF_NAME" --output dist
fi
cp README.md "$staging/"
if [ -f LICENSE ]; then cp LICENSE "$staging/"; fi
if [ -f NOTICE ]; then cp NOTICE "$staging/"; fi
if [ -f NOTICES ]; then cp NOTICES "$staging/"; fi
tar -czf "dist/${{ matrix.archive }}" -C "dist/${{ matrix.name }}" rustpost
(cd dist && shasum -a 256 "${{ matrix.archive }}" > "${{ matrix.archive }}.sha256")
- name: Upload release assets
uses: softprops/action-gh-release@v2
- name: Upload built assets
uses: actions/upload-artifact@v4
with:
files: |
dist/${{ matrix.archive }}
dist/${{ matrix.archive }}.sha256
name: ${{ matrix.name }}
path: dist/*.*
if-no-files-found: error

build-windows:
name: Build windows-x86_64
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust 1.91
uses: dtolnay/rust-toolchain@1.91.0
- name: Install Rust 1.99
uses: dtolnay/rust-toolchain@1.99.0
with:
targets: x86_64-pc-windows-msvc
- name: Cache Cargo
Expand All @@ -88,16 +93,66 @@ jobs:
$ErrorActionPreference = "Stop"
$staging = "dist/windows-x86_64/rustpost"
New-Item -ItemType Directory -Force -Path $staging | Out-Null
$version = $env:GITHUB_REF_NAME.TrimStart("v")
if ((& target/x86_64-pc-windows-msvc/release/rustpost-cli.exe --version) -ne "rustpost-cli $version") { throw "Tag does not match executable" }
Copy-Item "target/x86_64-pc-windows-msvc/release/rustpost-cli.exe" $staging
Copy-Item "README.md" $staging
if (Test-Path "LICENSE") { Copy-Item "LICENSE" $staging }
if (Test-Path "NOTICE") { Copy-Item "NOTICE" $staging }
if (Test-Path "NOTICES") { Copy-Item "NOTICES" $staging }
Compress-Archive -Path $staging -DestinationPath "dist/rustpost-windows-x86_64.zip" -Force
(Get-FileHash "dist/rustpost-windows-x86_64.zip" -Algorithm SHA256).Hash.ToLower() + " rustpost-windows-x86_64.zip" | Out-File -Encoding ascii "dist/rustpost-windows-x86_64.zip.sha256"
- name: Upload release assets
uses: softprops/action-gh-release@v2
- name: Upload built assets
uses: actions/upload-artifact@v4
with:
files: |
dist/rustpost-windows-x86_64.zip
dist/rustpost-windows-x86_64.zip.sha256
name: windows-x86_64
path: dist/*.*
if-no-files-found: error

publish:
name: Verify, sign and publish complete release
needs: [build-unix, build-windows]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Sign and publish stable release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RUSTPOST_UPDATE_SIGNING_KEY: ${{ secrets.RUSTPOST_UPDATE_SIGNING_KEY }}
shell: bash
run: |
set -euo pipefail
if [[ "$GITHUB_REF_NAME" != *-* ]]; then test -n "$RUSTPOST_UPDATE_SIGNING_KEY"; fi
key="$RUNNER_TEMP/rustpost-update-signing.pem"
trap 'rm -f "$key"' EXIT
umask 077
printf '%s\n' "$RUSTPOST_UPDATE_SIGNING_KEY" > "$key"
unset RUSTPOST_UPDATE_SIGNING_KEY
for archive in rustpost-linux-x86_64.tar.gz rustpost-linux-aarch64.tar.gz rustpost-macos-aarch64.tar.gz rustpost-windows-x86_64.zip; do
test -s "dist/$archive"
(cd dist && sha256sum -c "$archive.sha256")
done
if [[ "$GITHUB_REF_NAME" == *-* ]]; then
if gh release view "$GITHUB_REF_NAME" --json isDraft --jq '.isDraft' > "$RUNNER_TEMP/is-draft"; then
test "$(cat "$RUNNER_TEMP/is-draft")" = true
else
gh release create "$GITHUB_REF_NAME" --draft --prerelease --verify-tag --generate-notes
fi
gh release upload "$GITHUB_REF_NAME" dist/*.tar.gz dist/*.zip dist/*.sha256 --clobber
gh release edit "$GITHUB_REF_NAME" --draft=false --prerelease --latest=false
exit 0
fi
test -s "$key"
if gh release view "$GITHUB_REF_NAME" --json isDraft --jq '.isDraft' > "$RUNNER_TEMP/is-draft"; then
test "$(cat "$RUNNER_TEMP/is-draft")" = true
else
gh release create "$GITHUB_REF_NAME" --draft --verify-tag --generate-notes
fi
release_id=$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/$GITHUB_REF_NAME" --jq '.id')
python3 tools/sign-updates.py --directory dist --key "$key" --release-id "$release_id" --version "$GITHUB_REF_NAME"
gh release upload "$GITHUB_REF_NAME" dist/*.tar.gz dist/*.zip dist/*.sha256 dist/rustpost-update-*.json dist/rustpost-update-*.sig --clobber
gh release edit "$GITHUB_REF_NAME" --draft=false --latest
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,14 @@ uploads/**/*
!/tests/
!/tests/playwright/
/tests/playwright/*

# Durable configuration-console regression suite
!/tests/playwright/configuration-admin.spec.mjs
!/tests/playwright/configuration-admin.config.mjs
!/tests/playwright/configuration-admin.helpers.mjs

# Durable software-update browser regression suite
!/tests/playwright/software-updates.spec.mjs
!/tests/playwright/software-updates.config.mjs

!tests/playwright/software-updates.helpers.mjs
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ autobins = false
name = "rustpost-cli"
path = "src/bin/rustpost-cli.rs"

[[bin]]
name = "rustpost-updater"
path = "src/bin/rustpost-updater.rs"

[dependencies]
anyhow = "1.0.104"
arti-client = { version = "=0.46.0", default-features = false, features = ["tokio", "rustls", "onion-service-service"] }
Expand All @@ -31,6 +35,9 @@ getrandom = "0.4.3"
safelog = "0.9.1"
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
semver = "1.0.28"
ring = "0.17.14"
rustix = { version = "1.1.5", features = ["fs"] }
sha2 = "0.11.0"
rusqlite = { version = "0.36.0", features = ["bundled", "functions"] }
rustls = { version = "0.23.40", default-features = false, features = ["ring"] }
Expand Down
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
FROM rust:1.91-bookworm AS build
# Bootstrap from the published Debian 12 image; install the exact release compiler.
FROM rust:1.98.1-bookworm AS build
RUN rustup toolchain install 1.99.0 --profile minimal --component rustfmt --component clippy \
&& rustup default 1.99.0

WORKDIR /usr/src/rustpost
COPY Cargo.toml Cargo.lock ./
COPY src ./src
COPY assets ./assets
RUN cargo build --release --locked --bin rustpost-cli

FROM debian:bookworm-slim
Expand All @@ -18,10 +22,12 @@ RUN apt-get update \
COPY --from=build /usr/src/rustpost/target/release/rustpost-cli /usr/local/bin/rustpost-cli
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

ARG RUSTPOST_VERSION=1.0.0
LABEL org.opencontainers.image.source="https://github.com/csd113/RustPost" \
org.opencontainers.image.title="RustPost" \
org.opencontainers.image.version="1.0.0"
org.opencontainers.image.version="${RUSTPOST_VERSION}"

ENV RUSTPOST_CONTAINER=1
USER rustpost
VOLUME /data
EXPOSE 8080
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ Installing `ffmpeg` is optional for downloaded apps. Without it, RustPost can se

### Build from source

With [Rust 1.91 or newer](https://rustup.rs/) installed, run these commands from the repository folder:
With [rustup](https://rustup.rs/) installed, run these commands from the repository folder (rustup selects the pinned compiler):

Development and release builds use the Rust 1.99.0 pin in `rust-toolchain.toml`. CI also checks the latest `stable` compiler so future compiler and Clippy changes are reviewed before updating the release pin. After each stable release, install that exact version with rustup, update the toolchain/CI/container pins together, and pass the full validation gates before using it for releases. `Cargo.toml` keeps Rust 1.91 as the minimum supported Rust version (MSRV); it is a compatibility floor, not the build compiler pin. Existing toolchains and the global rustup default need no changes.

```sh
cargo build --release --locked
Expand Down Expand Up @@ -230,3 +232,7 @@ GitHub Actions also builds and tests on Linux x86_64, Linux ARM64, macOS Apple S
## License

RustPost is available under the [MIT License](LICENSE).

### Administrator software updates

[Software update guide](docs/software-updates.md) covers panel checks, signed native Linux installation, mandatory verified backups, automatic rollback, restricted updater deployment, and release-signing setup. Containers and unmanaged installations use their deployment mechanism.
Loading
Loading