Skip to content

Commit 9c991d3

Browse files
authored
Merge pull request #4961 from camilamacedo86/patch-release
🌱 Prepare: Patch release 4.7.1
2 parents cf5dbb3 + 0a26e2a commit 9c991d3

File tree

13 files changed

+221
-38
lines changed

13 files changed

+221
-38
lines changed

docs/book/src/cronjob-tutorial/testdata/project/test/utils/utils.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ func UninstallCertManager() {
111111
if _, err := Run(cmd); err != nil {
112112
warnError(err)
113113
}
114+
115+
// Delete leftover leases in kube-system (not cleaned by default)
116+
kubeSystemLeases := []string{
117+
"cert-manager-cainjector-leader-election",
118+
"cert-manager-controller",
119+
}
120+
for _, lease := range kubeSystemLeases {
121+
cmd = exec.Command("kubectl", "delete", "lease", lease,
122+
"-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0")
123+
if _, err := Run(cmd); err != nil {
124+
warnError(err)
125+
}
126+
}
114127
}
115128

116129
// InstallCertManager installs the cert manager bundle.

docs/book/src/getting-started/testdata/project/test/utils/utils.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ func UninstallCertManager() {
111111
if _, err := Run(cmd); err != nil {
112112
warnError(err)
113113
}
114+
115+
// Delete leftover leases in kube-system (not cleaned by default)
116+
kubeSystemLeases := []string{
117+
"cert-manager-cainjector-leader-election",
118+
"cert-manager-controller",
119+
}
120+
for _, lease := range kubeSystemLeases {
121+
cmd = exec.Command("kubectl", "delete", "lease", lease,
122+
"-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0")
123+
if _, err := Run(cmd); err != nil {
124+
warnError(err)
125+
}
126+
}
114127
}
115128

116129
// InstallCertManager installs the cert manager bundle.

docs/book/src/multiversion-tutorial/testdata/project/test/utils/utils.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ func UninstallCertManager() {
111111
if _, err := Run(cmd); err != nil {
112112
warnError(err)
113113
}
114+
115+
// Delete leftover leases in kube-system (not cleaned by default)
116+
kubeSystemLeases := []string{
117+
"cert-manager-cainjector-leader-election",
118+
"cert-manager-controller",
119+
}
120+
for _, lease := range kubeSystemLeases {
121+
cmd = exec.Command("kubectl", "delete", "lease", lease,
122+
"-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0")
123+
if _, err := Run(cmd); err != nil {
124+
warnError(err)
125+
}
126+
}
114127
}
115128

116129
// InstallCertManager installs the cert manager bundle.

docs/book/src/reference/commands/alpha_update.md

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ kubebuilder alpha update \
6161
--from-branch=main
6262
```
6363

64+
Force update even with merge conflicts:
65+
66+
```sh
67+
kubebuilder alpha update --force
68+
```
69+
6470
<aside class="note warning">
6571
<h1>You might need to upgrade your project first</h1>
6672

@@ -81,17 +87,58 @@ Once updated, you can use `kubebuilder alpha update` for future upgrades.
8187
| `--from-version` | **Required for projects initialized with versions earlier than v4.6.0.** Kubebuilder version your project was created with. If unset, uses the `PROJECT` file. |
8288
| `--to-version` | Version to upgrade to. Defaults to the latest version. |
8389
| `--from-branch` | Git branch that contains your current project code. Defaults to `main`. |
90+
| `--force` | Force the update even if conflicts occur. Conflicted files will include conflict markers, and a commit will be created automatically. Ideal for automation (e.g., cronjobs, CI). |
8491
| `-h, --help` | Show help for this command. |
85-
86-
<aside class="note warning">
87-
<h1>Projects generated with </h1>
88-
92+
<aside class="note">
8993
Projects generated with **Kubebuilder v4.6.0** or later include the `cliVersion` field in the `PROJECT` file.
9094
This field is used by `kubebuilder alpha update` to determine the correct CLI
9195
version for upgrading your project.
96+
</aside>
97+
98+
## Merge Conflicts with `--force`
99+
100+
When you use the `--force` flag with `kubebuilder alpha update`, Git will complete the merge even if there are conflicts. The resulting commit will include conflict markers like:
101+
```
102+
<<<<<<< HEAD
103+
Your changes
104+
=======
105+
Incoming changes
106+
>>>>>>> branch-name
107+
```
108+
These conflicts will be committed in the
109+
`tmp-kb-update-merge` branch.
92110

111+
<aside class="note warning">
112+
You must manually resolve these conflicts before merging into your main branch.
113+
114+
```suggestion
115+
<aside class="note warning">
116+
<H1>If you face conflicts (using or not the --force flag) </H1>
117+
If the merge introduces conflicts, you must resolve them and **ensure** you execute the following command to regenerate the manifests and organise the files properly:
118+
119+
```bash
120+
make manifests generate fmt vet lint-fix
121+
```
122+
123+
Alternatively, you may want to run:
124+
125+
```bash
126+
make all
127+
```
93128
</aside>
94129

