NewStatusCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"status",
		abroot.Trans("status.long"),
		abroot.Trans("status.short"),
		status,
	)

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"json",
			"j",
			abroot.Trans("status.jsonFlag"),
			false))

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"dump",
			"d",
			abroot.Trans("status.dumpFlag"),
			false))

	cmd.Example = "abroot status"

	return cmd
}

status function

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	if !core.RootCheck(false) {
		cmdr.Error.Println(abroot.Trans("status.rootRequired"))
		return nil
	}

	jsonFlag, err := cmd.Flags().GetBool("json")
	if err != nil {
		return err
	}

	dumpFlag, err := cmd.Flags().GetBool("dump")
	if err != nil {
		return err
	}

	a := core.NewABRootManager()
	present, err := a.GetPresent()
	if err != nil {
		return err
	}

	future, err := a.GetFuture()
	if err != nil {
		return err
	}

	specs := core.GetPCSpecs()
	abImage, err := core.NewABImageFromRoot()
	if err != nil {
		return err
	}

	kargs, err := core.KargsRead()
	if err != nil {
		return err
	}

	pkgMngAgreementStatus := false
	pkgMng, err := core.NewPackageManager(false)
	if err != nil {
		return err
	}
	if pkgMng.Status == core.PKG_MNG_REQ_AGREEMENT {
		err = pkgMng.CheckStatus()
		pkgMngAgreementStatus = err == nil
	}
	pkgsAdd, err := pkgMng.GetAddPackages()
	if err != nil {
		return err
	}
	pkgsRm, err := pkgMng.GetRemovePackages()
	if err != nil {
		return err
	}
	pkgsUnstg, err := pkgMng.GetUnstagedPackagesPlain()
	if err != nil {
		return err
	}

	if jsonFlag || dumpFlag {
		type status struct {
			Present         string       `json:"present"`
			Future          string       `json:"future"`
			CnfFile         string       `json:"cnfFile"`
			CPU             string       `json:"cpu"`
			GPU             []string     `json:"gpu"`
			Memory          string       `json:"memory"`
			ABImage         core.ABImage `json:"abimage"`
			Kargs           string       `json:"kargs"`
			PkgsAdd         []string     `json:"pkgsAdd"`
			PkgsRm          []string     `json:"pkgsRm"`
			PkgsUnstg       []string     `json:"pkgsUnstg"`
			PkgMngStatus    int          `json:"pkgMngStatus"`
			PkgMngAgreement bool         `json:"pkgMngAg"`
		}

		s := status{
			Present:         present.Label,
			Future:          future.Label,
			CnfFile:         settings.CnfFileUsed,
			CPU:             specs.CPU,
			GPU:             specs.GPU,
			Memory:          specs.Memory,
			ABImage:         *abImage,
			Kargs:           kargs,
			PkgsAdd:         pkgsAdd,
			PkgsRm:          pkgsRm,
			PkgsUnstg:       pkgsUnstg,
			PkgMngStatus:    settings.Cnf.IPkgMngStatus,
			PkgMngAgreement: pkgMngAgreementStatus,
		}

		b, err := json.Marshal(s)
		if err != nil {
			return err
		}

		if jsonFlag {
			fmt.Println(string(b))
			return nil
		}

		tarballPath := fmt.Sprintf("/tmp/abroot-status-%s.tar.gz", uuid.New().String())
		tarballFile, err := os.Create(tarballPath)
		if err != nil {
			return err
		}
		defer tarballFile.Close()

		gzipWriter := gzip.NewWriter(tarballFile)
		defer gzipWriter.Close()

		tarWriter := tar.NewWriter(gzipWriter)
		defer tarWriter.Close()

		tarHeader := &tar.Header{
			Name: "status.json",
			Mode: 0o644,
			Size: int64(len(b)),
		}
		err = tarWriter.WriteHeader(tarHeader)
		if err != nil {
			return err
		}
		_, err = tarWriter.Write(b)
		if err != nil {
			return err
		}

		err = filepath.Walk("/var/log/", func(path string, info os.FileInfo, err error) error {
			if strings.Contains(path, "abroot.log") {
				relPath, err := filepath.Rel("/var/log/", path)
				if err != nil {
					return err
				}
				tarHeader := &tar.Header{
					Name: filepath.Join("logs", relPath),
					Mode: 0o644,
					Size: info.Size(),
				}
				err = tarWriter.WriteHeader(tarHeader)
				if err != nil {
					return err
				}
				logFile, err := os.Open(path)
				if err != nil {
					return err
				}
				defer logFile.Close()
				_, err = io.Copy(tarWriter, logFile)
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			return err
		}

		cmdr.Info.Printf(abroot.Trans("status.dumpMsg"), tarballPath)
		return nil
	}

	formattedGPU := ""
	for _, gpu := range specs.GPU {
		formattedGPU += fmt.Sprintf("\n\t\t- %s", gpu)
	}

	unstagedAlert := ""
	if len(pkgsUnstg) > 0 {
		unstagedAlert = abroot.Trans("status.unstagedFoundMsg", len(pkgsUnstg))
	}

	presentMark, futureMark, err := getCurrentlyBootedPartition(a)
	if err != nil {
		return err
	}

	// ABRoot partitions:
	cmdr.Bold.Println(abroot.Trans("status.partitions.title"))
	cmdr.BulletList.WithItems([]cmdr.BulletListItem{
		{Level: 1, Text: abroot.Trans("status.partitions.present", present.Label, presentMark)},
		{Level: 1, Text: abroot.Trans("status.partitions.future", future.Label, futureMark)},
	}).Render()

	// Loaded Configuration: ...
	cmdr.Bold.Print(abroot.Trans("status.loadedConfig") + " ")
	cmdr.FgDefault.Println(settings.CnfFileUsed)
	fmt.Println()

	// Device Specification:
	cmdr.Bold.Println(abroot.Trans("status.specs.title"))
	cmdr.BulletList.WithItems([]cmdr.BulletListItem{
		{Level: 1, Text: abroot.Trans("status.specs.cpu", specs.CPU)},
		{Level: 1, Text: abroot.Trans("status.specs.gpu", specs.GPU)},
		{Level: 1, Text: abroot.Trans("status.specs.memory", specs.Memory)},
	}).Render()

	// ABImage:
	cmdr.Bold.Println(abroot.Trans("status.abimage.title"))
	cmdr.BulletList.WithItems([]cmdr.BulletListItem{
		{Level: 1, Text: abroot.Trans("status.abimage.digest", abImage.Digest)},
		{Level: 1, Text: abroot.Trans("status.abimage.timestamp", abImage.Timestamp.Format("2006-01-02 15:04:05"))},
		{Level: 1, Text: abroot.Trans("status.abimage.image", abImage.Image)},
	}).Render()

	// Kernel Arguments: ...
	cmdr.Bold.Printf(abroot.Trans("status.kargs") + " ")
	cmdr.FgDefault.Println(kargs)
	cmdr.FgDefault.Println()

	// Packages:
	cmdr.Bold.Println(abroot.Trans("status.packages.title"))
	cmdr.BulletList.WithItems([]cmdr.BulletListItem{
		{Level: 1, Text: abroot.Trans("status.packages.added", strings.Join(pkgsAdd, ", "))},
		{Level: 1, Text: abroot.Trans("status.packages.removed", strings.Join(pkgsRm, ", "))},
		{Level: 1, Text: abroot.Trans("status.packages.unstaged", strings.Join(pkgsUnstg, ", "), unstagedAlert)},
	}).Render()

	// Package Agreement: ...
	cmdr.Bold.Print(abroot.Trans("status.agreementStatus") + " ")
	cmdr.FgDefault.Println(pkgMngAgreementStatus)

	return nil
}

getCurrentlyBootedPartition function

Parameters:

  • a *core.ABRootManager

Returns:

  • string
  • string
  • error
Show/Hide Function Body
{
	bootPart, err := a.GetBoot()
	if err != nil {
		return "", "", err
	}
	uuid := uuid.New().String()
	tmpBootMount := filepath.Join("/tmp", uuid)
	err = os.Mkdir(tmpBootMount, 0o755)
	if err != nil {
		return "", "", err
	}
	err = bootPart.Mount(tmpBootMount)
	if err != nil {
		return "", "", err
	}
	defer bootPart.Unmount()

	g, err := core.NewGrub(bootPart)
	if err != nil {
		return "", "", err
	}
	isPresent, err := g.IsBootedIntoPresentRoot()
	if err != nil {
		return "", "", err
	}

	presentMark := ""
	futureMark := ""
	if isPresent {
		presentMark = " ✓"
	} else {
		futureMark = " ✓"
	}

	return presentMark, futureMark, nil
}

VarInvalidError struct

Fields:

  • passedDisk (string)

Methods:

Error


Returns:
  • string

Show/Hide Method Body
{
	return "the /var disk " + e.passedDisk + " does not exist"
}

NotEncryptedError struct

Methods:

Error


Returns:
  • string

Show/Hide Method Body
{
	return "the var partition is not encrypted"
}

NewUnlockVarCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"unlock-var",
		"",
		"",
		unlockVarCmd,
	)

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"dry-run",
			"d",
			"perform a dry run of the operation",
			false,
		),
	)

	// this is just meant for compatability with old Installations
	cmd.WithStringFlag(
		cmdr.NewStringFlag(
			"var-disk",
			"m",
			"pass /var disk directly instead of reading from configuration",
			"",
		),
	)

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"check-encrypted",
			"c",
			"check if drive is encrypted and return",
			false,
		),
	)

	cmd.Example = "abroot unlock-var"

	cmd.Hidden = true

	return cmd
}

