diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 61939ed..d5cdae0 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -1,3 +1,17 @@
+# Copyright 2022 Google LLC
+#
+# Licensed 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
on:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..e5c3b28
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement (CLA). You (or your employer) retain the copyright to your
+contribution; this simply gives us permission to use and redistribute your
+contributions as part of the project. Head over to
+ to see your current agreements on file or
+to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code Reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
diff --git a/Dockerfile b/Dockerfile
index 278a9b7..82ec5b2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,18 @@
-FROM gcr.io/cloud-builders/go:debian
+# Copyright 2022 Google LLC
+#
+# Licensed 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
SHELL ["/bin/bash", "-c"]
#RUN git clone https://github.com/mbj4668/pyang.git /workspace/results/pyang@head/pyang
diff --git a/LICENSE b/LICENSE
index 8dada3e..d645695 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,4 @@
+
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -178,7 +179,7 @@
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
+ boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
@@ -186,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright {yyyy} {name of copyright owner}
+ Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index a9af731..8d5b55a 100644
--- a/README.md
+++ b/README.md
@@ -171,7 +171,7 @@ regexp | Files moved into GOPATH from its folder during CI build
pyang & pyangbind | pip
oc-pyang | git clone
goyang/ygot | go get
-yanglint | Debian package periodically uploaded to cloud storage
+yanglint | Debian packages (libyang2 and libyang2-tools) periodically uploaded to cloud storage. These are renamed libyang.deb and yanglint.deb respectively in the GCS bucket.
## Setting Up GCB
diff --git a/cmd_gen/main.go b/cmd_gen/main.go
index ee506e2..9c768af 100644
--- a/cmd_gen/main.go
+++ b/cmd_gen/main.go
@@ -122,7 +122,7 @@ var (
// GCB script, which together create the running environment for the
// generated validator script.
scriptTemplates = map[string]*scriptSpec{
- "pyang": &scriptSpec{
+ "pyang": {
headerTemplate: mustTemplate("pyang-header", `#!/bin/bash
workdir={{ .ResultsDir }}
mkdir -p "$workdir"
@@ -138,7 +138,7 @@ script_options=(
function run-dir() {
declare prefix="$workdir"/"$1"=="$2"==
shift 2
- echo $cmd "${options[@]}" "$@" > ${prefix}cmd
+ echo pyang "${options[@]}" "$@" > ${prefix}cmd
if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
mv ${prefix}pass ${prefix}fail
fi
@@ -147,25 +147,27 @@ function run-dir() {
perModelTemplate: mustTemplate("pyang", `run-dir "{{ .ModelDirName }}" "{{ .ModelName }}" {{- range $i, $buildFile := .BuildFiles }} {{ $buildFile }} {{- end }} {{- if .Parallel }} & {{- end }}
`),
},
- "oc-pyang": &scriptSpec{
+ "oc-pyang": {
headerTemplate: mustTemplate("oc-pyang-header", `#!/bin/bash
workdir={{ .ResultsDir }}
mkdir -p "$workdir"
`+"{{`"+util.PYANG_MSG_TEMPLATE_STRING+"`}}"+`
cmd="$@"
options=(
- -p {{ .ModelRoot }}
- -p {{ .RepoRoot }}/third_party/ietf
--openconfig
--ignore-error=OC_RELATIVE_PATH
+ -p {{ .ModelRoot }}
+ -p {{ .RepoRoot }}/third_party/ietf
)
script_options=(
--msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
declare prefix="$workdir"/"$1"=="$2"==
+ local cmd_display_options=( --plugindir '$OCPYANG_PLUGIN_DIR' "${options[@]}" )
+ local options=( --plugindir "$OCPYANG_PLUGIN_DIR" "${options[@]}" )
shift 2
- echo $cmd "${options[@]}" "$@" > ${prefix}cmd
+ echo pyang "${cmd_display_options[@]}" "$@" > ${prefix}cmd
if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
mv ${prefix}pass ${prefix}fail
fi
@@ -174,26 +176,33 @@ function run-dir() {
perModelTemplate: mustTemplate("oc-pyang", `run-dir "{{ .ModelDirName }}" "{{ .ModelName }}" {{- range $i, $buildFile := .BuildFiles }} {{ $buildFile }} {{- end }} {{- if .Parallel }} & {{- end }}
`),
},
- "pyangbind": &scriptSpec{
+ "pyangbind": {
headerTemplate: mustTemplate("pyangbind-header", `#!/bin/bash
workdir={{ .ResultsDir }}
mkdir -p "$workdir"
`+"{{`"+util.PYANG_MSG_TEMPLATE_STRING+"`}}"+`
cmd="$@"
options=(
+ -f pybind
-p {{ .ModelRoot }}
-p {{ .RepoRoot }}/third_party/ietf
- -f pybind
)
script_options=(
--msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
declare prefix="$workdir"/"$1"=="$2"==
- local options=( -o "$1"."$2".binding.py "${options[@]}" )
+ local output_file="$1"."$2".binding.py
+ local cmd_display_options=( --plugindir '$PYANGBIND_PLUGIN_DIR' -o "${output_file}" "${options[@]}" )
+ local options=( --plugindir "$PYANGBIND_PLUGIN_DIR" -o "${output_file}" "${options[@]}" )
shift 2
- echo $cmd "${options[@]}" "$@" > ${prefix}cmd
- if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
+ echo pyang "${cmd_display_options[@]}" "$@" > ${prefix}cmd
+ status=0
+ $cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass || status=1
+ if [[ $status -eq "0" ]]; then
+ python "${output_file}" &>> ${prefix}pass || status=1
+ fi
+ if [[ $status -eq "1" ]]; then
mv ${prefix}pass ${prefix}fail
fi
}
@@ -201,7 +210,7 @@ function run-dir() {
perModelTemplate: mustTemplate("pyangbind", `run-dir "{{ .ModelDirName }}" "{{ .ModelName }}" {{- range $i, $buildFile := .BuildFiles }} {{ $buildFile }} {{- end }} {{- if .Parallel }} & {{- end }}
`),
},
- "goyang-ygot": &scriptSpec{
+ "goyang-ygot": {
headerTemplate: mustTemplate("goyang-ygot-header", `#!/bin/bash
workdir={{ .ResultsDir }}
mkdir -p "$workdir"
@@ -226,8 +235,9 @@ function run-dir() {
status=0
$cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass || status=1
cd "$outdir"
- go get &>> ${prefix}pass || status=1
if [[ $status -eq "0" ]]; then
+ go mod init &>> ${prefix}pass || status=1
+ go mod tidy &>> ${prefix}pass || status=1
go build &>> ${prefix}pass || status=1
fi
if [[ $status -eq "1" ]]; then
@@ -238,7 +248,7 @@ function run-dir() {
perModelTemplate: mustTemplate("goyang-ygot", `run-dir "{{ .ModelDirName }}" "{{ .ModelName }}" {{- range $i, $buildFile := .BuildFiles }} {{ $buildFile }} {{- end }} {{- if .Parallel }} & {{- end }}
`),
},
- "yanglint": &scriptSpec{
+ "yanglint": {
headerTemplate: mustTemplate("yanglint-header", `#!/bin/bash
workdir={{ .ResultsDir }}
mkdir -p "$workdir"
@@ -261,7 +271,7 @@ function run-dir() {
perModelTemplate: mustTemplate("yanglint", `run-dir "{{ .ModelDirName }}" "{{ .ModelName }}" {{- range $i, $buildFile := .BuildFiles }} {{ $buildFile }} {{- end }} {{- if .Parallel }} & {{- end }}
`),
},
- "confd": &scriptSpec{
+ "confd": {
headerTemplate: mustTemplate("confd-header", `#!/bin/bash
workdir={{ .ResultsDir }}
mkdir -p "$workdir"
@@ -275,7 +285,7 @@ if [[ $status -eq "1" ]]; then
fi
`),
},
- "misc-checks": &scriptSpec{
+ "misc-checks": {
headerTemplate: mustTemplate("misc-checks-header", `#!/bin/bash
workdir={{ .ResultsDir }}
mkdir -p "$workdir"
@@ -341,6 +351,7 @@ type labelPoster interface {
// will be run only on a single model as specified in the .spec.yml file.
// 2. Thus, a validation command and result is provided for each model.
// 3. A file indicating pass/fail is output for each model into the given result directory.
+//
// Files names follow the "modelDir==model==status" format with no file extensions.
// The local flag indicates to run this as a helper to generate the script,
// rather than running it within GCB.
@@ -461,7 +472,7 @@ func main() {
// If it's a push on master, just upload badge for normal validators as the only action.
if prNumber == 0 {
if branchName != "master" {
- log.Fatalf("cmd_gen: There is no action to take for a non-master branch push, please re-examine your push triggers")
+ log.Fatalf("cmd_gen: pr-number not supplied as a flag to the build. Try re-running (by commenting \"/gcbrun\" on the GitHub PR) to see whether the $_PR_NUMBER substitution variable for Google Cloud Build gets passed into the build. If this branch is not associated with a PR, then it is inferred that this is a non-master branch push action, and thus there is no CI action that is expected, and in this case please re-examine your push triggers.")
}
pushToMaster = true
}
diff --git a/cmd_gen/main_test.go b/cmd_gen/main_test.go
index 2a30917..1bac902 100644
--- a/cmd_gen/main_test.go
+++ b/cmd_gen/main_test.go
@@ -66,7 +66,7 @@ script_options=(
function run-dir() {
declare prefix="$workdir"/"$1"=="$2"==
shift 2
- echo $cmd "${options[@]}" "$@" > ${prefix}cmd
+ echo pyang "${options[@]}" "$@" > ${prefix}cmd
if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
mv ${prefix}pass ${prefix}fail
fi
@@ -97,7 +97,7 @@ script_options=(
function run-dir() {
declare prefix="$workdir"/"$1"=="$2"==
shift 2
- echo $cmd "${options[@]}" "$@" > ${prefix}cmd
+ echo pyang "${options[@]}" "$@" > ${prefix}cmd
if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
mv ${prefix}pass ${prefix}fail
fi
@@ -116,18 +116,20 @@ mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
- -p testdata
- -p /workspace/third_party/ietf
--openconfig
--ignore-error=OC_RELATIVE_PATH
+ -p testdata
+ -p /workspace/third_party/ietf
)
script_options=(
--msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
declare prefix="$workdir"/"$1"=="$2"==
+ local cmd_display_options=( --plugindir '$OCPYANG_PLUGIN_DIR' "${options[@]}" )
+ local options=( --plugindir "$OCPYANG_PLUGIN_DIR" "${options[@]}" )
shift 2
- echo $cmd "${options[@]}" "$@" > ${prefix}cmd
+ echo pyang "${cmd_display_options[@]}" "$@" > ${prefix}cmd
if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
mv ${prefix}pass ${prefix}fail
fi
@@ -147,19 +149,26 @@ mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
+ -f pybind
-p testdata
-p /workspace/third_party/ietf
- -f pybind
)
script_options=(
--msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
declare prefix="$workdir"/"$1"=="$2"==
- local options=( -o "$1"."$2".binding.py "${options[@]}" )
+ local output_file="$1"."$2".binding.py
+ local cmd_display_options=( --plugindir '$PYANGBIND_PLUGIN_DIR' -o "${output_file}" "${options[@]}" )
+ local options=( --plugindir "$PYANGBIND_PLUGIN_DIR" -o "${output_file}" "${options[@]}" )
shift 2
- echo $cmd "${options[@]}" "$@" > ${prefix}cmd
- if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
+ echo pyang "${cmd_display_options[@]}" "$@" > ${prefix}cmd
+ status=0
+ $cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass || status=1
+ if [[ $status -eq "0" ]]; then
+ python "${output_file}" &>> ${prefix}pass || status=1
+ fi
+ if [[ $status -eq "1" ]]; then
mv ${prefix}pass ${prefix}fail
fi
}
@@ -196,8 +205,9 @@ function run-dir() {
status=0
$cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass || status=1
cd "$outdir"
- go get &>> ${prefix}pass || status=1
if [[ $status -eq "0" ]]; then
+ go mod init &>> ${prefix}pass || status=1
+ go mod tidy &>> ${prefix}pass || status=1
go build &>> ${prefix}pass || status=1
fi
if [[ $status -eq "1" ]]; then
diff --git a/cmd_gen/testdata/acl/.spec.yml b/cmd_gen/testdata/acl/.spec.yml
index 0539d97..09783fe 100644
--- a/cmd_gen/testdata/acl/.spec.yml
+++ b/cmd_gen/testdata/acl/.spec.yml
@@ -1,3 +1,17 @@
+# Copyright 2022 Google LLC
+#
+# Licensed 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: openconfig-acl
docs:
- yang/acl/openconfig-packet-match-types.yang
diff --git a/cmd_gen/testdata/optical-transport/.spec.yml b/cmd_gen/testdata/optical-transport/.spec.yml
index 1e4c8bf..ab07ba4 100644
--- a/cmd_gen/testdata/optical-transport/.spec.yml
+++ b/cmd_gen/testdata/optical-transport/.spec.yml
@@ -1,3 +1,17 @@
+# Copyright 2022 Google LLC
+#
+# Licensed 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: openconfig-terminal-device
docs:
- yang/optical-transport/openconfig-transport-types.yang
diff --git a/commonci/github.go b/commonci/github.go
index fcaedf9..05ef633 100644
--- a/commonci/github.go
+++ b/commonci/github.go
@@ -184,7 +184,7 @@ func (g *GithubRequestHandler) IsPRApproved(owner, repo string, prNumber int) (b
}
// PostLabel posts the given label to the PR. It is idempotent.
-// unit tests can be created based onon actual models-ci repo data that's sent back.
+// unit tests can be created based on actual models-ci repo data that's sent back.
func (g *GithubRequestHandler) PostLabel(labelName, labelColor, owner, repo string, prNumber int) error {
if g.labels[labelName] {
// Label already exists.
@@ -249,13 +249,18 @@ func (g *GithubRequestHandler) AddPRComment(body *string, owner, repo string, pr
return nil
}
-// AddOrEditPRComment posts or edits a comment to a PR.
+// AddEditOrDeletePRComment posts or edits a comment to a PR.
// If signature is empty, it's equivalent to AddPRComment.
+// Signature is a string that can be found in the comment in order to find and edit it.
// Otherwise, all comments are searched first, and if any matches the
// signature, then the comment is edited. If none matches, then a new comment
// is posted.
-func (g *GithubRequestHandler) AddOrEditPRComment(signature string, body *string, owner, repo string, prNumber int) error {
+// If body is nil, then it indicates delete.
+func (g *GithubRequestHandler) AddEditOrDeletePRComment(signature string, body *string, owner, repo string, prNumber int) error {
if signature == "" {
+ if body == nil {
+ return fmt.Errorf("PR comment body unspecified")
+ }
return g.AddPRComment(body, owner, repo, prNumber)
}
@@ -269,20 +274,37 @@ func (g *GithubRequestHandler) AddOrEditPRComment(signature string, body *string
return err
}); err != nil {
// If somehow this fails, we should be resilient and just post another comment.
+ if body == nil {
+ return fmt.Errorf("list comments failed -- cannot find comment to delete")
+ }
return g.AddPRComment(body, owner, repo, prNumber)
}
for _, pc := range comments {
if strings.Contains(*pc.Body, signature) {
- if err := Retry(5, "edit PR comment", func() error {
- _, _, err := g.client.Issues.EditComment(ctx, owner, repo, *pc.ID, &github.IssueComment{Body: body})
- return err
- }); err != nil {
- return g.AddPRComment(body, owner, repo, prNumber)
+ switch body {
+ case nil:
+ if err := Retry(5, "delete PR comment", func() error {
+ _, err := g.client.Issues.DeleteComment(ctx, owner, repo, *pc.ID)
+ return err
+ }); err != nil {
+ return fmt.Errorf("cannot delete comment: %v", err)
+ }
+ default:
+ if err := Retry(5, "edit PR comment", func() error {
+ _, _, err := g.client.Issues.EditComment(ctx, owner, repo, *pc.ID, &github.IssueComment{Body: body})
+ return err
+ }); err != nil {
+ return g.AddPRComment(body, owner, repo, prNumber)
+ }
}
return nil
}
}
+
+ if body == nil {
+ return fmt.Errorf("PR comment body unspecified")
+ }
return g.AddPRComment(body, owner, repo, prNumber)
}
diff --git a/commonci/testdata/acl/.spec.yml b/commonci/testdata/acl/.spec.yml
index 0539d97..09783fe 100644
--- a/commonci/testdata/acl/.spec.yml
+++ b/commonci/testdata/acl/.spec.yml
@@ -1,3 +1,17 @@
+# Copyright 2022 Google LLC
+#
+# Licensed 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: openconfig-acl
docs:
- yang/acl/openconfig-packet-match-types.yang
diff --git a/commonci/testdata/optical-transport/.spec.yml b/commonci/testdata/optical-transport/.spec.yml
index 1e4c8bf..ab07ba4 100644
--- a/commonci/testdata/optical-transport/.spec.yml
+++ b/commonci/testdata/optical-transport/.spec.yml
@@ -1,3 +1,17 @@
+# Copyright 2022 Google LLC
+#
+# Licensed 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: openconfig-terminal-device
docs:
- yang/optical-transport/openconfig-transport-types.yang
diff --git a/go.mod b/go.mod
index 94b7f12..05cabe2 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
github.com/google/go-github v17.0.0+incompatible
github.com/google/go-querystring v1.0.0 // indirect
github.com/openconfig/gnmi v0.0.0-20200508230933-d19cebf5e7be
- github.com/openconfig/goyang v0.2.1
+ github.com/openconfig/goyang v1.0.0
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
google.golang.org/protobuf v1.21.0
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86
diff --git a/go.sum b/go.sum
index af44c8e..18f7e5a 100644
--- a/go.sum
+++ b/go.sum
@@ -35,8 +35,8 @@ github.com/openconfig/gnmi v0.0.0-20200414194230-1597cc0f2600/go.mod h1:M/EcuapN
github.com/openconfig/gnmi v0.0.0-20200508230933-d19cebf5e7be h1:VEK8utxoyZu/hkpjLxvuBmK5yW3NmBo/v/Wu5VQAJVs=
github.com/openconfig/gnmi v0.0.0-20200508230933-d19cebf5e7be/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A=
github.com/openconfig/goyang v0.0.0-20200115183954-d0a48929f0ea/go.mod h1:dhXaV0JgHJzdrHi2l+w0fZrwArtXL7jEFoiqLEdmkvU=
-github.com/openconfig/goyang v0.2.1 h1:TaADsLGzULGQL8xTmvLjmXqZ/J1F73dMzURDqrv9cLs=
-github.com/openconfig/goyang v0.2.1/go.mod h1:vX61x01Q46AzbZUzG617vWqh/cB+aisc+RrNkXRd3W8=
+github.com/openconfig/goyang v1.0.0 h1:nYaFu7BOAk/eQn4CgAUjgYPfp3J6CdXrBryp32E5CjI=
+github.com/openconfig/goyang v1.0.0/go.mod h1:vX61x01Q46AzbZUzG617vWqh/cB+aisc+RrNkXRd3W8=
github.com/openconfig/ygot v0.6.0/go.mod h1:o30svNf7O0xK+R35tlx95odkDmZWS9JyWWQSmIhqwAs=
github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
diff --git a/post_results/main.go b/post_results/main.go
index c05cff3..3d40d45 100644
--- a/post_results/main.go
+++ b/post_results/main.go
@@ -41,6 +41,8 @@ const (
IgnorePyangWarnings = true
// IgnoreConfdWarnings ignores all warnings from ConfD.
IgnoreConfdWarnings = false
+ // bucketName is the Google storage bucket name.
+ bucketName = "openconfig"
)
var (
@@ -59,7 +61,7 @@ var (
prNumber int
// badgeCmdTemplate is the badge creation and upload command generated for pushes to the master branch.
- badgeCmdTemplate = mustTemplate("badgeCmd", `REMOTE_PATH_PFX=gs://artifacts.disco-idea-817.appspot.com/compatibility-badges/{{ .RepoPrefix }}:
+ badgeCmdTemplate = mustTemplate("badgeCmd", fmt.Sprintf(`REMOTE_PATH_PFX=gs://%s/compatibility-badges/{{ .RepoPrefix }}:
RESULTSDIR={{ .ResultsDir }}
upload-public-file() {
gsutil cp $RESULTSDIR/$1 "$REMOTE_PATH_PFX"$1
@@ -69,7 +71,7 @@ upload-public-file() {
badge "{{ .Status }}" "{{ .ValidatorDesc }}" :{{ .Colour }} > $RESULTSDIR/{{ .ValidatorAndVersion }}.svg
upload-public-file {{ .ValidatorAndVersion }}.svg
upload-public-file {{ .ValidatorAndVersion }}.html
-`)
+`, bucketName))
)
// mustTemplate generates a template.Template for a particular named source template
@@ -193,9 +195,7 @@ func readGoyangVersionsLog(logPath string, masterBranch bool, fileProperties map
value = value[1 : len(value)-1] // Remove enclosing quotes.
}
switch name {
- case "openconfig-version":
- fallthrough
- case "latest-revision-version":
+ case "openconfig-version", "belonging-module", "latest-revision-version":
if masterBranch {
name = "master-" + name
}
@@ -212,101 +212,64 @@ func readGoyangVersionsLog(logPath string, masterBranch bool, fileProperties map
// according to semantic versioning rules.
// Note that any increase is fine, including jumps, e.g. 1.0.0 -> 1.0.2.
// If there isn't an increase, a descriptive error message is returned.
-func checkSemverIncrease(oldVersion, newVersion string) error {
+func checkSemverIncrease(oldVersion, newVersion, versionStringName string) (*semver.Version, *semver.Version, error) {
newV, err := semver.StrictNewVersion(newVersion)
if err != nil {
- return fmt.Errorf("invalid version string: %q", newVersion)
+ return nil, nil, fmt.Errorf("invalid version string: %q", newVersion)
}
oldV, err := semver.StrictNewVersion(oldVersion)
switch {
case err != nil:
- return fmt.Errorf("unexpected error, base branch version string unparseable: %q", oldVersion)
+ return nil, nil, fmt.Errorf("unexpected error, base branch version string unparseable: %q", oldVersion)
case newV.Equal(oldV):
- return fmt.Errorf("file updated but PR version not updated: %q", oldVersion)
+ return nil, nil, fmt.Errorf("file updated but %s string not updated: %q", versionStringName, oldVersion)
case !newV.GreaterThan(oldV):
- return fmt.Errorf("new semantic version not valid, old version: %q, new version: %q", oldVersion, newVersion)
+ return nil, nil, fmt.Errorf("new semantic version not valid, old version: %q, new version: %q", oldVersion, newVersion)
default:
- return nil
+ return oldV, newV, nil
}
}
-// processMiscChecksOutput takes the raw result output from the misc-checks
-// results directory and returns its formatted report and pass/fail status.
-func processMiscChecksOutput(resultsDir string) (string, bool, error) {
- fileProperties := map[string]map[string]string{}
- changedFiles, err := readYangFilesList(filepath.Join(resultsDir, "changed-files.txt"))
- if err != nil {
- return "", false, err
- }
- for _, file := range changedFiles {
- if _, ok := fileProperties[file]; !ok {
- fileProperties[file] = map[string]string{}
- }
- fileProperties[file]["changed"] = "true"
- }
- if err := readGoyangVersionsLog(filepath.Join(resultsDir, "pr-file-parse-log"), false, fileProperties); err != nil {
- return "", false, err
- }
- if err := readGoyangVersionsLog(filepath.Join(resultsDir, "master-file-parse-log"), true, fileProperties); err != nil {
- return "", false, err
- }
-
- var ocVersionViolations []string
- ocVersionChangedCount := 0
- var reachabilityViolations []string
- filesReachedCount := 0
- // Only look at the PR's files as they might be different from the master's files.
- allNonEmptyPRFiles, err := readYangFilesList(filepath.Join(resultsDir, "all-non-empty-files.txt"))
- if err != nil {
- return "", false, err
- }
- for _, file := range allNonEmptyPRFiles {
- properties, ok := fileProperties[file]
+type fileAndVersion struct {
+ name string
+ version *semver.Version
+}
- // Reachability check
- if !ok || properties["reachable"] != "true" {
- reachabilityViolations = append(reachabilityViolations, sprintLineHTML("%s: file not used by any .spec.yml build.", file))
- // If the file was not reached, then its other
- // parameters would not have been parsed by goyang, so
- // simply skip the rest of the checks.
- continue
+// versionGroupViolationsHTML returns the version violations where a group of
+// module/submodule files don't have matching versions.
+func versionGroupViolationsHTML(moduleFileGroups map[string][]fileAndVersion) []string {
+ var violations []string
+
+ var modules []string
+ for m := range moduleFileGroups {
+ modules = append(modules, m)
+ }
+ sort.Strings(modules)
+ for _, moduleName := range modules {
+ latestVersion := semver.MustParse("0.0.0")
+ latestVersionModule := ""
+ for _, nameAndVersion := range moduleFileGroups[moduleName] {
+ if nameAndVersion.version.GreaterThan(latestVersion) {
+ latestVersion = nameAndVersion.version
+ latestVersionModule = nameAndVersion.name
+ }
}
- filesReachedCount += 1
+ latestVersionString := latestVersion.Original()
- // openconfig-version update check
- ocVersion, hasVersion := properties["openconfig-version"]
- masterOcVersion, hadVersion := properties["master-openconfig-version"]
- switch {
- case properties["changed"] != "true":
- // We assume the versioning is correct without change.
- case hadVersion && hasVersion:
- if err := checkSemverIncrease(masterOcVersion, ocVersion); err != nil {
- ocVersionViolations = append(ocVersionViolations, sprintLineHTML(file+": "+err.Error()))
- } else {
- ocVersionChangedCount += 1
+ var violation strings.Builder
+ for _, nameAndVersion := range moduleFileGroups[moduleName] {
+ if version := nameAndVersion.version.Original(); version != latestVersionString {
+ if violation.Len() != 0 {
+ violation.WriteString(",")
+ }
+ violation.WriteString(fmt.Sprintf(" %s (%s)", nameAndVersion.name, version))
}
- case hadVersion && !hasVersion:
- ocVersionViolations = append(ocVersionViolations, sprintLineHTML("%s: openconfig-version was removed", file))
- default: // If didn't have version before, any new version is accepted.
- ocVersionChangedCount += 1
}
- }
-
- // Compute HTML string and pass/fail status.
- var out strings.Builder
- var pass = true
- appendViolationOut := func(desc string, violations []string, passString string) {
- if len(violations) == 0 {
- out.WriteString(sprintSummaryHTML(commonci.BoolStatusToString(true), desc, passString))
- } else {
- out.WriteString(sprintSummaryHTML(commonci.BoolStatusToString(false), desc, strings.Join(violations, "")))
- pass = false
+ if violation.Len() != 0 {
+ violations = append(violations, sprintLineHTML("module set %s is at %s (%s), non-matching files:%s", moduleName, latestVersionString, latestVersionModule, violation.String()))
}
}
- appendViolationOut("openconfig-version update check", ocVersionViolations, fmt.Sprintf("%d file(s) correctly updated.\n", ocVersionChangedCount))
- appendViolationOut(".spec.yml build reachability check", reachabilityViolations, fmt.Sprintf("%d files reached by build rules.\n", filesReachedCount))
-
- return out.String(), pass, nil
+ return violations
}
// processStandardOutput takes raw pyang/confd output and transforms it to an
@@ -391,6 +354,12 @@ func processPyangOutput(rawOut string, pass, noWarnings bool) (string, error) {
return out.String(), nil
}
+// userfyBashCommand changes the bash command displayed to the user to be
+// something that's easier to use.
+func userfyBashCommand(cmd string) string {
+ return strings.NewReplacer("/workspace/", "$OC_WORKSPACE/", "$OCPYANG_PLUGIN_DIR", "$GOPATH/src/github.com/openconfig/oc-pyang/openconfig_pyang/plugins", "$PYANGBIND_PLUGIN_DIR", "$GOPATH/src/github.com/robshakir/pyangbind/pyangbind/plugin").Replace(cmd)
+}
+
// parseModelResultsHTML transforms the output files of the validator script into HTML
// to be displayed on GitHub.
// If condensed=true, then only errors are provided.
@@ -442,7 +411,7 @@ func parseModelResultsHTML(validatorId, validatorResultDir string, condensed boo
// order, ${prefix}cmd should be walked first,
// such that ${prefix}pass or ${prefix}fail
// will have it ready to display to the user.
- bashCommand = outString
+ bashCommand = userfyBashCommand(outString)
bashCommandModelDirName = modelDirName
bashCommandModelName = modelName
return nil
@@ -478,7 +447,7 @@ func parseModelResultsHTML(validatorId, validatorResultDir string, condensed boo
// Display bash command that produced the validator result if it exists.
var bashCommandSummary string
if bashCommand != "" && bashCommandModelDirName == modelDirName && bashCommandModelName == modelName {
- bashCommandSummary = sprintSummaryHTML("cmd", "bash command", "
%s
", bashCommand)
+ bashCommandSummary = fmt.Sprintf("%s %s\n%s
\n", commonci.Emoji("cmd"), "bash command", bashCommand)
}
// Also display the error string.
modelHTML.WriteString(sprintSummaryHTML(status, modelName, bashCommandSummary+outString))
@@ -500,17 +469,24 @@ func parseModelResultsHTML(validatorId, validatorResultDir string, condensed boo
// getResult parses the results for the given validator and its results
// directory, and returns the string to be put in a GitHub gist comment as well
// as the status (i.e. pass or fail).
+//
+// It also returns a newline-separated string of the list of all YANG files
+// that have their major versions updated, which is empty if there are no major
+// version changes.
+//
// If condensed=true, then only errors are provided.
-func getResult(validatorId, resultsDir string, condensed bool) (string, bool, error) {
+func getResult(validatorId, resultsDir string, condensed bool) (string, bool, versionRecordSlice, error) {
validator, ok := commonci.Validators[validatorId]
if !ok {
- return "", false, fmt.Errorf("validator %q not found!", validatorId)
+ return "", false, nil, fmt.Errorf("validator %q not found!", validatorId)
}
// outString is parsed stdout.
var outString string
// pass is the overall validation result.
var pass bool
+ // versionRecords contains all information regarding YANG version changes.
+ var versionRecords versionRecordSlice
failFileBytes, err := ioutil.ReadFile(filepath.Join(resultsDir, commonci.FailFileName))
// existent fail file == failure.
@@ -529,7 +505,7 @@ func getResult(validatorId, resultsDir string, condensed bool) (string, bool, er
}
pass = false
case validator.IsPerModel && validatorId == "misc-checks":
- outString, pass, err = processMiscChecksOutput(resultsDir)
+ outString, pass, versionRecords, err = processMiscChecksOutput(resultsDir)
case validator.IsPerModel:
outString, pass, err = parseModelResultsHTML(validatorId, resultsDir, condensed)
if pass && condensed {
@@ -540,7 +516,7 @@ func getResult(validatorId, resultsDir string, condensed bool) (string, bool, er
pass = true
}
- return outString, pass, err
+ return outString, pass, versionRecords, err
}
// WriteBadgeUploadCmdFile writes a bash script into resultsDir that posts a
@@ -657,7 +633,7 @@ func postCompatibilityReport(validatorAndVersions []commonci.ValidatorAndVersion
resultsDir := commonci.ValidatorResultsDir(vv.ValidatorId, vv.Version)
// Post parsed test results as a gist comment.
- testResultString, pass, err := getResult(vv.ValidatorId, resultsDir, false)
+ testResultString, pass, _, err := getResult(vv.ValidatorId, resultsDir, false)
if err != nil {
return fmt.Errorf("postResult: couldn't parse results for <%s>@<%s> in resultsDir %q: %v", vv.ValidatorId, vv.Version, resultsDir, err)
}
@@ -665,18 +641,41 @@ func postCompatibilityReport(validatorAndVersions []commonci.ValidatorAndVersion
gistTitle := fmt.Sprintf("%s %s", commonci.Emoji(commonci.BoolStatusToString(pass)), validatorDescs[i])
id, err := g.AddGistComment(gistID, gistTitle, testResultString)
if err != nil {
- fmt.Errorf("postResult: could not add gist comment: %v", err)
+ return fmt.Errorf("postResult: could not add gist comment: %v", err)
}
commentBuilder.WriteString(fmt.Sprintf("%s [%s](%s#gistcomment-%d)\n", commonci.Emoji(commonci.BoolStatusToString(pass)), validatorDescs[i], gistURL, id))
}
comment := commentBuilder.String()
- if err := g.AddOrEditPRComment("Compatibility Report for commit", &comment, owner, repo, prNumber); err != nil {
+ if err := g.AddEditOrDeletePRComment("Compatibility Report for commit", &comment, owner, repo, prNumber); err != nil {
return fmt.Errorf("postCompatibilityReport: couldn't post comment: %v", err)
}
return nil
}
+// postBreakingChangeLabel posts label and information on whether the PR
+// contains breaking changes that necessitate a repository version bump.
+func postBreakingChangeLabel(g *commonci.GithubRequestHandler, versionRecords versionRecordSlice) error {
+ if versionRecords.hasBreaking() {
+ if err := g.PostLabel("breaking", "FF0000", owner, repo, prNumber); err != nil {
+ return fmt.Errorf("couldn't post label: %v", err)
+ }
+ g.DeleteLabel("non-breaking", owner, repo, prNumber)
+ } else {
+ if err := g.PostLabel("non-breaking", "00FF00", owner, repo, prNumber); err != nil {
+ return fmt.Errorf("couldn't post label: %v", err)
+ }
+ // Don't error out on error since it's possible the label doesn't exist.
+ g.DeleteLabel("breaking", owner, repo, prNumber)
+ }
+ // NOTE: "ajor" is not a typo.
+ majorVersionChangesComment := versionRecords.MajorVersionChanges()
+ if err := g.AddEditOrDeletePRComment("ajor YANG version changes in commit", &majorVersionChangesComment, owner, repo, prNumber); err != nil {
+ return fmt.Errorf("couldn't post major YANG version changes comment: %v", err)
+ }
+ return nil
+}
+
// postResult retrieves the test output for the given validator and version
// from its results folder and posts a gist and PR status linking to the gist.
func postResult(validatorId, version string) error {
@@ -719,7 +718,7 @@ func postResult(validatorId, version string) error {
if err != nil {
return fmt.Errorf("postResult: %v", err)
}
- testResultString, pass, err := getResult(validatorId, resultsDir, false)
+ testResultString, pass, versionRecords, err := getResult(validatorId, resultsDir, false)
if err != nil {
return fmt.Errorf("postResult: couldn't parse results: %v", err)
}
@@ -772,9 +771,15 @@ func postResult(validatorId, version string) error {
return fmt.Errorf("postResult: couldn't create gist: %v", err)
}
+ if !pushToMaster && validatorId == "misc-checks" {
+ if err := postBreakingChangeLabel(g, versionRecords); err != nil {
+ return err
+ }
+ }
+
// Post parsed test results as a gist comment.
if _, err := g.AddGistComment(gistID, fmt.Sprintf("%s %s", commonci.Emoji(commonci.BoolStatusToString(pass)), validatorDesc), testResultString); err != nil {
- fmt.Errorf("postResult: could not add gist comment: %v", err)
+ return fmt.Errorf("postResult: could not add gist comment: %v", err)
}
prUpdate := &commonci.GithubPRUpdate{
diff --git a/post_results/main_test.go b/post_results/main_test.go
index 9849fce..7f7514d 100644
--- a/post_results/main_test.go
+++ b/post_results/main_test.go
@@ -171,7 +171,7 @@ func TestCheckSemverIncrease(t *testing.T) {
desc: "no change",
inOldVersion: "1.0.1",
inNewVersion: "1.0.1",
- wantErrSubstr: "file updated but PR version not updated",
+ wantErrSubstr: "file updated but test-version string not updated",
}, {
desc: "decrease",
inOldVersion: "1.0.1",
@@ -191,10 +191,74 @@ func TestCheckSemverIncrease(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
- err := checkSemverIncrease(tt.inOldVersion, tt.inNewVersion)
+ oldver, newver, err := checkSemverIncrease(tt.inOldVersion, tt.inNewVersion, "test-version")
if diff := errdiff.Substring(err, tt.wantErrSubstr); diff != "" {
t.Fatalf("did not get expected error, %s", diff)
}
+ if err == nil {
+ if got, want := oldver.String(), tt.inOldVersion; got != want {
+ t.Fatalf("old version: got %s, want %s", got, want)
+ }
+ if got, want := newver.String(), tt.inNewVersion; got != want {
+ t.Fatalf("old version: got %s, want %s", got, want)
+ }
+ }
+ })
+ }
+}
+
+func TestVersionRecords(t *testing.T) {
+ tests := []struct {
+ desc string
+ inVersionRecords versionRecordSlice
+ wantHasBreaking bool
+ }{{
+ desc: "has breaking",
+ inVersionRecords: versionRecordSlice{{
+ File: "openconfig-interface-submodule.yang",
+ OldMajorVersion: 0,
+ NewMajorVersion: 1,
+ OldVersion: "0.5.0",
+ NewVersion: "1.0.0",
+ }, {
+ File: "openconfig-interface.yang",
+ OldMajorVersion: 1,
+ NewMajorVersion: 2,
+ OldVersion: "1.1.3",
+ NewVersion: "2.0.0",
+ }},
+ wantHasBreaking: true,
+ }, {
+ desc: "non-breaking",
+ inVersionRecords: versionRecordSlice{{
+ File: "openconfig-interface-submodule.yang",
+ OldMajorVersion: 0,
+ NewMajorVersion: 1,
+ OldVersion: "0.5.0",
+ NewVersion: "1.0.0",
+ }},
+ wantHasBreaking: false,
+ }, {
+ desc: "non-breaking",
+ inVersionRecords: versionRecordSlice{{
+ File: "openconfig-interface-submodule.yang",
+ OldMajorVersion: 1,
+ NewMajorVersion: 1,
+ OldVersion: "1.5.0",
+ NewVersion: "1.6.0",
+ }},
+ wantHasBreaking: false,
+ }, {
+ desc: "empty",
+ inVersionRecords: nil,
+ wantHasBreaking: false,
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ if gotHasBreaking, want := tt.inVersionRecords.hasBreaking(), tt.wantHasBreaking; gotHasBreaking != want {
+ t.Errorf("gotHasBreaking %v, want %v", gotHasBreaking, want)
+ }
})
}
}
@@ -203,14 +267,15 @@ func TestGetResult(t *testing.T) {
modelRoot = "/workspace/release/yang"
tests := []struct {
- name string
- inValidatorResultDir string
- inValidatorId string
- wantPass bool
- wantOut string
- wantCondensedOut string
- wantCondensedOutSame bool
- wantErrSubstr string
+ name string
+ inValidatorResultDir string
+ inValidatorId string
+ wantPass bool
+ wantOut string
+ wantCondensedOut string
+ wantCondensedOutSame bool
+ wantVersionRecordSlice versionRecordSlice
+ wantErrSubstr string
}{{
name: "basic pyang pass",
inValidatorResultDir: "testdata/oc-pyang",
@@ -220,10 +285,12 @@ func TestGetResult(t *testing.T) {
✅ acl
✅ openconfig-acl
-
- 💲 bash command
+💲 bash command
foo command
-
+$OC_WORKSPACE/foo/bar
+$GOPATH/src/github.com/openconfig/oc-pyang/openconfig_pyang/plugins
+$GOPATH/src/github.com/robshakir/pyangbind/pyangbind/plugin
+
Passed.
@@ -263,10 +330,12 @@ Test failed with no stderr output.
✅ acl
✅ openconfig-acl
-
- 💲 bash command
+💲 bash command
foo command
-
+$OC_WORKSPACE/foo/bar
+$GOPATH/src/github.com/openconfig/oc-pyang/openconfig_pyang/plugins
+$GOPATH/src/github.com/robshakir/pyangbind/pyangbind/plugin
+
Passed.
@@ -450,14 +519,48 @@ Failed.
inValidatorResultDir: "testdata/misc-checks-pass",
inValidatorId: "misc-checks",
wantPass: true,
+ wantVersionRecordSlice: versionRecordSlice{{
+ File: "openconfig-acl-submodule.yang",
+ OldMajorVersion: 1,
+ NewMajorVersion: 1,
+ OldVersion: "1.1.3",
+ NewVersion: "1.2.3",
+ }, {
+ File: "openconfig-acl.yang",
+ OldMajorVersion: 1,
+ NewMajorVersion: 1,
+ OldVersion: "1.2.2",
+ NewVersion: "1.2.3",
+ }, {
+ File: "openconfig-interface-submodule.yang",
+ OldMajorVersion: 0,
+ NewMajorVersion: 1,
+ OldVersion: "0.5.0",
+ NewVersion: "1.0.0",
+ }, {
+ File: "openconfig-interface.yang",
+ OldMajorVersion: 1,
+ NewMajorVersion: 2,
+ OldVersion: "1.1.3",
+ NewVersion: "2.0.0",
+ }, {
+ File: "openconfig-packet-match.yang",
+ OldMajorVersion: 1,
+ NewMajorVersion: 1,
+ OldVersion: "1.1.2",
+ NewVersion: "1.2.0",
+ }},
wantOut: `
✅ openconfig-version update check
-6 file(s) correctly updated.
+9 file(s) correctly updated.
✅ .spec.yml build reachability check
-8 files reached by build rules.
+11 files reached by build rules.
+
+ ✅ submodule versions must match the belonging module's version
+7 module/submodule file groups have matching versions
`,
wantCondensedOutSame: true,
}, {
@@ -468,7 +571,7 @@ Failed.
wantOut: `
⛔ openconfig-version update check
changed-version-to-noversion.yang: openconfig-version was removed
- openconfig-acl.yang: file updated but PR version not updated: "1.2.2"
+ openconfig-acl.yang: file updated but openconfig-version string not updated: "1.2.2"
openconfig-mpls.yang: new semantic version not valid, old version: "2.3.4", new version: "2.2.5"
@@ -478,6 +581,10 @@ Failed.
changed-version-to-unreached.yang: file not used by any .spec.yml build.
unchanged-unreached.yang: file not used by any .spec.yml build.
+
+ ⛔ submodule versions must match the belonging module's version
+ module set openconfig-mpls is at 2.3.4 (openconfig-mpls-submodule.yang), non-matching files: openconfig-mpls-submodule2.yang (2.3.2), openconfig-mpls.yang (2.2.5)
+
`,
wantCondensedOutSame: true,
}}
@@ -485,7 +592,7 @@ Failed.
for _, tt := range tests {
for _, condensed := range []bool{false, true} {
t.Run(fmt.Sprintf(tt.name+"@condensed=%v", condensed), func(t *testing.T) {
- gotOut, gotPass, err := getResult(tt.inValidatorId, tt.inValidatorResultDir, condensed)
+ gotOut, gotPass, versionRecords, err := getResult(tt.inValidatorId, tt.inValidatorResultDir, condensed)
if err != nil {
if diff := errdiff.Substring(err, tt.wantErrSubstr); diff != "" {
t.Fatalf("did not get expected error, %s", diff)
@@ -495,6 +602,9 @@ Failed.
if gotPass != tt.wantPass {
t.Errorf("gotPass %v, want %v", gotPass, tt.wantPass)
}
+ if diff := cmp.Diff(tt.wantVersionRecordSlice, versionRecords); diff != "" {
+ t.Errorf("versionRecords (-want, +got):\n%s", diff)
+ }
wantOut := tt.wantOut
if condensed && !tt.wantCondensedOutSame {
wantOut = tt.wantCondensedOut
@@ -581,7 +691,7 @@ func TestWriteBadgeUploadCmdFile(t *testing.T) {
inValidatorUniqueStr: "pyang@latest",
inPass: true,
inResultsDir: "results-dir",
- wantFileContent: `REMOTE_PATH_PFX=gs://artifacts.disco-idea-817.appspot.com/compatibility-badges/openconfig-repo:
+ wantFileContent: `REMOTE_PATH_PFX=gs://openconfig/compatibility-badges/openconfig-repo:
RESULTSDIR=results-dir
upload-public-file() {
gsutil cp $RESULTSDIR/$1 "$REMOTE_PATH_PFX"$1
@@ -598,7 +708,7 @@ upload-public-file pyang@latest.html
inValidatorUniqueStr: "pyang",
inPass: false,
inResultsDir: "results-directory",
- wantFileContent: `REMOTE_PATH_PFX=gs://artifacts.disco-idea-817.appspot.com/compatibility-badges/openconfig-repo:
+ wantFileContent: `REMOTE_PATH_PFX=gs://openconfig/compatibility-badges/openconfig-repo:
RESULTSDIR=results-directory
upload-public-file() {
gsutil cp $RESULTSDIR/$1 "$REMOTE_PATH_PFX"$1
diff --git a/post_results/misc-checks.go b/post_results/misc-checks.go
new file mode 100644
index 0000000..9053aac
--- /dev/null
+++ b/post_results/misc-checks.go
@@ -0,0 +1,143 @@
+package main
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "github.com/Masterminds/semver/v3"
+ "github.com/openconfig/models-ci/commonci"
+)
+
+type versionRecord struct {
+ File string
+ OldMajorVersion uint64
+ NewMajorVersion uint64
+ OldVersion string
+ NewVersion string
+}
+
+type versionRecordSlice []versionRecord
+
+func (s versionRecordSlice) MajorVersionChanges() string {
+ if len(s) == 0 {
+ return fmt.Sprintf("No major YANG version changes in commit %s", commitSHA)
+ }
+
+ var b strings.Builder
+ b.WriteString(fmt.Sprintf("Major YANG version changes in commit %s:\n", commitSHA))
+ for _, change := range s {
+ if change.OldMajorVersion != change.NewMajorVersion {
+ b.WriteString(fmt.Sprintf("%s: `%s` -> `%s`\n", change.File, change.OldVersion, change.NewVersion))
+ }
+ }
+ return b.String()
+}
+
+func (s versionRecordSlice) hasBreaking() bool {
+ for _, change := range s {
+ if change.OldMajorVersion != 0 && change.OldMajorVersion != change.NewMajorVersion {
+ return true
+ }
+ }
+ return false
+}
+
+// processMiscChecksOutput takes the raw result output from the misc-checks
+// results directory and returns its formatted report and pass/fail status.
+//
+// It also returns a list of version changes for each file.
+func processMiscChecksOutput(resultsDir string) (string, bool, versionRecordSlice, error) {
+ fileProperties := map[string]map[string]string{}
+ changedFiles, err := readYangFilesList(filepath.Join(resultsDir, "changed-files.txt"))
+ if err != nil {
+ return "", false, nil, err
+ }
+ for _, file := range changedFiles {
+ if _, ok := fileProperties[file]; !ok {
+ fileProperties[file] = map[string]string{}
+ }
+ fileProperties[file]["changed"] = "true"
+ }
+ if err := readGoyangVersionsLog(filepath.Join(resultsDir, "pr-file-parse-log"), false, fileProperties); err != nil {
+ return "", false, nil, err
+ }
+ if err := readGoyangVersionsLog(filepath.Join(resultsDir, "master-file-parse-log"), true, fileProperties); err != nil {
+ return "", false, nil, err
+ }
+
+ var ocVersionViolations []string
+ ocVersionChangedCount := 0
+ var reachabilityViolations []string
+ filesReachedCount := 0
+ // Only look at the PR's files as they might be different from the master's files.
+ allNonEmptyPRFiles, err := readYangFilesList(filepath.Join(resultsDir, "all-non-empty-files.txt"))
+ if err != nil {
+ return "", false, nil, err
+ }
+ moduleFileGroups := map[string][]fileAndVersion{}
+ var versionRecords versionRecordSlice
+ for _, file := range allNonEmptyPRFiles {
+ properties, ok := fileProperties[file]
+
+ // Reachability check
+ if !ok || properties["reachable"] != "true" {
+ reachabilityViolations = append(reachabilityViolations, sprintLineHTML("%s: file not used by any .spec.yml build.", file))
+ // If the file was not reached, then its other
+ // parameters would not have been parsed by goyang, so
+ // simply skip the rest of the checks.
+ continue
+ }
+ filesReachedCount += 1
+
+ // openconfig-version update check
+ ocVersion, hasVersion := properties["openconfig-version"]
+ masterOcVersion, hadVersion := properties["master-openconfig-version"]
+ switch {
+ case properties["changed"] != "true":
+ // We assume the versioning is correct without change.
+ case hadVersion && hasVersion:
+ oldver, newver, err := checkSemverIncrease(masterOcVersion, ocVersion, "openconfig-version")
+ if err != nil {
+ ocVersionViolations = append(ocVersionViolations, sprintLineHTML(file+": "+err.Error()))
+ break
+ }
+ ocVersionChangedCount += 1
+ versionRecords = append(versionRecords, versionRecord{
+ File: file,
+ OldMajorVersion: oldver.Major(),
+ NewMajorVersion: newver.Major(),
+ OldVersion: masterOcVersion,
+ NewVersion: ocVersion,
+ })
+ case hadVersion && !hasVersion:
+ ocVersionViolations = append(ocVersionViolations, sprintLineHTML("%s: openconfig-version was removed", file))
+ default: // If didn't have version before, any new version is accepted.
+ ocVersionChangedCount += 1
+ }
+
+ if mod, ok := properties["belonging-module"]; hasVersion && ok {
+ // Error checking is already done by the version update check.
+ if v, err := semver.StrictNewVersion(ocVersion); err == nil {
+ moduleFileGroups[mod] = append(moduleFileGroups[mod], fileAndVersion{name: file, version: v})
+ }
+ }
+ }
+
+ // Compute HTML string and pass/fail status.
+ var out strings.Builder
+ var pass = true
+ appendViolationOut := func(desc string, violations []string, passString string) {
+ if len(violations) == 0 {
+ out.WriteString(sprintSummaryHTML(commonci.BoolStatusToString(true), desc, passString))
+ } else {
+ out.WriteString(sprintSummaryHTML(commonci.BoolStatusToString(false), desc, strings.Join(violations, "")))
+ pass = false
+ }
+ }
+ appendViolationOut("openconfig-version update check", ocVersionViolations, fmt.Sprintf("%d file(s) correctly updated.\n", ocVersionChangedCount))
+ appendViolationOut(".spec.yml build reachability check", reachabilityViolations, fmt.Sprintf("%d files reached by build rules.\n", filesReachedCount))
+ appendViolationOut("submodule versions must match the belonging module's version", versionGroupViolationsHTML(moduleFileGroups), fmt.Sprintf("%d module/submodule file groups have matching versions", len(moduleFileGroups)))
+
+ return out.String(), pass, versionRecords, nil
+}
diff --git a/post_results/testdata/misc-checks-fail/all-non-empty-files.txt b/post_results/testdata/misc-checks-fail/all-non-empty-files.txt
index bb42c59..4ab6b1f 100644
--- a/post_results/testdata/misc-checks-fail/all-non-empty-files.txt
+++ b/post_results/testdata/misc-checks-fail/all-non-empty-files.txt
@@ -1,4 +1,6 @@
release/models/mpls/openconfig-mpls.yang
+release/models/mpls/openconfig-mpls-submodule.yang
+release/models/mpls/openconfig-mpls-submodule2.yang
release/models/acl/deeper/openconfig-acl.yang
release/models/bgp/changed-unreached-to-unreached.yang
diff --git a/post_results/testdata/misc-checks-fail/master-file-parse-log b/post_results/testdata/misc-checks-fail/master-file-parse-log
index 32d6369..96ecb18 100644
--- a/post_results/testdata/misc-checks-fail/master-file-parse-log
+++ b/post_results/testdata/misc-checks-fail/master-file-parse-log
@@ -1,6 +1,8 @@
-openconfig-mpls.yang: openconfig-version:"2.3.4" latest-revision-version:"2.3.4"
-openconfig-acl.yang: openconfig-version:"1.2.2" latest-revision-version:"1.2.2"
+openconfig-mpls.yang: belonging-module:"openconfig-mpls" openconfig-version:"2.3.4" latest-revision-version:"2.3.4"
+openconfig-mpls-submodule.yang: belonging-module:"openconfig-mpls" openconfig-version:"2.3.4" latest-revision-version:"2.3.4"
+openconfig-mpls-submodule2.yang: belonging-module:"openconfig-mpls" openconfig-version:"2.3.2" latest-revision-version:"2.3.2"
+openconfig-acl.yang: belonging-module:"openconfig-acl" openconfig-version:"1.2.2" latest-revision-version:"1.2.2"
-changed-noversion-to-unreached.yang:
-changed-version-to-unreached.yang: openconfig-version:"1.0.0"
-changed-version-to-noversion.yang: openconfig-version:"1.0.0"
+changed-noversion-to-unreached.yang: belonging-module:"changed-noversion-to-unreached"
+changed-version-to-unreached.yang: belonging-module:"changed-version-to-unreached" openconfig-version:"1.0.0"
+changed-version-to-noversion.yang: belonging-module:"changed-version-to-noversion" openconfig-version:"1.0.0"
diff --git a/post_results/testdata/misc-checks-fail/pr-file-parse-log b/post_results/testdata/misc-checks-fail/pr-file-parse-log
index 5cdfe35..dbcfdae 100644
--- a/post_results/testdata/misc-checks-fail/pr-file-parse-log
+++ b/post_results/testdata/misc-checks-fail/pr-file-parse-log
@@ -1,3 +1,5 @@
-openconfig-mpls.yang: openconfig-version:"2.2.5" latest-revision-version:"2.2.5"
-openconfig-acl.yang: openconfig-version:"1.2.2" latest-revision-version:"1.2.2"
+openconfig-mpls.yang: belonging-module:"openconfig-mpls" openconfig-version:"2.2.5" latest-revision-version:"2.2.5"
+openconfig-mpls-submodule.yang: belonging-module:"openconfig-mpls" openconfig-version:"2.3.4" latest-revision-version:"2.3.4"
+openconfig-mpls-submodule2.yang: belonging-module:"openconfig-mpls" openconfig-version:"2.3.2" latest-revision-version:"2.3.2"
+openconfig-acl.yang: belonging-module:"openconfig-acl" openconfig-version:"1.2.2" latest-revision-version:"1.2.2"
changed-version-to-noversion.yang:
diff --git a/post_results/testdata/misc-checks-pass/all-non-empty-files.txt b/post_results/testdata/misc-checks-pass/all-non-empty-files.txt
index 0d6f569..3f7bd71 100644
--- a/post_results/testdata/misc-checks-pass/all-non-empty-files.txt
+++ b/post_results/testdata/misc-checks-pass/all-non-empty-files.txt
@@ -1,6 +1,9 @@
release/models/mpls/openconfig-mpls-static.yang
release/models/acl/openconfig-acl.yang
+release/models/acl/openconfig-acl-submodule.yang
release/models/acl/deeper/openconfig-packet-match.yang
+release/models/interfaces/openconfig-interface.yang
+release/models/interfaces/openconfig-interface-submodule.yang
release/models/bgp/changed-unreached-to-reached-noversion.yang
release/models/bgp/changed-unreached-to-version.yang
diff --git a/post_results/testdata/misc-checks-pass/changed-files.txt b/post_results/testdata/misc-checks-pass/changed-files.txt
index 8fd6c87..a99ec4b 100644
--- a/post_results/testdata/misc-checks-pass/changed-files.txt
+++ b/post_results/testdata/misc-checks-pass/changed-files.txt
@@ -1,8 +1,11 @@
release/models/acl/openconfig-acl.yang
+release/models/acl/openconfig-acl-submodule.yang
release/models/acl/deeper/openconfig-packet-match.yang
release/models/bgp/openconfig-deleted.yang
release/models/bgp/openconfig-deleted-2.yang
release/models/bgp/openconfig-deleted-3.yang
+release/models/interfaces/openconfig-interface.yang
+release/models/interfaces/openconfig-interface-submodule.yang
release/models/bgp/changed-unreached-to-reached-noversion.yang
release/models/bgp/changed-unreached-to-version.yang
diff --git a/post_results/testdata/misc-checks-pass/master-file-parse-log b/post_results/testdata/misc-checks-pass/master-file-parse-log
index 296d6ee..e610bbc 100644
--- a/post_results/testdata/misc-checks-pass/master-file-parse-log
+++ b/post_results/testdata/misc-checks-pass/master-file-parse-log
@@ -1,9 +1,12 @@
-openconfig-acl.yang: openconfig-version:"1.2.2"
-openconfig-packet-match.yang: latest-revision-version:"1.1.2" openconfig-version:"1.1.2"
-openconfig-mpls-static.yang: openconfig-version:"1.0.0"
-openconfig-deleted.yang: openconfig-version:"1.0.0"
-openconfig-deleted-2.yang:
+openconfig-acl.yang: openconfig-version:"1.2.2" belonging-module:"openconfig-acl"
+openconfig-acl-submodule.yang: openconfig-version:"1.1.3" belonging-module:"openconfig-acl"
+openconfig-packet-match.yang: latest-revision-version:"1.1.2" openconfig-version:"1.1.2" belonging-module:"openconfig-packet-match"
+openconfig-interface.yang: belonging-module:"openconfig-interface" openconfig-version:"1.1.3" latest-revision-version:"1.1.3"
+openconfig-interface-submodule.yang: belonging-module:"openconfig-interface-submodule" openconfig-version:"0.5.0" latest-revision-version:"0.5.0"
+openconfig-mpls-static.yang: openconfig-version:"1.0.0" belonging-module:"openconfig-mpls-static"
+openconfig-deleted.yang: openconfig-version:"1.0.0" belonging-module:"openconfig-deleted"
+openconfig-deleted-2.yang: belonging-module:"openconfig-deleted-2"
-changed-noversion-to-noversion.yang:
-changed-noversion-to-version.yang:
-unchanged-noversion.yang:
+changed-noversion-to-noversion.yang: belonging-module:"changed-noversion-to-noversion.yang"
+changed-noversion-to-version.yang: belonging-module:"changed-noversion-to-version"
+unchanged-noversion.yang: belonging-module:"unchanged-noversion"
diff --git a/post_results/testdata/misc-checks-pass/pr-file-parse-log b/post_results/testdata/misc-checks-pass/pr-file-parse-log
index 5530d87..dc41065 100644
--- a/post_results/testdata/misc-checks-pass/pr-file-parse-log
+++ b/post_results/testdata/misc-checks-pass/pr-file-parse-log
@@ -1,10 +1,13 @@
-openconfig-mpls-static.yang: openconfig-version:"1.0.0" latest-revision-version:"1.0.0"
-openconfig-acl.yang: openconfig-version:"1.2.3" latest-revision-version:"1.2.3"
-openconfig-packet-match.yang: latest-revision-version:"1.2.0" openconfig-version:"1.2.0"
-openconfig-mpls-static.yang: openconfig-version:"1.0.0" latest-revision-version:"1.0.0"
+openconfig-mpls-static.yang: belonging-module:"openconfig-mpls-static" openconfig-version:"1.0.0" latest-revision-version:"1.0.0"
+openconfig-acl.yang: belonging-module:"openconfig-acl" openconfig-version:"1.2.3" latest-revision-version:"1.2.3"
+openconfig-acl-submodule.yang: belonging-module:"openconfig-acl" openconfig-version:"1.2.3" latest-revision-version:"1.2.3"
+openconfig-packet-match.yang: belonging-module:"openconfig-packet-match" latest-revision-version:"1.2.0" openconfig-version:"1.2.0"
+openconfig-interface.yang: belonging-module:"openconfig-interface" openconfig-version:"2.0.0" latest-revision-version:"2.0.0"
+openconfig-interface-submodule.yang: belonging-module:"openconfig-interface-submodule" openconfig-version:"1.0.0" latest-revision-version:"1.0.0"
+openconfig-mpls-static.yang: belonging-module:"openconfig-mpls-static" openconfig-version:"1.0.0" latest-revision-version:"1.0.0"
-changed-unreached-to-reached-noversion.yang:
-changed-unreached-to-version.yang: openconfig-version:"1.0.0"
-changed-noversion-to-noversion.yang:
-changed-noversion-to-version.yang: openconfig-version:"1.0.0"
-unchanged-noversion.yang:
+changed-unreached-to-reached-noversion.yang: belonging-module:"changed-unreached-to-reached-noversion"
+changed-unreached-to-version.yang: belonging-module:"changed-unreached-to-version" openconfig-version:"1.0.0"
+changed-noversion-to-noversion.yang: belonging-module:"changed-noversion-to-noversion"
+changed-noversion-to-version.yang: belonging-module:"changed-noversion-to-version" openconfig-version:"1.0.0"
+unchanged-noversion.yang: belonging-module:"unchanged-noversion"
diff --git a/post_results/testdata/oc-pyang/acl==openconfig-acl==cmd b/post_results/testdata/oc-pyang/acl==openconfig-acl==cmd
index 23d5bfa..6c96d97 100644
--- a/post_results/testdata/oc-pyang/acl==openconfig-acl==cmd
+++ b/post_results/testdata/oc-pyang/acl==openconfig-acl==cmd
@@ -1 +1,4 @@
foo command
+/workspace/foo/bar
+$OCPYANG_PLUGIN_DIR
+$PYANGBIND_PLUGIN_DIR
diff --git a/util/parseutil.go b/util/parseutil.go
index 2ab7259..db296cf 100644
--- a/util/parseutil.go
+++ b/util/parseutil.go
@@ -1,3 +1,17 @@
+// Copyright 2022 Google LLC
+//
+// Licensed 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.
+
// Package util contain utility functions for doing YANG model validation.
package util
diff --git a/util/parseutil_test.go b/util/parseutil_test.go
index 6aab630..ca38d76 100644
--- a/util/parseutil_test.go
+++ b/util/parseutil_test.go
@@ -1,3 +1,17 @@
+// Copyright 2022 Google LLC
+//
+// Licensed 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.
+
package util
import (
diff --git a/validators/compat_report.sh b/validators/compat_report.sh
index 4a65aaa..322627a 100755
--- a/validators/compat_report.sh
+++ b/validators/compat_report.sh
@@ -1,4 +1,18 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
ROOT_DIR=/workspace
USERCONFIG_DIR=$ROOT_DIR/user-config
diff --git a/validators/confd/test.sh b/validators/confd/test.sh
index 5e0e49d..60174f7 100755
--- a/validators/confd/test.sh
+++ b/validators/confd/test.sh
@@ -1,4 +1,18 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
ROOT_DIR=/workspace
ZIP_FILE=$ROOT_DIR/confd.zip
diff --git a/validators/goyang-ygot/test.sh b/validators/goyang-ygot/test.sh
index db6cef2..075c76a 100755
--- a/validators/goyang-ygot/test.sh
+++ b/validators/goyang-ygot/test.sh
@@ -1,4 +1,18 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
ROOT_DIR=/workspace
RESULTSDIR=$ROOT_DIR/results/goyang-ygot
@@ -9,8 +23,13 @@ if ! stat $RESULTSDIR; then
exit 0
fi
-GO111MODULE=on go list -m github.com/openconfig/ygot@latest > $RESULTSDIR/latest-version.txt
-if bash $RESULTSDIR/script.sh > $OUTFILE 2> $FAILFILE; then
+# module download logs go to stderr, so only fail if command failed.
+if ! go install github.com/openconfig/ygot/generator@latest &> "${OUTFILE}"; then
+ echo "failed: go install github.com/openconfig/ygot/generator@latest" > "${FAILFILE}"
+fi
+
+go list -m github.com/openconfig/ygot@latest > $RESULTSDIR/latest-version.txt
+if bash $RESULTSDIR/script.sh >> $OUTFILE 2>> $FAILFILE; then
# Delete fail file if it's empty and the script passed.
find $FAILFILE -size 0 -delete
fi
diff --git a/validators/misc-checks/ocversion/ocversion.go b/validators/misc-checks/ocversion/ocversion.go
index 6a28f5b..c1d6319 100644
--- a/validators/misc-checks/ocversion/ocversion.go
+++ b/validators/misc-checks/ocversion/ocversion.go
@@ -30,6 +30,15 @@ func init() {
flag.StringVar(&pathStr, "p", "", "comma separated list of directories to add to search path")
}
+// belongingModule returns the module name if m is a module and the belonging
+// module name if m is a submodule.
+func belongingModule(m *yang.Module) string {
+ if m.Kind() == "submodule" {
+ return m.BelongsTo.Name
+ }
+ return m.Name
+}
+
// ocVersionsList list all files with their openconfig-version value. If not
// present, it still lists the file.
// Any errors are reported to stderr.
@@ -43,6 +52,7 @@ func ocVersionsList(entries []*yang.Entry) string {
}
builder.WriteString(fmt.Sprintf("%s.yang:", m.Name))
+ builder.WriteString(fmt.Sprintf(" belonging-module:%q", belongingModule(m)))
for _, e := range m.Extensions {
keywordParts := strings.Split(e.Keyword, ":")
@@ -55,7 +65,7 @@ func ocVersionsList(entries []*yang.Entry) string {
extMod := yang.FindModuleByPrefix(m, pfx)
if extMod == nil {
builder.WriteString(fmt.Sprintf("unable to find module using prefix %q from referencing module %q\n", pfx, m.Name))
- } else if extMod.Name == "openconfig-extensions" {
+ } else if belongingModule(extMod) == "openconfig-extensions" {
builder.WriteString(fmt.Sprintf(" openconfig-version:%q", e.Argument))
}
}
@@ -67,6 +77,8 @@ func ocVersionsList(entries []*yang.Entry) string {
}
func buildModuleEntries(paths, files []string) ([]*yang.Entry, []error) {
+ ms := yang.NewModules()
+
var errs []error
for _, path := range paths {
expanded, err := yang.PathsWithModules(path)
@@ -74,11 +86,9 @@ func buildModuleEntries(paths, files []string) ([]*yang.Entry, []error) {
errs = append(errs, err)
continue
}
- yang.AddPath(expanded...)
+ ms.AddPath(expanded...)
}
- ms := yang.NewModules()
-
for _, name := range files {
if err := ms.Read(name); err != nil {
errs = append(errs, err)
diff --git a/validators/misc-checks/ocversion/ocversion_test.go b/validators/misc-checks/ocversion/ocversion_test.go
index 47eaab4..5ef4e14 100644
--- a/validators/misc-checks/ocversion/ocversion_test.go
+++ b/validators/misc-checks/ocversion/ocversion_test.go
@@ -29,18 +29,25 @@ func TestOcVersionsList(t *testing.T) {
want string
wantErr bool
}{{
- desc: "single extension",
- inPath: []string{"testdata"},
- inFiles: []string{"testdata/openconfig-single-extension.yang"},
- want: `openconfig-extensions.yang:
-openconfig-single-extension.yang: openconfig-version:"0.4.2"
+ desc: "module and submodule",
+ inPath: []string{"testdata"},
+ inFiles: []string{
+ "testdata/openconfig-extensions.yang",
+ "testdata/openconfig-extensions-submodule.yang",
+ "testdata/openconfig-single-extension.yang",
+ "testdata/openconfig-single-extension-submodule.yang",
+ },
+ want: `openconfig-extensions.yang: belonging-module:"openconfig-extensions"
+openconfig-extensions-submodule.yang: belonging-module:"openconfig-extensions" openconfig-version:"0.5.0"
+openconfig-single-extension.yang: belonging-module:"openconfig-single-extension" openconfig-version:"0.4.2"
+openconfig-single-extension-submodule.yang: belonging-module:"openconfig-single-extension" openconfig-version:"0.4.3"
`,
}, {
desc: "multiple extensions",
inPath: []string{"testdata"},
inFiles: []string{"testdata/openconfig-telemetry-types.yang"},
- want: `openconfig-extensions.yang:
-openconfig-telemetry-types.yang: openconfig-version:"0.4.2"
+ want: `openconfig-extensions.yang: belonging-module:"openconfig-extensions"
+openconfig-telemetry-types.yang: belonging-module:"openconfig-telemetry-types" openconfig-version:"0.4.2"
`,
}, {
desc: "invalid file",
@@ -51,9 +58,9 @@ openconfig-telemetry-types.yang: openconfig-version:"0.4.2"
desc: "other-extensions module used for openconfig-extension value",
inPath: []string{"testdata"},
inFiles: []string{"testdata/openconfig-use-other-extension.yang"},
- want: `openconfig-extensions.yang:
-openconfig-use-other-extension.yang:
-other-extensions.yang:
+ want: `openconfig-extensions.yang: belonging-module:"openconfig-extensions"
+openconfig-use-other-extension.yang: belonging-module:"openconfig-use-other-extension"
+other-extensions.yang: belonging-module:"other-extensions"
`,
}}
diff --git a/validators/misc-checks/ocversion/testdata/openconfig-extensions-submodule.yang b/validators/misc-checks/ocversion/testdata/openconfig-extensions-submodule.yang
new file mode 100644
index 0000000..f9577f1
--- /dev/null
+++ b/validators/misc-checks/ocversion/testdata/openconfig-extensions-submodule.yang
@@ -0,0 +1,9 @@
+submodule openconfig-extensions-submodule {
+
+ yang-version "1";
+
+ belongs-to openconfig-extensions { prefix oc-ext; }
+
+ oc-ext:openconfig-version "0.5.0";
+
+}
diff --git a/validators/misc-checks/ocversion/testdata/openconfig-single-extension-submodule.yang b/validators/misc-checks/ocversion/testdata/openconfig-single-extension-submodule.yang
new file mode 100644
index 0000000..33fa604
--- /dev/null
+++ b/validators/misc-checks/ocversion/testdata/openconfig-single-extension-submodule.yang
@@ -0,0 +1,11 @@
+submodule openconfig-single-extension-submodule {
+
+ yang-version "1";
+
+ belongs-to openconfig-single-extension { prefix oc-single-extension; }
+
+ import openconfig-extensions { prefix oc-ext; }
+
+ oc-ext:openconfig-version "0.4.3";
+
+}
diff --git a/validators/misc-checks/ocversion/testdata/openconfig-single-extension.yang b/validators/misc-checks/ocversion/testdata/openconfig-single-extension.yang
index 3f2d3ea..ad80840 100644
--- a/validators/misc-checks/ocversion/testdata/openconfig-single-extension.yang
+++ b/validators/misc-checks/ocversion/testdata/openconfig-single-extension.yang
@@ -3,12 +3,14 @@ module openconfig-single-extension {
yang-version "1";
// namespace
- namespace "http://openconfig.net/yang/telemetry-types";
+ namespace "http://openconfig.net/yang/single-extension";
- prefix "oc-telemetry-types";
+ prefix "oc-single-extension";
import openconfig-extensions { prefix oc-ext; }
+ include openconfig-single-extension-submodule;
+
oc-ext:openconfig-version "0.4.2";
}
diff --git a/validators/misc-checks/test.sh b/validators/misc-checks/test.sh
index bb4a4b9..c26835c 100755
--- a/validators/misc-checks/test.sh
+++ b/validators/misc-checks/test.sh
@@ -1,4 +1,18 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
ROOT_DIR=/workspace
RESULTSDIR=$ROOT_DIR/results/misc-checks
diff --git a/validators/oc-pyang/test.sh b/validators/oc-pyang/test.sh
index e76bbdc..f79bf79 100755
--- a/validators/oc-pyang/test.sh
+++ b/validators/oc-pyang/test.sh
@@ -1,4 +1,18 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
########################## SETUP #############################
ROOT_DIR=/workspace
@@ -19,10 +33,13 @@ setup() {
virtualenv $VENVDIR
source $VENVDIR/bin/activate
- git clone https://github.com/openconfig/oc-pyang $OCPYANG_REPO
+ git clone https://github.com/openconfig/oc-pyang $OCPYANG_REPO -b $_OC_PYANG_VERSION
+ cd $OCPYANG_REPO
+ echo -n "Running at github.com/openconfig/oc-pyang branch " >> $OUTFILE
+ git rev-parse --short HEAD >> $OUTFILE
+ cd ..
pip3 install --no-cache-dir -r $OCPYANG_DIR/requirements.txt
- pip3 install enum34
- pip3 install jinja2
+ pip3 install --no-cache-dir -r $OCPYANG_REPO/requirements.txt
pip3 install setuptools
pip3 install pyang
}
@@ -48,7 +65,7 @@ if [ $? -ne 0 ]; then
exit 0
fi
-if bash $RESULTSDIR/script.sh $VENVDIR/bin/pyang --plugindir $OCPYANG_PLUGIN_DIR > $OUTFILE 2> $FAILFILE; then
+if bash $RESULTSDIR/script.sh $VENVDIR/bin/pyang >> $OUTFILE 2> $FAILFILE; then
# Delete fail file if it's empty and the script passed.
find $FAILFILE -size 0 -delete
fi
diff --git a/validators/pyang/test.sh b/validators/pyang/test.sh
index 12cadf0..cbdc598 100755
--- a/validators/pyang/test.sh
+++ b/validators/pyang/test.sh
@@ -1,4 +1,18 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
########################## COMMON SETUP #############################
ROOT_DIR=/workspace
diff --git a/validators/pyangbind/test.sh b/validators/pyangbind/test.sh
index 4159e9b..0c46b07 100755
--- a/validators/pyangbind/test.sh
+++ b/validators/pyangbind/test.sh
@@ -1,4 +1,18 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
########################## SETUP #############################
ROOT_DIR=/workspace
@@ -20,7 +34,6 @@ setup() {
git clone https://github.com/robshakir/pyangbind $PYANGBIND_REPO
pip3 install --no-cache-dir -r $PYANGBIND_REPO/requirements.txt
- pip3 install pyangbind
pip3 install pyang
}
@@ -31,12 +44,13 @@ teardown() {
setup
########################## PYANGBIND #############################
-pip3 list | grep pyangbind > $RESULTSDIR/latest-version.txt
+echo -n "pyangbind@" > $RESULTSDIR/latest-version.txt
+cd "${PYANGBIND_REPO}" && git rev-parse --short HEAD >> $RESULTSDIR/latest-version.txt && cd -
find $RESULTSDIR/latest-version.txt -size 0 -delete
-export PYANGBIND_PLUGIN_DIR=`/usr/bin/env python3 -c \
- 'import pyangbind; import os; print ("{}/plugin".format(os.path.dirname(pyangbind.__file__)))'`
-if bash $RESULTSDIR/script.sh $VENVDIR/bin/pyang --plugindir $PYANGBIND_PLUGIN_DIR > $OUTFILE 2> $FAILFILE; then
+export PYTHONPATH="${PYTHONPATH}:${PYANGBIND_REPO}"
+export PYANGBIND_PLUGIN_DIR="${PYANGBIND_REPO}/pyangbind/plugin"
+if bash $RESULTSDIR/script.sh $VENVDIR/bin/pyang > $OUTFILE 2> $FAILFILE; then
# Delete fail file if it's empty and the script passed.
find $FAILFILE -size 0 -delete
fi
diff --git a/validators/regexp/test.sh b/validators/regexp/test.sh
index 94a8ef2..ffb5a6b 100755
--- a/validators/regexp/test.sh
+++ b/validators/regexp/test.sh
@@ -1,4 +1,18 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
########################## SETUP #############################
ROOT_DIR=/workspace
diff --git a/validators/yanglint/test.sh b/validators/yanglint/test.sh
index 8d701e7..5f228ac 100755
--- a/validators/yanglint/test.sh
+++ b/validators/yanglint/test.sh
@@ -1,7 +1,22 @@
#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Licensed 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.
+
ROOT_DIR=/workspace
-DEB_FILE=$ROOT_DIR/libyang.deb
+DEB_FILE1=$ROOT_DIR/libyang.deb
+DEB_FILE2=$ROOT_DIR/yanglint.deb
RESULTSDIR=$ROOT_DIR/results/yanglint
OUTFILE=$RESULTSDIR/out
FAILFILE=$RESULTSDIR/fail
@@ -10,7 +25,8 @@ if ! stat $RESULTSDIR; then
exit 0
fi
-apt install $DEB_FILE
+apt install $DEB_FILE1
+apt install $DEB_FILE2
yanglint -v > $RESULTSDIR/latest-version.txt
if bash $RESULTSDIR/script.sh > $OUTFILE 2> $FAILFILE; then