Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add functions for gh repository creation #2

Merged
merged 4 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { preparePayload } from './utils/payload/install';
import { installSupabase } from './utils/supabase/install';
import { prettify } from './utils/prettier/prettify';
import { prepareDrink } from './utils/bar/prepareDrink';
import { initializeRepository } from './utils/github/install';

interface ProjectOptions {
name: string;
Expand Down Expand Up @@ -31,5 +32,10 @@ export async function createProject(options: ProjectOptions) {

await prettify();

initializeRepository({
projectName: name,
visibility: 'private',
});

KarolinaKopacz marked this conversation as resolved.
Show resolved Hide resolved
prepareDrink(name);
}
68 changes: 68 additions & 0 deletions packages/core/utils/github/ghInstaller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { execSync } from 'child_process';
import * as os from 'os';

export function isGitHubCLIInstalled(): boolean {
try {
execSync('gh --version', { stdio: 'ignore' });
return true;
} catch (error) {
return false;
}
}

export function installGitHubCLI(): boolean {
const platform = os.platform();
let installCommand: string;

switch (platform) {
case 'darwin': // macOS
installCommand = 'brew install gh';
break;
case 'linux': // Linux
const linuxDistro = getLinuxDistro();
if (linuxDistro === 'ubuntu' || linuxDistro === 'debian') {
installCommand =
'sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key C99B11DEB97541F0 && sudo apt-add-repository https://cli.github.com/packages && sudo apt update && sudo apt install gh';
} else if (linuxDistro === 'fedora' || linuxDistro === 'centos' || linuxDistro === 'rhel') {
installCommand =
'sudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo && sudo dnf install gh';
} else {
console.log('🖇️ Automatic installation is not supported for your Linux distribution.');
console.log('🖇️ Please visit https://github.com/cli/cli#installation for installation instructions.');
return false;
}
break;
case 'win32': // Windows
installCommand = 'winget install --id GitHub.cli';
break;
default:
console.log('🖇️ Automatic installation is not supported for your operating system.');
console.log('🖇️ Please visit https://github.com/cli/cli#installation for installation instructions.');
return false;
}

console.log('🖇️ Installing GitHub CLI...');
try {
execSync(installCommand, { stdio: 'inherit' });
console.log('🖇️ GitHub CLI installed successfully.');
return true;
} catch (error) {
console.error('🍸 Failed to install GitHub CLI.');
console.log('🖇️ Please install it manually from: https://github.com/cli/cli#installation');
return false;
}
}

export function getLinuxDistro(): string {
try {
const osRelease = execSync('cat /etc/os-release').toString();
if (osRelease.includes('Ubuntu')) return 'ubuntu';
if (osRelease.includes('Debian')) return 'debian';
if (osRelease.includes('Fedora')) return 'fedora';
if (osRelease.includes('CentOS')) return 'centos';
if (osRelease.includes('Red Hat')) return 'rhel';
return 'unknown';
} catch (error) {
return 'unknown';
}
}
65 changes: 65 additions & 0 deletions packages/core/utils/github/install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
isGitHubAuthenticated,
createGitHubRepository,
fetchGitHubUsername,
authenticateGitHub,
setupGitRepository,
} from './repositoryManager';
import { installGitHubCLI, isGitHubCLIInstalled } from './ghInstaller';

interface ProjectOptions {
projectName: string;
visibility: 'public' | 'private';
}

// Helper function to check if GitHub CLI is installed
function checkGitHubCLI() {
console.log('🖇️ Checking GitHub CLI installation...');
if (!isGitHubCLIInstalled()) {
console.log('🖇️ GitHub CLI is not installed.');
const installed = installGitHubCLI();
if (!installed) {
console.error('🖇️ GitHub CLI installation failed. Exiting...');
process.exit(1);
}
}
}

// Helper function to ensure GitHub authentication
function ensureGitHubAuthentication() {
console.log('🖇️ Checking GitHub authentication...');

// Check if the user is already authenticated
if (isGitHubAuthenticated()) {
console.log('🖇️ You are already logged in to GitHub.');
return; // Exit early if authenticated
}

if (!authenticateGitHub()) {
console.error('🖇️ Authentication failed. Run "gh auth login" in your terminal and try again.');
process.exit(1);
}
}

