Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
4f96df7
Create update-badges action
ehennestad Jun 7, 2025
5e990e4
add gh-badges init files
ehennestad Jun 7, 2025
9e38e77
Update action.yml
ehennestad Jun 7, 2025
f2bad5b
Update action.yml
ehennestad Jun 7, 2025
ed71af9
Update installMatBox.m
ehennestad Jun 7, 2025
bf3aae2
Create action.yml
ehennestad Jun 7, 2025
ccb2d62
Update action.yml
ehennestad Jun 7, 2025
76b3916
Add composite actions for package and release
ehennestad Jun 7, 2025
860ad55
Update action.yml
ehennestad Jun 7, 2025
e517b58
Update action.yml
ehennestad Jun 7, 2025
185be5f
Create create_release_reusable.yml
ehennestad Jun 7, 2025
3052e0b
Update reusable_release_workflow.yml
ehennestad Jun 7, 2025
c9075c8
Update reusable_release_workflow.yml
ehennestad Jun 7, 2025
de01380
extraxt toolbox name from toolbox info json file
ehennestad Jun 7, 2025
e8c6007
Update packageToolbox.m
ehennestad Jun 7, 2025
1b05516
Update action.yml
ehennestad Jun 7, 2025
268196d
Update reusable_release_workflow.yml
ehennestad Jun 7, 2025
df4a1de
Update action.yml
ehennestad Jun 7, 2025
a222184
Update create release workflow
ehennestad Jun 7, 2025
92669de
Update release workflow
ehennestad Jun 8, 2025
0e7eb52
Update action.yml
ehennestad Jun 8, 2025
f5d0eb2
Make python installation optional fro matrix testing in release workflow
ehennestad Jun 8, 2025
2fba4e2
Add workflow template for preparing release
ehennestad Jun 8, 2025
871dabf
Update TasksTest.m
ehennestad Jun 8, 2025
cea4010
Update reusable_release_workflow.yml
ehennestad Jun 8, 2025
9ecf1c4
Merge branch 'main' into make-release-workflow-reusable
ehennestad Jun 8, 2025
f92c538
Added job to verify packaged toolbox can be installed
ehennestad Jun 8, 2025
098b59c
minor fixes
ehennestad Jun 8, 2025
bcb1395
minor changes
ehennestad Jun 8, 2025
06b7ffb
Update reusable_check_code.yml
ehennestad Jun 8, 2025
fd85239
Update reusable_release_workflow.yml
ehennestad Jun 8, 2025
f737c33
Update prepare-release.yml
ehennestad Jun 8, 2025
04f7dc5
Update reusable_release_workflow.yml
ehennestad Jun 8, 2025
832000c
Merge branch 'main' into make-release-workflow-reusable
ehennestad Jun 8, 2025
5e413fe
Move deprecated workflow to deprecated folder
ehennestad Jun 8, 2025
6b3905a
Changed tag pattern
ehennestad Jun 8, 2025
6aeba83
Add "reusable job" workflows
ehennestad Jun 9, 2025
ecbd9cb
Update action.yml
ehennestad Jun 9, 2025
78f71c4
Update reusable_release_workflow.yml
ehennestad Jun 9, 2025
4b05ea4
Add workflow template for prepare-release-modular
ehennestad Jun 9, 2025
d1c87f9
Create reusable-workflow_release.yml
ehennestad Jun 9, 2025
a30318d
Create README.md
ehennestad Jun 9, 2025
4d34406
Rename run-tests.yml
ehennestad Jun 9, 2025
e6ff2f0
Move workflows to deprecated
ehennestad Jun 9, 2025
4c4f7fb
Changed name in "reusable job" workflows
ehennestad Jun 9, 2025
e051b86
Update reusable-job_build-matrix.yml
ehennestad Jun 9, 2025
b9e7294
Create reusable-workflow-check-code.yml
ehennestad Jun 9, 2025
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
243 changes: 243 additions & 0 deletions .github/actions/build-test-matrix/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
name: 'Build Test Matrix'
description: 'Determine MATLAB versions from toolbox info and build test matrix with Python versions'

inputs:
tools_directory:
description: 'Directory containing MLToolboxInfo.json'
required: false
default: 'tools'
matlab_versions:
description: 'MATLAB versions to test (overrides auto-detection from MLToolboxInfo.json)'
required: false
default: '[]'
python_versions:
description: 'JSON object mapping MATLAB versions to Python versions (overrides for specific versions only)'
required: false
default: '{}'
include_python:
description: 'Whether to include Python versions in the test matrix'
required: false
default: 'true'

