Skip to content

Commit

Permalink
Merge pull request #83 from tiktok/feat-sparo-completion
Browse files Browse the repository at this point in the history
[sparo] shell completion feature
  • Loading branch information
jzhang026 authored Aug 15, 2024
2 parents cb2f0ea + 427acc5 commit 30a6efe
Show file tree
Hide file tree
Showing 89 changed files with 1,099 additions and 136 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ jspm_packages/
.idea/
*.iml

.nvmrc

# Rush temporary files
common/deploy/
common/temp/
Expand Down
33 changes: 18 additions & 15 deletions apps/sparo-lib/src/cli/SparoCommandLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,25 @@ export class SparoCommandLine {
private constructor() {}

public static async launchAsync(launchOptions: ILaunchOptions): Promise<void> {
if (launchOptions.collectTelemetryAsync) {
const telemetryService: TelemetryService = await getFromContainerAsync(TelemetryService);
telemetryService.setCollectTelemetryFunction(launchOptions.collectTelemetryAsync);
}
const isCompletion: boolean = ['completion', '--get-yargs-completions'].includes(process.argv[2]);
if (!isCompletion) {
if (launchOptions.collectTelemetryAsync) {
const telemetryService: TelemetryService = await getFromContainerAsync(TelemetryService);
telemetryService.setCollectTelemetryFunction(launchOptions.collectTelemetryAsync);
}

if (GitVersionCompatibility.reportGitRequiredVersion()) {
process.exit(1);
}
SparoStartupBanner.logBanner({
callerPackageJson: launchOptions.callerPackageJson
});
if (GitVersionCompatibility.reportGitRequiredVersion()) {
process.exit(1);
}
SparoStartupBanner.logBanner({
callerPackageJson: launchOptions.callerPackageJson
});

if (launchOptions.additionalSkeletonFolders) {
const gitSparseCheckoutService: GitSparseCheckoutService =
await getFromContainerAsync(GitSparseCheckoutService);
gitSparseCheckoutService.setAdditionalSkeletonFolders(launchOptions.additionalSkeletonFolders);
if (launchOptions.additionalSkeletonFolders) {
const gitSparseCheckoutService: GitSparseCheckoutService =
await getFromContainerAsync(GitSparseCheckoutService);
gitSparseCheckoutService.setAdditionalSkeletonFolders(launchOptions.additionalSkeletonFolders);
}
}

const sparo: SparoCommandLine = new SparoCommandLine();
Expand Down Expand Up @@ -79,6 +82,6 @@ export class SparoCommandLine {
}

private _supportedCommand(commandName: string): boolean {
return this._commandsMap.has(commandName);
return this._commandsMap.has(commandName) || commandName === 'completion';
}
}
15 changes: 12 additions & 3 deletions apps/sparo-lib/src/cli/commands/auto-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ export class AutoConfigCommand implements ICommand<IAutoConfigCommandOptions> {
type: 'boolean',
hidden: true,
default: false
})
.completion('completion', false, (current, argv, done) => {
const longParameters: string[] = [argv.overwrite ? '' : '--overwrite'].filter(Boolean);
if (current.startsWith('--')) {
done(
longParameters.filter((parameter) => {
return parameter.startsWith(current);
})
);
} else {
done([]);
}
});
}
public handler = async (
Expand All @@ -52,7 +64,4 @@ export class AutoConfigCommand implements ICommand<IAutoConfigCommandOptions> {
throw e;
}
};
public getHelp(): string {
return '';
}
}
1 change: 0 additions & 1 deletion apps/sparo-lib/src/cli/commands/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ export interface ICommand<O extends {}> {

builder: (yargs: Argv<O>) => void;
handler: (args: ArgumentsCamelCase<O>, terminalService: TerminalService) => Promise<void>;
getHelp: () => string;
}
115 changes: 109 additions & 6 deletions apps/sparo-lib/src/cli/commands/checkout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as child_process from 'child_process';
import { inject } from 'inversify';
import { JsonFile } from '@rushstack/node-core-library';
import { Command } from '../../decorator';
import { GitService } from '../../services/GitService';
import { GitRemoteFetchConfigService } from '../../services/GitRemoteFetchConfigService';
Expand Down Expand Up @@ -32,7 +33,7 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
@inject(GitRemoteFetchConfigService) private _gitRemoteFetchConfigService!: GitRemoteFetchConfigService;
@inject(SparoProfileService) private _sparoProfileService!: SparoProfileService;

