diff --git a/cmd/uplift/bump.go b/cmd/uplift/bump.go index 18a1fa9c..6a786558 100644 --- a/cmd/uplift/bump.go +++ b/cmd/uplift/bump.go @@ -27,8 +27,6 @@ import ( "io" "github.com/gembaadvantage/uplift/internal/context" - "github.com/gembaadvantage/uplift/internal/middleware/logging" - "github.com/gembaadvantage/uplift/internal/middleware/skip" "github.com/gembaadvantage/uplift/internal/semver" "github.com/gembaadvantage/uplift/internal/task" "github.com/gembaadvantage/uplift/internal/task/bump" @@ -110,7 +108,7 @@ func bumpFiles(opts bumpOptions, out io.Writer) error { return err } - tsks := []task.Runner{ + tasks := []task.Runner{ gitcheck.Task{}, before.Task{}, gpgimport.Task{}, @@ -123,13 +121,7 @@ func bumpFiles(opts bumpOptions, out io.Writer) error { after.Task{}, } - for _, tsk := range tsks { - if err := skip.Running(tsk.Skip, logging.Log(tsk.String(), tsk.Run))(ctx); err != nil { - return err - } - } - - return nil + return task.Execute(ctx, tasks) } func setupBumpContext(opts bumpOptions, out io.Writer) (*context.Context, error) { diff --git a/cmd/uplift/changelog.go b/cmd/uplift/changelog.go index e25f2d93..4e66f8e9 100644 --- a/cmd/uplift/changelog.go +++ b/cmd/uplift/changelog.go @@ -29,8 +29,6 @@ import ( "github.com/gembaadvantage/uplift/internal/context" "github.com/gembaadvantage/uplift/internal/git" - "github.com/gembaadvantage/uplift/internal/middleware/logging" - "github.com/gembaadvantage/uplift/internal/middleware/skip" "github.com/gembaadvantage/uplift/internal/task" "github.com/gembaadvantage/uplift/internal/task/changelog" "github.com/gembaadvantage/uplift/internal/task/gitcheck" @@ -139,7 +137,7 @@ func writeChangelog(opts changelogOptions, out io.Writer) error { return err } - tsks := []task.Runner{ + tasks := []task.Runner{ gitcheck.Task{}, before.Task{}, scm.Task{}, @@ -151,13 +149,7 @@ func writeChangelog(opts changelogOptions, out io.Writer) error { after.Task{}, } - for _, tsk := range tsks { - if err := skip.Running(tsk.Skip, logging.Log(tsk.String(), tsk.Run))(ctx); err != nil { - return err - } - } - - return nil + return task.Execute(ctx, tasks) } func writeChangelogDiff(opts changelogOptions, out io.Writer) error { @@ -166,7 +158,7 @@ func writeChangelogDiff(opts changelogOptions, out io.Writer) error { return err } - tsks := []task.Runner{ + tasks := []task.Runner{ gitcheck.Task{}, before.Task{}, scm.Task{}, @@ -174,13 +166,7 @@ func writeChangelogDiff(opts changelogOptions, out io.Writer) error { after.Task{}, } - for _, tsk := range tsks { - if err := skip.Running(tsk.Skip, logging.Log(tsk.String(), tsk.Run))(ctx); err != nil { - return err - } - } - - return nil + return task.Execute(ctx, tasks) } func setupChangelogContext(opts changelogOptions, out io.Writer) (*context.Context, error) { diff --git a/cmd/uplift/release.go b/cmd/uplift/release.go index e36499d2..6c81cc88 100644 --- a/cmd/uplift/release.go +++ b/cmd/uplift/release.go @@ -28,8 +28,6 @@ import ( "io" "github.com/gembaadvantage/uplift/internal/context" - "github.com/gembaadvantage/uplift/internal/middleware/logging" - "github.com/gembaadvantage/uplift/internal/middleware/skip" "github.com/gembaadvantage/uplift/internal/semver" "github.com/gembaadvantage/uplift/internal/task" "github.com/gembaadvantage/uplift/internal/task/bump" @@ -146,7 +144,7 @@ func release(opts releaseOptions, out io.Writer) error { return err } - tsks := []task.Runner{ + tasks := []task.Runner{ gitcheck.Task{}, before.Task{}, gpgimport.Task{}, @@ -167,13 +165,7 @@ func release(opts releaseOptions, out io.Writer) error { after.Task{}, } - for _, tsk := range tsks { - if err := skip.Running(tsk.Skip, logging.Log(tsk.String(), tsk.Run))(ctx); err != nil { - return err - } - } - - return nil + return task.Execute(ctx, tasks) } func setupReleaseContext(opts releaseOptions, out io.Writer) (*context.Context, error) { @@ -235,14 +227,12 @@ func checkRelease(opts releaseOptions, out io.Writer) error { return err } - tsks := []task.Runner{ + tasks := []task.Runner{ nextsemver.Task{}, } - for _, tsk := range tsks { - if err := skip.Running(tsk.Skip, logging.Log(tsk.String(), tsk.Run))(ctx); err != nil { - return err - } + if err := task.Execute(ctx, tasks); err != nil { + return err } if ctx.NoVersionChanged { diff --git a/cmd/uplift/tag.go b/cmd/uplift/tag.go index 7180852e..11b08e3c 100644 --- a/cmd/uplift/tag.go +++ b/cmd/uplift/tag.go @@ -28,8 +28,6 @@ import ( "github.com/gembaadvantage/uplift/internal/context" "github.com/gembaadvantage/uplift/internal/git" - "github.com/gembaadvantage/uplift/internal/middleware/logging" - "github.com/gembaadvantage/uplift/internal/middleware/skip" "github.com/gembaadvantage/uplift/internal/semver" "github.com/gembaadvantage/uplift/internal/task" "github.com/gembaadvantage/uplift/internal/task/fetchtag" @@ -168,19 +166,13 @@ func tagRepo(opts tagOptions, out io.Writer) error { return err } - tsks := tagRepoPipeline + tasks := tagRepoPipeline if ctx.PrintNextTag { - tsks = printNextTagPipeline + tasks = printNextTagPipeline } - for _, tsk := range tsks { - if err := skip.Running(tsk.Skip, logging.Log(tsk.String(), tsk.Run))(ctx); err != nil { - return err - } - } - - return nil + return task.Execute(ctx, tasks) } func setupTagContext(opts tagOptions, out io.Writer) (*context.Context, error) { diff --git a/go.mod b/go.mod index f64ac652..c33f1c3a 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect golang.org/x/sync v0.1.0 // indirect diff --git a/go.sum b/go.sum index 6b4c659c..157c31da 100644 --- a/go.sum +++ b/go.sum @@ -85,6 +85,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/internal/middleware/action.go b/internal/middleware/action.go deleted file mode 100644 index 34347a91..00000000 --- a/internal/middleware/action.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright (c) 2022 Gemba Advantage - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package middleware - -import "github.com/gembaadvantage/uplift/internal/context" - -// Action defines a function that allows middleware to easily be chained. -// Action is defined with the same method signature as task.Runner Run(), -// allowing tasks to be transparently wrapped more easily -type Action func(ctx *context.Context) error diff --git a/internal/middleware/logging/logging.go b/internal/middleware/logging/logging.go deleted file mode 100644 index 356c4874..00000000 --- a/internal/middleware/logging/logging.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright (c) 2022 Gemba Advantage - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package logging - -import ( - "github.com/apex/log" - "github.com/apex/log/handlers/cli" - "github.com/gembaadvantage/uplift/internal/context" - "github.com/gembaadvantage/uplift/internal/middleware" -) - -const ( - // DefaultPadding ensures all titles are indented by a set number of spaces - DefaultPadding = 3 - - // PrettyPadding ensures all other logging is indented twice the size of the default padding - PrettyPadding = DefaultPadding * 2 -) - -// Log executes the given action and ensures the output is pretty printed. -// The title will always be followed by any indented output from the -// action itself -func Log(title string, act middleware.Action) middleware.Action { - return func(ctx *context.Context) error { - defer func() { - cli.Default.Padding = DefaultPadding - }() - - cli.Default.Padding = DefaultPadding - log.Info(title) - cli.Default.Padding = PrettyPadding - - return act(ctx) - } -} diff --git a/internal/middleware/skip/skip.go b/internal/middleware/skip/skip.go deleted file mode 100644 index ed77efe2..00000000 --- a/internal/middleware/skip/skip.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (c) 2022 Gemba Advantage - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package skip - -import ( - "github.com/gembaadvantage/uplift/internal/context" - "github.com/gembaadvantage/uplift/internal/middleware" -) - -// Action defines a function that supports the skipping of future middleware -// in the chain. Action is defined with the same method signature as task.Runner -// Skip(), allow taks to be transparently wrapped -type Action func(ctx *context.Context) bool - -// Running checks if a skip condition is satisfied before invoking the action itself. -// When used within a middleware chain, any wrap actions will be skipped if the -// skip condition resolves to true -func Running(skipAct Action, act middleware.Action) middleware.Action { - return func(ctx *context.Context) error { - if skipAct(ctx) { - return nil - } - return act(ctx) - } -} diff --git a/internal/middleware/skip/skip_test.go b/internal/middleware/skip/skip_test.go deleted file mode 100644 index 716f332d..00000000 --- a/internal/middleware/skip/skip_test.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright (c) 2022 Gemba Advantage - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package skip - -import ( - "testing" - - "github.com/gembaadvantage/uplift/internal/context" - "github.com/stretchr/testify/assert" -) - -func TestRunning(t *testing.T) { - Running(func(ctx *context.Context) bool { - return true - }, func(ctx *context.Context) error { - assert.Fail(t, "action should be skipped") - return nil - })(&context.Context{}) -} - -func TestRunning_NoSkip(t *testing.T) { - exec := false - - Running(func(ctx *context.Context) bool { - return false - }, func(ctx *context.Context) error { - exec = true - return nil - })(&context.Context{}) - - assert.True(t, exec) -} diff --git a/internal/task/runner.go b/internal/task/runner.go index fa0e58b8..d6b67331 100644 --- a/internal/task/runner.go +++ b/internal/task/runner.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Gemba Advantage +Copyright (c) 2023 Gemba Advantage Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25,9 +25,19 @@ package task import ( "fmt" + "github.com/apex/log" + "github.com/apex/log/handlers/cli" "github.com/gembaadvantage/uplift/internal/context" ) +const ( + // DefaultPadding ensures all titles are indented by a set number of spaces + DefaultPadding = 3 + + // PrettyPadding ensures all other logging is indented twice the size of the default padding + PrettyPadding = DefaultPadding * 2 +) + // Runner defines a way of running a task. A task can either be run as a // standalone operation or chained into a series of consecutive operations type Runner interface { @@ -40,3 +50,28 @@ type Runner interface { // Skip running of the task based on the current context state Skip(ctx *context.Context) bool } + +// Execute a series of tasks, providing the [context.Context] to each. Before executing +// a task, a precondition check is performed, identifying if the task should be skipped +// or not. Tasks that are skipped, will automatically have [skipped] appended to their +// task name. Execution will be aborted upon the first encountered error +func Execute(ctx *context.Context, tasks []Runner) error { + for _, t := range tasks { + defer func() { + // Ensure padding is automatically reset + cli.Default.Padding = DefaultPadding + }() + + cli.Default.Padding = DefaultPadding + + if t.Skip(ctx) { + log.Debug(fmt.Sprintf("(skipped) %s", t.String())) + } else { + log.Info(t.String()) + cli.Default.Padding = PrettyPadding + t.Run(ctx) + } + } + + return nil +} diff --git a/internal/middleware/logging/logging_test.go b/internal/task/runner_test.go similarity index 58% rename from internal/middleware/logging/logging_test.go rename to internal/task/runner_test.go index 64e2e052..33a74778 100644 --- a/internal/middleware/logging/logging_test.go +++ b/internal/task/runner_test.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Gemba Advantage +Copyright (c) 2023 Gemba Advantage Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,30 +20,49 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package logging +package task_test import ( "testing" - "github.com/apex/log/handlers/cli" "github.com/gembaadvantage/uplift/internal/context" - "github.com/stretchr/testify/assert" + "github.com/gembaadvantage/uplift/internal/task" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) -func TestLog(t *testing.T) { - err := Log("test", func(ctx *context.Context) error { - return nil - })(&context.Context{}) +func TestExecute(t *testing.T) { + m := &MockedTask{} + m.On("Run", mock.Anything).Return(nil) + m.On("Skip", mock.Anything).Return(false) + + err := task.Execute(&context.Context{}, []task.Runner{m}) require.NoError(t, err) + m.AssertExpectations(t) } -func TestLog_PrettyPrints(t *testing.T) { - Log("test", func(ctx *context.Context) error { - require.Equal(t, 6, cli.Default.Padding) - return nil - })(&context.Context{}) +func TestExecute_Skips(t *testing.T) { + m := &MockedTask{} + m.On("Skip", mock.Anything).Return(true) + + err := task.Execute(&context.Context{}, []task.Runner{m}) + + require.NoError(t, err) + m.AssertExpectations(t) + m.AssertNotCalled(t, "Run") +} + +type MockedTask struct { + mock.Mock +} + +func (m *MockedTask) Run(ctx *context.Context) error { + args := m.Called(ctx) + return args.Error(0) +} - assert.Equal(t, 3, cli.Default.Padding) +func (m *MockedTask) Skip(ctx *context.Context) bool { + args := m.Called(ctx) + return args.Bool(0) }