• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / kubebuilder / 17064586798

19 Aug 2025 08:52AM UTC coverage: 70.933% (+0.2%) from 70.751%
17064586798

Pull #5018

github

camilamacedo86
(feat): Add option to allow open GitHub Issues after updates
When running alpha update, you can now pass --open-gh-issue to automatically create a pre-filled GitHub Issue.

Assited-by: ChatGPT (OpenAI)

Co-authored-by: Vitor Floriano <vitorfloriano@users.noreply.github.com>
Pull Request #5018: ✨ (alpha update) Add option to allow open GitHub Issues after updates

53 of 63 new or added lines in 4 files covered. (84.13%)

30 existing lines in 1 file now uncovered.

3065 of 4321 relevant lines covered (70.93%)

14.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.89
/pkg/cli/alpha/internal/update/validate.go
1
/*
2
Copyright 2025 The Kubernetes Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package update
18

19
import (
20
        "fmt"
21
        log "log/slog"
22
        "net/http"
23
        "os"
24
        "os/exec"
25
        "strings"
26

27
        "golang.org/x/mod/semver"
28

29
        "sigs.k8s.io/kubebuilder/v4/pkg/cli/alpha/internal/update/helpers"
30
)
31

32
// Validate checks the input info provided for the update and populates the cliVersion
33
func (opts *Update) Validate() error {
2✔
34
        if err := opts.validateEqualVersions(); err != nil {
2✔
35
                return fmt.Errorf("failed to validate equal versions: %w", err)
×
36
        }
×
37
        if err := opts.validateGitRepo(); err != nil {
3✔
38
                return fmt.Errorf("failed to validate git repository: %w", err)
1✔
39
        }
1✔
40
        if err := opts.validateFromBranch(); err != nil {
1✔
41
                return fmt.Errorf("failed to validate --from-branch: %w", err)
×
42
        }
×
43
        if err := opts.validateSemanticVersions(); err != nil {
1✔
44
                return fmt.Errorf("failed to validate the versions: %w", err)
×
45
        }
×
46
        if err := validateReleaseAvailability(opts.FromVersion); err != nil {
1✔
47
                return fmt.Errorf("unable to find release %s: %w", opts.FromVersion, err)
×
48
        }
×
49
        if err := validateReleaseAvailability(opts.ToVersion); err != nil {
1✔
50
                return fmt.Errorf("unable to find release %s: %w", opts.ToVersion, err)
×
51
        }
×
52

53
        if opts.OpenGhIssue {
1✔
NEW
54
                if err := exec.Command("gh", "--version").Run(); err != nil {
×
NEW
55
                        return fmt.Errorf("`gh` CLI not found or not authenticated. "+
×
NEW
56
                                "You must have gh instaled to use the --open-gh-issue option: %s", err)
×
NEW
57
                }
×
58
        }
59

60
        return nil
1✔
61
}
62

63
// validateGitRepo verifies if the current directory is a valid Git repository and checks for uncommitted changes.
64
func (opts *Update) validateGitRepo() error {
4✔
65
        log.Info("Checking if is a git repository")
4✔
66
        gitCmd := exec.Command("git", "rev-parse", "--git-dir")
4✔
67
        if err := gitCmd.Run(); err != nil {
6✔
68
                return fmt.Errorf("not in a git repository")
2✔
69
        }
2✔
70

71
        log.Info("Checking if branch has uncommitted changes")
2✔
72
        gitCmd = exec.Command("git", "status", "--porcelain")
2✔
73
        output, err := gitCmd.Output()
2✔
74
        if err != nil {
2✔
75
                return fmt.Errorf("failed to check branch status: %w", err)
×
76
        }
×
77
        if len(strings.TrimSpace(string(output))) > 0 {
2✔
78
                return fmt.Errorf("working directory has uncommitted changes. " +
×
79
                        "Please commit or stash them before updating")
×
80
        }
×
81
        return nil
2✔
82
}
83

84
// validateFromBranch the branch passed to the --from-branch flag
85
func (opts *Update) validateFromBranch() error {
3✔
86
        // Check if the branch exists
3✔
87
        gitCmd := exec.Command("git", "rev-parse", "--verify", opts.FromBranch)
3✔
88
        if err := gitCmd.Run(); err != nil {
4✔
89
                return fmt.Errorf("%s branch does not exist locally. "+
1✔
90
                        "Run 'git branch -a' to see all available branches",
1✔
91
                        opts.FromBranch)
1✔
92
        }
1✔
93
        return nil
2✔
94
}
95

96
// validateSemanticVersions the version informed by the user via --from-version flag
97
func (opts *Update) validateSemanticVersions() error {
3✔
98
        if !semver.IsValid(opts.FromVersion) {
4✔
99
                return fmt.Errorf(" version informed (%s) has invalid semantic version. "+
1✔
100
                        "Expect: vX.Y.Z (Ex: v4.5.0)", opts.FromVersion)
1✔
101
        }
1✔
102
        if !semver.IsValid(opts.ToVersion) {
2✔
103
                return fmt.Errorf(" version informed (%s) has invalid semantic version. "+
×
104
                        "Expect: vX.Y.Z (Ex: v4.5.0)", opts.ToVersion)
×
105
        }
×
106
        return nil
2✔
107
}
108

109
// validateReleaseAvailability will verify if the binary to scaffold from-version flag is available
110
func validateReleaseAvailability(version string) error {
4✔
111
        url := helpers.BuildReleaseURL(version)
4✔
112
        resp, err := http.Head(url)
4✔
113
        if err != nil {
4✔
114
                return fmt.Errorf("failed to check binary availability: %w", err)
×
115
        }
×
116
        defer func() {
8✔
117
                if err = resp.Body.Close(); err != nil {
4✔
118
                        log.Error("failed to close connection", "error", err)
×
119
                }
×
120
        }()
121

122
        switch resp.StatusCode {
4✔
123
        case http.StatusOK:
3✔
124
                log.Info("Binary version available", "version", version)
3✔
125
                return nil
3✔
126
        case http.StatusNotFound:
×
127
                return fmt.Errorf("binary version %s not found. Check versions available in releases",
×
128
                        version)
×
129
        default:
1✔
130
                return fmt.Errorf("unexpected response %d when checking binary availability for version %s",
1✔
131
                        resp.StatusCode, version)
1✔
132
        }
133
}
134

135
// validateEqualVersions checks if from-version and to-version are the same.
136
// If they are equal, logs an appropriate message and exits successfully.
137
func (opts *Update) validateEqualVersions() error {
2✔
138
        if opts.FromVersion == opts.ToVersion {
2✔
139
                // Check if this is the latest version to provide appropriate message
×
140
                latestVersion, err := fetchLatestRelease()
×
141
                if err != nil {
×
142
                        return fmt.Errorf("failed to fetch latest release for messaging: %w", err)
×
143
                }
×
144

145
                if opts.ToVersion == latestVersion {
×
146
                        log.Info("Your project already uses the latest version. No action taken.", "version", opts.FromVersion)
×
147
                } else {
×
148
                        log.Info("Your project already uses the specified version. No action taken.", "version", opts.FromVersion)
×
149
                }
×
150
                os.Exit(0)
×
151
        }
152
        return nil
2✔
153
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc