@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
392 lines (352 loc) • 11.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.initializeGitRepository = initializeGitRepository;
exports.addSubmodule = addSubmodule;
exports.removeSubmodule = removeSubmodule;
exports.updateSubmodules = updateSubmodules;
exports.getSubmoduleStatus = getSubmoduleStatus;
exports.createSubmoduleDocumentation = createSubmoduleDocumentation;
exports.generateSubmoduleScript = generateSubmoduleScript;
exports.isGitRepository = isGitRepository;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const child_process_1 = require("child_process");
const util_1 = require("util");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
async function initializeGitRepository(projectPath) {
try {
await execAsync('git init', { cwd: projectPath });
// Create initial commit
await execAsync('git add .', { cwd: projectPath });
await execAsync('git commit -m "Initial commit"', { cwd: projectPath });
}
catch (error) {
throw new Error(`Failed to initialize Git repository: ${error}`);
}
}
async function addSubmodule(submodulePath, repositoryUrl, branch = 'main', targetPath) {
const actualPath = targetPath || submodulePath;
try {
let command = `git submodule add`;
if (branch !== 'main') {
command += ` -b ${branch}`;
}
command += ` ${repositoryUrl} ${actualPath}`;
await execAsync(command);
// Initialize and update the submodule
await execAsync(`git submodule update --init --recursive ${actualPath}`);
console.log(`Submodule added: ${repositoryUrl} -> ${actualPath}`);
}
catch (error) {
throw new Error(`Failed to add submodule: ${error}`);
}
}
async function removeSubmodule(submodulePath) {
try {
// Remove from .gitmodules
await execAsync(`git submodule deinit -f ${submodulePath}`);
// Remove from .git/modules
await execAsync(`rm -rf .git/modules/${submodulePath}`);
// Remove from working tree
await execAsync(`git rm -f ${submodulePath}`);
console.log(`Submodule removed: ${submodulePath}`);
}
catch (error) {
throw new Error(`Failed to remove submodule: ${error}`);
}
}
async function updateSubmodules(specificPath) {
try {
const command = specificPath
? `git submodule update --remote --recursive ${specificPath}`
: 'git submodule update --remote --recursive';
await execAsync(command);
console.log('Submodules updated successfully');
}
catch (error) {
throw new Error(`Failed to update submodules: ${error}`);
}
}
async function getSubmoduleStatus() {
try {
const { stdout } = await execAsync('git submodule status --recursive');
const submodules = [];
const lines = stdout.trim().split('\n').filter(line => line.trim());
for (const line of lines) {
const match = line.match(/^([ +-U])([a-f0-9]+) (.+?)( \(.+\))?$/);
if (match) {
const [, statusChar, commit, submodulePath] = match;
let status = 'clean';
switch (statusChar) {
case '-':
status = 'untracked';
break;
case '+':
status = 'ahead';
break;
case 'U':
status = 'modified';
break;
default: status = 'clean';
}
// Get submodule URL and branch
const { url, branch } = await getSubmoduleInfo(submodulePath);
submodules.push({
name: path.basename(submodulePath),
path: submodulePath,
url,
branch,
commit: commit.substring(0, 8),
status
});
}
}
return submodules;
}
catch (error) {
// If no submodules exist, return empty array
if (error.toString().includes('No submodule mapping found')) {
return [];
}
throw new Error(`Failed to get submodule status: ${error}`);
}
}
async function getSubmoduleInfo(submodulePath) {
try {
const { stdout: url } = await execAsync(`git config submodule.${submodulePath}.url`);
let branch = 'main';
try {
const { stdout: branchOutput } = await execAsync(`git config submodule.${submodulePath}.branch`);
branch = branchOutput.trim() || 'main';
}
catch {
// Branch not configured, use default
}
return {
url: url.trim(),
branch
};
}
catch (error) {
return { url: 'unknown', branch: 'main' };
}
}
async function createSubmoduleDocumentation(projectPath, submodules) {
const docsPath = path.join(projectPath, 'docs');
await fs.ensureDir(docsPath);
const submoduleDocPath = path.join(docsPath, 'SUBMODULES.md');
const content = `# Submodules
This document describes the Git submodules used in this project.
## Overview
This project uses Git submodules to manage external dependencies and shared components. Each submodule represents a separate repository that can be developed independently.
## Submodules
${submodules.length === 0 ? 'No submodules configured.' : submodules.map((sub) => `
### ${sub.name}
- **Path**: \`${sub.path}\`
- **Repository**: ${sub.url}
- **Branch**: ${sub.branch}
- **Current Commit**: ${sub.commit}
- **Status**: ${sub.status}
`).join('\n')}
## Working with Submodules
### Initial Setup
When cloning this repository, initialize and update all submodules:
\`\`\`bash
git clone --recursive <repository-url>
# OR
git clone <repository-url>
git submodule update --init --recursive
\`\`\`
### Updating Submodules
Update all submodules to their latest commits:
\`\`\`bash
re-shell submodule update
# OR
git submodule update --remote --recursive
\`\`\`
Update a specific submodule:
\`\`\`bash
re-shell submodule update <path>
# OR
git submodule update --remote <path>
\`\`\`
### Adding New Submodules
\`\`\`bash
re-shell submodule add <repository-url> <path> [--branch <branch>]
\`\`\`
### Removing Submodules
\`\`\`bash
re-shell submodule remove <path>
\`\`\`
### Checking Status
\`\`\`bash
re-shell submodule status
\`\`\`
## Development Workflow
1. **Making Changes**: Work in the submodule directory as you would in any Git repository
2. **Committing**: Commit changes within the submodule first
3. **Updating Parent**: Commit the submodule reference update in the parent repository
4. **Pushing**: Push both the submodule and parent repository changes
## Best Practices
1. Always commit submodule changes before updating the parent repository
2. Use specific branches for submodules rather than tracking HEAD
3. Document any submodule dependencies and their purposes
4. Regularly update submodules to stay current with upstream changes
5. Use \`git submodule foreach\` for bulk operations across all submodules
## Troubleshooting
### Submodule Not Initialized
\`\`\`bash
git submodule update --init <path>
\`\`\`
### Submodule Conflicts
\`\`\`bash
git submodule deinit <path>
git submodule update --init <path>
\`\`\`
### Reset Submodule to Tracked Commit
\`\`\`bash
git submodule update --force <path>
\`\`\`
`;
await fs.writeFile(submoduleDocPath, content);
}
async function generateSubmoduleScript(projectPath) {
const scriptsPath = path.join(projectPath, 'scripts');
await fs.ensureDir(scriptsPath);
const scriptPath = path.join(scriptsPath, 'submodule-helper.sh');
const script = `#!/bin/bash
# Submodule Helper Script
# This script provides utilities for managing Git submodules
set -e
function show_help() {
echo "Usage: $0 [COMMAND] [OPTIONS]"
echo ""
echo "Commands:"
echo " init Initialize all submodules"
echo " update [PATH] Update submodules (all or specific path)"
echo " status Show submodule status"
echo " foreach [COMMAND] Run command in each submodule"
echo " clean Clean all submodules"
echo " reset Reset all submodules to tracked commits"
echo ""
echo "Examples:"
echo " $0 init"
echo " $0 update"
echo " $0 update apps/my-app"
echo " $0 foreach 'git pull origin main'"
echo " $0 status"
}
function init_submodules() {
echo "Initializing submodules..."
git submodule update --init --recursive
echo "Submodules initialized successfully"
}
function update_submodules() {
local path=$1
if [ -n "$path" ]; then
echo "Updating submodule: $path"
git submodule update --remote --recursive "$path"
else
echo "Updating all submodules..."
git submodule update --remote --recursive
fi
echo "Submodules updated successfully"
}
function show_status() {
echo "Submodule status:"
git submodule status --recursive
}
function foreach_command() {
local command=$1
if [ -z "$command" ]; then
echo "Error: No command specified for foreach"
exit 1
fi
echo "Running '$command' in each submodule..."
git submodule foreach --recursive "$command"
}
function clean_submodules() {
echo "Cleaning submodules..."
git submodule foreach --recursive 'git clean -fd'
echo "Submodules cleaned"
}
function reset_submodules() {
echo "Resetting submodules to tracked commits..."
git submodule update --force --recursive
echo "Submodules reset"
}
case "$1" in
init)
init_submodules
;;
update)
update_submodules "$2"
;;
status)
show_status
;;
foreach)
foreach_command "$2"
;;
clean)
clean_submodules
;;
reset)
reset_submodules
;;
help|--help|-h)
show_help
;;
*)
echo "Error: Unknown command '$1'"
echo ""
show_help
exit 1
;;
esac
`;
await fs.writeFile(scriptPath, script);
await fs.chmod(scriptPath, '755');
}
async function isGitRepository(dirPath = process.cwd()) {
try {
await execAsync('git rev-parse --git-dir', { cwd: dirPath });
return true;
}
catch {
return false;
}
}