From 75dae97f4e6e71309803502b33b66d6b640cd301 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 24 Sep 2025 12:14:08 +0200 Subject: [PATCH 01/46] improve comments for Link struct and its fields in linkedfiles.go --- internal/files/linkedfiles.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index 597999b50f..83e7c65da3 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -167,21 +167,20 @@ func (lfs *LinksFS) ListLinkedFilesByPackage() ([]PackageLinks, error) { } // A Link represents a linked file. -// It contains the path to the link file, the checksum of the link file, -// the path to the included file, and the checksum of the included file contents. -// It also contains a boolean indicating whether the link is up to date. +// A linked file is a file with the ".link" extension that contains a reference to another file in an other package. +// The link file contains the relative path to the included file and an optional checksum of the included file contents. type Link struct { - WorkDir string + WorkDir string // WorkDir is the path to the directory containing the link file. This is where the copy of the included file will be placed. - LinkFilePath string + LinkFilePath string // LinkFilePath is the relative path of the linked file and the package root LinkChecksum string - LinkPackageName string + LinkPackageName string // Package where the link file is located - IncludedFilePath string - IncludedFileContentsChecksum string - IncludedPackageName string + IncludedFilePath string // IncludedFilePath is the path to the included file, this is the content of the link file + IncludedFileContentsChecksum string // IncludedFileContentsChecksum is the checksum of the included file contents, this is the second field in the link file + IncludedPackageName string // IncludedPackageName is the package where the included file is located - UpToDate bool + UpToDate bool // UpToDate indicates whether the content of the included file matches the checksum in the link file } // NewLinkedFile creates a new Link from the given link file path. From 83e13ad6b8e167894d69ddecd6ee850f882552c3 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 24 Sep 2025 12:17:35 +0200 Subject: [PATCH 02/46] refactor: enhance newLinkedFile function for better error handling and clarity --- internal/files/linkedfiles.go | 92 ++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index 83e7c65da3..af41241929 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -183,66 +183,80 @@ type Link struct { UpToDate bool // UpToDate indicates whether the content of the included file matches the checksum in the link file } -// NewLinkedFile creates a new Link from the given link file path. -func newLinkedFile(root *os.Root, linkFilePath string) (Link, error) { - var l Link - l.WorkDir = filepath.Dir(linkFilePath) - if linkPackageRoot, _, _ := packages.FindPackageRootFrom(l.WorkDir); linkPackageRoot != "" { - l.LinkPackageName = filepath.Base(linkPackageRoot) +// newLinkedFile creates a new Link struct from the given absolute path to a link file. +// root is the repository root, used to validate paths and access files securely +func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { + + workDir := filepath.Dir(linkFilePath) + + // validate that workDir is within the package root + linkPackageRoot, ok, err := packages.FindPackageRootFrom(workDir) + if err != nil || !ok { + return nil, fmt.Errorf("could not find package root: %w", err) } + linkPackageName := filepath.Base(linkPackageRoot) - firstLine, err := readFirstLine(linkFilePath) + linkFileRelativePath, err := filepath.Rel(linkPackageRoot, linkFilePath) if err != nil { - return Link{}, err + return nil, fmt.Errorf("could not get relative path: %w", err) } - l.LinkFilePath, err = filepath.Rel(l.WorkDir, linkFilePath) + + // read the content of the .link file, extract the included file path and checksum + firstLine, err := readFirstLine(linkFilePath) if err != nil { - return Link{}, fmt.Errorf("could not get relative path: %w", err) + return nil, err } - fields := strings.Fields(firstLine) if len(fields) == 0 { - return Link{}, fmt.Errorf("link file %s is empty or has no valid content", linkFilePath) + return nil, fmt.Errorf("link file %s is empty or has no valid content", linkFilePath) + } else if len(fields) > 2 { + return nil, fmt.Errorf("link file %s has invalid format: expected 1 or 2 fields, got %d", linkFilePath, len(fields)) } - if len(fields) > 2 { - return Link{}, fmt.Errorf("link file %s has invalid format: expected 1 or 2 fields, got %d", linkFilePath, len(fields)) - } - l.IncludedFilePath = fields[0] + includedFileRelPath := fields[0] + // having the checksum is optional + var linkfileChecksum string if len(fields) == 2 { - l.LinkChecksum = fields[1] + linkfileChecksum = fields[1] } - pathName := filepath.Clean(filepath.Join(l.WorkDir, filepath.FromSlash(l.IncludedFilePath))) - - // Store the original absolute path for package root detection - originalAbsPath := pathName + // pathname represents the absolute path to the included file which content we want to copy to the link file + // resolves the path of the included file relative to the link file + includedFilePath := filepath.Clean(filepath.Join(workDir, filepath.FromSlash(includedFileRelPath))) - // Convert to relative path for secure access of target file - if filepath.IsAbs(pathName) { - pathName, err = filepath.Rel(root.Name(), pathName) + // get relative path of the included file from the root (packages root or repository root) + includedFilePathRelFromRoot, err := filepath.Rel(repoRoot.Name(), includedFilePath) if err != nil { - return Link{}, fmt.Errorf("could not get relative path: %w", err) + return nil, fmt.Errorf("could not get relative path: %w", err) } + // check the file exists + if _, err := repoRoot.Stat(includedFilePathRelFromRoot); err != nil { + return nil, err } - if _, err := root.Stat(pathName); err != nil { - return Link{}, err - } - - cs, err := getLinkedFileChecksumFromRoot(root, pathName) + // check if checksum is updated + cs, err := getLinkedFileChecksumFromRoot(repoRoot, includedFilePathRelFromRoot) if err != nil { - return Link{}, fmt.Errorf("could not collect file %s: %w", l.IncludedFilePath, err) + return nil, fmt.Errorf("could not collect file %s: %w", includedFilePathRelFromRoot, err) } - if l.LinkChecksum == cs { - l.UpToDate = true - } - l.IncludedFileContentsChecksum = cs - if includedPackageRoot, _, _ := packages.FindPackageRootFrom(filepath.Dir(originalAbsPath)); includedPackageRoot != "" { - l.IncludedPackageName = filepath.Base(includedPackageRoot) + checksumUpdated := cs == linkfileChecksum + + includedPackageRoot, ok, err := packages.FindPackageRootFrom(filepath.Dir(includedFilePath)) + if err != nil || !ok { + return nil, fmt.Errorf("could not find included package root: %w", err) } + includedPackageName := filepath.Base(includedPackageRoot) - return l, nil + return &Link{ + WorkDir: workDir, + LinkFilePath: linkFileRelativePath, + LinkChecksum: linkfileChecksum, + LinkPackageName: linkPackageName, + IncludedFilePath: includedFileRelPath, + IncludedFileContentsChecksum: cs, + IncludedPackageName: includedPackageName, + UpToDate: checksumUpdated, + }, nil } // updateChecksum function updates the checksum of the linked file. @@ -328,7 +342,7 @@ func listLinkedFiles(root *os.Root, fromDir string) ([]Link, error) { if err != nil { return nil, fmt.Errorf("could not initialize linked file %s: %w", f, err) } - links[i] = l + links[i] = *l } return links, nil From 9139bb2f1acc8fd3e5111653986a6fd90888c30d Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 24 Sep 2025 12:17:47 +0200 Subject: [PATCH 03/46] test: add unit test for newLinkedFile function to validate link file processing --- internal/files/linkedfiles_test.go | 72 ++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index f057f955ca..703d72d8b4 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -886,3 +886,75 @@ func TestLinksFS_WorkDirValidation(t *testing.T) { }) } } + +func Test_newLinkedFile(t *testing.T) { + + repoRoot := t.TempDir() + repoRootHandle, err := os.OpenRoot(repoRoot) + require.NoError(t, err) + t.Cleanup(func() { _ = repoRootHandle.Close() }) + + linkFileContent := "../../B/otherFolder/included.txt d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e" + includedFileContent := "included file content" + + // /packages/A/folder/link.txt.link + // /packages/B/otherFolder/included.txt + // included relative to link file: ../../B/otherFolder/included.txt + + err = repoRootHandle.MkdirAll(filepath.Join("packages", "A", "folder"), 0755) + require.NoError(t, err) + + // required to identify a package root + _, err = repoRootHandle.Create("packages/A/manifest.yml") + require.NoError(t, err) + fManifestA, err := repoRootHandle.Create("packages/A/manifest.yml") + require.NoError(t, err) + _, err = fManifestA.WriteString(`name: A +version: 1.0.0 +type: integration +`) + require.NoError(t, err) + require.NoError(t, fManifestA.Close()) + + fLink, err := repoRootHandle.Create("packages/A/folder/link.txt.link") + require.NoError(t, err) + _, err = fLink.WriteString(linkFileContent) + require.NoError(t, err) + require.NoError(t, fLink.Close()) + + err = repoRootHandle.MkdirAll(filepath.Join("packages", "B", "otherFolder"), 0755) + require.NoError(t, err) + + // required to identify a package root + _, err = repoRootHandle.Create("packages/B/manifest.yml") + require.NoError(t, err) + fManifestB, err := repoRootHandle.Create("packages/B/manifest.yml") + require.NoError(t, err) + _, err = fManifestB.WriteString(`name: B +version: 1.0.0 +type: integration +`) + require.NoError(t, err) + require.NoError(t, fManifestB.Close()) + + fIncluded, err := repoRootHandle.Create("packages/B/otherFolder/included.txt") + require.NoError(t, err) + _, err = fIncluded.WriteString(includedFileContent) + require.NoError(t, err) + require.NoError(t, fIncluded.Close()) + + l, err := newLinkedFile(repoRootHandle, fLink.Name()) + require.NoError(t, err) + assert.NotNil(t, l) + + assert.Equal(t, filepath.Join(repoRoot, "packages/A/folder"), l.WorkDir) + + assert.Equal(t, "folder/link.txt.link", l.LinkFilePath) + assert.Equal(t, "d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", l.LinkChecksum) + assert.Equal(t, "A", l.LinkPackageName) + + assert.Equal(t, "../../B/otherFolder/included.txt", l.IncludedFilePath) + assert.Equal(t, "1c14ca1eae312a58c08085436fd5dcd0c02451d3cdc8e4e4a1cc1415a6b4c6d0", l.IncludedFileContentsChecksum) + assert.Equal(t, "B", l.IncludedPackageName) + assert.False(t, l.UpToDate) +} From 60672b261e815dc47ff45c7e3baf4b069f0a7fce Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 25 Sep 2025 12:14:02 +0200 Subject: [PATCH 04/46] decoupled repoRoot from lint cmd and docs --- cmd/lint.go | 14 ++- internal/docs/links_map.go | 65 +++++------ internal/docs/links_map_test.go | 187 +++++++++++++++++--------------- internal/docs/readme.go | 22 ++-- internal/docs/readme_test.go | 20 ++-- 5 files changed, 160 insertions(+), 148 deletions(-) diff --git a/cmd/lint.go b/cmd/lint.go index bba2188f9d..11453c4854 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -12,6 +12,7 @@ import ( "github.com/elastic/elastic-package/internal/cobraext" "github.com/elastic/elastic-package/internal/docs" + "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/packages" "github.com/elastic/elastic-package/internal/validation" @@ -45,7 +46,18 @@ func setupLintCommand() *cobraext.Command { func lintCommandAction(cmd *cobra.Command, args []string) error { cmd.Println("Lint the package") - readmeFiles, err := docs.AreReadmesUpToDate() + + repoRoot, err := files.FindRepositoryRootDirectory() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + + linksFilePath, err := docs.LinksDefinitionsFilePath(repoRoot) + if err != nil { + return fmt.Errorf("locating links file failed: %w", err) + } + + readmeFiles, err := docs.AreReadmesUpToDate(linksFilePath) if err != nil { for _, f := range readmeFiles { if !f.UpToDate { diff --git a/internal/docs/links_map.go b/internal/docs/links_map.go index d024bf6906..94703dc5bf 100644 --- a/internal/docs/links_map.go +++ b/internal/docs/links_map.go @@ -12,7 +12,6 @@ import ( "gopkg.in/yaml.v3" "github.com/elastic/elastic-package/internal/environment" - "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/logger" ) @@ -28,20 +27,20 @@ type linkOptions struct { caption string } -func newLinkMap() linkMap { - var links linkMap - links.Links = make(map[string]string) - return links +func newLinkMap() *linkMap { + return &linkMap{ + Links: make(map[string]string), + } } -func (l linkMap) Get(key string) (string, error) { +func (l *linkMap) Get(key string) (string, error) { if url, ok := l.Links[key]; ok { return url, nil } return "", fmt.Errorf("link key not found: %s", key) } -func (l linkMap) Add(key, value string) error { +func (l *linkMap) Add(key, value string) error { if _, ok := l.Links[key]; ok { return fmt.Errorf("link key already present: %s", key) } @@ -49,29 +48,28 @@ func (l linkMap) Add(key, value string) error { return nil } -func readLinksMap() (linkMap, error) { - linksFilePath, err := linksDefinitionsFilePath() - if err != nil { - return linkMap{}, fmt.Errorf("locating links file failed: %w", err) - } - - links := newLinkMap() +// readLinksMap reads the links definitions file from the given repository root directory, +// parses its YAML contents, and returns a populated linkMap. If the links file does not exist, +// it returns an empty linkMap. Returns an error if locating, reading, or unmarshalling the file fails. +func readLinksMap(linksFilePath string) (*linkMap, error) { + // No links file, return empty map with Links initialized if linksFilePath == "" { - return links, nil + return newLinkMap(), nil } logger.Debugf("Using links definitions file: %s", linksFilePath) contents, err := os.ReadFile(linksFilePath) if err != nil { - return linkMap{}, fmt.Errorf("readfile failed (path: %s): %w", linksFilePath, err) + return nil, fmt.Errorf("readfile failed (path: %s): %w", linksFilePath, err) } - err = yaml.Unmarshal(contents, &links) + var lmap linkMap + err = yaml.Unmarshal(contents, &lmap) if err != nil { - return linkMap{}, err + return nil, err } - return links, nil + return &lmap, nil } func (l linkMap) RenderLink(key string, options linkOptions) (string, error) { @@ -85,32 +83,27 @@ func (l linkMap) RenderLink(key string, options linkOptions) (string, error) { return url, nil } -// linksDefinitionsFilePath returns the path where links definitions are located or empty string if the file does not exist. -// If linksMapFilePathEnvVar is defined, it returns the value of that env var. -func linksDefinitionsFilePath() (string, error) { - var err error - linksFilePath, ok := os.LookupEnv(linksMapFilePathEnvVar) - if ok { - _, err = os.Stat(linksFilePath) - if err != nil { +// LinksDefinitionsFilePath returns the file path to the links definitions file. +// It first checks if the environment variable specified by linksMapFilePathEnvVar is set. +// If set, it verifies that the file exists and returns its path, or an error if not found. +// If the environment variable is not set, it falls back to the default file path +// constructed from repoRoot and linksMapFileNameDefault, returning the path if the file exists, +// or nil if it does not. +func LinksDefinitionsFilePath(repoRoot string) (string, error) { + linksFilePath := os.Getenv(linksMapFilePathEnvVar) + if linksFilePath != "" { + if _, err := os.Stat(linksFilePath); err != nil { // if env var is defined, file must exist return "", fmt.Errorf("links definitions file set with %s doesn't exist: %s", linksMapFilePathEnvVar, linksFilePath) } return linksFilePath, nil } - dir, err := files.FindRepositoryRootDirectory() - if err != nil { - return "", err - } - - linksFilePath = filepath.Join(dir, linksMapFileNameDefault) - _, err = os.Stat(linksFilePath) - if err != nil { + linksFilePath = filepath.Join(repoRoot, linksMapFileNameDefault) + if _, err := os.Stat(linksFilePath); err != nil { logger.Debugf("links definitions default file doesn't exist: %s", linksFilePath) return "", nil } return linksFilePath, nil - } diff --git a/internal/docs/links_map_test.go b/internal/docs/links_map_test.go index b0753191ae..a86574fb01 100644 --- a/internal/docs/links_map_test.go +++ b/internal/docs/links_map_test.go @@ -90,93 +90,106 @@ func TestRenderLink(t *testing.T) { } func TestLinksDefinitionsFilePath(t *testing.T) { - currentDirectory, _ := os.Getwd() - temporalDirecotry := t.TempDir() - - cases := []struct { - title string - createFileFromEnvVar bool - createDefaultFile bool - linksFilePath string - expectedErrors bool - expected string - }{ - { - title: "No env var and no default file", - createFileFromEnvVar: false, - createDefaultFile: false, - linksFilePath: "", - expectedErrors: false, - expected: "", - }, - { - title: "No env var - default file", - createFileFromEnvVar: false, - createDefaultFile: true, - linksFilePath: "", - expectedErrors: false, - expected: filepath.Join(currentDirectory, "links_table.yml"), - }, - { - title: "Env var defined", - createFileFromEnvVar: true, - createDefaultFile: false, - linksFilePath: filepath.Join(temporalDirecotry, "links_table.yml"), - expectedErrors: false, - expected: filepath.Join(temporalDirecotry, "links_table.yml"), - }, - { - title: "Env var defined but just default file exists", - createFileFromEnvVar: false, - createDefaultFile: true, - linksFilePath: filepath.Join(temporalDirecotry, "links_table_2.yml"), - expectedErrors: true, - expected: "", - }, - } - - createGitFolder() - defer removeGitFolder() - - for _, c := range cases { - t.Run(c.title, func(t *testing.T) { - var err error - if c.linksFilePath != "" { - err = os.Setenv(linksMapFilePathEnvVar, c.linksFilePath) - require.NoError(t, err) - defer os.Unsetenv(linksMapFilePathEnvVar) - } - - if c.createFileFromEnvVar { - err = createLinksFile(c.linksFilePath) - defer removeLinksFile(c.linksFilePath) - require.NoError(t, err) - } - - if c.createDefaultFile { - err = createLinksFile(linksMapFileNameDefault) - require.NoError(t, err) - defer removeLinksFile(linksMapFileNameDefault) - } - - path, err := linksDefinitionsFilePath() - - if c.expectedErrors { - require.Error(t, err) - } else { - require.NoError(t, err) - assert.Equal(t, c.expected, path) - } - }) - } + t.Run("env var set and file exists", func(t *testing.T) { + repoRoot := t.TempDir() + defaultFilePath := filepath.Join(repoRoot, linksMapFileNameDefault) + testFile := filepath.Join(repoRoot, "custom_links.yml") + require.NoError(t, createLinksFile(testFile)) + require.NoError(t, createLinksFile(defaultFilePath)) // to ensure default file is ignored + t.Setenv(linksMapFilePathEnvVar, testFile) + + path, err := LinksDefinitionsFilePath(repoRoot) + require.NoError(t, err) + assert.Equal(t, testFile, path) + }) + + t.Run("env var set but file does not exist", func(t *testing.T) { + repoRoot := t.TempDir() + + missingFile := filepath.Join(repoRoot, "missing_links.yml") + t.Setenv(linksMapFilePathEnvVar, missingFile) + + path, err := LinksDefinitionsFilePath(repoRoot) + require.Error(t, err) + assert.Empty(t, path) + }) + + t.Run("env var not set, default file exists", func(t *testing.T) { + repoRoot := t.TempDir() + defaultFilePath := filepath.Join(repoRoot, linksMapFileNameDefault) + + require.NoError(t, createLinksFile(defaultFilePath)) + + path, err := LinksDefinitionsFilePath(repoRoot) + require.NoError(t, err) + + assert.Equal(t, defaultFilePath, path) + assert.Empty(t, os.Getenv(linksMapFilePathEnvVar)) + }) + + t.Run("env var not set, default file does not exist", func(t *testing.T) { + repoRoot := t.TempDir() + defaultFilePath := filepath.Join(repoRoot, linksMapFileNameDefault) + + _, err := os.Stat(defaultFilePath) + require.True(t, os.IsNotExist(err)) + + path, err := LinksDefinitionsFilePath(repoRoot) + require.NoError(t, err) + assert.Empty(t, path) + }) } -func createGitFolder() error { - return os.MkdirAll(".git", os.ModePerm) -} - -func removeGitFolder() error { - return os.RemoveAll(".git") +func TestReadLinksMap(t *testing.T) { + t.Run("empty path returns empty map", func(t *testing.T) { + lmap, err := readLinksMap("") + require.NoError(t, err) + require.NotNil(t, lmap) + assert.Empty(t, lmap.Links) + }) + + t.Run("non-existent file returns error", func(t *testing.T) { + tmpDir := t.TempDir() + missingFile := filepath.Join(tmpDir, "missing.yml") + lmap, err := readLinksMap(missingFile) + require.Error(t, err) + assert.Nil(t, lmap) + }) + + t.Run("invalid YAML returns error", func(t *testing.T) { + tmpDir := t.TempDir() + filePath := filepath.Join(tmpDir, "invalid.yml") + require.NoError(t, os.WriteFile(filePath, []byte("not: valid: yaml: ["), 0644)) + lmap, err := readLinksMap(filePath) + require.Error(t, err) + assert.Nil(t, lmap) + }) + + t.Run("valid YAML returns populated map", func(t *testing.T) { + tmpDir := t.TempDir() + filePath := filepath.Join(tmpDir, "links.yml") + yamlContent := []byte(`links: + intro: http://package-spec.test/intro + docs: http://package-spec.test/docs +`) + require.NoError(t, os.WriteFile(filePath, yamlContent, 0644)) + lmap, err := readLinksMap(filePath) + require.NoError(t, err) + require.NotNil(t, lmap) + assert.Equal(t, "http://package-spec.test/intro", lmap.Links["intro"]) + assert.Equal(t, "http://package-spec.test/docs", lmap.Links["docs"]) + }) + + t.Run("valid YAML with empty links returns empty map", func(t *testing.T) { + tmpDir := t.TempDir() + filePath := filepath.Join(tmpDir, "empty.yml") + yamlContent := []byte("links: {}\n") + require.NoError(t, os.WriteFile(filePath, yamlContent, 0644)) + lmap, err := readLinksMap(filePath) + require.NoError(t, err) + require.NotNil(t, lmap) + assert.Empty(t, lmap.Links) + }) } func createLinksFile(filepath string) error { @@ -187,7 +200,3 @@ func createLinksFile(filepath string) error { defer file.Close() return nil } - -func removeLinksFile(filepath string) error { - return os.Remove(filepath) -} diff --git a/internal/docs/readme.go b/internal/docs/readme.go index 0e44238db7..dc906a8d9b 100644 --- a/internal/docs/readme.go +++ b/internal/docs/readme.go @@ -33,7 +33,7 @@ const ( ) // AreReadmesUpToDate function checks if all the .md readme files are up-to-date. -func AreReadmesUpToDate() ([]ReadmeFile, error) { +func AreReadmesUpToDate(linksFilePath string) ([]ReadmeFile, error) { packageRoot, err := packages.MustFindPackageRoot() if err != nil { return nil, fmt.Errorf("package root not found: %w", err) @@ -47,7 +47,7 @@ func AreReadmesUpToDate() ([]ReadmeFile, error) { var readmeFiles []ReadmeFile for _, filePath := range files { fileName := filepath.Base(filePath) - ok, diff, err := isReadmeUpToDate(fileName, packageRoot) + ok, diff, err := isReadmeUpToDate(fileName, linksFilePath, packageRoot) if !ok || err != nil { readmeFile := ReadmeFile{ FileName: fileName, @@ -65,10 +65,10 @@ func AreReadmesUpToDate() ([]ReadmeFile, error) { return readmeFiles, nil } -func isReadmeUpToDate(fileName, packageRoot string) (bool, string, error) { +func isReadmeUpToDate(fileName, linksFilePath, packageRoot string) (bool, string, error) { logger.Debugf("Check if %s is up-to-date", fileName) - rendered, shouldBeRendered, err := generateReadme(fileName, packageRoot) + rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot) if err != nil { return false, "", fmt.Errorf("generating readme file failed: %w", err) } @@ -99,7 +99,7 @@ func isReadmeUpToDate(fileName, packageRoot string) (bool, string, error) { // UpdateReadmes function updates all .md readme files using a defined template // files. The function doesn't perform any action if the template file is not present. -func UpdateReadmes(packageRoot, buildDir string) ([]string, error) { +func UpdateReadmes(linksFilePath, packageRoot, buildDir string) ([]string, error) { readmeFiles, err := filepath.Glob(filepath.Join(packageRoot, "_dev", "build", "docs", "*.md")) if err != nil { return nil, fmt.Errorf("reading directory entries failed: %w", err) @@ -108,7 +108,7 @@ func UpdateReadmes(packageRoot, buildDir string) ([]string, error) { var targets []string for _, filePath := range readmeFiles { fileName := filepath.Base(filePath) - target, err := updateReadme(fileName, packageRoot, buildDir) + target, err := updateReadme(fileName, linksFilePath, packageRoot, buildDir) if err != nil { return nil, fmt.Errorf("updating readme file %s failed: %w", fileName, err) } @@ -120,10 +120,10 @@ func UpdateReadmes(packageRoot, buildDir string) ([]string, error) { return targets, nil } -func updateReadme(fileName, packageRoot, buildDir string) (string, error) { +func updateReadme(fileName, linksFilePath, packageRoot, buildDir string) (string, error) { logger.Debugf("Update the %s file", fileName) - rendered, shouldBeRendered, err := generateReadme(fileName, packageRoot) + rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot) if err != nil { return "", err } @@ -148,7 +148,7 @@ func updateReadme(fileName, packageRoot, buildDir string) (string, error) { return target, nil } -func generateReadme(fileName, packageRoot string) ([]byte, bool, error) { +func generateReadme(fileName, linksFilePath, packageRoot string) ([]byte, bool, error) { logger.Debugf("Generate %s file (package: %s)", fileName, packageRoot) templatePath, found, err := findReadmeTemplatePath(fileName, packageRoot) if err != nil { @@ -160,7 +160,7 @@ func generateReadme(fileName, packageRoot string) ([]byte, bool, error) { } logger.Debugf("Template file for %s found: %s", fileName, templatePath) - linksMap, err := readLinksMap() + linksMap, err := readLinksMap(linksFilePath) if err != nil { return nil, false, err } @@ -184,7 +184,7 @@ func findReadmeTemplatePath(fileName, packageRoot string) (string, bool, error) return templatePath, true, nil } -func renderReadme(fileName, packageRoot, templatePath string, linksMap linkMap) ([]byte, error) { +func renderReadme(fileName, packageRoot, templatePath string, linksMap *linkMap) ([]byte, error) { logger.Debugf("Render %s file (package: %s, templatePath: %s)", fileName, packageRoot, templatePath) t := template.New(fileName) diff --git a/internal/docs/readme_test.go b/internal/docs/readme_test.go index d63c069b0b..23f7f3b16c 100644 --- a/internal/docs/readme_test.go +++ b/internal/docs/readme_test.go @@ -18,15 +18,13 @@ import ( func TestGenerateReadme(t *testing.T) { cases := []struct { title string - packageRoot string filename string readmeTemplateContents string expected string }{ { - title: "Pure markdown", - packageRoot: t.TempDir(), - filename: "README.md", + title: "Pure markdown", + filename: "README.md", readmeTemplateContents: ` # README Introduction to the package`, @@ -35,9 +33,8 @@ Introduction to the package`, Introduction to the package`, }, { - title: "Generated headers", - packageRoot: t.TempDir(), - filename: "README.md", + title: "Generated headers", + filename: "README.md", readmeTemplateContents: ` {{- generatedHeader }} # README @@ -49,7 +46,6 @@ Introduction to the package`, }, { title: "Static README", - packageRoot: t.TempDir(), filename: "README.md", readmeTemplateContents: "", expected: "", @@ -57,10 +53,12 @@ Introduction to the package`, } for _, c := range cases { t.Run(c.title, func(t *testing.T) { - err := createReadmeFile(c.packageRoot, c.readmeTemplateContents) + + dir := t.TempDir() + err := createReadmeFile(dir, c.readmeTemplateContents) require.NoError(t, err) - rendered, isTemplate, err := generateReadme(c.filename, c.packageRoot) + rendered, isTemplate, err := generateReadme(c.filename, "", dir) require.NoError(t, err) if c.readmeTemplateContents != "" { @@ -84,7 +82,7 @@ func TestRenderReadmeWithLinks(t *testing.T) { packageRoot string templatePath string readmeTemplateContents string - linksMap linkMap + linksMap *linkMap expected string }{ { From 211c7c9fad2dff056b0c035ce473cc13ecd195b4 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 25 Sep 2025 15:50:22 +0200 Subject: [PATCH 05/46] update repository root handling across multiple components --- cmd/build.go | 12 +- cmd/links.go | 23 +++- internal/builder/packages.go | 43 ++++--- internal/builder/packages_test.go | 117 ++++++++++++++++++ internal/elasticsearch/ingest/datastream.go | 7 +- internal/files/linkedfiles.go | 49 ++++---- .../packages/archetype/data_stream_test.go | 19 ++- internal/packages/archetype/package_test.go | 40 ++---- internal/packages/installer/factory.go | 3 + internal/resources/fleetpackage.go | 5 + internal/resources/fleetpackage_test.go | 12 +- internal/resources/fleetpolicy.go | 4 + internal/resources/fleetpolicy_test.go | 15 ++- 13 files changed, 268 insertions(+), 81 deletions(-) create mode 100644 internal/builder/packages_test.go diff --git a/cmd/build.go b/cmd/build.go index a558a4eba8..cde40e6d8d 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -61,6 +61,16 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } } + repoRoot, err := files.FindRepositoryRootDirectory() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + + linksFilePath, err := docs.LinksDefinitionsFilePath(repoRoot) + if err != nil { + return fmt.Errorf("locating links file failed: %w", err) + } + packageRoot, err := packages.MustFindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) @@ -72,7 +82,7 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } logger.Debugf("Use build directory: %s", buildDir) - targets, err := docs.UpdateReadmes(packageRoot, buildDir) + targets, err := docs.UpdateReadmes(linksFilePath, packageRoot, buildDir) if err != nil { return fmt.Errorf("updating files failed: %w", err) } diff --git a/cmd/links.go b/cmd/links.go index 7fdbccfdbf..7e4c37d244 100644 --- a/cmd/links.go +++ b/cmd/links.go @@ -56,8 +56,13 @@ func linksCheckCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("reading current working directory failed: %w", err) } + // Find the repository root to create the links filesystem reference tied to the repository root + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("finding repository root: %w", err) + } - linksFS, err := files.CreateLinksFSFromPath(pwd) + linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) if err != nil { return fmt.Errorf("creating links filesystem failed: %w", err) } @@ -95,7 +100,13 @@ func linksUpdateCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("reading current working directory failed: %w", err) } - linksFS, err := files.CreateLinksFSFromPath(pwd) + // Find the repository root to create the links filesystem reference tied to the repository root + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("finding repository root: %w", err) + } + + linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) if err != nil { return fmt.Errorf("creating links filesystem failed: %w", err) } @@ -135,7 +146,13 @@ func linksListCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("reading current working directory failed: %w", err) } - linksFS, err := files.CreateLinksFSFromPath(pwd) + // Find the repository root to create the links filesystem reference tied to the repository root + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("finding repository root: %w", err) + } + + linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) if err != nil { return fmt.Errorf("creating links filesystem failed: %w", err) } diff --git a/internal/builder/packages.go b/internal/builder/packages.go index c3096d25c3..4078be2637 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -28,6 +28,7 @@ var repositoryLicenseEnv = environment.WithElasticPackagePrefix("REPOSITORY_LICE type BuildOptions struct { PackageRoot string BuildDir string + RepoRoot *os.Root CreateZip bool SignPackage bool @@ -182,7 +183,8 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { } logger.Debug("Copy license file if needed") - err = copyLicenseTextFile(filepath.Join(destinationDir, licenseTextFileName)) + destinationLicenseFilePath := filepath.Join(destinationDir, licenseTextFileName) + err = copyLicenseTextFile(options.RepoRoot, destinationLicenseFilePath) if err != nil { return "", fmt.Errorf("copying license text file: %w", err) } @@ -205,7 +207,7 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { } logger.Debug("Include linked files") - linksFS, err := files.CreateLinksFSFromPath(options.PackageRoot) + linksFS, err := files.CreateLinksFSFromPath(options.RepoRoot, options.PackageRoot) if err != nil { return "", fmt.Errorf("creating links filesystem failed: %w", err) } @@ -290,19 +292,23 @@ func signZippedPackage(options BuildOptions, zippedPackagePath string) error { return nil } -func copyLicenseTextFile(licensePath string) error { - _, err := os.Stat(licensePath) - if err == nil { +func copyLicenseTextFile(repoRoot *os.Root, licensePath string) error { + // if the given path exist, skip copying + if info, err := os.Stat(licensePath); err == nil && !info.IsDir() { logger.Debug("License file in the package will be used") return nil } + // lookup for the license file in the repository + // default license name can be overridden by the user repositoryLicenseTextFileName, userDefined := os.LookupEnv(repositoryLicenseEnv) if !userDefined { repositoryLicenseTextFileName = licenseTextFileName } - sourceLicensePath, err := findRepositoryLicense(repositoryLicenseTextFileName) + path := filepath.Join(repoRoot.Name(), repositoryLicenseTextFileName) + + sourceLicensePath, err := findRepositoryLicense(path) if !userDefined && errors.Is(err, os.ErrNotExist) { logger.Debug("No license text file is included in package") return nil @@ -341,17 +347,22 @@ func createBuildDirectory(dirs ...string) (string, error) { return buildDir, nil } -func findRepositoryLicense(licenseTextFileName string) (string, error) { - dir, err := files.FindRepositoryRootDirectory() - if err != nil { - return "", err - } - - sourceFileName := filepath.Join(dir, licenseTextFileName) - _, err = os.Stat(sourceFileName) - if err != nil { +// findRepositoryLicense checks if a license file exists at the specified path. +// If the file exists, it returns the path; otherwise, it returns an error indicating +// that the repository license could not be found. +// +// Parameters: +// +// licensePath - the path to the license file. +// +// Returns: +// +// string - the license file path if found. +// error - an error if the license file does not exist. +func findRepositoryLicense(licensePath string) (string, error) { + if _, err := os.Stat(licensePath); err != nil { return "", fmt.Errorf("failed to find repository license: %w", err) } - return sourceFileName, nil + return licensePath, nil } diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go new file mode 100644 index 0000000000..29dda959ab --- /dev/null +++ b/internal/builder/packages_test.go @@ -0,0 +1,117 @@ +package builder + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFindRepositoryLicense_FileExists(t *testing.T) { + dir := t.TempDir() + licensePath := filepath.Join(dir, "LICENSE.txt") + err := os.WriteFile(licensePath, []byte("license content"), 0644) + require.NoError(t, err) + + path, err := findRepositoryLicense(licensePath) + require.NoError(t, err) + assert.Equal(t, licensePath, path) +} + +func TestFindRepositoryLicense_FileDoesNotExist(t *testing.T) { + dir := t.TempDir() + licensePath := filepath.Join(dir, "NON_EXISTENT_LICENSE.txt") + + path, err := findRepositoryLicense(licensePath) + require.Error(t, err) + assert.Empty(t, path) +} +func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { + dir := t.TempDir() + licensePath := filepath.Join(dir, "LICENSE.txt") + err := os.WriteFile(licensePath, []byte("existing license"), 0644) + require.NoError(t, err) + + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + + // Should not attempt to copy, just return nil + err = copyLicenseTextFile(repoRoot, licensePath) + assert.NoError(t, err) + + // License file should remain unchanged + content, err := os.ReadFile(licensePath) + require.NoError(t, err) + assert.Equal(t, "existing license", string(content)) +} + +func TestCopyLicenseTextFile_CopiesFromRepo(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + + licensePath := filepath.Join(repoRoot.Name(), "LICENSE.txt") + err = os.WriteFile(licensePath, []byte("repo license"), 0644) + require.NoError(t, err) + + destDir := t.TempDir() + destLicensePath := filepath.Join(destDir, "LICENSE.txt") + + err = copyLicenseTextFile(repoRoot, destLicensePath) + assert.NoError(t, err) + + content, err := os.ReadFile(destLicensePath) + require.NoError(t, err) + assert.Equal(t, "repo license", string(content)) +} + +func TestCopyLicenseTextFile_NoRepoLicense_ReturnsNil(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + + destDir := t.TempDir() + destLicensePath := filepath.Join(destDir, "LICENSE.txt") + + err = copyLicenseTextFile(repoRoot, destLicensePath) + assert.NoError(t, err) + + _, err = os.Stat(destLicensePath) + assert.True(t, os.IsNotExist(err)) +} + +func TestCopyLicenseTextFile_EnvOverridesLicenseName(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + + customLicenseName := "CUSTOM_LICENSE.txt" + customLicensePath := filepath.Join(repoRoot.Name(), customLicenseName) + err = os.WriteFile(customLicensePath, []byte("custom license"), 0644) + require.NoError(t, err) + + destDir := t.TempDir() + destLicensePath := filepath.Join(destDir, "LICENSE.txt") + + t.Setenv(repositoryLicenseEnv, customLicenseName) + err = copyLicenseTextFile(repoRoot, destLicensePath) + assert.NoError(t, err) + + content, err := os.ReadFile(destLicensePath) + require.NoError(t, err) + assert.Equal(t, "custom license", string(content)) +} + +func TestCopyLicenseTextFile_ErrorCopyingFile(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + + licensePath := filepath.Join(repoRoot.Name(), "LICENSE.txt") + err = os.WriteFile(licensePath, []byte("repo license"), 0644) + require.NoError(t, err) + + // Use a destination path that is a directory, so sh.Copy should fail + destDir := t.TempDir() + + err = copyLicenseTextFile(repoRoot, destDir) + assert.Error(t, err) +} diff --git a/internal/elasticsearch/ingest/datastream.go b/internal/elasticsearch/ingest/datastream.go index 17e2b9289b..73d99db7ce 100644 --- a/internal/elasticsearch/ingest/datastream.go +++ b/internal/elasticsearch/ingest/datastream.go @@ -82,7 +82,12 @@ func loadIngestPipelineFiles(dataStreamPath string, nonce int64) ([]Pipeline, er pipelineFiles = append(pipelineFiles, files...) } - linksFS, err := files.CreateLinksFSFromPath(elasticsearchPath) + root, err := files.FindRepositoryRoot() + if err != nil { + return nil, fmt.Errorf("finding repository root failed: %w", err) + } + + linksFS, err := files.CreateLinksFSFromPath(root, elasticsearchPath) if err != nil { return nil, fmt.Errorf("creating links filesystem failed: %w", err) } diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index af41241929..97e4c0a4f4 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -29,23 +29,24 @@ type PackageLinks struct { } // CreateLinksFSFromPath creates a LinksFS for the given directory within the repository. -func CreateLinksFSFromPath(workDir string) (*LinksFS, error) { - repoRoot, err := FindRepositoryRootDirectory() - if err != nil { - return nil, fmt.Errorf("finding repository root: %w", err) +func CreateLinksFSFromPath(repoRoot *os.Root, workDir string) (*LinksFS, error) { + if workDir == "" { + return nil, fmt.Errorf("working directory is empty") } - root, err := os.OpenRoot(repoRoot) - if err != nil { - return nil, fmt.Errorf("opening repository root: %w", err) + // If workDir is not absolute, make it relative to the root + // This allows using both absolute and relative paths + // inside the LinksFS + if !filepath.IsAbs(workDir) { + workDir = filepath.Join(repoRoot.Name(), workDir) } - absWorkDir, err := filepath.Abs(workDir) - if err != nil { - return nil, fmt.Errorf("obtaining absolute path of working directory: %w", err) - } + // root, err := os.OpenRoot(rootPath) + // if err != nil { + // return nil, fmt.Errorf("opening repository root: %w", err) + // } - return NewLinksFS(root, absWorkDir) + return NewLinksFS(repoRoot, workDir) } var _ fs.FS = (*LinksFS)(nil) @@ -189,12 +190,14 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { workDir := filepath.Dir(linkFilePath) - // validate that workDir is within the package root - linkPackageRoot, ok, err := packages.FindPackageRootFrom(workDir) - if err != nil || !ok { - return nil, fmt.Errorf("could not find package root: %w", err) + var linkPackageName string + linkPackageRoot, _, _ := packages.FindPackageRootFrom(workDir) + if linkPackageRoot != "" { + linkPackageName = filepath.Base(linkPackageRoot) + } else { + // if the link file is not in a package, we consider the workdir as the package root + linkPackageRoot = workDir } - linkPackageName := filepath.Base(linkPackageRoot) linkFileRelativePath, err := filepath.Rel(linkPackageRoot, linkFilePath) if err != nil { @@ -225,9 +228,9 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { // get relative path of the included file from the root (packages root or repository root) includedFilePathRelFromRoot, err := filepath.Rel(repoRoot.Name(), includedFilePath) - if err != nil { + if err != nil { return nil, fmt.Errorf("could not get relative path: %w", err) - } + } // check the file exists if _, err := repoRoot.Stat(includedFilePathRelFromRoot); err != nil { return nil, err @@ -241,11 +244,11 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { checksumUpdated := cs == linkfileChecksum - includedPackageRoot, ok, err := packages.FindPackageRootFrom(filepath.Dir(includedFilePath)) - if err != nil || !ok { - return nil, fmt.Errorf("could not find included package root: %w", err) + var includedPackageName string + includedPackageRoot, _, _ := packages.FindPackageRootFrom(filepath.Dir(includedFilePath)) + if includedPackageRoot != "" { + includedPackageName = filepath.Base(includedPackageRoot) } - includedPackageName := filepath.Base(includedPackageRoot) return &Link{ WorkDir: workDir, diff --git a/internal/packages/archetype/data_stream_test.go b/internal/packages/archetype/data_stream_test.go index 30b124f5ed..f016c20ab4 100644 --- a/internal/packages/archetype/data_stream_test.go +++ b/internal/packages/archetype/data_stream_test.go @@ -5,6 +5,7 @@ package archetype import ( + "os" "path/filepath" "testing" @@ -58,15 +59,25 @@ func createDataStreamDescriptorForTest() DataStreamDescriptor { } func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamDescriptor, valid bool) { - tempDir := makeInRepoBuildTempDir(t) - err := createPackageInDir(pd, tempDir) + repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - packageRoot := filepath.Join(tempDir, pd.Manifest.Name) + linksFilePath := "" + + packagesDir := filepath.Join(repoRoot.Name(), "packages") + err = os.MkdirAll(packagesDir, 0o755) + require.NoError(t, err) + err = os.Chdir(packagesDir) + require.NoError(t, err) + + err = createPackageInDir(pd, packagesDir) + require.NoError(t, err) + + packageRoot := filepath.Join(packagesDir, pd.Manifest.Name) dd.PackageRoot = packageRoot err = CreateDataStream(dd) require.NoError(t, err) - checkPackage(t, packageRoot, valid) + checkPackage(t, repoRoot, linksFilePath, packageRoot, valid) } diff --git a/internal/packages/archetype/package_test.go b/internal/packages/archetype/package_test.go index c3297757c6..84fd2861e2 100644 --- a/internal/packages/archetype/package_test.go +++ b/internal/packages/archetype/package_test.go @@ -38,11 +38,15 @@ func TestPackage(t *testing.T) { } func createAndCheckPackage(t *testing.T, pd PackageDescriptor, valid bool) { - tempDir := makeInRepoBuildTempDir(t) - err := createPackageInDir(pd, tempDir) + repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - checkPackage(t, filepath.Join(tempDir, pd.Manifest.Name), valid) + linksFilePath := "" + packagesDir := filepath.Join(repoRoot.Name(), "packages") + err = createPackageInDir(pd, packagesDir) + require.NoError(t, err) + + checkPackage(t, repoRoot, linksFilePath, filepath.Join(packagesDir, pd.Manifest.Name), valid) } func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDescriptor { @@ -92,9 +96,9 @@ func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDe } } -func buildPackage(t *testing.T, packageRoot string) error { - buildDir := makeInRepoBuildTempDir(t) - _, err := docs.UpdateReadmes(packageRoot, buildDir) +func buildPackage(t *testing.T, repoRoot *os.Root, linksFilePath, packageRoot string) error { + buildDir := t.TempDir() + _, err := docs.UpdateReadmes(linksFilePath, packageRoot, buildDir) if err != nil { return err } @@ -102,12 +106,13 @@ func buildPackage(t *testing.T, packageRoot string) error { _, err = builder.BuildPackage(t.Context(), builder.BuildOptions{ PackageRoot: packageRoot, BuildDir: buildDir, + RepoRoot: repoRoot, }) return err } -func checkPackage(t *testing.T, packageRoot string, valid bool) { - err := buildPackage(t, packageRoot) +func checkPackage(t *testing.T, repoRoot *os.Root, linksFilePath, packageRoot string, valid bool) { + err := buildPackage(t, repoRoot, linksFilePath, packageRoot) if !valid { assert.Error(t, err) return @@ -140,22 +145,3 @@ func checkPackage(t *testing.T, packageRoot string, valid bool) { }) } } - -// makeInRepoBuildTempDir mimicks t.TempDir(), but creates the directory inside the current -// directory. -// FIXME: It should be possible to use t.TempDir(), but conflicts with links resolution, as -// t.TempDir() creates the directory out of the repository. We should refactor links resolution -// so it can write files out of the repository. -// https://github.com/elastic/elastic-package/issues/2797 -func makeInRepoBuildTempDir(t *testing.T) string { - t.Helper() - cwd, err := os.Getwd() - require.NoError(t, err) - dir, err := os.MkdirTemp(cwd, "_build-test-*") - require.NoError(t, err) - t.Cleanup(func() { - err := os.RemoveAll(dir) - assert.NoError(t, err) - }) - return dir -} diff --git a/internal/packages/installer/factory.go b/internal/packages/installer/factory.go index 5229bcacb7..2a9daa6580 100644 --- a/internal/packages/installer/factory.go +++ b/internal/packages/installer/factory.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "os" "github.com/Masterminds/semver/v3" @@ -37,6 +38,7 @@ type Options struct { RootPath string ZipPath string SkipValidation bool + RepoRoot *os.Root } // NewForPackage creates a new installer for a package, given its root path, or its prebuilt zip. @@ -85,6 +87,7 @@ func NewForPackage(ctx context.Context, options Options) (Installer, error) { CreateZip: supportsUploadZip, SignPackage: false, SkipValidation: options.SkipValidation, + RepoRoot: options.RepoRoot, }) if err != nil { return nil, fmt.Errorf("failed to build package: %v", err) diff --git a/internal/resources/fleetpackage.go b/internal/resources/fleetpackage.go index de7b1eb9d8..bb1095d131 100644 --- a/internal/resources/fleetpackage.go +++ b/internal/resources/fleetpackage.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "os" "github.com/Masterminds/semver/v3" "github.com/elastic/go-resource" @@ -24,6 +25,9 @@ type FleetPackage struct { // RootPath is the root of the package source to install. RootPath string + // RepoPath is the root of the repository. + RepoRoot *os.Root + // Absent is set to true to indicate that the package should not be installed. Absent bool @@ -59,6 +63,7 @@ func (f *FleetPackage) installer(ctx resource.Context) (installer.Installer, err Kibana: provider.Client, RootPath: f.RootPath, SkipValidation: true, + RepoRoot: f.RepoRoot, }) } diff --git a/internal/resources/fleetpackage_test.go b/internal/resources/fleetpackage_test.go index f755807130..4f8082f50b 100644 --- a/internal/resources/fleetpackage_test.go +++ b/internal/resources/fleetpackage_test.go @@ -12,7 +12,9 @@ import ( "github.com/elastic/go-resource" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/kibana" kibanatest "github.com/elastic/elastic-package/internal/kibana/test" ) @@ -46,12 +48,18 @@ func TestPackageLifecycle(t *testing.T) { t.FailNow() } + repoRoot, err := files.FindRepositoryRoot() + require.NoError(t, err) + + packageRootPath := filepath.Join(repoRoot.Name(), "test", "packages", "parallel", c.name) + fleetPackage := FleetPackage{ - RootPath: filepath.Join("..", "..", "test", "packages", "parallel", c.name), + RootPath: packageRootPath, + RepoRoot: repoRoot, } manager := resource.NewManager() manager.RegisterProvider(DefaultKibanaProviderName, &KibanaProvider{Client: kibanaClient}) - _, err := manager.Apply(resource.Resources{&fleetPackage}) + _, err = manager.Apply(resource.Resources{&fleetPackage}) assert.NoError(t, err) assertPackageInstalled(t, kibanaClient, "installed", c.name) diff --git a/internal/resources/fleetpolicy.go b/internal/resources/fleetpolicy.go index 37b4e9197f..092acd1cd7 100644 --- a/internal/resources/fleetpolicy.go +++ b/internal/resources/fleetpolicy.go @@ -7,6 +7,7 @@ package resources import ( "errors" "fmt" + "os" "slices" "strings" @@ -41,6 +42,9 @@ type FleetAgentPolicy struct { // PackagePolicies PackagePolicies []FleetPackagePolicy + + // RepoPath is the root of the repository. + RepoRoot *os.Root } type FleetPackagePolicy struct { diff --git a/internal/resources/fleetpolicy_test.go b/internal/resources/fleetpolicy_test.go index 7c9536f3d0..8c4643f551 100644 --- a/internal/resources/fleetpolicy_test.go +++ b/internal/resources/fleetpolicy_test.go @@ -12,7 +12,9 @@ import ( "github.com/elastic/go-resource" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/kibana" kibanatest "github.com/elastic/elastic-package/internal/kibana/test" ) @@ -30,6 +32,9 @@ func TestRequiredProviderFleetPolicy(t *testing.T) { } func TestPolicyLifecycle(t *testing.T) { + repoRoot, err := files.FindRepositoryRoot() + require.NoError(t, err) + cases := []struct { title string packagePolicies []FleetPackagePolicy @@ -42,7 +47,7 @@ func TestPolicyLifecycle(t *testing.T) { packagePolicies: []FleetPackagePolicy{ { Name: "nginx-1", - RootPath: "../../test/packages/parallel/nginx", + RootPath: filepath.Join(repoRoot.Name(), "test", "packages", "parallel", "nginx"), DataStreamName: "stubstatus", }, }, @@ -52,12 +57,12 @@ func TestPolicyLifecycle(t *testing.T) { packagePolicies: []FleetPackagePolicy{ { Name: "nginx-1", - RootPath: "../../test/packages/parallel/nginx", + RootPath: filepath.Join(repoRoot.Name(), "test", "packages", "parallel", "nginx"), DataStreamName: "stubstatus", }, { Name: "system-1", - RootPath: "../../test/packages/parallel/system", + RootPath: filepath.Join(repoRoot.Name(), "test", "packages", "parallel", "system"), DataStreamName: "process", }, }, @@ -67,7 +72,7 @@ func TestPolicyLifecycle(t *testing.T) { packagePolicies: []FleetPackagePolicy{ { Name: "input-1", - RootPath: "../../test/packages/parallel/sql_input", + RootPath: filepath.Join(repoRoot.Name(), "test", "packages", "parallel", "sql_input"), }, }, }, @@ -88,6 +93,7 @@ func TestPolicyLifecycle(t *testing.T) { Description: fmt.Sprintf("Test policy for %s", c.title), Namespace: "eptest", PackagePolicies: c.packagePolicies, + RepoRoot: repoRoot, } t.Cleanup(func() { deletePolicy(t, manager, agentPolicy) }) @@ -111,6 +117,7 @@ func withPackageResources(agentPolicy *FleetAgentPolicy) resource.Resources { resources = append(resources, &FleetPackage{ RootPath: policy.RootPath, Absent: agentPolicy.Absent, + RepoRoot: agentPolicy.RepoRoot, }) } return append(resources, agentPolicy) From 0b6191003cdac21f08ba4bfb5e6f1a70ab14a2e0 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 25 Sep 2025 15:57:15 +0200 Subject: [PATCH 06/46] standardize repository root handling across build, lint, and links map files --- cmd/build.go | 2 +- cmd/lint.go | 2 +- internal/docs/links_map.go | 4 ++-- internal/docs/links_map_test.go | 29 +++++++++++++++++++---------- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index cde40e6d8d..1911c611fa 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -61,7 +61,7 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } } - repoRoot, err := files.FindRepositoryRootDirectory() + repoRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } diff --git a/cmd/lint.go b/cmd/lint.go index 11453c4854..dee19049da 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -47,7 +47,7 @@ func setupLintCommand() *cobraext.Command { func lintCommandAction(cmd *cobra.Command, args []string) error { cmd.Println("Lint the package") - repoRoot, err := files.FindRepositoryRootDirectory() + repoRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } diff --git a/internal/docs/links_map.go b/internal/docs/links_map.go index 94703dc5bf..7334f49397 100644 --- a/internal/docs/links_map.go +++ b/internal/docs/links_map.go @@ -89,7 +89,7 @@ func (l linkMap) RenderLink(key string, options linkOptions) (string, error) { // If the environment variable is not set, it falls back to the default file path // constructed from repoRoot and linksMapFileNameDefault, returning the path if the file exists, // or nil if it does not. -func LinksDefinitionsFilePath(repoRoot string) (string, error) { +func LinksDefinitionsFilePath(repoRoot *os.Root) (string, error) { linksFilePath := os.Getenv(linksMapFilePathEnvVar) if linksFilePath != "" { if _, err := os.Stat(linksFilePath); err != nil { @@ -99,7 +99,7 @@ func LinksDefinitionsFilePath(repoRoot string) (string, error) { return linksFilePath, nil } - linksFilePath = filepath.Join(repoRoot, linksMapFileNameDefault) + linksFilePath = filepath.Join(repoRoot.Name(), linksMapFileNameDefault) if _, err := os.Stat(linksFilePath); err != nil { logger.Debugf("links definitions default file doesn't exist: %s", linksFilePath) return "", nil diff --git a/internal/docs/links_map_test.go b/internal/docs/links_map_test.go index a86574fb01..9d04fb0735 100644 --- a/internal/docs/links_map_test.go +++ b/internal/docs/links_map_test.go @@ -91,9 +91,12 @@ func TestRenderLink(t *testing.T) { func TestLinksDefinitionsFilePath(t *testing.T) { t.Run("env var set and file exists", func(t *testing.T) { - repoRoot := t.TempDir() - defaultFilePath := filepath.Join(repoRoot, linksMapFileNameDefault) - testFile := filepath.Join(repoRoot, "custom_links.yml") + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() + + defaultFilePath := filepath.Join(repoRoot.Name(), linksMapFileNameDefault) + testFile := filepath.Join(repoRoot.Name(), "custom_links.yml") require.NoError(t, createLinksFile(testFile)) require.NoError(t, createLinksFile(defaultFilePath)) // to ensure default file is ignored t.Setenv(linksMapFilePathEnvVar, testFile) @@ -104,9 +107,11 @@ func TestLinksDefinitionsFilePath(t *testing.T) { }) t.Run("env var set but file does not exist", func(t *testing.T) { - repoRoot := t.TempDir() + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() - missingFile := filepath.Join(repoRoot, "missing_links.yml") + missingFile := filepath.Join(repoRoot.Name(), "missing_links.yml") t.Setenv(linksMapFilePathEnvVar, missingFile) path, err := LinksDefinitionsFilePath(repoRoot) @@ -115,8 +120,10 @@ func TestLinksDefinitionsFilePath(t *testing.T) { }) t.Run("env var not set, default file exists", func(t *testing.T) { - repoRoot := t.TempDir() - defaultFilePath := filepath.Join(repoRoot, linksMapFileNameDefault) + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() + defaultFilePath := filepath.Join(repoRoot.Name(), linksMapFileNameDefault) require.NoError(t, createLinksFile(defaultFilePath)) @@ -128,10 +135,12 @@ func TestLinksDefinitionsFilePath(t *testing.T) { }) t.Run("env var not set, default file does not exist", func(t *testing.T) { - repoRoot := t.TempDir() - defaultFilePath := filepath.Join(repoRoot, linksMapFileNameDefault) + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() + defaultFilePath := filepath.Join(repoRoot.Name(), linksMapFileNameDefault) - _, err := os.Stat(defaultFilePath) + _, err = os.Stat(defaultFilePath) require.True(t, os.IsNotExist(err)) path, err := LinksDefinitionsFilePath(repoRoot) From ce07ec3b318655c46cddf087171a9b70acebc318 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 25 Sep 2025 16:06:47 +0200 Subject: [PATCH 07/46] ensure repository root is properly closed in multiple command actions and tests --- cmd/build.go | 1 + cmd/links.go | 3 +++ cmd/lint.go | 1 + internal/builder/packages_test.go | 5 +++++ internal/elasticsearch/ingest/datastream.go | 1 + internal/files/linkedfiles.go | 5 ----- internal/files/linkedfiles_test.go | 1 + internal/packages/archetype/data_stream_test.go | 1 + internal/packages/archetype/package_test.go | 1 + internal/resources/fleetpackage_test.go | 1 + internal/resources/fleetpolicy_test.go | 1 + 11 files changed, 16 insertions(+), 5 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 1911c611fa..a52344c36a 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -65,6 +65,7 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } + defer repoRoot.Close() linksFilePath, err := docs.LinksDefinitionsFilePath(repoRoot) if err != nil { diff --git a/cmd/links.go b/cmd/links.go index 7e4c37d244..cf431701d5 100644 --- a/cmd/links.go +++ b/cmd/links.go @@ -61,6 +61,7 @@ func linksCheckCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("finding repository root: %w", err) } + defer repoRoot.Close() linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) if err != nil { @@ -105,6 +106,7 @@ func linksUpdateCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("finding repository root: %w", err) } + defer repoRoot.Close() linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) if err != nil { @@ -151,6 +153,7 @@ func linksListCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("finding repository root: %w", err) } + defer repoRoot.Close() linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) if err != nil { diff --git a/cmd/lint.go b/cmd/lint.go index dee19049da..caaf464de4 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -51,6 +51,7 @@ func lintCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } + defer repoRoot.Close() linksFilePath, err := docs.LinksDefinitionsFilePath(repoRoot) if err != nil { diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index 29dda959ab..cad3039a2d 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -36,6 +36,7 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) + defer repoRoot.Close() // Should not attempt to copy, just return nil err = copyLicenseTextFile(repoRoot, licensePath) @@ -50,6 +51,7 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { func TestCopyLicenseTextFile_CopiesFromRepo(t *testing.T) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) + defer repoRoot.Close() licensePath := filepath.Join(repoRoot.Name(), "LICENSE.txt") err = os.WriteFile(licensePath, []byte("repo license"), 0644) @@ -69,6 +71,7 @@ func TestCopyLicenseTextFile_CopiesFromRepo(t *testing.T) { func TestCopyLicenseTextFile_NoRepoLicense_ReturnsNil(t *testing.T) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) + defer repoRoot.Close() destDir := t.TempDir() destLicensePath := filepath.Join(destDir, "LICENSE.txt") @@ -83,6 +86,7 @@ func TestCopyLicenseTextFile_NoRepoLicense_ReturnsNil(t *testing.T) { func TestCopyLicenseTextFile_EnvOverridesLicenseName(t *testing.T) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) + defer repoRoot.Close() customLicenseName := "CUSTOM_LICENSE.txt" customLicensePath := filepath.Join(repoRoot.Name(), customLicenseName) @@ -104,6 +108,7 @@ func TestCopyLicenseTextFile_EnvOverridesLicenseName(t *testing.T) { func TestCopyLicenseTextFile_ErrorCopyingFile(t *testing.T) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) + defer repoRoot.Close() licensePath := filepath.Join(repoRoot.Name(), "LICENSE.txt") err = os.WriteFile(licensePath, []byte("repo license"), 0644) diff --git a/internal/elasticsearch/ingest/datastream.go b/internal/elasticsearch/ingest/datastream.go index 73d99db7ce..9370a607eb 100644 --- a/internal/elasticsearch/ingest/datastream.go +++ b/internal/elasticsearch/ingest/datastream.go @@ -86,6 +86,7 @@ func loadIngestPipelineFiles(dataStreamPath string, nonce int64) ([]Pipeline, er if err != nil { return nil, fmt.Errorf("finding repository root failed: %w", err) } + defer root.Close() linksFS, err := files.CreateLinksFSFromPath(root, elasticsearchPath) if err != nil { diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index 97e4c0a4f4..5eed0ca7e9 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -41,11 +41,6 @@ func CreateLinksFSFromPath(repoRoot *os.Root, workDir string) (*LinksFS, error) workDir = filepath.Join(repoRoot.Name(), workDir) } - // root, err := os.OpenRoot(rootPath) - // if err != nil { - // return nil, fmt.Errorf("opening repository root: %w", err) - // } - return NewLinksFS(repoRoot, workDir) } diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index 703d72d8b4..d9b394e0c4 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -522,6 +522,7 @@ func TestLinksFSSecurityIsolation(t *testing.T) { // Create LinksFS root, err := os.OpenRoot(repoDir) require.NoError(t, err) + defer root.Close() // Get the relative path from repo root to work directory relWorkDir, err := filepath.Rel(repoDir, workDir) diff --git a/internal/packages/archetype/data_stream_test.go b/internal/packages/archetype/data_stream_test.go index f016c20ab4..b536b19cee 100644 --- a/internal/packages/archetype/data_stream_test.go +++ b/internal/packages/archetype/data_stream_test.go @@ -61,6 +61,7 @@ func createDataStreamDescriptorForTest() DataStreamDescriptor { func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamDescriptor, valid bool) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) + defer repoRoot.Close() linksFilePath := "" diff --git a/internal/packages/archetype/package_test.go b/internal/packages/archetype/package_test.go index 84fd2861e2..71f3bbf7f1 100644 --- a/internal/packages/archetype/package_test.go +++ b/internal/packages/archetype/package_test.go @@ -40,6 +40,7 @@ func TestPackage(t *testing.T) { func createAndCheckPackage(t *testing.T, pd PackageDescriptor, valid bool) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) + defer repoRoot.Close() linksFilePath := "" packagesDir := filepath.Join(repoRoot.Name(), "packages") diff --git a/internal/resources/fleetpackage_test.go b/internal/resources/fleetpackage_test.go index 4f8082f50b..43e6665316 100644 --- a/internal/resources/fleetpackage_test.go +++ b/internal/resources/fleetpackage_test.go @@ -50,6 +50,7 @@ func TestPackageLifecycle(t *testing.T) { repoRoot, err := files.FindRepositoryRoot() require.NoError(t, err) + defer repoRoot.Close() packageRootPath := filepath.Join(repoRoot.Name(), "test", "packages", "parallel", c.name) diff --git a/internal/resources/fleetpolicy_test.go b/internal/resources/fleetpolicy_test.go index 8c4643f551..2df40f65fd 100644 --- a/internal/resources/fleetpolicy_test.go +++ b/internal/resources/fleetpolicy_test.go @@ -34,6 +34,7 @@ func TestRequiredProviderFleetPolicy(t *testing.T) { func TestPolicyLifecycle(t *testing.T) { repoRoot, err := files.FindRepositoryRoot() require.NoError(t, err) + defer repoRoot.Close() cases := []struct { title string From 0deae99df48bee28ae9aee56ab12a62b30cb91d1 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 09:41:54 +0200 Subject: [PATCH 08/46] add repoRoot parameter to build command action --- cmd/build.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/build.go b/cmd/build.go index a52344c36a..06117c2196 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -99,6 +99,7 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { CreateZip: createZip, SignPackage: signPackage, SkipValidation: skipValidation, + RepoRoot: repoRoot, }) if err != nil { return fmt.Errorf("building package failed: %w", err) From 1fc643a690b43acf1a73649792e07cb184aaada4 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 11:06:51 +0200 Subject: [PATCH 09/46] update linked file handling to use absolute paths and improve clarity --- cmd/links.go | 3 +-- internal/builder/packages.go | 2 +- internal/files/linkedfiles.go | 31 +++++++++++---------------- internal/files/linkedfiles_test.go | 34 +++++++++++++++--------------- 4 files changed, 31 insertions(+), 39 deletions(-) diff --git a/cmd/links.go b/cmd/links.go index cf431701d5..ad2c9f7762 100644 --- a/cmd/links.go +++ b/cmd/links.go @@ -7,7 +7,6 @@ package cmd import ( "fmt" "os" - "path/filepath" "github.com/spf13/cobra" @@ -74,7 +73,7 @@ func linksCheckCommandAction(cmd *cobra.Command, args []string) error { } for _, f := range linkedFiles { if !f.UpToDate { - cmd.Printf("%s is outdated.\n", filepath.Join(f.WorkDir, f.LinkFilePath)) + cmd.Printf("%s is outdated.\n", f.LinkFilePath) } } if len(linkedFiles) > 0 { diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 4078be2637..ef6c93f231 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -217,7 +217,7 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { return "", fmt.Errorf("including linked files failed: %w", err) } for _, l := range links { - logger.Debugf("Linked file included (path: %s)", l.TargetFilePath(destinationDir)) + logger.Debugf("Linked file included (path: %s)", l.TargetRelPath) } if options.CreateZip { diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index 5eed0ca7e9..f7fb01e0c4 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -168,10 +168,12 @@ func (lfs *LinksFS) ListLinkedFilesByPackage() ([]PackageLinks, error) { type Link struct { WorkDir string // WorkDir is the path to the directory containing the link file. This is where the copy of the included file will be placed. - LinkFilePath string // LinkFilePath is the relative path of the linked file and the package root + LinkFilePath string // LinkFilePath is the absolute path of the linked file LinkChecksum string LinkPackageName string // Package where the link file is located + TargetRelPath string // TargetRelPath is the relative path to the target file, this will be the path where the content of the file is copied to + IncludedFilePath string // IncludedFilePath is the path to the included file, this is the content of the link file IncludedFileContentsChecksum string // IncludedFileContentsChecksum is the checksum of the included file contents, this is the second field in the link file IncludedPackageName string // IncludedPackageName is the package where the included file is located @@ -247,13 +249,14 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { return &Link{ WorkDir: workDir, - LinkFilePath: linkFileRelativePath, + LinkFilePath: linkFilePath, LinkChecksum: linkfileChecksum, LinkPackageName: linkPackageName, IncludedFilePath: includedFileRelPath, IncludedFileContentsChecksum: cs, IncludedPackageName: includedPackageName, UpToDate: checksumUpdated, + TargetRelPath: strings.TrimSuffix(linkFileRelativePath, linkExtension), }, nil } @@ -270,7 +273,7 @@ func (l *Link) updateChecksum() (bool, error) { return false, fmt.Errorf("checksum is empty for included file %s", l.IncludedFilePath) } newContent := fmt.Sprintf("%v %v", filepath.ToSlash(l.IncludedFilePath), l.IncludedFileContentsChecksum) - if err := writeFile(filepath.Join(l.WorkDir, l.LinkFilePath), []byte(newContent)); err != nil { + if err := writeFile(l.LinkFilePath, []byte(newContent)); err != nil { return false, fmt.Errorf("could not update checksum for link file %s: %w", l.LinkFilePath, err) } l.LinkChecksum = l.IncludedFileContentsChecksum @@ -278,22 +281,9 @@ func (l *Link) updateChecksum() (bool, error) { return true, nil } -// TargetFilePath returns the path where the linked file should be written. -// If workDir is provided, it uses that as the base directory, otherwise uses the link's WorkDir. -func (l *Link) TargetFilePath(workDir ...string) string { - targetFilePath := filepath.FromSlash(strings.TrimSuffix(l.LinkFilePath, linkExtension)) - wd := l.WorkDir - if len(workDir) > 0 { - wd = workDir[0] - } - return filepath.Join(wd, targetFilePath) -} - -// includeLinkedFiles function includes linked files from the source -// directory to the target directory. +// includeLinkedFiles function includes linked files from the source directory to the target directory. // It returns a slice of Link structs representing the included files. // It also updates the checksum of the linked files. -// Both directories must be relative to the root. func includeLinkedFiles(root *os.Root, fromDir, toDir string) ([]Link, error) { links, err := listLinkedFiles(root, fromDir) if err != nil { @@ -303,7 +293,10 @@ func includeLinkedFiles(root *os.Root, fromDir, toDir string) ([]Link, error) { if _, err := l.updateChecksum(); err != nil { return nil, fmt.Errorf("could not update checksum for file %s: %w", l.LinkFilePath, err) } - targetFilePath := l.TargetFilePath(toDir) + // targetFilePath is the path where the content of the file is copied to + targetFilePath := filepath.Join(toDir, l.TargetRelPath) + + // from l.IncludedFilePath, we just need the path name without .link suffix and to be relative to the toDir instead of the package root if err := copyFromRoot( root, filepath.Join(l.WorkDir, filepath.FromSlash(l.IncludedFilePath)), @@ -479,7 +472,7 @@ func linkedFilesByPackageFrom(root *os.Root, fromDir string) ([]PackageLinks, er packageName != l.IncludedPackageName { continue } - byPackageMap[l.LinkPackageName] = append(byPackageMap[l.LinkPackageName], filepath.Join(l.WorkDir, l.LinkFilePath)) + byPackageMap[l.LinkPackageName] = append(byPackageMap[l.LinkPackageName], l.LinkFilePath) } var packages []string diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index d9b394e0c4..1eea42dd1b 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -97,17 +97,17 @@ func TestListLinkedFiles(t *testing.T) { require.Len(t, linkedFiles, 2) // Expect exactly 2 link files in testdata // Verify first file (outdated.yml.link) - should be outdated (no checksum) - assert.Equal(t, "outdated.yml.link", linkedFiles[0].LinkFilePath) + assert.True(t, strings.HasSuffix(linkedFiles[0].LinkFilePath, "outdated.yml.link")) assert.Empty(t, linkedFiles[0].LinkChecksum) // No checksum = outdated - assert.Equal(t, "outdated.yml", linkedFiles[0].TargetFilePath("")) + assert.Equal(t, "outdated.yml", linkedFiles[0].TargetRelPath) assert.Equal(t, "./included.yml", linkedFiles[0].IncludedFilePath) assert.Equal(t, "d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", linkedFiles[0].IncludedFileContentsChecksum) assert.False(t, linkedFiles[0].UpToDate) // Verify second file (uptodate.yml.link) - should be up-to-date (has matching checksum) - assert.Equal(t, "uptodate.yml.link", linkedFiles[1].LinkFilePath) + assert.True(t, strings.HasSuffix(linkedFiles[1].LinkFilePath, "uptodate.yml.link")) assert.Equal(t, "d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", linkedFiles[1].LinkChecksum) - assert.Equal(t, "uptodate.yml", linkedFiles[1].TargetFilePath("")) + assert.Equal(t, "uptodate.yml", linkedFiles[1].TargetRelPath) assert.Equal(t, "./included.yml", linkedFiles[1].IncludedFilePath) assert.Equal(t, "d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", linkedFiles[1].IncludedFileContentsChecksum) assert.True(t, linkedFiles[1].UpToDate) @@ -171,9 +171,9 @@ func TestAreLinkedFilesUpToDate(t *testing.T) { assert.Len(t, linkedFiles, 1) // Expect exactly 1 outdated file (outdated.yml.link) // Verify the outdated file details - assert.Equal(t, "outdated.yml.link", linkedFiles[0].LinkFilePath) + assert.True(t, strings.HasSuffix(linkedFiles[0].LinkFilePath, "outdated.yml.link")) assert.Empty(t, linkedFiles[0].LinkChecksum) // No checksum indicates outdated - assert.Equal(t, "outdated.yml", linkedFiles[0].TargetFilePath("")) + assert.Equal(t, "outdated.yml", linkedFiles[0].TargetRelPath) assert.Equal(t, "./included.yml", linkedFiles[0].IncludedFilePath) assert.Equal(t, "d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", linkedFiles[0].IncludedFileContentsChecksum) assert.False(t, linkedFiles[0].UpToDate) @@ -293,12 +293,12 @@ func TestIncludeLinkedFiles(t *testing.T) { require.Equal(t, 1, len(linkedFiles)) // Expect 1 linked file to be processed // Verify the target file was created in the destination directory - assert.FileExists(t, linkedFiles[0].TargetFilePath(toDir)) + assert.FileExists(t, filepath.Join(toDir, linkedFiles[0].TargetRelPath)) // Verify the copied file has identical content to the original included file equal, err := filesEqual( filepath.Join(linkedFiles[0].WorkDir, filepath.FromSlash(linkedFiles[0].IncludedFilePath)), - linkedFiles[0].TargetFilePath(toDir), + filepath.Join(toDir, linkedFiles[0].TargetRelPath), ) assert.NoError(t, err) assert.True(t, equal, "files should be equal after copying") @@ -906,9 +906,9 @@ func Test_newLinkedFile(t *testing.T) { require.NoError(t, err) // required to identify a package root - _, err = repoRootHandle.Create("packages/A/manifest.yml") + _, err = repoRootHandle.Create(filepath.Join("packages", "A", "manifest.yml")) require.NoError(t, err) - fManifestA, err := repoRootHandle.Create("packages/A/manifest.yml") + fManifestA, err := repoRootHandle.Create(filepath.Join("packages", "A", "manifest.yml")) require.NoError(t, err) _, err = fManifestA.WriteString(`name: A version: 1.0.0 @@ -917,7 +917,7 @@ type: integration require.NoError(t, err) require.NoError(t, fManifestA.Close()) - fLink, err := repoRootHandle.Create("packages/A/folder/link.txt.link") + fLink, err := repoRootHandle.Create(filepath.Join("packages", "A", "folder", "link.txt.link")) require.NoError(t, err) _, err = fLink.WriteString(linkFileContent) require.NoError(t, err) @@ -927,9 +927,9 @@ type: integration require.NoError(t, err) // required to identify a package root - _, err = repoRootHandle.Create("packages/B/manifest.yml") + _, err = repoRootHandle.Create(filepath.Join("packages", "B", "manifest.yml")) require.NoError(t, err) - fManifestB, err := repoRootHandle.Create("packages/B/manifest.yml") + fManifestB, err := repoRootHandle.Create(filepath.Join("packages", "B", "manifest.yml")) require.NoError(t, err) _, err = fManifestB.WriteString(`name: B version: 1.0.0 @@ -938,7 +938,7 @@ type: integration require.NoError(t, err) require.NoError(t, fManifestB.Close()) - fIncluded, err := repoRootHandle.Create("packages/B/otherFolder/included.txt") + fIncluded, err := repoRootHandle.Create(filepath.Join("packages", "B", "otherFolder", "included.txt")) require.NoError(t, err) _, err = fIncluded.WriteString(includedFileContent) require.NoError(t, err) @@ -948,13 +948,13 @@ type: integration require.NoError(t, err) assert.NotNil(t, l) - assert.Equal(t, filepath.Join(repoRoot, "packages/A/folder"), l.WorkDir) + assert.Equal(t, filepath.Join(repoRoot, filepath.Join("packages", "A", "folder")), l.WorkDir) - assert.Equal(t, "folder/link.txt.link", l.LinkFilePath) + assert.True(t, strings.HasSuffix(l.LinkFilePath, filepath.Join("folder", "link.txt.link"))) assert.Equal(t, "d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", l.LinkChecksum) assert.Equal(t, "A", l.LinkPackageName) - assert.Equal(t, "../../B/otherFolder/included.txt", l.IncludedFilePath) + assert.Equal(t, filepath.Join("..", "..", "B", "otherFolder", "included.txt"), l.IncludedFilePath) assert.Equal(t, "1c14ca1eae312a58c08085436fd5dcd0c02451d3cdc8e4e4a1cc1415a6b4c6d0", l.IncludedFileContentsChecksum) assert.Equal(t, "B", l.IncludedPackageName) assert.False(t, l.UpToDate) From 6820b4941a6897733ddb2cdec72891abc73b20b0 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 11:23:17 +0200 Subject: [PATCH 10/46] add copyright notice to packages_test.go file --- internal/builder/packages_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index cad3039a2d..54b9760ac7 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -1,3 +1,7 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + package builder import ( From 2c16cab885624793061e75055224e7b9b677e201 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 11:29:44 +0200 Subject: [PATCH 11/46] refactor linkedfiles_test.go to use os.MkdirAll for directory creation --- internal/files/linkedfiles_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index 1eea42dd1b..e314581001 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -902,7 +902,7 @@ func Test_newLinkedFile(t *testing.T) { // /packages/B/otherFolder/included.txt // included relative to link file: ../../B/otherFolder/included.txt - err = repoRootHandle.MkdirAll(filepath.Join("packages", "A", "folder"), 0755) + err = os.MkdirAll(filepath.Join(repoRoot, "packages", "A", "folder"), 0755) require.NoError(t, err) // required to identify a package root @@ -923,7 +923,7 @@ type: integration require.NoError(t, err) require.NoError(t, fLink.Close()) - err = repoRootHandle.MkdirAll(filepath.Join("packages", "B", "otherFolder"), 0755) + err = os.MkdirAll(filepath.Join(repoRoot, "packages", "B", "otherFolder"), 0755) require.NoError(t, err) // required to identify a package root From 403527ec40102bf2853dfb47471fbb29e0fb847e Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 12:26:02 +0200 Subject: [PATCH 12/46] implement repository root handling in benchmark and install commands, and update options in rally and stream runners --- cmd/benchmark.go | 13 +++++++++++++ cmd/install.go | 7 +++++++ internal/benchrunner/runners/rally/options.go | 8 ++++++++ internal/benchrunner/runners/rally/runner.go | 1 + internal/benchrunner/runners/stream/options.go | 8 ++++++++ internal/benchrunner/runners/stream/runner.go | 1 + 6 files changed, 38 insertions(+) diff --git a/cmd/benchmark.go b/cmd/benchmark.go index e8e7394021..7875b91f1e 100644 --- a/cmd/benchmark.go +++ b/cmd/benchmark.go @@ -25,6 +25,7 @@ import ( "github.com/elastic/elastic-package/internal/cobraext" "github.com/elastic/elastic-package/internal/common" "github.com/elastic/elastic-package/internal/elasticsearch" + "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/install" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/packages" @@ -302,6 +303,11 @@ func rallyCommandAction(cmd *cobra.Command, args []string) error { } } + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + profile, err := cobraext.GetProfileFlag(cmd) if err != nil { return err @@ -336,6 +342,7 @@ func rallyCommandAction(cmd *cobra.Command, args []string) error { rally.WithRallyDryRun(rallyDryRun), rally.WithRallyPackageFromRegistry(packageName, packageVersion), rally.WithRallyCorpusAtPath(corpusAtPath), + rally.WithRepoRoot(repoRoot), } esMetricsClient, err := initializeESMetricsClient(ctx) @@ -473,6 +480,11 @@ func streamCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("locating package root failed: %w", err) } + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + profile, err := cobraext.GetProfileFlag(cmd) if err != nil { return err @@ -507,6 +519,7 @@ func streamCommandAction(cmd *cobra.Command, args []string) error { stream.WithESAPI(esClient.API), stream.WithKibanaClient(kc), stream.WithProfile(profile), + stream.WithRepoRoot(repoRoot), } runner := stream.NewStreamBenchmark(stream.NewOptions(withOpts...)) diff --git a/cmd/install.go b/cmd/install.go index 670b2393db..171e9b8ea1 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/cobra" "github.com/elastic/elastic-package/internal/cobraext" + "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/install" "github.com/elastic/elastic-package/internal/kibana" "github.com/elastic/elastic-package/internal/packages" @@ -82,11 +83,17 @@ func installCommandAction(cmd *cobra.Command, _ []string) error { } } + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + installer, err := installer.NewForPackage(cmd.Context(), installer.Options{ Kibana: kibanaClient, RootPath: packageRootPath, SkipValidation: skipValidation, ZipPath: zipPathFile, + RepoRoot: repoRoot, }) if err != nil { return fmt.Errorf("package installation failed: %w", err) diff --git a/internal/benchrunner/runners/rally/options.go b/internal/benchrunner/runners/rally/options.go index 0f7eae2c22..0225af7cd0 100644 --- a/internal/benchrunner/runners/rally/options.go +++ b/internal/benchrunner/runners/rally/options.go @@ -5,6 +5,7 @@ package rally import ( + "os" "time" "github.com/elastic/elastic-package/internal/elasticsearch" @@ -29,6 +30,7 @@ type Options struct { PackageName string PackageVersion string CorpusAtPath string + RepoRoot *os.Root } type ClientOptions struct { @@ -118,3 +120,9 @@ func WithRallyCorpusAtPath(c string) OptionFunc { opts.CorpusAtPath = c } } + +func WithRepoRoot(r *os.Root) OptionFunc { + return func(opts *Options) { + opts.RepoRoot = r + } +} diff --git a/internal/benchrunner/runners/rally/runner.go b/internal/benchrunner/runners/rally/runner.go index ebf6d9a649..601f50ab91 100644 --- a/internal/benchrunner/runners/rally/runner.go +++ b/internal/benchrunner/runners/rally/runner.go @@ -489,6 +489,7 @@ func (r *runner) installPackageFromPackageRoot(ctx context.Context) error { Kibana: r.options.KibanaClient, RootPath: r.options.PackageRootPath, SkipValidation: true, + RepoRoot: r.options.RepoRoot, }) if err != nil { return fmt.Errorf("failed to initialize package installer: %w", err) diff --git a/internal/benchrunner/runners/stream/options.go b/internal/benchrunner/runners/stream/options.go index e1c0889aff..2c257fc6dc 100644 --- a/internal/benchrunner/runners/stream/options.go +++ b/internal/benchrunner/runners/stream/options.go @@ -5,6 +5,7 @@ package stream import ( + "os" "time" "github.com/elastic/elastic-package/internal/elasticsearch" @@ -25,6 +26,7 @@ type Options struct { PackageRootPath string Variant string Profile *profile.Profile + RepoRoot *os.Root } type ClientOptions struct { @@ -107,3 +109,9 @@ func WithTimestampField(t string) OptionFunc { opts.TimestampField = t } } + +func WithRepoRoot(r *os.Root) OptionFunc { + return func(opts *Options) { + opts.RepoRoot = r + } +} diff --git a/internal/benchrunner/runners/stream/runner.go b/internal/benchrunner/runners/stream/runner.go index 9322d279b5..dea617480b 100644 --- a/internal/benchrunner/runners/stream/runner.go +++ b/internal/benchrunner/runners/stream/runner.go @@ -256,6 +256,7 @@ func (r *runner) installPackageFromPackageRoot(ctx context.Context) error { Kibana: r.options.KibanaClient, RootPath: r.options.PackageRootPath, SkipValidation: true, + RepoRoot: r.options.RepoRoot, }) if err != nil { From 96d5129cbb7658c7a3436e47bac7c23bf34d285e Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 14:28:50 +0200 Subject: [PATCH 13/46] read repoRoot at test runner cmd --- cmd/testrunner.go | 13 +++++++++++++ internal/testrunner/runners/asset/runner.go | 5 +++++ internal/testrunner/runners/asset/tester.go | 5 +++++ internal/testrunner/runners/policy/runner.go | 3 +++ internal/testrunner/runners/system/runner.go | 4 ++++ 5 files changed, 30 insertions(+) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index dae556c3ee..7f5374c740 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -17,6 +17,7 @@ import ( "github.com/elastic/elastic-package/internal/cobraext" "github.com/elastic/elastic-package/internal/common" + "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/install" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/packages" @@ -153,6 +154,11 @@ func testRunnerAssetCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("locating package root failed: %w", err) } + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + manifest, err := packages.ReadPackageManifestFromPackageRoot(packageRootPath) if err != nil { return fmt.Errorf("reading package manifest failed (path: %s): %w", packageRootPath, err) @@ -177,6 +183,7 @@ func testRunnerAssetCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.Asset, WithCoverage: testCoverage, CoverageType: testCoverageFormat, + RepoRoot: repoRoot, }) results, err := testrunner.RunSuite(ctx, runner) @@ -494,6 +501,11 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("locating package root failed: %w", err) } + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + runSetup, err := cmd.Flags().GetBool(cobraext.SetupFlagName) if err != nil { return cobraext.FlagParsingError(err, cobraext.SetupFlagName) @@ -578,6 +590,7 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.System, WithCoverage: testCoverage, CoverageType: testCoverageFormat, + RepoRoot: repoRoot, }) logger.Debugf("Running suite...") diff --git a/internal/testrunner/runners/asset/runner.go b/internal/testrunner/runners/asset/runner.go index 8f39f8e701..3da335d78e 100644 --- a/internal/testrunner/runners/asset/runner.go +++ b/internal/testrunner/runners/asset/runner.go @@ -6,6 +6,7 @@ package asset import ( "context" + "os" "path/filepath" "github.com/elastic/elastic-package/internal/kibana" @@ -23,6 +24,7 @@ type runner struct { globalTestConfig testrunner.GlobalRunnerTestConfig withCoverage bool coverageType string + repoRoot *os.Root } type AssetTestRunnerOptions struct { @@ -31,6 +33,7 @@ type AssetTestRunnerOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string + RepoRoot *os.Root } func NewAssetTestRunner(options AssetTestRunnerOptions) *runner { @@ -40,6 +43,7 @@ func NewAssetTestRunner(options AssetTestRunnerOptions) *runner { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, + repoRoot: options.RepoRoot, } return &runner } @@ -70,6 +74,7 @@ func (r *runner) GetTests(ctx context.Context) ([]testrunner.Tester, error) { GlobalTestConfig: r.globalTestConfig, WithCoverage: r.withCoverage, CoverageType: r.coverageType, + RepoRoot: r.repoRoot, }), } return testers, nil diff --git a/internal/testrunner/runners/asset/tester.go b/internal/testrunner/runners/asset/tester.go index d6996e3609..43c9385911 100644 --- a/internal/testrunner/runners/asset/tester.go +++ b/internal/testrunner/runners/asset/tester.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "os" "strings" "github.com/elastic/elastic-package/internal/kibana" @@ -25,6 +26,7 @@ type tester struct { globalTestConfig testrunner.GlobalRunnerTestConfig withCoverage bool coverageType string + repoRoot *os.Root } type AssetTesterOptions struct { @@ -34,6 +36,7 @@ type AssetTesterOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string + RepoRoot *os.Root } func NewAssetTester(options AssetTesterOptions) *tester { @@ -44,6 +47,7 @@ func NewAssetTester(options AssetTesterOptions) *tester { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, + repoRoot: options.RepoRoot, } manager := resources.NewManager() @@ -83,6 +87,7 @@ func (r *tester) resources(installedPackage bool) resources.Resources { RootPath: r.packageRootPath, Absent: !installedPackage, Force: installedPackage, // Force re-installation, in case there are code changes in the same package version. + RepoRoot: r.repoRoot, }, } } diff --git a/internal/testrunner/runners/policy/runner.go b/internal/testrunner/runners/policy/runner.go index 30788323d6..29952cd035 100644 --- a/internal/testrunner/runners/policy/runner.go +++ b/internal/testrunner/runners/policy/runner.go @@ -7,6 +7,7 @@ package policy import ( "context" "fmt" + "os" "path/filepath" "strings" @@ -22,6 +23,7 @@ const ( ) type runner struct { + repoRoot *os.Root packageRootPath string kibanaClient *kibana.Client @@ -157,6 +159,7 @@ func (r *runner) Type() testrunner.TestType { func (r *runner) setupSuite(ctx context.Context, manager *resources.Manager) (cleanup func(ctx context.Context) error, err error) { packageResource := resources.FleetPackage{ RootPath: r.packageRootPath, + RepoRoot: r.repoRoot, } setupResources := resources.Resources{ &packageResource, diff --git a/internal/testrunner/runners/system/runner.go b/internal/testrunner/runners/system/runner.go index 6ba7125117..07dffe817b 100644 --- a/internal/testrunner/runners/system/runner.go +++ b/internal/testrunner/runners/system/runner.go @@ -25,6 +25,7 @@ import ( type runner struct { profile *profile.Profile + repoRoot *os.Root packageRootPath string kibanaClient *kibana.Client esAPI *elasticsearch.API @@ -55,6 +56,7 @@ var _ testrunner.TestRunner = new(runner) type SystemTestRunnerOptions struct { Profile *profile.Profile PackageRootPath string + RepoRoot *os.Root KibanaClient *kibana.Client API *elasticsearch.API @@ -97,6 +99,7 @@ func NewSystemTestRunner(options SystemTestRunnerOptions) *runner { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, + repoRoot: options.RepoRoot, } r.resourcesManager = resources.NewManager() @@ -286,6 +289,7 @@ func (r *runner) resources(opts resourcesOptions) resources.Resources { RootPath: r.packageRootPath, Absent: !opts.installedPackage, Force: opts.installedPackage, // Force re-installation, in case there are code changes in the same package version. + RepoRoot: r.repoRoot, }, } } From 90ba403f68a1cdb08a1853603b5e9de97aa1cf06 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 14:38:51 +0200 Subject: [PATCH 14/46] refactor linkedfiles_test.go to use filepath.Join for constructing link file paths --- internal/files/linkedfiles_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index e314581001..01197c2bd2 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -895,7 +895,7 @@ func Test_newLinkedFile(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { _ = repoRootHandle.Close() }) - linkFileContent := "../../B/otherFolder/included.txt d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e" + linkFileContent := fmt.Sprintf("%s d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", filepath.Join("..", "..", "B", "otherFolder", "included.txt")) includedFileContent := "included file content" // /packages/A/folder/link.txt.link From aeeee55899962e2d9ccdbf3b19826bf85b9b8f56 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 14:59:26 +0200 Subject: [PATCH 15/46] inject repoRoot to needed test options --- cmd/testrunner.go | 5 +++++ internal/packages/archetype/data_stream_test.go | 2 +- internal/resources/fleetpackage_test.go | 15 +++++++++++++-- internal/resources/fleetpolicy_test.go | 12 +++++++++--- internal/testrunner/runners/policy/runner.go | 3 +++ internal/testrunner/runners/policy/tester.go | 5 +++++ 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index 7f5374c740..37c6582695 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -671,6 +671,10 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("locating package root failed: %w", err) } + repoRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } dataStreams, err := getDataStreamsFlag(cmd, packageRootPath) if err != nil { @@ -704,6 +708,7 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.Policy, WithCoverage: testCoverage, CoverageType: testCoverageFormat, + RepoRoot: repoRoot, }) results, err := testrunner.RunSuite(ctx, runner) diff --git a/internal/packages/archetype/data_stream_test.go b/internal/packages/archetype/data_stream_test.go index b536b19cee..87e395e85f 100644 --- a/internal/packages/archetype/data_stream_test.go +++ b/internal/packages/archetype/data_stream_test.go @@ -61,7 +61,7 @@ func createDataStreamDescriptorForTest() DataStreamDescriptor { func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamDescriptor, valid bool) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + t.Cleanup(func() { _ = repoRoot.Close() }) linksFilePath := "" diff --git a/internal/resources/fleetpackage_test.go b/internal/resources/fleetpackage_test.go index 43e6665316..6b3024fb14 100644 --- a/internal/resources/fleetpackage_test.go +++ b/internal/resources/fleetpackage_test.go @@ -21,9 +21,15 @@ import ( func TestRequiredProvider(t *testing.T) { manager := resource.NewManager() - _, err := manager.Apply(resource.Resources{ + + repoRoot, err := files.FindRepositoryRoot() + require.NoError(t, err) + t.Cleanup(func() { _ = repoRoot.Close() }) + + _, err = manager.Apply(resource.Resources{ &FleetPackage{ RootPath: "../../test/packages/parallel/nginx", + RepoRoot: repoRoot, }, }) if assert.Error(t, err) { @@ -78,15 +84,20 @@ func TestSystemPackageIsNotRemoved(t *testing.T) { t.FailNow() } + repoRoot, err := files.FindRepositoryRoot() + require.NoError(t, err) + t.Cleanup(func() { _ = repoRoot.Close() }) + fleetPackage := FleetPackage{ RootPath: "../../test/packages/parallel/system", Absent: true, + RepoRoot: repoRoot, } manager := resource.NewManager() manager.RegisterProvider(DefaultKibanaProviderName, &KibanaProvider{Client: kibanaClient}) // Try to uninstall the package, it should not be installed. - _, err := manager.Apply(resource.Resources{&fleetPackage}) + _, err = manager.Apply(resource.Resources{&fleetPackage}) assert.NoError(t, err) assertPackageInstalled(t, kibanaClient, "installed", "system") diff --git a/internal/resources/fleetpolicy_test.go b/internal/resources/fleetpolicy_test.go index 2df40f65fd..3ba65d8185 100644 --- a/internal/resources/fleetpolicy_test.go +++ b/internal/resources/fleetpolicy_test.go @@ -20,10 +20,16 @@ import ( ) func TestRequiredProviderFleetPolicy(t *testing.T) { + + repoRoot, err := files.FindRepositoryRoot() + require.NoError(t, err) + t.Cleanup(func() { _ = repoRoot.Close() }) + manager := resource.NewManager() - _, err := manager.Apply(resource.Resources{ + _, err = manager.Apply(resource.Resources{ &FleetAgentPolicy{ - Name: "test-policy", + Name: "test-policy", + RepoRoot: repoRoot, }, }) if assert.Error(t, err) { @@ -34,7 +40,7 @@ func TestRequiredProviderFleetPolicy(t *testing.T) { func TestPolicyLifecycle(t *testing.T) { repoRoot, err := files.FindRepositoryRoot() require.NoError(t, err) - defer repoRoot.Close() + t.Cleanup(func() { _ = repoRoot.Close() }) cases := []struct { title string diff --git a/internal/testrunner/runners/policy/runner.go b/internal/testrunner/runners/policy/runner.go index 29952cd035..ac03c4ca99 100644 --- a/internal/testrunner/runners/policy/runner.go +++ b/internal/testrunner/runners/policy/runner.go @@ -50,6 +50,7 @@ type PolicyTestRunnerOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string + RepoRoot *os.Root } func NewPolicyTestRunner(options PolicyTestRunnerOptions) *runner { @@ -62,6 +63,7 @@ func NewPolicyTestRunner(options PolicyTestRunnerOptions) *runner { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, + repoRoot: options.RepoRoot, } runner.resourcesManager = resources.NewManager() runner.resourcesManager.RegisterProvider(resources.DefaultKibanaProviderName, &resources.KibanaProvider{Client: runner.kibanaClient}) @@ -145,6 +147,7 @@ func (r *runner) GetTests(ctx context.Context) ([]testrunner.Tester, error) { GlobalTestConfig: r.globalTestConfig, WithCoverage: r.withCoverage, CoverageType: r.coverageType, + RepoRoot: r.repoRoot, })) } diff --git a/internal/testrunner/runners/policy/tester.go b/internal/testrunner/runners/policy/tester.go index 9cf62b7f2b..bf39f43b56 100644 --- a/internal/testrunner/runners/policy/tester.go +++ b/internal/testrunner/runners/policy/tester.go @@ -6,6 +6,7 @@ package policy import ( "context" + "os" "path/filepath" "strings" @@ -28,6 +29,7 @@ type tester struct { coverageType string resourcesManager *resources.Manager + repoRoot *os.Root } // Ensures that runner implements testrunner.Tester interface @@ -42,6 +44,7 @@ type PolicyTesterOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string + RepoRoot *os.Root } func NewPolicyTester(options PolicyTesterOptions) *tester { @@ -54,6 +57,7 @@ func NewPolicyTester(options PolicyTesterOptions) *tester { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, + repoRoot: options.RepoRoot, } tester.resourcesManager = resources.NewManager() tester.resourcesManager.RegisterProvider(resources.DefaultKibanaProviderName, &resources.KibanaProvider{Client: tester.kibanaClient}) @@ -121,6 +125,7 @@ func (r *tester) runTest(ctx context.Context, manager *resources.Manager, testPa DataStreamVars: testConfig.DataStream.Vars, }, }, + RepoRoot: r.repoRoot, } resources := resource.Resources{&policy} _, testErr := manager.ApplyCtx(ctx, resources) From 385fc896d72b87d293a62b94665f0ae21c5838ee Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 29 Sep 2025 15:43:04 +0200 Subject: [PATCH 16/46] Update data stream tests to include repoRoot parameter --- .../packages/archetype/data_stream_test.go | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/internal/packages/archetype/data_stream_test.go b/internal/packages/archetype/data_stream_test.go index 87e395e85f..eeef8fa180 100644 --- a/internal/packages/archetype/data_stream_test.go +++ b/internal/packages/archetype/data_stream_test.go @@ -20,21 +20,30 @@ func TestDataStream(t *testing.T) { dd := createDataStreamDescriptorForTest() dd.Manifest.Type = "logs" - createAndCheckDataStream(t, pd, dd, true) + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + + createAndCheckDataStream(t, pd, dd, true, repoRoot) }) t.Run("valid-metrics", func(t *testing.T) { pd := createPackageDescriptorForTest("integration", "^7.13.0") dd := createDataStreamDescriptorForTest() dd.Manifest.Type = "metrics" - createAndCheckDataStream(t, pd, dd, true) + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + + createAndCheckDataStream(t, pd, dd, true, repoRoot) }) t.Run("missing-type", func(t *testing.T) { pd := createPackageDescriptorForTest("integration", "^7.13.0") dd := createDataStreamDescriptorForTest() dd.Manifest.Type = "" - createAndCheckDataStream(t, pd, dd, false) + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + + createAndCheckDataStream(t, pd, dd, false, repoRoot) }) } @@ -58,15 +67,11 @@ func createDataStreamDescriptorForTest() DataStreamDescriptor { } } -func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamDescriptor, valid bool) { - repoRoot, err := os.OpenRoot(t.TempDir()) - require.NoError(t, err) - t.Cleanup(func() { _ = repoRoot.Close() }) - +func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamDescriptor, valid bool, repoRoot *os.Root) { linksFilePath := "" packagesDir := filepath.Join(repoRoot.Name(), "packages") - err = os.MkdirAll(packagesDir, 0o755) + err := os.MkdirAll(packagesDir, 0o755) require.NoError(t, err) err = os.Chdir(packagesDir) require.NoError(t, err) From 9edca9b77af36002260604fef61c0055f2851bf6 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Tue, 30 Sep 2025 10:24:07 +0200 Subject: [PATCH 17/46] Ensure repoRoot is closed in all data stream test cases --- internal/packages/archetype/data_stream_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/packages/archetype/data_stream_test.go b/internal/packages/archetype/data_stream_test.go index eeef8fa180..ba58441ac6 100644 --- a/internal/packages/archetype/data_stream_test.go +++ b/internal/packages/archetype/data_stream_test.go @@ -24,6 +24,9 @@ func TestDataStream(t *testing.T) { require.NoError(t, err) createAndCheckDataStream(t, pd, dd, true, repoRoot) + + err = repoRoot.Close() + require.NoError(t, err) }) t.Run("valid-metrics", func(t *testing.T) { pd := createPackageDescriptorForTest("integration", "^7.13.0") @@ -34,6 +37,9 @@ func TestDataStream(t *testing.T) { require.NoError(t, err) createAndCheckDataStream(t, pd, dd, true, repoRoot) + + err = repoRoot.Close() + require.NoError(t, err) }) t.Run("missing-type", func(t *testing.T) { pd := createPackageDescriptorForTest("integration", "^7.13.0") @@ -44,6 +50,9 @@ func TestDataStream(t *testing.T) { require.NoError(t, err) createAndCheckDataStream(t, pd, dd, false, repoRoot) + + err = repoRoot.Close() + require.NoError(t, err) }) } From c60b83695c56bdb2c0e6a7192f34b35b2778c489 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Tue, 30 Sep 2025 14:22:12 +0200 Subject: [PATCH 18/46] Remove unnecessary directory change in createAndCheckDataStream function --- internal/packages/archetype/data_stream_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/packages/archetype/data_stream_test.go b/internal/packages/archetype/data_stream_test.go index ba58441ac6..8dc35c32d0 100644 --- a/internal/packages/archetype/data_stream_test.go +++ b/internal/packages/archetype/data_stream_test.go @@ -82,8 +82,6 @@ func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamD packagesDir := filepath.Join(repoRoot.Name(), "packages") err := os.MkdirAll(packagesDir, 0o755) require.NoError(t, err) - err = os.Chdir(packagesDir) - require.NoError(t, err) err = createPackageInDir(pd, packagesDir) require.NoError(t, err) From ef3497f0209fe587e549d68a11042a276341b763 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 6 Oct 2025 10:32:29 +0200 Subject: [PATCH 19/46] refactor at copyLicenceTextFile to ensure license inside reporoot, refactor tests and added readme note --- README.md | 2 +- internal/builder/packages.go | 38 +++- internal/builder/packages_test.go | 216 ++++++++++++-------- internal/docs/links_map_test.go | 2 +- internal/packages/archetype/package_test.go | 6 +- tools/readme/readme.md.tmpl | 2 +- 6 files changed, 165 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 0dbdab90a4..6a46410cb5 100644 --- a/README.md +++ b/README.md @@ -708,7 +708,7 @@ There are available some environment variables that could be used to change some - `ELASTIC_PACKAGE_DATA_HOME`: Custom path to be used for `elastic-package` data directory. By default this is `~/.elastic-package`. - Related to the build process: - - `ELASTIC_PACKAGE_REPOSITORY_LICENSE`: Path to the default repository license. + - `ELASTIC_PACKAGE_REPOSITORY_LICENSE`: Path to the default repository license. This path should be relative to the repository root. - `ELASTIC_PACKAGE_LINKS_FILE_PATH`: Path to the links table file (e.g. `links_table.yml`) with the link definitions to be used in the build process of a package. - Related to signing packages: diff --git a/internal/builder/packages.go b/internal/builder/packages.go index ef6c93f231..5afdd7ba02 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -10,6 +10,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/magefile/mage/sh" @@ -292,12 +293,34 @@ func signZippedPackage(options BuildOptions, zippedPackagePath string) error { return nil } +// copyLicenseTextFile checks if a license file exists in the package directory. +// If the file already exists in the package, it does nothing. +// If the file does not exist in the package, it looks for a license file in the repository root directory. +// If a license file is found in the repository, it copies it to the package directory. func copyLicenseTextFile(repoRoot *os.Root, licensePath string) error { // if the given path exist, skip copying - if info, err := os.Stat(licensePath); err == nil && !info.IsDir() { + info, err := os.Stat(licensePath) + if err == nil && !info.IsDir() { logger.Debug("License file in the package will be used") return nil } + // if the given path does not exist, continue + if err != nil && !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("can't check license path (%s): %w", licensePath, err) + } + // if the given path exist but is a directory, return an error + if info != nil && info.IsDir() { + return fmt.Errorf("license path (%s) is a directory", licensePath) + } + + // Ensure licensePath is inside the repoRoot + rel, err := filepath.Rel(repoRoot.Name(), licensePath) + if err != nil { + return fmt.Errorf("failed to get relative path for licensePath (%s) from repoRoot (%s): %w", licensePath, repoRoot.Name(), err) + } + if strings.HasPrefix(rel, "..") { + return fmt.Errorf("licensePath (%s) is outside of the repoRoot (%s)", licensePath, repoRoot.Name()) + } // lookup for the license file in the repository // default license name can be overridden by the user @@ -306,9 +329,7 @@ func copyLicenseTextFile(repoRoot *os.Root, licensePath string) error { repositoryLicenseTextFileName = licenseTextFileName } - path := filepath.Join(repoRoot.Name(), repositoryLicenseTextFileName) - - sourceLicensePath, err := findRepositoryLicense(path) + sourceLicensePath, err := findRepositoryLicensePath(repoRoot, repositoryLicenseTextFileName) if !userDefined && errors.Is(err, os.ErrNotExist) { logger.Debug("No license text file is included in package") return nil @@ -347,7 +368,7 @@ func createBuildDirectory(dirs ...string) (string, error) { return buildDir, nil } -// findRepositoryLicense checks if a license file exists at the specified path. +// findRepositoryLicensePath checks if a license file exists at the specified path. // If the file exists, it returns the path; otherwise, it returns an error indicating // that the repository license could not be found. // @@ -359,10 +380,11 @@ func createBuildDirectory(dirs ...string) (string, error) { // // string - the license file path if found. // error - an error if the license file does not exist. -func findRepositoryLicense(licensePath string) (string, error) { - if _, err := os.Stat(licensePath); err != nil { +func findRepositoryLicensePath(repoRoot *os.Root, repositoryLicenseTextFileName string) (string, error) { + path := filepath.Join(repoRoot.Name(), repositoryLicenseTextFileName) + if _, err := os.Stat(path); err != nil { return "", fmt.Errorf("failed to find repository license: %w", err) } - return licensePath, nil + return path, nil } diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index 54b9760ac7..a131634834 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -13,114 +13,154 @@ import ( "github.com/stretchr/testify/require" ) -func TestFindRepositoryLicense_FileExists(t *testing.T) { - dir := t.TempDir() - licensePath := filepath.Join(dir, "LICENSE.txt") - err := os.WriteFile(licensePath, []byte("license content"), 0644) - require.NoError(t, err) - - path, err := findRepositoryLicense(licensePath) - require.NoError(t, err) - assert.Equal(t, licensePath, path) -} +func TestFindRepositoryLicense(t *testing.T) { + + t.Run("FileExists", func(t *testing.T) { + root, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer root.Close() + + // Create a LICENSE.txt file in the temp directory + expectedPath := filepath.Join(root.Name(), "LICENSE.txt") + err = os.WriteFile(expectedPath, []byte("license content"), 0644) + require.NoError(t, err) -func TestFindRepositoryLicense_FileDoesNotExist(t *testing.T) { - dir := t.TempDir() - licensePath := filepath.Join(dir, "NON_EXISTENT_LICENSE.txt") + path, err := findRepositoryLicensePath(root, "LICENSE.txt") + require.NoError(t, err) + assert.Equal(t, expectedPath, path) + }) + + t.Run("FileDoesNotExist", func(t *testing.T) { + root, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer root.Close() + + path, err := findRepositoryLicensePath(root, "NON_EXISTENT_LICENSE.txt") + require.Error(t, err) + assert.Empty(t, path) + }) - path, err := findRepositoryLicense(licensePath) - require.Error(t, err) - assert.Empty(t, path) } + func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { - dir := t.TempDir() - licensePath := filepath.Join(dir, "LICENSE.txt") - err := os.WriteFile(licensePath, []byte("existing license"), 0644) - require.NoError(t, err) - - repoRoot, err := os.OpenRoot(t.TempDir()) - require.NoError(t, err) - defer repoRoot.Close() - - // Should not attempt to copy, just return nil - err = copyLicenseTextFile(repoRoot, licensePath) - assert.NoError(t, err) - - // License file should remain unchanged - content, err := os.ReadFile(licensePath) - require.NoError(t, err) - assert.Equal(t, "existing license", string(content)) -} -func TestCopyLicenseTextFile_CopiesFromRepo(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) - require.NoError(t, err) - defer repoRoot.Close() + t.Run("ExistingFile", func(t *testing.T) { + dir := t.TempDir() + licensePath := filepath.Join(dir, "LICENSE.txt") + err := os.WriteFile(licensePath, []byte("existing license"), 0644) + require.NoError(t, err) - licensePath := filepath.Join(repoRoot.Name(), "LICENSE.txt") - err = os.WriteFile(licensePath, []byte("repo license"), 0644) - require.NoError(t, err) + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() - destDir := t.TempDir() - destLicensePath := filepath.Join(destDir, "LICENSE.txt") + // Should not attempt to copy, just return nil + err = copyLicenseTextFile(repoRoot, licensePath) + assert.NoError(t, err) - err = copyLicenseTextFile(repoRoot, destLicensePath) - assert.NoError(t, err) + // License file should remain unchanged + content, err := os.ReadFile(licensePath) + require.NoError(t, err) + assert.Equal(t, "existing license", string(content)) - content, err := os.ReadFile(destLicensePath) - require.NoError(t, err) - assert.Equal(t, "repo license", string(content)) -} + }) -func TestCopyLicenseTextFile_NoRepoLicense_ReturnsNil(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) - require.NoError(t, err) - defer repoRoot.Close() + t.Run("ExistingDirectory", func(t *testing.T) { + dir := t.TempDir() - destDir := t.TempDir() - destLicensePath := filepath.Join(destDir, "LICENSE.txt") + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() - err = copyLicenseTextFile(repoRoot, destLicensePath) - assert.NoError(t, err) + err = copyLicenseTextFile(repoRoot, dir) + assert.Error(t, err) + assert.Contains(t, err.Error(), "is a directory") + }) - _, err = os.Stat(destLicensePath) - assert.True(t, os.IsNotExist(err)) -} + t.Run("StatError", func(t *testing.T) { + // Using a path that is likely invalid to trigger a stat error + invalidPath := string([]byte{0}) -func TestCopyLicenseTextFile_EnvOverridesLicenseName(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) - require.NoError(t, err) - defer repoRoot.Close() + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() - customLicenseName := "CUSTOM_LICENSE.txt" - customLicensePath := filepath.Join(repoRoot.Name(), customLicenseName) - err = os.WriteFile(customLicensePath, []byte("custom license"), 0644) - require.NoError(t, err) + err = copyLicenseTextFile(repoRoot, invalidPath) + assert.Error(t, err) + assert.Contains(t, err.Error(), "can't check license path") + }) - destDir := t.TempDir() - destLicensePath := filepath.Join(destDir, "LICENSE.txt") + t.Run("RepoLicenseDefaultFileName", func(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() - t.Setenv(repositoryLicenseEnv, customLicenseName) - err = copyLicenseTextFile(repoRoot, destLicensePath) - assert.NoError(t, err) + // original license file path + originalFile := filepath.Join(repoRoot.Name(), licenseTextFileName) + err = os.WriteFile(originalFile, []byte("repo license"), 0644) + require.NoError(t, err) - content, err := os.ReadFile(destLicensePath) - require.NoError(t, err) - assert.Equal(t, "custom license", string(content)) -} + expectedPath := filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt") + + err = copyLicenseTextFile(repoRoot, expectedPath) + assert.NoError(t, err) + + content, err := os.ReadFile(expectedPath) + require.NoError(t, err) + assert.Equal(t, "repo license", string(content)) + }) + + t.Run("RepoLicenseCustomFileName", func(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() + + // original license file path + originalFile := filepath.Join(repoRoot.Name(), "CUSTOM_LICENSE.txt") + err = os.WriteFile(originalFile, []byte("repo license"), 0644) + require.NoError(t, err) + + t.Setenv(repositoryLicenseEnv, "CUSTOM_LICENSE.txt") + + expectedPath := filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt") + + err = copyLicenseTextFile(repoRoot, expectedPath) + assert.NoError(t, err) + + content, err := os.ReadFile(expectedPath) + require.NoError(t, err) + assert.Equal(t, "repo license", string(content)) + }) + + t.Run("RepoLicenseFileDoesNotExist", func(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() + + expectedPath := filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt") + + err = copyLicenseTextFile(repoRoot, expectedPath) + assert.NoError(t, err) + + _, err = os.Stat(filepath.Join(repoRoot.Name(), "LICENSE.txt")) + assert.ErrorIs(t, err, os.ErrNotExist) + + _, err = os.Stat(expectedPath) + assert.ErrorIs(t, err, os.ErrNotExist) + }) -func TestCopyLicenseTextFile_ErrorCopyingFile(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) - require.NoError(t, err) - defer repoRoot.Close() + t.Run("RepoLicensePathOutsideRepoRoot", func(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() - licensePath := filepath.Join(repoRoot.Name(), "LICENSE.txt") - err = os.WriteFile(licensePath, []byte("repo license"), 0644) - require.NoError(t, err) + // Create a LICENSE.txt file in a different temp directory + outsideDir := t.TempDir() + outsideLicensePath := filepath.Join(outsideDir, "LICENSE.txt") - // Use a destination path that is a directory, so sh.Copy should fail - destDir := t.TempDir() + err = copyLicenseTextFile(repoRoot, outsideLicensePath) + assert.Error(t, err) + assert.Contains(t, err.Error(), "is outside of the repoRoot") + }) - err = copyLicenseTextFile(repoRoot, destDir) - assert.Error(t, err) } diff --git a/internal/docs/links_map_test.go b/internal/docs/links_map_test.go index 9d04fb0735..aa2f6f3fe1 100644 --- a/internal/docs/links_map_test.go +++ b/internal/docs/links_map_test.go @@ -141,7 +141,7 @@ func TestLinksDefinitionsFilePath(t *testing.T) { defaultFilePath := filepath.Join(repoRoot.Name(), linksMapFileNameDefault) _, err = os.Stat(defaultFilePath) - require.True(t, os.IsNotExist(err)) + require.ErrorIs(t, err, os.ErrNotExist) path, err := LinksDefinitionsFilePath(repoRoot) require.NoError(t, err) diff --git a/internal/packages/archetype/package_test.go b/internal/packages/archetype/package_test.go index 71f3bbf7f1..93103c1068 100644 --- a/internal/packages/archetype/package_test.go +++ b/internal/packages/archetype/package_test.go @@ -98,8 +98,10 @@ func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDe } func buildPackage(t *testing.T, repoRoot *os.Root, linksFilePath, packageRoot string) error { - buildDir := t.TempDir() - _, err := docs.UpdateReadmes(linksFilePath, packageRoot, buildDir) + buildDir := filepath.Join(repoRoot.Name(), "build") + err := os.MkdirAll(buildDir, 0o755) + require.NoError(t, err) + _, err = docs.UpdateReadmes(linksFilePath, packageRoot, buildDir) if err != nil { return err } diff --git a/tools/readme/readme.md.tmpl b/tools/readme/readme.md.tmpl index 49bb18bdf5..cb0e1949ab 100644 --- a/tools/readme/readme.md.tmpl +++ b/tools/readme/readme.md.tmpl @@ -228,7 +228,7 @@ There are available some environment variables that could be used to change some - `ELASTIC_PACKAGE_DATA_HOME`: Custom path to be used for `elastic-package` data directory. By default this is `~/.elastic-package`. - Related to the build process: - - `ELASTIC_PACKAGE_REPOSITORY_LICENSE`: Path to the default repository license. + - `ELASTIC_PACKAGE_REPOSITORY_LICENSE`: Path to the default repository license. This path should be relative to the repository root. - `ELASTIC_PACKAGE_LINKS_FILE_PATH`: Path to the links table file (e.g. `links_table.yml`) with the link definitions to be used in the build process of a package. - Related to signing packages: From ec2a7e103f3097cbbdf4394c2d2c3f3370bd9c08 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 11:03:58 +0200 Subject: [PATCH 20/46] refactor linkefiles management to use relative paths and root --- internal/files/linkedfiles.go | 124 ++++++++++++++++------------- internal/files/linkedfiles_test.go | 111 ++++++++++++++------------ 2 files changed, 129 insertions(+), 106 deletions(-) diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index f7fb01e0c4..3d8879722a 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -20,6 +20,13 @@ import ( "github.com/elastic/elastic-package/internal/packages" ) +var ( + errEmptyWorkDir = fmt.Errorf("working directory is empty") + errInvalidWorkDir = fmt.Errorf("working directory must be an absolute path or a path relative to the repository root") + errInvalidWorkDirNotDir = fmt.Errorf("working directory is not a directory") + errFileNotUpToDate = fmt.Errorf("linked file is not up to date") +) + const linkExtension = ".link" // PackageLinks represents linked files grouped by package. @@ -29,19 +36,34 @@ type PackageLinks struct { } // CreateLinksFSFromPath creates a LinksFS for the given directory within the repository. +// +// - workDir can be an absolute path or a path relative to the repository root. +// in both cases, it must point to a directory within the repository. func CreateLinksFSFromPath(repoRoot *os.Root, workDir string) (*LinksFS, error) { if workDir == "" { - return nil, fmt.Errorf("working directory is empty") + return nil, errEmptyWorkDir } - // If workDir is not absolute, make it relative to the root - // This allows using both absolute and relative paths - // inside the LinksFS - if !filepath.IsAbs(workDir) { - workDir = filepath.Join(repoRoot.Name(), workDir) + var relWorkDir string + if filepath.IsAbs(workDir) { + var err error + relWorkDir, err = filepath.Rel(repoRoot.Name(), workDir) + if err != nil { + return nil, fmt.Errorf("unable to find rel path for %s: %w: %w", workDir, errInvalidWorkDir, err) + } + } else { + relWorkDir = workDir } - return NewLinksFS(repoRoot, workDir) + info, err := repoRoot.Stat(relWorkDir) + if err != nil { + return nil, fmt.Errorf("unable to stat %s: %w: %w", relWorkDir, errInvalidWorkDir, err) + } + if !info.IsDir() { + return nil, fmt.Errorf("working directory %s is not a directory: %w", relWorkDir, errInvalidWorkDirNotDir) + } + + return &LinksFS{repoRoot: repoRoot, workDir: relWorkDir}, nil } var _ fs.FS = (*LinksFS)(nil) @@ -54,70 +76,53 @@ var _ fs.FS = (*LinksFS)(nil) type LinksFS struct { repoRoot *os.Root // The root of the repository, used to check if paths are within the repository. workDir string - inner fs.FS } -// NewLinksFS creates a new LinksFS. workDir must be an absolute path, or a path relative to -// the repository root. -func NewLinksFS(repoRoot *os.Root, workDir string) (*LinksFS, error) { - // Ensure workDir is absolute for os.DirFS - var absWorkDir string - if filepath.IsAbs(workDir) { - absWorkDir = workDir - relative, err := filepath.Rel(repoRoot.Name(), absWorkDir) - if err != nil { - return nil, fmt.Errorf("invalid working directory %s: %w", absWorkDir, err) - } - workDir = relative - } else { - absWorkDir = filepath.Clean(filepath.Join(repoRoot.Name(), workDir)) - } - - info, err := repoRoot.Stat(workDir) +// Open opens a file in the filesystem. +// +// - name can be an absolute path or a path relative to the workDir. +// If name is absolute, it must be within the workDir. +func (lfs *LinksFS) Open(name string) (fs.File, error) { + // innerRoot is the filesystem rooted at the workDir + // we use it to check if the file exists in the workDir + // and to open non-link files directly + innerRoot, err := lfs.repoRoot.OpenRoot(lfs.workDir) if err != nil { - return nil, fmt.Errorf("invalid working directory %s: %w", absWorkDir, err) - } - if !info.IsDir() { - return nil, fmt.Errorf("working directory %s is not a directory", absWorkDir) + return nil, fmt.Errorf("could not open workDir in root: %w", err) } + defer innerRoot.Close() - return &LinksFS{repoRoot: repoRoot, workDir: absWorkDir, inner: os.DirFS(absWorkDir)}, nil -} - -// Open opens a file in the filesystem. -func (lfs *LinksFS) Open(name string) (fs.File, error) { - // Ensure name is relative for os.DirFS compatibility - var relativeName string + relName := name if filepath.IsAbs(name) { var err error - relativeName, err = filepath.Rel(lfs.workDir, name) + relName, err = filepath.Rel(filepath.Join(lfs.repoRoot.Name(), lfs.workDir), name) if err != nil { return nil, fmt.Errorf("could not make name relative to workDir: %w", err) } - } else { - relativeName = name + } + _, err = innerRoot.Stat(relName) + if err != nil { + return nil, fmt.Errorf("file %s not found in workDir %s: %w", relName, lfs.workDir, err) } // For non-link files, use the inner filesystem - if filepath.Ext(relativeName) != linkExtension { - return lfs.inner.Open(relativeName) + if filepath.Ext(relName) != linkExtension { + return innerRoot.Open(relName) } - // For link files, construct the absolute path to the link file - // Since workDir is expected to be absolute, we can directly join - linkFilePath := filepath.Join(lfs.workDir, relativeName) - + linkFilePath := filepath.Join(lfs.repoRoot.Name(), lfs.workDir, relName) l, err := newLinkedFile(lfs.repoRoot, linkFilePath) if err != nil { return nil, err } if !l.UpToDate { - return nil, fmt.Errorf("linked file %s is not up to date", relativeName) + return nil, fmt.Errorf("%w: file %s", errFileNotUpToDate, relName) } - // Calculate the included file path relative to the link file's directory - linkDir := filepath.Dir(linkFilePath) - includedPath := filepath.Join(linkDir, l.IncludedFilePath) + // includedPath is the absolute path to the included file referenced at the link file + // inside a link file, the path to the included file is relative to the link file location + // so we need to join the directory of the link file with the included file path + includedPath := filepath.Join(filepath.Dir(linkFilePath), filepath.FromSlash(l.IncludedFilePath)) // Convert to relative path from repository root for secure access of target file relativePath, err := filepath.Rel(lfs.repoRoot.Name(), includedPath) @@ -309,26 +314,29 @@ func includeLinkedFiles(root *os.Root, fromDir, toDir string) ([]Link, error) { return links, nil } -// listLinkedFiles function returns a slice of Link structs representing linked files. +// listLinkedFiles returns a slice of Link structs representing linked files +// within the given directory. +// +// - fromDir should be relative to the repository root. func listLinkedFiles(root *os.Root, fromDir string) ([]Link, error) { - var linkFiles []string + var linkFilesPaths []string if err := filepath.Walk( - filepath.FromSlash(fromDir), + filepath.Join(root.Name(), fromDir), func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && strings.HasSuffix(info.Name(), linkExtension) { - linkFiles = append(linkFiles, path) + linkFilesPaths = append(linkFilesPaths, path) } return nil }); err != nil { return nil, err } - links := make([]Link, len(linkFiles)) + links := make([]Link, len(linkFilesPaths)) - for i, f := range linkFiles { + for i, f := range linkFilesPaths { l, err := newLinkedFile(root, filepath.FromSlash(f)) if err != nil { return nil, fmt.Errorf("could not initialize linked file %s: %w", f, err) @@ -454,16 +462,18 @@ func updateLinkedFilesChecksums(root *os.Root, fromDir string) ([]Link, error) { // linkedFilesByPackageFrom function returns a slice of PackageLinks containing linked files grouped by package. // Each PackageLinks contains the package name and a slice of linked file paths. +// +// - fromDir should be relative to the repository root. func linkedFilesByPackageFrom(root *os.Root, fromDir string) ([]PackageLinks, error) { // we list linked files from all the root directory // to check which ones are linked to the 'fromDir' package - links, err := listLinkedFiles(root, root.Name()) + links, err := listLinkedFiles(root, ".") if err != nil { return nil, fmt.Errorf("listing linked files failed: %w", err) } var packageName string - if packageRoot, _, _ := packages.FindPackageRootFrom(fromDir); packageRoot != "" { + if packageRoot, _, _ := packages.FindPackageRootFrom(filepath.Join(root.Name(), fromDir)); packageRoot != "" { packageName = filepath.Base(packageRoot) } byPackageMap := map[string][]string{} diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index 01197c2bd2..46de56c934 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -90,8 +90,11 @@ func TestListLinkedFiles(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { _ = root.Close() }) + fromDir, err := filepath.Rel(root.Name(), basePath) + require.NoError(t, err) + // Use the private function directly for testing - linkedFiles, err := listLinkedFiles(root, basePath) + linkedFiles, err := listLinkedFiles(root, fromDir) require.NoError(t, err) require.NotEmpty(t, linkedFiles) require.Len(t, linkedFiles, 2) // Expect exactly 2 link files in testdata @@ -161,7 +164,7 @@ func TestAreLinkedFilesUpToDate(t *testing.T) { t.Cleanup(func() { _ = root.Close() }) // Create LinksFS - linksFS, err := NewLinksFS(root, basePath) + linksFS, err := CreateLinksFSFromPath(root, basePath) require.NoError(t, err) // Get all outdated linked files from the test directory @@ -202,7 +205,7 @@ func TestUpdateLinkedFilesChecksums(t *testing.T) { t.Cleanup(func() { _ = root.Close() }) // Create LinksFS - linksFS, err := NewLinksFS(root, basePath) + linksFS, err := CreateLinksFSFromPath(root, basePath) require.NoError(t, err) // Update checksums for all outdated linked files @@ -235,8 +238,11 @@ func TestLinkedFilesByPackageFrom(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { _ = root.Close() }) + fromDir, err := filepath.Rel(root.Name(), basePath) + require.NoError(t, err) + // Create LinksFS - linksFS, err := NewLinksFS(root, basePath) + linksFS, err := CreateLinksFSFromPath(root, fromDir) require.NoError(t, err) // Get linked files organized by package @@ -286,7 +292,7 @@ func TestIncludeLinkedFiles(t *testing.T) { t.Cleanup(func() { _ = root.Close() }) // Include (copy) all linked files from source to destination using LinksFS - linksFS, err := NewLinksFS(root, fromDir) + linksFS, err := CreateLinksFSFromPath(root, fromDir) assert.NoError(t, err) linkedFiles, err := linksFS.IncludeLinkedFiles(toDir) assert.NoError(t, err) @@ -528,7 +534,7 @@ func TestLinksFSSecurityIsolation(t *testing.T) { relWorkDir, err := filepath.Rel(repoDir, workDir) require.NoError(t, err) - lfs, err := NewLinksFS(root, relWorkDir) + lfs, err := CreateLinksFSFromPath(root, relWorkDir) require.NoError(t, err) // Test opening the linked file - this should work and use the repository root @@ -566,26 +572,33 @@ func TestLinksFS_Open(t *testing.T) { require.NoError(t, err) // Create test files - regularFile := filepath.Join(workDir, "regular.txt") - err = os.WriteFile(regularFile, []byte("regular content"), 0644) + regularFileRel := "regular.txt" + regularFileAbs := filepath.Join(workDir, regularFileRel) + err = os.WriteFile(regularFileAbs, []byte("regular content"), 0644) require.NoError(t, err) - includedFile := filepath.Join(workDir, "included.txt") + includedFileAbs := filepath.Join(repoDir, "other", "included.txt") + err = os.MkdirAll(filepath.Dir(includedFileAbs), 0755) + require.NoError(t, err) includedContent := "included content" - err = os.WriteFile(includedFile, []byte(includedContent), 0644) + err = os.WriteFile(includedFileAbs, []byte(includedContent), 0644) require.NoError(t, err) // Create link file with correct checksum - linkFile := filepath.Join(workDir, "linked.txt.link") + linkFileRel := filepath.Join("packages", "packageB", "linked.txt.link") + linkFileAbs := filepath.Join(workDir, linkFileRel) + err = os.MkdirAll(filepath.Dir(linkFileAbs), 0755) + require.NoError(t, err) hash := sha256.Sum256([]byte(includedContent)) checksum := hex.EncodeToString(hash[:]) - linkContent := fmt.Sprintf("./included.txt %s", checksum) - err = os.WriteFile(linkFile, []byte(linkContent), 0644) + linkContent := fmt.Sprintf("../../../other/included.txt %s", checksum) + err = os.WriteFile(linkFileAbs, []byte(linkContent), 0644) require.NoError(t, err) // Create outdated link file (no checksum) - outdatedLinkFile := filepath.Join(workDir, "outdated.txt.link") - err = os.WriteFile(outdatedLinkFile, []byte("./included.txt"), 0644) + outdatedLinkFileRel := "outdated.txt.link" + outdatedLinkFileAbs := filepath.Join(workDir, outdatedLinkFileRel) + err = os.WriteFile(outdatedLinkFileAbs, []byte("../other/included.txt"), 0644) require.NoError(t, err) // Setup LinksFS with absolute workDir @@ -593,46 +606,49 @@ func TestLinksFS_Open(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { _ = root.Close() }) - lfs, err := NewLinksFS(root, workDir) + lfs, err := CreateLinksFSFromPath(root, workDir) require.NoError(t, err) tests := []struct { name string - fileName string - expectError bool - errorMsg string + fileName string // filename should be relative to workDir or absolute + expectedErr error expectFile bool }{ { name: "open regular file with relative path", - fileName: "regular.txt", + fileName: regularFileRel, expectFile: true, }, { name: "open regular file with absolute path", - fileName: filepath.Join(workDir, "regular.txt"), + fileName: regularFileAbs, expectFile: true, }, { name: "open up-to-date link file", - fileName: "linked.txt.link", + fileName: linkFileRel, expectFile: true, }, { name: "open up-to-date link file with absolute path", - fileName: filepath.Join(workDir, "linked.txt.link"), + fileName: linkFileAbs, expectFile: true, }, { name: "open outdated link file should fail", - fileName: "outdated.txt.link", - expectError: true, - errorMsg: "not up to date", + fileName: outdatedLinkFileRel, + expectedErr: errFileNotUpToDate, + }, + { + name: "open outdated link file with absolute pathshould fail", + fileName: outdatedLinkFileAbs, + expectedErr: errFileNotUpToDate, }, { name: "open non-existent file should fail", fileName: "nonexistent.txt", - expectError: true, + expectedErr: os.ErrNotExist, }, } @@ -640,30 +656,27 @@ func TestLinksFS_Open(t *testing.T) { t.Run(tc.name, func(t *testing.T) { file, err := lfs.Open(tc.fileName) - if tc.expectError { - assert.Error(t, err) - if tc.errorMsg != "" { - assert.Contains(t, err.Error(), tc.errorMsg) - } - assert.Nil(t, file) + if tc.expectedErr != nil { + require.Error(t, err) + require.Nil(t, file) + assert.ErrorIs(t, err, tc.expectedErr) } else { + require.NoError(t, err) + require.NotNil(t, file) + + // Verify we can read from the file + content, err := io.ReadAll(file) assert.NoError(t, err) - assert.NotNil(t, file) - if file != nil { - // Verify we can read from the file - content, err := io.ReadAll(file) - assert.NoError(t, err) + // For link files, content should be from the included file + if strings.HasSuffix(tc.fileName, ".link") { + assert.Equal(t, includedContent, string(content)) + } else { + assert.Equal(t, "regular content", string(content)) + } - // For link files, content should be from the included file - if strings.HasSuffix(tc.fileName, ".link") { - assert.Equal(t, includedContent, string(content)) - } else { - assert.Equal(t, "regular content", string(content)) - } + file.Close() - file.Close() - } } }) } @@ -707,7 +720,7 @@ func TestLinksFS_RelativeWorkDir(t *testing.T) { t.Cleanup(func() { _ = root.Close() }) // Use relative path "work" instead of absolute path - lfs, err := NewLinksFS(root, "work") + lfs, err := CreateLinksFSFromPath(root, "work") require.NoError(t, err) tests := []struct { @@ -781,7 +794,7 @@ func TestLinksFS_ErrorConditions(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { _ = root.Close() }) - lfs, err := NewLinksFS(root, workDir) + lfs, err := CreateLinksFSFromPath(root, workDir) require.NoError(t, err) notFoundErrorMsg := "no such file or directory" @@ -874,7 +887,7 @@ func TestLinksFS_WorkDirValidation(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - lfs, err := NewLinksFS(root, tc.workDir) + lfs, err := CreateLinksFSFromPath(root, tc.workDir) if tc.expectError { assert.Error(t, err) From 169777fc3af404875c4c08fd494e5bd01bebbbde Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 11:19:05 +0200 Subject: [PATCH 21/46] revert linksDefinitionsFilePath from public to private --- cmd/build.go | 7 +------ cmd/lint.go | 6 +++--- internal/docs/links_map.go | 4 ++-- internal/docs/links_map_test.go | 10 +++++----- internal/docs/readme.go | 14 +++++++++----- internal/packages/archetype/data_stream_test.go | 3 +-- internal/packages/archetype/package_test.go | 11 +++++------ 7 files changed, 26 insertions(+), 29 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 06117c2196..1d70a4fc8f 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -67,11 +67,6 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } defer repoRoot.Close() - linksFilePath, err := docs.LinksDefinitionsFilePath(repoRoot) - if err != nil { - return fmt.Errorf("locating links file failed: %w", err) - } - packageRoot, err := packages.MustFindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) @@ -83,7 +78,7 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } logger.Debugf("Use build directory: %s", buildDir) - targets, err := docs.UpdateReadmes(linksFilePath, packageRoot, buildDir) + targets, err := docs.UpdateReadmes(repoRoot, packageRoot, buildDir) if err != nil { return fmt.Errorf("updating files failed: %w", err) } diff --git a/cmd/lint.go b/cmd/lint.go index caaf464de4..f34775415a 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -53,12 +53,12 @@ func lintCommandAction(cmd *cobra.Command, args []string) error { } defer repoRoot.Close() - linksFilePath, err := docs.LinksDefinitionsFilePath(repoRoot) + packageRoot, err := packages.MustFindPackageRoot() if err != nil { - return fmt.Errorf("locating links file failed: %w", err) + return fmt.Errorf("package root not found: %w", err) } - readmeFiles, err := docs.AreReadmesUpToDate(linksFilePath) + readmeFiles, err := docs.AreReadmesUpToDate(repoRoot, packageRoot) if err != nil { for _, f := range readmeFiles { if !f.UpToDate { diff --git a/internal/docs/links_map.go b/internal/docs/links_map.go index 7334f49397..4d7ad5fc0f 100644 --- a/internal/docs/links_map.go +++ b/internal/docs/links_map.go @@ -83,13 +83,13 @@ func (l linkMap) RenderLink(key string, options linkOptions) (string, error) { return url, nil } -// LinksDefinitionsFilePath returns the file path to the links definitions file. +// linksDefinitionsFilePath returns the file path to the links definitions file. // It first checks if the environment variable specified by linksMapFilePathEnvVar is set. // If set, it verifies that the file exists and returns its path, or an error if not found. // If the environment variable is not set, it falls back to the default file path // constructed from repoRoot and linksMapFileNameDefault, returning the path if the file exists, // or nil if it does not. -func LinksDefinitionsFilePath(repoRoot *os.Root) (string, error) { +func linksDefinitionsFilePath(repoRoot *os.Root) (string, error) { linksFilePath := os.Getenv(linksMapFilePathEnvVar) if linksFilePath != "" { if _, err := os.Stat(linksFilePath); err != nil { diff --git a/internal/docs/links_map_test.go b/internal/docs/links_map_test.go index aa2f6f3fe1..0701c6a541 100644 --- a/internal/docs/links_map_test.go +++ b/internal/docs/links_map_test.go @@ -89,7 +89,7 @@ func TestRenderLink(t *testing.T) { } } -func TestLinksDefinitionsFilePath(t *testing.T) { +func Test_linksDefinitionsFilePath(t *testing.T) { t.Run("env var set and file exists", func(t *testing.T) { repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) @@ -101,7 +101,7 @@ func TestLinksDefinitionsFilePath(t *testing.T) { require.NoError(t, createLinksFile(defaultFilePath)) // to ensure default file is ignored t.Setenv(linksMapFilePathEnvVar, testFile) - path, err := LinksDefinitionsFilePath(repoRoot) + path, err := linksDefinitionsFilePath(repoRoot) require.NoError(t, err) assert.Equal(t, testFile, path) }) @@ -114,7 +114,7 @@ func TestLinksDefinitionsFilePath(t *testing.T) { missingFile := filepath.Join(repoRoot.Name(), "missing_links.yml") t.Setenv(linksMapFilePathEnvVar, missingFile) - path, err := LinksDefinitionsFilePath(repoRoot) + path, err := linksDefinitionsFilePath(repoRoot) require.Error(t, err) assert.Empty(t, path) }) @@ -127,7 +127,7 @@ func TestLinksDefinitionsFilePath(t *testing.T) { require.NoError(t, createLinksFile(defaultFilePath)) - path, err := LinksDefinitionsFilePath(repoRoot) + path, err := linksDefinitionsFilePath(repoRoot) require.NoError(t, err) assert.Equal(t, defaultFilePath, path) @@ -143,7 +143,7 @@ func TestLinksDefinitionsFilePath(t *testing.T) { _, err = os.Stat(defaultFilePath) require.ErrorIs(t, err, os.ErrNotExist) - path, err := LinksDefinitionsFilePath(repoRoot) + path, err := linksDefinitionsFilePath(repoRoot) require.NoError(t, err) assert.Empty(t, path) }) diff --git a/internal/docs/readme.go b/internal/docs/readme.go index dc906a8d9b..dcc849bc2e 100644 --- a/internal/docs/readme.go +++ b/internal/docs/readme.go @@ -16,7 +16,6 @@ import ( "github.com/elastic/elastic-package/internal/builder" "github.com/elastic/elastic-package/internal/logger" - "github.com/elastic/elastic-package/internal/packages" ) // ReadmeFile contains file name and status of each readme file. @@ -33,10 +32,10 @@ const ( ) // AreReadmesUpToDate function checks if all the .md readme files are up-to-date. -func AreReadmesUpToDate(linksFilePath string) ([]ReadmeFile, error) { - packageRoot, err := packages.MustFindPackageRoot() +func AreReadmesUpToDate(repoRoot *os.Root, packageRoot string) ([]ReadmeFile, error) { + linksFilePath, err := linksDefinitionsFilePath(repoRoot) if err != nil { - return nil, fmt.Errorf("package root not found: %w", err) + return nil, fmt.Errorf("locating links file failed: %w", err) } files, err := filepath.Glob(filepath.Join(packageRoot, "_dev", "build", "docs", "*.md")) @@ -99,7 +98,12 @@ func isReadmeUpToDate(fileName, linksFilePath, packageRoot string) (bool, string // UpdateReadmes function updates all .md readme files using a defined template // files. The function doesn't perform any action if the template file is not present. -func UpdateReadmes(linksFilePath, packageRoot, buildDir string) ([]string, error) { +func UpdateReadmes(repoRoot *os.Root, packageRoot, buildDir string) ([]string, error) { + linksFilePath, err := linksDefinitionsFilePath(repoRoot) + if err != nil { + return nil, fmt.Errorf("locating links file failed: %w", err) + } + readmeFiles, err := filepath.Glob(filepath.Join(packageRoot, "_dev", "build", "docs", "*.md")) if err != nil { return nil, fmt.Errorf("reading directory entries failed: %w", err) diff --git a/internal/packages/archetype/data_stream_test.go b/internal/packages/archetype/data_stream_test.go index 8dc35c32d0..f314957b21 100644 --- a/internal/packages/archetype/data_stream_test.go +++ b/internal/packages/archetype/data_stream_test.go @@ -77,7 +77,6 @@ func createDataStreamDescriptorForTest() DataStreamDescriptor { } func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamDescriptor, valid bool, repoRoot *os.Root) { - linksFilePath := "" packagesDir := filepath.Join(repoRoot.Name(), "packages") err := os.MkdirAll(packagesDir, 0o755) @@ -92,5 +91,5 @@ func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamD err = CreateDataStream(dd) require.NoError(t, err) - checkPackage(t, repoRoot, linksFilePath, packageRoot, valid) + checkPackage(t, repoRoot, packageRoot, valid) } diff --git a/internal/packages/archetype/package_test.go b/internal/packages/archetype/package_test.go index 93103c1068..caf85f7554 100644 --- a/internal/packages/archetype/package_test.go +++ b/internal/packages/archetype/package_test.go @@ -42,12 +42,11 @@ func createAndCheckPackage(t *testing.T, pd PackageDescriptor, valid bool) { require.NoError(t, err) defer repoRoot.Close() - linksFilePath := "" packagesDir := filepath.Join(repoRoot.Name(), "packages") err = createPackageInDir(pd, packagesDir) require.NoError(t, err) - checkPackage(t, repoRoot, linksFilePath, filepath.Join(packagesDir, pd.Manifest.Name), valid) + checkPackage(t, repoRoot, filepath.Join(packagesDir, pd.Manifest.Name), valid) } func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDescriptor { @@ -97,11 +96,11 @@ func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDe } } -func buildPackage(t *testing.T, repoRoot *os.Root, linksFilePath, packageRoot string) error { +func buildPackage(t *testing.T, repoRoot *os.Root, packageRoot string) error { buildDir := filepath.Join(repoRoot.Name(), "build") err := os.MkdirAll(buildDir, 0o755) require.NoError(t, err) - _, err = docs.UpdateReadmes(linksFilePath, packageRoot, buildDir) + _, err = docs.UpdateReadmes(repoRoot, packageRoot, buildDir) if err != nil { return err } @@ -114,8 +113,8 @@ func buildPackage(t *testing.T, repoRoot *os.Root, linksFilePath, packageRoot st return err } -func checkPackage(t *testing.T, repoRoot *os.Root, linksFilePath, packageRoot string, valid bool) { - err := buildPackage(t, repoRoot, linksFilePath, packageRoot) +func checkPackage(t *testing.T, repoRoot *os.Root, packageRoot string, valid bool) { + err := buildPackage(t, repoRoot, packageRoot) if !valid { assert.Error(t, err) return From 9229664f981fd4439eba3c3c4535a515e9a4e5f1 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 11:23:03 +0200 Subject: [PATCH 22/46] add validation for RepoRoot in Options --- internal/packages/installer/factory.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/packages/installer/factory.go b/internal/packages/installer/factory.go index 2a9daa6580..f9008162d2 100644 --- a/internal/packages/installer/factory.go +++ b/internal/packages/installer/factory.go @@ -35,10 +35,10 @@ type Installer interface { // Options are the parameters used to build an installer. type Options struct { Kibana *kibana.Client - RootPath string + RootPath string // Root path of the package to be installed. ZipPath string SkipValidation bool - RepoRoot *os.Root + RepoRoot *os.Root // Root of the repository where package source code is located. } // NewForPackage creates a new installer for a package, given its root path, or its prebuilt zip. @@ -53,6 +53,9 @@ func NewForPackage(ctx context.Context, options Options) (Installer, error) { if options.RootPath == "" && options.ZipPath == "" { return nil, errors.New("missing package root path or pre-built zip package") } + if options.RepoRoot == nil { + return nil, errors.New("missing repo root") + } version, err := kibanaVersion(options.Kibana) if err != nil { From 0436a378e6623807a9afa012ad17f24184c74ae1 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 11:24:47 +0200 Subject: [PATCH 23/46] clarify variable name for included file path in newLinkedFile function --- internal/files/linkedfiles.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index 3d8879722a..cd7f4ebca9 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -224,7 +224,7 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { linkfileChecksum = fields[1] } - // pathname represents the absolute path to the included file which content we want to copy to the link file + // includedFilePath represents the absolute path to the included file which content we want to copy to the link file // resolves the path of the included file relative to the link file includedFilePath := filepath.Clean(filepath.Join(workDir, filepath.FromSlash(includedFileRelPath))) From 9793ffd4e951ead1f476b70b57a095ee05098209 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 11:26:11 +0200 Subject: [PATCH 24/46] simplify checksum comparison logic in newLinkedFile function --- internal/files/linkedfiles.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index cd7f4ebca9..b9093d3477 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -244,8 +244,6 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { return nil, fmt.Errorf("could not collect file %s: %w", includedFilePathRelFromRoot, err) } - checksumUpdated := cs == linkfileChecksum - var includedPackageName string includedPackageRoot, _, _ := packages.FindPackageRootFrom(filepath.Dir(includedFilePath)) if includedPackageRoot != "" { @@ -260,7 +258,7 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { IncludedFilePath: includedFileRelPath, IncludedFileContentsChecksum: cs, IncludedPackageName: includedPackageName, - UpToDate: checksumUpdated, + UpToDate: cs == linkfileChecksum, TargetRelPath: strings.TrimSuffix(linkFileRelativePath, linkExtension), }, nil } From 5d6ef55463ed021203847ec23eedb0a36255d65c Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 11:38:58 +0200 Subject: [PATCH 25/46] refactor findRepositoryLicensePath to use repoRoot --- internal/builder/packages.go | 9 ++++----- internal/builder/packages_test.go | 12 ++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 5afdd7ba02..8b45de7230 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -374,17 +374,16 @@ func createBuildDirectory(dirs ...string) (string, error) { // // Parameters: // -// licensePath - the path to the license file. +// repositoryLicenseTextFileName - the relative path to the license file from the repository root. // // Returns: // -// string - the license file path if found. +// string - the license file absolute path if found. // error - an error if the license file does not exist. func findRepositoryLicensePath(repoRoot *os.Root, repositoryLicenseTextFileName string) (string, error) { - path := filepath.Join(repoRoot.Name(), repositoryLicenseTextFileName) - if _, err := os.Stat(path); err != nil { + if _, err := repoRoot.Stat(repositoryLicenseTextFileName); err != nil { return "", fmt.Errorf("failed to find repository license: %w", err) } - + path := filepath.Join(repoRoot.Name(), repositoryLicenseTextFileName) return path, nil } diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index a131634834..2600a2df64 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -38,6 +38,18 @@ func TestFindRepositoryLicense(t *testing.T) { path, err := findRepositoryLicensePath(root, "NON_EXISTENT_LICENSE.txt") require.Error(t, err) assert.Empty(t, path) + assert.ErrorIs(t, err, os.ErrNotExist) + }) + + t.Run("FileOutsideRoot", func(t *testing.T) { + root, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer root.Close() + + path, err := findRepositoryLicensePath(root, "../../out.txt") + require.Error(t, err) + assert.Empty(t, path) + assert.ErrorContains(t, err, "path escapes from parent") }) } From ecfeb9b5d13a1dfcf0e9e4a67c7ee4a73f1ff2ee Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 14:23:37 +0200 Subject: [PATCH 26/46] refactor copyLicenseTextFile to use repoRoot.Stat --- internal/builder/packages.go | 26 +++++++------ internal/builder/packages_test.go | 65 ++++++++++++++++++------------- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 8b45de7230..69b4abdaf1 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -10,7 +10,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "github.com/magefile/mage/sh" @@ -298,8 +297,22 @@ func signZippedPackage(options BuildOptions, zippedPackagePath string) error { // If the file does not exist in the package, it looks for a license file in the repository root directory. // If a license file is found in the repository, it copies it to the package directory. func copyLicenseTextFile(repoRoot *os.Root, licensePath string) error { + + // check licensePath is within the repository + relPath := filepath.Clean(licensePath) + if filepath.IsAbs(licensePath) { + var err error + relPath, err = filepath.Rel(repoRoot.Name(), licensePath) + if err != nil { + return fmt.Errorf("failed to get relative path for licensePath (%s) from repoRoot (%s): %w", licensePath, repoRoot.Name(), err) + } + } else { + // if relative path, make it relative to the repo root + licensePath = filepath.Join(repoRoot.Name(), licensePath) + } + // if the given path exist, skip copying - info, err := os.Stat(licensePath) + info, err := repoRoot.Stat(relPath) if err == nil && !info.IsDir() { logger.Debug("License file in the package will be used") return nil @@ -313,15 +326,6 @@ func copyLicenseTextFile(repoRoot *os.Root, licensePath string) error { return fmt.Errorf("license path (%s) is a directory", licensePath) } - // Ensure licensePath is inside the repoRoot - rel, err := filepath.Rel(repoRoot.Name(), licensePath) - if err != nil { - return fmt.Errorf("failed to get relative path for licensePath (%s) from repoRoot (%s): %w", licensePath, repoRoot.Name(), err) - } - if strings.HasPrefix(rel, "..") { - return fmt.Errorf("licensePath (%s) is outside of the repoRoot (%s)", licensePath, repoRoot.Name()) - } - // lookup for the license file in the repository // default license name can be overridden by the user repositoryLicenseTextFileName, userDefined := os.LookupEnv(repositoryLicenseEnv) diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index 2600a2df64..81ec729f8d 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -56,35 +56,54 @@ func TestFindRepositoryLicense(t *testing.T) { func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { - t.Run("ExistingFile", func(t *testing.T) { - dir := t.TempDir() - licensePath := filepath.Join(dir, "LICENSE.txt") - err := os.WriteFile(licensePath, []byte("existing license"), 0644) + t.Run("ExistingFile_RelPath", func(t *testing.T) { + + repoRoot, err := os.OpenRoot(t.TempDir()) + require.NoError(t, err) + defer repoRoot.Close() + + licensePathRel := filepath.Join("LICENSE.txt") + err = repoRoot.WriteFile(licensePathRel, []byte("existing license"), 0644) require.NoError(t, err) + // Should not attempt to copy, just return nil + err = copyLicenseTextFile(repoRoot, licensePathRel) + assert.NoError(t, err) + + // License file should remain unchanged + content, err := repoRoot.ReadFile(licensePathRel) + require.NoError(t, err) + assert.Equal(t, "existing license", string(content)) + + }) + + t.Run("ExistingFile_AbsPath", func(t *testing.T) { + repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) defer repoRoot.Close() + licensePathRel := filepath.Join("LICENSE.txt") + err = repoRoot.WriteFile(licensePathRel, []byte("existing license"), 0644) + require.NoError(t, err) + // Should not attempt to copy, just return nil - err = copyLicenseTextFile(repoRoot, licensePath) + err = copyLicenseTextFile(repoRoot, filepath.Join(repoRoot.Name(), licensePathRel)) assert.NoError(t, err) // License file should remain unchanged - content, err := os.ReadFile(licensePath) + content, err := repoRoot.ReadFile(licensePathRel) require.NoError(t, err) assert.Equal(t, "existing license", string(content)) }) t.Run("ExistingDirectory", func(t *testing.T) { - dir := t.TempDir() - repoRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) defer repoRoot.Close() - err = copyLicenseTextFile(repoRoot, dir) + err = copyLicenseTextFile(repoRoot, ".") assert.Error(t, err) assert.Contains(t, err.Error(), "is a directory") }) @@ -108,16 +127,13 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { defer repoRoot.Close() // original license file path - originalFile := filepath.Join(repoRoot.Name(), licenseTextFileName) - err = os.WriteFile(originalFile, []byte("repo license"), 0644) + err = repoRoot.WriteFile(licenseTextFileName, []byte("repo license"), 0644) require.NoError(t, err) - expectedPath := filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt") - - err = copyLicenseTextFile(repoRoot, expectedPath) + err = copyLicenseTextFile(repoRoot, "REPO_LICENSE.txt") assert.NoError(t, err) - content, err := os.ReadFile(expectedPath) + content, err := repoRoot.ReadFile("REPO_LICENSE.txt") require.NoError(t, err) assert.Equal(t, "repo license", string(content)) }) @@ -128,18 +144,15 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { defer repoRoot.Close() // original license file path - originalFile := filepath.Join(repoRoot.Name(), "CUSTOM_LICENSE.txt") - err = os.WriteFile(originalFile, []byte("repo license"), 0644) + err = repoRoot.WriteFile("CUSTOM_LICENSE.txt", []byte("repo license"), 0644) require.NoError(t, err) t.Setenv(repositoryLicenseEnv, "CUSTOM_LICENSE.txt") - expectedPath := filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt") - - err = copyLicenseTextFile(repoRoot, expectedPath) + err = copyLicenseTextFile(repoRoot, "REPO_LICENSE.txt") assert.NoError(t, err) - content, err := os.ReadFile(expectedPath) + content, err := repoRoot.ReadFile("REPO_LICENSE.txt") require.NoError(t, err) assert.Equal(t, "repo license", string(content)) }) @@ -149,15 +162,13 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { require.NoError(t, err) defer repoRoot.Close() - expectedPath := filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt") - - err = copyLicenseTextFile(repoRoot, expectedPath) + err = copyLicenseTextFile(repoRoot, "REPO_LICENSE.txt") assert.NoError(t, err) - _, err = os.Stat(filepath.Join(repoRoot.Name(), "LICENSE.txt")) + _, err = repoRoot.Stat("LICENSE.txt") assert.ErrorIs(t, err, os.ErrNotExist) - _, err = os.Stat(expectedPath) + _, err = repoRoot.Stat("REPO_LICENSE.txt") assert.ErrorIs(t, err, os.ErrNotExist) }) @@ -172,7 +183,7 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { err = copyLicenseTextFile(repoRoot, outsideLicensePath) assert.Error(t, err) - assert.Contains(t, err.Error(), "is outside of the repoRoot") + assert.Contains(t, err.Error(), "path escapes from parent") }) } From 4412b0bd218edc55f235dae41c6b211344654b74 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 15:22:16 +0200 Subject: [PATCH 27/46] refactor FindPackageRoot and handle error on FindPackageRootFrom calls --- cmd/benchmark.go | 21 +++---------- cmd/create_data_stream.go | 8 ++--- cmd/format.go | 6 +--- cmd/install.go | 7 +---- cmd/lint.go | 6 +--- cmd/service.go | 6 +--- cmd/status.go | 8 ++--- cmd/testrunner.go | 25 ++++------------ cmd/uninstall.go | 6 +--- internal/fields/validate.go | 13 +++++---- internal/fields/validate_test.go | 4 +-- internal/files/linkedfiles.go | 16 ++++++++-- internal/files/linkedfiles_test.go | 34 +++++++++++++--------- internal/files/testdata/links/manifest.yml | 2 ++ internal/packages/packages.go | 23 ++++++++------- 15 files changed, 80 insertions(+), 105 deletions(-) create mode 100644 internal/files/testdata/links/manifest.yml diff --git a/cmd/benchmark.go b/cmd/benchmark.go index 7875b91f1e..8b458751a7 100644 --- a/cmd/benchmark.go +++ b/cmd/benchmark.go @@ -137,10 +137,7 @@ func pipelineCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(err, cobraext.BenchNumTopProcsFlagName) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } @@ -292,12 +289,8 @@ func rallyCommandAction(cmd *cobra.Command, args []string) error { } var packageRootPath string - var found bool if len(packageName) == 0 { - packageRootPath, found, err = packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err = packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } @@ -472,10 +465,7 @@ func streamCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(err, cobraext.BenchStreamTimestampFieldFlagName) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } @@ -585,10 +575,7 @@ func systemCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(err, cobraext.BenchReindexToMetricstoreFlagName) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } diff --git a/cmd/create_data_stream.go b/cmd/create_data_stream.go index 01e10e3868..102b7475a4 100644 --- a/cmd/create_data_stream.go +++ b/cmd/create_data_stream.go @@ -37,13 +37,13 @@ type newDataStreamAnswers struct { func createDataStreamCommandAction(cmd *cobra.Command, args []string) error { cmd.Println("Create a new data stream") - packageRoot, found, err := packages.FindPackageRoot() + packageRoot, err := packages.FindPackageRoot() if err != nil { + if errors.Is(err, packages.ErrPackageRootNotFound) { + return errors.New("package root not found, you can only create new data stream in the package context") + } return fmt.Errorf("locating package root failed: %w", err) } - if !found { - return errors.New("package root not found, you can only create new data stream in the package context") - } manifest, err := packages.ReadPackageManifestFromPackageRoot(packageRoot) if err != nil { diff --git a/cmd/format.go b/cmd/format.go index 8a654f790f..8976ac6bb0 100644 --- a/cmd/format.go +++ b/cmd/format.go @@ -5,7 +5,6 @@ package cmd import ( - "errors" "fmt" "github.com/spf13/cobra" @@ -35,13 +34,10 @@ func setupFormatCommand() *cobraext.Command { func formatCommandAction(cmd *cobra.Command, args []string) error { cmd.Println("Format the package") - packageRoot, found, err := packages.FindPackageRoot() + packageRoot, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } - if !found { - return errors.New("package root not found") - } ff, err := cmd.Flags().GetBool(cobraext.FailFastFlagName) if err != nil { diff --git a/cmd/install.go b/cmd/install.go index 171e9b8ea1..998266f4d2 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -5,7 +5,6 @@ package cmd import ( - "errors" "fmt" "github.com/spf13/cobra" @@ -72,12 +71,8 @@ func installCommandAction(cmd *cobra.Command, _ []string) error { } if zipPathFile == "" && packageRootPath == "" { - var found bool var err error - packageRootPath, found, err = packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err = packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } diff --git a/cmd/lint.go b/cmd/lint.go index f34775415a..1254869743 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -5,7 +5,6 @@ package cmd import ( - "errors" "fmt" "github.com/spf13/cobra" @@ -74,10 +73,7 @@ func lintCommandAction(cmd *cobra.Command, args []string) error { } func validateSourceCommandAction(cmd *cobra.Command, args []string) error { - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } diff --git a/cmd/service.go b/cmd/service.go index ed542838ce..76c4ee7723 100644 --- a/cmd/service.go +++ b/cmd/service.go @@ -5,7 +5,6 @@ package cmd import ( - "errors" "fmt" "path/filepath" @@ -47,13 +46,10 @@ func setupServiceCommand() *cobraext.Command { func upCommandAction(cmd *cobra.Command, args []string) error { cmd.Println("Boot up the service stack") - packageRoot, found, err := packages.FindPackageRoot() + packageRoot, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } - if !found { - return errors.New("package root not found") - } var dataStreamPath string dataStreamFlag, _ := cmd.Flags().GetString(cobraext.DataStreamFlagName) diff --git a/cmd/status.go b/cmd/status.go index 0b87da5ee7..1d4c7cf8ba 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -163,11 +163,11 @@ func getPackageStatus(packageName string, options registry.SearchOptions) (*stat if packageName != "" { return status.RemotePackage(packageName, options) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return nil, errors.New("no package specified and package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { + if errors.Is(err, packages.ErrPackageRootNotFound) { + return nil, errors.New("no package specified and package root not found") + } return nil, fmt.Errorf("locating package root failed: %w", err) } return status.LocalPackage(packageRootPath, options) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index 37c6582695..a39b1379dd 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -146,10 +146,7 @@ func testRunnerAssetCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(fmt.Errorf("coverage format not available: %s", testCoverageFormat), cobraext.TestCoverageFormatFlagName) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } @@ -242,10 +239,7 @@ func testRunnerStaticCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(fmt.Errorf("coverage format not available: %s", testCoverageFormat), cobraext.TestCoverageFormatFlagName) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } @@ -349,10 +343,7 @@ func testRunnerPipelineCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(err, cobraext.DeferCleanupFlagName) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } @@ -493,10 +484,7 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(err, cobraext.VariantFlagName) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } @@ -664,10 +652,7 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(fmt.Errorf("coverage format not available: %s", testCoverageFormat), cobraext.TestCoverageFormatFlagName) } - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } diff --git a/cmd/uninstall.go b/cmd/uninstall.go index 1624d93c30..01075d3922 100644 --- a/cmd/uninstall.go +++ b/cmd/uninstall.go @@ -5,7 +5,6 @@ package cmd import ( - "errors" "fmt" "github.com/spf13/cobra" @@ -35,10 +34,7 @@ func setupUninstallCommand() *cobraext.Command { } func uninstallCommandAction(cmd *cobra.Command, args []string) error { - packageRootPath, found, err := packages.FindPackageRoot() - if !found { - return errors.New("package root not found") - } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) } diff --git a/internal/fields/validate.go b/internal/fields/validate.go index 95563ec641..8cce1a1f01 100644 --- a/internal/fields/validate.go +++ b/internal/fields/validate.go @@ -257,12 +257,12 @@ func WithOTELValidation(otelValidation bool) ValidatorOption { } type packageRootFinder interface { - FindPackageRoot() (string, bool, error) + FindPackageRoot() (string, error) } type packageRoot struct{} -func (p packageRoot) FindPackageRoot() (string, bool, error) { +func (p packageRoot) FindPackageRoot() (string, error) { return packages.FindPackageRoot() } @@ -288,13 +288,14 @@ func createValidatorForDirectoryAndPackageRoot(fieldsParentDir string, finder pa var fdm *DependencyManager if !v.disabledDependencyManagement { - packageRoot, found, err := finder.FindPackageRoot() + packageRoot, err := finder.FindPackageRoot() if err != nil { + if errors.Is(err, packages.ErrPackageRootNotFound) { + return nil, errors.New("package root not found and dependency management is enabled") + } return nil, fmt.Errorf("can't find package root: %w", err) } - if !found { - return nil, errors.New("package root not found and dependency management is enabled") - } + fdm, v.Schema, err = initDependencyManagement(packageRoot, v.specVersion, v.enabledImportAllECSSchema) if err != nil { return nil, fmt.Errorf("failed to initialize dependency management: %w", err) diff --git a/internal/fields/validate_test.go b/internal/fields/validate_test.go index 5f3a9a4b10..dcbf5c5e77 100644 --- a/internal/fields/validate_test.go +++ b/internal/fields/validate_test.go @@ -28,8 +28,8 @@ type packageRootTestFinder struct { packageRootPath string } -func (p packageRootTestFinder) FindPackageRoot() (string, bool, error) { - return p.packageRootPath, true, nil +func (p packageRootTestFinder) FindPackageRoot() (string, error) { + return p.packageRootPath, nil } func TestValidate_NoWildcardFields(t *testing.T) { diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index b9093d3477..37b0447573 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -193,7 +193,10 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { workDir := filepath.Dir(linkFilePath) var linkPackageName string - linkPackageRoot, _, _ := packages.FindPackageRootFrom(workDir) + linkPackageRoot, err := packages.FindPackageRootFrom(workDir) + if err != nil { + return nil, fmt.Errorf("could not find package root for link file %s: %w", linkFilePath, err) + } if linkPackageRoot != "" { linkPackageName = filepath.Base(linkPackageRoot) } else { @@ -245,7 +248,10 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { } var includedPackageName string - includedPackageRoot, _, _ := packages.FindPackageRootFrom(filepath.Dir(includedFilePath)) + includedPackageRoot, err := packages.FindPackageRootFrom(filepath.Dir(includedFilePath)) + if err != nil { + return nil, fmt.Errorf("could not find package root for included file %s: %w", includedFilePath, err) + } if includedPackageRoot != "" { includedPackageName = filepath.Base(includedPackageRoot) } @@ -471,7 +477,11 @@ func linkedFilesByPackageFrom(root *os.Root, fromDir string) ([]PackageLinks, er } var packageName string - if packageRoot, _, _ := packages.FindPackageRootFrom(filepath.Join(root.Name(), fromDir)); packageRoot != "" { + packageRoot, err := packages.FindPackageRootFrom(filepath.Join(root.Name(), fromDir)) + if err != nil { + return nil, fmt.Errorf("finding package root failed: %w", err) + } + if packageRoot != "" { packageName = filepath.Base(packageRoot) } byPackageMap := map[string][]string{} diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index 46de56c934..deee6bec5a 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -498,6 +498,19 @@ func TestNewLinkedFileRejectsPathTraversal(t *testing.T) { } } +func createPackageStructure(t *testing.T, dirs ...string) string { + + packageDir := filepath.Join(dirs...) + err := os.MkdirAll(packageDir, 0755) + require.NoError(t, err) + + manifestFile := filepath.Join(packageDir, "manifest.yml") + err = os.WriteFile(manifestFile, []byte("version: '1.0'\ntype: integration\n"), 0644) + require.NoError(t, err) + + return packageDir +} + func TestLinksFSSecurityIsolation(t *testing.T) { tempDir := t.TempDir() @@ -506,10 +519,7 @@ func TestLinksFSSecurityIsolation(t *testing.T) { err := os.MkdirAll(repoDir, 0755) require.NoError(t, err) - // Create a working directory inside repo - workDir := filepath.Join(repoDir, "work") - err = os.MkdirAll(workDir, 0755) - require.NoError(t, err) + workDir := createPackageStructure(t, repoDir, "testpackage") // Create a valid included file in the repo includedFile := filepath.Join(workDir, "included.txt") @@ -567,9 +577,7 @@ func TestLinksFS_Open(t *testing.T) { err := os.MkdirAll(repoDir, 0755) require.NoError(t, err) - workDir := filepath.Join(repoDir, "work") - err = os.MkdirAll(workDir, 0755) - require.NoError(t, err) + workDir := createPackageStructure(t, repoDir, "work") // Create test files regularFileRel := "regular.txt" @@ -577,6 +585,8 @@ func TestLinksFS_Open(t *testing.T) { err = os.WriteFile(regularFileAbs, []byte("regular content"), 0644) require.NoError(t, err) + createPackageStructure(t, repoDir, "other") + includedFileAbs := filepath.Join(repoDir, "other", "included.txt") err = os.MkdirAll(filepath.Dir(includedFileAbs), 0755) require.NoError(t, err) @@ -584,6 +594,8 @@ func TestLinksFS_Open(t *testing.T) { err = os.WriteFile(includedFileAbs, []byte(includedContent), 0644) require.NoError(t, err) + createPackageStructure(t, repoDir, "packages", "packageB") + // Create link file with correct checksum linkFileRel := filepath.Join("packages", "packageB", "linked.txt.link") linkFileAbs := filepath.Join(workDir, linkFileRel) @@ -691,9 +703,7 @@ func TestLinksFS_RelativeWorkDir(t *testing.T) { err := os.MkdirAll(repoDir, 0755) require.NoError(t, err) - workDir := filepath.Join(repoDir, "work") - err = os.MkdirAll(workDir, 0755) - require.NoError(t, err) + workDir := createPackageStructure(t, repoDir, "work") // Create test files regularFile := filepath.Join(workDir, "regular.txt") @@ -770,9 +780,7 @@ func TestLinksFS_ErrorConditions(t *testing.T) { err := os.MkdirAll(repoDir, 0755) require.NoError(t, err) - workDir := filepath.Join(repoDir, "work") - err = os.MkdirAll(workDir, 0755) - require.NoError(t, err) + workDir := createPackageStructure(t, repoDir, "work") // Create link file that points to non-existent file (this will fail security check) brokenLinkFile := filepath.Join(workDir, "broken.txt.link") diff --git a/internal/files/testdata/links/manifest.yml b/internal/files/testdata/links/manifest.yml new file mode 100644 index 0000000000..852577ae19 --- /dev/null +++ b/internal/files/testdata/links/manifest.yml @@ -0,0 +1,2 @@ +version: '1.0' +type: integration \ No newline at end of file diff --git a/internal/packages/packages.go b/internal/packages/packages.go index 3d4e60e249..1feddfc12f 100644 --- a/internal/packages/packages.go +++ b/internal/packages/packages.go @@ -281,27 +281,30 @@ func (t *Transform) HasSource(name string) (bool, error) { // MustFindPackageRoot finds and returns the path to the root folder of a package. // It fails with an error if the package root can't be found. func MustFindPackageRoot() (string, error) { - root, found, err := FindPackageRoot() + root, err := FindPackageRoot() if err != nil { return "", fmt.Errorf("locating package root failed: %w", err) } - if !found { - return "", errors.New("package root not found") - } return root, nil } // FindPackageRoot finds and returns the path to the root folder of a package from the working directory. -func FindPackageRoot() (string, bool, error) { +func FindPackageRoot() (string, error) { workDir, err := os.Getwd() if err != nil { - return "", false, fmt.Errorf("locating working directory failed: %w", err) + return "", fmt.Errorf("locating working directory failed: %w", err) } return FindPackageRootFrom(workDir) } +var ( + ErrPackageRootNotFound = errors.New("package root not found") +) + // FindPackageRootFrom finds and returns the path to the root folder of a package from a given directory. -func FindPackageRootFrom(fromDir string) (string, bool, error) { +// +// - fromDir should be an absolute path to a directory. +func FindPackageRootFrom(fromDir string) (string, error) { // VolumeName() will return something like "C:" in Windows, and "" in other OSs // rootDir will be something like "C:\" in Windows, and "/" everywhere else. rootDir := filepath.VolumeName(fromDir) + string(filepath.Separator) @@ -313,10 +316,10 @@ func FindPackageRootFrom(fromDir string) (string, bool, error) { if err == nil && !fileInfo.IsDir() { ok, err := isPackageManifest(path) if err != nil { - return "", false, fmt.Errorf("verifying manifest file failed (path: %s): %w", path, err) + return "", fmt.Errorf("verifying manifest file failed (path: %s): %w", path, err) } if ok { - return dir, true, nil + return dir, nil } } @@ -325,7 +328,7 @@ func FindPackageRootFrom(fromDir string) (string, bool, error) { } dir = filepath.Dir(dir) } - return "", false, nil + return "", ErrPackageRootNotFound } // FindDataStreamRootForPath finds and returns the path to the root folder of a data stream. From 9b7a7212fba184caf2f7ca59f1fb6934e9178a3e Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 9 Oct 2025 15:41:44 +0200 Subject: [PATCH 28/46] remove repoRoot.Write and Read --- internal/builder/packages_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index 81ec729f8d..2640103c33 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -63,7 +63,7 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { defer repoRoot.Close() licensePathRel := filepath.Join("LICENSE.txt") - err = repoRoot.WriteFile(licensePathRel, []byte("existing license"), 0644) + err = os.WriteFile(filepath.Join(repoRoot.Name(), licensePathRel), []byte("existing license"), 0644) require.NoError(t, err) // Should not attempt to copy, just return nil @@ -71,7 +71,7 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { assert.NoError(t, err) // License file should remain unchanged - content, err := repoRoot.ReadFile(licensePathRel) + content, err := os.ReadFile(filepath.Join(repoRoot.Name(), licensePathRel)) require.NoError(t, err) assert.Equal(t, "existing license", string(content)) @@ -83,16 +83,16 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { require.NoError(t, err) defer repoRoot.Close() - licensePathRel := filepath.Join("LICENSE.txt") - err = repoRoot.WriteFile(licensePathRel, []byte("existing license"), 0644) + licensePath := filepath.Join(repoRoot.Name(), "LICENSE.txt") + err = os.WriteFile(licensePath, []byte("existing license"), 0644) require.NoError(t, err) // Should not attempt to copy, just return nil - err = copyLicenseTextFile(repoRoot, filepath.Join(repoRoot.Name(), licensePathRel)) + err = copyLicenseTextFile(repoRoot, licensePath) assert.NoError(t, err) // License file should remain unchanged - content, err := repoRoot.ReadFile(licensePathRel) + content, err := os.ReadFile(licensePath) require.NoError(t, err) assert.Equal(t, "existing license", string(content)) @@ -127,13 +127,13 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { defer repoRoot.Close() // original license file path - err = repoRoot.WriteFile(licenseTextFileName, []byte("repo license"), 0644) + err = os.WriteFile(filepath.Join(repoRoot.Name(), licenseTextFileName), []byte("repo license"), 0644) require.NoError(t, err) err = copyLicenseTextFile(repoRoot, "REPO_LICENSE.txt") assert.NoError(t, err) - content, err := repoRoot.ReadFile("REPO_LICENSE.txt") + content, err := os.ReadFile(filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt")) require.NoError(t, err) assert.Equal(t, "repo license", string(content)) }) @@ -144,7 +144,7 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { defer repoRoot.Close() // original license file path - err = repoRoot.WriteFile("CUSTOM_LICENSE.txt", []byte("repo license"), 0644) + err = os.WriteFile(filepath.Join(repoRoot.Name(), "CUSTOM_LICENSE.txt"), []byte("repo license"), 0644) require.NoError(t, err) t.Setenv(repositoryLicenseEnv, "CUSTOM_LICENSE.txt") @@ -152,7 +152,7 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { err = copyLicenseTextFile(repoRoot, "REPO_LICENSE.txt") assert.NoError(t, err) - content, err := repoRoot.ReadFile("REPO_LICENSE.txt") + content, err := os.ReadFile(filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt")) require.NoError(t, err) assert.Equal(t, "repo license", string(content)) }) From 66274da9e89f9f0169377ae49297290d92d86dfa Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 10:40:21 +0200 Subject: [PATCH 29/46] rename repoRoot to repositoryRoot for clarity --- cmd/benchmark.go | 8 +- cmd/build.go | 8 +- cmd/install.go | 4 +- cmd/links.go | 18 ++-- cmd/lint.go | 6 +- cmd/testrunner.go | 12 +-- internal/benchrunner/runners/rally/options.go | 6 +- internal/benchrunner/runners/rally/runner.go | 2 +- .../benchrunner/runners/stream/options.go | 6 +- internal/benchrunner/runners/stream/runner.go | 2 +- internal/builder/packages.go | 28 +++--- internal/builder/packages_test.go | 68 +++++++------- internal/docs/links_map.go | 6 +- internal/docs/links_map_test.go | 34 +++---- internal/docs/readme.go | 8 +- internal/files/linkedfiles.go | 92 +++++++++---------- internal/files/linkedfiles_test.go | 26 +++--- .../packages/archetype/data_stream_test.go | 24 ++--- internal/packages/archetype/package_test.go | 24 ++--- internal/packages/installer/factory.go | 6 +- internal/resources/fleetpackage.go | 6 +- internal/resources/fleetpackage_test.go | 28 +++--- internal/resources/fleetpolicy.go | 2 +- internal/resources/fleetpolicy_test.go | 28 +++--- internal/testrunner/runners/asset/runner.go | 8 +- internal/testrunner/runners/asset/tester.go | 14 +-- internal/testrunner/runners/policy/runner.go | 12 +-- internal/testrunner/runners/policy/tester.go | 8 +- internal/testrunner/runners/system/runner.go | 14 +-- 29 files changed, 254 insertions(+), 254 deletions(-) diff --git a/cmd/benchmark.go b/cmd/benchmark.go index 8b458751a7..f44735d1e8 100644 --- a/cmd/benchmark.go +++ b/cmd/benchmark.go @@ -296,7 +296,7 @@ func rallyCommandAction(cmd *cobra.Command, args []string) error { } } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } @@ -335,7 +335,7 @@ func rallyCommandAction(cmd *cobra.Command, args []string) error { rally.WithRallyDryRun(rallyDryRun), rally.WithRallyPackageFromRegistry(packageName, packageVersion), rally.WithRallyCorpusAtPath(corpusAtPath), - rally.WithRepoRoot(repoRoot), + rally.WithRepositoryRoot(repositoryRoot), } esMetricsClient, err := initializeESMetricsClient(ctx) @@ -470,7 +470,7 @@ func streamCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("locating package root failed: %w", err) } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } @@ -509,7 +509,7 @@ func streamCommandAction(cmd *cobra.Command, args []string) error { stream.WithESAPI(esClient.API), stream.WithKibanaClient(kc), stream.WithProfile(profile), - stream.WithRepoRoot(repoRoot), + stream.WithRepositoryRoot(repositoryRoot), } runner := stream.NewStreamBenchmark(stream.NewOptions(withOpts...)) diff --git a/cmd/build.go b/cmd/build.go index 1d70a4fc8f..f702426fd5 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -61,11 +61,11 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } - defer repoRoot.Close() + defer repositoryRoot.Close() packageRoot, err := packages.MustFindPackageRoot() if err != nil { @@ -78,7 +78,7 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } logger.Debugf("Use build directory: %s", buildDir) - targets, err := docs.UpdateReadmes(repoRoot, packageRoot, buildDir) + targets, err := docs.UpdateReadmes(repositoryRoot, packageRoot, buildDir) if err != nil { return fmt.Errorf("updating files failed: %w", err) } @@ -94,7 +94,7 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { CreateZip: createZip, SignPackage: signPackage, SkipValidation: skipValidation, - RepoRoot: repoRoot, + RepositoryRoot: repositoryRoot, }) if err != nil { return fmt.Errorf("building package failed: %w", err) diff --git a/cmd/install.go b/cmd/install.go index 998266f4d2..cb2f4d1e43 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -78,7 +78,7 @@ func installCommandAction(cmd *cobra.Command, _ []string) error { } } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } @@ -88,7 +88,7 @@ func installCommandAction(cmd *cobra.Command, _ []string) error { RootPath: packageRootPath, SkipValidation: skipValidation, ZipPath: zipPathFile, - RepoRoot: repoRoot, + RepositoryRoot: repositoryRoot, }) if err != nil { return fmt.Errorf("package installation failed: %w", err) diff --git a/cmd/links.go b/cmd/links.go index ad2c9f7762..4315c8ba22 100644 --- a/cmd/links.go +++ b/cmd/links.go @@ -56,13 +56,13 @@ func linksCheckCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("reading current working directory failed: %w", err) } // Find the repository root to create the links filesystem reference tied to the repository root - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("finding repository root: %w", err) } - defer repoRoot.Close() + defer repositoryRoot.Close() - linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) + linksFS, err := files.CreateLinksFSFromPath(repositoryRoot, pwd) if err != nil { return fmt.Errorf("creating links filesystem failed: %w", err) } @@ -101,13 +101,13 @@ func linksUpdateCommandAction(cmd *cobra.Command, args []string) error { } // Find the repository root to create the links filesystem reference tied to the repository root - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("finding repository root: %w", err) } - defer repoRoot.Close() + defer repositoryRoot.Close() - linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) + linksFS, err := files.CreateLinksFSFromPath(repositoryRoot, pwd) if err != nil { return fmt.Errorf("creating links filesystem failed: %w", err) } @@ -148,13 +148,13 @@ func linksListCommandAction(cmd *cobra.Command, args []string) error { } // Find the repository root to create the links filesystem reference tied to the repository root - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("finding repository root: %w", err) } - defer repoRoot.Close() + defer repositoryRoot.Close() - linksFS, err := files.CreateLinksFSFromPath(repoRoot, pwd) + linksFS, err := files.CreateLinksFSFromPath(repositoryRoot, pwd) if err != nil { return fmt.Errorf("creating links filesystem failed: %w", err) } diff --git a/cmd/lint.go b/cmd/lint.go index 1254869743..a0759752f7 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -46,18 +46,18 @@ func setupLintCommand() *cobraext.Command { func lintCommandAction(cmd *cobra.Command, args []string) error { cmd.Println("Lint the package") - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } - defer repoRoot.Close() + defer repositoryRoot.Close() packageRoot, err := packages.MustFindPackageRoot() if err != nil { return fmt.Errorf("package root not found: %w", err) } - readmeFiles, err := docs.AreReadmesUpToDate(repoRoot, packageRoot) + readmeFiles, err := docs.AreReadmesUpToDate(repositoryRoot, packageRoot) if err != nil { for _, f := range readmeFiles { if !f.UpToDate { diff --git a/cmd/testrunner.go b/cmd/testrunner.go index a39b1379dd..789900300a 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -151,7 +151,7 @@ func testRunnerAssetCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("locating package root failed: %w", err) } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } @@ -180,7 +180,7 @@ func testRunnerAssetCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.Asset, WithCoverage: testCoverage, CoverageType: testCoverageFormat, - RepoRoot: repoRoot, + RepositoryRoot: repositoryRoot, }) results, err := testrunner.RunSuite(ctx, runner) @@ -489,7 +489,7 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("locating package root failed: %w", err) } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } @@ -578,7 +578,7 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.System, WithCoverage: testCoverage, CoverageType: testCoverageFormat, - RepoRoot: repoRoot, + RepositoryRoot: repositoryRoot, }) logger.Debugf("Running suite...") @@ -656,7 +656,7 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("locating package root failed: %w", err) } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() if err != nil { return fmt.Errorf("locating repository root failed: %w", err) } @@ -693,7 +693,7 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.Policy, WithCoverage: testCoverage, CoverageType: testCoverageFormat, - RepoRoot: repoRoot, + RepositoryRoot: repositoryRoot, }) results, err := testrunner.RunSuite(ctx, runner) diff --git a/internal/benchrunner/runners/rally/options.go b/internal/benchrunner/runners/rally/options.go index 0225af7cd0..769730e844 100644 --- a/internal/benchrunner/runners/rally/options.go +++ b/internal/benchrunner/runners/rally/options.go @@ -30,7 +30,7 @@ type Options struct { PackageName string PackageVersion string CorpusAtPath string - RepoRoot *os.Root + RepositoryRoot *os.Root } type ClientOptions struct { @@ -121,8 +121,8 @@ func WithRallyCorpusAtPath(c string) OptionFunc { } } -func WithRepoRoot(r *os.Root) OptionFunc { +func WithRepositoryRoot(r *os.Root) OptionFunc { return func(opts *Options) { - opts.RepoRoot = r + opts.RepositoryRoot = r } } diff --git a/internal/benchrunner/runners/rally/runner.go b/internal/benchrunner/runners/rally/runner.go index 601f50ab91..f6262b5175 100644 --- a/internal/benchrunner/runners/rally/runner.go +++ b/internal/benchrunner/runners/rally/runner.go @@ -489,7 +489,7 @@ func (r *runner) installPackageFromPackageRoot(ctx context.Context) error { Kibana: r.options.KibanaClient, RootPath: r.options.PackageRootPath, SkipValidation: true, - RepoRoot: r.options.RepoRoot, + RepositoryRoot: r.options.RepositoryRoot, }) if err != nil { return fmt.Errorf("failed to initialize package installer: %w", err) diff --git a/internal/benchrunner/runners/stream/options.go b/internal/benchrunner/runners/stream/options.go index 2c257fc6dc..f554021e1f 100644 --- a/internal/benchrunner/runners/stream/options.go +++ b/internal/benchrunner/runners/stream/options.go @@ -26,7 +26,7 @@ type Options struct { PackageRootPath string Variant string Profile *profile.Profile - RepoRoot *os.Root + RepositoryRoot *os.Root } type ClientOptions struct { @@ -110,8 +110,8 @@ func WithTimestampField(t string) OptionFunc { } } -func WithRepoRoot(r *os.Root) OptionFunc { +func WithRepositoryRoot(r *os.Root) OptionFunc { return func(opts *Options) { - opts.RepoRoot = r + opts.RepositoryRoot = r } } diff --git a/internal/benchrunner/runners/stream/runner.go b/internal/benchrunner/runners/stream/runner.go index dea617480b..a5fbf6050b 100644 --- a/internal/benchrunner/runners/stream/runner.go +++ b/internal/benchrunner/runners/stream/runner.go @@ -256,7 +256,7 @@ func (r *runner) installPackageFromPackageRoot(ctx context.Context) error { Kibana: r.options.KibanaClient, RootPath: r.options.PackageRootPath, SkipValidation: true, - RepoRoot: r.options.RepoRoot, + RepositoryRoot: r.options.RepositoryRoot, }) if err != nil { diff --git a/internal/builder/packages.go b/internal/builder/packages.go index f20485556f..6dd8e9073f 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -26,9 +26,9 @@ const licenseTextFileName = "LICENSE.txt" var repositoryLicenseEnv = environment.WithElasticPackagePrefix("REPOSITORY_LICENSE") type BuildOptions struct { - PackageRoot string - BuildDir string - RepoRoot *os.Root + PackageRoot string + BuildDir string + RepositoryRoot *os.Root CreateZip bool SignPackage bool @@ -184,7 +184,7 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { logger.Debug("Copy license file if needed") destinationLicenseFilePath := filepath.Join(destinationDir, licenseTextFileName) - err = copyLicenseTextFile(options.RepoRoot, destinationLicenseFilePath) + err = copyLicenseTextFile(options.RepositoryRoot, destinationLicenseFilePath) if err != nil { return "", fmt.Errorf("copying license text file: %w", err) } @@ -207,7 +207,7 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { } logger.Debug("Include linked files") - linksFS, err := files.CreateLinksFSFromPath(options.RepoRoot, options.PackageRoot) + linksFS, err := files.CreateLinksFSFromPath(options.RepositoryRoot, options.PackageRoot) if err != nil { return "", fmt.Errorf("creating links filesystem failed: %w", err) } @@ -301,23 +301,23 @@ func signZippedPackage(options BuildOptions, zippedPackagePath string) error { // If the file already exists in the package, it does nothing. // If the file does not exist in the package, it looks for a license file in the repository root directory. // If a license file is found in the repository, it copies it to the package directory. -func copyLicenseTextFile(repoRoot *os.Root, licensePath string) error { +func copyLicenseTextFile(repositoryRoot *os.Root, licensePath string) error { // check licensePath is within the repository relPath := filepath.Clean(licensePath) if filepath.IsAbs(licensePath) { var err error - relPath, err = filepath.Rel(repoRoot.Name(), licensePath) + relPath, err = filepath.Rel(repositoryRoot.Name(), licensePath) if err != nil { - return fmt.Errorf("failed to get relative path for licensePath (%s) from repoRoot (%s): %w", licensePath, repoRoot.Name(), err) + return fmt.Errorf("failed to get relative path for licensePath (%s) from repositoryRoot (%s): %w", licensePath, repositoryRoot.Name(), err) } } else { // if relative path, make it relative to the repo root - licensePath = filepath.Join(repoRoot.Name(), licensePath) + licensePath = filepath.Join(repositoryRoot.Name(), licensePath) } // if the given path exist, skip copying - info, err := repoRoot.Stat(relPath) + info, err := repositoryRoot.Stat(relPath) if err == nil && !info.IsDir() { logger.Debug("License file in the package will be used") return nil @@ -338,7 +338,7 @@ func copyLicenseTextFile(repoRoot *os.Root, licensePath string) error { repositoryLicenseTextFileName = licenseTextFileName } - sourceLicensePath, err := findRepositoryLicensePath(repoRoot, repositoryLicenseTextFileName) + sourceLicensePath, err := findRepositoryLicensePath(repositoryRoot, repositoryLicenseTextFileName) if !userDefined && errors.Is(err, os.ErrNotExist) { logger.Debug("No license text file is included in package") return nil @@ -389,10 +389,10 @@ func createBuildDirectory(dirs ...string) (string, error) { // // string - the license file absolute path if found. // error - an error if the license file does not exist. -func findRepositoryLicensePath(repoRoot *os.Root, repositoryLicenseTextFileName string) (string, error) { - if _, err := repoRoot.Stat(repositoryLicenseTextFileName); err != nil { +func findRepositoryLicensePath(repositoryRoot *os.Root, repositoryLicenseTextFileName string) (string, error) { + if _, err := repositoryRoot.Stat(repositoryLicenseTextFileName); err != nil { return "", fmt.Errorf("failed to find repository license: %w", err) } - path := filepath.Join(repoRoot.Name(), repositoryLicenseTextFileName) + path := filepath.Join(repositoryRoot.Name(), repositoryLicenseTextFileName) return path, nil } diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index 2640103c33..50e356e8ec 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -58,20 +58,20 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { t.Run("ExistingFile_RelPath", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() licensePathRel := filepath.Join("LICENSE.txt") - err = os.WriteFile(filepath.Join(repoRoot.Name(), licensePathRel), []byte("existing license"), 0644) + err = os.WriteFile(filepath.Join(repositoryRoot.Name(), licensePathRel), []byte("existing license"), 0644) require.NoError(t, err) // Should not attempt to copy, just return nil - err = copyLicenseTextFile(repoRoot, licensePathRel) + err = copyLicenseTextFile(repositoryRoot, licensePathRel) assert.NoError(t, err) // License file should remain unchanged - content, err := os.ReadFile(filepath.Join(repoRoot.Name(), licensePathRel)) + content, err := os.ReadFile(filepath.Join(repositoryRoot.Name(), licensePathRel)) require.NoError(t, err) assert.Equal(t, "existing license", string(content)) @@ -79,16 +79,16 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { t.Run("ExistingFile_AbsPath", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() - licensePath := filepath.Join(repoRoot.Name(), "LICENSE.txt") + licensePath := filepath.Join(repositoryRoot.Name(), "LICENSE.txt") err = os.WriteFile(licensePath, []byte("existing license"), 0644) require.NoError(t, err) // Should not attempt to copy, just return nil - err = copyLicenseTextFile(repoRoot, licensePath) + err = copyLicenseTextFile(repositoryRoot, licensePath) assert.NoError(t, err) // License file should remain unchanged @@ -99,11 +99,11 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { }) t.Run("ExistingDirectory", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() - err = copyLicenseTextFile(repoRoot, ".") + err = copyLicenseTextFile(repositoryRoot, ".") assert.Error(t, err) assert.Contains(t, err.Error(), "is a directory") }) @@ -112,76 +112,76 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { // Using a path that is likely invalid to trigger a stat error invalidPath := string([]byte{0}) - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() - err = copyLicenseTextFile(repoRoot, invalidPath) + err = copyLicenseTextFile(repositoryRoot, invalidPath) assert.Error(t, err) assert.Contains(t, err.Error(), "can't check license path") }) t.Run("RepoLicenseDefaultFileName", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() // original license file path - err = os.WriteFile(filepath.Join(repoRoot.Name(), licenseTextFileName), []byte("repo license"), 0644) + err = os.WriteFile(filepath.Join(repositoryRoot.Name(), licenseTextFileName), []byte("repo license"), 0644) require.NoError(t, err) - err = copyLicenseTextFile(repoRoot, "REPO_LICENSE.txt") + err = copyLicenseTextFile(repositoryRoot, "REPO_LICENSE.txt") assert.NoError(t, err) - content, err := os.ReadFile(filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt")) + content, err := os.ReadFile(filepath.Join(repositoryRoot.Name(), "REPO_LICENSE.txt")) require.NoError(t, err) assert.Equal(t, "repo license", string(content)) }) t.Run("RepoLicenseCustomFileName", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() // original license file path - err = os.WriteFile(filepath.Join(repoRoot.Name(), "CUSTOM_LICENSE.txt"), []byte("repo license"), 0644) + err = os.WriteFile(filepath.Join(repositoryRoot.Name(), "CUSTOM_LICENSE.txt"), []byte("repo license"), 0644) require.NoError(t, err) t.Setenv(repositoryLicenseEnv, "CUSTOM_LICENSE.txt") - err = copyLicenseTextFile(repoRoot, "REPO_LICENSE.txt") + err = copyLicenseTextFile(repositoryRoot, "REPO_LICENSE.txt") assert.NoError(t, err) - content, err := os.ReadFile(filepath.Join(repoRoot.Name(), "REPO_LICENSE.txt")) + content, err := os.ReadFile(filepath.Join(repositoryRoot.Name(), "REPO_LICENSE.txt")) require.NoError(t, err) assert.Equal(t, "repo license", string(content)) }) t.Run("RepoLicenseFileDoesNotExist", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() - err = copyLicenseTextFile(repoRoot, "REPO_LICENSE.txt") + err = copyLicenseTextFile(repositoryRoot, "REPO_LICENSE.txt") assert.NoError(t, err) - _, err = repoRoot.Stat("LICENSE.txt") + _, err = repositoryRoot.Stat("LICENSE.txt") assert.ErrorIs(t, err, os.ErrNotExist) - _, err = repoRoot.Stat("REPO_LICENSE.txt") + _, err = repositoryRoot.Stat("REPO_LICENSE.txt") assert.ErrorIs(t, err, os.ErrNotExist) }) - t.Run("RepoLicensePathOutsideRepoRoot", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + t.Run("RepoLicensePathOutsideRepositoryRoot", func(t *testing.T) { + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() // Create a LICENSE.txt file in a different temp directory outsideDir := t.TempDir() outsideLicensePath := filepath.Join(outsideDir, "LICENSE.txt") - err = copyLicenseTextFile(repoRoot, outsideLicensePath) + err = copyLicenseTextFile(repositoryRoot, outsideLicensePath) assert.Error(t, err) assert.Contains(t, err.Error(), "path escapes from parent") }) diff --git a/internal/docs/links_map.go b/internal/docs/links_map.go index 4d7ad5fc0f..8083304423 100644 --- a/internal/docs/links_map.go +++ b/internal/docs/links_map.go @@ -87,9 +87,9 @@ func (l linkMap) RenderLink(key string, options linkOptions) (string, error) { // It first checks if the environment variable specified by linksMapFilePathEnvVar is set. // If set, it verifies that the file exists and returns its path, or an error if not found. // If the environment variable is not set, it falls back to the default file path -// constructed from repoRoot and linksMapFileNameDefault, returning the path if the file exists, +// constructed from repositoryRoot and linksMapFileNameDefault, returning the path if the file exists, // or nil if it does not. -func linksDefinitionsFilePath(repoRoot *os.Root) (string, error) { +func linksDefinitionsFilePath(repositoryRoot *os.Root) (string, error) { linksFilePath := os.Getenv(linksMapFilePathEnvVar) if linksFilePath != "" { if _, err := os.Stat(linksFilePath); err != nil { @@ -99,7 +99,7 @@ func linksDefinitionsFilePath(repoRoot *os.Root) (string, error) { return linksFilePath, nil } - linksFilePath = filepath.Join(repoRoot.Name(), linksMapFileNameDefault) + linksFilePath = filepath.Join(repositoryRoot.Name(), linksMapFileNameDefault) if _, err := os.Stat(linksFilePath); err != nil { logger.Debugf("links definitions default file doesn't exist: %s", linksFilePath) return "", nil diff --git a/internal/docs/links_map_test.go b/internal/docs/links_map_test.go index 0701c6a541..2db803cfc9 100644 --- a/internal/docs/links_map_test.go +++ b/internal/docs/links_map_test.go @@ -91,43 +91,43 @@ func TestRenderLink(t *testing.T) { func Test_linksDefinitionsFilePath(t *testing.T) { t.Run("env var set and file exists", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() - defaultFilePath := filepath.Join(repoRoot.Name(), linksMapFileNameDefault) - testFile := filepath.Join(repoRoot.Name(), "custom_links.yml") + defaultFilePath := filepath.Join(repositoryRoot.Name(), linksMapFileNameDefault) + testFile := filepath.Join(repositoryRoot.Name(), "custom_links.yml") require.NoError(t, createLinksFile(testFile)) require.NoError(t, createLinksFile(defaultFilePath)) // to ensure default file is ignored t.Setenv(linksMapFilePathEnvVar, testFile) - path, err := linksDefinitionsFilePath(repoRoot) + path, err := linksDefinitionsFilePath(repositoryRoot) require.NoError(t, err) assert.Equal(t, testFile, path) }) t.Run("env var set but file does not exist", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() - missingFile := filepath.Join(repoRoot.Name(), "missing_links.yml") + missingFile := filepath.Join(repositoryRoot.Name(), "missing_links.yml") t.Setenv(linksMapFilePathEnvVar, missingFile) - path, err := linksDefinitionsFilePath(repoRoot) + path, err := linksDefinitionsFilePath(repositoryRoot) require.Error(t, err) assert.Empty(t, path) }) t.Run("env var not set, default file exists", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() - defaultFilePath := filepath.Join(repoRoot.Name(), linksMapFileNameDefault) + defer repositoryRoot.Close() + defaultFilePath := filepath.Join(repositoryRoot.Name(), linksMapFileNameDefault) require.NoError(t, createLinksFile(defaultFilePath)) - path, err := linksDefinitionsFilePath(repoRoot) + path, err := linksDefinitionsFilePath(repositoryRoot) require.NoError(t, err) assert.Equal(t, defaultFilePath, path) @@ -135,15 +135,15 @@ func Test_linksDefinitionsFilePath(t *testing.T) { }) t.Run("env var not set, default file does not exist", func(t *testing.T) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() - defaultFilePath := filepath.Join(repoRoot.Name(), linksMapFileNameDefault) + defer repositoryRoot.Close() + defaultFilePath := filepath.Join(repositoryRoot.Name(), linksMapFileNameDefault) _, err = os.Stat(defaultFilePath) require.ErrorIs(t, err, os.ErrNotExist) - path, err := linksDefinitionsFilePath(repoRoot) + path, err := linksDefinitionsFilePath(repositoryRoot) require.NoError(t, err) assert.Empty(t, path) }) diff --git a/internal/docs/readme.go b/internal/docs/readme.go index dcc849bc2e..eb43dc22af 100644 --- a/internal/docs/readme.go +++ b/internal/docs/readme.go @@ -32,8 +32,8 @@ const ( ) // AreReadmesUpToDate function checks if all the .md readme files are up-to-date. -func AreReadmesUpToDate(repoRoot *os.Root, packageRoot string) ([]ReadmeFile, error) { - linksFilePath, err := linksDefinitionsFilePath(repoRoot) +func AreReadmesUpToDate(repositoryRoot *os.Root, packageRoot string) ([]ReadmeFile, error) { + linksFilePath, err := linksDefinitionsFilePath(repositoryRoot) if err != nil { return nil, fmt.Errorf("locating links file failed: %w", err) } @@ -98,8 +98,8 @@ func isReadmeUpToDate(fileName, linksFilePath, packageRoot string) (bool, string // UpdateReadmes function updates all .md readme files using a defined template // files. The function doesn't perform any action if the template file is not present. -func UpdateReadmes(repoRoot *os.Root, packageRoot, buildDir string) ([]string, error) { - linksFilePath, err := linksDefinitionsFilePath(repoRoot) +func UpdateReadmes(repositoryRoot *os.Root, packageRoot, buildDir string) ([]string, error) { + linksFilePath, err := linksDefinitionsFilePath(repositoryRoot) if err != nil { return nil, fmt.Errorf("locating links file failed: %w", err) } diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index 37b0447573..8859de8e47 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -39,7 +39,7 @@ type PackageLinks struct { // // - workDir can be an absolute path or a path relative to the repository root. // in both cases, it must point to a directory within the repository. -func CreateLinksFSFromPath(repoRoot *os.Root, workDir string) (*LinksFS, error) { +func CreateLinksFSFromPath(repositoryRoot *os.Root, workDir string) (*LinksFS, error) { if workDir == "" { return nil, errEmptyWorkDir } @@ -47,7 +47,7 @@ func CreateLinksFSFromPath(repoRoot *os.Root, workDir string) (*LinksFS, error) var relWorkDir string if filepath.IsAbs(workDir) { var err error - relWorkDir, err = filepath.Rel(repoRoot.Name(), workDir) + relWorkDir, err = filepath.Rel(repositoryRoot.Name(), workDir) if err != nil { return nil, fmt.Errorf("unable to find rel path for %s: %w: %w", workDir, errInvalidWorkDir, err) } @@ -55,7 +55,7 @@ func CreateLinksFSFromPath(repoRoot *os.Root, workDir string) (*LinksFS, error) relWorkDir = workDir } - info, err := repoRoot.Stat(relWorkDir) + info, err := repositoryRoot.Stat(relWorkDir) if err != nil { return nil, fmt.Errorf("unable to stat %s: %w: %w", relWorkDir, errInvalidWorkDir, err) } @@ -63,7 +63,7 @@ func CreateLinksFSFromPath(repoRoot *os.Root, workDir string) (*LinksFS, error) return nil, fmt.Errorf("working directory %s is not a directory: %w", relWorkDir, errInvalidWorkDirNotDir) } - return &LinksFS{repoRoot: repoRoot, workDir: relWorkDir}, nil + return &LinksFS{repositoryRoot: repositoryRoot, workDir: relWorkDir}, nil } var _ fs.FS = (*LinksFS)(nil) @@ -74,8 +74,8 @@ var _ fs.FS = (*LinksFS)(nil) // and its checksum. If the included file is up to date, it returns the included file. // Otherwise, it returns an error. type LinksFS struct { - repoRoot *os.Root // The root of the repository, used to check if paths are within the repository. - workDir string + repositoryRoot *os.Root // The root of the repository, used to check if paths are within the repository. + workDir string } // Open opens a file in the filesystem. @@ -86,7 +86,7 @@ func (lfs *LinksFS) Open(name string) (fs.File, error) { // innerRoot is the filesystem rooted at the workDir // we use it to check if the file exists in the workDir // and to open non-link files directly - innerRoot, err := lfs.repoRoot.OpenRoot(lfs.workDir) + innerRoot, err := lfs.repositoryRoot.OpenRoot(lfs.workDir) if err != nil { return nil, fmt.Errorf("could not open workDir in root: %w", err) } @@ -95,7 +95,7 @@ func (lfs *LinksFS) Open(name string) (fs.File, error) { relName := name if filepath.IsAbs(name) { var err error - relName, err = filepath.Rel(filepath.Join(lfs.repoRoot.Name(), lfs.workDir), name) + relName, err = filepath.Rel(filepath.Join(lfs.repositoryRoot.Name(), lfs.workDir), name) if err != nil { return nil, fmt.Errorf("could not make name relative to workDir: %w", err) } @@ -110,8 +110,8 @@ func (lfs *LinksFS) Open(name string) (fs.File, error) { return innerRoot.Open(relName) } - linkFilePath := filepath.Join(lfs.repoRoot.Name(), lfs.workDir, relName) - l, err := newLinkedFile(lfs.repoRoot, linkFilePath) + linkFilePath := filepath.Join(lfs.repositoryRoot.Name(), lfs.workDir, relName) + l, err := newLinkedFile(lfs.repositoryRoot, linkFilePath) if err != nil { return nil, err } @@ -125,12 +125,12 @@ func (lfs *LinksFS) Open(name string) (fs.File, error) { includedPath := filepath.Join(filepath.Dir(linkFilePath), filepath.FromSlash(l.IncludedFilePath)) // Convert to relative path from repository root for secure access of target file - relativePath, err := filepath.Rel(lfs.repoRoot.Name(), includedPath) + relativePath, err := filepath.Rel(lfs.repositoryRoot.Name(), includedPath) if err != nil { return nil, fmt.Errorf("could not get relative path: %w", err) } - return lfs.repoRoot.Open(relativePath) + return lfs.repositoryRoot.Open(relativePath) } // ReadFile reads a file from the filesystem. @@ -146,25 +146,25 @@ func (lfs *LinksFS) ReadFile(name string) ([]byte, error) { // CheckLinkedFiles checks if all linked files in the directory are up-to-date. // Returns a list of outdated links that need updating. func (lfs *LinksFS) CheckLinkedFiles() ([]Link, error) { - return areLinkedFilesUpToDate(lfs.repoRoot, lfs.workDir) + return areLinkedFilesUpToDate(lfs.repositoryRoot, lfs.workDir) } // UpdateLinkedFiles updates the checksums of all outdated linked files in the directory. // Returns a list of links that were updated. func (lfs *LinksFS) UpdateLinkedFiles() ([]Link, error) { - return updateLinkedFilesChecksums(lfs.repoRoot, lfs.workDir) + return updateLinkedFilesChecksums(lfs.repositoryRoot, lfs.workDir) } // IncludeLinkedFiles copies all linked files from the source directory to the target directory. // This is used during package building to include linked files in the build output. func (lfs *LinksFS) IncludeLinkedFiles(toDir string) ([]Link, error) { - return includeLinkedFiles(lfs.repoRoot, lfs.workDir, toDir) + return includeLinkedFiles(lfs.repositoryRoot, lfs.workDir, toDir) } // ListLinkedFilesByPackage returns a mapping of packages to their linked files that reference // files from the given directory. func (lfs *LinksFS) ListLinkedFilesByPackage() ([]PackageLinks, error) { - return linkedFilesByPackageFrom(lfs.repoRoot, lfs.workDir) + return linkedFilesByPackageFrom(lfs.repositoryRoot, lfs.workDir) } // A Link represents a linked file. @@ -188,7 +188,7 @@ type Link struct { // newLinkedFile creates a new Link struct from the given absolute path to a link file. // root is the repository root, used to validate paths and access files securely -func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { +func newLinkedFile(repositoryRoot *os.Root, linkFilePath string) (*Link, error) { workDir := filepath.Dir(linkFilePath) @@ -232,17 +232,17 @@ func newLinkedFile(repoRoot *os.Root, linkFilePath string) (*Link, error) { includedFilePath := filepath.Clean(filepath.Join(workDir, filepath.FromSlash(includedFileRelPath))) // get relative path of the included file from the root (packages root or repository root) - includedFilePathRelFromRoot, err := filepath.Rel(repoRoot.Name(), includedFilePath) + includedFilePathRelFromRoot, err := filepath.Rel(repositoryRoot.Name(), includedFilePath) if err != nil { return nil, fmt.Errorf("could not get relative path: %w", err) } // check the file exists - if _, err := repoRoot.Stat(includedFilePathRelFromRoot); err != nil { + if _, err := repositoryRoot.Stat(includedFilePathRelFromRoot); err != nil { return nil, err } // check if checksum is updated - cs, err := getLinkedFileChecksumFromRoot(repoRoot, includedFilePathRelFromRoot) + cs, err := getLinkedFileChecksumFromRoot(repositoryRoot, includedFilePathRelFromRoot) if err != nil { return nil, fmt.Errorf("could not collect file %s: %w", includedFilePathRelFromRoot, err) } @@ -293,8 +293,8 @@ func (l *Link) updateChecksum() (bool, error) { // includeLinkedFiles function includes linked files from the source directory to the target directory. // It returns a slice of Link structs representing the included files. // It also updates the checksum of the linked files. -func includeLinkedFiles(root *os.Root, fromDir, toDir string) ([]Link, error) { - links, err := listLinkedFiles(root, fromDir) +func includeLinkedFiles(repositoryRoot *os.Root, fromDir, toDir string) ([]Link, error) { + links, err := listLinkedFiles(repositoryRoot, fromDir) if err != nil { return nil, fmt.Errorf("including linked files failed: %w", err) } @@ -307,7 +307,7 @@ func includeLinkedFiles(root *os.Root, fromDir, toDir string) ([]Link, error) { // from l.IncludedFilePath, we just need the path name without .link suffix and to be relative to the toDir instead of the package root if err := copyFromRoot( - root, + repositoryRoot, filepath.Join(l.WorkDir, filepath.FromSlash(l.IncludedFilePath)), targetFilePath, ); err != nil { @@ -322,10 +322,10 @@ func includeLinkedFiles(root *os.Root, fromDir, toDir string) ([]Link, error) { // within the given directory. // // - fromDir should be relative to the repository root. -func listLinkedFiles(root *os.Root, fromDir string) ([]Link, error) { +func listLinkedFiles(repositoryRoot *os.Root, fromDir string) ([]Link, error) { var linkFilesPaths []string if err := filepath.Walk( - filepath.Join(root.Name(), fromDir), + filepath.Join(repositoryRoot.Name(), fromDir), func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -341,7 +341,7 @@ func listLinkedFiles(root *os.Root, fromDir string) ([]Link, error) { links := make([]Link, len(linkFilesPaths)) for i, f := range linkFilesPaths { - l, err := newLinkedFile(root, filepath.FromSlash(f)) + l, err := newLinkedFile(repositoryRoot, filepath.FromSlash(f)) if err != nil { return nil, fmt.Errorf("could not initialize linked file %s: %w", f, err) } @@ -352,57 +352,57 @@ func listLinkedFiles(root *os.Root, fromDir string) ([]Link, error) { } // createDirInRoot function creates a directory and all its parents within the root. -func createDirInRoot(root *os.Root, dir string) error { +func createDirInRoot(repositoryRoot *os.Root, dir string) error { dir = filepath.Clean(dir) if dir == "." || dir == "/" { return nil } // Check if the directory already exists - if _, err := root.Stat(dir); err == nil { + if _, err := repositoryRoot.Stat(dir); err == nil { return nil } // Create parent directory first parent := filepath.Dir(dir) if parent != dir { // Avoid infinite recursion - if err := createDirInRoot(root, parent); err != nil { + if err := createDirInRoot(repositoryRoot, parent); err != nil { return err } } // Create the directory - return root.Mkdir(dir, 0700) + return repositoryRoot.Mkdir(dir, 0700) } // copyFromRoot function copies a file from to to inside the root. -func copyFromRoot(root *os.Root, from, to string) error { +func copyFromRoot(repositoryRoot *os.Root, from, to string) error { var err error if filepath.IsAbs(from) { - from, err = filepath.Rel(root.Name(), filepath.FromSlash(from)) + from, err = filepath.Rel(repositoryRoot.Name(), filepath.FromSlash(from)) if err != nil { return fmt.Errorf("could not get relative path: %w", err) } } - source, err := root.Open(from) + source, err := repositoryRoot.Open(from) if err != nil { return err } defer source.Close() if filepath.IsAbs(to) { - to, err = filepath.Rel(root.Name(), filepath.FromSlash(to)) + to, err = filepath.Rel(repositoryRoot.Name(), filepath.FromSlash(to)) if err != nil { return fmt.Errorf("could not get relative path: %w", err) } } dir := filepath.Dir(to) - if _, err := root.Stat(dir); os.IsNotExist(err) { - if err := createDirInRoot(root, dir); err != nil { + if _, err := repositoryRoot.Stat(dir); os.IsNotExist(err) { + if err := createDirInRoot(repositoryRoot, dir); err != nil { return err } } - destination, err := root.Create(to) + destination, err := repositoryRoot.Create(to) if err != nil { return err } @@ -424,8 +424,8 @@ func writeFile(to string, b []byte) error { } // areLinkedFilesUpToDate function checks if all the linked files are up-to-date. -func areLinkedFilesUpToDate(root *os.Root, fromDir string) ([]Link, error) { - links, err := listLinkedFiles(root, fromDir) +func areLinkedFilesUpToDate(repositoryRoot *os.Root, fromDir string) ([]Link, error) { + links, err := listLinkedFiles(repositoryRoot, fromDir) if err != nil { return nil, fmt.Errorf("checking linked files failed: %w", err) } @@ -444,8 +444,8 @@ func areLinkedFilesUpToDate(root *os.Root, fromDir string) ([]Link, error) { // updateLinkedFilesChecksums function updates the checksums of the linked files. // It returns a slice of updated links. // If no links were updated, it returns an empty slice. -func updateLinkedFilesChecksums(root *os.Root, fromDir string) ([]Link, error) { - links, err := listLinkedFiles(root, fromDir) +func updateLinkedFilesChecksums(repositoryRoot *os.Root, fromDir string) ([]Link, error) { + links, err := listLinkedFiles(repositoryRoot, fromDir) if err != nil { return nil, fmt.Errorf("updating linked files checksums failed: %w", err) } @@ -468,16 +468,16 @@ func updateLinkedFilesChecksums(root *os.Root, fromDir string) ([]Link, error) { // Each PackageLinks contains the package name and a slice of linked file paths. // // - fromDir should be relative to the repository root. -func linkedFilesByPackageFrom(root *os.Root, fromDir string) ([]PackageLinks, error) { +func linkedFilesByPackageFrom(repositoryRoot *os.Root, fromDir string) ([]PackageLinks, error) { // we list linked files from all the root directory // to check which ones are linked to the 'fromDir' package - links, err := listLinkedFiles(root, ".") + links, err := listLinkedFiles(repositoryRoot, ".") if err != nil { return nil, fmt.Errorf("listing linked files failed: %w", err) } var packageName string - packageRoot, err := packages.FindPackageRootFrom(filepath.Join(root.Name(), fromDir)) + packageRoot, err := packages.FindPackageRootFrom(filepath.Join(repositoryRoot.Name(), fromDir)) if err != nil { return nil, fmt.Errorf("finding package root failed: %w", err) } @@ -510,8 +510,8 @@ func linkedFilesByPackageFrom(root *os.Root, fromDir string) ([]PackageLinks, er } // getLinkedFileChecksumFromRoot calculates the SHA256 checksum of a file using root-relative access. -func getLinkedFileChecksumFromRoot(root *os.Root, relativePath string) (string, error) { - file, err := root.Open(filepath.FromSlash(relativePath)) +func getLinkedFileChecksumFromRoot(repositoryRoot *os.Root, relativePath string) (string, error) { + file, err := repositoryRoot.Open(filepath.FromSlash(relativePath)) if err != nil { return "", err } diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index deee6bec5a..2cf0e7a487 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -911,10 +911,10 @@ func TestLinksFS_WorkDirValidation(t *testing.T) { func Test_newLinkedFile(t *testing.T) { - repoRoot := t.TempDir() - repoRootHandle, err := os.OpenRoot(repoRoot) + repositoryRoot := t.TempDir() + repositoryRootHandle, err := os.OpenRoot(repositoryRoot) require.NoError(t, err) - t.Cleanup(func() { _ = repoRootHandle.Close() }) + t.Cleanup(func() { _ = repositoryRootHandle.Close() }) linkFileContent := fmt.Sprintf("%s d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", filepath.Join("..", "..", "B", "otherFolder", "included.txt")) includedFileContent := "included file content" @@ -923,13 +923,13 @@ func Test_newLinkedFile(t *testing.T) { // /packages/B/otherFolder/included.txt // included relative to link file: ../../B/otherFolder/included.txt - err = os.MkdirAll(filepath.Join(repoRoot, "packages", "A", "folder"), 0755) + err = os.MkdirAll(filepath.Join(repositoryRoot, "packages", "A", "folder"), 0755) require.NoError(t, err) // required to identify a package root - _, err = repoRootHandle.Create(filepath.Join("packages", "A", "manifest.yml")) + _, err = repositoryRootHandle.Create(filepath.Join("packages", "A", "manifest.yml")) require.NoError(t, err) - fManifestA, err := repoRootHandle.Create(filepath.Join("packages", "A", "manifest.yml")) + fManifestA, err := repositoryRootHandle.Create(filepath.Join("packages", "A", "manifest.yml")) require.NoError(t, err) _, err = fManifestA.WriteString(`name: A version: 1.0.0 @@ -938,19 +938,19 @@ type: integration require.NoError(t, err) require.NoError(t, fManifestA.Close()) - fLink, err := repoRootHandle.Create(filepath.Join("packages", "A", "folder", "link.txt.link")) + fLink, err := repositoryRootHandle.Create(filepath.Join("packages", "A", "folder", "link.txt.link")) require.NoError(t, err) _, err = fLink.WriteString(linkFileContent) require.NoError(t, err) require.NoError(t, fLink.Close()) - err = os.MkdirAll(filepath.Join(repoRoot, "packages", "B", "otherFolder"), 0755) + err = os.MkdirAll(filepath.Join(repositoryRoot, "packages", "B", "otherFolder"), 0755) require.NoError(t, err) // required to identify a package root - _, err = repoRootHandle.Create(filepath.Join("packages", "B", "manifest.yml")) + _, err = repositoryRootHandle.Create(filepath.Join("packages", "B", "manifest.yml")) require.NoError(t, err) - fManifestB, err := repoRootHandle.Create(filepath.Join("packages", "B", "manifest.yml")) + fManifestB, err := repositoryRootHandle.Create(filepath.Join("packages", "B", "manifest.yml")) require.NoError(t, err) _, err = fManifestB.WriteString(`name: B version: 1.0.0 @@ -959,17 +959,17 @@ type: integration require.NoError(t, err) require.NoError(t, fManifestB.Close()) - fIncluded, err := repoRootHandle.Create(filepath.Join("packages", "B", "otherFolder", "included.txt")) + fIncluded, err := repositoryRootHandle.Create(filepath.Join("packages", "B", "otherFolder", "included.txt")) require.NoError(t, err) _, err = fIncluded.WriteString(includedFileContent) require.NoError(t, err) require.NoError(t, fIncluded.Close()) - l, err := newLinkedFile(repoRootHandle, fLink.Name()) + l, err := newLinkedFile(repositoryRootHandle, fLink.Name()) require.NoError(t, err) assert.NotNil(t, l) - assert.Equal(t, filepath.Join(repoRoot, filepath.Join("packages", "A", "folder")), l.WorkDir) + assert.Equal(t, filepath.Join(repositoryRoot, filepath.Join("packages", "A", "folder")), l.WorkDir) assert.True(t, strings.HasSuffix(l.LinkFilePath, filepath.Join("folder", "link.txt.link"))) assert.Equal(t, "d709feed45b708c9548a18ca48f3ad4f41be8d3f691f83d7417ca902a20e6c1e", l.LinkChecksum) diff --git a/internal/packages/archetype/data_stream_test.go b/internal/packages/archetype/data_stream_test.go index f314957b21..d6e2deaa25 100644 --- a/internal/packages/archetype/data_stream_test.go +++ b/internal/packages/archetype/data_stream_test.go @@ -20,12 +20,12 @@ func TestDataStream(t *testing.T) { dd := createDataStreamDescriptorForTest() dd.Manifest.Type = "logs" - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - createAndCheckDataStream(t, pd, dd, true, repoRoot) + createAndCheckDataStream(t, pd, dd, true, repositoryRoot) - err = repoRoot.Close() + err = repositoryRoot.Close() require.NoError(t, err) }) t.Run("valid-metrics", func(t *testing.T) { @@ -33,12 +33,12 @@ func TestDataStream(t *testing.T) { dd := createDataStreamDescriptorForTest() dd.Manifest.Type = "metrics" - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - createAndCheckDataStream(t, pd, dd, true, repoRoot) + createAndCheckDataStream(t, pd, dd, true, repositoryRoot) - err = repoRoot.Close() + err = repositoryRoot.Close() require.NoError(t, err) }) t.Run("missing-type", func(t *testing.T) { @@ -46,12 +46,12 @@ func TestDataStream(t *testing.T) { dd := createDataStreamDescriptorForTest() dd.Manifest.Type = "" - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - createAndCheckDataStream(t, pd, dd, false, repoRoot) + createAndCheckDataStream(t, pd, dd, false, repositoryRoot) - err = repoRoot.Close() + err = repositoryRoot.Close() require.NoError(t, err) }) } @@ -76,9 +76,9 @@ func createDataStreamDescriptorForTest() DataStreamDescriptor { } } -func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamDescriptor, valid bool, repoRoot *os.Root) { +func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamDescriptor, valid bool, repositoryRoot *os.Root) { - packagesDir := filepath.Join(repoRoot.Name(), "packages") + packagesDir := filepath.Join(repositoryRoot.Name(), "packages") err := os.MkdirAll(packagesDir, 0o755) require.NoError(t, err) @@ -91,5 +91,5 @@ func createAndCheckDataStream(t *testing.T, pd PackageDescriptor, dd DataStreamD err = CreateDataStream(dd) require.NoError(t, err) - checkPackage(t, repoRoot, packageRoot, valid) + checkPackage(t, repositoryRoot, packageRoot, valid) } diff --git a/internal/packages/archetype/package_test.go b/internal/packages/archetype/package_test.go index caf85f7554..944a91b76d 100644 --- a/internal/packages/archetype/package_test.go +++ b/internal/packages/archetype/package_test.go @@ -38,15 +38,15 @@ func TestPackage(t *testing.T) { } func createAndCheckPackage(t *testing.T, pd PackageDescriptor, valid bool) { - repoRoot, err := os.OpenRoot(t.TempDir()) + repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() - packagesDir := filepath.Join(repoRoot.Name(), "packages") + packagesDir := filepath.Join(repositoryRoot.Name(), "packages") err = createPackageInDir(pd, packagesDir) require.NoError(t, err) - checkPackage(t, repoRoot, filepath.Join(packagesDir, pd.Manifest.Name), valid) + checkPackage(t, repositoryRoot, filepath.Join(packagesDir, pd.Manifest.Name), valid) } func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDescriptor { @@ -96,25 +96,25 @@ func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDe } } -func buildPackage(t *testing.T, repoRoot *os.Root, packageRoot string) error { - buildDir := filepath.Join(repoRoot.Name(), "build") +func buildPackage(t *testing.T, repositoryRoot *os.Root, packageRoot string) error { + buildDir := filepath.Join(repositoryRoot.Name(), "build") err := os.MkdirAll(buildDir, 0o755) require.NoError(t, err) - _, err = docs.UpdateReadmes(repoRoot, packageRoot, buildDir) + _, err = docs.UpdateReadmes(repositoryRoot, packageRoot, buildDir) if err != nil { return err } _, err = builder.BuildPackage(t.Context(), builder.BuildOptions{ - PackageRoot: packageRoot, - BuildDir: buildDir, - RepoRoot: repoRoot, + PackageRoot: packageRoot, + BuildDir: buildDir, + RepositoryRoot: repositoryRoot, }) return err } -func checkPackage(t *testing.T, repoRoot *os.Root, packageRoot string, valid bool) { - err := buildPackage(t, repoRoot, packageRoot) +func checkPackage(t *testing.T, repositoryRoot *os.Root, packageRoot string, valid bool) { + err := buildPackage(t, repositoryRoot, packageRoot) if !valid { assert.Error(t, err) return diff --git a/internal/packages/installer/factory.go b/internal/packages/installer/factory.go index f9008162d2..e3d120fb4c 100644 --- a/internal/packages/installer/factory.go +++ b/internal/packages/installer/factory.go @@ -38,7 +38,7 @@ type Options struct { RootPath string // Root path of the package to be installed. ZipPath string SkipValidation bool - RepoRoot *os.Root // Root of the repository where package source code is located. + RepositoryRoot *os.Root // Root of the repository where package source code is located. } // NewForPackage creates a new installer for a package, given its root path, or its prebuilt zip. @@ -53,7 +53,7 @@ func NewForPackage(ctx context.Context, options Options) (Installer, error) { if options.RootPath == "" && options.ZipPath == "" { return nil, errors.New("missing package root path or pre-built zip package") } - if options.RepoRoot == nil { + if options.RepositoryRoot == nil { return nil, errors.New("missing repo root") } @@ -90,7 +90,7 @@ func NewForPackage(ctx context.Context, options Options) (Installer, error) { CreateZip: supportsUploadZip, SignPackage: false, SkipValidation: options.SkipValidation, - RepoRoot: options.RepoRoot, + RepositoryRoot: options.RepositoryRoot, }) if err != nil { return nil, fmt.Errorf("failed to build package: %v", err) diff --git a/internal/resources/fleetpackage.go b/internal/resources/fleetpackage.go index bb1095d131..0c0389d48c 100644 --- a/internal/resources/fleetpackage.go +++ b/internal/resources/fleetpackage.go @@ -25,8 +25,8 @@ type FleetPackage struct { // RootPath is the root of the package source to install. RootPath string - // RepoPath is the root of the repository. - RepoRoot *os.Root + // RepositoryRoot is the root of the repository. + RepositoryRoot *os.Root // Absent is set to true to indicate that the package should not be installed. Absent bool @@ -63,7 +63,7 @@ func (f *FleetPackage) installer(ctx resource.Context) (installer.Installer, err Kibana: provider.Client, RootPath: f.RootPath, SkipValidation: true, - RepoRoot: f.RepoRoot, + RepositoryRoot: f.RepositoryRoot, }) } diff --git a/internal/resources/fleetpackage_test.go b/internal/resources/fleetpackage_test.go index 6b3024fb14..01d8331f33 100644 --- a/internal/resources/fleetpackage_test.go +++ b/internal/resources/fleetpackage_test.go @@ -22,14 +22,14 @@ import ( func TestRequiredProvider(t *testing.T) { manager := resource.NewManager() - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() require.NoError(t, err) - t.Cleanup(func() { _ = repoRoot.Close() }) + t.Cleanup(func() { _ = repositoryRoot.Close() }) _, err = manager.Apply(resource.Resources{ &FleetPackage{ - RootPath: "../../test/packages/parallel/nginx", - RepoRoot: repoRoot, + RootPath: "../../test/packages/parallel/nginx", + RepositoryRoot: repositoryRoot, }, }) if assert.Error(t, err) { @@ -54,15 +54,15 @@ func TestPackageLifecycle(t *testing.T) { t.FailNow() } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() require.NoError(t, err) - defer repoRoot.Close() + defer repositoryRoot.Close() - packageRootPath := filepath.Join(repoRoot.Name(), "test", "packages", "parallel", c.name) + packageRootPath := filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", c.name) fleetPackage := FleetPackage{ - RootPath: packageRootPath, - RepoRoot: repoRoot, + RootPath: packageRootPath, + RepositoryRoot: repositoryRoot, } manager := resource.NewManager() manager.RegisterProvider(DefaultKibanaProviderName, &KibanaProvider{Client: kibanaClient}) @@ -84,14 +84,14 @@ func TestSystemPackageIsNotRemoved(t *testing.T) { t.FailNow() } - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() require.NoError(t, err) - t.Cleanup(func() { _ = repoRoot.Close() }) + t.Cleanup(func() { _ = repositoryRoot.Close() }) fleetPackage := FleetPackage{ - RootPath: "../../test/packages/parallel/system", - Absent: true, - RepoRoot: repoRoot, + RootPath: "../../test/packages/parallel/system", + Absent: true, + RepositoryRoot: repositoryRoot, } manager := resource.NewManager() manager.RegisterProvider(DefaultKibanaProviderName, &KibanaProvider{Client: kibanaClient}) diff --git a/internal/resources/fleetpolicy.go b/internal/resources/fleetpolicy.go index 092acd1cd7..f000ae833c 100644 --- a/internal/resources/fleetpolicy.go +++ b/internal/resources/fleetpolicy.go @@ -44,7 +44,7 @@ type FleetAgentPolicy struct { PackagePolicies []FleetPackagePolicy // RepoPath is the root of the repository. - RepoRoot *os.Root + RepositoryRoot *os.Root } type FleetPackagePolicy struct { diff --git a/internal/resources/fleetpolicy_test.go b/internal/resources/fleetpolicy_test.go index 3ba65d8185..8920121f66 100644 --- a/internal/resources/fleetpolicy_test.go +++ b/internal/resources/fleetpolicy_test.go @@ -21,15 +21,15 @@ import ( func TestRequiredProviderFleetPolicy(t *testing.T) { - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() require.NoError(t, err) - t.Cleanup(func() { _ = repoRoot.Close() }) + t.Cleanup(func() { _ = repositoryRoot.Close() }) manager := resource.NewManager() _, err = manager.Apply(resource.Resources{ &FleetAgentPolicy{ - Name: "test-policy", - RepoRoot: repoRoot, + Name: "test-policy", + RepositoryRoot: repositoryRoot, }, }) if assert.Error(t, err) { @@ -38,9 +38,9 @@ func TestRequiredProviderFleetPolicy(t *testing.T) { } func TestPolicyLifecycle(t *testing.T) { - repoRoot, err := files.FindRepositoryRoot() + repositoryRoot, err := files.FindRepositoryRoot() require.NoError(t, err) - t.Cleanup(func() { _ = repoRoot.Close() }) + t.Cleanup(func() { _ = repositoryRoot.Close() }) cases := []struct { title string @@ -54,7 +54,7 @@ func TestPolicyLifecycle(t *testing.T) { packagePolicies: []FleetPackagePolicy{ { Name: "nginx-1", - RootPath: filepath.Join(repoRoot.Name(), "test", "packages", "parallel", "nginx"), + RootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "nginx"), DataStreamName: "stubstatus", }, }, @@ -64,12 +64,12 @@ func TestPolicyLifecycle(t *testing.T) { packagePolicies: []FleetPackagePolicy{ { Name: "nginx-1", - RootPath: filepath.Join(repoRoot.Name(), "test", "packages", "parallel", "nginx"), + RootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "nginx"), DataStreamName: "stubstatus", }, { Name: "system-1", - RootPath: filepath.Join(repoRoot.Name(), "test", "packages", "parallel", "system"), + RootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "system"), DataStreamName: "process", }, }, @@ -79,7 +79,7 @@ func TestPolicyLifecycle(t *testing.T) { packagePolicies: []FleetPackagePolicy{ { Name: "input-1", - RootPath: filepath.Join(repoRoot.Name(), "test", "packages", "parallel", "sql_input"), + RootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "sql_input"), }, }, }, @@ -100,7 +100,7 @@ func TestPolicyLifecycle(t *testing.T) { Description: fmt.Sprintf("Test policy for %s", c.title), Namespace: "eptest", PackagePolicies: c.packagePolicies, - RepoRoot: repoRoot, + RepositoryRoot: repositoryRoot, } t.Cleanup(func() { deletePolicy(t, manager, agentPolicy) }) @@ -122,9 +122,9 @@ func withPackageResources(agentPolicy *FleetAgentPolicy) resource.Resources { var resources resource.Resources for _, policy := range agentPolicy.PackagePolicies { resources = append(resources, &FleetPackage{ - RootPath: policy.RootPath, - Absent: agentPolicy.Absent, - RepoRoot: agentPolicy.RepoRoot, + RootPath: policy.RootPath, + Absent: agentPolicy.Absent, + RepositoryRoot: agentPolicy.RepositoryRoot, }) } return append(resources, agentPolicy) diff --git a/internal/testrunner/runners/asset/runner.go b/internal/testrunner/runners/asset/runner.go index 3da335d78e..93f90c3508 100644 --- a/internal/testrunner/runners/asset/runner.go +++ b/internal/testrunner/runners/asset/runner.go @@ -24,7 +24,7 @@ type runner struct { globalTestConfig testrunner.GlobalRunnerTestConfig withCoverage bool coverageType string - repoRoot *os.Root + repositoryRoot *os.Root } type AssetTestRunnerOptions struct { @@ -33,7 +33,7 @@ type AssetTestRunnerOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string - RepoRoot *os.Root + RepositoryRoot *os.Root } func NewAssetTestRunner(options AssetTestRunnerOptions) *runner { @@ -43,7 +43,7 @@ func NewAssetTestRunner(options AssetTestRunnerOptions) *runner { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, - repoRoot: options.RepoRoot, + repositoryRoot: options.RepositoryRoot, } return &runner } @@ -74,7 +74,7 @@ func (r *runner) GetTests(ctx context.Context) ([]testrunner.Tester, error) { GlobalTestConfig: r.globalTestConfig, WithCoverage: r.withCoverage, CoverageType: r.coverageType, - RepoRoot: r.repoRoot, + RepositoryRoot: r.repositoryRoot, }), } return testers, nil diff --git a/internal/testrunner/runners/asset/tester.go b/internal/testrunner/runners/asset/tester.go index 43c9385911..ba12191359 100644 --- a/internal/testrunner/runners/asset/tester.go +++ b/internal/testrunner/runners/asset/tester.go @@ -26,7 +26,7 @@ type tester struct { globalTestConfig testrunner.GlobalRunnerTestConfig withCoverage bool coverageType string - repoRoot *os.Root + repositoryRoot *os.Root } type AssetTesterOptions struct { @@ -36,7 +36,7 @@ type AssetTesterOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string - RepoRoot *os.Root + RepositoryRoot *os.Root } func NewAssetTester(options AssetTesterOptions) *tester { @@ -47,7 +47,7 @@ func NewAssetTester(options AssetTesterOptions) *tester { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, - repoRoot: options.RepoRoot, + repositoryRoot: options.RepositoryRoot, } manager := resources.NewManager() @@ -84,10 +84,10 @@ func (r *tester) Run(ctx context.Context) ([]testrunner.TestResult, error) { func (r *tester) resources(installedPackage bool) resources.Resources { return resources.Resources{ &resources.FleetPackage{ - RootPath: r.packageRootPath, - Absent: !installedPackage, - Force: installedPackage, // Force re-installation, in case there are code changes in the same package version. - RepoRoot: r.repoRoot, + RootPath: r.packageRootPath, + Absent: !installedPackage, + Force: installedPackage, // Force re-installation, in case there are code changes in the same package version. + RepositoryRoot: r.repositoryRoot, }, } } diff --git a/internal/testrunner/runners/policy/runner.go b/internal/testrunner/runners/policy/runner.go index ac03c4ca99..40690d16e2 100644 --- a/internal/testrunner/runners/policy/runner.go +++ b/internal/testrunner/runners/policy/runner.go @@ -23,7 +23,7 @@ const ( ) type runner struct { - repoRoot *os.Root + repositoryRoot *os.Root packageRootPath string kibanaClient *kibana.Client @@ -50,7 +50,7 @@ type PolicyTestRunnerOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string - RepoRoot *os.Root + RepositoryRoot *os.Root } func NewPolicyTestRunner(options PolicyTestRunnerOptions) *runner { @@ -63,7 +63,7 @@ func NewPolicyTestRunner(options PolicyTestRunnerOptions) *runner { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, - repoRoot: options.RepoRoot, + repositoryRoot: options.RepositoryRoot, } runner.resourcesManager = resources.NewManager() runner.resourcesManager.RegisterProvider(resources.DefaultKibanaProviderName, &resources.KibanaProvider{Client: runner.kibanaClient}) @@ -147,7 +147,7 @@ func (r *runner) GetTests(ctx context.Context) ([]testrunner.Tester, error) { GlobalTestConfig: r.globalTestConfig, WithCoverage: r.withCoverage, CoverageType: r.coverageType, - RepoRoot: r.repoRoot, + RepositoryRoot: r.repositoryRoot, })) } @@ -161,8 +161,8 @@ func (r *runner) Type() testrunner.TestType { func (r *runner) setupSuite(ctx context.Context, manager *resources.Manager) (cleanup func(ctx context.Context) error, err error) { packageResource := resources.FleetPackage{ - RootPath: r.packageRootPath, - RepoRoot: r.repoRoot, + RootPath: r.packageRootPath, + RepositoryRoot: r.repositoryRoot, } setupResources := resources.Resources{ &packageResource, diff --git a/internal/testrunner/runners/policy/tester.go b/internal/testrunner/runners/policy/tester.go index bf39f43b56..021e3f81a6 100644 --- a/internal/testrunner/runners/policy/tester.go +++ b/internal/testrunner/runners/policy/tester.go @@ -29,7 +29,7 @@ type tester struct { coverageType string resourcesManager *resources.Manager - repoRoot *os.Root + repositoryRoot *os.Root } // Ensures that runner implements testrunner.Tester interface @@ -44,7 +44,7 @@ type PolicyTesterOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string - RepoRoot *os.Root + RepositoryRoot *os.Root } func NewPolicyTester(options PolicyTesterOptions) *tester { @@ -57,7 +57,7 @@ func NewPolicyTester(options PolicyTesterOptions) *tester { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, - repoRoot: options.RepoRoot, + repositoryRoot: options.RepositoryRoot, } tester.resourcesManager = resources.NewManager() tester.resourcesManager.RegisterProvider(resources.DefaultKibanaProviderName, &resources.KibanaProvider{Client: tester.kibanaClient}) @@ -125,7 +125,7 @@ func (r *tester) runTest(ctx context.Context, manager *resources.Manager, testPa DataStreamVars: testConfig.DataStream.Vars, }, }, - RepoRoot: r.repoRoot, + RepositoryRoot: r.repositoryRoot, } resources := resource.Resources{&policy} _, testErr := manager.ApplyCtx(ctx, resources) diff --git a/internal/testrunner/runners/system/runner.go b/internal/testrunner/runners/system/runner.go index 07dffe817b..d5cb82135f 100644 --- a/internal/testrunner/runners/system/runner.go +++ b/internal/testrunner/runners/system/runner.go @@ -25,7 +25,7 @@ import ( type runner struct { profile *profile.Profile - repoRoot *os.Root + repositoryRoot *os.Root packageRootPath string kibanaClient *kibana.Client esAPI *elasticsearch.API @@ -56,7 +56,7 @@ var _ testrunner.TestRunner = new(runner) type SystemTestRunnerOptions struct { Profile *profile.Profile PackageRootPath string - RepoRoot *os.Root + RepositoryRoot *os.Root KibanaClient *kibana.Client API *elasticsearch.API @@ -99,7 +99,7 @@ func NewSystemTestRunner(options SystemTestRunnerOptions) *runner { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, - repoRoot: options.RepoRoot, + repositoryRoot: options.RepositoryRoot, } r.resourcesManager = resources.NewManager() @@ -286,10 +286,10 @@ func (r *runner) Type() testrunner.TestType { func (r *runner) resources(opts resourcesOptions) resources.Resources { return resources.Resources{ &resources.FleetPackage{ - RootPath: r.packageRootPath, - Absent: !opts.installedPackage, - Force: opts.installedPackage, // Force re-installation, in case there are code changes in the same package version. - RepoRoot: r.repoRoot, + RootPath: r.packageRootPath, + Absent: !opts.installedPackage, + Force: opts.installedPackage, // Force re-installation, in case there are code changes in the same package version. + RepositoryRoot: r.repositoryRoot, }, } } From 542912162b4600109735818d77e4a506bdef4b1b Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 10:53:01 +0200 Subject: [PATCH 30/46] fix: correct grammar in comments for license file handling --- internal/builder/packages.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 6dd8e9073f..71c9701123 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -302,7 +302,6 @@ func signZippedPackage(options BuildOptions, zippedPackagePath string) error { // If the file does not exist in the package, it looks for a license file in the repository root directory. // If a license file is found in the repository, it copies it to the package directory. func copyLicenseTextFile(repositoryRoot *os.Root, licensePath string) error { - // check licensePath is within the repository relPath := filepath.Clean(licensePath) if filepath.IsAbs(licensePath) { @@ -316,7 +315,7 @@ func copyLicenseTextFile(repositoryRoot *os.Root, licensePath string) error { licensePath = filepath.Join(repositoryRoot.Name(), licensePath) } - // if the given path exist, skip copying + // if the given path exists, skip copying info, err := repositoryRoot.Stat(relPath) if err == nil && !info.IsDir() { logger.Debug("License file in the package will be used") @@ -326,7 +325,7 @@ func copyLicenseTextFile(repositoryRoot *os.Root, licensePath string) error { if err != nil && !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("can't check license path (%s): %w", licensePath, err) } - // if the given path exist but is a directory, return an error + // if the given path exists, but is a directory, return an error if info != nil && info.IsDir() { return fmt.Errorf("license path (%s) is a directory", licensePath) } From b3e73acdea2a9e4577b7001ea28cdf535e48744a Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 11:18:22 +0200 Subject: [PATCH 31/46] update copyLicenseTextFile function to improve clarity and error handling --- internal/builder/packages.go | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 71c9701123..3f7026cb6d 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -297,22 +297,23 @@ func signZippedPackage(options BuildOptions, zippedPackagePath string) error { return nil } -// copyLicenseTextFile checks if a license file exists in the package directory. -// If the file already exists in the package, it does nothing. -// If the file does not exist in the package, it looks for a license file in the repository root directory. -// If a license file is found in the repository, it copies it to the package directory. -func copyLicenseTextFile(repositoryRoot *os.Root, licensePath string) error { - // check licensePath is within the repository - relPath := filepath.Clean(licensePath) - if filepath.IsAbs(licensePath) { +// copyLicenseTextFile checks the targetLicencePath and copies the license file from the repository root if needed. +// If the targetLicensePath already exists, it will skip copying. +// If the targetLicensePath does not exist, it will look for a source license file in the repository root and copy it to the targetLicensePath. +// The source license file name can be overridden by setting the REPOSITORY_LICENSE environment variable. +func copyLicenseTextFile(repositoryRoot *os.Root, targetLicensePath string) error { + // targetLicensePath is expected to be either an absolute path or a path relative to the repository root + // when allowing builds outside of the repository root, we need to handle this check for targetLicensePath + relPath := filepath.Clean(targetLicensePath) + if filepath.IsAbs(targetLicensePath) { var err error - relPath, err = filepath.Rel(repositoryRoot.Name(), licensePath) + relPath, err = filepath.Rel(repositoryRoot.Name(), targetLicensePath) if err != nil { - return fmt.Errorf("failed to get relative path for licensePath (%s) from repositoryRoot (%s): %w", licensePath, repositoryRoot.Name(), err) + return fmt.Errorf("failed to get relative path for licensePath (%s) from repositoryRoot (%s): %w", targetLicensePath, repositoryRoot.Name(), err) } } else { // if relative path, make it relative to the repo root - licensePath = filepath.Join(repositoryRoot.Name(), licensePath) + targetLicensePath = filepath.Join(repositoryRoot.Name(), targetLicensePath) } // if the given path exists, skip copying @@ -323,11 +324,11 @@ func copyLicenseTextFile(repositoryRoot *os.Root, licensePath string) error { } // if the given path does not exist, continue if err != nil && !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("can't check license path (%s): %w", licensePath, err) + return fmt.Errorf("can't check license path (%s): %w", targetLicensePath, err) } // if the given path exists, but is a directory, return an error if info != nil && info.IsDir() { - return fmt.Errorf("license path (%s) is a directory", licensePath) + return fmt.Errorf("license path (%s) is a directory", targetLicensePath) } // lookup for the license file in the repository @@ -337,6 +338,7 @@ func copyLicenseTextFile(repositoryRoot *os.Root, licensePath string) error { repositoryLicenseTextFileName = licenseTextFileName } + // sourceLicensePath is an absolute path to the repositoryLicenseTextFileName in the repository root sourceLicensePath, err := findRepositoryLicensePath(repositoryRoot, repositoryLicenseTextFileName) if !userDefined && errors.Is(err, os.ErrNotExist) { logger.Debug("No license text file is included in package") @@ -347,7 +349,7 @@ func copyLicenseTextFile(repositoryRoot *os.Root, licensePath string) error { } logger.Infof("License text found in %q will be included in package", sourceLicensePath) - err = sh.Copy(licensePath, sourceLicensePath) + err = sh.Copy(targetLicensePath, sourceLicensePath) if err != nil { return fmt.Errorf("can't copy license from repository: %w", err) } From 563522ffdeedef3c6d9ab57264041a46480fae16 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 11:28:01 +0200 Subject: [PATCH 32/46] enhance findRepositoryLicensePath function to check for empty license files --- internal/builder/packages.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 3f7026cb6d..dc1b089d66 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -378,7 +378,7 @@ func createBuildDirectory(dirs ...string) (string, error) { return buildDir, nil } -// findRepositoryLicensePath checks if a license file exists at the specified path. +// findRepositoryLicensePath checks if a license file exists at the specified path and its not empty. // If the file exists, it returns the path; otherwise, it returns an error indicating // that the repository license could not be found. // @@ -391,9 +391,13 @@ func createBuildDirectory(dirs ...string) (string, error) { // string - the license file absolute path if found. // error - an error if the license file does not exist. func findRepositoryLicensePath(repositoryRoot *os.Root, repositoryLicenseTextFileName string) (string, error) { - if _, err := repositoryRoot.Stat(repositoryLicenseTextFileName); err != nil { + bytes, err := repositoryRoot.ReadFile(repositoryLicenseTextFileName) + if err != nil { return "", fmt.Errorf("failed to find repository license: %w", err) } + if len(bytes) == 0 { + return "", fmt.Errorf("repository license file is empty") + } path := filepath.Join(repositoryRoot.Name(), repositoryLicenseTextFileName) return path, nil } From bfe509e6e8222f9c01de2bb83cf2e6b98154c179 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 11:31:21 +0200 Subject: [PATCH 33/46] improve path handling in TestFindRepositoryLicense and TestCopyLicenseTextFile --- internal/builder/packages_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index 50e356e8ec..42d9d2b5c4 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -14,7 +14,6 @@ import ( ) func TestFindRepositoryLicense(t *testing.T) { - t.Run("FileExists", func(t *testing.T) { root, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) @@ -46,7 +45,7 @@ func TestFindRepositoryLicense(t *testing.T) { require.NoError(t, err) defer root.Close() - path, err := findRepositoryLicensePath(root, "../../out.txt") + path, err := findRepositoryLicensePath(root, filepath.Join("..", "..", "out.txt")) require.Error(t, err) assert.Empty(t, path) assert.ErrorContains(t, err, "path escapes from parent") @@ -57,7 +56,6 @@ func TestFindRepositoryLicense(t *testing.T) { func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { t.Run("ExistingFile_RelPath", func(t *testing.T) { - repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) defer repositoryRoot.Close() @@ -78,7 +76,6 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { }) t.Run("ExistingFile_AbsPath", func(t *testing.T) { - repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) defer repositoryRoot.Close() From 59436db80d3571e9cbe1cf3435c4fe37007b7c86 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 11:34:53 +0200 Subject: [PATCH 34/46] rename test function and update comment for clarity --- internal/docs/links_map_test.go | 2 +- internal/files/linkedfiles.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/docs/links_map_test.go b/internal/docs/links_map_test.go index 2db803cfc9..ef3c5f3c63 100644 --- a/internal/docs/links_map_test.go +++ b/internal/docs/links_map_test.go @@ -89,7 +89,7 @@ func TestRenderLink(t *testing.T) { } } -func Test_linksDefinitionsFilePath(t *testing.T) { +func TestLinksDefinitionsFilePath(t *testing.T) { t.Run("env var set and file exists", func(t *testing.T) { repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) diff --git a/internal/files/linkedfiles.go b/internal/files/linkedfiles.go index 8859de8e47..ad9c25fa29 100644 --- a/internal/files/linkedfiles.go +++ b/internal/files/linkedfiles.go @@ -187,7 +187,7 @@ type Link struct { } // newLinkedFile creates a new Link struct from the given absolute path to a link file. -// root is the repository root, used to validate paths and access files securely +// repositoryRoot is the repository root, used to validate paths and access files securely func newLinkedFile(repositoryRoot *os.Root, linkFilePath string) (*Link, error) { workDir := filepath.Dir(linkFilePath) From 248a70482dba9cb030e06fbdbc839f95a9249392 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 11:36:45 +0200 Subject: [PATCH 35/46] remove unnecessary blank lines in test files for improved readability --- internal/files/linkedfiles_test.go | 3 +-- internal/resources/fleetpolicy_test.go | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/files/linkedfiles_test.go b/internal/files/linkedfiles_test.go index 2cf0e7a487..3900a425ab 100644 --- a/internal/files/linkedfiles_test.go +++ b/internal/files/linkedfiles_test.go @@ -499,7 +499,6 @@ func TestNewLinkedFileRejectsPathTraversal(t *testing.T) { } func createPackageStructure(t *testing.T, dirs ...string) string { - packageDir := filepath.Join(dirs...) err := os.MkdirAll(packageDir, 0755) require.NoError(t, err) @@ -909,7 +908,7 @@ func TestLinksFS_WorkDirValidation(t *testing.T) { } } -func Test_newLinkedFile(t *testing.T) { +func TestNewLinkedFile(t *testing.T) { repositoryRoot := t.TempDir() repositoryRootHandle, err := os.OpenRoot(repositoryRoot) diff --git a/internal/resources/fleetpolicy_test.go b/internal/resources/fleetpolicy_test.go index 8920121f66..ca18647838 100644 --- a/internal/resources/fleetpolicy_test.go +++ b/internal/resources/fleetpolicy_test.go @@ -20,7 +20,6 @@ import ( ) func TestRequiredProviderFleetPolicy(t *testing.T) { - repositoryRoot, err := files.FindRepositoryRoot() require.NoError(t, err) t.Cleanup(func() { _ = repositoryRoot.Close() }) From c74c6c847ed001486a74de6bdcbdb49c766906cb Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 11:41:31 +0200 Subject: [PATCH 36/46] rename PackageRoot to PackageRootPath for consistency across builder and installer --- cmd/build.go | 12 +++++----- cmd/install.go | 10 ++++---- internal/benchrunner/runners/rally/runner.go | 8 +++---- internal/benchrunner/runners/stream/runner.go | 8 +++---- internal/builder/packages.go | 24 +++++++++---------- internal/packages/archetype/package_test.go | 18 +++++++------- internal/packages/installer/factory.go | 22 ++++++++--------- internal/resources/fleetpackage.go | 22 ++++++++--------- internal/resources/fleetpackage_test.go | 14 +++++------ internal/resources/fleetpolicy_test.go | 6 ++--- internal/testrunner/runners/asset/tester.go | 8 +++---- internal/testrunner/runners/policy/runner.go | 4 ++-- internal/testrunner/runners/system/runner.go | 8 +++---- 13 files changed, 82 insertions(+), 82 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index f702426fd5..b6f6006967 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -89,12 +89,12 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } target, err := builder.BuildPackage(cmd.Context(), builder.BuildOptions{ - PackageRoot: packageRoot, - BuildDir: buildDir, - CreateZip: createZip, - SignPackage: signPackage, - SkipValidation: skipValidation, - RepositoryRoot: repositoryRoot, + PackageRootPath: packageRoot, + BuildDir: buildDir, + CreateZip: createZip, + SignPackage: signPackage, + SkipValidation: skipValidation, + RepositoryRoot: repositoryRoot, }) if err != nil { return fmt.Errorf("building package failed: %w", err) diff --git a/cmd/install.go b/cmd/install.go index cb2f4d1e43..e99c57f2d9 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -84,11 +84,11 @@ func installCommandAction(cmd *cobra.Command, _ []string) error { } installer, err := installer.NewForPackage(cmd.Context(), installer.Options{ - Kibana: kibanaClient, - RootPath: packageRootPath, - SkipValidation: skipValidation, - ZipPath: zipPathFile, - RepositoryRoot: repositoryRoot, + Kibana: kibanaClient, + PackageRootPath: packageRootPath, + SkipValidation: skipValidation, + ZipPath: zipPathFile, + RepositoryRoot: repositoryRoot, }) if err != nil { return fmt.Errorf("package installation failed: %w", err) diff --git a/internal/benchrunner/runners/rally/runner.go b/internal/benchrunner/runners/rally/runner.go index f6262b5175..99840ba683 100644 --- a/internal/benchrunner/runners/rally/runner.go +++ b/internal/benchrunner/runners/rally/runner.go @@ -486,10 +486,10 @@ func (r *runner) installPackageFromRegistry(ctx context.Context, packageName, pa func (r *runner) installPackageFromPackageRoot(ctx context.Context) error { logger.Debug("Installing package...") installer, err := installer.NewForPackage(ctx, installer.Options{ - Kibana: r.options.KibanaClient, - RootPath: r.options.PackageRootPath, - SkipValidation: true, - RepositoryRoot: r.options.RepositoryRoot, + Kibana: r.options.KibanaClient, + PackageRootPath: r.options.PackageRootPath, + SkipValidation: true, + RepositoryRoot: r.options.RepositoryRoot, }) if err != nil { return fmt.Errorf("failed to initialize package installer: %w", err) diff --git a/internal/benchrunner/runners/stream/runner.go b/internal/benchrunner/runners/stream/runner.go index a5fbf6050b..fb2d64f029 100644 --- a/internal/benchrunner/runners/stream/runner.go +++ b/internal/benchrunner/runners/stream/runner.go @@ -253,10 +253,10 @@ func (r *runner) installPackage(ctx context.Context) error { func (r *runner) installPackageFromPackageRoot(ctx context.Context) error { logger.Debug("Installing package...") installer, err := installer.NewForPackage(ctx, installer.Options{ - Kibana: r.options.KibanaClient, - RootPath: r.options.PackageRootPath, - SkipValidation: true, - RepositoryRoot: r.options.RepositoryRoot, + Kibana: r.options.KibanaClient, + PackageRootPath: r.options.PackageRootPath, + SkipValidation: true, + RepositoryRoot: r.options.RepositoryRoot, }) if err != nil { diff --git a/internal/builder/packages.go b/internal/builder/packages.go index dc1b089d66..4615e238ec 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -26,9 +26,9 @@ const licenseTextFileName = "LICENSE.txt" var repositoryLicenseEnv = environment.WithElasticPackagePrefix("REPOSITORY_LICENSE") type BuildOptions struct { - PackageRoot string - BuildDir string - RepositoryRoot *os.Root + PackageRootPath string + BuildDir string + RepositoryRoot *os.Root CreateZip bool SignPackage bool @@ -164,7 +164,7 @@ func FindBuildPackagesDirectory() (string, bool, error) { // BuildPackage function builds the package. func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { - destinationDir, err := BuildPackagesDirectory(options.PackageRoot, options.BuildDir) + destinationDir, err := BuildPackagesDirectory(options.PackageRootPath, options.BuildDir) if err != nil { return "", fmt.Errorf("can't locate build directory: %w", err) } @@ -176,8 +176,8 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { return "", fmt.Errorf("clearing package contents failed: %w", err) } - logger.Debugf("Copy package content (source: %s)", options.PackageRoot) - err = files.CopyWithoutDev(options.PackageRoot, destinationDir) + logger.Debugf("Copy package content (source: %s)", options.PackageRootPath) + err = files.CopyWithoutDev(options.PackageRootPath, destinationDir) if err != nil { return "", fmt.Errorf("copying package contents failed: %w", err) } @@ -196,18 +196,18 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { } logger.Debug("Resolve external fields") - err = resolveExternalFields(options.PackageRoot, destinationDir) + err = resolveExternalFields(options.PackageRootPath, destinationDir) if err != nil { return "", fmt.Errorf("resolving external fields failed: %w", err) } - err = addDynamicMappings(options.PackageRoot, destinationDir) + err = addDynamicMappings(options.PackageRootPath, destinationDir) if err != nil { return "", fmt.Errorf("adding dynamic mappings: %w", err) } logger.Debug("Include linked files") - linksFS, err := files.CreateLinksFSFromPath(options.RepositoryRoot, options.PackageRoot) + linksFS, err := files.CreateLinksFSFromPath(options.RepositoryRoot, options.PackageRootPath) if err != nil { return "", fmt.Errorf("creating links filesystem failed: %w", err) } @@ -247,7 +247,7 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { func buildZippedPackage(ctx context.Context, options BuildOptions, destinationDir string) (string, error) { logger.Debug("Build zipped package") - zippedPackagePath, err := buildPackagesZipPath(options.PackageRoot) + zippedPackagePath, err := buildPackagesZipPath(options.PackageRootPath) if err != nil { return "", fmt.Errorf("can't evaluate path for the zipped package: %w", err) } @@ -282,9 +282,9 @@ func buildZippedPackage(ctx context.Context, options BuildOptions, destinationDi func signZippedPackage(options BuildOptions, zippedPackagePath string) error { logger.Debug("Sign the package") - m, err := packages.ReadPackageManifestFromPackageRoot(options.PackageRoot) + m, err := packages.ReadPackageManifestFromPackageRoot(options.PackageRootPath) if err != nil { - return fmt.Errorf("reading package manifest failed (path: %s): %w", options.PackageRoot, err) + return fmt.Errorf("reading package manifest failed (path: %s): %w", options.PackageRootPath, err) } err = files.Sign(zippedPackagePath, files.SignOptions{ diff --git a/internal/packages/archetype/package_test.go b/internal/packages/archetype/package_test.go index 944a91b76d..5f739d91ac 100644 --- a/internal/packages/archetype/package_test.go +++ b/internal/packages/archetype/package_test.go @@ -96,32 +96,32 @@ func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDe } } -func buildPackage(t *testing.T, repositoryRoot *os.Root, packageRoot string) error { +func buildPackage(t *testing.T, repositoryRoot *os.Root, packageRootPath string) error { buildDir := filepath.Join(repositoryRoot.Name(), "build") err := os.MkdirAll(buildDir, 0o755) require.NoError(t, err) - _, err = docs.UpdateReadmes(repositoryRoot, packageRoot, buildDir) + _, err = docs.UpdateReadmes(repositoryRoot, packageRootPath, buildDir) if err != nil { return err } _, err = builder.BuildPackage(t.Context(), builder.BuildOptions{ - PackageRoot: packageRoot, - BuildDir: buildDir, - RepositoryRoot: repositoryRoot, + PackageRootPath: packageRootPath, + BuildDir: buildDir, + RepositoryRoot: repositoryRoot, }) return err } -func checkPackage(t *testing.T, repositoryRoot *os.Root, packageRoot string, valid bool) { - err := buildPackage(t, repositoryRoot, packageRoot) +func checkPackage(t *testing.T, repositoryRoot *os.Root, packageRootPath string, valid bool) { + err := buildPackage(t, repositoryRoot, packageRootPath) if !valid { assert.Error(t, err) return } require.NoError(t, err) - manifest, err := packages.ReadPackageManifestFromPackageRoot(packageRoot) + manifest, err := packages.ReadPackageManifestFromPackageRoot(packageRootPath) require.NoError(t, err) // Running in subtests because manifest subobjects can be pointers that can panic when dereferenced by assertions. @@ -135,7 +135,7 @@ func checkPackage(t *testing.T, repositoryRoot *os.Root, packageRoot string, val if manifest.Type == "integration" { t.Run("integration", func(t *testing.T) { - ds, err := filepath.Glob(filepath.Join(packageRoot, "data_stream", "*")) + ds, err := filepath.Glob(filepath.Join(packageRootPath, "data_stream", "*")) require.NoError(t, err) for _, d := range ds { manifest, err := packages.ReadDataStreamManifest(filepath.Join(d, "manifest.yml")) diff --git a/internal/packages/installer/factory.go b/internal/packages/installer/factory.go index e3d120fb4c..5766bc6b32 100644 --- a/internal/packages/installer/factory.go +++ b/internal/packages/installer/factory.go @@ -34,11 +34,11 @@ type Installer interface { // Options are the parameters used to build an installer. type Options struct { - Kibana *kibana.Client - RootPath string // Root path of the package to be installed. - ZipPath string - SkipValidation bool - RepositoryRoot *os.Root // Root of the repository where package source code is located. + Kibana *kibana.Client + PackageRootPath string // Root path of the package to be installed. + ZipPath string + SkipValidation bool + RepositoryRoot *os.Root // Root of the repository where package source code is located. } // NewForPackage creates a new installer for a package, given its root path, or its prebuilt zip. @@ -50,7 +50,7 @@ func NewForPackage(ctx context.Context, options Options) (Installer, error) { if options.Kibana == nil { return nil, errors.New("missing kibana client") } - if options.RootPath == "" && options.ZipPath == "" { + if options.PackageRootPath == "" && options.ZipPath == "" { return nil, errors.New("missing package root path or pre-built zip package") } if options.RepositoryRoot == nil { @@ -86,11 +86,11 @@ func NewForPackage(ctx context.Context, options Options) (Installer, error) { } target, err := builder.BuildPackage(ctx, builder.BuildOptions{ - PackageRoot: options.RootPath, - CreateZip: supportsUploadZip, - SignPackage: false, - SkipValidation: options.SkipValidation, - RepositoryRoot: options.RepositoryRoot, + PackageRootPath: options.PackageRootPath, + CreateZip: supportsUploadZip, + SignPackage: false, + SkipValidation: options.SkipValidation, + RepositoryRoot: options.RepositoryRoot, }) if err != nil { return nil, fmt.Errorf("failed to build package: %v", err) diff --git a/internal/resources/fleetpackage.go b/internal/resources/fleetpackage.go index 0c0389d48c..7fab894ae2 100644 --- a/internal/resources/fleetpackage.go +++ b/internal/resources/fleetpackage.go @@ -22,8 +22,8 @@ type FleetPackage struct { // Provider is the name of the provider to use, defaults to "kibana". Provider string - // RootPath is the root of the package source to install. - RootPath string + // PackageRootPath is the root of the package source to install. + PackageRootPath string // RepositoryRoot is the root of the repository. RepositoryRoot *os.Root @@ -37,7 +37,7 @@ type FleetPackage struct { } func (f *FleetPackage) String() string { - return fmt.Sprintf("[FleetPackage:%s:%s]", f.Provider, f.RootPath) + return fmt.Sprintf("[FleetPackage:%s:%s]", f.Provider, f.PackageRootPath) } func (f *FleetPackage) provider(ctx resource.Context) (*KibanaProvider, error) { @@ -60,10 +60,10 @@ func (f *FleetPackage) installer(ctx resource.Context) (installer.Installer, err } return installer.NewForPackage(ctx, installer.Options{ - Kibana: provider.Client, - RootPath: f.RootPath, - SkipValidation: true, - RepositoryRoot: f.RepositoryRoot, + Kibana: provider.Client, + PackageRootPath: f.PackageRootPath, + SkipValidation: true, + RepositoryRoot: f.RepositoryRoot, }) } @@ -73,9 +73,9 @@ func (f *FleetPackage) Get(ctx resource.Context) (current resource.ResourceState return nil, err } - manifest, err := packages.ReadPackageManifestFromPackageRoot(f.RootPath) + manifest, err := packages.ReadPackageManifestFromPackageRoot(f.PackageRootPath) if err != nil { - return nil, fmt.Errorf("failed to read manifest from %s: %w", f.RootPath, err) + return nil, fmt.Errorf("failed to read manifest from %s: %w", f.PackageRootPath, err) } fleetPackage, err := provider.Client.GetPackage(ctx, manifest.Name) @@ -117,7 +117,7 @@ func (f *FleetPackage) Create(ctx resource.Context) error { } // Using uninstallPachage instead of f.uninstall because we want to pass a context without cancellation. - uninstallErr = uninstallPackage(context.WithoutCancel(ctx), provider.Client, f.RootPath) + uninstallErr = uninstallPackage(context.WithoutCancel(ctx), provider.Client, f.PackageRootPath) if uninstallErr != nil { return fmt.Errorf("failed to uninstall package (%w) after installation failed: %w", uninstallErr, err) } @@ -134,7 +134,7 @@ func (f *FleetPackage) uninstall(ctx resource.Context) error { return err } - return uninstallPackage(ctx, provider.Client, f.RootPath) + return uninstallPackage(ctx, provider.Client, f.PackageRootPath) } func uninstallPackage(ctx context.Context, client *kibana.Client, rootPath string) error { diff --git a/internal/resources/fleetpackage_test.go b/internal/resources/fleetpackage_test.go index 01d8331f33..7749ad20e6 100644 --- a/internal/resources/fleetpackage_test.go +++ b/internal/resources/fleetpackage_test.go @@ -28,8 +28,8 @@ func TestRequiredProvider(t *testing.T) { _, err = manager.Apply(resource.Resources{ &FleetPackage{ - RootPath: "../../test/packages/parallel/nginx", - RepositoryRoot: repositoryRoot, + PackageRootPath: "../../test/packages/parallel/nginx", + RepositoryRoot: repositoryRoot, }, }) if assert.Error(t, err) { @@ -61,8 +61,8 @@ func TestPackageLifecycle(t *testing.T) { packageRootPath := filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", c.name) fleetPackage := FleetPackage{ - RootPath: packageRootPath, - RepositoryRoot: repositoryRoot, + PackageRootPath: packageRootPath, + RepositoryRoot: repositoryRoot, } manager := resource.NewManager() manager.RegisterProvider(DefaultKibanaProviderName, &KibanaProvider{Client: kibanaClient}) @@ -89,9 +89,9 @@ func TestSystemPackageIsNotRemoved(t *testing.T) { t.Cleanup(func() { _ = repositoryRoot.Close() }) fleetPackage := FleetPackage{ - RootPath: "../../test/packages/parallel/system", - Absent: true, - RepositoryRoot: repositoryRoot, + PackageRootPath: "../../test/packages/parallel/system", + Absent: true, + RepositoryRoot: repositoryRoot, } manager := resource.NewManager() manager.RegisterProvider(DefaultKibanaProviderName, &KibanaProvider{Client: kibanaClient}) diff --git a/internal/resources/fleetpolicy_test.go b/internal/resources/fleetpolicy_test.go index ca18647838..2f56c96feb 100644 --- a/internal/resources/fleetpolicy_test.go +++ b/internal/resources/fleetpolicy_test.go @@ -121,9 +121,9 @@ func withPackageResources(agentPolicy *FleetAgentPolicy) resource.Resources { var resources resource.Resources for _, policy := range agentPolicy.PackagePolicies { resources = append(resources, &FleetPackage{ - RootPath: policy.RootPath, - Absent: agentPolicy.Absent, - RepositoryRoot: agentPolicy.RepositoryRoot, + PackageRootPath: policy.RootPath, + Absent: agentPolicy.Absent, + RepositoryRoot: agentPolicy.RepositoryRoot, }) } return append(resources, agentPolicy) diff --git a/internal/testrunner/runners/asset/tester.go b/internal/testrunner/runners/asset/tester.go index ba12191359..b0eabb811a 100644 --- a/internal/testrunner/runners/asset/tester.go +++ b/internal/testrunner/runners/asset/tester.go @@ -84,10 +84,10 @@ func (r *tester) Run(ctx context.Context) ([]testrunner.TestResult, error) { func (r *tester) resources(installedPackage bool) resources.Resources { return resources.Resources{ &resources.FleetPackage{ - RootPath: r.packageRootPath, - Absent: !installedPackage, - Force: installedPackage, // Force re-installation, in case there are code changes in the same package version. - RepositoryRoot: r.repositoryRoot, + PackageRootPath: r.packageRootPath, + Absent: !installedPackage, + Force: installedPackage, // Force re-installation, in case there are code changes in the same package version. + RepositoryRoot: r.repositoryRoot, }, } } diff --git a/internal/testrunner/runners/policy/runner.go b/internal/testrunner/runners/policy/runner.go index 40690d16e2..762a0b4e07 100644 --- a/internal/testrunner/runners/policy/runner.go +++ b/internal/testrunner/runners/policy/runner.go @@ -161,8 +161,8 @@ func (r *runner) Type() testrunner.TestType { func (r *runner) setupSuite(ctx context.Context, manager *resources.Manager) (cleanup func(ctx context.Context) error, err error) { packageResource := resources.FleetPackage{ - RootPath: r.packageRootPath, - RepositoryRoot: r.repositoryRoot, + PackageRootPath: r.packageRootPath, + RepositoryRoot: r.repositoryRoot, } setupResources := resources.Resources{ &packageResource, diff --git a/internal/testrunner/runners/system/runner.go b/internal/testrunner/runners/system/runner.go index d5cb82135f..669089f125 100644 --- a/internal/testrunner/runners/system/runner.go +++ b/internal/testrunner/runners/system/runner.go @@ -286,10 +286,10 @@ func (r *runner) Type() testrunner.TestType { func (r *runner) resources(opts resourcesOptions) resources.Resources { return resources.Resources{ &resources.FleetPackage{ - RootPath: r.packageRootPath, - Absent: !opts.installedPackage, - Force: opts.installedPackage, // Force re-installation, in case there are code changes in the same package version. - RepositoryRoot: r.repositoryRoot, + PackageRootPath: r.packageRootPath, + Absent: !opts.installedPackage, + Force: opts.installedPackage, // Force re-installation, in case there are code changes in the same package version. + RepositoryRoot: r.repositoryRoot, }, } } From 68ed0d81ea6553213d33ecc65bdf2a5cc567b939 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 14:29:39 +0200 Subject: [PATCH 37/46] add repository root handling in benchmark and test runner commands --- cmd/benchmark.go | 6 ++++++ cmd/testrunner.go | 6 ++++++ internal/benchrunner/runners/pipeline/options.go | 9 +++++++++ internal/benchrunner/runners/pipeline/runner.go | 2 +- internal/elasticsearch/ingest/datastream.go | 14 ++++---------- internal/testrunner/runners/pipeline/runner.go | 5 +++++ internal/testrunner/runners/pipeline/tester.go | 7 +++++-- 7 files changed, 36 insertions(+), 13 deletions(-) diff --git a/cmd/benchmark.go b/cmd/benchmark.go index f44735d1e8..cb7588ffbe 100644 --- a/cmd/benchmark.go +++ b/cmd/benchmark.go @@ -137,6 +137,11 @@ func pipelineCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(err, cobraext.BenchNumTopProcsFlagName) } + repositoryRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) @@ -201,6 +206,7 @@ func pipelineCommandAction(cmd *cobra.Command, args []string) error { pipeline.WithESAPI(esClient.API), pipeline.WithNumTopProcs(numTopProcs), pipeline.WithFormat(reportFormat), + pipeline.WithRepositoryRoot(repositoryRoot), ) runner := pipeline.NewPipelineBenchmark(opts) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index 789900300a..db3b9bcffe 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -343,6 +343,11 @@ func testRunnerPipelineCommandAction(cmd *cobra.Command, args []string) error { return cobraext.FlagParsingError(err, cobraext.DeferCleanupFlagName) } + repositoryRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + packageRootPath, err := packages.FindPackageRoot() if err != nil { return fmt.Errorf("locating package root failed: %w", err) @@ -386,6 +391,7 @@ func testRunnerPipelineCommandAction(cmd *cobra.Command, args []string) error { CoverageType: testCoverageFormat, DeferCleanup: deferCleanup, GlobalTestConfig: globalTestConfig.Pipeline, + RepositoryRoot: repositoryRoot, }) results, err := testrunner.RunSuite(ctx, runner) diff --git a/internal/benchrunner/runners/pipeline/options.go b/internal/benchrunner/runners/pipeline/options.go index c2699d6be4..90db1964f2 100644 --- a/internal/benchrunner/runners/pipeline/options.go +++ b/internal/benchrunner/runners/pipeline/options.go @@ -5,6 +5,8 @@ package pipeline import ( + "os" + "github.com/elastic/elastic-package/internal/elasticsearch" "github.com/elastic/elastic-package/internal/testrunner" ) @@ -17,6 +19,7 @@ type Options struct { API *elasticsearch.API NumTopProcs int Format Format + RepositoryRoot *os.Root } type OptionFunc func(*Options) @@ -64,3 +67,9 @@ func WithBenchmarkName(name string) OptionFunc { opts.BenchName = name } } + +func WithRepositoryRoot(root *os.Root) OptionFunc { + return func(opts *Options) { + opts.RepositoryRoot = root + } +} diff --git a/internal/benchrunner/runners/pipeline/runner.go b/internal/benchrunner/runners/pipeline/runner.go index 17b6532d9d..2cc8db9e28 100644 --- a/internal/benchrunner/runners/pipeline/runner.go +++ b/internal/benchrunner/runners/pipeline/runner.go @@ -48,7 +48,7 @@ func (r *runner) SetUp(ctx context.Context) error { return errors.New("data stream root not found") } - r.entryPipeline, r.pipelines, err = ingest.InstallDataStreamPipelines(ctx, r.options.API, dataStreamPath) + r.entryPipeline, r.pipelines, err = ingest.InstallDataStreamPipelines(ctx, r.options.API, dataStreamPath, r.options.RepositoryRoot) if err != nil { return fmt.Errorf("installing ingest pipelines failed: %w", err) } diff --git a/internal/elasticsearch/ingest/datastream.go b/internal/elasticsearch/ingest/datastream.go index f8c8555913..3bd9280252 100644 --- a/internal/elasticsearch/ingest/datastream.go +++ b/internal/elasticsearch/ingest/datastream.go @@ -49,7 +49,7 @@ type RerouteProcessor struct { Namespace []string `yaml:"namespace"` } -func InstallDataStreamPipelines(ctx context.Context, api *elasticsearch.API, dataStreamPath string) (string, []Pipeline, error) { +func InstallDataStreamPipelines(ctx context.Context, api *elasticsearch.API, dataStreamPath string, repositoryRoot *os.Root) (string, []Pipeline, error) { dataStreamManifest, err := packages.ReadDataStreamManifest(filepath.Join(dataStreamPath, packages.DataStreamManifestFile)) if err != nil { return "", nil, fmt.Errorf("reading data stream manifest failed: %w", err) @@ -58,7 +58,7 @@ func InstallDataStreamPipelines(ctx context.Context, api *elasticsearch.API, dat nonce := time.Now().UnixNano() mainPipeline := GetPipelineNameWithNonce(dataStreamManifest.GetPipelineNameOrDefault(), nonce) - pipelines, err := LoadIngestPipelineFiles(dataStreamPath, nonce) + pipelines, err := LoadIngestPipelineFiles(dataStreamPath, nonce, repositoryRoot) if err != nil { return "", nil, fmt.Errorf("loading ingest pipeline files failed: %w", err) } @@ -73,7 +73,7 @@ func InstallDataStreamPipelines(ctx context.Context, api *elasticsearch.API, dat // LoadIngestPipelineFiles returns the set of pipelines found in the directory // elasticsearch/ingest_pipeline under the provided data stream path. The names // of the pipelines are decorated with the provided nonce. -func LoadIngestPipelineFiles(dataStreamPath string, nonce int64) ([]Pipeline, error) { +func LoadIngestPipelineFiles(dataStreamPath string, nonce int64, repositoryRoot *os.Root) ([]Pipeline, error) { elasticsearchPath := filepath.Join(dataStreamPath, "elasticsearch", "ingest_pipeline") var pipelineFiles []string @@ -85,13 +85,7 @@ func LoadIngestPipelineFiles(dataStreamPath string, nonce int64) ([]Pipeline, er pipelineFiles = append(pipelineFiles, files...) } - root, err := files.FindRepositoryRoot() - if err != nil { - return nil, fmt.Errorf("finding repository root failed: %w", err) - } - defer root.Close() - - linksFS, err := files.CreateLinksFSFromPath(root, elasticsearchPath) + linksFS, err := files.CreateLinksFSFromPath(repositoryRoot, elasticsearchPath) if err != nil { return nil, fmt.Errorf("creating links filesystem failed: %w", err) } diff --git a/internal/testrunner/runners/pipeline/runner.go b/internal/testrunner/runners/pipeline/runner.go index 01de91c644..f556f67323 100644 --- a/internal/testrunner/runners/pipeline/runner.go +++ b/internal/testrunner/runners/pipeline/runner.go @@ -35,6 +35,8 @@ type runner struct { coverageType string deferCleanup time.Duration globalTestConfig testrunner.GlobalRunnerTestConfig + + repositoryRoot *os.Root } type PipelineTestRunnerOptions struct { @@ -48,6 +50,7 @@ type PipelineTestRunnerOptions struct { CoverageType string DeferCleanup time.Duration GlobalTestConfig testrunner.GlobalRunnerTestConfig + RepositoryRoot *os.Root } func NewPipelineTestRunner(options PipelineTestRunnerOptions) *runner { @@ -62,6 +65,7 @@ func NewPipelineTestRunner(options PipelineTestRunnerOptions) *runner { coverageType: options.CoverageType, deferCleanup: options.DeferCleanup, globalTestConfig: options.GlobalTestConfig, + repositoryRoot: options.RepositoryRoot, } return &runner } @@ -138,6 +142,7 @@ func (r *runner) GetTests(ctx context.Context) ([]testrunner.Tester, error) { API: r.esAPI, TestCaseFile: caseFile, GlobalTestConfig: r.globalTestConfig, + RepositoryRoot: r.repositoryRoot, }) if err != nil { return nil, fmt.Errorf("failed to create pipeline tester: %w", err) diff --git a/internal/testrunner/runners/pipeline/tester.go b/internal/testrunner/runners/pipeline/tester.go index 0ab0c348e0..ea96741609 100644 --- a/internal/testrunner/runners/pipeline/tester.go +++ b/internal/testrunner/runners/pipeline/tester.go @@ -53,7 +53,8 @@ type tester struct { runCompareResults bool - provider stack.Provider + provider stack.Provider + repositoryRoot *os.Root } type PipelineTesterOptions struct { @@ -67,6 +68,7 @@ type PipelineTesterOptions struct { CoverageType string TestCaseFile string GlobalTestConfig testrunner.GlobalRunnerTestConfig + RepositoryRoot *os.Root } func NewPipelineTester(options PipelineTesterOptions) (*tester, error) { @@ -85,6 +87,7 @@ func NewPipelineTester(options PipelineTesterOptions) (*tester, error) { withCoverage: options.WithCoverage, coverageType: options.CoverageType, globalTestConfig: options.GlobalTestConfig, + repositoryRoot: options.RepositoryRoot, } stackConfig, err := stack.LoadConfig(r.profile) @@ -168,7 +171,7 @@ func (r *tester) run(ctx context.Context) ([]testrunner.TestResult, error) { startTesting := time.Now() var entryPipeline string - entryPipeline, r.pipelines, err = ingest.InstallDataStreamPipelines(ctx, r.esAPI, dataStreamPath) + entryPipeline, r.pipelines, err = ingest.InstallDataStreamPipelines(ctx, r.esAPI, dataStreamPath, r.repositoryRoot) if err != nil { return nil, fmt.Errorf("installing ingest pipelines failed: %w", err) } From 54d85027d8ed07b2ae02c14346ab51ad08711783 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 14:46:45 +0200 Subject: [PATCH 38/46] rename newLinkMap to newEmptyLinkMap and revert related functions to use value receiver instead of pointer --- internal/docs/links_map.go | 18 +++++++++--------- internal/docs/links_map_test.go | 6 ++++-- internal/docs/readme.go | 2 +- internal/docs/readme_test.go | 8 ++++---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/internal/docs/links_map.go b/internal/docs/links_map.go index 8083304423..1af18f5029 100644 --- a/internal/docs/links_map.go +++ b/internal/docs/links_map.go @@ -27,20 +27,20 @@ type linkOptions struct { caption string } -func newLinkMap() *linkMap { - return &linkMap{ +func newEmptyLinkMap() linkMap { + return linkMap{ Links: make(map[string]string), } } -func (l *linkMap) Get(key string) (string, error) { +func (l linkMap) Get(key string) (string, error) { if url, ok := l.Links[key]; ok { return url, nil } return "", fmt.Errorf("link key not found: %s", key) } -func (l *linkMap) Add(key, value string) error { +func (l linkMap) Add(key, value string) error { if _, ok := l.Links[key]; ok { return fmt.Errorf("link key already present: %s", key) } @@ -51,25 +51,25 @@ func (l *linkMap) Add(key, value string) error { // readLinksMap reads the links definitions file from the given repository root directory, // parses its YAML contents, and returns a populated linkMap. If the links file does not exist, // it returns an empty linkMap. Returns an error if locating, reading, or unmarshalling the file fails. -func readLinksMap(linksFilePath string) (*linkMap, error) { +func readLinksMap(linksFilePath string) (linkMap, error) { // No links file, return empty map with Links initialized if linksFilePath == "" { - return newLinkMap(), nil + return newEmptyLinkMap(), nil } logger.Debugf("Using links definitions file: %s", linksFilePath) contents, err := os.ReadFile(linksFilePath) if err != nil { - return nil, fmt.Errorf("readfile failed (path: %s): %w", linksFilePath, err) + return newEmptyLinkMap(), fmt.Errorf("readfile failed (path: %s): %w", linksFilePath, err) } var lmap linkMap err = yaml.Unmarshal(contents, &lmap) if err != nil { - return nil, err + return newEmptyLinkMap(), err } - return &lmap, nil + return lmap, nil } func (l linkMap) RenderLink(key string, options linkOptions) (string, error) { diff --git a/internal/docs/links_map_test.go b/internal/docs/links_map_test.go index ef3c5f3c63..12d7737947 100644 --- a/internal/docs/links_map_test.go +++ b/internal/docs/links_map_test.go @@ -162,7 +162,8 @@ func TestReadLinksMap(t *testing.T) { missingFile := filepath.Join(tmpDir, "missing.yml") lmap, err := readLinksMap(missingFile) require.Error(t, err) - assert.Nil(t, lmap) + assert.NotNil(t, lmap) + assert.Empty(t, lmap.Links) }) t.Run("invalid YAML returns error", func(t *testing.T) { @@ -171,7 +172,8 @@ func TestReadLinksMap(t *testing.T) { require.NoError(t, os.WriteFile(filePath, []byte("not: valid: yaml: ["), 0644)) lmap, err := readLinksMap(filePath) require.Error(t, err) - assert.Nil(t, lmap) + assert.NotNil(t, lmap) + assert.Empty(t, lmap.Links) }) t.Run("valid YAML returns populated map", func(t *testing.T) { diff --git a/internal/docs/readme.go b/internal/docs/readme.go index eb43dc22af..d7e97857f7 100644 --- a/internal/docs/readme.go +++ b/internal/docs/readme.go @@ -188,7 +188,7 @@ func findReadmeTemplatePath(fileName, packageRoot string) (string, bool, error) return templatePath, true, nil } -func renderReadme(fileName, packageRoot, templatePath string, linksMap *linkMap) ([]byte, error) { +func renderReadme(fileName, packageRoot, templatePath string, linksMap linkMap) ([]byte, error) { logger.Debugf("Render %s file (package: %s, templatePath: %s)", fileName, packageRoot, templatePath) t := template.New(fileName) diff --git a/internal/docs/readme_test.go b/internal/docs/readme_test.go index 23f7f3b16c..95f7d2f58a 100644 --- a/internal/docs/readme_test.go +++ b/internal/docs/readme_test.go @@ -74,7 +74,7 @@ Introduction to the package`, } func TestRenderReadmeWithLinks(t *testing.T) { - minimumLinksMap := newLinkMap() + minimumLinksMap := newEmptyLinkMap() minimumLinksMap.Add("foo", "http://www.example.com/bar") cases := []struct { @@ -82,7 +82,7 @@ func TestRenderReadmeWithLinks(t *testing.T) { packageRoot string templatePath string readmeTemplateContents string - linksMap *linkMap + linksMap linkMap expected string }{ { @@ -159,7 +159,7 @@ An example event for ` + "`example`" + ` looks as following: }, } - linksMap := newLinkMap() + linksMap := newEmptyLinkMap() for _, c := range cases { t.Run(c.title, func(t *testing.T) { filename := filepath.Base(c.templatePath) @@ -264,7 +264,7 @@ Introduction to the package }, } - linksMap := newLinkMap() + linksMap := newEmptyLinkMap() for _, c := range cases { t.Run(c.title, func(t *testing.T) { filename := filepath.Base(c.templatePath) From 3b32fcc22a5e19c06d7cd669b7470699f95edd2e Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 15 Oct 2025 15:42:13 +0200 Subject: [PATCH 39/46] update findRepositoryLicensePath to use os.ReadFile and improve error handling in tests --- internal/builder/packages.go | 6 ++++-- internal/builder/packages_test.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 4615e238ec..16c8bf2be9 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -391,9 +391,11 @@ func createBuildDirectory(dirs ...string) (string, error) { // string - the license file absolute path if found. // error - an error if the license file does not exist. func findRepositoryLicensePath(repositoryRoot *os.Root, repositoryLicenseTextFileName string) (string, error) { - bytes, err := repositoryRoot.ReadFile(repositoryLicenseTextFileName) + // root.ReadFile is supported after go1.25, + // https://go.dev/doc/go1.25 + bytes, err := os.ReadFile(filepath.Join(repositoryRoot.Name(), repositoryLicenseTextFileName)) if err != nil { - return "", fmt.Errorf("failed to find repository license: %w", err) + return "", fmt.Errorf("failed to read repository license: %w", err) } if len(bytes) == 0 { return "", fmt.Errorf("repository license file is empty") diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index 42d9d2b5c4..8ec0a015c2 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -48,7 +48,7 @@ func TestFindRepositoryLicense(t *testing.T) { path, err := findRepositoryLicensePath(root, filepath.Join("..", "..", "out.txt")) require.Error(t, err) assert.Empty(t, path) - assert.ErrorContains(t, err, "path escapes from parent") + assert.ErrorIs(t, err, os.ErrNotExist) }) } From 8af2c99ae14478b3c8db24903b08a2836f62b087 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 16 Oct 2025 15:32:54 +0200 Subject: [PATCH 40/46] remove RepositoryRoot references from FleetAgentPolicy, rename RootPath to PackageRootPath --- cmd/testrunner.go | 5 --- internal/resources/fleetpolicy.go | 16 +++----- internal/resources/fleetpolicy_test.go | 43 ++++++++++---------- internal/testrunner/runners/policy/runner.go | 6 --- internal/testrunner/runners/policy/tester.go | 17 +++----- 5 files changed, 33 insertions(+), 54 deletions(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index db3b9bcffe..b397581c31 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -662,10 +662,6 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("locating package root failed: %w", err) } - repositoryRoot, err := files.FindRepositoryRoot() - if err != nil { - return fmt.Errorf("locating repository root failed: %w", err) - } dataStreams, err := getDataStreamsFlag(cmd, packageRootPath) if err != nil { @@ -699,7 +695,6 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.Policy, WithCoverage: testCoverage, CoverageType: testCoverageFormat, - RepositoryRoot: repositoryRoot, }) results, err := testrunner.RunSuite(ctx, runner) diff --git a/internal/resources/fleetpolicy.go b/internal/resources/fleetpolicy.go index f000ae833c..a3890b8b40 100644 --- a/internal/resources/fleetpolicy.go +++ b/internal/resources/fleetpolicy.go @@ -7,7 +7,6 @@ package resources import ( "errors" "fmt" - "os" "slices" "strings" @@ -42,9 +41,6 @@ type FleetAgentPolicy struct { // PackagePolicies PackagePolicies []FleetPackagePolicy - - // RepoPath is the root of the repository. - RepositoryRoot *os.Root } type FleetPackagePolicy struct { @@ -60,9 +56,9 @@ type FleetPackagePolicy struct { // TemplateName is the policy template to use from the package manifest. TemplateName string - // RootPath is the root of the source of the package to configure, from it we should + // PackageRootPath is the root of the source of the package to configure, from it we should // be able to read the manifest, the data stream manifests and the policy template to use. - RootPath string + PackageRootPath string // DataStreamName is the name of the data stream to configure, for integration packages. DataStreamName string @@ -160,9 +156,9 @@ func (f *FleetAgentPolicy) Create(ctx resource.Context) error { } func createPackagePolicy(policy FleetAgentPolicy, packagePolicy FleetPackagePolicy) (*kibana.PackageDataStream, error) { - manifest, err := packages.ReadPackageManifestFromPackageRoot(packagePolicy.RootPath) + manifest, err := packages.ReadPackageManifestFromPackageRoot(packagePolicy.PackageRootPath) if err != nil { - return nil, fmt.Errorf("could not read package manifest at %s: %w", packagePolicy.RootPath, err) + return nil, fmt.Errorf("could not read package manifest at %s: %w", packagePolicy.PackageRootPath, err) } switch manifest.Type { @@ -180,9 +176,9 @@ func createIntegrationPackagePolicy(policy FleetAgentPolicy, manifest packages.P return nil, fmt.Errorf("expected data stream for integration package policy %q", packagePolicy.Name) } - dsManifest, err := packages.ReadDataStreamManifestFromPackageRoot(packagePolicy.RootPath, packagePolicy.DataStreamName) + dsManifest, err := packages.ReadDataStreamManifestFromPackageRoot(packagePolicy.PackageRootPath, packagePolicy.DataStreamName) if err != nil { - return nil, fmt.Errorf("could not read %q data stream manifest for package at %s: %w", packagePolicy.DataStreamName, packagePolicy.RootPath, err) + return nil, fmt.Errorf("could not read %q data stream manifest for package at %s: %w", packagePolicy.DataStreamName, packagePolicy.PackageRootPath, err) } policyTemplateName := packagePolicy.TemplateName diff --git a/internal/resources/fleetpolicy_test.go b/internal/resources/fleetpolicy_test.go index 2f56c96feb..4d6c547159 100644 --- a/internal/resources/fleetpolicy_test.go +++ b/internal/resources/fleetpolicy_test.go @@ -7,6 +7,7 @@ package resources import ( "errors" "fmt" + "os" "path/filepath" "testing" @@ -27,8 +28,7 @@ func TestRequiredProviderFleetPolicy(t *testing.T) { manager := resource.NewManager() _, err = manager.Apply(resource.Resources{ &FleetAgentPolicy{ - Name: "test-policy", - RepositoryRoot: repositoryRoot, + Name: "test-policy", }, }) if assert.Error(t, err) { @@ -52,9 +52,9 @@ func TestPolicyLifecycle(t *testing.T) { title: "one-package", packagePolicies: []FleetPackagePolicy{ { - Name: "nginx-1", - RootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "nginx"), - DataStreamName: "stubstatus", + Name: "nginx-1", + PackageRootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "nginx"), + DataStreamName: "stubstatus", }, }, }, @@ -62,14 +62,14 @@ func TestPolicyLifecycle(t *testing.T) { title: "multiple-packages", packagePolicies: []FleetPackagePolicy{ { - Name: "nginx-1", - RootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "nginx"), - DataStreamName: "stubstatus", + Name: "nginx-1", + PackageRootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "nginx"), + DataStreamName: "stubstatus", }, { - Name: "system-1", - RootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "system"), - DataStreamName: "process", + Name: "system-1", + PackageRootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "system"), + DataStreamName: "process", }, }, }, @@ -77,8 +77,8 @@ func TestPolicyLifecycle(t *testing.T) { title: "input-package", packagePolicies: []FleetPackagePolicy{ { - Name: "input-1", - RootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "sql_input"), + Name: "input-1", + PackageRootPath: filepath.Join(repositoryRoot.Name(), "test", "packages", "parallel", "sql_input"), }, }, }, @@ -99,16 +99,15 @@ func TestPolicyLifecycle(t *testing.T) { Description: fmt.Sprintf("Test policy for %s", c.title), Namespace: "eptest", PackagePolicies: c.packagePolicies, - RepositoryRoot: repositoryRoot, } - t.Cleanup(func() { deletePolicy(t, manager, agentPolicy) }) + t.Cleanup(func() { deletePolicy(t, manager, agentPolicy, repositoryRoot) }) - _, err := manager.Apply(withPackageResources(&agentPolicy)) + _, err := manager.Apply(withPackageResources(&agentPolicy, repositoryRoot)) assert.NoError(t, err) assertPolicyPresent(t, kibanaClient, true, agentPolicy.ID) agentPolicy.Absent = true - _, err = manager.Apply(withPackageResources(&agentPolicy)) + _, err = manager.Apply(withPackageResources(&agentPolicy, repositoryRoot)) assert.NoError(t, err) assertPolicyPresent(t, kibanaClient, false, agentPolicy.ID) }) @@ -117,13 +116,13 @@ func TestPolicyLifecycle(t *testing.T) { // withPackageResources prepares a list of resources that ensures that all required packages are installed // before creating the policy. -func withPackageResources(agentPolicy *FleetAgentPolicy) resource.Resources { +func withPackageResources(agentPolicy *FleetAgentPolicy, repostoryRoot *os.Root) resource.Resources { var resources resource.Resources for _, policy := range agentPolicy.PackagePolicies { resources = append(resources, &FleetPackage{ - PackageRootPath: policy.RootPath, + PackageRootPath: policy.PackageRootPath, Absent: agentPolicy.Absent, - RepositoryRoot: agentPolicy.RepositoryRoot, + RepositoryRoot: repostoryRoot, }) } return append(resources, agentPolicy) @@ -144,10 +143,10 @@ func assertPolicyPresent(t *testing.T, client *kibana.Client, expected bool, pol return false } -func deletePolicy(t *testing.T, manager *resource.Manager, agentPolicy FleetAgentPolicy) { +func deletePolicy(t *testing.T, manager *resource.Manager, agentPolicy FleetAgentPolicy, repositoryRoot *os.Root) { t.Helper() agentPolicy.Absent = true - _, err := manager.Apply(withPackageResources(&agentPolicy)) + _, err := manager.Apply(withPackageResources(&agentPolicy, repositoryRoot)) assert.NoError(t, err, "cleanup execution") } diff --git a/internal/testrunner/runners/policy/runner.go b/internal/testrunner/runners/policy/runner.go index 762a0b4e07..af2d4f8513 100644 --- a/internal/testrunner/runners/policy/runner.go +++ b/internal/testrunner/runners/policy/runner.go @@ -7,7 +7,6 @@ package policy import ( "context" "fmt" - "os" "path/filepath" "strings" @@ -23,7 +22,6 @@ const ( ) type runner struct { - repositoryRoot *os.Root packageRootPath string kibanaClient *kibana.Client @@ -50,7 +48,6 @@ type PolicyTestRunnerOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string - RepositoryRoot *os.Root } func NewPolicyTestRunner(options PolicyTestRunnerOptions) *runner { @@ -63,7 +60,6 @@ func NewPolicyTestRunner(options PolicyTestRunnerOptions) *runner { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, - repositoryRoot: options.RepositoryRoot, } runner.resourcesManager = resources.NewManager() runner.resourcesManager.RegisterProvider(resources.DefaultKibanaProviderName, &resources.KibanaProvider{Client: runner.kibanaClient}) @@ -147,7 +143,6 @@ func (r *runner) GetTests(ctx context.Context) ([]testrunner.Tester, error) { GlobalTestConfig: r.globalTestConfig, WithCoverage: r.withCoverage, CoverageType: r.coverageType, - RepositoryRoot: r.repositoryRoot, })) } @@ -162,7 +157,6 @@ func (r *runner) Type() testrunner.TestType { func (r *runner) setupSuite(ctx context.Context, manager *resources.Manager) (cleanup func(ctx context.Context) error, err error) { packageResource := resources.FleetPackage{ PackageRootPath: r.packageRootPath, - RepositoryRoot: r.repositoryRoot, } setupResources := resources.Resources{ &packageResource, diff --git a/internal/testrunner/runners/policy/tester.go b/internal/testrunner/runners/policy/tester.go index 021e3f81a6..40f93c0894 100644 --- a/internal/testrunner/runners/policy/tester.go +++ b/internal/testrunner/runners/policy/tester.go @@ -6,7 +6,6 @@ package policy import ( "context" - "os" "path/filepath" "strings" @@ -29,7 +28,6 @@ type tester struct { coverageType string resourcesManager *resources.Manager - repositoryRoot *os.Root } // Ensures that runner implements testrunner.Tester interface @@ -44,7 +42,6 @@ type PolicyTesterOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string - RepositoryRoot *os.Root } func NewPolicyTester(options PolicyTesterOptions) *tester { @@ -57,7 +54,6 @@ func NewPolicyTester(options PolicyTesterOptions) *tester { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, - repositoryRoot: options.RepositoryRoot, } tester.resourcesManager = resources.NewManager() tester.resourcesManager.RegisterProvider(resources.DefaultKibanaProviderName, &resources.KibanaProvider{Client: tester.kibanaClient}) @@ -117,15 +113,14 @@ func (r *tester) runTest(ctx context.Context, manager *resources.Manager, testPa Namespace: "ep", PackagePolicies: []resources.FleetPackagePolicy{ { - Name: testName + "-" + r.testFolder.Package, - RootPath: r.packageRootPath, - DataStreamName: r.testFolder.DataStream, - InputName: testConfig.Input, - Vars: testConfig.Vars, - DataStreamVars: testConfig.DataStream.Vars, + Name: testName + "-" + r.testFolder.Package, + PackageRootPath: r.packageRootPath, + DataStreamName: r.testFolder.DataStream, + InputName: testConfig.Input, + Vars: testConfig.Vars, + DataStreamVars: testConfig.DataStream.Vars, }, }, - RepositoryRoot: r.repositoryRoot, } resources := resource.Resources{&policy} _, testErr := manager.ApplyCtx(ctx, resources) From d08ed58141155a6aed9ce83187e58029948a7faa Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Thu, 16 Oct 2025 16:00:53 +0200 Subject: [PATCH 41/46] refactor copyLicenseTextFile to enforce absolute target license path and update related tests --- internal/builder/packages.go | 16 ++------- internal/builder/packages_test.go | 59 ++++++++----------------------- 2 files changed, 18 insertions(+), 57 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 16c8bf2be9..433ec577a4 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -302,22 +302,12 @@ func signZippedPackage(options BuildOptions, zippedPackagePath string) error { // If the targetLicensePath does not exist, it will look for a source license file in the repository root and copy it to the targetLicensePath. // The source license file name can be overridden by setting the REPOSITORY_LICENSE environment variable. func copyLicenseTextFile(repositoryRoot *os.Root, targetLicensePath string) error { - // targetLicensePath is expected to be either an absolute path or a path relative to the repository root - // when allowing builds outside of the repository root, we need to handle this check for targetLicensePath - relPath := filepath.Clean(targetLicensePath) - if filepath.IsAbs(targetLicensePath) { - var err error - relPath, err = filepath.Rel(repositoryRoot.Name(), targetLicensePath) - if err != nil { - return fmt.Errorf("failed to get relative path for licensePath (%s) from repositoryRoot (%s): %w", targetLicensePath, repositoryRoot.Name(), err) - } - } else { - // if relative path, make it relative to the repo root - targetLicensePath = filepath.Join(repositoryRoot.Name(), targetLicensePath) + if !filepath.IsAbs(targetLicensePath) { + return fmt.Errorf("target license path (%s) is not an absolute path", targetLicensePath) } // if the given path exists, skip copying - info, err := repositoryRoot.Stat(relPath) + info, err := os.Stat(targetLicensePath) if err == nil && !info.IsDir() { logger.Debug("License file in the package will be used") return nil diff --git a/internal/builder/packages_test.go b/internal/builder/packages_test.go index 8ec0a015c2..002b0ddda5 100644 --- a/internal/builder/packages_test.go +++ b/internal/builder/packages_test.go @@ -55,27 +55,20 @@ func TestFindRepositoryLicense(t *testing.T) { func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { - t.Run("ExistingFile_RelPath", func(t *testing.T) { + t.Run("targetLicensePath is relative", func(t *testing.T) { repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) defer repositoryRoot.Close() licensePathRel := filepath.Join("LICENSE.txt") - err = os.WriteFile(filepath.Join(repositoryRoot.Name(), licensePathRel), []byte("existing license"), 0644) - require.NoError(t, err) // Should not attempt to copy, just return nil err = copyLicenseTextFile(repositoryRoot, licensePathRel) - assert.NoError(t, err) - - // License file should remain unchanged - content, err := os.ReadFile(filepath.Join(repositoryRoot.Name(), licensePathRel)) - require.NoError(t, err) - assert.Equal(t, "existing license", string(content)) + assert.Error(t, err) }) - t.Run("ExistingFile_AbsPath", func(t *testing.T) { + t.Run("targetLicensePath is absolute", func(t *testing.T) { repositoryRoot, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) defer repositoryRoot.Close() @@ -100,22 +93,11 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { require.NoError(t, err) defer repositoryRoot.Close() - err = copyLicenseTextFile(repositoryRoot, ".") - assert.Error(t, err) - assert.Contains(t, err.Error(), "is a directory") - }) - - t.Run("StatError", func(t *testing.T) { - // Using a path that is likely invalid to trigger a stat error - invalidPath := string([]byte{0}) - - repositoryRoot, err := os.OpenRoot(t.TempDir()) - require.NoError(t, err) - defer repositoryRoot.Close() + targetLicensePath := filepath.Join(t.TempDir()) - err = copyLicenseTextFile(repositoryRoot, invalidPath) + err = copyLicenseTextFile(repositoryRoot, targetLicensePath) assert.Error(t, err) - assert.Contains(t, err.Error(), "can't check license path") + assert.Contains(t, err.Error(), "is a directory") }) t.Run("RepoLicenseDefaultFileName", func(t *testing.T) { @@ -123,14 +105,14 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { require.NoError(t, err) defer repositoryRoot.Close() - // original license file path + targetLicensePath := filepath.Join(t.TempDir(), "REPO_LICENSE.txt") err = os.WriteFile(filepath.Join(repositoryRoot.Name(), licenseTextFileName), []byte("repo license"), 0644) require.NoError(t, err) - err = copyLicenseTextFile(repositoryRoot, "REPO_LICENSE.txt") + err = copyLicenseTextFile(repositoryRoot, targetLicensePath) assert.NoError(t, err) - content, err := os.ReadFile(filepath.Join(repositoryRoot.Name(), "REPO_LICENSE.txt")) + content, err := os.ReadFile(targetLicensePath) require.NoError(t, err) assert.Equal(t, "repo license", string(content)) }) @@ -140,16 +122,18 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { require.NoError(t, err) defer repositoryRoot.Close() + targetLicensePath := filepath.Join(t.TempDir(), "REPO_LICENSE.txt") + // original license file path err = os.WriteFile(filepath.Join(repositoryRoot.Name(), "CUSTOM_LICENSE.txt"), []byte("repo license"), 0644) require.NoError(t, err) t.Setenv(repositoryLicenseEnv, "CUSTOM_LICENSE.txt") - err = copyLicenseTextFile(repositoryRoot, "REPO_LICENSE.txt") + err = copyLicenseTextFile(repositoryRoot, targetLicensePath) assert.NoError(t, err) - content, err := os.ReadFile(filepath.Join(repositoryRoot.Name(), "REPO_LICENSE.txt")) + content, err := os.ReadFile(targetLicensePath) require.NoError(t, err) assert.Equal(t, "repo license", string(content)) }) @@ -159,7 +143,8 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { require.NoError(t, err) defer repositoryRoot.Close() - err = copyLicenseTextFile(repositoryRoot, "REPO_LICENSE.txt") + targetLicensePath := filepath.Join(t.TempDir(), "REPO_LICENSE.txt") + err = copyLicenseTextFile(repositoryRoot, targetLicensePath) assert.NoError(t, err) _, err = repositoryRoot.Stat("LICENSE.txt") @@ -169,18 +154,4 @@ func TestCopyLicenseTextFile_UsesExistingLicenseFile(t *testing.T) { assert.ErrorIs(t, err, os.ErrNotExist) }) - t.Run("RepoLicensePathOutsideRepositoryRoot", func(t *testing.T) { - repositoryRoot, err := os.OpenRoot(t.TempDir()) - require.NoError(t, err) - defer repositoryRoot.Close() - - // Create a LICENSE.txt file in a different temp directory - outsideDir := t.TempDir() - outsideLicensePath := filepath.Join(outsideDir, "LICENSE.txt") - - err = copyLicenseTextFile(repositoryRoot, outsideLicensePath) - assert.Error(t, err) - assert.Contains(t, err.Error(), "path escapes from parent") - }) - } From c4a5205f4f88faa2c8d06cdd98a36a82ae3a023b Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 20 Oct 2025 11:10:48 +0200 Subject: [PATCH 42/46] refactor build package handling to inject buildDir --- cmd/build.go | 2 ++ internal/builder/packages.go | 53 ++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index b6f6006967..11f09b77d1 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -72,6 +72,8 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("locating package root failed: %w", err) } + // Currently the build directory is placed inside the repository build/ folder. + // In the future we might want to make this configurable. buildDir, err := builder.BuildDirectory() if err != nil { return fmt.Errorf("can't prepare build directory: %w", err) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 433ec577a4..58309ba872 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -26,8 +26,8 @@ const licenseTextFileName = "LICENSE.txt" var repositoryLicenseEnv = environment.WithElasticPackagePrefix("REPOSITORY_LICENSE") type BuildOptions struct { - PackageRootPath string - BuildDir string + PackageRootPath string // path to the package source content + BuildDir string // directory where all the built packages are placed and zipped packages are stored RepositoryRoot *os.Root CreateZip bool @@ -105,12 +105,8 @@ func BuildPackagesDirectory(packageRoot string, buildDir string) (string, error) return filepath.Join(buildDir, m.Name, m.Version), nil } -// buildPackagesZipPath function locates the target zipped package path. -func buildPackagesZipPath(packageRoot string) (string, error) { - buildDir, err := buildPackagesRootDirectory() - if err != nil { - return "", fmt.Errorf("can't locate build packages root directory: %w", err) - } +// buildPackagesZipPath function returns the path to zipped built package. +func buildPackagesZipPath(packageRoot, buildDir string) (string, error) { m, err := packages.ReadPackageManifestFromPackageRoot(packageRoot) if err != nil { return "", fmt.Errorf("reading package manifest failed (path: %s): %w", packageRoot, err) @@ -164,44 +160,46 @@ func FindBuildPackagesDirectory() (string, bool, error) { // BuildPackage function builds the package. func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { - destinationDir, err := BuildPackagesDirectory(options.PackageRootPath, options.BuildDir) + // builtPackageDir is the directory where the built package content is placed + // eg. /packages// + builtPackageDir, err := BuildPackagesDirectory(options.PackageRootPath, options.BuildDir) if err != nil { return "", fmt.Errorf("can't locate build directory: %w", err) } - logger.Debugf("Build directory: %s\n", destinationDir) + logger.Debugf("Build directory: %s\n", builtPackageDir) - logger.Debugf("Clear target directory (path: %s)", destinationDir) - err = files.ClearDir(destinationDir) + logger.Debugf("Clear target directory (path: %s)", builtPackageDir) + err = files.ClearDir(builtPackageDir) if err != nil { return "", fmt.Errorf("clearing package contents failed: %w", err) } logger.Debugf("Copy package content (source: %s)", options.PackageRootPath) - err = files.CopyWithoutDev(options.PackageRootPath, destinationDir) + err = files.CopyWithoutDev(options.PackageRootPath, builtPackageDir) if err != nil { return "", fmt.Errorf("copying package contents failed: %w", err) } logger.Debug("Copy license file if needed") - destinationLicenseFilePath := filepath.Join(destinationDir, licenseTextFileName) + destinationLicenseFilePath := filepath.Join(builtPackageDir, licenseTextFileName) err = copyLicenseTextFile(options.RepositoryRoot, destinationLicenseFilePath) if err != nil { return "", fmt.Errorf("copying license text file: %w", err) } logger.Debug("Encode dashboards") - err = encodeDashboards(destinationDir) + err = encodeDashboards(builtPackageDir) if err != nil { return "", fmt.Errorf("encoding dashboards failed: %w", err) } logger.Debug("Resolve external fields") - err = resolveExternalFields(options.PackageRootPath, destinationDir) + err = resolveExternalFields(options.PackageRootPath, builtPackageDir) if err != nil { return "", fmt.Errorf("resolving external fields failed: %w", err) } - err = addDynamicMappings(options.PackageRootPath, destinationDir) + err = addDynamicMappings(options.PackageRootPath, builtPackageDir) if err != nil { return "", fmt.Errorf("adding dynamic mappings: %w", err) } @@ -212,7 +210,7 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { return "", fmt.Errorf("creating links filesystem failed: %w", err) } - links, err := linksFS.IncludeLinkedFiles(destinationDir) + links, err := linksFS.IncludeLinkedFiles(builtPackageDir) if err != nil { return "", fmt.Errorf("including linked files failed: %w", err) } @@ -220,39 +218,40 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { logger.Debugf("Linked file included (path: %s)", l.TargetRelPath) } - err = resolveTransformDefinitions(destinationDir) + err = resolveTransformDefinitions(builtPackageDir) if err != nil { return "", fmt.Errorf("resolving transform manifests failed: %w", err) } if options.CreateZip { - return buildZippedPackage(ctx, options, destinationDir) + return buildZippedPackage(ctx, options, builtPackageDir, options.BuildDir) } if options.SkipValidation { logger.Debug("Skip validation of the built package") - return destinationDir, nil + return builtPackageDir, nil } - logger.Debugf("Validating built package (path: %s)", destinationDir) - errs, skipped := validation.ValidateAndFilterFromPath(destinationDir) + logger.Debugf("Validating built package (path: %s)", builtPackageDir) + errs, skipped := validation.ValidateAndFilterFromPath(builtPackageDir) if skipped != nil { logger.Infof("Skipped errors: %v", skipped) } if errs != nil { return "", fmt.Errorf("invalid content found in built package: %w", errs) } - return destinationDir, nil + return builtPackageDir, nil } -func buildZippedPackage(ctx context.Context, options BuildOptions, destinationDir string) (string, error) { +// buildZippedPackage function builds the zipped package from the sourcePackageDir and stores it in buildDir. +func buildZippedPackage(ctx context.Context, options BuildOptions, sourcePackageDir, buildDir string) (string, error) { logger.Debug("Build zipped package") - zippedPackagePath, err := buildPackagesZipPath(options.PackageRootPath) + zippedPackagePath, err := buildPackagesZipPath(options.PackageRootPath, buildDir) if err != nil { return "", fmt.Errorf("can't evaluate path for the zipped package: %w", err) } - err = files.Zip(ctx, destinationDir, zippedPackagePath) + err = files.Zip(ctx, sourcePackageDir, zippedPackagePath) if err != nil { return "", fmt.Errorf("can't compress the built package (compressed file path: %s): %w", zippedPackagePath, err) } From 5d84f0174eb4c1b1b31c360d9659bec8ee0d51ca Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Mon, 20 Oct 2025 15:47:57 +0200 Subject: [PATCH 43/46] refactor readme generation to include sourceFilesRoot parameter and update related tests --- cmd/build.go | 21 +++++++++-------- internal/docs/readme.go | 45 +++++++++++++++++++++++------------- internal/docs/readme_test.go | 2 +- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 11f09b77d1..e56c8a3da8 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -80,16 +80,6 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } logger.Debugf("Use build directory: %s", buildDir) - targets, err := docs.UpdateReadmes(repositoryRoot, packageRoot, buildDir) - if err != nil { - return fmt.Errorf("updating files failed: %w", err) - } - - for _, target := range targets { - fileName := filepath.Base(target) - cmd.Printf("%s file rendered: %s\n", fileName, target) - } - target, err := builder.BuildPackage(cmd.Context(), builder.BuildOptions{ PackageRootPath: packageRoot, BuildDir: buildDir, @@ -101,6 +91,17 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("building package failed: %w", err) } + + targets, err := docs.UpdateReadmes(repositoryRoot, packageRoot, buildDir) + if err != nil { + return fmt.Errorf("updating files failed: %w", err) + } + + for _, target := range targets { + fileName := filepath.Base(target) + cmd.Printf("%s file rendered: %s\n", fileName, target) + } + cmd.Printf("Package built: %s\n", target) cmd.Println("Done") diff --git a/internal/docs/readme.go b/internal/docs/readme.go index d7e97857f7..39791172ee 100644 --- a/internal/docs/readme.go +++ b/internal/docs/readme.go @@ -64,10 +64,12 @@ func AreReadmesUpToDate(repositoryRoot *os.Root, packageRoot string) ([]ReadmeFi return readmeFiles, nil } +// isReadmeUpToDate function checks if a single readme file is up-to-date. func isReadmeUpToDate(fileName, linksFilePath, packageRoot string) (bool, string, error) { logger.Debugf("Check if %s is up-to-date", fileName) - rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot) + // the readme is generated within the package root, so source should be the packageRoot files too + rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot, packageRoot) if err != nil { return false, "", fmt.Errorf("generating readme file failed: %w", err) } @@ -124,10 +126,17 @@ func UpdateReadmes(repositoryRoot *os.Root, packageRoot, buildDir string) ([]str return targets, nil } +// updateReadme function updates a single readme file using a defined template file. +// It writes the rendered file to both the package directory and the package build directory. func updateReadme(fileName, linksFilePath, packageRoot, buildDir string) (string, error) { logger.Debugf("Update the %s file", fileName) - rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot) + packageBuildRoot, err := builder.BuildPackagesDirectory(packageRoot, buildDir) + if err != nil { + return "", fmt.Errorf("package build root not found: %w", err) + } + + rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot, packageBuildRoot) if err != nil { return "", err } @@ -140,11 +149,6 @@ func updateReadme(fileName, linksFilePath, packageRoot, buildDir string) (string return "", fmt.Errorf("writing %s file failed: %w", fileName, err) } - packageBuildRoot, err := builder.BuildPackagesDirectory(packageRoot, buildDir) - if err != nil { - return "", fmt.Errorf("package build root not found: %w", err) - } - _, err = writeReadme(fileName, packageBuildRoot, rendered) if err != nil { return "", fmt.Errorf("writing %s file failed: %w", fileName, err) @@ -152,7 +156,12 @@ func updateReadme(fileName, linksFilePath, packageRoot, buildDir string) (string return target, nil } -func generateReadme(fileName, linksFilePath, packageRoot string) ([]byte, bool, error) { +// generateReadme function generates the readme file content +// the readme takes a template that lives under the _dev/build/docs directory at the package root. +// the readme template reads data from the sourceFilesRoot directory. +// sourceFilesRoot is usually the package root when generating readme for checking up-to-dateness, +// and the built package root when generating readme for the built package. +func generateReadme(fileName, linksFilePath, packageRoot, sourceFilesRoot string) ([]byte, bool, error) { logger.Debugf("Generate %s file (package: %s)", fileName, packageRoot) templatePath, found, err := findReadmeTemplatePath(fileName, packageRoot) if err != nil { @@ -169,13 +178,16 @@ func generateReadme(fileName, linksFilePath, packageRoot string) ([]byte, bool, return nil, false, err } - rendered, err := renderReadme(fileName, packageRoot, templatePath, linksMap) + // templatePath lives under the _dev/build/docs directory at the package root. + // builtPackageRoot is the root directory of the built package. + rendered, err := renderReadme(fileName, sourceFilesRoot, templatePath, linksMap) if err != nil { return nil, true, fmt.Errorf("rendering Readme failed: %w", err) } return rendered, true, nil } +// findReadmeTemplatePath function looks for the README template file in the _dev/build/docs directory. func findReadmeTemplatePath(fileName, packageRoot string) (string, bool, error) { templatePath := filepath.Join(packageRoot, "_dev", "build", "docs", fileName) _, err := os.Stat(templatePath) @@ -188,23 +200,24 @@ func findReadmeTemplatePath(fileName, packageRoot string) (string, bool, error) return templatePath, true, nil } -func renderReadme(fileName, packageRoot, templatePath string, linksMap linkMap) ([]byte, error) { - logger.Debugf("Render %s file (package: %s, templatePath: %s)", fileName, packageRoot, templatePath) +// renderReadme function renders the readme file reading from +func renderReadme(fileName, sourceFilesRoot, templatePath string, linksMap linkMap) ([]byte, error) { + logger.Debugf("Render %s file (package: %s, templatePath: %s)", fileName, sourceFilesRoot, templatePath) t := template.New(fileName) t, err := t.Funcs(template.FuncMap{ "event": func(args ...string) (string, error) { if len(args) > 0 { - return renderSampleEvent(packageRoot, args[0]) + return renderSampleEvent(sourceFilesRoot, args[0]) } - return renderSampleEvent(packageRoot, "") + return renderSampleEvent(sourceFilesRoot, "") }, "fields": func(args ...string) (string, error) { if len(args) > 0 { - dataStreamPath := filepath.Join(packageRoot, "data_stream", args[0]) + dataStreamPath := filepath.Join(sourceFilesRoot, "data_stream", args[0]) return renderExportedFields(dataStreamPath) } - return renderExportedFields(packageRoot) + return renderExportedFields(sourceFilesRoot) }, "url": func(args ...string) (string, error) { options := linkOptions{} @@ -214,7 +227,7 @@ func renderReadme(fileName, packageRoot, templatePath string, linksMap linkMap) return linksMap.RenderLink(args[0], options) }, "inputDocs": func() (string, error) { - return renderInputDocs(packageRoot) + return renderInputDocs(sourceFilesRoot) }, "generatedHeader": func() string { return doNotModifyStr diff --git a/internal/docs/readme_test.go b/internal/docs/readme_test.go index 95f7d2f58a..a06be43071 100644 --- a/internal/docs/readme_test.go +++ b/internal/docs/readme_test.go @@ -58,7 +58,7 @@ Introduction to the package`, err := createReadmeFile(dir, c.readmeTemplateContents) require.NoError(t, err) - rendered, isTemplate, err := generateReadme(c.filename, "", dir) + rendered, isTemplate, err := generateReadme(c.filename, "", dir, dir) require.NoError(t, err) if c.readmeTemplateContents != "" { From 102dfcd7790c28382c151647392437cc9f40d9df Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Tue, 21 Oct 2025 10:55:13 +0200 Subject: [PATCH 44/46] streamline linked files inclusion in BuildPackage function prior to resolve external fields --- internal/builder/packages.go | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 58309ba872..e18e41bc67 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -187,6 +187,22 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { return "", fmt.Errorf("copying license text file: %w", err) } + // when CopyWithoutDev is used, .link files are skipped. + // Include them before resolving external fields + logger.Debug("Include linked files") + linksFS, err := files.CreateLinksFSFromPath(options.RepositoryRoot, options.PackageRootPath) + if err != nil { + return "", fmt.Errorf("creating links filesystem failed: %w", err) + } + + links, err := linksFS.IncludeLinkedFiles(builtPackageDir) + if err != nil { + return "", fmt.Errorf("including linked files failed: %w", err) + } + for _, l := range links { + logger.Debugf("Linked file included (path: %s)", l.TargetRelPath) + } + logger.Debug("Encode dashboards") err = encodeDashboards(builtPackageDir) if err != nil { @@ -204,20 +220,6 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { return "", fmt.Errorf("adding dynamic mappings: %w", err) } - logger.Debug("Include linked files") - linksFS, err := files.CreateLinksFSFromPath(options.RepositoryRoot, options.PackageRootPath) - if err != nil { - return "", fmt.Errorf("creating links filesystem failed: %w", err) - } - - links, err := linksFS.IncludeLinkedFiles(builtPackageDir) - if err != nil { - return "", fmt.Errorf("including linked files failed: %w", err) - } - for _, l := range links { - logger.Debugf("Linked file included (path: %s)", l.TargetRelPath) - } - err = resolveTransformDefinitions(builtPackageDir) if err != nil { return "", fmt.Errorf("resolving transform manifests failed: %w", err) From 75705816a9c02ba91505f7ecf8f1710755779ec5 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Tue, 21 Oct 2025 16:32:37 +0200 Subject: [PATCH 45/46] restore zipped built package path to build/packages --- internal/builder/packages.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/internal/builder/packages.go b/internal/builder/packages.go index e18e41bc67..4b1914faab 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -106,12 +106,16 @@ func BuildPackagesDirectory(packageRoot string, buildDir string) (string, error) } // buildPackagesZipPath function returns the path to zipped built package. -func buildPackagesZipPath(packageRoot, buildDir string) (string, error) { +func buildPackagesZipPath(packageRoot string) (string, error) { + buildPackagesDir, err := buildPackagesRootDirectory() + if err != nil { + return "", fmt.Errorf("can't locate build packages root directory: %w", err) + } m, err := packages.ReadPackageManifestFromPackageRoot(packageRoot) if err != nil { return "", fmt.Errorf("reading package manifest failed (path: %s): %w", packageRoot, err) } - return ZippedBuiltPackagePath(buildDir, *m), nil + return ZippedBuiltPackagePath(buildPackagesDir, *m), nil } // ZippedBuiltPackagePath function returns the path to zipped built package. @@ -226,7 +230,7 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { } if options.CreateZip { - return buildZippedPackage(ctx, options, builtPackageDir, options.BuildDir) + return buildZippedPackage(ctx, options, builtPackageDir) } if options.SkipValidation { @@ -245,15 +249,15 @@ func BuildPackage(ctx context.Context, options BuildOptions) (string, error) { return builtPackageDir, nil } -// buildZippedPackage function builds the zipped package from the sourcePackageDir and stores it in buildDir. -func buildZippedPackage(ctx context.Context, options BuildOptions, sourcePackageDir, buildDir string) (string, error) { +// buildZippedPackage function builds the zipped package from the builtPackageDir and stores it in buildPackagesDir. +func buildZippedPackage(ctx context.Context, options BuildOptions, builtPackageDir string) (string, error) { logger.Debug("Build zipped package") - zippedPackagePath, err := buildPackagesZipPath(options.PackageRootPath, buildDir) + zippedPackagePath, err := buildPackagesZipPath(options.PackageRootPath) if err != nil { return "", fmt.Errorf("can't evaluate path for the zipped package: %w", err) } - err = files.Zip(ctx, sourcePackageDir, zippedPackagePath) + err = files.Zip(ctx, builtPackageDir, zippedPackagePath) if err != nil { return "", fmt.Errorf("can't compress the built package (compressed file path: %s): %w", zippedPackagePath, err) } From 94a16d70fb74addc28d4b30987a2686eee87be49 Mon Sep 17 00:00:00 2001 From: Teresa Romero Date: Wed, 22 Oct 2025 09:06:00 +0200 Subject: [PATCH 46/46] Add repository root handling to test runner and installer --- cmd/testrunner.go | 6 ++++++ internal/packages/installer/factory.go | 2 +- internal/testrunner/runners/policy/runner.go | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index b397581c31..f06a33045c 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -663,6 +663,11 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("locating package root failed: %w", err) } + repositoryRoot, err := files.FindRepositoryRoot() + if err != nil { + return fmt.Errorf("locating repository root failed: %w", err) + } + dataStreams, err := getDataStreamsFlag(cmd, packageRootPath) if err != nil { return err @@ -695,6 +700,7 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.Policy, WithCoverage: testCoverage, CoverageType: testCoverageFormat, + RepositoryRoot: repositoryRoot, }) results, err := testrunner.RunSuite(ctx, runner) diff --git a/internal/packages/installer/factory.go b/internal/packages/installer/factory.go index 5766bc6b32..ec278e6c57 100644 --- a/internal/packages/installer/factory.go +++ b/internal/packages/installer/factory.go @@ -54,7 +54,7 @@ func NewForPackage(ctx context.Context, options Options) (Installer, error) { return nil, errors.New("missing package root path or pre-built zip package") } if options.RepositoryRoot == nil { - return nil, errors.New("missing repo root") + return nil, errors.New("missing repository root") } version, err := kibanaVersion(options.Kibana) diff --git a/internal/testrunner/runners/policy/runner.go b/internal/testrunner/runners/policy/runner.go index af2d4f8513..0f8166981f 100644 --- a/internal/testrunner/runners/policy/runner.go +++ b/internal/testrunner/runners/policy/runner.go @@ -7,6 +7,7 @@ package policy import ( "context" "fmt" + "os" "path/filepath" "strings" @@ -34,6 +35,8 @@ type runner struct { resourcesManager *resources.Manager cleanup func(context.Context) error + + repositoryRoot *os.Root } // Ensures that runner implements testrunner.TestRunner interface @@ -48,6 +51,7 @@ type PolicyTestRunnerOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig WithCoverage bool CoverageType string + RepositoryRoot *os.Root } func NewPolicyTestRunner(options PolicyTestRunnerOptions) *runner { @@ -60,6 +64,7 @@ func NewPolicyTestRunner(options PolicyTestRunnerOptions) *runner { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, + repositoryRoot: options.RepositoryRoot, } runner.resourcesManager = resources.NewManager() runner.resourcesManager.RegisterProvider(resources.DefaultKibanaProviderName, &resources.KibanaProvider{Client: runner.kibanaClient}) @@ -157,6 +162,7 @@ func (r *runner) Type() testrunner.TestType { func (r *runner) setupSuite(ctx context.Context, manager *resources.Manager) (cleanup func(ctx context.Context) error, err error) { packageResource := resources.FleetPackage{ PackageRootPath: r.packageRootPath, + RepositoryRoot: r.repositoryRoot, } setupResources := resources.Resources{ &packageResource,