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;
|
||||
startPoint?: string;
|
||||
addProfile?: string[];
|
||||
to?: string[];
|
||||
from?: string[];
|
||||
}
|
||||
|
||||
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.
|
||||
* 1. sparo checkout [-b|-B] <new-branch> [start-point] [--profile <profile...>]
|
||||
* 2. sparo checkout [branch] [--profile <profile...>]
|
||||
* 3. sparo checkout [branch] [--to <project-name...>] [--from <project-name...>]
|
||||
*
|
||||
* TODO: implement more checkout functionalities
|
||||
*/
|
||||
|
@ -67,7 +70,19 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
.array('profile')
|
||||
.default('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 (
|
||||
|
@ -76,7 +91,9 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
): Promise<void> => {
|
||||
const { _gitService: gitService } = this;
|
||||
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;
|
||||
|
||||
|
@ -145,10 +162,11 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
}
|
||||
|
||||
// preprocess profile related args
|
||||
const { isNoProfile, profiles, addProfiles } = await this._sparoProfileService.preprocessProfileArgs({
|
||||
addProfilesFromArg: args.addProfile ?? [],
|
||||
profilesFromArg: args.profile
|
||||
});
|
||||
const { isNoProfile, profiles, addProfiles, isProfileRestoreFromLocal } =
|
||||
await this._sparoProfileService.preprocessProfileArgs({
|
||||
addProfilesFromArg: args.addProfile ?? [],
|
||||
profilesFromArg: args.profile
|
||||
});
|
||||
|
||||
// Check wether profiles exist in local or operation branch
|
||||
// Skip check in the following cases:
|
||||
|
@ -216,7 +234,10 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
// Sync local sparse checkout state with given profiles.
|
||||
await this._sparoProfileService.syncProfileState({
|
||||
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 { inject } from 'inversify';
|
||||
import { Service } from '../decorator';
|
||||
import { SparoProfile, ISelection } from '../logic/SparoProfile';
|
||||
import { SparoProfile, ISelection, ISparoProfileJson } from '../logic/SparoProfile';
|
||||
import { TerminalService } from './TerminalService';
|
||||
import { GitService } from './GitService';
|
||||
import { GitSparseCheckoutService } from './GitSparseCheckoutService';
|
||||
|
@ -18,6 +18,7 @@ export interface IResolveSparoProfileOptions {
|
|||
}
|
||||
|
||||
const defaultSparoProfileFolder: string = 'common/sparo-profiles';
|
||||
const INTERNAL_RUSH_SELECTOR_PSEUDO_PROFILE: string = '__INTERNAL_RUSH_SELECTOR_PSEUDO_PROFILE__';
|
||||
|
||||
@Service()
|
||||
export class SparoProfileService {
|
||||
|
@ -170,10 +171,12 @@ ${availableProfiles.join(',')}
|
|||
addProfilesFromArg: string[];
|
||||
}): Promise<{
|
||||
isNoProfile: boolean;
|
||||
isProfileRestoreFromLocal: boolean;
|
||||
profiles: Set<string>;
|
||||
addProfiles: Set<string>;
|
||||
}> {
|
||||
let isNoProfile: boolean = false;
|
||||
let isProfileRestoreFromLocal: boolean = false;
|
||||
/**
|
||||
* --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.
|
||||
// 2. If none profile specified, read from existing profile from local state as default.
|
||||
const localStateProfiles: ILocalStateProfiles | undefined = await this._localState.getProfiles();
|
||||
|
||||
isProfileRestoreFromLocal = true;
|
||||
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 {
|
||||
isNoProfile,
|
||||
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
|
||||
*/
|
||||
public async syncProfileState({
|
||||
profiles,
|
||||
addProfiles
|
||||
addProfiles,
|
||||
fromProjects,
|
||||
toProjects,
|
||||
isProfileRestoreFromLocal
|
||||
}: {
|
||||
profiles?: Set<string>;
|
||||
addProfiles?: Set<string>;
|
||||
fromProjects?: Set<string>;
|
||||
toProjects?: Set<string>;
|
||||
isProfileRestoreFromLocal?: boolean;
|
||||
}): 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();
|
||||
const allProfiles: string[] = Array.from([...(profiles ?? []), ...(addProfiles ?? [])]);
|
||||
if (allProfiles.length > 1) {
|
||||
|
@ -239,11 +279,11 @@ ${availableProfiles.join(',')}
|
|||
`Syncing checkout with these Sparo profiles:\n${allProfiles.join(', ')}`
|
||||
);
|
||||
} else if (allProfiles.length === 1) {
|
||||
this._terminalService.terminal.writeLine(
|
||||
`Syncing checkout with the Sparo profile: ${allProfiles[0]}`
|
||||
);
|
||||
this._terminalService.terminal.writeLine(`Syncing checkout with the Sparo profile: ${allProfiles[0]}`);
|
||||
} 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();
|
||||
if (!profiles || profiles.size === 0) {
|
||||
|
@ -298,5 +338,32 @@ ${availableProfiles.join(',')}
|
|||
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]
|
||||
--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>; if it
|
||||
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: []]
|
||||
--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'],
|
||||
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
|
||||
{
|
||||
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