mirror of
https://github.com/tiktok/sparo.git
synced 2024-11-14 19:35:12 -05:00
Merge pull request #67 from jzhang026/feat/checout-to-from-selector
feat: Sparo checkout support --to/--from option for direct projects selection
This commit is contained in:
commit
486822b310
8 changed files with 224 additions and 16 deletions
|
@ -14,6 +14,8 @@ export interface ICheckoutCommandOptions {
|
||||||
B?: boolean;
|
B?: boolean;
|
||||||
startPoint?: string;
|
startPoint?: string;
|
||||||
addProfile?: string[];
|
addProfile?: string[];
|
||||||
|
to?: string[];
|
||||||
|
from?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type ICheckoutTargetKind = 'branch' | 'tag' | 'commit' | 'filePath';
|
type ICheckoutTargetKind = 'branch' | 'tag' | 'commit' | 'filePath';
|
||||||
|
@ -43,6 +45,7 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
||||||
* have been implemented, while other scenarios are yet to be implemented.
|
* have been implemented, while other scenarios are yet to be implemented.
|
||||||
* 1. sparo checkout [-b|-B] <new-branch> [start-point] [--profile <profile...>]
|
* 1. sparo checkout [-b|-B] <new-branch> [start-point] [--profile <profile...>]
|
||||||
* 2. sparo checkout [branch] [--profile <profile...>]
|
* 2. sparo checkout [branch] [--profile <profile...>]
|
||||||
|
* 3. sparo checkout [branch] [--to <project-name...>] [--from <project-name...>]
|
||||||
*
|
*
|
||||||
* TODO: implement more checkout functionalities
|
* TODO: implement more checkout functionalities
|
||||||
*/
|
*/
|
||||||
|
@ -67,7 +70,19 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
||||||
.array('profile')
|
.array('profile')
|
||||||
.default('profile', [])
|
.default('profile', [])
|
||||||
.array('add-profile')
|
.array('add-profile')
|
||||||
.default('add-profile', []);
|
.default('add-profile', [])
|
||||||
|
.option('to', {
|
||||||
|
type: 'array',
|
||||||
|
default: [],
|
||||||
|
description:
|
||||||
|
'Checkout projects up to (and including) project <to..>, can be used together with option --profile/--add-profile to form a union selection of the two options. The projects selectors here will never replace what have been checked out by profiles'
|
||||||
|
})
|
||||||
|
.option('from', {
|
||||||
|
type: 'array',
|
||||||
|
default: [],
|
||||||
|
description:
|
||||||
|
'Checkout projects downstream from (and including itself and all its dependencies) project <from..>, can be used together with option --profile/--add-profile to form a union selection of the two options. The projects selectors here will never replace what have been checked out by profiles'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public handler = async (
|
public handler = async (
|
||||||
|
@ -76,7 +91,9 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const { _gitService: gitService } = this;
|
const { _gitService: gitService } = this;
|
||||||
terminalService.terminal.writeDebugLine(`got args in checkout command: ${JSON.stringify(args)}`);
|
terminalService.terminal.writeDebugLine(`got args in checkout command: ${JSON.stringify(args)}`);
|
||||||
const { b, B, startPoint } = args;
|
const { b, B, startPoint, to, from } = args;
|
||||||
|
const toProjects: Set<string> = new Set(to);
|
||||||
|
const fromProjects: Set<string> = new Set(from);
|
||||||
|
|
||||||
let branch: string | undefined = args.branch;
|
let branch: string | undefined = args.branch;
|
||||||
|
|
||||||
|
@ -145,7 +162,8 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// preprocess profile related args
|
// preprocess profile related args
|
||||||
const { isNoProfile, profiles, addProfiles } = await this._sparoProfileService.preprocessProfileArgs({
|
const { isNoProfile, profiles, addProfiles, isProfileRestoreFromLocal } =
|
||||||
|
await this._sparoProfileService.preprocessProfileArgs({
|
||||||
addProfilesFromArg: args.addProfile ?? [],
|
addProfilesFromArg: args.addProfile ?? [],
|
||||||
profilesFromArg: args.profile
|
profilesFromArg: args.profile
|
||||||
});
|
});
|
||||||
|
@ -216,7 +234,10 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
||||||
// Sync local sparse checkout state with given profiles.
|
// Sync local sparse checkout state with given profiles.
|
||||||
await this._sparoProfileService.syncProfileState({
|
await this._sparoProfileService.syncProfileState({
|
||||||
profiles: isNoProfile ? undefined : profiles,
|
profiles: isNoProfile ? undefined : profiles,
|
||||||
addProfiles
|
addProfiles,
|
||||||
|
fromProjects,
|
||||||
|
toProjects,
|
||||||
|
isProfileRestoreFromLocal
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { FileSystem, Async } from '@rushstack/node-core-library';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { inject } from 'inversify';
|
import { inject } from 'inversify';
|
||||||
import { Service } from '../decorator';
|
import { Service } from '../decorator';
|
||||||
import { SparoProfile, ISelection } from '../logic/SparoProfile';
|
import { SparoProfile, ISelection, ISparoProfileJson } from '../logic/SparoProfile';
|
||||||
import { TerminalService } from './TerminalService';
|
import { TerminalService } from './TerminalService';
|
||||||
import { GitService } from './GitService';
|
import { GitService } from './GitService';
|
||||||
import { GitSparseCheckoutService } from './GitSparseCheckoutService';
|
import { GitSparseCheckoutService } from './GitSparseCheckoutService';
|
||||||
|
@ -18,6 +18,7 @@ export interface IResolveSparoProfileOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultSparoProfileFolder: string = 'common/sparo-profiles';
|
const defaultSparoProfileFolder: string = 'common/sparo-profiles';
|
||||||
|
const INTERNAL_RUSH_SELECTOR_PSEUDO_PROFILE: string = '__INTERNAL_RUSH_SELECTOR_PSEUDO_PROFILE__';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SparoProfileService {
|
export class SparoProfileService {
|
||||||
|
@ -170,10 +171,12 @@ ${availableProfiles.join(',')}
|
||||||
addProfilesFromArg: string[];
|
addProfilesFromArg: string[];
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
isNoProfile: boolean;
|
isNoProfile: boolean;
|
||||||
|
isProfileRestoreFromLocal: boolean;
|
||||||
profiles: Set<string>;
|
profiles: Set<string>;
|
||||||
addProfiles: Set<string>;
|
addProfiles: Set<string>;
|
||||||
}> {
|
}> {
|
||||||
let isNoProfile: boolean = false;
|
let isNoProfile: boolean = false;
|
||||||
|
let isProfileRestoreFromLocal: boolean = false;
|
||||||
/**
|
/**
|
||||||
* --profile is defined as array type parameter, specifying --no-profile is resolved to false by yargs.
|
* --profile is defined as array type parameter, specifying --no-profile is resolved to false by yargs.
|
||||||
*
|
*
|
||||||
|
@ -210,28 +213,65 @@ ${availableProfiles.join(',')}
|
||||||
// 1. If profile specified from CLI parameter, preferential use it.
|
// 1. If profile specified from CLI parameter, preferential use it.
|
||||||
// 2. If none profile specified, read from existing profile from local state as default.
|
// 2. If none profile specified, read from existing profile from local state as default.
|
||||||
const localStateProfiles: ILocalStateProfiles | undefined = await this._localState.getProfiles();
|
const localStateProfiles: ILocalStateProfiles | undefined = await this._localState.getProfiles();
|
||||||
|
isProfileRestoreFromLocal = true;
|
||||||
if (localStateProfiles) {
|
if (localStateProfiles) {
|
||||||
Object.keys(localStateProfiles).forEach((p) => profiles.add(p));
|
Object.keys(localStateProfiles).forEach((p) => {
|
||||||
|
if (p === INTERNAL_RUSH_SELECTOR_PSEUDO_PROFILE) return;
|
||||||
|
profiles.add(p);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
isNoProfile,
|
isNoProfile,
|
||||||
profiles,
|
profiles,
|
||||||
addProfiles
|
addProfiles,
|
||||||
|
isProfileRestoreFromLocal
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _syncRushSelectors(): Promise<ISelection[]>;
|
||||||
|
private async _syncRushSelectors(selections: ISelection[]): Promise<void>;
|
||||||
|
private async _syncRushSelectors(selections?: ISelection[]): Promise<void | ISelection[]> {
|
||||||
|
if (typeof selections !== 'undefined') {
|
||||||
|
return this._localState.setProfiles(
|
||||||
|
{
|
||||||
|
[INTERNAL_RUSH_SELECTOR_PSEUDO_PROFILE]: {
|
||||||
|
selections
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'add'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const localStateProfiles: ILocalStateProfiles | undefined = await this._localState.getProfiles();
|
||||||
|
if (localStateProfiles) {
|
||||||
|
const rushSelectorProfiles: ISparoProfileJson | undefined =
|
||||||
|
localStateProfiles[INTERNAL_RUSH_SELECTOR_PSEUDO_PROFILE];
|
||||||
|
return rushSelectorProfiles?.selections || [];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sync local sparse checkout state with specified profiles
|
* sync local sparse checkout state with specified profiles
|
||||||
*/
|
*/
|
||||||
public async syncProfileState({
|
public async syncProfileState({
|
||||||
profiles,
|
profiles,
|
||||||
addProfiles
|
addProfiles,
|
||||||
|
fromProjects,
|
||||||
|
toProjects,
|
||||||
|
isProfileRestoreFromLocal
|
||||||
}: {
|
}: {
|
||||||
profiles?: Set<string>;
|
profiles?: Set<string>;
|
||||||
addProfiles?: Set<string>;
|
addProfiles?: Set<string>;
|
||||||
|
fromProjects?: Set<string>;
|
||||||
|
toProjects?: Set<string>;
|
||||||
|
isProfileRestoreFromLocal?: boolean;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
|
// only if user didn't specify any profile during a sparo checkout, we need to
|
||||||
|
// retain any previously checked out projects based on Rush Selectors
|
||||||
|
// https://rushjs.io/pages/developer/selecting_subsets/
|
||||||
|
const rushSelectorState: ISelection[] = isProfileRestoreFromLocal ? await this._syncRushSelectors() : [];
|
||||||
this._localState.reset();
|
this._localState.reset();
|
||||||
const allProfiles: string[] = Array.from([...(profiles ?? []), ...(addProfiles ?? [])]);
|
const allProfiles: string[] = Array.from([...(profiles ?? []), ...(addProfiles ?? [])]);
|
||||||
if (allProfiles.length > 1) {
|
if (allProfiles.length > 1) {
|
||||||
|
@ -239,11 +279,11 @@ ${availableProfiles.join(',')}
|
||||||
`Syncing checkout with these Sparo profiles:\n${allProfiles.join(', ')}`
|
`Syncing checkout with these Sparo profiles:\n${allProfiles.join(', ')}`
|
||||||
);
|
);
|
||||||
} else if (allProfiles.length === 1) {
|
} else if (allProfiles.length === 1) {
|
||||||
this._terminalService.terminal.writeLine(
|
this._terminalService.terminal.writeLine(`Syncing checkout with the Sparo profile: ${allProfiles[0]}`);
|
||||||
`Syncing checkout with the Sparo profile: ${allProfiles[0]}`
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
this._terminalService.terminal.writeLine('Syncing checkout with the Sparo skeleton (no profile selection)');
|
this._terminalService.terminal.writeLine(
|
||||||
|
'Syncing checkout with the Sparo skeleton (no profile selection)'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this._terminalService.terminal.writeLine();
|
this._terminalService.terminal.writeLine();
|
||||||
if (!profiles || profiles.size === 0) {
|
if (!profiles || profiles.size === 0) {
|
||||||
|
@ -298,5 +338,32 @@ ${availableProfiles.join(',')}
|
||||||
checkoutAction: 'add'
|
checkoutAction: 'add'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle case of `sparo checkout --to project-A project-B --from project-C project-D
|
||||||
|
const toSelector: Set<string> = toProjects || new Set();
|
||||||
|
const fromSelector: Set<string> = fromProjects || new Set();
|
||||||
|
// If Rush Selector --to <projects> is specified, using `git sparse-checkout add` to add folders of the projects specified
|
||||||
|
const projectsSelections: ISelection[] = [...rushSelectorState];
|
||||||
|
|
||||||
|
for (const project of toSelector) {
|
||||||
|
projectsSelections.push({
|
||||||
|
selector: '--to',
|
||||||
|
argument: project
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const project of fromSelector) {
|
||||||
|
projectsSelections.push({
|
||||||
|
selector: '--from',
|
||||||
|
argument: project
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectsSelections.length > 0) {
|
||||||
|
await this._syncRushSelectors(projectsSelections);
|
||||||
|
await this._gitSparseCheckoutService.checkoutAsync({
|
||||||
|
selections: projectsSelections,
|
||||||
|
checkoutAction: 'add'
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,4 +20,19 @@ Options:
|
||||||
already exists, reset it to <start-point> [boolean]
|
already exists, reset it to <start-point> [boolean]
|
||||||
--profile [array] [default: []]
|
--profile [array] [default: []]
|
||||||
--add-profile [array] [default: []]
|
--add-profile [array] [default: []]
|
||||||
|
--to Checkout projects up to (and including) project <to..>, can
|
||||||
|
be used together with option --profile/--add-profile to
|
||||||
|
form a union selection of the two options. The projects
|
||||||
|
selectors here will never replace what have been checked
|
||||||
|
out by profiles [array] [default: []]
|
||||||
|
--from Checkout projects downstream from (and including itself and
|
||||||
|
all its dependencies) project <from..>, can be used
|
||||||
|
together with option --profile/--add-profile to form a
|
||||||
|
union selection of the two options. The projects selectors
|
||||||
|
here will never replace what have been checked out by
|
||||||
|
profiles [array] [default: []]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,5 +19,16 @@ Options:
|
||||||
-b Create a new branch and start it at <start-point> [boolean]
|
-b Create a new branch and start it at <start-point> [boolean]
|
||||||
-B Create a new branch and start it at <start-point>; if it
|
-B Create a new branch and start it at <start-point>; if it
|
||||||
already exists, reset it to <start-point> [boolean]
|
already exists, reset it to <start-point> [boolean]
|
||||||
|
--to Checkout projects up to (and including) project <to..>, can
|
||||||
|
be used together with option --profile/--add-profile to
|
||||||
|
form a union selection of the two options. The projects
|
||||||
|
selectors here will never replace what have been checked
|
||||||
|
out by profiles [array] [default: []]
|
||||||
|
--from Checkout projects downstream from (and including itself and
|
||||||
|
all its dependencies) project <from..>, can be used
|
||||||
|
together with option --profile/--add-profile to form a
|
||||||
|
union selection of the two options. The projects selectors
|
||||||
|
here will never replace what have been checked out by
|
||||||
|
profiles [array] [default: []]
|
||||||
--profile [array] [default: []]
|
--profile [array] [default: []]
|
||||||
--add-profile [array] [default: []]
|
--add-profile [array] [default: []]
|
||||||
|
|
35
build-tests/sparo-real-repo-test/etc/checkout-from.txt
Normal file
35
build-tests/sparo-real-repo-test/etc/checkout-from.txt
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
Running "sparo checkout --from sparo":
|
||||||
|
|
||||||
|
[bold]Sparo accelerator for Git __VERSION__ -[normal][cyan] https://tiktok.github.io/sparo/[default]
|
||||||
|
Node.js version is __VERSION__ (LTS)
|
||||||
|
Git version is __VERSION__
|
||||||
|
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git checkout[normal] [gray]]-------------------------------------------------------------[default]
|
||||||
|
Your branch is up to date with 'origin/test-artifacts/sparo-real-repo-test'.
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Syncing checkout with the Sparo profile: my-build-test
|
||||||
|
|
||||||
|
Checking out and updating core files...
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git sparse-checkout[normal] [gray]]------------------------------------------------------[default]
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Checking out skeleton...
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git sparse-checkout[normal] [gray]]------------------------------------------------------[default]
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Checking out __FOLDER_COUNT__ folders...
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git sparse-checkout[normal] [gray]]------------------------------------------------------[default]
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Sparse checkout completed in __DURATION__
|
||||||
|
Checking out __FOLDER_COUNT__ folders...
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git sparse-checkout[normal] [gray]]------------------------------------------------------[default]
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Sparse checkout completed in __DURATION__
|
35
build-tests/sparo-real-repo-test/etc/checkout-to.txt
Normal file
35
build-tests/sparo-real-repo-test/etc/checkout-to.txt
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
Running "sparo checkout --to sparo-output-test":
|
||||||
|
|
||||||
|
[bold]Sparo accelerator for Git __VERSION__ -[normal][cyan] https://tiktok.github.io/sparo/[default]
|
||||||
|
Node.js version is __VERSION__ (LTS)
|
||||||
|
Git version is __VERSION__
|
||||||
|
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git checkout[normal] [gray]]-------------------------------------------------------------[default]
|
||||||
|
Your branch is up to date with 'origin/test-artifacts/sparo-real-repo-test'.
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Syncing checkout with the Sparo profile: my-build-test
|
||||||
|
|
||||||
|
Checking out and updating core files...
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git sparse-checkout[normal] [gray]]------------------------------------------------------[default]
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Checking out skeleton...
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git sparse-checkout[normal] [gray]]------------------------------------------------------[default]
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Checking out __FOLDER_COUNT__ folders...
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git sparse-checkout[normal] [gray]]------------------------------------------------------[default]
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Sparse checkout completed in __DURATION__
|
||||||
|
Checking out __FOLDER_COUNT__ folders...
|
||||||
|
|
||||||
|
[gray]--[[default] [bold]git sparse-checkout[normal] [gray]]------------------------------------------------------[default]
|
||||||
|
[gray]-------------------------------------------------------------------------------[default]
|
||||||
|
|
||||||
|
Sparse checkout completed in __DURATION__
|
|
@ -80,6 +80,20 @@ export async function runAsync(runScriptOptions: IRunScriptOptions): Promise<voi
|
||||||
args: ['checkout', '--profile', 'my-build-test'],
|
args: ['checkout', '--profile', 'my-build-test'],
|
||||||
currentWorkingDirectory: repoFolder
|
currentWorkingDirectory: repoFolder
|
||||||
},
|
},
|
||||||
|
// sparo checkout --to sparo-output-test
|
||||||
|
{
|
||||||
|
kind: 'sparo-command',
|
||||||
|
name: 'checkout-to',
|
||||||
|
args: ['checkout', '--to', 'sparo-output-test'],
|
||||||
|
currentWorkingDirectory: repoFolder
|
||||||
|
},
|
||||||
|
// sparo checkout --from build-test-utilities
|
||||||
|
{
|
||||||
|
kind: 'sparo-command',
|
||||||
|
name: 'checkout-from',
|
||||||
|
args: ['checkout', '--from', 'sparo'],
|
||||||
|
currentWorkingDirectory: repoFolder
|
||||||
|
},
|
||||||
// sparo list-profiles
|
// sparo list-profiles
|
||||||
{
|
{
|
||||||
kind: 'sparo-command',
|
kind: 'sparo-command',
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"packageName": "sparo",
|
||||||
|
"comment": "[sparo checkout] support cli --to/--from option for projects selection",
|
||||||
|
"type": "none"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packageName": "sparo"
|
||||||
|
}
|
Loading…
Reference in a new issue