Skip to content

Commit 3fdb91c

Browse files
committed
pack: added .packignore
In some cases, there are files that may be useful, but should not be part of artifact. The ability to add files and directories to the .packignore file has been added, which allows you to ignore these files and directories when packing. Closes: [812](#812)
1 parent e59c041 commit 3fdb91c

File tree

43 files changed

+274
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+274
-5
lines changed

cli/pack/common.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,14 +277,30 @@ func getDestAppDir(bundleEnvPath, appName string,
277277

278278
// copyApplications copies applications from current env to the result bundle.
279279
func copyApplications(bundleEnvPath string, packCtx *PackCtx,
280-
cliOpts, newOpts *config.CliOpts) error {
281-
var err error
280+
cliOpts, newOpts *config.CliOpts, toIgnore map[string]struct{}) error {
282281
for appName, instances := range packCtx.AppsInfo {
283282
if len(instances) == 0 {
284283
return fmt.Errorf("application %q does not have any instances", appName)
285284
}
286285
inst := instances[0]
287286
appPath := inst.AppDir
287+
288+
projectPath := filepath.Dir(packCtx.configFilePath)
289+
relAppPath, err := filepath.Rel(projectPath, appPath)
290+
if err != nil {
291+
return err
292+
}
293+
relAppPathUnix := filepath.ToSlash(relAppPath)
294+
295+
ignore, err := shouldIgnore(relAppPathUnix, toIgnore)
296+
if err != nil {
297+
return err
298+
}
299+
if ignore {
300+
log.Infof("Application %s found in .packignore, skipping it...", appName)
301+
continue
302+
}
303+
288304
if inst.IsFileApp {
289305
appPath = inst.InstanceScript
290306
resolvedAppPath, err := filepath.EvalSymlinks(appPath)
@@ -363,6 +379,12 @@ func prepareBundle(cmdCtx *cmdcontext.CmdCtx, packCtx *PackCtx,
363379
packCtx.AppList = getAppNamesToPack(packCtx)
364380
log.Infof("Apps to pack: %s", strings.Join(packCtx.AppList, " "))
365381

382+
projectPath := filepath.Dir(packCtx.configFilePath)
383+
ignorePatterns, err := readPackIgnore(projectPath)
384+
if err != nil {
385+
return "", fmt.Errorf("failed to read .packignore: %v", err)
386+
}
387+
366388
if bundleEnvPath, err = updateEnvPath(bundleEnvPath, packCtx, cliOpts); err != nil {
367389
return "", err
368390
}
@@ -373,16 +395,20 @@ func prepareBundle(cmdCtx *cmdcontext.CmdCtx, packCtx *PackCtx,
373395
return "", fmt.Errorf("error copying binaries: %s", err)
374396
}
375397

376-
if err = copyApplications(bundleEnvPath, packCtx, cliOpts, newOpts); err != nil {
398+
if err = copyApplications(bundleEnvPath, packCtx, cliOpts, newOpts, ignorePatterns); err != nil {
377399
return "", fmt.Errorf("error copying applications: %s", err)
378400
}
379401

380402
if packCtx.Archive.All {
381-
if err = copyArtifacts(*packCtx, bundleEnvPath, newOpts, packCtx.AppsInfo); err != nil {
403+
if err = copyArtifacts(*packCtx, bundleEnvPath, newOpts, packCtx.AppsInfo, ignorePatterns); err != nil {
382404
return "", fmt.Errorf("failed copying artifacts: %s", err)
383405
}
384406
}
385407

408+
if err = removeIgnoredFiles(bundleEnvPath, ignorePatterns); err != nil {
409+
return "", fmt.Errorf("failed to remove ignored files: %v", err)
410+
}
411+
386412
if buildRocks {
387413
err = buildAppRocks(cmdCtx, packCtx, cliOpts, bundleEnvPath)
388414
if err != nil && !os.IsNotExist(err) {
@@ -425,7 +451,7 @@ func copyAppSrc(packCtx *PackCtx, cliOpts *config.CliOpts, srcAppPath, dstAppPat
425451
// copyArtifacts copies all artifacts from the current bundle configuration
426452
// to the passed package structure from the passed path.
427453
func copyArtifacts(packCtx PackCtx, basePath string, newOpts *config.CliOpts,
428-
appsInfo map[string][]running.InstanceCtx) error {
454+
appsInfo map[string][]running.InstanceCtx, toIgnore map[string]struct{}) error {
429455

430456
for _, appName := range packCtx.AppList {
431457
for _, inst := range appsInfo[appName] {

cli/pack/common_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,28 @@ func Test_prepareBundle(t *testing.T) {
10681068
{assert.FileExists, "app/tt.yaml"},
10691069
},
10701070
},
1071+
{
1072+
name: "Packing env with packignore:.",
1073+
params: params{
1074+
configPath: "testdata/bundle_with_packignore/tt.yaml",
1075+
tntExecutable: tntExecutable,
1076+
packCtx: PackCtx{Name: "app"},
1077+
},
1078+
wantErr: false,
1079+
checks: []check{
1080+
{assert.NoFileExists, "app2/testfile.txt"},
1081+
1082+
{assert.DirExists, "instances.enabled"},
1083+
{assert.NoFileExists, "instances.enabled/app"},
1084+
1085+
{assert.NoDirExists, "modules"},
1086+
1087+
{assert.DirExists, "app2"},
1088+
{assert.NoDirExists, "app2/var"},
1089+
1090+
{assert.NoFileExists, "app.lua"},
1091+
},
1092+
},
10711093
}
10721094

10731095
for _, tt := range tests {

cli/pack/ignore.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package pack
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
)
10+
11+
// readPackIgnore reads the .packignore file and returns a slice of ignore patterns.
12+
func readPackIgnore(projectPath string) (map[string]struct{}, error) {
13+
ignoreFilePath := filepath.Join(projectPath, ".packignore")
14+
file, err := os.Open(ignoreFilePath)
15+
if err != nil {
16+
if os.IsNotExist(err) {
17+
return map[string]struct{}{}, nil
18+
}
19+
return nil, err
20+
}
21+
defer file.Close()
22+
23+
patterns := make(map[string]struct{})
24+
scanner := bufio.NewScanner(file)
25+
for scanner.Scan() {
26+
line := scanner.Text()
27+
line = strings.TrimSpace(line)
28+
if line == "" || strings.HasPrefix(line, "#") {
29+
continue
30+
}
31+
patterns[line] = struct{}{}
32+
}
33+
34+
if err := scanner.Err(); err != nil {
35+
return nil, err
36+
}
37+
return patterns, nil
38+
}
39+
40+
// shouldIgnore checks if the given file path matches any of the ignore patterns.
41+
func shouldIgnore(path string, patterns map[string]struct{}) (bool, error) {
42+
for pattern := range patterns {
43+
pattern = filepath.ToSlash(pattern)
44+
filePath := filepath.ToSlash(path)
45+
46+
if strings.HasSuffix(pattern, "/") {
47+
if strings.HasPrefix(filePath, pattern) {
48+
return true, nil
49+
}
50+
continue
51+
}
52+
53+
match, err := filepath.Match(pattern, filePath)
54+
if err != nil {
55+
return false, err
56+
}
57+
if match {
58+
return true, nil
59+
}
60+
}
61+
return false, nil
62+
}
63+
64+
// removeIgnoredFiles walks through the bundle directory and removes files or directories
65+
// that match the ignore patterns.
66+
func removeIgnoredFiles(bundleEnvPath string, patterns map[string]struct{}) error {
67+
return filepath.Walk(bundleEnvPath, func(path string, info os.FileInfo, err error) error {
68+
if err != nil {
69+
return err
70+
}
71+
72+
relPath, err := filepath.Rel(bundleEnvPath, path)
73+
if err != nil {
74+
return err
75+
}
76+
77+
relPathUnix := filepath.ToSlash(relPath)
78+
79+
ignore, err := shouldIgnore(relPathUnix, patterns)
80+
if err != nil {
81+
return err
82+
}
83+
84+
if ignore {
85+
if info.IsDir() {
86+
err = os.RemoveAll(path)
87+
if err != nil {
88+
return fmt.Errorf("failed to remove directory %q: %v", path, err)
89+
}
90+
return filepath.SkipDir
91+
} else {
92+
err = os.Remove(path)
93+
if err != nil {
94+
return fmt.Errorf("failed to remove file %q: %v", path, err)
95+
}
96+
}
97+
}
98+
return nil
99+
})
100+
}

cli/pack/ignore_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package pack
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
)
8+
9+
func TestPackIgnore(t *testing.T) {
10+
projectDir, err := os.MkdirTemp("", "test")
11+
if err != nil {
12+
t.Fatalf("Failed to create temp directory: %v", err)
13+
}
14+
defer os.RemoveAll(projectDir)
15+
16+
packignoreFile := `
17+
.txt
18+
logs/
19+
secret.key
20+
temp*
21+
*.bak
22+
`
23+
packignorePath := filepath.Join(projectDir, ".packignore")
24+
err = os.WriteFile(packignorePath, []byte(packignoreFile), 0644)
25+
if err != nil {
26+
t.Fatalf("Failed to write .packignore file: %v", err)
27+
}
28+
29+
patterns, err := readPackIgnore(projectDir)
30+
if err != nil {
31+
t.Fatalf("Failed to read .packignore: %v", err)
32+
}
33+
34+
tests := []struct {
35+
path string
36+
expected bool
37+
}{
38+
{"file.txt", true},
39+
{"document.txt", true},
40+
{"image.png", false},
41+
{"logs/error.log", true},
42+
{"logs/", true},
43+
{"logs", false},
44+
{"secret.key", true},
45+
{"config.yaml", false},
46+
{"tempfile", true},
47+
{"temporary", true},
48+
{"template", true},
49+
{"data.bak", true},
50+
{"backup.bak", true},
51+
{"main.go", false},
52+
}
53+
54+
for _, test := range tests {
55+
ignore, err := shouldIgnore(test.path, patterns)
56+
if err != nil {
57+
t.Errorf("Error in shouldIgnore for path %q: %v", test.path, err)
58+
}
59+
if ignore != test.expected {
60+
t.Errorf("For path %q, expected ignore=%v, got %v", test.path, test.expected, ignore)
61+
}
62+
}
63+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
app2/testfile.txt
2+
instances.enabled/app
3+
modules
4+
app2/var
5+
app.lua

cli/pack/testdata/bundle_with_packignore/app.lua

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package = 'app2'
2+
version = 'scm-1'
3+
source = {
4+
url = '/dev/null',
5+
}
6+
-- Put any modules your app depends on here
7+
dependencies = {
8+
'tarantool',
9+
'lua >= 5.1',
10+
}
11+
build = {
12+
type = 'none';
13+
}
14+

cli/pack/testdata/bundle_with_packignore/app2/init.lua

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
inst1:
2+
3+
inst2:

cli/pack/testdata/bundle_with_packignore/app2/testfile.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)