@@ -144,7 +144,13 @@ func parameterDataSource() *schema.Resource {
144144 input = & envValue
145145 }
146146
147- value , diags := parameter .ValidateInput (input )
147+ var previous * string
148+ envPreviousValue , ok := os .LookupEnv (ParameterEnvironmentVariablePrevious (parameter .Name ))
149+ if ok {
150+ previous = & envPreviousValue
151+ }
152+
153+ value , diags := parameter .ValidateInput (input , previous )
148154 if diags .HasError () {
149155 return diags
150156 }
@@ -393,7 +399,7 @@ func valueIsType(typ OptionType, value string) error {
393399 return nil
394400}
395401
396- func (v * Parameter ) ValidateInput (input * string ) (string , diag.Diagnostics ) {
402+ func (v * Parameter ) ValidateInput (input * string , previous * string ) (string , diag.Diagnostics ) {
397403 var err error
398404 var optionType OptionType
399405
@@ -436,7 +442,7 @@ func (v *Parameter) ValidateInput(input *string) (string, diag.Diagnostics) {
436442 forcedValue = * value
437443 }
438444
439- d := v .validValue (forcedValue , optionType , optionValues , cty.Path {})
445+ d := v .validValue (forcedValue , previous , optionType , optionValues , cty.Path {})
440446 if d .HasError () {
441447 return "" , d
442448 }
@@ -500,7 +506,7 @@ func (v *Parameter) ValidOptions(optionType OptionType) (map[string]struct{}, di
500506 return optionValues , nil
501507}
502508
503- func (v * Parameter ) validValue (value string , optionType OptionType , optionValues map [string ]struct {}, path cty.Path ) diag.Diagnostics {
509+ func (v * Parameter ) validValue (value string , previous * string , optionType OptionType , optionValues map [string ]struct {}, path cty.Path ) diag.Diagnostics {
504510 // name is used for constructing more precise error messages.
505511 name := "Value"
506512 if path .Equals (defaultValuePath ) {
@@ -567,7 +573,7 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
567573
568574 if len (v .Validation ) == 1 {
569575 validCheck := & v .Validation [0 ]
570- err := validCheck .Valid (v .Type , value )
576+ err := validCheck .Valid (v .Type , value , previous )
571577 if err != nil {
572578 return diag.Diagnostics {
573579 {
@@ -583,7 +589,7 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
583589 return nil
584590}
585591
586- func (v * Validation ) Valid (typ OptionType , value string ) error {
592+ func (v * Validation ) Valid (typ OptionType , value string , previous * string ) error {
587593 if typ != OptionTypeNumber {
588594 if ! v .MinDisabled {
589595 return fmt .Errorf ("a min cannot be specified for a %s type" , typ )
@@ -633,6 +639,28 @@ func (v *Validation) Valid(typ OptionType, value string) error {
633639 if v .Monotonic != "" && v .Monotonic != ValidationMonotonicIncreasing && v .Monotonic != ValidationMonotonicDecreasing {
634640 return fmt .Errorf ("number monotonicity can be either %q or %q" , ValidationMonotonicIncreasing , ValidationMonotonicDecreasing )
635641 }
642+
643+ switch v .Monotonic {
644+ case "" :
645+ // No monotonicity check
646+ case ValidationMonotonicIncreasing , ValidationMonotonicDecreasing :
647+ if previous != nil { // Only check if previous value exists
648+ previousNum , err := strconv .Atoi (* previous )
649+ if err != nil {
650+ return fmt .Errorf ("previous value %q is not a number" , * previous )
651+ }
652+
653+ if v .Monotonic == ValidationMonotonicIncreasing && ! (num >= previousNum ) {
654+ return fmt .Errorf ("parameter value '%d' must be equal or greater than previous value: %d" , num , previousNum )
655+ }
656+
657+ if v .Monotonic == ValidationMonotonicDecreasing && ! (num <= previousNum ) {
658+ return fmt .Errorf ("parameter value '%d' must be equal or lower than previous value: %d" , num , previousNum )
659+ }
660+ }
661+ default :
662+ return fmt .Errorf ("number monotonicity can be either %q or %q" , ValidationMonotonicIncreasing , ValidationMonotonicDecreasing )
663+ }
636664 case OptionTypeListString :
637665 var listOfStrings []string
638666 err := json .Unmarshal ([]byte (value ), & listOfStrings )
@@ -660,6 +688,15 @@ func ParameterEnvironmentVariable(name string) string {
660688 return "CODER_PARAMETER_" + hex .EncodeToString (sum [:])
661689}
662690
691+ // ParameterEnvironmentVariablePrevious returns the environment variable to
692+ // specify for a parameter's previous value. This is used for workspace
693+ // subsequent builds after the first. Primarily to validate monotonicity in the
694+ // `validation` block.
695+ func ParameterEnvironmentVariablePrevious (name string ) string {
696+ sum := sha256 .Sum256 ([]byte (name ))
697+ return "CODER_PARAMETER_PREVIOUS_" + hex .EncodeToString (sum [:])
698+ }
699+
663700func takeFirstError (errs ... error ) error {
664701 for _ , err := range errs {
665702 if err != nil {
0 commit comments