130+
131+
## When to Use `--force`
132+
Use `--force` only in scenarios like:
133+
- Automated environments (e.g., CI pipelines or cron jobs)
134+
- When you need to create a PR even if conflicts are present
135+
- When a human will resolve the conflicts later
136+
`kubebuilder alpha update --force`
137+
138+
This ensures the update proceeds without manual blocking but shifts responsibility for conflict resolution to a follow-up manual step.
139+
140+
This approach is typically used in automation workflows where conflict markers are later addressed by a human, or where preserving the conflicting changes is acceptable for follow-up processing.
141+
95142
## Requirements
96143

97144
- A valid [PROJECT][project-config] file at the root of your project

pkg/cli/alpha/internal/update/prepare.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (opts *Update) defineFromVersion(config store.Store) (string, error) {
7575

7676
func (opts *Update) defineToVersion() string {
7777
if len(opts.ToVersion) != 0 {
78-
if !strings.HasPrefix(opts.FromVersion, "v") {
78+
if !strings.HasPrefix(opts.ToVersion, "v") {
7979
return "v" + opts.ToVersion
8080
}
8181
return opts.ToVersion

pkg/cli/alpha/internal/update/update.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type Update struct {
3838
ToVersion string
3939
// FromBranch stores the branch to update from, e.g., "main".
4040
FromBranch string
41+
// Force commits the update changes even with merge conflicts
42+
Force bool
4143

4244
// UpdateBranches
4345
AncestorBranch string
@@ -334,8 +336,15 @@ func (opts *Update) mergeOriginalToUpgrade() error {
334336
var exitErr *exec.ExitError
335337
// If the merge has an error that is not a conflict, return an error 2
336338
if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 {
337-
log.Warn("Merge completed with conflicts. Manual resolution required.")
338339
hasConflicts = true
340+
if !opts.Force {
341+
log.Warn("Merge stopped due to conflicts. Manual resolution is required.")
342+
log.Warn("After resolving the conflicts, run the following command:")
343+
log.Warn(" make manifests generate fmt vet lint-fix")
344+
log.Warn("This ensures manifests and generated files are up to date, and the project layout remains consistent.")
345+
return fmt.Errorf("merge stopped due to conflicts")
346+
}
347+
log.Warn("Merge completed with conflicts. Conflict markers will be committed.")
339348
} else {
340349
return fmt.Errorf("merge failed unexpectedly: %w", err)
341350
}

pkg/cli/alpha/update.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,8 @@ The process uses Git branches:
4949
- upgrade: scaffold from the target version
5050
- merge: result of the 3-way merge
5151
52-
If conflicts occur during the merge, resolve them manually in the 'merge' branch.
53-
Once resolved, commit and push it as a pull request. This branch will contain the
54-
final upgraded project with the latest Kubebuilder layout and your custom code.
52+
If conflicts occur during the merge, the command will stop and leave the merge branch for manual resolution.
53+
Use --force to commit conflicts with markers instead.
5554
5655
Examples:
5756
# Update from the version specified in the PROJECT file to the latest release
@@ -63,6 +62,9 @@ Examples:
6362
# Update from a specific version to an specific release
6463
kubebuilder alpha update --from-version v4.5.0 --to-version v4.7.0
6564
65+
# Force update even with merge conflicts (commit conflict markers)
66+
kubebuilder alpha update --force
67+
6668
`,
6769

6870
PreRunE: func(_ *cobra.Command, _ []string) error {
@@ -92,5 +94,9 @@ Examples:
9294
updateCmd.Flags().StringVar(&opts.FromBranch, "from-branch", "",
9395
"Git branch to use as current state of the project for the update.")
9496

97+
updateCmd.Flags().BoolVar(&opts.Force, "force", false,
98+
"Force the update even if conflicts occur. Conflicted files will include conflict markers, and a "+
99+
"commit will be created automatically. Ideal for automation (e.g., cronjobs, CI).")
100+
95101
return updateCmd
96102
}

pkg/machinery/scaffold.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"strings"
2626
"text/template"
2727

28+
log "github.com/sirupsen/logrus"
2829
"github.com/spf13/afero"
2930
"golang.org/x/tools/imports"
3031

@@ -236,7 +237,15 @@ func doTemplate(t Template) ([]byte, error) {
236237
func (s Scaffold) updateFileModel(i Inserter, models map[string]*File) error {
237238
m, err := s.loadPreviousModel(i, models)
238239
if err != nil {
239-
return fmt.Errorf("failed to load previous model: %w", err)
240+
// TODO(kubebuilder/issues/4960): Create Machinery implementation to allow defining IfNotExistsAction
241+
// If the file path starts with test/, warn and skip
242+
// Workaround to allow projects be backwards compatible with previous versions
243+
if strings.HasPrefix(i.GetPath(), "test/") {
244+
log.Warnf("Skipping missing test file: %s", i.GetPath())
245+
log.Warn("The code fragments will not be inserted.")
246+
return nil
247+
}
248+
return fmt.Errorf("failed to load previous model for %s: %w", i.GetPath(), err)
240249
}
241250

242251
// Get valid code fragments

pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/test.go

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ limitations under the License.
1717
package e2e
1818

1919
import (
20+
"bytes"
2021
"fmt"
22+
"os"
2123
"path/filepath"
2224

25+
log "github.com/sirupsen/logrus"
26+
2327
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
2428
)
2529

@@ -77,41 +81,58 @@ func (f *WebhookTestUpdater) GetMarkers() []machinery.Marker {
7781

7882
// GetCodeFragments implements file.Inserter
7983
func (f *WebhookTestUpdater) GetCodeFragments() machinery.CodeFragmentsMap {
80-
codeFragments := machinery.CodeFragmentsMap{}
8184
if !f.WireWebhook {
8285
return nil
8386
}
84-
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)] = append(
85-
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)],
86-
webhookChecksFragment,
87-
)
88-
89-
if f.Resource != nil && f.Resource.HasDefaultingWebhook() {
90-
mutatingWebhookCode := fmt.Sprintf(mutatingWebhookChecksFragment, f.ProjectName)
91-
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)] = append(
92-
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)],
93-
mutatingWebhookCode,
94-
)
87+
88+
filePath := f.GetPath()
89+
90+
content, err := os.ReadFile(filePath)
91+
if err != nil {
92+
log.Warnf("Unable to read %q: %v", filePath, err)
93+
log.Warnf("Webhook test code injection will be skipped for this file.")
94+
log.Warnf("This typically occurs when the file was removed and is missing.")
95+
log.Warnf("If you intend to scaffold webhook tests, ensure the file and its markers exist.")
96+
return nil
9597
}
9698

97-
if f.Resource.HasValidationWebhook() {
98-
validatingWebhookCode := fmt.Sprintf(validatingWebhookChecksFragment, f.ProjectName)
99-
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)] = append(
100-
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)],
101-
validatingWebhookCode,
102-
)
99+
codeFragments := machinery.CodeFragmentsMap{}
100+
markers := f.GetMarkers()
101+
102+
for _, marker := range markers {
103+
if !bytes.Contains(content, []byte(marker.String())) {
104+
log.Warnf("Marker %q not found in %s; skipping injection of webhook test code",
105+
marker.String(), filePath)
106+
continue // skip this marker
107+
}
108+
109+
var fragments []string
110+
fragments = append(fragments, webhookChecksFragment)
111+
112+
if f.Resource != nil && f.Resource.HasDefaultingWebhook() {
113+
mutatingWebhookCode := fmt.Sprintf(mutatingWebhookChecksFragment, f.ProjectName)
114+
fragments = append(fragments, mutatingWebhookCode)
115+
}
116+
117+
if f.Resource != nil && f.Resource.HasValidationWebhook() {
118+
validatingWebhookCode := fmt.Sprintf(validatingWebhookChecksFragment, f.ProjectName)
119+
fragments = append(fragments, validatingWebhookCode)
120+
}
121+
122+
if f.Resource != nil && f.Resource.HasConversionWebhook() {
123+
conversionWebhookCode := fmt.Sprintf(
124+
conversionWebhookChecksFragment,
125+
f.Resource.Kind,
126+
f.Resource.Plural+"."+f.Resource.Group+"."+f.Resource.Domain,
127+
)
128+
fragments = append(fragments, conversionWebhookCode)
129+
}
130+
131+
codeFragments[marker] = fragments
103132
}
104133

105-
if f.Resource.HasConversionWebhook() {
106-
conversionWebhookCode := fmt.Sprintf(
107-
conversionWebhookChecksFragment,
108-
f.Resource.Kind,
109-
f.Resource.Plural+"."+f.Resource.Group+"."+f.Resource.Domain,
110-
)
111-
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)] = append(
112-
codeFragments[machinery.NewMarkerFor(f.GetPath(), webhookChecksMarker)],
113-
conversionWebhookCode,
114-
)
134+
if len(codeFragments) == 0 {
135+
return nil
115136
}
116137

117138
return codeFragments

pkg/plugins/golang/v4/scaffolds/internal/templates/test/utils/utils.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,19 @@ func UninstallCertManager() {
139139
if _, err := Run(cmd); err != nil {
140140
warnError(err)
141141
}
142+
143+
// Delete leftover leases in kube-system (not cleaned by default)
144+
kubeSystemLeases := []string{
145+
"cert-manager-cainjector-leader-election",
146+
"cert-manager-controller",
147+
}
148+
for _, lease := range kubeSystemLeases {
149+
cmd = exec.Command("kubectl", "delete", "lease", lease,
150+
"-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0")
151+
if _, err := Run(cmd); err != nil {
152+
warnError(err)
153+
}
154+
}
142155
}
143156
144157
// InstallCertManager installs the cert manager bundle.

0 commit comments

Comments
 (0)