ian

package module
v2.0.1+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 20, 2018 License: MIT Imports: 14 Imported by: 0

README

go-ian

Simple debian package building and management named in memory of the late Ian Murdock, founder of the Debian project.

The purpose of this tool is to decrease the overhead in maintaining a debian package stored in a git repository. It tries to mimic the CLI of other popular tools such as git and bundler. It is intended to be helpful when integrating other build tools/systems and with CI/CD.

It has been ported to golang from the ruby project of the same name.

You can download binaries and Debian packages from the releases page.

Requirements

I shell out a bit to save time, will eventually make things more native. For now, need the following tools:

  • dpkg-deb
  • fakeroot
  • rsync

This should do it.

sudo apt-get install fakeroot dpkg-dev rsync coreutils findutils

Installation

Simple to build / install, provided you have go setup:

go get github.com/penguinpowernz/go-ian
go install github.com/penguinpowernz/go-ian/cmd/ian

Or you can download a pre-built binary or Debian package from the releases page.

Usage

This tool is used for working with what Debian called "Binary packages" - that is ones that have the DEBIAN folder in capitals to slap Debian packages together quickly. It uses dpkg-deb -b in the background which most Debian package maintainers frown at but it is suitable enough for rolling your own packages quickly, and it scratches an itch.

Creation/Initializing
ian new my-package

Analogous to bundle new gemname or rails new appname this will create a folder called my-package in the current folder and initialize it with the appropriate debian files.

ian init

Analagous to git init this will do the same as new but do it in the current folder.

Now you will see you have a DEBIAN folder with a control and postinst file.

Info
ian info

This will simply dump the control file contents out.

Set fields in the control file

The architecture and the version can be set quickly in this manner. Other fields are not (yet) supported.

ian set -a amd64
ian set -v 1.2.3-test

You can also use increments on semantic versions like so:

ian set -v +M    # increment the Major number
ian set -v +m    # increment the minor number
ian set -v +p    # increment patch level
Packaging
ian pkg [-b]

The one you came here for. Packages the repo in a debian package, excluding junk files like .git and .gitignore, moves root files (like README.md) to a /usr/share/doc folder so you don't dirty your root partition on install.
The package will be output to a pkg directory in the root of the repo. It will also generate the md5sums file and calculate the package size proir to packaging. By adding a -b flag it will run the build script before packaging.

Build
ian build

This will run the build script found in DEBIAN/build parsing it the following arguments:

  • root directory of the package git repository
  • architecture from the control file
  • version from the control file

It can do whatever things you need it to do to prepare for the packaging such as building binaries, etc.

Push
ian push [target]

Setup scripts to run in a file called .ianpush in the repo root and running ian push will run all the lines in the file as commands with the current package. The package filename will be appended to each command unless $PKG is found on the line, in which case that will be replaced with the package filename. Also the target name can be given as an argument to push to specfic targets (supports globbing).

package_cloud push user/app-testing/debian/wheezy
stable: package_cloud push user/app-stable/debian/wheezy
devbox: scp $PKG root@192.168.1.200:~
s3: aws s3 cp $PKG s3://mybucket/dpkg/

Note that targets requiring input will fail as there is no terminal attached to the command. For SCP, it is recommended to use the SSH config files to your advantage.

Other

Some other commands:

ian install     # installs the current package
ian excludes    # shows the excluded files
ian size        # calculates the package size (in kB)
ian -v          # prints the ian version
ian version     # prints the package version
ian versions    # prints all known versions
ian deps        # prints the dependencies line by line
bpi             # run build, pkg, install
pi              # run pkg, install
pp [target]     # run pkg, push
bp              # run build, pkg
bpp [target]    # run build, pkg push

You can also use the envvar IAN_DIR in the same way that you would use GIT_DIR - that is, to do stuff with ian but from a different folder location.

Use DEBUG=1 ian pkg to show debug logs for ian commands.

Library Usage

GoDoc

The Debian package Control struct could come in handy for others. As a quick overview here's what it can do:

  • Parse([]byte) (Control, error) - parse the bytes from the control file
  • Read(string) (Control, error) - read the given file and parse it's contents
  • Default() (Control) - a default package control file
  • ctrl.Filename() string - the almost Debian standard filename (missing distro name)
  • ctrl.String() string - render the control file as a string
  • ctrl.WriteFile(string) error - write the string into the given filename

Plus the exported fields on the Control struct that mirror the dpkg field names.

For more information please check the godocs.

Dogfooding

The debian package source for Ian is actually managed by Ian in the folder dpkg. So you can build the debian package for ian, using ian. Give it a try!

go get github.com/penguinpowernz/go-ian
go install github.com/penguinpowernz/go-ian/cmd/ian
cd $GOPATH/src/github.com/penguinpowernz/go-ian/dpkg
ian build
ian pkg
sudo $GOBIN/ian install # or sudo dpkg -i pkg/ian_v1.0.0_amd64.deb

