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
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
rustflags = ["--cfg", "tokio_unstable"]
17 changes: 17 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# EditorConfig is awesome: https://editorconfig.org
root = true

# The Rust workspace: agentctl, the Sandbox crates and the root build files.
[{agentctl/**,sandbox/**,Makefile,*.toml,.cargo/**}]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.rs]
indent_style = space
indent_size = 4
tab_width = 4

[Makefile]
indent_style = tab
16 changes: 16 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,19 @@ scripts/ @olebhansen
# review-gaten der er deres, ikke orkestrator-pipelinens
agentctl/ @martinothamar
sandbox/ @martinothamar

# Rust-arbeidsområdet på rot og workflowene til agentctl/sandbox. Står etter
# .github/ slik at review av disse ikke krever orkestrator-pipelinens eier, og
# omvendt.
/Cargo.toml @martinothamar
/Cargo.lock @martinothamar
/rust-toolchain.toml @martinothamar
/rustfmt.toml @martinothamar
/clippy.toml @martinothamar
/deny.toml @martinothamar
/Makefile @martinothamar
/.cargo/ @martinothamar
/.editorconfig @martinothamar
/.github/workflows/rust.yml @martinothamar
/.github/workflows/agentctl-release.yml @martinothamar
/.github/workflows/agentctl-changelog.yml @martinothamar
48 changes: 48 additions & 0 deletions .github/workflows/agentctl-changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: agentctl changelog entry

# Requires pull requests that change agentctl or the Sandbox crates to add an entry to agentctl/CHANGELOG.md under
# [Unreleased], using agentctl/changelog.sh. Apply the 'skip-changelog' label to pull requests with no user-visible
# change (refactors, test-only or CI-only work) or a change an Unreleased entry already describes with its issue
# linked; the labeled/unlabeled triggers re-evaluate the check when the label is toggled.
# Markdown-only changes do not trigger the check, except the changelog itself, so that a pull request editing only the
# changelog is still validated.

on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review, labeled, unlabeled]
paths:
- 'agentctl/**'
- 'sandbox/**'
- '.github/workflows/agentctl-changelog.yml'
- '!agentctl/**/*.md'
- '!sandbox/**/*.md'
- 'agentctl/CHANGELOG.md'

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref || github.run_id }}
cancel-in-progress: true

permissions:
contents: read

jobs:
require-changelog-entry:
name: Require agentctl changelog entry
if: ${{ !github.event.pull_request.draft && !contains(github.event.pull_request.labels.*.name, 'skip-changelog') }}
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false

- name: Validate changelog structure
run: agentctl/changelog.sh validate

- name: Require an Unreleased entry
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: agentctl/changelog.sh check-unreleased "$BASE_SHA" "$HEAD_SHA"
171 changes: 171 additions & 0 deletions .github/workflows/agentctl-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Publishes an agentctl release for an `agentctl/v<version>` tag: the archives for every host, the installers, the
# license and third-party notices, with the version's CHANGELOG.md section as the release notes.
name: agentctl release

on:
push:
tags:
- 'agentctl/v*'

permissions:
contents: read

jobs:
build:
name: Build ${{ matrix.archive }}
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
archive: agent-linux-x86_64
- runner: ubuntu-24.04-arm
archive: agent-linux-aarch64
- runner: macos-15
archive: agent-macos-aarch64
- runner: windows-2025
archive: agent-windows-x86_64
- runner: windows-11-arm
archive: agent-windows-aarch64
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
defaults:
run:
shell: bash
steps:
- name: Enable long paths for git on Windows
if: runner.os == 'Windows'
run: git config --system core.longpaths true

- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Install native build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends libcap-ng-dev

