From 4f96df7fc47209036eeeb7781cf58f2063fb8f66 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 20:28:19 +0200 Subject: [PATCH 01/45] Create update-badges action --- .github/actions/update-badges/action.yml | 72 ++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/actions/update-badges/action.yml diff --git a/.github/actions/update-badges/action.yml b/.github/actions/update-badges/action.yml new file mode 100644 index 0000000..bcf206d --- /dev/null +++ b/.github/actions/update-badges/action.yml @@ -0,0 +1,72 @@ +# Should only be used in a job where MATLAB and MatBox is installed in previous steps + +name: 'Update "tested with" badge' +description: 'Generate the "tested with" badge for a release and push to `gh-badges` branch' + +inputs: + version_number: + description: 'Version number for the release' + required: true + tools_directory: + description: 'Directory containing badge tools' + required: false + default: 'tools' + +runs: + using: "composite" + steps: + - name: Generate tested with badge + uses: matlab-actions/run-command@v2 + with: + command: | + addpath(genpath("${{ inputs.tools_directory }}")); + versionNumberStr = "v${{ inputs.version_number }}" + if exist("createTestedWithBadgeforToolbox", "file") + createTestedWithBadgeforToolbox(versionNumberStr); + else + matbox.tasks.createTestedWithBadgeforToolbox(versionNumberStr, pwd); + end + + - name: Checkout gh-badges branch + uses: actions/checkout@v4 + continue-on-error: true + id: checkout_badges + with: + ref: gh-badges + path: gh-badges + + - name: Create gh-badges branch if it doesn't exist + if: steps.checkout_badges.outcome == 'failure' + shell: bash + run: | + echo "gh-badges branch doesn't exist, creating it..." + mkdir -p gh-badges + cd gh-badges + git init + git remote add origin https://github.com/${{ github.repository }}.git + git checkout --orphan gh-badges + echo "# GitHub Badges" > README.md + git add README.md + git config user.name "${{ github.workflow }} by ${{ github.actor }}" + git config user.email "<>" + git commit -m "Initialize gh-badges branch" + git push origin gh-badges + + - name: Push to gh-badges + shell: bash + run: | + mkdir -p gh-badges/.github/badges/v${{ inputs.version_number }} + cp .github/badges/v${{ inputs.version_number }}/tested_with.json gh-badges/.github/badges/v${{ inputs.version_number }}/tested_with.json + cd gh-badges + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # Only proceed with commit and push if changes are detected + if [[ $(git add .github/badges/* --dry-run | wc -l) -gt 0 ]]; then + git add .github/badges/* + git commit -m "Update tested with badge for release" + git push origin gh-badges + else + echo "Nothing to commit" + fi From 5e990e4f2e554ab4065f4fba42b3d5928aff4378 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 20:35:39 +0200 Subject: [PATCH 02/45] add gh-badges init files --- .github/actions/update-badges/action.yml | 11 +++++++---- .../actions/update-badges/gh-badges-init/.gitignore | 7 +++++++ .../actions/update-badges/gh-badges-init/README.md | 3 +++ 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 .github/actions/update-badges/gh-badges-init/.gitignore create mode 100644 .github/actions/update-badges/gh-badges-init/README.md diff --git a/.github/actions/update-badges/action.yml b/.github/actions/update-badges/action.yml index bcf206d..2011c82 100644 --- a/.github/actions/update-badges/action.yml +++ b/.github/actions/update-badges/action.yml @@ -45,10 +45,13 @@ runs: git init git remote add origin https://github.com/${{ github.repository }}.git git checkout --orphan gh-badges - echo "# GitHub Badges" > README.md - git add README.md - git config user.name "${{ github.workflow }} by ${{ github.actor }}" - git config user.email "<>" + + # Copy initialization files + cp ${{ github.action_path }}/gh-badges-init/* . + + git add . + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git commit -m "Initialize gh-badges branch" git push origin gh-badges diff --git a/.github/actions/update-badges/gh-badges-init/.gitignore b/.github/actions/update-badges/gh-badges-init/.gitignore new file mode 100644 index 0000000..379e122 --- /dev/null +++ b/.github/actions/update-badges/gh-badges-init/.gitignore @@ -0,0 +1,7 @@ +* +!.gitignore +!README.md +!.github +!.github/badges +!.github/badges/* +!.github/badges/**/* diff --git a/.github/actions/update-badges/gh-badges-init/README.md b/.github/actions/update-badges/gh-badges-init/README.md new file mode 100644 index 0000000..4dd23a0 --- /dev/null +++ b/.github/actions/update-badges/gh-badges-init/README.md @@ -0,0 +1,3 @@ +# GitHub Badges + +This branch is reserved for storing badges for the GitHub releases page From 9e38e770649f26ea75349bbf718358f0df61a92c Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 20:51:34 +0200 Subject: [PATCH 03/45] Update action.yml --- .github/actions/update-badges/action.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/actions/update-badges/action.yml b/.github/actions/update-badges/action.yml index 2011c82..6c83335 100644 --- a/.github/actions/update-badges/action.yml +++ b/.github/actions/update-badges/action.yml @@ -27,19 +27,30 @@ runs: matbox.tasks.createTestedWithBadgeforToolbox(versionNumberStr, pwd); end - - name: Checkout gh-badges branch + - name: Check if gh-badges branch exists + id: check_branch + shell: bash + run: | + if git ls-remote --heads origin gh-badges | grep -q gh-badges; then + echo "branch_exists=true" >> $GITHUB_OUTPUT + echo "gh-badges branch exists" + else + echo "branch_exists=false" >> $GITHUB_OUTPUT + echo "gh-badges branch does not exist" + fi + + - name: Checkout existing gh-badges branch + if: steps.check_branch.outputs.branch_exists == 'true' uses: actions/checkout@v4 - continue-on-error: true - id: checkout_badges with: ref: gh-badges path: gh-badges - name: Create gh-badges branch if it doesn't exist - if: steps.checkout_badges.outcome == 'failure' + if: steps.check_branch.outputs.branch_exists == 'false' shell: bash run: | - echo "gh-badges branch doesn't exist, creating it..." + echo "Creating gh-badges branch..." mkdir -p gh-badges cd gh-badges git init From f2bad5b15849c6cc3d05b566369882df9801941f Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 21:05:46 +0200 Subject: [PATCH 04/45] Update action.yml --- .github/actions/update-badges/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/update-badges/action.yml b/.github/actions/update-badges/action.yml index 6c83335..58f74c1 100644 --- a/.github/actions/update-badges/action.yml +++ b/.github/actions/update-badges/action.yml @@ -54,7 +54,7 @@ runs: mkdir -p gh-badges cd gh-badges git init - git remote add origin https://github.com/${{ github.repository }}.git + git remote add origin https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.git git checkout --orphan gh-badges # Copy initialization files From ed71af9788accb496b48fad2d22bf533085e2b11 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 21:14:46 +0200 Subject: [PATCH 05/45] Update installMatBox.m --- .github/actions/install-matbox/installMatBox.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/install-matbox/installMatBox.m b/.github/actions/install-matbox/installMatBox.m index 68223be..d328cb6 100644 --- a/.github/actions/install-matbox/installMatBox.m +++ b/.github/actions/install-matbox/installMatBox.m @@ -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 From bf3aae2291ca8a38a913d3f175e07da885e3edf9 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 21:24:29 +0200 Subject: [PATCH 06/45] Create action.yml --- .github/actions/validate-version/action.yml | 53 +++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/actions/validate-version/action.yml diff --git a/.github/actions/validate-version/action.yml b/.github/actions/validate-version/action.yml new file mode 100644 index 0000000..f7d7a9a --- /dev/null +++ b/.github/actions/validate-version/action.yml @@ -0,0 +1,53 @@ +name: 'Validate Version' +description: 'Validate version number format from tag or input' + +inputs: + version: + description: 'Version number (for manual triggers)' + required: false + ref_name: + description: 'GitHub ref name (for tag triggers)' + required: false + +outputs: + version_number: + description: 'Validated version number (without v prefix)' + value: ${{ steps.set_version.outputs.version_number }} + +runs: + using: "composite" + steps: + - name: Check for retag + if: ${{ contains(github.event.head_commit.message, '[skip-ci]') }} + shell: bash + run: | + echo "Error: Commit message contains [skip-ci], skipping." + exit 1 + + - name: Set version based on trigger type + id: set_version + shell: bash + run: | + if [[ -n "${{ inputs.version }}" ]]; then + # For manual trigger, use the input version + VERSION_NUMBER="${{ inputs.version }}" + # Validate format + if [[ ! "$VERSION_NUMBER" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Input for 'version' ('$VERSION_NUMBER') is not in the expected major.minor.patch format." + exit 1 + fi + else + # For tag trigger, use the tag name + TAG_NAME="${{ inputs.ref_name }}" + # Remove 'v' prefix if present for the version number + VERSION_NUMBER="${TAG_NAME#v}" + + # Validate format + if [[ ! "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Tag name ('$TAG_NAME') is not in the expected v*.*.* format." + exit 1 + fi + fi + + echo "Using version number: $VERSION_NUMBER" + echo "version_number=$VERSION_NUMBER" >> $GITHUB_OUTPUT From ccb2d6249ce94ed521cd8c19a59277b5b34e4551 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 21:31:59 +0200 Subject: [PATCH 07/45] Update action.yml fixed indentation --- .github/actions/push-badges/action.yml | 54 +++++++++++++------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/actions/push-badges/action.yml b/.github/actions/push-badges/action.yml index 67df8b9..d4c72c4 100644 --- a/.github/actions/push-badges/action.yml +++ b/.github/actions/push-badges/action.yml @@ -12,33 +12,33 @@ inputs: runs: using: "composite" steps: - # Check out the actual source branch in a separate working tree - - name: Checkout PR branch for pushing badges - uses: actions/checkout@v4 - with: - ref: ${{ inputs.pr-ref }} - repository: ${{ inputs.pr-repo }} - path: pr-branch + # Check out the actual source branch in a separate working tree + - name: Checkout PR branch for pushing badges + uses: actions/checkout@v4 + with: + ref: ${{ inputs.pr-ref }} + repository: ${{ inputs.pr-repo }} + path: pr-branch - # Copy generated files to actual branch - - name: Copy badges to PR branch - shell: bash - run: | - cp -r .github/badges/* pr-branch/.github/badges/ 2>/dev/null || echo "No badges to copy" + # Copy generated files to actual branch + - name: Copy badges to PR branch + shell: bash + run: | + cp -r .github/badges/* pr-branch/.github/badges/ 2>/dev/null || echo "No badges to copy" - # Commit updated SVG badges for the issues and tests (if changed) - - name: Commit and push SVG badges if updated - working-directory: pr-branch - shell: bash - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git fetch + # Commit updated SVG badges for the issues and tests (if changed) + - name: Commit and push SVG badges if updated + working-directory: pr-branch + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git fetch - if [[ $(git add .github/badges/* --dry-run | wc -l) -gt 0 ]]; then - git add .github/badges - git commit -m "Update GitHub badges" - git push origin HEAD - else - echo "Nothing to commit" - fi + if [[ $(git add .github/badges/* --dry-run | wc -l) -gt 0 ]]; then + git add .github/badges + git commit -m "Update GitHub badges" + git push origin HEAD + else + echo "Nothing to commit" + fi From 76b3916376cdef7045b7bdc01dc5915937b39332 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 21:46:07 +0200 Subject: [PATCH 08/45] Add composite actions for package and release --- .../actions/create-github-release/action.yml | 55 +++++++++++++++++++ .github/actions/package-toolbox/action.yml | 47 ++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .github/actions/create-github-release/action.yml create mode 100644 .github/actions/package-toolbox/action.yml diff --git a/.github/actions/create-github-release/action.yml b/.github/actions/create-github-release/action.yml new file mode 100644 index 0000000..e9c8ead --- /dev/null +++ b/.github/actions/create-github-release/action.yml @@ -0,0 +1,55 @@ +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 + version_underscore: + description: 'Version number with underscores' + 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 and remotely + git tag -d "v${{ inputs.version_number }}" + git push origin --delete "v${{ inputs.version_number }}" + + # Recreate 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)" diff --git a/.github/actions/package-toolbox/action.yml b/.github/actions/package-toolbox/action.yml new file mode 100644 index 0000000..7c88ea1 --- /dev/null +++ b/.github/actions/package-toolbox/action.yml @@ -0,0 +1,47 @@ +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 }} + version_underscore: + description: 'Version number with underscores for file naming' + value: ${{ steps.set_version.outputs.version_underscore }} + +runs: + using: "composite" + steps: + - name: Package toolbox + uses: matlab-actions/run-command@v2 + with: + command: | + addpath(genpath("${{ inputs.tools_directory }}")); + versionNumberStr = "v${{ inputs.version_number }}"; + if exist("packageToolbox", "file") + packageToolbox("specific", versionNumberStr); + else + matbox.tasks.packageToolbox(pwd, "specific", versionNumberStr, ... + 'SourceFolderName', "${{ inputs.code_directory }}"); + end + + - name: Set version number + id: set_version + shell: bash + run: | + versionUnderscore=$(echo "${{ inputs.version_number }}" | sed 's/\./_/g') + echo "version_underscore=$versionUnderscore" >> $GITHUB_OUTPUT + echo "mltbx_path=releases/MatBox_${versionUnderscore}.mltbx" >> $GITHUB_OUTPUT From 860ad5513f1144e1a4e3be7333a2386f61edd935 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 22:13:34 +0200 Subject: [PATCH 09/45] Update action.yml auppress output --- .github/actions/update-badges/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/update-badges/action.yml b/.github/actions/update-badges/action.yml index 58f74c1..7578539 100644 --- a/.github/actions/update-badges/action.yml +++ b/.github/actions/update-badges/action.yml @@ -20,7 +20,7 @@ runs: with: command: | addpath(genpath("${{ inputs.tools_directory }}")); - versionNumberStr = "v${{ inputs.version_number }}" + versionNumberStr = "v${{ inputs.version_number }}"; if exist("createTestedWithBadgeforToolbox", "file") createTestedWithBadgeforToolbox(versionNumberStr); else From e517b587ddb2424d8622c924da5d68b546bdc4bf Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 22:24:17 +0200 Subject: [PATCH 10/45] Update action.yml --- .github/actions/test-code/action.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/actions/test-code/action.yml b/.github/actions/test-code/action.yml index 9e60b7d..90ca733 100644 --- a/.github/actions/test-code/action.yml +++ b/.github/actions/test-code/action.yml @@ -10,6 +10,14 @@ inputs: tools_directory: description: 'Where the testToolbox function is located' default: './tools' + report_subdirectory: + description: 'Subdirectory for test reports' + required: false + default: '' + create_badge: + description: 'Whether to create a badge' + required: false + default: 'true' runs: using: "composite" @@ -21,15 +29,20 @@ runs: command: | addpath(genpath("${{ inputs.tools_directory }}")); if exist("testToolbox", "file") - testToolbox(); + testToolbox(... + "ReportSubdirectory", "${{ inputs.report_subdirectory }}", ... + "CreateBadge", strcmp(${{ inputs.create_badge }}, 'true')); else matbox.tasks.testToolbox(pwd, ... "SourceFolderName", "${{ inputs.code_directory }}", ... - "ToolsFolderName", "${{ inputs.tools_directory }}"); + "ToolsFolderName", "${{ inputs.tools_directory }}", ... + "ReportSubdirectory", "${{ inputs.report_subdirectory }}", ... + "CreateBadge", strcmp(${{ inputs.create_badge }}, 'true')); end - name: Publish test results uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: - files: "docs/reports/test-results.xml" + check_name: ${{ inputs.report_subdirectory != '' && format('Test Results ({0})', inputs.report_subdirectory) || 'Test Results' }} + files: "docs/reports/**/test-results.xml" From 185be5f6c7e0d91aefcefa15f8e6883576768b9a Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 22:24:59 +0200 Subject: [PATCH 11/45] Create create_release_reusable.yml --- .../workflows/reusable_release_workflow.yml | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 .github/workflows/reusable_release_workflow.yml diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml new file mode 100644 index 0000000..190d6d8 --- /dev/null +++ b/.github/workflows/reusable_release_workflow.yml @@ -0,0 +1,153 @@ +name: Reusable Release Workflow + +on: + workflow_call: + inputs: + version: + type: string + required: false + description: 'Version number in major.minor.patch format (for manual triggers)' + ref_name: + type: string + required: false + description: 'GitHub ref name (for tag triggers)' + code_directory: + type: string + default: 'code' + description: 'Directory containing source code' + tools_directory: + type: string + default: 'tools' + description: 'Directory containing tools' + matlab_versions: + type: string + default: '["R2023a", "R2023b", "R2024a", "R2024b"]' + description: 'JSON array of MATLAB versions to test' + python_versions: + type: string + default: '{"R2023a": "3.10", "R2023b": "3.11", "R2024a": "3.11", "R2024b": "3.12"}' + description: 'JSON object mapping MATLAB versions to Python versions' + secrets: + DEPLOY_KEY: + required: true + description: 'SSH deploy key for pushing to protected branches' + +jobs: + validate_version: + runs-on: ubuntu-latest + outputs: + version_number: ${{ steps.validate.outputs.version_number }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Validate version + id: validate + uses: ehennestad/matbox/.github/actions/validate-version@make-release-workflow-reusable + with: + version: ${{ inputs.version }} + ref_name: ${{ inputs.ref_name }} + + test: + name: Run MATLAB tests (${{ matrix.MATLABVersion }}) + needs: [validate_version] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + MATLABVersion: ${{ fromJSON(inputs.matlab_versions) }} + include: + - MATLABVersion: R2023a + pythonVersion: '3.10' + - MATLABVersion: R2023b + pythonVersion: '3.11' + - MATLABVersion: R2024a + pythonVersion: '3.11' + - MATLABVersion: R2024b + pythonVersion: '3.12' + + steps: + # Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.pythonVersion }} + + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + with: + release: ${{ matrix.MATLABVersion }} + + - name: Install MatBox + uses: ehennestad/matbox/.github/actions/install-matbox@v0.9 + + # Runs all tests in the project. Put results in a version specific subdirectory + - name: Run tests + uses: ehennestad/matbox/.github/actions/test-code@make-release-workflow-reusable + with: + code_directory: ${{ inputs.code_directory }} + tools_directory: ${{ inputs.tools_directory }} + report_subdirectory: ${{ matrix.MATLABVersion }} + create_badge: false + + # Save the contents of the report directory from each release into an artifact. + - name: Save report directory + uses: actions/upload-artifact@v4 + if: always() + with: + name: reports-${{ matrix.MATLABVersion }} + path: docs/reports + + release: + needs: [test, validate_version] + runs-on: ubuntu-latest + steps: + - name: Checkout repository using deploy key + uses: actions/checkout@v4 + with: + ref: refs/heads/main + ssh-key: ${{ secrets.DEPLOY_KEY }} + + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + + # Download test reports + - uses: actions/download-artifact@v4 + with: + pattern: reports-* + path: docs/reports + merge-multiple: true + + # Generate badge + - name: Generate tested with badge + uses: ehennestad/matbox/.github/actions/update-badges@make-release-workflow-reusable + with: + version_number: ${{ needs.validate_version.outputs.version_number }} + tools_directory: ${{ inputs.tools_directory }} + + # Package toolbox + - name: Package toolbox + id: package + uses: ehennestad/matbox/.github/actions/package-toolbox@make-release-workflow-reusable + with: + version_number: ${{ needs.validate_version.outputs.version_number }} + code_directory: ${{ inputs.code_directory }} + tools_directory: ${{ inputs.tools_directory }} + + # Save the MLTBX. + - name: Save packaged toolbox + uses: actions/upload-artifact@v4 + with: + name: MatBox${{ steps.package.outputs.version_underscore }}.mltbx + path: ${{ steps.package.outputs.mltbx_path }} + + # Create GitHub release + - name: Create GitHub release + uses: ehennestad/matbox/.github/actions/create-github-release@make-release-workflow-reusable + with: + version_number: ${{ needs.validate_version.outputs.version_number }} + mltbx_path: ${{ steps.package.outputs.mltbx_path }} + version_underscore: ${{ steps.package.outputs.version_underscore }} From 3052e0be2ed730f67e0168af12186a17c0849e12 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 22:43:53 +0200 Subject: [PATCH 12/45] Update reusable_release_workflow.yml --- .github/workflows/reusable_release_workflow.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 190d6d8..d83fe44 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -113,7 +113,10 @@ jobs: - name: Set up MATLAB uses: matlab-actions/setup-matlab@v2 - + + - name: Install MatBox + uses: ehennestad/matbox/.github/actions/install-matbox@v0.9 + # Download test reports - uses: actions/download-artifact@v4 with: From c9075c8efc2c01c390a7d7a4ec1949676613656c Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 22:58:30 +0200 Subject: [PATCH 13/45] Update reusable_release_workflow.yml --- .github/workflows/reusable_release_workflow.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index d83fe44..f59b8bc 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -41,6 +41,15 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Check for MLToolboxInfo.json + run: | + if [ ! -f "${{ inputs.tools_directory }}/MLToolboxInfo.json" ]; then + echo "Error: MLToolboxInfo.json not found in ${{ inputs.tools_directory }} directory" + echo "This file is required for the release workflow to function properly" + exit 1 + fi + echo "MLToolboxInfo.json found in ${{ inputs.tools_directory }} directory" + - name: Validate version id: validate uses: ehennestad/matbox/.github/actions/validate-version@make-release-workflow-reusable From de013807f175aa502c0e88ea92664bef647c5ee6 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 23:20:59 +0200 Subject: [PATCH 14/45] extraxt toolbox name from toolbox info json file --- .github/actions/package-toolbox/action.yml | 7 ++++++- .github/workflows/reusable_release_workflow.yml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/actions/package-toolbox/action.yml b/.github/actions/package-toolbox/action.yml index 7c88ea1..864b80a 100644 --- a/.github/actions/package-toolbox/action.yml +++ b/.github/actions/package-toolbox/action.yml @@ -21,6 +21,9 @@ outputs: version_underscore: description: 'Version number with underscores for file naming' value: ${{ steps.set_version.outputs.version_underscore }} + toolbox_name: + description: 'Name of the toolbox from MLToolboxInfo.json' + value: ${{ steps.set_version.outputs.toolbox_name }} runs: using: "composite" @@ -43,5 +46,7 @@ runs: shell: bash run: | versionUnderscore=$(echo "${{ inputs.version_number }}" | sed 's/\./_/g') + toolboxName=$(jq -r '.ToolboxOptions.ToolboxName' "${{ inputs.tools_directory }}/MLToolboxInfo.json") echo "version_underscore=$versionUnderscore" >> $GITHUB_OUTPUT - echo "mltbx_path=releases/MatBox_${versionUnderscore}.mltbx" >> $GITHUB_OUTPUT + echo "toolbox_name=$toolboxName" >> $GITHUB_OUTPUT + echo "mltbx_path=releases/${toolboxName}_${versionUnderscore}.mltbx" >> $GITHUB_OUTPUT diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index f59b8bc..1179040 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -153,7 +153,7 @@ jobs: - name: Save packaged toolbox uses: actions/upload-artifact@v4 with: - name: MatBox${{ steps.package.outputs.version_underscore }}.mltbx + name: ${{ steps.package.outputs.toolbox_name }}${{ steps.package.outputs.version_underscore }}.mltbx path: ${{ steps.package.outputs.mltbx_path }} # Create GitHub release From e8c600777be41524d3b8caed222fcad8abf4498c Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 23:32:15 +0200 Subject: [PATCH 15/45] Update packageToolbox.m --- code/+matbox/+tasks/packageToolbox.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/+matbox/+tasks/packageToolbox.m b/code/+matbox/+tasks/packageToolbox.m index 94845cb..422e7b2 100644 --- a/code/+matbox/+tasks/packageToolbox.m +++ b/code/+matbox/+tasks/packageToolbox.m @@ -82,6 +82,8 @@ % https://github.com/ehennestad/MatBox/issues/21 matbox.toolbox.internal.fixRequiredAdditionalSoftware(initialOutputFile, finalOutputFile) + fprintf('Exported MLTBX to %s\n', finalOutputFile) + if ~nargout clear newVersion end From 1b05516136edf24c731f017dd4b540c0196b64a2 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 23:33:04 +0200 Subject: [PATCH 16/45] Update action.yml --- .../actions/create-github-release/action.yml | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/actions/create-github-release/action.yml b/.github/actions/create-github-release/action.yml index e9c8ead..fb749d0 100644 --- a/.github/actions/create-github-release/action.yml +++ b/.github/actions/create-github-release/action.yml @@ -35,11 +35,23 @@ runs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - # Delete the existing tag locally and remotely - git tag -d "v${{ inputs.version_number }}" - git push origin --delete "v${{ inputs.version_number }}" + # 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 - # Recreate the tag with a message, including [skip ci] to prevent CI workflows + # 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 From 268196d685920d4f272fd2c5f8ff34e5fc9fc8d7 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 23:41:08 +0200 Subject: [PATCH 17/45] Update reusable_release_workflow.yml --- .github/workflows/reusable_release_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 1179040..52c6a07 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -124,7 +124,7 @@ jobs: uses: matlab-actions/setup-matlab@v2 - name: Install MatBox - uses: ehennestad/matbox/.github/actions/install-matbox@v0.9 + uses: ehennestad/matbox/.github/actions/install-matbox@make-release-workflow-reusable # Download test reports - uses: actions/download-artifact@v4 From df4a1de7ec5570823d59b25a0a9df11f72a71f37 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 7 Jun 2025 23:53:38 +0200 Subject: [PATCH 18/45] Update action.yml --- .github/actions/package-toolbox/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/package-toolbox/action.yml b/.github/actions/package-toolbox/action.yml index 864b80a..7b28947 100644 --- a/.github/actions/package-toolbox/action.yml +++ b/.github/actions/package-toolbox/action.yml @@ -49,4 +49,4 @@ runs: toolboxName=$(jq -r '.ToolboxOptions.ToolboxName' "${{ inputs.tools_directory }}/MLToolboxInfo.json") echo "version_underscore=$versionUnderscore" >> $GITHUB_OUTPUT echo "toolbox_name=$toolboxName" >> $GITHUB_OUTPUT - echo "mltbx_path=releases/${toolboxName}_${versionUnderscore}.mltbx" >> $GITHUB_OUTPUT + echo "mltbx_path=releases/${toolboxName}_v${versionUnderscore}.mltbx" >> $GITHUB_OUTPUT From a222184a727ffff6d1fccd45a8a4fe9da1ca07bd Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 00:11:00 +0200 Subject: [PATCH 19/45] Update create release workflow --- .github/workflows/create_release.yml | 243 +---------------- .../workflows/create_release_deprecated.yml | 249 ++++++++++++++++++ 2 files changed, 259 insertions(+), 233 deletions(-) create mode 100644 .github/workflows/create_release_deprecated.yml diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index 601733e..7cc5026 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -1,6 +1,3 @@ -# Test the toolbox across all supported releases of MATLAB, package toolbox, create release -# Adapted from: https://github.com/mathworks/climatedatastore/blob/main/.github/workflows/release.yml - name: Create new release # Run workflow when a tag is created @@ -16,234 +13,14 @@ on: type: string jobs: - # This workflow contains: - # 1. a validation of the provided version number - # 2. a matrixed test job run across a bunch of releases of MATLAB - # 3. a reporting job that summarizes the tests, and updates release badge - - validate_version: - runs-on: ubuntu-latest - outputs: - version: ${{ steps.set_version.outputs.version }} - version_tag: ${{ steps.set_version.outputs.version_tag }} - steps: - - name: Check for retag - if: ${{ contains(github.event.head_commit.message, '[skip-ci]') }} - run: | - echo "Error: Commit message contains [skip-ci], skipping." - exit 1 - - - name: Set version based on trigger type - id: set_version - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - # For manual trigger, use the input version - VERSION="${{ github.event.inputs.version }}" - # Validate format - if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Error: Input for 'version' ('$VERSION') is not in the expected major.minor.patch format." - exit 1 - fi - VERSION_TAG="v$VERSION" - else - # For tag trigger, use the tag name - VERSION_TAG="${{ github.ref_name }}" - # Remove 'v' prefix if present for the version number - VERSION="${VERSION_TAG#v}" - - # Validate format - if [[ ! "$VERSION_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Error: Tag name ('$VERSION_TAG') is not in the expected v*.*.* format." - exit 1 - fi - fi - - echo "Using version: $VERSION (tag: $VERSION_TAG)" - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "version_tag=$VERSION_TAG" >> $GITHUB_OUTPUT - - test: - name: Run MATLAB tests (${{ matrix.MATLABVersion }}) - needs: [validate_version] - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - MATLABVersion: [R2023a, R2023b, R2024a, R2024b] - include: - - MATLABVersion: R2023a - pythonVersion: '3.10' - - MATLABVersion: R2023b - pythonVersion: '3.11' - - MATLABVersion: R2024a - pythonVersion: '3.11' - - MATLABVersion: R2024b - pythonVersion: '3.12' - - steps: - # Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.pythonVersion }} - - - name: Set up MATLAB - uses: matlab-actions/setup-matlab@v2 - with: - release: ${{ matrix.MATLABVersion }} - - # Runs all tests in the project. Put results in a version specific subdirectory - - name: Run tests - uses: matlab-actions/run-command@v2 - with: - command: | - addpath(genpath("tools")); - testToolbox('ReportSubdirectory', "${{ matrix.MATLABVersion }}", 'CreateBadge', false); - - # Upload code coverage information to Codecov - - name: Upload code coverage report to Codecov - uses: codecov/codecov-action@v4 - if: always() - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: docs/reports/codecoverage.xml - env_vars: ${{ matrix.MATLABVersion }} - - # Save the contents of the report directory from each release into an artifact. - - name: Save report directory - uses: actions/upload-artifact@v4 - if: always() - with: - name: reports-${{ matrix.MATLABVersion }} - path: docs/reports - - # Report on what releases tested successfully. - # Generate a draft release based on the tag - # Recreate the tag with the final version of files release: - needs: [test, validate_version] - runs-on: ubuntu-latest - env: - VERSION: ${{ needs.validate_version.outputs.version }} # needed? - VERSION_TAG: ${{ needs.validate_version.outputs.version_tag }} - - steps: - # Use deploy key to push back to protected branch - - name: Checkout repository using deploy key - uses: actions/checkout@v4 - with: - ref: refs/heads/main - ssh-key: ${{ secrets.DEPLOY_KEY }} - - - name: Set up MATLAB - uses: matlab-actions/setup-matlab@v2 - - # Copy all the reports down into the container - - uses: actions/download-artifact@v4 - with: - pattern: reports-* - path: docs/reports - merge-multiple: true - - # Generate the JSON for the releases tested badge - - name: Generate tested with badge - uses: matlab-actions/run-command@v2 - with: - command: addpath(genpath("tools")), createTestedWithBadgeforToolbox("${{ env.VERSION_TAG }}") - - # Publish test results from all the releases - - name: Publish test results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - junit_files: "docs/reports/*/test-results.xml" - - # Package the MLTBX - - name: Package toolbox - uses: matlab-actions/run-command@v2 - with: - command: addpath(genpath("tools")), packageToolbox("specific","${{ env.VERSION_TAG }}") - - # Define the versionNumber using underscores, as this is used in the MLTBX - - name: Set version number - id: set_version - run: | - versionNumber=$(echo "${{ env.VERSION_TAG }}" | sed 's/\./_/g') - echo "versionNumber=$versionNumber" >> $GITHUB_ENV - - # Save the MLTBX. - - name: Save packaged toolbox - uses: actions/upload-artifact@v4 - with: - name: MatBox${{ env.versionNumber }}.mltbx - path: releases/MatBox_${{ env.versionNumber }}.mltbx - - # Commit the updated Contents.m - - name: Commit updated Contents.m file - continue-on-error: true - run: | - git config user.name "${{ github.workflow }} by ${{ github.actor }}" - git config user.email "<>" - git status - git add code/Contents.m - git commit -m "Final check-ins for release ${{ env.VERSION_TAG }} [skip-ci]" - git fetch - git push - - # Commit the JSON for the MATLAB releases test badge to gh-badges branch - - name: Checkout gh-badges branch - uses: actions/checkout@v4 - with: - ref: gh-badges - path: gh-badges - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Push to gh-badges - run: | - mkdir -p gh-badges/.github/badges/${{ env.VERSION_TAG }} - cp .github/badges/${{ env.VERSION_TAG }}/tested_with.json gh-badges/.github/badges/${{ env.VERSION_TAG }}/tested_with.json - cd gh-badges - - git config user.name "${{ github.workflow }} by ${{ github.actor }}" - git config user.email "<>" - - # Only proceed with commit and push if changes are detected - if [[ $(git add .github/badges/* --dry-run | wc -l) -gt 0 ]]; then - git add .github/badges/* - git commit -m "Update tested with badge for release" - git push -f - else - echo "Nothing to commit" - fi - - # Retag the repo so that the updated files are included in the release tag - - name: Update tag - if: always() - continue-on-error: true - run: | - git config user.name "${{ github.workflow }} by ${{ github.actor }}" - git config user.email "<>" - - # Delete the existing tag locally and remotely - git tag -d "${{ env.VERSION_TAG }}" - git push origin --delete "${{ env.VERSION_TAG }}" - - # Recreate the tag with a message, including [skip ci] to prevent CI workflows - git tag -a "${{ env.VERSION_TAG }}" -m "Release ${{ env.VERSION_TAG }} [skip ci]" - - # Push the new tag to the remote repository - git push origin "${{ env.VERSION_TAG }}" - - # Create the release - - name: Create GitHub release - uses: ncipollo/release-action@v1 - with: - draft: true - artifacts: "releases/MatBox_${{ env.versionNumber }}.mltbx" - tag: ${{ env.VERSION_TAG }} - generateReleaseNotes: true - body: "![MATLAB Versions Tested](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fehennestad%2Fmatbox%2Fgh-badges%2F.github%2Fbadges%2F${{ env.VERSION_TAG }}%2Ftested_with.json)" - + uses: ehennestad/matbox/.github/workflows/reusable_release_workflow.yml@make-release-workflow-reusable + with: + version: ${{ github.event.inputs.version }} + ref_name: ${{ github.ref_name }} + code_directory: 'code' + tools_directory: 'tools' + matlab_versions: '["R2023a", "R2023b", "R2024a", "R2024b"]' + python_versions: '{"R2023a": "3.10", "R2023b": "3.11", "R2024a": "3.11", "R2024b": "3.12"}' + secrets: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} diff --git a/.github/workflows/create_release_deprecated.yml b/.github/workflows/create_release_deprecated.yml new file mode 100644 index 0000000..601733e --- /dev/null +++ b/.github/workflows/create_release_deprecated.yml @@ -0,0 +1,249 @@ +# Test the toolbox across all supported releases of MATLAB, package toolbox, create release +# Adapted from: https://github.com/mathworks/climatedatastore/blob/main/.github/workflows/release.yml + +name: Create new release + +# Run workflow when a tag is created +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' # Matches tags like v1.2.3 + workflow_dispatch: + inputs: + version: + description: 'Version number in major.minor.patch format, i.e 0.9.x' + required: true + type: string + +jobs: + # This workflow contains: + # 1. a validation of the provided version number + # 2. a matrixed test job run across a bunch of releases of MATLAB + # 3. a reporting job that summarizes the tests, and updates release badge + + validate_version: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.set_version.outputs.version }} + version_tag: ${{ steps.set_version.outputs.version_tag }} + steps: + - name: Check for retag + if: ${{ contains(github.event.head_commit.message, '[skip-ci]') }} + run: | + echo "Error: Commit message contains [skip-ci], skipping." + exit 1 + + - name: Set version based on trigger type + id: set_version + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + # For manual trigger, use the input version + VERSION="${{ github.event.inputs.version }}" + # Validate format + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Input for 'version' ('$VERSION') is not in the expected major.minor.patch format." + exit 1 + fi + VERSION_TAG="v$VERSION" + else + # For tag trigger, use the tag name + VERSION_TAG="${{ github.ref_name }}" + # Remove 'v' prefix if present for the version number + VERSION="${VERSION_TAG#v}" + + # Validate format + if [[ ! "$VERSION_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Tag name ('$VERSION_TAG') is not in the expected v*.*.* format." + exit 1 + fi + fi + + echo "Using version: $VERSION (tag: $VERSION_TAG)" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "version_tag=$VERSION_TAG" >> $GITHUB_OUTPUT + + test: + name: Run MATLAB tests (${{ matrix.MATLABVersion }}) + needs: [validate_version] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + MATLABVersion: [R2023a, R2023b, R2024a, R2024b] + include: + - MATLABVersion: R2023a + pythonVersion: '3.10' + - MATLABVersion: R2023b + pythonVersion: '3.11' + - MATLABVersion: R2024a + pythonVersion: '3.11' + - MATLABVersion: R2024b + pythonVersion: '3.12' + + steps: + # Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.pythonVersion }} + + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + with: + release: ${{ matrix.MATLABVersion }} + + # Runs all tests in the project. Put results in a version specific subdirectory + - name: Run tests + uses: matlab-actions/run-command@v2 + with: + command: | + addpath(genpath("tools")); + testToolbox('ReportSubdirectory', "${{ matrix.MATLABVersion }}", 'CreateBadge', false); + + # Upload code coverage information to Codecov + - name: Upload code coverage report to Codecov + uses: codecov/codecov-action@v4 + if: always() + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: docs/reports/codecoverage.xml + env_vars: ${{ matrix.MATLABVersion }} + + # Save the contents of the report directory from each release into an artifact. + - name: Save report directory + uses: actions/upload-artifact@v4 + if: always() + with: + name: reports-${{ matrix.MATLABVersion }} + path: docs/reports + + # Report on what releases tested successfully. + # Generate a draft release based on the tag + # Recreate the tag with the final version of files + release: + needs: [test, validate_version] + runs-on: ubuntu-latest + env: + VERSION: ${{ needs.validate_version.outputs.version }} # needed? + VERSION_TAG: ${{ needs.validate_version.outputs.version_tag }} + + steps: + # Use deploy key to push back to protected branch + - name: Checkout repository using deploy key + uses: actions/checkout@v4 + with: + ref: refs/heads/main + ssh-key: ${{ secrets.DEPLOY_KEY }} + + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + + # Copy all the reports down into the container + - uses: actions/download-artifact@v4 + with: + pattern: reports-* + path: docs/reports + merge-multiple: true + + # Generate the JSON for the releases tested badge + - name: Generate tested with badge + uses: matlab-actions/run-command@v2 + with: + command: addpath(genpath("tools")), createTestedWithBadgeforToolbox("${{ env.VERSION_TAG }}") + + # Publish test results from all the releases + - name: Publish test results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + junit_files: "docs/reports/*/test-results.xml" + + # Package the MLTBX + - name: Package toolbox + uses: matlab-actions/run-command@v2 + with: + command: addpath(genpath("tools")), packageToolbox("specific","${{ env.VERSION_TAG }}") + + # Define the versionNumber using underscores, as this is used in the MLTBX + - name: Set version number + id: set_version + run: | + versionNumber=$(echo "${{ env.VERSION_TAG }}" | sed 's/\./_/g') + echo "versionNumber=$versionNumber" >> $GITHUB_ENV + + # Save the MLTBX. + - name: Save packaged toolbox + uses: actions/upload-artifact@v4 + with: + name: MatBox${{ env.versionNumber }}.mltbx + path: releases/MatBox_${{ env.versionNumber }}.mltbx + + # Commit the updated Contents.m + - name: Commit updated Contents.m file + continue-on-error: true + run: | + git config user.name "${{ github.workflow }} by ${{ github.actor }}" + git config user.email "<>" + git status + git add code/Contents.m + git commit -m "Final check-ins for release ${{ env.VERSION_TAG }} [skip-ci]" + git fetch + git push + + # Commit the JSON for the MATLAB releases test badge to gh-badges branch + - name: Checkout gh-badges branch + uses: actions/checkout@v4 + with: + ref: gh-badges + path: gh-badges + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Push to gh-badges + run: | + mkdir -p gh-badges/.github/badges/${{ env.VERSION_TAG }} + cp .github/badges/${{ env.VERSION_TAG }}/tested_with.json gh-badges/.github/badges/${{ env.VERSION_TAG }}/tested_with.json + cd gh-badges + + git config user.name "${{ github.workflow }} by ${{ github.actor }}" + git config user.email "<>" + + # Only proceed with commit and push if changes are detected + if [[ $(git add .github/badges/* --dry-run | wc -l) -gt 0 ]]; then + git add .github/badges/* + git commit -m "Update tested with badge for release" + git push -f + else + echo "Nothing to commit" + fi + + # Retag the repo so that the updated files are included in the release tag + - name: Update tag + if: always() + continue-on-error: true + run: | + git config user.name "${{ github.workflow }} by ${{ github.actor }}" + git config user.email "<>" + + # Delete the existing tag locally and remotely + git tag -d "${{ env.VERSION_TAG }}" + git push origin --delete "${{ env.VERSION_TAG }}" + + # Recreate the tag with a message, including [skip ci] to prevent CI workflows + git tag -a "${{ env.VERSION_TAG }}" -m "Release ${{ env.VERSION_TAG }} [skip ci]" + + # Push the new tag to the remote repository + git push origin "${{ env.VERSION_TAG }}" + + # Create the release + - name: Create GitHub release + uses: ncipollo/release-action@v1 + with: + draft: true + artifacts: "releases/MatBox_${{ env.versionNumber }}.mltbx" + tag: ${{ env.VERSION_TAG }} + generateReleaseNotes: true + body: "![MATLAB Versions Tested](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fehennestad%2Fmatbox%2Fgh-badges%2F.github%2Fbadges%2F${{ env.VERSION_TAG }}%2Ftested_with.json)" + From 92669de8ddb5bbe557e507ba4f92efce26138d66 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 09:53:55 +0200 Subject: [PATCH 20/45] Update release workflow mltbx path is returned from packageToolbox function and used in workflow matlab and python versions are determined dynamically --- .github/actions/build-test-matrix/action.yml | 127 ++++++++++++++++++ .github/actions/package-toolbox/action.yml | 11 +- .../workflows/reusable_release_workflow.yml | 35 +++-- code/+matbox/+tasks/packageToolbox.m | 5 +- tools/tasks/packageToolbox.m | 4 +- 5 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 .github/actions/build-test-matrix/action.yml diff --git a/.github/actions/build-test-matrix/action.yml b/.github/actions/build-test-matrix/action.yml new file mode 100644 index 0000000..899f056 --- /dev/null +++ b/.github/actions/build-test-matrix/action.yml @@ -0,0 +1,127 @@ +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_fallback: + description: 'Fallback MATLAB versions if auto-detection fails' + required: false + default: '["R2023a", "R2023b", "R2024a", "R2024b"]' + python_versions: + description: 'JSON object mapping MATLAB versions to Python versions' + required: false + default: '{"R2023a": "3.10", "R2023b": "3.11", "R2024a": "3.11", "R2024b": "3.12"}' + +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: | + # Read min and max MATLAB releases from MLToolboxInfo.json + min_release=$(jq -r '.ToolboxOptions.MinimumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json") + max_release=$(jq -r '.ToolboxOptions.MaximumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json") + + # If max_release is empty or null, get the latest MATLAB release + if [ "$max_release" = "null" ] || [ "$max_release" = "" ]; then + max_release=$(curl -s "https://ssd.mathworks.com/supportfiles/ci/matlab-release/v0/latest") + fi + + echo "Min release: $min_release" + echo "Max release: $max_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: | + # Use determined MATLAB versions or fall back to input + matlab_versions='${{ steps.determine.outputs.matlab_versions }}' + if [ "$matlab_versions" = "" ] || [ "$matlab_versions" = "null" ]; then + matlab_versions='${{ inputs.matlab_versions_fallback }}' + echo "Using fallback MATLAB versions: $matlab_versions" + else + echo "Using determined MATLAB versions: $matlab_versions" + fi + + python_versions='${{ inputs.python_versions }}' + + # 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 + matrix="{\"MATLABVersion\":$matlab_versions,\"include\":[$include_items]}" + echo "Generated matrix: $matrix" + echo "matrix=$matrix" >> $GITHUB_OUTPUT diff --git a/.github/actions/package-toolbox/action.yml b/.github/actions/package-toolbox/action.yml index 7b28947..2c1fb1e 100644 --- a/.github/actions/package-toolbox/action.yml +++ b/.github/actions/package-toolbox/action.yml @@ -29,24 +29,29 @@ 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") - packageToolbox("specific", versionNumberStr); + [~, mltbxPath] = packageToolbox("specific", versionNumberStr); else - matbox.tasks.packageToolbox(pwd, "specific", versionNumberStr, ... + [~, 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: | versionUnderscore=$(echo "${{ inputs.version_number }}" | sed 's/\./_/g') toolboxName=$(jq -r '.ToolboxOptions.ToolboxName' "${{ inputs.tools_directory }}/MLToolboxInfo.json") + mltbxPath=$(cat mltbx_path.txt) echo "version_underscore=$versionUnderscore" >> $GITHUB_OUTPUT echo "toolbox_name=$toolboxName" >> $GITHUB_OUTPUT - echo "mltbx_path=releases/${toolboxName}_v${versionUnderscore}.mltbx" >> $GITHUB_OUTPUT + echo "mltbx_path=$mltbxPath" >> $GITHUB_OUTPUT diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 52c6a07..9c41302 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -22,7 +22,7 @@ on: matlab_versions: type: string default: '["R2023a", "R2023b", "R2024a", "R2024b"]' - description: 'JSON array of MATLAB versions to test' + description: 'JSON array of MATLAB versions to test (optional - will be determined from MLToolboxInfo.json if not provided)' python_versions: type: string default: '{"R2023a": "3.10", "R2023b": "3.11", "R2024a": "3.11", "R2024b": "3.12"}' @@ -57,23 +57,30 @@ jobs: version: ${{ inputs.version }} ref_name: ${{ inputs.ref_name }} + build_matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.build_matrix.outputs.matrix }} + matlab_versions: ${{ steps.build_matrix.outputs.matlab_versions }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build test matrix + id: build_matrix + uses: ehennestad/matbox/.github/actions/build-test-matrix@make-release-workflow-reusable + with: + tools_directory: ${{ inputs.tools_directory }} + matlab_versions_fallback: ${{ inputs.matlab_versions }} + python_versions: ${{ inputs.python_versions }} + test: name: Run MATLAB tests (${{ matrix.MATLABVersion }}) - needs: [validate_version] + needs: [validate_version, build_matrix] runs-on: ubuntu-latest strategy: fail-fast: false - matrix: - MATLABVersion: ${{ fromJSON(inputs.matlab_versions) }} - include: - - MATLABVersion: R2023a - pythonVersion: '3.10' - - MATLABVersion: R2023b - pythonVersion: '3.11' - - MATLABVersion: R2024a - pythonVersion: '3.11' - - MATLABVersion: R2024b - pythonVersion: '3.12' + matrix: ${{ fromJSON(needs.build_matrix.outputs.matrix) }} steps: # Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it @@ -134,7 +141,7 @@ jobs: merge-multiple: true # Generate badge - - name: Generate tested with badge + - name: Generate "tested with" badge uses: ehennestad/matbox/.github/actions/update-badges@make-release-workflow-reusable with: version_number: ${{ needs.validate_version.outputs.version_number }} diff --git a/code/+matbox/+tasks/packageToolbox.m b/code/+matbox/+tasks/packageToolbox.m index 422e7b2..d2ba77a 100644 --- a/code/+matbox/+tasks/packageToolbox.m +++ b/code/+matbox/+tasks/packageToolbox.m @@ -1,4 +1,4 @@ -function newVersion = packageToolbox(projectRootDirectory, releaseType, versionString, options) +function [newVersion, mltbxPath] = packageToolbox(projectRootDirectory, releaseType, versionString, options) % packageToolbox Package a new version of a toolbox. Package a new version % of the toolbox based on the toolbox packaging (.prj) file in current % working directory. MLTBX file is put in ./release directory. @@ -84,6 +84,9 @@ fprintf('Exported MLTBX to %s\n', finalOutputFile) + % Return the MLTBX path + mltbxPath = finalOutputFile; + if ~nargout clear newVersion end diff --git a/tools/tasks/packageToolbox.m b/tools/tasks/packageToolbox.m index 7db10f2..93733a5 100644 --- a/tools/tasks/packageToolbox.m +++ b/tools/tasks/packageToolbox.m @@ -1,4 +1,4 @@ -function packageToolbox(releaseType, versionString) +function [newVersion, mltbxPath] = packageToolbox(releaseType, versionString) arguments releaseType {mustBeTextScalar,mustBeMember(releaseType,["build","major","minor","patch","specific"])} = "build" versionString {mustBeTextScalar} = ""; @@ -6,5 +6,5 @@ function packageToolbox(releaseType, versionString) installMatBox() projectRootDir = matboxtools.projectdir(); addpath(genpath(projectRootDir)) - matbox.tasks.packageToolbox(projectRootDir, releaseType, versionString) + [newVersion, mltbxPath] = matbox.tasks.packageToolbox(projectRootDir, releaseType, versionString); end From 0e7eb529b6b1e02886cb7785fe36d10627766729 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 10:09:09 +0200 Subject: [PATCH 21/45] Update action.yml --- .github/actions/build-test-matrix/action.yml | 32 +++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/actions/build-test-matrix/action.yml b/.github/actions/build-test-matrix/action.yml index 899f056..31c0c75 100644 --- a/.github/actions/build-test-matrix/action.yml +++ b/.github/actions/build-test-matrix/action.yml @@ -9,11 +9,21 @@ inputs: matlab_versions_fallback: description: 'Fallback MATLAB versions if auto-detection fails' required: false - default: '["R2023a", "R2023b", "R2024a", "R2024b"]' + default: '["R2021a", "R2021b", "R2022a", "R2022b", "R2023a", "R2023b", "R2024a", "R2024b"]' python_versions: description: 'JSON object mapping MATLAB versions to Python versions' required: false - default: '{"R2023a": "3.10", "R2023b": "3.11", "R2024a": "3.11", "R2024b": "3.12"}' + default: > + '{ + "R2021a": "3.8", + "R2021b": "3.9", + "R2022a": "3.9", + "R2022b": "3.10", + "R2023a": "3.10", + "R2023b": "3.11", + "R2024a": "3.11", + "R2024b": "3.12" + }' outputs: matrix: @@ -31,15 +41,29 @@ runs: shell: bash run: | # Read min and max MATLAB releases from MLToolboxInfo.json - min_release=$(jq -r '.ToolboxOptions.MinimumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json") + min_release_raw=$(jq -r '.ToolboxOptions.MinimumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json") max_release=$(jq -r '.ToolboxOptions.MaximumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json") + # Set minimum release to R2021a if not specified or if specified release is older + min_release="R2021a" + if [ "$min_release_raw" != "null" ] && [ "$min_release_raw" != "" ]; then + # Compare releases to ensure we don't go below R2021a + min_year=${min_release_raw:1:4} + min_letter=${min_release_raw:5:1} + + # If the specified minimum is R2021a or newer, use it + if [ $min_year -gt 2021 ] || ([ $min_year -eq 2021 ] && [ "$min_letter" != "a" ]); then + min_release="$min_release_raw" + fi + fi + # If max_release is empty or null, get the latest MATLAB release if [ "$max_release" = "null" ] || [ "$max_release" = "" ]; then max_release=$(curl -s "https://ssd.mathworks.com/supportfiles/ci/matlab-release/v0/latest") fi - echo "Min release: $min_release" + echo "Min release from MLToolboxInfo: $min_release_raw" + echo "Min release for GitHub Actions: $min_release" echo "Max release: $max_release" # Generate MATLAB releases between min and max From f5d0eb2e729eb2fdf31aca498849118ad2cfa950 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 11:43:48 +0200 Subject: [PATCH 22/45] Make python installation optional fro matrix testing in release workflow --- .github/actions/build-test-matrix/action.yml | 180 +++++++++++++----- .github/actions/build-test-matrix/config.json | 15 ++ .../workflows/reusable_release_workflow.yml | 12 +- 3 files changed, 160 insertions(+), 47 deletions(-) create mode 100644 .github/actions/build-test-matrix/config.json diff --git a/.github/actions/build-test-matrix/action.yml b/.github/actions/build-test-matrix/action.yml index 31c0c75..a404dbb 100644 --- a/.github/actions/build-test-matrix/action.yml +++ b/.github/actions/build-test-matrix/action.yml @@ -6,24 +6,18 @@ inputs: description: 'Directory containing MLToolboxInfo.json' required: false default: 'tools' - matlab_versions_fallback: - description: 'Fallback MATLAB versions if auto-detection fails' + matlab_versions: + description: 'MATLAB versions to test (overrides auto-detection from MLToolboxInfo.json)' required: false - default: '["R2021a", "R2021b", "R2022a", "R2022b", "R2023a", "R2023b", "R2024a", "R2024b"]' + default: '[]' python_versions: - description: 'JSON object mapping MATLAB versions to 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: > - '{ - "R2021a": "3.8", - "R2021b": "3.9", - "R2022a": "3.9", - "R2022b": "3.10", - "R2023a": "3.10", - "R2023b": "3.11", - "R2024a": "3.11", - "R2024b": "3.12" - }' + default: 'true' outputs: matrix: @@ -40,31 +34,53 @@ runs: 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=$(jq -r '.ToolboxOptions.MaximumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json") + max_release_raw=$(jq -r '.ToolboxOptions.MaximumMatlabRelease' "${{ inputs.tools_directory }}/MLToolboxInfo.json") - # Set minimum release to R2021a if not specified or if specified release is older - min_release="R2021a" + # 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 R2021a + # 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 R2021a or newer, use it - if [ $min_year -gt 2021 ] || ([ $min_year -eq 2021 ] && [ "$min_letter" != "a" ]); then + # 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 - # If max_release is empty or null, get the latest MATLAB release - if [ "$max_release" = "null" ] || [ "$max_release" = "" ]; then - max_release=$(curl -s "https://ssd.mathworks.com/supportfiles/ci/matlab-release/v0/latest") + # 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: $max_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() { @@ -122,30 +138,106 @@ runs: id: build shell: bash run: | - # Use determined MATLAB versions or fall back to input - matlab_versions='${{ steps.determine.outputs.matlab_versions }}' - if [ "$matlab_versions" = "" ] || [ "$matlab_versions" = "null" ]; then - matlab_versions='${{ inputs.matlab_versions_fallback }}' - echo "Using fallback MATLAB versions: $matlab_versions" + # 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 - echo "Using determined MATLAB versions: $matlab_versions" + # Use auto-determined MATLAB versions from previous step + matlab_versions='${{ steps.determine.outputs.matlab_versions }}' + echo "Using auto-determined MATLAB versions: $matlab_versions" fi - python_versions='${{ inputs.python_versions }}' + # Check if Python should be included in the matrix + include_python='${{ inputs.include_python }}' - # 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\"}" + 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 - done + + # 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 - # Create the full matrix - matrix="{\"MATLABVersion\":$matlab_versions,\"include\":[$include_items]}" echo "Generated matrix: $matrix" echo "matrix=$matrix" >> $GITHUB_OUTPUT diff --git a/.github/actions/build-test-matrix/config.json b/.github/actions/build-test-matrix/config.json new file mode 100644 index 0000000..1a99a97 --- /dev/null +++ b/.github/actions/build-test-matrix/config.json @@ -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" + } +} diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 9c41302..e045708 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -21,12 +21,16 @@ on: description: 'Directory containing tools' matlab_versions: type: string - default: '["R2023a", "R2023b", "R2024a", "R2024b"]' + default: '[]' description: 'JSON array of MATLAB versions to test (optional - will be determined from MLToolboxInfo.json if not provided)' python_versions: type: string - default: '{"R2023a": "3.10", "R2023b": "3.11", "R2024a": "3.11", "R2024b": "3.12"}' + default: '' description: 'JSON object mapping MATLAB versions to Python versions' + needs_python: + type: boolean + default: false + description: 'Whether Python is needed for testing (controls if Python versions are included in test matrix)' secrets: DEPLOY_KEY: required: true @@ -71,8 +75,9 @@ jobs: uses: ehennestad/matbox/.github/actions/build-test-matrix@make-release-workflow-reusable with: tools_directory: ${{ inputs.tools_directory }} - matlab_versions_fallback: ${{ inputs.matlab_versions }} + matlab_versions: ${{ inputs.matlab_versions }} python_versions: ${{ inputs.python_versions }} + include_python: ${{ inputs.needs_python }} test: name: Run MATLAB tests (${{ matrix.MATLABVersion }}) @@ -88,6 +93,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python + if: ${{ inputs.needs_python }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.pythonVersion }} From 2fba4e2896fab06daa42417c3a8c5d1a1d7082ff Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 12:04:50 +0200 Subject: [PATCH 23/45] Add workflow template for preparing release --- .../prepare-release.properties.json | 10 ++++ .../workflow-templates/prepare-release.yml | 55 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 .github/workflow-templates/prepare-release.properties.json create mode 100644 .github/workflow-templates/prepare-release.yml diff --git a/.github/workflow-templates/prepare-release.properties.json b/.github/workflow-templates/prepare-release.properties.json new file mode 100644 index 0000000..8650a6c --- /dev/null +++ b/.github/workflow-templates/prepare-release.properties.json @@ -0,0 +1,10 @@ +{ + "name": "Prepare toolbox release", + "description": "Test toolbox code across supported MATLAB releases, package toolbox and create a draft release on GitHub.", + "iconName": "octicon gift", + "categories": [ + "testing", + "utilities", + "MATLAB" + ] +} diff --git a/.github/workflow-templates/prepare-release.yml b/.github/workflow-templates/prepare-release.yml new file mode 100644 index 0000000..d355e3d --- /dev/null +++ b/.github/workflow-templates/prepare-release.yml @@ -0,0 +1,55 @@ +name: Prepare toolbox release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + version: + description: 'Version number in major.minor.patch format' + required: true + type: string + +jobs: + reuse-workflow-release: + name: Prepare toolbox release + uses: ehennestad/matbox/.github/workflows/reusable_release_workflow.yml@make-release-workflow-reusable + with: + # Do not change + version: ${{ inputs.version }} + + # Do not change + ref_name: ${{ github.ref_name }} + + # Path to directory containing source code. This directory will be packaged + # into the toolbox. Important: This should not be a namespace directory + code_directory: code + + # Path to directory containing tools and MLToolboxInfo.json. Used for finding + # unit tests, running customized MatBox tasks, and determining toolbox metadata + tools_directory: tools + + # JSON array of MATLAB versions to test. If empty (default), versions will be + # automatically determined from MLToolboxInfo.json. Example for manual override: + # matlab_versions: '["R2023a", "R2023b", "R2024a"]' + matlab_versions: '[]' + + # JSON object mapping MATLAB versions to Python versions. Use this to override + # specific Python versions for certain MATLAB releases. Only specify versions + # you want to override - others will use latest supported release from + # https://se.mathworks.com/support/requirements/python-compatibility.html + # Example: + # python_versions: '{"R2024a": "3.10", "R2024b": "3.11"}' + python_versions: '{}' + + # Whether Python is needed for testing. Set to true if your toolbox requires + # Python functionality. When false, Python setup is skipped and only MATLAB + # versions are included in the test matrix + needs_python: false + + secrets: + # SSH deploy key for pushing to protected branches. Required for creating + # releases and updating badges. Generate an SSH key pair and add the public + # key as a deploy key with write access, then add the private key as this secret + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} From 871dabf1cd0bbb2cd9fb53d222ecf225539228fa Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 13:02:24 +0200 Subject: [PATCH 24/45] Update TasksTest.m Fix faulty test --- tools/tests/+matboxtools/+unittest/TasksTest.m | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/tests/+matboxtools/+unittest/TasksTest.m b/tools/tests/+matboxtools/+unittest/TasksTest.m index b0d4a9a..3028ce7 100644 --- a/tools/tests/+matboxtools/+unittest/TasksTest.m +++ b/tools/tests/+matboxtools/+unittest/TasksTest.m @@ -39,6 +39,7 @@ function testPackageToolbox(testCase) copyfile(pathStr, pwd); if isfolder(fullfile(pwd, 'releases')) rmdir(fullfile(pwd, 'releases'), 's') + mkdir(fullfile(pwd, 'releases')) end matbox.tasks.packageToolbox(pwd, "build", "") testCase.verifyTrue(isfolder(fullfile(pwd, "releases"))) From cea401059ed62be84098c2daeb86ba6e6334af22 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 13:10:30 +0200 Subject: [PATCH 25/45] Update reusable_release_workflow.yml --- .github/workflows/reusable_release_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index e045708..5ad6b5d 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -104,7 +104,7 @@ jobs: release: ${{ matrix.MATLABVersion }} - name: Install MatBox - uses: ehennestad/matbox/.github/actions/install-matbox@v0.9 + uses: ehennestad/matbox/.github/actions/install-matbox@make-release-workflow-reusable # Runs all tests in the project. Put results in a version specific subdirectory - name: Run tests From f92c538e84e8aeb125573e19f4dc087929ba6a2c Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 14:11:59 +0200 Subject: [PATCH 26/45] Added job to verify packaged toolbox can be installed --- .github/actions/package-toolbox/action.yml | 5 -- .../workflows/reusable_release_workflow.yml | 58 ++++++++++++++++++- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/.github/actions/package-toolbox/action.yml b/.github/actions/package-toolbox/action.yml index 2c1fb1e..8e8c913 100644 --- a/.github/actions/package-toolbox/action.yml +++ b/.github/actions/package-toolbox/action.yml @@ -18,9 +18,6 @@ outputs: mltbx_path: description: 'Path to the packaged MLTBX file' value: ${{ steps.set_version.outputs.mltbx_path }} - version_underscore: - description: 'Version number with underscores for file naming' - value: ${{ steps.set_version.outputs.version_underscore }} toolbox_name: description: 'Name of the toolbox from MLToolboxInfo.json' value: ${{ steps.set_version.outputs.toolbox_name }} @@ -49,9 +46,7 @@ runs: id: set_version shell: bash run: | - versionUnderscore=$(echo "${{ inputs.version_number }}" | sed 's/\./_/g') toolboxName=$(jq -r '.ToolboxOptions.ToolboxName' "${{ inputs.tools_directory }}/MLToolboxInfo.json") mltbxPath=$(cat mltbx_path.txt) - echo "version_underscore=$versionUnderscore" >> $GITHUB_OUTPUT echo "toolbox_name=$toolboxName" >> $GITHUB_OUTPUT echo "mltbx_path=$mltbxPath" >> $GITHUB_OUTPUT diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 5ad6b5d..03f8ed1 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -126,6 +126,8 @@ jobs: release: needs: [test, validate_version] runs-on: ubuntu-latest + outputs: + toolbox_name: ${{ steps.package.outputs.toolbox_name }} steps: - name: Checkout repository using deploy key uses: actions/checkout@v4 @@ -166,7 +168,7 @@ jobs: - name: Save packaged toolbox uses: actions/upload-artifact@v4 with: - name: ${{ steps.package.outputs.toolbox_name }}${{ steps.package.outputs.version_underscore }}.mltbx + name: ${{ steps.package.outputs.toolbox_name }}.mltbx path: ${{ steps.package.outputs.mltbx_path }} # Create GitHub release @@ -176,3 +178,57 @@ jobs: version_number: ${{ needs.validate_version.outputs.version_number }} mltbx_path: ${{ steps.package.outputs.mltbx_path }} version_underscore: ${{ steps.package.outputs.version_underscore }} + + verify_installation: + name: Verify toolbox installation (${{ matrix.os }}) + needs: [release, validate_version] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + + - name: Download packaged toolbox + uses: actions/download-artifact@v4 + with: + name: ${{ needs.release.outputs.toolbox_name }}.mltbx + + - name: Install and verify toolbox + uses: matlab-actions/run-command@v2 + with: + command: | + % Get the MLTBX file name + mltbxFiles = dir('*.mltbx'); + if isempty(mltbxFiles) + error('No MLTBX file found'); + end + mltbxFile = mltbxFiles(1).name; + + % Install the toolbox + fprintf('Installing toolbox: %s\n', mltbxFile); + agreeToLicense = true; + installedToolbox = matlab.addons.install(mltbxFile, agreeToLicense); + + % Verify installation + fprintf('Toolbox installed successfully:\n'); + fprintf(' Name: %s\n', installedToolbox.Name); + fprintf(' Version: %s\n', installedToolbox.Version); + fprintf(' Identifier: %s\n', installedToolbox.Identifier); + + % Try to access toolbox directory (if applicable) + try + tbxDir = toolboxdir(installedToolbox.Name); + if ~isempty(tbxDir) && isfolder(tbxDir) + fprintf(' Toolbox directory: %s\n', tbxDir); + else + fprintf(' Toolbox directory not found or not applicable\n'); + end + catch ME + fprintf(' Could not access toolbox directory: %s\n', ME.message); + end + + fprintf('Verification completed successfully on %s\n', computer); From 098b59c1bbc72545b38b57784abd3f699f72f77c Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 17:20:03 +0200 Subject: [PATCH 27/45] minor fixes --- .github/actions/update-badges/action.yml | 7 +++ .../workflows/reusable_release_workflow.yml | 44 +++++-------------- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/.github/actions/update-badges/action.yml b/.github/actions/update-badges/action.yml index 7578539..82b23fa 100644 --- a/.github/actions/update-badges/action.yml +++ b/.github/actions/update-badges/action.yml @@ -15,6 +15,13 @@ inputs: runs: using: "composite" steps: + - name: Download test reports + uses: actions/download-artifact@v4 + with: + pattern: reports-* + path: docs/reports + merge-multiple: true + - name: Generate tested with badge uses: matlab-actions/run-command@v2 with: diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 03f8ed1..6b32228 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -44,7 +44,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - + - name: Check for MLToolboxInfo.json run: | if [ ! -f "${{ inputs.tools_directory }}/MLToolboxInfo.json" ]; then @@ -53,7 +53,7 @@ jobs: exit 1 fi echo "MLToolboxInfo.json found in ${{ inputs.tools_directory }} directory" - + - name: Validate version id: validate uses: ehennestad/matbox/.github/actions/validate-version@make-release-workflow-reusable @@ -69,7 +69,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - + - name: Build test matrix id: build_matrix uses: ehennestad/matbox/.github/actions/build-test-matrix@make-release-workflow-reusable @@ -97,7 +97,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.pythonVersion }} - + - name: Set up MATLAB uses: matlab-actions/setup-matlab@v2 with: @@ -132,29 +132,21 @@ jobs: - name: Checkout repository using deploy key uses: actions/checkout@v4 with: - ref: refs/heads/main ssh-key: ${{ secrets.DEPLOY_KEY }} - + - name: Set up MATLAB uses: matlab-actions/setup-matlab@v2 - + - name: Install MatBox uses: ehennestad/matbox/.github/actions/install-matbox@make-release-workflow-reusable - - # Download test reports - - uses: actions/download-artifact@v4 - with: - pattern: reports-* - path: docs/reports - merge-multiple: true - + # Generate badge - name: Generate "tested with" badge uses: ehennestad/matbox/.github/actions/update-badges@make-release-workflow-reusable with: version_number: ${{ needs.validate_version.outputs.version_number }} tools_directory: ${{ inputs.tools_directory }} - + # Package toolbox - name: Package toolbox id: package @@ -170,7 +162,7 @@ jobs: with: name: ${{ steps.package.outputs.toolbox_name }}.mltbx path: ${{ steps.package.outputs.mltbx_path }} - + # Create GitHub release - name: Create GitHub release uses: ehennestad/matbox/.github/actions/create-github-release@make-release-workflow-reusable @@ -187,16 +179,16 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - + steps: - name: Set up MATLAB uses: matlab-actions/setup-matlab@v2 - + - name: Download packaged toolbox uses: actions/download-artifact@v4 with: name: ${{ needs.release.outputs.toolbox_name }}.mltbx - + - name: Install and verify toolbox uses: matlab-actions/run-command@v2 with: @@ -218,17 +210,5 @@ jobs: fprintf(' Name: %s\n', installedToolbox.Name); fprintf(' Version: %s\n', installedToolbox.Version); fprintf(' Identifier: %s\n', installedToolbox.Identifier); - - % Try to access toolbox directory (if applicable) - try - tbxDir = toolboxdir(installedToolbox.Name); - if ~isempty(tbxDir) && isfolder(tbxDir) - fprintf(' Toolbox directory: %s\n', tbxDir); - else - fprintf(' Toolbox directory not found or not applicable\n'); - end - catch ME - fprintf(' Could not access toolbox directory: %s\n', ME.message); - end fprintf('Verification completed successfully on %s\n', computer); From bcb13954a75c8724f1b5cefaa2b0c1ce60be14f9 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 19:47:59 +0200 Subject: [PATCH 28/45] minor changes --- .github/workflow-templates/test-code.yml | 8 +++++++- .github/workflows/reusable_release_workflow.yml | 1 + .github/workflows/reusable_run_codespell.yml | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflow-templates/test-code.yml b/.github/workflow-templates/test-code.yml index 73563c6..3848d83 100644 --- a/.github/workflow-templates/test-code.yml +++ b/.github/workflow-templates/test-code.yml @@ -3,9 +3,15 @@ name: Test code on: push: branches: [ $default-branch ] - + paths-ignore: + - '*md' + - '.github/**' + pull_request: branches: [ $default-branch ] + paths-ignore: + - '*md' + - '.github/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 6b32228..efeca09 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -162,6 +162,7 @@ jobs: with: name: ${{ steps.package.outputs.toolbox_name }}.mltbx path: ${{ steps.package.outputs.mltbx_path }} + retention-days: 1 # Create GitHub release - name: Create GitHub release diff --git a/.github/workflows/reusable_run_codespell.yml b/.github/workflows/reusable_run_codespell.yml index 66679f0..0544df4 100644 --- a/.github/workflows/reusable_run_codespell.yml +++ b/.github/workflows/reusable_run_codespell.yml @@ -1,4 +1,6 @@ -# NB: Will only process "skip" and "ignore-words-list" from the codespell +# Note: This workflow allows specifying a custom location for the Codespell +# configuration file by defining CONFIG_FILE as an input variable. +# Warning: Will only process "skip" and "ignore-words-list" from the codespell # config file if provided name: Run codespell From 06b7ffb0fc21be50230bba9809d9a13c997ff379 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 19:55:39 +0200 Subject: [PATCH 29/45] Update reusable_check_code.yml Made cache option an input --- .github/workflows/reusable_check_code.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable_check_code.yml b/.github/workflows/reusable_check_code.yml index fbc4c19..507dd9b 100644 --- a/.github/workflows/reusable_check_code.yml +++ b/.github/workflows/reusable_check_code.yml @@ -15,7 +15,10 @@ on: description: MATLAB release to use when running code analysis. type: string default: 'latest' - + matlab_use_cache: + description: Whether to cache the MATLAB installation for faster subsequent setups. + type: boolean + default: false jobs: # This workflow contains a single job called "check" check: @@ -30,7 +33,7 @@ jobs: uses: matlab-actions/setup-matlab@v2 with: release: ${{ inputs.matlab_release }} - cache: true + cache: ${{ inputs.matlab_use_cache }} - name: Install MatBox uses: ehennestad/matbox/.github/actions/install-matbox@v0.9 From fd85239b23727ba777b9ffdc26b0f430d02ebda4 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 20:20:49 +0200 Subject: [PATCH 30/45] Update reusable_release_workflow.yml --- .github/workflows/reusable_release_workflow.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index efeca09..80fa958 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -19,6 +19,10 @@ on: type: string default: 'tools' description: 'Directory containing tools' + matlab_products: + description: Optional list of MATLAB products to install. + type: string + default: '' matlab_versions: type: string default: '[]' @@ -102,6 +106,8 @@ jobs: uses: matlab-actions/setup-matlab@v2 with: release: ${{ matrix.MATLABVersion }} + products: ${{ inputs.matlab_products }} + - name: Install MatBox uses: ehennestad/matbox/.github/actions/install-matbox@make-release-workflow-reusable From f737c33d8bb1064d3e993ac32a3cb288dadcc85e Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 20:22:02 +0200 Subject: [PATCH 31/45] Update prepare-release.yml Added matlab_products input example --- .github/workflow-templates/prepare-release.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflow-templates/prepare-release.yml b/.github/workflow-templates/prepare-release.yml index d355e3d..6822ec6 100644 --- a/.github/workflow-templates/prepare-release.yml +++ b/.github/workflow-templates/prepare-release.yml @@ -35,6 +35,12 @@ jobs: # matlab_versions: '["R2023a", "R2023b", "R2024a"]' matlab_versions: '[]' + # Optional list of MATLAB products to install. Example if adding multiple + # products (use ">" and one product per line): + # matlab_products: > + # Image_Processing_Toolbox + # Statistics_and_Machine_Learning_Toolbox + # JSON object mapping MATLAB versions to Python versions. Use this to override # specific Python versions for certain MATLAB releases. Only specify versions # you want to override - others will use latest supported release from From 04f7dc509a4fa6372014588d6cbd4cd069da0d42 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 20:31:42 +0200 Subject: [PATCH 32/45] Update reusable_release_workflow.yml Added names for jobs --- .github/workflows/reusable_release_workflow.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 80fa958..6e4cfbf 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -42,6 +42,7 @@ on: jobs: validate_version: + name: Validate version number runs-on: ubuntu-latest outputs: version_number: ${{ steps.validate.outputs.version_number }} @@ -66,6 +67,7 @@ jobs: ref_name: ${{ inputs.ref_name }} build_matrix: + name: Build release test matrix runs-on: ubuntu-latest outputs: matrix: ${{ steps.build_matrix.outputs.matrix }} @@ -130,6 +132,7 @@ jobs: path: docs/reports release: + name: Package toolbox and create draft release needs: [test, validate_version] runs-on: ubuntu-latest outputs: From 5e413fe2fc5b28bad434fe6590c16d8aea89a919 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 20:44:23 +0200 Subject: [PATCH 33/45] Move deprecated workflow to deprecated folder --- .../{ => deprecated}/create_release_deprecated.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{ => deprecated}/create_release_deprecated.yml (99%) diff --git a/.github/workflows/create_release_deprecated.yml b/.github/workflows/deprecated/create_release_deprecated.yml similarity index 99% rename from .github/workflows/create_release_deprecated.yml rename to .github/workflows/deprecated/create_release_deprecated.yml index 601733e..2d77fa0 100644 --- a/.github/workflows/create_release_deprecated.yml +++ b/.github/workflows/deprecated/create_release_deprecated.yml @@ -5,9 +5,9 @@ name: Create new release # Run workflow when a tag is created on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' # Matches tags like v1.2.3 +# push: +# tags: +# - 'v[0-9]+.[0-9]+.[0-9]+' # Matches tags like v1.2.3 workflow_dispatch: inputs: version: From 6b3905a7fc9206eae49222cdcd2b01d3c8015dbf Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sun, 8 Jun 2025 20:44:42 +0200 Subject: [PATCH 34/45] Changed tag pattern --- .github/workflow-templates/prepare-release.yml | 2 +- .github/workflows/prepare-release.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflow-templates/prepare-release.yml b/.github/workflow-templates/prepare-release.yml index 6822ec6..ad59ca2 100644 --- a/.github/workflow-templates/prepare-release.yml +++ b/.github/workflow-templates/prepare-release.yml @@ -3,7 +3,7 @@ name: Prepare toolbox release on: push: tags: - - 'v*' + - 'v[0-9]+.[0-9]+.[0-9]+' # Matches tags like v1.2.3 workflow_dispatch: inputs: version: diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index e977773..8784289 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -3,7 +3,7 @@ name: Prepare toolbox release on: push: tags: - - 'v*' + - 'v[0-9]+.[0-9]+.[0-9]+' # Matches tags like v1.2.3 workflow_dispatch: inputs: version: @@ -12,8 +12,8 @@ on: type: string jobs: - reuse-workflow-release: - name: Prepare toolbox release + release: + name: Prepare toolbox release (MatBox) uses: ehennestad/matbox/.github/workflows/reusable_release_workflow.yml@make-release-workflow-reusable with: # Do not change From 6aeba833556a5c64205251db04e597214f5c76fc Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 16:07:17 +0200 Subject: [PATCH 35/45] Add "reusable job" workflows --- .../workflows/reusable-job_build-matrix.yml | 48 ++++++++++++ .../reusable-job_package-toolbox.yml | 78 +++++++++++++++++++ .github/workflows/reusable-job_run-tests.yml | 70 +++++++++++++++++ .../reusable-job_validate-version.yml | 47 +++++++++++ .../reusable-job_verify-installation.yml | 55 +++++++++++++ 5 files changed, 298 insertions(+) create mode 100644 .github/workflows/reusable-job_build-matrix.yml create mode 100644 .github/workflows/reusable-job_package-toolbox.yml create mode 100644 .github/workflows/reusable-job_run-tests.yml create mode 100644 .github/workflows/reusable-job_validate-version.yml create mode 100644 .github/workflows/reusable-job_verify-installation.yml diff --git a/.github/workflows/reusable-job_build-matrix.yml b/.github/workflows/reusable-job_build-matrix.yml new file mode 100644 index 0000000..9169de3 --- /dev/null +++ b/.github/workflows/reusable-job_build-matrix.yml @@ -0,0 +1,48 @@ +name: Reusable Job - Build Matrix + +on: + workflow_call: + inputs: + tools_directory: + type: string + default: 'tools' + description: 'Directory containing tools' + matlab_versions: + type: string + default: '[]' + description: 'JSON array of MATLAB versions to test (optional - will be determined from MLToolboxInfo.json if not provided)' + python_versions: + type: string + default: '' + description: 'JSON object mapping MATLAB versions to Python versions' + needs_python: + type: boolean + default: false + description: 'Whether Python is needed for testing (controls if Python versions are included in test matrix)' + outputs: + matrix: + description: 'Test matrix JSON' + value: ${{ jobs.build_matrix.outputs.matrix }} + matlab_versions: + description: 'MATLAB versions array' + value: ${{ jobs.build_matrix.outputs.matlab_versions }} + +jobs: + build_matrix: + name: Build release test matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.build_matrix.outputs.matrix }} + matlab_versions: ${{ steps.build_matrix.outputs.matlab_versions }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build test matrix + id: build_matrix + uses: ehennestad/matbox/.github/actions/build-test-matrix@make-release-workflow-reusable + with: + tools_directory: ${{ inputs.tools_directory }} + matlab_versions: ${{ inputs.matlab_versions }} + python_versions: ${{ inputs.python_versions }} + include_python: ${{ inputs.needs_python }} diff --git a/.github/workflows/reusable-job_package-toolbox.yml b/.github/workflows/reusable-job_package-toolbox.yml new file mode 100644 index 0000000..99c0160 --- /dev/null +++ b/.github/workflows/reusable-job_package-toolbox.yml @@ -0,0 +1,78 @@ +name: Reusable Job - Package Toolbox + +on: + workflow_call: + inputs: + version_number: + type: string + required: true + description: 'Version number for the release' + code_directory: + type: string + default: 'code' + description: 'Directory containing source code' + tools_directory: + type: string + default: 'tools' + description: 'Directory containing tools' + secrets: + DEPLOY_KEY: + required: true + description: 'SSH deploy key for pushing to protected branches' + outputs: + toolbox_name: + description: 'Name of the packaged toolbox' + value: ${{ jobs.release.outputs.toolbox_name }} + mltbx_path: + description: 'Path to the packaged MLTBX file' + value: ${{ jobs.release.outputs.mltbx_path }} + +jobs: + release: + name: Package toolbox and create draft release + runs-on: ubuntu-latest + outputs: + toolbox_name: ${{ steps.package.outputs.toolbox_name }} + mltbx_path: ${{ steps.package.outputs.mltbx_path }} + steps: + - name: Checkout repository using deploy key + uses: actions/checkout@v4 + with: + ssh-key: ${{ secrets.DEPLOY_KEY }} + + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + + - name: Install MatBox + uses: ehennestad/matbox/.github/actions/install-matbox@make-release-workflow-reusable + + # Generate badge + - name: Generate "tested with" badge + uses: ehennestad/matbox/.github/actions/update-badges@make-release-workflow-reusable + with: + version_number: ${{ inputs.version_number }} + tools_directory: ${{ inputs.tools_directory }} + + # Package toolbox + - name: Package toolbox + id: package + uses: ehennestad/matbox/.github/actions/package-toolbox@make-release-workflow-reusable + with: + version_number: ${{ inputs.version_number }} + code_directory: ${{ inputs.code_directory }} + tools_directory: ${{ inputs.tools_directory }} + + # Save the MLTBX. + - name: Save packaged toolbox + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.package.outputs.toolbox_name }}.mltbx + path: ${{ steps.package.outputs.mltbx_path }} + retention-days: 1 + + # Create GitHub release + - name: Create GitHub release + uses: ehennestad/matbox/.github/actions/create-github-release@make-release-workflow-reusable + with: + version_number: ${{ inputs.version_number }} + mltbx_path: ${{ steps.package.outputs.mltbx_path }} diff --git a/.github/workflows/reusable-job_run-tests.yml b/.github/workflows/reusable-job_run-tests.yml new file mode 100644 index 0000000..0805f49 --- /dev/null +++ b/.github/workflows/reusable-job_run-tests.yml @@ -0,0 +1,70 @@ +name: Reusable Job - Run Tests + +on: + workflow_call: + inputs: + code_directory: + type: string + default: 'code' + description: 'Directory containing source code' + tools_directory: + type: string + default: 'tools' + description: 'Directory containing tools' + matlab_products: + description: Optional list of MATLAB products to install. + type: string + default: '' + needs_python: + type: boolean + default: false + description: 'Whether Python is needed for testing (controls if Python versions are included in test matrix)' + matrix_json: + type: string + required: true + description: 'JSON string containing the test matrix from build-matrix job' + +jobs: + test: + name: Run MATLAB tests (${{ matrix.MATLABVersion }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJSON(inputs.matrix_json) }} + + steps: + # Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + if: ${{ inputs.needs_python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.pythonVersion }} + + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + with: + release: ${{ matrix.MATLABVersion }} + products: ${{ inputs.matlab_products }} + + - name: Install MatBox + uses: ehennestad/matbox/.github/actions/install-matbox@make-release-workflow-reusable + + # Runs all tests in the project. Put results in a version specific subdirectory + - name: Run tests + uses: ehennestad/matbox/.github/actions/test-code@make-release-workflow-reusable + with: + code_directory: ${{ inputs.code_directory }} + tools_directory: ${{ inputs.tools_directory }} + report_subdirectory: ${{ matrix.MATLABVersion }} + create_badge: false + + # Save the contents of the report directory from each release into an artifact. + - name: Save report directory + uses: actions/upload-artifact@v4 + if: always() + with: + name: reports-${{ matrix.MATLABVersion }} + path: docs/reports diff --git a/.github/workflows/reusable-job_validate-version.yml b/.github/workflows/reusable-job_validate-version.yml new file mode 100644 index 0000000..687958a --- /dev/null +++ b/.github/workflows/reusable-job_validate-version.yml @@ -0,0 +1,47 @@ +name: Reusable Job - Validate Version + +on: + workflow_call: + inputs: + version: + type: string + required: false + description: 'Version number in major.minor.patch format (for manual triggers)' + ref_name: + type: string + required: false + description: 'GitHub ref name (for tag triggers)' + tools_directory: + type: string + default: 'tools' + description: 'Directory containing tools' + outputs: + version_number: + description: 'Validated version number' + value: ${{ jobs.validate_version.outputs.version_number }} + +jobs: + validate_version: + name: Validate version number + runs-on: ubuntu-latest + outputs: + version_number: ${{ steps.validate.outputs.version_number }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Check for MLToolboxInfo.json + run: | + if [ ! -f "${{ inputs.tools_directory }}/MLToolboxInfo.json" ]; then + echo "Error: MLToolboxInfo.json not found in ${{ inputs.tools_directory }} directory" + echo "This file is required for the release workflow to function properly" + exit 1 + fi + echo "MLToolboxInfo.json found in ${{ inputs.tools_directory }} directory" + + - name: Validate version + id: validate + uses: ehennestad/matbox/.github/actions/validate-version@make-release-workflow-reusable + with: + version: ${{ inputs.version }} + ref_name: ${{ inputs.ref_name }} diff --git a/.github/workflows/reusable-job_verify-installation.yml b/.github/workflows/reusable-job_verify-installation.yml new file mode 100644 index 0000000..6aa0b51 --- /dev/null +++ b/.github/workflows/reusable-job_verify-installation.yml @@ -0,0 +1,55 @@ +name: Reusable Job - Verify Installation + +on: + workflow_call: + inputs: + toolbox_name: + type: string + required: true + description: 'Name of the toolbox to verify' + os_matrix: + type: string + default: '["ubuntu-latest", "windows-latest", "macos-latest"]' + description: 'JSON array of operating systems to test on' + +jobs: + verify_installation: + name: Verify toolbox installation (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ${{ fromJSON(inputs.os_matrix) }} + + steps: + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + + - name: Download packaged toolbox + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.toolbox_name }}.mltbx + + - name: Install and verify toolbox + uses: matlab-actions/run-command@v2 + with: + command: | + % Get the MLTBX file name + mltbxFiles = dir('*.mltbx'); + if isempty(mltbxFiles) + error('No MLTBX file found'); + end + mltbxFile = mltbxFiles(1).name; + + % Install the toolbox + fprintf('Installing toolbox: %s\n', mltbxFile); + agreeToLicense = true; + installedToolbox = matlab.addons.install(mltbxFile, agreeToLicense); + + % Verify installation + fprintf('Toolbox installed successfully:\n'); + fprintf(' Name: %s\n', installedToolbox.Name); + fprintf(' Version: %s\n', installedToolbox.Version); + fprintf(' Identifier: %s\n', installedToolbox.Identifier); + + fprintf('Verification completed successfully on %s\n', computer); From ecbd9cb8ffa46ba6d13a2f90c69e63a0d746c4f1 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 17:52:52 +0200 Subject: [PATCH 36/45] Update action.yml Removed deprecated output --- .github/actions/create-github-release/action.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/actions/create-github-release/action.yml b/.github/actions/create-github-release/action.yml index fb749d0..4f02e23 100644 --- a/.github/actions/create-github-release/action.yml +++ b/.github/actions/create-github-release/action.yml @@ -8,9 +8,6 @@ inputs: mltbx_path: description: 'Path to the MLTBX file' required: true - version_underscore: - description: 'Version number with underscores' - required: true runs: using: "composite" From 78f71c4d2003060bcd66f36124332e20146736fd Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 20:33:01 +0200 Subject: [PATCH 37/45] Update reusable_release_workflow.yml Removed deprecated input --- .github/workflows/reusable_release_workflow.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/reusable_release_workflow.yml index 6e4cfbf..6d6d611 100644 --- a/.github/workflows/reusable_release_workflow.yml +++ b/.github/workflows/reusable_release_workflow.yml @@ -179,7 +179,6 @@ jobs: with: version_number: ${{ needs.validate_version.outputs.version_number }} mltbx_path: ${{ steps.package.outputs.mltbx_path }} - version_underscore: ${{ steps.package.outputs.version_underscore }} verify_installation: name: Verify toolbox installation (${{ matrix.os }}) From 4b05ea47efcd2e119b0aae3025f89d6f111e2bac Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 20:33:30 +0200 Subject: [PATCH 38/45] Add workflow template for prepare-release-modular --- .../prepare-release-modular.properties.json | 10 +++ .../prepare-release-modular.yml | 82 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 .github/workflow-templates/prepare-release-modular.properties.json create mode 100644 .github/workflow-templates/prepare-release-modular.yml diff --git a/.github/workflow-templates/prepare-release-modular.properties.json b/.github/workflow-templates/prepare-release-modular.properties.json new file mode 100644 index 0000000..9a2ce27 --- /dev/null +++ b/.github/workflow-templates/prepare-release-modular.properties.json @@ -0,0 +1,10 @@ +{ + "name": "Prepare toolbox release", + "description": "Test toolbox code across supported MATLAB releases, package toolbox and create a draft release on GitHub. This workflow supports modular replacement for individual jobs.", + "iconName": "octicon gift", + "categories": [ + "testing", + "utilities", + "MATLAB" + ] +} diff --git a/.github/workflow-templates/prepare-release-modular.yml b/.github/workflow-templates/prepare-release-modular.yml new file mode 100644 index 0000000..71df0ac --- /dev/null +++ b/.github/workflow-templates/prepare-release-modular.yml @@ -0,0 +1,82 @@ +name: Prepare Release (Modular) + +# CONFIGURATION GUIDE: +# This workflow uses modular reusable job workflows that can be customized by changing the values below. +# To customize for your project, find and replace these values throughout the workflow: +# +# 1. CODE_DIRECTORY (default: 'code') +# - Directory containing your MATLAB source code (relative to repository root) +# - Find: code_directory: code +# - Replace with your source directory, e.g.: code_directory: src +# +# 2. TOOLS_DIRECTORY (default: 'tools') +# - Directory containing tools and MLToolboxInfo.json (relative to repository root) +# - Find: tools_directory: tools +# - Replace with your tools directory, e.g.: tools_directory: build +# +# 3. NEEDS_PYTHON (default: false) +# - Set to true if your toolbox requires Python for testing +# - This includes Python versions in test matrix and sets up Python environments +# - Find: needs_python: false +# - Replace with: needs_python: true (if Python is required) +# +# 4. MATLAB_PRODUCTS (default: '') +# - Optional MATLAB products to install during testing (space-separated list) +# - Find: matlab_products: '' +# - Replace with products, e.g.: matlab_products: 'Simulink Signal_Processing_Toolbox' + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + version: + description: 'Version number in major.minor.patch format' + required: true + type: string + +jobs: + validate_version: + name: Validate version number + uses: ehennestad/matbox/.github/workflows/reusable-job_validate-version.yml@make-release-workflow-reusable + with: + version: ${{ inputs.version }} + ref_name: ${{ github.ref_name }} + tools_directory: tools + + build_matrix: + name: Build release test matrix + uses: ehennestad/matbox/.github/workflows/reusable-job_build-matrix.yml@make-release-workflow-reusable + with: + tools_directory: tools + needs_python: false + + test: + name: Run MATLAB tests + needs: [validate_version, build_matrix] + uses: ehennestad/matbox/.github/workflows/reusable-job_run-tests.yml@make-release-workflow-reusable + with: + code_directory: code + tools_directory: tools + matlab_products: '' + needs_python: false + matrix_json: ${{ needs.build_matrix.outputs.matrix }} + + release: + name: Package toolbox and create draft release + needs: [test, validate_version] + uses: ehennestad/matbox/.github/workflows/reusable-job_package-toolbox.yml@make-release-workflow-reusable + with: + version_number: ${{ needs.validate_version.outputs.version_number }} + code_directory: code + tools_directory: tools + secrets: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + + verify_installation: + name: Verify toolbox installation + needs: [release, validate_version] + uses: ehennestad/matbox/.github/workflows/reusable-job_verify-installation.yml@make-release-workflow-reusable + with: + toolbox_name: ${{ needs.release.outputs.toolbox_name }} From d1c87f9ff15830241394c46bbf122861a51c0c7b Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 20:35:13 +0200 Subject: [PATCH 39/45] Create reusable-workflow_release.yml --- .../workflows/reusable-workflow_release.yml | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/reusable-workflow_release.yml diff --git a/.github/workflows/reusable-workflow_release.yml b/.github/workflows/reusable-workflow_release.yml new file mode 100644 index 0000000..122f962 --- /dev/null +++ b/.github/workflows/reusable-workflow_release.yml @@ -0,0 +1,88 @@ +name: Reusable Workflow - Release + +on: + workflow_call: + inputs: + version: + type: string + required: false + description: 'Version number in major.minor.patch format (for manual triggers)' + ref_name: + type: string + required: false + description: 'GitHub ref name (for tag triggers)' + code_directory: + type: string + default: 'code' + description: 'Directory containing source code' + tools_directory: + type: string + default: 'tools' + description: 'Directory containing tools' + matlab_products: + description: Optional list of MATLAB products to install. + type: string + default: '' + matlab_versions: + type: string + default: '[]' + description: 'JSON array of MATLAB versions to test (optional - will be determined from MLToolboxInfo.json if not provided)' + python_versions: + type: string + default: '' + description: 'JSON object mapping MATLAB versions to Python versions' + needs_python: + type: boolean + default: false + description: 'Whether Python is needed for testing (controls if Python versions are included in test matrix)' + secrets: + DEPLOY_KEY: + required: true + description: 'SSH deploy key for pushing to protected branches' + +jobs: + validate_version: + name: Validate version number + uses: ehennestad/matbox/.github/workflows/reusable-job_validate-version.yml@make-release-workflow-reusable + with: + version: ${{ inputs.version }} + ref_name: ${{ inputs.ref_name }} + tools_directory: ${{ inputs.tools_directory }} + + build_matrix: + name: Build release test matrix + uses: ehennestad/matbox/.github/workflows/reusable-job_build-matrix.yml@make-release-workflow-reusable + with: + tools_directory: ${{ inputs.tools_directory }} + matlab_versions: ${{ inputs.matlab_versions }} + python_versions: ${{ inputs.python_versions }} + needs_python: ${{ inputs.needs_python }} + + test: + name: Run MATLAB tests + needs: [validate_version, build_matrix] + uses: ehennestad/matbox/.github/workflows/reusable-job_run-tests.yml@make-release-workflow-reusable + with: + code_directory: ${{ inputs.code_directory }} + tools_directory: ${{ inputs.tools_directory }} + matlab_products: ${{ inputs.matlab_products }} + needs_python: ${{ inputs.needs_python }} + matrix_json: ${{ needs.build_matrix.outputs.matrix }} + + release: + name: Package toolbox and create draft release + needs: [test, validate_version] + uses: ehennestad/matbox/.github/workflows/reusable-job_package-toolbox.yml@make-release-workflow-reusable + with: + version_number: ${{ needs.validate_version.outputs.version_number }} + code_directory: ${{ inputs.code_directory }} + tools_directory: ${{ inputs.tools_directory }} + secrets: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + + verify_installation: + name: Verify toolbox installation + needs: [release, validate_version] + uses: ehennestad/matbox/.github/workflows/reusable-job_verify-installation.yml@make-release-workflow-reusable + with: + toolbox_name: ${{ needs.release.outputs.toolbox_name }} From a30318d396928ac994498dc0daf0b1d6fa64a7fa Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 20:35:19 +0200 Subject: [PATCH 40/45] Create README.md --- .github/workflows/README.md | 198 ++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 .github/workflows/README.md diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..f4911f3 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,198 @@ +# Reusable GitHub Actions Workflows + +This directory contains reusable GitHub Actions workflows for MATLAB toolbox development and release management. + +## Overview + +The workflows are organized into two levels: + +1. **Complete Workflows** (`reusable-workflow_*`): Full end-to-end processes +2. **Job Workflows** (`reusable-job_*`): Individual job components that can be mixed and matched + +## Complete Workflows + +### `reusable-workflow_release.yml` + +A complete release workflow that validates versions, runs tests, packages the toolbox, and verifies installation. + +**Usage:** +```yaml +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + uses: ./.github/workflows/reusable-workflow_release.yml + with: + ref_name: ${{ github.ref_name }} + needs_python: true + secrets: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} +``` + +## Job Workflows + +Individual job workflows that can be used independently or combined in custom workflows: + +### `reusable-job_validate-version.yml` + +Validates version numbers and checks for required files. + +**Inputs:** +- `version` (optional): Version number for manual triggers +- `ref_name` (optional): GitHub ref name for tag triggers +- `tools_directory` (default: 'tools'): Directory containing tools + +**Outputs:** +- `version_number`: Validated version number + +### `reusable-job_build-matrix.yml` + +Builds test matrices for MATLAB and Python versions. + +**Inputs:** +- `tools_directory` (default: 'tools'): Directory containing tools +- `matlab_versions` (default: '[]'): JSON array of MATLAB versions +- `python_versions` (default: ''): JSON object mapping MATLAB to Python versions +- `needs_python` (default: false): Whether Python is needed + +**Outputs:** +- `matrix`: Test matrix JSON +- `matlab_versions`: MATLAB versions array + +### `reusable-job_run-tests.yml` + +Runs MATLAB tests across multiple versions. + +**Inputs:** +- `code_directory` (default: 'code'): Source code directory +- `tools_directory` (default: 'tools'): Tools directory +- `matlab_products` (default: ''): MATLAB products to install +- `needs_python` (default: false): Whether Python is needed +- `matrix_json` (required): Test matrix from build-matrix job + +### `reusable-job_package-toolbox.yml` + +Packages the toolbox and creates a GitHub release. + +**Inputs:** +- `version_number` (required): Version number for the release +- `code_directory` (default: 'code'): Source code directory +- `tools_directory` (default: 'tools'): Tools directory + +**Secrets:** +- `DEPLOY_KEY` (required): SSH deploy key for pushing to protected branches + +**Outputs:** +- `toolbox_name`: Name of the packaged toolbox +- `mltbx_path`: Path to the packaged MLTBX file + +### `reusable-job_verify-installation.yml` + +Verifies toolbox installation across multiple operating systems. + +**Inputs:** +- `toolbox_name` (required): Name of the toolbox to verify +- `os_matrix` (default: '["ubuntu-latest", "windows-latest", "macos-latest"]'): OS array + +## Customization Examples + +### Adding Virtual Display Support + +When you need to add custom setup (like virtual displays) to the test job, you can create a custom workflow that uses most of the reusable jobs but implements a custom test job: + +```yaml +name: Custom Release with Virtual Display + +on: + push: + tags: + - 'v*' + +jobs: + validate_version: + uses: ./.github/workflows/reusable-job_validate-version.yml + with: + ref_name: ${{ github.ref_name }} + + build_matrix: + uses: ./.github/workflows/reusable-job_build-matrix.yml + with: + needs_python: true + + # Custom test job with virtual display + test: + name: Run MATLAB tests with virtual display + needs: [validate_version, build_matrix] + runs-on: ubuntu-latest + strategy: + matrix: ${{ fromJSON(needs.build_matrix.outputs.matrix) }} + steps: + - uses: actions/checkout@v4 + + # Custom virtual display setup + - name: Setup virtual display + run: | + sudo apt-get install -y xvfb + echo "DISPLAY=:99.0" >> $GITHUB_ENV + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + + # Continue with standard test steps... + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + with: + release: ${{ matrix.MATLABVersion }} + + - name: Run tests + uses: ehennestad/matbox/.github/actions/test-code@make-release-workflow-reusable + with: + code_directory: code + tools_directory: tools + + release: + needs: [test, validate_version] + uses: ./.github/workflows/reusable-job_package-toolbox.yml + with: + version_number: ${{ needs.validate_version.outputs.version_number }} + secrets: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + + verify_installation: + needs: [release] + uses: ./.github/workflows/reusable-job_verify-installation.yml + with: + toolbox_name: ${{ needs.release.outputs.toolbox_name }} +``` + +### Adding Custom Secrets + +If you need additional secrets for specific jobs, you can pass them through: + +```yaml +jobs: + custom_test: + name: Custom test with additional secrets + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Use custom secret + - name: Setup custom service + env: + API_KEY: ${{ secrets.CUSTOM_API_KEY }} + run: | + echo "Setting up custom service with API key" + + # Continue with standard steps... +``` + +## Benefits + +1. **Flexibility**: Use individual job workflows for maximum customization +2. **Reusability**: Standard workflows for common use cases +3. **Maintainability**: Changes to core functionality only need to be made in one place +4. **Gradual Migration**: Can adopt job-by-job without breaking existing workflows From 4d3440629f20c51d83bfdbfe9f739c6cb7e2ccef Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 21:34:06 +0200 Subject: [PATCH 41/45] Rename run-tests.yml --- .github/workflows/{run_tests.yml => run-tests.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{run_tests.yml => run-tests.yml} (100%) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run-tests.yml similarity index 100% rename from .github/workflows/run_tests.yml rename to .github/workflows/run-tests.yml From e6ff2f0faee5beb4dfbfddcb03cee20f07660981 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 22:22:40 +0200 Subject: [PATCH 42/45] Move workflows to deprecated --- .github/workflows/{ => deprecated}/create_release.yml | 0 .github/workflows/{ => deprecated}/reusable_release_workflow.yml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{ => deprecated}/create_release.yml (100%) rename .github/workflows/{ => deprecated}/reusable_release_workflow.yml (100%) diff --git a/.github/workflows/create_release.yml b/.github/workflows/deprecated/create_release.yml similarity index 100% rename from .github/workflows/create_release.yml rename to .github/workflows/deprecated/create_release.yml diff --git a/.github/workflows/reusable_release_workflow.yml b/.github/workflows/deprecated/reusable_release_workflow.yml similarity index 100% rename from .github/workflows/reusable_release_workflow.yml rename to .github/workflows/deprecated/reusable_release_workflow.yml From 4c4f7fb7963225bde754a4e9a9c6af57056422c6 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 22:27:15 +0200 Subject: [PATCH 43/45] Changed name in "reusable job" workflows --- .github/workflows/reusable-job_package-toolbox.yml | 2 +- .github/workflows/reusable-job_run-tests.yml | 4 ++-- .github/workflows/reusable-job_validate-version.yml | 2 +- .github/workflows/reusable-job_verify-installation.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/reusable-job_package-toolbox.yml b/.github/workflows/reusable-job_package-toolbox.yml index 99c0160..32fe2c2 100644 --- a/.github/workflows/reusable-job_package-toolbox.yml +++ b/.github/workflows/reusable-job_package-toolbox.yml @@ -1,4 +1,4 @@ -name: Reusable Job - Package Toolbox +name: Package toolbox on: workflow_call: diff --git a/.github/workflows/reusable-job_run-tests.yml b/.github/workflows/reusable-job_run-tests.yml index 0805f49..6225100 100644 --- a/.github/workflows/reusable-job_run-tests.yml +++ b/.github/workflows/reusable-job_run-tests.yml @@ -1,4 +1,4 @@ -name: Reusable Job - Run Tests +name: Run Tests on: workflow_call: @@ -52,7 +52,7 @@ jobs: - name: Install MatBox uses: ehennestad/matbox/.github/actions/install-matbox@make-release-workflow-reusable - # Runs all tests in the project. Put results in a version specific subdirectory + # Run all tests in the project. Put results in a version specific subdirectory - name: Run tests uses: ehennestad/matbox/.github/actions/test-code@make-release-workflow-reusable with: diff --git a/.github/workflows/reusable-job_validate-version.yml b/.github/workflows/reusable-job_validate-version.yml index 687958a..c1a1f40 100644 --- a/.github/workflows/reusable-job_validate-version.yml +++ b/.github/workflows/reusable-job_validate-version.yml @@ -1,4 +1,4 @@ -name: Reusable Job - Validate Version +name: Validate version number on: workflow_call: diff --git a/.github/workflows/reusable-job_verify-installation.yml b/.github/workflows/reusable-job_verify-installation.yml index 6aa0b51..942f26f 100644 --- a/.github/workflows/reusable-job_verify-installation.yml +++ b/.github/workflows/reusable-job_verify-installation.yml @@ -1,4 +1,4 @@ -name: Reusable Job - Verify Installation +name: Verify toolbox installation on: workflow_call: From e051b8646294ca9088a5c8d68ab056aad21503f7 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 22:28:34 +0200 Subject: [PATCH 44/45] Update reusable-job_build-matrix.yml Changed name, added comment --- .github/workflows/reusable-job_build-matrix.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-job_build-matrix.yml b/.github/workflows/reusable-job_build-matrix.yml index 9169de3..78c25b8 100644 --- a/.github/workflows/reusable-job_build-matrix.yml +++ b/.github/workflows/reusable-job_build-matrix.yml @@ -1,4 +1,13 @@ -name: Reusable Job - Build Matrix +name: Build test matrix + +# Build matrix variables for running tests across different MATLAB releases. +# Will return a json with the following variables: +# - MATLABVersion +# - pythonVersion (if needs_python == true) +# The pythonVersion will be the latest compatible python version for each +# MATLAB release. +# See also: +# .github/actions/build-test-matrix/action.yml on: workflow_call: From b9e729489574ec617b13ad11b62865e064bcb588 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 9 Jun 2025 22:33:47 +0200 Subject: [PATCH 45/45] Create reusable-workflow-check-code.yml --- .../reusable-workflow-check-code.yml | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows-reusable/reusable-workflow-check-code.yml diff --git a/.github/workflows-reusable/reusable-workflow-check-code.yml b/.github/workflows-reusable/reusable-workflow-check-code.yml new file mode 100644 index 0000000..507dd9b --- /dev/null +++ b/.github/workflows-reusable/reusable-workflow-check-code.yml @@ -0,0 +1,52 @@ +name: Analyse code + +on: + workflow_call: + inputs: + code_directory: + description: Path to the directory containing code. Code analysis will run on the contents of this folder and its subfolders. + type: string + default: 'code' + tools_directory: + description: Path to the directory containing CI tools. Used for locating customized MatBox tasks. + type: string + default: 'tools' + matlab_release: + description: MATLAB release to use when running code analysis. + type: string + default: 'latest' + matlab_use_cache: + description: Whether to cache the MATLAB installation for faster subsequent setups. + type: boolean + default: false +jobs: + # This workflow contains a single job called "check" + check: + name: Check code + runs-on: ubuntu-latest + + steps: + - name: Check out repo + uses: actions/checkout@v4 + + - name: Set up MATLAB (${{ inputs.matlab_release }}) + uses: matlab-actions/setup-matlab@v2 + with: + release: ${{ inputs.matlab_release }} + cache: ${{ inputs.matlab_use_cache }} + + - name: Install MatBox + uses: ehennestad/matbox/.github/actions/install-matbox@v0.9 + + - name: Check code and upload report + uses: ehennestad/matbox/.github/actions/check-code@v0.9 + with: + code_directory: ${{ inputs.code_directory }} + tools_directory: ${{ inputs.tools_directory }} + + - name: Commit SVG badges if updated + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + uses: ehennestad/matbox/.github/actions/push-badges@v0.9 + with: + pr-ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref_name }} + pr-repo: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }}