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

atlasexec: update public APIs #8

Merged
merged 2 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
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
79 changes: 42 additions & 37 deletions atlasexec/atlas.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
)
Expand All @@ -19,8 +18,8 @@ type (
execPath string
workingDir string
}
// ApplyParams are the parameters for the `migrate apply` command.
ApplyParams struct {
// MigrateApplyParams are the parameters for the `migrate apply` command.
MigrateApplyParams struct {
Env string
ConfigURL string
DirURL string
Expand All @@ -31,17 +30,17 @@ type (
Amount uint64
Vars Vars
}
// StatusParams are the parameters for the `migrate status` command.
StatusParams struct {
// MigrateStatusParams are the parameters for the `migrate status` command.
MigrateStatusParams struct {
Env string
ConfigURL string
DirURL string
URL string
RevisionsSchema string
Vars Vars
}
// LintParams are the parameters for the `migrate lint` command.
LintParams struct {
// MigrateLintParams are the parameters for the `migrate lint` command.
MigrateLintParams struct {
Env string
ConfigURL string
DevURL string
Expand Down Expand Up @@ -75,19 +74,17 @@ type (
Vars map[string]string
)

// NewClient returns a new Atlas client.
// The client will try to find the Atlas CLI in the current directory,
// and in the PATH.
func NewClient(dir, name string) (*Client, error) {
path, err := execPath(dir, name)
if err != nil {
return nil, err
}
return NewClientWithDir("", path)
}
type (
// @deprecated use MigrateApplyParams instead.
ApplyParams = MigrateApplyParams
// @deprecated use MigrateStatusParams instead.
StatusParams = MigrateStatusParams
// @deprecated use MigrateLintParams instead.
LintParams = MigrateLintParams
)

// NewClientWD returns a new Atlas client with the given atlas-cli path.
func NewClientWithDir(workingDir, execPath string) (*Client, error) {
func NewClient(workingDir, execPath string) (*Client, error) {
if execPath == "" {
return nil, fmt.Errorf("execPath cannot be empty")
}
Expand All @@ -104,7 +101,25 @@ func NewClientWithDir(workingDir, execPath string) (*Client, error) {
}

// Apply runs the 'migrate apply' command.
func (c *Client) Apply(ctx context.Context, data *ApplyParams) (*ApplyReport, error) {
// @deprecated use MigrateApply instead.
func (c *Client) Apply(ctx context.Context, data *MigrateApplyParams) (*MigrateApply, error) {
return c.MigrateApply(ctx, data)
}

// Lint runs the 'migrate lint' command.
// @deprecated use MigrateLint instead.
func (c *Client) Lint(ctx context.Context, data *MigrateLintParams) (*SummaryReport, error) {
return c.MigrateLint(ctx, data)
}

// Status runs the 'migrate status' command.
// @deprecated use MigrateStatus instead.
func (c *Client) Status(ctx context.Context, data *MigrateStatusParams) (*MigrateStatus, error) {
return c.MigrateStatus(ctx, data)
}

// MigrateApply runs the 'migrate apply' command.
func (c *Client) MigrateApply(ctx context.Context, data *MigrateApplyParams) (*MigrateApply, error) {
args := []string{"migrate", "apply", "--format", "{{ json . }}"}
if data.Env != "" {
args = append(args, "--env", data.Env)
Expand All @@ -131,7 +146,7 @@ func (c *Client) Apply(ctx context.Context, data *ApplyParams) (*ApplyReport, er
args = append(args, strconv.FormatUint(data.Amount, 10))
}
args = append(args, data.Vars.AsArgs()...)
return jsonDecode[ApplyReport](c.runCommand(ctx, args))
return jsonDecode[MigrateApply](c.runCommand(ctx, args))
}

// SchemaApply runs the 'schema apply' command.
Expand Down Expand Up @@ -195,8 +210,8 @@ func (c *Client) SchemaInspect(ctx context.Context, data *SchemaInspectParams) (
return stringVal(c.runCommand(ctx, args))
}

// Lint runs the 'migrate lint' command.
func (c *Client) Lint(ctx context.Context, data *LintParams) (*SummaryReport, error) {
// MigrateLint runs the 'migrate lint' command.
func (c *Client) MigrateLint(ctx context.Context, data *MigrateLintParams) (*SummaryReport, error) {
args := []string{"migrate", "lint", "--format", "{{ json . }}"}
if data.Env != "" {
args = append(args, "--env", data.Env)
Expand All @@ -217,8 +232,8 @@ func (c *Client) Lint(ctx context.Context, data *LintParams) (*SummaryReport, er
return jsonDecode[SummaryReport](c.runCommand(ctx, args))
}

// Status runs the 'migrate status' command.
func (c *Client) Status(ctx context.Context, data *StatusParams) (*StatusReport, error) {
// MigrateStatus runs the 'migrate status' command.
func (c *Client) MigrateStatus(ctx context.Context, data *MigrateStatusParams) (*MigrateStatus, error) {
args := []string{"migrate", "status", "--format", "{{ json . }}"}
if data.Env != "" {
args = append(args, "--env", data.Env)
Expand All @@ -236,7 +251,7 @@ func (c *Client) Status(ctx context.Context, data *StatusParams) (*StatusReport,
args = append(args, "--revisions-schema", data.RevisionsSchema)
}
args = append(args, data.Vars.AsArgs()...)
return jsonDecode[StatusReport](c.runCommand(ctx, args))
return jsonDecode[MigrateStatus](c.runCommand(ctx, args))
}

// runCommand runs the given command and unmarshals the output into the given
Expand Down Expand Up @@ -282,7 +297,7 @@ func (c *Client) runCommand(ctx context.Context, args []string) (io.Reader, erro
}

// LatestVersion returns the latest version of the migrations directory.
func (r StatusReport) LatestVersion() string {
func (r MigrateStatus) LatestVersion() string {
if l := len(r.Available); l > 0 {
return r.Available[l-1].Version
}
Expand All @@ -297,7 +312,7 @@ func (r StatusReport) LatestVersion() string {
//
// If the version is not found, it returns 0 and the second
// return value is false.
func (r StatusReport) Amount(version string) (amount uint64, ok bool) {
func (r MigrateStatus) Amount(version string) (amount uint64, ok bool) {
if version == "" {
amount := uint64(len(r.Pending))
return amount, amount == 0
Expand Down Expand Up @@ -330,16 +345,6 @@ func TempFile(content, ext string) (string, func() error, error) {
}, nil
}

func execPath(dir, name string) (file string, err error) {
file = filepath.Join(dir, name)
if _, err = os.Stat(file); err == nil {
return file, nil
}
// If the binary is not in the current directory,
// try to find it in the PATH.
return exec.LookPath(name)
}

type cliError struct {
summary string
detail string
Expand Down
69 changes: 26 additions & 43 deletions atlasexec/atlas_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package atlasexec
import (
"time"

"ariga.io/atlas/sql/sqlcheck"
"ariga.io/atlas/cmd/atlas/x"
"ariga.io/atlas/sql/sqlclient"
)

Expand All @@ -26,8 +26,8 @@ type (
Error string // Error returned by the database.
}
}
// ApplyReport contains a summary of a migration applying attempt on a database.
ApplyReport struct {
// MigrateApply contains a summary of a migration applying attempt on a database.
MigrateApply struct {
Pending []File `json:"Pending,omitempty"` // Pending migration files
Applied []*AppliedFile `json:"Applied,omitempty"` // Applied files
Current string `json:"Current,omitempty"` // Current migration version
Expand All @@ -38,21 +38,8 @@ type (
// but by Atlas, e.g. when committing or rolling back a transaction.
Error string `json:"Error,omitempty"`
}
// A Revision denotes an applied migration in a deployment. Used to track migration executions state of a database.
Revision struct {
Version string `json:"Version"` // Version of the migration.
Description string `json:"Description"` // Description of this migration.
Type string `json:"Type"` // Type of the migration.
Applied int `json:"Applied"` // Applied amount of statements in the migration.
Total int `json:"Total"` // Total amount of statements in the migration.
ExecutedAt time.Time `json:"ExecutedAt"` // ExecutedAt is the starting point of execution.
ExecutionTime time.Duration `json:"ExecutionTime"` // ExecutionTime of the migration.
Error string `json:"Error,omitempty"` // Error of the migration, if any occurred.
ErrorStmt string `json:"ErrorStmt,omitempty"` // ErrorStmt is the statement that raised Error.
OperatorVersion string `json:"OperatorVersion"` // OperatorVersion that executed this migration.
}
// StatusReport contains a summary of the migration status of a database.
StatusReport struct {
// MigrateStatus contains a summary of the migration status of a database.
MigrateStatus struct {
Available []File `json:"Available,omitempty"` // Available migration files
Pending []File `json:"Pending,omitempty"` // Pending migration files
Applied []*Revision `json:"Applied,omitempty"` // Applied migration files
Expand All @@ -64,33 +51,9 @@ type (
Error string `json:"Error,omitempty"` // Last Error that occurred
SQL string `json:"SQL,omitempty"` // SQL that caused the last Error
}
// FileReport contains a summary of the analysis of a single file.
FileReport struct {
Name string `json:"Name,omitempty"` // Name of the file.
Text string `json:"Text,omitempty"` // Contents of the file.
Reports []sqlcheck.Report `json:"Reports,omitempty"` // List of reports.
Error string `json:"Error,omitempty"` // File specific error.
}
// A SummaryReport contains a summary of the analysis of all files.
// It is used as an input to templates to report the CI results.
SummaryReport struct {
// Steps of the analysis. Added in verbose mode.
Steps []struct {
Name string `json:"Name,omitempty"` // Step name.
Text string `json:"Text,omitempty"` // Step description.
Error string `json:"Error,omitempty"` // Error that cause the execution to halt.
Result any `json:"Result,omitempty"` // Result of the step. For example, a diagnostic.
}

// Schema versions found by the runner.
Schema struct {
Current string `json:"Current,omitempty"` // Current schema.
Desired string `json:"Desired,omitempty"` // Desired schema.
}

// Files reports. Non-empty in case there are findings.
Files []*FileReport `json:"Files,omitempty"`
}
SummaryReport = x.SummaryReport
// StmtError groups a statement with its execution error.
StmtError struct {
Stmt string `json:"Stmt,omitempty"` // SQL statement that failed.
Expand All @@ -116,4 +79,24 @@ type (
// e.g., when committing or rolling back a transaction.
Error string `json:"Error,omitempty"`
}
// A Revision denotes an applied migration in a deployment. Used to track migration executions state of a database.
Revision struct {
Version string `json:"Version"` // Version of the migration.
Description string `json:"Description"` // Description of this migration.
Type string `json:"Type"` // Type of the migration.
Applied int `json:"Applied"` // Applied amount of statements in the migration.
Total int `json:"Total"` // Total amount of statements in the migration.
ExecutedAt time.Time `json:"ExecutedAt"` // ExecutedAt is the starting point of execution.
ExecutionTime time.Duration `json:"ExecutionTime"` // ExecutionTime of the migration.
Error string `json:"Error,omitempty"` // Error of the migration, if any occurred.
ErrorStmt string `json:"ErrorStmt,omitempty"` // ErrorStmt is the statement that raised Error.
OperatorVersion string `json:"OperatorVersion"` // OperatorVersion that executed this migration.
}
)

type (
// @deprecated use MigrateStatus instead
StatusReport = MigrateStatus
// @deprecated use MigrateApply instead
ApplyReport = MigrateApply
)
14 changes: 7 additions & 7 deletions atlasexec/atlas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ func Test_MigrateApply(t *testing.T) {
t.Cleanup(func() {
require.NoError(t, ec.Close())
})
c, err := atlasexec.NewClientWithDir(ec.Path(), "atlas")
c, err := atlasexec.NewClient(ec.Path(), "atlas")
require.NoError(t, err)
got, err := c.Apply(context.Background(), &atlasexec.ApplyParams{
got, err := c.MigrateApply(context.Background(), &atlasexec.MigrateApplyParams{
Env: "test",
})
require.EqualError(t, err, `atlasexec: required flag "url" not set`)
require.Nil(t, got)
// Set the env var and try again
os.Setenv("DB_URL", "sqlite://file?_fk=1&cache=shared&mode=memory")
got, err = c.Apply(context.Background(), &atlasexec.ApplyParams{
got, err = c.MigrateApply(context.Background(), &atlasexec.MigrateApplyParams{
Env: "test",
})
require.NoError(t, err)
Expand All @@ -57,7 +57,7 @@ func Test_MigrateApply(t *testing.T) {
func Test_MigrateStatus(t *testing.T) {
type args struct {
ctx context.Context
data *atlasexec.StatusParams
data *atlasexec.MigrateStatusParams
}
tests := []struct {
name string
Expand All @@ -69,7 +69,7 @@ func Test_MigrateStatus(t *testing.T) {
{
args: args{
ctx: context.Background(),
data: &atlasexec.StatusParams{
data: &atlasexec.MigrateStatusParams{
DirURL: "file://testdata/migrations",
},
},
Expand All @@ -86,7 +86,7 @@ func Test_MigrateStatus(t *testing.T) {
dbpath := sqlitedb(t)
path := fmt.Sprintf("sqlite://%s", dbpath)
tt.args.data.URL = path
got, err := c.Status(tt.args.ctx, tt.args.data)
got, err := c.MigrateStatus(tt.args.ctx, tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("migrateStatus() error = %v, wantErr %v", err, tt.wantErr)
return
Expand All @@ -107,7 +107,7 @@ func Test_SchemaApply(t *testing.T) {
require.NoError(t, err)
defer os.Remove(f.Name())
u := fmt.Sprintf("sqlite://%s?_fk=1", f.Name())
c, err := atlasexec.NewClientWithDir(ce.Path(), "atlas")
c, err := atlasexec.NewClient(ce.Path(), "atlas")
require.NoError(t, err)

s1 := `
Expand Down
21 changes: 21 additions & 0 deletions atlasexec/working_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ type (
Option func(ce *WorkingDir) error
)

// WithAtlasHCLString creates the atlas.hcl file with the given string.
func WithAtlasHCLString(s string) Option {
return WithAtlasHCL(func(w io.Writer) error {
_, err := w.Write([]byte(s))
return err
})
}

// WithAtlasHCLPath creates the atlas.hcl file by copying the file at the given path.
func WithAtlasHCLPath(path string) Option {
return WithAtlasHCL(func(w io.Writer) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(w, f)
return err
})
}

// WithAtlasHCL accept a function to create the atlas.hcl file.
func WithAtlasHCL(fn func(w io.Writer) error) Option {
return func(ce *WorkingDir) error {
Expand Down
Loading