Skip to content

Commit 41693a4

Browse files
authored
Move mage targets for checking uncommitted changes (#43)
1 parent 649a0f7 commit 41693a4

File tree

14 files changed

+2221
-4
lines changed

14 files changed

+2221
-4
lines changed

dev-tools/mage/check.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package mage
19+
20+
import (
21+
"bufio"
22+
"bytes"
23+
"fmt"
24+
"log"
25+
"os"
26+
"os/exec"
27+
"strings"
28+
29+
"github.com/magefile/mage/mg"
30+
"github.com/magefile/mage/sh"
31+
32+
"github.com/elastic/elastic-agent-libs/processors/dissect"
33+
)
34+
35+
func CheckNoChanges() error {
36+
changes, err := GitDiffIndex()
37+
if err != nil {
38+
return fmt.Errorf("failed to diff the git index: %w", err)
39+
}
40+
41+
if len(changes) > 0 {
42+
if mg.Verbose() {
43+
err = GitDiff()
44+
if err != nil {
45+
return fmt.Errorf("failed to run git diff: %w", err)
46+
}
47+
}
48+
49+
return fmt.Errorf("some files are not up-to-date. "+
50+
"Run 'mage update' then review and commit the changes. "+
51+
"Modified: %v", changes)
52+
}
53+
return nil
54+
}
55+
56+
// GitDiffIndex returns a list of files that differ from what is committed.
57+
// These could file that were created, deleted, modified, or moved.
58+
func GitDiffIndex() ([]string, error) {
59+
// Ensure the index is updated so that diff-index gives accurate results.
60+
if err := sh.Run("git", "update-index", "-q", "--refresh"); err != nil {
61+
return nil, err
62+
}
63+
64+
// git diff-index provides a list of modified files.
65+
// https://www.git-scm.com/docs/git-diff-index
66+
out, err := sh.Output("git", "diff-index", "HEAD", "--", ".")
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
// Example formats.
72+
// :100644 100644 bcd1234... 0123456... M file0
73+
// :100644 100644 abcd123... 1234567... R86 file1 file3
74+
d, err := dissect.New(":%{src_mode} %{dst_mode} %{src_sha1} %{dst_sha1} %{status}\t%{paths}")
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
// Parse lines.
80+
var modified []string
81+
s := bufio.NewScanner(bytes.NewBufferString(out))
82+
for s.Scan() {
83+
m, err := d.Dissect(s.Text())
84+
if err != nil {
85+
return nil, fmt.Errorf("failed to dissect git diff-index output: %w", err)
86+
}
87+
88+
paths := strings.Split(m["paths"], "\t")
89+
if len(paths) > 1 {
90+
modified = append(modified, paths[1])
91+
} else {
92+
modified = append(modified, paths[0])
93+
}
94+
}
95+
if err = s.Err(); err != nil {
96+
return nil, err
97+
}
98+
99+
return modified, nil
100+
}
101+
102+
// GitDiff runs 'git diff' and writes the output to stdout.
103+
func GitDiff() error {
104+
c := exec.Command("git", "--no-pager", "diff", "--minimal")
105+
c.Stdin = nil
106+
c.Stdout = os.Stdout
107+
c.Stderr = os.Stderr
108+
log.Println("exec:", strings.Join(c.Args, " "))
109+
err := c.Run()
110+
return err
111+
}

magefile.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import (
2828

2929
// mage:import
3030
"github.com/elastic/elastic-agent-libs/dev-tools/mage"
31+
32+
devtools "github.com/elastic/elastic-agent-libs/dev-tools/mage"
3133
"github.com/elastic/elastic-agent-libs/dev-tools/mage/gotool"
3234
)
3335

@@ -41,7 +43,8 @@ var Aliases = map[string]interface{}{
4143
// Check runs all the checks
4244
// nolint: deadcode,unparam // it's used as a `mage` target and requires returning an error
4345
func Check() error {
44-
mg.Deps(mage.Deps.CheckModuleTidy, CheckLicenseHeaders)
46+
mg.Deps(devtools.Deps.CheckModuleTidy, CheckLicenseHeaders)
47+
mg.Deps(devtools.CheckNoChanges)
4548
return nil
4649
}
4750

@@ -54,7 +57,7 @@ func Fmt() {
5457
func AddLicenseHeaders() error {
5558
fmt.Println(">> fmt - go-licenser: Adding missing headers")
5659

57-
mg.Deps(mage.InstallGoLicenser)
60+
mg.Deps(devtools.InstallGoLicenser)
5861

5962
licenser := gotool.Licenser
6063

@@ -65,7 +68,7 @@ func AddLicenseHeaders() error {
6568

6669
// CheckLicenseHeaders checks ASL2 headers in .go files
6770
func CheckLicenseHeaders() error {
68-
mg.Deps(mage.InstallGoLicenser)
71+
mg.Deps(devtools.InstallGoLicenser)
6972

7073
licenser := gotool.Licenser
7174

@@ -77,7 +80,7 @@ func CheckLicenseHeaders() error {
7780

7881
// Notice generates a NOTICE.txt file for the module.
7982
func Notice() error {
80-
return mage.GenerateNotice(
83+
return devtools.GenerateNotice(
8184
filepath.Join("dev-tools", "templates", "notice", "overrides.json"),
8285
filepath.Join("dev-tools", "templates", "notice", "rules.json"),
8386
filepath.Join("dev-tools", "templates", "notice", "NOTICE.txt.tmpl"),

processors/dissect/const.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package dissect
19+
20+
import (
21+
"errors"
22+
"regexp"
23+
)
24+
25+
var (
26+
// delimiterRE tokenizes the following string into walkable with extracted delimiter + key.
27+
// string:
28+
// ` %{key}, %{key/2}`
29+
// into:
30+
// [["", "key" ], [", ", "key/2"]]
31+
ordinalIndicator = "/"
32+
fixedLengthIndicator = "#"
33+
34+
skipFieldPrefix = "?"
35+
appendFieldPrefix = "+"
36+
indirectFieldPrefix = "&"
37+
appendIndirectPrefix = "+&"
38+
indirectAppendPrefix = "&+"
39+
greedySuffix = "->"
40+
pointerFieldPrefix = "*"
41+
dataTypeIndicator = "|"
42+
dataTypeSeparator = "\\|" // Needed for regexp
43+
44+
numberRE = "\\d{1,2}"
45+
alphaRE = "[[:alpha:]]*"
46+
47+
delimiterRE = regexp.MustCompile(`(?s)(.*?)%\{([^}]*?)}`)
48+
suffixRE = regexp.MustCompile("(.+?)" + // group 1 for key name
49+
"(" + ordinalIndicator + "(" + numberRE + ")" + ")?" + // group 2, 3 for ordinal
50+
"(" + fixedLengthIndicator + "(" + numberRE + ")" + ")?" + // group 4, 5 for fixed length
51+
"(" + greedySuffix + ")?" + // group 6 for greedy
52+
"(" + dataTypeSeparator + "(" + alphaRE + ")?" + ")?$") // group 7,8 for data type separator and data type
53+
54+
defaultJoinString = " "
55+
56+
errParsingFailure = errors.New("parsing failure")
57+
errInvalidTokenizer = errors.New("invalid dissect tokenizer")
58+
errEmpty = errors.New("empty string provided")
59+
errMixedPrefixIndirectAppend = errors.New("mixed prefix `&+`")
60+
errMixedPrefixAppendIndirect = errors.New("mixed prefix `&+`")
61+
errInvalidDatatype = errors.New("invalid data type")
62+
errMissingDatatype = errors.New("missing data type")
63+
)

processors/dissect/delimiter.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package dissect
19+
20+
import (
21+
"fmt"
22+
"strings"
23+
)
24+
25+
//delimiter represents a text section after or before a key, it keeps track of the needle and allows
26+
// to retrieve the position where it starts from a haystack.
27+
type delimiter interface {
28+
// IndexOf receives the haystack and a offset position and will return the absolute position where
29+
// the needle is found.
30+
IndexOf(haystack string, offset int) int
31+
32+
// Len returns the length of the needle used to calculate boundaries.
33+
Len() int
34+
35+
// String displays debugging information.
36+
String() string
37+
38+
// Delimiter returns the actual delimiter string.
39+
Delimiter() string
40+
41+
// IsGreedy return true if the next key should be greedy (end of string) or when explicitly
42+
// configured.
43+
IsGreedy() bool
44+
45+
// MarkGreedy marks this delimiter as greedy.
46+
MarkGreedy()
47+
48+
// Next returns the next delimiter in the chain.
49+
Next() delimiter
50+
51+
//SetNext sets the next delimiter or nil if current delimiter is the last.
52+
SetNext(d delimiter)
53+
}
54+
55+
// zeroByte represents a zero string delimiter its usually start of the line.
56+
type zeroByte struct {
57+
needle string
58+
greedy bool
59+
next delimiter
60+
}
61+
62+
func (z *zeroByte) IndexOf(haystack string, offset int) int {
63+
return offset
64+
}
65+
66+
func (z *zeroByte) Len() int {
67+
return 0
68+
}
69+
70+
func (z *zeroByte) String() string {
71+
return "delimiter: zerobyte"
72+
}
73+
74+
func (z *zeroByte) Delimiter() string {
75+
return z.needle
76+
}
77+
78+
func (z *zeroByte) IsGreedy() bool {
79+
return z.greedy
80+
}
81+
82+
func (z *zeroByte) MarkGreedy() {
83+
z.greedy = true
84+
}
85+
86+
func (z *zeroByte) Next() delimiter {
87+
return z.next
88+
}
89+
90+
func (z *zeroByte) SetNext(d delimiter) {
91+
z.next = d
92+
}
93+
94+
// multiByte represents a delimiter with at least one byte.
95+
type multiByte struct {
96+
needle string
97+
greedy bool
98+
next delimiter
99+
}
100+
101+
func (m *multiByte) IndexOf(haystack string, offset int) int {
102+
i := strings.Index(haystack[offset:], m.needle)
103+
if i != -1 {
104+
return i + offset
105+
}
106+
return -1
107+
}
108+
109+
func (m *multiByte) Len() int {
110+
return len(m.needle)
111+
}
112+
113+
func (m *multiByte) IsGreedy() bool {
114+
return m.greedy
115+
}
116+
117+
func (m *multiByte) MarkGreedy() {
118+
m.greedy = true
119+
}
120+
121+
func (m *multiByte) String() string {
122+
return fmt.Sprintf("delimiter: multibyte (match: '%s', len: %d)", m.needle, m.Len())
123+
}
124+
125+
func (m *multiByte) Delimiter() string {
126+
return m.needle
127+
}
128+
129+
func (m *multiByte) Next() delimiter {
130+
return m.next
131+
}
132+
133+
func (m *multiByte) SetNext(d delimiter) {
134+
m.next = d
135+
}
136+
137+
func newDelimiter(needle string) delimiter {
138+
if len(needle) == 0 {
139+
return &zeroByte{}
140+
}
141+
return &multiByte{needle: needle}
142+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package dissect
19+
20+
import (
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
func TestMultiByte(t *testing.T) {
27+
m := newDelimiter("needle")
28+
assert.Equal(t, 3, m.IndexOf(" needle", 1))
29+
}
30+
31+
func TestSingleByte(t *testing.T) {
32+
m := newDelimiter("")
33+
assert.Equal(t, 5, m.IndexOf(" needle", 5))
34+
}

0 commit comments

Comments
 (0)