forked from Plemya-x/ALR
feat: add firejailed support (#106)
Reviewed-on: Plemya-x/ALR#106 Co-authored-by: Maxim Slipenko <no-reply@maxim.slipenko.com> Co-committed-by: Maxim Slipenko <no-reply@maxim.slipenko.com>
This commit is contained in:
237
internal/build/firejail.go
Normal file
237
internal/build/firejail.go
Normal file
@ -0,0 +1,237 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// 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 build
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
firejailedDir = "/usr/lib/alr/firejailed"
|
||||
defaultDirMode = 0o755
|
||||
defaultScriptMode = 0o755
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidDestination = errors.New("invalid destination path")
|
||||
ErrMissingProfile = errors.New("default profile is missing")
|
||||
ErrEmptyPackageName = errors.New("package name cannot be empty")
|
||||
)
|
||||
|
||||
var binaryDirectories = []string{
|
||||
"/usr/bin/",
|
||||
"/bin/",
|
||||
"/usr/local/bin/",
|
||||
}
|
||||
|
||||
func applyFirejailIntegration(
|
||||
vars *alrsh.Package,
|
||||
dirs types.Directories,
|
||||
contents []*files.Content,
|
||||
) ([]*files.Content, error) {
|
||||
slog.Info(gotext.Get("Applying FireJail integration"), "package", vars.Name)
|
||||
|
||||
if err := createFirejailedDirectory(dirs.PkgDir); err != nil {
|
||||
return nil, fmt.Errorf("failed to create firejailed directory: %w", err)
|
||||
}
|
||||
|
||||
newContents, err := processBinaryFiles(vars, contents, dirs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to process binary files: %w", err)
|
||||
}
|
||||
|
||||
return append(contents, newContents...), nil
|
||||
}
|
||||
|
||||
func createFirejailedDirectory(pkgDir string) error {
|
||||
firejailedPath := filepath.Join(pkgDir, firejailedDir)
|
||||
return os.MkdirAll(firejailedPath, defaultDirMode)
|
||||
}
|
||||
|
||||
func processBinaryFiles(pkg *alrsh.Package, contents []*files.Content, dirs types.Directories) ([]*files.Content, error) {
|
||||
var newContents []*files.Content
|
||||
|
||||
for _, content := range contents {
|
||||
if content.Type == "dir" {
|
||||
continue
|
||||
}
|
||||
|
||||
if !isBinaryFile(content.Destination) {
|
||||
slog.Debug("content not binary file", "content", content)
|
||||
continue
|
||||
}
|
||||
|
||||
slog.Debug("process content", "content", content)
|
||||
|
||||
newContent, err := createFirejailedBinary(pkg, content, dirs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create firejailed binary for %s: %w", content.Destination, err)
|
||||
}
|
||||
|
||||
if newContent != nil {
|
||||
newContents = append(newContents, newContent...)
|
||||
}
|
||||
}
|
||||
|
||||
return newContents, nil
|
||||
}
|
||||
|
||||
func isBinaryFile(destination string) bool {
|
||||
for _, binDir := range binaryDirectories {
|
||||
if strings.HasPrefix(destination, binDir) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func createFirejailedBinary(
|
||||
pkg *alrsh.Package,
|
||||
content *files.Content,
|
||||
dirs types.Directories,
|
||||
) ([]*files.Content, error) {
|
||||
origFilePath, err := generateFirejailedPath(content.Destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
profiles := pkg.FireJailProfiles.Resolved()
|
||||
sourceProfilePath, ok := profiles[content.Destination]
|
||||
|
||||
if !ok {
|
||||
sourceProfilePath, ok = profiles["default"]
|
||||
if !ok {
|
||||
return nil, errors.New("default profile is missing")
|
||||
}
|
||||
}
|
||||
|
||||
sourceProfilePath = filepath.Join(dirs.ScriptDir, sourceProfilePath)
|
||||
dest, err := createFirejailProfilePath(content.Destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = createProfile(filepath.Join(dirs.PkgDir, dest), sourceProfilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := osutils.Move(content.Source, filepath.Join(dirs.PkgDir, origFilePath)); err != nil {
|
||||
return nil, fmt.Errorf("failed to move original binary: %w", err)
|
||||
}
|
||||
|
||||
// Create wrapper script
|
||||
if err := createWrapperScript(content.Source, origFilePath, dest); err != nil {
|
||||
return nil, fmt.Errorf("failed to create wrapper script: %w", err)
|
||||
}
|
||||
|
||||
profile, err := getContentFromPath(dest, dirs.PkgDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bin, err := getContentFromPath(origFilePath, dirs.PkgDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []*files.Content{
|
||||
bin,
|
||||
profile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getContentFromPath(path, base string) (*files.Content, error) {
|
||||
absPath := filepath.Join(base, path)
|
||||
|
||||
fi, err := os.Lstat(absPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get file info: %w", err)
|
||||
}
|
||||
|
||||
return &files.Content{
|
||||
Source: absPath,
|
||||
Destination: path,
|
||||
FileInfo: &files.ContentFileInfo{
|
||||
MTime: fi.ModTime(),
|
||||
Mode: fi.Mode(),
|
||||
Size: fi.Size(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func generateSafeName(destination string) (string, error) {
|
||||
cleanPath := strings.TrimPrefix(destination, ".")
|
||||
if cleanPath == "" {
|
||||
return "", fmt.Errorf("invalid destination path: %s", destination)
|
||||
}
|
||||
return strings.ReplaceAll(cleanPath, "/", "_"), nil
|
||||
}
|
||||
|
||||
func generateFirejailedPath(destination string) (string, error) {
|
||||
safeName, err := generateSafeName(destination)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(firejailedDir, safeName), nil
|
||||
}
|
||||
|
||||
func createProfile(destProfilePath, profilePath string) error {
|
||||
srcFile, err := os.Open(profilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
destFile, err := os.Create(destProfilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
_, err = io.Copy(destFile, srcFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return destFile.Sync()
|
||||
}
|
||||
|
||||
func createWrapperScript(scriptPath, origFilePath, profilePath string) error {
|
||||
scriptContent := fmt.Sprintf("#!/bin/bash\nexec firejail --profile=%q %q \"$@\"\n", profilePath, origFilePath)
|
||||
return os.WriteFile(scriptPath, []byte(scriptContent), defaultDirMode)
|
||||
}
|
||||
|
||||
func createFirejailProfilePath(binaryPath string) (string, error) {
|
||||
name, err := generateSafeName(binaryPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(firejailedDir, fmt.Sprintf("%s.profile", name)), nil
|
||||
}
|
316
internal/build/firejail_test.go
Normal file
316
internal/build/firejail_test.go
Normal file
@ -0,0 +1,316 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// 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 build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
|
||||
)
|
||||
|
||||
func TestIsBinaryFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
destination string
|
||||
expected bool
|
||||
}{
|
||||
{"usr/bin binary", "/usr/bin/test", true},
|
||||
{"bin binary", "/bin/test", true},
|
||||
{"usr/local/bin binary", "/usr/local/bin/test", true},
|
||||
{"lib file", "/usr/lib/test.so", false},
|
||||
{"etc file", "/etc/config", false},
|
||||
{"empty destination", "", false},
|
||||
{"root level file", "./test", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := isBinaryFile(tt.destination)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateSafeName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
destination string
|
||||
expected string
|
||||
expectError bool
|
||||
}{
|
||||
{"usr/bin path", "./usr/bin/test", "_usr_bin_test", false},
|
||||
{"bin path", "./bin/test", "_bin_test", false},
|
||||
{"nested path", "./usr/local/bin/app", "_usr_local_bin_app", false},
|
||||
{"path with spaces", "./usr/bin/my app", "_usr_bin_my app", false},
|
||||
{"empty after trim", ".", "", true},
|
||||
{"empty string", "", "", true},
|
||||
{"only dots", "..", ".", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := generateSafeName(tt.destination)
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateWrapperScript(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
origFilePath string
|
||||
profilePath string
|
||||
expectedContent string
|
||||
}{
|
||||
{
|
||||
"basic wrapper",
|
||||
"/usr/lib/alr/firejailed/_usr_bin_test",
|
||||
"/usr/lib/alr/firejailed/_usr_bin_test.profile",
|
||||
"#!/bin/bash\nexec firejail --profile=\"/usr/lib/alr/firejailed/_usr_bin_test.profile\" \"/usr/lib/alr/firejailed/_usr_bin_test\" \"$@\"\n",
|
||||
},
|
||||
{
|
||||
"path with spaces",
|
||||
"/usr/lib/alr/firejailed/_usr_bin_my_app",
|
||||
"/usr/lib/alr/firejailed/_usr_bin_my_app.profile",
|
||||
"#!/bin/bash\nexec firejail --profile=\"/usr/lib/alr/firejailed/_usr_bin_my_app.profile\" \"/usr/lib/alr/firejailed/_usr_bin_my_app\" \"$@\"\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
scriptPath := filepath.Join(tmpDir, "wrapper.sh")
|
||||
|
||||
err := createWrapperScript(scriptPath, tt.origFilePath, tt.profilePath)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.FileExists(t, scriptPath)
|
||||
|
||||
content, err := os.ReadFile(scriptPath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedContent, string(content))
|
||||
|
||||
// Check file permissions
|
||||
info, err := os.Stat(scriptPath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, os.FileMode(defaultDirMode), info.Mode())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateFirejailedBinary(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFunc func(string) (*alrsh.Package, *files.Content, types.Directories)
|
||||
expectError bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
"successful creation with default profile",
|
||||
func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
|
||||
pkgDir := filepath.Join(tmpDir, "pkg")
|
||||
scriptDir := filepath.Join(tmpDir, "scripts")
|
||||
os.MkdirAll(pkgDir, 0o755)
|
||||
os.MkdirAll(scriptDir, 0o755)
|
||||
|
||||
srcBinary := filepath.Join(tmpDir, "test-binary")
|
||||
os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
|
||||
|
||||
defaultProfile := filepath.Join(scriptDir, "default.profile")
|
||||
os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile\nnet none"), 0o644)
|
||||
|
||||
pkg := &alrsh.Package{
|
||||
Name: "test-pkg",
|
||||
FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{
|
||||
"": {"default": "default.profile"},
|
||||
}),
|
||||
}
|
||||
alrsh.ResolvePackage(pkg, []string{""})
|
||||
|
||||
content := &files.Content{
|
||||
Source: srcBinary,
|
||||
Destination: "./usr/bin/test-binary",
|
||||
Type: "file",
|
||||
}
|
||||
|
||||
dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
|
||||
return pkg, content, dirs
|
||||
},
|
||||
false,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"successful creation with specific profile",
|
||||
func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
|
||||
pkgDir := filepath.Join(tmpDir, "pkg")
|
||||
scriptDir := filepath.Join(tmpDir, "scripts")
|
||||
os.MkdirAll(pkgDir, 0o755)
|
||||
os.MkdirAll(scriptDir, 0o755)
|
||||
|
||||
srcBinary := filepath.Join(tmpDir, "special-binary")
|
||||
os.WriteFile(srcBinary, []byte("#!/bin/bash\necho special"), 0o755)
|
||||
|
||||
defaultProfile := filepath.Join(scriptDir, "default.profile")
|
||||
os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile"), 0o644)
|
||||
|
||||
specialProfile := filepath.Join(scriptDir, "special.profile")
|
||||
os.WriteFile(specialProfile, []byte("include /etc/firejail/default.profile\nnet none\nprivate-tmp"), 0o644)
|
||||
|
||||
pkg := &alrsh.Package{
|
||||
Name: "test-pkg",
|
||||
FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{
|
||||
"": {"default": "default.profile", "/usr/bin/special-binary": "special.profile"},
|
||||
}),
|
||||
}
|
||||
alrsh.ResolvePackage(pkg, []string{""})
|
||||
|
||||
content := &files.Content{
|
||||
Source: srcBinary,
|
||||
Destination: "./usr/bin/special-binary",
|
||||
Type: "file",
|
||||
}
|
||||
|
||||
dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
|
||||
return pkg, content, dirs
|
||||
},
|
||||
false,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"missing default profile",
|
||||
func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
|
||||
pkgDir := filepath.Join(tmpDir, "pkg")
|
||||
scriptDir := filepath.Join(tmpDir, "scripts")
|
||||
os.MkdirAll(pkgDir, 0o755)
|
||||
os.MkdirAll(scriptDir, 0o755)
|
||||
|
||||
srcBinary := filepath.Join(tmpDir, "test-binary")
|
||||
os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
|
||||
|
||||
pkg := &alrsh.Package{
|
||||
Name: "test-pkg",
|
||||
FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {}}),
|
||||
}
|
||||
alrsh.ResolvePackage(pkg, []string{""})
|
||||
|
||||
content := &files.Content{Source: srcBinary, Destination: "./usr/bin/test-binary", Type: "file"}
|
||||
dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
|
||||
return pkg, content, dirs
|
||||
},
|
||||
true,
|
||||
"default profile is missing",
|
||||
},
|
||||
{
|
||||
"profile file not found",
|
||||
func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
|
||||
pkgDir := filepath.Join(tmpDir, "pkg")
|
||||
scriptDir := filepath.Join(tmpDir, "scripts")
|
||||
os.MkdirAll(pkgDir, 0o755)
|
||||
os.MkdirAll(scriptDir, 0o755)
|
||||
|
||||
srcBinary := filepath.Join(tmpDir, "test-binary")
|
||||
os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
|
||||
|
||||
pkg := &alrsh.Package{
|
||||
Name: "test-pkg",
|
||||
FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {"default": "nonexistent.profile"}}),
|
||||
}
|
||||
alrsh.ResolvePackage(pkg, []string{""})
|
||||
|
||||
content := &files.Content{Source: srcBinary, Destination: "./usr/bin/test-binary", Type: "file"}
|
||||
dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
|
||||
return pkg, content, dirs
|
||||
},
|
||||
true,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"invalid destination path",
|
||||
func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
|
||||
pkgDir := filepath.Join(tmpDir, "pkg")
|
||||
scriptDir := filepath.Join(tmpDir, "scripts")
|
||||
os.MkdirAll(pkgDir, 0o755)
|
||||
os.MkdirAll(scriptDir, 0o755)
|
||||
|
||||
srcBinary := filepath.Join(tmpDir, "test-binary")
|
||||
os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
|
||||
|
||||
defaultProfile := filepath.Join(scriptDir, "default.profile")
|
||||
os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile"), 0o644)
|
||||
|
||||
pkg := &alrsh.Package{Name: "test-pkg", FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {"default": "default.profile"}})}
|
||||
alrsh.ResolvePackage(pkg, []string{""})
|
||||
|
||||
content := &files.Content{Source: srcBinary, Destination: ".", Type: "file"}
|
||||
dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
|
||||
return pkg, content, dirs
|
||||
},
|
||||
true,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
pkg, content, dirs := tt.setupFunc(tmpDir)
|
||||
|
||||
err := createFirejailedDirectory(dirs.PkgDir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result, err := createFirejailedBinary(pkg, content, dirs)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
if tt.errorMsg != "" {
|
||||
assert.Contains(t, err.Error(), tt.errorMsg)
|
||||
}
|
||||
assert.Nil(t, result)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
binContent := result[0]
|
||||
assert.Contains(t, binContent.Destination, "usr/lib/alr/firejailed/")
|
||||
assert.FileExists(t, binContent.Source)
|
||||
|
||||
profileContent := result[1]
|
||||
assert.Contains(t, profileContent.Destination, "usr/lib/alr/firejailed/")
|
||||
assert.Contains(t, profileContent.Destination, ".profile")
|
||||
assert.FileExists(t, profileContent.Source)
|
||||
|
||||
assert.FileExists(t, content.Source)
|
||||
wrapperBytes, err := os.ReadFile(content.Source)
|
||||
assert.NoError(t, err)
|
||||
wrapper := string(wrapperBytes)
|
||||
assert.Contains(t, wrapper, "#!/bin/bash")
|
||||
assert.Contains(t, wrapper, "firejail --profile=")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -248,6 +248,16 @@ func buildPkgMetadata(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
normalizeContents(contents)
|
||||
|
||||
if vars.FireJailed.Resolved() {
|
||||
contents, err = applyFirejailIntegration(vars, dirs, contents)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pkgInfo.Overridables.Contents = contents
|
||||
|
||||
if len(vars.AutoProv.Resolved()) == 1 && decoder.IsTruthy(vars.AutoProv.Resolved()[0]) {
|
||||
|
@ -154,6 +154,12 @@ func buildContents(vars *alrsh.Package, dirs types.Directories, preferedContents
|
||||
return contents, nil
|
||||
}
|
||||
|
||||
func normalizeContents(contents []*files.Content) {
|
||||
for _, content := range contents {
|
||||
content.Destination = filepath.Join("/", content.Destination)
|
||||
}
|
||||
}
|
||||
|
||||
var RegexpALRPackageName = regexp.MustCompile(`^(?P<package>[^+]+)\+alr-(?P<repo>.+)$`)
|
||||
|
||||
func getBasePkgInfo(vars *alrsh.Package, input interface {
|
||||
|
Reference in New Issue
Block a user