Skip to content

Commit

Permalink
feat: add --auto-detect option
Browse files Browse the repository at this point in the history
  • Loading branch information
zlalvani committed May 11, 2024
1 parent 4d994ba commit 86d1f8b
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 41 deletions.
55 changes: 38 additions & 17 deletions apps/cli/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ const command = program
3000,
)
.option("-n, --no-upgrade", "skip applying the upgrade")
.option("-a, --auto-detect", "auto-detect the package to upgrade")
.option("-s, --simple", "simple mode")
.option("-i, --ipc", "run in ipc mode")
.option("-d, --dir <dir>", "target directory for the upgrade")
.parse();

const { model, language, port, ipc, simple, token, upgrade, dir } =
const { model, language, port, ipc, simple, token, upgrade, dir, autoDetect } =
command.opts();

let [pkg, version] = command.processedArgs;
Expand All @@ -73,23 +74,43 @@ const bumpFinder = makeBumpFinder({
const available = await bumpFinder.list();

if (!pkg) {
if (available.length === 0) {
console.log("All packages are on their latest major version!");
process.exit(0);
}

const choice = await select({
message: "Select a package to upgrade (major version changes only)",
choices: available.map((pkg, index) => {
return {
name: `${pkg.packageName}@${pkg.newVersion}`,
value: index,
};
}),
});
if (autoDetect) {
const detected = await bumpFinder.detect();

if (detected.length > 0) {
if (detected.length > 1) {
console.error("Multiple package changes detected");
detected.forEach((p) =>
console.log(`${p.packageName}@${p.newVersion}`),
);
process.exit(1);
}

pkg = detected[0]!.packageName;
version = detected[0]!.newVersion;
} else {
console.log("No package changes detected");
process.exit(0);
}
} else {
if (available.length === 0) {
console.log("All packages are on their latest major version!");
process.exit(0);
}

pkg = available[choice]!.packageName;
version = available[choice]!.newVersion;
const choice = await select({
message: "Select a package to upgrade (major version changes only)",
choices: available.map((pkg, index) => {
return {
name: `${pkg.packageName}@${pkg.newVersion}`,
value: index,
};
}),
});

pkg = available[choice]!.packageName;
version = available[choice]!.newVersion;
}
}

if (!version) {
Expand Down
5 changes: 5 additions & 0 deletions packages/bumpgen-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ const bumpFinder = ({
const { projectRoot } = args;
return await language.packages.upgrade.list(projectRoot);
},
detect: async () => {
const { language } = services;
const { projectRoot } = args;
return await language.packages.upgrade.detect(projectRoot);
},
};
};

Expand Down
34 changes: 27 additions & 7 deletions packages/bumpgen-core/src/services/git/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
import type { SimpleGit, SimpleGitOptions } from "simple-git";
import type { SimpleGit } from "simple-git";
import { simpleGit } from "simple-git";

export const createGitService = (git: SimpleGit) => {
return git;
import type { SubprocessService } from "../subprocess";
import { injectSubprocessService } from "../subprocess";

export const createGitService = (
git: SimpleGit,
subprocess: SubprocessService,
) => {
return {
raw: git,
getMainBranch: async (cwd: string) => {
await git.cwd(cwd);
return (
await subprocess.exec(
"git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'",
{
cwd,
},
)
).trim();
},
};
};

export const injectGitService = (basePath: string) => {
const options: Partial<SimpleGitOptions> = {
baseDir: basePath,
export const injectGitService = () => {
const options = {
binary: "git",
maxConcurrentProcesses: 6,
};
const git = simpleGit(options);

return createGitService(git);
const subprocess = injectSubprocessService();

return createGitService(git, subprocess);
};

export type GitService = ReturnType<typeof createGitService>;
1 change: 1 addition & 0 deletions packages/bumpgen-core/src/services/language/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type BumpgenLanguageService<TAst = any> = {
packages: {
upgrade: {
list: (projectRoot: string) => Promise<PackageUpgrade[]>;
detect: (projectRoot: string) => Promise<PackageUpgrade[]>;
apply: (
projectRoot: string,
upgrade: PackageUpgrade,
Expand Down
54 changes: 49 additions & 5 deletions packages/bumpgen-core/src/services/language/typescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import type {
DependencyGraphNode,
} from "../../../models/graph/dependency";
import type { FilesystemService } from "../../filesystem";
import type { GraphService } from "../../graph";
import type { GitService } from "../../git";
import type { SubprocessService } from "../../subprocess";
import type { BumpgenLanguageService } from "../types";
import { injectFilesystemService } from "../../filesystem";
import { injectGraphService } from "../../graph";
import { injectGitService } from "../../git";
import { injectSubprocessService } from "../../subprocess";
import { processSourceFile } from "./process";
import { getImportSignature, getSignature, isImportNode } from "./signatures";
Expand Down Expand Up @@ -56,7 +56,7 @@ const isRegexParsed = (value: unknown): value is ErrorRegexParsed => {
export const makeTypescriptService = (
filesystem: FilesystemService,
subprocess: SubprocessService,
_graphService: GraphService,
git: GitService,
) => {
const findPackageManager = async (projectRoot: string) => {
let currentDir = projectRoot;
Expand Down Expand Up @@ -211,6 +211,50 @@ export const makeTypescriptService = (
};
});
},
detect: async (projectRoot) => {
const packageJson = await PackageJson.load(projectRoot);

const existingDependencies = new Set([
...(packageJson.content.dependencies
? Object.keys(packageJson.content.dependencies)
: []),
...(packageJson.content.devDependencies
? Object.keys(packageJson.content.devDependencies)
: []),
]);

await git.raw.cwd(projectRoot);
const branch = (await git.raw.branch()).current;

let diff = await git.raw.diff([branch, "package.json"]);

// if there are no changes in the package.json, we check against the main branch
if (!diff) {
const mainBranch = await git.getMainBranch(projectRoot);
diff = await git.raw.diff([mainBranch, "package.json"]);
}

const upgradedPackages = [];

const lines = diff.split("\n");
for (const line of lines) {
const match = line.match(/^\+[\s]*"([^"]+)": "([^"]+)",?$/);
if (match) {
const packageName = match[1];
const newVersion = match[2];

if (
packageName &&
newVersion &&
existingDependencies.has(packageName)
) {
upgradedPackages.push({ packageName, newVersion });
}
}
}

return upgradedPackages;
},
apply: async (projectRoot, upgrade) => {
const { packageManager } = await findPackageManager(projectRoot);
const packageJson = await PackageJson.load(projectRoot);
Expand Down Expand Up @@ -402,12 +446,12 @@ export const makeTypescriptService = (
export const injectTypescriptService = () => {
const filesystemService = injectFilesystemService();
const subprocessService = injectSubprocessService();
const graphService = injectGraphService();
const gitService = injectGitService();

return makeTypescriptService(
filesystemService,
subprocessService,
graphService,
gitService,
);
};

Expand Down
31 changes: 19 additions & 12 deletions packages/bumpgen-core/src/services/subprocess/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,31 @@ export const createSubprocessService = (
return {
exec: async (
command: string,
options: {
options?: {
rejectOnStderr?: boolean;
cwd?: string;
},
) => {
return await new Promise<string>((resolve, reject) => {
childProcess.exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error);
reject(new Error(`Failed to execute command '${command}'`));
}
if (stderr) {
console.error(stderr);
if (options.rejectOnStderr) {
childProcess.exec(
command,
{
cwd: options?.cwd,
},
(error, stdout, stderr) => {
if (error) {
console.error(error);
reject(new Error(`Failed to execute command '${command}'`));
}
}
resolve(stdout);
});
if (stderr) {
console.error(stderr);
if (options?.rejectOnStderr) {
reject(new Error(`Failed to execute command '${command}'`));
}
}
resolve(stdout);
},
);
});
},
spawn: async (
Expand Down

0 comments on commit 86d1f8b

Please sign in to comment.