diff --git a/bt/changelog.adoc b/bt/changelog.adoc index 8edef54..6971d1a 100644 --- a/bt/changelog.adoc +++ b/bt/changelog.adoc @@ -1,5 +1,9 @@ = bt +== v0.12.0: New features + +* Invalidate init when the `.terraform.lock.hcl` file has been modified. + == v0.11.0: New features * Add retries to stack components. diff --git a/bt/terraform/build.go b/bt/terraform/build.go index fbfeb5c..e8dfd0f 100644 --- a/bt/terraform/build.go +++ b/bt/terraform/build.go @@ -22,7 +22,7 @@ func buildCMD(ctx context.Context, parent *getoptions.GetOpt) *getoptions.GetOpt opt.Bool("destroy", false) opt.Bool("detailed-exitcode", false) opt.Bool("dry-run", false) - opt.Bool("ignore-cache", false, opt.Description("Ignore the cache and re-run the plan"), opt.Alias("ic")) + opt.Bool("ignore-cache", false, opt.Description("Ignore the cache and re-run the init and plan"), opt.Alias("ic")) opt.Bool("no-checks", false, opt.Description("Do not run pre-apply/post-apply checks"), opt.Alias("nc")) opt.Bool("show", false, opt.Description("Show Terraform plan")) opt.Bool("lock", false, opt.Description("Run 'terraform providers lock' after init")) @@ -61,15 +61,6 @@ func BuildRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error } } - initFn := func(ctx context.Context, opt *getoptions.GetOpt, args []string) error { - // TODO: Add logic to only run when files have been modified - initFile := filepath.Join(dir, ".tf.init") - if _, err := os.Stat(initFile); os.IsNotExist(err) { - return initRun(ctx, opt, args) - } - return nil - } - lockFn := func(ctx context.Context, opt *getoptions.GetOpt, args []string) error { lockFile := filepath.Join(dir, ".tf.lock") if _, err := os.Stat(lockFile); os.IsNotExist(err) { @@ -83,7 +74,7 @@ func BuildRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error } tm := dag.NewTaskMap() - tm.Add("init", initFn) + tm.Add("init", initRun) if lock { tm.Add("lock", lockFn) } diff --git a/bt/terraform/init.go b/bt/terraform/init.go index 109af9e..bc154cf 100644 --- a/bt/terraform/init.go +++ b/bt/terraform/init.go @@ -17,6 +17,7 @@ import ( func initCMD(ctx context.Context, parent *getoptions.GetOpt) *getoptions.GetOpt { opt := parent.NewCommand("init", "") opt.Bool("dry-run", false) + opt.Bool("ignore-cache", false, opt.Description("Ignore the cache and re-run the init"), opt.Alias("ic")) opt.SetCommandFn(initRun) return opt } @@ -25,12 +26,29 @@ func initRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error { dryRun := opt.Value("dry-run").(bool) profile := opt.Value("profile").(string) color := opt.Value("color").(string) + ignoreCache := opt.Value("ignore-cache").(bool) cfg := config.ConfigFromContext(ctx) dir := DirFromContext(ctx) LogConfig(cfg, profile) os.Setenv("CONFIG_ROOT", cfg.ConfigRoot) + lockFile := ".terraform.lock.hcl" + initFile := ".tf.init" + files, modified, err := fsmodtime.Target(os.DirFS(dir), []string{initFile}, []string{lockFile}) + if err != nil { + Logger.Printf("failed to check changes for: '%s'\n", lockFile) + } + if !ignoreCache && !modified { + Logger.Printf("no changes: skipping init\n") + return nil + } + if len(files) > 0 { + Logger.Printf("modified: %v\n", files) + } else { + Logger.Printf("missing target: %v\n", initFile) + } + cmd := []string{cfg.TFProfile[cfg.Profile(profile)].BinaryName, "init"} for _, bvars := range cfg.TFProfile[cfg.Profile(profile)].Init.BackendConfig { @@ -50,7 +68,7 @@ func initRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error { cmd = append(cmd, args...) dataDir := fmt.Sprintf("TF_DATA_DIR=%s", getDataDir(cfg.Config.DefaultTerraformProfile, cfg.Profile(profile))) Logger.Printf("export %s\n", dataDir) - err := run.CMDCtx(ctx, cmd...).Stdin().Log().Env(dataDir).Dir(dir).DryRun(dryRun).Run() + err = run.CMDCtx(ctx, cmd...).Stdin().Log().Env(dataDir).Dir(dir).DryRun(dryRun).Run() if err != nil { os.Remove(filepath.Join(dir, ".tf.lock")) return fmt.Errorf("failed to run: %w", err) @@ -61,13 +79,13 @@ func initRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error { } os.Remove(filepath.Join(dir, ".tf.lock")) - initFile := filepath.Join(dir, ".tf.init") - fh, err := os.Create(initFile) + initFilePath := filepath.Join(dir, initFile) + fh, err := os.Create(initFilePath) if err != nil { return fmt.Errorf("failed to create file: %w", err) } fh.Close() - Logger.Printf("Create %s\n", initFile) + Logger.Printf("Create %s\n", initFilePath) return nil } diff --git a/bt/terraform/init_test.go b/bt/terraform/init_test.go index 83bb14f..eb1a4e7 100644 --- a/bt/terraform/init_test.go +++ b/bt/terraform/init_test.go @@ -45,6 +45,7 @@ func TestInit(t *testing.T) { ctx = run.ContextWithRunInfo(ctx, mock) opt := getoptions.New() opt.Bool("dry-run", false) + opt.Bool("ignore-cache", false) opt.String("profile", "default") opt.String("color", "auto") err := initRun(ctx, opt, []string{}) @@ -82,6 +83,7 @@ func TestInit(t *testing.T) { ctx = run.ContextWithRunInfo(ctx, mock) opt := getoptions.New() opt.Bool("dry-run", false) + opt.Bool("ignore-cache", false) opt.String("profile", "default") opt.String("color", "auto") err := initRun(ctx, opt, []string{}) @@ -121,6 +123,7 @@ func TestInit(t *testing.T) { ctx = run.ContextWithRunInfo(ctx, mock) opt := getoptions.New() opt.Bool("dry-run", false) + opt.Bool("ignore-cache", false) opt.String("profile", "dev") opt.String("color", "auto") err := initRun(ctx, opt, []string{}) @@ -159,6 +162,7 @@ func TestInit(t *testing.T) { ctx = run.ContextWithRunInfo(ctx, mock) opt := getoptions.New() opt.Bool("dry-run", false) + opt.Bool("ignore-cache", false) opt.String("profile", "prod") opt.String("color", "auto") err := initRun(ctx, opt, []string{})