mirror of
https://github.com/safing/portmaster
synced 2025-04-09 05:29:11 +00:00
196 lines
4.5 KiB
Go
196 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/safing/portmaster/base/updater"
|
|
"github.com/safing/portmaster/base/utils"
|
|
)
|
|
|
|
var (
|
|
releaseCmd = &cobra.Command{
|
|
Use: "release",
|
|
Short: "Release scans the distribution directory and creates registry indexes and the symlink structure",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: release,
|
|
}
|
|
preReleaseCmd = &cobra.Command{
|
|
Use: "prerelease",
|
|
Short: "Stage scans the specified directory and loads the indexes - it then creates a staging index with all files newer than the stable and beta indexes",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: release,
|
|
}
|
|
preReleaseFrom string
|
|
resetPreReleases bool
|
|
)
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(releaseCmd)
|
|
rootCmd.AddCommand(preReleaseCmd)
|
|
|
|
preReleaseCmd.Flags().StringVar(&preReleaseFrom, "from", "", "Make a pre-release based on the given channel")
|
|
_ = preReleaseCmd.MarkFlagRequired("from")
|
|
preReleaseCmd.Flags().BoolVar(&resetPreReleases, "reset", false, "Reset pre-release assets")
|
|
}
|
|
|
|
func release(cmd *cobra.Command, args []string) error {
|
|
channel := args[0]
|
|
|
|
// Check if we want to reset instead.
|
|
if resetPreReleases {
|
|
return removeFilesFromIndex(getChannelVersions(preReleaseFrom, true))
|
|
}
|
|
|
|
// Write new index.
|
|
err := writeIndex(
|
|
channel,
|
|
getChannelVersions(preReleaseFrom, false),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Only when doing a release:
|
|
if preReleaseFrom == "" {
|
|
// Create symlinks to latest stable versions.
|
|
if !confirm("\nDo you want to write latest symlinks?") {
|
|
fmt.Println("aborted...")
|
|
return nil
|
|
}
|
|
symlinksDir := registry.StorageDir().ChildDir("latest", utils.PublicReadPermission)
|
|
err = registry.CreateSymlinks(symlinksDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("written latest symlinks")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeIndex(channel string, versions map[string]string) error {
|
|
// Create new index file.
|
|
indexFile := &updater.IndexFile{
|
|
Channel: channel,
|
|
Published: time.Now().UTC().Round(time.Second),
|
|
Releases: versions,
|
|
}
|
|
|
|
// Export versions and format them.
|
|
confirmData, err := json.MarshalIndent(indexFile, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Build index paths.
|
|
oldIndexPath := filepath.Join(registry.StorageDir().Path, channel+".json")
|
|
newIndexPath := filepath.Join(registry.StorageDir().Path, channel+".v2.json")
|
|
|
|
// Print preview.
|
|
fmt.Printf("%s\n%s\n%s\n\n", channel, oldIndexPath, newIndexPath)
|
|
fmt.Println(string(confirmData))
|
|
|
|
// Ask for confirmation.
|
|
if !confirm("\nDo you want to write this index?") {
|
|
fmt.Println("aborted...")
|
|
return nil
|
|
}
|
|
|
|
// Write indexes.
|
|
err = writeAsJSON(oldIndexPath, versions)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write %s: %w", oldIndexPath, err)
|
|
}
|
|
err = writeAsJSON(newIndexPath, indexFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write %s: %w", newIndexPath, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeAsJSON(path string, data any) error {
|
|
// Marshal to JSON.
|
|
jsonData, err := json.MarshalIndent(data, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write to disk.
|
|
err = os.WriteFile(path, jsonData, 0o0644) //nolint:gosec
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("written %s\n", path)
|
|
return nil
|
|
}
|
|
|
|
func removeFilesFromIndex(versions map[string]string) error {
|
|
// Print preview.
|
|
fmt.Println("To be deleted:")
|
|
for _, filePath := range versions {
|
|
fmt.Println(filePath)
|
|
}
|
|
|
|
// Ask for confirmation.
|
|
if !confirm("\nDo you want to delete these files?") {
|
|
fmt.Println("aborted...")
|
|
return nil
|
|
}
|
|
|
|
// Delete files.
|
|
for _, filePath := range versions {
|
|
err := os.Remove(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
fmt.Println("deleted")
|
|
|
|
return nil
|
|
}
|
|
|
|
func getChannelVersions(prereleaseFrom string, storagePath bool) map[string]string {
|
|
if prereleaseFrom != "" {
|
|
registry.AddIndex(updater.Index{
|
|
Path: prereleaseFrom + ".json",
|
|
PreRelease: false,
|
|
})
|
|
err := registry.LoadIndexes(context.Background())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// Sort all versions.
|
|
registry.SelectVersions()
|
|
export := registry.Export()
|
|
|
|
// Go through all versions and save the highest version, if not stable or beta.
|
|
versions := make(map[string]string)
|
|
for _, rv := range export {
|
|
highestVersion := rv.Versions[0]
|
|
|
|
// Ignore versions that are in the reference release channel.
|
|
if highestVersion.CurrentRelease {
|
|
continue
|
|
}
|
|
|
|
// Add highest version of matching release channel.
|
|
if storagePath {
|
|
versions[rv.Identifier] = rv.GetFile().Path()
|
|
} else {
|
|
versions[rv.Identifier] = highestVersion.VersionNumber
|
|
}
|
|
}
|
|
|
|
return versions
|
|
}
|