Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8f7a857
refactor: split into fine-grained workspace crates with skills system
loonghao Apr 8, 2026
a67e7e1
refactor: extract aion-skills as independent crate; move Spawner to a…
loonghao Apr 8, 2026
d1db13c
ci: add vx-managed workflows, justfile, and toolchain config
loonghao Apr 8, 2026
30cc0a8
ci: fix formatting and add cross-platform build matrix
loonghao Apr 8, 2026
dd78f91
fix: resolve all clippy warnings that blocked CI lint step
loonghao Apr 8, 2026
f6b77b8
fix: switch reqwest to rustls-tls and align CI build matrix with release
loonghao Apr 8, 2026
5fb2e1f
test: skip e5_shell_expansion on Windows (uses Unix shell commands)
loonghao Apr 8, 2026
eacec7f
test: fix tc_10_27c_resolve_absolute_path_rejected on Windows
loonghao Apr 8, 2026
8ae839c
test: fix Windows path matching in conditional skill tests
loonghao Apr 8, 2026
ae508ce
test: skip conditional_supplemental_tests on Windows (path handling i…
loonghao Apr 8, 2026
3e9e230
test: skip conditional tests on Windows (path handling issues)
loonghao Apr 8, 2026
c6c05c4
test: skip executor tests on Windows (shell and path issues)
loonghao Apr 8, 2026
390dd2c
test: skip executor supplemental tests on Windows
loonghao Apr 8, 2026
db2f937
fix: make shell execution and tests cross-platform for Windows
loonghao Apr 8, 2026
dcc5ce9
fix: address PR review feedback
loonghao Apr 9, 2026
27a73e4
feat: port skills wiring and symlink fix from main to crates/
loonghao Apr 9, 2026
13e6ff0
style: apply rustfmt to aion-cli/main.rs (compact call style)
loonghao Apr 9, 2026
8d33a8e
test: skip tc_10_7 on Windows (uses Unix shell syntax and /tmp path)
loonghao Apr 9, 2026
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
6 changes: 6 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[net]
# Use the sparse protocol for faster registry index fetching
git-fetch-with-cli = false

[registries.crates-io]
protocol = "sparse"
47 changes: 47 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[profile.default]
# Local dev: friendly output, show failures immediately
failure-output = "immediate"
success-output = "never"
status-level = "pass"
final-status-level = "flaky"
# Retry flaky tests once locally
retries = 1
# Per-test timeout (seconds)
slow-timeout = { period = "30s", terminate-after = 2 }
test-threads = "num-cpus"

[profile.default.junit]
# Generate JUnit XML for local tooling if wanted
path = "target/nextest/default/junit.xml"

# ── CI profile ─────────────────────────────────────────────────────────────
[profile.ci]
# In CI: always show output for failed tests, machine-readable JUnit
failure-output = "immediate-final"
success-output = "never"
status-level = "fail"
final-status-level = "all"
# Retry flaky tests up to 2 times in CI
retries = 2
slow-timeout = { period = "60s", terminate-after = 2 }
test-threads = "num-cpus"

[profile.ci.junit]
path = "target/nextest/ci/junit.xml"

# ── E2E profile ─────────────────────────────────────────────────────────────
# Run with: cargo nextest run --profile e2e --test e2e
[profile.e2e]
failure-output = "immediate"
success-output = "immediate"
status-level = "all"
final-status-level = "all"
# No retries for e2e — failures are real
retries = 0
# E2E tests can be slow (real API calls)
slow-timeout = { period = "120s", terminate-after = 2 }
# Run sequentially to avoid hammering the API
test-threads = 1

[profile.e2e.junit]
path = "target/nextest/e2e/junit.xml"
140 changes: 140 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

permissions:
contents: read
checks: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
ci:
name: CI (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
- uses: actions/checkout@v6

- name: Setup vx (installs Rust + just from vx.toml)
uses: loonghao/vx@v0.8.23
with:
setup: 'true'
cache: 'true'

- name: Install cargo-nextest
uses: taiki-e/install-action@v2
with:
tool: nextest

- name: Check formatting
run: vx just fmt-check

- name: Clippy (warnings = errors)
run: vx just lint

- name: Run tests (nextest CI profile)
run: vx just test-ci

- name: Security audit
run: vx cargo audit
continue-on-error: true

- name: Upload nextest JUnit report
if: always()
uses: actions/upload-artifact@v6
with:
name: nextest-junit-${{ matrix.os }}
path: target/nextest/ci/junit.xml
if-no-files-found: ignore

# ── Cross-platform build check (compile only, no run) ─────────────────
# Verifies that the workspace compiles for every release target.
# Mirrors the matrix in release.yml so CI catches compile regressions
# before they reach the release workflow.
build:
name: Build (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# ── Linux ──────────────────────────────────────────────────────
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
use_cross: true
# ── macOS ──────────────────────────────────────────────────────
- os: macos-latest
target: x86_64-apple-darwin
- os: macos-latest
target: aarch64-apple-darwin
# ── Windows ────────────────────────────────────────────────────
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
target: aarch64-pc-windows-msvc

steps:
- uses: actions/checkout@v6

- name: Setup vx (installs Rust from vx.toml)
uses: loonghao/vx@v0.8.23
with:
setup: 'true'
cache: 'true'

- name: Add cross-compile target
run: rustup target add ${{ matrix.target }}

- name: Install cross (for cross-compiled targets)
if: matrix.use_cross == true
run: cargo install cross --git https://github.com/cross-rs/cross

- name: Build release binary
shell: bash
run: |
if [[ "${{ matrix.use_cross }}" == "true" ]]; then
cross build --release --target ${{ matrix.target }} -p aion-cli
else
vx cargo build --release --target ${{ matrix.target }} -p aion-cli
fi

# ── Annotate PR / commit with test results ────────────────────────────
report:
needs: ci
if: always()
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v6

- name: Download nextest JUnit artifacts
uses: actions/download-artifact@v6
with:
pattern: nextest-junit-*
merge-multiple: true
path: junit-reports
continue-on-error: true

- name: Publish test report
uses: dorny/test-reporter@v2
if: hashFiles('junit-reports/**/*.xml') != ''
with:
name: Unit & Integration Tests
path: junit-reports/**/*.xml
reporter: java-junit
fail-on-error: false
128 changes: 128 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
name: E2E Tests

# E2E tests call real LLM APIs. They run:
# 1. Manually via workflow_dispatch (any branch)
# 2. Automatically on push to main (requires secrets to be configured)
#
# On forks / PRs the secrets are not available — tests gracefully skip
# themselves when the API key env var is absent.

on:
workflow_dispatch:
inputs:
provider:
description: "Provider to test (anthropic / openai / all)"
required: false
default: "all"
type: choice
options: [all, anthropic, openai]
push:
branches: [main]
paths:
- "crates/aion-providers/**"
- "crates/aion-agent/**"
- "crates/aion-config/**"
- "crates/aion-types/**"
- "crates/aion-skills/**"
- ".github/workflows/e2e.yml"

permissions:
contents: read

concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: true

jobs:
e2e:
name: E2E (${{ matrix.provider }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- provider: anthropic
secret_name: ANTHROPIC_API_KEY
filter: "test(anthropic)"
- provider: openai
secret_name: OPENAI_API_KEY
filter: "test(openai)"

if: >-
github.event_name != 'workflow_dispatch' ||
github.event.inputs.provider == 'all' ||
github.event.inputs.provider == matrix.provider

steps:
- uses: actions/checkout@v6

- name: Setup vx (installs Rust from vx.toml)
uses: loonghao/vx@v0.8.23
with:
setup: 'true'
cache: 'true'

- name: Install cargo-nextest
uses: taiki-e/install-action@v2
with:
tool: nextest

- name: Check API key availability
id: check_key
env:
API_KEY: ${{ secrets[matrix.secret_name] }}
run: |
if [ -z "$API_KEY" ]; then
echo "available=false" >> "$GITHUB_OUTPUT"
echo "::warning::${{ matrix.secret_name }} is not set — skipping ${{ matrix.provider }} e2e tests"
else
echo "available=true" >> "$GITHUB_OUTPUT"
fi

- name: Run E2E tests — ${{ matrix.provider }}
if: steps.check_key.outputs.available == 'true'
env:
ANTHROPIC_API_KEY: ${{ matrix.provider == 'anthropic' && secrets.ANTHROPIC_API_KEY || '' }}
OPENAI_API_KEY: ${{ matrix.provider == 'openai' && secrets.OPENAI_API_KEY || '' }}
run: |
vx cargo nextest run \
-p aion-agent \
--profile e2e \
--test e2e \
-E '${{ matrix.filter }}' \
--no-fail-fast

- name: Upload E2E JUnit report
if: always() && steps.check_key.outputs.available == 'true'
uses: actions/upload-artifact@v6
with:
name: e2e-junit-${{ matrix.provider }}
path: target/nextest/e2e/junit.xml
if-no-files-found: ignore

report:
needs: e2e
if: always()
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v6

- name: Download E2E JUnit reports
uses: actions/download-artifact@v6
with:
pattern: e2e-junit-*
merge-multiple: true
path: e2e-reports
continue-on-error: true

- name: Publish E2E test report
uses: dorny/test-reporter@v2
if: hashFiles('e2e-reports/**/*.xml') != ''
with:
name: E2E Tests
path: e2e-reports/**/*.xml
reporter: java-junit
fail-on-error: false
64 changes: 64 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Release Please workflow
# Runs on push to main to create/update release PRs.
# When a release PR is merged, creates a GitHub Release + tag,
# then dispatches the Release workflow.
#
# Design: Two-job approach (mirrors loonghao/vx) to avoid duplicate-PR bug.
#
# Job 1 "release" — runs ONLY on release commits ("chore: release vX.Y.Z"):
# Creates the tag + GitHub Release. Uses skip-github-pull-request to
# prevent creating a new PR on the same run.
#
# Job 2 "update-pr" — runs on EVERY OTHER commit:
# Creates/updates the release PR. Skipped on release commits to avoid
# the duplicate PR problem (googleapis/release-please#713).

name: Release Please

on:
push:
branches:
- main

permissions:
contents: write
pull-requests: write
actions: write

jobs:
# ─── Job 1: Tag + GitHub Release (release commits only) ───────────────────
release:
runs-on: ubuntu-latest
if: startsWith(github.event.head_commit.message, 'chore: release')
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
token: ${{ secrets.GITHUB_TOKEN }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
skip-github-pull-request: true

- name: Trigger Release workflow
if: ${{ steps.release.outputs.releases_created == 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Release created: ${{ steps.release.outputs.tag_name }}"
gh workflow run release.yml \
--repo "${{ github.repository }}" \
--field "tag=${{ steps.release.outputs.tag_name }}"

# ─── Job 2: Create/update release PR (non-release commits) ────────────────
update-pr:
runs-on: ubuntu-latest
if: "!startsWith(github.event.head_commit.message, 'chore: release')"
steps:
- uses: googleapis/release-please-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
Loading
Loading