diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f32caac..3a0c253 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,21 +2,11 @@ name: Publish Packages on: push: - branches: master - paths: - - ".github/workflows/docker.yml" - - "**/*.go" tags: - 'v*.*.*' - release: - types: published jobs: packages: - if: | - github.actor != 'dependabot[bot]' && - github.actor != 'github-actions[bot]' && - github.actor != 'protected-auto-commits[bot]' name: Docker Images runs-on: ubuntu-latest permissions: @@ -24,7 +14,6 @@ jobs: packages: write attestations: write id-token: write - steps: - name: GitHub App Token uses: actions/create-github-app-token@v1 @@ -71,16 +60,9 @@ jobs: ${{ vars.DOCKER_USERNAME }}/automatedgo ghcr.io/${{ github.repository_owner }}/AutomatedGo tags: | - type=ref,event=branch - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - - name: Debug Metadata Outputs - run: | - echo "Tags extracted:" - echo "${{ steps.meta.outputs.tags }}" - echo "Labels extracted:" - echo "${{ steps.meta.outputs.labels }}" + type=semver,pattern={{version}},skip-sha + type=semver,pattern={{major}}.{{minor}},skip-sha + type=semver,pattern={{major}}.{{minor}}.{{patch}},skip-sha - name: Build & Push Docker Images id: push @@ -88,7 +70,7 @@ jobs: with: context: . file: ./Dockerfile - push: ${{ github.event_name != 'pull_request' && steps.meta.outputs.tags != '' }} + push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha @@ -118,8 +100,9 @@ jobs: needs: packages permissions: contents: read + packages: write steps: - - name: Checkout Code + - name: Checkout Repo uses: actions/checkout@v4 - name: Login to Docker Hub @@ -135,42 +118,36 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: List Docker Hub Tags + - name: List & Delete Old Docker Hub Tags run: | + echo "Fetching Docker Hub tags..." tags=$(curl -s "https://hub.docker.com/v2/repositories/${{ vars.DOCKER_USERNAME }}/automatedgo/tags" | jq -r '.results[].name') echo "Tags found in Docker Hub:" echo "$tags" - echo "TAGS=$tags" >> $GITHUB_ENV - - - name: Delete Old Docker Hub Tags - run: | - latest_tag=$(echo "${TAGS}" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -rV | head -n 1) - for tag in ${TAGS}; do - if [[ "$tag" != "$latest_tag" && "$tag" != "master" ]]; then - echo "Deleting tag $tag from Docker Hub" - curl -X DELETE "https://hub.docker.com/v2/repositories/${{ vars.DOCKER_USERNAME }}/automatedgo/tags/$tag/" \ - -u "${{ vars.DOCKER_USERNAME }}:${{ secrets.DOCKER_TOKEN }}" - fi + latest_tag=$(echo "$tags" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -rV | head -n 1) + echo "Latest semantic version tag is $latest_tag" + for tag in $tags; do + if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ || "$tag" != "$latest_tag" ]]; then + echo "Deleting tag $tag from Docker Hub" + curl -X DELETE "https://hub.docker.com/v2/repositories/${{ vars.DOCKER_USERNAME }}/automatedgo/tags/$tag/" \ + -u "${{ vars.DOCKER_USERNAME }}:${{ secrets.DOCKER_TOKEN }}" + fi done - - name: List GHCR Tags + - name: Delete Old GHCR Tags env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + echo "Fetching GHCR tags..." tags=$(gh api "repos/${{ github.repository_owner }}/packages/container/automatedgo/versions" | jq -r '.[].metadata.container.tags[]') echo "Tags found in GHCR:" echo "$tags" - echo "GHCR_TAGS=$tags" >> $GITHUB_ENV - - - name: Delete Old GHCR Tags - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - latest_tag=$(echo "${GHCR_TAGS}" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -rV | head -n 1) - for tag in ${GHCR_TAGS}; do - if [[ "$tag" != "$latest_tag" && "$tag" != "master" ]]; then - echo "Deleting tag $tag from GHCR" - version_id=$(gh api "repos/${{ github.repository_owner }}/packages/container/automatedgo/versions" | jq -r ".[] | select(.metadata.container.tags[] == \"$tag\") | .id") - gh api --method DELETE "/user/packages/container/automatedgo/versions/$version_id" - fi + latest_tag=$(echo "$tags" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -rV | head -n 1) + echo "Latest semantic version tag is $latest_tag" + for tag in $tags; do + if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ || "$tag" != "$latest_tag" ]]; then + echo "Deleting tag $tag from GHCR" + version_id=$(gh api "repos/${{ github.repository_owner }}/packages/container/automatedgo/versions" | jq -r ".[] | select(.metadata.container.tags[] == \"$tag\") | .id") + gh api --method DELETE "/user/packages/container/automatedgo/versions/$version_id" + fi done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 682d59b..c6cdda8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22.x" + go-version: "1.23.x" - name: Semantic Release uses: go-semantic-release/action@v1 @@ -83,7 +83,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22.x" + go-version: "1.23.x" - name: Run GoReleaser uses: goreleaser/goreleaser-action@v6 diff --git a/.gitignore b/.gitignore index 8982f8f..e0d095d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ go.work go.work.sum dist/ + +# Environment Variables +.env +env diff --git a/Dockerfile b/Dockerfile index ceac36b..24a1b4b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,9 @@ # Use the official Golang image FROM golang:1.23.1-alpine +# Set author metadata using labels +LABEL org.opencontainers.image.authors="https://github.com/Nicconike" + # Set the Current Working Directory inside the container WORKDIR /automatedgo diff --git a/README.md b/README.md index 20095db..215a4dd 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ go install github.com/Nicconike/AutomatedGo/cmd/automatedgo@v2.0.1 ### Basic Usage ```sh -AutomatedGo -file -os -arch +automatedgo -file -os -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. @@ -62,25 +62,25 @@ This will check the specified file for the current Go version, compare it with t 1. Get version from a Dockerfile: ```sh - AutomatedGo -f Dockerfile + automatedgo -f Dockerfile ``` ![Dockerfile Example](https://github.com/Nicconike/AutomatedGo/blob/master/assets/dockerfile_example.png) 2. Get version from go.mod: ```sh - AutomatedGo -f go.mod + automatedgo -f go.mod ``` ![Go Mod Example](https://github.com/Nicconike/AutomatedGo/blob/master/assets/gomod_example.png) 3. Specify version directly: ```sh - AutomatedGo -v 1.17 + automatedgo -v 1.17 ``` ![Direct Example](https://github.com/Nicconike/AutomatedGo/blob/master/assets/direct_example.png) 4. Download for a specific OS and architecture: ```sh - AutomatedGo -f version.json -os linux -arch arm64 + automatedgo -f version.json -os linux -arch arm64 ``` ![JSON Example with OS](https://github.com/Nicconike/AutomatedGo/blob/master/assets/json_example_os_arch.png) diff --git a/doc.go b/docs/doc.go similarity index 100% rename from doc.go rename to docs/doc.go diff --git a/pkg/downloader.go b/pkg/downloader.go index 01ae44e..133c40e 100644 --- a/pkg/downloader.go +++ b/pkg/downloader.go @@ -112,13 +112,15 @@ func DownloadGo(version, targetOS, arch, path string, downloader FileDownloader, extension = "zip" } - filename := fmt.Sprintf("%s/go%s.%s-%s.%s", path, version, targetOS, arch, extension) + filename := fmt.Sprintf("go%s.%s-%s.%s", version, targetOS, arch, extension) fmt.Printf("Fetching Official Checksum for %s\n", filename) officialChecksum, err := checksum.GetOfficialChecksum(filename) if err != nil { fmt.Printf("Failed to get official checksum: %s\n", err) return err + } else { + fmt.Printf("Successfully fetched official checksum: %s\n", officialChecksum) } url := fmt.Sprintf(DownloadURLFormat, version, targetOS, arch, extension) diff --git a/pkg/run.go b/pkg/run.go index b0c9a5e..3d45d5b 100644 --- a/pkg/run.go +++ b/pkg/run.go @@ -9,71 +9,78 @@ import ( ) func confirmDownload(input io.Reader, output io.Writer) bool { - reader := bufio.NewReader(input) - fmt.Fprint(output, "Do you want to download the latest version? (yes/no): ") - response, _ := reader.ReadString('\n') - response = strings.TrimSpace(strings.ToLower(response)) - return response == "yes" + reader := bufio.NewReader(input) + fmt.Fprint(output, "Do you want to download the latest version? (yes/no): ") + response, _ := reader.ReadString('\n') + response = strings.TrimSpace(strings.ToLower(response)) + return response == "yes" } func getDownloadPath(input io.Reader, output io.Writer) string { - reader := bufio.NewReader(input) - fmt.Fprint(output, "Do you want to download it to the current working directory? (yes/no): ") - locationChoice, _ := reader.ReadString('\n') - locationChoice = strings.TrimSpace(strings.ToLower(locationChoice)) + 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): ") + pathChoice, _ := reader.ReadString('\n') + pathChoice = strings.TrimSpace(pathChoice) - if locationChoice == "no" { - fmt.Fprint(output, "Enter the path where you want to download the file: ") - pathChoice, _ := reader.ReadString('\n') - pathChoice = strings.TrimSpace(pathChoice) + if pathChoice == "cancel" { + return "" + } - if _, err := os.Stat(pathChoice); os.IsNotExist(err) { - return "" - } + if pathChoice == "" { + currentDir, err := os.Getwd() + if err != nil { + fmt.Fprintln(output, "Error getting current directory:", err) + continue + } + fmt.Fprintf(output, "Using current directory: %s\n", currentDir) + return currentDir + } - return pathChoice - } + if _, err := os.Stat(pathChoice); os.IsNotExist(err) { + fmt.Fprintln(output, "Specified path does not exist. Please try again.") + continue + } - currentDir, err := os.Getwd() - if err != nil { - return "" - } - return currentDir + return pathChoice + } } func Run(service VersionChecker, versionFile, currentVersion, targetOS, targetArch string, input io.Reader, output io.Writer) error { - if versionFile == "" && currentVersion == "" { - return fmt.Errorf("error: Either -file (-f) or -version (-v) must be specified") - } + if versionFile == "" && currentVersion == "" { + return fmt.Errorf("error: Either -file (-f) or -version (-v) must be specified") + } - cv, err := service.GetCurrentVersion(versionFile, currentVersion) - if err != nil { - return fmt.Errorf("error getting current version: %v", err) - } - fmt.Fprintf(output, "Current version: %s\n", cv) + cv, err := service.GetCurrentVersion(versionFile, currentVersion) + if err != nil { + return fmt.Errorf("error getting current version: %v", err) + } + fmt.Fprintf(output, "Current version: %s\n", cv) - latestVersion, err := service.GetLatestVersion() - if err != nil { - return fmt.Errorf("error checking latest version: %v", err) - } - fmt.Fprintf(output, "Latest version: %s\n", latestVersion) + latestVersion, err := service.GetLatestVersion() + if err != nil { + return fmt.Errorf("error checking latest version: %v", err) + } + fmt.Fprintf(output, "Latest version: %s\n", latestVersion) - if service.IsNewer(latestVersion, cv) { - fmt.Fprintln(output, "A newer version is available") - if confirmDownload(input, output) { - downloadPath := getDownloadPath(input, output) - if downloadPath == "" { - return fmt.Errorf("specified path does not exist") - } - err := service.DownloadGo(latestVersion, targetOS, targetArch, downloadPath) - if err != nil { - return fmt.Errorf("error downloading Go: %v", err) - } - } else { - fmt.Fprintln(output, "Download aborted by user") - } - } else { - fmt.Fprintln(output, "You have the latest version") - } - return nil + if service.IsNewer(latestVersion, cv) { + fmt.Fprintln(output, "A newer version is available") + if confirmDownload(input, output) { + downloadPath := getDownloadPath(input, output) + if downloadPath == "" { + fmt.Fprintln(output, "Download cancelled by user") + return nil + } + err := service.DownloadGo(latestVersion, targetOS, targetArch, downloadPath) + if err != nil { + return fmt.Errorf("error downloading Go: %v", err) + } + fmt.Fprintf(output, "%s has been downloaded to %s\n", latestVersion, downloadPath) + } else { + fmt.Fprintln(output, "Download aborted by user") + } + } else { + fmt.Fprintln(output, "You have the latest version") + } + return nil }