export async function initializeRepository(options: ProjectOptions) {
const { projectName, visibility } = options;

checkGitHubCLI();
ensureGitHubAuthentication();

// Retrieve GitHub username once
const username = await fetchGitHubUsername();
if (!username) {
console.error('🖇️ Failed to retrieve GitHub username. Aborting repository creation.');
process.exit(1);
}

// Check if the repository exists and create it
const repoCreated = await createGitHubRepository(projectName, visibility, username);
if (!repoCreated) {
console.error('🖇️ Failed to create GitHub repository. Check your permissions or if the repository already exists.');
process.exit(1);
}

await setupGitRepository(projectName, username);
}
128 changes: 128 additions & 0 deletions packages/core/utils/github/repositoryManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { execSync } from 'child_process';

async function executeCommand(command: string, silent = false): Promise<string | null> {
try {
const result = execSync(command, { stdio: 'pipe' }); // Use 'pipe' to capture output
const output = result.toString().trim(); // Convert buffer to string
return output || null; // Ensure we return null if output is empty
} catch (error) {
console.error(`Error executing command: ${command}`);
if (error instanceof Error) {
console.error(`Error message: ${error.message}`);
if (error) {
console.error(`Command stdout: ${error.toString()}`); // Log stdout
}
}
return null;
}
}

export function isGitHubAuthenticated(): boolean {
console.log('🖇️ Checking GitHub authentication status...');

try {
// Use execSync to run the command and capture output
const result = execSync('gh auth status', { stdio: 'pipe' }).toString();

// Check if the output includes "Logged in" - this is to be changed in the future but couldn't find a better way
return result.includes('Logged in');
} catch (error) {
console.error(`🖇️ Error checking authentication status: ${error}`);
return false;
}
}

export async function authenticateGitHub(): Promise<boolean> {
console.log('🖇️ Attempting to authenticate with GitHub...');

const result = await executeCommand('gh auth login');

if (result) {
// Immediately check authentication status after login attempt
const isAuthenticated = isGitHubAuthenticated();

if (isAuthenticated) {
console.log('🖇️ Authentication was successful.');
return true;
} else {
console.error('🖇️ Authentication failed after login attempt.');
return false;
}
}

console.error('🖇️ No output from gh auth login command.');
return false;
}

export async function fetchGitHubUsername(): Promise<string | null> {
try {
// Run the command without --jq first to inspect raw output
const username = await executeCommand('echo "$(gh api user --jq .login)"');

if (username) {
console.log(`🖇️ Hello \x1b[36m${username}\x1b[0m!`);
return username;
} else {
console.log('🖇️ No username returned or an error occurred.');
return null;
}
} catch (error) {
console.error('🖇️ Error fetching GitHub username:', error);
return null;
}
}

export async function createGitHubRepository(
projectName: string,
repositoryVisibility: 'public' | 'private',
username: string,
): Promise<boolean> {
console.log(`🖇️ Checking if repository already exists...`);

// Check if the repository exists
const repoCheckCommand = `echo "$(gh repo view ${username}/${projectName} --json name)"`;
const existingRepo = await executeCommand(repoCheckCommand, true); // Silent mode to suppress output

if (existingRepo) {
console.error(`🖇️ Repository "${projectName}" already exists.`);
return false; // Return false to indicate the repo was not created
}

console.log(`🖇️ Creating GitHub repository: \x1b[36m${projectName}\x1b[0m`);

const visibility = repositoryVisibility === 'public' ? '--public' : '--private';
const command = `gh repo create ${projectName} ${visibility} --confirm`;

const result = await executeCommand(command);

if (result) {
console.log(`🖇️ Repository created successfully at \x1b[36m${result}\x1b[0m`);
return true; // Return true to indicate success
}

console.error('🖇️ Failed to create GitHub repository.');
return false; // Return false on failure
}

// New function to set up the local Git repository
export async function setupGitRepository(projectName: string, username: string) {
console.log(`🖇️ Setting up Git for the repository...`);

// Set the remote origin and push to GitHub
const commands = [
`git init`,
`git add .`,
`git commit -m "feat: initial commit"`,
`git branch -M main`,
`git remote add origin [email protected]:${username}/${projectName}.git`,
`git push -u origin main`,
];

for (const cmd of commands) {
const result = executeCommand(cmd);
if (!result) {
console.error(`🖇️ Failed to execute command: ${cmd}`);
process.exit(1);
}
}
}