From 49250d88bbc0f659815fcf409c4c35a4dd6b659a Mon Sep 17 00:00:00 2001 From: Adrian Z <10289356+jzhang026@users.noreply.github.com> Date: Thu, 25 Apr 2024 22:13:38 +0800 Subject: [PATCH 1/5] feat: sparo checkout support rush selectors from cli input --- apps/sparo-lib/src/cli/commands/checkout.ts | 17 +++++++-- .../src/services/SparoProfileService.ts | 38 ++++++++++++++++--- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apps/sparo-lib/src/cli/commands/checkout.ts b/apps/sparo-lib/src/cli/commands/checkout.ts index ad3535b..5fcc9bf 100644 --- a/apps/sparo-lib/src/cli/commands/checkout.ts +++ b/apps/sparo-lib/src/cli/commands/checkout.ts @@ -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 { * have been implemented, while other scenarios are yet to be implemented. * 1. sparo checkout [-b|-B] [start-point] [--profile ] * 2. sparo checkout [branch] [--profile ] + * 3. sparo checkout [branch] [--to ] * * TODO: implement more checkout functionalities */ @@ -67,7 +70,11 @@ export class CheckoutCommand implements ICommand { .array('profile') .default('profile', []) .array('add-profile') - .default('add-profile', []); + .default('add-profile', []) + .array('to') + .default('to', []) + .array('from') + .default('from', []); } public handler = async ( @@ -76,7 +83,9 @@ export class CheckoutCommand implements ICommand { ): Promise => { 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 = new Set(to); + const fromProjects: Set = new Set(from); let branch: string | undefined = args.branch; @@ -209,7 +218,9 @@ export class CheckoutCommand implements ICommand { // Sync local sparse checkout state with given profiles. await this._sparoProfileService.syncProfileState({ profiles: isNoProfile ? undefined : profiles, - addProfiles + addProfiles, + fromProjects, + toProjects }); } }; diff --git a/apps/sparo-lib/src/services/SparoProfileService.ts b/apps/sparo-lib/src/services/SparoProfileService.ts index 6be05fe..52c4f72 100644 --- a/apps/sparo-lib/src/services/SparoProfileService.ts +++ b/apps/sparo-lib/src/services/SparoProfileService.ts @@ -227,10 +227,14 @@ ${availableProfiles.join(',')} */ public async syncProfileState({ profiles, - addProfiles + addProfiles, + fromProjects, + toProjects }: { profiles?: Set; addProfiles?: Set; + fromProjects?: Set; + toProjects?: Set; }): Promise { this._localState.reset(); const allProfiles: string[] = Array.from([...(profiles ?? []), ...(addProfiles ?? [])]); @@ -239,11 +243,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 +302,29 @@ ${availableProfiles.join(',')} checkoutAction: 'add' }); } + + // handle case of `sparo checkout --to project-A project-B --from project-C project-D + const toSelector: Set = toProjects || new Set(); + const fromSelector: Set = toProjects || new Set(); + // If Rush Selector --to is specified, using `git sparse-checkout add` to add folders of the projects specified + const projectsSelections: ISelection[] = []; + + for (const project of toSelector) { + projectsSelections.push({ + selector: '--to', + argument: project + }); + } + for (const project of fromSelector) { + projectsSelections.push({ + selector: '--from', + argument: project + }); + } + + await this._gitSparseCheckoutService.checkoutAsync({ + selections: projectsSelections, + checkoutAction: 'add' + }); } } From 3c96313f84072a2a6bbb73104184801cbbc2d9bf Mon Sep 17 00:00:00 2001 From: Adrian Z <10289356+jzhang026@users.noreply.github.com> Date: Thu, 25 Apr 2024 22:16:48 +0800 Subject: [PATCH 2/5] chore: rush change --- .../feat-checkout-to-selector_2024-04-25-14-16.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/sparo/feat-checkout-to-selector_2024-04-25-14-16.json diff --git a/common/changes/sparo/feat-checkout-to-selector_2024-04-25-14-16.json b/common/changes/sparo/feat-checkout-to-selector_2024-04-25-14-16.json new file mode 100644 index 0000000..4991209 --- /dev/null +++ b/common/changes/sparo/feat-checkout-to-selector_2024-04-25-14-16.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "sparo", + "comment": "[sparo checkout] support cli --to/--from option for projects seletion", + "type": "none" + } + ], + "packageName": "sparo" +} \ No newline at end of file From 2155169d5562b10507776aa25104dabcd94ec5ba Mon Sep 17 00:00:00 2001 From: Adrian Z <10289356+jzhang026@users.noreply.github.com> Date: Thu, 25 Apr 2024 22:49:01 +0800 Subject: [PATCH 3/5] chore: add test --- .../src/services/SparoProfileService.ts | 10 +++--- .../sparo-output-test/etc/checkout-help.txt | 2 ++ .../etc/checkout-from.txt | 29 +++++++++++++++ .../sparo-real-repo-test/etc/checkout-to.txt | 35 +++++++++++++++++++ .../sparo-real-repo-test/src/start-test.ts | 14 ++++++++ 5 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 build-tests/sparo-real-repo-test/etc/checkout-from.txt create mode 100644 build-tests/sparo-real-repo-test/etc/checkout-to.txt diff --git a/apps/sparo-lib/src/services/SparoProfileService.ts b/apps/sparo-lib/src/services/SparoProfileService.ts index 52c4f72..4033fae 100644 --- a/apps/sparo-lib/src/services/SparoProfileService.ts +++ b/apps/sparo-lib/src/services/SparoProfileService.ts @@ -322,9 +322,11 @@ ${availableProfiles.join(',')} }); } - await this._gitSparseCheckoutService.checkoutAsync({ - selections: projectsSelections, - checkoutAction: 'add' - }); + if (projectsSelections.length > 0) { + await this._gitSparseCheckoutService.checkoutAsync({ + selections: projectsSelections, + checkoutAction: 'add' + }); + } } } diff --git a/build-tests/sparo-output-test/etc/checkout-help.txt b/build-tests/sparo-output-test/etc/checkout-help.txt index e9e0c78..fe81830 100644 --- a/build-tests/sparo-output-test/etc/checkout-help.txt +++ b/build-tests/sparo-output-test/etc/checkout-help.txt @@ -21,3 +21,5 @@ Options: already exists, reset it to [boolean] --profile [array] [default: []] --add-profile [array] [default: []] + --to [array] [default: []] + --from [array] [default: []] diff --git a/build-tests/sparo-real-repo-test/etc/checkout-from.txt b/build-tests/sparo-real-repo-test/etc/checkout-from.txt new file mode 100644 index 0000000..a20036c --- /dev/null +++ b/build-tests/sparo-real-repo-test/etc/checkout-from.txt @@ -0,0 +1,29 @@ +Running "sparo checkout --from build-test-utilities": + +[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__ diff --git a/build-tests/sparo-real-repo-test/etc/checkout-to.txt b/build-tests/sparo-real-repo-test/etc/checkout-to.txt new file mode 100644 index 0000000..17b4503 --- /dev/null +++ b/build-tests/sparo-real-repo-test/etc/checkout-to.txt @@ -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__ diff --git a/build-tests/sparo-real-repo-test/src/start-test.ts b/build-tests/sparo-real-repo-test/src/start-test.ts index 5417124..c4ee27c 100644 --- a/build-tests/sparo-real-repo-test/src/start-test.ts +++ b/build-tests/sparo-real-repo-test/src/start-test.ts @@ -80,6 +80,20 @@ export async function runAsync(runScriptOptions: IRunScriptOptions): Promise Date: Fri, 26 Apr 2024 07:45:44 +0800 Subject: [PATCH 4/5] Update feat-checkout-to-selector_2024-04-25-14-16.json Co-authored-by: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> --- .../sparo/feat-checkout-to-selector_2024-04-25-14-16.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/changes/sparo/feat-checkout-to-selector_2024-04-25-14-16.json b/common/changes/sparo/feat-checkout-to-selector_2024-04-25-14-16.json index 4991209..11efe2b 100644 --- a/common/changes/sparo/feat-checkout-to-selector_2024-04-25-14-16.json +++ b/common/changes/sparo/feat-checkout-to-selector_2024-04-25-14-16.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "sparo", - "comment": "[sparo checkout] support cli --to/--from option for projects seletion", + "comment": "[sparo checkout] support cli --to/--from option for projects selection", "type": "none" } ], From 580e47b57788a9d6816fef8bb9220c68500c5e06 Mon Sep 17 00:00:00 2001 From: Adrian Z <10289356+jzhang026@users.noreply.github.com> Date: Mon, 29 Apr 2024 20:32:16 +0800 Subject: [PATCH 5/5] chore: resolve comments --- apps/sparo-lib/src/cli/commands/checkout.ts | 30 +++++++---- .../src/services/SparoProfileService.ts | 51 ++++++++++++++++--- .../docs/pages/commands/sparo_checkout.md | 15 ++++++ .../sparo-output-test/etc/checkout-help.txt | 13 ++++- .../etc/checkout-from.txt | 8 ++- .../sparo-real-repo-test/src/start-test.ts | 2 +- 6 files changed, 98 insertions(+), 21 deletions(-) diff --git a/apps/sparo-lib/src/cli/commands/checkout.ts b/apps/sparo-lib/src/cli/commands/checkout.ts index 5fcc9bf..47bc3c7 100644 --- a/apps/sparo-lib/src/cli/commands/checkout.ts +++ b/apps/sparo-lib/src/cli/commands/checkout.ts @@ -45,7 +45,7 @@ export class CheckoutCommand implements ICommand { * have been implemented, while other scenarios are yet to be implemented. * 1. sparo checkout [-b|-B] [start-point] [--profile ] * 2. sparo checkout [branch] [--profile ] - * 3. sparo checkout [branch] [--to ] + * 3. sparo checkout [branch] [--to ] [--from ] * * TODO: implement more checkout functionalities */ @@ -71,10 +71,18 @@ export class CheckoutCommand implements ICommand { .default('profile', []) .array('add-profile') .default('add-profile', []) - .array('to') - .default('to', []) - .array('from') - .default('from', []); + .option('to', { + type: 'array', + default: [], + description: + 'Checkout projects up to (and including) project , 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 , 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 ( @@ -147,10 +155,11 @@ export class CheckoutCommand implements ICommand { } // 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: @@ -220,7 +229,8 @@ export class CheckoutCommand implements ICommand { profiles: isNoProfile ? undefined : profiles, addProfiles, fromProjects, - toProjects + toProjects, + isProfileRestoreFromLocal }); } }; diff --git a/apps/sparo-lib/src/services/SparoProfileService.ts b/apps/sparo-lib/src/services/SparoProfileService.ts index 4033fae..bbbae27 100644 --- a/apps/sparo-lib/src/services/SparoProfileService.ts +++ b/apps/sparo-lib/src/services/SparoProfileService.ts @@ -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; addProfiles: Set; }> { 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,18 +213,45 @@ ${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; + private async _syncRushSelectors(selections: ISelection[]): Promise; + private async _syncRushSelectors(selections?: ISelection[]): Promise { + 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 */ @@ -229,13 +259,19 @@ ${availableProfiles.join(',')} profiles, addProfiles, fromProjects, - toProjects + toProjects, + isProfileRestoreFromLocal }: { profiles?: Set; addProfiles?: Set; fromProjects?: Set; toProjects?: Set; + isProfileRestoreFromLocal?: boolean; }): Promise { + // 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) { @@ -305,9 +341,9 @@ ${availableProfiles.join(',')} // handle case of `sparo checkout --to project-A project-B --from project-C project-D const toSelector: Set = toProjects || new Set(); - const fromSelector: Set = toProjects || new Set(); + const fromSelector: Set = fromProjects || new Set(); // If Rush Selector --to is specified, using `git sparse-checkout add` to add folders of the projects specified - const projectsSelections: ISelection[] = []; + const projectsSelections: ISelection[] = [...rushSelectorState]; for (const project of toSelector) { projectsSelections.push({ @@ -323,6 +359,7 @@ ${availableProfiles.join(',')} } if (projectsSelections.length > 0) { + await this._syncRushSelectors(projectsSelections); await this._gitSparseCheckoutService.checkoutAsync({ selections: projectsSelections, checkoutAction: 'add' diff --git a/apps/website/docs/pages/commands/sparo_checkout.md b/apps/website/docs/pages/commands/sparo_checkout.md index 2545c10..0ced130 100644 --- a/apps/website/docs/pages/commands/sparo_checkout.md +++ b/apps/website/docs/pages/commands/sparo_checkout.md @@ -20,4 +20,19 @@ Options: already exists, reset it to [boolean] --profile [array] [default: []] --add-profile [array] [default: []] + --to Checkout projects up to (and including) project , 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 , 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: []] ``` + + + + diff --git a/build-tests/sparo-output-test/etc/checkout-help.txt b/build-tests/sparo-output-test/etc/checkout-help.txt index fe81830..333768b 100644 --- a/build-tests/sparo-output-test/etc/checkout-help.txt +++ b/build-tests/sparo-output-test/etc/checkout-help.txt @@ -19,7 +19,16 @@ Options: -b Create a new branch and start it at [boolean] -B Create a new branch and start it at ; if it already exists, reset it to [boolean] + --to Checkout projects up to (and including) project , 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 , 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: []] - --to [array] [default: []] - --from [array] [default: []] diff --git a/build-tests/sparo-real-repo-test/etc/checkout-from.txt b/build-tests/sparo-real-repo-test/etc/checkout-from.txt index a20036c..08c88db 100644 --- a/build-tests/sparo-real-repo-test/etc/checkout-from.txt +++ b/build-tests/sparo-real-repo-test/etc/checkout-from.txt @@ -1,4 +1,4 @@ -Running "sparo checkout --from build-test-utilities": +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) @@ -27,3 +27,9 @@ Checking out __FOLDER_COUNT__ folders... [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__ diff --git a/build-tests/sparo-real-repo-test/src/start-test.ts b/build-tests/sparo-real-repo-test/src/start-test.ts index c4ee27c..79afcc9 100644 --- a/build-tests/sparo-real-repo-test/src/start-test.ts +++ b/build-tests/sparo-real-repo-test/src/start-test.ts @@ -91,7 +91,7 @@ export async function runAsync(runScriptOptions: IRunScriptOptions): Promise