Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/atlas/internal/cmdapi: allow ent version to check if dir rebased #2272

Merged
merged 1 commit into from
Nov 9, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions cmd/atlas/internal/cmdapi/cmdapi.go
Original file line number Diff line number Diff line change
@@ -90,24 +90,51 @@ Atlas is licensed under Apache 2.0 as found in https://github.com/ariga/atlas/bl
}
)

// FormattedError is an error that format the command output when returned.
type FormattedError struct {
Err error
Prefix string // Prefix to use on error.
Silent bool // Silent errors are not printed.
}
type (
// ErrorFormatter implemented by the errors below to
// allow them format command output on error.
ErrorFormatter interface {
FormatError(*cobra.Command)
}
// FormattedError is an error that format the command output when returned.
FormattedError struct {
Err error
Prefix string // Prefix to use on error.
Silent bool // Silent errors are not printed.
}
// AbortError returns a command error that is formatted as "Abort: ..." when
// the execution is aborted by the user.
AbortError struct {
Err error
}
)

func (e *FormattedError) Error() string { return e.Err.Error() }

func (e *FormattedError) FormatError(cmd *cobra.Command) {
cmd.SilenceErrors = e.Silent
if e.Prefix != "" {
cmd.SetErrPrefix(e.Prefix)
}
}

// AbortErrorf is like fmt.Errorf for creating AbortError.
func AbortErrorf(format string, a ...any) error {
return &AbortError{Err: fmt.Errorf(format, a...)}
}

func (e *AbortError) Error() string { return e.Err.Error() }

func (e *AbortError) FormatError(cmd *cobra.Command) {
cmd.SetErrPrefix("Abort:")
}

