diff --git a/.github/workflows/quay_binaries_push.yml b/.github/workflows/quay_binaries_push.yml index d1fe756..799798b 100644 --- a/.github/workflows/quay_binaries_push.yml +++ b/.github/workflows/quay_binaries_push.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - arch: [amd64, arm64, ppc64le, s390x] + arch: [amd64, arm64] steps: - name: Checkout code @@ -75,14 +75,10 @@ jobs: # Load all arch images and capture their IDs AMD64_ID=$(buildah pull oci-archive:oadp-cli-image-amd64/oadp-cli-amd64.tar) ARM64_ID=$(buildah pull oci-archive:oadp-cli-image-arm64/oadp-cli-arm64.tar) - PPC64LE_ID=$(buildah pull oci-archive:oadp-cli-image-ppc64le/oadp-cli-ppc64le.tar) - S390X_ID=$(buildah pull oci-archive:oadp-cli-image-s390x/oadp-cli-s390x.tar) # Tag the loaded images using their IDs buildah tag $AMD64_ID ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-amd64 buildah tag $ARM64_ID ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-arm64 - buildah tag $PPC64LE_ID ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-ppc64le - buildah tag $S390X_ID ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-s390x - name: Create and push multi-arch manifest (version tag) if: github.ref_name != 'oadp-dev' @@ -90,8 +86,6 @@ jobs: buildah manifest create ${{ env.IMAGE_REPO }}:${{ github.ref_name }} buildah manifest add ${{ env.IMAGE_REPO }}:${{ github.ref_name }} ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-amd64 buildah manifest add ${{ env.IMAGE_REPO }}:${{ github.ref_name }} ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-arm64 - buildah manifest add ${{ env.IMAGE_REPO }}:${{ github.ref_name }} ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-ppc64le - buildah manifest add ${{ env.IMAGE_REPO }}:${{ github.ref_name }} ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-s390x buildah manifest push --all ${{ env.IMAGE_REPO }}:${{ github.ref_name }} - name: Create and push multi-arch manifest (latest tag) @@ -100,6 +94,4 @@ jobs: buildah manifest create ${{ env.IMAGE_REPO }}:latest buildah manifest add ${{ env.IMAGE_REPO }}:latest ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-amd64 buildah manifest add ${{ env.IMAGE_REPO }}:latest ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-arm64 - buildah manifest add ${{ env.IMAGE_REPO }}:latest ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-ppc64le - buildah manifest add ${{ env.IMAGE_REPO }}:latest ${{ env.IMAGE_REPO }}:${{ github.ref_name }}-s390x buildah manifest push --all ${{ env.IMAGE_REPO }}:latest \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a0867a..f98ae8f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,7 +80,7 @@ jobs: 4. Verify installation: `kubectl oadp --help` ### Supported Platforms - - Linux (amd64, arm64, ppc64le, s390x) + - Linux (amd64, arm64) - macOS (amd64, arm64) - Windows (amd64, arm64) diff --git a/Containerfile.download b/Containerfile.download index 613636b..049922e 100644 --- a/Containerfile.download +++ b/Containerfile.download @@ -13,10 +13,8 @@ RUN go mod download && go mod verify COPY . . -RUN make release-archives && \ - mkdir -p /archives && \ - mv *.tar.gz *.sha256 /archives/ && \ - rm -rf /root/.cache/go-build /tmp/* release-build/ +RUN make release-binaries && \ + rm -rf /root/.cache/go-build /tmp/* # Build the download server for the TARGET platform (the arch this container will run on) RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o download-server ./cmd/downloads/ && \ @@ -25,8 +23,8 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o download-ser FROM registry.access.redhat.com/ubi9/ubi-minimal:latest -# Only copy compressed tarballs (much smaller than raw binaries) -COPY --from=builder /archives /archives +# Copy binaries, checksums, and LICENSE +COPY --from=builder /app/release-binaries /archives # Copy the download server COPY --from=builder /app/download-server /usr/local/bin/download-server diff --git a/Makefile b/Makefile index 517e265..94d3865 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ LDFLAGS := -X github.com/vmware-tanzu/velero/pkg/buildinfo.Version=$(VERSION) \ # Centralized platform definitions to avoid duplication # Matches architectures supported by Kubernetes: https://kubernetes.io/releases/download/#binaries -PLATFORMS = linux/amd64 linux/arm64 linux/ppc64le linux/s390x darwin/amd64 darwin/arm64 windows/amd64 windows/arm64 +PLATFORMS = linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64 # Platform variables for multi-arch builds # Usage: make build PLATFORM=linux/amd64 @@ -50,8 +50,6 @@ help: ## Show this help message @echo "Build with different platforms:" @echo " make build PLATFORM=linux/amd64" @echo " make build PLATFORM=linux/arm64" - @echo " make build PLATFORM=linux/ppc64le" - @echo " make build PLATFORM=linux/s390x" @echo " make build PLATFORM=darwin/amd64" @echo " make build PLATFORM=darwin/arm64" @echo " make build PLATFORM=windows/amd64" @@ -67,6 +65,7 @@ help: ## Show this help message @echo "Release commands:" @echo " make release-build # Build binaries for all platforms" @echo " make release-archives # Create tar.gz archives for all platforms" + @echo " make release-binaries # Create clean-named binaries for download server" # Build targets .PHONY: build @@ -514,6 +513,38 @@ release-archives: release-build ## Create tar.gz archives with SHA256 checksums release: release-archives ## Build and create release archives for all platforms @echo "🚀 Release build complete! Archives ready for distribution." +.PHONY: release-binaries +release-binaries: release-build ## Copy binaries with clean names and SHA256 checksums (for download server) + @echo "Creating clean-named binaries for download server..." + @if [ ! -f LICENSE ]; then \ + echo "❌ LICENSE file not found! Please ensure LICENSE file exists."; \ + exit 1; \ + fi + @mkdir -p release-binaries + @cp LICENSE release-binaries/LICENSE + @for platform in $(PLATFORMS); do \ + GOOS=$$(echo $$platform | cut -d'/' -f1); \ + GOARCH=$$(echo $$platform | cut -d'/' -f2); \ + if [ -n "$(VERSION)" ]; then \ + version_suffix="_$(VERSION)"; \ + else \ + version_suffix=""; \ + fi; \ + if [ "$$GOOS" = "windows" ]; then \ + platform_binary="$(BINARY_NAME)$${version_suffix}_$${GOOS}_$${GOARCH}.exe"; \ + clean_name="$(BINARY_NAME)_$${GOOS}_$${GOARCH}.exe"; \ + else \ + platform_binary="$(BINARY_NAME)$${version_suffix}_$${GOOS}_$${GOARCH}"; \ + clean_name="$(BINARY_NAME)_$${GOOS}_$${GOARCH}"; \ + fi; \ + echo "Copying $$clean_name..."; \ + cp $$platform_binary release-binaries/$$clean_name; \ + sha256sum release-binaries/$$clean_name > release-binaries/$$clean_name.sha256; \ + echo "✅ $$clean_name"; \ + done + @echo "✅ All binaries and checksums ready in release-binaries/" + @ls -la release-binaries/ + # Optimized krew-manifest generation using Python script for better reliability .PHONY: krew-manifest krew-manifest: release-archives ## Generate Krew plugin manifest with SHA256 checksums diff --git a/cmd/downloads/server.go b/cmd/downloads/server.go index b766f95..bf1b886 100644 --- a/cmd/downloads/server.go +++ b/cmd/downloads/server.go @@ -19,7 +19,7 @@ var templateFS embed.FS var staticFS embed.FS var ( - archiveDir = getEnv("ARCHIVE_DIR", "/archives") + binaryDir = getEnv("ARCHIVE_DIR", "/archives") port = getEnv("PORT", "8080") pageTemplate = template.Must(template.ParseFS(templateFS, "templates/index.html")) ) @@ -31,7 +31,7 @@ func getEnv(key, fallback string) string { return fallback } -type archiveFile struct { +type binaryFile struct { Name string Size float64 OS string @@ -40,11 +40,11 @@ type archiveFile struct { } func main() { - files, err := filepath.Glob(filepath.Join(archiveDir, "*.tar.gz")) + files, err := discoverBinaries() if err != nil || len(files) == 0 { - log.Fatal("No archives found in ", archiveDir) + log.Fatal("No binaries found in ", binaryDir) } - log.Printf("Found %d archives", len(files)) + log.Printf("Found %d binaries", len(files)) staticContent, err := fs.Sub(staticFS, "static") if err != nil { @@ -55,21 +55,45 @@ func main() { http.HandleFunc("/download/", downloadBinary) log.Printf("Starting server on port %s", port) - log.Printf("Serving archives from %s", archiveDir) + log.Printf("Serving binaries from %s", binaryDir) if err := http.ListenAndServe(":"+port, nil); err != nil { log.Fatal(err) } } +// discoverBinaries finds kubectl-oadp binaries (excluding .sha256 and LICENSE files). +func discoverBinaries() ([]string, error) { + entries, err := os.ReadDir(binaryDir) + if err != nil { + return nil, err + } + var binaries []string + for _, e := range entries { + name := e.Name() + if e.IsDir() || strings.HasSuffix(name, ".sha256") || name == "LICENSE" { + continue + } + if strings.HasPrefix(name, "kubectl-oadp_") { + binaries = append(binaries, filepath.Join(binaryDir, name)) + } + } + return binaries, nil +} + func listBinaries(w http.ResponseWriter, r *http.Request) { - files, err := filepath.Glob(filepath.Join(archiveDir, "*.tar.gz")) + files, err := discoverBinaries() if err != nil { - http.Error(w, "Error listing archives", http.StatusInternalServerError) + http.Error(w, "Error listing binaries", http.StatusInternalServerError) return } - var linuxFiles, darwinFiles, windowsFiles []archiveFile + hasLicense := false + if _, err := os.Stat(filepath.Join(binaryDir, "LICENSE")); err == nil { + hasLicense = true + } + + var linuxFiles, darwinFiles, windowsFiles []binaryFile for _, file := range files { name := filepath.Base(file) info, err := os.Stat(file) @@ -79,24 +103,25 @@ func listBinaries(w http.ResponseWriter, r *http.Request) { size := float64(info.Size()) / (1024 * 1024) osName, arch := parsePlatform(name) checksum := readChecksum(file + ".sha256") - af := archiveFile{Name: name, Size: size, OS: osName, Arch: arch, Checksum: checksum} + bf := binaryFile{Name: name, Size: size, OS: osName, Arch: arch, Checksum: checksum} switch osName { case "linux": - linuxFiles = append(linuxFiles, af) + linuxFiles = append(linuxFiles, bf) case "darwin": - darwinFiles = append(darwinFiles, af) + darwinFiles = append(darwinFiles, bf) case "windows": - windowsFiles = append(windowsFiles, af) + windowsFiles = append(windowsFiles, bf) default: - linuxFiles = append(linuxFiles, af) + linuxFiles = append(linuxFiles, bf) } } data := struct { - LinuxFiles []archiveFile - DarwinFiles []archiveFile - WindowsFiles []archiveFile - }{linuxFiles, darwinFiles, windowsFiles} + LinuxFiles []binaryFile + DarwinFiles []binaryFile + WindowsFiles []binaryFile + HasLicense bool + }{linuxFiles, darwinFiles, windowsFiles, hasLicense} w.Header().Set("Content-Type", "text/html") if err := pageTemplate.Execute(w, data); err != nil { @@ -117,7 +142,7 @@ func readChecksum(path string) string { } func parsePlatform(filename string) (string, string) { - name := strings.TrimSuffix(filename, ".tar.gz") + name := strings.TrimSuffix(filename, ".exe") parts := strings.Split(name, "_") if len(parts) >= 3 { return parts[len(parts)-2], parts[len(parts)-1] @@ -128,20 +153,30 @@ func parsePlatform(filename string) (string, string) { func downloadBinary(w http.ResponseWriter, r *http.Request) { filename := filepath.Base(r.URL.Path[len("/download/"):]) - if filepath.Dir(filename) != "." || !strings.HasSuffix(filename, ".tar.gz") { + if filepath.Dir(filename) != "." { http.Error(w, "Invalid filename", http.StatusBadRequest) return } - filePath := filepath.Join(archiveDir, filename) + // Allow downloading LICENSE or any kubectl-oadp binary + if filename != "LICENSE" && !strings.HasPrefix(filename, "kubectl-oadp_") { + http.Error(w, "Invalid filename", http.StatusBadRequest) + return + } + + filePath := filepath.Join(binaryDir, filename) if _, err := os.Stat(filePath); os.IsNotExist(err) { - http.Error(w, "Archive not found", http.StatusNotFound) + http.Error(w, "File not found", http.StatusNotFound) return } w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) - w.Header().Set("Content-Type", "application/gzip") + if filename == "LICENSE" { + w.Header().Set("Content-Type", "text/plain") + } else { + w.Header().Set("Content-Type", "application/octet-stream") + } http.ServeFile(w, r, filePath) log.Printf("Downloaded: %s from %s", filename, r.RemoteAddr) diff --git a/cmd/downloads/templates/index.html b/cmd/downloads/templates/index.html index a552ad9..d7cdfa6 100644 --- a/cmd/downloads/templates/index.html +++ b/cmd/downloads/templates/index.html @@ -100,29 +100,32 @@ {{end}} + {{if .HasLicense}} +
tar -xzf kubectl-oadp_*.tar.gz
-
- chmod +x kubectl-oadp
-
+ chmod +x kubectl-oadp_*
+
sudo mv kubectl-oadp /usr/local/bin/
-
+ sudo mv kubectl-oadp_* /usr/local/bin/kubectl-oadp
+