public builder(yargs: Argv<{}>): void {
public builder = (yargs: Argv<{}>): void => {
/**
* git checkout [-q] [-f] [-m] [<branch>]
* git checkout [-q] [-f] [-m] --detach [<branch>]
Expand Down Expand Up @@ -98,8 +99,114 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
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'
})
.completion('completion', false, (current, argv, done) => {
const isNoProfile: boolean = argv.profile.some(
(profile: string | boolean) => typeof profile === 'boolean' && profile === false
);
const shortParameters: string[] = [argv.b ? '' : '-b', argv.B ? '' : '-B'].filter(Boolean);
const longParameters: string[] = [
isNoProfile ? '' : '--no-profile',
isNoProfile ? '' : '--profile',
isNoProfile ? '' : '--add-profile',
'--to',
'--from'
].filter(Boolean);

if (current === '-') {
done(shortParameters);
} else if (current === '--') {
done(longParameters);
} else if (current === '--profile' || current === '--add-profile') {
const profileNameSet: Set<string> = new Set(this._sparoProfileService.loadProfileNames());
for (const profile of argv.profile) {
if (typeof profile === 'string') {
profileNameSet.delete(profile);
}
}
for (const profile of argv.addProfile) {
if (typeof profile === 'string') {
profileNameSet.delete(profile);
}
}
done(Array.from(profileNameSet));
} else if (current === '--to' || current === '--from') {
let rushJson: { projects?: { packageName: string }[] } = {};
const root: string = this._gitService.getRepoInfo().root;
try {
rushJson = JsonFile.load(`${root}/rush.json`);
} catch (e) {
// no-catch
}
if (Array.isArray(rushJson.projects)) {
const packageNameSet: Set<string> = new Set<string>(
rushJson.projects.map((project) => project.packageName)
);
if (current === '--to') {
for (const packageName of argv.to) {
packageNameSet.delete(packageName);
}
}
if (current === '--from') {
for (const packageName of argv.from) {
packageNameSet.delete(packageName);
}
}
const packageNames: string[] = Array.from(packageNameSet).sort();
if (process.cwd() !== root) {
packageNames.unshift('.');
}
done(packageNames);
}
done([]);
} else if (current.startsWith('--')) {
done(longParameters.filter((parameter) => parameter.startsWith(current)));
} else {
const previous: string = process.argv.slice(-2)[0];
if (previous === '--profile' || previous === '--add-profile') {
const profileNameSet: Set<string> = new Set(this._sparoProfileService.loadProfileNames());
for (const profile of argv.profile) {
if (typeof profile === 'string') {
profileNameSet.delete(profile);
}
}
for (const profile of argv.addProfile) {
if (typeof profile === 'string') {
profileNameSet.delete(profile);
}
}
done(Array.from(profileNameSet).filter((profileName) => profileName.startsWith(current)));
} else if (previous === '--to' || previous === '--from') {
let rushJson: { projects?: { packageName: string }[] } = {};
const root: string = this._gitService.getRepoInfo().root;
try {
rushJson = JsonFile.load(`${root}/rush.json`);
} catch (e) {
// no-catch
}
if (Array.isArray(rushJson.projects)) {
const packageNameSet: Set<string> = new Set<string>(
rushJson.projects.map((project) => project.packageName)
);
if (previous === '--to') {
for (const packageName of argv.to) {
packageNameSet.delete(packageName);
}
}
if (previous === '--from') {
for (const packageName of argv.from) {
packageNameSet.delete(packageName);
}
}
const packageNames: string[] = Array.from(packageNameSet).sort();
done(packageNames.filter((packageName) => packageName.startsWith(current)));
}
done([]);
}
done([]);
}
});
}
};

public handler = async (
args: ArgumentsCamelCase<ICheckoutCommandOptions>,
Expand Down Expand Up @@ -258,10 +365,6 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
}
};

public getHelp(): string {
return '';
}

private _ensureBranchInLocal(branch: string): boolean {
// fetch from remote
const remote: string = this._gitService.getBranchRemote(branch);
Expand Down
3 changes: 0 additions & 3 deletions apps/sparo-lib/src/cli/commands/ci-checkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,4 @@ export class CICheckoutCommand implements ICommand<ICICheckoutCommandOptions> {
from
});
};
public getHelp(): string {
return 'sparse help';
}
}
4 changes: 0 additions & 4 deletions apps/sparo-lib/src/cli/commands/ci-clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,4 @@ export class CICloneCommand implements ICommand<ICloneCommandOptions> {

terminal.writeLine(`Remember to run "cd ${directory}"`);
};

public getHelp(): string {
return `clone help`;
}
}
17 changes: 12 additions & 5 deletions apps/sparo-lib/src/cli/commands/clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface ICloneCommandOptions {
@Command()
export class CloneCommand implements ICommand<ICloneCommandOptions> {
public cmd: string = 'clone <repository> [directory]';
public description: string = '';
public description: string = 'Clone a repository into a new directory';

@inject(GitService) private _gitService!: GitService;
@inject(GitCloneService) private _gitCloneService!: GitCloneService;
Expand Down Expand Up @@ -61,6 +61,17 @@ export class CloneCommand implements ICommand<ICloneCommandOptions> {
return 'You must specify a repository to clone.';
}
return true;
})
.completion('completion', false, (current, argv, done) => {
const longParameters: string[] = ['--profile', '--branch', '--full', '--skip-git-config'];

if (current === '--') {
done(longParameters);
} else if (current.startsWith('--')) {
done(longParameters.filter((parameter) => parameter.startsWith(current)));
} else {
done([]);
}
});
}

Expand Down Expand Up @@ -154,8 +165,4 @@ export class CloneCommand implements ICommand<ICloneCommandOptions> {
terminal.writeLine(' ' + Colorize.cyan('sparo init-profile --profile <profile_name>'));
}
};

public getHelp(): string {
return `clone help`;
}
}
33 changes: 26 additions & 7 deletions apps/sparo-lib/src/cli/commands/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class FetchCommand implements ICommand<IFetchCommandOptions> {
@inject(GitService) private _gitService!: GitService;
@inject(GitRemoteFetchConfigService) private _gitRemoteFetchConfigService!: GitRemoteFetchConfigService;

public builder(yargs: Argv<{}>): void {
public builder = (yargs: Argv<{}>): void => {
/**
* sparo fetch <remote> <branch> [--all]
*/
Expand All @@ -31,8 +31,31 @@ export class FetchCommand implements ICommand<IFetchCommandOptions> {
.positional('branch', { type: 'string' })
.string('remote')
.string('branch')
.boolean('all');
}
.boolean('all')
.completion('completion', false, (current, argv, done) => {
const longParameters: string[] = [argv.all ? '' : '--all', argv.tags ? '' : '--tags'].filter(Boolean);
if (current === 'fetch') {
done(['origin']);
} else if (current === 'origin') {
const branchNames: string[] = this._gitRemoteFetchConfigService.getBranchNamesFromRemote('origin');
branchNames.unshift('HEAD');
done(branchNames);
} else if (current === '--') {
done(longParameters);
} else if (current.startsWith('--')) {
done(longParameters.filter((parameter) => parameter.startsWith(current)));
} else {
const previous: string = process.argv.slice(-2)[0];
if (previous === 'origin') {
const branchNames: string[] =
this._gitRemoteFetchConfigService.getBranchNamesFromRemote('origin');
branchNames.unshift('HEAD');
done(branchNames.filter((name) => name.startsWith(current)));
}
done([]);
}
});
};

public handler = async (
args: ArgumentsCamelCase<IFetchCommandOptions>,
Expand Down Expand Up @@ -75,8 +98,4 @@ export class FetchCommand implements ICommand<IFetchCommandOptions> {

restoreSingleBranchCallback?.();
};

public getHelp(): string {
return `fetch help`;
}
}
4 changes: 0 additions & 4 deletions apps/sparo-lib/src/cli/commands/git-checkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,4 @@ export class GitCheckoutCommand implements ICommand<{}> {
terminal.writeDebugLine(`proxy args in git-checkout command: ${JSON.stringify(rawArgs)}`);
gitService.executeGitCommand({ args: rawArgs });
};

public getHelp(): string {
return `git-checkout help`;
}
}
4 changes: 0 additions & 4 deletions apps/sparo-lib/src/cli/commands/git-clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,4 @@ export class GitCloneCommand implements ICommand<{}> {
terminal.writeDebugLine(`proxy args in git-clone command: ${JSON.stringify(rawArgs)}`);
gitService.executeGitCommand({ args: rawArgs });
};

public getHelp(): string {
return `git-clone help`;
}
}
4 changes: 0 additions & 4 deletions apps/sparo-lib/src/cli/commands/git-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,4 @@ export class GitFetchCommand implements ICommand<{}> {
terminal.writeDebugLine(`proxy args in git-fetch command: ${JSON.stringify(rawArgs)}`);
gitService.executeGitCommand({ args: rawArgs });
};

public getHelp(): string {
return `git-fetch help`;
}
}
3 changes: 0 additions & 3 deletions apps/sparo-lib/src/cli/commands/git-pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,4 @@ export class GitPullCommand implements ICommand<{}> {
terminal.writeDebugLine(`proxy args in git-pull command: ${JSON.stringify(rawArgs)}`);
gitService.executeGitCommand({ args: rawArgs });
};
public getHelp(): string {
return `git-pull help`;
}
}
Loading

0 comments on commit 30a6efe

Please sign in to comment.