Skip to content

Commit 25e8c56

Browse files
committed
[PLUTO-1396] Add new command, dont update on all others
1 parent 054c857 commit 25e8c56

File tree

4 files changed

+263
-10
lines changed

4 files changed

+263
-10
lines changed

cli-v2.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ func main() {
2323
}
2424
}
2525

26-
// Check if command is init
27-
if len(os.Args) > 1 && os.Args[1] == "init" {
26+
// Check if command is init/use
27+
if len(os.Args) > 1 && (os.Args[1] == "init" || os.Args[1] == "use") {
2828
cmd.Execute()
2929
return
3030
}

cmd/root.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"os"
67
"path/filepath"
@@ -15,6 +16,12 @@ var rootCmd = &cobra.Command{
1516
Long: "",
1617
Example: getExampleText(),
1718
Run: func(cmd *cobra.Command, args []string) {
19+
// Check version flag
20+
if showVersion, _ := cmd.Flags().GetBool("version"); showVersion {
21+
fmt.Println(getInstalledVersion())
22+
os.Exit(0)
23+
}
24+
1825
// Check if .codacy directory exists
1926
if _, err := os.Stat(".codacy"); os.IsNotExist(err) {
2027
// Show welcome message if .codacy doesn't exist
@@ -34,6 +41,33 @@ func Execute() {
3441
}
3542
}
3643

44+
func getInstalledVersion() string {
45+
// Get the current executable path
46+
executable, err := os.Executable()
47+
if err != nil {
48+
return "unknown"
49+
}
50+
51+
// Get the directory containing the executable
52+
binDir := filepath.Dir(executable)
53+
versionFile := filepath.Join(binDir, "version.json")
54+
55+
// Read the version file
56+
data, err := os.ReadFile(versionFile)
57+
if err != nil {
58+
return "unknown"
59+
}
60+
61+
var versionInfo struct {
62+
Version string `json:"version"`
63+
}
64+
if err := json.Unmarshal(data, &versionInfo); err != nil {
65+
return "unknown"
66+
}
67+
68+
return versionInfo.Version
69+
}
70+
3771
func showWelcomeMessage() {
3872
bold := color.New(color.Bold)
3973
cyan := color.New(color.FgCyan)
@@ -69,12 +103,17 @@ func getExampleText() string {
69103
color.New(color.FgCyan).Sprint("Run analysis and output in SARIF format:") + "\n" +
70104
color.New(color.FgGreen).Sprint(" codacy-cli analyze --tool eslint --format sarif") + "\n\n" +
71105
color.New(color.FgCyan).Sprint("Upload results to Codacy:") + "\n" +
72-
color.New(color.FgGreen).Sprint(" codacy-cli upload -s results.sarif -c <commit-uuid> -t <project-token>")
106+
color.New(color.FgGreen).Sprint(" codacy-cli upload -s results.sarif -c <commit-uuid> -t <project-token>") + "\n\n" +
107+
color.New(color.FgCyan).Sprint("Switch to a specific version:") + "\n" +
108+
color.New(color.FgGreen).Sprint(" codacy-cli use -v 1.0.0") + "\n\n" +
109+
color.New(color.FgCyan).Sprint("Switch to the latest version:") + "\n" +
110+
color.New(color.FgGreen).Sprint(" codacy-cli use -v latest")
73111
}
74112

75113
func init() {
76114
// Add global flags here
77115
rootCmd.PersistentFlags().String("config", filepath.Join(".codacy", "codacy.yaml"), "config file")
116+
rootCmd.Flags().Bool("version", false, "Print the version number")
78117

79118
// Customize help template
80119
rootCmd.SetUsageTemplate(`

cmd/use.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package cmd
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"os"
9+
"os/exec"
10+
"path/filepath"
11+
"runtime"
12+
13+
"github.com/spf13/cobra"
14+
)
15+
16+
var version string
17+
18+
func init() {
19+
useCmd.Flags().StringVarP(&version, "version", "v", "", "Specific version to use (e.g. 1.0.0) or 'latest' to get the latest version")
20+
useCmd.MarkFlagRequired("version")
21+
rootCmd.AddCommand(useCmd)
22+
}
23+
24+
var useCmd = &cobra.Command{
25+
Use: "use",
26+
Short: "Switch to a specific version",
27+
Long: `Switch to a specific version of the CLI. The command will:
28+
1. Use the version if it's already installed locally
29+
2. If not, download and install the specified version
30+
3. Switch to the specified version
31+
32+
Use 'latest' to get the latest version.`,
33+
Run: func(cmd *cobra.Command, args []string) {
34+
// Check if CODACY_CLI_V2_VERSION is set
35+
if envVersion := os.Getenv("CODACY_CLI_V2_VERSION"); envVersion != "" {
36+
fmt.Printf("CODACY_CLI_V2_VERSION is set to '%s'\n", envVersion)
37+
fmt.Printf("Overwrite this version with `%s' ? (y/N): ", version)
38+
39+
var response string
40+
fmt.Scanln(&response)
41+
if response != "y" && response != "Y" {
42+
fmt.Println("Operation cancelled")
43+
os.Exit(0)
44+
}
45+
}
46+
47+
// If version is "latest", get the latest version
48+
if version == "latest" {
49+
fmt.Println("Checking for latest version...")
50+
latestVersion, err := getLatestVersion()
51+
if err != nil {
52+
fmt.Printf("Failed to get latest version: %v\n", err)
53+
os.Exit(1)
54+
}
55+
version = latestVersion
56+
}
57+
58+
// Get the cache directory based on OS
59+
var cacheDir string
60+
switch runtime.GOOS {
61+
case "linux":
62+
cacheDir = filepath.Join(os.Getenv("HOME"), ".cache", "codacy", "codacy-cli-v2")
63+
case "darwin":
64+
cacheDir = filepath.Join(os.Getenv("HOME"), "Library", "Caches", "Codacy", "codacy-cli-v2")
65+
default:
66+
cacheDir = ".codacy-cli-v2"
67+
}
68+
69+
// Check if version is already installed
70+
versionDir := filepath.Join(cacheDir, version)
71+
cachedBinary := filepath.Join(versionDir, "codacy-cli-v2")
72+
if _, err := os.Stat(cachedBinary); err == nil {
73+
fmt.Printf("Version %s is already installed locally\n", version)
74+
} else {
75+
// Create version-specific directory
76+
if err := os.MkdirAll(versionDir, 0755); err != nil {
77+
fmt.Printf("Failed to create cache directory: %v\n", err)
78+
os.Exit(1)
79+
}
80+
81+
// Download and extract the specified version to cache
82+
fmt.Printf("Downloading version %s...\n", version)
83+
if err := downloadAndExtract(version, versionDir); err != nil {
84+
fmt.Printf("Failed to download and extract version %s: %v\n", version, err)
85+
os.Exit(1)
86+
}
87+
}
88+
89+
// Get the current executable path
90+
executable, err := os.Executable()
91+
if err != nil {
92+
fmt.Printf("Failed to get executable path: %v\n", err)
93+
os.Exit(1)
94+
}
95+
96+
// Copy the binary from cache to the executable location
97+
if err := copyFile(cachedBinary, executable); err != nil {
98+
fmt.Printf("Failed to copy binary: %v\n", err)
99+
os.Exit(1)
100+
}
101+
102+
fmt.Printf("Successfully switched to version %s\n", version)
103+
},
104+
}
105+
106+
func getLatestVersion() (string, error) {
107+
// Get the latest version from GitHub
108+
resp, err := http.Get("https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest")
109+
if err != nil {
110+
return "", fmt.Errorf("failed to get latest version: %w", err)
111+
}
112+
defer resp.Body.Close()
113+
114+
if resp.StatusCode != http.StatusOK {
115+
return "", fmt.Errorf("failed to get latest version: status code %d", resp.StatusCode)
116+
}
117+
118+
var release struct {
119+
TagName string `json:"tag_name"`
120+
}
121+
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
122+
return "", fmt.Errorf("failed to decode response: %w", err)
123+
}
124+
125+
return release.TagName, nil
126+
}
127+
128+
func downloadAndExtract(version string, targetDir string) error {
129+
// Construct the download URL
130+
osName := runtime.GOOS
131+
arch := runtime.GOARCH
132+
if arch == "amd64" {
133+
arch = "x86_64"
134+
}
135+
136+
fileName := fmt.Sprintf("codacy-cli-v2_%s_%s_%s.tar.gz", version, osName, arch)
137+
url := fmt.Sprintf("https://github.com/codacy/codacy-cli-v2/releases/download/%s/%s", version, fileName)
138+
139+
// Download the file directly to the target directory
140+
resp, err := http.Get(url)
141+
if err != nil {
142+
return fmt.Errorf("failed to download file: %w", err)
143+
}
144+
defer resp.Body.Close()
145+
146+
if resp.StatusCode != http.StatusOK {
147+
return fmt.Errorf("failed to download file: status code %d", resp.StatusCode)
148+
}
149+
150+
// Create the tar.gz file in the target directory
151+
tarFile := filepath.Join(targetDir, fileName)
152+
out, err := os.Create(tarFile)
153+
if err != nil {
154+
return fmt.Errorf("failed to create file: %w", err)
155+
}
156+
defer out.Close()
157+
158+
// Write the downloaded content to the file
159+
if _, err := io.Copy(out, resp.Body); err != nil {
160+
return fmt.Errorf("failed to write to file: %w", err)
161+
}
162+
163+
// Extract the tar.gz file
164+
cmd := exec.Command("tar", "xzf", tarFile, "-C", targetDir)
165+
if err := cmd.Run(); err != nil {
166+
return fmt.Errorf("failed to extract file: %w", err)
167+
}
168+
169+
// Remove the tar.gz file after extraction
170+
if err := os.Remove(tarFile); err != nil {
171+
return fmt.Errorf("failed to remove tar file: %w", err)
172+
}
173+
174+
return nil
175+
}
176+
177+
func copyFile(src, dst string) error {
178+
srcFile, err := os.Open(src)
179+
if err != nil {
180+
return fmt.Errorf("failed to open source file: %w", err)
181+
}
182+
defer srcFile.Close()
183+
184+
dstFile, err := os.Create(dst)
185+
if err != nil {
186+
return fmt.Errorf("failed to create destination file: %w", err)
187+
}
188+
defer dstFile.Close()
189+
190+
if _, err := io.Copy(dstFile, srcFile); err != nil {
191+
return fmt.Errorf("failed to copy file: %w", err)
192+
}
193+
194+
return nil
195+
}

codacy-cli.sh

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@ case "$arch" in
1414
;;
1515
esac
1616

17+
18+
handle_rate_limit() {
19+
local response="$1"
20+
if echo "$response" | grep -q "API rate limit exceeded"; then
21+
fatal "Error: GitHub API rate limit exceeded. Please try again later"
22+
fi
23+
}
24+
25+
get_latest_version() {
26+
local response
27+
if [ -n "$GH_TOKEN" ]; then
28+
response=$(curl -Lq --header "Authorization: Bearer $GH_TOKEN" "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null)
29+
else
30+
response=$(curl -Lq "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null)
31+
fi
32+
33+
handle_rate_limit "$response"
34+
echo "$response" | grep -m 1 tag_name | cut -d'"' -f4
35+
}
36+
1737
download_file() {
1838
local url="$1"
1939

@@ -23,7 +43,7 @@ download_file() {
2343
elif command -v wget > /dev/null 2>&1; then
2444
wget "$url"
2545
else
26-
fatal "Could not find curl or wget, please install one."
46+
fatal "Error: Could not find curl or wget, please install one."
2747
fi
2848
}
2949

@@ -65,11 +85,10 @@ fi
6585

6686
# if no version is specified, we fetch the latest
6787
if [ -z "$CODACY_CLI_V2_VERSION" ]; then
68-
if [ -n "$GH_TOKEN" ]; then
69-
CODACY_CLI_V2_VERSION="$(curl -Lq --header "Authorization: Bearer $GH_TOKEN" "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null | grep -m 1 tag_name | cut -d'"' -f4)"
70-
else
71-
CODACY_CLI_V2_VERSION="$(curl -Lq "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null | grep -m 1 tag_name | cut -d'"' -f4)"
72-
fi
88+
CODACY_CLI_V2_VERSION=$(get_latest_version)
89+
if [ -z "$CODACY_CLI_V2_VERSION" ]; then
90+
fatal "Error: Failed to retrieve the latest version. Please try again later."
91+
fi
7392
fi
7493

7594
# Folder containing the binary
@@ -93,7 +112,7 @@ if [ -z "$run_command" ]; then
93112
fi
94113

95114
if [ "$#" -eq 1 ] && [ "$1" = "download" ]; then
96-
echo "$g" "Codacy cli v2 download succeeded";
115+
echo "Codacy cli v2 download succeeded"
97116
else
98117
eval "$run_command $*"
99118
fi

0 commit comments

Comments
 (0)