Skip to content

Commit fc31452

Browse files
committed
Update Dockerfile and Go modules; enhance installation script
- Updated Dockerfile to use Go version 1.24.5. - Improved the build process in main.go by implementing concurrency with errgroup for cross-compilation. - Added a new function getOutputFilename to generate output filenames based on platform and version. - Enhanced createArchives function to support concurrent archive creation. - Updated go.mod and go.sum to reflect dependency updates. - Modified install.sh to improve binary extraction logic and cleanup process after installation.
1 parent e02f611 commit fc31452

5 files changed

Lines changed: 223 additions & 120 deletions

File tree

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Dockerfile
2-
FROM golang:1.24.4 AS builder
2+
FROM golang:1.24.5 AS builder
33
WORKDIR /app
44

55
# Define build arguments for version, commit, and date.
@@ -15,7 +15,7 @@ RUN go mod download
1515
COPY . .
1616
RUN CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION} -X main.commit=${COMMIT} -X main.buildDate=${DATE}" -v -o ./bin/gcx ./cmd/gcx
1717

18-
FROM golang:1.24.4
18+
FROM golang:1.24.5
1919

2020
ENV GOTOOLCHAIN=auto
2121
ENV GOROOT=/usr/local/go

cmd/gcx/main.go

Lines changed: 178 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/minio/minio-go/v7/pkg/credentials"
2828
"github.com/sxwebdev/gcx/internal/helpers"
2929
"github.com/urfave/cli/v3"
30+
"golang.org/x/sync/errgroup"
3031
"gopkg.in/yaml.v3"
3132
)
3233

@@ -407,6 +408,24 @@ func getGitCommitHash() string {
407408
return strings.TrimSpace(string(out))
408409
}
409410

