Skip to content
Open
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
32 changes: 32 additions & 0 deletions .github/actions/setup-muse2-main/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Setup MUSE2 from main
description: Build and configure MUSE2_PATH from the MUSE2 main branch via cargo install
inputs:
muse2_exe:
description: MUSE2 executable name for the current OS
required: true
runs:
using: composite
steps:
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1

- name: Build and install MUSE2 from main (cargo)
shell: bash
run: |
cargo install \
--git https://github.com/EnergySystemsModellingLab/MUSE2 \
--branch main \
--root "$GITHUB_WORKSPACE/muse2_cargo_root"

- name: Set MUSE2_PATH (Linux / macOS)
if: runner.os == 'Linux' || runner.os == 'macOS'
shell: bash
run: |
echo "MUSE2_PATH=${{ github.workspace }}/muse2_cargo_root/bin/${{ inputs.muse2_exe }}" >> "$GITHUB_ENV"

- name: Set MUSE2_PATH (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$exePath = "${{ github.workspace }}\muse2_cargo_root\bin\${{ inputs.muse2_exe }}"
echo "MUSE2_PATH=$exePath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
55 changes: 55 additions & 0 deletions .github/actions/setup-muse2-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Setup MUSE2 from latest release
description: Download latest MUSE2 release asset for current OS and configure MUSE2_PATH
inputs:
github_token:
description: GitHub token for API access
required: true
muse2_exe:
description: MUSE2 executable name for the current OS
required: true
runs:
using: composite
steps:
- name: Get latest MUSE2 release tag
id: muse2_release
shell: bash
run: |
TAG=$(curl -sSf \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ inputs.github_token }}" \
https://api.github.com/repos/EnergySystemsModellingLab/MUSE2/releases/latest \
| jq -r '.tag_name')

if [[ -z "$TAG" || "$TAG" == "null" ]]; then
echo "::error::Failed to retrieve latest MUSE2 release tag."
exit 1
fi

echo "Resolved latest MUSE2 release: $TAG"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Download MUSE2 release asset
shell: bash
run: |
curl -sSfL \
"https://github.com/EnergySystemsModellingLab/MUSE2/releases/download/${{ steps.muse2_release.outputs.tag }}/${{ runner.os == 'Windows' && 'muse2_windows.zip' || runner.os == 'macOS' && 'muse2_macos_arm.tar.gz' || 'muse2_linux.tar.gz' }}" \
--output "muse2_asset_download"

- name: Extract and set MUSE2_PATH (Linux / macOS)
if: runner.os == 'Linux' || runner.os == 'macOS'
shell: bash
run: |
mkdir -p muse2_bin
tar -xf muse2_asset_download -C muse2_bin
chmod +x "muse2_bin/${{ inputs.muse2_exe }}"
echo "MUSE2_PATH=${{ github.workspace }}/muse2_bin/${{ inputs.muse2_exe }}" >> "$GITHUB_ENV"

