Skip to content

Commit d0923b6

Browse files
(chore): Add plugin for Alpha Update
Co-authored-by: Vitor Floriano <vitorfloriano@users.noreply.github.com>
1 parent 2fc27db commit d0923b6

File tree

10 files changed

+485
-0
lines changed

10 files changed

+485
-0
lines changed

cmd/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang"
3333
deployimagev1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/deploy-image/v1alpha1"
3434
golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4"
35+
autoupdatev1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/autoupdate/v1alpha"
3536
grafanav1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/grafana/v1alpha"
3637
helmv1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha"
3738
)
@@ -74,6 +75,7 @@ func Run() {
7475
&deployimagev1alpha1.Plugin{},
7576
&grafanav1alpha1.Plugin{},
7677
&helmv1alpha1.Plugin{},
78+
&autoupdatev1alpha1.Plugin{},
7779
),
7880
cli.WithPlugins(externalPlugins...),
7981
cli.WithDefaultPlugins(cfgv3.Version, gov4Bundle),

pkg/cli/alpha/internal/generate.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
3232
"sigs.k8s.io/kubebuilder/v4/pkg/plugin/util"
3333
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/deploy-image/v1alpha1"
34+
autoupdate "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/autoupdate/v1alpha"
3435
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/grafana/v1alpha"
3536
hemlv1alpha "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha"
3637
)
@@ -106,6 +107,10 @@ func (opts *Generate) Generate() error {
106107
return fmt.Errorf("error migrating Grafana plugin: %w", err)
107108
}
108109

