github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/scripts/versioninfo-generator/main.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/ActiveState/cli/internal/assets"
    11  	"github.com/ActiveState/cli/internal/errs"
    12  	"github.com/blang/semver"
    13  )
    14  
    15  const (
    16  	versionInfoAssetsFileName = "versioninfo.json"
    17  	versionInfoLegalCopyright = "© ActiveState Software, Inc. %d"
    18  )
    19  
    20  type versionInfo struct {
    21  	FixedFileInfo  FixedFileInfo  `json:"FixedFileInfo"`
    22  	StringFileInfo StringFileInfo `json:"StringFileInfo"`
    23  
    24  	// The remainder is data we don't care about but need
    25  	// to preserve in the JSON file.
    26  	VarFileInfo  VarFileInfo `json:"VarFileInfo"`
    27  	IconPath     string      `json:"IconPath"`
    28  	ManifestPath string      `json:"ManifestPath"`
    29  }
    30  
    31  type FixedFileInfo struct {
    32  	FileVersion    FileVersion    `json:"FileVersion"`
    33  	ProductVersion ProductVersion `json:"ProductVersion"`
    34  
    35  	// The remainder is data we don't care about but need
    36  	// to preserve in the JSON file.
    37  	FileFlagsMask string `json:"FileFlagsMask"`
    38  	FileFlags     string `json:"FileFlags"`
    39  	FileOS        string `json:"FileOS"`
    40  	FileType      string `json:"FileType"`
    41  	FileSubType   string `json:"FileSubType"`
    42  }
    43  
    44  type FileVersion struct {
    45  	Major int `json:"Major"`
    46  	Minor int `json:"Minor"`
    47  	Patch int `json:"Patch"`
    48  	Build int `json:"Build"`
    49  }
    50  
    51  type ProductVersion struct {
    52  	Major int `json:"Major"`
    53  	Minor int `json:"Minor"`
    54  	Patch int `json:"Patch"`
    55  	Build int `json:"Build"`
    56  }
    57  
    58  type StringFileInfo struct {
    59  	FileVersion    string `json:"FileVersion"`
    60  	ProductVersion string `json:"ProductVersion"`
    61  
    62  	// The remainder is data we don't care about but need
    63  	// to preserve in the JSON file.
    64  	Comments         string `json:"Comments"`
    65  	CompanyName      string `json:"CompanyName"`
    66  	FileDescription  string `json:"FileDescription"`
    67  	InternalName     string `json:"InternalName"`
    68  	LegalCopyright   string `json:"LegalCopyright"`
    69  	LegalTrademarks  string `json:"LegalTrademarks"`
    70  	OriginalFilename string `json:"OriginalFilename"`
    71  	PrivateBuild     string `json:"PrivateBuild"`
    72  	ProductName      string `json:"ProductName"`
    73  	SpecialBuild     string `json:"SpecialBuild"`
    74  }
    75  
    76  type VarFileInfo struct {
    77  	Translation Translation `json:"Translation"`
    78  }
    79  
    80  type Translation struct {
    81  	LangID    string `json:"LangID"`
    82  	CharsetID string `json:"CharsetID"`
    83  }
    84  
    85  func main() {
    86  	if err := run(); err != nil {
    87  		panic(err)
    88  	}
    89  }
    90  
    91  func run() error {
    92  	if len(os.Args) != 4 {
    93  		return errs.New("Usage: versioninfo-generator <version.txt> <versioninfo.json>")
    94  	}
    95  
    96  	versionFile := os.Args[1]
    97  	versionInfoFilePath := os.Args[2]
    98  	productName := os.Args[3]
    99  
   100  	// Read version.txt
   101  	version, err := os.ReadFile(versionFile)
   102  	if err != nil {
   103  		return errs.Wrap(err, "failed to read version.txt")
   104  	}
   105  
   106  	// The goversioninfo library does not support prerelease versions
   107  	if strings.Contains(string(version), "-") {
   108  		parts := strings.SplitN(string(version), "-", 2)
   109  		version = []byte(parts[0])
   110  	}
   111  
   112  	// Parse semver from version.txt
   113  	semver, err := semver.Parse(string(version))
   114  	if err != nil {
   115  		return errs.Wrap(err, "failed to parse version.txt")
   116  	}
   117  
   118  	versionInfoFile, err := assets.ReadFileBytes(versionInfoAssetsFileName)
   119  	if err != nil {
   120  		return errs.Wrap(err, "failed to read versioninfo.json")
   121  	}
   122  
   123  	// Parse versioninfo.json to data map
   124  	var data map[string]interface{}
   125  	if err := json.Unmarshal(versionInfoFile, &data); err != nil {
   126  		return errs.Wrap(err, "failed to parse versioninfo.json")
   127  	}
   128  
   129  	// Parse versioninfo.json
   130  	var versionInfo versionInfo
   131  	if err := json.Unmarshal(versionInfoFile, &versionInfo); err != nil {
   132  		return errs.Wrap(err, "failed to parse versioninfo.json")
   133  	}
   134  
   135  	// Update versioninfo.json
   136  	versionInfo.FixedFileInfo.FileVersion.Major = int(semver.Major)
   137  	versionInfo.FixedFileInfo.FileVersion.Minor = int(semver.Minor)
   138  	versionInfo.FixedFileInfo.FileVersion.Patch = int(semver.Patch)
   139  	versionInfo.FixedFileInfo.ProductVersion.Major = int(semver.Major)
   140  	versionInfo.FixedFileInfo.ProductVersion.Minor = int(semver.Minor)
   141  	versionInfo.FixedFileInfo.ProductVersion.Patch = int(semver.Patch)
   142  	versionInfo.StringFileInfo.FileVersion = semver.String()
   143  	versionInfo.StringFileInfo.ProductVersion = semver.String()
   144  	versionInfo.StringFileInfo.ProductName = productName
   145  	versionInfo.StringFileInfo.LegalCopyright = fmt.Sprintf(versionInfoLegalCopyright, time.Now().Year())
   146  
   147  	// Marshal the updated versioninfo.json
   148  	finalJSON, err := json.MarshalIndent(versionInfo, "", "  ")
   149  	if err != nil {
   150  		return errs.Wrap(err, "failed to marshal versioninfo.json")
   151  	}
   152  
   153  	// Save the final JSON back to the file
   154  	err = os.WriteFile(versionInfoFilePath, finalJSON, 0644)
   155  	if err != nil {
   156  		return errs.Wrap(err, "failed to write versioninfo.json")
   157  	}
   158  	fmt.Println("Successfully generated versioninfo.json at ", versionInfoFilePath)
   159  
   160  	return nil
   161  
   162  }