TODO

  • more tests
  • add help page
  • add subcommands help
  • releasing
  • pushing
  • test pushing
  • ignore file
  • allow specifying where to output the package to after building
  • deps management
  • running of a build script
  • install after packaging
  • package a specific version
  • optional semver enforcement
  • utilize rules file
  • support copyright file
  • support changelog
  • don't shell out for md5sums
  • don't shell out for rsync
  • don't shell out for find
  • don't shell out for dpkg-deb
  • pull maintainer from git config

Contributor Code of Conduct

This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters.

For more information please visit the No Code of Conduct homepage.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/penguinpowernz/ian.

In Memory Of

In memory of Ian Ashley Murdock (1973 - 2015) founder of the Debian project.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CalculateMD5Sums = func(br *BuildRequest) error {
	outfile := (&Pkg{dir: br.tmp}).CtrlDir("md5sums")
	sums, err := md5walk.Walk(br.tmp)
	if err != nil {
		return fmt.Errorf("failed to generate md5sums: %s", err)
	}

	f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		return fmt.Errorf("failed to write md5sums: %s", err)
	}

	_, err = sums.Write(f)
	return err
}

CalculateMD5Sums is a packaging step that calculates the file sums

View Source
var CalculateSize = func(br *BuildRequest) error {
	b, err := file.DirSize(br.tmp, br.pkg.Excludes())
	if err != nil {
		return fmt.Errorf("failed to calculate package size: %s", err)
	}

	br.pkg.ctrl.Size = strconv.Itoa(b / 1024)
	br.pkg.ctrl.WriteFile((&Pkg{dir: br.tmp}).CtrlFile())
	br.pkg.ctrl.WriteFile(br.pkg.CtrlFile())
	return nil
}

CalculateSize of a directory using du, excluding any given paths

View Source
var CleanRoot = func(br *BuildRequest) error {
	list, err := file.ListFilesIn(br.tmp)
	if err != nil {
		return fmt.Errorf("failed to find root files: %s", err)
	}

	docpath := filepath.Join(br.tmp, "usr", "share", "doc", br.pkg.ctrl.Name)
	if err := os.MkdirAll(docpath, 0755); err != nil {
		return fmt.Errorf("failed to create the doc path %s: %s", docpath, err)
	}

	return file.MoveFiles(list, docpath)
}

CleanRoot is a packaging step to clean the root folder of the package so that the target root file system is not polluted

View Source
var Debug = false

Debug is the default debug mode for the build options when they are not explicity specified with the BuildWithOpts() call

View Source
var DpkgDebBuild = func(br *BuildRequest) error {
	if br.debpath == "" {
		br.debpath = br.pkg.Dir("pkg")
	}

	if err := os.MkdirAll(br.debpath, 0755); err != nil {
		return fmt.Errorf("failed to make package dir at %s: %s", br.debpath, err)
	}

	if err := os.Chmod(br.pkg.dir, 0755); err != nil {
		return fmt.Errorf("failed to set the proper perms on the control dir")
	}

	for _, fpath := range br.pkg.CtrlFiles() {
		if err := os.Chmod(fpath, 0755); err != nil {
			return fmt.Errorf("failed to set the proper perms on the control file %s", fpath)
		}
	}

	br.debpath = filepath.Join(br.debpath, br.pkg.ctrl.Filename())

	cmd := exec.Command("/usr/bin/fakeroot", "dpkg-deb", "-b", br.tmp, br.debpath)
	if br.Debug {
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stderr
	}

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to build package %s from %s: %s", br.debpath, br.tmp, err)
	}

	return nil
}

DpkgDebBuild is a packaging step that builds the package using dpkg-deb

View Source
var PrintPackageTree = func(br *BuildRequest) error {
	if !Debug {
		return nil
	}

	os.Stderr.WriteString("\nResultant Package Tree\n")
	os.Stderr.WriteString("-------------------------------------------------\n")
	for _, fn := range file.Glob(br.tmp, "**") {
		os.Stderr.WriteString(strings.Replace(fn, br.tmp+"/", "", -1) + "\n")
	}
	os.Stderr.WriteString("-------------------------------------------------\n\n")

	return nil
}
View Source
var StageFiles = func(br *BuildRequest) error {
	var err error
	br.tmp, err = ioutil.TempDir("/tmp", "go-ian")
	if err != nil {
		return fmt.Errorf("couldn't make tmp dir: %s", err)
	}

	args := []string{"-rav"}
	for _, s := range br.pkg.Excludes() {
		if s == "" {
			continue
		}
		args = append(args, fmt.Sprintf("--exclude=%s", s))
	}
	args = append(args, br.pkg.Dir()+"/", br.tmp)

	cmd := exec.Command("/usr/bin/rsync", args...)
	if br.Debug {
		os.Stderr.WriteString("\nStaging files to " + br.tmp + "\n")
		os.Stderr.WriteString("-------------------------------------------------\n")
		tell.Debugf("running: %s", str.CommandString(cmd))
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stderr
	}

	err = cmd.Run()

	if br.Debug {
		os.Stderr.WriteString("-------------------------------------------------\n\n")
	}

	return err
}

