Skip to content

Commit 1605c11

Browse files
alerizzonedaKaighobadiCopilot
authored
feat: install CLI in workspace (#55)
* download cli locally * add cwd to all execs * refactoring * refactor, update, and fix some issues * Update src/commands/installAnalysisCLI.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Neda Kaighobadi <neda.kaighobadi@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 32030d7 commit 1605c11

File tree

4 files changed

+82
-62
lines changed

4 files changed

+82
-62
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
},
9292
{
9393
"view": "codacy:cli",
94-
"contents": "[Install Codacy CLI](command:codacy.installCLI)",
94+
"contents": "Install Codacy CLI to analyze your code locally\n[Install Codacy CLI](command:codacy.installCLI)",
9595
"when": "!codacy:cliInstalled && codacy:canInstallCLI && !codacy:cliInstalling"
9696
},
9797
{
@@ -121,12 +121,12 @@
121121
},
122122
{
123123
"view": "codacy:mcp",
124-
"contents": "Enable your AI Chat to talk to Codacy's Cloud API \n[Add Codacy MCP Server](command:codacy.configureMCP)",
124+
"contents": "Enable your AI Agent to interact with Codacy Guardrails \n[Add Codacy MCP Server](command:codacy.configureMCP)",
125125
"when": "Codacy:RepositoryManagerStateContext == Loaded && codacy:supportsMCP && !codacy:mcpConfigured"
126126
},
127127
{
128128
"view": "codacy:mcp",
129-
"contents": "MCP Server is enabled\n[Reset MCP Server](command:codacy.configureMCP.reset)",
129+
"contents": "Codacy Guardrails MCP Server is enabled\n[Reset MCP Server](command:codacy.configureMCP.reset)",
130130
"when": "Codacy:RepositoryManagerStateContext == Loaded && codacy:supportsMCP && codacy:mcpConfigured"
131131
}
132132
],

src/commands/installAnalysisCLI.ts

Lines changed: 75 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,113 @@
1-
import * as os from 'os'
21
import * as vscode from 'vscode'
32
import * as fs from 'fs'
43
import * as path from 'path'
54
import { exec } from 'child_process'
6-
import { promisify } from 'util'
75
import { Config } from '../common/config'
86
import { Repository } from '../api/client'
7+
import Logger from '../common/logger'
98

10-
const execAsync = promisify(exec)
9+
const CLI_FILE_NAME = 'cli.sh'
10+
const CLI_FOLDER_NAME = '.codacy'
11+
const CLI_COMMAND = `${CLI_FOLDER_NAME}/${CLI_FILE_NAME}`
12+
13+
// Set a larger buffer size (10MB)
14+
const MAX_BUFFER_SIZE = 1024 * 1024 * 10
15+
16+
const execAsync = (command: string) => {
17+
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ''
18+
19+
return new Promise((resolve, reject) => {
20+
exec(
21+
`CODACY_CLI_V2_VERSION=1.0.0-main.232.a6a6368 ${command}`,
22+
{
23+
cwd: workspacePath,
24+
maxBuffer: MAX_BUFFER_SIZE, // To solve: stdout maxBuffer exceeded
25+
},
26+
(error, stdout, stderr) => {
27+
if (error) {
28+
reject(error)
29+
return
30+
}
31+
32+
if (stderr && (!stdout || /error|fail|exception/i.test(stderr))) {
33+
reject(new Error(stderr))
34+
return
35+
}
36+
37+
resolve({ stdout, stderr })
38+
}
39+
)
40+
})
41+
}
1142

1243
export async function isCLIInstalled(): Promise<boolean> {
1344
try {
14-
await execAsync('codacy-cli --help')
45+
await execAsync(`${CLI_COMMAND} --help`)
1546
return true
1647
} catch {
1748
return false
1849
}
1950
}
2051