- name: Install Rust toolchain
run: |
RUST_VERSION="$(sed -n 's/^channel = "\([^"]*\)"$/\1/p' rust-toolchain.toml)"
test -n "${RUST_VERSION}"
rustup toolchain install "${RUST_VERSION}" --profile minimal --component clippy,rustfmt
rustup default "${RUST_VERSION}"

- name: Build release binaries
run: |
AGENT_VERSION="${GITHUB_REF_NAME#agentctl/}" cargo build --release --locked -p agent --bins

- name: Package release binaries
env:
ARCHIVE: ${{ matrix.archive }}
run: |
agentctl/package.sh "dist/${ARCHIVE}.tar.gz" target/release

- name: Smoke-test managed and standalone installation
env:
ARCHIVE: ${{ matrix.archive }}
AGENT_RELEASE_TAG: ${{ github.ref_name }}
run: |
VERSION="${AGENT_RELEASE_TAG#agentctl/}"
export VERSION AGENT_VERSION="${VERSION}"
SMOKE_ROOT="${RUNNER_TEMP}/agent-install-smoke"
ARCHIVE_PATH="${GITHUB_WORKSPACE}/dist/${ARCHIVE}.tar.gz"
if [ "${RUNNER_OS}" = "Windows" ]; then
export AGENT_INSTALL_ROOT="$(cygpath -w "${SMOKE_ROOT}/install")"
export AGENT_INSTALL_DIR="$(cygpath -w "${SMOKE_ROOT}/bin")"
export AGENT_HOME="$(cygpath -w "${SMOKE_ROOT}/home")"
export AGENT_LOCAL_ARCHIVE="$(cygpath -w "${ARCHIVE_PATH}")"
pwsh -NoProfile -File "$(cygpath -w agentctl/install.ps1)"
export AGENT_SMOKE_BIN="${AGENT_INSTALL_DIR}"
unset AGENT_INSTALL_ROOT AGENT_INSTALL_DIR
pwsh -NoProfile -Command '& (Join-Path $env:AGENT_SMOKE_BIN "agentctl.cmd") --home $env:AGENT_HOME self update --version $env:VERSION'
export AGENT_INSTALL_MODE=standalone
export AGENT_INSTALL_DIR="$(cygpath -w "${SMOKE_ROOT}/standalone")"
pwsh -NoProfile -File "$(cygpath -w agentctl/install.ps1)"
pwsh -NoProfile -Command '& (Join-Path $env:AGENT_INSTALL_DIR "agentctl.exe") --version'
else
export AGENT_INSTALL_ROOT="${SMOKE_ROOT}/install"
export AGENT_INSTALL_DIR="${SMOKE_ROOT}/bin"
export AGENT_HOME="${SMOKE_ROOT}/home"
export AGENT_LOCAL_ARCHIVE="${ARCHIVE_PATH}"
sh agentctl/install.sh
AGENT_SMOKE_BIN="${AGENT_INSTALL_DIR}"
unset AGENT_INSTALL_ROOT AGENT_INSTALL_DIR
"${AGENT_SMOKE_BIN}/agentctl" --home "${AGENT_HOME}" self update --version "${VERSION}"
AGENT_INSTALL_MODE=standalone AGENT_INSTALL_DIR="${SMOKE_ROOT}/standalone" \
sh agentctl/install.sh
"${SMOKE_ROOT}/standalone/agentctl" --version
fi

- name: Upload release artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: ${{ matrix.archive }}
path: dist/agent-*
if-no-files-found: error

release:
name: Publish GitHub release
needs: build
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Build release notes from the changelog
env:
TAG: ${{ github.ref_name }}
run: |
agentctl/changelog.sh validate
agentctl/changelog.sh extract "${TAG#agentctl/}" > release-notes.md
cat release-notes.md

- name: Download binaries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: dist
merge-multiple: true

- name: Add installers
run: |
cp agentctl/install.sh agentctl/install.ps1 dist/

- name: Install Rust toolchain
run: |
RUST_VERSION="$(sed -n 's/^channel = "\([^"]*\)"$/\1/p' rust-toolchain.toml)"
test -n "${RUST_VERSION}"
rustup toolchain install "${RUST_VERSION}" --profile minimal
rustup default "${RUST_VERSION}"

- name: Install cargo-about
run: cargo install cargo-about --locked --features cli --version 0.9.2

# The archives hold only the two binaries the installers and `agentctl self update` accept, so the license and
# notices are separate assets.
- name: Add license and third-party notices
run: |
cp LICENSE dist/LICENSE
agentctl/notices/third-party-notices.sh dist/THIRD_PARTY_NOTICES.md

# Immutable releases lock their assets on publication, so upload every asset to a draft first and publish it
# last. A draft left by a failed run is not immutable and is replaced.
- name: Publish release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
run: |
if [ "$(gh release view "${TAG}" --json isDraft --jq .isDraft 2>/dev/null)" = true ]; then
gh release delete "${TAG}" --yes
fi
gh release create "${TAG}" --draft --prerelease --latest=false --verify-tag \
--title "agentctl ${TAG#agentctl/}" --notes-file release-notes.md
gh release upload "${TAG}" dist/*
gh release edit "${TAG}" --draft=false
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
pull-requests: read
outputs:
integrations: ${{ steps.areas.outputs.integrations }}
rust: ${{ steps.areas.outputs.rust }}
steps:
- name: Detect changed areas
id: areas
Expand All @@ -40,6 +41,7 @@ jobs:
# Each area's paths, as an extended regular expression over the changed file names.
areas=(
'integrations=^(integrations/|\.github/workflows/(ci|integrations)\.yml$)'
'rust=^(agentctl/|sandbox/|\.cargo/|(Cargo\.toml|Cargo\.lock|rust-toolchain\.toml|rustfmt\.toml|clippy\.toml|deny\.toml|Makefile)$|\.github/workflows/(ci|rust)\.yml$)'
)
# A renamed file counts under both its old and its new name.
names='.filename, (.previous_filename // empty)'
Expand All @@ -65,10 +67,15 @@ jobs:
if: needs.changes.outputs.integrations == 'true'
uses: ./.github/workflows/integrations.yml

rust:
needs: changes
if: needs.changes.outputs.rust == 'true'
uses: ./.github/workflows/rust.yml

ci-gate:
name: ci-gate
if: always()
needs: [changes, integrations]
needs: [changes, integrations, rust]
runs-on: ubuntu-latest
steps:
- name: Check that no needed job failed
Expand Down
Loading
Loading