From 59d52df30e8fc250a2812be408860f7b341d1ee9 Mon Sep 17 00:00:00 2001 From: Harvey Zuccon Date: Tue, 5 Aug 2025 22:55:04 +1000 Subject: [PATCH] Add Gitea alias and setup function --- README.md | 45 ++++++++++---- package.json | 3 +- src/cli.ts | 13 ++++ src/index.ts | 20 +++++- src/shell-setup.ts | 150 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 217 insertions(+), 14 deletions(-) create mode 100644 src/shell-setup.ts diff --git a/README.md b/README.md index 2e40f01..c260fc2 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,16 @@ A command line tool to create repositories on Gitea servers with automatic git s 2. Install dependencies: `npm install` 3. Build the project: `npm run build` 4. Link globally: `npm link` -5. Use `gitea-creator` command anywhere +5. Use `gitea-creator` or `gitea` command anywhere ### Option 3: Global Installation (from npm) ```bash # If published to npm npm install -g gitea-creator -# Use anywhere +# Use anywhere with either command gitea-creator my-repo --public +gitea my-repo --public # Short alias ``` ### Option 4: Direct Usage @@ -58,7 +59,18 @@ cp env.example .env ### For Global Installation When installed globally, the tool supports multiple configuration methods: -#### Option 1: System Environment Variables (Recommended) +#### Option 1: Automated Setup (Easiest!) +```bash +# Interactive setup - adds environment variables to your shell profile +gitea --setup +# or +gitea-creator --setup + +# Follow the prompts, then reload your shell: +source ~/.zshrc # or ~/.bashrc +``` + +#### Option 2: Manual Environment Variables ```bash # Add to your shell profile (~/.bashrc, ~/.zshrc, etc.) export GITEA_TOKEN=your_gitea_access_token @@ -68,7 +80,7 @@ export GITEA_API_URL=https://your-gitea-server.com/api/v1 source ~/.zshrc # or ~/.bashrc ``` -#### Option 2: Global Configuration File +#### Option 3: Global Configuration File ```bash # Create global config directory mkdir -p ~/.gitea-creator @@ -78,12 +90,12 @@ echo "GITEA_TOKEN=your_token_here" > ~/.gitea-creator/.env echo "GITEA_API_URL=https://your-gitea-server.com/api/v1" >> ~/.gitea-creator/.env ``` -#### Option 3: View Configuration Instructions +#### Option 4: View Configuration Instructions ```bash # Get setup instructions anytime +gitea --config +# or gitea-creator --config -# or short form -gitea-creator -c ``` ### Getting a Gitea Access Token @@ -96,6 +108,8 @@ gitea-creator -c ```bash gitea-creator [options] +# or use the short alias +gitea [options] ``` ### Options @@ -103,26 +117,31 @@ gitea-creator [options] - `-p, --public`: Create a public repository (non-private) - `-d, --description `: Add a description to the repository - `--https`: Use HTTPS URL instead of SSH URL (SSH is default) +- `--setup`: Interactive setup for shell environment variables - `-c, --config`: Show configuration setup instructions - `-h, --help`: Display help information ### Examples ```bash +# Setup environment variables (first time only) +gitea --setup + # Create a private repository (SSH URL by default) -gitea-creator my-new-repo +gitea my-new-repo +gitea-creator my-new-repo # Same thing # Create a public repository with description -gitea-creator my-public-repo --public --description "My awesome project" +gitea my-public-repo --public --description "My awesome project" # Create with HTTPS URL instead of SSH -gitea-creator my-repo --https +gitea my-repo --https # Combine options -gitea-creator my-repo -p -d "Short description" --https +gitea my-repo -p -d "Short description" --https # Get configuration help -gitea-creator --config +gitea --config ``` ## Workflow @@ -159,6 +178,8 @@ npm run dev # Test the CLI node dist/index.js my-test-repo --public +# or if linked globally +gitea my-test-repo --public ``` ## Requirements diff --git a/package.json b/package.json index c32515d..35fa00b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "dist/index.js", "type": "module", "bin": { - "gitea-creator": "./dist/index.js" + "gitea-creator": "./dist/index.js", + "gitea": "./dist/index.js" }, "scripts": { "build": "tsc", diff --git a/src/cli.ts b/src/cli.ts index 8ff5149..591e432 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -15,6 +15,8 @@ export interface CliOptions { config?: boolean; /** Use HTTPS URL instead of SSH URL */ https?: boolean; + /** Setup shell environment variables */ + setup?: boolean; } /** @@ -41,6 +43,7 @@ export class CliService { .option('-d, --description ', 'Repository description') .option('--https', 'Use HTTPS URL instead of SSH URL', false) .option('-c, --config', 'Show configuration setup instructions') + .option('--setup', 'Setup environment variables in shell profile') .helpOption('-h, --help', 'Display help for command'); } @@ -60,6 +63,15 @@ export class CliService { process.exit(0); } + // Handle setup option + if (options.setup) { + // This will be handled in the main function + return { + name: '', // Not needed for setup + setup: true, + } as CliOptions; + } + if (!name) { console.error('āŒ Error: Repository name is required'); this.program.help(); @@ -72,6 +84,7 @@ export class CliService { public: options.public, config: options.config, https: options.https, + setup: options.setup, }; } diff --git a/src/index.ts b/src/index.ts index 6c5a16d..a29384e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { env } from "./env.js"; import { CliService } from "./cli.js"; import { GitService } from "./git.js"; import { PromptService } from "./prompts.js"; +import { ShellSetupService } from "./shell-setup.js"; /** * Main application class that orchestrates the CLI workflow @@ -49,6 +50,14 @@ export class App { const cliService = new CliService(); const options = cliService.parse(); + // Handle setup option + if (options.setup) { + const setupService = new ShellSetupService(); + process.on('SIGINT', () => setupService.onCancel()); + await setupService.setup(); + return; + } + console.log(`šŸš€ Creating repository: ${options.name}`); if (options.description) { console.log(`šŸ“ Description: ${options.description}`); @@ -161,6 +170,14 @@ async function main(): Promise { return; } + // Handle setup option early (before requiring repository name) + if (args.includes('--setup')) { + const setupService = new ShellSetupService(); + process.on('SIGINT', () => setupService.onCancel()); + await setupService.setup(); + return; + } + const app = new App(); await app.run(); } @@ -168,7 +185,8 @@ async function main(): Promise { // Run the application - handles both direct execution and npm global installation // Check if this file is being executed as the main module const isMainModule = import.meta.url === `file://${process.argv[1]}` || - process.argv[1]?.includes('gitea-creator'); + process.argv[1]?.includes('gitea-creator') || + process.argv[1]?.includes('gitea'); if (isMainModule) { main().catch((error) => { diff --git a/src/shell-setup.ts b/src/shell-setup.ts new file mode 100644 index 0000000..064b457 --- /dev/null +++ b/src/shell-setup.ts @@ -0,0 +1,150 @@ +import prompts from 'prompts'; +import { appendFileSync, existsSync } from 'fs'; +import { homedir } from 'os'; +import path from 'path'; + +/** + * Shell setup service for automatically configuring environment variables + */ +export class ShellSetupService { + /** + * Interactive setup for shell environment variables + */ + async setup(): Promise { + console.log('šŸš€ Gitea Creator Shell Setup'); + console.log('This will add environment variables to your shell profile.\n'); + + try { + // Get API endpoint + const { apiUrl } = await prompts({ + type: 'text', + name: 'apiUrl', + message: 'Enter your Gitea API URL:', + initial: 'https://git.harvmaster.com/api/v1', + validate: (value: string) => { + if (!value.trim()) return 'API URL is required'; + if (!value.startsWith('http')) return 'API URL must start with http:// or https://'; + return true; + } + }); + + if (!apiUrl) { + console.log('āŒ Setup cancelled'); + return; + } + + // Get token + const { token } = await prompts({ + type: 'password', + name: 'token', + message: 'Enter your Gitea access token:', + validate: (value: string) => { + if (!value.trim()) return 'Access token is required'; + if (value.length < 10) return 'Token seems too short'; + return true; + } + }); + + if (!token) { + console.log('āŒ Setup cancelled'); + return; + } + + // Detect shell and get appropriate config file + const shell = this.detectShell(); + const configFile = this.getShellConfigFile(shell); + + // Prepare environment variables + const envVars = ` +# Gitea Creator Configuration +export GITEA_TOKEN=${token} +export GITEA_API_URL=${apiUrl} +`; + + // Check if variables already exist + if (existsSync(configFile)) { + const { shouldOverwrite } = await prompts({ + type: 'confirm', + name: 'shouldOverwrite', + message: `Add environment variables to ${configFile}?`, + initial: true + }); + + if (!shouldOverwrite) { + console.log('āŒ Setup cancelled'); + return; + } + } + + // Append to shell config file + try { + appendFileSync(configFile, envVars); + console.log('āœ… Environment variables added successfully!'); + console.log(`šŸ“ Updated file: ${configFile}`); + console.log(''); + console.log('šŸ”„ To activate the changes, run:'); + console.log(` source ${configFile}`); + console.log(''); + console.log('Or restart your terminal.'); + console.log(''); + console.log('šŸŽ‰ Setup complete! You can now use gitea-creator anywhere.'); + } catch (error) { + throw new Error(`Failed to write to ${configFile}: ${error}`); + } + + } catch (error) { + if (error instanceof Error) { + console.error('āŒ Setup failed:', error.message); + } else { + console.error('āŒ Setup failed:', String(error)); + } + process.exit(1); + } + } + + /** + * Detect the user's shell + */ + private detectShell(): string { + const shell = process.env.SHELL || ''; + if (shell.includes('zsh')) return 'zsh'; + if (shell.includes('bash')) return 'bash'; + if (shell.includes('fish')) return 'fish'; + + // Default to bash if we can't detect + return 'bash'; + } + + /** + * Get the appropriate shell configuration file path + */ + private getShellConfigFile(shell: string): string { + const home = homedir(); + + switch (shell) { + case 'zsh': + return path.join(home, '.zshrc'); + case 'bash': + // Check for .bash_profile first (macOS), then .bashrc + const bashProfile = path.join(home, '.bash_profile'); + const bashrc = path.join(home, '.bashrc'); + + if (existsSync(bashProfile)) { + return bashProfile; + } + return bashrc; + case 'fish': + return path.join(home, '.config/fish/config.fish'); + default: + return path.join(home, '.bashrc'); + } + } + + /** + * Handle the case when prompts are cancelled (Ctrl+C) + */ + onCancel() { + console.log('\nāŒ Setup cancelled by user'); + process.exit(0); + } +} \ No newline at end of file