Skip to content

Commit 0fba823

Browse files
committed
Merge branch 'gitUpdateFix' into dev
2 parents 7579a3e + 570a6c6 commit 0fba823

File tree

11 files changed

+465
-91
lines changed

11 files changed

+465
-91
lines changed

CHANGELOG.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22

33
Starting after Rigbox 2.2.0, this file contains a curated, chronologically ordered list of notable changes made to the master branch. Each bullet point in the list is followed by the accompanying commit hash, and the date of the commit. This changelog is based on [keep a changelog](https://keepachangelog.com)
44

5-
## [Most Recent Commits](https://github.com/cortex-lab/Rigbox/commits/master) 2.3.0
5+
## [Most Recent Commits](https://github.com/cortex-lab/Rigbox/commits/master) 2.3.1
6+
- patch to readme linking to most up-to-date documentation `4ff1a21`
7+
- Updates to `+git` package and its tests
8+
9+
## 2.3.0
610

711
- patch in alyx-matlab submodule 2019-07-25
812
- updated Signals performance test `993d906` 2019-07-19
913
- fixes to tests for the Alyx Panel `eb5e9b9` 2019-07-19
1014
- added tests for most used burgbox functions `` 2019-08
1115
- documentation added for numerous functions `661450`, `de0dc9` 2019-08-11
1216
- removed +dat/parseAlyxInstance.m `1939b2` 2019-08-08
13-
- fix for large json hardware arrays (issue #168) `058001`
17+
- fix for large json hardware arrays (issue #168) `058001`
1418
- fix for checking functions on path in newer versions `c4022d` 2019-08-09
1519
- fix for chrono wiringInfo in Timeline `fdfe72` 2019-08-11
1620
- fixes to burgbox utils `cf3c384`, `bb5c3f7`, `9be079`, `de0dc92`
@@ -19,4 +23,4 @@ Starting after Rigbox 2.2.0, this file contains a curated, chronologically order
1923
- improvements to water expServer calibration function `dd0adb7` 2019-08-14
2024
- updates to signals submodule `f760e5e` 2019-09-03
2125

22-
## [2.2.1](https://github.com/cortex-lab/Rigbox/releases/tag/v2.2.1) (Most Recent Stable Version)
26+
## [2.2.1](https://github.com/cortex-lab/Rigbox/releases/tag/v2.2.1)

CONTRIBUTING.md

Lines changed: 48 additions & 23 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
----------
22
# Rigbox
3-
![Coverage badge](https://img.shields.io/endpoint.svg?url=http%3A%2F%2Flab.lab.ultrahook.com%2Fcoverage%2Frigbox%2Fdev)
4-
![Build status badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Flab.lab.ultrahook.com%2Fstatus%2Frigbox%2Fdev)
3+
![Coverage badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgladius.serveo.net%2Fcoverage%2Frigbox%2Fdev)
4+
![Build status badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgladius.serveo.net%2Fstatus%2Frigbox%2Fdev)
55

66
Rigbox is a high-performance, open-source software toolbox for managing behavioral neuroscience experiments. Initially developed to probe mouse behavior for the [Steering Wheel Setup](https://www.ucl.ac.uk/cortexlab/tools/wheel), Rigbox is under active, test-driven development to encompass a variety of experimental paradigms across behavioral neuroscience. Rigbox simplifies hardware/software interfacing, synchronizes data streams from multiple sources, manages experimental data via communication with a remote database, implements a viewing model for visual stimuli, and creates a runtime environment in which an experiment's parameters can be easily monitored and manipulated. Rigbox’s object-oriented paradigm facilitates a modular approach to designing experiments. Rigbox requires two machines, one for stimulus presentation ('the stimulus computer' or 'sc') and another for controlling and monitoring the experiment ('the master computer' or 'mc').
77

@@ -121,6 +121,9 @@ The "cortexlab" directory is intended for functions and classes that are rig or
121121

122122
The "tests" directory contains code for running unit tests within Rigbox.
123123

124+
### docs
125+
Contains various guides for how to configure and use Rigbox.
126+
124127
### submodules
125128

126129
Additional information on the [alyx-matlab](https://github.com/cortex-lab/alyx-matlab), [npy-matlab](https://github.com/kwikteam/npy-matlab), [signals](https://github.com/cortex-lab/signals) and [wheelAnalysis](https://github.com/cortex-lab/wheelAnalysis) submodules can be found in their respective github repositories.

cortexlab/+git/runCmd.m

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
function [exitCode, cmdOut] = runCmd(cmd, varargin)
2+
%GIT.RUNCMD runs git commands in MATLAB.
3+
%
4+
% Inputs:
5+
% `cmd`: A str or cellstr array of git command(s) to run.
6+
% `dir`: An optional string name-value paired argument which specifies
7+
% the directory in which to run the command (default is `pwd`)
8+
% `echo`: An optional boolean name-value paired argument which specifies
9+
% whether to display command output on the MATLAB command window (default
10+
% is `true`).
11+
%
12+
% Outputs:
13+
% `exitCode`: A flag array indicating whether each command in `cmd` was
14+
% run succesfully (0) or there was an error (1).
15+
% `cmdOut`: A cellstr array of the output of `cmd`.
16+
%
17+
% Example: Get the status and log for the git repository in the working
18+
% folder, display the commands' outputs in MATLAB, and save the command
19+
% outputs in the `cmdOut` cellstr:
20+
% [exitCode, cmdOut] = git.runCmd({'status', 'log -n 1'});
21+
%
22+
% Example: Stash the working changes in the Rigbox repository without
23+
% displaying the command's output:
24+
% rigboxPath = which('addRigboxPaths');
25+
% exitCode = git.runCmd({'stash push -m "WIP..."'}, 'dir', rigboxPath,...
26+
% 'echo', false);
27+
28+
%% set-up
29+
% If `cmd` is a char or cellstr, turn it into a string
30+
cmd = string(cmd); % Convert to string array
31+
32+
% When this function terminates, return to the working directory.
33+
origDir = pwd;
34+
cleanup = onCleanup(@() cd(origDir));
35+
36+
% Define default input args in a struct.
37+
defaults.dir = pwd;
38+
defaults.echo = true;
39+
try
40+
% Parse Name-Value pairs
41+
inputs = cell2struct(varargin(2:2:end)', varargin(1:2:end)');
42+
args = mergeStructs(inputs, defaults);
43+
catch
44+
% If the name-value pairs don't match up, throw error.
45+
error('Rigbox:git:runCmd:nameValueArgs', ['%s requires optional input '...
46+
'args to be constructed in name-value pairs'], mfilename);
47+
end
48+
49+
% Change into the specified directory.
50+
cd(args.dir);
51+
52+
%% run commands
53+
% Search `dat.paths` for the path to the Git exe; if not found, perform a
54+
% system search; if still not found, throw an error.
55+
gitexepath = getOr(dat.paths, 'gitExe');
56+
if isempty(gitexepath)
57+
[exitCode, gitexepath] = system('where git');
58+
if exitCode
59+
error('Rigbox:git:runCmd:gitNotFound', ['Could not find the git '...
60+
'executable on your system. Please ensure that you have git '...
61+
'installed, and set it''s full path in `+dat/paths.m`.']);
62+
end
63+
end
64+
% Convert to string for running system commands.
65+
gitexepath = ['"', strtrim(gitexepath), '"'];
66+
67+
% Run commands.
68+
exitCode = zeros(1, length(cmd));
69+
cmdOut = cell(1, length(cmd));
70+
cmd = strcat(gitexepath, " ", cmd);
71+
72+
for i = 1:length(cmd)
73+
if args.echo
74+
[exitCode(i), cmdOut{i}] = system(cmd{i}, '-echo');
75+
else
76+
[exitCode(i), cmdOut{i}] = system(cmd{i});
77+
end
78+
end

cortexlab/+git/update.m

Lines changed: 70 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,89 @@
1-
function update(scheduled)
2-
% GIT.UPDATE Pull latest Rigbox code
3-
% Pulls the latest code from the remote Github repository. If 'scheduled'
4-
% is a value in the range [1 7] - corresponding to the days of the week,
5-
% with Sunday=1 - code will be pulled only on the 'scheduled' day,
6-
% provided the last fetch was over a day ago. Code will also be pulled if
7-
% it is not the scheduled day, but the last fetch was over a week ago. If
8-
% scheduled is 0, the function will pull changes provided the last fetch
9-
% was over an hour ago.
1+
function exitCode = update(scheduled)
2+
% GIT.UPDATE Pulls latest Rigbox code
3+
% `git.update` pulls the latest code from the remote Rigbox Github
4+
% repository if it is run on a specific day of the week (provided the
5+
% remote code was last fetched over a day ago), or immediately (provided
6+
% the remote code was last fetched over an hour ago), according to
7+
% `scheduled`. If run without an input arg, code is pulled according to
8+
% the `updateSchedule` field in the struct returned by `dat.paths`, or
9+
% immediately if the `updateSchedule` field is not found in `dat.paths`
10+
% (provided the remote code was last fetched over an hour ago).
11+
%
12+
% Inputs:
13+
% `scheduled`: an optional input as an integer in the interval [0,7].
14+
% When 0, the remote code will be pulled provided the last fetch was
15+
% over an hour ago. When in the interval [1,7], code will be pulled on
16+
% a corresponding day according to `weekday` (Sunday=1, Monday=2, ...
17+
% Saturday = 7), provided the last fetch was over a day ago. Code will
18+
% also be pulled if it is not the scheduled day, but the last fetch was
19+
% over a week ago. If scheduled is 0, the function will pull changes
20+
% provided the last fetch was over an hour ago.
1021
%
11-
% TODO Find quicker way to check for changes
22+
% Outputs:
23+
% `exitCode`: An integer in the interval [0,2]. 0 indicates a
24+
% successful update of the code. 1 indicates an error running git
25+
% commands. 2 indicates successfully returning from the function
26+
% without updating the code if the last fetch was within an hour and
27+
% `scheduled` == 0, or if the last fetch was within 24 hours and
28+
% `scheduled` is an integer in the interval [1,7]).
29+
%
30+
% Example: Pull remote code immediately, provided last code fetch was
31+
% over an hour ago:
32+
% git.update(0);
33+
%
34+
% Example: Pull remote code if today is Monday, provided last code fetch
35+
% was over a day ago:
36+
% git.update(2);
37+
%
1238
% See also DAT.PATHS
39+
%
40+
% TODO Find quicker way to check for changes
1341

1442
% Check that paths are set up
1543
assert(~isempty(which('dat.paths')), ...
1644
'rigbox:git:update:copyPaths',...
1745
['Error: ''dat.paths'' file not found. Please ensure that a '...
1846
'''dat.paths'' file exists for your setup. A template can be found at '...
1947
'''docs/setup/paths_template''.'])
48+
49+
% If no input arg, or input arg is not an acceptable value, use
50+
% `updateSchedule` in `dat.paths`. If `updateSchedule` is not found, set
51+
% `scheduled` to 0.
52+
if nargin < 1
53+
scheduled = getOr(dat.paths, 'updateSchedule', 0);
54+
elseif ~isnumeric(scheduled) || ~any(0:7 == scheduled)
55+
error('Rigbox:git:update:valueError', ...
56+
'Input must be integer between 0 and 7')
57+
end
58+
root = getOr(dat.paths, 'rigbox'); % Rigbox root directory
2059

21-
% If not given as input argument, use 'updateSchedule' in 'dat.paths'. If
22-
% not found, set 'scheduled' to 0.
23-
if nargin < 1; scheduled = getOr(dat.paths, 'updateSchedule', 0); end
24-
root = fileparts(which('addRigboxPaths')); % Rigbox root directory
2560
% Attempt to find date of last fetch
2661
fetch_head = fullfile(root, '.git', 'FETCH_HEAD');
27-
lastFetch = iff(exist(fetch_head,'file')==2, ... % If FETCH_HEAD file exists
28-
@()getOr(dir(fetch_head), 'datenum'), 0); % Retrieve date modified
62+
% If `FETCH_HEAD` file exists, retrieve datenum for when last modified,
63+
% else return 0 (i.e. there was never a fetch)
64+
lastFetch = iff(exist(fetch_head,'file')==2, ...
65+
file.modDate(fetch_head), 0);
2966

30-
% Don't pull changes if the following conditions are met:
31-
% 1. The updates are scheduled for a different day and the last fetch was
32-
% less than a week ago.
33-
% 2. The updates are scheduled for today and the last fetch was today.
34-
% 3. The updates are scheduled for every day and the last fetch was less
35-
% than an hour ago.
36-
if (scheduled && (weekday(now) ~= scheduled) && now - lastFetch < 7) || ...
37-
(scheduled && (weekday(now) == scheduled) && now - lastFetch < 1) || ...
38-
(~scheduled && now - lastFetch < 1/24)
67+
% Pull changes if the following conditions are met:
68+
% 1. The last fetch was over a week ago.
69+
% 2. The updates are scheduled for today and not yet fetched today.
70+
% 3. The updates are daily and the last fetch was over an hour ago.
71+
fetchDay = scheduled == weekday(now) || scheduled == false;
72+
minTime = iff(scheduled, iff(fetchDay, 1, 7), 1/24);
73+
fetched = now-lastFetch < minTime;
74+
if fetched || ~fetchDay
75+
exitCode = 2;
3976
return
4077
end
4178
disp('Updating code...')
4279

43-
% Get the path to the Git exe
44-
gitexepath = getOr(dat.paths, 'gitExe');
45-
if isempty(gitexepath)
46-
[~,gitexepath] = system('where git'); % todo: this doesn't always work
47-
end
48-
gitexepath = ['"', strtrim(gitexepath), '"'];
49-
50-
% Temporarily change directory into Rigbox to git pull
51-
origDir = pwd;
52-
cd(root)
53-
5480
% Create Windows system commands for git stashing, initializing submodules,
5581
% and pulling
56-
cmdstrStash = [gitexepath, ' stash push -m "stash Rigbox working changes before scheduled git update"'];
57-
cmdstrStashSubs = [gitexepath, ' submodule foreach "git stash push"'];
58-
cmdstrInit = [gitexepath, ' submodule update --init'];
59-
cmdstrPull = [gitexepath, ' pull --recurse-submodules --strategy-option=theirs'];
60-
61-
% Stash any WIP, check submodules are initialized, pull
62-
try
63-
[~, cmdout] = system(cmdstrStash, '-echo');
64-
[~, cmdout] = system(cmdstrStashSubs, '-echo');
65-
[~, cmdout] = system(cmdstrInit, '-echo');
66-
[~, cmdout] = system(cmdstrPull, '-echo'); %#ok<ASGLU>
67-
catch ex
68-
cd(origDir)
69-
error('gitUpdate:pull:pullFailed', 'Failed to pull latest changes:, %s', cmdout)
70-
end
71-
72-
% Run any new tasks
73-
changesPath = fullfile(root, 'cortexlab', '+git', 'changes.m');
74-
if exist(changesPath, 'file')
75-
git.changes;
76-
delete(changesPath);
77-
end
78-
cd(origDir)
79-
end
82+
stash = ['stash push -m "stash Rigbox working changes before '...
83+
'scheduled git update"'];
84+
stashSubs = 'submodule foreach "git stash push"';
85+
init = 'submodule update --init';
86+
pull = 'pull --recurse-submodules --strategy-option=theirs';
87+
cmds = {stash, stashSubs, init, pull};
88+
% run commands in Rigbox root folder
89+
exitCode = any(git.runCmd(cmds, 'dir', root, 'echo', true));

tests/cortexlab/git/runCmd_test.m

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
%RUNCMD_TEST contains unit tests for `git.runCmd`
2+
3+
% preconditions:
4+
% set this in case a test we want to fail actually passes
5+
ex.identifier = '';
6+
root = getOr(dat.paths, 'rigbox');
7+
8+
%% Test 1: Inputs not specified correctly fail
9+
% Name-value args are not specified correctly.
10+
try git.runCmd('status', pi, true); catch ex, end
11+
msg = 'Illegaly accepts a name-value paired arg where name is not a char';
12+
assert(strcmp(ex.identifier, 'Rigbox:git:runCmd:nameValueArgs'), msg)
13+
14+
try git.runCmd('status', 'echo'); catch ex, end
15+
msg = ['Illegaly accepts a name-value paired arg where a name has no '...
16+
'matching value'];
17+
assert(strcmp(ex.identifier, 'Rigbox:git:runCmd:nameValueArgs'), msg)
18+
19+
%% Test 2 : Proper cleanup
20+
dir = pwd;
21+
git.runCmd('status', 'dir', root, 'echo', false);
22+
msg = '`onCleanup` did not run correctly';
23+
assert(strcmp(pwd, dir), msg)
24+
25+
%% Test 3 : Illegal system command fails
26+
exitCode = git.runCmd('not a valid command');
27+
msg = 'Runs an illegal command with exiting with appropriate exit code';
28+
assert(exitCode == 1, msg)
29+
30+
%% Test 4 : Running different commands while using optional name-value args
31+
% Single command.
32+
exitCode = git.runCmd({'status'}, 'echo', false);
33+
msg = 'Failed to run a single command';
34+
assert(exitCode == 0, msg)
35+
% Multiple commands.
36+
errCode = ...
37+
git.runCmd({'status', 'log -n 1', 'branch'},...
38+
'dir', root, 'echo', false);
39+
msg = 'Failed to run multiple commands with user-set name-value args';
40+
assert(~any(errCode), msg)
41+
42+
%% Test 5 : Echo parameter
43+
% Test the echo input has desired effect
44+
T = evalc("git.runCmd('status', 'echo', true);");
45+
assert(~isempty(T), 'Failed to echo output');
46+
47+
T = evalc("git.runCmd('status', 'echo', false);");
48+
assert(isempty(T), 'Unexpected output printed to command window');

0 commit comments

Comments
 (0)