mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Add path support to script files (#40821)
Fixes #38659 Enables IT admins to reference `.sh` or `.ps1` script files directly in the GitOps `path` field for software packages.
This commit is contained in:
parent
f6da7974b2
commit
328f4d5079
8 changed files with 304 additions and 76 deletions
1
changes/38659-script-path-support
Normal file
1
changes/38659-script-path-support
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Added support for referencing `.sh` or `.ps1` script files directly in the GitOps `path` field for software packages.
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
|
@ -2408,42 +2409,70 @@ func (svc *Service) softwareBatchUpload(
|
|||
return fmt.Errorf("package not found with hash %s", p.SHA256)
|
||||
}
|
||||
|
||||
var filename string
|
||||
var tfr *fleet.TempFileReader
|
||||
var resp *http.Response
|
||||
err = retry.Do(func() error {
|
||||
var retryErr error
|
||||
resp, tfr, retryErr = downloadURLFn(ctx, p.URL)
|
||||
if retryErr != nil {
|
||||
return retryErr
|
||||
|
||||
// Handle script packages from path (script:// URL scheme)
|
||||
if filename, ok := strings.CutPrefix(p.URL, "script://"); ok {
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
ext = strings.TrimPrefix(ext, ".")
|
||||
|
||||
if !fleet.IsScriptPackage(ext) {
|
||||
return fmt.Errorf("script:// URL must reference a .sh or .ps1 file, got: %s", filename)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, retry.WithMaxAttempts(fleet.BatchDownloadMaxRetries), retry.WithInterval(fleet.BatchSoftwareInstallerRetryInterval()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p.InstallScript == "" {
|
||||
return fmt.Errorf("script package %s has no install script content", filename)
|
||||
}
|
||||
|
||||
installer.InstallerFile = tfr
|
||||
toBeClosedTFRs[i] = tfr
|
||||
scriptContent := []byte(p.InstallScript)
|
||||
tfr, err = fleet.NewTempFileReader(bytes.NewReader(scriptContent), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating temp file for script package %s: %w", filename, err)
|
||||
}
|
||||
|
||||
filename = maintained_apps.FilenameFromResponse(resp)
|
||||
installer.Filename = filename
|
||||
installer.InstallerFile = tfr
|
||||
toBeClosedTFRs[i] = tfr
|
||||
installer.Filename = filename
|
||||
|
||||
// For script packages (.sh and .ps1) and in-house apps (.ipa), clear
|
||||
// unsupported fields early. Determine extension from filename to
|
||||
// validate before metadata extraction.
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
ext = strings.TrimPrefix(ext, ".")
|
||||
if fleet.IsScriptPackage(ext) {
|
||||
installer.PostInstallScript = ""
|
||||
installer.UninstallScript = ""
|
||||
installer.PreInstallQuery = ""
|
||||
} else if ext == "ipa" {
|
||||
installer.InstallScript = ""
|
||||
installer.PostInstallScript = ""
|
||||
installer.UninstallScript = ""
|
||||
installer.PreInstallQuery = ""
|
||||
} else {
|
||||
var resp *http.Response
|
||||
err = retry.Do(func() error {
|
||||
var retryErr error
|
||||
resp, tfr, retryErr = downloadURLFn(ctx, p.URL)
|
||||
if retryErr != nil {
|
||||
return retryErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}, retry.WithMaxAttempts(fleet.BatchDownloadMaxRetries), retry.WithInterval(fleet.BatchSoftwareInstallerRetryInterval()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
installer.InstallerFile = tfr
|
||||
toBeClosedTFRs[i] = tfr
|
||||
|
||||
filename := maintained_apps.FilenameFromResponse(resp)
|
||||
installer.Filename = filename
|
||||
|
||||
// For script packages (.sh and .ps1) and in-house apps (.ipa), clear
|
||||
// unsupported fields early. Determine extension from filename to
|
||||
// validate before metadata extraction.
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
ext = strings.TrimPrefix(ext, ".")
|
||||
if fleet.IsScriptPackage(ext) {
|
||||
installer.PostInstallScript = ""
|
||||
installer.UninstallScript = ""
|
||||
installer.PreInstallQuery = ""
|
||||
} else if ext == "ipa" {
|
||||
installer.InstallScript = ""
|
||||
installer.PostInstallScript = ""
|
||||
installer.UninstallScript = ""
|
||||
installer.PreInstallQuery = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1537,8 +1537,8 @@ func parseSoftware(top map[string]json.RawMessage, result *GitOps, baseDir strin
|
|||
// A single item in Packages can result in multiple SoftwarePackageSpecs being generated
|
||||
var softwarePackageSpecs []*fleet.SoftwarePackageSpec
|
||||
if teamLevelPackage.Path != nil {
|
||||
yamlPath := resolveApplyRelativePath(baseDir, *teamLevelPackage.Path)
|
||||
fileBytes, err := os.ReadFile(yamlPath)
|
||||
resolvedPath := resolveApplyRelativePath(baseDir, *teamLevelPackage.Path)
|
||||
fileBytes, err := os.ReadFile(resolvedPath)
|
||||
if err != nil {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("failed to read software package file %s: %w", *teamLevelPackage.Path, err))
|
||||
continue
|
||||
|
|
@ -1549,38 +1549,60 @@ func parseSoftware(top map[string]json.RawMessage, result *GitOps, baseDir strin
|
|||
multiError = multierror.Append(multiError, fmt.Errorf("failed to expand environment in file %s: %w", *teamLevelPackage.Path, err))
|
||||
continue
|
||||
}
|
||||
var singlePackageSpec SoftwarePackage
|
||||
singlePackageSpec.ReferencedYamlPath = yamlPath
|
||||
if err := YamlUnmarshal(fileBytes, &singlePackageSpec); err == nil {
|
||||
if singlePackageSpec.IncludesFieldsDisallowedInPackageFile() {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("labels, categories, setup_experience, and self_service values must be specified at the team level; package-level specified in %s", *teamLevelPackage.Path))
|
||||
continue
|
||||
}
|
||||
softwarePackageSpecs = append(softwarePackageSpecs, &singlePackageSpec.SoftwarePackageSpec)
|
||||
} else if err = YamlUnmarshal(fileBytes, &softwarePackageSpecs); err == nil {
|
||||
// Failing that, try to unmarshal as a list of SoftwarePackageSpecs
|
||||
for i, spec := range softwarePackageSpecs {
|
||||
if spec.IncludesFieldsDisallowedInPackageFile() {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("labels, categories, setup_experience, and self_service values must be specified at the team level; package-level specified in %s", *teamLevelPackage.Path))
|
||||
continue
|
||||
}
|
||||
|
||||
softwarePackageSpecs[i].ReferencedYamlPath = yamlPath
|
||||
ext := strings.ToLower(filepath.Ext(resolvedPath))
|
||||
switch ext {
|
||||
case ".sh", ".ps1":
|
||||
// Script file becomes the install script for a script-only package
|
||||
scriptSpec := fleet.SoftwarePackageSpec{
|
||||
InstallScript: fleet.TeamSpecSoftwareAsset{Path: resolvedPath},
|
||||
ReferencedYamlPath: resolvedPath,
|
||||
}
|
||||
} else {
|
||||
// If we reached here, we couldn't unmarshal as either format.
|
||||
multiError = multierror.Append(multiError, MaybeParseTypeError(*teamLevelPackage.Path, []string{"software", "packages"}, err))
|
||||
continue
|
||||
}
|
||||
|
||||
for i, spec := range softwarePackageSpecs {
|
||||
softwarePackageSpec := spec.ResolveSoftwarePackagePaths(filepath.Dir(spec.ReferencedYamlPath))
|
||||
softwarePackageSpec, err = teamLevelPackage.HydrateToPackageLevel(softwarePackageSpec)
|
||||
scriptSpec, err = teamLevelPackage.HydrateToPackageLevel(scriptSpec)
|
||||
if err != nil {
|
||||
multiError = multierror.Append(multiError, err)
|
||||
continue
|
||||
}
|
||||
softwarePackageSpecs[i] = &softwarePackageSpec
|
||||
softwarePackageSpecs = append(softwarePackageSpecs, &scriptSpec)
|
||||
|
||||
case ".yml", ".yaml":
|
||||
var singlePackageSpec SoftwarePackage
|
||||
singlePackageSpec.ReferencedYamlPath = resolvedPath
|
||||
if err := YamlUnmarshal(fileBytes, &singlePackageSpec); err == nil {
|
||||
if singlePackageSpec.IncludesFieldsDisallowedInPackageFile() {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("labels, categories, setup_experience, and self_service values must be specified at the team level; package-level specified in %s", *teamLevelPackage.Path))
|
||||
continue
|
||||
}
|
||||
softwarePackageSpecs = append(softwarePackageSpecs, &singlePackageSpec.SoftwarePackageSpec)
|
||||
} else if err = YamlUnmarshal(fileBytes, &softwarePackageSpecs); err == nil {
|
||||
// Failing that, try to unmarshal as a list of SoftwarePackageSpecs
|
||||
for i, spec := range softwarePackageSpecs {
|
||||
if spec.IncludesFieldsDisallowedInPackageFile() {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("labels, categories, setup_experience, and self_service values must be specified at the team level; package-level specified in %s", *teamLevelPackage.Path))
|
||||
continue
|
||||
}
|
||||
|
||||
softwarePackageSpecs[i].ReferencedYamlPath = resolvedPath
|
||||
}
|
||||
} else {
|
||||
// If we reached here, we couldn't unmarshal as either format.
|
||||
multiError = multierror.Append(multiError, MaybeParseTypeError(*teamLevelPackage.Path, []string{"software", "packages"}, err))
|
||||
continue
|
||||
}
|
||||
|
||||
for i, spec := range softwarePackageSpecs {
|
||||
softwarePackageSpec := spec.ResolveSoftwarePackagePaths(filepath.Dir(spec.ReferencedYamlPath))
|
||||
softwarePackageSpec, err = teamLevelPackage.HydrateToPackageLevel(softwarePackageSpec)
|
||||
if err != nil {
|
||||
multiError = multierror.Append(multiError, err)
|
||||
continue
|
||||
}
|
||||
softwarePackageSpecs[i] = &softwarePackageSpec
|
||||
}
|
||||
|
||||
default:
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software package path %s has unsupported extension %q; only .yml, .yaml, .sh, or .ps1 files are supported", *teamLevelPackage.Path, ext))
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
softwarePackageSpec := teamLevelPackage.SoftwarePackageSpec.ResolveSoftwarePackagePaths(baseDir)
|
||||
|
|
@ -1614,7 +1636,9 @@ func parseSoftware(top map[string]json.RawMessage, result *GitOps, baseDir strin
|
|||
multiError = multierror.Append(multiError, fmt.Errorf("hash_sha256 value %q must be a valid lower-case hex-encoded (64-character) SHA-256 hash value", softwarePackageSpec.SHA256))
|
||||
continue
|
||||
}
|
||||
if softwarePackageSpec.SHA256 == "" && softwarePackageSpec.URL == "" {
|
||||
// Script packages from path don't require URL or hash_sha256
|
||||
isScriptPackageFromPath := fleet.IsScriptPackage(filepath.Ext(softwarePackageSpec.ReferencedYamlPath))
|
||||
if !isScriptPackageFromPath && softwarePackageSpec.SHA256 == "" && softwarePackageSpec.URL == "" {
|
||||
errorMessage := "at least one of hash_sha256 or url is required for each software package"
|
||||
if softwarePackageSpec.ReferencedYamlPath != "" {
|
||||
errorMessage += fmt.Sprintf("; missing in %s", softwarePackageSpec.ReferencedYamlPath)
|
||||
|
|
@ -1626,25 +1650,29 @@ func parseSoftware(top map[string]json.RawMessage, result *GitOps, baseDir strin
|
|||
multiError = multierror.Append(multiError, errors.New(errorMessage))
|
||||
continue
|
||||
}
|
||||
if len(softwarePackageSpec.URL) > fleet.SoftwareInstallerURLMaxLength {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software URL %q is too long, must be %d characters or less", softwarePackageSpec.URL, fleet.SoftwareInstallerURLMaxLength))
|
||||
continue
|
||||
}
|
||||
parsedUrl, err := url.Parse(softwarePackageSpec.URL)
|
||||
if err != nil {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software URL %s is not a valid URL", softwarePackageSpec.URL))
|
||||
continue
|
||||
}
|
||||
if softwarePackageSpec.InstallScript.Path == "" || softwarePackageSpec.UninstallScript.Path == "" {
|
||||
// URL checks won't catch everything, but might as well include a lightweight check here to fail fast if it's
|
||||
// certain that the package will fail later.
|
||||
if strings.HasSuffix(parsedUrl.Path, ".exe") {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software URL %s refers to an .exe package, which requires both install_script and uninstall_script", softwarePackageSpec.URL))
|
||||
|
||||
// Skip URL-related validations for script packages from path
|
||||
if !isScriptPackageFromPath {
|
||||
if len(softwarePackageSpec.URL) > fleet.SoftwareInstallerURLMaxLength {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software URL %q is too long, must be %d characters or less", softwarePackageSpec.URL, fleet.SoftwareInstallerURLMaxLength))
|
||||
continue
|
||||
} else if strings.HasSuffix(parsedUrl.Path, ".tar.gz") || strings.HasSuffix(parsedUrl.Path, ".tgz") {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software URL %s refers to a .tar.gz archive, which requires both install_script and uninstall_script", softwarePackageSpec.URL))
|
||||
}
|
||||
parsedUrl, err := url.Parse(softwarePackageSpec.URL)
|
||||
if err != nil {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software URL %s is not a valid URL", softwarePackageSpec.URL))
|
||||
continue
|
||||
}
|
||||
if softwarePackageSpec.InstallScript.Path == "" || softwarePackageSpec.UninstallScript.Path == "" {
|
||||
// URL checks won't catch everything, but might as well include a lightweight check here to fail fast if it's
|
||||
// certain that the package will fail later.
|
||||
if strings.HasSuffix(parsedUrl.Path, ".exe") {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software URL %s refers to an .exe package, which requires both install_script and uninstall_script", softwarePackageSpec.URL))
|
||||
continue
|
||||
} else if strings.HasSuffix(parsedUrl.Path, ".tar.gz") || strings.HasSuffix(parsedUrl.Path, ".tgz") {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("software URL %s refers to a .tar.gz archive, which requires both install_script and uninstall_script", softwarePackageSpec.URL))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate display_name length (matches database VARCHAR(255))
|
||||
|
|
|
|||
|
|
@ -1929,3 +1929,149 @@ func TestGitOpsGlobScripts(t *testing.T) {
|
|||
assert.Equal(t, filepath.Join(scriptsDir, "beta.sh"), *result.Controls.Scripts[1].Path)
|
||||
assert.Equal(t, filepath.Join(scriptsDir, "gamma.ps1"), *result.Controls.Scripts[2].Path)
|
||||
}
|
||||
|
||||
func TestSoftwarePackagesScriptPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
appConfig := &fleet.EnrichedAppConfig{}
|
||||
appConfig.License = &fleet.LicenseInfo{
|
||||
Tier: fleet.TierPremium,
|
||||
}
|
||||
|
||||
t.Run("valid_sh_script_path", func(t *testing.T) {
|
||||
config := getTeamConfig([]string{"software"})
|
||||
config += `
|
||||
software:
|
||||
packages:
|
||||
- path: software/install-app.sh
|
||||
categories:
|
||||
- Utilities
|
||||
self_service: true
|
||||
`
|
||||
path, basePath := createTempFile(t, "", config)
|
||||
|
||||
err := file.Copy(
|
||||
filepath.Join("testdata", "software", "install-app.sh"),
|
||||
filepath.Join(basePath, "software", "install-app.sh"),
|
||||
os.FileMode(0o755),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := GitOpsFromFile(path, basePath, appConfig, nopLogf)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Software.Packages, 1)
|
||||
assert.True(t, strings.HasSuffix(result.Software.Packages[0].InstallScript.Path, "install-app.sh"))
|
||||
assert.Equal(t, []string{"Utilities"}, result.Software.Packages[0].Categories)
|
||||
assert.True(t, result.Software.Packages[0].SelfService)
|
||||
assert.Empty(t, result.Software.Packages[0].URL)
|
||||
assert.Empty(t, result.Software.Packages[0].SHA256)
|
||||
})
|
||||
|
||||
t.Run("valid_ps1_script_path", func(t *testing.T) {
|
||||
config := getTeamConfig([]string{"software"})
|
||||
config += `
|
||||
software:
|
||||
packages:
|
||||
- path: software/install-app.ps1
|
||||
self_service: false
|
||||
`
|
||||
path, basePath := createTempFile(t, "", config)
|
||||
|
||||
// Copy the test script file
|
||||
err := file.Copy(
|
||||
filepath.Join("testdata", "software", "install-app.ps1"),
|
||||
filepath.Join(basePath, "software", "install-app.ps1"),
|
||||
os.FileMode(0o755),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := GitOpsFromFile(path, basePath, appConfig, nopLogf)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Software.Packages, 1)
|
||||
assert.True(t, strings.HasSuffix(result.Software.Packages[0].InstallScript.Path, "install-app.ps1"))
|
||||
})
|
||||
|
||||
t.Run("invalid_extension_error", func(t *testing.T) {
|
||||
config := getTeamConfig([]string{"software"})
|
||||
config += `
|
||||
software:
|
||||
packages:
|
||||
- path: software/install-app.txt
|
||||
`
|
||||
path, basePath := createTempFile(t, "", config)
|
||||
|
||||
// Create a .txt file
|
||||
err := os.MkdirAll(filepath.Join(basePath, "software"), 0o755)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(filepath.Join(basePath, "software", "install-app.txt"), []byte("test"), 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = GitOpsFromFile(path, basePath, appConfig, nopLogf)
|
||||
assert.ErrorContains(t, err, "unsupported extension")
|
||||
assert.ErrorContains(t, err, "only .yml, .yaml, .sh, or .ps1 files are supported")
|
||||
})
|
||||
|
||||
t.Run("script_with_team_options", func(t *testing.T) {
|
||||
config := getTeamConfig([]string{"software"})
|
||||
config += `
|
||||
software:
|
||||
packages:
|
||||
- path: software/install-app.sh
|
||||
categories:
|
||||
- Browsers
|
||||
- Productivity
|
||||
self_service: true
|
||||
setup_experience: true
|
||||
labels_include_any:
|
||||
- include_label
|
||||
`
|
||||
path, basePath := createTempFile(t, "", config)
|
||||
|
||||
err := file.Copy(
|
||||
filepath.Join("testdata", "software", "install-app.sh"),
|
||||
filepath.Join(basePath, "software", "install-app.sh"),
|
||||
os.FileMode(0o755),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := GitOpsFromFile(path, basePath, appConfig, nopLogf)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Software.Packages, 1)
|
||||
pkg := result.Software.Packages[0]
|
||||
assert.Equal(t, []string{"Browsers", "Productivity"}, pkg.Categories)
|
||||
assert.True(t, pkg.SelfService)
|
||||
assert.True(t, pkg.InstallDuringSetup.Value)
|
||||
assert.Equal(t, []string{"include_label"}, pkg.LabelsIncludeAny)
|
||||
})
|
||||
|
||||
t.Run("mixed_yaml_and_script_paths", func(t *testing.T) {
|
||||
config := getTeamConfig([]string{"software"})
|
||||
config += `
|
||||
software:
|
||||
packages:
|
||||
- path: software/single-package.yml
|
||||
- path: software/install-app.sh
|
||||
self_service: true
|
||||
`
|
||||
path, basePath := createTempFile(t, "", config)
|
||||
|
||||
err := file.Copy(
|
||||
filepath.Join("testdata", "software", "single-package.yml"),
|
||||
filepath.Join(basePath, "software", "single-package.yml"),
|
||||
os.FileMode(0o755),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
err = file.Copy(
|
||||
filepath.Join("testdata", "software", "install-app.sh"),
|
||||
filepath.Join(basePath, "software", "install-app.sh"),
|
||||
os.FileMode(0o755),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := GitOpsFromFile(path, basePath, appConfig, nopLogf)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Software.Packages, 2)
|
||||
assert.NotEmpty(t, result.Software.Packages[0].SHA256)
|
||||
assert.True(t, strings.HasSuffix(result.Software.Packages[1].InstallScript.Path, "install-app.sh"))
|
||||
assert.True(t, result.Software.Packages[1].SelfService)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
4
pkg/spec/testdata/software/install-app.ps1
vendored
Normal file
4
pkg/spec/testdata/software/install-app.ps1
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Test PowerShell script for script-only software packages
|
||||
Write-Host "Installing application..."
|
||||
# Simulated installation steps
|
||||
exit 0
|
||||
3
pkg/spec/testdata/software/install-app.sh
vendored
Normal file
3
pkg/spec/testdata/software/install-app.sh
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
echo "Installing application..."
|
||||
exit 0
|
||||
|
|
@ -423,8 +423,13 @@ type ScriptPayload struct {
|
|||
}
|
||||
|
||||
type SoftwareInstallerPayload struct {
|
||||
URL string `json:"url"`
|
||||
PreInstallQuery string `json:"pre_install_query"`
|
||||
// URL is the download URL for the installer. For script packages specified via
|
||||
// the path field, this uses "script://filename" to pass the filename; in that
|
||||
// case InstallScript contains the script content directly.
|
||||
URL string `json:"url"`
|
||||
PreInstallQuery string `json:"pre_install_query"`
|
||||
// InstallScript is the script to run after downloading the installer. For script
|
||||
// packages via "script://" URL, this contains the package content itself.
|
||||
InstallScript string `json:"install_script"`
|
||||
UninstallScript string `json:"uninstall_script"`
|
||||
PostInstallScript string `json:"post_install_script"`
|
||||
|
|
|
|||
|
|
@ -1257,8 +1257,20 @@ func buildSoftwarePackagesPayload(specs []fleet.SoftwarePackageSpec, installDuri
|
|||
installDuringSetup = &si.InstallDuringSetup.Value
|
||||
}
|
||||
|
||||
// For script packages from path, use "script://" URL scheme to pass the filename
|
||||
urlValue := si.URL
|
||||
sha256Value := si.SHA256
|
||||
if fleet.IsScriptPackage(filepath.Ext(si.ReferencedYamlPath)) && si.URL == "" {
|
||||
scriptFilename := filepath.Base(si.ReferencedYamlPath)
|
||||
urlValue = "script://" + scriptFilename
|
||||
if sha256Value == "" && len(ic) > 0 {
|
||||
hash := sha256.Sum256(ic)
|
||||
sha256Value = hex.EncodeToString(hash[:])
|
||||
}
|
||||
}
|
||||
|
||||
softwarePayloads[i] = fleet.SoftwareInstallerPayload{
|
||||
URL: si.URL,
|
||||
URL: urlValue,
|
||||
SelfService: si.SelfService,
|
||||
PreInstallQuery: qc,
|
||||
InstallScript: string(ic),
|
||||
|
|
@ -1267,7 +1279,7 @@ func buildSoftwarePackagesPayload(specs []fleet.SoftwarePackageSpec, installDuri
|
|||
InstallDuringSetup: installDuringSetup,
|
||||
LabelsIncludeAny: si.LabelsIncludeAny,
|
||||
LabelsExcludeAny: si.LabelsExcludeAny,
|
||||
SHA256: si.SHA256,
|
||||
SHA256: sha256Value,
|
||||
Categories: si.Categories,
|
||||
DisplayName: si.DisplayName,
|
||||
IconPath: si.Icon.Path,
|
||||
|
|
|
|||
Loading…
Reference in a new issue