Skip to content

Commit f0b13b6

Browse files
CLI (#21)
* Added CLI parse, build and lex * Also added some dependency management using ``dep``
1 parent b2573fc commit f0b13b6

14 files changed

+373
-57
lines changed

.dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build

.gitignore

+8-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,11 @@
33

44
# ignore generated files
55
# 1. test artifacts
6-
cmd/test*/
6+
cmd/test*/
7+
8+
# 2. binaries
9+
/crossplane*
10+
debug.test
11+
12+
# 3. vendor
13+
/vendor/

Gopkg.lock

+33
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Gopkg.toml example
2+
#
3+
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
4+
# for detailed Gopkg.toml documentation.
5+
#
6+
# required = ["github.com/user/thing/cmd/thing"]
7+
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
8+
#
9+
# [[constraint]]
10+
# name = "github.com/user/project"
11+
# version = "1.0.0"
12+
#
13+
# [[constraint]]
14+
# name = "github.com/user/project2"
15+
# branch = "dev"
16+
# source = "github.com/myfork/project2"
17+
#
18+
# [[override]]
19+
# name = "github.com/x/y"
20+
# version = "2.4.0"
21+
#
22+
# [prune]
23+
# non-go = false
24+
# go-tests = true
25+
# unused-packages = true
26+
27+
28+
[[constraint]]
29+
name = "github.com/spf13/cobra"
30+
version = "0.0.5"
31+
32+
[prune]
33+
go-tests = true
34+
unused-packages = true

Makefile

+12-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ BUILD_IN_CONTAINER = 1
1010
DOCKERFILEPATH = build
1111
GOLANG_CONTAINER = golang:1.12
1212

13+
requirements:
14+
go get -u \
15+
github.com/golang/dep/cmd/dep \
16+
github.com/golangci/golangci-lint/cmd/golangci-lint
17+
18+
dependencies:
19+
dep ensure
20+
1321
build:
1422
ifeq ($(BUILD_IN_CONTAINER),1)
1523
$(DOCKER_BUILD_RUN) -e CGO_ENABLED=0 $(GOLANG_CONTAINER) go build -installsuffix cgo -ldflags "-w" -o /go/src/github.com/nginxinc/crossplane-go/crossplane-go
@@ -19,7 +27,9 @@ endif
1927

2028
test:
2129
ifeq ($(BUILD_IN_CONTAINER),1)
22-
$(DOCKER_RUN) $(GOLANG_CONTAINER) go test ./...
30+
docker run --rm -v $(shell pwd):/go/src/github.com/nginxinc/crossplane-go \
31+
$(shell docker build -f ./build/Dockerfile -q .) \
32+
go test $(shell go list ./... | grep -v /vendor/)
2333
else
2434
go test ./...
2535
endif
@@ -28,4 +38,4 @@ lint:
2838
golangci-lint run
2939

3040
clean:
31-
rm -f crossplane-go
41+
rm -f crossplane-go

README.md

+72-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,73 @@
11
# crossplane-go
2-
A library for working with NGINX configs in Go
2+
3+
A quick and reliable way to convert NGINX configurations into JSON and back.
4+
5+
built with ❤ by nginxinc and gophers who live in Cork and are from Cork
6+
7+
```
8+
Usage:
9+
crossplane [command]
10+
11+
Available Commands:
12+
build Build an NGINX config using a JSON format
13+
help Help about any command
14+
lex Lexes tokens from an NGINX config file
15+
parse Parses an NGINX config for a JSON format
16+
17+
Flags:
18+
-h, --help help for crossplane
19+
20+
Use "crossplane [command] --help" for more information about a command.
21+
```
22+
23+
## crossplane build
24+
25+
```
26+
Build an NGINX config using a JSON format
27+
28+
Usage:
29+
crossplane build [/path/to/payload.json] [flags]
30+
31+
Flags:
32+
-f, --force Force overwrite existing files
33+
-h, --help help for build
34+
-i, --indent uint Set spaces for indentation (default 4)
35+
-d, --path string Output to a directory. If not specified, it will output to STDOUT
36+
-t, --tabs Use tabs instead of spaces on built files
37+
```
38+
39+
## crossplane parse
40+
41+
```
42+
Parses an NGINX config for a JSON format
43+
44+
Usage:
45+
crossplane parse [/path/to/nginx.conf] [flags]
46+
47+
Flags:
48+
--catch-errors Stop parse after first error
49+
--check-args Run arg count analysis on directives
50+
--check-ctx Run context analysis on directives
51+
--combine Inline includes to create single config object
52+
-h, --help help for parse
53+
--ignore stringArray List of ignored directives
54+
-i, --indent uint Set spaces for indentation (default 4)
55+
-o, --out string Output to a file. If not specified, it will output to STDOUT
56+
--single Skip includes
57+
--strict Strict mode: error on unrecognized directives
58+
```
59+
60+
## crossplane lex
61+
62+
```
63+
Lexes tokens from an NGINX config file
64+
65+
Usage:
66+
crossplane lex [/path/to/tokens-file.txt] [flags]
67+
68+
Flags:
69+
-h, --help help for lex
70+
-i, --indent uint Set spaces for indentation (default 4)
71+
-n, --line-numbers Include line numbers in output
72+
-o, --out string Output to a file. If not specified, it will output to STDOUT
73+
```

build/Dockerfile

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM golang:1
2+
3+
RUN go get -u \
4+
github.com/golang/dep/cmd/dep \
5+
github.com/golangci/golangci-lint/cmd/golangci-lint

build/ci/Dockerfile.CI

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ FROM golang:1.12
33
ENV GOLANGCI_LINT_VERSION v1.15.0
44

55
# Dependencies
6-
RUN wget -O - -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s ${GOLANGCI_LINT_VERSION}
6+
RUN wget -O - -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s ${GOLANGCI_LINT_VERSION}
7+
RUN curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh

build/ci/Jenkinsfile

+5-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pipeline {
2121
stages {
2222
stage('PrepareRun') {
2323
agent any
24-
24+
2525
stages {
2626
stage ('Checkout') {
2727
steps {
@@ -40,9 +40,12 @@ pipeline {
4040
}
4141

4242
steps {
43+
4344
print "Running tests against crossplane-go branch ${NS1_BRANCH} Number ${BUILD_NUMBER}"
4445

4546
sh """
47+
cd /go/src/github.com/nginxinc/crossplane-go
48+
make dependencies
4649
make BUILD_IN_CONTAINER=0 test
4750
make lint
4851
"""
@@ -69,7 +72,7 @@ pipeline {
6972
script {
7073
if (BRANCH_NAME == 'master') {
7174
slackSend(
72-
color: '#FF0000',
75+
color: '#FF0000',
7376
channel: '#crossplane-go',
7477
message: "FAILED: Job ${JOB_NAME} [${BUILD_NUMBER}] (${RUN_DISPLAY_URL}) :peter_wow:"
7578
)

cmd/crossplane.go

+150-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,155 @@
11
package cmd
22

3+
import (
4+
"encoding/json"
5+
"io/ioutil"
6+
"log"
7+
"os"
8+
"strings"
9+
10+
"github.com/nginxinc/crossplane-go/pkg/builder"
11+
"github.com/nginxinc/crossplane-go/pkg/lexer"
12+
"github.com/nginxinc/crossplane-go/pkg/parser"
13+
"github.com/spf13/cobra"
14+
)
15+
16+
var rootCmd = &cobra.Command{
17+
Use: "crossplane",
18+
Short: "Crossplane is a quick and reliable way to convert NGINX configurations into JSON and back.",
19+
Long: `A quick and reliable way to convert NGINX configurations into JSON and back.
20+
21+
built with ❤ by nginxinc and gophers who live in Cork and are from Cork
22+
Complete documentation is available at: https://github.com/nginxinc/crossplane-go
23+
`,
24+
}
25+
26+
var parseCmd = &cobra.Command{
27+
Use: "parse [/path/to/nginx.conf]",
28+
Short: "Parses an NGINX config for a JSON format",
29+
Args: cobra.ExactArgs(1),
30+
}
31+
32+
var buildCmd = &cobra.Command{
33+
Use: "build [/path/to/payload.json]",
34+
Short: "Build an NGINX config using a JSON format",
35+
Args: cobra.ExactArgs(1),
36+
}
37+
38+
var lexCmd = &cobra.Command{
39+
Use: "lex [/path/to/tokens-file.txt]",
40+
Short: "Lexes tokens from an NGINX config file",
41+
Args: cobra.ExactArgs(1),
42+
}
43+
344
// Execute - cmd entrypoint
4-
func Execute() {
45+
func Execute() (err error) {
46+
47+
// TODO: strict mode (BoolVarP)
48+
// TODO: ignore directives (StringArrayVarP)
49+
50+
var (
51+
indent uint
52+
outFile string
53+
catchErrors, combine, comment, single, strict, checkctx, checkargs bool
54+
ignore []string
55+
)
56+
parseCmd.Flags().UintVarP(&indent, "indent", "i", 4, "Set spaces for indentation")
57+
parseCmd.Flags().StringVarP(&outFile, "out", "o", "", "Output to a file. If not specified, it will output to STDOUT")
58+
parseCmd.Flags().BoolVar(&catchErrors, "catch-errors", false, "Stop parse after first error")
59+
parseCmd.Flags().BoolVar(&combine, "combine", false, "Inline includes to create single config object")
60+
parseCmd.Flags().BoolVar(&single, "single", false, "Skip includes")
61+
parseCmd.Flags().BoolVar(&strict, "strict", false, "Strict mode: error on unrecognized directives")
62+
parseCmd.Flags().BoolVar(&checkctx, "check-ctx", false, "Run context analysis on directives")
63+
parseCmd.Flags().BoolVar(&checkargs, "check-args", false, "Run arg count analysis on directives")
64+
parseCmd.Flags().BoolVar(&comment, "include-comments", false, "Include comments in json")
65+
parseCmd.Flags().StringArrayVar(&ignore, "ignore", []string{}, "List of ignored directives")
66+
parseCmd.Run = func(cmd *cobra.Command, args []string) {
67+
filename := args[0]
68+
_, err := os.Stat(filename)
69+
if err != nil {
70+
log.Fatalf("Error: cannot access file %s", filename)
71+
}
72+
payload, err := parser.Parse(filename, catchErrors, ignore, single, comment, strict, combine, true, checkctx, checkargs)
73+
if err != nil {
74+
log.Fatalf("Error parsing file %s: %v", filename, err)
75+
}
76+
s := make([]string, indent)
77+
b, err := json.MarshalIndent(payload, "", strings.Join(s, " "))
78+
if err != nil {
79+
log.Fatalf("Error marshalling data: %v", err)
80+
}
81+
if outFile != "" {
82+
if err = ioutil.WriteFile(outFile, b, 0644); err != nil {
83+
log.Fatalf("Error writing data file %s: %v", outFile, err)
84+
}
85+
} else {
86+
os.Stdout.Write(b)
87+
os.Stdout.Write([]byte("\n")) // compatibility: always return newline at end
88+
}
89+
}
90+
91+
var (
92+
buildPath string
93+
force, tabs bool
94+
)
95+
buildCmd.Flags().UintVarP(&indent, "indent", "i", 4, "Set spaces for indentation")
96+
buildCmd.Flags().StringVarP(&buildPath, "path", "d", "", "Output to a directory. If not specified, it will output to STDOUT")
97+
buildCmd.Flags().BoolVarP(&tabs, "tabs", "t", false, "Use tabs instead of spaces on built files")
98+
buildCmd.Flags().BoolVarP(&force, "force", "f", false, "Force overwrite existing files")
99+
buildCmd.Run = func(cmd *cobra.Command, args []string) {
100+
filename := args[0]
101+
_, err := os.Stat(filename)
102+
if err != nil {
103+
log.Fatalf("Error: cannot access file %s", filename)
104+
}
105+
106+
f, err := ioutil.ReadFile(filename)
107+
if err != nil {
108+
log.Fatalf("Error: cannot read file %s: %v", filename, err)
109+
}
110+
input, err := builder.NewPayload(f)
111+
if err != nil {
112+
log.Fatalf("Error translating payload file %s: %v", filename, err)
113+
}
114+
output, err := builder.BuildFiles(input, buildPath, int(indent), tabs, false)
115+
if err != nil {
116+
log.Fatalf("Error: cannot build file %s: %v", filename, err)
117+
}
118+
os.Stdout.WriteString(output)
119+
os.Stdout.Write([]byte("\n")) // compatibility: always return newline at end
120+
}
121+
122+
var (
123+
lineNumbers bool
124+
)
125+
lexCmd.Flags().UintVarP(&indent, "indent", "i", 4, "Set spaces for indentation")
126+
lexCmd.Flags().StringVarP(&outFile, "out", "o", "", "Output to a file. If not specified, it will output to STDOUT")
127+
lexCmd.Flags().BoolVarP(&lineNumbers, "line-numbers", "n", false, "Include line numbers in output")
128+
lexCmd.Run = func(cmd *cobra.Command, args []string) {
129+
filename := args[0]
130+
_, err := os.Stat(filename)
131+
if err != nil {
132+
log.Fatalf("Error: cannot access file %s", filename)
133+
}
134+
f, err := ioutil.ReadFile(filename)
135+
if err != nil {
136+
log.Fatalf("Error: cannot read file %s: %v", filename, err)
137+
}
138+
input := string(f)
139+
tokenStream := lexer.LexScanner(input)
140+
li := []interface{}{}
141+
for token := range tokenStream {
142+
li = append(li, token.Repr(lineNumbers))
143+
}
144+
b, err := json.Marshal(li)
145+
if err != nil {
146+
log.Fatalf("Error marshalling token stream data from lexer: %v", err)
147+
}
148+
os.Stdout.Write(b)
149+
os.Stdout.Write([]byte("\n")) // compatibility: always return newline at end
150+
}
5151

152+
rootCmd.AddCommand(parseCmd, buildCmd, lexCmd)
153+
err = rootCmd.Execute()
154+
return err
6155
}

0 commit comments

Comments
 (0)