Skip to content

Commit eda3019

Browse files
committed
feat: add local build workflow and debug options
Adds support for local, remote workflow generation and debug configuration via command-line flags. Workflow name and registry login can be configured via flags too. Debug mode adds workflow_dispatch trigger and func CLI caching for rapid iterations on workflow runs. Config creation refactored to use cobra flags. Issue SRVOCF-744 Signed-off-by: Stanislav Jakuschevskij <sjakusch@redhat.com>
1 parent 0e7352b commit eda3019

File tree

5 files changed

+261
-48
lines changed

5 files changed

+261
-48
lines changed

cmd/ci/config.go

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package ci
22

3-
import "path/filepath"
3+
import (
4+
"path/filepath"
5+
6+
"github.com/spf13/cobra"
7+
"github.com/spf13/viper"
8+
)
49

510
// CIConfig readonly configuration
611
type CIConfig struct {
@@ -12,7 +17,9 @@ type CIConfig struct {
1217
registryUserSecretKey,
1318
registryPassSecretKey string
1419
useRegistryLogin,
15-
selfHostedRunner bool
20+
useRemoteBuild,
21+
selfHostedRunner,
22+
debug bool
1623
}
1724

1825
func (cc *CIConfig) FnGithubWorkflowDir(fnRoot string) string {
@@ -31,10 +38,18 @@ func (cc *CIConfig) UseRegistryLogin() bool {
3138
return cc.useRegistryLogin
3239
}
3340

41+
func (cc *CIConfig) UseRemoteBuild() bool {
42+
return cc.useRemoteBuild
43+
}
44+
3445
func (cc *CIConfig) SelfHostedRunner() bool {
3546
return cc.selfHostedRunner
3647
}
3748

49+
func (cc *CIConfig) UseDebug() bool {
50+
return cc.debug
51+
}
52+
3853
func (cc *CIConfig) KubeconfigSecretKey() string {
3954
return cc.kubeconfigSecretKey
4055
}
@@ -67,7 +82,9 @@ func NewCIConfigBuilder() *ciConfigBuilder {
6782
registryUserSecretKey: "REGISTRY_USERNAME",
6883
registryPassSecretKey: "REGISTRY_PASSWORD",
6984
useRegistryLogin: true,
85+
useRemoteBuild: false,
7086
selfHostedRunner: false,
87+
debug: false,
7188
},
7289
}
7390
}
@@ -97,8 +114,13 @@ func (b *ciConfigBuilder) WithRegistryPassKey(key string) *ciConfigBuilder {
97114
return b
98115
}
99116

100-
func (b *ciConfigBuilder) WithRegistryLogin(useLogin bool) *ciConfigBuilder {
101-
b.result.useRegistryLogin = useLogin
117+
func (b *ciConfigBuilder) WithoutRegistryLogin() *ciConfigBuilder {
118+
b.result.useRegistryLogin = false
119+
return b
120+
}
121+
122+
func (b *ciConfigBuilder) WithRemoteBuild() *ciConfigBuilder {
123+
b.result.useRemoteBuild = true
102124
return b
103125
}
104126

@@ -107,6 +129,50 @@ func (b *ciConfigBuilder) WithSelfHosted(useSelfHosted bool) *ciConfigBuilder {
107129
return b
108130
}
109131

132+
func (b *ciConfigBuilder) WithDebug() *ciConfigBuilder {
133+
b.result.debug = true
134+
return b
135+
}
136+
110137
func (b *ciConfigBuilder) Build() CIConfig {
111138
return b.result
112139
}
140+
141+
const (
142+
UseRegistryLoginOption = "use-registry-login"
143+
DefaultUseRegistryLoginValue = true
144+
145+
WorkflowNameOption = "workflow-name"
146+
DefaultWorkflowName = "Remote Build and Deploy"
147+
)
148+
149+
func NewCiGithubConfigViaViper() CIConfig {
150+
result := NewCIConfigBuilder().
151+
WithWorkflowName(viper.GetString(WorkflowNameOption))
152+
153+
if !viper.GetBool(UseRegistryLoginOption) {
154+
result.WithoutRegistryLogin()
155+
}
156+
157+
return result.Build()
158+
}
159+
160+
func NewCiGithubConfigVia(cmd *cobra.Command) (CIConfig, error) {
161+
result := NewCIConfigBuilder()
162+
163+
workflowName, err := cmd.Flags().GetString(WorkflowNameOption)
164+
if err != nil {
165+
return CIConfig{}, err
166+
}
167+
result.WithWorkflowName(workflowName)
168+
169+
useRegistryLogin, err := cmd.Flags().GetBool(UseRegistryLoginOption)
170+
if err != nil {
171+
return CIConfig{}, err
172+
}
173+
if !useRegistryLogin {
174+
result.WithoutRegistryLogin()
175+
}
176+
177+
return result.Build(), nil
178+
}

cmd/ci/workflow.go

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ci
22

33
import (
4+
"bytes"
45
"fmt"
56
"os"
67
"path/filepath"
@@ -13,14 +14,31 @@ const (
1314
filePerm = 0644 // o: rw, g|u: r
1415
)
1516

17+
// TODO(twoGiants)
18+
// - encapsulate => create Interface and defaultGithubWorkflow struct
19+
// - provide printers for configurable properties
20+
// - provide toYamlString for checks in tests
1621
type GithubWorkflow struct {
1722
Name string `yaml:"name"`
1823
On WorkflowTriggers `yaml:"on"`
1924
Jobs map[string]Job `yaml:"jobs"`
2025
}
2126

2227
type WorkflowTriggers struct {
23-
Push *PushTrigger `yaml:"push,omitempty"`
28+
Push *PushTrigger `yaml:"push,omitempty"`
29+
WorkflowDispatch *struct{} `yaml:"workflow_dispatch,omitempty"`
30+
}
31+
32+
func newPushTrigger(branch string, debug bool) WorkflowTriggers {
33+
result := WorkflowTriggers{
34+
Push: &PushTrigger{Branches: []string{branch}},
35+
}
36+
37+
if debug {
38+
result.WorkflowDispatch = &struct{}{}
39+
}
40+
41+
return result
2442
}
2543

2644
type PushTrigger struct {
@@ -34,6 +52,8 @@ type Job struct {
3452

3553
type Step struct {
3654
Name string `yaml:"name,omitempty"`
55+
ID string `yaml:"id,omitempty"`
56+
If string `yaml:"if,omitempty"`
3757
Uses string `yaml:"uses,omitempty"`
3858
Run string `yaml:"run,omitempty"`
3959
With map[string]string `yaml:"with,omitempty"`
@@ -43,6 +63,16 @@ func newStep(name string) *Step {
4363
return &Step{Name: name}
4464
}
4565

66+
func (s *Step) withID(id string) *Step {
67+
s.ID = id
68+
return s
69+
}
70+
71+
func (s *Step) withIf(ifCond string) *Step {
72+
s.If = ifCond
73+
return s
74+
}
75+
4676
func (s *Step) withUses(u string) *Step {
4777
s.Uses = u
4878
return s
@@ -70,13 +100,17 @@ func NewGithubWorkflow(
70100
registryUserSecretKey,
71101
registryPassSecretKey string,
72102
useRegistryLogin,
73-
selfHosted bool,
103+
useRemoteBuild,
104+
selfHosted,
105+
useDebug bool,
74106
) *GithubWorkflow {
75107
runsOn := "ubuntu-latest"
76108
if selfHosted {
77109
runsOn = "self-hosted"
78110
}
79111

112+
pushTrigger := newPushTrigger("main", useDebug)
113+
80114
var steps []Step
81115
checkoutCode := newStep("Checkout code").
82116
withUses("actions/checkout@v4")
@@ -97,21 +131,35 @@ func NewGithubWorkflow(
97131
steps = append(steps, *loginToContainerRegistry)
98132
}
99133

134+
if useDebug {
135+
funcCliCache := newStep("Restore func cli from cache").
136+
withID("func-cli-cache").
137+
withUses("actions/cache@v4").
138+
withActionConfig("path", "func").
139+
withActionConfig("key", "func-cli-knative-v1.19.1")
140+
steps = append(steps, *funcCliCache)
141+
}
142+
100143
installFuncCli := newStep("Install func cli").
101144
withUses("gauron99/knative-func-action@main").
102145
withActionConfig("version", "knative-v1.19.1").
103146
withActionConfig("name", "func")
147+
if useDebug {
148+
installFuncCli.withIf("${{ steps.func-cli-cache.outputs.cache-hit != 'true' }}")
149+
}
104150
steps = append(steps, *installFuncCli)
105151

152+
runFuncDeploy := "func deploy"
153+
if useRemoteBuild {
154+
runFuncDeploy += " --remote"
155+
}
106156
deployFunc := newStep("Deploy function").
107-
withRun("func deploy --remote --registry=" + newSecret(registryUrlSecretKey) + " -v")
157+
withRun(runFuncDeploy + " --registry=" + newSecret(registryUrlSecretKey) + " -v")
108158
steps = append(steps, *deployFunc)
109159

110160
return &GithubWorkflow{
111161
Name: name,
112-
On: WorkflowTriggers{
113-
Push: &PushTrigger{Branches: []string{"main"}},
114-
},
162+
On: pushTrigger,
115163
Jobs: map[string]Job{
116164
"deploy": {
117165
RunsOn: runsOn,
@@ -136,7 +184,7 @@ func NewGithubWorkflowFromPath(path string) (*GithubWorkflow, error) {
136184
}
137185

138186
func (gw *GithubWorkflow) Persist(path string) error {
139-
raw, err := yaml.Marshal(gw)
187+
raw, err := gw.toYaml()
140188
if err != nil {
141189
return err
142190
}
@@ -153,14 +201,27 @@ func (gw *GithubWorkflow) Persist(path string) error {
153201
}
154202

155203
func (gw *GithubWorkflow) YamlString() (string, error) {
156-
raw, err := yaml.Marshal(gw)
204+
raw, err := gw.toYaml()
157205
if err != nil {
158206
return "", err
159207
}
160208

161209
return string(raw), nil
162210
}
163211

212+
func (gw *GithubWorkflow) toYaml() ([]byte, error) {
213+
var buf bytes.Buffer
214+
encoder := yaml.NewEncoder(&buf)
215+
encoder.SetIndent(2)
216+
217+
if err := encoder.Encode(gw); err != nil {
218+
return nil, err
219+
}
220+
encoder.Close()
221+
222+
return buf.Bytes(), nil
223+
}
224+
164225
func newSecret(key string) string {
165226
return fmt.Sprintf("${{ secrets.%s }}", key)
166227
}

cmd/ci/workflow_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ func TestGithubWorkflow_PersistAndLoad(t *testing.T) {
1616
"REGISTRY_USERNAME",
1717
"REGISTRY_PASSWORD",
1818
false,
19+
false,
20+
false,
1921
false)
2022
tempDir := t.TempDir()
2123
targetPath := tempDir + "/" + gw.Name + ".yaml"

cmd/config_ci.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,42 @@ import (
1010
func NewConfigCICmd(loaderSaver common.FunctionLoaderSaver, ciConfig ci.CIConfig) *cobra.Command {
1111
cmd := &cobra.Command{
1212
Use: "ci",
13+
// TODO(twoGiants): needs fix => see comment in runConfigCIGithub
14+
PreRunE: bindEnv(ci.UseRegistryLoginOption, ci.WorkflowNameOption),
1315
RunE: func(cmd *cobra.Command, args []string) (err error) {
14-
return runConfigCIGithub(loaderSaver, ciConfig)
16+
return runConfigCIGithub(cmd, loaderSaver, ciConfig)
1517
},
1618
}
1719

1820
addGithubFlag(cmd)
21+
cmd.Flags().Bool(
22+
ci.UseRegistryLoginOption,
23+
ci.DefaultUseRegistryLoginValue,
24+
"Add a registry login step in the github workflow",
25+
)
26+
cmd.Flags().String(
27+
ci.WorkflowNameOption,
28+
ci.DefaultWorkflowName,
29+
"Use a custom workflow name",
30+
)
1931

2032
return cmd
2133
}
2234

2335
func runConfigCIGithub(
36+
cmd *cobra.Command,
2437
fnLoaderSaver common.FunctionLoaderSaver,
2538
ciConfig ci.CIConfig,
2639
) error {
40+
41+
// TODO(twoGiants): broken => can't test with flags --use-registry-login
42+
// or --workflow-name => flags aren't propagated to viper
43+
// _ = ci.NewCiGithubConfigViaViper()
44+
cfg, err := ci.NewCiGithubConfigVia(cmd)
45+
if err != nil {
46+
return err
47+
}
48+
2749
f, err := initConfigCommand(fnLoaderSaver)
2850
if err != nil {
2951
return err
@@ -35,8 +57,10 @@ func runConfigCIGithub(
3557
ciConfig.RegistryUrlSecretKey(),
3658
ciConfig.RegistryUserSecretKey(),
3759
ciConfig.RegistryPassSecretKey(),
38-
ciConfig.UseRegistryLogin(),
60+
cfg.UseRegistryLogin(),
61+
ciConfig.UseRemoteBuild(),
3962
ciConfig.SelfHostedRunner(),
63+
ciConfig.UseDebug(),
4064
)
4165
if err := githubWorkflow.Persist(ciConfig.FnGithubWorkflowFilepath(f.Root)); err != nil {
4266
return err
@@ -46,9 +70,8 @@ func runConfigCIGithub(
4670
}
4771

4872
func addGithubFlag(cmd *cobra.Command) {
49-
cmd.Flags().BoolP(
73+
cmd.Flags().Bool(
5074
"github",
51-
"",
5275
false,
5376
"Generate GitHub Action ci workflow",
5477
)

0 commit comments

Comments
 (0)