Skip to content

Commit

Permalink
Bootstrap skeleton based on snapd.
Browse files Browse the repository at this point in the history
  • Loading branch information
niemeyer committed Nov 10, 2020
1 parent b1a3202 commit 50466ba
Show file tree
Hide file tree
Showing 119 changed files with 26,442 additions and 0 deletions.
86 changes: 86 additions & 0 deletions cmd/mkversion.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/bin/sh
set -e

# debugging if anything fails is tricky as dh-golang eats up all output
# uncomment the lines below to get a useful trace if you have to touch
# this again (my advice is: DON'T)
#set -x
#logfile=/tmp/mkversions.log
#exec >> $logfile 2>&1
#echo "env: $(set)"
#echo "mkversion.sh run from: $0"
#echo "pwd: $(pwd)"

# we have two directories we need to care about:
# - our toplevel pkg builddir which is where "mkversion.sh" is located
# and where "snap-confine" expects its cmd/VERSION file
# - the GO_GENERATE_BUILDDIR which may be the toplevel pkg dir. but
# during "dpkg-buildpackage" it will become a different _build/ dir
# that dh-golang creates and that only contains a subset of the
# files of the toplevel buildir.
PKG_BUILDDIR=$(dirname "$0")/..
GO_GENERATE_BUILDDIR="$(pwd)"

# run from "go generate" adjust path
if [ "$GOPACKAGE" = "cmd" ]; then
GO_GENERATE_BUILDDIR="$(pwd)/.."
fi

OUTPUT_ONLY=false
if [ "$1" = "--output-only" ]; then
OUTPUT_ONLY=true
shift
fi

# If the version is passed in as an argument to mkversion.sh, let's use that.
if [ -n "$1" ]; then
v="$1"
o=shell
fi

if [ -z "$v" ]; then
# Let's try to derive the version from git..
if command -v git >/dev/null; then
# not using "--dirty" here until the following bug is fixed:
# https://bugs.launchpad.net/snapcraft/+bug/1662388
v="$(git describe --always | sed -e 's/-/+git/;y/-/./' )"
o=git
fi
fi

if [ -z "$v" ]; then
# at this point we maybe in _build/src/github etc where we have no
# debian/changelog (dh-golang only exports the sources here)
# switch to the real source dir for the changelog parsing
v="$(cd "$PKG_BUILDDIR"; dpkg-parsechangelog --show-field Version)";
o=debian/changelog
fi

if [ -z "$v" ]; then
exit 1
fi

if [ "$OUTPUT_ONLY" = true ]; then
echo "$v"
exit 0
fi

echo "*** Setting version to '$v' from $o." >&2

cat <<EOF > "$GO_GENERATE_BUILDDIR/cmd/version_generated.go"
package cmd
// generated by mkversion.sh; do not edit
func init() {
Version = "$v"
}
EOF

cat <<EOF > "$PKG_BUILDDIR/cmd/VERSION"
$v
EOF

#cat <<EOF > "$PKG_BUILDDIR/data/info"
#VERSION=$v
#EOF
26 changes: 26 additions & 0 deletions cmd/pebble/cmd_debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2014-2020 Canonical Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.


package main

type cmdDebug struct{}

var shortDebugHelp = "Run debug commands"
var longDebugHelp = `
The debug command contains a selection of additional sub-commands.
Debug commands can be removed without notice and may not work on
non-development systems.
`
255 changes: 255 additions & 0 deletions cmd/pebble/cmd_help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
// Copyright (c) 2014-2020 Canonical Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package main

import (
"bytes"
"fmt"
"io"
"regexp"
"strings"
"unicode/utf8"

"github.com/jessevdk/go-flags"
)

var shortHelpHelp = "Show help about a command"
var longHelpHelp = `
The help command displays information about commands.
`

// addHelp adds --help like what go-flags would do for us, but hidden
func addHelp(parser *flags.Parser) error {
var help struct {
ShowHelp func() error `short:"h" long:"help"`
}
help.ShowHelp = func() error {
// this function is called via --help (or -h). In that
// case, parser.Command.Active should be the command
// on which help is being requested (like "pebble foo
// --help", active is foo), or nil in the toplevel.
if parser.Command.Active == nil {
// this means *either* a bare 'pebble --help',
// *or* 'pebble --help command'
//
// If we return nil in the first case go-flags
// will throw up an ErrCommandRequired on its
// own, but in the second case it'll go on to
// run the command, which is very unexpected.
//
// So we force the ErrCommandRequired here.

// toplevel --help gets handled via ErrCommandRequired
return &flags.Error{Type: flags.ErrCommandRequired}
}
// not toplevel, so ask for regular help
return &flags.Error{Type: flags.ErrHelp}
}
hlpgrp, err := parser.AddGroup("Help Options", "", &help)
if err != nil {
return err
}
hlpgrp.Hidden = true
hlp := parser.FindOptionByLongName("help")
hlp.Description = "Show this help message"
hlp.Hidden = true

return nil
}

type cmdHelp struct {
All bool `long:"all"`
Manpage bool `long:"man" hidden:"true"`
Positional struct {
Subs []string `positional-arg-name:"<command>"`
} `positional-args:"yes"`
parser *flags.Parser
}

func init() {
addCommand("help", shortHelpHelp, longHelpHelp, func() flags.Commander { return &cmdHelp{} },
map[string]string{
"all": "Show a short summary of all commands",
"man": "Generate the manpage",
}, nil)
}