unlockVarCmd function

helper function which only returns syntax errors and prints other ones

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	err := unlockVar(cmd, args)
	if err != nil {
		cmdr.Error.Println(err)
		os.Exit(1)
		return nil
	}
	return nil
}

unlockVar function

Parameters:

  • cmd *cobra.Command
  • _ []string

Returns:

  • error
Show/Hide Function Body
{
	if !core.RootCheck(false) {
		cmdr.Error.Println("You must be root to run this command.")
		os.Exit(2)
		return nil
	}

	varDisk, err := cmd.Flags().GetString("var-disk")
	if err != nil {
		return err
	}

	check_only, err := cmd.Flags().GetBool("check-encrypted")
	if err != nil {
		return err
	}

	_, err = os.Stat(filepath.Join("/dev/disk/by-label/", settings.Cnf.PartLabelVar))
	if err == nil || !errors.Is(err, os.ErrNotExist) {
		return &NotEncryptedError{}
	}
	if check_only {
		cmdr.Info.Println("The var partition is encrypted.")
		return nil
	}

	if varDisk == "" {
		if settings.Cnf.PartCryptVar == "" {
			cmdr.Error.Println("Encrypted var partition not found in configuration.")
			os.Exit(3)
			return nil
		}

		varDisk = settings.Cnf.PartCryptVar
	}

	dryRun, err := cmd.Flags().GetBool("dry-run")
	if err != nil {
		return err
	}

	partitions, err := core.NewDiskManager().GetPartitions("")
	if err != nil {
		return err
	}

	var varLuksPart core.Partition
	foundPart := false

	for _, partition := range partitions {
		devName := "/dev/"
		if partition.IsDevMapper() {
			devName += "mapper/"
		}
		devName += partition.Device

		if devName == varDisk {
			varLuksPart = partition
			foundPart = true
			break
		}
	}
	if !foundPart {
		return &VarInvalidError{varDisk}
	}

	uuid := varLuksPart.Uuid
	cmdr.FgDefault.Println("unlocking", varDisk)

	if dryRun {
		cmdr.Info.Println("Dry run complete.")
	} else {
		cryptsetupCmd := exec.Command("/usr/sbin/cryptsetup", "luksOpen", varDisk, "luks-"+uuid)
		cryptsetupCmd.Stdin = os.Stdin
		cryptsetupCmd.Stderr = os.Stderr
		cryptsetupCmd.Stdout = os.Stdout
		err := cryptsetupCmd.Run()
		if err != nil {
			return err
		}
		cmdr.Info.Println("The system mounts have been performed successfully.")
	}

	return nil
}

NewUpdateInitfsCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"update-initramfs",
		abroot.Trans("updateInitramfs.long"),
		abroot.Trans("updateInitramfs.short"),
		updateInitramfs,
	)

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"dry-run",
			"d",
			abroot.Trans("updateInitramfs.dryRunFlag"),
			false))

	cmd.Example = "abroot update-initramfs"

	return cmd
}

updateInitramfs function

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	if !core.RootCheck(false) {
		cmdr.Error.Println(abroot.Trans("updateInitramfs.rootRequired"))
		return nil
	}

	dryRun, err := cmd.Flags().GetBool("dry-run")
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	aBsys, err := core.NewABSystem()
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	if dryRun {
		err = aBsys.RunOperation(core.DRY_RUN_INITRAMFS)
	} else {
		err = aBsys.RunOperation(core.INITRAMFS)
	}
	if err != nil {
		cmdr.Error.Printf(abroot.Trans("updateInitramfs.updateFailed"), err)
		return err
	}

	cmdr.Info.Println(abroot.Trans("updateInitramfs.updateSuccess"))

	return nil
}

NewRollbackCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"rollback",
		abroot.Trans("rollback.long"),
		abroot.Trans("rollback.short"),
		rollback,
	)

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"check-only",
			"c",
			abroot.Trans("rollback.checkOnlyFlag"),
			false))

	cmd.Example = "abroot rollback"

	return cmd
}

rollback function

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	if !core.RootCheck(false) {
		cmdr.Error.Println(abroot.Trans("rollback.rootRequired"))
		return nil
	}

	checkOnly, err := cmd.Flags().GetBool("check-only")
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	aBsys, err := core.NewABSystem()
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	response, err := aBsys.Rollback(checkOnly)
	switch response {
	case core.ROLLBACK_RES_YES:
		// NOTE: the following strings could lead to misinterpretation, with
		// "can" and "cannot", we don't mean "is it possible to rollback?",
		// but "is it necessary to rollback?"
		cmdr.Info.Println(abroot.Trans("rollback.canRollback"))
		os.Exit(0)
	case core.ROLLBACK_RES_NO:
		cmdr.Info.Println(abroot.Trans("rollback.cannotRollback"))
		os.Exit(1)
	case core.ROLLBACK_UNNECESSARY:
		cmdr.Info.Println(abroot.Trans("rollback.rollbackUnnecessary"))
		os.Exit(0)
	case core.ROLLBACK_SUCCESS:
		cmdr.Info.Println(abroot.Trans("rollback.rollbackSuccess"))
		os.Exit(0)
	case core.ROLLBACK_FAILED:
		cmdr.Info.Println(abroot.Trans("rollback.rollbackFailed", err))
		os.Exit(1)
		return err
	}

	return nil
}

NewKargsCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"kargs edit|show",
		abroot.Trans("kargs.long"),
		abroot.Trans("kargs.short"),
		kargs,
	)

	cmd.Args = cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs)
	cmd.ValidArgs = validKargsArgs
	cmd.Example = "abroot kargs edit"

	return cmd
}

kargs function

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	if !core.RootCheck(false) {
		cmdr.Error.Println(abroot.Trans("kargs.rootRequired"))
		return nil
	}

	switch args[0] {
	case "edit":
		changed, err := core.KargsEdit()
		if err != nil {
			cmdr.Error.Println(err)
			return err
		}

		if !changed {
			cmdr.Info.Println(abroot.Trans("kargs.notChanged"))
			return nil
		}

		aBsys, err := core.NewABSystem()
		if err != nil {
			cmdr.Error.Println(err)
			return err
		}
		err = aBsys.RunOperation(core.APPLY)
		if err != nil {
			cmdr.Error.Println(abroot.Trans("pkg.applyFailed"))
			return err
		}
	case "show":
		kargsStr, err := core.KargsRead()
		if err != nil {
			cmdr.Error.Println(err)
			return err
		}
		cmdr.Info.Println(kargsStr)
	default:
		return errors.New(abroot.Trans("kargs.unknownCommand", args[0]))
	}

	return nil
}

DiskLayoutError struct

Fields:

  • Device (string)

Methods:

Error


Returns:
  • string

Show/Hide Method Body
{
	return fmt.Sprintf("device %s has an unsupported layout", e.Device)
}

PartNotFoundError struct

Fields:

  • Partition (string)

Methods:

Error


Returns:
  • string

Show/Hide Method Body
{
	return fmt.Sprintf("partition %s could not be found", e.Partition)
}

NewMountSysCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"mount-sys",
		"",
		"",
		mountSysCmd,
	)

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"dry-run",
			"d",
			"perform a dry run of the operation",
			false,
		),
	)

	cmd.Example = "abroot mount-sys"

	cmd.Hidden = true

	return cmd
}

mountSysCmd function

helper function which only returns syntax errors and prints other ones

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	err := mountSys(cmd, args)
	if err != nil {
		cmdr.Error.Println(err)
		os.Exit(1)
	}
	return nil
}

mountSys function

Parameters:

  • cmd *cobra.Command
  • _ []string

Returns:

  • error
Show/Hide Function Body
{
	if !core.RootCheck(false) {
		cmdr.Error.Println("This operation requires root.")
		return nil
	}

	dryRun, err := cmd.Flags().GetBool("dry-run")
	if err != nil {
		return err
	}

	manager := core.NewABRootManager()
	present, err := manager.GetPresent()
	if err != nil {
		return err
	}

	err = mountVar(manager.VarPartition, dryRun)
	if err != nil {
		cmdr.Error.Println(err)
		os.Exit(5)
	}

	err = mountBindMounts(dryRun)
	if err != nil {
		cmdr.Error.Println(err)
		os.Exit(6)
	}

	if present.Label == "" {
		return &PartNotFoundError{"current root"}
	}
	err = mountOverlayMounts(present.Label, dryRun)
	if err != nil {
		cmdr.Error.Println(err)
		os.Exit(7)
	}

	if present.Uuid == "" {
		return &PartNotFoundError{"current root"}
	}
	err = adjustFstab(present.Uuid, dryRun)
	if err != nil {
		cmdr.Error.Println(err)
		os.Exit(8)
	}

	if dryRun {
		cmdr.Info.Println("Dry run complete.")
	} else {
		cmdr.Info.Println("The system mounts have been performed successfully.")
	}

	return nil
}

mountVar function

Parameters:

  • varPart core.Partition
  • dryRun bool

Returns:

  • error
Show/Hide Function Body
{
	cmdr.FgDefault.Println("mounting " + varPart.Device + " in /var")

	if varPart.Device == "" {
		return &PartNotFoundError{settings.Cnf.PartLabelVar}
	}

	if !dryRun {
		err := varPart.Mount("/var")
		if err != nil {
			return err
		}
	}

	return nil
}

mountBindMounts function

Parameters:

  • dryRun bool

Returns:

  • error
Show/Hide Function Body
{
	type bindMount struct {
		from, to string
		options  uintptr
	}

	binds := []bindMount{
		{"/var/home", "/home", 0},
		{"/var/opt", "/opt", 0},
		{"/.system/usr", "/.system/usr", syscall.MS_RDONLY},
	}

	for _, bind := range binds {
		cmdr.FgDefault.Println("bind-mounting " + bind.from + " to " + bind.to)
		if !dryRun {
			err := syscall.Mount(bind.from, bind.to, "", syscall.MS_BIND|bind.options, "")
			if err != nil {
				return err
			}
		}
	}

	return nil
}

mountOverlayMounts function

Parameters:

  • rootLabel string
  • dryRun bool

Returns:

  • error
Show/Hide Function Body
{
	type overlayMount struct {
		destination       string
		lowerdirs         []string
		upperdir, workdir string
	}

	overlays := []overlayMount{
		{"/.system/etc", []string{"/.system/etc"}, "/var/lib/abroot/etc/" + rootLabel, "/var/lib/abroot/etc/" + rootLabel + "-work"},
	}

	for _, overlay := range overlays {
		lowerCombined := strings.Join(overlay.lowerdirs, ":")
		options := "lowerdir=" + lowerCombined + ",upperdir=" + overlay.upperdir + ",workdir=" + overlay.workdir

		cmdr.FgDefault.Println("mounting overlay mount " + overlay.destination + " with options " + options)

		if !dryRun {
			err := syscall.Mount("overlay", overlay.destination, "overlay", 0, options)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

adjustFstab function

Parameters:

  • uuid string
  • dryRun bool

Returns:

  • error
Show/Hide Function Body
{
	cmdr.FgDefault.Println("switching the root in fstab")

	const fstabFile = "/etc/fstab"
	systemMounts := []string{"/", "/var", "/.system/usr", "/.system/etc"}

	fstabContentsRaw, err := os.ReadFile(fstabFile)
	if err != nil {
		return err
	}

	fstabContents := string(fstabContentsRaw)

	lines := strings.Split(fstabContents, "\n")

	linesNew := make([]string, 0, len(lines))

	// remove system Mounts if they exist in fstab
	for _, line := range lines {

		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") {
			linesNew = append(linesNew, line)
			continue
		}

		words := strings.Fields(line)
		if len(words) < 2 {
			linesNew = append(linesNew, line)
			continue
		}

		if !slices.Contains[[]string](systemMounts, words[1]) {
			linesNew = append(linesNew, line)
			continue
		}

		cmdr.FgDefault.Println("Deleting line: ", line)
	}

	currentRootLine := "UUID=" + uuid + " / btrfs defaults 0 0"

	cmdr.FgDefault.Println("Adding line: ", currentRootLine)

	linesNew = append([]string{currentRootLine}, linesNew...)

	newFstabContents := strings.Join(linesNew, "\n")

	newFstabFile := fstabFile + ".new"

	if !dryRun {
		cmdr.FgDefault.Println("writing new fstab file")
		err := os.WriteFile(newFstabFile, []byte(newFstabContents), 0o644)
		if err != nil {
			return err
		}
		err = core.AtomicSwap(fstabFile, newFstabFile)
		if err != nil {
			return err
		}
		err = os.Rename(newFstabFile, fstabFile+".old")
		if err != nil {
			cmdr.Warning.Println("Old Fstab file will keep .new suffix")
			// ignore, backup is not neccessary to boot
		}
	}

	return nil
}

NewPkgCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"pkg add|remove|list|apply",
		abroot.Trans("pkg.long"),
		abroot.Trans("pkg.short"),
		pkg,
	)

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"dry-run",
			"d",
			abroot.Trans("pkg.dryRunFlag"),
			false))

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"force-enable-user-agreement",
			"f",
			abroot.Trans("pkg.forceEnableUserAgreementFlag"),
			false))

	cmd.Args = cobra.MinimumNArgs(1)
	cmd.ValidArgs = validPkgArgs
	cmd.Example = "abroot pkg add <pkg>"

	return cmd
}

pkg function

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	if !core.RootCheck(false) {
		cmdr.Error.Println(abroot.Trans("pkg.rootRequired"))
		return nil
	}

	dryRun, err := cmd.Flags().GetBool("dry-run")
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	forceEnableUserAgreement, err := cmd.Flags().GetBool("force-enable-user-agreement")
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	pkgM, err := core.NewPackageManager(false)
	if err != nil {
		cmdr.Error.Println(abroot.Trans("pkg.failedGettingPkgManagerInstance", err))
		return err
	}

	// Check for user agreement, here we could simply call the CheckStatus
	// function which also checks if the package manager is enabled or not
	// since this pkg command is not even added to the root command if the
	// package manager is disabled, but we want to be explicit here to avoid
	// potential hard to debug errors in the future in weird development
	// scenarios. Yeah, trust me, I've been there.
	if pkgM.Status == core.PKG_MNG_REQ_AGREEMENT {
		err = pkgM.CheckStatus()
		if err != nil {
			if !forceEnableUserAgreement {
				cmdr.Info.Println(abroot.Trans("pkg.agreementMsg"))
				reader := bufio.NewReader(os.Stdin)
				answer, _ := reader.ReadString('\n')
				answer = strings.TrimSpace(answer)
				if answer == "y" || answer == "Y" {
					err := pkgM.AcceptUserAgreement()
					if err != nil {
						cmdr.Error.Println(abroot.Trans("pkg.agreementSignFailed"), err)
						return err
					}
				} else {
					cmdr.Info.Println(abroot.Trans("pkg.agreementDeclined"))
					return nil
				}
			} else {
				err := pkgM.AcceptUserAgreement()
				if err != nil {
					cmdr.Error.Println(abroot.Trans("pkg.agreementSignFailed"), err)
					return err
				}
			}
		}
	}

	switch args[0] {
	case "add":
		if len(args) < 2 {
			return errors.New(abroot.Trans("pkg.noPackageNameProvided"))
		}
		for _, pkg := range args[1:] {
			err := pkgM.Add(pkg)
			if err != nil {
				cmdr.Error.Println(err)
				return err
			}
		}
		cmdr.Info.Printf(abroot.Trans("pkg.addedMsg"), strings.Join(args[1:], ", "))
	case "remove":
		if len(args) < 2 {
			return errors.New(abroot.Trans("pkg.noPackageNameProvided"))
		}
		for _, pkg := range args[1:] {
			err := pkgM.Remove(pkg)
			if err != nil {
				cmdr.Error.Println(err)
				return err
			}
		}
		cmdr.Info.Printf(abroot.Trans("pkg.removedMsg"), strings.Join(args[1:], ", "))
	case "list":
		added, err := pkgM.GetAddPackagesString("\n")
		if err != nil {
			cmdr.Error.Println(err)
			return err
		}

		removed, err := pkgM.GetRemovePackagesString("\n")
		if err != nil {
			cmdr.Error.Println(err)
			return err
		}

		cmdr.Info.Printf(abroot.Trans("pkg.listMsg"), added, removed)
		return nil
	case "apply":
		unstaged, err := pkgM.GetUnstagedPackages()
		if err != nil {
			cmdr.Error.Println(err)
			return err
		}

		if len(unstaged) == 0 {
			cmdr.Info.Println(abroot.Trans("pkg.noChanges"))
			return nil
		}

		aBsys, err := core.NewABSystem()
		if err != nil {
			cmdr.Error.Println(err)
			return err
		}

		if dryRun {
			err = aBsys.RunOperation(core.DRY_RUN_APPLY)
		} else {
			err = aBsys.RunOperation(core.APPLY)
		}
		if err != nil {
			cmdr.Error.Printf(abroot.Trans("pkg.applyFailed"), err)
			return err
		}
	default:
		cmdr.Error.Println(abroot.Trans("pkg.unknownCommand", args[0]))
		return nil
	}

	return nil
}

New function

Parameters:

  • version string
  • fs embed.FS

Returns:

  • *cmdr.App
Show/Hide Function Body
{
	abroot = cmdr.NewApp("abroot", version, fs)
	return abroot
}

NewRootCommand function

Parameters:

  • version string

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	root := cmdr.NewCommand(
		abroot.Trans("abroot.use"),
		abroot.Trans("abroot.long"),
		abroot.Trans("abroot.short"),
		nil).
		WithPersistentBoolFlag(
			cmdr.NewBoolFlag(
				verboseFlag,
				"V",
				abroot.Trans("abroot.verboseFlag"),
				false))
	root.Version = version

	return root
}

NewUpgradeCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"upgrade",
		abroot.Trans("upgrade.long"),
		abroot.Trans("upgrade.short"),
		upgrade,
	)

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"check-only",
			"c",
			abroot.Trans("upgrade.checkOnlyFlag"),
			false))

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"dry-run",
			"d",
			abroot.Trans("upgrade.dryRunFlag"),
			false))

	cmd.WithBoolFlag(
		cmdr.NewBoolFlag(
			"force",
			"f",
			abroot.Trans("upgrade.forceFlag"),
			false))

	cmd.Example = "abroot upgrade"

	return cmd
}

upgrade function

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	checkOnly, err := cmd.Flags().GetBool("check-only")
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	dryRun, err := cmd.Flags().GetBool("dry-run")
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	aBsys, err := core.NewABSystem()
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	if checkOnly {
		_, raw := os.LookupEnv("ABROOT_JSON_OUTPUT")
		if !raw {
			cmdr.Info.Println(abroot.Trans("upgrade.checkingSystemUpdate"))
		}

		// Check for image updates
		newDigest, res := aBsys.CheckUpdate()
		sysAdded, sysUpgraded, sysDowngraded, sysRemoved := []diff.PackageDiff{}, []diff.PackageDiff{}, []diff.PackageDiff{}, []diff.PackageDiff{}
		if res {
			if !raw {
				cmdr.Info.Println(abroot.Trans("upgrade.systemUpdateAvailable"))
			}

			sysAdded, sysUpgraded, sysDowngraded, sysRemoved, err = core.BaseImagePackageDiff(aBsys.CurImage.Digest, newDigest)
			if err != nil {
				return err
			}
			if !raw {
				err = renderPackageDiff(sysAdded, sysUpgraded, sysDowngraded, sysRemoved)
				if err != nil {
					return err
				}
			}
		} else if !raw {
			cmdr.Info.Println(abroot.Trans("upgrade.noUpdateAvailable"))
		}

		// Check for package updates
		if !raw {
			cmdr.Info.Println(abroot.Trans("upgrade.checkingPackageUpdate"))
		}
		ovlAdded, ovlUpgraded, ovlDowngraded, ovlRemoved, err := core.OverlayPackageDiff()
		if err != nil {
			return err
		}

		sumChanges := len(ovlAdded) + len(ovlUpgraded) + len(ovlDowngraded) + len(ovlRemoved)
		if sumChanges == 0 && !raw {
			cmdr.Info.Println(abroot.Trans("upgrade.noUpdateAvailable"))
		} else if !raw {
			cmdr.Info.Sprintf(abroot.Trans("upgrade.packageUpdateAvailable"), sumChanges)

			err = renderPackageDiff(ovlAdded, ovlUpgraded, ovlDowngraded, ovlRemoved)
			if err != nil {
				return err
			}
		}

		if raw {
			newDigestIfHasUpdate := ""
			if res {
				newDigestIfHasUpdate = newDigest
			}

			out, err := json.Marshal(map[string]any{
				"hasUpdate": res,
				"newDigest": newDigestIfHasUpdate,
				"systemPackageDiff": map[string][]diff.PackageDiff{
					"added":      sysAdded,
					"upgraded":   sysUpgraded,
					"downgraded": sysDowngraded,
					"removed":    sysRemoved,
				},
				"overlayPackageDiff": map[string][]diff.PackageDiff{
					"added":      ovlAdded,
					"upgraded":   ovlUpgraded,
					"downgraded": ovlDowngraded,
					"removed":    ovlRemoved,
				},
			})
			if err != nil {
				cmdr.Error.Println(err)
			}

			fmt.Println(string(out))
		}

		if !res && sumChanges == 0 {
			os.Exit(1) // No update available
		} else {
			os.Exit(0) // Update available
		}
	}

	if !core.RootCheck(false) {
		cmdr.Error.Println(abroot.Trans("upgrade.rootRequired"))
		return nil
	}

	force, err := cmd.Flags().GetBool("force")
	if err != nil {
		cmdr.Error.Println(err)
		return err
	}

	var operation core.ABSystemOperation
	if force {
		operation = core.FORCE_UPGRADE
	} else if dryRun {
		operation = core.DRY_RUN_UPGRADE
	} else {
		operation = core.UPGRADE
	}

	cmdr.Info.Println(abroot.Trans("upgrade.checkingSystemUpdate"))
	err = aBsys.RunOperation(operation)
	if err != nil {
		if err == core.ErrNoUpdate {
			cmdr.Info.Println(abroot.Trans("upgrade.noUpdateAvailable"))
			return err
		}

		cmdr.Error.Println(err)
		return err
	}

	if dryRun {
		cmdr.Info.Println(abroot.Trans("upgrade.dryRunSuccess"))
	}

	cmdr.Info.Println(abroot.Trans("upgrade.success"))
	os.Exit(0)
	return nil
}

renderPackageDiff function

Parameters:

  • added []diff.PackageDiff
  • upgraded []diff.PackageDiff
  • downgraded []diff.PackageDiff
  • removed []diff.PackageDiff

Returns:

  • error
Show/Hide Function Body
{
	pkgFmt := "%s  '%s' -> '%s'"

	// Calculate largest string for proper alignment
	largestPkgName := 0
	for _, pkgSet := range [][]diff.PackageDiff{added, upgraded, downgraded, removed} {
		for _, pkg := range pkgSet {
			if len(pkg.Name) > largestPkgName {
				largestPkgName = len(pkg.Name)
			}
		}
	}

	for _, pkgSet := range []struct {
		Set    []diff.PackageDiff
		Header string
		Color  cmdr.Color
	}{
		{added, abroot.Trans("upgrade.added"), cmdr.FgGreen},
		{upgraded, abroot.Trans("upgrade.upgraded"), cmdr.FgBlue},
		{downgraded, abroot.Trans("upgrade.downgraded"), cmdr.FgYellow},
		{removed, abroot.Trans("upgrade.removed"), cmdr.FgRed},
	} {
		cmdr.NewStyle(cmdr.Bold, pkgSet.Color).Println(pkgSet.Header + ":")
		bulletItems := []cmdr.BulletListItem{}
		for _, pkg := range pkgSet.Set {
			bulletItems = append(bulletItems, cmdr.BulletListItem{
				Level: 1,
				Text:  fmt.Sprintf(pkgFmt, pkg.Name+strings.Repeat(" ", largestPkgName-len(pkg.Name)), pkg.NewVersion, pkg.PreviousVersion),
			})
		}
		err := cmdr.BulletList.WithItems(bulletItems).Render()
		if err != nil {
			return err
		}
	}

	return nil
}

NewConfCommand function

Returns:

  • *cmdr.Command
Show/Hide Function Body
{
	cmd := cmdr.NewCommand(
		"config-editor",
		abroot.Trans("cnf.long"),
		abroot.Trans("cnf.short"),
		cnf,
	)

	return cmd
}

cnf function

Parameters:

  • cmd *cobra.Command
  • args []string

Returns:

  • error
Show/Hide Function Body
{
	if !core.RootCheck(false) {
		cmdr.Error.Println(abroot.Trans("cnf.rootRequired"))
		return nil
	}

	result, err := core.ConfEdit()
	switch result {
	case core.CONF_CHANGED:
		cmdr.Info.Println(abroot.Trans("cnf.changed"))
	case core.CONF_UNCHANGED:
		cmdr.Info.Println(abroot.Trans("cnf.unchanged"))
	case core.CONF_FAILED:
		cmdr.Error.Println(abroot.Trans("cnf.failed", err))
	}

	return nil
}

archive/tar import

Import example:

import "archive/tar"

compress/gzip import

Import example:

import "compress/gzip"

encoding/json import

Import example:

import "encoding/json"

fmt import

Import example:

import "fmt"

io import

Import example:

import "io"

os import

Import example:

import "os"

path/filepath import

Import example:

import "path/filepath"

strings import

Import example:

import "strings"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/google/uuid import

Import example:

import "github.com/google/uuid"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/abroot/settings import

Import example:

import "github.com/vanilla-os/abroot/settings"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

errors import

Import example:

import "errors"

os import

Import example:

import "os"

os/exec import

Import example:

import "os/exec"

path/filepath import

Import example:

import "path/filepath"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/abroot/settings import

Import example:

import "github.com/vanilla-os/abroot/settings"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

os import

Import example:

import "os"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

errors import

Import example:

import "errors"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

fmt import

Import example:

import "fmt"

os import

Import example:

import "os"

slices import

Import example:

import "slices"

strings import

Import example:

import "strings"

syscall import

Import example:

import "syscall"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/abroot/settings import

Import example:

import "github.com/vanilla-os/abroot/settings"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

bufio import

Import example:

import "bufio"

errors import

Import example:

import "errors"

os import

Import example:

import "os"

strings import

Import example:

import "strings"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

embed import

Import example:

import "embed"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

encoding/json import

Import example:

import "encoding/json"

fmt import

Import example:

import "fmt"

os import

Import example:

import "os"

strings import

Import example:

import "strings"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/differ/diff import

Import example:

import "github.com/vanilla-os/differ/diff"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"

github.com/spf13/cobra import

Import example:

import "github.com/spf13/cobra"

github.com/vanilla-os/abroot/core import

Import example:

import "github.com/vanilla-os/abroot/core"

github.com/vanilla-os/orchid/cmdr import

Import example:

import "github.com/vanilla-os/orchid/cmdr"