Skip to content

Commit f234c6b

Browse files
authored
feat: implement substitute command for the native CLI (#65)
`ngssc substitute` substitutes the variable ${NGSSC_CSP_HASH} in files ending with ".template" and copies the file while removing the ".template" extension. ${NGSSC_CSP_HASH} represents the CSP hash value of the IIFE generated/inserted by the insert command, wrapped by single quotes. By default looks for "*.template" files in the current working directory. Specify another directory to search for "*.template" files via argument. (e.g. ngssc substitute /path/to/template/files) When applying the variable(s), the file is copied to the same directory without the ".template" extension with the substituion applied. (e.g. ngssc substitute: /a/my.conf.template => /a/my.conf) Use the "--out" flag to define a different output directory. (e.g. ngssc substitute --out=/b: /a/my.conf.template => /b/my.conf) Optionally supports substituting environment variables with the --include-env flag. The format ${EXAMPLE} must be used ($EXAMPLE will not work). Additionally only alphanumeric characters and _ are allowed as variable names (e.g. ${EXAMPLE_KEY}). (e.g. ngssc substitute --include-env) Closes #64
1 parent a01eff8 commit f234c6b

24 files changed

+1162
-375
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ coverage
2020
# nyc test coverage
2121
.nyc_output
2222

23+
# Golang coverage
24+
cli/coverage.out
25+
2326
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
2427
.grunt
2528

README.md

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export const environment = {
105105
};
106106
```
107107

108-
#### NG_ENV.\* _Deprecated_
108+
#### NG\_ENV.\* *Deprecated*
109109

110110
Import NG_ENV from `angular-server-side-configuration/ng-env`
111111
and use NG_ENV.NAME in your environment.prod.ts, where NAME is the
@@ -157,13 +157,54 @@ if you need an additional environment.)
157157

158158
#### ngssc insert
159159

160+
Insert environment variables. Looks for an ngssc.json file inside the current or
161+
given directory. Directory defaults to current working directory.
162+
160163
Usage: ngssc insert [options] [directory]
161164

162165
| Options | Description |
163166
| ----------------- | --------------------------------------------------------------------------------- |
164-
| `-r, --recursive` | Recursively searches for ngssc.json files and applies the contained configuration |
167+
| `--recursive, -r` | Recursively searches for ngssc.json files and applies the contained configuration |
165168
| `--dry` | Perform the insert without actually inserting the variables |
166169

170+
#### ngssc substitute
171+
172+
Substitutes the variable \$\{NGSSC_CSP_HASH\} in files ending with ".template"
173+
and copies the file while removing the ".template" extension.
174+
175+
\$\{NGSSC_CSP_HASH\} represents the CSP hash value of the IIFE generated/inserted by the
176+
insert command, wrapped by single quotes.
177+
178+
By default looks for "\*.template" files in the current working directory. Specify another
179+
directory to search for "\*.template" files via argument.
180+
(e.g. `ngssc substitute /path/to/template/files`)
181+
182+
When applying the variable(s), the file is copied to the same directory without the
183+
".template" extension with the substituion applied.
184+
(e.g. `ngssc substitute`: /a/my.conf.template => /a/my.conf)
185+
186+
Use the "--out" flag to define a different output directory.
187+
(e.g. `ngssc substitute --out=/b`: /a/my.conf.template => /b/my.conf)
188+
189+
Optionally supports substituting environment variables with the --include-env flag.
190+
The format \$\{EXAMPLE\} must be used (\$EXAMPLE will not work). Additionally only
191+
alphanumeric characters and \_ are allowed as variable names (e.g. \$\{EXAMPLE_KEY\}).
192+
(e.g. `ngssc substitute --include-env`)
193+
194+
**ATTENTION**: Since the official nginx container image already provides a similar mechanism
195+
with substitution, it is recommended **not** to use the `/etc/nginx/templates` directory.
196+
Instead use the directory `/etc/nginx/ngssc-templates`.
197+
198+
Usage: ngssc substitute [options] [directory]
199+
200+
| Options | Description |
201+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
202+
| `--ngssc-path` | Path to the ngssc.json file or containing directory to be used for the generated IIFE. Supports glob. Defaults to [current working directory]/\*\*/ngssc.json. Throws if multiple ngssc.json with different variant or variables are found. |
203+
| `--hash-algorithm, -a` | The hash algorithm to be used. Supports sha256, sha384 and sha512. Defaults to sha512. |
204+
| `--out, -o` | The directory into which the updated files should be copied. |
205+
| `--include-env, -e` | Substitute all variables in the format of \$\{VARIABLE_NAME\}. |
206+
| `--dry` | Perform the insert without actually inserting the variables |
207+
167208
##### Minimal Example
168209

169210
Dockerfile
@@ -188,6 +229,9 @@ ngssc.sh
188229
```bash
189230
#!/bin/sh
190231
ngssc insert /usr/share/nginx/html
232+
# Substitute CSP variable (and optionally environment variables)
233+
# See documentation above
234+
#ngssc substitute --ngssc-path=/usr/share/nginx/html -o=/etc/nginx/conf.d/ /etc/nginx/ngssc-templates/
191235
```
192236

193237
### ngssc.json

cli/commands/insert.go

Lines changed: 0 additions & 62 deletions
This file was deleted.

cli/config/ngssc_config.go

Lines changed: 0 additions & 76 deletions
This file was deleted.

cli/helpers_test.go

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"bytes"
5+
"fmt"
56
"io"
67
"io/ioutil"
78
"os"
@@ -59,36 +60,25 @@ func (result TestResult) StdoutContains(pattern string) bool {
5960
return strings.Contains(result.Stdout(), pattern)
6061
}
6162

62-
type TestIOContext struct {
63+
type TestDir struct {
6364
path string
6465
}
6566

66-
func NewTestIOContext(t *testing.T) TestIOContext {
67-
dir, err := ioutil.TempDir("", "ngssc_test")
68-
if err != nil {
69-
panic(err)
70-
}
71-
72-
context := TestIOContext{path: dir}
73-
t.Cleanup(func() {
74-
err := os.RemoveAll(context.path)
75-
if err != nil {
76-
panic(err)
77-
}
78-
})
79-
return context
67+
func newTestDir(t *testing.T) TestDir {
68+
dir := t.TempDir()
69+
return TestDir{path: dir}
8070
}
8171

82-
func (context TestIOContext) CreateFile(fileName string, content string) {
72+
func (context TestDir) CreateFile(fileName string, content string) {
8373
filePath := filepath.Join(context.path, fileName)
8474
ioutil.WriteFile(filePath, []byte(content), 0644)
8575
}
8676

87-
func (context TestIOContext) FileContains(fileName string, pattern string) bool {
77+
func (context TestDir) FileContains(fileName string, pattern string) bool {
8878
return strings.Contains(context.ReadFile(fileName), pattern)
8979
}
9080

91-
func (context TestIOContext) ReadFile(fileName string) string {
81+
func (context TestDir) ReadFile(fileName string) string {
9282
filePath := filepath.Join(context.path, fileName)
9383
fileContent, err := ioutil.ReadFile(filePath)
9484
if err != nil {
@@ -98,13 +88,89 @@ func (context TestIOContext) ReadFile(fileName string) string {
9888
return string(fileContent)
9989
}
10090

101-
func (context TestIOContext) CreateLanguageContext(language string) TestIOContext {
91+
func (context TestDir) CreateDirectory(language string) TestDir {
10292
path := filepath.Join(context.path, language)
10393
err := os.Mkdir(path, 0755)
10494
if err != nil {
10595
panic(err)
10696
}
10797

108-
languageContext := TestIOContext{path}
109-
return languageContext
98+
return TestDir{path}
99+
}
100+
101+
func chdir(t *testing.T, dir string) {
102+
t.Helper()
103+
wd, err := os.Getwd()
104+
if err != nil {
105+
t.Fatalf("chdir %s: %v", dir, err)
106+
}
107+
if err := os.Chdir(dir); err != nil {
108+
t.Fatal(err)
109+
}
110+
111+
t.Cleanup(func() {
112+
if err := os.Chdir(wd); err != nil {
113+
t.Fatalf("restoring working directory: %v", err)
114+
}
115+
})
116+
}
117+
118+
func tmpEnv(t *testing.T, key string, value string) {
119+
os.Setenv("TEST_VALUE", "example value")
120+
121+
t.Cleanup(func() {
122+
os.Unsetenv("TEST_VALUE")
123+
})
124+
}
125+
126+
func assertContains(t *testing.T, s string, substring string, message string) {
127+
t.Helper()
128+
debugMessage := fmt.Sprintf("Expected %v to contain %v", s, substring)
129+
assertTrue(t, strings.Contains(s, substring), appendDebugMessage(message, debugMessage))
130+
}
131+
132+
func assertNotContains(t *testing.T, s string, substring string, message string) {
133+
t.Helper()
134+
debugMessage := fmt.Sprintf("Expected %v to not contain %v", s, substring)
135+
assertTrue(t, !strings.Contains(s, substring), appendDebugMessage(message, debugMessage))
136+
}
137+
138+
func assertEqual(t *testing.T, a interface{}, b interface{}, message string) {
139+
t.Helper()
140+
assertTrue(t, a == b, appendDebugMessage(message, fmt.Sprintf("%v != %v", a, b)))
141+
}
142+
143+
func assertFailure(t *testing.T, result TestResult) {
144+
t.Helper()
145+
if !result.Success() {
146+
return
147+
}
148+
t.Fatalf("Expected to fail, but succeeded: %v %v", result.Stdout(), result.Error())
149+
}
150+
151+
func assertSuccess(t *testing.T, result TestResult) {
152+
t.Helper()
153+
if result.Success() {
154+
return
155+
}
156+
t.Fatalf("Expected to succeed, but failed: %v %v", result.Stdout(), result.Error())
157+
}
158+
159+
func assertTrue(t *testing.T, v bool, message string) {
160+
t.Helper()
161+
if v {
162+
return
163+
}
164+
if len(message) == 0 {
165+
message = "Expected value to be true"
166+
}
167+
t.Fatal(message)
168+
}
169+
170+
func appendDebugMessage(message string, debugMessage string) string {
171+
if len(message) == 0 {
172+
return debugMessage
173+
} else {
174+
return message + "\n" + debugMessage
175+
}
110176
}

0 commit comments

Comments
 (0)