Skip to content

Commit

Permalink
test: improve code coverage
Browse files Browse the repository at this point in the history
refactor: refactor DownloadGo function
docs: update documentation
chore: upgrade to go1.23
  • Loading branch information
Nicconike committed Oct 17, 2024
1 parent 96f47d2 commit 3b62e11
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 113 deletions.
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ linters:
- gocyclo
- ineffassign
- misspell
linters-settings:
gofmt:
simplify: true
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ automatedgo -file <path-to-file> -os <target-os> -arch <target-arch>
This will check the specified file for the current Go version, compare it with the latest available version, and download the new version if an update is available.

> [!NOTE]
> If you don't specify the `os` and `arch` type, the tool will download the latest version for your current operating system and architecture.
> If you don't specify the `os` and `arch` type, the tool will download the latest version by detecting your current operating system and architecture.
### Command-line Options

Expand All @@ -57,25 +57,25 @@ This will check the specified file for the current Go version, compare it with t

### Examples

1. Get version from a Dockerfile:
1. Download latest version from Dockerfile:
```sh
automatedgo -f Dockerfile
```
![Dockerfile Example](https://github.com/Nicconike/AutomatedGo/blob/master/assets/dockerfile_example.png)

2. Get version from go.mod:
2. Download latest version from go.mod:
```sh
automatedgo -f go.mod
```
![Go Mod Example](https://github.com/Nicconike/AutomatedGo/blob/master/assets/gomod_example.png)

3. Specify version directly:
3. Download latest version by just specifying version directly as argument:
```sh
automatedgo -v 1.18
```
![Direct Example](https://github.com/Nicconike/AutomatedGo/blob/master/assets/direct_example.png)

4. Download for a specific OS and architecture:
4. Download latest version from JSON for a specific OS and architecture:
```sh
automatedgo -f version.json -os linux -arch arm64
```
Expand All @@ -98,7 +98,7 @@ Missing any file types you expected to see? Let me know via [discussions](https:

## Contributing

Star⭐ and Fork🍴 the Repo to start with your feature request(or bug) and experiment with the project to implement whatever Idea you might have and sent the Pull Request through 🤙
Star⭐ and Fork🍴 the repo to support and start with your feature request(or bug) and experiment with the project to implement whatever new idea you might have and send the pull request through 🤙

Please refer [Contributing.md](https://github.com/Nicconike/AutomatedGo/blob/master/.github/CONTRIBUTING.md) to get to know how to contribute to this project.
And thank you for considering to contribute.
Expand Down
129 changes: 60 additions & 69 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,97 +1,88 @@
/*
Package AutomatedGo provides tools for automating Go version checks and downloads.
**AutomatedGo Package**
AutomatedGo provides tools for automated Go version management and updates.
Features:
- Detect current Go version from various file types (Dockerfile, go.mod, JSON configs, etc.)
- Get the current Go version from a specified file or input
- Check for the latest available Go version
- Download the latest Go version if an update is available
- Support for different operating systems and architectures
- Compare Go versions to determine if an update is available
- Download the latest Go version for different operating systems and architectures
- Checksum validation for downloaded Go versions to ensure integrity
Do's:
1. Always check for errors returned by functions in this package.
2. Use GetLatestVersion() to fetch the most recent Go version information.
3. Specify the correct target OS and architecture when using DownloadGo().
4. Ensure you have necessary permissions to download and write files.
5. Define Go version in your project files using one of these formats:
- In go.mod: go 1.x
- In Dockerfile:
* FROM golang:1.x.x
* ENV GO_VERSION=1.x.x
- In other files:
* go_version = "1.x.x"
* GO_VERSION: 1.x.x
* golang_version: "1.x.x"
6. Use the package to automate version checks in your CI/CD pipelines.
7. Verify checksums of downloaded Go versions for security.
Usage Example:
Don'ts:
package main
1. Don't assume the latest version is always compatible with your project.
2. Avoid using this package to modify your system's Go installation directly.
3. Don't use this package in production environments without thorough testing.
4. Don't ignore version constraints specified in your go.mod file.
5. Avoid manually modifying files downloaded by this package.
6. Don't use non-standard formats for specifying Go versions in your project files.
import (
Example usage:
"fmt"
"log"
// Get the latest Go version
latestVersion, err := pkg.GetLatestVersion()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Latest Go version: %s\n", latestVersion)
"github.com/Nicconike/AutomatedGo/v2/pkg"
// Get current version from a file
currentVersion, err := pkg.GetCurrentVersion("go.mod", "")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Current Go version: %s\n", currentVersion)
)
// Check if update is needed
if pkg.IsNewer(latestVersion, currentVersion) {
fmt.Println("An update is available")
func main() {
// Create a new VersionService
service := pkg.NewVersionService(
&pkg.DefaultDownloader{},
&pkg.DefaultRemover{},
&pkg.DefaultChecksumCalculator{},
)
// Download the latest Go version
err = pkg.DownloadGo(latestVersion, "linux", "amd64")
// Get the current Go version
currentVersion, err := service.GetCurrentVersion("", "")
if err != nil {
log.Fatal(err)
log.Fatalf("Error getting current version: %v", err)
}
fmt.Println("Successfully downloaded new Go version")
fmt.Printf("Current Go version: %s\n", currentVersion)
// Verify checksum
filename := fmt.Sprintf("go%s.linux-amd64.tar.gz", latestVersion)
checksum, err := pkg.CalculateFileChecksum(filename)
// Check for the latest Go version
latestVersion, err := service.GetLatestVersion()
if err != nil {
log.Fatal(err)
log.Fatalf("Error getting latest version: %v", err)
}
fmt.Printf("Latest Go version: %s\n", latestVersion)
// Check if an update is available
if service.IsNewer(latestVersion, currentVersion) {
fmt.Println("An update is available!")
// Download the latest version
err = service.DownloadGo(latestVersion, "", "", "/tmp", nil, nil)
if err != nil {
log.Fatalf("Error downloading Go: %v", err)
}
fmt.Printf("Successfully downloaded Go %s to /tmp\n", latestVersion)
} else {
fmt.Println("You have the latest version of Go.")
}
fmt.Printf("Checksum of downloaded file: %s\n", checksum)
} else {
fmt.Println("You have the latest version")
}
Functions:
- GetLatestVersion() (string, error)
Fetches the latest available Go version.
This example demonstrates how to use the AutomatedGo package to check for updates,
compare versions, and download the latest version of Go if an update is available.
- GetCurrentVersion(filename, version string) (string, error)
Detects the current Go version from a file or uses the provided version.
Do's:
- IsNewer(version1, version2 string) bool
Compares two version strings and returns true if version1 is newer.
1. Always check for errors returned by functions in this package.
2. Use GetLatestVersion() to fetch the most recent Go version information.
3. Use GetCurrentVersion() with appropriate parameters to determine the current version.
4. Specify the correct target OS and architecture when using DownloadGo() if needed.
5. Ensure you have necessary permissions to download and write files.
6. Use IsNewer() to compare versions and determine if an update is needed.
7. Use the package to automate version checks in your CI/CD pipelines.
- DownloadGo(version, targetOS, arch string) error
Downloads the specified Go version for the given OS and architecture.
Don'ts:
- CalculateFileChecksum(filename string) (string, error)
Calculates the SHA256 checksum of the specified file.
1. Don't assume the latest version is always compatible with your project.
2. Avoid using this package to modify your system's Go installation directly.
3. Don't use this package in production environments without thorough testing.
4. Don't ignore version constraints specified in your go.mod file.
5. Avoid manually modifying files downloaded by this package.
For more detailed information and advanced usage, refer to the README.md file
and the package documentation at https://pkg.go.dev/github.com/Nicconike/AutomatedGo/v2.
Note: The package allows flexible version checking and downloading. You can provide
specific version information or let the package determine versions automatically.
*/
package AutomatedGo
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/Nicconike/AutomatedGo/v2

go 1.22
go 1.23

require (
github.com/schollz/progressbar/v3 v3.16.1
Expand Down
113 changes: 80 additions & 33 deletions pkg/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,69 +81,116 @@ type DownloadConfig struct {
Output io.Writer
}

func DownloadGo(config DownloadConfig) error {
version := strings.TrimPrefix(config.Version, "go")
fmt.Fprintf(config.Output, "Preparing to download Go version %s\n", version)

func promptForTargetOS(config *DownloadConfig) error {
if config.TargetOS == "" {
fmt.Fprint(config.Output, "Enter target OS (windows, linux, darwin): ")
fmt.Fscan(config.Input, &config.TargetOS)
}

validArchs, ok := validPlatforms[config.TargetOS]
if !ok {
return fmt.Errorf("unsupported operating system: %s", config.TargetOS)
if _, err := fmt.Fscan(config.Input, &config.TargetOS); err != nil {
return fmt.Errorf("failed to read target OS: %w", err)
}
}
return nil
}

func promptForArchitecture(config *DownloadConfig, validArchs []string) error {
if config.Arch == "" {
fmt.Fprintf(config.Output, "Enter target architecture %v: ", validArchs)
fmt.Fscan(config.Input, &config.Arch)
if _, err := fmt.Fscan(config.Input, &config.Arch); err != nil {
return fmt.Errorf("failed to read architecture: %w", err)
}
}
return nil
}

valid := false
func isValidArchitecture(arch string, validArchs []string) bool {
for _, validArch := range validArchs {
if config.Arch == validArch {
valid = true
break
if arch == validArch {
return true
}
}
if !valid {
return fmt.Errorf("unsupported architecture %s for OS %s", config.Arch, config.TargetOS)
}
return false
}

extension := "tar.gz"
if config.TargetOS == "windows" {
extension = "zip"
func getExtension(targetOS string) string {
if targetOS == "windows" {
return "zip"
}
return "tar.gz"
}

filename := fmt.Sprintf("go%s.%s-%s.%s", version, config.TargetOS, config.Arch, extension)
fmt.Fprintf(config.Output, "Fetching Official Checksum for %s\n", filename)
func getFilename(version string, config DownloadConfig) string {
return fmt.Sprintf("go%s.%s-%s.%s", version, config.TargetOS, config.Arch, getExtension(config.TargetOS))
}

officialChecksum, err := config.Checksum.GetOfficialChecksum(filename)
func fetchOfficialChecksum(config DownloadConfig, filename string) (string, error) {
checksum, err := config.Checksum.GetOfficialChecksum(filename)
if err != nil {
fmt.Fprintf(config.Output, "Failed to get official checksum: %s\n", err)
return err
return "", err
}
fmt.Fprintf(config.Output, "Successfully fetched official checksum: %s\n", officialChecksum)
fmt.Fprintf(config.Output, "Successfully fetched official checksum: %s\n", checksum)
return checksum, nil
}

url := fmt.Sprintf(DownloadURLFormat, version, config.TargetOS, config.Arch, extension)
err = config.Downloader.Download(url, filename)
func downloadFile(config DownloadConfig, url string, filename string) error {
err := config.Downloader.Download(url, filename)
if err != nil {
fmt.Fprintf(config.Output, "Error downloading file: %s\n", err)
return err
}
return err
}

func handleChecksumMismatch(config DownloadConfig, filename string) {
if removeErr := config.Remover.Remove(filename); removeErr != nil {
fmt.Fprintf(config.Output, "Error removing file %s after failed checksum calculation: %s\n", filename, removeErr)
}
}

func verifyChecksum(config DownloadConfig, filename string, officialChecksum string) error {
fmt.Fprintf(config.Output, "\nCalculating checksum for %s\n", filename)
calculatedChecksum, err := config.Checksum.Calculate(filename)
if err != nil || calculatedChecksum != officialChecksum {
if removeErr := config.Remover.Remove(filename); removeErr != nil {
fmt.Fprintf(config.Output, "Error removing file %s after failed checksum calculation: %s\n", filename, removeErr)
}
handleChecksumMismatch(config, filename)
errMsg := fmt.Sprintf("Checksum mismatch: expected %s, got %s for file %s", officialChecksum, calculatedChecksum, filename)
fmt.Fprintln(config.Output, errMsg)
return errors.New(errMsg)
}

fmt.Fprintln(config.Output, "Checksum verification successful!")
return nil
}

func DownloadGo(config DownloadConfig) error {
version := strings.TrimPrefix(config.Version, "go")
fmt.Fprintf(config.Output, "Preparing to download Go version %s\n", version)

if err := promptForTargetOS(&config); err != nil {
return err
}

validArchs, ok := validPlatforms[config.TargetOS]
if !ok {
return fmt.Errorf("unsupported operating system: %s", config.TargetOS)
}

if err := promptForArchitecture(&config, validArchs); err != nil {
return err
}

if !isValidArchitecture(config.Arch, validArchs) {
return fmt.Errorf("unsupported architecture %s for OS %s", config.Arch, config.TargetOS)
}

filename := getFilename(version, config)
fmt.Fprintf(config.Output, "Fetching Official Checksum for %s\n", filename)

officialChecksum, err := fetchOfficialChecksum(config, filename)
if err != nil {
return err
}

url := fmt.Sprintf(DownloadURLFormat, version, config.TargetOS, config.Arch, getExtension(config.TargetOS))
if err = downloadFile(config, url, filename); err != nil {
return err
}

return verifyChecksum(config, filename, officialChecksum)
}
4 changes: 2 additions & 2 deletions pkg/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func confirmDownload(input io.Reader, output io.Writer) bool {
return response == "yes"
}

func getDownloadPath(input io.Reader, output io.Writer) string {
func GetDownloadPath(input io.Reader, output io.Writer) string {
reader := bufio.NewReader(input)
for {
fmt.Fprint(output, "Enter the path where you want to download the file (press Enter for current directory, or 'cancel' to abort): ")
Expand Down Expand Up @@ -66,7 +66,7 @@ func Run(service VersionChecker, versionFile, currentVersion, targetOS, targetAr
if service.IsNewer(latestVersion, cv) {
fmt.Fprintln(output, "A newer version is available")
if confirmDownload(input, output) {
downloadPath := getDownloadPath(input, output)
downloadPath := GetDownloadPath(input, output)
if downloadPath == "" {
fmt.Fprintln(output, "Download cancelled by user")
return nil
Expand Down
Loading

0 comments on commit 3b62e11

Please sign in to comment.