github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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  	"github.com/jessevdk/go-flags"
    27  	"golang.org/x/xerrors"
    28  
    29  	"github.com/snapcore/snapd/i18n"
    30  
    31  	// for SanitizePlugsSlots
    32  	"github.com/snapcore/snapd/interfaces/builtin"
    33  	"github.com/snapcore/snapd/snap"
    34  	"github.com/snapcore/snapd/snap/pack"
    35  )
    36  
    37  type packCmd struct {
    38  	CheckSkeleton bool   `long:"check-skeleton"`
    39  	Filename      string `long:"filename"`
    40  	Compression   string `long:"compression"`
    41  	Positional    struct {
    42  		SnapDir   string `positional-arg-name:"<snap-dir>"`
    43  		TargetDir string `positional-arg-name:"<target-dir>"`
    44  	} `positional-args:"yes"`
    45  }
    46  
    47  var shortPackHelp = i18n.G("Pack the given directory as a snap")
    48  var longPackHelp = i18n.G(`
    49  The pack command packs the given snap-dir as a snap and writes the result to
    50  target-dir. If target-dir is omitted, the result is written to current
    51  directory. If both source-dir and target-dir are omitted, the pack command packs
    52  the current directory.
    53  
    54  The default file name for a snap can be derived entirely from its snap.yaml, but
    55  in some situations it's simpler for a script to feed the filename in. In those
    56  cases, --filename can be given to override the default. If this filename is
    57  not absolute it will be taken as relative to target-dir.
    58  
    59  When used with --check-skeleton, pack only checks whether snap-dir contains
    60  valid snap metadata and raises an error otherwise. Application commands listed
    61  in snap metadata file, but appearing with incorrect permission bits result in an
    62  error. Commands that are missing from snap-dir are listed in diagnostic
    63  messages.
    64  `)
    65  
    66  func init() {
    67  	cmd := addCommand("pack",
    68  		shortPackHelp,
    69  		longPackHelp,
    70  		func() flags.Commander {
    71  			return &packCmd{}
    72  		}, map[string]string{
    73  			// TRANSLATORS: This should not start with a lowercase letter.
    74  			"check-skeleton": i18n.G("Validate snap-dir metadata only"),
    75  			// TRANSLATORS: This should not start with a lowercase letter.
    76  			"filename": i18n.G("Output to this filename"),
    77  			// TRANSLATORS: This should not start with a lowercase letter.
    78  			"compression": i18n.G("Compression to use (e.g. xz or lzo)"),
    79  		}, nil)
    80  	cmd.extra = func(cmd *flags.Command) {
    81  		// TRANSLATORS: this describes the default filename for a snap, e.g. core_16-2.35.2_amd64.snap
    82  		cmd.FindOptionByLongName("filename").DefaultMask = i18n.G("<name>_<version>_<architecture>.snap")
    83  	}
    84  }
    85  
    86  func (x *packCmd) Execute([]string) error {
    87  	// plug/slot sanitization is disabled (no-op) by default at the package level for "snap" command,
    88  	// for "snap pack" however we want real validation.
    89  	snap.SanitizePlugsSlots = builtin.SanitizePlugsSlots
    90  
    91  	if x.Positional.TargetDir != "" && x.Filename != "" && filepath.IsAbs(x.Filename) {
    92  		return fmt.Errorf(i18n.G("you can't specify an absolute filename while also specifying target dir."))
    93  	}
    94  
    95  	if x.Positional.SnapDir == "" {
    96  		x.Positional.SnapDir = "."
    97  	}
    98  	if x.Positional.TargetDir == "" {
    99  		x.Positional.TargetDir = "."
   100  	}
   101  
   102  	if x.CheckSkeleton {
   103  		err := pack.CheckSkeleton(Stderr, x.Positional.SnapDir)
   104  		if err == snap.ErrMissingPaths {
   105  			return nil
   106  		}
   107  		return err
   108  	}
   109  
   110  	snapPath, err := pack.Snap(x.Positional.SnapDir, &pack.Options{
   111  		TargetDir:   x.Positional.TargetDir,
   112  		SnapName:    x.Filename,
   113  		Compression: x.Compression,
   114  	})
   115  	if err != nil {
   116  		// TRANSLATORS: the %q is the snap-dir (the first positional
   117  		// argument to the command); the %v is an error
   118  		return xerrors.Errorf(i18n.G("cannot pack %q: %w"), x.Positional.SnapDir, err)
   119  
   120  	}
   121  	// TRANSLATORS: %s is the path to the built snap file
   122  	fmt.Fprintf(Stdout, i18n.G("built: %s\n"), snapPath)
   123  	return nil
   124  }