StageFiles is a packaging step that stages the package files to a temporary directory to work from

Functions

func FindMaintainer

func FindMaintainer() (string, bool)

func Ignored

func Ignored(dir string) ([]string, error)

Ignored will return the ignore patterns from the .ianignore file

func Initialize

func Initialize(dir string) error

Initialize will turn the given directory into an ian repo

func IsInitialized

func IsInitialized(dir string) bool

IsInitialized determines if the directory is already initialized

func Push

func Push(pushFile, pkg string, slctr string) error

Push will run the given pkg name against the commands found in pushFile given. This is meant to be used to push the packages to repositories

Types

type BuildOpts

type BuildOpts struct {
	Outpath string
	Debug   bool
}

type BuildRequest

type BuildRequest struct {
	Debug bool
	// contains filtered or unexported fields
}

BuildRequest is like a context object for packager strategies to make us of and share knowledge

func (*BuildRequest) CleanUp

func (br *BuildRequest) CleanUp()

CleanUp is run at the end of the package build to clean up any leftover resources

type Packager

type Packager []PackagerStrategy

Packager is a collection of packaging steps/strategies that can be used together to build a package

func DefaultPackager

func DefaultPackager() (p Packager)

DefaultPackager returns a preconfigured packager using the default packaging steps/strategies

func (Packager) Build

func (pkgr Packager) Build(p *Pkg) (string, error)

Build will create a debian package from the given control file and directory. It does this by using rsync to copy the repo to a temp dir, excluded unwanted files and moving any files in the root of the package to a /usr/share/doc folder. Then it calculates the package size, file checksums and calls dpkg-deb to build the package. The path to the package and an error (if any) is returned.

func (Packager) BuildWithOpts

func (pkgr Packager) BuildWithOpts(p *Pkg, opts BuildOpts) (string, error)

BuildWithOpts does the same as build but with specifc options

type PackagerStrategy

type PackagerStrategy func(br *BuildRequest) error

PackagerStrategy is a function that represents a strategy or stage in the packaging process

type Pkg

type Pkg struct {
	// contains filtered or unexported fields
}

Pkg represents a ian debian package with helpers for various operations around managing a debian package with ian

func NewPackage

func NewPackage(dir string) (p *Pkg, err error)

NewPackage returns a new Pkg object with the control file contained within, when given a directory

func (*Pkg) BuildCommand

func (p *Pkg) BuildCommand() *exec.Cmd

BuildCommand returns the command to run in order to run the packages build script

func (*Pkg) BuildFile

func (p *Pkg) BuildFile() string

BuildFile returns the filepath to the build script file

func (*Pkg) Ctrl

func (p *Pkg) Ctrl() *control.Control

Ctrl returns the control file as a control object

func (*Pkg) CtrlDir

func (p *Pkg) CtrlDir(paths ...string) string

CtrlDir returns the path to the control dir in the given directory. Optional extra paths will result in getting a path inside the control dir e.g. to the postinst file

func (*Pkg) CtrlFile

func (p *Pkg) CtrlFile() string

CtrlFile returns the path to the control file in the given directory

func (*Pkg) CtrlFiles

func (p *Pkg) CtrlFiles() []string

CtrlFiles returns a list of all the files in the control dir

func (*Pkg) DebFile

func (p *Pkg) DebFile() string

DebFile returns the filepath to where the debian package should be placed after building it

func (*Pkg) Dir

func (p *Pkg) Dir(paths ...string) string

Dir returns the path to the repo root directory. Optional extra paths will result in getting a path inside the dir

func (*Pkg) DocPath

func (p *Pkg) DocPath() string

DocPath returns the path to the packages doc folder

func (*Pkg) Excludes

func (p *Pkg) Excludes() []string

Excludes provides things in the repo to be excluded from the package

func (*Pkg) IgnoreFile

func (p *Pkg) IgnoreFile() string

IgnoreFile returns the path to the packages ignore file

func (*Pkg) IgnoreList

func (p *Pkg) IgnoreList() []string

IgnoreList will use the ignore file from the package to generate a list of ignored file patterns. If there is no ignore file then an empty slice is returned

func (*Pkg) Initialized

func (p *Pkg) Initialized() bool

Initialized will return true if the package has been initialized

func (*Pkg) PushFile

func (p *Pkg) PushFile() string

PushFile returns the filepath to the push file for this package

func (*Pkg) Size

func (p *Pkg) Size() (string, error)

Size returns the total size of the files to be included in the package

Directories

Path Synopsis
cmd
ian
debian
str

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL