safing-portmaster/cmds/updatemgr/release.go
Daniel Hååvi 80664d1a27
Restructure modules (#1572)
* Move portbase into monorepo

* Add new simple module mgr

* [WIP] Switch to new simple module mgr

* Add StateMgr and more worker variants

* [WIP] Switch more modules

* [WIP] Switch more modules

* [WIP] swtich more modules

* [WIP] switch all SPN modules

* [WIP] switch all service modules

* [WIP] Convert all workers to the new module system

* [WIP] add new task system to module manager

* [WIP] Add second take for scheduling workers

* [WIP] Add FIXME for bugs in new scheduler

* [WIP] Add minor improvements to scheduler

* [WIP] Add new worker scheduler

* [WIP] Fix more bug related to new module system

* [WIP] Fix start handing of the new module system

* [WIP] Improve startup process

* [WIP] Fix minor issues

* [WIP] Fix missing subsystem in settings

* [WIP] Initialize managers in constructor

* [WIP] Move module event initialization to constrictors

* [WIP] Fix setting for enabling and disabling the SPN module

* [WIP] Move API registeration into module construction

* [WIP] Update states mgr for all modules

* [WIP] Add CmdLine operation support

* Add state helper methods to module group and instance

* Add notification and module status handling to status package

* Fix starting issues

* Remove pilot widget and update security lock to new status data

* Remove debug logs

* Improve http server shutdown

* Add workaround for cleanly shutting down firewall+netquery

* Improve logging

* Add syncing states with notifications for new module system

* Improve starting, stopping, shutdown; resolve FIXMEs/TODOs

* [WIP] Fix most unit tests

* Review new module system and fix minor issues

* Push shutdown and restart events again via API

* Set sleep mode via interface

* Update example/template module

* [WIP] Fix spn/cabin unit test

* Remove deprecated UI elements

* Make log output more similar for the logging transition phase

* Switch spn hub and observer cmds to new module system

* Fix log sources

* Make worker mgr less error prone

* Fix tests and minor issues

* Fix observation hub

* Improve shutdown and restart handling

* Split up big connection.go source file

* Move varint and dsd packages to structures repo

* Improve expansion test

* Fix linter warnings

* Fix interception module on windows

* Fix linter errors

---------

Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
2024-08-09 18:15:48 +03:00

195 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"
)
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", 0o755)
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
}