func (cmd *cmdHelp) setParser(parser *flags.Parser) {
cmd.parser = parser
}

// manfixer is a hackish way to fix drawbacks in the generated manpage:
// - no way to get it into section 8
// - duplicated TP lines that break older groff (e.g. 14.04), lp:1814767
type manfixer struct {
bytes.Buffer
done bool
}

func (w *manfixer) Write(buf []byte) (int, error) {
if !w.done {
w.done = true
if bytes.HasPrefix(buf, []byte(".TH pebble 1 ")) {
// io.Writer.Write must not modify the buffer, even temporarily
n, _ := w.Buffer.Write(buf[:9])
w.Buffer.Write([]byte{'8'})
m, err := w.Buffer.Write(buf[10:])
return n + m + 1, err
}
}
return w.Buffer.Write(buf)
}

var tpRegexp = regexp.MustCompile(`(?m)(?:^\.TP\n)+`)

func (w *manfixer) flush() {
str := tpRegexp.ReplaceAllLiteralString(w.Buffer.String(), ".TP\n")
io.Copy(Stdout, strings.NewReader(str))
}

func (cmd cmdHelp) Execute(args []string) error {
if len(args) > 0 {
return ErrExtraArgs
}
if cmd.Manpage {
// you shouldn't try to to combine --man with --all nor a
// subcommand, but --man is hidden so no real need to check.
out := &manfixer{}
cmd.parser.WriteManPage(out)
out.flush()
return nil
}
if cmd.All {
if len(cmd.Positional.Subs) > 0 {
return fmt.Errorf("help accepts a command, or '--all', but not both.")
}
printLongHelp(cmd.parser)
return nil
}

var subcmd = cmd.parser.Command
for _, subname := range cmd.Positional.Subs {
subcmd = subcmd.Find(subname)
if subcmd == nil {
sug := "pebble help"
if x := cmd.parser.Command.Active; x != nil && x.Name != "help" {
sug = "pebble help " + x.Name
}
return fmt.Errorf("unknown command %q, see '%s'.", subname, sug)
}
// this makes "pebble help foo" work the same as "pebble foo --help"
cmd.parser.Command.Active = subcmd
}
if subcmd != cmd.parser.Command {
return &flags.Error{Type: flags.ErrHelp}
}
return &flags.Error{Type: flags.ErrCommandRequired}
}

type helpCategory struct {
Label string
Description string
Commands []string
}

// helpCategories helps us by grouping commands
var helpCategories = []helpCategory{{
Label: "Basics",
Description: "basic management",
Commands: []string{"foo", "bar", "baz"},
}, {
Label: "...more",
Description: "slightly more advanced management",
Commands: []string{"more-foo", "more-bar", "more-baz"},
}}

var (
longPebbleDescription = strings.TrimSpace(`
Pebble lets you control services and perform managemnet actions on
the system that is running them.
`)
pebbleUsage = "Usage: pebble <command> [<options>...]"
pebbleHelpCategoriesIntro = "Commands can be classified as follows:"
pebbleHelpAllFooter = "For more information about a command, run 'pebble help <command>'."
pebbleHelpFooter = "For a short summary of all commands, run 'pebble help --all'."
)

func printHelpHeader() {
fmt.Fprintln(Stdout, longPebbleDescription)
fmt.Fprintln(Stdout)
fmt.Fprintln(Stdout, pebbleUsage)
fmt.Fprintln(Stdout)
fmt.Fprintln(Stdout, pebbleHelpCategoriesIntro)
fmt.Fprintln(Stdout)
}

func printHelpAllFooter() {
fmt.Fprintln(Stdout)
fmt.Fprintln(Stdout, pebbleHelpAllFooter)
}

func printHelpFooter() {
printHelpAllFooter()
fmt.Fprintln(Stdout, pebbleHelpFooter)
}

// this is called when the Execute returns a flags.Error with ErrCommandRequired
func printShortHelp() {
printHelpHeader()
maxLen := 0
for _, categ := range helpCategories {
if l := utf8.RuneCountInString(categ.Label); l > maxLen {
maxLen = l
}
}
for _, categ := range helpCategories {
fmt.Fprintf(Stdout, "%*s: %s\n", maxLen+2, categ.Label, strings.Join(categ.Commands, ", "))
}
printHelpFooter()
}

// this is "pebble help --all"
func printLongHelp(parser *flags.Parser) {
printHelpHeader()
maxLen := 0
for _, categ := range helpCategories {
for _, command := range categ.Commands {
if l := len(command); l > maxLen {
maxLen = l
}
}
}

// flags doesn't have a LookupCommand?
commands := parser.Commands()
cmdLookup := make(map[string]*flags.Command, len(commands))
for _, cmd := range commands {
cmdLookup[cmd.Name] = cmd
}

for _, categ := range helpCategories {
fmt.Fprintln(Stdout)
fmt.Fprintf(Stdout, " %s (%s):\n", categ.Label, categ.Description)
for _, name := range categ.Commands {
cmd := cmdLookup[name]
if cmd == nil {
fmt.Fprintf(Stderr, "??? Cannot find command %q mentioned in help categories, please report!\n", name)
} else {
fmt.Fprintf(Stdout, " %*s %s\n", -maxLen, name, cmd.ShortDescription)
}
}
}
printHelpAllFooter()
}
Loading

0 comments on commit 50466ba

Please sign in to comment.