github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/scripts/ci/update-generator/main.go (about) 1 package main 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "encoding/json" 7 "flag" 8 "fmt" 9 "log" 10 "os" 11 "path/filepath" 12 "runtime" 13 14 "github.com/mholt/archiver" 15 16 "github.com/ActiveState/cli/internal/condition" 17 "github.com/ActiveState/cli/internal/constants" 18 "github.com/ActiveState/cli/internal/environment" 19 "github.com/ActiveState/cli/internal/errs" 20 "github.com/ActiveState/cli/internal/fileutils" 21 "github.com/ActiveState/cli/internal/osutils" 22 "github.com/ActiveState/cli/internal/updater" 23 ) 24 25 var ( 26 rootPath = environment.GetRootPathUnsafe() 27 defaultBuildDir = filepath.Join(rootPath, "build") 28 defaultInputDir = filepath.Join(defaultBuildDir, "payload", constants.LegacyToplevelInstallArchiveDir) 29 defaultOutputDir = filepath.Join(rootPath, "public") 30 ) 31 32 func main() { 33 if !condition.InUnitTest() { 34 err := run() 35 if err != nil { 36 fmt.Fprintf(os.Stderr, "%s error: %v", os.Args[0], errs.JoinMessage(err)) 37 os.Exit(1) 38 } 39 } 40 } 41 42 func fetchPlatform() string { 43 goos := os.Getenv("GOOS") 44 goarch := os.Getenv("GOARCH") 45 if goos != "" && goarch != "" { 46 return goos + "-" + goarch 47 } 48 return runtime.GOOS + "-" + runtime.GOARCH 49 } 50 51 func generateSha256(path string) string { 52 hasher := sha256.New() 53 b, err := os.ReadFile(path) 54 if err != nil { 55 log.Fatal(err) 56 } 57 hasher.Write(b) 58 return hex.EncodeToString(hasher.Sum(nil)) 59 } 60 61 func archiveMeta() (archiveMethod archiver.Archiver, ext string) { 62 if runtime.GOOS == "windows" { 63 return archiver.NewZip(), ".zip" 64 } 65 return archiver.NewTarGz(), ".tar.gz" 66 } 67 68 func createUpdate(outputPath, channel, version, versionNumber, platform, target string) error { 69 relChannelPath := filepath.Join(channel, platform) 70 relVersionedPath := filepath.Join(channel, versionNumber, platform) 71 _ = os.MkdirAll(filepath.Join(outputPath, relChannelPath), 0o755) 72 _ = os.MkdirAll(filepath.Join(outputPath, relVersionedPath), 0o755) 73 74 archive, archiveExt := archiveMeta() 75 relArchivePath := filepath.Join(relVersionedPath, fmt.Sprintf("state-%s-%s%s", platform, version, archiveExt)) 76 archivePath := filepath.Join(outputPath, relArchivePath) 77 78 // Remove archive path if it already exists 79 _ = os.Remove(archivePath) 80 // Create main archive 81 fmt.Printf("Creating %s\n", archivePath) 82 if err := archive.Archive([]string{target}, archivePath); err != nil { 83 return errs.Wrap(err, "Archiving failed") 84 } 85 86 avUpdate := updater.NewAvailableUpdate(channel, version, platform, filepath.ToSlash(relArchivePath), generateSha256(archivePath), "") 87 b, err := json.MarshalIndent(avUpdate, "", " ") 88 if err != nil { 89 return errs.Wrap(err, "Failed to marshal AvailableUpdate information.") 90 } 91 92 infoPath := filepath.Join(outputPath, relChannelPath, "info.json") 93 fmt.Printf("Creating %s\n", infoPath) 94 err = os.WriteFile(infoPath, b, 0o755) 95 if err != nil { 96 return errs.Wrap(err, "Failed to write info.json.") 97 } 98 99 err = fileutils.CopyFile(infoPath, filepath.Join(outputPath, relVersionedPath, filepath.Base(infoPath))) 100 if err != nil { 101 return errs.Wrap(err, "Could not copy info.json file") 102 } 103 104 return nil 105 } 106 107 func createInstaller(buildPath, outputPath, channel, platform string) error { 108 installer := filepath.Join(buildPath, "state-installer"+osutils.ExeExtension) 109 if !fileutils.FileExists(installer) { 110 return errs.New("state-installer does not exist in build dir") 111 } 112 113 archive, archiveExt := archiveMeta() 114 relArchivePath := filepath.Join(channel, platform, "state-installer"+archiveExt) 115 archivePath := filepath.Join(outputPath, relArchivePath) 116 117 // Remove archive path if it already exists 118 _ = os.Remove(archivePath) 119 // Create main archive 120 fmt.Printf("Creating %s\n", archivePath) 121 err := archive.Archive([]string{installer}, archivePath) 122 if err != nil { 123 return errs.Wrap(err, "Archiving failed") 124 } 125 126 return nil 127 } 128 129 func run() error { 130 var ( 131 binDir = defaultBuildDir 132 inDir = defaultInputDir 133 outDir = defaultOutputDir 134 platform = fetchPlatform() 135 channel = constants.ChannelName 136 version = constants.Version 137 versionNumber = constants.VersionNumber 138 ) 139 140 flag.StringVar(&outDir, "o", outDir, "Override directory to output archive to.") 141 flag.StringVar( 142 &platform, "platform", platform, 143 "Target platform in the form OS-ARCH. Defaults to running os/arch or the combination of the environment variables GOOS and GOARCH if both are set.", 144 ) 145 flag.StringVar(&channel, "b", channel, "Override target channel. (Channel to receive update.)") 146 flag.StringVar(&version, "v", version, "Override version number for this update.") 147 flag.Parse() 148 149 if err := fileutils.MkdirUnlessExists(outDir); err != nil { 150 return err 151 } 152 153 if err := createUpdate(outDir, channel, version, versionNumber, platform, inDir); err != nil { 154 return err 155 } 156 157 if err := createInstaller(binDir, outDir, channel, platform); err != nil { 158 return err 159 } 160 161 return nil 162 }