Skip to content

fix(linux-scripts): fix linux scripts when systemd is degraded + imp… (#5871) #123

fix(linux-scripts): fix linux scripts when systemd is degraded + imp… (#5871)

fix(linux-scripts): fix linux scripts when systemd is degraded + imp… (#5871) #123

Workflow file for this run

name: "[Agent] CI"
on:
push:
branches: [main]
tags:
- "[0-9]+.[0-9]+.[0-9]+"
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# ──────────────────────────────────────────────────────────────────
# Quality gates (fmt + audit).
#
# Single-platform fast checks. Clippy lives in its own job because
# it needs to run per (os, arch) target to catch cfg-gated code.
# ──────────────────────────────────────────────────────────────────
quality:
name: "🔍 Quality gates (fmt + audit)"
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Cache
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: cargo-quality-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
- name: Run cargo fmt
run: cargo fmt -- --check
- name: Install cargo audit
run: |
if [ -x "$HOME/.cargo/bin/cargo-audit" ]; then
echo "cargo-audit already installed."
else
cargo install cargo-audit --locked
fi
- name: Run cargo audit
run: cargo audit
# ──────────────────────────────────────────────────────────────────
# Clippy lint per-OS + PSScriptAnalyzer on Windows.
# Per-OS (not per-arch) is required because the agent has
# cfg(target_os=...) code paths the lint must validate on each
# platform. The cfg gates are arch-independent so one target per
# OS is sufficient.
# ──────────────────────────────────────────────────────────────────
clippy:
name: "🧹 Clippy (${{ matrix.os }})"
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
env:
IS_MUSL_UBUNTU: ${{ matrix.os == 'ubuntu' }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu
runner: ubuntu-latest
target: x86_64-unknown-linux-musl
- os: windows
runner: windows-latest
target: x86_64-pc-windows-msvc
- os: macos
runner: macos-latest
target: aarch64-apple-darwin
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Install musl-tools
if: env.IS_MUSL_UBUNTU == 'true'
run: sudo apt-get update && sudo apt-get install -y musl-tools
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
components: clippy
- name: Cache
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-clippy-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
- name: Run cargo clippy
run: cargo clippy --target=${{ matrix.target }} -- -D warnings
- name: Lint PowerShell installer scripts (PSScriptAnalyzer)
if: matrix.os == 'windows'
shell: pwsh
run: ./installer/windows/Run-Lint.ps1
# ──────────────────────────────────────────────────────────────────
# Run the test suite on every (os, arch) target. This is the
# cross-platform correctness gate; `coverage` measures line coverage
# on Linux only.
# ──────────────────────────────────────────────────────────────────
tests:
name: "🧪 Tests (${{ matrix.os }}-${{ matrix.arch }})"
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
env:
IS_MUSL_UBUNTU: ${{ (matrix.os == 'ubuntu' && contains(matrix.target, 'musl')) }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu
arch: x86_64
runner: ubuntu-latest
target: x86_64-unknown-linux-musl
- os: ubuntu
arch: arm64
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-musl
- os: windows
arch: x86_64
runner: windows-latest
target: x86_64-pc-windows-msvc
- os: windows
arch: arm64
runner: windows-11-arm
target: aarch64-pc-windows-msvc
- os: macos
arch: x86_64
runner: macos-15-intel
target: x86_64-apple-darwin
- os: macos
arch: arm64
runner: macos-latest
target: aarch64-apple-darwin
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Install musl-tools
if: env.IS_MUSL_UBUNTU == 'true'
run: sudo apt-get update && sudo apt-get install -y musl-tools
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-tests-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('Cargo.lock') }}
- name: Run cargo test
run: cargo test --target=${{ matrix.target }} --locked
# ──────────────────────────────────────────────────────────────────
# Code coverage (Linux/x86_64 only). cargo-llvm-cov re-runs the
# test suite with LLVM source-based instrumentation to produce
# lcov.info, which is uploaded to Codecov. The `tests` job above
# is the cross-platform correctness gate; this job's role is
# coverage measurement.
# ──────────────────────────────────────────────────────────────────
coverage:
name: "📊 Coverage"
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- name: Cache
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-coverage-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov
- name: Generate coverage (lcov)
run: |
cargo llvm-cov --workspace --all-features --locked \
--lcov --output-path lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: lcov.info
flags: agent
disable_search: true
fail_ci_if_error: false
# ──────────────────────────────────────────────────────────────────
# Build + test the agent for every (os, arch) target, then stage
# artifacts for downstream promotion.
# ──────────────────────────────────────────────────────────────────
build:
name: "🔨 Build (${{ matrix.os }}-${{ matrix.arch }})"
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
env:
IS_MUSL_UBUNTU: ${{ (matrix.os == 'ubuntu' && contains(matrix.target, 'musl')) }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu
arch: x86_64
runner: ubuntu-latest
target: x86_64-unknown-linux-musl
jfrog_path: linux/x86_64
- os: ubuntu
arch: arm64
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-musl
jfrog_path: linux/arm64
- os: windows
arch: x86_64
runner: windows-latest
target: x86_64-pc-windows-msvc
jfrog_path: windows/x86_64
- os: windows
arch: arm64
runner: windows-11-arm
target: aarch64-pc-windows-msvc
jfrog_path: windows/arm64
- os: macos
arch: x86_64
runner: macos-15-intel
target: x86_64-apple-darwin
jfrog_path: macos/x86_64
- os: macos
arch: arm64
runner: macos-latest
target: aarch64-apple-darwin
jfrog_path: macos/arm64
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Install musl-tools
if: env.IS_MUSL_UBUNTU == 'true'
run: sudo apt-get update && sudo apt-get install -y musl-tools
- name: Install NSIS (Windows)
if: matrix.os == 'windows'
run: choco install -y nsis
shell: pwsh
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-build-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('Cargo.lock') }}
- name: Build (release)
run: |
# Static CRT linkage for Windows is configured in .cargo/config.toml
# (target-feature=+crt-static) so VCRUNTIME140.dll is not required at
# runtime.
#
# IMPORTANT: We must NOT set RUSTFLAGS here because it would *override*
# (not combine with) the target-specific rustflags in .cargo/config.toml,
# effectively dropping +crt-static. This was the root cause of the
# VCRUNTIME140.dll missing error on Windows ARM64.
cargo build --target=${{ matrix.target }} --release --locked
shell: bash
- name: Verify no dynamic CRT dependency (Windows)
if: matrix.os == 'windows'
shell: bash
run: |
BIN="./target/${{ matrix.target }}/release/openaev-agent.exe"
echo "Checking $BIN for VCRUNTIME imports…"
# rustup includes llvm-objdump; use it instead of dumpbin which
# may not be on PATH (especially on windows-11-arm runners).
if rustup run stable llvm-objdump --private-headers "$BIN" \
| grep -qi "vcruntime"; then
echo "::error::Binary still dynamically links to VCRUNTIME:"
rustup run stable llvm-objdump --private-headers "$BIN" | grep -i "vcruntime"
exit 1
fi
echo "✅ No VCRUNTIME dependency found — static CRT linkage confirmed."
- name: Build agent installers with NSIS (Windows)
if: matrix.os == 'windows'
shell: pwsh
run: |
# The .nsi files reference ..\..\target\release\openaev-agent.exe (the
# default unflavored release dir). Because we build with --target=...,
# the binary actually lives in target/<triple>/release/. Copy it to the
# legacy path so makensis can find it.
New-Item -ItemType Directory -Force -Path "./target/release" | Out-Null
Copy-Item -Force `
"./target/${{ matrix.target }}/release/openaev-agent.exe" `
"./target/release/openaev-agent.exe"
$installers = @(
"agent-installer.nsi",
"agent-installer-service-user.nsi",
"agent-installer-session-user.nsi"
)
foreach ($nsi in $installers) {
& "C:\Program Files (x86)\NSIS\Bin\makensis.exe" "./installer/windows/$nsi"
if ($LASTEXITCODE -ne 0) {
Write-Host "makensis failed for $nsi with exit code $LASTEXITCODE"
exit 1
}
}
- name: Install cargo cache
run: |
if command -v cargo-cache &>/dev/null; then
echo "cargo-cache already installed."
else
cargo install cargo-cache --locked
fi
shell: bash
- name: Run cargo cache
run: cargo cache -a -l || true
shell: bash
# ──────────────────────────────────────────────────────────────
# Stage artifacts for downstream promotion.
#
# Each matrix job uploads a flat directory layout matching the
# JFrog destination paths so the downstream workflow only needs
# to append the version suffix and PUT each file.
#
# Layout for the per-target artifact (agent-bin-<os>-<arch>):
# <jfrog_path>/openaev-agent[.exe] binary
# <jfrog_path>/openaev-agent-installer.exe (Windows only)
# <jfrog_path>/openaev-agent-installer-service-user.exe
# <jfrog_path>/openaev-agent-installer-session-user.exe
# ──────────────────────────────────────────────────────────────
- name: Stage binary + Windows installers for upload
shell: bash
run: |
set -euo pipefail
STAGE="staging/${{ matrix.jfrog_path }}"
mkdir -p "$STAGE"
if [[ "${{ matrix.os }}" == "windows" ]]; then
cp "./target/${{ matrix.target }}/release/openaev-agent.exe" \
"$STAGE/openaev-agent.exe"
for installer in agent-installer.exe \
agent-installer-service-user.exe \
agent-installer-session-user.exe; do
cp "./installer/windows/$installer" "$STAGE/openaev-$installer"
done
else
cp "./target/${{ matrix.target }}/release/openaev-agent" \
"$STAGE/openaev-agent"
fi
ls -la "$STAGE"
- name: Upload binary artifact
uses: actions/upload-artifact@v7
with:
name: agent-bin-${{ matrix.os }}-${{ matrix.arch }}
path: staging/
if-no-files-found: error
retention-days: 14
# ──────────────────────────────────────────────────────────────────
# OS-level installer / upgrade scripts (arch-independent).
# Uploaded once per OS so the downstream workflow does not need to
# deduplicate matrix outputs.
# ──────────────────────────────────────────────────────────────────
package-scripts:
name: "📦 Package installer scripts"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Stage scripts
shell: bash
run: |
set -euo pipefail
mkdir -p staging/linux staging/macos staging/windows
# Linux / macOS shell scripts
for os in linux macos; do
for script in \
agent-installer.sh \
agent-upgrade.sh \
agent-installer-session-user.sh \
agent-upgrade-session-user.sh \
agent-installer-service-user.sh \
agent-upgrade-service-user.sh; do
cp "./installer/${os}/${script}" \
"staging/${os}/openaev-${script%.sh}.sh"
done
done
# Windows PowerShell scripts
for script in \
agent-installer.ps1 \
agent-upgrade.ps1 \
agent-installer-session-user.ps1 \
agent-upgrade-session-user.ps1 \
agent-installer-service-user.ps1 \
agent-upgrade-service-user.ps1; do
cp "./installer/windows/${script}" \
"staging/windows/openaev-${script%.ps1}.ps1"
done
find staging -type f
- name: Upload scripts artifact
uses: actions/upload-artifact@v7
with:
name: agent-scripts
path: staging/
if-no-files-found: error
retention-days: 14