github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/snap/cmd_pack.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package main
    21  
    22  import (
    23  	"fmt"
    24  	"path/filepath"
    25  
    26  	"golang.org/x/xerrors"
    27  
    28  	"github.com/jessevdk/go-flags"
    29  
    30  	"github.com/snapcore/snapd/i18n"
    31  	"github.com/snapcore/snapd/snap"
    32  	"github.com/snapcore/snapd/snap/pack"
    33  
    34  	// for SanitizePlugsSlots
    35  	"github.com/snapcore/snapd/interfaces/builtin"
    36  )
    37  
    38  type packCmd struct {
    39  	CheckSkeleton bool   `long:"check-skeleton"`
    40  	Filename      string `long:"filename"`
    41  	Compression   string `long:"compression" hidden:"yes"`
    42  	Positional    struct {
    43  		SnapDir   string `positional-arg-name:"<snap-dir>"`
    44  		TargetDir string `positional-arg-name:"<target-dir>"`
    45  	} `positional-args:"yes"`
    46  }
    47  
    48  var shortPackHelp = i18n.G("Pack the given directory as a snap")
    49  var longPackHelp = i18n.G(`
    50  The pack command packs the given snap-dir as a snap and writes the result to
    51  target-dir. If target-dir is omitted, the result is written to current
    52  directory. If both source-dir and target-dir are omitted, the pack command packs
    53  the current directory.
    54  
    55  The default file name for a snap can be derived entirely from its snap.yaml, but
    56  in some situations it's simpler for a script to feed the filename in. In those
    57  cases, --filename can be given to override the default. If this filename is
    58  not absolute it will be taken as relative to target-dir.
    59  
    60  When used with --check-skeleton, pack only checks whether snap-dir contains
    61  valid snap metadata and raises an error otherwise. Application commands listed
    62  in snap metadata file, but appearing with incorrect permission bits result in an
    63  error. Commands that are missing from snap-dir are listed in diagnostic
    64  messages.
    65  `)
    66  
    67  func init() {
    68  	cmd := addCommand("pack",
    69  		shortPackHelp,
    70  		longPackHelp,
    71  		func() flags.Commander {
    72  			return &packCmd{}
    73  		}, map[string]string{
    74  			// TRANSLATORS: This should not start with a lowercase letter.
    75  			"check-skeleton": i18n.G("Validate snap-dir metadata only"),
    76  			// TRANSLATORS: This should not start with a lowercase letter.
    77  			"filename": i18n.G("Output to this filename"),
    78  			// TRANSLATORS: This should not start with a lowercase letter.
    79  			"compression": i18n.G("Compression to use (e.g. xz)"),
    80  		}, nil)
    81  	cmd.extra = func(cmd *flags.Command) {
    82  		// TRANSLATORS: this describes the default filename for a snap, e.g. core_16-2.35.2_amd64.snap
    83  		cmd.FindOptionByLongName("filename").DefaultMask = i18n.G("<name>_<version>_<architecture>.snap")
    84  	}
    85  }
    86  
    87  func (x *packCmd) Execute([]string) error {
    88  	// plug/slot sanitization is disabled (no-op) by default at the package level for "snap" command,
    89  	// for "snap pack" however we want real validation.
    90  	snap.SanitizePlugsSlots = builtin.SanitizePlugsSlots
    91  
    92  	if x.Positional.TargetDir != "" && x.Filename != "" && filepath.IsAbs(x.Filename) {
    93  		return fmt.Errorf(i18n.G("you can't specify an absolute filename while also specifying target dir."))
    94  	}
    95  
    96  	if x.Positional.SnapDir == "" {
    97  		x.Positional.SnapDir = "."
    98  	}
    99  	if x.Positional.TargetDir == "" {
   100  		x.Positional.TargetDir = "."
   101  	}
   102  
   103  	if x.CheckSkeleton {
   104  		err := pack.CheckSkeleton(Stderr, x.Positional.SnapDir)
   105  		if err == snap.ErrMissingPaths {
   106  			return nil
   107  		}
   108  		return err
   109  	}
   110  
   111  	snapPath, err := pack.Snap(x.Positional.SnapDir, &pack.Options{
   112  		TargetDir:   x.Positional.TargetDir,
   113  		SnapName:    x.Filename,
   114  		Compression: x.Compression,
   115  	})
   116  	if err != nil {
   117  		// TRANSLATORS: the %q is the snap-dir (the first positional
   118  		// argument to the command); the %v is an error
   119  		return xerrors.Errorf(i18n.G("cannot pack %q: %w"), x.Positional.SnapDir, err)
   120  
   121  	}
   122  	// TRANSLATORS: %s is the path to the built snap file
   123  	fmt.Fprintf(Stdout, i18n.G("built: %s\n"), snapPath)
   124  	return nil
   125  }