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  }