Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple <<includes(file)>> and embedded <<includes(file)>> #737

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Prev Previous commit
Next Next commit
Add support for multiple <<includes(file)>> and embedded <<includes(f…
…ile)>>
Mongey committed Jun 15, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit da0e4ed5a90f55d4c1acb5fe46021da9e9213ee5
27 changes: 18 additions & 9 deletions integration_tests/features/orb_pack.feature
Original file line number Diff line number Diff line change
@@ -22,22 +22,27 @@ Feature: Orb pack
And the exit status should be 0

@mocked_home_directory
Scenario: Orb pack with multiple includes fails
Scenario: Orb pack with multiple includes
Given a file named "src/@orb.yml" with:
"""
commands:
test:
steps:
- run:
command: <<include(script.sh)>> <<include(script.sh)>>
command: <<include(script.sh)>> <<include(script2.sh)>>
"""
Given a file named "src/script.sh" with "echo Hello, world!"
Given a file named "src/script.sh" with "echo Hello,"
Given a file named "src/script2.sh" with "world!"
When I run `circleci orb pack src`
Then the output should contain:
"""
Error: An unexpected error occurred: multiple include statements: '<<include(script.sh)>> <<include(script.sh)>>'
commands:
test:
steps:
- run:
command: echo Hello, world!
"""
And the exit status should be 255
And the exit status should be 0

@mocked_home_directory
Scenario: Orb pack with include statement in bigger string
@@ -47,15 +52,19 @@ Feature: Orb pack
test:
steps:
- run:
command: include <<include(script.sh)>>
command: echo "<<include(script.sh)>>"
"""
Given a file named "src/script.sh" with "echo Hello, world!"
Given a file named "src/script.sh" with "Hello, world!"
When I run `circleci orb pack src`
Then the output should contain:
"""
Error: An unexpected error occurred: entire string must be include statement: 'include <<include(script.sh)>>'
commands:
test:
steps:
- run:
command: echo "Hello, world!"
"""
And the exit status should be 255
And the exit status should be 0

@mocked_home_directory
Scenario: Missing @orb.yml for orb packing
19 changes: 5 additions & 14 deletions process/process.go
Original file line number Diff line number Diff line change
@@ -15,29 +15,20 @@ func MaybeIncludeFile(s string, orbDirectory string) (string, error) {
// View: https://regexr.com/599mq
includeRegex := regexp.MustCompile(`<<[\s]*include\(([-\w\/\.]+)\)?[\s]*>>`)

// only find up to 2 matches, because we throw an error if we find >1
includeMatches := includeRegex.FindAllStringSubmatch(s, 2)
if len(includeMatches) > 1 {
return "", fmt.Errorf("multiple include statements: '%s'", s)
}
includeMatches := includeRegex.FindAllStringSubmatch(s, -1)

if len(includeMatches) == 1 {
match := includeMatches[0]
for _, match := range includeMatches {
fullMatch, subMatch := match[0], match[1]

// throw an error if the entire string wasn't matched
if fullMatch != s {
return "", fmt.Errorf("entire string must be include statement: '%s'", s)
}

filepath := filepath.Join(orbDirectory, subMatch)
file, err := ioutil.ReadFile(filepath)

if err != nil {
return "", fmt.Errorf("could not open %s for inclusion", filepath)
}
escaped := strings.ReplaceAll(string(file), "<<", "\\<<")

return escaped, nil
escaped := strings.ReplaceAll(string(file), "<<", "\\<<")
s = strings.ReplaceAll(s, fullMatch, escaped)
}

return s, nil
86 changes: 86 additions & 0 deletions process/process_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package process

import (
"io/ioutil"
"os"
"testing"
)

func Test_MaybeIncludeFile(t *testing.T) {
testCases := []struct {
description string
template string
expected string
files map[string]string
errExpected bool
}{
{
description: "File gets replaced",
template: "<<include(example.txt)>>",
expected: "world",
files: map[string]string{
"example.txt": "world",
},
errExpected: false,
},
{
description: "Partial line include",
template: "hello <<include(example-2.txt)>>",
expected: "hello world",
files: map[string]string{
"example-2.txt": "world",
},
errExpected: false,
},
{
description: "Multiple includes",
template: "<<include(example-1.txt)>> <<include(example-2.txt)>>",
expected: "hello world",
files: map[string]string{
"example-1.txt": "hello",
"example-2.txt": "world",
},
errExpected: false,
},
{
description: "File does not exist",
template: "<<include(file-that-does-not-exist.txt)>>",
files: map[string]string{},
errExpected: true,
},
{
description: "Included files are escaped",
template: "<<include(example-1.txt)>> world",
expected: "\\<< hello world",
files: map[string]string{
"example-1.txt": "<< hello",
},
errExpected: false,
},
}

for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
dir, err := ioutil.TempDir("", "circleci-cli-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
for name, content := range tc.files {
if err := ioutil.WriteFile(dir+"/"+name, []byte(content), 0644); err != nil {
t.Fatal(err)
}
}

orbDirectory := dir
res, err := MaybeIncludeFile(tc.template, orbDirectory)
if err != nil && !tc.errExpected {
t.Errorf("Unexpected error: %v", err)
}

if !tc.errExpected && res != tc.expected {
t.Errorf("expected '%s', got '%s'", tc.expected, res)
}
})
}
}