From 3c89453661bf49f406c3e45088ca7a6f43370cd0 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 1 Apr 2025 12:52:20 +0100 Subject: [PATCH 1/4] feature: adds cli support, installation, eslint analysis CY-7427 CY-7428 --- index.ts | 16 ++++++- src/handlers/CLIanalysis.ts | 58 ++++++++++++++++++++++++ src/handlers/index.ts | 2 + src/handlers/installCLI.ts | 87 ++++++++++++++++++++++++++++++++++++ src/tools/CLIanalysisTool.ts | 26 +++++++++++ src/tools/index.ts | 2 + src/tools/installCLITool.ts | 15 +++++++ 7 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 src/handlers/CLIanalysis.ts create mode 100644 src/handlers/installCLI.ts create mode 100644 src/tools/CLIanalysisTool.ts create mode 100644 src/tools/installCLITool.ts diff --git a/index.ts b/index.ts index b708aa3..b034019 100644 --- a/index.ts +++ b/index.ts @@ -26,6 +26,8 @@ import { getPatternTool, getIssueTool, getRepositoryPullRequestTool, + installCLITool, + CLIanalysisTool, listOrganizationsTool, } from './src/tools/index.js'; import { @@ -47,6 +49,8 @@ import { listRepositoryToolsHandler, listToolsHandler, getPatternHandler, + installCLIHandler, + CLIanalysisHandler, listOrganizationsHandler, } from './src/handlers/index.js'; import { validateOrganization } from './src/middleware/validation.js'; @@ -115,7 +119,9 @@ type toolKeys = | 'codacy_get_file_with_analysis' | 'codacy_get_repository_pull_request' | 'codacy_get_issue' - | 'codacy_get_pattern'; + | 'codacy_get_pattern' + | 'codacy_install_cli' + | 'codacy_cli_analysis' interface ToolDefinition { tool: Tool; handler: (args: any) => Promise; @@ -182,6 +188,14 @@ const toolDefinitions: { [key in toolKeys]: ToolDefinition } = { tool: listRepositoryToolsTool, handler: listRepositoryToolsHandler, }, + codacy_install_cli: { + tool: installCLITool, + handler: installCLIHandler, + }, + codacy_cli_analysis: { + tool: CLIanalysisTool, + handler: CLIanalysisHandler, + }, codacy_list_organization: { tool: listOrganizationsTool, handler: listOrganizationsHandler, diff --git a/src/handlers/CLIanalysis.ts b/src/handlers/CLIanalysis.ts new file mode 100644 index 0000000..e3021e8 --- /dev/null +++ b/src/handlers/CLIanalysis.ts @@ -0,0 +1,58 @@ +import { exec } from 'child_process'; +import os from 'os'; +import https from 'https'; + +export async function CLIanalysisHandler(args: { tool: string; format: string; output: string }): Promise<{ message: string }> { + + const latestReleaseTag = await getLatestReleaseTag(); + + const codacyCliPath = os.homedir + "/Library/Caches/Codacy/codacy-cli-v2/" + latestReleaseTag + "/codacy-cli-v2" + + + return new Promise((resolve, reject) => { + const command = codacyCliPath + ` analyze --tool ${args.tool} --format ${args.format} -o ${args.output}`; + + exec(command, (err, stdout) => { + if (err) { + console.error(`Analysis error: ${err}`); + reject({ message: `Analysis failed: ${err.message}` }); + return; + } + + console.log(`Analysis completed: ${stdout}`); + resolve({ message: `Analysis completed successfully. Output saved to ${args.output}` }); + }); + }); +} + +const getLatestReleaseTag = (): Promise => { + return new Promise((resolve, reject) => { + https + .get( + 'https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest', + { + headers: { 'User-Agent': 'node.js' }, + }, + res => { + let data = ''; + + res.on('data', chunk => { + data += chunk; + }); + + res.on('end', () => { + try { + const json = JSON.parse(data); + const tagName = json.tag_name; + resolve(tagName); + } catch (error) { + reject(new Error('Failed to parse response')); + } + }); + } + ) + .on('error', error => { + reject(new Error(`Request failed: ${error.message}`)); + }); + }); + }; \ No newline at end of file diff --git a/src/handlers/index.ts b/src/handlers/index.ts index ff14e02..ae59676 100644 --- a/src/handlers/index.ts +++ b/src/handlers/index.ts @@ -4,4 +4,6 @@ export * from './analysis.js'; export * from './organization.js'; export * from './security.js'; export * from './tools.js'; +export * from './installCLI.js'; +export * from './CLIanalysis.js'; export * from './account.js'; diff --git a/src/handlers/installCLI.ts b/src/handlers/installCLI.ts new file mode 100644 index 0000000..0f6b3f3 --- /dev/null +++ b/src/handlers/installCLI.ts @@ -0,0 +1,87 @@ +import { exec } from 'child_process'; +import https from 'https'; +import os from 'os'; + +export async function installCLIHandler(): Promise<{ message: string }> { + + const latestReleaseTag = await getLatestReleaseTag(); + + const codacyCliPath = os.homedir + "/Library/Caches/Codacy/codacy-cli-v2/" + latestReleaseTag + "/codacy-cli-v2" + + console.error("About to run install command") + + // Download the installation script + exec('curl -Ls https://raw.githubusercontent.com/codacy/codacy-cli-v2/main/codacy-cli.sh -o codacy-cli.sh', (err, stdout) => { + if (err) { + console.error(`Download error: ${err}`); + return; + } + + console.error(`Download completed: ${stdout}`); + exec('chmod +x codacy-cli.sh', (err, stdout) => { + if (err) { + console.error(`Chmod error: ${err}`); + return; + } + console.error(`Chmod completed: ${stdout}`); + + exec(codacyCliPath + ' init', (rmErr) => { + if (rmErr) { + console.error(`Init error: ${rmErr}`); + return; + } + console.error('CLI Init completed'); + exec(codacyCliPath + ' install', (rmErr) => { + if (rmErr) { + console.error(`Install error: ${rmErr}`); + return; + } + console.error('CLI Install completed'); + exec('rm codacy-cli.sh', (rmErr) => { + if (rmErr) { + console.error(`Cleanup error: ${rmErr}`); + return; + } + console.error('Installation script removed'); + }); + }); + }); + }); + }); + + return { + message: "Installation completed" + }; +} + +const getLatestReleaseTag = (): Promise => { + return new Promise((resolve, reject) => { + https + .get( + 'https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest', + { + headers: { 'User-Agent': 'node.js' }, + }, + res => { + let data = ''; + + res.on('data', chunk => { + data += chunk; + }); + + res.on('end', () => { + try { + const json = JSON.parse(data); + const tagName = json.tag_name; + resolve(tagName); + } catch (error) { + reject(new Error('Failed to parse response')); + } + }); + } + ) + .on('error', error => { + reject(new Error(`Request failed: ${error.message}`)); + }); + }); + }; diff --git a/src/tools/CLIanalysisTool.ts b/src/tools/CLIanalysisTool.ts new file mode 100644 index 0000000..6099218 --- /dev/null +++ b/src/tools/CLIanalysisTool.ts @@ -0,0 +1,26 @@ +import { Tool } from '@modelcontextprotocol/sdk/types.js'; + +export const CLIanalysisTool: Tool = { + name: 'codacy_cli_analysis', + description: 'Run analysis using Codacy CLI', + inputSchema: { + type: 'object', + properties: { + tool: { + type: 'string', + description: 'Tool to use for analysis (e.g., eslint)', + default: 'eslint' + }, + format: { + type: 'string', + description: 'Output format (e.g., sarif)', + default: 'sarif' + }, + output: { + type: 'string', + description: 'Output file path', + default: 'results.sarif' + } + } + } +}; diff --git a/src/tools/index.ts b/src/tools/index.ts index 618d349..d746e11 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -16,4 +16,6 @@ export * from './getRepositoryPullRequestTool.js'; export * from './listRepositoryToolPatternsTool.js'; export * from './listRepositoryToolsTool.js'; export * from './listToolsTool.js'; +export * from './installCLITool.js'; +export * from './CLIanalysisTool.js'; export * from './listOrganizationsTool.js'; diff --git a/src/tools/installCLITool.ts b/src/tools/installCLITool.ts new file mode 100644 index 0000000..e27f697 --- /dev/null +++ b/src/tools/installCLITool.ts @@ -0,0 +1,15 @@ +import { Tool } from '@modelcontextprotocol/sdk/types.js'; + +export const installCLITool: Tool = { + name: 'codacy_install_cli', + description: 'Install and configure the Codacy CLI', + inputSchema: { + type: 'object', + properties: { + token: { + type: 'string', + description: 'The Codacy account token', + }, + }, + }, +}; \ No newline at end of file From 04c57651758ba53ae5d2eb1793d2d95202f25c74 Mon Sep 17 00:00:00 2001 From: Milosz Jakubanis Date: Wed, 2 Apr 2025 12:00:56 +0100 Subject: [PATCH 2/4] feat: Improved error handling and checks For external tools we need to be sure they are handled correctly. --- index.ts | 7 +- src/handlers/CLIanalysis.ts | 112 ++++++++++--------- src/handlers/installCLI.ts | 204 ++++++++++++++++++++++------------- src/tools/CLIanalysisTool.ts | 46 ++++---- src/tools/installCLITool.ts | 10 +- 5 files changed, 224 insertions(+), 155 deletions(-) diff --git a/index.ts b/index.ts index b034019..512ac54 100644 --- a/index.ts +++ b/index.ts @@ -49,8 +49,8 @@ import { listRepositoryToolsHandler, listToolsHandler, getPatternHandler, - installCLIHandler, CLIanalysisHandler, + installCliHandler, listOrganizationsHandler, } from './src/handlers/index.js'; import { validateOrganization } from './src/middleware/validation.js'; @@ -121,7 +121,8 @@ type toolKeys = | 'codacy_get_issue' | 'codacy_get_pattern' | 'codacy_install_cli' - | 'codacy_cli_analysis' + | 'codacy_cli_analysis'; + interface ToolDefinition { tool: Tool; handler: (args: any) => Promise; @@ -190,7 +191,7 @@ const toolDefinitions: { [key in toolKeys]: ToolDefinition } = { }, codacy_install_cli: { tool: installCLITool, - handler: installCLIHandler, + handler: installCliHandler, }, codacy_cli_analysis: { tool: CLIanalysisTool, diff --git a/src/handlers/CLIanalysis.ts b/src/handlers/CLIanalysis.ts index e3021e8..8308931 100644 --- a/src/handlers/CLIanalysis.ts +++ b/src/handlers/CLIanalysis.ts @@ -1,58 +1,68 @@ import { exec } from 'child_process'; import os from 'os'; import https from 'https'; +import * as fs from 'node:fs'; -export async function CLIanalysisHandler(args: { tool: string; format: string; output: string }): Promise<{ message: string }> { - - const latestReleaseTag = await getLatestReleaseTag(); - - const codacyCliPath = os.homedir + "/Library/Caches/Codacy/codacy-cli-v2/" + latestReleaseTag + "/codacy-cli-v2" - - - return new Promise((resolve, reject) => { - const command = codacyCliPath + ` analyze --tool ${args.tool} --format ${args.format} -o ${args.output}`; - - exec(command, (err, stdout) => { - if (err) { - console.error(`Analysis error: ${err}`); - reject({ message: `Analysis failed: ${err.message}` }); - return; - } - - console.log(`Analysis completed: ${stdout}`); - resolve({ message: `Analysis completed successfully. Output saved to ${args.output}` }); - }); +const macOsPath = '/Library/Caches/Codacy/codacy-cli-v2/'; +const linuxPath = '/.cache/Codacy/codacy-cli-v2/'; + +export async function CLIanalysisHandler(args: { + tool: string; + format: string; + output: string; +}): Promise<{ + message: string; +}> { + const latestReleaseTag = await getLatestReleaseTag(); + + const codacyCliPath = + os.homedir + '/Library/Caches/Codacy/codacy-cli-v2/' + latestReleaseTag + '/codacy-cli-v2'; + + return new Promise((resolve, reject) => { + console.error('pwd: ' + process.cwd()); + const command = `${codacyCliPath} analyze --tool ${args.tool} --format ${args.format} -o ${args.output}`; + + exec(command, (err, stdout) => { + if (err) { + console.error(`Analysis error: ${err}`); + reject({ message: `Analysis failed: ${err.message}` }); + return; + } + + console.log(`Analysis completed: ${stdout}`); + resolve({ message: `Analysis completed successfully. Output saved to ${args.output}` }); }); -} + }); +} const getLatestReleaseTag = (): Promise => { - return new Promise((resolve, reject) => { - https - .get( - 'https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest', - { - headers: { 'User-Agent': 'node.js' }, - }, - res => { - let data = ''; - - res.on('data', chunk => { - data += chunk; - }); - - res.on('end', () => { - try { - const json = JSON.parse(data); - const tagName = json.tag_name; - resolve(tagName); - } catch (error) { - reject(new Error('Failed to parse response')); - } - }); - } - ) - .on('error', error => { - reject(new Error(`Request failed: ${error.message}`)); - }); - }); - }; \ No newline at end of file + return new Promise((resolve, reject) => { + https + .get( + 'https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest', + { + headers: { 'User-Agent': 'node.js' }, + }, + res => { + let data = ''; + + res.on('data', chunk => { + data += chunk; + }); + + res.on('end', () => { + try { + const json = JSON.parse(data); + const tagName = json.tag_name; + resolve(tagName); + } catch (error) { + reject(new Error('Failed to parse response')); + } + }); + } + ) + .on('error', error => { + reject(new Error(`Request failed: ${error.message}`)); + }); + }); +}; diff --git a/src/handlers/installCLI.ts b/src/handlers/installCLI.ts index 0f6b3f3..8272223 100644 --- a/src/handlers/installCLI.ts +++ b/src/handlers/installCLI.ts @@ -1,87 +1,145 @@ import { exec } from 'child_process'; import https from 'https'; import os from 'os'; +import path from 'node:path'; +import { promisify } from 'node:util'; +import * as fs from 'node:fs'; -export async function installCLIHandler(): Promise<{ message: string }> { +const MAC_OS_PATH = path.join(os.homedir(), 'Library/Caches/Codacy/codacy-cli-v2/'); +const LINUX_PATH = path.join(os.homedir(), '.cache/Codacy/codacy-cli-v2/'); +const GITHUB_LATEST_RELEASE_URL = + 'https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest'; +const GITHUB_INSTALL_SCRIPT_URL = + 'https://raw.githubusercontent.com/codacy/codacy-cli-v2/main/codacy-cli.sh'; - const latestReleaseTag = await getLatestReleaseTag(); +export const installCliHandler = async (): Promise<{ message: string }> => { + const isCliInstalled = await isCodacyCliInstalled(); + const isConfigPresent = isCodacyConfigPresent(); - const codacyCliPath = os.homedir + "/Library/Caches/Codacy/codacy-cli-v2/" + latestReleaseTag + "/codacy-cli-v2" + if (!isCliInstalled) { + const downloadSuccessful = await downloadCliTool(); + if (!downloadSuccessful) { + console.error('Failed to download Codacy CLI'); + throw new Error('Failed to download Codacy CLI'); + } + } - console.error("About to run install command") + const cliPath = await getCodacyCliPath(); - // Download the installation script - exec('curl -Ls https://raw.githubusercontent.com/codacy/codacy-cli-v2/main/codacy-cli.sh -o codacy-cli.sh', (err, stdout) => { - if (err) { - console.error(`Download error: ${err}`); - return; - } + if (!isConfigPresent) { + await execPromise(`${cliPath} init`); + await execPromise(`${cliPath} install`); + } else { + return { + message: 'Codacy CLI is already installed and configured', + }; + } - console.error(`Download completed: ${stdout}`); - exec('chmod +x codacy-cli.sh', (err, stdout) => { - if (err) { - console.error(`Chmod error: ${err}`); - return; - } - console.error(`Chmod completed: ${stdout}`); - - exec(codacyCliPath + ' init', (rmErr) => { - if (rmErr) { - console.error(`Init error: ${rmErr}`); - return; - } - console.error('CLI Init completed'); - exec(codacyCliPath + ' install', (rmErr) => { - if (rmErr) { - console.error(`Install error: ${rmErr}`); - return; - } - console.error('CLI Install completed'); - exec('rm codacy-cli.sh', (rmErr) => { - if (rmErr) { - console.error(`Cleanup error: ${rmErr}`); - return; - } - console.error('Installation script removed'); - }); - }); - }); - }); - }); + const isConfigInstallationSuccessful = isCodacyConfigPresent(); + if (isConfigInstallationSuccessful) { + return { + message: 'Codacy CLI installed successfully', + }; + } else { return { - message: "Installation completed" + message: 'Codacy CLI downloaded, but installation has failed', }; -} + } +}; const getLatestReleaseTag = (): Promise => { - return new Promise((resolve, reject) => { - https - .get( - 'https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest', - { - headers: { 'User-Agent': 'node.js' }, - }, - res => { - let data = ''; - - res.on('data', chunk => { - data += chunk; - }); - - res.on('end', () => { - try { - const json = JSON.parse(data); - const tagName = json.tag_name; - resolve(tagName); - } catch (error) { - reject(new Error('Failed to parse response')); - } - }); - } - ) - .on('error', error => { - reject(new Error(`Request failed: ${error.message}`)); - }); - }); - }; + return new Promise((resolve, reject) => { + https + .get( + GITHUB_LATEST_RELEASE_URL, + { + headers: { 'User-Agent': 'node.js' }, + }, + res => { + let data = ''; + + res.on('data', chunk => { + data += chunk; + }); + + res.on('end', () => { + try { + const json = JSON.parse(data); + const tagName = json.tag_name; + resolve(tagName); + } catch (error) { + reject(new Error('Failed to parse response')); + } + }); + } + ) + .on('error', error => { + reject(new Error(`Request failed: ${error.message}`)); + }); + }); +}; + +const execAsync = promisify(exec); + +const execPromise = async (command: string): Promise => { + try { + const { stdout, stderr } = await execAsync(command); + + if (stderr) return `Command "${command}" has failed, reason: ${stderr}`; + + return stdout.trim(); + } catch (error) { + return `Command failed: ${command}\n${error instanceof Error ? error.message : error}`; + } +}; + +const downloadCliTool = () => { + return new Promise((resolve, reject) => { + exec( + `bash <(curl -Ls ${GITHUB_INSTALL_SCRIPT_URL})`, + { shell: '/bin/bash' }, + (error, stdout, stderr) => { + // if (error) { + // reject(new Error('Error: ' + error.message)); + // return; + // } + // if (stderr) { + // reject(new Error(stderr)); + // return; + // } + resolve(true); + } + ); + }).then(() => true); + //TODO this catch always occurs for some reason + // .catch(error => { + // console.error('Error downloading CLI tool: ', error); + // return false; + // }); +}; + +//TODO better error handling +const getCodacyCliPath: () => Promise = async () => { + const latestReleaseTag = await getLatestReleaseTag(); + + if (os.platform() === 'darwin') { + return path.join(MAC_OS_PATH, latestReleaseTag, 'codacy-cli-v2'); + } else if (os.platform() === 'linux') { + return path.join(LINUX_PATH, latestReleaseTag, 'codacy-cli-v2'); + } else { + throw new Error('Unsupported OS'); + } +}; + +const isCodacyCliInstalled: () => Promise = async () => { + const codacyCliPath = await getCodacyCliPath(); + return fs.existsSync(codacyCliPath); +}; + +const isCodacyConfigPresent = () => { + return ( + fs.existsSync(path.join(process.cwd(), '.codacy', 'codacy.yml')) || + fs.existsSync(path.join(process.cwd(), '.codacy', 'codacy.yaml')) + ); +}; diff --git a/src/tools/CLIanalysisTool.ts b/src/tools/CLIanalysisTool.ts index 6099218..0f08821 100644 --- a/src/tools/CLIanalysisTool.ts +++ b/src/tools/CLIanalysisTool.ts @@ -1,26 +1,26 @@ import { Tool } from '@modelcontextprotocol/sdk/types.js'; export const CLIanalysisTool: Tool = { - name: 'codacy_cli_analysis', - description: 'Run analysis using Codacy CLI', - inputSchema: { - type: 'object', - properties: { - tool: { - type: 'string', - description: 'Tool to use for analysis (e.g., eslint)', - default: 'eslint' - }, - format: { - type: 'string', - description: 'Output format (e.g., sarif)', - default: 'sarif' - }, - output: { - type: 'string', - description: 'Output file path', - default: 'results.sarif' - } - } - } -}; + name: 'codacy_cli_analysis', + description: 'Run analysis using Codacy CLI', + inputSchema: { + type: 'object', + properties: { + tool: { + type: 'string', + description: 'Tool to use for analysis (e.g., eslint)', + default: 'eslint', + }, + format: { + type: 'string', + description: 'Output format (e.g., sarif)', + default: 'sarif', + }, + output: { + type: 'string', + description: 'Output file path', + default: 'results.sarif', + }, + }, + }, +}; diff --git a/src/tools/installCLITool.ts b/src/tools/installCLITool.ts index e27f697..5784a78 100644 --- a/src/tools/installCLITool.ts +++ b/src/tools/installCLITool.ts @@ -6,10 +6,10 @@ export const installCLITool: Tool = { inputSchema: { type: 'object', properties: { - token: { - type: 'string', - description: 'The Codacy account token', - }, + token: { + type: 'string', + description: 'The Codacy account token', + }, }, }, -}; \ No newline at end of file +}; From 8a0ec5b9ae50396b07422e746a4ceb8c64b9985e Mon Sep 17 00:00:00 2001 From: Milosz Jakubanis Date: Wed, 2 Apr 2025 13:09:52 +0100 Subject: [PATCH 3/4] feat: Codacy CLI downloads and installs CY-7427 --- index.ts | 6 +- src/handlers/CLIanalysis.ts | 47 +--------- src/handlers/installCLI.ts | 93 +++++++++---------- ...{CLIanalysisTool.ts => CliAnalysisTool.ts} | 2 +- 4 files changed, 50 insertions(+), 98 deletions(-) rename src/tools/{CLIanalysisTool.ts => CliAnalysisTool.ts} (93%) diff --git a/index.ts b/index.ts index 512ac54..086100b 100644 --- a/index.ts +++ b/index.ts @@ -49,7 +49,7 @@ import { listRepositoryToolsHandler, listToolsHandler, getPatternHandler, - CLIanalysisHandler, + cliAnalysisHandler, installCliHandler, listOrganizationsHandler, } from './src/handlers/index.js'; @@ -194,8 +194,8 @@ const toolDefinitions: { [key in toolKeys]: ToolDefinition } = { handler: installCliHandler, }, codacy_cli_analysis: { - tool: CLIanalysisTool, - handler: CLIanalysisHandler, + tool: cliAnalysisTool, + handler: cliAnalysisHandler, }, codacy_list_organization: { tool: listOrganizationsTool, diff --git a/src/handlers/CLIanalysis.ts b/src/handlers/CLIanalysis.ts index 8308931..34d4a83 100644 --- a/src/handlers/CLIanalysis.ts +++ b/src/handlers/CLIanalysis.ts @@ -1,25 +1,16 @@ import { exec } from 'child_process'; -import os from 'os'; -import https from 'https'; -import * as fs from 'node:fs'; +import { getCodacyCliPath } from './installCLI.js'; -const macOsPath = '/Library/Caches/Codacy/codacy-cli-v2/'; -const linuxPath = '/.cache/Codacy/codacy-cli-v2/'; - -export async function CLIanalysisHandler(args: { +export async function cliAnalysisHandler(args: { tool: string; format: string; output: string; }): Promise<{ message: string; }> { - const latestReleaseTag = await getLatestReleaseTag(); - - const codacyCliPath = - os.homedir + '/Library/Caches/Codacy/codacy-cli-v2/' + latestReleaseTag + '/codacy-cli-v2'; + const codacyCliPath = await getCodacyCliPath(); return new Promise((resolve, reject) => { - console.error('pwd: ' + process.cwd()); const command = `${codacyCliPath} analyze --tool ${args.tool} --format ${args.format} -o ${args.output}`; exec(command, (err, stdout) => { @@ -34,35 +25,3 @@ export async function CLIanalysisHandler(args: { }); }); } - -const getLatestReleaseTag = (): Promise => { - return new Promise((resolve, reject) => { - https - .get( - 'https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest', - { - headers: { 'User-Agent': 'node.js' }, - }, - res => { - let data = ''; - - res.on('data', chunk => { - data += chunk; - }); - - res.on('end', () => { - try { - const json = JSON.parse(data); - const tagName = json.tag_name; - resolve(tagName); - } catch (error) { - reject(new Error('Failed to parse response')); - } - }); - } - ) - .on('error', error => { - reject(new Error(`Request failed: ${error.message}`)); - }); - }); -}; diff --git a/src/handlers/installCLI.ts b/src/handlers/installCLI.ts index 8272223..cc7ee08 100644 --- a/src/handlers/installCLI.ts +++ b/src/handlers/installCLI.ts @@ -19,33 +19,48 @@ export const installCliHandler = async (): Promise<{ message: string }> => { if (!isCliInstalled) { const downloadSuccessful = await downloadCliTool(); if (!downloadSuccessful) { - console.error('Failed to download Codacy CLI'); - throw new Error('Failed to download Codacy CLI'); + return { + message: 'Failed to download Codacy CLI', + }; } } const cliPath = await getCodacyCliPath(); - if (!isConfigPresent) { - await execPromise(`${cliPath} init`); - await execPromise(`${cliPath} install`); - } else { + if (isConfigPresent) { return { message: 'Codacy CLI is already installed and configured', }; } - const isConfigInstallationSuccessful = isCodacyConfigPresent(); - - if (isConfigInstallationSuccessful) { + const initSuccessful = await execPromise(`${cliPath} init`); + if (!initSuccessful) { return { - message: 'Codacy CLI installed successfully', + message: 'Failed to initialize Codacy CLI', }; - } else { + } + + const installSuccessful = await execPromise(`${cliPath} install`); + if (!installSuccessful) { return { - message: 'Codacy CLI downloaded, but installation has failed', + message: 'Failed to install Codacy CLI', }; } + return { + message: 'Codacy CLI installed successfully', + }; +}; + +export const getCodacyCliPath: () => Promise = async () => { + const latestReleaseTag = await getLatestReleaseTag(); + + if (os.platform() === 'darwin') { + return path.join(MAC_OS_PATH, latestReleaseTag, 'codacy-cli-v2'); + } else if (os.platform() === 'linux') { + return path.join(LINUX_PATH, latestReleaseTag, 'codacy-cli-v2'); + } else { + throw new Error('Unsupported OS'); + } }; const getLatestReleaseTag = (): Promise => { @@ -82,54 +97,32 @@ const getLatestReleaseTag = (): Promise => { const execAsync = promisify(exec); -const execPromise = async (command: string): Promise => { +const execPromise = async (command: string): Promise => { try { - const { stdout, stderr } = await execAsync(command); - - if (stderr) return `Command "${command}" has failed, reason: ${stderr}`; - - return stdout.trim(); + await execAsync(command); + return true; } catch (error) { - return `Command failed: ${command}\n${error instanceof Error ? error.message : error}`; + console.error(`Error executing command: ${command}, reason: ${error}`); + return false; } }; -const downloadCliTool = () => { - return new Promise((resolve, reject) => { +const downloadCliTool = (): Promise => { + return new Promise((resolve, _reject) => { exec( `bash <(curl -Ls ${GITHUB_INSTALL_SCRIPT_URL})`, { shell: '/bin/bash' }, - (error, stdout, stderr) => { - // if (error) { - // reject(new Error('Error: ' + error.message)); - // return; - // } - // if (stderr) { - // reject(new Error(stderr)); - // return; - // } - resolve(true); + (error, _stdout, _stderr) => { + if (error == null) { + resolve(true); + return; + } else { + resolve(false); + return; + } } ); - }).then(() => true); - //TODO this catch always occurs for some reason - // .catch(error => { - // console.error('Error downloading CLI tool: ', error); - // return false; - // }); -}; - -//TODO better error handling -const getCodacyCliPath: () => Promise = async () => { - const latestReleaseTag = await getLatestReleaseTag(); - - if (os.platform() === 'darwin') { - return path.join(MAC_OS_PATH, latestReleaseTag, 'codacy-cli-v2'); - } else if (os.platform() === 'linux') { - return path.join(LINUX_PATH, latestReleaseTag, 'codacy-cli-v2'); - } else { - throw new Error('Unsupported OS'); - } + }); }; const isCodacyCliInstalled: () => Promise = async () => { diff --git a/src/tools/CLIanalysisTool.ts b/src/tools/CliAnalysisTool.ts similarity index 93% rename from src/tools/CLIanalysisTool.ts rename to src/tools/CliAnalysisTool.ts index 0f08821..85b2967 100644 --- a/src/tools/CLIanalysisTool.ts +++ b/src/tools/CliAnalysisTool.ts @@ -1,6 +1,6 @@ import { Tool } from '@modelcontextprotocol/sdk/types.js'; -export const CLIanalysisTool: Tool = { +export const cliAnalysisTool: Tool = { name: 'codacy_cli_analysis', description: 'Run analysis using Codacy CLI', inputSchema: { From 430ff59172d8be5450d9f7b5156c8125cc52eb9b Mon Sep 17 00:00:00 2001 From: Milosz Jakubanis Date: Wed, 2 Apr 2025 13:15:35 +0100 Subject: [PATCH 4/4] feat: Fix rebase, rename file CY-7427 Renamed file to better fit rest of the project. --- index.ts | 2 +- src/handlers/{CLIanalysis.ts => cliAnalysis.ts} | 0 src/handlers/index.ts | 2 +- src/tools/index.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/handlers/{CLIanalysis.ts => cliAnalysis.ts} (100%) diff --git a/index.ts b/index.ts index 086100b..afa23bc 100644 --- a/index.ts +++ b/index.ts @@ -27,7 +27,7 @@ import { getIssueTool, getRepositoryPullRequestTool, installCLITool, - CLIanalysisTool, + cliAnalysisTool, listOrganizationsTool, } from './src/tools/index.js'; import { diff --git a/src/handlers/CLIanalysis.ts b/src/handlers/cliAnalysis.ts similarity index 100% rename from src/handlers/CLIanalysis.ts rename to src/handlers/cliAnalysis.ts diff --git a/src/handlers/index.ts b/src/handlers/index.ts index ae59676..6e9292b 100644 --- a/src/handlers/index.ts +++ b/src/handlers/index.ts @@ -5,5 +5,5 @@ export * from './organization.js'; export * from './security.js'; export * from './tools.js'; export * from './installCLI.js'; -export * from './CLIanalysis.js'; +export * from './cliAnalysis.js'; export * from './account.js'; diff --git a/src/tools/index.ts b/src/tools/index.ts index d746e11..4d8119d 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -17,5 +17,5 @@ export * from './listRepositoryToolPatternsTool.js'; export * from './listRepositoryToolsTool.js'; export * from './listToolsTool.js'; export * from './installCLITool.js'; -export * from './CLIanalysisTool.js'; +export * from './cliAnalysisTool.js'; export * from './listOrganizationsTool.js';