This repository has been archived by the owner on Jan 4, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from tulilirockz/feat-parallel-ops
feat: Parallel operations
- Loading branch information
Showing
7 changed files
with
385 additions
and
342 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,113 @@ | ||
package activate | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"log/slog" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/ublue-os/bext/internal" | ||
) | ||
|
||
var ActivateCmd = &cobra.Command{ | ||
Use: "activate [TARGET]", | ||
Short: "Activate a layer and refresh sysext", | ||
Long: `Activate a selected layer (symlink it to /var/lib/extensions) and refresh the system extensions store.`, | ||
Use: "activate [TARGET...]", | ||
Short: "Activate layers and refresh sysext", | ||
Long: `Activate selected layers and refresh the system extensions store.`, | ||
RunE: activateCmd, | ||
Args: cobra.MinimumNArgs(1), | ||
} | ||
|
||
var ( | ||
fFromFile *string | ||
fFromFile bool | ||
fOverride bool | ||
) | ||
|
||
func init() { | ||
fFromFile = ActivateCmd.Flags().StringP("file", "f", "", "Activate directly from file instead of cache") | ||
ActivateCmd.Flags().BoolVarP(&fFromFile, "file", "f", false, "Parse positional arguments as files instead of layers") | ||
ActivateCmd.Flags().BoolVar(&fOverride, "override", true, "Write over old symlinks") | ||
} | ||
|
||
func activateCmd(cmd *cobra.Command, args []string) error { | ||
if len(args) < 1 { | ||
return internal.NewPositionalError("TARGET") | ||
} | ||
|
||
target_layer := args[0] | ||
|
||
extensions_dir, err := filepath.Abs(path.Clean(internal.Config.ExtensionsDir)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if *fFromFile != "" { | ||
if !strings.HasSuffix(target_layer, internal.ValidSysextExtension) { | ||
target_layer, err = filepath.Abs(path.Clean(target_layer + internal.ValidSysextExtension)) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
target_path := path.Join(extensions_dir, target_layer) | ||
slog.Debug("acitavate", | ||
slog.String("fromfile", *fFromFile), | ||
slog.String("layer", target_layer), | ||
slog.String("path", target_path), | ||
) | ||
if err := os.Symlink(target_layer, path.Join(extensions_dir, target_layer)); err != nil { | ||
return err | ||
} | ||
slog.Info(fmt.Sprintf("Successfully activated layer %s\n", path.Base(target_layer))) | ||
return nil | ||
} | ||
|
||
var ( | ||
errChan = make(chan error, len(args)) | ||
wg sync.WaitGroup | ||
) | ||
cache_dir, err := filepath.Abs(path.Clean(internal.Config.CacheDir)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
current_blob_path := path.Join(cache_dir, target_layer, internal.CurrentBlobName) | ||
if _, err := os.Stat(current_blob_path); err != nil { | ||
if err := os.MkdirAll(internal.Config.ExtensionsDir, 0755); err != nil { | ||
return err | ||
} | ||
|
||
if err := os.MkdirAll(internal.Config.ExtensionsDir, 0755); err != nil { | ||
return err | ||
for _, target_file := range args { | ||
slog.Debug("Activating layer "+target_file, | ||
slog.Bool("fromfile", fFromFile), | ||
slog.String("layer", target_file), | ||
) | ||
|
||
wg.Add(1) | ||
go func(errChan chan<- error, target string) { | ||
defer wg.Done() | ||
var ( | ||
deployment_path string | ||
target_path string | ||
) | ||
|
||
if !strings.HasSuffix(target, internal.ValidSysextExtension) && fFromFile { | ||
errChan <- errors.New("failed to parse file name, invalid sysext extension. should be " + internal.ValidSysextExtension) | ||
return | ||
} | ||
|
||
if fFromFile { | ||
layer_name := strings.Split(path.Base(target), ".")[0] | ||
target_path = path.Join(extensions_dir, layer_name) | ||
deployment_path, err = filepath.Abs(layer_name) | ||
if err != nil { | ||
errChan <- err | ||
return | ||
} | ||
} else { | ||
deployment_path = path.Join(cache_dir, target, internal.CurrentBlobName) | ||
if _, err := os.Stat(deployment_path); err != nil { | ||
errChan <- errors.New("target layer " + target + " could not be found") | ||
return | ||
} | ||
target_path = path.Join(extensions_dir, target+internal.ValidSysextExtension) | ||
} | ||
if fOverride { | ||
_ = os.Remove(target_path) | ||
} else { | ||
errChan <- errors.New(target + " is already activated") | ||
} | ||
if err := os.Symlink(deployment_path, target_path); err != nil { | ||
errChan <- err | ||
} | ||
}(errChan, target_file) | ||
} | ||
|
||
target_path := path.Join(extensions_dir, path.Base(path.Dir(current_blob_path))+internal.ValidSysextExtension) | ||
slog.Debug("acitavate", | ||
slog.String("fromfile", *fFromFile), | ||
slog.String("layer", target_layer), | ||
slog.String("blob", current_blob_path), | ||
) | ||
if err := os.Symlink(current_blob_path, target_path); err != nil { | ||
return err | ||
go func() { | ||
wg.Wait() | ||
close(errChan) | ||
}() | ||
|
||
for err := range errChan { | ||
slog.Warn(fmt.Sprintf("Error encountered when activating layers: %s", err.Error()), slog.String("error", err.Error())) | ||
} | ||
|
||
slog.Info(fmt.Sprintf("Successfully activated layer %s\n", path.Base(target_layer))) | ||
if len(errChan) == 0 { | ||
slog.Info("Successfully activated layers", slog.String("layers", strings.Join(args, " "))) | ||
} | ||
return nil | ||
} |
Oops, something went wrong.