- name: Extract and set MUSE2_PATH (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path muse2_bin | Out-Null
Rename-Item -Path "muse2_asset_download" -NewName "muse2_asset_download.zip"
Expand-Archive -Path "muse2_asset_download.zip" -DestinationPath muse2_bin -Force
$exePath = "${{ github.workspace }}\muse2_bin\${{ inputs.muse2_exe }}"
echo "MUSE2_PATH=$exePath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
24 changes: 24 additions & 0 deletions .github/actions/verify-muse2/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Verify MUSE2 installation
description: Validate that MUSE2_PATH points to an executable file for the current OS
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The action description says it validates that MUSE2_PATH points to an executable, but the checks only assert the path is an existing file (Linux/macOS: -f; Windows: Test-Path -PathType Leaf). Either update the checks to verify executability (e.g., -x on Linux/macOS) or adjust the description to match what is actually validated.

Suggested change
description: Validate that MUSE2_PATH points to an executable file for the current OS
description: Validate that MUSE2_PATH points to an existing file for the current OS

Copilot uses AI. Check for mistakes.
runs:
using: composite
steps:
- name: Verify MUSE2_PATH (Linux / macOS)
if: runner.os == 'Linux' || runner.os == 'macOS'
shell: bash
run: |
if [[ ! -f "$MUSE2_PATH" ]]; then
echo "::error::MUSE2 executable not found at: $MUSE2_PATH"
Comment on lines +10 to +11
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Linux/macOS the verification uses [[ ! -f "$MUSE2_PATH" ]], which won’t catch the case where the file exists but is not executable. Consider checking -x (or both -f and -x) so the failure is caught here with a clearer error instead of later when trying to run MUSE2.

Suggested change
if [[ ! -f "$MUSE2_PATH" ]]; then
echo "::error::MUSE2 executable not found at: $MUSE2_PATH"
if [[ ! -f "$MUSE2_PATH" || ! -x "$MUSE2_PATH" ]]; then
echo "::error::MUSE2 executable not found or is not executable at: $MUSE2_PATH"

Copilot uses AI. Check for mistakes.
exit 1
fi
echo "MUSE2_PATH is set to: $MUSE2_PATH"

- name: Verify MUSE2_PATH (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
if (-not (Test-Path -Path $env:MUSE2_PATH -PathType Leaf)) {
Write-Error "MUSE2 executable not found at: $env:MUSE2_PATH"
exit 1
}
Write-Host "MUSE2_PATH is set to: $env:MUSE2_PATH"
126 changes: 14 additions & 112 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Test and build
name: Test against MUSE2
on:
push:
branches: [main]
Expand All @@ -7,114 +7,16 @@ on:
workflow_call:

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python-version: '3.14'
muse2_asset: muse2_linux.tar.gz
muse2_exe: muse2
- os: windows-latest
python-version: '3.14'
muse2_asset: muse2_windows.zip
muse2_exe: muse2.exe
- os: macos-latest
python-version: '3.14'
muse2_asset: muse2_macos_arm.tar.gz
muse2_exe: muse2

steps:
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
prune-cache: false
activate-environment: true
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: uv sync

# Query the GitHub API for the latest MUSE2 release tag (e.g. "v2.0.0").
# The tag is written to GITHUB_OUTPUT so subsequent steps can reference it.
- name: Get latest MUSE2 release tag
id: muse2_release
shell: bash
run: |
TAG=$(curl -sSf \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ github.token }}" \
https://api.github.com/repos/EnergySystemsModellingLab/MUSE2/releases/latest \
| jq -r '.tag_name')

if [[ -z "$TAG" || "$TAG" == "null" ]]; then
echo "::error::Failed to retrieve latest MUSE2 release tag."
exit 1
fi

echo "Resolved latest MUSE2 release: $TAG"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

# Download the platform-appropriate MUSE2 release asset.
# 'shell: bash' is used on all platforms
- name: Download MUSE2 release asset
shell: bash
run: |
curl -sSfL \
"https://github.com/EnergySystemsModellingLab/MUSE2/releases/download/${{ steps.muse2_release.outputs.tag }}/${{ matrix.muse2_asset }}" \
--output "muse2_asset_download"

- name: Extract MUSE2 and set environment variable (Linux / macOS)
if: runner.os == 'Linux' || runner.os == 'macOS'
shell: bash
run: |
mkdir -p muse2_bin
tar -xf muse2_asset_download -C muse2_bin
chmod +x "muse2_bin/${{ matrix.muse2_exe }}"
echo "MUSE2_PATH=${{ github.workspace }}/muse2_bin/${{ matrix.muse2_exe }}" >> "$GITHUB_ENV"

- name: Extract MUSE2 and set environment variable (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path muse2_bin | Out-Null
Rename-Item -Path "muse2_asset_download" -NewName "muse2_asset_download.zip"
Expand-Archive -Path "muse2_asset_download.zip" -DestinationPath muse2_bin -Force
$exePath = "${{ github.workspace }}\muse2_bin\${{ matrix.muse2_exe }}"
echo "MUSE2_PATH=$exePath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

# Confirm the muse2 executable is present and the environment variable is set correctly.
- name: Verify MUSE2 installation (Linux / macOS)
if: runner.os == 'Linux' || runner.os == 'macOS'
shell: bash
run: |
if [[ ! -f "$MUSE2_PATH" ]]; then
echo "::error::MUSE2 executable not found at: $MUSE2_PATH"
exit 1
fi
echo "MUSE2_PATH is set to: $MUSE2_PATH"

- name: Verify MUSE2 installation (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
if (-not (Test-Path -Path $env:MUSE2_PATH -PathType Leaf)) {
Write-Error "MUSE2 executable not found at: $env:MUSE2_PATH"
exit 1
}
Write-Host "MUSE2_PATH is set to: $env:MUSE2_PATH"

- name: Run tests
run: pytest

- name: Upload coverage to Codecov
if: runner.os == 'Linux'
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
fail_ci_if_error: true
test_release:
name: Latest MUSE2 release
uses: ./.github/workflows/test-with-muse2.yml
with:
muse2_source: release
secrets: inherit

test_main:
name: Current MUSE2 main branch
uses: ./.github/workflows/test-with-muse2.yml
with:
muse2_source: main
secrets: inherit
Comment on lines +17 to +22
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds a full “build MUSE2 from main” test job to the standard CI on every push/PR, but the PR description/issue calls out running the main-branch build periodically. If the intent is periodic coverage, consider gating test_main behind a schedule/manual trigger (or making it conditional) to avoid significantly increasing CI duration and external network/build load on every PR.

Copilot uses AI. Check for mistakes.
71 changes: 71 additions & 0 deletions .github/workflows/test-with-muse2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Test with MUSE2

on:
workflow_call:
inputs:
muse2_source:
description: Which MUSE2 source to test against (release or main)
required: true
type: string
secrets:
CODECOV_TOKEN:
required: false

jobs:
test:
name: Run Pytest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python-version: '3.14'
muse2_exe: muse2
- os: windows-latest
python-version: '3.14'
muse2_exe: muse2.exe
- os: macos-latest
python-version: '3.14'
muse2_exe: muse2

steps:
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
prune-cache: false
activate-environment: true
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: uv sync

- name: Setup MUSE2 from main
if: inputs.muse2_source == 'main'
uses: ./.github/actions/setup-muse2-main
with:
muse2_exe: ${{ matrix.muse2_exe }}
Comment on lines +45 to +49
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because MUSE2 setup is entirely conditional on inputs.muse2_source, any unexpected value will skip both setup steps and then fail later in verify-muse2 with a misleading “executable not found” error. Consider adding an explicit validation step early in the job (and a clear error message) to enforce allowed values (e.g. main/release).

Copilot uses AI. Check for mistakes.

- name: Setup MUSE2 from latest release
if: inputs.muse2_source == 'release'
uses: ./.github/actions/setup-muse2-release
with:
github_token: ${{ github.token }}
muse2_exe: ${{ matrix.muse2_exe }}

- name: Verify MUSE2 installation
uses: ./.github/actions/verify-muse2

- name: Run tests
run: pytest

- name: Upload coverage to Codecov
# Latest linux release only - they should all be the same
if: runner.os == 'Linux' && inputs.muse2_source == 'release'
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CODECOV_TOKEN is declared as an optional secret for this reusable workflow, but the Codecov step is configured with fail_ci_if_error: true and always passes the token input. If the token isn’t available (e.g. fork PRs), this can cause CI failures unrelated to the tests—consider gating the step on the token being present, omitting the token input when empty, or not failing CI on upload errors in those contexts.

Suggested change
if: runner.os == 'Linux' && inputs.muse2_source == 'release'
if: runner.os == 'Linux' && inputs.muse2_source == 'release' && secrets.CODECOV_TOKEN != ''

Copilot uses AI. Check for mistakes.
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
fail_ci_if_error: true
Comment on lines +64 to +71
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CODECOV_TOKEN is declared as an optional secret for this reusable workflow, but the Codecov step will still run (on Linux release) and passes token: ${{ secrets.CODECOV_TOKEN }} with fail_ci_if_error: true. In contexts where secrets aren’t available (e.g., PRs from forks) or the caller doesn’t provide the token, this can cause the workflow to fail unexpectedly. Consider updating the if: guard to also require a non-empty token (or skip uploads on forked PRs), or make the token required if failures are desired.

Copilot uses AI. Check for mistakes.
Loading