name: Rust CI
on:
push:
branches: [ master ]
tags: [ 'v*' ]
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
# A newer push supersedes this run: on a branch or PR the run in
# progress is cancelled; on master it finishes, and only a run still
# waiting is replaced.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
build:
strategy:
matrix:
include:
- runner: arc-runner-set
arch: x86_64
- runner: ubuntu-24.04-arm
arch: aarch64
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Install system dependencies
run: |
if command -v apt-get &> /dev/null; then
sudo apt-get update
# build-essential/pkg-config for the C shims nix and blake3 build;
# git because a bare self-hosted container has none and some build
# scripts want it. No OpenSSL: the iroh stack is rustls all the way.
sudo apt-get install -y build-essential pkg-config git
elif command -v dnf &> /dev/null; then
sudo dnf install -y gcc gcc-c++ pkgconfig git
elif command -v yum &> /dev/null; then
sudo yum install -y gcc gcc-c++ pkgconfig git
elif command -v pacman &> /dev/null; then
sudo pacman -Sy --noconfirm base-devel pkgconf git
elif command -v apk &> /dev/null; then
sudo apk add --no-cache build-base pkgconfig git
else
echo "Package manager not found. Install a C toolchain, pkg-config and git manually."
exit 1
fi
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.92"
components: rustfmt, clippy
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
- name: Build
run: cargo build --workspace --verbose
- name: Run tests
run: cargo test --workspace --verbose
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --workspace --all-targets --locked -- -D warnings
- name: Build release binaries
run: cargo build --workspace --release --verbose
- name: Strip binaries
run: strip target/release/devendra-agent target/release/devendra-controller
- name: Install cargo-deb
run: cargo install cargo-deb
# Every CI build has to carry a distinct version, or the second
# one publishes the same filename with different bytes and the
# registry correctly refuses it with a 409 — and no machine that
# installed the first ever sees the second.
#
# `+` rather than `-`: in both ecosystems `0.1.0+dev.<stamp>`
# sorts *after* 0.1.0, which is what you want from a repository
# you added in order to run the latest build. A tag publishes the
# bare Cargo version.
- name: Stamp the package version
id: version
run: |
set -euo pipefail
# The crates all inherit version.workspace = true, so the one
# version that matters lives under [workspace.package] here.
base=$(grep -m1 '^version = ' Cargo.toml | cut -d'"' -f2)
if [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then
version="$base"
else
version="$base+dev.$(date -u +%s)"
fi
echo "version: $version"
echo "version=$version" >> "$GITHUB_OUTPUT"
# Rewritten after the release build, so the binaries are cached
# against the unstamped manifest and only packaging sees this.
sed -i "0,/^version = .*/s//version = \"$version\"/" Cargo.toml
# Only the agent has [package.metadata.deb] — it is the thing that
# gets installed on every managed host. The controller is run from
# the repo, so it has nothing to package yet.
- name: Build Debian package
run: cargo deb -p devendra-agent --no-build --no-strip
# `vars.CI_FORGE_URL` is what the forge injects into a run; fall
# back to what the runner already knows, and say so plainly if
# neither resolves rather than handing curl an empty URL.
- name: Resolve the forge URL
env:
CI_FORGE_URL: ${{ vars.CI_FORGE_URL }}
run: |
set -euo pipefail
forge_url=${CI_FORGE_URL:-${GITHUB_SERVER_URL:-}}
if [ -z "$forge_url" ]; then
echo "No forge URL available." >&2
echo "Set a CI_FORGE_URL variable on this repository or its org." >&2
exit 1
fi
echo "FORGE_URL=$forge_url" >> "$GITHUB_ENV"
# `${{ github.token }}` is minted for this job and authorized to
# publish to THIS repository's packages and nowhere else, so
# there is no secret to create first.
#
# Pull requests don't publish: a PR build is a check, and a
# registry that carries every proposed change is not a registry
# anyone can install from.
- name: Publish the .deb to the forge
if: github.event_name != 'pull_request'
env:
REPOSITORY: ${{ github.repository }}
TOKEN: ${{ github.token }}
run: |
set -euo pipefail
for deb in target/debian/*.deb; do
echo "publishing $deb"
curl --fail-with-body -sS \
-u "gitgud-actions:$TOKEN" \
-H "Content-Type: application/vnd.debian.binary-package" \
--data-binary "@$deb" \
"$FORGE_URL/pkg/deb/$REPOSITORY/upload?dist=stable&component=main"
echo
done
- name: How to install it
if: github.event_name != 'pull_request'
env:
OWNER: ${{ github.repository_owner }}
run: |
cat <<EOF
# Debian / Ubuntu
curl -fsSL $FORGE_URL/pkg/_keys/deb/$OWNER | sudo tee /etc/apt/keyrings/$OWNER.asc >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/$OWNER.asc] $FORGE_URL/pkg/deb/$OWNER/_all stable main" | sudo tee /etc/apt/sources.list.d/$OWNER.list
sudo apt update && sudo apt install devendra-agent
# Then put a join ticket in /etc/devendra/agent.env and:
sudo systemctl start devendra-agent
EOF
# Artifact storage is a GitHub feature; this forge has none, so
# these steps have nothing to upload to. They used to run and
# fail with continue-on-error, which is a red step on every
# single run — and a build that is always a bit red teaches
# everyone to stop reading it. Skipping says the same thing
# honestly and leaves the badge meaningful.
- name: Upload binaries
uses: actions/upload-artifact@v4
if: ${{ startsWith(github.server_url, 'https://github.com') }}
with:
name: devendra-${{ matrix.arch }}-linux
path: |
target/release/devendra-agent
target/release/devendra-controller
- name: Upload Debian package
uses: actions/upload-artifact@v4
if: ${{ startsWith(github.server_url, 'https://github.com') }}
with:
name: devendra-agent-${{ matrix.arch }}-linux.deb
path: target/debian/*.deb
release:
name: Release
if: startsWith(github.ref, 'refs/tags/v')
needs: build
permissions:
contents: write
packages: write
strategy:
matrix:
include:
- runner: arc-runner-set
arch: x86_64
- runner: ubuntu-24.04-arm
arch: aarch64
runs-on: ${{ matrix.runner }}
# Linux only, on purpose: the agent reconciles systemd units and
# resolves uids through nix, so there is no macOS or Windows build
# to ship.
steps:
- uses: actions/checkout@v4
- name: Install system dependencies
run: |
if command -v apt-get &> /dev/null; then
sudo apt-get update
sudo apt-get install -y build-essential pkg-config git
elif command -v dnf &> /dev/null; then
sudo dnf install -y gcc gcc-c++ pkgconfig git
else
echo "Package manager not found. Install a C toolchain, pkg-config and git manually."
exit 1
fi
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.92"
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
with:
key: release-${{ matrix.arch }}
- name: Build release binaries
run: cargo build --workspace --release --verbose
- name: Strip binaries
run: strip target/release/devendra-agent target/release/devendra-controller
- name: Create release archive
run: |
tar -czf devendra-${{ matrix.arch }}-linux-gnu.tar.gz \
-C target/release devendra-agent devendra-controller
- name: Install cargo-deb
run: cargo install cargo-deb
- name: Build Debian package
run: cargo deb -p devendra-agent --no-build --no-strip
- name: Upload release archive
uses: actions/upload-artifact@v4
if: ${{ startsWith(github.server_url, 'https://github.com') }}
with:
name: devendra-${{ matrix.arch }}-linux-gnu.tar.gz
path: devendra-${{ matrix.arch }}-linux-gnu.tar.gz
- name: Upload Debian package
uses: actions/upload-artifact@v4
if: ${{ startsWith(github.server_url, 'https://github.com') }}
with:
name: devendra-agent-${{ matrix.arch }}.deb
path: target/debian/*.deb
create-release:
name: Create Release
runs-on: arc-runner-set
# Makes a *GitHub* Release out of artifacts a GitHub runner uploaded.
# Off GitHub it would download nothing and then fail, which is the
# permanently-red build this workflow is trying to stop having.
# Tagged builds still publish their packages — that happens in the
# build job, which runs everywhere.
if: >-
startsWith(github.ref, 'refs/tags/v') &&
startsWith(github.server_url, 'https://github.com')
needs: release
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create release notes
run: |
cat > release_notes.md <<EOF
# devendra ${GITHUB_REF_NAME}
## Contents
- \`devendra-agent\` — host agent (systemd unit + quadlet reconciler)
- \`devendra-controller\` — controller web UI
## Supported platforms
- Linux (x86_64)
- Linux (aarch64)
## Installation
Install the \`.deb\` on each managed host, put a join ticket in
\`/etc/devendra/agent.env\`, then \`systemctl start devendra-agent\`.
EOF
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
artifacts/*/*.tar.gz
artifacts/*/*.deb
body_path: release_notes.md
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
neiam
/devendra
public · Issues · Pulls · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
11.3 KiB · text
History
d69022d