outputs:
matrix:
description: 'Test matrix with MATLAB and Python versions'
value: ${{ steps.build.outputs.matrix }}
matlab_versions:
description: 'Determined MATLAB versions array'
value: ${{ steps.determine.outputs.matlab_versions }}

runs:
using: "composite"
steps:
- name: Determine MATLAB versions from toolbox info
id: determine
shell: bash
run: |
# Load configuration from config.json
config_file="${{ github.action_path }}/config.json"
config_min_release=$(jq -r '.minimumMatlabRelease' "$config_file")
config_max_release=$(jq -r '.maximumMatlabRelease' "$config_file")

# Get the latest MATLAB release from MathWorks API (this is our ceiling)
latest_matlab_release=$(curl -s "https://ssd.mathworks.com/supportfiles/ci/matlab-release/v0/latest")
echo "Latest MATLAB release from MathWorks API: $latest_matlab_release"

# Read min and max MATLAB releases from MLToolboxInfo.json
min_release_raw=$(jq -r '.ToolboxOptions.MinimumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json")
max_release_raw=$(jq -r '.ToolboxOptions.MaximumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json")

# Determine minimum release (use config default if not specified or if older than config minimum)
min_release="$config_min_release"
if [ "$min_release_raw" != "null" ] && [ "$min_release_raw" != "" ]; then
# Compare releases to ensure we don't go below config minimum
min_year=${min_release_raw:1:4}
min_letter=${min_release_raw:5:1}
config_min_year=${config_min_release:1:4}
config_min_letter=${config_min_release:5:1}

# If the specified minimum is newer than config minimum, use it
if [ $min_year -gt $config_min_year ] || ([ $min_year -eq $config_min_year ] && [ "$min_letter" \> "$config_min_letter" ]); then
min_release="$min_release_raw"
fi
fi

# Determine maximum release (use latest from API as ceiling)
max_release="$latest_matlab_release"
if [ "$max_release_raw" != "null" ] && [ "$max_release_raw" != "" ]; then
# Compare with latest release to enforce ceiling
max_year=${max_release_raw:1:4}
max_letter=${max_release_raw:5:1}
latest_year=${latest_matlab_release:1:4}
latest_letter=${latest_matlab_release:5:1}

# If the specified maximum is older than latest, use the specified one
if [ $max_year -lt $latest_year ] || ([ $max_year -eq $latest_year ] && [ "$max_letter" \< "$latest_letter" ]); then
max_release="$max_release_raw"
fi
fi

echo "Min release from MLToolboxInfo: $min_release_raw"
echo "Min release for GitHub Actions: $min_release"
echo "Max release from MLToolboxInfo: $max_release_raw"
echo "Max release for GitHub Actions: $max_release (capped at $latest_matlab_release)"

# Generate MATLAB releases between min and max
generate_matlab_releases() {
local min_release=$1
local max_release=$2

# Extract year and letter from releases (e.g., R2023a -> 2023, a)
min_year=${min_release:1:4}
min_letter=${min_release:5:1}
max_year=${max_release:1:4}
max_letter=${max_release:5:1}

releases=""

for year in $(seq $min_year $max_year); do
if [ $year -eq $min_year ] && [ $year -eq $max_year ]; then
# Same year - use range from min to max letter
start_letter=$min_letter
end_letter=$max_letter
elif [ $year -eq $min_year ]; then
# First year - start from min letter, go to 'b'
start_letter=$min_letter
end_letter="b"
elif [ $year -eq $max_year ]; then
# Last year - start from 'a', go to max letter
start_letter="a"
end_letter=$max_letter
else
# Middle years - include both 'a' and 'b'
start_letter="a"
end_letter="b"
fi

# Add releases for this year
if [ "$start_letter" = "a" ]; then
if [ -n "$releases" ]; then releases="$releases,"; fi
releases="$releases\"R${year}a\""
if [ "$end_letter" = "b" ]; then
releases="$releases,\"R${year}b\""
fi
elif [ "$start_letter" = "b" ]; then
if [ -n "$releases" ]; then releases="$releases,"; fi
releases="$releases\"R${year}b\""
fi
done

echo "[$releases]"
}

matlab_versions=$(generate_matlab_releases "$min_release" "$max_release")
echo "Generated MATLAB versions: $matlab_versions"
echo "matlab_versions=$matlab_versions" >> $GITHUB_OUTPUT

- name: Build test matrix
id: build
shell: bash
run: |
# Check if input MATLAB versions are provided
input_matlab_versions='${{ inputs.matlab_versions }}'
if [ "$input_matlab_versions" != "" ] && [ "$input_matlab_versions" != "null" ] && [ "$input_matlab_versions" != "[]" ]; then
# Use input MATLAB versions but filter them by config min/max limits
config_file="${{ github.action_path }}/config.json"
config_min_release=$(jq -r '.minimumMatlabRelease' "$config_file")

# Get the latest MATLAB release from MathWorks API (this is our ceiling)
latest_matlab_release=$(curl -s "https://ssd.mathworks.com/supportfiles/ci/matlab-release/v0/latest")

# Function to compare MATLAB releases
is_version_in_range() {
local version=$1
local min_release=$2
local max_release=$3

# Extract year and letter from version
version_year=${version:1:4}
version_letter=${version:5:1}

# Extract min release components
min_year=${min_release:1:4}
min_letter=${min_release:5:1}

# Extract max release components
max_year=${max_release:1:4}
max_letter=${max_release:5:1}

# Check if version is >= min_release
if [ $version_year -lt $min_year ] || ([ $version_year -eq $min_year ] && [ "$version_letter" \< "$min_letter" ]); then
return 1
fi

# Check if version is <= max_release
if [ $version_year -gt $max_year ] || ([ $version_year -eq $max_year ] && [ "$version_letter" \> "$max_letter" ]); then
return 1
fi

return 0
}

# Filter input versions to only include those within config limits
filtered_versions=""
for version in $(echo $input_matlab_versions | jq -r '.[]'); do
if is_version_in_range "$version" "$config_min_release" "$latest_matlab_release"; then
if [ -n "$filtered_versions" ]; then
filtered_versions="$filtered_versions,"
fi
filtered_versions="$filtered_versions\"$version\""
else
echo "Excluding $version (outside config range $config_min_release to $latest_matlab_release)"
fi
done

matlab_versions="[$filtered_versions]"
echo "Using input MATLAB versions (filtered by config limits): $matlab_versions"
else
# Use auto-determined MATLAB versions from previous step
matlab_versions='${{ steps.determine.outputs.matlab_versions }}'
echo "Using auto-determined MATLAB versions: $matlab_versions"
fi

# Check if Python should be included in the matrix
include_python='${{ inputs.include_python }}'

if [ "$include_python" = "true" ]; then
# Load Python versions from config file
config_file="${{ github.action_path }}/config.json"
config_python_versions=$(jq -c '.pythonVersions' "$config_file")

# Merge input python_versions overrides with config defaults
input_python_versions='${{ inputs.python_versions }}'
if [ "$input_python_versions" != "" ] && [ "$input_python_versions" != "null" ] && [ "$input_python_versions" != "{}" ]; then
# Merge input overrides with config defaults
python_versions=$(echo "$config_python_versions $input_python_versions" | jq -s '.[0] * .[1]')
echo "Using config Python versions with input overrides: $python_versions"
else
python_versions="$config_python_versions"
echo "Using config Python versions: $python_versions"
fi

# Build the matrix include section dynamically
include_items=""
for version in $(echo $matlab_versions | jq -r '.[]'); do
python_version=$(echo $python_versions | jq -r --arg v "$version" '.[$v]')
if [ "$python_version" != "null" ]; then
if [ -n "$include_items" ]; then
include_items="$include_items,"
fi
include_items="$include_items{\"MATLABVersion\":\"$version\",\"pythonVersion\":\"$python_version\"}"
fi
done

# Create the full matrix with Python versions
matrix="{\"MATLABVersion\":$matlab_versions,\"include\":[$include_items]}"
else
# Create matrix without Python versions
matrix="{\"MATLABVersion\":$matlab_versions}"
echo "Python versions excluded from matrix"
fi

echo "Generated matrix: $matrix"
echo "matrix=$matrix" >> $GITHUB_OUTPUT
15 changes: 15 additions & 0 deletions .github/actions/build-test-matrix/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"minimumMatlabRelease": "R2021a",
"maximumMatlabRelease": "latest",
"pythonVersions": {
"R2021a": "3.8",
"R2021b": "3.9",
"R2022a": "3.9",
"R2022b": "3.10",
"R2023a": "3.10",
"R2023b": "3.11",
"R2024a": "3.11",
"R2024b": "3.12",
"R2025a": "3.12"
}
}
64 changes: 64 additions & 0 deletions .github/actions/create-github-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: 'Create GitHub Release'
description: 'Create a GitHub release with packaged toolbox'

inputs:
version_number:
description: 'Version number for the release'
required: true
mltbx_path:
description: 'Path to the MLTBX file'
required: true

runs:
using: "composite"
steps:
- name: Commit updated Contents.m file
shell: bash
continue-on-error: true
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git status
git add code/Contents.m
git commit -m "Final check-ins for release v${{ inputs.version_number }} [skip-ci]"
git fetch
git push

- name: Update tag
shell: bash
if: always()
continue-on-error: true
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

# Delete the existing tag locally if it exists
if git tag -l "v${{ inputs.version_number }}" | grep -q "v${{ inputs.version_number }}"; then
echo "Deleting existing local tag v${{ inputs.version_number }}"
git tag -d "v${{ inputs.version_number }}"
else
echo "Local tag v${{ inputs.version_number }} does not exist"
fi

# Delete the existing tag remotely if it exists
if git ls-remote --tags origin | grep -q "refs/tags/v${{ inputs.version_number }}"; then
echo "Deleting existing remote tag v${{ inputs.version_number }}"
git push origin --delete "v${{ inputs.version_number }}"
else
echo "Remote tag v${{ inputs.version_number }} does not exist"
fi

# Create the tag with a message, including [skip ci] to prevent CI workflows
git tag -a "v${{ inputs.version_number }}" -m "Release v${{ inputs.version_number }} [skip ci]"

# Push the new tag to the remote repository
git push origin "v${{ inputs.version_number }}"

- name: Create GitHub release
uses: ncipollo/release-action@v1
with:
draft: true
artifacts: "${{ inputs.mltbx_path }}"
tag: "v${{ inputs.version_number }}"
generateReleaseNotes: true
body: "![MATLAB Versions Tested](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2F${{ github.repository }}%2Fgh-badges%2F.github%2Fbadges%2Fv${{ inputs.version_number }}%2Ftested_with.json)"
4 changes: 3 additions & 1 deletion .github/actions/install-matbox/installMatBox.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ function installFromCommit()

scriptPath = mfilename('fullpath');
projectFolder = extractBefore(scriptPath, fullfile('.github', 'actions'));

% We only want to add the code folder to MATLAB's path here to avoid
% potential path conflicts with task functions in the tools folder.
codeDirectory = fullfile(projectFolder, 'code');

addpath(genpath(codeDirectory))
savepath()
end
52 changes: 52 additions & 0 deletions .github/actions/package-toolbox/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: 'Package MATLAB Toolbox'
description: 'Package a MATLAB toolbox with specified version'

inputs:
version_number:
description: 'Version number to use for packaging'
required: true
code_directory:
description: 'Directory containing the MATLAB code to package'
required: false
default: 'code'
tools_directory:
description: 'Directory containing packaging tools'
required: false
default: 'tools'

outputs:
mltbx_path:
description: 'Path to the packaged MLTBX file'
value: ${{ steps.set_version.outputs.mltbx_path }}
toolbox_name:
description: 'Name of the toolbox from MLToolboxInfo.json'
value: ${{ steps.set_version.outputs.toolbox_name }}

runs:
using: "composite"
steps:
- name: Package toolbox
id: package
uses: matlab-actions/run-command@v2
with:
command: |
addpath(genpath("${{ inputs.tools_directory }}"));
versionNumberStr = "v${{ inputs.version_number }}";
if exist("packageToolbox", "file")
[~, mltbxPath] = packageToolbox("specific", versionNumberStr);
else
[~, mltbxPath] = matbox.tasks.packageToolbox(pwd, "specific", versionNumberStr, ...
'SourceFolderName', "${{ inputs.code_directory }}");
end

% Write the MLTBX path to a file for the next step
matbox.utility.filewrite('mltbx_path.txt', mltbxPath);

- name: Set version number
id: set_version
shell: bash
run: |
toolboxName=$(jq -r '.ToolboxOptions.ToolboxName' "${{ inputs.tools_directory }}/MLToolboxInfo.json")
mltbxPath=$(cat mltbx_path.txt)
echo "toolbox_name=$toolboxName" >> $GITHUB_OUTPUT
echo "mltbx_path=$mltbxPath" >> $GITHUB_OUTPUT
Loading