UNPKG

postpipe

Version:

An helper bot for deploying API collections to pipelines

1,208 lines (1,073 loc) 37.3 kB
#!/usr/bin/env node import fetch from "node-fetch"; import fs from "fs-extra"; import path from "path"; import { execSync } from "child_process"; import inquirer from "inquirer"; import FormData from "form-data"; /** * Create a package.json file with specified dependencies and install them. * @param {string} dirPath - Directory where the package.json will be created. * @param {Object} dependencies - Dependencies to include in package.json. */ export function createAndInstallPackageJson(dirPath, dependencies) { const packageJsonPath = path.join(dirPath, "package.json"); // Create package.json content const packageJsonContent = { name: "postman-collection-runner", version: "1.0.0", description: "A Node.js project for running Postman collections using Newman", main: "index.js", scripts: { test: `npx newman run ${dependencies.collectionFilePath}`, }, dependencies: { newman: "6.1.3", "newman-reporter-htmlextra": "1.23.1", }, }; // Write package.json to the specified directory fs.writeFileSync( packageJsonPath, JSON.stringify(packageJsonContent, null, 2), "utf8" ); console.log("package.json created successfully."); // Install the dependencies execSync("npm install", { cwd: dirPath, stdio: "inherit" }); console.log("Dependencies installed successfully."); } export function moveJsonFile(sourceFilePath, targetDir) { try { // Check if sourceFilePath is defined if (!sourceFilePath) { throw new Error("Source file path is undefined."); } // Debug: Log the input sourceFilePath console.log("Input sourceFilePath:", sourceFilePath); // Resolve the source file path const resolvedSourceFilePath = path.normalize(sourceFilePath); console.log("Resolved source file path:", resolvedSourceFilePath); // Ensure the source file exists and is a JSON file if (!fs.existsSync(resolvedSourceFilePath)) { throw new Error(`Source file does not exist: ${resolvedSourceFilePath}`); } if (path.extname(resolvedSourceFilePath).toLowerCase() !== ".json") { throw new Error(`Source file is not a JSON file: ${resolvedSourceFilePath}`); } // Ensure the target directory exists try { fs.ensureDirSync(targetDir); console.log("Target directory created or already exists:", targetDir); } catch (error) { throw new Error(`Failed to create target directory: ${error.message}`); } // Extract the filename from the source path let fileName; try { fileName = path.basename(resolvedSourceFilePath); console.log("File name:", fileName); } catch (error) { throw new Error(`Failed to extract filename: ${error.message}`); } // Construct the target file path let targetFilePath; try { targetFilePath = path.join(targetDir, fileName); console.log("Target file path:", targetFilePath); } catch (error) { throw new Error(`Failed to construct target file path: ${error.message}`); } // Move the file try { fs.moveSync(resolvedSourceFilePath, targetFilePath, { overwrite: true }); console.log(`File moved successfully to: ${targetFilePath}`); } catch (error) { throw new Error(`Failed to move the file: ${error.message}`); } // Verify the file was moved if (fs.existsSync(targetFilePath)) { console.log("File verification successful."); return targetFilePath; // Return the path of the moved file } else { throw new Error("Failed to verify the moved file."); } } catch (error) { console.error("An error occurred while moving the file:", error.message); throw error; // Rethrow the error if you want the calling code to handle it } } /** * Generate a specific YAML file for running Postman tests. * @param {string} versionChoice - Version Control tool type. * @param {string} collectionFilePath - Path to the Postman collection JSON file. * @param {string} environmentFilePath - Path to the Postman environment JSON file. * @param {string} outputYamlFilePath - Path where the YAML file should be saved. */ export function generateYaml( versionChoice, collectionFilePath, environmentFilePath, outputYamlFilePath ) { const outputDir = path.dirname(outputYamlFilePath); fs.ensureDirSync(outputDir); /** SOLUTION * CREATE A ROOT FOLDER FOR WHAT'S TO BE CONVERTED * EXTRACT THE COLLECTION INTO THE NEW JSON FILE IN A SPECIFIED PLACE IN THE ROOT FOLDER * THAT FORMS THE COLLECTION FILE PATH. SAME MODEL FOR ENVIRONMENT FILE PATH AS WELL. * IN SHAA ALLAH */ let yamlFileContent; switch (versionChoice) { case "GitHub": if (!environmentFilePath) { yamlFileContent = ` name: Run Postman Collection on: [push] jobs: run-postman-collection: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up node uses: actions/setup-node@v3 with: node-version: '18' - name: Install Dependencies run: | npm install echo "check dependencies version" npx newman --version npx newman-reporter-htmlextra --version - name: Run Postman collection run: | echo "create report directory" mkdir -p ./newman npx newman run ${collectionFilePath} -r cli,htmlextra --reporter-htmlextra-export ./newman/results.html - name: Upload Test Results uses: actions/upload-artifact@v4 with: name: postman-test-results path: ./newman/results.html `; } else { yamlFileContent = ` name: Run Postman Collection on: [push] jobs: run-postman-collection: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up node uses: actions/setup-node@v3 with: node-version: '18' - name: Install Dependencies run: | npm install echo "check dependencies version" npx newman --version npx newman-reporter-htmlextra --version - name: Run Postman collection run: | echo "create report directory" mkdir -p ./newman npx newman run ${collectionFilePath} -e ${environmentFilePath} -r cli,htmlextra --reporter-htmlextra-export ./newman/results.html - name: Upload Test Results uses: actions/upload-artifact@v4 with: name: postman-test-results path: ./newman/results.html `; } console.log( `Generating GitHub Actions YAML file at ${outputYamlFilePath} successfully` ); fs.writeFileSync(outputYamlFilePath, yamlFileContent, "utf8"); console.log("YAML file generated successfully."); break; case "Gitlab": if (!environmentFilePath) { yamlFileContent = ` stages: - test pipeline: image: node:latest stage: test script: # Install from package.json - npm i - npx newman run ${collectionFilePath} -r cli,htmlextra --reporter-htmlextra-export ./newman/results.html artifacts: paths: - ./newman/results.html `; } else { yamlFileContent = ` stages: - test pipeline: image: node:latest stage: test script: # Install from package.json - npm i - npx newman run ${collectionFilePath} -e ${environmentFilePath} -r cli,htmlextra --reporter-htmlextra-export ./newman/results.html artifacts: paths: - ./newman/results.html `; } console.log( `Generating Gitlab YAML file at ${outputYamlFilePath} successfully` ); fs.writeFileSync(outputYamlFilePath, yamlFileContent, "utf8"); console.log("YAML file generated successfully."); break; case "Bitbucket": if (!environmentFilePath) { yamlFileContent = ` image: node:latest pipelines: default: - step: script: - npm i - mkdir -p ./newman - npx newman --version - npx newman run ${collectionFilePath} -r cli,htmlextra --reporter-htmlextra-export ./newman/results.html artifacts: - newman/results.html `; } else { yamlFileContent = ` image: postman/newman pipelines: default: - step: script: - npm i - mkdir -p ./newman - newman --version - npx newman run ${collectionFilePath} -e ${environmentFilePath} -r cli,htmlextra --reporter-htmlextra-export ./newman/results.html artifacts: - newman/results.html `; } console.log( `Generating Gitlab YAML file at ${outputYamlFilePath} successfully` ); fs.writeFileSync(outputYamlFilePath, yamlFileContent, "utf8"); console.log("YAML file generated successfully."); } } /** * Commit multiple files to the specified repository in a single commit. * @param {string} versionChoice - Version Control tool type. * @param {string} token - GitHub personal access token. * @param {string} repoFullName - Full repository name (e.g., username/repo). * @param {Array<string>} files - List of file paths to commit. * @param {string} message - Commit message. */ export async function makeCommit( versionChoice, token, repoFullName, files, message ) { switch (versionChoice) { case "GitHub": try { const headers = { Authorization: `token ${token}`, Accept: "application/vnd.github.v3+json", }; const branch = "main"; const fileBlobs = []; // Process each file to be committed for (const file of files) { // Ensure the file exists if (!fs.existsSync(file)) { throw new Error(`File not found: ${file}`); } // Read file content as base64 const content = fs.readFileSync(file, "base64"); // Normalize the path and ensure it uses forward slashes const repoFilePath = path.relative(process.cwd(), file).replace(/\\/g, "/"); // Validate the path to ensure it doesn't contain illegal characters if (!/^[a-zA-Z0-9_\-./]+$/.test(repoFilePath)) { throw new Error(`Invalid file path: ${repoFilePath}`); } console.log(`Preparing to commit file: ${repoFilePath}`); // Debugging // Add the file to the list of blobs to be committed fileBlobs.push({ path: repoFilePath, content: content, }); } // Fetch the latest commit SHA for the branch const refResponse = await fetch( `https://api.github.com/repos/${repoFullName}/git/refs/heads/${branch}`, { headers } ); const refData = await refResponse.json(); if (!refResponse.ok) { throw new Error(`Failed to fetch reference: ${refData.message}`); } const latestCommitSha = refData.object.sha; // Fetch the tree SHA of the latest commit const commitResponse = await fetch( `https://api.github.com/repos/${repoFullName}/git/commits/${latestCommitSha}`, { headers } ); const commitData = await commitResponse.json(); if (!commitResponse.ok) { throw new Error(`Failed to fetch commit: ${commitData.message}`); } const treeSha = commitData.tree.sha; // Create a new tree with the updated files const treeResponse = await fetch( `https://api.github.com/repos/${repoFullName}/git/trees`, { method: "POST", headers, body: JSON.stringify({ base_tree: treeSha, tree: fileBlobs.map((file) => ({ path: file.path, mode: "100644", // File mode (100644 for normal files) type: "blob", // Type of object (blob for files) content: Buffer.from(file.content, "base64").toString("utf8"), // Decode base64 content })), }), } ); const treeData = await treeResponse.json(); if (!treeResponse.ok) { throw new Error(`Failed to create tree: ${treeData.message}`); } // Create a new commit const newCommitResponse = await fetch( `https://api.github.com/repos/${repoFullName}/git/commits`, { method: "POST", headers, body: JSON.stringify({ message: message, // Commit message tree: treeData.sha, // SHA of the new tree parents: [latestCommitSha], // Parent commit SHA }), } ); const newCommitData = await newCommitResponse.json(); if (!newCommitResponse.ok) { throw new Error(`Failed to create commit: ${newCommitData.message}`); } // Update the branch to point to the new commit const updateResponse = await fetch( `https://api.github.com/repos/${repoFullName}/git/refs/heads/${branch}`, { method: "PATCH", headers, body: JSON.stringify({ sha: newCommitData.sha, // SHA of the new commit }), } ); const updateData = await updateResponse.json(); if (!updateResponse.ok) { throw new Error(`Failed to update branch: ${updateData.message}`); } console.log(`Commit successfully created: ${newCommitData.sha}`); } catch (error) { console.error( "An error occurred while committing files to GitHub:", error.message ); throw error; } break; case "Gitlab": try { const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json", Accept: "application/vnd.gitlab.v4+json", }; // Step 1: Get the authenticated user's ID const userResponse = await fetch("https://gitlab.com/api/v4/user", { headers, }); const userData = await userResponse.json(); if (!userResponse.ok) { throw new Error(`Failed to fetch user ID: ${userData.message}`); } const userId = userData.id; // Step 2: Get the project ID based on the repoFullName const projectsResponse = await fetch( `https://gitlab.com/api/v4/users/${userId}/projects`, { headers } ); const projectsData = await projectsResponse.json(); if (!projectsResponse.ok) { throw new Error( `Failed to fetch projects for user ID ${userId}: ${projectsData.message}` ); } // Find the project that matches the repoFullName const project = projectsData.find( (proj) => proj.path_with_namespace === repoFullName ); if (!project) { throw new Error( `Project ${repoFullName} not found for user ID ${userId}` ); } const projectId = project.id; const branch = "main"; // Step 3: Prepare the actions for the commit with existence check const actions = await Promise.all( files.map(async (file) => { const repoFilePath = path .relative(process.cwd(), file) .replace(/\\/g, "/"); // Check if the file exists in the repository const fileExistsResponse = await fetch( `https://gitlab.com/api/v4/projects/${projectId}/repository/files/${encodeURIComponent( repoFilePath )}/raw?ref=${branch}`, { headers } ); const content = fs.readFileSync(file, "utf8"); // Determine the correct action based on the file's existence const actionType = fileExistsResponse.ok ? "update" : "create"; return { action: actionType, file_path: repoFilePath, content: content, }; }) ); // Step 4: Create the commit with the updated files const commitResponse = await fetch( `https://gitlab.com/api/v4/projects/${projectId}/repository/commits`, { method: "POST", headers, body: JSON.stringify({ branch, commit_message: message, actions, }), } ); const commitData = await commitResponse.json(); if (!commitResponse.ok) { throw new Error(`Failed to create commit: ${commitData.message}`); } console.log(`Commit successfully created: ${commitData.id}`); } catch (error) { console.error( "An error occurred while committing files to GitLab:", error.message ); throw error; } break; case "Bitbucket": try { const authHeader = `Basic ${Buffer.from( `${username}:${token}` ).toString("base64")}`; const baseUrl = `https://api.bitbucket.org/2.0/repositories/${repoFullName}`; const branch = "main"; // Step 1: Check if the pipeline configuration file exists const pipelineFileUrl = `${baseUrl}/src/${branch}/bitbucket-pipelines.yml`; let pipelineFileResponse = await fetch(pipelineFileUrl, { headers: { Authorization: authHeader, Accept: "application/json", }, }); if (pipelineFileResponse.status === 404) { console.warn(`No Commits Yet. Enabling Bitbucket pipelines.`); // Enable pipelines if the configuration file does not exist const pipelineEnableUrl = `${baseUrl}/pipelines_config`; const enablePipelineResponse = await fetch(pipelineEnableUrl, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: authHeader, }, body: JSON.stringify({ enabled: true }), }); if (!enablePipelineResponse.ok) { const errorText = await enablePipelineResponse.text(); throw new Error(`Failed to enable pipelines: ${errorText}`); } } else if (!pipelineFileResponse.ok) { const errorText = await pipelineFileResponse.text(); throw new Error( `Failed to check pipeline configuration file: ${errorText}` ); } // Step 2: Fetch the latest commit SHA for the branch const branchResponse = await fetch( `${baseUrl}/refs/branches/${branch}`, { headers: { Authorization: authHeader, Accept: "application/json", }, } ); if (!branchResponse.ok) { const errorText = await branchResponse.text(); throw new Error(`Failed to fetch branch: ${errorText}`); } const branchData = await branchResponse.json(); const latestCommitSha = branchData.target.hash; // Step 3: Prepare form-data for the commit const form = new FormData(); form.append("message", message); // Commit message form.append("branch", branch); // Branch to commit to form.append("parents", latestCommitSha); // Parent commit SHA // Add each file to the form-data for (const file of files) { const content = fs.readFileSync(file); // Read file content as buffer const repoFilePath = path .relative(process.cwd(), file) .replace(/\\/g, "/"); // Convert to relative path with forward slashes form.append(repoFilePath, content, { filename: repoFilePath, contentType: "application/octet-stream", // MIME type for binary data }); } // Step 4: Send the commit request const commitResponse = await fetch(`${baseUrl}/src`, { method: "POST", headers: { Authorization: authHeader, ...form.getHeaders(), // Headers required for multipart/form-data }, body: form, // Multipart form-data body }); // Step 5: Check for response and log accordingly if (!commitResponse.ok) { const errorText = await commitResponse.text(); throw new Error(`Failed to create commit: ${errorText}`); } else { console.log(`Commit successfully created`); } } catch (error) { console.error( "An error occurred while committing files to Bitbucket:", error.message ); throw error; } break; default: throw new Error("Unsupported version control platform"); } } /** * Export a Postman collection to a file. * @param {string} apiKey - Postman API key. * @param {string} collectionId - Postman collection ID. * @param {string} outputFilePath - Path where the JSON file should be saved. */ export async function exportPostmanCollection( apiKey, collectionId, outputFilePath ) { try { console.log( `Exporting Postman collection ${collectionId} to ${outputFilePath}` ); const response = await fetch( `https://api.getpostman.com/collections/${collectionId}`, { headers: { "X-Api-Key": apiKey, }, } ); const data = await response.json(); if (!response.ok) { throw new Error(`Failed to export Postman collection: ${data.error}`); } fs.writeFileSync(outputFilePath, JSON.stringify(data, null, 2), "utf8"); console.log("Postman collection exported successfully."); } catch (error) { console.error( "An error occurred while exporting the Postman collection:", error.message ); throw error; } } /** * Export a Postman collection to a file. * @param {string} apiKey - Postman API key. * @param {string} environmentId - Postman environment ID. * @param {string} outputFilePath - Path where the JSON file should be saved. */ export async function exportPostmanEnvironment( apiKey, environmentId, outputFilePath ) { try { console.log( `Exporting Postman collection ${environmentId} to ${outputFilePath}` ); const response = await fetch( `https://api.getpostman.com/environments/${environmentId}`, { headers: { "X-Api-Key": apiKey, }, } ); const data = await response.json(); if (!response.ok) { throw new Error(`Failed to export Postman environment: ${data.error}`); } fs.writeFileSync(outputFilePath, JSON.stringify(data, null, 2), "utf8"); console.log("Postman environment exported successfully."); } catch (error) { console.error( "An error occurred while exporting the Postman environment:", error.message ); throw error; } } /** * Create a new repository. * @param {string} versionChoice - Version Control tool type. * @param {string} token - GitHub personal access token. * @param {string} repoName - Name of the repository to create. * @returns {string} - Full name of the created repository (e.g., username/repo). */ let username; let selectedWorkspace; export async function createRepo(versionChoice, token, repoName) { switch (versionChoice) { case "GitHub": try { console.log(`Creating GitHub repository ${repoName}`); const response = await fetch("https://api.github.com/user/repos", { method: "POST", headers: { Authorization: `token ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: repoName, private: false, }), }); const data = await response.json(); if (!response.ok) { throw new Error( `Failed to create GitHub repository: ${data.message}` ); } console.log("GitHub repository created successfully:", data.full_name); return data.full_name; } catch (error) { console.error( "An error occurred while creating the GitHub repository:", error.message ); throw error; } break; case "Gitlab": try { console.log(`Creating GitLab repository ${repoName}`); const response = await fetch("https://gitlab.com/api/v4/projects", { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify({ name: repoName, visibility: "public", // Set to 'private' for a private repository }), }); const data = await response.json(); if (!response.ok) { throw new Error( `Failed to create GitLab repository: ${data.message}` ); } console.log( "GitLab repository created successfully:", data.path_with_namespace ); return data.path_with_namespace; } catch (error) { console.error( "An error occurred while creating the GitLab repository:", error.message ); throw error; } break; case "Bitbucket": try { // Get username from user const { bitBucketName } = await inquirer.prompt({ type: "input", name: "bitBucketName", message: "What is your BitBucket username?", }); username = bitBucketName; // Fetch available workspaces const workspacesResponse = await fetch( `https://api.bitbucket.org/2.0/workspaces`, { method: "GET", headers: { Authorization: `Basic ${Buffer.from( `${username}:${token}` ).toString("base64")}`, Accept: "application/json", }, } ); const workspacesData = await workspacesResponse.json(); if (!workspacesResponse.ok) { throw new Error( `Failed to fetch workspaces: ${workspacesData.error.message}` ); } const workspaces = workspacesData.values.map( (workspace) => workspace.slug ); if (workspaces.length === 0) { throw new Error("No workspaces found for this account."); } // Select the first workspace or prompt the user to select one selectedWorkspace = workspaces[0]; // Or you can prompt to choose from workspaces if there are multiple console.log(`Selected workspace: ${selectedWorkspace}`); // Create the Bitbucket repository console.log( `Creating Bitbucket repository ${repoName} in workspace ${selectedWorkspace}` ); const repoResponse = await fetch( `https://api.bitbucket.org/2.0/repositories/${selectedWorkspace}/${repoName}`, { method: "POST", headers: { Authorization: `Basic ${Buffer.from( `${username}:${token}` ).toString("base64")}`, "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify({ scm: "git", is_private: false, }), } ); const repoData = await repoResponse.json(); if (!repoResponse.ok) { throw new Error( `Failed to create Bitbucket repository: ${ repoData.error?.message || "Unknown error" }` ); } console.log( "Bitbucket repository created successfully:", repoData.full_name ); return repoData.full_name; } catch (error) { console.error( "An error occurred while creating the Bitbucket repository:", error.message ); throw error; } } } /** * Verify if a GitHub Actions workflow is created. * @param {string} repoFullName - Full repository name (e.g., username/repo). * @returns {boolean} - True if workflow exists, false otherwise. */ export async function verifyGithubActionsWorkflow( repoFullName, branch = "main", retries = 3 ) { try { console.log( `Verifying GitHub Actions workflow for repository ${repoFullName}` ); const apiUrl = `https://api.github.com/repos/${repoFullName}/actions/workflows`; for (let attempt = 1; attempt <= retries; attempt++) { const response = await fetch(apiUrl); const data = await response.json(); if (!response.ok) { throw new Error( `Failed to verify GitHub Actions workflow: ${data.message}` ); } console.log( `Attempt ${attempt}: GitHub Actions workflow verification - ${ data.total_count > 0 ? "exists" : "does not exist" }` ); if (data.total_count > 0) { return true; } await new Promise((resolve) => setTimeout(resolve, 10000)); // Wait 10 seconds between retries } return false; } catch (error) { console.error( "An error occurred while verifying the GitHub Actions workflow:", error.message ); throw error; } } /** * Initialize a repository with a README file. * @param {string} versionChoice - Version Control tool type. * @param {string} token - GitHub personal access token. * @param {string} repoFullName - Full repository name (e.g., username/repo). * @returns {boolean} - True if initialization is successful, false otherwise. */ export async function initializeRepoWithReadme( versionChoice, token, repoFullName ) { switch (versionChoice) { case "GitHub": try { console.log( `Initializing repository ${repoFullName} with a README file` ); const apiUrl = `https://api.github.com/repos/${repoFullName}/contents/README.md`; const body = JSON.stringify({ message: "Initial commit", content: Buffer.from("# " + repoFullName.split("/").pop()).toString( "base64" ), }); const response = await fetch(apiUrl, { method: "PUT", headers: { Authorization: `token ${token}`, "Content-Type": "application/json", }, body, }); if (!response.ok) { const data = await response.json(); throw new Error(`Failed to initialize repository: ${data.message}`); } console.log("Repository initialized with README successfully."); return true; } catch (error) { console.error( "An error occurred while initializing the repository with README:", error.message ); throw error; } break; case "Gitlab": try { console.log( `Initializing GitLab repository ${repoFullName} with a README file` ); const [namespace, project] = repoFullName.split("/"); const apiUrl = `https://gitlab.com/api/v4/projects/${encodeURIComponent( namespace )}%2F${encodeURIComponent(project)}/repository/files/README.md`; const body = JSON.stringify({ branch: "main", content: "# " + repoFullName.split("/").pop(), commit_message: "Initial commit", }); const response = await fetch(apiUrl, { method: "POST", // In GitLab, `POST` is used to create a new file headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body, }); if (!response.ok) { const data = await response.json(); throw new Error(`Failed to initialize repository: ${data.message}`); } console.log("GitLab repository initialized with README successfully."); return true; } catch (error) { console.error( "An error occurred while initializing the GitLab repository with README:", error.message ); throw error; } break; case "Bitbucket": try { console.log( `Initializing Bitbucket repository ${repoFullName} with a README file` ); // const [workspace, repoSlug] = repoFullName.split("/"); const apiUrl = `https://api.bitbucket.org/2.0/repositories/${repoFullName}/src`; const body = new URLSearchParams({ message: "Initial commit", branch: "main", [`README.md`]: "# " + repoFullName.split("/").pop(), }); const response = await fetch(apiUrl, { method: "POST", headers: { Authorization: `Basic ${Buffer.from( `${username}:${token}` ).toString("base64")}`, "Content-Type": "application/x-www-form-urlencoded", }, body, }); if (!response.ok) { const data = await response.json(); throw new Error( `Failed to initialize repository: ${data.error.message}` ); } console.log( "Bitbucket repository initialized with README successfully." ); return true; } catch (error) { console.error( "An error occurred while initializing the Bitbucket repository with README:", error.message ); throw error; } } } /** * Check if README.md exists in the repository. * @param {string} token - GitHub personal access token. * @param {string} repoFullName - Full repository name (e.g., username/repo). * @returns {boolean} - True if README.md exists, false otherwise. */ // COMPLETE FOR GITLAB AND BITBUCKET LATER export async function readmeExists(versionChoice, token, repoFullName) { switch (versionChoice) { case "GitHub": try { console.log( `Checking if README.md exists in GitHub repository ${repoFullName}` ); const apiUrl = `https://api.github.com/repos/${repoFullName}/contents/README.md`; const response = await fetch(apiUrl, { headers: { Authorization: `token ${token}`, }, }); console.log( `README.md ${response.status === 200 ? "exists" : "does not exist"}` ); return response.status === 200; } catch (error) { console.error( "An error occurred while checking if README.md exists in GitHub:", error.message ); throw error; } break case "Gitlab": try { // Split the repoFullName into namespace and project const [namespace, project] = repoFullName.split("/"); // Build the API URL to check for the README.md file in the default branch (main) const apiUrl = `https://gitlab.com/api/v4/projects/${encodeURIComponent( namespace )}%2F${encodeURIComponent( project )}/repository/files/README.md?ref=main`; console.log( `Checking if README.md exists in GitLab repository ${repoFullName}...` ); const response = await fetch(apiUrl, { headers: { Authorization: `Bearer ${token}`, }, }); if (response.status === 200) { console.log("README.md exists in the repository."); return true; } else if (response.status === 404) { console.log("README.md does not exist in the repository."); return false; } else { console.log("Unexpected response:", response.status); return false; } } catch (error) { console.error( "An error occurred while checking for README.md in GitLab:", error.message ); throw error; } break; case "Bitbucket": const { bitBucketName } = await inquirer.prompt({ type: "input", name: "bitBucketName", message: "What is your BitBucket username?", }); username = bitBucketName; try { console.log( `Checking if README.md exists in BitBucket repository ${repoFullName}` ); const apiUrl = `https://api.bitbucket.org/2.0/repositories/${repoFullName}/src/main/README.md`; const response = await fetch(apiUrl, { headers: { Authorization: `Basic ${Buffer.from( `${username}:${token}` ).toString("base64")}`, }, }); const readmeExists = response.status === 200; console.log(`README.md ${readmeExists ? "exists" : "does not exist"}`); return readmeExists; } catch (error) { console.error( "An error occurred while checking if README.md exists:", error.message ); throw error; } } }