From c9ffc601749dae6619e541d969173ac051e6f573 Mon Sep 17 00:00:00 2001
From: Przemyslaw Zan
Date: Mon, 5 Dec 2022 16:38:42 +0100
Subject: [PATCH 1/6] Added support for presets.
---
index.js | 2 +
lib/commands/save.js | 34 ++-
lib/utils/getoptions.js | 12 +-
tests/commands/save.js | 219 +++++++++++++++++-
.../fixtures/project-with-presets/mrgit.json | 12 +
tests/utils/getoptions.js | 32 +++
6 files changed, 302 insertions(+), 9 deletions(-)
create mode 100644 tests/fixtures/project-with-presets/mrgit.json
diff --git a/index.js b/index.js
index fa5d537..0ed3333 100755
--- a/index.js
+++ b/index.js
@@ -114,6 +114,8 @@ function handleCli() {
${ y( '--scope' ) } Restricts the command to packages which names match the given glob pattern.
${ g( 'Default: null' ) }
+ ${ y( '--preset' ) } Uses an alternative set of dependencies defined in the config file.
+
${ u( 'Git Options:' ) }
Git options are supported by the following commands: commit, diff, fetch, push.
Type "mrgit [command] -h" in order to see which options are supported.
diff --git a/lib/commands/save.js b/lib/commands/save.js
index 04c3216..527c75e 100644
--- a/lib/commands/save.js
+++ b/lib/commands/save.js
@@ -99,8 +99,9 @@ module.exports = {
*
* @param {Set} processedPackages Collection of processed packages.
* @param {Set} commandResponses Results of executed command for each package.
+ * @param {Options} toolOptions Options resolved by mrgit.
*/
- afterExecute( processedPackages, commandResponses ) {
+ afterExecute( processedPackages, commandResponses, toolOptions ) {
const cwd = require( '../utils/getcwd' )();
const mrgitJsonPath = path.join( cwd, 'mrgit.json' );
@@ -112,15 +113,42 @@ module.exports = {
.replace( tagPattern, '' )
.split( '#' )[ 0 ];
+ const objectToUpdate = getObjectToUpdate( json, toolOptions, response.packageName );
+
// If returned branch is equal to 'master', save only the repository path.
if ( response.branch && response.data === 'master' ) {
- json.dependencies[ response.packageName ] = repository;
+ objectToUpdate[ response.packageName ] = repository;
} else {
- json.dependencies[ response.packageName ] = `${ repository }#${ response.data }`;
+ objectToUpdate[ response.packageName ] = `${ repository }#${ response.data }`;
}
}
return json;
} );
+
+ /**
+ * If preset is being used it should update the value defined in the preset,
+ * rather than the one in the base "dependencies" object.
+ *
+ * @param {Object} json
+ * @param {Options} toolOptions
+ * @param {String} packageName
+ * @returns
+ */
+ function getObjectToUpdate( json, toolOptions ) {
+ if ( !toolOptions.preset ) {
+ return json.dependencies;
+ }
+
+ if ( !json.presets ) {
+ return json.dependencies;
+ }
+
+ if ( !json.presets[ toolOptions.preset ] ) {
+ return json.dependencies;
+ }
+
+ return json.presets[ toolOptions.preset ];
+ }
}
};
diff --git a/lib/utils/getoptions.js b/lib/utils/getoptions.js
index caf12e1..649b1d1 100644
--- a/lib/utils/getoptions.js
+++ b/lib/utils/getoptions.js
@@ -14,7 +14,7 @@ const shell = require( 'shelljs' );
* @param {String} cwd An absolute path to the directory where `mrgit.json` is available.
* @returns {Options} The options object.
*/
-module.exports = function cwdResolver( callOptions, cwd ) {
+module.exports = function getOptions( callOptions, cwd ) {
const mrgitJsonPath = path.resolve( cwd, 'mrgit.json' );
// Default options.
@@ -52,6 +52,16 @@ module.exports = function cwdResolver( callOptions, cwd ) {
options.cwdPackageBranch = response.stdout.trim();
}
+ if ( !options.preset ) {
+ return options;
+ }
+
+ if ( !options.presets || !options.presets[ options.preset ] ) {
+ throw new Error( `Preset "${ options.preset }" is not defined in "mrgit.json" file.` );
+ }
+
+ options.dependencies = Object.assign( options.dependencies, options.presets[ options.preset ] );
+
return options;
};
diff --git a/tests/commands/save.js b/tests/commands/save.js
index f329915..1a5aeb2 100644
--- a/tests/commands/save.js
+++ b/tests/commands/save.js
@@ -176,7 +176,7 @@ describe( 'commands/save', () => {
} );
describe( 'afterExecute()', () => {
- it( 'updates collected hashes in "mrgit.json" (--hash option)', () => {
+ it( 'updates collected hashes in "mrgit.json" (--hash option, default behavior)', () => {
const processedPackages = new Set();
const commandResponses = new Set();
@@ -196,7 +196,7 @@ describe( 'commands/save', () => {
branch: false
} );
- saveCommand.afterExecute( processedPackages, commandResponses );
+ saveCommand.afterExecute( processedPackages, commandResponses, toolOptions );
let json = {
dependencies: {
@@ -238,7 +238,7 @@ describe( 'commands/save', () => {
branch: true
} );
- saveCommand.afterExecute( processedPackages, commandResponses );
+ saveCommand.afterExecute( processedPackages, commandResponses, toolOptions );
let json = {
dependencies: {
@@ -260,6 +260,215 @@ describe( 'commands/save', () => {
} );
} );
+ it( 'updates collected hashes in "mrgit.json" (--preset option)', () => {
+ const processedPackages = new Set();
+ const commandResponses = new Set();
+
+ processedPackages.add( 'test-package' );
+ processedPackages.add( 'package-test' );
+
+ commandResponses.add( {
+ packageName: 'test-package',
+ data: '584f341',
+ hash: true,
+ branch: false
+ } );
+ commandResponses.add( {
+ packageName: 'package-test',
+ data: '52910fe',
+ hash: true,
+ branch: false
+ } );
+
+ saveCommand.afterExecute( processedPackages, commandResponses, toolOptions );
+
+ let json = {
+ dependencies: {
+ 'test-package': 'organization/test-package',
+ 'package-test': 'organization/package-test'
+ },
+ preset: 'development',
+ presets: {
+ development: {
+ 'test-package': 'organization/test-package#development',
+ 'package-test': 'organization/package-test#development'
+ }
+ }
+ };
+
+ toolOptions.preset = 'development';
+
+ expect( mrgitJsonPath ).to.equal( normalizedDirname + '/mrgit.json' );
+ expect( updateFunction ).to.be.a( 'function' );
+
+ json = updateFunction( json );
+
+ expect( json ).to.deep.equal( {
+ dependencies: {
+ 'test-package': 'organization/test-package',
+ 'package-test': 'organization/package-test'
+ },
+ preset: 'development',
+ presets: {
+ development: {
+ 'test-package': 'organization/test-package#584f341',
+ 'package-test': 'organization/package-test#52910fe'
+ }
+ }
+ } );
+ } );
+
+ it( 'adds new value to the preset if its not in it, but it is in the base dependencies (--preset option)', () => {
+ const processedPackages = new Set();
+ const commandResponses = new Set();
+
+ processedPackages.add( 'test-package' );
+ processedPackages.add( 'package-test' );
+
+ commandResponses.add( {
+ packageName: 'test-package',
+ data: '584f341',
+ hash: true,
+ branch: false
+ } );
+ commandResponses.add( {
+ packageName: 'package-test',
+ data: '52910fe',
+ hash: true,
+ branch: false
+ } );
+
+ saveCommand.afterExecute( processedPackages, commandResponses, toolOptions );
+
+ let json = {
+ dependencies: {
+ 'test-package': 'organization/test-package',
+ 'package-test': 'organization/package-test'
+ },
+ preset: 'development',
+ presets: {
+ development: {
+ 'test-package': 'organization/test-package#development'
+ }
+ }
+ };
+
+ toolOptions.preset = 'development';
+
+ expect( mrgitJsonPath ).to.equal( normalizedDirname + '/mrgit.json' );
+ expect( updateFunction ).to.be.a( 'function' );
+
+ json = updateFunction( json );
+
+ expect( json ).to.deep.equal( {
+ dependencies: {
+ 'test-package': 'organization/test-package',
+ 'package-test': 'organization/package-test'
+ },
+ preset: 'development',
+ presets: {
+ development: {
+ 'test-package': 'organization/test-package#584f341',
+ 'package-test': 'organization/package-test#52910fe'
+ }
+ }
+ } );
+ } );
+
+ it( 'updates base dependencies if specified preset is not defined (--preset option)', () => {
+ const processedPackages = new Set();
+ const commandResponses = new Set();
+
+ processedPackages.add( 'test-package' );
+ processedPackages.add( 'package-test' );
+
+ commandResponses.add( {
+ packageName: 'test-package',
+ data: '584f341',
+ hash: true,
+ branch: false
+ } );
+ commandResponses.add( {
+ packageName: 'package-test',
+ data: '52910fe',
+ hash: true,
+ branch: false
+ } );
+
+ saveCommand.afterExecute( processedPackages, commandResponses, toolOptions );
+
+ let json = {
+ dependencies: {
+ 'test-package': 'organization/test-package',
+ 'package-test': 'organization/package-test'
+ },
+ preset: 'development',
+ presets: {}
+ };
+
+ toolOptions.preset = 'development';
+
+ expect( mrgitJsonPath ).to.equal( normalizedDirname + '/mrgit.json' );
+ expect( updateFunction ).to.be.a( 'function' );
+
+ json = updateFunction( json );
+
+ expect( json ).to.deep.equal( {
+ dependencies: {
+ 'test-package': 'organization/test-package#584f341',
+ 'package-test': 'organization/package-test#52910fe'
+ },
+ preset: 'development',
+ presets: {}
+ } );
+ } );
+
+ it( 'updates base dependencies if specified presets are not defined (--preset option)', () => {
+ const processedPackages = new Set();
+ const commandResponses = new Set();
+
+ processedPackages.add( 'test-package' );
+ processedPackages.add( 'package-test' );
+
+ commandResponses.add( {
+ packageName: 'test-package',
+ data: '584f341',
+ hash: true,
+ branch: false
+ } );
+ commandResponses.add( {
+ packageName: 'package-test',
+ data: '52910fe',
+ hash: true,
+ branch: false
+ } );
+
+ saveCommand.afterExecute( processedPackages, commandResponses, toolOptions );
+
+ let json = {
+ dependencies: {
+ 'test-package': 'organization/test-package',
+ 'package-test': 'organization/package-test'
+ },
+ preset: 'development'
+ };
+
+ toolOptions.preset = 'development';
+
+ expect( mrgitJsonPath ).to.equal( normalizedDirname + '/mrgit.json' );
+ expect( updateFunction ).to.be.a( 'function' );
+
+ json = updateFunction( json );
+
+ expect( json ).to.deep.equal( {
+ dependencies: {
+ 'test-package': 'organization/test-package#584f341',
+ 'package-test': 'organization/package-test#52910fe'
+ },
+ preset: 'development'
+ } );
+ } );
+
it( 'does not save "#master" branch because it is default branch', () => {
const processedPackages = new Set();
const commandResponses = new Set();
@@ -273,7 +482,7 @@ describe( 'commands/save', () => {
branch: true
} );
- saveCommand.afterExecute( processedPackages, commandResponses );
+ saveCommand.afterExecute( processedPackages, commandResponses, toolOptions );
let json = {
dependencies: {
@@ -311,7 +520,7 @@ describe( 'commands/save', () => {
branch: true
} );
- saveCommand.afterExecute( processedPackages, commandResponses );
+ saveCommand.afterExecute( processedPackages, commandResponses, toolOptions );
let json = {
dependencies: {
diff --git a/tests/fixtures/project-with-presets/mrgit.json b/tests/fixtures/project-with-presets/mrgit.json
new file mode 100644
index 0000000..936d4bd
--- /dev/null
+++ b/tests/fixtures/project-with-presets/mrgit.json
@@ -0,0 +1,12 @@
+{
+ "packages": "packages/",
+ "dependencies": {
+ "linters-config": "foo/linters-config@latest",
+ "dev-tools": "foo/dev-tools@latest"
+ },
+ "presets": {
+ "development": {
+ "dev-tools": "foo/dev-tools#developmentBranch"
+ }
+ }
+}
diff --git a/tests/utils/getoptions.js b/tests/utils/getoptions.js
index 0db2ce9..bdf3878 100644
--- a/tests/utils/getoptions.js
+++ b/tests/utils/getoptions.js
@@ -178,5 +178,37 @@ describe( 'utils', () => {
fsExistsStub.restore();
shelljsStub.restore();
} );
+
+ it( 'throws an error when --preset option is used, but presets are not defined in "mrgit.json"', () => {
+ try {
+ getOptions( { preset: 'foo' }, cwd );
+ throw new Error( 'Expected to throw.' );
+ } catch ( error ) {
+ expect( error.message ).to.equal( 'Preset "foo" is not defined in "mrgit.json" file.' );
+ }
+ } );
+
+ it( 'throws an error when --preset option is used, but the specific preset is not defined in "mrgit.json"', () => {
+ const cwdForPresets = path.resolve( __dirname, '..', 'fixtures', 'project-with-presets' );
+
+ try {
+ getOptions( { preset: 'foo' }, cwdForPresets );
+ throw new Error( 'Expected to throw.' );
+ } catch ( error ) {
+ expect( error.message ).to.equal( 'Preset "foo" is not defined in "mrgit.json" file.' );
+ }
+ } );
+
+ it( 'returns options with preset merged with dependencies when --preset option is used', () => {
+ const cwdForPresets = path.resolve( __dirname, '..', 'fixtures', 'project-with-presets' );
+
+ const options = getOptions( { preset: 'development' }, cwdForPresets );
+
+ expect( options ).to.have.property( 'dependencies' );
+ expect( options.dependencies ).to.deep.equal( {
+ 'linters-config': 'foo/linters-config@latest',
+ 'dev-tools': 'foo/dev-tools#developmentBranch'
+ } );
+ } );
} );
} );
From 5440fca1d96b0d69a6d42e2b8e8cd2db20c20c31 Mon Sep 17 00:00:00 2001
From: Przemyslaw Zan
Date: Mon, 5 Dec 2022 17:16:00 +0100
Subject: [PATCH 2/6] Updated message displayed by sync command when being on a
specific commit.
---
lib/commands/status.js | 11 ++++++++---
tests/commands/status.js | 42 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/lib/commands/status.js b/lib/commands/status.js
index 1b4cef4..3a46bce 100644
--- a/lib/commands/status.js
+++ b/lib/commands/status.js
@@ -146,11 +146,15 @@ module.exports = {
shouldDisplaySyncHint = true;
}
- if ( !shouldUseTag && status.branch !== mrgitBranch ) {
+ if ( !shouldUseTag && status.branch !== mrgitBranch && commit !== mrgitBranch ) {
branchOrTag = `${ chalk.cyan( '!' ) } ${ branchOrTag }`;
shouldDisplaySyncHint = true;
}
+ if ( !shouldUseTag && mrgitBranch === commit ) {
+ branchOrTag = 'Using saved commit →';
+ }
+
if ( status.ahead ) {
branchOrTag += chalk.yellow( ` ↑${ status.ahead }` );
}
@@ -199,8 +203,9 @@ module.exports = {
hints.push( [
chalk.green( 'L' ),
'This is the latest local tag. To ensure having latest remote tag, execute',
- chalk.blue( 'mrgit sync' )
- ].join( ' ' ) + '.' );
+ chalk.blue( 'mrgit fetch' ),
+ 'before checking status.'
+ ].join( ' ' ) );
}
if ( shouldDisplaySyncHint ) {
diff --git a/tests/commands/status.js b/tests/commands/status.js
index 5f1a804..9ba83b9 100644
--- a/tests/commands/status.js
+++ b/tests/commands/status.js
@@ -627,6 +627,48 @@ describe( 'commands/status', () => {
logStub.restore();
} );
+ it( 'displays appropriate message when being checked out on a specific commit', () => {
+ const logStub = sinon.stub( console, 'log' );
+
+ const processedPackages = new Set();
+ const commandResponses = new Set();
+
+ processedPackages.add( '@ckeditor/ckeditor5-bar' );
+
+ commandResponses.add( {
+ packageName: 'bar',
+ status: {
+ branch: 'HEAD (no branch)',
+ tag: 'v35.3.2',
+ detachedHead: true,
+ ahead: null,
+ behind: null,
+ staged: [],
+ modified: [],
+ untracked: [],
+ unmerged: []
+ },
+ commit: 'ef45678',
+ mrgitBranch: 'ef45678',
+ mrgitTag: undefined,
+ currentTag: 'v35.3.2',
+ latestTag: 'v35.3.2'
+ } );
+
+ statusCommand.afterExecute( processedPackages, commandResponses );
+
+ expect( stubs.table.push.firstCall.args[ 0 ] ).to.deep.equal(
+ [ 'bar', 'Using saved commit →', 'ef45678', '' ]
+ );
+
+ expect( stubs.chalk.cyan.callCount ).to.equal( 1 );
+
+ // Table
+ expect( stubs.chalk.cyan.getCall( 0 ).args[ 0 ] ).to.equal( '!' );
+
+ logStub.restore();
+ } );
+
it( 'sorts packages alphabetically', () => {
const logStub = sinon.stub( console, 'log' );
From 2e434650dc4f97a6a9b3087d36b3d690012beb0c Mon Sep 17 00:00:00 2001
From: Przemyslaw Zan
Date: Mon, 5 Dec 2022 19:15:59 +0100
Subject: [PATCH 3/6] Updated README.md.
---
README.md | 30 ++++++++++++++++++++++++++----
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 09a07cb..c0affa7 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,8 @@ CLI options:
--scope Restricts the command to packages which names match the given glob pattern.
Default: null
+
+--preset Uses an alternative set of dependencies defined in the config file.
```
All these options can also be specified in `mrgit.json` (options passed through CLI takes precedence):
@@ -137,34 +139,54 @@ The dependency keys can be any strings, but it's recommended to use package name
Examples:
-```js
+```json
// Clone 'git@github.com:cksource/foo.git' and check out to 'master'.
{
"foo": "git@github.com:cksource/foo.git"
}
```
-```js
+```json
// Short format. Clone 'git@github.com:cksource/foo.git' and check out to branch 'dev'.
{
"@cksource/foo": "cksource/foo#dev"
}
```
-```js
+```json
// Clone 'https://github.com/cksource/foo.git' (via HTTPS) and check out to tag 'v1.2.3'.
{
"foo": "https://github.com/cksource/foo.git@v1.2.3"
}
```
-```js
+```json
// Clone 'cksource/foo' and check out to the latest available tag.
{
"foo": "cksource/foo@latest"
}
```
+### The `presets` option
+
+This option allows the user to create an easy switch between different states of the dependencies. When using any command with the `--preset` option, it will behave as if the `dependencies` option was using values from the given preset. Dependencies that are not defined in the preset but are defined in the `dependencies`, will use version from the `dependencies` as a fallback.
+
+Example:
+
+```json
+{
+ "presets": {
+ "dev": {
+ "@cksource/foo": "cksource/foo#dev"
+ },
+ "newFeature": {
+ "@cksource/foo": "cksource/foo#newFeatureBranch",
+ "@cksource/bar": "cksource/foo#newFeatureBranch"
+ }
+ }
+}
+```
+
### Recursive cloning
When the `--recursive` option is used `mrgit` will clone repositories recursively. First, it will clone the `dependencies` specified in `mrgit.json` and, then, their `dependencies` and `devDependencies` specified in `package.json` files located in cloned repositories.
From aa4427adb8a4f7d8831b58193e1da7180f02fede Mon Sep 17 00:00:00 2001
From: Przemyslaw Zan
Date: Tue, 6 Dec 2022 10:51:17 +0100
Subject: [PATCH 4/6] Review requests.
---
README.md | 6 +++---
tests/utils/getoptions.js | 14 ++++----------
2 files changed, 7 insertions(+), 13 deletions(-)
diff --git a/README.md b/README.md
index c0affa7..8bcbf34 100644
--- a/README.md
+++ b/README.md
@@ -179,9 +179,9 @@ Example:
"dev": {
"@cksource/foo": "cksource/foo#dev"
},
- "newFeature": {
- "@cksource/foo": "cksource/foo#newFeatureBranch",
- "@cksource/bar": "cksource/foo#newFeatureBranch"
+ "exampleFeature": {
+ "@cksource/foo": "cksource/foo#i/1-example-feature",
+ "@cksource/bar": "cksource/foo#i/1-example-feature"
}
}
}
diff --git a/tests/utils/getoptions.js b/tests/utils/getoptions.js
index bdf3878..57a953a 100644
--- a/tests/utils/getoptions.js
+++ b/tests/utils/getoptions.js
@@ -180,23 +180,17 @@ describe( 'utils', () => {
} );
it( 'throws an error when --preset option is used, but presets are not defined in "mrgit.json"', () => {
- try {
+ expect( () => {
getOptions( { preset: 'foo' }, cwd );
- throw new Error( 'Expected to throw.' );
- } catch ( error ) {
- expect( error.message ).to.equal( 'Preset "foo" is not defined in "mrgit.json" file.' );
- }
+ } ).to.throw( Error, 'Preset "foo" is not defined in "mrgit.json" file.' );
} );
it( 'throws an error when --preset option is used, but the specific preset is not defined in "mrgit.json"', () => {
const cwdForPresets = path.resolve( __dirname, '..', 'fixtures', 'project-with-presets' );
- try {
+ expect( () => {
getOptions( { preset: 'foo' }, cwdForPresets );
- throw new Error( 'Expected to throw.' );
- } catch ( error ) {
- expect( error.message ).to.equal( 'Preset "foo" is not defined in "mrgit.json" file.' );
- }
+ } ).to.throw( Error, 'Preset "foo" is not defined in "mrgit.json" file.' );
} );
it( 'returns options with preset merged with dependencies when --preset option is used', () => {
From cc7d94138941908488c635ccc15e1340b6b6a296 Mon Sep 17 00:00:00 2001
From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com>
Date: Tue, 6 Dec 2022 11:05:49 +0100
Subject: [PATCH 5/6] Update README.md
Co-authored-by: Kamil Piechaczek
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8bcbf34..9573f99 100644
--- a/README.md
+++ b/README.md
@@ -179,7 +179,7 @@ Example:
"dev": {
"@cksource/foo": "cksource/foo#dev"
},
- "exampleFeature": {
+ "example-feature": {
"@cksource/foo": "cksource/foo#i/1-example-feature",
"@cksource/bar": "cksource/foo#i/1-example-feature"
}
From 4d65d82f3251fdf72f4e61bed420f0a5780c48c4 Mon Sep 17 00:00:00 2001
From: Kamil Piechaczek
Date: Tue, 6 Dec 2022 20:35:01 +0100
Subject: [PATCH 6/6] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 9573f99..9c627a8 100644
--- a/README.md
+++ b/README.md
@@ -169,7 +169,7 @@ Examples:
### The `presets` option
-This option allows the user to create an easy switch between different states of the dependencies. When using any command with the `--preset` option, it will behave as if the `dependencies` option was using values from the given preset. Dependencies that are not defined in the preset but are defined in the `dependencies`, will use version from the `dependencies` as a fallback.
+This option allows the user to switch between different states of dependencies easily. When using any command with the `--preset` option, it will behave as if the `dependencies` option was using values from the given preset. Dependencies not specified in the preset but in the `dependencies` object will use a version from the latter as a fallback.
Example: