Skip to content

ci: drop Windows target, fix --target flag with BURRITO_TARGET env #24

ci: drop Windows target, fix --target flag with BURRITO_TARGET env

ci: drop Windows target, fix --target flag with BURRITO_TARGET env #24

Workflow file for this run

name: CI
on:
push:
branches: [main, "feature/*", "fix/*"]
tags: ["v*"]
pull_request:
branches: [main]
workflow_dispatch:
# Cancel in-progress runs for the same branch / PR
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
MIX_ENV: test
OTP_VERSION: "29.0"
ELIXIR_VERSION: "1.20.1"
jobs:
# ── Linux: full CI quality gate ─────────────────────────────────────
linux:
name: "Linux · Elixir 1.20.1 · OTP 29.0"
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{ env.OTP_VERSION }}
elixir-version: ${{ env.ELIXIR_VERSION }}
- name: Cache build artifacts
uses: actions/cache@v4
with:
path: |
_build
deps
priv/plts
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-
${{ runner.os }}-
- name: Install dependencies
run: mix deps.get
- name: Compile (warnings-as-errors)
# We intentionally don't pass --all-warnings: that flag treats
# third-party dep warnings (hpax bitstring ops, yamerl's
# deprecated catch, etc.) as errors. We only want to enforce
# warnings-as-errors on our own code, which `mix compile
# --warnings-as-errors` already does.
run: mix compile --warnings-as-errors
- name: Check formatting
run: mix format --check-formatted
- name: Credo (strict)
run: mix credo --strict
- name: Audit dependencies for CVEs
run: mix deps.audit
- name: Check for unused dependencies
run: mix deps.unlock --check-unused
- name: xref (no orphan modules)
run: mix xref graph --label compile-connected --fail-above 0
- name: Dialyzer
run: mix ci.dialyzer
timeout-minutes: 15
- name: Run tests with coverage
# mix test --cover runs the tests once and produces both
# the Elixir coverage report (cover/*.html) and the
# excoveralls JSON (cover/excoveralls.json via tool: ExCoveralls).
# The strict threshold check is disabled (threshold: 0 in
# mix.exs) until CLI integration tests are added.
run: mix test --cover
- name: Post coverage to Codecov
# Push the JSON to codecov.io using their bash uploader. This
# is the path recommended by excoveralls for Codecov.
#
# Requires CODECOV_TOKEN in repo Settings -> Secrets -> Actions.
# Get the token at https://codecov.io/gh/gilbertwong96/ado_cli
# -> Settings -> Upload Token. The bash uploader reads it from
# the CODECOV_TOKEN env var.
#
# Skip the step if the secret is not set. We don't fail the
# build over missing coverage uploads — coverage is still
# available as an action artifact download.
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: |
if [ -z "$CODECOV_TOKEN" ]; then
echo "::notice::CODECOV_TOKEN not set — skipping Codecov upload."
echo "Add it at https://codecov.io/gh/gilbertwong96/ado_cli"
echo "to enable the coverage badge."
exit 0
fi
curl -Os https://uploader.codecov.io/latest/linux/codecov
chmod +x codecov
./codecov --token "$CODECOV_TOKEN" --file ./cover/excoveralls.json
continue-on-error: true
- name: Upload coverage artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-linux
path: cover/excoveralls.json
retention-days: 30
# ── macOS: smoke test (the CLI is cross-compiled via Burrito) ──────
macos:
name: "macOS · Elixir 1.20.1"
runs-on: macos-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{ env.OTP_VERSION }}
elixir-version: ${{ env.ELIXIR_VERSION }}
- name: Cache build artifacts
uses: actions/cache@v4
with:
path: |
_build
deps
priv/plts
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-
${{ runner.os }}-
- name: Install dependencies
run: mix deps.get
- name: Build escript
run: mix escript.build
- name: "Smoke test: --help works"
run: "./ado --help"
- name: "Smoke test: whoami with no auth returns friendly error"
run: |
rm -f "$HOME/.ado_cli/config.json"
./ado whoami || true
- name: "Smoke test: logout is idempotent"
run: ./ado logout
- name: Run tests
run: mix test
# ── Summary job — gate merge on all green ───────────────────────────
ci-status:
name: CI Status
if: always()
needs: [linux, macos]
runs-on: ubuntu-latest
steps:
- name: Fail if any matrix job failed
if: needs.linux.result != 'success' || needs.macos.result != 'success'
run: |
echo "Linux result: ${{ needs.linux.result }}"
echo "macOS result: ${{ needs.macos.result }}"
exit 1
# ── Release build: cross-compile via Burrito, rename, upload artifacts ─
release:
name: "Release · Burrito · ${{ matrix.label }}"
# Only build releases on pushes to main (not on PRs) and on tag pushes.
# Use workflow_dispatch to trigger manually for ad-hoc builds.
if: |
github.event_name == 'push' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))
needs: [ci-status]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
# Don't cancel other platforms if one fails — we want all
# binaries even if e.g. macOS signing fails.
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: linux
cpu: x86_64
ext: ""
label: "Linux x86_64"
artifact: ado-linux-x86_64
- os: ubuntu-latest
target: linux_arm
cpu: aarch64
ext: ""
label: "Linux ARM64"
artifact: ado-linux-aarch64
- os: macos-latest
target: macos
cpu: aarch64
ext: ""
label: "macOS Apple Silicon"
artifact: ado-macos-aarch64
- os: macos-latest
target: macos_x86
cpu: x86_64
ext: ""
label: "macOS Intel"
artifact: ado-macos-x86_64
steps:
- uses: actions/checkout@v4
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{ env.OTP_VERSION }}
elixir-version: ${{ env.ELIXIR_VERSION }}
- name: Cache build artifacts
uses: actions/cache@v4
with:
path: |
_build
deps
priv/plts
key: release-${{ runner.os }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
release-${{ runner.os }}-
- name: Install dependencies
run: mix deps.get
- name: Build Burrito release
env:
MIX_ENV: prod
# Burrito accepts BURRITO_TARGET=<target-key> to build only
# that target from the targets list in mix.exs. This avoids
# cross-compiling for all 4 platforms on each runner.
BURRITO_TARGET: ${{ matrix.target }}
# Burrito writes the binary to burrito_out/ado_<target> by
# convention. We rename the output below to a stable,
# versioned name.
run: |
mkdir -p release_bin
mix release --overwrite
ls -lh burrito_out/
- name: Get version
id: version
run: |
# Read the version from mix.exs so the release artifact name
# always matches the source of truth.
VERSION=$(grep -E '^\s*version:\s*"' mix.exs | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Rename binary
# Normalize the binary name to:
# ado-<version>-<os>-<arch><.exe?>
# e.g. ado-0.1.0-macos-aarch64
# ado-0.1.0-linux-x86_64
run: |
set -euo pipefail
VERSION="${{ steps.version.outputs.version }}"
# Map matrix.target to a clean {os}-{arch} string
case "${{ matrix.target }}" in
linux) SUFFIX="linux-x86_64" ;;
linux_arm) SUFFIX="linux-aarch64" ;;
macos) SUFFIX="macos-aarch64" ;;
macos_x86) SUFFIX="macos-x86_64" ;;
*) echo "::error::Unknown target ${{ matrix.target }}"; exit 1 ;;
esac
OUT_NAME="ado-${VERSION}-${SUFFIX}${{ matrix.ext }}"
SRC="burrito_out/ado_${{ matrix.target }}${{ matrix.ext }}"
DEST="release_bin/${OUT_NAME}"
if [[ ! -f "${SRC}" ]]; then
echo "::error::Expected Burrito output not found: ${SRC}"
ls -lh burrito_out/
exit 1
fi
mv "${SRC}" "${DEST}"
chmod +x "${DEST}"
echo "Renamed ${SRC} -> ${DEST}"
ls -lh release_bin/
- name: Smoke test the binary
run: |
set -euo pipefail
# Glob is intentional here — there's exactly one file in
# the dir, but actionlint warns about unquoted globs.
BINARY=$(ls release_bin/ado-*)
echo "Testing $BINARY"
"$BINARY" --version || "$BINARY" --help | head -20
- name: Upload release binary
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: release_bin/ado-*
retention-days: 90
if-no-files-found: error
# ── Attach binaries to GitHub Release on tag push ──────────────────
release-attach:
name: Publish GitHub Release
if: startsWith(github.ref, 'refs/tags/')
needs: [release]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: dist/
merge-multiple: true
- name: Display downloaded artifacts
run: ls -lh dist/
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: "AdoCli ${{ github.ref_name }}"
body: |
## AdoCli ${{ github.ref_name }}
Pre-built binaries for all supported platforms.
| Platform | Architecture | Binary |
|----------|--------------|--------|
| Linux | x86_64 | `ado-*-linux-x86_64` |
| Linux | aarch64 | `ado-*-linux-aarch64` |
| macOS | Apple Silicon | `ado-*-macos-aarch64` |
| macOS | Intel | `ado-*-macos-x86_64` |
See [README.md](https://github.com/gilbertwong96/ado_cli) for
installation instructions.
draft: false
prerelease: false
files: |
dist/ado-*