github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/osutils/shortcut/shortcut_linux.go (about)

     1  package shortcut
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"github.com/ActiveState/cli/internal/errs"
     9  	"github.com/ActiveState/cli/internal/fileutils"
    10  	"github.com/ActiveState/cli/internal/logging"
    11  	"github.com/ActiveState/cli/internal/multilog"
    12  	"github.com/ActiveState/cli/internal/osutils"
    13  	"github.com/ActiveState/cli/internal/rollbar"
    14  	"github.com/ActiveState/cli/internal/strutils"
    15  )
    16  
    17  type SaveOpts struct {
    18  	Name        string
    19  	GenericName string
    20  	Comment     string
    21  	Keywords    string
    22  	IconData    []byte
    23  	IconPath    string
    24  }
    25  
    26  func Save(target, path string, args []string, opts SaveOpts) (file string, err error) {
    27  	if !fileutils.FileExists(target) {
    28  		return "", errs.New("Target does not exist")
    29  	}
    30  
    31  	iconName := filepath.Base(opts.IconPath)
    32  	iconName = strings.TrimSuffix(iconName, filepath.Ext(iconName))
    33  
    34  	name := opts.Name
    35  	if name == "" {
    36  		name = filepath.Base(path)
    37  	}
    38  
    39  	exec := []string{target}
    40  	exec = append(exec, args...)
    41  
    42  	data := desktopFileData{
    43  		Name:        name,
    44  		GenericName: opts.GenericName,
    45  		Comment:     opts.Comment,
    46  		Exec:        strings.Join(exec, " "),
    47  		Keywords:    opts.Keywords,
    48  		IconName:    iconName,
    49  	}
    50  	desktopFile, err := strutils.ParseTemplate(desktopFileTmpl, data, nil)
    51  	if err != nil {
    52  		return "", errs.Wrap(err, "Could not execute template")
    53  	}
    54  
    55  	if err := fileutils.WriteFile(opts.IconPath, opts.IconData); err != nil {
    56  		return "", errs.Wrap(err, "Could not write icon file")
    57  	}
    58  
    59  	if err := fileutils.WriteFile(path, []byte(desktopFile)); err != nil {
    60  		return "", errs.Wrap(err, "Could not write desktop file")
    61  	}
    62  
    63  	f, err := os.Open(path)
    64  	if err != nil {
    65  		return "", errs.Wrap(err, "Could not open desktop file")
    66  	}
    67  	err = f.Chmod(0770)
    68  	f.Close()
    69  	if err != nil {
    70  		return "", errs.Wrap(err, "Could not make file executable")
    71  	}
    72  
    73  	// set the executable as trusted so users do not need to do it manually
    74  	// gio is "Gnome input/output"
    75  	stdoutText, stderrText, err := osutils.ExecSimple("gio", []string{"set", path, "metadata::trusted", "true"}, []string{})
    76  	if err != nil {
    77  		multilog.Log(logging.ErrorNoStacktrace, rollbar.Error)("Could not set desktop file as trusted: %v (stdout: %s; stderr: %s)", errs.JoinMessage(err), stdoutText, stderrText)
    78  	}
    79  
    80  	return path, nil
    81  }
    82  
    83  type desktopFileData struct {
    84  	Name        string
    85  	GenericName string
    86  	Comment     string
    87  	Exec        string
    88  	Keywords    string
    89  	IconName    string
    90  }
    91  
    92  var desktopFileTmpl = strings.TrimPrefix(`
    93  [Desktop Entry]
    94  Name={{ .Name }}
    95  GenericName={{ .GenericName }}
    96  Type=Application
    97  Comment={{ .Comment }}
    98  Exec="{{ .Exec }}"
    99  Terminal=false
   100  Keywords={{ .Keywords }}
   101  Categories=Utility;Development;
   102  Hidden=false
   103  NoDisplay=false
   104  StartupNotify=false
   105  Icon={{ .IconName }}
   106  Name[en_US]={{ .Name }}
   107  `, "\n")