You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

249 lines
8.1 KiB

#!/usr/bin/env node
/**
* GitRepublic CLI Setup Script
*
* Automatically configures git credential helper and commit signing hook
*
* Usage:
* node scripts/setup.js [options]
*
* Options:
* --credential-only Only set up credential helper
* --hook-only Only set up commit hook
* --domain <domain> Configure credential helper for specific domain (default: all)
* --global-hook Install hook globally for all repositories (default: current repo)
*/
import { fileURLToPath } from 'url';
import { dirname, join, resolve } from 'path';
import { existsSync, mkdirSync, unlinkSync, symlinkSync } from 'fs';
import { spawnSync } from 'child_process';
// Get the directory where this script is located
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const scriptsDir = __dirname;
const credentialScript = join(scriptsDir, 'git-credential-nostr.js');
const commitHookScript = join(scriptsDir, 'git-commit-msg-hook.js');
// Show help
function showHelp() {
console.log(`
GitRepublic CLI Setup
Automatically configures git credential helper and commit signing hook.
Usage:
gitrep-setup [options] (or gitrepublic-setup)
Options:
--credential-only Only set up credential helper
--hook-only Only set up commit hook
--domain <domain> Configure credential helper for specific domain
--global-hook Install hook globally for all repositories
--help, -h Show this help message
Examples:
gitrep-setup # Setup both credential helper and hook
gitrep-setup --domain your-domain.com # Configure for specific domain
gitrep-setup --global-hook # Install hook globally
gitrep-setup --credential-only # Only setup credential helper
The setup script will:
- Automatically find the scripts (works with npm install or git clone)
- Configure git credential helper
- Install commit signing hook (current repo or globally)
- Check if NOSTRGIT_SECRET_KEY is set
For multiple servers, run setup multiple times:
gitrep-setup --domain server1.com --credential-only
gitrep-setup --domain server2.com --credential-only
Documentation: https://github.com/silberengel/gitrepublic-cli
GitCitadel: Visit us on GitHub: https://github.com/ShadowySupercode or on our homepage: https://gitcitadel.com
GitRepublic CLI - Copyright (c) 2026 GitCitadel LLC
Licensed under MIT License
`);
}
// Parse command line arguments
const args = process.argv.slice(2);
const showHelpFlag = args.includes('--help') || args.includes('-h');
const credentialOnly = args.includes('--credential-only');
const hookOnly = args.includes('--hook-only');
const globalHook = args.includes('--global-hook');
const domainIndex = args.indexOf('--domain');
const domain = domainIndex >= 0 && args[domainIndex + 1] ? args[domainIndex + 1] : null;
if (showHelpFlag) {
showHelp();
process.exit(0);
}
// Check if scripts exist
if (!existsSync(credentialScript)) {
console.error('Error: git-credential-nostr.js not found at', credentialScript);
process.exit(1);
}
if (!existsSync(commitHookScript)) {
console.error('Error: git-commit-msg-hook.js not found at', commitHookScript);
process.exit(1);
}
// Check if NOSTRGIT_SECRET_KEY is set
const secretKey = process.env.NOSTRGIT_SECRET_KEY;
if (!secretKey) {
console.warn('⚠ Warning: NOSTRGIT_SECRET_KEY environment variable is not set.');
console.warn(' Set it with: export NOSTRGIT_SECRET_KEY="nsec1..."');
console.warn(' Or add to ~/.bashrc or ~/.zshrc for persistence\n');
}
// Setup credential helper
function setupCredentialHelper() {
console.log('🔐 Setting up git credential helper...');
try {
let configCommand;
// Security: Use spawnSync with argument arrays to prevent command injection
if (domain) {
// Configure for specific domain
// Validate domain to prevent injection
const protocol = domain.startsWith('https://') ? 'https' : domain.startsWith('http://') ? 'http' : 'https';
const host = domain.replace(/^https?:\/\//, '').split('/')[0];
// Validate host format (basic check)
if (!/^[a-zA-Z0-9.-]+$/.test(host)) {
throw new Error('Invalid domain format');
}
const configKey = `credential.${protocol}://${host}.helper`;
const configValue = `!node ${credentialScript}`;
spawnSync('git', ['config', '--global', configKey, configValue], { stdio: 'inherit' });
console.log(` Configuring for domain: ${host}`);
} else {
// Configure globally for all domains
spawnSync('git', ['config', '--global', 'credential.helper', `!node ${credentialScript}`], { stdio: 'inherit' });
console.log(' Configuring globally for all domains');
}
console.log('✅ Credential helper configured successfully!\n');
} catch (error) {
console.error('❌ Failed to configure credential helper:', error.message);
process.exit(1);
}
}
// Setup commit hook
function setupCommitHook() {
console.log('✍ Setting up commit signing hook...');
try {
if (globalHook) {
// Install globally
const hooksDir = resolve(process.env.HOME, '.git-hooks');
// Create hooks directory if it doesn't exist
// Security: Use fs.mkdirSync instead of execSync
if (!existsSync(hooksDir)) {
mkdirSync(hooksDir, { recursive: true });
}
// Create symlink
const hookPath = join(hooksDir, 'commit-msg');
if (existsSync(hookPath)) {
console.log(' Removing existing hook...');
unlinkSync(hookPath);
}
// Security: Use fs.symlinkSync instead of execSync
symlinkSync(commitHookScript, hookPath);
// Configure git to use global hooks
// Note: Using ~/.git-hooks is safe as it's a literal string, not user input
spawnSync('git', ['config', '--global', 'core.hooksPath', '~/.git-hooks'], { stdio: 'inherit' });
console.log('✅ Commit hook installed globally for all repositories!\n');
} else {
// Install for current repository
const gitDir = findGitDir();
if (!gitDir) {
console.error('❌ Error: Not in a git repository. Run this from a git repo or use --global-hook');
process.exit(1);
}
const hookPath = join(gitDir, 'hooks', 'commit-msg');
// Create hooks directory if it doesn't exist
// Security: Use fs.mkdirSync instead of execSync
const hooksDir = join(gitDir, 'hooks');
if (!existsSync(hooksDir)) {
mkdirSync(hooksDir, { recursive: true });
}
// Create symlink
// Security: Use fs operations instead of execSync
if (existsSync(hookPath)) {
console.log(' Removing existing hook...');
unlinkSync(hookPath);
}
symlinkSync(commitHookScript, hookPath);
console.log('✅ Commit hook installed for current repository!\n');
}
} catch (error) {
console.error('❌ Failed to setup commit hook:', error.message);
process.exit(1);
}
}
// Find .git directory
function findGitDir() {
let currentDir = process.cwd();
const maxDepth = 10;
let depth = 0;
while (depth < maxDepth) {
const gitDir = join(currentDir, '.git');
if (existsSync(gitDir)) {
return gitDir;
}
const parentDir = resolve(currentDir, '..');
if (parentDir === currentDir) {
break;
}
currentDir = parentDir;
depth++;
}
return null;
}
// Main execution
console.log('🚀 GitRepublic CLI Setup\n');
console.log('Scripts location:', scriptsDir);
console.log('Credential helper:', credentialScript);
console.log('Commit hook:', commitHookScript);
console.log('');
if (!credentialOnly && !hookOnly) {
// Setup both
setupCredentialHelper();
setupCommitHook();
} else if (credentialOnly) {
setupCredentialHelper();
} else if (hookOnly) {
setupCommitHook();
}
console.log('✨ Setup complete!');
console.log('');
console.log('Next steps:');
if (!secretKey) {
console.log('1. Set NOSTRGIT_SECRET_KEY: export NOSTRGIT_SECRET_KEY="nsec1..."');
}
console.log('2. Test credential helper: gitrep clone <gitrepublic-repo-url> gitrepublic-web');
console.log('3. Test commit signing: gitrep commit -m "Test commit"');