Skip to content

Commit d165a3f

Browse files
authored
CLOUDP-279514 Detect and block breaking changes (#4064)
1 parent 50c4b1f commit d165a3f

File tree

8 files changed

+615
-0
lines changed

8 files changed

+615
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Detect Breaking Changes
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
breaking-changes-manifest:
8+
name: Generate Breaking Changes Manifest
9+
runs-on: ubuntu-latest
10+
outputs:
11+
breaking-changes: ${{ steps.breakvalidator.outputs.JSON }}
12+
steps:
13+
- uses: GitHubSecurityLab/actions-permissions/monitor@v1
14+
with:
15+
config: ${{ vars.PERMISSIONS_CONFIG }}
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
with:
19+
ref: ${{ github.event.pull_request.base.sha }}
20+
- name: Install Go
21+
uses: actions/setup-go@v5
22+
with:
23+
go-version-file: go.mod
24+
- name: Install dependencies
25+
run: go mod download
26+
- name: Run breaking changes validator
27+
id: breakvalidator
28+
run: |
29+
set -e
30+
go run ./tools/cmd/breakvalidator generate > main.json
31+
- name: Upload manifest
32+
uses: actions/upload-artifact@v4
33+
with:
34+
name: breaking-changes-manifest
35+
path: main.json
36+
detect-breaking-changes:
37+
name: Detect Breaking Changes
38+
runs-on: ubuntu-latest
39+
needs: breaking-changes-manifest
40+
steps:
41+
- uses: GitHubSecurityLab/actions-permissions/monitor@v1
42+
with:
43+
config: ${{ vars.PERMISSIONS_CONFIG }}
44+
- name: Checkout code
45+
uses: actions/checkout@v4
46+
- name: Install Go
47+
uses: actions/setup-go@v5
48+
with:
49+
go-version-file: go.mod
50+
- name: Download manifest
51+
uses: actions/download-artifact@v4
52+
with:
53+
name: breaking-changes-manifest
54+
path: main.json
55+
- name: Run breaking changes validator
56+
run: |
57+
set -e
58+
go run ./tools/cmd/breakvalidator generate > changed.json
59+
go run ./tools/cmd/breakvalidator validate -m main.json -c changed.json

.github/workflows/close-jira.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ jobs:
1010
runs-on: ubuntu-latest
1111
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'auto_close_jira')
1212
steps:
13+
- uses: GitHubSecurityLab/actions-permissions/monitor@v1
14+
with:
15+
config: ${{ vars.PERMISSIONS_CONFIG }}
1316
- name: set jira key (branch name)
1417
if: ${{ startsWith(github.event.pull_request.head.ref, 'CLOUDP') }}
1518
env:

scripts/add-copy.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ find_files() {
2121
\( \
2222
-wholename '*mock*' \
2323
-o -wholename '*third_party*' \
24+
-o -wholename '*docs/command*' \
2425
\) -prune \
2526
\) \
2627
\( -name '*.go' -o -name '*.sh' \)

tools/cmd/breakvalidator/generate.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"encoding/json"
19+
"io"
20+
21+
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/root"
22+
"github.com/spf13/cobra"
23+
"github.com/spf13/pflag"
24+
)
25+
26+
func generateCmd(cmd *cobra.Command) cmdData {
27+
data := cmdData{}
28+
if len(cmd.Aliases) > 0 {
29+
data.Aliases = cmd.Aliases
30+
}
31+
flags := false
32+
data.Flags = map[string]flagData{}
33+
cmd.Flags().VisitAll(func(f *pflag.Flag) {
34+
data.Flags[f.Name] = flagData{
35+
Type: f.Value.Type(),
36+
Default: f.DefValue,
37+
Short: f.Shorthand,
38+
}
39+
flags = true
40+
})
41+
if !flags {
42+
data.Flags = nil
43+
}
44+
return data
45+
}
46+
47+
func generateCmds(cmd *cobra.Command) map[string]cmdData {
48+
data := map[string]cmdData{}
49+
data[cmd.CommandPath()] = generateCmd(cmd)
50+
for _, c := range cmd.Commands() {
51+
for k, v := range generateCmds(c) {
52+
data[k] = v
53+
}
54+
}
55+
return data
56+
}
57+
58+
func generateCmdRun(output io.Writer) error {
59+
cliCmd := root.Builder()
60+
data := generateCmds(cliCmd)
61+
return json.NewEncoder(output).Encode(data)
62+
}
63+
64+
func buildGenerateCmd() *cobra.Command {
65+
generateCmd := &cobra.Command{
66+
Use: "generate",
67+
Short: "Generate the CLI command structure.",
68+
RunE: func(cmd *cobra.Command, _ []string) error {
69+
return generateCmdRun(cmd.OutOrStdout())
70+
},
71+
}
72+
return generateCmd
73+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"reflect"
19+
"testing"
20+
21+
"github.com/spf13/cobra"
22+
)
23+
24+
func TestGenerateCmds(t *testing.T) {
25+
cliCmd := &cobra.Command{
26+
Use: "test",
27+
Aliases: []string{"testa"},
28+
}
29+
cliCmd.Flags().StringP("flag1", "f", "default1", "flag1")
30+
generatedData := generateCmds(cliCmd)
31+
32+
expectedData := map[string]cmdData{
33+
"test": {
34+
Aliases: []string{"testa"},
35+
Flags: map[string]flagData{
36+
"flag1": {
37+
Type: "string",
38+
Default: "default1",
39+
Short: "f",
40+
},
41+
},
42+
},
43+
}
44+
45+
if !reflect.DeepEqual(generatedData, expectedData) {
46+
t.Fatalf("got: %v, expected: %v", generatedData, expectedData)
47+
}
48+
}

tools/cmd/breakvalidator/main.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"os"
19+
20+
"github.com/spf13/cobra"
21+
)
22+
23+
type flagData struct {
24+
Type string `json:"type"`
25+
Default string `json:"default"`
26+
Short string `json:"short"`
27+
}
28+
29+
type cmdData struct {
30+
Aliases []string `json:"aliases"`
31+
Flags map[string]flagData `json:"flags"`
32+
}
33+
34+
func buildRootCmd() *cobra.Command {
35+
rootCmd := &cobra.Command{
36+
Use: "breakvalidator",
37+
Short: "CLI tool to validate breaking changes in the CLI.",
38+
}
39+
rootCmd.AddCommand(buildGenerateCmd())
40+
rootCmd.AddCommand(buildValidateCmd())
41+
42+
return rootCmd
43+
}
44+
45+
func main() {
46+
rootCmd := buildRootCmd()
47+
err := rootCmd.Execute()
48+
if err != nil {
49+
os.Exit(1)
50+
}
51+
}

0 commit comments

Comments
 (0)