Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

branchforge

Package Overview
Dependencies
Maintainers
0
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

branchforge - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

232

dist/index.js

@@ -42,13 +42,93 @@ #!/usr/bin/env node

const commander_1 = require("commander");
const yaml = __importStar(require("yaml"));
const simple_git_1 = __importDefault(require("simple-git"));
const yaml = __importStar(require("yaml"));
const git = (0, simple_git_1.default)();
// Git utility class to handle git-related operations
class GitService {
constructor() {
this.git = (0, simple_git_1.default)();
}
getCurrentBranch() {
return __awaiter(this, void 0, void 0, function* () {
return yield this.git.revparse(["--abbrev-ref", "HEAD"]);
});
}
branchExists(branch) {
return __awaiter(this, void 0, void 0, function* () {
try {
const branches = yield this.git.branch();
return branches.all.includes(branch);
}
catch (error) {
if (error instanceof Error) {
console.error(`Error checking branch: ${error.message}`);
}
return false;
}
});
}
getBranchStatus(branch, base) {
return __awaiter(this, void 0, void 0, function* () {
const status = yield this.git.status();
const logDiff = yield this.git.log([`${base}..${branch}`]);
return { status, logDiff };
});
}
deleteBranch(branchName) {
return __awaiter(this, void 0, void 0, function* () {
yield this.git.branch(["-D", branchName]);
console.log(`Deleted branch "${branchName}".`);
});
}
recreateBranch(base, branchName, includes) {
return __awaiter(this, void 0, void 0, function* () {
console.log(`Rebuilding branch "${branchName}" from base "${base}" and includes: ${includes.join(", ")}.`);
yield this.git.checkout(base);
yield this.git.checkoutLocalBranch(branchName);
for (const include of includes) {
yield this.git.merge([include]);
}
console.log(`Branch "${branchName}" has been rebuilt.`);
});
}
isBranchEqualToBaseAndIncludes(branchName, base, includes) {
return __awaiter(this, void 0, void 0, function* () {
const baseAndIncludesBranches = [base, ...includes];
try {
const combinedCommits = yield this.git.raw([
"rev-list",
"--no-merges",
...baseAndIncludesBranches,
]);
const branchCommits = yield this.git.raw([
"rev-list",
"--no-merges",
branchName,
]);
return branchCommits.trim() === combinedCommits.trim();
}
catch (error) {
if (error instanceof Error) {
console.error(`Error comparing branches: ${error.message}`);
}
return false;
}
});
}
checkout(branch) {
return __awaiter(this, void 0, void 0, function* () {
yield this.git.checkout(branch);
console.log(`Checked out branch "${branch}".`);
});
}
}
// Load and parse branch specification from YAML files
const loadBranchSpec = (yamlFiles) => {
for (const filePath of yamlFiles) {
console.debug("Attempting to load", filePath);
try {
const file = fs.readFileSync(filePath, 'utf8');
const file = fs.readFileSync(filePath, "utf8");
return yaml.parse(file);
}
catch (error) {
if (error.code !== 'ENOENT') {
if (error.code !== "ENOENT") {
console.error(`Failed to load or parse ${filePath}:`, error);

@@ -58,27 +138,6 @@ }

}
console.error('No valid branch specification files found.');
return null;
throw new Error("No valid branch specification files found.");
};
// Function to check if a branch has changes or is behind its base
const isBranchBehind = (branchName, base) => __awaiter(void 0, void 0, void 0, function* () {
// Check if the branch is behind the base branch
const countBehind = yield git.raw(['rev-list', '--count', `${branchName}..${base}`]);
return parseInt(countBehind) > 0; // Returns true if the branch is behind
});
const hasLocalChanges = (branchName) => __awaiter(void 0, void 0, void 0, function* () {
const status = yield git.status();
return (status.modified.includes(branchName) ||
status.renamed.some((renamed) => renamed.from === branchName || renamed.to === branchName) ||
status.deleted.includes(branchName));
});
const rebuildBranch = (branchName) => __awaiter(void 0, void 0, void 0, function* () {
const yamlFiles = [
path.join(__dirname, '../branch-spec.yml'),
path.join(__dirname, '../branch-spec.yaml')
];
const spec = loadBranchSpec(yamlFiles);
if (!spec || !spec.branches) {
console.error(`Branch specification file is missing or improperly formatted.`);
return;
}
// Rebuild the branch if it doesn't match the spec or is behind
const rebuildBranch = (branchName, spec, gitService, force) => __awaiter(void 0, void 0, void 0, function* () {
const branchConfig = spec.branches[branchName];

@@ -90,72 +149,73 @@ if (!branchConfig) {

const { base, includes } = branchConfig;
try {
const branches = yield git.branch();
// Checkout the base branch
yield git.checkout(base);
// Check if the base branch is ahead of the current branch
const isBaseAhead = yield isBranchBehind(branchName, base);
if (isBaseAhead) {
console.log(`Base branch ${base} is ahead of ${branchName}. It has been updated.`);
const baseExists = yield gitService.branchExists(base);
const includesExist = yield Promise.all(includes.map(gitService.branchExists.bind(gitService)));
if (!baseExists) {
console.error(`Base branch "${base}" does not exist.`);
return;
}
const missingIncludes = includes.filter((_, i) => !includesExist[i]);
if (missingIncludes.length > 0) {
console.error(`Includes branches do not exist: ${missingIncludes.join(", ")}`);
return;
}
const branchExistsAlready = yield gitService.branchExists(branchName);
if (!branchExistsAlready) {
console.log(`Branch "${branchName}" does not exist, creating it.`);
if (force) {
yield gitService.recreateBranch(base, branchName, includes);
}
else {
console.log(`Base branch ${base} is not ahead of ${branchName}.`);
return;
}
const isEqual = yield gitService.isBranchEqualToBaseAndIncludes(branchName, base, includes);
if (!isEqual) {
console.log(`Branch "${branchName}" differs from "${base}" + [${includes.join(", ")}], rebuilding.`);
if (force) {
yield gitService.deleteBranch(branchName);
yield gitService.recreateBranch(base, branchName, includes);
}
// If the branch does not exist, create it
if (!branches.all.includes(branchName)) {
console.log(`Creating branch: ${branchName} from ${base}`);
yield git.checkout(base);
yield git.checkoutLocalBranch(branchName);
}
else {
// Check for local changes and if the branch is behind the base
const localChanges = yield hasLocalChanges(branchName);
if (localChanges || isBaseAhead) {
console.log(`Branch ${branchName} has changes or is behind the base branch, rebuilding...`);
}
else {
console.log(`Branch ${branchName} has no changes since last build. Skipping.`);
return;
}
console.log(`Deleting existing branch: ${branchName}`);
yield git.branch(['-D', branchName]);
// Create the branch again from base
console.log(`Creating branch: ${branchName} from ${base}`);
yield git.checkout(base);
yield git.checkoutLocalBranch(branchName);
}
// Cherry-pick the specified commits into the new branch
for (const include of includes) {
console.log(`Cherry-picking ${include} into ${branchName}`);
yield git.raw(['cherry-pick', include]);
}
console.log(`Branch ${branchName} created successfully.`);
}
catch (error) {
console.error(`Error: ${error.message}`);
else {
console.log(`Branch "${branchName}" is exactly the same as "${base}" + [${includes.join(", ")}], no need to rebuild.`);
}
});
// Function to handle command-line input
// Handle CLI input
const runCLI = () => __awaiter(void 0, void 0, void 0, function* () {
const gitService = new GitService();
const originalBranch = yield gitService.getCurrentBranch(); // Use the new method
console.log('starting out in branch', originalBranch);
commander_1.program
.version('1.0.0')
.command('update [branch]')
.description('Rebuild branches based on the spec')
.action((branch) => __awaiter(void 0, void 0, void 0, function* () {
.version("1.0.0")
.command("update [branch]")
.description("Rebuild branches based on the spec")
.option("--force", "Force the operation, making changes.")
.action((branch, options) => __awaiter(void 0, void 0, void 0, function* () {
console.log("OPTIONS", options);
const yamlFiles = [
path.join(__dirname, '../branch-spec.yml'),
path.join(__dirname, '../branch-spec.yaml')
path.join(__dirname, "../branch-spec.yml"),
path.join(__dirname, "../branch-spec.yaml"),
];
const spec = loadBranchSpec(yamlFiles);
if (!spec || !spec.branches) {
console.error(`Branch specification file is missing or improperly formatted.`);
return;
try {
const spec = loadBranchSpec(yamlFiles);
if (!spec || !spec.branches) {
console.error("Branch specification file is missing or improperly formatted.");
return;
}
if (branch) {
yield rebuildBranch(branch, spec, gitService, options.force);
}
else {
for (const branchName of Object.keys(spec.branches)) {
yield rebuildBranch(branchName, spec, gitService, options.force);
}
}
}
if (branch) {
yield rebuildBranch(branch);
}
else {
for (const branchName of Object.keys(spec.branches)) {
yield rebuildBranch(branchName);
catch (error) {
if (error instanceof Error) {
console.error(error.message);
}
}
finally {
// Ensure the original branch is checked out again
yield gitService.checkout(originalBranch);
}
}));

@@ -162,0 +222,0 @@ commander_1.program.parse(process.argv);

{
"name": "branchforge",
"version": "0.2.0",
"version": "0.3.0",
"private": false,

@@ -5,0 +5,0 @@ "main": "dist/index.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc