Skip to content

Commit

Permalink
actions: install-dpkg: Implement action to install local Debian packages
Browse files Browse the repository at this point in the history
The install-dpkg action allows local .deb packages to be installed using
the apt command, much like the apt action but for local packages rather
than for packages retrieved from remote apt repositories.

Resolves: #157
Closes: #165
Signed-off-by: Christopher Obbard <chris.obbard@collabora.com>
  • Loading branch information
obbardc committed Jul 27, 2023
1 parent 2308a42 commit 493a65a
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Some of the actions provided by debos to customize and produce images are:
* download: download a single file from the internet
* filesystem-deploy: deploy a root filesystem to an image previously created
* image-partition: create an image file, make partitions and format them
* install-dpkg: install packages and their dependencies from local 'deb' files
* ostree-commit: create an OSTree commit from rootfs
* ostree-deploy: deploy an OSTree branch to the image
* overlay: do a recursive copy of directories or files to the target filesystem
Expand Down
182 changes: 182 additions & 0 deletions actions/install_dpkg_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
InstallDpkg Action
Install packages from .deb files and their dependencies to the target rootfs
using 'apt'.
Dependencies will be satisfied first from the `packages` list (i.e. locally
available packages) and then from the target's configured apt repositories. If
`deps` is set to false, dependencies will not be installed and an error will be
thrown. TODO: check this
Attempting to downgrade packages which are already installed is not allowed and
will throw an error.
# Yaml syntax:
- action: install-dpkg
origin: name
recommends: bool
unauthenticated: bool
deps: bool
packages:
- package_path.deb
- *.deb
Mandatory properties:
- packages -- list of package files to install from the filesystem (or named
origin). Resolves Unix-style glob patterns. If installing from a named origin,
e.g. the result of a download action, the package path will be automatically
generated from the origin contents and the `packages` property can be omitted.
Optional properties:
- origin -- reference to named file or directory. Defaults to recipe directory.
- recommends -- boolean indicating if suggested packages will be installed. Defaults to false.
- unauthenticated -- boolean indicating if unauthenticated packages can be installed. Defaults to false.
- update -- boolean indicating if `apt update` will be run. Default 'true'.
Example to install all packages from recipe subdirectory `pkgs/`:
- action: install-dpkg
description: Install Debian packages from local recipe
packages:
- pkgs/*.deb
Example to install named packages from recipe subdirectory `pkgs/`:
- action: install-dpkg
description: Install Debian packages from local recipe
packages:
- pkgs/bmap-tools_*_all.deb
- pkgs/fakemachine_*_amd64.deb
Example to download and install a package:
- action: download
description: Install Debian package from url
url: http://ftp.us.debian.org/debian/pool/main/b/bmap-tools/bmap-tools_3.5-2_all.deb
name: bmap-tools-pkg
- action: install-dpkg
description: Install Debian package from url
origin: bmap-tools-pkg
*/

package actions

import (
"fmt"
"log"
"os"
"path"
"path/filepath"
"strings"

"github.com/go-debos/debos"
"github.com/go-debos/debos/wrapper"
)

type InstallDpkgAction struct {
debos.BaseAction `yaml:",inline"`
Recommends bool
Unauthenticated bool
Update bool
Origin string
Packages []string
}

func NewInstallDpkgAction() *InstallDpkgAction {
a := &InstallDpkgAction{Update: true}
return a
}

func (apt *InstallDpkgAction) Run(context *debos.DebosContext) error {
apt.LogStart()

aptCommand := wrapper.NewAptCommand(*context, "install-dpkg")

/* check if named origin exists or fallback to RecipeDir if no origin set */
var origin string = context.RecipeDir
if len(apt.Origin) > 0 {
var found bool
if origin, found = context.Origins[apt.Origin]; !found {
return fmt.Errorf("origin %s not found", apt.Origin)
}
}

/* create a list of full paths of packages to install: if the origin is a
* single file (e.g download action) then just return that package, otherwise
* append package name to the origin path and glob to create a list of packages.
* In other words, install all packages which are in the origin's directory.
*/
packages := []string{}
file, err := os.Stat(origin)
if err != nil {
return err
}

if file.IsDir() {
if len(apt.Packages) == 0 {
return fmt.Errorf("no packages defined")
}

for _, pkg := range apt.Packages {
// resolve globs
source := path.Join(origin, pkg)
matches, err := filepath.Glob(source)
if err != nil {
return err
}
if len(matches) == 0 {
return fmt.Errorf("file(s) not found after globbing: %s", pkg)
}

packages = append(packages, matches...)
}
} else {
packages = append(packages, origin)
}

/* bind mount each package into rootfs & update the list with the
* path relative to the chroot */
for idx, pkg := range packages {
// check for duplicates after globbing
for j := idx + 1; j < len(packages); j++ {
if packages[j] == pkg {
return fmt.Errorf("duplicate package found: %s", pkg)
}
}

log.Printf("Installing %s", pkg)

/* Only bind mount the package if the file is outside the rootfs */
if strings.HasPrefix(pkg, context.Rootdir) {
pkg = strings.TrimPrefix(pkg, context.Rootdir)
} else {
aptCommand.AddBindMount(pkg, "")
}

/* update pkg list with the complete resolved path */
packages[idx] = pkg
}

if apt.Update {
if err := aptCommand.Update(); err != nil {
return err
}
}

if err := aptCommand.Install(packages, apt.Recommends, apt.Unauthenticated); err != nil {
return err
}

if err := aptCommand.Clean(); err != nil {
return err
}

return nil
}
4 changes: 4 additions & 0 deletions actions/recipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Supported actions
- image-partition -- https://godoc.org/github.com/go-debos/debos/actions#hdr-ImagePartition_Action
- install-dpkg -- https://godoc.org/github.com/go-debos/debos/actions#hdr-InstallDpkg_Action
- ostree-commit -- https://godoc.org/github.com/go-debos/debos/actions#hdr-OstreeCommit_Action
- ostree-deploy -- https://godoc.org/github.com/go-debos/debos/actions#hdr-OstreeDeploy_Action
Expand Down Expand Up @@ -124,6 +126,8 @@ func (y *YamlAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
y.Action = &OverlayAction{}
case "image-partition":
y.Action = &ImagePartitionAction{}
case "install-dpkg":
y.Action = NewInstallDpkgAction()
case "filesystem-deploy":
y.Action = NewFilesystemDeployAction()
case "raw":
Expand Down
1 change: 1 addition & 0 deletions actions/recipe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ actions:
- action: download
- action: filesystem-deploy
- action: image-partition
- action: install-dpkg
- action: ostree-commit
- action: ostree-deploy
- action: overlay
Expand Down

0 comments on commit 493a65a

Please sign in to comment.