This is an automated email from the ASF dual-hosted git repository.
zrhoffman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 37721fe Go fmt action (#5077)
37721fe is described below
commit 37721fe7d2854c3ec8030f5a6ec8e7d9c1bc0991
Author: ocket8888 <[email protected]>
AuthorDate: Mon Sep 28 18:30:20 2020 -0600
Go fmt action (#5077)
* Added script to parse git diff output into GHA actions
* Add code workspaces to .gitignore
* Add doctest for file parsing
* add main function and full diff parsing function
* Add go-fmt action
* Fix docker tag
* Fix missing license header
* introduce diff
* pipe to parse_diffs
* fixed inverted logic
* Try to replace newlines
* Fix introduced diff
* Add more safety replacements to error strings
* Add module docstring
* Lint fixes for the Level class
* Fix the rest of the linting errors
* Add badge to readme
* Go fmt fix
* Remove unnecessary exit code
* Make entrypoint directly executable
* Remove unused line
* Remove unnecessary --no-pager option
---
.github/actions/go-fmt/Dockerfile | 24 ++++
.github/actions/go-fmt/README.md | 37 +++++
.github/actions/go-fmt/action.yml | 22 +++
.github/actions/go-fmt/entrypoint.sh | 37 +++++
.github/workflows/go.fmt.yml | 38 +++++
.gitignore | 2 +-
README.md | 1 +
misc/parse_diffs.py | 261 +++++++++++++++++++++++++++++++++++
traffic_ops_ort/t3c/util/util.go | 8 +-
9 files changed, 425 insertions(+), 5 deletions(-)
diff --git a/.github/actions/go-fmt/Dockerfile
b/.github/actions/go-fmt/Dockerfile
new file mode 100644
index 0000000..a9d59a9
--- /dev/null
+++ b/.github/actions/go-fmt/Dockerfile
@@ -0,0 +1,24 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+FROM golang:1.14.2-alpine
+
+RUN apk add --no-cache git python3
+
+COPY entrypoint.sh /entrypoint.sh
+
+ENTRYPOINT /entrypoint.sh
diff --git a/.github/actions/go-fmt/README.md b/.github/actions/go-fmt/README.md
new file mode 100644
index 0000000..dec7ff7
--- /dev/null
+++ b/.github/actions/go-fmt/README.md
@@ -0,0 +1,37 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+# go-fmt Docker action
+
+This action checks for discrepancies with `go fmt` formatting standards.
+
+## Inputs
+
+## Outputs
+
+### `exit-code`
+
+1 if formatting errors are found, 0 otherwise
+
+## Example usage
+```yaml
+uses: actions/go-fmt@v1
+with:
+ dir: './lib/...'
+```
diff --git a/.github/actions/go-fmt/action.yml
b/.github/actions/go-fmt/action.yml
new file mode 100644
index 0000000..62b3309
--- /dev/null
+++ b/.github/actions/go-fmt/action.yml
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: 'go-fmt'
+description: 'Checks for go fmt issues'
+runs:
+ using: 'docker'
+ image: 'Dockerfile'
diff --git a/.github/actions/go-fmt/entrypoint.sh
b/.github/actions/go-fmt/entrypoint.sh
new file mode 100755
index 0000000..cab06a1
--- /dev/null
+++ b/.github/actions/go-fmt/entrypoint.sh
@@ -0,0 +1,37 @@
+#!/bin/sh -l
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -e
+
+GOPATH="$(mktemp -d)"
+SRCDIR="$GOPATH/src/github.com/apache"
+mkdir -p "$SRCDIR"
+ln -s "$PWD" "$SRCDIR/trafficcontrol"
+cd "$SRCDIR/trafficcontrol"
+
+/usr/local/go/bin/go fmt ./...
+DIFF_FILE="$(mktemp)"
+git diff >"$DIFF_FILE"
+
+if [ -s "$DIFF_FILE" ]; then
+ ./misc/parse_diffs.py <"$DIFF_FILE";
+ rm "$DIFF_FILE";
+ exit 1;
+fi
+
+echo "No diff found"
diff --git a/.github/workflows/go.fmt.yml b/.github/workflows/go.fmt.yml
new file mode 100644
index 0000000..d44f330
--- /dev/null
+++ b/.github/workflows/go.fmt.yml
@@ -0,0 +1,38 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: Go Format
+
+on:
+ push:
+ paths:
+ - "**.go"
+ create:
+ pull_request:
+ paths:
+ - "**.go"
+ types: [opened, reopened, edited, synchronize]
+
+jobs:
+ format:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@master
+ - name: Run Go fmt
+ uses: ./.github/actions/go-fmt
diff --git a/.gitignore b/.gitignore
index 9f72bff..033cdd8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@ local.tar.gz
*.sublime-project
*.sublime-workspace
.vscode/
+*.code-workspace
*.pydevproject
.idea/
.project
@@ -64,4 +65,3 @@ local.tar.gz
./traffic_ops_golang
# Created by TP/TO docker integration tests
junit/
-
diff --git a/README.md b/README.md
index ccd1098..b3f45cb 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@



+
[](http://traffic-control-cdn.readthedocs.io/en/latest/?badge=latest)
# Apache Traffic Control
diff --git a/misc/parse_diffs.py b/misc/parse_diffs.py
new file mode 100755
index 0000000..2baa63b
--- /dev/null
+++ b/misc/parse_diffs.py
@@ -0,0 +1,261 @@
+#!/usr/bin/env python3
+
+"""
+This script parses git diffs and outputs a set of GitHub Action annotations
where each section of
+each file is an annotation containing the diff chunk as a message. All
annotations are error-level.
+"""
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import re
+import sys
+import typing
+
+from enum import Enum
+
+class Level(Enum):
+ """
+ Level encodes the level of an annotation.
+ """
+
+ error = "error"
+ notice = "notice" # notice not implemented (yet?)
+ warn = "warning" # warning unused - diffs are assumed errors
+
+class Annotation(typing.NamedTuple):
+ """
+ Annotation represents a GitHub Actions Annotation. To preview the
GitHub Annotation string,
+ coerce it to a string value. For a fully sanitized, printable
Annotation, use the sanitize
+ method.
+
+ >>> print(Annotation(level=Level.error, file='test', line=1,
message='test'))
+ ::error file=test,line=1::test
+ """
+ level: Level
+ file: str
+ line: int
+ message: str
+
+ def __str__(self) -> str:
+ msg = self.message.replace("]", "%5D").replace(";", "%3B")
+ return f"::{self.level.value}
file={self.file},line={self.line}::{msg}"
+
+ def __repr__(self) -> str:
+ return f"Annotation(level={self.level}, file='{self.file}',
line={self.line})"
+
+ def sanitize(self) -> str:
+ """
+ sanitize returns a sanitized string, suitable for GHA, but not
for humans to read because
+ all newlines have been replaced with their URL percent-encoded
code points.
+ """
+ return str(self).replace("\r", "%0D").replace("\n", "%0A")
+
+CHUNK_HEADER_PATTERN = re.compile(r"^@@ -\d+,\d+ \+(\d+),(\d+) @@")
+
+def parse_chunk(chunk: str, file: str) -> Annotation:
+ """
+ parse_chunk parses a single diff chunk and produces a corresponding
annotation.
+
+ >>> chunk = '''@@ -1,3 +1,3 @@
+ ... {
+ ... + "test": "quest"
+ ... - "foo": "bar"
+ ... }'''
+ >>> ann = parse_chunk(chunk, "test")
+ >>> ann
+ Annotation(level=Level.error, file='test', line=2)
+ >>> print(ann)
+ ::error file=test,line=2::Format error
+ ```diff
+ @@ -1,3 +1,3 @@
+ {
+ + "test": "quest"
+ - "foo": "bar"
+ }
+ ```
+ """
+ lines = chunk.splitlines()
+ if len(lines) < 2:
+ raise ValueError(f"invalid diff chunk: {chunk}")
+
+ match = CHUNK_HEADER_PATTERN.match(lines[0])
+ if not match or len(match.groups()) != 2:
+ raise ValueError(f"invalid diff chunk header: {lines[0]}")
+
+ line = int(match.groups()[0]) + int(match.groups()[1])//2
+
+ content = "\n".join(["Format error", "```diff", chunk, "```"])
+
+ return Annotation(Level.error, file, line, content)
+
+
+FILE_HEADER_PATTERN = re.compile(r"^diff --git a/(.+) b/(.+)$")
+
+def parse_file(contents: str) -> typing.List[Annotation]:
+ """
+ parse_file parses the diff for a single file and returns the
corresponding annotations.
+
+ >>> file = '''diff --git a/test b/test
+ ... index c9072dcb7..2b7686061 100644
+ ... --- a/test
+ ... +++ b/test
+ ... @@ -24,7 +24,7 @@ package tc
+ ... // in: body
+ ... type ASNsResponse struct {
+ ... // in: body
+ ... - Response []ASN `json:"response"`
+ ... +Response []ASN `json:"response"`
+ ... Alerts
+ ... }
+ ...
+ ... @@ -85,7 +85,7 @@ type ASNNullable struct {
+ ... // ID of the ASN
+ ... //
+ ... // required: true
+ ... - ID *int `json:"id" db:"id"`
+ ... +ID *int `json:"id" db:"id"`
+ ...
+ ... // LastUpdated
+ ... //
+ ... '''
+ >>> anns = parse_file(file)
+ >>> len(anns)
+ 2
+ >>> anns[0]
+ Annotation(level=Level.error, file='test', line=27)
+ >>> anns[1]
+ Annotation(level=Level.error, file='test', line=88)
+ """
+ lines = contents.splitlines()
+ if len(lines) < 5:
+ raise ValueError(f"'{contents}' does not represent a file diff
in git format")
+
+ match = FILE_HEADER_PATTERN.match(lines[0])
+ if not match or len(match.groups()) != 2:
+ raise ValueError(f"invalid git diff file header: '{lines[0]}'")
+
+ fname = match.groups()[1]
+
+ lines = lines[4:]
+
+ chunk = [lines[0]]
+ annotations = []
+ for line in lines[1:]:
+ if CHUNK_HEADER_PATTERN.match(line):
+ annotations.append(parse_chunk("\n".join(chunk), fname))
+ chunk = []
+
+ chunk.append(line)
+
+ if chunk:
+ annotations.append(parse_chunk("\n".join(chunk), fname))
+
+ return annotations
+
+def parse_diff(diff: str) -> typing.List[Annotation]:
+ """
+ parse_diff parses a git diff output and returns the corresponding
annotations.
+
+ >>> diff= '''diff --git a/test b/test
+ ... index c9072dcb7..2b7686061 100644
+ ... --- a/test
+ ... +++ b/test
+ ... @@ -24,7 +24,7 @@ package tc
+ ... // in: body
+ ... type ASNsResponse struct {
+ ... // in: body
+ ... - Response []ASN `json:"response"`
+ ... +Response []ASN `json:"response"`
+ ... Alerts
+ ... }
+ ...
+ ... @@ -85,7 +85,7 @@ type ASNNullable struct {
+ ... // ID of the ASN
+ ... //
+ ... // required: true
+ ... - ID *int `json:"id" db:"id"`
+ ... +ID *int `json:"id" db:"id"`
+ ...
+ ... // LastUpdated
+ ... //
+ ... diff --git a/quest b/quest
+ ... index 283901f14..0c1e2b0c1 100644
+ ... --- a/quest
+ ... +++ b/quest
+ ... @@ -1,7 +1,7 @@
+ ... package tc
+ ...
+ ... import (
+ ... - "database/sql"
+ ... +"database/sql"
+ ... )
+ ...
+ ... /*
+ ... '''
+ >>> anns = parse_diff(diff)
+ >>> len(anns)
+ 3
+ >>> anns[0]
+ Annotation(level=Level.error, file='test', line=27)
+ >>> anns[1]
+ Annotation(level=Level.error, file='test', line=88)
+ >>> anns[2]
+ Annotation(level=Level.error, file='quest', line=4)
+ """
+
+ lines = diff.splitlines()
+ if len(lines) < 5:
+ raise ValueError(f"'{diff}' does not represent a git diff")
+
+ match = FILE_HEADER_PATTERN.match(lines[0])
+ if not match or len(match.groups()) != 2:
+ raise ValueError(f"invalid git diff file header: '{lines[0]}''")
+
+ file = lines[:4]
+ lines = lines[4:]
+ annotations = []
+ for line in lines:
+ if FILE_HEADER_PATTERN.match(line):
+ annotations += parse_file("\n".join(file))
+ file = []
+ file.append(line)
+
+ if file:
+ annotations += parse_file("\n".join(file))
+
+ return annotations
+
+def main() -> int:
+ """
+ Runs the main program, based on the passed-in arguments.
+
+ Returns an exit code based on success or failure of the script - NOT
based
+ on the presence of any error-level annotations.
+ """
+ try:
+ print(*(x.sanitize() for x in parse_diff(sys.stdin.read())),
sep="\n")
+ return 0
+ except ValueError as e:
+ print("error:", e, file=sys.stderr)
+ return 1
+ except OSError as e:
+ print("error reading input:", e, file=sys.stderr)
+ return 2
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/traffic_ops_ort/t3c/util/util.go b/traffic_ops_ort/t3c/util/util.go
index ec5e165..623599b 100644
--- a/traffic_ops_ort/t3c/util/util.go
+++ b/traffic_ops_ort/t3c/util/util.go
@@ -106,10 +106,10 @@ func ExecCommand(fullCommand string, arg ...string)
([]byte, int, error) {
cmd.Stderr = &errbuf
err := cmd.Run()
- if err != nil {
- return outbuf.Bytes(), cmd.ProcessState.ExitCode(),
- errors.New("Error executing '" + fullCommand + "': " + errbuf.String())
- }
+ if err != nil {
+ return outbuf.Bytes(), cmd.ProcessState.ExitCode(),
+ errors.New("Error executing '" + fullCommand + "': " +
errbuf.String())
+ }
return outbuf.Bytes(), cmd.ProcessState.ExitCode(), err
}