Merge pull request #8550 from dolthub/db/ci-export

[no-release-notes] support workflow export and ls
This commit is contained in:
Dustin Brown
2024-11-12 11:12:50 -08:00
committed by GitHub
6 changed files with 587 additions and 79 deletions
+2
View File
@@ -22,4 +22,6 @@ var Commands = cli.NewHiddenSubCommandHandler("ci", "Commands for working with D
InitCmd{},
DestroyCmd{},
ImportCmd{},
ExportCmd{},
ListCmd{},
})
+172
View File
@@ -0,0 +1,172 @@
// Copyright 2024 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ci
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/fatih/color"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/commands"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/env/actions/dolt_ci"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
)
var exportDocs = cli.CommandDocumentationContent{
ShortDesc: "Export a Dolt continuous integration workflow file by name",
LongDesc: "Export a Dolt continuous integration workflow file by name",
Synopsis: []string{
"{{.LessThan}}workflow name{{.GreaterThan}}",
},
}
type ExportCmd struct{}
// Name implements cli.Command.
func (cmd ExportCmd) Name() string {
return "export"
}
// Description implements cli.Command.
func (cmd ExportCmd) Description() string {
return exportDocs.ShortDesc
}
// RequiresRepo implements cli.Command.
func (cmd ExportCmd) RequiresRepo() bool {
return true
}
// Docs implements cli.Command.
func (cmd ExportCmd) Docs() *cli.CommandDocumentation {
ap := cmd.ArgParser()
return cli.NewCommandDocumentation(exportDocs, ap)
}
// Hidden should return true if this command should be hidden from the help text
func (cmd ExportCmd) Hidden() bool {
return true
}
// ArgParser implements cli.Command.
func (cmd ExportCmd) ArgParser() *argparser.ArgParser {
ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 1)
return ap
}
// Exec implements cli.Command.
func (cmd ExportCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int {
ap := cmd.ArgParser()
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, exportDocs, ap))
apr := cli.ParseArgsOrDie(ap, args, help)
if !cli.CheckEnvIsValid(dEnv) {
return 1
}
var verr errhand.VerboseError
verr = validateExportArgs(apr)
if verr != nil {
return commands.HandleVErrAndExitCode(verr, usage)
}
workflowName := apr.Arg(0)
queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
if closeFunc != nil {
defer closeFunc()
}
user, email, err := env.GetNameAndEmail(dEnv.Config)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
hasTables, err := dolt_ci.HasDoltCITables(sqlCtx)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
if !hasTables {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(fmt.Errorf("dolt ci has not been initialized, please initialize with: dolt ci init")), usage)
}
wm := dolt_ci.NewWorkflowManager(user, email, queryist.Query)
db, err := newDatabase(sqlCtx, sqlCtx.GetCurrentDatabase(), dEnv, false)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
config, err := wm.GetWorkflowConfig(sqlCtx, db, workflowName)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
cwd, err := os.Getwd()
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
outpath, err := writeWorkflowConfig(config, cwd)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
cli.Println(color.CyanString(fmt.Sprintf("Dolt CI Workflow '%s' exported to %s.", config.Name.Value, outpath)))
return 0
}
func writeWorkflowConfig(config *dolt_ci.WorkflowConfig, dir string) (outpath string, err error) {
filename := strings.Replace(config.Name.Value, " ", "_", -1)
outpath = filepath.Join(dir, fmt.Sprintf("%s.yaml", filename))
var f *os.File
f, err = os.Create(outpath)
if err != nil {
return
}
defer func() {
rerr := f.Close()
if err == nil {
err = rerr
}
}()
var r io.Reader
r, err = dolt_ci.WorkflowConfigToYaml(config)
if err != nil {
return
}
_, err = io.Copy(f, r)
return
}
func validateExportArgs(apr *argparser.ArgParseResults) errhand.VerboseError {
if apr.NArg() != 1 {
return errhand.BuildDError("expected 1 argument").SetPrintUsage().Build()
}
return nil
}
+5 -4
View File
@@ -17,11 +17,12 @@ package ci
import (
"context"
"fmt"
"github.com/fatih/color"
"os"
"path/filepath"
"strings"
"github.com/fatih/color"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/commands"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
@@ -93,7 +94,7 @@ func (cmd ImportCmd) Exec(ctx context.Context, commandStr string, args []string,
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
querist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx)
queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
@@ -125,14 +126,14 @@ func (cmd ImportCmd) Exec(ctx context.Context, commandStr string, args []string,
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
wr := dolt_ci.NewWorkflowManager(user, email, querist.Query)
wm := dolt_ci.NewWorkflowManager(user, email, queryist.Query)
db, err := newDatabase(sqlCtx, sqlCtx.GetCurrentDatabase(), dEnv, false)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
err = wr.StoreAndCommit(sqlCtx, db, workflowConfig)
err = wm.StoreAndCommit(sqlCtx, db, workflowConfig)
if err != nil {
errorText := err.Error()
switch {
+121
View File
@@ -0,0 +1,121 @@
// Copyright 2024 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ci
import (
"context"
"fmt"
"github.com/fatih/color"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/commands"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/env/actions/dolt_ci"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
)
var listDocs = cli.CommandDocumentationContent{
ShortDesc: "List Dolt CI workflows",
LongDesc: "List Dolt CI workflows",
Synopsis: []string{
"{{.LessThan}}ls{{.GreaterThan}}",
},
}
type ListCmd struct{}
// Name implements cli.Command.
func (cmd ListCmd) Name() string {
return "ls"
}
// Description implements cli.Command.
func (cmd ListCmd) Description() string {
return listDocs.ShortDesc
}
// RequiresRepo implements cli.Command.
func (cmd ListCmd) RequiresRepo() bool {
return true
}
// Docs implements cli.Command.
func (cmd ListCmd) Docs() *cli.CommandDocumentation {
ap := cmd.ArgParser()
return cli.NewCommandDocumentation(listDocs, ap)
}
// Hidden should return true if this command should be hidden from the help text
func (cmd ListCmd) Hidden() bool {
return true
}
// ArgParser implements cli.Command.
func (cmd ListCmd) ArgParser() *argparser.ArgParser {
ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 0)
return ap
}
// Exec implements cli.Command.
func (cmd ListCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int {
ap := cmd.ArgParser()
_, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, listDocs, ap))
if !cli.CheckEnvIsValid(dEnv) {
return 1
}
queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
if closeFunc != nil {
defer closeFunc()
}
db, err := newDatabase(sqlCtx, sqlCtx.GetCurrentDatabase(), dEnv, false)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
name, email, err := env.GetNameAndEmail(dEnv.Config)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
hasTables, err := dolt_ci.HasDoltCITables(sqlCtx)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
if !hasTables {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(fmt.Errorf("dolt ci has not been initialized, please initialize with: dolt ci init")), usage)
}
wm := dolt_ci.NewWorkflowManager(name, email, queryist.Query)
workflows, err := wm.ListWorkflows(sqlCtx, db)
if err != nil {
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
for _, w := range workflows {
cli.Println(color.CyanString(fmt.Sprintf("%s", w)))
}
return 0
}
+40 -22
View File
@@ -15,6 +15,8 @@
package dolt_ci
import (
"bytes"
"errors"
"fmt"
"io"
@@ -22,24 +24,24 @@ import (
)
type Step struct {
Name string `yaml:"name"`
SavedQueryName string `yaml:"saved_query_name"`
ExpectedColumns string `yaml:"expected_columns"`
ExpectedRows string `yaml:"expected_rows"`
Name yaml.Node `yaml:"name"`
SavedQueryName yaml.Node `yaml:"saved_query_name"`
ExpectedColumns yaml.Node `yaml:"expected_columns"`
ExpectedRows yaml.Node `yaml:"expected_rows"`
}
type Job struct {
Name string `yaml:"name"`
Steps []Step `yaml:"steps"`
Name yaml.Node `yaml:"name"`
Steps []Step `yaml:"steps"`
}
type Push struct {
Branches []string `yaml:"branches"`
Branches []yaml.Node `yaml:"branches"`
}
type PullRequest struct {
Branches []string `yaml:"branches"`
Activities []string `yaml:"activities"`
Branches []yaml.Node `yaml:"branches"`
Activities []yaml.Node `yaml:"activities"`
}
type WorkflowDispatch struct{}
@@ -51,9 +53,9 @@ type On struct {
}
type WorkflowConfig struct {
Name string `yaml:"name"`
On On `yaml:"on"`
Jobs []Job `yaml:"jobs"`
Name yaml.Node `yaml:"name"`
On On `yaml:"on"`
Jobs []Job `yaml:"jobs"`
}
func ParseWorkflowConfig(r io.Reader) (workflow *WorkflowConfig, err error) {
@@ -68,6 +70,22 @@ func ParseWorkflowConfig(r io.Reader) (workflow *WorkflowConfig, err error) {
return
}
func WorkflowConfigToYaml(workflow *WorkflowConfig) (r io.Reader, err error) {
if workflow == nil {
err = errors.New("workflow config is nil")
return
}
var b []byte
b, err = yaml.Marshal(workflow)
if err != nil {
return
}
r = bytes.NewReader(b)
return
}
func ValidateWorkflowConfig(workflow *WorkflowConfig) error {
if workflow.On.WorkflowDispatch == nil && workflow.On.Push == nil && workflow.On.PullRequest == nil {
return fmt.Errorf("invalid config: no event triggers defined for workflow")
@@ -77,11 +95,11 @@ func ValidateWorkflowConfig(workflow *WorkflowConfig) error {
branches := make(map[string]bool)
for _, branch := range workflow.On.Push.Branches {
_, ok := branches[branch]
_, ok := branches[branch.Value]
if ok {
return fmt.Errorf("invalid config: on push branch duplicated: %s", branch)
} else {
branches[branch] = true
branches[branch.Value] = true
}
}
}
@@ -89,21 +107,21 @@ func ValidateWorkflowConfig(workflow *WorkflowConfig) error {
if workflow.On.PullRequest != nil {
branches := make(map[string]bool)
for _, branch := range workflow.On.PullRequest.Branches {
_, ok := branches[branch]
_, ok := branches[branch.Value]
if ok {
return fmt.Errorf("invalid config: on pull request branch duplicated: %s", branch)
} else {
branches[branch] = true
branches[branch.Value] = true
}
}
activities := make(map[string]bool)
for _, activity := range workflow.On.PullRequest.Activities {
_, ok := activities[activity]
_, ok := activities[activity.Value]
if ok {
return fmt.Errorf("invalid config: on pull request activities duplicated: %s", activity)
} else {
activities[activity] = true
activities[activity.Value] = true
}
}
}
@@ -120,19 +138,19 @@ func ValidateWorkflowConfig(workflow *WorkflowConfig) error {
return fmt.Errorf("invalid config: no steps defined for job: %s", job.Name)
}
_, ok := jobs[job.Name]
_, ok := jobs[job.Name.Value]
if ok {
return fmt.Errorf("invalid config: job duplicated: %s", job.Name)
} else {
jobs[job.Name] = true
jobs[job.Name.Value] = true
}
for _, step := range job.Steps {
_, ok := steps[step.Name]
_, ok := steps[step.Name.Value]
if ok {
return fmt.Errorf("invalid config: step duplicated: %s", step.Name)
} else {
steps[step.Name] = true
steps[step.Name.Value] = true
}
}
}
+247 -53
View File
@@ -17,12 +17,14 @@ package dolt_ci
import (
"errors"
"fmt"
"sort"
"strconv"
"strings"
"time"
"github.com/dolthub/go-mysql-server/sql"
"github.com/google/uuid"
"gopkg.in/yaml.v3"
"github.com/dolthub/dolt/go/libraries/doltcore/branch_control"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
@@ -38,6 +40,8 @@ var ErrWorkflowNotFound = errors.New("workflow not found")
var ErrMultipleWorkflowsFound = errors.New("multiple workflows found")
type WorkflowManager interface {
ListWorkflows(ctx *sql.Context, db sqle.Database) ([]string, error)
GetWorkflowConfig(ctx *sql.Context, db sqle.Database, workflowName string) (*WorkflowConfig, error)
StoreAndCommit(ctx *sql.Context, db sqle.Database, config *WorkflowConfig) error
}
@@ -697,6 +701,11 @@ func (d *doltWorkflowManager) listWorkflowEventsByWorkflowName(ctx *sql.Context,
return d.retrieveWorkflowEvents(ctx, query)
}
func (d *doltWorkflowManager) listWorkflows(ctx *sql.Context) ([]*Workflow, error) {
query := d.selectAllFromWorkflowsTableQuery()
return d.retrieveWorkflows(ctx, query)
}
func (d *doltWorkflowManager) listWorkflowEventsByWorkflowNameWhereEventTypeIsPush(ctx *sql.Context, workflowName WorkflowName) ([]*WorkflowEvent, error) {
query := d.selectAllFromWorkflowEventsTableByWorkflowNameWhereEventTypeIsPushQuery(string(workflowName))
return d.retrieveWorkflowEvents(ctx, query)
@@ -913,21 +922,21 @@ func (d *doltWorkflowManager) getWorkflow(ctx *sql.Context, workflowName string)
func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *WorkflowConfig) error {
// delete where there is no On field definition
if config.On.Push == nil {
err := d.deletePushWorkflowEvents(ctx, WorkflowName(config.Name))
err := d.deletePushWorkflowEvents(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
}
if config.On.PullRequest == nil {
err := d.deletePullRequestWorkflowEvents(ctx, WorkflowName(config.Name))
err := d.deletePullRequestWorkflowEvents(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
}
if config.On.WorkflowDispatch == nil {
err := d.deleteWorkflowDispatchWorkflowEvents(ctx, WorkflowName(config.Name))
err := d.deleteWorkflowDispatchWorkflowEvents(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
@@ -936,13 +945,13 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
// handle on push
if config.On.Push != nil {
if len(config.On.Push.Branches) == 0 {
events, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPush(ctx, WorkflowName(config.Name))
events, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPush(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
if len(events) == 0 {
_, err = d.writeWorkflowEventRow(ctx, WorkflowName(config.Name), WorkflowEventTypePush)
_, err = d.writeWorkflowEventRow(ctx, WorkflowName(config.Name.Value), WorkflowEventTypePush)
if err != nil {
return err
}
@@ -964,10 +973,10 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
} else {
configBranches := make(map[string]string)
for _, branch := range config.On.Push.Branches {
configBranches[branch] = branch
configBranches[branch.Value] = branch.Value
}
pushEvents, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPush(ctx, WorkflowName(config.Name))
pushEvents, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPush(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
@@ -1022,7 +1031,7 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
// handle case where there's no defined push event
if len(pushEvents) == 0 {
eventID, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name), WorkflowEventTypePush)
eventID, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name.Value), WorkflowEventTypePush)
if err != nil {
return err
}
@@ -1043,7 +1052,7 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
if config.On.PullRequest != nil {
if len(config.On.PullRequest.Branches) == 0 {
events, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPullRequest(ctx, WorkflowName(config.Name))
events, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPullRequest(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
@@ -1064,10 +1073,10 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
} else {
configBranches := make(map[string]string)
for _, branch := range config.On.PullRequest.Branches {
configBranches[branch] = branch
configBranches[branch.Value] = branch.Value
}
prEvents, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPullRequest(ctx, WorkflowName(config.Name))
prEvents, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPullRequest(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
@@ -1122,7 +1131,7 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
// handle case where there's no defined pull request event
if len(prEvents) == 0 {
eventID, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name), WorkflowEventTypePullRequest)
eventID, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name.Value), WorkflowEventTypePullRequest)
if err != nil {
return err
}
@@ -1141,7 +1150,7 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
}
if len(config.On.PullRequest.Activities) == 0 {
events, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPullRequest(ctx, WorkflowName(config.Name))
events, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPullRequest(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
@@ -1162,10 +1171,10 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
} else {
configActivities := make(map[string]string)
for _, activity := range config.On.PullRequest.Activities {
configActivities[activity] = activity
configActivities[activity.Value] = activity.Value
}
prEvents, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPullRequest(ctx, WorkflowName(config.Name))
prEvents, err := d.listWorkflowEventsByWorkflowNameWhereEventTypeIsPullRequest(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
@@ -1220,7 +1229,7 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
// handle case where there's no defined pull request event
if len(prEvents) == 0 {
eventID, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name), WorkflowEventTypePullRequest)
eventID, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name.Value), WorkflowEventTypePullRequest)
if err != nil {
return err
}
@@ -1239,7 +1248,7 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
}
if len(config.On.PullRequest.Branches) == 0 && len(config.On.PullRequest.Activities) == 0 {
_, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name), WorkflowEventTypePullRequest)
_, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name.Value), WorkflowEventTypePullRequest)
if err != nil {
return err
}
@@ -1247,7 +1256,7 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
}
if config.On.WorkflowDispatch != nil {
_, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name), WorkflowEventTypeWorkflowDispatch)
_, err := d.writeWorkflowEventRow(ctx, WorkflowName(config.Name.Value), WorkflowEventTypeWorkflowDispatch)
if err != nil {
return err
}
@@ -1255,10 +1264,10 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
configJobs := make(map[string]Job)
for _, job := range config.Jobs {
configJobs[job.Name] = job
configJobs[job.Name.Value] = job
}
jobs, err := d.listWorkflowJobsByWorkflowName(ctx, WorkflowName(config.Name))
jobs, err := d.listWorkflowJobsByWorkflowName(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
@@ -1275,10 +1284,10 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
orderedSteps := make(map[string]int)
for _, configJob := range config.Jobs {
if configJob.Name == job.Name {
if configJob.Name.Value == job.Name {
for idx, step := range configJob.Steps {
orderedSteps[step.Name] = idx
configSteps[step.Name] = step
orderedSteps[step.Name.Value] = idx
configSteps[step.Name.Value] = step
}
break
}
@@ -1310,13 +1319,13 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
}
}
if configStep.SavedQueryName != "" {
newExpectedColumnComparisonType, newExpectedColumnCount, err := d.parseSavedQueryExpectedResultString(configStep.ExpectedColumns)
if configStep.SavedQueryName.Value != "" {
newExpectedColumnComparisonType, newExpectedColumnCount, err := d.parseSavedQueryExpectedResultString(configStep.ExpectedColumns.Value)
if err != nil {
return err
}
newExpectedRowComparisonType, newExpectedRowCount, err := d.parseSavedQueryExpectedResultString(configStep.ExpectedRows)
newExpectedRowComparisonType, newExpectedRowCount, err := d.parseSavedQueryExpectedResultString(configStep.ExpectedRows.Value)
if err != nil {
return err
}
@@ -1327,8 +1336,8 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
}
if savedQueryStep.SavedQueryExpectedResultsType == WorkflowSavedQueryExpectedResultsTypeRowColumnCount {
if configStep.SavedQueryName != savedQueryStep.SavedQueryName {
err = d.updateWorkflowSavedQueryStepRow(ctx, *savedQueryStep.Id, configStep.SavedQueryName)
if configStep.SavedQueryName.Value != savedQueryStep.SavedQueryName {
err = d.updateWorkflowSavedQueryStepRow(ctx, *savedQueryStep.Id, configStep.SavedQueryName.Value)
if err != nil {
return err
}
@@ -1358,28 +1367,28 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
}
for _, step := range configSteps {
orderIdx, ok := orderedSteps[step.Name]
orderIdx, ok := orderedSteps[step.Name.Value]
if !ok {
return errors.New("failed to get step order")
}
stepOrder := orderIdx + 1
stepID, err := d.writeWorkflowStepRow(ctx, *job.Id, step.Name, stepOrder, WorkflowStepTypeSavedQuery)
stepID, err := d.writeWorkflowStepRow(ctx, *job.Id, step.Name.Value, stepOrder, WorkflowStepTypeSavedQuery)
if err != nil {
return err
}
savedQueryStepID, err := d.writeWorkflowSavedQueryStepRow(ctx, stepID, step.SavedQueryName, WorkflowSavedQueryExpectedResultsTypeRowColumnCount)
savedQueryStepID, err := d.writeWorkflowSavedQueryStepRow(ctx, stepID, step.SavedQueryName.Value, WorkflowSavedQueryExpectedResultsTypeRowColumnCount)
if err != nil {
return err
}
expectedColumnComparisonType, expectedColumnCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedColumns)
expectedColumnComparisonType, expectedColumnCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedColumns.Value)
if err != nil {
return err
}
expectedRowComparisonType, expectedRowCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedRows)
expectedRowComparisonType, expectedRowCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedRows.Value)
if err != nil {
return err
}
@@ -1389,8 +1398,8 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
return err
}
delete(configSteps, step.Name)
delete(orderedSteps, step.Name)
delete(configSteps, step.Name.Value)
delete(orderedSteps, step.Name.Value)
}
delete(configJobs, job.Name)
@@ -1399,27 +1408,27 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
// create all jobs that do not yet exist
for _, job := range configJobs {
jobID, err := d.writeWorkflowJobRow(ctx, WorkflowName(config.Name), job.Name)
jobID, err := d.writeWorkflowJobRow(ctx, WorkflowName(config.Name.Value), job.Name.Value)
if err != nil {
return err
}
for idx, step := range job.Steps {
stepID, err := d.writeWorkflowStepRow(ctx, jobID, step.Name, idx+1, WorkflowStepTypeSavedQuery)
stepID, err := d.writeWorkflowStepRow(ctx, jobID, step.Name.Value, idx+1, WorkflowStepTypeSavedQuery)
if err != nil {
return err
}
savedQueryStepID, err := d.writeWorkflowSavedQueryStepRow(ctx, stepID, step.SavedQueryName, WorkflowSavedQueryExpectedResultsTypeRowColumnCount)
savedQueryStepID, err := d.writeWorkflowSavedQueryStepRow(ctx, stepID, step.SavedQueryName.Value, WorkflowSavedQueryExpectedResultsTypeRowColumnCount)
if err != nil {
return err
}
expectedColumnComparisonType, expectedColumnCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedColumns)
expectedColumnComparisonType, expectedColumnCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedColumns.Value)
if err != nil {
return err
}
expectedRowComparisonType, expectedRowCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedRows)
expectedRowComparisonType, expectedRowCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedRows.Value)
if err != nil {
return err
}
@@ -1430,7 +1439,7 @@ func (d *doltWorkflowManager) updateExistingWorkflow(ctx *sql.Context, config *W
}
}
delete(configJobs, job.Name)
delete(configJobs, job.Name.Value)
}
return nil
@@ -1599,13 +1608,36 @@ func (d *doltWorkflowManager) parseSavedQueryExpectedResultString(str string) (W
return WorkflowSavedQueryExpectedRowColumnComparisonTypeLessThan, i, nil
case "<=":
return WorkflowSavedQueryExpectedRowColumnComparisonTypeLessThanOrEqual, i, nil
default:
return 0, 0, errors.New("unknown comparison type")
}
}
return 0, 0, fmt.Errorf("unable to parse comparison string: %s", str)
}
func (d *doltWorkflowManager) toSavedQueryExpectedResultString(comparisonType WorkflowSavedQueryExpectedRowColumnComparisonType, count int64) (string, error) {
var compareStr string
switch comparisonType {
case WorkflowSavedQueryExpectedRowColumnComparisonTypeEquals:
compareStr = "=="
case WorkflowSavedQueryExpectedRowColumnComparisonTypeNotEquals:
compareStr = "!="
case WorkflowSavedQueryExpectedRowColumnComparisonTypeGreaterThan:
compareStr = ">"
case WorkflowSavedQueryExpectedRowColumnComparisonTypeGreaterThanOrEqual:
compareStr = ">="
case WorkflowSavedQueryExpectedRowColumnComparisonTypeLessThan:
compareStr = "<"
case WorkflowSavedQueryExpectedRowColumnComparisonTypeLessThanOrEqual:
compareStr = "<="
default:
return "", errors.New("unknown comparison type")
}
return fmt.Sprintf("%s %d", compareStr, count), nil
}
func (d *doltWorkflowManager) createWorkflow(ctx *sql.Context, config *WorkflowConfig) error {
workflowName, err := d.writeWorkflowRow(ctx, WorkflowName(config.Name))
workflowName, err := d.writeWorkflowRow(ctx, WorkflowName(config.Name.Value))
if err != nil {
return err
}
@@ -1688,7 +1720,7 @@ func (d *doltWorkflowManager) createWorkflow(ctx *sql.Context, config *WorkflowC
// handle pushes
if pushBranchesTriggerEventID != "" {
for _, branch := range config.On.Push.Branches {
_, err = d.writeWorkflowEventTriggerBranchesRow(ctx, pushBranchesTriggerEventID, branch)
_, err = d.writeWorkflowEventTriggerBranchesRow(ctx, pushBranchesTriggerEventID, branch.Value)
if err != nil {
return err
}
@@ -1698,7 +1730,7 @@ func (d *doltWorkflowManager) createWorkflow(ctx *sql.Context, config *WorkflowC
// handle pull requests
if pullRequestBranchesTriggerEventID != "" {
for _, branch := range config.On.PullRequest.Branches {
_, err = d.writeWorkflowEventTriggerBranchesRow(ctx, pullRequestBranchesTriggerEventID, branch)
_, err = d.writeWorkflowEventTriggerBranchesRow(ctx, pullRequestBranchesTriggerEventID, branch.Value)
if err != nil {
return err
}
@@ -1709,7 +1741,7 @@ func (d *doltWorkflowManager) createWorkflow(ctx *sql.Context, config *WorkflowC
// handle pull requests
if pullRequestActivitiesTriggerEventID != "" {
for _, activity := range config.On.PullRequest.Activities {
_, err = d.writeWorkflowEventTriggerActivitiesRow(ctx, pullRequestActivitiesTriggerEventID, activity)
_, err = d.writeWorkflowEventTriggerActivitiesRow(ctx, pullRequestActivitiesTriggerEventID, activity.Value)
if err != nil {
return err
}
@@ -1719,7 +1751,7 @@ func (d *doltWorkflowManager) createWorkflow(ctx *sql.Context, config *WorkflowC
// handle jobs
for _, job := range config.Jobs {
// insert into jobs
jobID, err := d.writeWorkflowJobRow(ctx, workflowName, job.Name)
jobID, err := d.writeWorkflowJobRow(ctx, workflowName, job.Name.Value)
if err != nil {
return err
}
@@ -1730,29 +1762,29 @@ func (d *doltWorkflowManager) createWorkflow(ctx *sql.Context, config *WorkflowC
order := idx + 1
var stepType WorkflowStepType
if step.SavedQueryName != "" {
if step.SavedQueryName.Value != "" {
stepType = WorkflowStepTypeSavedQuery
}
stepID, err := d.writeWorkflowStepRow(ctx, jobID, step.Name, order, stepType)
stepID, err := d.writeWorkflowStepRow(ctx, jobID, step.Name.Value, order, stepType)
if err != nil {
return err
}
// insert into saved query steps
if stepType == WorkflowStepTypeSavedQuery {
savedQueryStepID, err := d.writeWorkflowSavedQueryStepRow(ctx, stepID, step.SavedQueryName, WorkflowSavedQueryExpectedResultsTypeRowColumnCount)
savedQueryStepID, err := d.writeWorkflowSavedQueryStepRow(ctx, stepID, step.SavedQueryName.Value, WorkflowSavedQueryExpectedResultsTypeRowColumnCount)
if err != nil {
return err
}
// insert into expected results
expectedColumnComparisonType, expectedColumnCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedColumns)
expectedColumnComparisonType, expectedColumnCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedColumns.Value)
if err != nil {
return err
}
expectedRowComparisonType, expectedRowCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedRows)
expectedRowComparisonType, expectedRowCount, err := d.parseSavedQueryExpectedResultString(step.ExpectedRows.Value)
if err != nil {
return err
}
@@ -1767,8 +1799,137 @@ func (d *doltWorkflowManager) createWorkflow(ctx *sql.Context, config *WorkflowC
return nil
}
func (d *doltWorkflowManager) getWorkflowConfig(ctx *sql.Context, workflowName string) (*WorkflowConfig, error) {
config := &WorkflowConfig{}
workflow, err := d.getWorkflow(ctx, workflowName)
if err != nil {
return nil, err
}
config.Name = newScalarDoubleQuotedYamlNode(string(*workflow.Name))
events, err := d.listWorkflowEventsByWorkflowName(ctx, *workflow.Name)
if err != nil {
return nil, err
}
on := On{}
for _, event := range events {
triggers, err := d.listWorkflowEventTriggersByEventId(ctx, *event.Id)
if err != nil {
return nil, err
}
activities := make([]yaml.Node, 0)
branches := make([]yaml.Node, 0)
for _, trigger := range triggers {
if trigger.EventTriggerType == WorkflowEventTriggerTypeActivities {
acts, err := d.listWorkflowEventTriggerActivitiesByEventTriggerId(ctx, *trigger.Id)
if err != nil {
return nil, err
}
for _, act := range acts {
activities = append(activities, newScalarDoubleQuotedYamlNode(act.Activity))
}
} else if trigger.EventTriggerType == WorkflowEventTriggerTypeBranches {
brns, err := d.listWorkflowEventTriggerBranchesByEventTriggerId(ctx, *trigger.Id)
if err != nil {
return nil, err
}
for _, brn := range brns {
branches = append(branches, newScalarDoubleQuotedYamlNode(brn.Branch))
}
}
}
if event.EventType == WorkflowEventTypePush {
on.Push = &Push{}
if len(branches) > 0 {
on.Push.Branches = branches
}
} else if event.EventType == WorkflowEventTypePullRequest {
on.PullRequest = &PullRequest{}
if len(branches) > 0 {
on.PullRequest.Branches = branches
}
if len(activities) > 0 {
on.PullRequest.Activities = activities
}
} else if event.EventType == WorkflowEventTypeWorkflowDispatch {
on.WorkflowDispatch = &WorkflowDispatch{}
}
}
config.On = on
jobs := make([]Job, 0)
jbs, err := d.listWorkflowJobsByWorkflowName(ctx, *workflow.Name)
if err != nil {
return nil, err
}
for _, jb := range jbs {
steps := make([]Step, 0)
stps, err := d.listWorkflowStepsByJobId(ctx, *jb.Id)
if err != nil {
return nil, err
}
sort.Slice(stps, func(i, j int) bool {
return stps[i].StepOrder < stps[j].StepOrder
})
for _, stp := range stps {
if stp.StepType == WorkflowStepTypeSavedQuery {
savedQueryStep, err := d.getWorkflowSavedQueryStepByStepId(ctx, *stp.Id)
if err != nil {
return nil, err
}
expectedResult, err := d.getWorkflowSavedQueryExpectedRowColumnResultBySavedQueryStepId(ctx, *savedQueryStep.Id)
if err != nil {
return nil, err
}
expectedColumnsStr, err := d.toSavedQueryExpectedResultString(expectedResult.ExpectedColumnCountComparisonType, expectedResult.ExpectedColumnCount)
if err != nil {
return nil, err
}
expectedRowsStr, err := d.toSavedQueryExpectedResultString(expectedResult.ExpectedRowCountComparisonType, expectedResult.ExpectedRowCount)
if err != nil {
return nil, err
}
step := Step{
Name: newScalarDoubleQuotedYamlNode(stp.Name),
SavedQueryName: newScalarDoubleQuotedYamlNode(savedQueryStep.SavedQueryName),
ExpectedColumns: newScalarDoubleQuotedYamlNode(expectedColumnsStr),
ExpectedRows: newScalarDoubleQuotedYamlNode(expectedRowsStr),
}
steps = append(steps, step)
}
}
job := Job{
Name: newScalarDoubleQuotedYamlNode(jb.Name),
Steps: steps,
}
jobs = append(jobs, job)
}
config.Jobs = jobs
return config, nil
}
func (d *doltWorkflowManager) storeFromConfig(ctx *sql.Context, config *WorkflowConfig) error {
_, err := d.getWorkflow(ctx, config.Name)
_, err := d.getWorkflow(ctx, config.Name.Value)
if err != nil {
if err == ErrWorkflowNotFound {
return d.createWorkflow(ctx, config)
@@ -1778,6 +1939,31 @@ func (d *doltWorkflowManager) storeFromConfig(ctx *sql.Context, config *Workflow
return d.updateExistingWorkflow(ctx, config)
}
func (d *doltWorkflowManager) GetWorkflowConfig(ctx *sql.Context, db sqle.Database, workflowName string) (*WorkflowConfig, error) {
if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Read); err != nil {
return nil, err
}
return d.getWorkflowConfig(ctx, workflowName)
}
func (d *doltWorkflowManager) ListWorkflows(ctx *sql.Context, db sqle.Database) ([]string, error) {
if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Read); err != nil {
return nil, err
}
names := make([]string, 0)
workflows, err := d.listWorkflows(ctx)
if err != nil {
return nil, err
}
sort.Slice(workflows, func(i, j int) bool {
return *workflows[i].Name < *workflows[j].Name
})
for _, w := range workflows {
names = append(names, string(*w.Name))
}
return names, nil
}
func (d *doltWorkflowManager) StoreAndCommit(ctx *sql.Context, db sqle.Database, config *WorkflowConfig) error {
if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
return err
@@ -1788,7 +1974,7 @@ func (d *doltWorkflowManager) StoreAndCommit(ctx *sql.Context, db sqle.Database,
return err
}
return d.commitWorkflow(ctx, config.Name)
return d.commitWorkflow(ctx, config.Name.Value)
}
func SqlWriteQuery(ctx *sql.Context, qf queryFunc, query string) error {
@@ -1799,3 +1985,11 @@ func SqlWriteQuery(ctx *sql.Context, qf queryFunc, query string) error {
_, err = sql.RowIterToRows(ctx, rowIter)
return err
}
func newScalarDoubleQuotedYamlNode(value string) yaml.Node {
return yaml.Node{
Kind: yaml.ScalarNode,
Style: yaml.DoubleQuotedStyle,
Value: value,
}
}