66"encoding/json"
77"fmt"
88"io"
9+ "slices"
910"strings"
1011
1112"cdr.dev/slog"
@@ -1053,7 +1054,7 @@ func markActive(ctx context.Context, client *codersdk.Client, templateID uuid.UU
10531054ID :versionID ,
10541055})
10551056if err != nil {
1056- return fmt .Errorf ("Failed to update active template version: %s" ,err )
1057+ return fmt .Errorf ("failed to update active template version: %s" ,err )
10571058}
10581059tflog .Info (ctx ,"marked template version as active" )
10591060return nil
@@ -1231,8 +1232,9 @@ type LastVersionsByHash = map[string][]PreviousTemplateVersion
12311232var LastVersionsKey = "last_versions"
12321233
12331234type PreviousTemplateVersion struct {
1234- ID uuid.UUID `json:"id"`
1235- Name string `json:"name"`
1235+ ID uuid.UUID `json:"id"`
1236+ Name string `json:"name"`
1237+ TFVars map [string ]string
12361238}
12371239
12381240type privateState interface {
@@ -1244,18 +1246,24 @@ func (v Versions) setPrivateState(ctx context.Context, ps privateState) (diags d
12441246lv := make (LastVersionsByHash )
12451247for _ ,version := range v {
12461248vbh ,ok := lv [version .DirectoryHash .ValueString ()]
1249+ tfVars := make (map [string ]string ,len (version .TerraformVariables ))
1250+ for _ ,tfVar := range version .TerraformVariables {
1251+ tfVars [tfVar .Name .ValueString ()]= tfVar .Value .ValueString ()
1252+ }
12471253// Store the IDs and names of all versions with the same directory hash,
12481254// in the order they appear
12491255if ok {
12501256lv [version .DirectoryHash .ValueString ()]= append (vbh ,PreviousTemplateVersion {
1251- ID :version .ID .ValueUUID (),
1252- Name :version .Name .ValueString (),
1257+ ID :version .ID .ValueUUID (),
1258+ Name :version .Name .ValueString (),
1259+ TFVars :tfVars ,
12531260})
12541261}else {
12551262lv [version .DirectoryHash .ValueString ()]= []PreviousTemplateVersion {
12561263{
1257- ID :version .ID .ValueUUID (),
1258- Name :version .Name .ValueString (),
1264+ ID :version .ID .ValueUUID (),
1265+ Name :version .Name .ValueString (),
1266+ TFVars :tfVars ,
12591267},
12601268}
12611269}
@@ -1269,6 +1277,13 @@ func (v Versions) setPrivateState(ctx context.Context, ps privateState) (diags d
12691277}
12701278
12711279func (planVersions Versions )reconcileVersionIDs (lv LastVersionsByHash ,configVersions Versions ) {
1280+ // We remove versions that we've matched from `lv`, so make a copy for
1281+ // resolving tfvar changes at the end.
1282+ fullLv := make (LastVersionsByHash )
1283+ for k ,v := range lv {
1284+ fullLv [k ]= slices .Clone (v )
1285+ }
1286+
12721287for i := range planVersions {
12731288prevList ,ok := lv [planVersions [i ].DirectoryHash .ValueString ()]
12741289// If not in state, mark as known after apply since we'll create a new version.
@@ -1308,4 +1323,47 @@ func (planVersions Versions) reconcileVersionIDs(lv LastVersionsByHash, configVe
13081323lv [planVersions [i ].DirectoryHash .ValueString ()]= prevList [1 :]
13091324}
13101325}
1326+
1327+ // If only the Terraform variables have changed,
1328+ // we need to create a new version with the new variables.
1329+ for i := range planVersions {
1330+ if ! planVersions [i ].ID .IsUnknown () {
1331+ prevs ,ok := fullLv [planVersions [i ].DirectoryHash .ValueString ()]
1332+ if ! ok {
1333+ continue
1334+ }
1335+ if tfVariablesChanged (prevs ,& planVersions [i ]) {
1336+ planVersions [i ].ID = NewUUIDUnknown ()
1337+ // We could always set the name to unknown here, to generate a
1338+ // random one (this is what the Web UI currently does when
1339+ // only updating tfvars).
1340+ // However, I think it'd be weird if the provider just started
1341+ // ignoring the name you set in the config, we'll instead
1342+ // require that users update the name if they update the tfvars.
1343+ if configVersions [i ].Name .IsNull () {
1344+ planVersions [i ].Name = types .StringUnknown ()
1345+ }
1346+ }
1347+ }
1348+ }
1349+ }
1350+
1351+ func tfVariablesChanged (prevs []PreviousTemplateVersion ,planned * TemplateVersion )bool {
1352+ for _ ,prev := range prevs {
1353+ if prev .ID == planned .ID .ValueUUID () {
1354+ // If the previous version has no TFVars, then it was created using
1355+ // an older provider version.
1356+ if prev .TFVars == nil {
1357+ return true
1358+ }
1359+ for _ ,tfVar := range planned .TerraformVariables {
1360+ if prev .TFVars [tfVar .Name .ValueString ()]!= tfVar .Value .ValueString () {
1361+ return true
1362+ }
1363+ }
1364+ return false
1365+ }
1366+ }
1367+ return true
1368+
13111369}