diff --git a/.github/scripts/gentz.sh b/.github/scripts/gentz.sh new file mode 100755 index 0000000..1bb527a --- /dev/null +++ b/.github/scripts/gentz.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -e # Exit on any error + +fail() { + echo "Error: $1" >&2 + exit 1 +} + +# Get GOROOT using `go env` +GOROOT=$(go env GOROOT) +if [ $? -ne 0 ]; then + fail "Failed to get GOROOT" +fi + +# Get the path to the zoneinfo.zip +ZIPNAME="$GOROOT/lib/time/zoneinfo.zip" +if [ ! -f "$ZIPNAME" ]; then + fail "zoneinfo.zip not found at $ZIPNAME" +fi + +# Open the output file +GOFILE=${GOFILE:-"timezones.go"} +GOFILE="$(basename "$GOFILE" ".go")_generated.go" +if ! touch "$GOFILE"; then + fail "Failed to create output file $GOFILE" +fi + +# Write the generated code header +{ + echo "// Code generated by \"$(basename "${BASH_SOURCE[0]}")\"" + echo "" + echo "package ${GOPACKAGE:-main}" + echo "" + echo "var TimeZones = []string{" +} > "$GOFILE" + +# List the contents of the zip file and append them to the output file +if ! unzip -Z1 "$ZIPNAME" | while read -r path; do + echo " \"${path//Etc\//}\"," >> "$GOFILE" +done; then + fail "Failed to process zip file $ZIPNAME" +fi + +# Close the Go array +echo "}" >> "$GOFILE" + +echo "TimeZones list generated successfully in $GOFILE." diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 174e25f..9b4314e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,7 +37,7 @@ jobs: XUNIT_OUTFILE: test-reports/unit.xml run: | test -d "$(dirname $XUNIT_OUTFILE)" || mkdir -p "$(dirname $XUNIT_OUTFILE)" - .github/scripts/gotest.sh ./... + make test - name: Publish Results uses: EnricoMi/publish-unit-test-result-action@v2 diff --git a/Makefile b/Makefile index 336cc6a..d99702c 100644 --- a/Makefile +++ b/Makefile @@ -48,32 +48,23 @@ prereq:: $(GOCMD) install github.com/jstemmer/go-junit-report@v1.0.0 GOBIN=${TOOLS_DIR} $(GOCMD) install go.uber.org/mock/mockgen@v0.5.0 -build:: - go env GOOS GOARCH - go build -ldflags="${LINKERFLAGS}" -gcflags ${COMPILERFLAGS} -o ${BINARY_CLI}/worker-cli-plugin main.go - - -build-install:: build - mkdir -p "${HOME}/.jfrog/plugins/worker/bin" - mv ${BINARY_CLI}/worker-cli-plugin "${HOME}/.jfrog/plugins/worker/bin/worker" - chmod +x "${HOME}/.jfrog/plugins/worker/bin/worker" - ########## TEST ########## -.PHONY: clean-mock clean-mock: @echo Cleaning generated mock files find . -path "*/mocks/*.go" -delete -.PHONY: generate-mock -generate-mock: clean-mock - @echo Generating test mocks - TOOLS_DIR=$(TOOLS_DIR) go generate ./... +generate: prereq clean-mock + TOOLS_DIR=$(TOOLS_DIR) SCRIPTS_DIR=$(SCRIPTS_DIR) go generate ./... + +# Used in pipeline so we keep this alias +generate-mock: generate -test-prereq: prereq generate-mock +test-prereq: generate-mock mkdir -p target/reports test: PACKAGES=./... +test: TAGS=-tags=test test: TEST_ARGS=-short test: test-prereq do-run-tests diff --git a/cli/cli.go b/cli/cli.go index c058523..5b9f868 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -29,6 +29,7 @@ func getWorkerNamespace() components.Namespace { commands.GetListCommand(), commands.GetAddSecretCommand(), commands.GetListEventsCommand(), + commands.GetEditScheduleCommand(), }, } } diff --git a/commands/common/manifest.go b/commands/common/manifest.go index db8359f..ee6a3fa 100644 --- a/commands/common/manifest.go +++ b/commands/common/manifest.go @@ -11,6 +11,7 @@ import ( "github.com/jfrog/jfrog-cli-platform-services/model" "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/robfig/cron/v3" ) // ReadManifest reads a manifest from the working directory or from the directory provided as argument. @@ -144,6 +145,29 @@ func DecryptManifestSecrets(mf *model.Manifest, withPassword ...string) error { return nil } +var cronParser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow) + +func ValidateScheduleCriteria(c *model.ScheduleFilterCriteria) error { + if c.Cron == "" { + return errors.New("missing cron expression") + } + + if _, err := cronParser.Parse(c.Cron); err != nil { + log.Debug(fmt.Sprintf("invalid cron expression: %+v", err)) + return errors.New("invalid cron expression") + } + + if c.Timezone == "" { + return errors.New("missing timezone") + } + + if !model.IsValidTimezone(c.Timezone) { + return errors.New("invalid timezone '" + c.Timezone + "'") + } + + return nil +} + func invalidManifestErr(reason string) error { return fmt.Errorf("invalid manifest: %s", reason) } diff --git a/commands/common/manifest_test.go b/commands/common/manifest_test.go index 22aa6ca..57c4c6d 100644 --- a/commands/common/manifest_test.go +++ b/commands/common/manifest_test.go @@ -5,8 +5,10 @@ package common import ( "encoding/json" + "errors" "os" "path/filepath" + "regexp" "testing" "github.com/jfrog/jfrog-cli-platform-services/model" @@ -212,7 +214,7 @@ func TestManifest_Validate(t *testing.T) { mf.Action = "HACK_ME" }), assert: func(t *testing.T, err error) { - assert.EqualError(t, err, invalidManifestErr("action 'HACK_ME' not found").Error()) + assert.Regexp(t, regexp.MustCompile("action 'HACK_ME' not found"), err) }, }, { @@ -291,6 +293,56 @@ func TestManifest_DecryptSecrets(t *testing.T) { } } +func TestManifest_ValidateScheduleCriteria(t *testing.T) { + tests := []struct { + name string + criteria *model.ScheduleFilterCriteria + wantErr error + }{ + { + name: "valid", + criteria: &model.ScheduleFilterCriteria{ + Cron: "0 1 1 * *", + Timezone: "UTC", + }, + }, + { + name: "missing cron", + criteria: &model.ScheduleFilterCriteria{ + Timezone: "UTC", + }, + wantErr: errors.New("missing cron expression"), + }, + { + name: "invalid cron", + criteria: &model.ScheduleFilterCriteria{ + Cron: "0 0 0 * * * *", + Timezone: "UTC", + }, + wantErr: errors.New("invalid cron expression"), + }, + { + name: "invalid timezone", + criteria: &model.ScheduleFilterCriteria{ + Cron: "0 1 1 * *", + Timezone: "America/Toulouse", + }, + wantErr: errors.New("invalid timezone 'America/Toulouse'"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateScheduleCriteria(tt.criteria) + if tt.wantErr == nil { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tt.wantErr.Error()) + } + }) + } +} + func patchedManifestSample(patch func(mf *model.Manifest)) *model.Manifest { patched := *manifestSample patch(&patched) diff --git a/commands/common/test_commons.go b/commands/common/test_commons.go index cc5b440..f1aaa91 100644 --- a/commands/common/test_commons.go +++ b/commands/common/test_commons.go @@ -78,6 +78,7 @@ func GenerateFromSamples(t require.TestingT, templates embed.FS, action string, "Application": actionMeta.Action.Application, "WorkerName": workerName, "HasRepoFilterCriteria": actionMeta.MandatoryFilter && actionMeta.FilterType == model.FilterTypeRepo, + "HasSchedule": actionMeta.MandatoryFilter && actionMeta.FilterType == model.FilterTypeSchedule, "HasTests": len(skipTests) == 0 || !skipTests[0], "HasRequestType": actionMeta.ExecutionRequestType != "", "ExecutionRequestType": actionMeta.ExecutionRequestType, diff --git a/commands/common/test_worker_server.go b/commands/common/test_worker_server.go index cf0a622..5f0d2ee 100644 --- a/commands/common/test_worker_server.go +++ b/commands/common/test_worker_server.go @@ -361,7 +361,9 @@ func (s *ServerStub) handleGetAllMetadata(metadata ActionsMetadata) http.Handler res.Header().Set("Content-Type", "application/json") _, err := res.Write([]byte(MustJsonMarshal(s.test, metadata))) - require.NoError(s.test, err) + if err != nil { + s.test.Logf("Failed to write response: %v", err) + } } } diff --git a/commands/deploy_cmd.go b/commands/deploy_cmd.go index 0a131d0..7a17b01 100644 --- a/commands/deploy_cmd.go +++ b/commands/deploy_cmd.go @@ -56,6 +56,17 @@ func GetDeployCommand() components.Command { return err } + actionMeta, err := actionsMeta.FindAction(manifest.Action, manifest.Application) + if err != nil { + return err + } + + if actionMeta.MandatoryFilter && actionMeta.FilterType == model.FilterTypeSchedule { + if err = common.ValidateScheduleCriteria(&manifest.FilterCriteria.Schedule); err != nil { + return fmt.Errorf("manifest validation failed: %w", err) + } + } + if !c.GetBoolFlagValue(model.FlagNoSecrets) { if err = common.DecryptManifestSecrets(manifest); err != nil { return err diff --git a/commands/deploy_cmd_test.go b/commands/deploy_cmd_test.go index e5f9e4f..a4e23fa 100644 --- a/commands/deploy_cmd_test.go +++ b/commands/deploy_cmd_test.go @@ -109,6 +109,19 @@ func TestDeployCommand(t *testing.T) { mf.ProjectKey = "proj-1" }, }, + { + name: "should validate schedule", + workerAction: "SCHEDULED_EVENT", + workerName: "wk-3", + serverBehavior: common.NewServerStub(t).WithGetOneEndpoint(), + patchManifest: func(mf *model.Manifest) { + mf.FilterCriteria.Schedule = model.ScheduleFilterCriteria{ + Cron: "1h", + Timezone: "Asia/New_York", + } + }, + wantErr: errors.New("manifest validation failed: invalid cron expression"), + }, { name: "fails if timeout exceeds", commandArgs: []string{"--" + model.FlagTimeout, "500"}, @@ -155,7 +168,7 @@ func TestDeployCommand(t *testing.T) { if tt.wantErr == nil { assert.NoError(t, err) } else { - assert.EqualError(t, tt.wantErr, err.Error()) + assert.EqualError(t, err, tt.wantErr.Error()) } }) } diff --git a/commands/dry_run_cmd_test.go b/commands/dry_run_cmd_test.go index 8b27bde..fb0c299 100644 --- a/commands/dry_run_cmd_test.go +++ b/commands/dry_run_cmd_test.go @@ -11,8 +11,6 @@ import ( "github.com/jfrog/jfrog-cli-platform-services/commands/common" - "github.com/stretchr/testify/require" - "github.com/jfrog/jfrog-cli-platform-services/model" ) @@ -20,6 +18,7 @@ func TestDryRun(t *testing.T) { tests := []struct { name string commandArgs []string + initExtraArgs []string assert common.AssertOutputFunc patchManifest func(mf *model.Manifest) // Use this workerKey instead of a random generated one @@ -47,7 +46,7 @@ func TestDryRun(t *testing.T) { WithToken("invalid-token"). WithTestEndpoint(nil, nil), commandArgs: []string{`{}`}, - assert: common.AssertOutputErrorRegexp(`command\s.+returned\san\sunexpected\sstatus\scode\s403`), + assert: common.AssertOutputErrorRegexp(`command.*returned\san\sunexpected\sstatus\scode\s403`), }, { name: "reads from stdin", @@ -94,7 +93,7 @@ func TestDryRun(t *testing.T) { { name: "fails if timeout exceeds", commandArgs: []string{"--" + model.FlagTimeout, "500", `{}`}, - serverStub: common.NewServerStub(t).WithDelay(5*time.Second).WithTestEndpoint(nil, nil), + serverStub: common.NewServerStub(t).WithDelay(2*time.Second).WithTestEndpoint(nil, nil), assert: common.AssertOutputError("request timed out after 500ms"), }, { @@ -108,8 +107,9 @@ func TestDryRun(t *testing.T) { assert: common.AssertOutputError("missing file path"), }, { - name: "should propagate projectKey", - workerKey: "my-worker", + name: "should propagate projectKey", + workerKey: "my-worker", + initExtraArgs: []string{"--" + model.FlagProjectKey, "my-project"}, serverStub: common.NewServerStub(t). WithProjectKey("my-project"). WithTestEndpoint( @@ -136,13 +136,6 @@ func TestDryRun(t *testing.T) { workerName = tt.workerKey } - err := runCmd("worker", "init", "BEFORE_DOWNLOAD", workerName) - require.NoError(t, err) - - if tt.patchManifest != nil { - common.PatchManifest(t, tt.patchManifest) - } - if tt.serverStub == nil { tt.serverStub = common.NewServerStub(t) } @@ -157,6 +150,17 @@ func TestDryRun(t *testing.T) { }), ) + initCmd := append([]string{"worker", "init"}, tt.initExtraArgs...) + err := runCmd(append(initCmd, "BEFORE_DOWNLOAD", workerName)...) + if err != nil { + tt.assert(t, nil, err) + return + } + + if tt.patchManifest != nil { + common.PatchManifest(t, tt.patchManifest) + } + if tt.stdInput != "" { common.SetCliIn(bytes.NewReader([]byte(tt.stdInput))) t.Cleanup(func() { diff --git a/commands/edit_schedule_cmd.go b/commands/edit_schedule_cmd.go new file mode 100644 index 0000000..0ffd79c --- /dev/null +++ b/commands/edit_schedule_cmd.go @@ -0,0 +1,71 @@ +package commands + +import ( + "fmt" + + "github.com/jfrog/jfrog-cli-platform-services/commands/common" + + "github.com/jfrog/jfrog-cli-core/v2/plugins/components" + "github.com/jfrog/jfrog-client-go/utils/log" + + "github.com/jfrog/jfrog-cli-platform-services/model" +) + +type editScheduleCommand struct { + ctx *components.Context +} + +const ( + flagScheduleCron = "cron" + flagScheduleTimezone = "timezone" +) + +func GetEditScheduleCommand() components.Command { + return components.Command{ + Name: "edit-schedule", + Description: "Edit the schedule criteria of a SCHEDULED_EVENT worker", + Aliases: []string{"es"}, + Flags: []components.Flag{ + components.NewStringFlag(flagScheduleCron, "A standard cron expression indicating the execution time.", components.WithStrDefaultValue("")), + components.NewStringFlag(flagScheduleTimezone, "The timezone to use for scheduling.", components.WithStrDefaultValue("")), + }, + Action: func(c *components.Context) error { + cmd := &editScheduleCommand{c} + return cmd.run() + }, + } +} + +func (c *editScheduleCommand) run() error { + manifest, err := common.ReadManifest() + if err != nil { + return err + } + + if err = common.ValidateManifest(manifest, nil); err != nil { + return err + } + + if manifest.Action != "SCHEDULED_EVENT" { + return fmt.Errorf("the worker is not a SCHEDULED_EVENT worker") + } + + newCriteria := model.ScheduleFilterCriteria{ + Cron: c.ctx.GetStringFlagValue(flagScheduleCron), + Timezone: c.ctx.GetStringFlagValue(flagScheduleTimezone), + } + + if err = common.ValidateScheduleCriteria(&newCriteria); err != nil { + return fmt.Errorf("invalid schedule provided: %w", err) + } + + manifest.FilterCriteria.Schedule = newCriteria + + if err = common.SaveManifest(manifest); err != nil { + return fmt.Errorf("failed to save manifest: %w", err) + } + + log.Info("Manifest updated successfully. Run 'jf worker deploy' to apply the changes.") + + return nil +} diff --git a/commands/edit_schedule_cmd_test.go b/commands/edit_schedule_cmd_test.go new file mode 100644 index 0000000..3dc15e4 --- /dev/null +++ b/commands/edit_schedule_cmd_test.go @@ -0,0 +1,92 @@ +//go:build test +// +build test + +package commands + +import ( + "errors" + "testing" + + "github.com/jfrog/jfrog-cli-platform-services/commands/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEditScheduleCommand(t *testing.T) { + tests := []struct { + name string + cron string + timezone string + workerAction string + wantErr error + }{ + { + name: "edit schedule", + cron: "0 1 * * *", + timezone: "America/New_York", + }, + { + name: "edit schedule with empty cron", + timezone: "UTC", + wantErr: errors.New("invalid schedule provided: missing cron expression"), + }, + { + name: "edit schedule with empty timezone", + cron: "0 1 * * *", + wantErr: errors.New("invalid schedule provided: missing timezone"), + }, + { + name: "edit schedule with invalid cron", + cron: "0 1 * * * * * *", + timezone: "America/New_York", + wantErr: errors.New("invalid schedule provided: invalid cron expression"), + }, + { + name: "edit schedule with invalid timezone", + cron: "0 1 * * *", + timezone: "Asia/Chicago", + wantErr: errors.New("invalid schedule provided: invalid timezone 'Asia/Chicago'"), + }, + { + name: "edit schedule for non-scheduled worker", + workerAction: "GENERIC_EVENT", + wantErr: errors.New("the worker is not a SCHEDULED_EVENT worker"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + common.NewMockWorkerServer(t, common.NewServerStub(t).WithDefaultActionsMetadataEndpoint()) + + runCmd := common.CreateCliRunner(t, GetInitCommand(), GetEditScheduleCommand()) + + _, workerName := common.PrepareWorkerDirForTest(t) + + workerAction := tt.workerAction + if workerAction == "" { + workerAction = "SCHEDULED_EVENT" + } + + err := runCmd("worker", "init", workerAction, workerName) + require.NoError(t, err) + + var commandArgs []string + if tt.cron != "" { + commandArgs = append(commandArgs, "--"+flagScheduleCron, tt.cron) + } + if tt.timezone != "" { + commandArgs = append(commandArgs, "--"+flagScheduleTimezone, tt.timezone) + } + + cmd := append([]string{"worker", "edit-schedule"}, commandArgs...) + + err = runCmd(cmd...) + + if tt.wantErr == nil { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tt.wantErr.Error()) + } + }) + } +} diff --git a/commands/execute_cmd_test.go b/commands/execute_cmd_test.go index 48a7103..23cb3c0 100644 --- a/commands/execute_cmd_test.go +++ b/commands/execute_cmd_test.go @@ -11,8 +11,6 @@ import ( "github.com/jfrog/jfrog-cli-platform-services/commands/common" - "github.com/stretchr/testify/require" - "github.com/jfrog/jfrog-cli-platform-services/model" ) @@ -20,11 +18,12 @@ func TestExecute(t *testing.T) { payload := map[string]any{"my": "payload"} tests := []struct { - name string - commandArgs []string - assert common.AssertOutputFunc - action string - workerKey string + name string + commandArgs []string + initExtraArgs []string + assert common.AssertOutputFunc + action string + workerKey string // The server behavior serverStub *common.ServerStub // If provided the cliIn will be filled with this content @@ -59,7 +58,7 @@ func TestExecute(t *testing.T) { name: "fails if not OK status", serverStub: common.NewServerStub(t).WithToken("invalid-token").WithExecuteEndpoint(nil, nil), commandArgs: []string{`{}`}, - assert: common.AssertOutputErrorRegexp(`command\sPOST.+returned\san\sunexpected\sstatus\scode\s403`), + assert: common.AssertOutputErrorRegexp(`command.+returned\san\sunexpected\sstatus\scode\s403`), }, { name: "reads from stdin", @@ -88,8 +87,9 @@ func TestExecute(t *testing.T) { serverStub: common.NewServerStub(t). WithProjectKey("my-project"). WithExecuteEndpoint(nil, payload), - commandArgs: []string{"-"}, - stdInput: `{}`, + initExtraArgs: []string{"--" + model.FlagProjectKey, "my-project"}, + commandArgs: []string{"-"}, + stdInput: `{}`, patchManifest: func(mf *model.Manifest) { mf.ProjectKey = "my-project" mf.Name = "my-worker" @@ -120,7 +120,7 @@ func TestExecute(t *testing.T) { { name: "fails if timeout exceeds", commandArgs: []string{"--" + model.FlagTimeout, "500", `{}`}, - serverStub: common.NewServerStub(t).WithDelay(5*time.Second).WithExecuteEndpoint(nil, nil), + serverStub: common.NewServerStub(t).WithDelay(2*time.Second).WithExecuteEndpoint(nil, nil), assert: common.AssertOutputError("request timed out after 500ms"), }, { @@ -150,13 +150,6 @@ func TestExecute(t *testing.T) { action = tt.action } - err := runCmd("worker", "init", action, workerName) - require.NoError(t, err) - - if tt.patchManifest != nil { - common.PatchManifest(t, tt.patchManifest) - } - if tt.serverStub == nil { tt.serverStub = common.NewServerStub(t) } @@ -171,6 +164,17 @@ func TestExecute(t *testing.T) { }), ) + initCmd := append([]string{"worker", "init"}, tt.initExtraArgs...) + err := runCmd(append(initCmd, action, workerName)...) + if err != nil { + tt.assert(t, nil, err) + return + } + + if tt.patchManifest != nil { + common.PatchManifest(t, tt.patchManifest) + } + if tt.stdInput != "" { common.SetCliIn(bytes.NewReader([]byte(tt.stdInput))) t.Cleanup(func() { diff --git a/commands/init_cmd.go b/commands/init_cmd.go index d552e63..20f7410 100644 --- a/commands/init_cmd.go +++ b/commands/init_cmd.go @@ -140,6 +140,7 @@ func (c *initHandler) initGenerator(targetDir string, workerName string, project "Application": md.Action.Application, "WorkerName": workerName, "HasRepoFilterCriteria": md.MandatoryFilter && md.FilterType == model.FilterTypeRepo, + "HasSchedule": md.MandatoryFilter && md.FilterType == model.FilterTypeSchedule, "HasTests": !skipTests, "HasRequestType": md.ExecutionRequestType != "", "ExecutionRequestType": md.ExecutionRequestType, diff --git a/commands/init_cmd_test.go b/commands/init_cmd_test.go index 9cec44d..4528981 100644 --- a/commands/init_cmd_test.go +++ b/commands/init_cmd_test.go @@ -62,7 +62,7 @@ func TestInitWorker(t *testing.T) { name: "invalid action", test: func(t *testing.T, runCommand runCommandFunc) { err := runCommand("worker", "init", "--timeout-ms", "60000", "HACK_SYSTEM", "root") - assert.Regexp(t, regexp.MustCompile(`^\s*invalid\s+action\s+'HACK_SYSTEM'\s+action\s+should\s+be\s+one\s+of:\s+\[[^]]+]\s*$`), err) + assert.Regexp(t, regexp.MustCompile(`^\s*action\s+'HACK_SYSTEM'\s+not\s+found+\s*.\s+It\s+should\s+be\s+one\s+of\s+\[[^]]+]\s*$`), err) }, }, { diff --git a/commands/remove_cmd_test.go b/commands/remove_cmd_test.go index 77251b5..aeb88e9 100644 --- a/commands/remove_cmd_test.go +++ b/commands/remove_cmd_test.go @@ -52,9 +52,10 @@ func TestRemoveCommand(t *testing.T) { assert: common.AssertOutputError("request timed out after 500ms"), }, { - name: "fails if invalid timeout", - commandArgs: []string{"--" + model.FlagTimeout, "abc"}, - assert: common.AssertOutputError("invalid timeout provided"), + name: "fails if invalid timeout", + commandArgs: []string{"--" + model.FlagTimeout, "abc"}, + serverBehavior: common.NewServerStub(t).WithDefaultActionsMetadataEndpoint(), + assert: common.AssertOutputError("invalid timeout provided"), }, } diff --git a/commands/templates/manifest.json_template b/commands/templates/manifest.json_template index 443b601..4bec9cf 100644 --- a/commands/templates/manifest.json_template +++ b/commands/templates/manifest.json_template @@ -7,6 +7,14 @@ "repoKeys": ["example-repo-local"] } }, +{{- end }} +{{- if .HasSchedule }} + "filterCriteria": { + "schedule": { + "cron": "0 * * * *", + "timezone": "UTC" + } + }, {{- end }} "secrets": {}, "sourceCodePath": "./worker.ts", diff --git a/go.mod b/go.mod index f90e9e5..5bd3d97 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,12 @@ module github.com/jfrog/jfrog-cli-platform-services require ( github.com/google/uuid v1.6.0 github.com/jfrog/go-mockhttp v0.3.1 - github.com/jfrog/jfrog-cli-core/v2 v2.56.8 - github.com/jfrog/jfrog-client-go v1.48.0 + github.com/jfrog/jfrog-cli-core/v2 v2.57.1 + github.com/jfrog/jfrog-client-go v1.48.3 + github.com/robfig/cron/v3 v3.0.1 github.com/stretchr/testify v1.10.0 go.uber.org/mock v0.5.0 - golang.org/x/crypto v0.29.0 + golang.org/x/crypto v0.31.0 ) require ( @@ -39,7 +40,7 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jedib0t/go-pretty/v6 v6.6.1 // indirect github.com/jfrog/archiver/v3 v3.6.1 // indirect - github.com/jfrog/build-info-go v1.10.5 // indirect + github.com/jfrog/build-info-go v1.10.6 // indirect github.com/jfrog/gofrog v1.7.6 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -81,10 +82,10 @@ require ( golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.31.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.26.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.27.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 89ccc5d..82d5b5a 100644 --- a/go.sum +++ b/go.sum @@ -88,16 +88,16 @@ github.com/jedib0t/go-pretty/v6 v6.6.1 h1:iJ65Xjb680rHcikRj6DSIbzCex2huitmc7bDtx github.com/jedib0t/go-pretty/v6 v6.6.1/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI= github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw= -github.com/jfrog/build-info-go v1.10.5 h1:cW03JlPlKv7RMUU896uLUxyLWXAmCgR5Y5QX0fwgz0Q= -github.com/jfrog/build-info-go v1.10.5/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE= +github.com/jfrog/build-info-go v1.10.6 h1:zH1ZhXlVfi5DlFyunygHjrdOcnv5qxfeLqmsfD4+lc4= +github.com/jfrog/build-info-go v1.10.6/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE= github.com/jfrog/go-mockhttp v0.3.1 h1:/wac8v4GMZx62viZmv4wazB5GNKs+GxawuS1u3maJH8= github.com/jfrog/go-mockhttp v0.3.1/go.mod h1:LmKHex73SUZswM8ANS8kPxLihTOvtq44HVcCoTJKuqc= github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s= github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4= -github.com/jfrog/jfrog-cli-core/v2 v2.56.8 h1:UexulAwRVN20VmYACijkTFYKqtUq5myE4okEgmUrorw= -github.com/jfrog/jfrog-cli-core/v2 v2.56.8/go.mod h1:RY74eDpw1WBxruSfZ0HO1ax7c1NAj+rbBgA/hVOJNME= -github.com/jfrog/jfrog-client-go v1.48.0 h1:hx5B7+Wnobmzq4aFVZtALtbEVDFcjpn0Wb4q2m6H4KU= -github.com/jfrog/jfrog-client-go v1.48.0/go.mod h1:1a7bmQHkRmPEza9wva2+WVrYzrGbosrMymq57kyG5gU= +github.com/jfrog/jfrog-cli-core/v2 v2.57.1 h1:YVuiSoavDisE8Dc9TOhYo5fE3d2C4/BrXuLDO/SZpJo= +github.com/jfrog/jfrog-cli-core/v2 v2.57.1/go.mod h1:e95/VWV6LL+UUxSNTJZ+sLmqJhCO5lDRhhLUQMV8WK4= +github.com/jfrog/jfrog-client-go v1.48.3 h1:HJpKGul0f/S2i7Uf7K/GwS1EUGiirt1LWXL1lanKNhU= +github.com/jfrog/jfrog-client-go v1.48.3/go.mod h1:1a7bmQHkRmPEza9wva2+WVrYzrGbosrMymq57kyG5gU= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -163,6 +163,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -226,8 +228,8 @@ go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= @@ -236,8 +238,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -257,14 +259,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= diff --git a/main.go b/main.go deleted file mode 100644 index a342a1f..0000000 --- a/main.go +++ /dev/null @@ -1,10 +0,0 @@ -package main - -import ( - "github.com/jfrog/jfrog-cli-core/v2/plugins" - "github.com/jfrog/jfrog-cli-platform-services/cli" -) - -func main() { - plugins.PluginMain(cli.GetPlatformServicesApp()) -} diff --git a/model/actions.go b/model/actions.go index df9328b..75e67ad 100644 --- a/model/actions.go +++ b/model/actions.go @@ -4,7 +4,7 @@ type ActionFilterType string const ( FilterTypeRepo = "FILTER_REPO" - FilterTypeSchedule = "FILTER_SCHEDULE" + FilterTypeSchedule = "SCHEDULE" ) type Action struct { diff --git a/model/manifest.go b/model/manifest.go index 5ff3923..f1744a3 100644 --- a/model/manifest.go +++ b/model/manifest.go @@ -4,8 +4,14 @@ type ArtifactFilterCriteria struct { RepoKeys []string `json:"repoKeys,omitempty"` } +type ScheduleFilterCriteria struct { + Cron string `json:"cron,omitempty"` + Timezone string `json:"timezone,omitempty"` +} + type FilterCriteria struct { ArtifactFilterCriteria ArtifactFilterCriteria `json:"artifactFilterCriteria,omitempty"` + Schedule ScheduleFilterCriteria `json:"schedule,omitempty"` } type Secrets map[string]string diff --git a/model/tz.go b/model/tz.go new file mode 100644 index 0000000..b91eb79 --- /dev/null +++ b/model/tz.go @@ -0,0 +1,14 @@ +//go:generate ${SCRIPTS_DIR}/gentz.sh + +package model + +import ( + "slices" + "strings" +) + +// For the timezone to be available we must call "make generate" the result must be committed + +func IsValidTimezone(timezone string) bool { + return slices.Index(TimeZones, strings.TrimSpace(timezone)) != -1 +} diff --git a/model/tz_generated.go b/model/tz_generated.go new file mode 100644 index 0000000..15eb137 --- /dev/null +++ b/model/tz_generated.go @@ -0,0 +1,603 @@ +// Code generated by "gentz.sh" + +package model + +var TimeZones = []string{ + "Africa/Abidjan", + "Africa/Accra", + "Africa/Addis_Ababa", + "Africa/Algiers", + "Africa/Asmara", + "Africa/Asmera", + "Africa/Bamako", + "Africa/Bangui", + "Africa/Banjul", + "Africa/Bissau", + "Africa/Blantyre", + "Africa/Brazzaville", + "Africa/Bujumbura", + "Africa/Cairo", + "Africa/Casablanca", + "Africa/Ceuta", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Douala", + "Africa/El_Aaiun", + "Africa/Freetown", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Juba", + "Africa/Kampala", + "Africa/Khartoum", + "Africa/Kigali", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Lome", + "Africa/Luanda", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Malabo", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Africa/Mogadishu", + "Africa/Monrovia", + "Africa/Nairobi", + "Africa/Ndjamena", + "Africa/Niamey", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Porto-Novo", + "Africa/Sao_Tome", + "Africa/Timbuktu", + "Africa/Tripoli", + "Africa/Tunis", + "Africa/Windhoek", + "America/Adak", + "America/Anchorage", + "America/Anguilla", + "America/Antigua", + "America/Araguaina", + "America/Argentina/Buenos_Aires", + "America/Argentina/Catamarca", + "America/Argentina/ComodRivadavia", + "America/Argentina/Cordoba", + "America/Argentina/Jujuy", + "America/Argentina/La_Rioja", + "America/Argentina/Mendoza", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Aruba", + "America/Asuncion", + "America/Atikokan", + "America/Atka", + "America/Bahia", + "America/Bahia_Banderas", + "America/Barbados", + "America/Belem", + "America/Belize", + "America/Blanc-Sablon", + "America/Boa_Vista", + "America/Bogota", + "America/Boise", + "America/Buenos_Aires", + "America/Cambridge_Bay", + "America/Campo_Grande", + "America/Cancun", + "America/Caracas", + "America/Catamarca", + "America/Cayenne", + "America/Cayman", + "America/Chicago", + "America/Chihuahua", + "America/Ciudad_Juarez", + "America/Coral_Harbour", + "America/Cordoba", + "America/Costa_Rica", + "America/Creston", + "America/Cuiaba", + "America/Curacao", + "America/Danmarkshavn", + "America/Dawson", + "America/Dawson_Creek", + "America/Denver", + "America/Detroit", + "America/Dominica", + "America/Edmonton", + "America/Eirunepe", + "America/El_Salvador", + "America/Ensenada", + "America/Fort_Nelson", + "America/Fort_Wayne", + "America/Fortaleza", + "America/Glace_Bay", + "America/Godthab", + "America/Goose_Bay", + "America/Grand_Turk", + "America/Grenada", + "America/Guadeloupe", + "America/Guatemala", + "America/Guayaquil", + "America/Guyana", + "America/Halifax", + "America/Havana", + "America/Hermosillo", + "America/Indiana/Indianapolis", + "America/Indiana/Knox", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Tell_City", + "America/Indiana/Vevay", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Indianapolis", + "America/Inuvik", + "America/Iqaluit", + "America/Jamaica", + "America/Jujuy", + "America/Juneau", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/Knox_IN", + "America/Kralendijk", + "America/La_Paz", + "America/Lima", + "America/Los_Angeles", + "America/Louisville", + "America/Lower_Princes", + "America/Maceio", + "America/Managua", + "America/Manaus", + "America/Marigot", + "America/Martinique", + "America/Matamoros", + "America/Mazatlan", + "America/Mendoza", + "America/Menominee", + "America/Merida", + "America/Metlakatla", + "America/Mexico_City", + "America/Miquelon", + "America/Moncton", + "America/Monterrey", + "America/Montevideo", + "America/Montreal", + "America/Montserrat", + "America/Nassau", + "America/New_York", + "America/Nipigon", + "America/Nome", + "America/Noronha", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Nuuk", + "America/Ojinaga", + "America/Panama", + "America/Pangnirtung", + "America/Paramaribo", + "America/Phoenix", + "America/Port-au-Prince", + "America/Port_of_Spain", + "America/Porto_Acre", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Punta_Arenas", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Recife", + "America/Regina", + "America/Resolute", + "America/Rio_Branco", + "America/Rosario", + "America/Santa_Isabel", + "America/Santarem", + "America/Santiago", + "America/Santo_Domingo", + "America/Sao_Paulo", + "America/Scoresbysund", + "America/Shiprock", + "America/Sitka", + "America/St_Barthelemy", + "America/St_Johns", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Swift_Current", + "America/Tegucigalpa", + "America/Thule", + "America/Thunder_Bay", + "America/Tijuana", + "America/Toronto", + "America/Tortola", + "America/Vancouver", + "America/Virgin", + "America/Whitehorse", + "America/Winnipeg", + "America/Yakutat", + "America/Yellowknife", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/DumontDUrville", + "Antarctica/Macquarie", + "Antarctica/Mawson", + "Antarctica/McMurdo", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/South_Pole", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok", + "Arctic/Longyearbyen", + "Asia/Aden", + "Asia/Almaty", + "Asia/Amman", + "Asia/Anadyr", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Ashkhabad", + "Asia/Atyrau", + "Asia/Baghdad", + "Asia/Bahrain", + "Asia/Baku", + "Asia/Bangkok", + "Asia/Barnaul", + "Asia/Beirut", + "Asia/Bishkek", + "Asia/Brunei", + "Asia/Calcutta", + "Asia/Chita", + "Asia/Choibalsan", + "Asia/Chongqing", + "Asia/Chungking", + "Asia/Colombo", + "Asia/Dacca", + "Asia/Damascus", + "Asia/Dhaka", + "Asia/Dili", + "Asia/Dubai", + "Asia/Dushanbe", + "Asia/Famagusta", + "Asia/Gaza", + "Asia/Harbin", + "Asia/Hebron", + "Asia/Ho_Chi_Minh", + "Asia/Hong_Kong", + "Asia/Hovd", + "Asia/Irkutsk", + "Asia/Istanbul", + "Asia/Jakarta", + "Asia/Jayapura", + "Asia/Jerusalem", + "Asia/Kabul", + "Asia/Kamchatka", + "Asia/Karachi", + "Asia/Kashgar", + "Asia/Kathmandu", + "Asia/Katmandu", + "Asia/Khandyga", + "Asia/Kolkata", + "Asia/Krasnoyarsk", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Kuwait", + "Asia/Macao", + "Asia/Macau", + "Asia/Magadan", + "Asia/Makassar", + "Asia/Manila", + "Asia/Muscat", + "Asia/Nicosia", + "Asia/Novokuznetsk", + "Asia/Novosibirsk", + "Asia/Omsk", + "Asia/Oral", + "Asia/Phnom_Penh", + "Asia/Pontianak", + "Asia/Pyongyang", + "Asia/Qatar", + "Asia/Qostanay", + "Asia/Qyzylorda", + "Asia/Rangoon", + "Asia/Riyadh", + "Asia/Saigon", + "Asia/Sakhalin", + "Asia/Samarkand", + "Asia/Seoul", + "Asia/Shanghai", + "Asia/Singapore", + "Asia/Srednekolymsk", + "Asia/Taipei", + "Asia/Tashkent", + "Asia/Tbilisi", + "Asia/Tehran", + "Asia/Tel_Aviv", + "Asia/Thimbu", + "Asia/Thimphu", + "Asia/Tokyo", + "Asia/Tomsk", + "Asia/Ujung_Pandang", + "Asia/Ulaanbaatar", + "Asia/Ulan_Bator", + "Asia/Urumqi", + "Asia/Ust-Nera", + "Asia/Vientiane", + "Asia/Vladivostok", + "Asia/Yakutsk", + "Asia/Yangon", + "Asia/Yekaterinburg", + "Asia/Yerevan", + "Atlantic/Azores", + "Atlantic/Bermuda", + "Atlantic/Canary", + "Atlantic/Cape_Verde", + "Atlantic/Faeroe", + "Atlantic/Faroe", + "Atlantic/Jan_Mayen", + "Atlantic/Madeira", + "Atlantic/Reykjavik", + "Atlantic/South_Georgia", + "Atlantic/St_Helena", + "Atlantic/Stanley", + "Australia/ACT", + "Australia/Adelaide", + "Australia/Brisbane", + "Australia/Broken_Hill", + "Australia/Canberra", + "Australia/Currie", + "Australia/Darwin", + "Australia/Eucla", + "Australia/Hobart", + "Australia/LHI", + "Australia/Lindeman", + "Australia/Lord_Howe", + "Australia/Melbourne", + "Australia/NSW", + "Australia/North", + "Australia/Perth", + "Australia/Queensland", + "Australia/South", + "Australia/Sydney", + "Australia/Tasmania", + "Australia/Victoria", + "Australia/West", + "Australia/Yancowinna", + "Brazil/Acre", + "Brazil/DeNoronha", + "Brazil/East", + "Brazil/West", + "CET", + "CST6CDT", + "Canada/Atlantic", + "Canada/Central", + "Canada/Eastern", + "Canada/Mountain", + "Canada/Newfoundland", + "Canada/Pacific", + "Canada/Saskatchewan", + "Canada/Yukon", + "Chile/Continental", + "Chile/EasterIsland", + "Cuba", + "EET", + "EST", + "EST5EDT", + "Egypt", + "Eire", + "GMT", + "GMT+0", + "GMT+1", + "GMT+10", + "GMT+11", + "GMT+12", + "GMT+2", + "GMT+3", + "GMT+4", + "GMT+5", + "GMT+6", + "GMT+7", + "GMT+8", + "GMT+9", + "GMT-0", + "GMT-1", + "GMT-10", + "GMT-11", + "GMT-12", + "GMT-13", + "GMT-14", + "GMT-2", + "GMT-3", + "GMT-4", + "GMT-5", + "GMT-6", + "GMT-7", + "GMT-8", + "GMT-9", + "GMT0", + "Greenwich", + "UCT", + "UTC", + "Universal", + "Zulu", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Astrakhan", + "Europe/Athens", + "Europe/Belfast", + "Europe/Belgrade", + "Europe/Berlin", + "Europe/Bratislava", + "Europe/Brussels", + "Europe/Bucharest", + "Europe/Budapest", + "Europe/Busingen", + "Europe/Chisinau", + "Europe/Copenhagen", + "Europe/Dublin", + "Europe/Gibraltar", + "Europe/Guernsey", + "Europe/Helsinki", + "Europe/Isle_of_Man", + "Europe/Istanbul", + "Europe/Jersey", + "Europe/Kaliningrad", + "Europe/Kiev", + "Europe/Kirov", + "Europe/Kyiv", + "Europe/Lisbon", + "Europe/Ljubljana", + "Europe/London", + "Europe/Luxembourg", + "Europe/Madrid", + "Europe/Malta", + "Europe/Mariehamn", + "Europe/Minsk", + "Europe/Monaco", + "Europe/Moscow", + "Europe/Nicosia", + "Europe/Oslo", + "Europe/Paris", + "Europe/Podgorica", + "Europe/Prague", + "Europe/Riga", + "Europe/Rome", + "Europe/Samara", + "Europe/San_Marino", + "Europe/Sarajevo", + "Europe/Saratov", + "Europe/Simferopol", + "Europe/Skopje", + "Europe/Sofia", + "Europe/Stockholm", + "Europe/Tallinn", + "Europe/Tirane", + "Europe/Tiraspol", + "Europe/Ulyanovsk", + "Europe/Uzhgorod", + "Europe/Vaduz", + "Europe/Vatican", + "Europe/Vienna", + "Europe/Vilnius", + "Europe/Volgograd", + "Europe/Warsaw", + "Europe/Zagreb", + "Europe/Zaporozhye", + "Europe/Zurich", + "Factory", + "GB", + "GB-Eire", + "GMT", + "GMT+0", + "GMT-0", + "GMT0", + "Greenwich", + "HST", + "Hongkong", + "Iceland", + "Indian/Antananarivo", + "Indian/Chagos", + "Indian/Christmas", + "Indian/Cocos", + "Indian/Comoro", + "Indian/Kerguelen", + "Indian/Mahe", + "Indian/Maldives", + "Indian/Mauritius", + "Indian/Mayotte", + "Indian/Reunion", + "Iran", + "Israel", + "Jamaica", + "Japan", + "Kwajalein", + "Libya", + "MET", + "MST", + "MST7MDT", + "Mexico/BajaNorte", + "Mexico/BajaSur", + "Mexico/General", + "NZ", + "NZ-CHAT", + "Navajo", + "PRC", + "PST8PDT", + "Pacific/Apia", + "Pacific/Auckland", + "Pacific/Bougainville", + "Pacific/Chatham", + "Pacific/Chuuk", + "Pacific/Easter", + "Pacific/Efate", + "Pacific/Enderbury", + "Pacific/Fakaofo", + "Pacific/Fiji", + "Pacific/Funafuti", + "Pacific/Galapagos", + "Pacific/Gambier", + "Pacific/Guadalcanal", + "Pacific/Guam", + "Pacific/Honolulu", + "Pacific/Johnston", + "Pacific/Kanton", + "Pacific/Kiritimati", + "Pacific/Kosrae", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Marquesas", + "Pacific/Midway", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Norfolk", + "Pacific/Noumea", + "Pacific/Pago_Pago", + "Pacific/Palau", + "Pacific/Pitcairn", + "Pacific/Pohnpei", + "Pacific/Ponape", + "Pacific/Port_Moresby", + "Pacific/Rarotonga", + "Pacific/Saipan", + "Pacific/Samoa", + "Pacific/Tahiti", + "Pacific/Tarawa", + "Pacific/Tongatapu", + "Pacific/Truk", + "Pacific/Wake", + "Pacific/Wallis", + "Pacific/Yap", + "Poland", + "Portugal", + "ROC", + "ROK", + "Singapore", + "Turkey", + "UCT", + "US/Alaska", + "US/Aleutian", + "US/Arizona", + "US/Central", + "US/East-Indiana", + "US/Eastern", + "US/Hawaii", + "US/Indiana-Starke", + "US/Michigan", + "US/Mountain", + "US/Pacific", + "US/Samoa", + "UTC", + "Universal", + "W-SU", + "WET", + "Zulu", +} diff --git a/qa-plugin/main.go b/qa-plugin/main.go index fd66da2..838f4a1 100644 --- a/qa-plugin/main.go +++ b/qa-plugin/main.go @@ -29,5 +29,6 @@ func getCommands() []components.Command { commands.GetListCommand(), commands.GetAddSecretCommand(), commands.GetListEventsCommand(), + commands.GetEditScheduleCommand(), } } diff --git a/test/commands/deploy_cmd_test.go b/test/commands/deploy_cmd_test.go index 0707530..d8d2de2 100644 --- a/test/commands/deploy_cmd_test.go +++ b/test/commands/deploy_cmd_test.go @@ -23,6 +23,7 @@ type deployTestCase struct { skip bool wantErr error workerKey string + workerAction string commandArgs []string initWorkers []*model.WorkerDetails patchManifest func(mf *model.Manifest) @@ -34,6 +35,11 @@ func TestDeployCommand(t *testing.T) { name: "create", workerKey: "wk-0", }), + deployTestSpec(deployTestCase{ + name: "deploy scheduled event", + workerKey: "wk-1_1", + workerAction: "SCHEDULED_EVENT", + }), deployTestSpec(deployTestCase{ name: "update", workerKey: "wk-1", @@ -94,7 +100,12 @@ func deployTestSpec(tc deployTestCase) infra.TestDefinition { workerName = tc.workerKey } - err := it.RunCommand(infra.AppName, "init", "GENERIC_EVENT", workerName) + workerAction := tc.workerAction + if workerAction == "" { + workerAction = "GENERIC_EVENT" + } + + err := it.RunCommand(infra.AppName, "init", "SCHEDULED_EVENT", workerName) require.NoError(it, err) if tc.patchManifest != nil { diff --git a/test/commands/list_cmd_test.go b/test/commands/list_cmd_test.go index 21f2822..b8a5f45 100644 --- a/test/commands/list_cmd_test.go +++ b/test/commands/list_cmd_test.go @@ -8,12 +8,13 @@ import ( "encoding/json" "errors" "fmt" - "github.com/jfrog/jfrog-cli-platform-services/commands/common" "slices" "strings" "testing" "time" + "github.com/jfrog/jfrog-cli-platform-services/commands/common" + "github.com/jfrog/jfrog-cli-platform-services/model" "github.com/jfrog/jfrog-cli-platform-services/test/infra" "github.com/stretchr/testify/assert" diff --git a/test/infra/itest_runner.go b/test/infra/itest_runner.go index 1b8d1c6..ef58c8d 100644 --- a/test/infra/itest_runner.go +++ b/test/infra/itest_runner.go @@ -9,8 +9,6 @@ import ( "net/http" "os" "path" - "path/filepath" - "runtime" "testing" "time" @@ -19,7 +17,7 @@ import ( "github.com/jfrog/jfrog-cli-platform-services/model" "github.com/google/uuid" - coreCommans "github.com/jfrog/jfrog-cli-core/v2/common/commands" + corecommands "github.com/jfrog/jfrog-cli-core/v2/common/commands" "github.com/jfrog/jfrog-cli-core/v2/plugins" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" @@ -80,8 +78,7 @@ func RunITests(tests []TestDefinition, t *testing.T) { } func runTest(t *testing.T, testSpec TestDefinition) { - homeDir := createTestHomeDir(t) - + homeDir := t.TempDir() // Setup cli home for tests err := os.Setenv(coreutils.HomeDir, homeDir) require.NoError(t, err) @@ -101,7 +98,7 @@ func runTest(t *testing.T, testSpec TestDefinition) { require.NotEmpty(t, accessToken, "No platform token provided, please set JF_PLATFORM_ACCESS_TOKEN env var") // Generates a server config - configCmd := coreCommans.NewConfigCommand(coreCommans.AddOrEdit, serverId) + configCmd := corecommands.NewConfigCommand(corecommands.AddOrEdit, serverId) configCmd.SetInteractive(false) configCmd.SetMakeDefault(true) configCmd.SetEncPassword(false) @@ -161,19 +158,6 @@ func (it *Test) PrepareWorkerTestDir() (string, string) { return dir, workerName } -func createTestHomeDir(t *testing.T) string { - _, filename, _, _ := runtime.Caller(0) - dir, err := os.MkdirTemp(filepath.Dir(filename), ".itest-*-no-git") - require.NoError(t, err) - t.Cleanup(func() { - err := os.RemoveAll(dir) - if err != nil { - t.Logf("Cannot remove test dir: %+v", err) - } - }) - return dir -} - func (it *Test) RunCommand(args ...string) error { oldArgs := os.Args defer func() { diff --git a/test/infra/test_app.go b/test/infra/test_app.go index 442bb51..69f4848 100644 --- a/test/infra/test_app.go +++ b/test/infra/test_app.go @@ -23,6 +23,7 @@ func getApp() components.App { commands.GetListCommand(), commands.GetAddSecretCommand(), commands.GetListEventsCommand(), + commands.GetEditScheduleCommand(), } return app }