diff --git a/.gitignore b/.gitignore index 21831aa..8eb2f0a 100644 --- a/.gitignore +++ b/.gitignore @@ -179,3 +179,45 @@ brewy-darwin-arm64 brewy-darwin-x64 brewy-linux brewy-windows.exe + +# Node.js dependencies +node_modules/ + +# Build output +dist/ +.bun-build* + +# Binaries +brewy +brewy-darwin-arm64 +brewy-darwin-x64 +brewy-linux +brewy-windows.exe + +# Lock files +bun.lockb + +# Logs +*.log + +# OS generated files +.DS_Store +Thumbs.db + +# Temporary files +*.tmp +*.temp + +# Archive files +*.tar.gz + +# IDE specific files +.vscode/ +.idea/ +*.sublime-project +*.sublime-workspace + +# Environment variables +.env + +.*.bun-build \ No newline at end of file diff --git a/brewy.rb b/brewy.rb deleted file mode 100644 index d7cd033..0000000 --- a/brewy.rb +++ /dev/null @@ -1,23 +0,0 @@ -class Brewy < Formula - desc "Description of your project" - homepage "https://github.com/transgirllucy/brewy" - url "https://github.com/transgirllucy/brewy/releases/download/alpha/binaries.tar.gz" - sha256 "6f1d590b3d258dc9605ff41e78093a2a88b7beb0b0d79ad4b5fd1b85bcca416e" - - def install - if OS.mac? - if Hardware::CPU.arm? - bin.install "dist/brewy-darwin-arm64" => "brewy" - elsif Hardware::CPU.intel? - bin.install "dist/brewy-darwin-x64" => "brewy" - end - elsif OS.linux? - if Hardware::CPU.intel - bin.install "dist/brewy-linux" => "brewy" - end - end - - test do - system "#{bin}/brewy", "version" # Adjust this to a valid command for testing - end - end \ No newline at end of file diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..400bb08 --- /dev/null +++ b/default.nix @@ -0,0 +1,48 @@ +{ pkgs ? import {} }: + +let + # Define the version and the base URL for the binaries + version = "1.0.0"; + baseUrl = "https://github.com/transgirllucy/brewy/releases/download/${version}"; + + # Function to determine the binary URL and SHA256 based on the system + getBinaryInfo = pkgs: { + inherit (pkgs.stdenv) isDarwin isAarch64; + + url = if isDarwin then + if isAarch64 then "${baseUrl}/brewy-darwin-arm64" + else "${baseUrl}/brewy-darwin-x64" + else "${baseUrl}/brewy-linux-x64"; + + sha256 = if isDarwin then + if isAarch64 then "672e8250fe54a41675f029eadb053bb238268ecda93bbe4803503c8a523a1726" + else "653192ecfa71ce38a31dc1bc57729c278e4cdcb891560d20c58c326b7bb61df3" + else "7dc8659cd61fe62f68293de13f53168c83bb77dd3294ba50cdb9d1e6f721e8c2"; + }; + + binaryInfo = getBinaryInfo pkgs; + +in + +pkgs.stdenv.mkDerivation { + pname = "brewy"; + inherit version; + + src = pkgs.fetchurl { + url = binaryInfo.url; + sha256 = binaryInfo.sha256; + }; + + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/brewy + chmod +x $out/bin/brewy + ''; + + meta = with pkgs.lib; { + description = "A description of your project"; + homepage = "https://github.com/transgirllucy/brewy"; + license = licenses.mit; # Adjust this to the actual license of your project + maintainers = with maintainers; [ yourName ]; # Replace with your name or GitHub handle + }; +} \ No newline at end of file diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..547bfa9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,22 @@ +{ + description = "A flake for the brewy project"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = { self, nixpkgs }: let + # Import the local package + brewy = nixpkgs.callPackage ./default.nix {}; + + in { + # Expose the package for building and running + packages.x86_64-linux = brewy; + packages.darwin = brewy; + + # Default app to run + defaultPackage.x86_64-linux = brewy; + defaultPackage.aarch64-linux = brewy; + defaultPackage.darwin = brewy; + }; +} \ No newline at end of file diff --git a/package.json b/package.json index 4569986..bdf5321 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "brewy", + "version": "1.0.0", "module": "src/index.ts", "type": "module", "scripts": { diff --git a/src/command.ts b/src/command.ts index cacb132..3db675f 100644 --- a/src/command.ts +++ b/src/command.ts @@ -1,14 +1,16 @@ import { exec } from 'child_process'; -import { consola } from 'consola'; +import consola from 'consola'; -export function runCommand(command: string): Promise { +export function runCommand(command: string, options?: { cwd?: string }): Promise { return new Promise((resolve, reject) => { - exec(command, (error, stdout, stderr) => { + exec(command, options, (error, stdout, stderr) => { if (error) { - reject(`❌ Error: ${stderr}`); + consola.error(`❌ Error executing command: "${command}"\n${stderr}`); + reject(`Error: ${stderr}`); } else { - resolve(`✅ Success: ${stdout}`); + // consola.success(`✅ Command executed successfully: "${command}"`); + resolve(stdout.toString().trim()); } }); }); -} +} \ No newline at end of file diff --git a/src/features/info.ts b/src/features/info.ts index a27edc2..604adca 100644 --- a/src/features/info.ts +++ b/src/features/info.ts @@ -1,7 +1,13 @@ import { consola } from "consola"; import { runCommand } from "../command"; -async function handleInfo(packageName: string, options: { analytics?: boolean; json?: boolean; quiet?: boolean }) { +interface InfoOptions { + analytics?: boolean; + json?: boolean; + quiet?: boolean; +} + +async function handleInfo(packageName: string, options: InfoOptions) { try { // Construct the command based on options let command = `brew info ${packageName}`; @@ -14,11 +20,21 @@ async function handleInfo(packageName: string, options: { analytics?: boolean; j if (options.quiet) { command += ' --quiet'; } + const result = await runCommand(command); - if (!options.quiet) { + + // Handle output based on options + if (options.json) { + try { + const jsonData = JSON.parse(result); + consola.info(`📦 Information for "${packageName}":\n`, jsonData); + } catch (jsonError: any) { + consola.error(`Error parsing JSON output for "${packageName}": ${jsonError.message}`); + } + } else if (!options.quiet) { consola.info(`📦 Information for "${packageName}":\n${result}`); } } catch (error: any) { - consola.error(`Error retrieving information for "${packageName}": ${error.message}`); + consola.error(`❌ Error retrieving information for "${packageName}": ${error.message}`); } } \ No newline at end of file diff --git a/src/features/install_package.ts b/src/features/install_package.ts index b3e7514..89cd666 100644 --- a/src/features/install_package.ts +++ b/src/features/install_package.ts @@ -1,7 +1,15 @@ import { runCommand } from '../command'; import { consola } from 'consola'; import ora from 'ora'; -export async function handleInstall(packageName: string, options: { quiet?: boolean; force?: boolean; verbose?: boolean; dryRun?: boolean; }) { + +interface InstallOptions { + quiet?: boolean; + force?: boolean; + verbose?: boolean; + dryRun?: boolean; +} + +export async function handleInstall(packageName: string, options: InstallOptions) { try { let command = `brew install ${packageName}`; if (options.force) { @@ -16,17 +24,23 @@ export async function handleInstall(packageName: string, options: { quiet?: bool if (options.quiet) { command += ' --quiet'; } - const confirmation = await consola.prompt(`Do you want to install the following packages ${packageName}`, { + + const confirmation = await consola.prompt(`Do you want to install the following package: "${packageName}"?`, { type: "confirm", }); if (confirmation) { const spinner = ora(`🔄 Installing ${packageName}...`).start(); // Start the spinner after confirmation - const result = await runCommand(command); - spinner.succeed(`✅ Successfully installed "${packageName}":\n${result}`); - if (!options.quiet) { - consola.info(`✅ Successfully installed "${packageName}":\n${result}`); + try { + const result = await runCommand(command); + spinner.succeed(`✅ Successfully installed "${packageName}":\n${result}`); + if (!options.quiet) { + consola.info(`✅ Successfully installed "${packageName}":\n${result}`); + } + } catch (installError: any) { + spinner.fail(`❌ Failed to install "${packageName}": ${installError.message}`); + consola.error(`❌ Error details: ${installError.message}`); } } else { consola.warn('❌ Installation cancelled.'); @@ -34,6 +48,6 @@ export async function handleInstall(packageName: string, options: { quiet?: bool } } catch (error: any) { - consola.error(`❌ Error installing "${packageName}": ${error.message}`); + consola.error(`❌ Error during installation process: ${error.message}`); } } \ No newline at end of file diff --git a/src/features/search.ts b/src/features/search.ts index 6edc981..842374b 100644 --- a/src/features/search.ts +++ b/src/features/search.ts @@ -1,11 +1,24 @@ +import ora from "ora"; import { runCommand } from "../command"; import { consola } from "consola"; -export async function searchPackage({ term, options }: { term: string; options: { formula?: boolean; cask?: boolean; desc?: boolean; quiet?: boolean } }) { +interface SearchOptions { + formula?: boolean; + cask?: boolean; + desc?: boolean; + quiet?: boolean; +} + +export async function searchPackage({ term, options }: { term: string; options: SearchOptions }) { try { + if (!term) { + consola.warn('❌ Please provide a search term.'); + return; + } + let command = `brew search `; if (term.startsWith('/') && term.endsWith('/')) { - command += term; + command += term; // Use regex search } else { command += term.replace(/ /g, '\\ '); // Escape spaces in the term } @@ -26,11 +39,16 @@ export async function searchPackage({ term, options }: { term: string; options: command += ' --quiet'; } + const spinner = ora('🔍 Searching...').start(); const result = await runCommand(command); if (!options.quiet) { - consola.info(`📦 Search results for "${term}":\n${result}`); + const formattedResults = result.split('\n').map(line => line.trim()).filter(line => line); + console.log("\n"); + formattedResults.forEach(line => { + consola.info(`📦 ${line}`); + }); } - } catch (error) { - consola.error(error); + } catch (error: any) { + consola.error(`❌ Error searching for package "${term}": ${error.message}`); } } \ No newline at end of file diff --git a/src/features/services.ts b/src/features/services.ts new file mode 100644 index 0000000..e16b032 --- /dev/null +++ b/src/features/services.ts @@ -0,0 +1,50 @@ +import { runCommand } from "../command"; // Assuming runCommand is a function that executes shell commands +import { consola } from "consola"; +import ora from "ora"; + +interface ServiceOptions { + all?: boolean; + json?: boolean; + debug?: boolean; + quiet?: boolean; + verbose?: boolean; + file?: string; + serviceUser ?: string; + maxWait?: number; + noWait?: boolean; +} + +export async function handleServices(subcommand: string, formula?: string, options?: ServiceOptions) { + const spinner = ora(`🔄 Executing brew services ${subcommand}...`).start(); // Start spinner with emoji + + try { + let command = `brew services ${subcommand}`; + + // Handle options + if (options) { + if (options.all) command += ' --all'; + if (options.json) command += ' --json'; + if (options.debug) command += ' --debug'; + if (options.quiet) command += ' --quiet'; + if (options.verbose) command += ' --verbose'; + if (options.file) command += ` --file=${options.file}`; + if (options.serviceUser ) command += ` --sudo-service-user=${options.serviceUser }`; + if (options.maxWait !== undefined) command += ` --max-wait=${options.maxWait}`; + if (options.noWait) command += ' --no-wait'; + } + + if (formula) { + command += ` ${formula}`; + } + + const result = await runCommand(command); + spinner.succeed(`✅ Successfully executed: ${command}`); // Success message with emoji + + // Output the result + if (!options?.quiet) { + consola.info(`📜 ${result}`); // Output result with an emoji + } + } catch (error: any) { + spinner.fail(`❌ Error executing brew services ${subcommand}: ${error.message}`); // Error message with emoji + } +} \ No newline at end of file diff --git a/src/features/uninstall.ts b/src/features/uninstall.ts index fa74df4..d6611b0 100644 --- a/src/features/uninstall.ts +++ b/src/features/uninstall.ts @@ -1,8 +1,22 @@ import { consola } from "consola"; import { runCommand } from "../command"; -export async function handleUninstall(packageName: string, options: { force?: boolean; zap?: boolean; ignoreDependencies?: boolean; quiet?: boolean; verbose?: boolean; cask?: boolean }) { +interface UninstallOptions { + force?: boolean; + zap?: boolean; + ignoreDependencies?: boolean; + quiet?: boolean; + verbose?: boolean; + cask?: boolean; +} + +export async function handleUninstall(packageName: string, options: UninstallOptions) { try { + if (!packageName) { + consola.warn('❌ Please provide a package name to uninstall.'); + return; + } + let command = 'brew uninstall'; if (options.force) { command += ' --force'; @@ -24,17 +38,17 @@ export async function handleUninstall(packageName: string, options: { force?: bo } command += ` ${packageName}`; - // Execute the command without logging the output + // Execute the command await runCommand(command); // Assuming runCommand accepts an options object - // Your own message + // Log success message if (!options.quiet) { consola.info(`🗑️ Successfully uninstalled "${packageName}".`); } - } catch (error) { - // Optionally log the error if you want to handle it + } catch (error: any) { + // Log the error if not in quiet mode if (!options.quiet) { - consola.error(`Error during uninstallation of "${packageName}": ${error.message}`); + consola.error(`❌ Error during uninstallation of "${packageName}": ${error.message}`); } } } \ No newline at end of file diff --git a/src/features/update.ts b/src/features/update.ts index 7c28e9e..3df0ba5 100644 --- a/src/features/update.ts +++ b/src/features/update.ts @@ -1,6 +1,15 @@ import { runCommand } from "../command"; import { consola } from "consola"; -export async function handleUpdate(options: { merge?: boolean; autoUpdate?: boolean; force?: boolean; quiet?: boolean; verbose?: boolean }) { + +interface UpdateOptions { + merge?: boolean; + autoUpdate?: boolean; + force?: boolean; + quiet?: boolean; + verbose?: boolean; +} + +export async function handleUpdate(options: UpdateOptions) { try { // Construct the command based on options let command = 'brew update'; @@ -25,7 +34,6 @@ export async function handleUpdate(options: { merge?: boolean; autoUpdate?: bool consola.info(`🔄 Update completed:\n${result}`); } } catch (error: any) { - consola.error(`Error during update: ${error.message}`); + consola.error(`❌ Error during update: ${error.message}`); } -} - +} \ No newline at end of file diff --git a/src/features/upgrade.ts b/src/features/upgrade.ts index 0e35864..7938f72 100644 --- a/src/features/upgrade.ts +++ b/src/features/upgrade.ts @@ -1,9 +1,21 @@ import consola from 'consola'; // Ensure this import is correct import { runCommand } from '../command'; +interface UpgradeOptions { + quiet?: boolean; + force?: boolean; + verbose?: boolean; + dryRun?: boolean; + cask?: boolean; +} -export async function handleUpgrade(packageNames: string[], options: { quiet?: boolean; force?: boolean; verbose?: boolean; dryRun?: boolean; cask?: boolean; }) { +export async function handleUpgrade(packageNames: string[], options: UpgradeOptions) { try { + if (packageNames.length === 0) { + consola.warn('❌ Please provide at least one package name to upgrade.'); + return; + } + let command = 'brew upgrade'; if (options.force) { command += ' --force'; @@ -21,15 +33,13 @@ export async function handleUpgrade(packageNames: string[], options: { quiet?: b command += ' --cask'; } - if (packageNames.length) { - command += ` ${packageNames.join(' ')}`; - } + command += ` ${packageNames.join(' ')}`; const result = await runCommand(command); if (!options.quiet) { consola.info(`🔄 Upgrade completed:\n${result}`); } } catch (error: any) { - consola.error(`Error during upgrade: ${error.message}`); + consola.error(`❌ Error during upgrade: ${error.message}`); } } \ No newline at end of file diff --git a/src/features/version.ts b/src/features/version.ts index be3f530..6d58be3 100644 --- a/src/features/version.ts +++ b/src/features/version.ts @@ -1,7 +1,7 @@ import consola from 'consola'; // Ensure this import is correct import { runCommand } from '../command'; +import { version } from '../../package.json'; // Assuming version is defined in package.json - -export function handleVersion() { - consola.box("Brewy Version 1.0.0") +export function handleVersion(): void { + consola.box(`Brewy Version ${version}`); } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 32bb564..5fa0d90 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import { handleConfig } from './features/config'; import { handleCreate } from './features/create'; import { handleEdit } from './features/edit'; import { handleVersion } from './features/version'; +import { handleServices } from './features/services'; const program = new Command(); program @@ -141,6 +142,22 @@ program.command('uninstall ') handleVersion(); }); + program + .command('services [formula]') + .description('Manage background services with Homebrew') + .option('-a, --all', 'Run subcommand on all services') + .option('--json', 'Output as JSON') + .option('-d, --debug', 'Display any debugging information') + .option('-q, --quiet', 'Make some output more quiet') + .option('-v, --verbose', 'Make some output more verbose') + .option('--file ', 'Use the service file from this location to start the service') + .option('--sudo-service-user ', 'When run as root on macOS, run the service(s) as this user') + .option('--max-wait ', 'Wait at most this many seconds for stop to finish stopping a service') + .option('--no-wait', 'Don\'t wait for stop to finish stopping the service') + .action(async (subcommand, formula, options) => { + await handleServices(subcommand, formula, options); + }); + program.parse(process.argv);