Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 80 additions & 31 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,71 @@

name: Create new release

# Run workflow when a tag is created
# Run workflow when a tag is created or it is triggered manually
on:
push:
tags:
- 'v*'
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 matrixed test job run across a bunch of releases of MATLAB
# 2. a reporting job that summarizes the tests, and updates release badge
# 1. a validation of the provided version number
# 2. a matrixed test job run across MATLAB releases from R2021b to R2024b
# 3. a reporting job that summarizes the tests, and updates release badge

setup_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:
needs: [setup_version]
strategy:
fail-fast: false
matrix:
MATLABVersion: [R2022b, R2023a, R2023b, R2024a, R2024b]
MATLABVersion: [R2021b, R2022a, R2022b, R2023a, R2023b, R2024a, R2024b]
# The type of runner that the job will run on
runs-on: ubuntu-latest

Expand All @@ -39,15 +89,6 @@ jobs:
addpath(genpath("tools"));
testToolbox('ReportSubdirectory',"${{ matrix.MATLABVersion }}", 'CreateBadge', false)

# Upload code coverage information to Codecov
- name: Upload code coverage report to Codecov (https://app.codecov.io/gh/openMetadataInitiative/openMINDS_MATLAB)
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
Expand All @@ -58,11 +99,13 @@ jobs:

# Report on what releases tested successfully.
# Generate a draft release based on the tag
# Recreate the tag with the final version of JSON files
# Recreate the tag with the final version of updated files
release:
needs: test
if: always()
needs: [test, setup_version]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.setup_version.outputs.version }}
VERSION_TAG: ${{ needs.setup_version.outputs.version_tag }}

steps:
# Use deploy key to push back to protected branch
Expand All @@ -86,7 +129,7 @@ jobs:
- name: Generate tested with badge
uses: matlab-actions/run-command@v2
with:
command: addpath(genpath("tools")), createTestedWithBadgeforToolbox("${{ github.ref_name }}")
command: addpath(genpath("tools")), createTestedWithBadgeforToolbox("${{ env.VERSION_TAG }}")

# Publish test results from all the releases
- name: Publish test results
Expand All @@ -99,13 +142,13 @@ jobs:
- name: Package toolbox
uses: matlab-actions/run-command@v2
with:
command: addpath(genpath("tools")), packageToolbox("specific","${{ github.ref_name }}")
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 "${{ github.ref_name }}" | sed 's/\./_/g')
versionNumber=$(echo "${{ env.VERSION_TAG }}" | sed 's/\./_/g')
echo "versionNumber=$versionNumber" >> $GITHUB_ENV

# Save the MLTBX.
Expand All @@ -120,7 +163,7 @@ jobs:
with:
command: |
addpath(genpath("tools"));
updateCodeMetaFile("${{ github.ref_name }}")
updateCodeMetaFile("${{ env.VERSION_TAG }}")

# Commit the updated Contents.m
- name: Commit updated Contents.m file
Expand All @@ -131,7 +174,7 @@ jobs:
git status
git add code/Contents.m
git add codemeta.json
git commit -m "Final checkins for release ${{ github.ref_name }}"
git commit -m "Final checkins for release ${{ env.VERSION_TAG }}"
git fetch
git push

Expand All @@ -145,8 +188,8 @@ jobs:

- 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
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 }}"
Expand All @@ -161,29 +204,35 @@ jobs:
echo "Nothing to commit"
fi

# Retag the repo so that the updated files are included in the release tag
# (Re)tag 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 }}"
# Delete the existing tag locally and remotely if it already exists
if git rev-parse --verify "refs/tags/${{ env.VERSION_TAG }}" > /dev/null 2>&1; then
# Delete the existing tag locally and remotely only if it exists
git tag -d "${{ env.VERSION_TAG }}"
git push origin --delete "${{ env.VERSION_TAG }}"
else
echo "Tag '${{ env.VERSION_TAG }}' does not exist, skipping deletion."
fi

# 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]"
# Recreate the tag with a message, including [skip-ci] to prevent triggering 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 "${{ github.ref_name }}"
git push origin "${{ env.VERSION_TAG }}"

# Create the release
- name: Create GitHub release
uses: ncipollo/release-action@v1
with:
draft: true
artifacts: "releases/openMINDS_MATLAB_${{ 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%2FopenMetadataInitiative%2FopenMINDS_MATLAB%2Fgh-badges%2F.github%2Fbadges%2F${{ github.ref_name }}%2Ftested_with.json)"
body: "![MATLAB Versions Tested](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FopenMetadataInitiative%2FopenMINDS_MATLAB%2Fgh-badges%2F.github%2Fbadges%2F${{ env.VERSION_TAG }}%2Ftested_with.json)"
3 changes: 1 addition & 2 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ on:
push:
branches: [ "main" ]
paths-ignore:
- '*README.md'
- '*md'
- '*.md'
- '.github/**'
pull_request:
branches: [ "main" ]
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@

