Repository: ariga/atlas
Pull request #866 (2022-06-15 08:07:44)

sql/migrate: files containing `atlas:sum none` in their first line wi…
…ll now be excluded from atlas sum file computation


Patch:
diff --git a/sql/migrate/migrate.go b/sql/migrate/migrate.go
index d09c2202..cb6e15d3 100644
--- a/sql/migrate/migrate.go
+++ b/sql/migrate/migrate.go
@@ -14,8 +14,10 @@ import (
 	"fmt"
 	"io"
 	"io/fs"
+	"io/ioutil"
 	"os"
 	"path/filepath"
+	"regexp"
 	"sort"
 	"strings"
 	"text/template"
@@ -842,13 +844,21 @@ type templateFile struct {
 // Name implements the File interface.
 func (f *templateFile) Name() string { return f.n }
 
-// Filename of the migration directory integrity sum file.
-const hashFile = "atlas.sum"
+const (
+	// Filename of the migration directory integrity sum file.
+	hashFile = "atlas.sum"
+	// Directive used it a file should be excluded by the sum computation.
+	directiveNone = "ignore"
+)
+
+// Determine if an "atlas:sum" directive is used on the file.
+var reSumDirective = regexp.MustCompile(`atlas:sum ([a-zA-Z-]*)`)
 
 // HashFile represents the integrity sum file of the migration dir.
 type HashFile []struct{ N, H string }
 
 // HashSum reads the whole dir, sorts the files by name and creates a HashSum from its contents.
+// If a files first line matches the above regex, it will be excluded from hash creation.
 func HashSum(dir Dir) (HashFile, error) {
 	var (
 		hs HashFile
@@ -871,7 +881,18 @@ func HashSum(dir Dir) (HashFile, error) {
 			if _, err := h.Write([]byte(path)); err != nil {
 				return err
 			}
-			if _, err = io.Copy(h, f); err != nil {
+			c, err := ioutil.ReadAll(f)
+			if err != nil {
+				return err
+			}
+			// Check if this file contains an "atlas:sum" directive and if so, act to it.
+			if drctv := reSumDirective.FindSubmatch(bytes.SplitN(c, []byte("\n"), 2)[0]); len(drctv) > 0 {
+				switch string(drctv[1]) {
+				case directiveNone:
+					return nil
+				}
+			}
+			if _, err := h.Write(c); err != nil {
 				return err
 			}
 			hs = append(hs, struct{ N, H string }{path, base64.StdEncoding.EncodeToString(h.Sum(nil))})


Test patch:
diff --git a/sql/migrate/migrate_test.go b/sql/migrate/migrate_test.go
index d7d5ab36..5f9ca7f8 100644
--- a/sql/migrate/migrate_test.go
+++ b/sql/migrate/migrate_test.go
@@ -90,6 +90,7 @@ func TestPlanner_Plan(t *testing.T) {
 }
 
 func TestHashSum(t *testing.T) {
+	// Sum file gets created.
 	p := t.TempDir()
 	d, err := migrate.NewLocalDir(p)
 	require.NoError(t, err)
@@ -102,6 +103,7 @@ func TestHashSum(t *testing.T) {
 	requireFileEqual(t, d, v+"_plan.sql", "cmd;\n")
 	require.FileExists(t, filepath.Join(p, "atlas.sum"))
 
+	// Disable sum.
 	p = t.TempDir()
 	d, err = migrate.NewLocalDir(p)
 	require.NoError(t, err)
@@ -110,6 +112,24 @@ func TestHashSum(t *testing.T) {
 	require.NoError(t, pl.WritePlan(plan))
 	require.Equal(t, 1, countFiles(t, d))
 	requireFileEqual(t, d, v+"_plan.sql", "cmd;\n")
+
+	// Files with directive in first line get ignored.
+	p = t.TempDir()
+	d, err = migrate.NewLocalDir(p)
+	require.NoError(t, err)
+	pl = migrate.NewPlanner(nil, d)
+	require.NotNil(t, pl)
+	require.NoError(t, os.WriteFile(filepath.Join(p, "include"), []byte("//atlas:sum\nfoo"), 0600))
+	require.NoError(t, os.WriteFile(filepath.Join(p, "exclude_1"), []byte("//atlas:sum ignore bar"), 0600))
+	require.NoError(t, os.WriteFile(filepath.Join(p, "exclude_2"), []byte("//atlas:sum ignore\nbar"), 0600))
+	require.NoError(t, pl.WritePlan(plan))
+	require.Equal(t, 5, countFiles(t, d))
+	requireFileEqual(t, d, v+"_plan.sql", "cmd;\n")
+	c, err := os.ReadFile(filepath.Join(p, "atlas.sum"))
+	require.NoError(t, err)
+	require.Contains(t, string(c), "include")
+	require.NotContains(t, string(c), "exclude_2")
+	require.NotContains(t, string(c), "exclude_2")
 }
 
 //go:embed testdata/migrate/atlas.sum