Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorganize repo and add workflows for testing and releasing #186

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions .github/workflows/create_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# 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*'

jobs:
# This workflow contains:
# 1. a matrixed test job run across a bunch of releases of MATLAB
# 2. a reporting job that summarizes the tests, and updates release badge
test:
strategy:
fail-fast: false
matrix:
MATLABVersion: [R2020b, R2021a, R2021b, R2022a, R2022b, R2023a, R2023b, R2024a, R2024b]
# The type of runner that the job will run on
runs-on: ubuntu-latest

steps:
# Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it
- uses: actions/checkout@v4

- 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)

# 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
if: always()
runs-on: ubuntu-latest
env:
TOOLBOX_FILE_NAME: Brain_Observatory_Toolbox

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("${{ github.ref_name }}")

# 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","${{ github.ref_name }}")

# Define the versionNumber using underscores, as this is used in the MLTBX
- name: Set version number
id: set_version
run: |
versionNumber=$(echo "${{ github.ref_name }}" | sed 's/\./_/g')
echo "versionNumber=$versionNumber" >> $GITHUB_ENV

# Save the MLTBX.
- name: Save packaged toolbox
uses: actions/upload-artifact@v4
with:
name: ${{TOOLBOX_FILE_NAME}}${{ env.versionNumber }}.mltbx
path: releases/${{TOOLBOX_FILE_NAME}}_${{ 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 checkins for release ${{ github.ref_name }}"
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/${{ github.ref_name }}
cp .github/badges/${{ github.ref_name }}/tested_with.json gh-badges/.github/badges/${{ github.ref_name }}/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 "${{ github.ref_name }}"
git push origin --delete "${{ github.ref_name }}"

# Recreate the tag with a message, including [skip ci] to prevent CI workflows
git tag -a "${{ github.ref_name }}" -m "Release ${{ github.ref_name }} [skip ci]"

# Push the new tag to the remote repository
git push origin "${{ github.ref_name }}"

# Create the release
- name: Create GitHub Release
uses: ncipollo/release-action@v1
with:
draft: true
artifacts: "releases/${{TOOLBOX_FILE_NAME}}_${{ env.versionNumber }}.mltbx"
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%2F${{ github.ref_name }}%2Ftested_with.json)"

91 changes: 91 additions & 0 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Run tests

on:
# Triggers the workflow on push or pull request events for the "main" branch
push:
branches: [ "main" ]
paths-ignore:
- '*md'
- '.github/**'
- 'docs/reports/**'
pull_request:
branches: [ "main" ]

# Allows for manually running this workflow from the Actions tab
workflow_dispatch:

jobs:
# This workflow contains a single job called "test"
test:
name: Test toolbox code
# The type of runner that the job will run on
runs-on: ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it
- name: Check out repo
uses: actions/checkout@v4

- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v2

# Check for MATLAB code issues in the project.
- name: Check for MATLAB code issues
uses: matlab-actions/run-command@v2
if: always()
with:
command: addpath(genpath("tools")), codecheckToolbox()

# Upload code issues report
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v3
with:
# Path to SARIF file relative to the root of the repository
sarif_file: docs/reports/code_issues.sarif

# Runs all tests in the project.
- name: Run tests
uses: matlab-actions/run-command@v2
if: always()
with:
command: addpath(genpath("tools")), testToolbox()

# Commit updated SVG badges for the issues and tests (if changed)
- name: Commit svg badges if updated
if: always()
continue-on-error: true
run: |
git config user.name "${{ github.workflow }} by ${{ github.actor }}"
git config user.email "<>"
git fetch

if [[ $(git add .github/badges/* --dry-run | wc -l) -gt 0 ]]; then
git add .github/badges/*
git commit -m "Update code issues and tests badges"
git push -f
else
echo "Nothing to commit"
fi

- name: Upload code coverage report to Codecov
uses: codecov/codecov-action@v4
if: always()
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: docs/reports/codecoverage.xml

# Publish test results
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: "docs/reports/test-results.xml"

# Save the contents of the reports directory as an artifact
- name: Save reports directory
uses: actions/upload-artifact@v4
if: always()
with:
name: reports
path: docs/reports
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# BOT Data Cache
Cache/

# Ignore everything in docs/reports
docs/reports/*

# Autosave files
*.asv
*.m~
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions Contents.m → code/Contents.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
% Brain Observatory Toolbox
% Brain-Observatory-Toolbox
% Version 0.9.4 19-feb-2024
%
% Copyright (c) 2017, Ethan Meyers
% ----------------------------------
% Copyright (c) 2024, Ethan Meyers
% --------------------------------
%
% A MATLAB toolbox for accessing and using the public neural
% recording datasets from the Allen Brain Observatory.
Expand Down
4 changes: 4 additions & 0 deletions tools/+bottools/projectdir.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function folderPath = botProjectdir()

Check warning

Code scanning / Code Analyzer

Function name 'botProjectdir' is known to MATLAB by its file name: 'projectdir'. Warning

Function name 'botProjectdir' is known to MATLAB by its file name: 'projectdir'.
% projectdir - Get project root directory for a matlab toolbox code repository
folderPath = fileparts(fileparts(fileparts(mfilename('fullpath'))));
end
14 changes: 14 additions & 0 deletions tools/MLToolboxInfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"Namespace" : "bot",
"ToolboxOptions": {
"Identifier": "85a3255c-4ff5-42ef-9c10-b441318b4322",
"ToolboxName": "Brain-Observatory-Toolbox",
"AuthorName": "Ethan Meyers",
"AuthorEmail": "",
"AuthorCompany": "",
"Summary": "A MATLAB toolbox for interacting with the Allen Brain Observatory",
"Description": "A MATLAB toolbox for accessing and using the neural recording public datasets from the Allen Brain Observatory. Available datasets: Available datasets: Visual Coding, Visual Behavior (Neuropixels and 2-Photon Calcium Imaging)",
"MinimumMatlabRelease": "",
"MaximumMatlabRelease": ""
}
}
5 changes: 5 additions & 0 deletions tools/tasks/codecheckToolbox.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function codecheckToolbox()
installMatBox("commit")
projectRootDirectory = bottools.projectdir();
matbox.tasks.codecheckToolbox(projectRootDirectory)
end
8 changes: 8 additions & 0 deletions tools/tasks/createTestedWithBadgeforToolbox.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function createTestedWithBadgeforToolbox(versionNumber)
arguments
versionNumber (1,1) string
end
installMatBox()
projectRootDirectory = bottools.projectdir();
matbox.tasks.createTestedWithBadgeforToolbox(versionNumber, projectRootDirectory)
end
63 changes: 63 additions & 0 deletions tools/tasks/installMatBox.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
function installMatBox(mode)
% installMatBox - Install MatBox from latest release or latest commit

% Todo:
% - If MatBox release has been updated on remote, should reinstall.

arguments
mode (1,1) string {mustBeMember(mode, ["release", "commit"])} = "release"
end

if mode == "release"
installFromRelease() % local function
elseif mode == "commit"
installFromCommit() % local function
end
end

function installFromRelease()
addonsTable = matlab.addons.installedAddons();
isMatchedAddon = addonsTable.Name == "MatBox";

if ~isempty(isMatchedAddon) && any(isMatchedAddon)
matlab.addons.enableAddon('MatBox')
else
info = webread('https://api.github.com/repos/ehennestad/MatBox/releases/latest');
assetNames = {info.assets.name};
isMltbx = startsWith(assetNames, 'MatBox');

mltbx_URL = info.assets(isMltbx).browser_download_url;

% Download matbox
tempFilePath = websave(tempname, mltbx_URL);
cleanupObj = onCleanup(@(fp) delete(tempFilePath));

% Install toolbox
matlab.addons.install(tempFilePath);
end
end


function installFromCommit()
% Download latest zipped version of repo
url = "https://github.com/ehennestad/MatBox/archive/refs/heads/main.zip";
tempFilePath = websave(tempname, url);
cleanupObj = onCleanup(@(fp) delete(tempFilePath));

% Unzip in temporary location
unzippedFiles = unzip(tempFilePath, tempdir);
unzippedFolder = unzippedFiles{1};
if endsWith(unzippedFolder, filesep)
unzippedFolder = unzippedFolder(1:end-1);
end

% Move to installation location
[~, repoFolderName] = fileparts(unzippedFolder);
targetFolder = fullfile(userpath, "Add-Ons");
targetFolder = fullfile(targetFolder, repoFolderName);
if isfolder(targetFolder); rmdir(targetFolder, "s"); end
movefile(unzippedFolder, targetFolder);

% Add to MATLAB's search path
addpath(genpath(targetFolder))
end
Loading