21-
async function isBrewInstalled(): Promise<boolean> {
22-
try {
23-
await execAsync('brew --version')
24-
return true
25-
} catch {
26-
return false
52+
async function downloadCodacyCLI(): Promise<void> {
53+
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ''
54+
const codacyFolder = path.join(workspacePath, CLI_FOLDER_NAME)
55+
const codacyCliPath = path.join(codacyFolder, CLI_FILE_NAME)
56+
57+
// Create .codacy folder if it doesn't exist
58+
if (!fs.existsSync(codacyFolder)) {
59+
fs.mkdirSync(codacyFolder, { recursive: true })
60+
}
61+
62+
// Download cli.sh if it doesn't exist
63+
if (!fs.existsSync(codacyCliPath)) {
64+
await execAsync(
65+
`curl -Ls -o "${CLI_COMMAND}" https://raw.githubusercontent.com/codacy/codacy-cli-v2/main/codacy-cli.sh`
66+
)
67+
68+
await execAsync(`chmod +x "${CLI_COMMAND}"`)
2769
}
2870
}
2971

30-
async function initializeCLI(repository: Repository): Promise<void> {
72+
async function initializeCLI(repository?: Repository): Promise<void> {
3173
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ''
3274
const codacyYamlPath = path.join(workspacePath, '.codacy', 'codacy.yaml')
33-
const apiToken = Config.apiToken
3475

35-
const { provider, owner: organization, name: repositoryName } = repository
76+
const apiToken = Config.apiToken ? `--api-token ${Config.apiToken}` : ''
77+
const repositoryAccess = repository
78+
? `--provider ${repository.provider} --organization ${repository.owner} --repository ${repository.name}`
79+
: ''
3680

37-
try {
38-
if (!fs.existsSync(codacyYamlPath)) {
39-
await execAsync(
40-
`codacy-cli init --api-token ${apiToken} --provider ${provider} --organization ${organization} --repository ${repositoryName}`
41-
)
42-
}
43-
await execAsync('codacy-cli install')
44-
} catch (error) {
45-
if (error instanceof Error) {
46-
throw new Error(`Failed to initialize Codacy CLI: ${error.message}`)
47-
}
48-
throw error
81+
if (!fs.existsSync(codacyYamlPath)) {
82+
await execAsync(`${CLI_COMMAND} init ${apiToken} ${repositoryAccess}`)
4983
}
50-
}
5184

52-
export async function installCodacyCLI(repository: Repository): Promise<void> {
53-
const platform = os.platform()
85+
await execAsync(`${CLI_COMMAND} install`)
5486

55-
if (await isCLIInstalled()) {
56-
await initializeCLI(repository)
57-
return
87+
// add cli.sh to .gitignore
88+
const gitignorePath = path.join(workspacePath, '.codacy', '.gitignore')
89+
if (!fs.existsSync(gitignorePath)) {
90+
fs.writeFileSync(gitignorePath, '*.sh\n')
91+
} else {
92+
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8')
93+
if (!gitignoreContent.includes('*.sh')) {
94+
fs.appendFileSync(gitignorePath, '*.sh\n')
95+
}
5896
}
97+
}
5998

99+
export async function installCodacyCLI(repository?: Repository): Promise<void> {
60100
try {
61-
switch (platform) {
62-
case 'darwin':
63-
if (!(await isBrewInstalled())) {
64-
throw new Error('Please install Homebrew first and then try installing the Codacy CLI again.')
65-
}
66-
await execAsync('brew install codacy/codacy-cli-v2/codacy-cli-v2')
67-
break
68-
69-
case 'linux':
70-
throw new Error(
71-
'Codacy CLI cannot be automatically installed on Linux yet. For manual installation, please refer to the [Codacy CLI documentation](https://github.com/codacy/codacy-cli-v2).'
72-
)
73-
break
74-
75-
case 'win32':
76-
throw new Error('Codacy CLI is not supported on Windows yet.')
101+
const isInstalled = await isCLIInstalled()
77102

78-
default:
79-
throw new Error(`Unsupported operating system: ${platform}`)
103+
if (!isInstalled) {
104+
await downloadCodacyCLI()
80105
}
81106

82107
await initializeCLI(repository)
83108
} catch (error) {
84109
if (error instanceof Error) {
85-
throw new Error(`Failed to install Codacy CLI: ${error.message}`)
110+
Logger.error(`Failed to install Codacy CLI: ${error.message}`)
86111
}
87112
throw error
88113
}

src/commands/runCodacyAnalyze.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export async function runCodacyAnalyze(filePath?: string) {
2727
const relativeFilePath = sanitizeFilePath(workspaceRoot, filePath)
2828

2929
// Construct the command
30-
const command = `CODACY_CLI_V2_VERSION=1.0.0-main.232.a6a6368 codacy-cli analyze --format sarif ${
30+
const command = `CODACY_CLI_V2_VERSION=1.0.0-main.232.a6a6368 .codacy/cli.sh analyze --format sarif ${
3131
relativeFilePath || ''
3232
}`
3333

src/extension.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,9 @@ export async function activate(context: vscode.ExtensionContext) {
204204
},
205205
async () => {
206206
try {
207-
const repository = repositoryManager.repository
208-
if (repository) {
209-
await installCodacyCLI(repository)
210-
await updateCLIState()
211-
vscode.window.showInformationMessage('Codacy CLI installed successfully!')
212-
} else {
213-
throw new Error('No repository found')
214-
}
207+
await installCodacyCLI(repositoryManager.repository)
208+
await updateCLIState()
209+
vscode.window.showInformationMessage('Codacy CLI installed successfully!')
215210
} catch (error) {
216211
vscode.window.showErrorMessage(
217212
`Failed to install Codacy CLI: ${error instanceof Error ? error.message : 'Unknown error'}`

0 commit comments

Comments
 (0)