Add safety check for package list validation
This commit is contained in:
@@ -61,7 +61,8 @@ docker
|
|||||||
|
|
||||||
| Flag | Alias | Description |
|
| Flag | Alias | Description |
|
||||||
|------|-------|-------------|
|
|------|-------|-------------|
|
||||||
| `--state` | `-s` | State file to read package list from (can be used multiple times) |
|
| `--state` | `-s` | State file(s) to read package list from (can be used multiple times) |
|
||||||
|
| `--nocheck` | | Skip safety check (allow significant package count reductions)
|
||||||
| `--dry-run` | | Preview changes without applying them |
|
| `--dry-run` | | Preview changes without applying them |
|
||||||
| `--verbose` | `-v` | Enable verbose output |
|
| `--verbose` | `-v` | Enable verbose output |
|
||||||
| `--help` | `-h` | Show help message |
|
| `--help` | `-h` | Show help message |
|
||||||
|
|||||||
+12
-3
@@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
StateFiles []string
|
StateFiles []string
|
||||||
NoConfirm bool
|
NoCheck bool
|
||||||
DryRun bool
|
DryRun bool
|
||||||
Verbose bool
|
Verbose bool
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,11 @@ func main() {
|
|||||||
Usage: "State file(s) to read package list from",
|
Usage: "State file(s) to read package list from",
|
||||||
Destination: &cfg.StateFiles,
|
Destination: &cfg.StateFiles,
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "nocheck",
|
||||||
|
Usage: "Skip safety check",
|
||||||
|
Destination: &cfg.NoCheck,
|
||||||
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "dry-run",
|
Name: "dry-run",
|
||||||
Usage: "Simulate the sync without making changes",
|
Usage: "Simulate the sync without making changes",
|
||||||
@@ -70,7 +75,11 @@ func run(cfg *Config) error {
|
|||||||
}
|
}
|
||||||
log.Debug("run: packages read (%.2fs)", time.Since(start).Seconds())
|
log.Debug("run: packages read (%.2fs)", time.Since(start).Seconds())
|
||||||
|
|
||||||
merged := merge.Merge(packages)
|
merged, err := merge.Merge(packages)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.DryRun {
|
if cfg.DryRun {
|
||||||
result, err := read.DryRun(merged)
|
result, err := read.DryRun(merged)
|
||||||
@@ -89,7 +98,7 @@ func run(cfg *Config) error {
|
|||||||
}
|
}
|
||||||
defer log.Close()
|
defer log.Close()
|
||||||
|
|
||||||
result, err := pacman.Sync(merged)
|
result, err := pacman.Sync(merged, cfg.NoCheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
+9
-2
@@ -1,9 +1,16 @@
|
|||||||
package merge
|
package merge
|
||||||
|
|
||||||
func Merge(packages map[string]bool) []string {
|
import "errors"
|
||||||
|
|
||||||
|
var ErrEmptyList = errors.New("package list is empty")
|
||||||
|
|
||||||
|
func Merge(packages map[string]bool) ([]string, error) {
|
||||||
result := make([]string, 0, len(packages))
|
result := make([]string, 0, len(packages))
|
||||||
for name := range packages {
|
for name := range packages {
|
||||||
result = append(result, name)
|
result = append(result, name)
|
||||||
}
|
}
|
||||||
return result
|
if len(result) == 0 {
|
||||||
|
return nil, ErrEmptyList
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ type Result struct {
|
|||||||
|
|
||||||
func Format(r *Result) string {
|
func Format(r *Result) string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
b.WriteString(fmt.Sprintf("Installed %d packages, removed %d packages", r.Installed, r.Removed))
|
b.WriteString(fmt.Sprintf("installed %d packages, removed %d packages", r.Installed, r.Removed))
|
||||||
if len(r.ToInstall) > 0 {
|
if len(r.ToInstall) > 0 {
|
||||||
b.WriteString("\nWould install: ")
|
b.WriteString("\nwould install: ")
|
||||||
b.WriteString(strings.Join(r.ToInstall, ", "))
|
b.WriteString(strings.Join(r.ToInstall, ", "))
|
||||||
}
|
}
|
||||||
if len(r.ToRemove) > 0 {
|
if len(r.ToRemove) > 0 {
|
||||||
b.WriteString("\nWould remove: ")
|
b.WriteString("\nwould remove: ")
|
||||||
b.WriteString(strings.Join(r.ToRemove, ", "))
|
b.WriteString(strings.Join(r.ToRemove, ", "))
|
||||||
}
|
}
|
||||||
return b.String()
|
return b.String()
|
||||||
|
|||||||
+12
-1
@@ -13,10 +13,21 @@ import (
|
|||||||
"github.com/Riyyi/declpac/pkg/pacman/sync"
|
"github.com/Riyyi/declpac/pkg/pacman/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Sync(packages []string) (*output.Result, error) {
|
func Sync(packages []string, noCheck bool) (*output.Result, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Debug("Sync: starting...")
|
log.Debug("Sync: starting...")
|
||||||
|
|
||||||
|
explicitList, err := read.ExplicitList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
explicitCount := len(explicitList)
|
||||||
|
|
||||||
|
if !noCheck && len(packages) < explicitCount/2 {
|
||||||
|
errMsg := "safety check: state packages (%d) less than half of explicitly installed (%d), override with --nocheck"
|
||||||
|
return nil, fmt.Errorf(errMsg, len(packages), explicitCount)
|
||||||
|
}
|
||||||
|
|
||||||
list, err := read.List()
|
list, err := read.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -35,6 +35,25 @@ func List() ([]string, error) {
|
|||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExplicitList() ([]string, error) {
|
||||||
|
start := time.Now()
|
||||||
|
log.Debug("ExplicitList: starting...")
|
||||||
|
|
||||||
|
cmd := exec.Command("pacman", "-Qqe")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
list := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||||
|
if len(list) > 0 && list[0] == "" {
|
||||||
|
list = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("ExplicitList: done (%.2fs)", time.Since(start).Seconds())
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ListOrphans() ([]string, error) {
|
func ListOrphans() ([]string, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Debug("ListOrphans: starting...")
|
log.Debug("ListOrphans: starting...")
|
||||||
|
|||||||
Reference in New Issue
Block a user