---


MATLAB package for the openMINDS metadata framework for linked data in neuroscience. The package contains all the latest openMINDS schemas as MATLAB classes in addition to schema base classes and utility methods.
MATLAB toolbox for creating openMINDS compliant linked metadata, supporting import and export of metadata instances in JSON-LD format.

To generally learn more about the openMINDS metadata framework please go to :arrow_right: [**ReadTheDocs**](https://openminds-documentation.readthedocs.io).
To learn more about the openMINDS metadata framework please go to :arrow_right: [**ReadTheDocs**](https://openminds-documentation.readthedocs.io).
You can test and learn how to use openMINDS in MATLAB by going through a small :arrow_right: [**DEMO**](https://matlab.mathworks.com/open/github/v1?repo=openMetadataInitiative/openMINDS_MATLAB&file=code/gettingStarted.mlx) on MATLAB Online.

MathWorks provides a free basic version of [MATLAB Online](https://uk.mathworks.com/products/matlab-online.html), but you need to register for a [MathWorks Account](https://www.mathworks.com/mwaccount/register?uri=https%3A%2F%2Fwww.mathworks.com%2Fproducts%2Fmatlab.html).
Expand Down
7 changes: 4 additions & 3 deletions code/+openminds/@Collection/Collection.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@
properties (SetAccess = protected)
% Nodes - Dictionary storing instances as values with identifiers
% as keys
Nodes {mustBeA(Nodes, ["dictionary", "containers.Map"])} = containers.Map %#ok<MCHDP>
Nodes {mustBeA(Nodes, ["dictionary", "containers.Map"])} = containers.Map %#ok<MCHDP> Constructor will overwrite
end

properties (SetAccess = protected, Hidden)
% TypeMap - Keeps a map/dictionary of types and instance ids to
% efficiently extract instances of a specific type.
TypeMap {mustBeA(TypeMap, ["dictionary", "containers.Map"])} = containers.Map %#ok<MCHDP>
TypeMap {mustBeA(TypeMap, ["dictionary", "containers.Map"])} = containers.Map %#ok<MCHDP> Constructor will overwrite
end

properties
Expand Down Expand Up @@ -526,7 +526,8 @@
if any(isMatch)
if isa(obj.TypeMap, 'dictionary')
if isMATLABReleaseOlderThan("R2023b")
instanceKeys = string( obj.TypeMap(typeKeys(isMatch)) );
instanceKeys = obj.TypeMap(typeKeys(isMatch));
instanceKeys = instanceKeys{1};

Check warning on line 530 in code/+openminds/@Collection/Collection.m

View check run for this annotation

Codecov / codecov/patch

code/+openminds/@Collection/Collection.m#L529-L530

Added lines #L529 - L530 were not covered by tests
else
instanceKeys = obj.TypeMap{typeKeys(isMatch)};
end
Expand Down
2 changes: 1 addition & 1 deletion code/internal/+openminds/+abstract/Schema.m
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@
varargout = cell(1, numOutputs);
[varargout{:}] = builtin('subsref', obj, subs);
else
obj = builtin('subsref', obj, subs);
obj = builtin('subsref', obj, subs); %#ok<NASGU>

Check warning on line 387 in code/internal/+openminds/+abstract/Schema.m

View check run for this annotation

Codecov / codecov/patch

code/internal/+openminds/+abstract/Schema.m#L387

Added line #L387 was not covered by tests
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
if strcmp(firstLetter, lower(firstLetter))
newNameLowercase = newName;
newName{1}(1) = upper(firstLetter);
warning('Classname should start with a capital letter. Changed name from "%s" to "%s"', newNameLowercase, newName) %#ok<PFCEL>
warning(['Classname should start with a capital letter. ', ...
'Changed name from "%s" to "%s"'], newNameLowercase, string(newName))
end
obj.ClassName = newName;
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
for i = 1:numSchemas

iSchemaName = schemaTable{i, "SchemaName"};
iModelName = schemaTable{i, "ModuleName"};
iModuleName = schemaTable{i, "ModuleName"};

Check warning on line 22 in code/internal/+openminds/+internal/+helper/listSchemasWithNonGenericLabel.m

View check run for this annotation

Codecov / codecov/patch

code/internal/+openminds/+internal/+helper/listSchemasWithNonGenericLabel.m#L22

Added line #L22 was not covered by tests
iSubmoduleName = schemaTable{i, "SubModuleName"};

schemaClassFunctionName = openminds.internal.utility.string.buildClassName(iSchemaName, iSubmoduleName, iModelName);
schemaClassFunctionName = openminds.internal.utility.string.buildClassName(iSchemaName, iSubmoduleName, iModuleName);

Check warning on line 25 in code/internal/+openminds/+internal/+helper/listSchemasWithNonGenericLabel.m

View check run for this annotation

Codecov / codecov/patch

code/internal/+openminds/+internal/+helper/listSchemasWithNonGenericLabel.m#L25

Added line #L25 was not covered by tests
schemaFcn = str2func(schemaClassFunctionName);

mc = meta.class.fromName(schemaClassFunctionName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
properties (Access = private)
% Registry - Storage for cached Type objects
% Uses dictionary in newer MATLAB versions, containers.Map in older ones
Registry {mustBeA(Registry, ["dictionary", "containers.Map"])} = containers.Map %#ok<MCHDP>
Registry {mustBeA(Registry, ["dictionary", "containers.Map"])} = containers.Map %#ok<MCHDP> Constructor will overwrite
end

properties (SetAccess = private)
Expand Down Expand Up @@ -59,13 +59,7 @@
options.ModelVersion (1,1) string
end

% Initialize registry using dictionary if available (R2022b+)
% otherwise fall back to containers.Map
if exist('dictionary', 'file')
obj.Registry = configureDictionary('string', 'cell');
else
obj.Registry = containers.Map();
end
obj.initializeCache()

% Apply options
obj.ModelVersion = options.ModelVersion;
Expand All @@ -86,15 +80,25 @@
% clearCache - Clear the registry cache
%
% This method can be used to force reloading of Type objects
obj.initializeCache()
end
end

methods (Access = private)
function initializeCache(obj)
% Initialize registry using dictionary if available (R2022b+)
% otherwise fall back to containers.Map
if exist('dictionary', 'file')
obj.Registry = configureDictionary('string', 'cell');
if exist('configureDictionary', 'file') % From R2023b
obj.Registry = configureDictionary('string', 'cell');

Check warning on line 93 in code/internal/+openminds/+internal/+meta/@MetaTypeRegistry/MetaTypeRegistry.m

View check run for this annotation

Codecov / codecov/patch

code/internal/+openminds/+internal/+meta/@MetaTypeRegistry/MetaTypeRegistry.m#L93

Added line #L93 was not covered by tests
else % Fallback for R2022b and R2023a
obj.Registry = dictionary(string.empty, {});
end
else
obj.Registry = containers.Map();
end
end
end

methods (Access = private)
function isValid = isValidKey(obj, keyName)
% isValidKey - Check if a key is valid for the registry
%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@

methods (Hidden, Access = protected) % CustomDisplay - Method implementation

function requiredProperties = getRequiredProperties(obj)
function requiredProperties = getRequiredProperties(~)
% Subclasses should override.
requiredProperties = [];
end

Expand Down Expand Up @@ -173,7 +174,7 @@
rep = matlab.display.PlainTextRepresentation(obj, repmat({str}, numRows, 1), displayConfiguration);
elseif numObjects >= 1
% str = obj.DisplayString;
rep = fullDataRepresentation(obj, displayConfiguration, 'StringArray', arrayfun(@(i) obj(i).DisplayString, [1:numRows]', 'uni', 0) );
rep = fullDataRepresentation(obj, displayConfiguration, 'StringArray', arrayfun(@(i) obj(i).DisplayString, (1:numRows)', 'uni', 0) );

Check warning on line 177 in code/internal/+openminds/+internal/+mixin/CustomInstanceDisplay.m

View check run for this annotation

Codecov / codecov/patch

code/internal/+openminds/+internal/+mixin/CustomInstanceDisplay.m#L177

Added line #L177 was not covered by tests

elseif numObjects > 1
% rep = fullDataRepresentation(obj, displayConfiguration, 'StringArray', obj.DisplayString );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
end
end

function instances = deserialize(obj)
function instances = deserialize(obj) %#ok<MANU,STOUT>
error('Not implemented')

Check warning on line 40 in code/internal/+openminds/+internal/+serializer/JsonLdSerializer.m

View check run for this annotation

Codecov / codecov/patch

code/internal/+openminds/+internal/+serializer/JsonLdSerializer.m#L40

Added line #L40 was not covered by tests
% Todo:
% json to struct
% struct to openminds instances
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
function schemaInfo = listSourceSchemas(schemaModule, options)
function schemaInfo = listSourceSchemas(options)
%listSourceSchemas List information about all available schemas.
%
% schemaInfo = listSourceSchemas() returns a table with information
% about all the available schemas.

arguments
schemaModule = {}
options.SchemaType (1,1) string = "schema.omi.json";
options.SchemaFileExtension = '*.omi.json';
options.VersionNumber (1,1) openminds.internal.utility.VersionNumber ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@
schemaModule char
end

schemaCategory = '';

schemaName = openminds.internal.utility.string.pascalCase(schemaName);
schemaModule = lower(schemaModule);
schemaCategory = lower(schemaCategory);

schemaCategory = strrep( schemaCategory, 'schemas', '');

if isempty(schemaCategory) % This might be empty, i.e for controlled terms
name = strjoin({'openminds', schemaModule, schemaName}, '.');
Expand Down
Loading
Loading