411+
// getOutputFilename returns the output filename based on the configuration and platform.
412+
func getOutputFilename(
413+
usePlatformSuffix bool,
414+
outDir, binaryBase, version, goos, goarch, goarm string,
415+
) string {
416+
var outputName string
417+
if usePlatformSuffix {
418+
if goarch == "arm" && goarm != "" {
419+
outputName = fmt.Sprintf("%s/%s_%s_%s_%s_%s/%s", outDir, binaryBase, version, goos, goarch, goarm, binaryBase)
420+
} else {
421+
outputName = fmt.Sprintf("%s/%s_%s_%s_%s/%s", outDir, binaryBase, version, goos, goarch, binaryBase)
422+
}
423+
} else {
424+
outputName = fmt.Sprintf("%s/%s_%s/%s", outDir, binaryBase, version, binaryBase)
425+
}
426+
return outputName
427+
}
428+
410429
// buildBinaries performs cross-compilation of binaries according to the configuration.
411430
func buildBinaries(cfg *Config) error {
412431
// Execute hooks (e.g., "go mod tidy")
@@ -504,32 +523,57 @@ func buildBinaries(cfg *Config) error {
504523
processedLdflags = append(processedLdflags, buf.String())
505524
}
506525

526+
eg := errgroup.Group{}
527+
eg.SetLimit(runtime.NumCPU())
528+
529+
log.Printf("Use %d CPU cores for building...\n", runtime.NumCPU())
530+
507531
// Iterate over all combinations of GOOS and GOARCH
508532
for _, goos := range buildCfg.Goos {
509-
for _, goarch := range buildCfg.Goarch {
510-
// If the architecture is arm and OS is not linux, skip build
511-
if goarch == "arm" && goos != "linux" {
512-
continue
513-
}
514-
// If architecture is arm and goarm parameters are provided, iterate over them
515-
if goarch == "arm" && len(buildCfg.Goarm) > 0 {
516-
for _, goarm := range buildCfg.Goarm {
533+
eg.Go(func() error {
534+
for _, goarch := range buildCfg.Goarch {
535+
// If the architecture is arm and OS is not linux, skip build
536+
if goarch == "arm" && goos != "linux" {
537+
continue
538+
}
539+
// If architecture is arm and goarm parameters are provided, iterate over them
540+
if goarch == "arm" && len(buildCfg.Goarm) > 0 {
541+
for _, goarm := range buildCfg.Goarm {
542+
envs := os.Environ()
543+
envs = append(envs, "GOOS="+goos, "GOARCH="+goarch, "GOARM="+goarm)
544+
envs = append(envs, buildCfg.Env...)
545+
outputName := getOutputFilename(
546+
usePlatformSuffix, outDir, binaryBase, currentTag, goos, goarch, goarm,
547+
)
548+
args := []string{"build"}
549+
args = append(args, buildCfg.Flags...)
550+
if len(processedLdflags) > 0 {
551+
args = append(args, "-ldflags", strings.Join(processedLdflags, " "))
552+
}
553+
args = append(args, "-o", outputName, buildCfg.Main)
554+
log.Printf("Building %s for %s/%s arm%s...", binaryBase, goos, goarch, goarm)
555+
cmd := exec.Command("go", args...)
556+
cmd.Env = envs
557+
cmd.Stdout = os.Stdout
558+
cmd.Stderr = os.Stderr
559+
if err := cmd.Run(); err != nil {
560+
return fmt.Errorf("build error: %w", err)
561+
}
562+
}
563+
} else {
517564
envs := os.Environ()
518-
envs = append(envs, "GOOS="+goos, "GOARCH="+goarch, "GOARM="+goarm)
565+
envs = append(envs, "GOOS="+goos, "GOARCH="+goarch)
519566
envs = append(envs, buildCfg.Env...)
520-
var outputName string
521-
if usePlatformSuffix {
522-
outputName = fmt.Sprintf("%s/%s_%s_%s_%s", outDir, binaryBase, goos, goarch, goarm)
523-
} else {
524-
outputName = fmt.Sprintf("%s/%s", outDir, binaryBase)
525-
}
567+
outputName := getOutputFilename(
568+
usePlatformSuffix, outDir, binaryBase, currentTag, goos, goarch, "",
569+
)
526570
args := []string{"build"}
527571
args = append(args, buildCfg.Flags...)
528572
if len(processedLdflags) > 0 {
529573
args = append(args, "-ldflags", strings.Join(processedLdflags, " "))
530574
}
531575
args = append(args, "-o", outputName, buildCfg.Main)
532-
log.Printf("Building %s for %s/%s arm%s...", binaryBase, goos, goarch, goarm)
576+
log.Printf("Building %s for %s/%s...", binaryBase, goos, goarch)
533577
cmd := exec.Command("go", args...)
534578
cmd.Env = envs
535579
cmd.Stdout = os.Stdout
@@ -538,32 +582,13 @@ func buildBinaries(cfg *Config) error {
538582
return fmt.Errorf("build error: %w", err)
539583
}
540584
}
541-
} else {
542-
envs := os.Environ()
543-
envs = append(envs, "GOOS="+goos, "GOARCH="+goarch)
544-
envs = append(envs, buildCfg.Env...)
545-
var outputName string
546-
if usePlatformSuffix {
547-
outputName = fmt.Sprintf("%s/%s_%s_%s", outDir, binaryBase, goos, goarch)
548-
} else {
549-
outputName = fmt.Sprintf("%s/%s", outDir, binaryBase)
550-
}
551-
args := []string{"build"}
552-
args = append(args, buildCfg.Flags...)
553-
if len(processedLdflags) > 0 {
554-
args = append(args, "-ldflags", strings.Join(processedLdflags, " "))
555-
}
556-
args = append(args, "-o", outputName, buildCfg.Main)
557-
log.Printf("Building %s for %s/%s...", binaryBase, goos, goarch)
558-
cmd := exec.Command("go", args...)
559-
cmd.Env = envs
560-
cmd.Stdout = os.Stdout
561-
cmd.Stderr = os.Stderr
562-
if err := cmd.Run(); err != nil {
563-
return fmt.Errorf("build error: %w", err)
564-
}
565585
}
566-
}
586+
return nil
587+
})
588+
}
589+
590+
if err := eg.Wait(); err != nil {
591+
return fmt.Errorf("build error: %w", err)
567592
}
568593
}
569594

@@ -814,38 +839,37 @@ func createArchives(cfg *Config, artifactsDir string) error {
814839
return nil
815840
}
816841

817-
// Get current version
818-
version := getGitTag()
819-
820842
// Read all files in artifacts directory
821843
files, err := os.ReadDir(artifactsDir)
822844
if err != nil {
823845
return fmt.Errorf("failed to read artifacts directory: %w", err)
824846
}
825847

848+
eg := errgroup.Group{}
849+
eg.SetLimit(runtime.NumCPU())
850+
851+
log.Printf("Use %d CPU cores for creating archives...\n", runtime.NumCPU())
852+
826853
// Track which files were archived
827854
archivedFiles := make(map[string]bool)
828855

829-
// Create archives for each file according to configuration
856+
// Create archives for each file/directory according to configuration
830857
for _, file := range files {
831-
if file.IsDir() {
832-
continue
833-
}
834-
835-
// Parse filename to get platform information
836858
fileName := file.Name()
859+
837860
parts := strings.Split(fileName, "_")
838-
if len(parts) < 3 {
861+
if len(parts) < 4 {
839862
continue
840863
}
841864

842-
binary := parts[0]
843-
os := parts[1]
844-
arch := parts[2]
865+
binaryName := parts[0]
866+
version := parts[1]
867+
os := parts[2]
868+
arch := parts[3]
845869

846870
// Template data
847871
tmplData := ArchiveTemplateData{
848-
Binary: binary,
872+
Binary: binaryName,
849873
Version: version,
850874
Os: os,
851875
Arch: arch,
@@ -854,55 +878,74 @@ func createArchives(cfg *Config, artifactsDir string) error {
854878
// For each archive configuration
855879
for _, archive := range cfg.Archives {
856880
// Create archive name from template
857-
tmpl, err := template.New("archive").Parse(archive.NameTemplate)
858-
if err != nil {
859-
return fmt.Errorf("failed to parse archive name template: %w", err)
860-
}
881+
archiveName := fileName
882+
if archive.NameTemplate != "" {
883+
tmpl, err := template.New("archive").Parse(archive.NameTemplate)
884+
if err != nil {
885+
return fmt.Errorf("failed to parse archive name template: %w", err)
886+
}
887+
888+
var nameBuffer strings.Builder
889+
if err := tmpl.Execute(&nameBuffer, tmplData); err != nil {
890+
return fmt.Errorf("failed to execute archive name template: %w", err)
891+
}
861892

862-
var nameBuffer strings.Builder
863-
if err := tmpl.Execute(&nameBuffer, tmplData); err != nil {
864-
return fmt.Errorf("failed to execute archive name template: %w", err)
893+
archiveName = nameBuffer.String()
865894
}
866895

867896
// For each archive format
868897
for _, format := range archive.Formats {
869-
archiveName := nameBuffer.String() + "." + format
870-
archivePath := filepath.Join(artifactsDir, archiveName)
898+
archiveFileName := archiveName + "." + format
899+
archivePath := filepath.Join(artifactsDir, archiveFileName)
900+
sourcePath := filepath.Join(artifactsDir, fileName)
871901

872902
switch format {
873903
case "tar.gz":
874-
if err := createTarGz(filepath.Join(artifactsDir, fileName), archivePath); err != nil {
875-
return fmt.Errorf("failed to create tar.gz archive: %w", err)
876-
}
877-
// Mark the source file as archived
904+
// Mark the source file/directory as archived
878905
archivedFiles[fileName] = true
879-
// Here you can add support for other archive formats
906+
907+
eg.Go(func() error {
908+
if err := createTarGz(sourcePath, archivePath); err != nil {
909+
return fmt.Errorf("failed to create tar.gz archive: %w", err)
910+
}
911+
912+
return nil
913+
})
880914
default:
881915
log.Printf("Unsupported archive format: %s", format)
882916
}
883917
}
884918
}
885919
}
886920

887-
// Remove all source files that were archived
921+
if err := eg.Wait(); err != nil {
922+
return fmt.Errorf("error creating archives: %w", err)
923+
}
924+
925+
// Remove all source files/directories that were archived
888926
for _, file := range files {
889-
if file.IsDir() {
890-
continue
891-
}
892927
fileName := file.Name()
893928
if archivedFiles[fileName] {
894929
filePath := filepath.Join(artifactsDir, fileName)
895-
if err := os.Remove(filePath); err != nil {
896-
log.Printf("Warning: failed to remove source file %s: %v", filePath, err)
930+
if file.IsDir() {
931+
if err := os.RemoveAll(filePath); err != nil {
932+
log.Printf("Warning: failed to remove source directory %s: %v", filePath, err)
933+
}
934+
} else {
935+
if err := os.Remove(filePath); err != nil {
936+
log.Printf("Warning: failed to remove source file %s: %v", filePath, err)
937+
}
897938
}
898939
}
899940
}
900941

942+
log.Printf("All archives created successfully.")
943+
901944
return nil
902945
}
903946

904-
// createTarGz creates a tar.gz archive from a file
905-
func createTarGz(srcFile, destFile string) error {
947+
// createTarGz creates a tar.gz archive from a file or directory
948+
func createTarGz(srcPath, destFile string) error {
906949
// Create archive file
907950
archive, err := os.Create(destFile)
908951
if err != nil {
@@ -918,8 +961,26 @@ func createTarGz(srcFile, destFile string) error {
918961
tw := tar.NewWriter(gw)
919962
defer tw.Close()
920963

964+
// Check if source is file or directory
965+
srcInfo, err := os.Stat(srcPath)
966+
if err != nil {
967+
return fmt.Errorf("failed to get source info: %w", err)
968+
}
969+
970+
if srcInfo.IsDir() {
971+
// Archive directory - use directory name as base
972+
dirName := filepath.Base(srcPath)
973+
return addDirToTar(tw, srcPath, dirName)
974+
} else {
975+
// Archive single file
976+
return addFileToTar(tw, srcPath, filepath.Base(srcPath))
977+
}
978+
}
979+
980+
// addFileToTar adds a single file to tar archive
981+
func addFileToTar(tw *tar.Writer, filePath, nameInTar string) error {
921982
// Open source file
922-
file, err := os.Open(srcFile)
983+
file, err := os.Open(filePath)
923984
if err != nil {
924985
return fmt.Errorf("failed to open source file: %w", err)
925986
}
@@ -933,7 +994,7 @@ func createTarGz(srcFile, destFile string) error {
933994

934995
// Create tar header
935996
header := &tar.Header{
936-
Name: filepath.Base(srcFile),
997+
Name: nameInTar,
937998
Size: stat.Size(),
938999
Mode: int64(stat.Mode()),
9391000
ModTime: stat.ModTime(),
@@ -952,6 +1013,44 @@ func createTarGz(srcFile, destFile string) error {
9521013
return nil
9531014
}
9541015

1016+
// addDirToTar recursively adds directory contents to tar archive
1017+
func addDirToTar(tw *tar.Writer, dirPath, baseInTar string) error {
1018+
return filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
1019+
if err != nil {
1020+
return err
1021+
}
1022+
1023+
// Calculate relative path for tar
1024+
relPath, err := filepath.Rel(dirPath, path)
1025+
if err != nil {
1026+
return fmt.Errorf("failed to get relative path: %w", err)
1027+
}
1028+
1029+
var nameInTar string
1030+
if relPath == "." {
1031+
// This is the root directory itself
1032+
nameInTar = baseInTar
1033+
} else {
1034+
// Combine with base path in tar
1035+
nameInTar = filepath.Join(baseInTar, relPath)
1036+
}
1037+
1038+
// Handle directories
1039+
if info.IsDir() {
1040+
header := &tar.Header{
1041+
Name: nameInTar + "/",
1042+
Mode: int64(info.Mode()),
1043+
ModTime: info.ModTime(),
1044+
Typeflag: tar.TypeDir,
1045+
}
1046+
return tw.WriteHeader(header)
1047+
}
1048+
1049+
// Handle regular files
1050+
return addFileToTar(tw, path, nameInTar)
1051+
})
1052+
}
1053+
9551054
// deployArtifacts executes deployment according to the configuration
9561055
func deployArtifacts(cfg *Config, deployName string) error {
9571056
if len(cfg.Deploys) == 0 {

0 commit comments

Comments
 (0)