Skip to content

chore: remove stray committed files and extend .gitignore #283

chore: remove stray committed files and extend .gitignore

chore: remove stray committed files and extend .gitignore #283

name: Cardano Ecosystem CI
# Thin orchestrator.
#
# Most of the work — installing toolchains, the matrix shape, the
# upload/download artifact pattern — lives in reusable workflows under
# .github/workflows/_*.yml and composite actions under .github/actions/.
#
# Off-chain tests run in a cross-product: each off-chain framework is tested
# against every on-chain framework whose example set it shares. The discover job
# computes that cross-product as `offchain-matrix` (in local-test-discovery.sh),
# and a single matrix-driven `test-offchain` job runs every combo through the
# runtime-generic reusable workflow `_test-offchain.yml`.
# Artifact and log naming: plutus-<onchain>-<example> / logs-<prefix>-<onchain>-<example>
# so the dashboard generator can correlate them automatically.
#
# Adding a new OFF-CHAIN framework:
# 1. Add it to frameworks.json (kind: offchain, with runtime/subdir/entryGlob).
# It flows into `offchain-matrix` automatically — no YAML changes here.
#
# Adding a new ON-CHAIN framework:
# 1. Add it to frameworks.json (kind: onchain).
# 2. Add a compile-<id> job below pointing at the right reusable compile workflow,
# and add it to test-offchain's `needs` + the report-and-dashboard `needs`.
# (Off-chain combos against it appear in `offchain-matrix` automatically.)
on:
push:
branches:
- main
pull_request:
schedule:
- cron: '0 2 * * *' # 2am UTC daily
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
AIKEN_VERSION: v1.1.21
SCALUS_JAVA_VERSION: '21'
CACHE_VERSION: v1
jobs:
# ───────────────────────────────────────────────────────────────────────────
# Discovery
# ───────────────────────────────────────────────────────────────────────────
discover:
name: Discover Examples
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
# On-chain example lists drive the compile jobs; offchain-matrix is the
# off-chain × on-chain cross-product that drives the single test job.
aiken-examples: ${{ steps.discovery.outputs.aiken-examples }}
scalus-examples: ${{ steps.discovery.outputs.scalus-examples }}
offchain-matrix: ${{ steps.discovery.outputs.offchain-matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run discovery script
id: discovery
run: |
chmod +x scripts/local-test-discovery.sh
scripts/local-test-discovery.sh
# (Cross-product example lists are now computed in local-test-discovery.sh and
# exposed as the single `offchain-matrix` output of the discover job.)
# ───────────────────────────────────────────────────────────────────────────
# Toolchain prepare
# ───────────────────────────────────────────────────────────────────────────
prepare-toolchains:
name: Prepare toolchains
needs: discover
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Aiken
if: needs.discover.outputs.aiken-examples != '[]'
uses: ./.github/actions/setup-aiken
with:
aiken-version: ${{ env.AIKEN_VERSION }}
cache-version: ${{ env.CACHE_VERSION }}
- name: Setup SBT
if: needs.discover.outputs.scalus-examples != '[]'
uses: ./.github/actions/setup-sbt
with:
java-version: ${{ env.SCALUS_JAVA_VERSION }}
cache-version: ${{ env.CACHE_VERSION }}
- name: Setup JBang + Java
if: contains(needs.discover.outputs.offchain-matrix, '"runtime":"jbang"')
uses: ./.github/actions/setup-jbang-java
with:
cache-version: ${{ env.CACHE_VERSION }}
- name: Setup Yaci DevKit (install only)
if: needs.discover.outputs.offchain-matrix != '[]'
uses: ./.github/actions/setup-yaci-devkit
with:
start: 'false'
cache-version: ${{ env.CACHE_VERSION }}
# ───────────────────────────────────────────────────────────────────────────
# On-chain compile
# ───────────────────────────────────────────────────────────────────────────
compile-aiken:
needs: [discover, prepare-toolchains]
if: needs.discover.outputs.aiken-examples != '[]'
uses: ./.github/workflows/_compile-aiken.yml
with:
examples: ${{ needs.discover.outputs.aiken-examples }}
aiken-version: v1.1.21
cache-version: v1
compile-scalus:
needs: [discover, prepare-toolchains]
if: needs.discover.outputs.scalus-examples != '[]'
uses: ./.github/workflows/_compile-scalus.yml
with:
examples: ${{ needs.discover.outputs.scalus-examples }}
java-version: '21'
cache-version: v1
# ───────────────────────────────────────────────────────────────────────────
# Off-chain tests — one matrix-driven job over the off-chain × on-chain
# cross-product computed by discover (`offchain-matrix`). Each matrix row is a
# {framework-id, status-prefix, subdir, runtime, onchain, examples} combo run
# through the runtime-generic reusable workflow. Adding a framework adds rows
# to the matrix automatically — no new jobs here.
# ───────────────────────────────────────────────────────────────────────────
test-offchain:
needs: [discover, prepare-toolchains, compile-aiken, compile-scalus]
# Run whenever the matrix is non-empty and no prerequisite *failed*. A
# conditionally-skipped compile job (e.g. no scalus examples) must not block
# the aiken combos, so we allow 'skipped'/'success' but not 'failure'.
if: >-
${{ !cancelled()
&& needs.discover.outputs.offchain-matrix != '[]'
&& needs.compile-aiken.result != 'failure'
&& needs.compile-scalus.result != 'failure' }}
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.discover.outputs.offchain-matrix) }}
uses: ./.github/workflows/_test-offchain.yml
with:
framework-id: ${{ matrix.framework-id }}
status-prefix: ${{ matrix.status-prefix }}
subdir: ${{ matrix.subdir }}
runtime: ${{ matrix.runtime }}
examples: ${{ toJson(matrix.examples) }}
onchain-framework: ${{ matrix.onchain }}
cache-version: v1
# ───────────────────────────────────────────────────────────────────────────
# Report & Dashboard
# ───────────────────────────────────────────────────────────────────────────
report-and-dashboard:
name: Report & Dashboard
needs:
- compile-aiken
- compile-scalus
- test-offchain
if: always()
runs-on: ubuntu-latest
timeout-minutes: 10
# Publishing the dashboard needs to push commits and open PRs.
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
ref: ${{ github.head_ref || github.ref }}
- name: Install jq
run: sudo apt-get install -y jq
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
- name: Reconstruct status files from artifacts
run: |
mkdir -p .local-test-results
# Onchain compile status: plutus-<onchain>-<example> → <onchain>-<example>-status.txt
for d in artifacts/plutus-*/; do
[ -d "$d" ] || continue
# Strip "plutus-" prefix → e.g. "aiken-payment-splitter" or "scalus-auction"
rest=$(basename "$d" | sed 's/^plutus-//')
if [ -f "$d/plutus.json" ]; then
echo "success" > ".local-test-results/${rest}-status.txt"
else
echo "failed" > ".local-test-results/${rest}-status.txt"
fi
done
# Off-chain test status from logs-<prefix>-<onchain>-<example> artifacts.
# The example name is everything after "logs-<prefix>-", so it naturally
# becomes "<onchain>-<example>" which is what generate-dashboard.sh expects.
resolve_status() {
local prefix="$1" log_pattern="$2"
for d in artifacts/logs-${prefix}-*/; do
[ -d "$d" ] || continue
local rest
rest=$(basename "$d" | sed "s/^logs-${prefix}-//")
local out=".local-test-results/${prefix}-${rest}-status.txt"
if [ -f "$d/test-status.txt" ]; then
cp "$d/test-status.txt" "$out"
elif [ -f "$d/test-output.log" ] && grep -qE "$log_pattern" "$d/test-output.log" 2>/dev/null; then
echo "failed" > "$out"
elif [ -f "$d/test-output.log" ]; then
echo "success" > "$out"
else
echo "failed" > "$out"
fi
done
}
resolve_status ccl "AssertionError|BUILD FAILED|Exception in thread"
resolve_status mesh "Uncaught|AssertionError|TypeError:|ReferenceError:|SyntaxError:|^error:"
resolve_status evosdk "Uncaught|AssertionError|TypeError:|ReferenceError:|SyntaxError:|^error:"
resolve_status pycardano "Traceback|Error:|Exception:"
- name: Generate markdown report
env:
REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
run: |
echo "# Cardano Ecosystem Test Report" > report.md
echo "" >> report.md
echo "**Generated**: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> report.md
echo "**Workflow Run**: https://github.com/${REPO}/actions/runs/${RUN_ID}" >> report.md
echo "" >> report.md
echo "## Summary" >> report.md
echo "" >> report.md
AIKEN_COUNT=$(find artifacts -name "plutus-aiken-*" -type d 2>/dev/null | wc -l)
SCALUS_COUNT=$(find artifacts -name "plutus-scalus-*" -type d 2>/dev/null | wc -l)
CCL_COUNT=$(find artifacts -name "logs-ccl-*" -type d 2>/dev/null | wc -l)
MESH_COUNT=$(find artifacts -name "logs-mesh-*" -type d 2>/dev/null | wc -l)
EVOSDK_COUNT=$(find artifacts -name "logs-evosdk-*" -type d 2>/dev/null | wc -l)
PYCARDANO_COUNT=$(find artifacts -name "logs-pycardano-*" -type d 2>/dev/null | wc -l)
echo "- **Aiken Examples Compiled**: $AIKEN_COUNT" >> report.md
echo "- **Scalus Examples Compiled**: $SCALUS_COUNT" >> report.md
echo "- **CCL Java Tests**: $CCL_COUNT" >> report.md
echo "- **Mesh.js Tests**: $MESH_COUNT" >> report.md
echo "- **Evolution SDK Tests**: $EVOSDK_COUNT" >> report.md
echo "- **PyCardano Tests**: $PYCARDANO_COUNT" >> report.md
echo "" >> report.md
echo "All test logs and build artifacts are available in the workflow artifacts." >> report.md
- name: Run version check
run: bash scripts/check-library-versions.sh
- name: Generate dashboard
run: bash scripts/generate-dashboard.sh
- name: Add report and version status to job summary
if: always()
run: |
cat report.md >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f .local-test-results/version-report.md ]; then
cat .local-test-results/version-report.md >> $GITHUB_STEP_SUMMARY
fi
- name: Upload reports artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: reports
path: |
report.md
.local-test-results/version-report.json
.local-test-results/version-report.md
retention-days: 30
# Only comment on the PR when the version check flagged outdated libraries —
# avoids posting a report on every run when there's nothing actionable.
- name: Post PR comment
if: github.event_name == 'pull_request' && env.VERSIONS_OUTDATED == 'true'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('report.md', 'utf8');
const versionReport = fs.existsSync('.local-test-results/version-report.md')
? '\n\n' + fs.readFileSync('.local-test-results/version-report.md', 'utf8')
: '';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: report + versionReport
});
# Publish the dashboard resiliently:
# • only when dashboard.json *meaningfully* changed (lastUpdated ignored);
# • on the default branch → open a PR (never a direct commit to main);
# • on any other branch (e.g. a PR's head branch) → commit directly;
# • never fail the build if publishing can't push/open a PR.
- name: Detect dashboard change
id: dash
env:
BRANCH: ${{ github.head_ref || github.ref_name }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
changed=false
if [ ! -f dashboard/dashboard.json ]; then
echo "dashboard/dashboard.json missing — nothing to publish"
else
CURR=$(jq -S 'del(.lastUpdated)' dashboard/dashboard.json 2>/dev/null || echo '')
PREV=$(git show HEAD:dashboard/dashboard.json 2>/dev/null \
| jq -S 'del(.lastUpdated)' 2>/dev/null || echo '')
if [ "$CURR" = "$PREV" ]; then
echo "No meaningful change (lastUpdated ignored) — discarding timestamp-only churn"
git checkout -- dashboard/dashboard.json 2>/dev/null || true
else
echo "Dashboard changed — will publish"
changed=true
fi
fi
echo "changed=$changed" >> "$GITHUB_OUTPUT"
if [ "$BRANCH" = "$DEFAULT_BRANCH" ]; then
echo "on default branch ($BRANCH) → PR"
echo "is_default=true" >> "$GITHUB_OUTPUT"
else
echo "on branch ($BRANCH) → direct commit"
echo "is_default=false" >> "$GITHUB_OUTPUT"
fi
- name: Commit dashboard.json to branch
if: steps.dash.outputs.changed == 'true' && steps.dash.outputs.is_default != 'true'
continue-on-error: true
env:
BRANCH: ${{ github.head_ref || github.ref_name }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add dashboard/dashboard.json
git commit -m "chore: update dashboard.json [skip ci]"
git push origin "HEAD:${BRANCH}"
- name: Open dashboard PR (default branch)
if: steps.dash.outputs.changed == 'true' && steps.dash.outputs.is_default == 'true'
continue-on-error: true
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
base: ${{ github.event.repository.default_branch }}
branch: automated/dashboard-update
delete-branch: true
add-paths: dashboard/dashboard.json
title: "chore: update dashboard.json"
commit-message: "chore: update dashboard.json [skip ci]"
body: |
## Automated dashboard update
The ecosystem CI detected a change in the cross-check matrix.
This PR updates `dashboard/dashboard.json` (the `lastUpdated`
timestamp alone never triggers it).
---
_Generated by the ecosystem CI workflow_
- name: Update versions.json from upstream
if: github.event_name == 'schedule' && env.VERSIONS_OUTDATED == 'true'
run: |
jq '.libraries | to_entries | map({key: .key, value: .value.latest}) | from_entries' \
.local-test-results/version-report.json > versions.json.new
mv versions.json.new versions.json
echo "Updated versions.json:"
cat versions.json
- name: Apply version sync
if: github.event_name == 'schedule' && env.VERSIONS_OUTDATED == 'true'
run: bash scripts/sync-versions.sh
- name: Create version-bump PR
if: github.event_name == 'schedule' && env.VERSIONS_OUTDATED == 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: automated/version-bump
delete-branch: true
# Only the files the version sync actually touches — the workspace
# also holds downloaded artifacts/ and the generated report.md,
# which must never end up in the PR.
add-paths: |
versions.json
**/aiken.toml
**/offchain/meshjs/deno.json
**/offchain/evolutionsdk/deno.json
**/offchain/ccl-java/*.java
**/offchain/pycardano/requirements.txt
title: "chore: update library versions to latest"
labels: ${{ env.VERSION_BUMP_LABEL }}
body: |
## Automated Version Bump
The version check detected outdated libraries.
This PR updates all dependency files to the latest versions.
See the `reports` artifact from the workflow run for details.
---
_Generated by the ecosystem CI workflow_
commit-message: "chore: bump library versions to latest"