// RunE wraps the command cobra.Command.RunE function with additional postrun logic.
func RunE(f func(*cobra.Command, []string) error) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
err := f(cmd, args)
if err1 := (*FormattedError)(nil); errors.As(err, &err1) {
cmd.SilenceErrors = err1.Silent
if err1.Prefix != "" {
cmd.SetErrPrefix(err1.Prefix)
}
if ef, ok := err.(ErrorFormatter); ok {
ef.FormatError(cmd)
}
return err
}
9 changes: 8 additions & 1 deletion cmd/atlas/internal/cmdapi/cmdapi_oss.go
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import (
"ariga.io/atlas/cmd/atlas/internal/cloudapi"
cmdmigrate "ariga.io/atlas/cmd/atlas/internal/migrate"
"ariga.io/atlas/cmd/atlas/internal/migratelint"
"ariga.io/atlas/sql/migrate"
"ariga.io/atlas/sql/sqlcheck"
"ariga.io/atlas/sql/sqlclient"

@@ -120,5 +121,11 @@ func promptApply(cmd *cobra.Command, flags schemaApplyFlags, diff *diff, client,

// withTokenContext allows attaching token to the context.
func withTokenContext(ctx context.Context, _ string, _ *cloudapi.Client) (context.Context, error) {
return ctx, nil
return ctx, nil // unimplemented.
}

// checkDirRebased checks that the local directory is up-to-date with the latest version of the directory.
// For example, Atlas Cloud or Git.
func checkDirRebased(context.Context, *cobra.Command, migrate.Dir) error {
return nil // unimplemented.
}
4 changes: 3 additions & 1 deletion cmd/atlas/internal/cmdapi/migrate.go
Original file line number Diff line number Diff line change
@@ -132,7 +132,6 @@ If run with the "--dry-run" flag, atlas will not execute any SQL.`,
addFlagLockTimeout(cmd.Flags(), &flags.lockTimeout)
cmd.Flags().StringVarP(&flags.baselineVersion, flagBaseline, "", "", "start the first migration after the given baseline version")
cmd.Flags().StringVarP(&flags.txMode, flagTxMode, "", txModeFile, "set transaction mode [none, file, all]")
// The following flag is hidden as it is used only by our CI programs.
cmd.Flags().StringVar(&flags.context, flagContext, "", "describes what triggered this command (e.g., GitHub Action)")
cobra.CheckErr(cmd.Flags().MarkHidden(flagContext))
cmd.Flags().BoolVarP(&flags.allowDirty, flagAllowDirty, "", false, "allow start working on a non-clean database")
@@ -617,6 +616,9 @@ func migrateDiffRun(cmd *cobra.Command, args []string, flags migrateDiffFlags, e
if err != nil {
return err
}
if err := checkDirRebased(ctx, cmd, dir); err != nil {
return err
}
if flags.edit {
dir = &editDir{dir}
}
7 changes: 4 additions & 3 deletions cmd/atlas/internal/cmdapi/schema.go
Original file line number Diff line number Diff line change
@@ -738,9 +738,10 @@ const (
// cmdPrompt returns a promptui.Select that uses the given command's input and output.
func cmdPrompt(cmd *cobra.Command) *promptui.Select {
return &promptui.Select{
Label: "Are you sure?",
Stdin: io.NopCloser(cmd.InOrStdin()),
Stdout: nopCloser{cmd.OutOrStdout()},
Label: "Are you sure?",
HideHelp: true,
Stdin: io.NopCloser(cmd.InOrStdin()),
Stdout: nopCloser{cmd.OutOrStdout()},
}
}

19 changes: 4 additions & 15 deletions cmd/atlas/internal/cmdapi/vercheck/vercheck_test.go
Original file line number Diff line number Diff line change
@@ -18,15 +18,12 @@ import (
"time"

"ariga.io/atlas/cmd/atlas/internal/cloudapi"
"ariga.io/atlas/cmd/atlas/internal/cmdstate"

"github.com/mitchellh/go-homedir"
"github.com/stretchr/testify/require"
)

func TestVerCheck(t *testing.T) {
homedir.DisableCache = true
t.Cleanup(func() { homedir.DisableCache = false })

var path, ua string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
output := `{"latest":{"Version":"v0.7.2","Summary":"","Link":"https://github.com/ariga/atlas/releases/tag/v0.7.2"},"advisory":null}`
@@ -36,8 +33,7 @@ func TestVerCheck(t *testing.T) {
}))
defer srv.Close()

home := t.TempDir()
t.Setenv("HOME", home)
home := cmdstate.TestingHome(t)
vc := New(srv.URL)
ver := "v0.1.2"
check, err := vc.Check(context.Background(), ver)
@@ -61,8 +57,6 @@ func TestVerCheck(t *testing.T) {
}

func TestState(t *testing.T) {
homedir.DisableCache = true
t.Cleanup(func() { homedir.DisableCache = false })
hrAgo, err := json.Marshal(State{CheckedAt: time.Now().Add(-time.Hour)})
require.NoError(t, err)
weekAgo, err := json.Marshal(State{CheckedAt: time.Now().Add(-time.Hour * 24 * 7)})
@@ -100,13 +94,12 @@ func TestState(t *testing.T) {
_, _ = w.Write([]byte(`{}`))
}))
t.Cleanup(srv.Close)
home := t.TempDir()
home := cmdstate.TestingHome(t)
path := filepath.Join(home, ".atlas", StateFileName)
if tt.state != "" {
require.NoError(t, os.MkdirAll(filepath.Dir(path), os.ModePerm))
require.NoError(t, os.WriteFile(path, []byte(tt.state), 0666))
}
t.Setenv("HOME", home)
vc := New(srv.URL)
_, _ = vc.Check(context.Background(), "v0.1.2")
require.EqualValues(t, tt.expectedRun, ran)
@@ -123,16 +116,12 @@ func TestState(t *testing.T) {
}

func TestStatePersist(t *testing.T) {
homedir.DisableCache = true
t.Cleanup(func() { homedir.DisableCache = false })

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`{}`))
}))
t.Cleanup(srv.Close)
home := t.TempDir()
home := cmdstate.TestingHome(t)
path := filepath.Join(home, ".atlas", StateFileName)
t.Setenv("HOME", home)
vc := New(srv.URL)
_, err := vc.Check(context.Background(), "v0.1.2")
require.NoError(t, err)
19 changes: 19 additions & 0 deletions cmd/atlas/internal/cmdstate/cmdstate.go
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@ import (
"os"
"path/filepath"
"reflect"
"sync"
"testing"

"github.com/mitchellh/go-homedir"
)
@@ -80,3 +82,20 @@ func newT[T any](t T) T {
}
return t
}

// muDisableCache ensures homedir.DisableCache is not changed concurrently on tests.
var muDisableCache sync.Mutex

// TestingHome is a helper function for testing that
// sets the HOME directory to a temporary directory.
func TestingHome(t *testing.T) string {
muDisableCache.Lock()
homedir.DisableCache = true
t.Cleanup(func() {
homedir.DisableCache = false
muDisableCache.Unlock()
})
home := t.TempDir()
t.Setenv("HOME", home)
return home
}