110+
if err = migrateAutoUpdatePlugin(projectConfig); err != nil {
111+
return fmt.Errorf("error migrating AutoUpdate plugin: %w", err)
112+
}
113+
109114
if hasHelmPlugin(projectConfig) {
110115
if err = kubebuilderHelmEdit(); err != nil {
111116
return fmt.Errorf("error editing Helm plugin: %w", err)
@@ -233,6 +238,24 @@ func migrateGrafanaPlugin(s store.Store, src, des string) error {
233238
return kubebuilderGrafanaEdit()
234239
}
235240

241+
// Migrates the Grafana plugin.
242+
func migrateAutoUpdatePlugin(s store.Store) error {
243+
var autoUpdatePlugin struct{}
244+
err := s.Config().DecodePluginConfig(plugin.KeyFor(autoupdate.Plugin{}), autoUpdatePlugin)
245+
if errors.As(err, &config.PluginKeyNotFoundError{}) {
246+
log.Info("Auto Update plugin not found, skipping migration")
247+
return nil
248+
} else if err != nil {
249+
return fmt.Errorf("failed to decode autoupdate plugin config: %w", err)
250+
}
251+
252+
args := []string{"edit", "--plugins", plugin.KeyFor(v1alpha.Plugin{})}
253+
if err := util.RunCmd("kubebuilder edit", "kubebuilder", args...); err != nil {
254+
return fmt.Errorf("failed to run edit subcommand for Auto plugin: %w", err)
255+
}
256+
return nil
257+
}
258+
236259
// Migrates the Deploy Image plugin.
237260
func migrateDeployImagePlugin(s store.Store) error {
238261
var deployImagePlugin v1alpha1.PluginConfig
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
//nolint:dupl
18+
package v1alpha
19+
20+
import (
21+
"fmt"
22+
23+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
24+
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
25+
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
26+
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/autoupdate/v1alpha/scaffolds"
27+
)
28+
29+
var _ plugin.EditSubcommand = &editSubcommand{}
30+
31+
type editSubcommand struct {
32+
config config.Config
33+
}
34+
35+
func (p *editSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
36+
subcmdMeta.Description = metaDataDescription
37+
38+
subcmdMeta.Examples = fmt.Sprintf(` # Edit a common project with this plugin
39+
%[1]s edit --plugins=%[2]s
40+
`, cliMeta.CommandName, pluginKey)
41+
}
42+
43+
func (p *editSubcommand) InjectConfig(c config.Config) error {
44+
p.config = c
45+
return nil
46+
}
47+
48+
func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
49+
if err := insertPluginMetaToConfig(p.config, pluginConfig{}); err != nil {
50+
return fmt.Errorf("error inserting project plugin meta to configuration: %w", err)
51+
}
52+
53+
scaffolder := scaffolds.NewInitScaffolder()
54+
scaffolder.InjectFS(fs)
55+
if err := scaffolder.Scaffold(); err != nil {
56+
return fmt.Errorf("error scaffolding edit subcommand: %w", err)
57+
}
58+
59+
return nil
60+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
//nolint:dupl
18+
package v1alpha
19+
20+
import (
21+
"fmt"
22+
23+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
24+
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
25+
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
26+
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/autoupdate/v1alpha/scaffolds"
27+
)
28+
29+
var _ plugin.InitSubcommand = &initSubcommand{}
30+
31+
type initSubcommand struct {
32+
config config.Config
33+
}
34+
35+
func (p *initSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
36+
subcmdMeta.Description = metaDataDescription
37+
38+
subcmdMeta.Examples = fmt.Sprintf(` # Initialize a common project with this plugin
39+
%[1]s init --plugins=%[2]s
40+
`, cliMeta.CommandName, pluginKey)
41+
}
42+
43+
func (p *initSubcommand) InjectConfig(c config.Config) error {
44+
p.config = c
45+
return nil
46+
}
47+
48+
func (p *initSubcommand) Scaffold(fs machinery.Filesystem) error {
49+
if err := insertPluginMetaToConfig(p.config, pluginConfig{}); err != nil {
50+
return fmt.Errorf("error inserting project plugin meta to configuration: %w", err)
51+
}
52+
53+
scaffolder := scaffolds.NewInitScaffolder()
54+
scaffolder.InjectFS(fs)
55+
if err := scaffolder.Scaffold(); err != nil {
56+
return fmt.Errorf("error scaffolding init subcommand: %w", err)
57+
}
58+
59+
return nil
60+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
Copyright 2024 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 v1alpha
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
23+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
24+
cfgv3 "sigs.k8s.io/kubebuilder/v4/pkg/config/v3"
25+
"sigs.k8s.io/kubebuilder/v4/pkg/model/stage"
26+
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
27+
"sigs.k8s.io/kubebuilder/v4/pkg/plugins"
28+
)
29+
30+
// nolinter:lll
31+
const metaDataDescription = `This plugin scaffolds a GitHub Action to help you keep your project up to date with the latest Kubebuilder improvements. With a minimal amount of setup, you can receive **automatic issue notifications** whenever a new Kubebuilder release is available, with the URL and steps to upgrade your project, ensuring your project remains **maintained, secure, and aligned with ecosystem changes**.
32+
33+
This automation uses the 'kubebuilder alpha update' command with a **3-way merge strategy** to update your project's scaffold. The command is wrapped in a GitHub Actions workflow that automatically creates a new branch and opens an issue with a link to the Pull Request for you to review.
34+
35+
### How to Set Up the Automation
36+
37+
1. **Add the Plugin**: Use the 'kubebuilder' CLI to scaffold the auto-update GitHub Action into your project.
38+
39+
2. **Review the Workflow**: A file named '.github/workflows/alpha-update.yml' will be created. This file contains the workflow that runs on a schedule to check for updates.
40+
41+
3. **Understand Permissions**: The workflow uses the built-in 'GITHUB_TOKEN'. To function, it needs **'contents: write'** and **'issues: write'** permissions.
42+
* 'contents: write': Allows the workflow to create a new branch and push code to it.
43+
* 'issues: write': Allows the workflow to create a new GitHub issue to notify you of the automated update.
44+
45+
4. **Secure Your Project**: To ensure security, you **must** have **branch protection rules** enabled on your main development branches (e.g., 'main', 'master'). This prevents the workflow from pushing directly to your main branch, ensuring all automated changes are reviewed via a Pull Request before being merged.`
46+
47+
const pluginName = "autoupdate." + plugins.DefaultNameQualifier
48+
49+
var (
50+
pluginVersion = plugin.Version{Number: 1, Stage: stage.Alpha}
51+
supportedProjectVersions = []config.Version{cfgv3.Version}
52+
pluginKey = plugin.KeyFor(Plugin{})
53+
)
54+
55+
// Plugin implements the plugin.Full interface
56+
type Plugin struct {
57+
editSubcommand
58+
}
59+
60+
var _ plugin.Edit = Plugin{}
61+
62+
type pluginConfig struct{}
63+
64+
// Name returns the name of the plugin
65+
func (Plugin) Name() string { return pluginName }
66+
67+
// Version returns the version of the Helm plugin
68+
func (Plugin) Version() plugin.Version { return pluginVersion }
69+
70+
// SupportedProjectVersions returns an array with all project versions supported by the plugin
71+
func (Plugin) SupportedProjectVersions() []config.Version { return supportedProjectVersions }
72+
73+
// GetEditSubcommand will return the subcommand which is responsible for adding and/or edit a helm chart
74+
func (p Plugin) GetEditSubcommand() plugin.EditSubcommand { return &p.editSubcommand }
75+
76+
// DeprecationWarning define the deprecation message or return empty when plugin is not deprecated
77+
func (p Plugin) DeprecationWarning() string {
78+
return ""
79+
}
80+
81+
// insertPluginMetaToConfig will insert the metadata to the plugin configuration
82+
func insertPluginMetaToConfig(target config.Config, cfg pluginConfig) error {
83+
err := target.DecodePluginConfig(pluginKey, cfg)
84+
if !errors.As(err, &config.UnsupportedFieldError{}) {
85+
if err != nil && !errors.As(err, &config.PluginKeyNotFoundError{}) {
86+
return fmt.Errorf("error decoding plugin configuration: %w", err)
87+
}
88+
89+
if err = target.EncodePluginConfig(pluginKey, cfg); err != nil {
90+
return fmt.Errorf("error encoding plugin configuration: %w", err)
91+
}
92+
}
93+
94+
return nil
95+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
Copyright 2022 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 scaffolds
18+
19+
import (
20+
"fmt"
21+
log "log/slog"
22+
23+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
24+
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
25+
"sigs.k8s.io/kubebuilder/v4/pkg/plugins"
26+
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/autoupdate/v1alpha/scaffolds/internal/github"
27+
)
28+
29+
var _ plugins.Scaffolder = &initScaffolder{}
30+
31+
type initScaffolder struct {
32+
config config.Config
33+
34+
// fs is the filesystem that will be used by the scaffolder
35+
fs machinery.Filesystem
36+
}
37+
38+
// NewInitScaffolder returns a new Scaffolder for project initialization operations
39+
func NewInitScaffolder() plugins.Scaffolder {
40+
return &initScaffolder{}
41+
}
42+
43+
// InjectFS implements cmdutil.Scaffolder
44+
func (s *initScaffolder) InjectFS(fs machinery.Filesystem) {
45+
s.fs = fs
46+
}
47+
48+
// Scaffold implements cmdutil.Scaffolder
49+
func (s *initScaffolder) Scaffold() error {
50+
log.Info("Writing scaffold for you to edit...")
51+
52+
scaffold := machinery.NewScaffold(s.fs,
53+
machinery.WithConfig(s.config),
54+
)
55+
56+
err := scaffold.Execute(
57+
&github.AutoUpdate{},
58+
)
59+
if err != nil {
60+
return fmt.Errorf("failed to execute init scaffold: %w", err)
61+
}
62+
63+
return nil
64+
}

0 commit comments

Comments
 (0)