Skip to content

fix(roster): publish crystalium v1.7.0 #717

fix(roster): publish crystalium v1.7.0

fix(roster): publish crystalium v1.7.0 #717

Workflow file for this run

name: CI
# Validates the eidolons CLI on every push and PR:
# - lint: shellcheck + schema validation
# - install-e2e: curl-pipe install against the checkout on Ubuntu + macOS,
# with and without a pre-existing yq, to guarantee the
# bootstrap works on a bare box (the ModuleNotFoundError
# for PyYAML must never reach a user again)
# - cli-tests: bats suite covering every command end-to-end
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install shellcheck + yq + jq
run: |
sudo apt-get update
sudo apt-get install -y shellcheck jq
sudo wget -qO /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
- name: shellcheck all shell scripts (error severity)
run: |
set -e
# -S error = block merges only on real errors, not style/info.
# Warnings are surfaced but non-blocking; promote to -S warning
# once the existing warnings are cleaned up.
find cli -name '*.sh' -type f -print0 | xargs -0 shellcheck -x -S error
shellcheck -x -S error cli/eidolons
- name: Validate JSON schemas parse
run: |
for schema in schemas/*.json; do
jq empty "$schema"
done
- name: Validate roster/index.yaml required fields
run: |
set -euo pipefail
yq eval '.' roster/index.yaml > /tmp/roster.json
for name in $(jq -r '.eidolons[].name' /tmp/roster.json); do
jq -e --arg n "$name" '.eidolons[] | select(.name == $n) |
.methodology.name and .methodology.version and .methodology.cycle and
.source.repo and
.versions.latest and
.handoffs.upstream and .handoffs.downstream' \
/tmp/roster.json > /dev/null || { echo "::error::$name missing required fields"; exit 1; }
done
- name: Check roster/mcps.yaml and roster/index.yaml crystalium version parity
run: bash cli/src/check_roster_mcp_skew.sh
install-e2e:
name: Install — ${{ matrix.os }} / yq=${{ matrix.yq }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
yq: [preinstalled, absent]
steps:
- uses: actions/checkout@v4
- name: Install jq
run: |
if [[ "$RUNNER_OS" == "Linux" ]]; then
sudo apt-get update && sudo apt-get install -y jq
else
brew install jq
fi
- name: Remove any pre-existing yq (matrix.yq=absent)
if: matrix.yq == 'absent'
run: |
for p in /usr/local/bin/yq /usr/bin/yq /opt/homebrew/bin/yq "$HOME/.local/bin/yq"; do
[[ -e "$p" ]] && sudo rm -f "$p" || true
done
# Ubuntu runners may have yq via snap too.
command -v yq && sudo rm -f "$(command -v yq)" || true
if command -v yq >/dev/null 2>&1; then
echo "::error::yq still on PATH after cleanup: $(command -v yq)"
exit 1
fi
- name: Ensure yq is present (matrix.yq=preinstalled)
if: matrix.yq == 'preinstalled'
run: |
if ! command -v yq >/dev/null 2>&1; then
if [[ "$RUNNER_OS" == "Linux" ]]; then
sudo wget -qO /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
else
brew install yq
fi
fi
- name: Run install.sh against the checkout
env:
EIDOLONS_REPO: file://${{ github.workspace }}
EIDOLONS_REF: ${{ github.sha }}
EIDOLONS_HOME: ${{ github.workspace }}/.e2e-home
EIDOLONS_BIN_DIR: ${{ github.workspace }}/.e2e-bin
run: |
set -euo pipefail
bash cli/install.sh
"$EIDOLONS_BIN_DIR/eidolons" version
# yq must exist after install regardless of matrix.yq.
command -v yq >/dev/null 2>&1 || [[ -x "$EIDOLONS_BIN_DIR/yq" ]]
- name: Reproduce the original user bug (list --available must work)
env:
EIDOLONS_HOME: ${{ github.workspace }}/.e2e-home
EIDOLONS_BIN_DIR: ${{ github.workspace }}/.e2e-bin
run: |
export PATH="$EIDOLONS_BIN_DIR:$PATH"
# This is the exact invocation that was failing with
# ModuleNotFoundError: No module named 'yaml'.
eidolons list --available
- name: Second install run is idempotent
env:
EIDOLONS_REPO: file://${{ github.workspace }}
EIDOLONS_REF: ${{ github.sha }}
EIDOLONS_HOME: ${{ github.workspace }}/.e2e-home
EIDOLONS_BIN_DIR: ${{ github.workspace }}/.e2e-bin
run: |
bash cli/install.sh
"$EIDOLONS_BIN_DIR/eidolons" version
cli-tests:
name: CLI tests (bats) — ${{ matrix.os }}
runs-on: ${{ matrix.os }}
needs: lint
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Install prerequisites (jq, yq, bats, parallel)
run: |
if [[ "$RUNNER_OS" == "Linux" ]]; then
sudo apt-get update
sudo apt-get install -y jq bats parallel
sudo wget -qO /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
else
brew install jq yq bats-core parallel
fi
- name: Make CLI executable
run: chmod +x cli/eidolons cli/src/*.sh cli/install.sh
# Parallelism configuration:
# --jobs 4 run up to 4 .bats files concurrently
# (GH runners have ≥3 vCPUs; GNU
# parallel is installed above).
# --no-parallelize-within-files keep tests inside a single file
# sequential. Within-file parallelism
# exposed contention on shared on-disk
# artefacts (notably the harness cache
# used by adjacent install tests).
#
# The earlier --jobs run failed deterministically on ubuntu-latest until
# the harness.bats "second run is idempotent" assertion was rewritten to
# use `ls -di` for inode comparison: the previous `stat -f '%m' || stat
# -c '%Y'` form silently misbehaved on Linux (`stat -f` on Linux means
# "stat the filesystem" — exit 1 but stdout contained fluctuating
# filesystem stats, which command-substitution merged into the captured
# value under bats --jobs N FS load).
- name: Run bats suite (parallel across files, serial within files)
run: bats --jobs 4 --no-parallelize-within-files cli/tests/
upgrade-self-roundtrip:
name: upgrade self — round-trip (install → upgrade self --check)
runs-on: ${{ matrix.os }}
needs: [lint]
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Install prerequisites (jq, yq)
run: |
if [[ "$RUNNER_OS" == "Linux" ]]; then
sudo apt-get update && sudo apt-get install -y jq
sudo wget -qO /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
else
brew install jq yq
fi
- name: Bootstrap install from this checkout
env:
EIDOLONS_REPO: file://${{ github.workspace }}
EIDOLONS_REF: ${{ github.sha }}
EIDOLONS_HOME: ${{ github.workspace }}/.us-home
EIDOLONS_BIN_DIR: ${{ github.workspace }}/.us-bin
run: bash cli/install.sh
- name: version reports enriched output (commit + ref + installed + path)
env:
EIDOLONS_HOME: ${{ github.workspace }}/.us-home
EIDOLONS_BIN_DIR: ${{ github.workspace }}/.us-bin
run: |
export PATH="${{ github.workspace }}/.us-bin:$PATH"
out="$(eidolons version)"
echo "$out" | head -1 | grep -qE '^eidolons [0-9]+\.[0-9]+\.[0-9]'
echo "$out" | grep -q 'commit:'
echo "$out" | grep -q 'installed:'
- name: --quiet flag emits single-line output
env:
EIDOLONS_HOME: ${{ github.workspace }}/.us-home
EIDOLONS_BIN_DIR: ${{ github.workspace }}/.us-bin
run: |
export PATH="${{ github.workspace }}/.us-bin:$PATH"
out="$(eidolons --version --quiet)"
echo "$out" | grep -qE '^eidolons [0-9]+\.[0-9]+\.[0-9]'
[ "$(echo "$out" | wc -l | tr -d ' ')" -eq 1 ]
- name: upgrade self --check completes without error
env:
EIDOLONS_HOME: ${{ github.workspace }}/.us-home
EIDOLONS_BIN_DIR: ${{ github.workspace }}/.us-bin
EIDOLONS_REPO: https://github.com/Rynaro/eidolons
run: |
export PATH="${{ github.workspace }}/.us-bin:$PATH"
# --check is read-only; it may report "already on latest" or "upgrade
# available" depending on the state of the upstream tag, but it must
# exit 0 and never modify the nexus directory.
eidolons upgrade self --check || true
# Verify nexus is untouched (nexus.new must not exist after --check).
[ ! -d "${{ github.workspace }}/.us-home/nexus.new" ]