diff --git a/atlasexec/atlas_migrate.go b/atlasexec/atlas_migrate.go index fa2dc034baa..b8243a7bc38 100644 --- a/atlasexec/atlas_migrate.go +++ b/atlasexec/atlas_migrate.go @@ -165,6 +165,27 @@ type ( URL string RevisionsSchema string } + // MigrateLsParams are the parameters for the `migrate ls` command. + MigrateLsParams struct { + ConfigURL string + Env string + Vars VarArgs + + DirURL string + Short bool // -s: print only migration version (omit description and .sql suffix) + Latest bool // -l: print only the latest migration file + } + // MigrateSetParams are the parameters for the `migrate set` command. + MigrateSetParams struct { + ConfigURL string + Env string + Vars VarArgs + + DirURL string + URL string + RevisionsSchema string + Version string + } // MigrateDiffParams are the parameters for the `migrate diff` command. MigrateDiffParams struct { ConfigURL string @@ -419,6 +440,58 @@ func (c *Client) MigrateStatus(ctx context.Context, params *MigrateStatusParams) return firstResult(jsonDecode[MigrateStatus](c.runCommand(ctx, args))) } +// MigrateLs runs the 'migrate ls' command and returns the listed migration file names (or versions when Short is true), one per line. +func (c *Client) MigrateLs(ctx context.Context, params *MigrateLsParams) (string, error) { + args := []string{"migrate", "ls"} + if params.ConfigURL != "" { + args = append(args, "--config", params.ConfigURL) + } + if params.Env != "" { + args = append(args, "--env", params.Env) + } + if params.DirURL != "" { + args = append(args, "--dir", params.DirURL) + } + if params.Vars != nil { + args = append(args, params.Vars.AsArgs()...) + } + if params.Short { + args = append(args, "--short") + } + if params.Latest { + args = append(args, "--latest") + } + return stringVal(c.runCommand(ctx, args)) +} + +// MigrateSet runs the 'migrate set' command. +func (c *Client) MigrateSet(ctx context.Context, params *MigrateSetParams) error { + args := []string{"migrate", "set"} + if params.Env != "" { + args = append(args, "--env", params.Env) + } + if params.ConfigURL != "" { + args = append(args, "--config", params.ConfigURL) + } + if params.URL != "" { + args = append(args, "--url", params.URL) + } + if params.DirURL != "" { + args = append(args, "--dir", params.DirURL) + } + if params.RevisionsSchema != "" { + args = append(args, "--revisions-schema", params.RevisionsSchema) + } + if params.Vars != nil { + args = append(args, params.Vars.AsArgs()...) + } + if params.Version != "" { + args = append(args, params.Version) + } + _, err := c.runCommand(ctx, args) + return err +} + // MigrateDiff runs the 'migrate diff --dry-run' command and returns the generated migration files without changing the filesystem. // Requires atlas CLI to be logged in to the cloud. func (c *Client) MigrateDiff(ctx context.Context, params *MigrateDiffParams) (*MigrateDiff, error) { @@ -526,7 +599,7 @@ func (c *Client) MigrateRebase(ctx context.Context, params *MigrateRebaseParams) if params.DirURL != "" { args = append(args, "--dir", params.DirURL) } - args = append(args, strings.Join(params.Files, " ")) + args = append(args, params.Files...) _, err := c.runCommand(ctx, args) return err } @@ -734,10 +807,16 @@ func newMigrateApplyError(r []*MigrateApply, stderr string) error { // Error implements the error interface. func (e *MigrateApplyError) Error() string { + var errs []string + for _, r := range e.Result { + if r.Error != "" { + errs = append(errs, r.Error) + } + } if e.Stderr != "" { - return e.Stderr + errs = append(errs, e.Stderr) } - return last(e.Result).Error + return strings.Join(errs, "\n") } func plural(n int) (s string) { diff --git a/atlasexec/atlas_migrate_example_test.go b/atlasexec/atlas_migrate_example_test.go index 43e375dc98a..f0dc97fa945 100644 --- a/atlasexec/atlas_migrate_example_test.go +++ b/atlasexec/atlas_migrate_example_test.go @@ -41,3 +41,33 @@ func ExampleClient_MigrateApply() { } fmt.Printf("Applied %d migrations\n", len(res.Applied)) } + +func ExampleClient_MigrateSet() { + // Define the execution context, supplying a migration directory + // and potentially an `atlas.hcl` configuration file using `atlasexec.WithHCL`. + workdir, err := atlasexec.NewWorkingDir( + atlasexec.WithMigrations( + os.DirFS("./migrations"), + ), + ) + if err != nil { + log.Fatalf("failed to load working directory: %v", err) + } + // atlasexec works on a temporary directory, so we need to close it + defer workdir.Close() + + // Initialize the client. + client, err := atlasexec.NewClient(workdir.Path(), "atlas") + if err != nil { + log.Fatalf("failed to initialize client: %v", err) + } + // Run `atlas migrate set` to mark migrations as applied up to version "3". + err = client.MigrateSet(context.Background(), &atlasexec.MigrateSetParams{ + URL: "sqlite:///tmp/demo.db?_fk=1&cache=shared", + Version: "3", + }) + if err != nil { + log.Fatalf("failed to set migrations: %v", err) + } + fmt.Println("Migration version set successfully") +} diff --git a/atlasexec/atlas_migrate_test.go b/atlasexec/atlas_migrate_test.go index 3d103714436..47985d67af5 100644 --- a/atlasexec/atlas_migrate_test.go +++ b/atlasexec/atlas_migrate_test.go @@ -129,6 +129,183 @@ func TestMigrate_Apply(t *testing.T) { } } +func TestMigrate_Ls(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + c, err := atlasexec.NewClient(t.TempDir(), filepath.Join(wd, "./mock-atlas.sh")) + require.NoError(t, err) + + for _, tt := range []struct { + name string + params *atlasexec.MigrateLsParams + args string + stdout string + }{ + { + name: "no params", + params: &atlasexec.MigrateLsParams{}, + args: "migrate ls", + stdout: "\n", + }, + { + name: "with dir", + params: &atlasexec.MigrateLsParams{ + DirURL: "file://migrations", + }, + args: "migrate ls --dir file://migrations", + stdout: "\n", + }, + { + name: "with short", + params: &atlasexec.MigrateLsParams{ + Short: true, + }, + args: "migrate ls --short", + stdout: "\n", + }, + { + name: "with latest", + params: &atlasexec.MigrateLsParams{ + Latest: true, + }, + args: "migrate ls --latest", + stdout: "\n", + }, + { + name: "with short and latest", + params: &atlasexec.MigrateLsParams{ + DirURL: "file://migrations", + Short: true, + Latest: true, + }, + args: "migrate ls --dir file://migrations --short --latest", + stdout: "20230727105615\n", + }, + { + name: "with config and env", + params: &atlasexec.MigrateLsParams{ + ConfigURL: "file://atlas.hcl", + Env: "dev", + }, + args: "migrate ls --config file://atlas.hcl --env dev", + stdout: "\n", + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Setenv("TEST_ARGS", tt.args) + t.Setenv("TEST_STDOUT", tt.stdout) + got, err := c.MigrateLs(context.Background(), tt.params) + require.NoError(t, err) + require.Equal(t, tt.stdout, got) + }) + } +} + +func TestMigrate_Ls_Integration(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + c, err := atlasexec.NewClient(wd, "atlas") + require.NoError(t, err) + + dirURL := "file://testdata/migrations" + out, err := c.MigrateLs(context.Background(), &atlasexec.MigrateLsParams{DirURL: dirURL}) + if err != nil { + if strings.Contains(err.Error(), "unknown") || strings.Contains(err.Error(), "unknown command") { + t.Skip("atlas binary does not support 'migrate ls' (e.g. OSS build)") + } + require.NoError(t, err) + } + lines := strings.Split(strings.TrimSpace(out), "\n") + require.GreaterOrEqual(t, len(lines), 2) + require.Contains(t, out, "20230727105553_init.sql") + require.Contains(t, out, "20230727105615_t2.sql") + + outShort, err := c.MigrateLs(context.Background(), &atlasexec.MigrateLsParams{DirURL: dirURL, Short: true}) + if err != nil && strings.Contains(err.Error(), "unknown flag") { + t.Skip("atlas binary does not support --short/--latest (use enterprise or newer build)") + } + require.NoError(t, err) + require.Contains(t, outShort, "20230727105553") + require.Contains(t, outShort, "20230727105615") + require.NotContains(t, outShort, ".sql") + + outLatest, err := c.MigrateLs(context.Background(), &atlasexec.MigrateLsParams{DirURL: dirURL, Latest: true}) + require.NoError(t, err) + require.Equal(t, 1, len(strings.Split(strings.TrimSpace(outLatest), "\n"))) + require.Contains(t, outLatest, "20230926085734_destructive-change.sql") +} + +func TestMigrate_Set(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + c, err := atlasexec.NewClient(t.TempDir(), filepath.Join(wd, "./mock-atlas.sh")) + require.NoError(t, err) + + for _, tt := range []struct { + name string + params *atlasexec.MigrateSetParams + args string + }{ + { + name: "no params", + params: &atlasexec.MigrateSetParams{}, + args: "migrate set", + }, + { + name: "with env", + params: &atlasexec.MigrateSetParams{ + Env: "test", + }, + args: "migrate set --env test", + }, + { + name: "with url", + params: &atlasexec.MigrateSetParams{ + URL: "sqlite://file?_fk=1&cache=shared&mode=memory", + }, + args: "migrate set --url sqlite://file?_fk=1&cache=shared&mode=memory", + }, + { + name: "with dir", + params: &atlasexec.MigrateSetParams{ + DirURL: "file://migrations", + }, + args: "migrate set --dir file://migrations", + }, + { + name: "with revisions-schema", + params: &atlasexec.MigrateSetParams{ + RevisionsSchema: "my_revisions", + }, + args: "migrate set --revisions-schema my_revisions", + }, + { + name: "with version", + params: &atlasexec.MigrateSetParams{ + Version: "3", + }, + args: "migrate set 3", + }, + { + name: "with all params", + params: &atlasexec.MigrateSetParams{ + URL: "sqlite://file?_fk=1&cache=shared&mode=memory", + DirURL: "file://migrations", + RevisionsSchema: "my_revisions", + Version: "1.2.4", + }, + args: "migrate set --url sqlite://file?_fk=1&cache=shared&mode=memory --dir file://migrations --revisions-schema my_revisions 1.2.4", + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Setenv("TEST_ARGS", tt.args) + t.Setenv("TEST_STDOUT", "ok") + err := c.MigrateSet(context.Background(), tt.params) + require.NoError(t, err) + }) + } +} + func TestMigrate_Down(t *testing.T) { wd, err := os.Getwd() require.NoError(t, err) @@ -317,6 +494,73 @@ func TestAtlasMigrate_ApplyBroken(t *testing.T) { }, report.Result[0].Applied[0].Error) } +func TestMigrateApplyError_Error(t *testing.T) { + t.Run("single result error only", func(t *testing.T) { + e := &atlasexec.MigrateApplyError{ + Result: []*atlasexec.MigrateApply{ + {Error: "sql/migrate: execution failed"}, + }, + } + require.Equal(t, "sql/migrate: execution failed", e.Error()) + }) + + t.Run("stderr only", func(t *testing.T) { + e := &atlasexec.MigrateApplyError{ + Stderr: "Error: unable to acquire lock", + } + require.Equal(t, "Error: unable to acquire lock", e.Error()) + }) + + t.Run("single result error and stderr", func(t *testing.T) { + e := &atlasexec.MigrateApplyError{ + Result: []*atlasexec.MigrateApply{ + {Error: "sql/migrate: execution failed"}, + }, + Stderr: "Error: unable to acquire lock", + } + require.Equal(t, "sql/migrate: execution failed\nError: unable to acquire lock", e.Error()) + }) + + t.Run("multiple result errors and stderr", func(t *testing.T) { + e := &atlasexec.MigrateApplyError{ + Result: []*atlasexec.MigrateApply{ + {Error: "error on target 1"}, + {Error: "error on target 2"}, + }, + Stderr: "Error: unable to acquire lock", + } + require.Equal(t, "error on target 1\nerror on target 2\nError: unable to acquire lock", e.Error()) + }) + + t.Run("multiple results with some having no error", func(t *testing.T) { + e := &atlasexec.MigrateApplyError{ + Result: []*atlasexec.MigrateApply{ + {Error: ""}, + {Error: "error on target 2"}, + {Error: ""}, + }, + Stderr: "Error: unable to acquire lock", + } + require.Equal(t, "error on target 2\nError: unable to acquire lock", e.Error()) + }) + + t.Run("no errors at all", func(t *testing.T) { + e := &atlasexec.MigrateApplyError{ + Result: []*atlasexec.MigrateApply{ + {Error: ""}, + }, + } + require.Equal(t, "", e.Error()) + }) + + t.Run("nil result with stderr", func(t *testing.T) { + e := &atlasexec.MigrateApplyError{ + Stderr: "Error: connection refused", + } + require.Equal(t, "Error: connection refused", e.Error()) + }) +} + func TestAtlasMigrate_Apply(t *testing.T) { ec, err := atlasexec.NewWorkingDir( atlasexec.WithMigrations(os.DirFS(filepath.Join("testdata", "migrations"))), diff --git a/atlasexec/atlas_models.go b/atlasexec/atlas_models.go index d9b3d6c54e0..bc7e313137a 100644 --- a/atlasexec/atlas_models.go +++ b/atlasexec/atlas_models.go @@ -6,16 +6,11 @@ package atlasexec import ( "errors" - "fmt" - "strings" "time" "ariga.io/atlas/sql/schema" "ariga.io/atlas/sql/sqlcheck" "ariga.io/atlas/sql/sqlclient" - - "github.com/prometheus/common/expfmt" - "github.com/prometheus/common/model" ) type ( @@ -174,41 +169,3 @@ func (r *SummaryReport) Errors() []error { } return errs } - -// ParsePrometheusMetrics parses Prometheus format metrics and extract table size metrics -// data is expected to be of this format: -// atlas_table_size_bytes{schema="public",table="users"} 123456 -// atlas_table_size_bytes{schema="public",table="orders"} 789012 -func ParsePrometheusMetrics(data string) ([]TableSizeMetric, error) { - var metrics []TableSizeMetric - parser := expfmt.NewTextParser(model.LegacyValidation) - metricFamilies, err := parser.TextToMetricFamilies(strings.NewReader(data)) - if err != nil { - return nil, fmt.Errorf("failed to parse prometheus metrics: %w", err) - } - for _, mf := range metricFamilies { - if mf.GetName() == MetricTableSizeBytes { - for _, metric := range mf.GetMetric() { - var schema, table string - for _, label := range metric.GetLabel() { - switch label.GetName() { - case "schema": - schema = label.GetValue() - case "table": - table = label.GetValue() - } - } - var value float64 - if gauge := metric.GetGauge(); gauge != nil { - value = gauge.GetValue() - } - metrics = append(metrics, TableSizeMetric{ - Schema: schema, - Table: table, - Value: value, - }) - } - } - } - return metrics, nil -} diff --git a/atlasexec/atlas_schema.go b/atlasexec/atlas_schema.go index 4f2ee6cc128..f9260a00a1e 100644 --- a/atlasexec/atlas_schema.go +++ b/atlasexec/atlas_schema.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "strconv" + "strings" "time" "ariga.io/atlas/sql/migrate" @@ -267,7 +268,6 @@ type ( Vars VarArgs URL string - Format string Exclude []string Include []string Schema []string @@ -804,8 +804,8 @@ func (c *Client) SchemaLint(ctx context.Context, params *SchemaLintParams) (*Sch } // SchemaStatsInspect runs the 'schema stats inspect' command. -func (c *Client) SchemaStatsInspect(ctx context.Context, params *SchemaStatsInspectParams) ([]TableSizeMetric, error) { - args := []string{"schema", "stats", "inspect"} +func (c *Client) SchemaStatsInspect(ctx context.Context, params *SchemaStatsInspectParams) (string, error) { + args := []string{"schema", "stats", "inspect", "--format", "{{ json .Realm }}"} if params.Env != "" { args = append(args, "--env", params.Env) } @@ -815,9 +815,6 @@ func (c *Client) SchemaStatsInspect(ctx context.Context, params *SchemaStatsInsp if params.URL != "" { args = append(args, "--url", params.URL) } - if params.Format != "" { - args = append(args, "--format", params.Format) - } if len(params.Schema) > 0 { args = append(args, "--schema", listString(params.Schema)) } @@ -830,11 +827,7 @@ func (c *Client) SchemaStatsInspect(ctx context.Context, params *SchemaStatsInsp if params.Vars != nil { args = append(args, params.Vars.AsArgs()...) } - output, err := stringVal(c.runCommand(ctx, args)) - if err != nil { - return nil, err - } - return ParsePrometheusMetrics(output) + return stringVal(c.runCommand(ctx, args)) } // AsArgs returns the parameters as arguments. @@ -875,8 +868,14 @@ func newSchemaApplyError(r []*SchemaApply, stderr string) error { // Error implements the error interface. func (e *SchemaApplyError) Error() string { + var errs []string + for _, r := range e.Result { + if r.Error != "" { + errs = append(errs, r.Error) + } + } if e.Stderr != "" { - return e.Stderr + errs = append(errs, e.Stderr) } - return last(e.Result).Error + return strings.Join(errs, "\n") } diff --git a/atlasexec/atlas_schema_test.go b/atlasexec/atlas_schema_test.go index 30e0ff16d92..1c334ef541c 100644 --- a/atlasexec/atlas_schema_test.go +++ b/atlasexec/atlas_schema_test.go @@ -803,6 +803,73 @@ func TestSchema_ApplyEnvs(t *testing.T) { require.Equal(t, "sqlite://local-bu.db", err2.Result[2].URL.String()) } +func TestSchemaApplyError_Error(t *testing.T) { + t.Run("single result error only", func(t *testing.T) { + e := &atlasexec.SchemaApplyError{ + Result: []*atlasexec.SchemaApply{ + {Error: "schema apply failed"}, + }, + } + require.Equal(t, "schema apply failed", e.Error()) + }) + + t.Run("stderr only", func(t *testing.T) { + e := &atlasexec.SchemaApplyError{ + Stderr: "Error: unable to acquire lock", + } + require.Equal(t, "Error: unable to acquire lock", e.Error()) + }) + + t.Run("single result error and stderr", func(t *testing.T) { + e := &atlasexec.SchemaApplyError{ + Result: []*atlasexec.SchemaApply{ + {Error: "schema apply failed"}, + }, + Stderr: "Error: unable to acquire lock", + } + require.Equal(t, "schema apply failed\nError: unable to acquire lock", e.Error()) + }) + + t.Run("multiple result errors and stderr", func(t *testing.T) { + e := &atlasexec.SchemaApplyError{ + Result: []*atlasexec.SchemaApply{ + {Error: "error on target 1"}, + {Error: "error on target 2"}, + }, + Stderr: "Error: unable to acquire lock", + } + require.Equal(t, "error on target 1\nerror on target 2\nError: unable to acquire lock", e.Error()) + }) + + t.Run("multiple results with some having no error", func(t *testing.T) { + e := &atlasexec.SchemaApplyError{ + Result: []*atlasexec.SchemaApply{ + {Error: ""}, + {Error: "error on target 2"}, + {Error: ""}, + }, + Stderr: "Error: unable to acquire lock", + } + require.Equal(t, "error on target 2\nError: unable to acquire lock", e.Error()) + }) + + t.Run("no errors at all", func(t *testing.T) { + e := &atlasexec.SchemaApplyError{ + Result: []*atlasexec.SchemaApply{ + {Error: ""}, + }, + } + require.Equal(t, "", e.Error()) + }) + + t.Run("nil result with stderr", func(t *testing.T) { + e := &atlasexec.SchemaApplyError{ + Stderr: "Error: connection refused", + } + require.Equal(t, "Error: connection refused", e.Error()) + }) +} + func TestAtlasSchema_Lint(t *testing.T) { t.Run("with broken config", func(t *testing.T) { c, err := atlasexec.NewClient(".", "atlas") @@ -914,35 +981,3 @@ func TestSchema_Lint(t *testing.T) { }) } } - -func TestSchema_StatsInspect(t *testing.T) { - wd, err := os.Getwd() - require.NoError(t, err) - c, err := atlasexec.NewClient(t.TempDir(), filepath.Join(wd, "./mock-atlas.sh")) - require.NoError(t, err) - - // Test case with Prometheus metrics output - prometheusOutput := `# HELP atlas_table_size_bytes Size of the table in bytes. -# TYPE atlas_table_size_bytes gauge -atlas_table_size_bytes{schema="test",table="test"} 16384.0 -atlas_table_size_bytes{schema="test",table="users"} 6.832128e+06 -` - t.Run("basic stats with prometheus metrics", func(t *testing.T) { - params := &atlasexec.SchemaStatsInspectParams{ - URL: "sqlite://test.db", - } - t.Setenv("TEST_ARGS", "schema stats inspect --url sqlite://test.db") - t.Setenv("TEST_STDOUT", prometheusOutput) - result, err := c.SchemaStatsInspect(context.Background(), params) - - require.NoError(t, err) - require.Len(t, result, 2) - // Verify metrics - require.Equal(t, "test", result[0].Schema) - require.Equal(t, "test", result[0].Table) - require.Equal(t, 16384.0, result[0].Value) - require.Equal(t, "test", result[1].Schema) - require.Equal(t, "users", result[1].Table) - require.Equal(t, 6832128.0, result[1].Value) - }) -} diff --git a/cmd/atlas/go.mod b/cmd/atlas/go.mod index 4e97ab02aee..a33edcb74f4 100644 --- a/cmd/atlas/go.mod +++ b/cmd/atlas/go.mod @@ -1,6 +1,6 @@ module ariga.io/atlas/cmd/atlas -go 1.24.9 +go 1.24.13 replace ariga.io/atlas => ../.. diff --git a/cmd/atlas/main_oss.go b/cmd/atlas/main_oss.go index 7ad1f543516..f31f432dde2 100644 --- a/cmd/atlas/main_oss.go +++ b/cmd/atlas/main_oss.go @@ -8,6 +8,13 @@ package main import ( "context" + "fmt" + "os" + "runtime" + "time" + + "ariga.io/atlas/cmd/atlas/internal/cmdlog" + "ariga.io/atlas/cmd/atlas/internal/cmdstate" ) func extendContext(ctx context.Context) (context.Context, error) { @@ -20,5 +27,32 @@ func vercheckEndpoint(context.Context) string { // initialize is a no-op for the OSS version. func initialize(ctx context.Context) (context.Context, func(error)) { - return ctx, func(error) {} + return ctx, func(err error) { + if err == nil { + return + } + const errorsFileName = "community_error.json" + type prompt struct { + LastSuggested time.Time `json:"last_suggested"` + } + state := &cmdstate.File[prompt]{Name: errorsFileName} + prev, err := state.Read() + if err != nil || time.Since(prev.LastSuggested) < 24*time.Hour { + return + } + release := "curl -sSf https://atlasgo.sh | sh" + if runtime.GOOS == "windows" { + release = "https://release.ariga.io/atlas/atlas-windows-amd64-latest.exe" + } + if err := cmdlog.WarnOnce(os.Stderr, cmdlog.ColorCyan(fmt.Sprintf(`You're running the community build of Atlas, which may differ from the official version. +If this error persists, try installing the official version as a troubleshooting step: + + %s + +More installation options: https://atlasgo.io/docs#installation +`, release))); err == nil { + prev.LastSuggested = time.Now() + _ = state.Write(prev) + } + } } diff --git a/go.mod b/go.mod index 1a2dbda5406..39d34b7e5f3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module ariga.io/atlas -go 1.24.9 +go 1.24.13 require ( github.com/DATA-DOG/go-sqlmock v1.5.0 @@ -8,7 +8,6 @@ require ( github.com/go-openapi/inflect v0.19.0 github.com/hashicorp/hcl/v2 v2.13.0 github.com/mattn/go-sqlite3 v1.14.28 - github.com/prometheus/common v0.66.1 github.com/stretchr/testify v1.11.1 github.com/ydb-platform/ydb-go-sdk/v3 v3.125.4 github.com/zclconf/go-cty v1.14.4 @@ -26,14 +25,12 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect + github.com/kr/pretty v0.2.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.6.2 // indirect github.com/ydb-platform/ydb-go-genproto v0.0.0-20251222105147-0bf751469a4a // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.39.0 // indirect diff --git a/go.sum b/go.sum index 355bb38d781..aee86bca537 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,10 @@ github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgC github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -86,15 +88,9 @@ github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEu github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= -github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/rekby/fixenv v0.6.1 h1:jUFiSPpajT4WY2cYuc++7Y1zWrnCxnovGCIX72PZniM= github.com/rekby/fixenv v0.6.1/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -128,8 +124,6 @@ go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42s go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/internal/integration/go.mod b/internal/integration/go.mod index 362e1c89f58..7e8fba15c08 100644 --- a/internal/integration/go.mod +++ b/internal/integration/go.mod @@ -1,6 +1,6 @@ module ariga.io/atlas/internal/integration -go 1.24.9 +go 1.24.13 replace ariga.io/atlas => ../../ diff --git a/sql/migrate/dir.go b/sql/migrate/dir.go index 715c29a8a1f..5e341f6370d 100644 --- a/sql/migrate/dir.go +++ b/sql/migrate/dir.go @@ -274,7 +274,7 @@ const ( directivePrefixSQL = "-- " ) -var reDirective = regexp.MustCompile(`^([ -~]*)atlas:(\w+)(?: +([ -~]*))*`) +var reDirective = regexp.MustCompile(`^([ -~]*)atlas:(\w+)(?: +(.+))*`) // directive searches in the content a line that matches a directive // with the given prefix and name. For example: diff --git a/sql/migrate/dir_test.go b/sql/migrate/dir_test.go index 79a529b8baf..8b6aae69cc1 100644 --- a/sql/migrate/dir_test.go +++ b/sql/migrate/dir_test.go @@ -430,11 +430,11 @@ alter table users drop column id; -- atlas:import -- atlas:import bar baz -- atlas:import qux +-- atlas:import files_📄 alter table users drop column id; `)) - require.Equal(t, []string{"foo", "", "bar baz", "qux"}, f.Directive("import")) - + require.Equal(t, []string{"foo", "", "bar baz", "qux", "files_📄"}, f.Directive("import")) f = migrate.NewLocalFile("1.sql", []byte("-- atlas:import foo\n")) require.Equal(t, []string{"foo"}, f.Directive("import")) }