16306: Add note about dpkg-scanpackages.
[arvados.git] / cmd / arvados-package / cmd.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "context"
9         "flag"
10         "fmt"
11         "io"
12         "os"
13         "path/filepath"
14         "strings"
15
16         "git.arvados.org/arvados.git/lib/cmd"
17         "git.arvados.org/arvados.git/lib/install"
18         "git.arvados.org/arvados.git/sdk/go/ctxlog"
19 )
20
21 var (
22         handler = cmd.Multi(map[string]cmd.Handler{
23                 "version":   cmd.Version,
24                 "-version":  cmd.Version,
25                 "--version": cmd.Version,
26
27                 "build":       cmdFunc(build),
28                 "testinstall": cmdFunc(testinstall),
29                 "_fpm":        cmdFunc(fpm),    // internal use
30                 "_install":    install.Command, // internal use
31         })
32 )
33
34 func main() {
35         if len(os.Args) < 2 || strings.HasPrefix(os.Args[1], "-") {
36                 parseFlags([]string{"-help"})
37                 os.Exit(2)
38         }
39         os.Exit(handler.RunCommand(os.Args[0], os.Args[1:], os.Stdin, os.Stdout, os.Stderr))
40 }
41
42 type cmdFunc func(ctx context.Context, opts opts, stdin io.Reader, stdout, stderr io.Writer) error
43
44 func (cf cmdFunc) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
45         logger := ctxlog.New(stderr, "text", "info")
46         ctx := ctxlog.Context(context.Background(), logger)
47         opts, err := parseFlags(args)
48         if err != nil {
49                 logger.WithError(err).Error("error parsing command line flags")
50                 return 1
51         }
52         err = cf(ctx, opts, stdin, stdout, stderr)
53         if err != nil {
54                 logger.WithError(err).Error("failed")
55                 return 1
56         }
57         return 0
58 }
59
60 type opts struct {
61         PackageVersion string
62         PackageDir     string
63         PackageChown   string
64         RebuildImage   bool
65         SourceDir      string
66         TargetOS       string
67 }
68
69 func parseFlags(args []string) (opts, error) {
70         opts := opts{
71                 SourceDir: ".",
72                 TargetOS:  "debian:10",
73         }
74         flags := flag.NewFlagSet("", flag.ContinueOnError)
75         flags.StringVar(&opts.PackageVersion, "package-version", opts.PackageVersion, "package version to build/test, like \"1.2.3\"")
76         flags.StringVar(&opts.SourceDir, "source", opts.SourceDir, "arvados source tree location")
77         flags.StringVar(&opts.PackageDir, "package-dir", opts.PackageDir, "destination directory for new package (default is cwd)")
78         flags.StringVar(&opts.PackageChown, "package-chown", opts.PackageChown, "desired uid:gid for new package (default is current user:group)")
79         flags.StringVar(&opts.TargetOS, "target-os", opts.TargetOS, "target operating system vendor:version")
80         flags.BoolVar(&opts.RebuildImage, "rebuild-image", opts.RebuildImage, "rebuild docker image(s) instead of using existing")
81         flags.Usage = func() {
82                 fmt.Fprint(flags.Output(), `Usage: arvados-package <subcommand> [options]
83
84 Subcommands:
85         build
86                 use a docker container to build a package from a checked
87                 out version of the arvados source tree
88         testinstall
89                 use a docker container to install a package and confirm
90                 the resulting installation is functional
91         version
92                 show program version
93
94 Internally used subcommands:
95         _fpm
96                 build a package
97         _install
98                 equivalent to "arvados-server install"
99
100 Automation/integration notes:
101         The first time a given machine runs "build" or "testinstall" (and
102         any time the -rebuild-image is used), new docker images are built,
103         which is quite slow. If you use on-demand VMs to run automated builds,
104         run "build" and "testinstall" once when setting up your initial VM
105         image, and be prepared to rebuild that VM image when package-building
106         slows down (this will happen when new dependencies are introduced).
107
108         The "build" subcommand, if successful, also runs
109         dpkg-scanpackages to create/replace Packages.gz in the package
110         dir. This enables the "testinstall" subcommand to list the
111         package dir as a source in /etc/apt/sources.*.
112
113 Options:
114 `)
115                 flags.PrintDefaults()
116         }
117         err := flags.Parse(args)
118         if err != nil {
119                 return opts, err
120         }
121         if len(flags.Args()) > 0 {
122                 return opts, fmt.Errorf("unrecognized command line arguments: %v", flags.Args())
123         }
124         if opts.SourceDir == "" {
125                 d, err := os.Getwd()
126                 if err != nil {
127                         return opts, fmt.Errorf("Getwd: %w", err)
128                 }
129                 opts.SourceDir = d
130         }
131         opts.PackageDir = filepath.Clean(opts.PackageDir)
132         opts.SourceDir, err = filepath.Abs(opts.SourceDir)
133         if err != nil {
134                 return opts, err
135         }
136         return opts, nil
137 }