From 33214f585791725c2377fde97e6031c85cccda41 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 13:49:24 +0200 Subject: [PATCH 01/12] Correct backend error --- coderd/apidoc/docs.go | 3 --- coderd/apidoc/swagger.json | 9 +++------ coderd/wsbuilder/wsbuilder.go | 2 +- codersdk/provisionerdaemons.go | 3 +-- docs/api/builds.md | 11 +++++----- docs/api/schemas.md | 14 ++++++------- docs/api/templates.md | 20 +++++++++---------- docs/api/workspaces.md | 10 +++++----- site/src/api/typesGenerated.ts | 9 ++------- .../createTemplate/createTemplateXService.ts | 8 -------- 10 files changed, 32 insertions(+), 57 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 04aed7c9be52a..45c66a1fe2543 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8411,11 +8411,9 @@ const docTemplate = `{ "codersdk.JobErrorCode": { "type": "string", "enum": [ - "MISSING_TEMPLATE_PARAMETER", "REQUIRED_TEMPLATE_VARIABLES" ], "x-enum-varnames": [ - "MissingTemplateParameter", "RequiredTemplateVariables" ] }, @@ -8949,7 +8947,6 @@ const docTemplate = `{ }, "error_code": { "enum": [ - "MISSING_TEMPLATE_PARAMETER", "REQUIRED_TEMPLATE_VARIABLES" ], "allOf": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ee710a7f8e51f..861f1c878e3dc 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7547,11 +7547,8 @@ }, "codersdk.JobErrorCode": { "type": "string", - "enum": ["MISSING_TEMPLATE_PARAMETER", "REQUIRED_TEMPLATE_VARIABLES"], - "x-enum-varnames": [ - "MissingTemplateParameter", - "RequiredTemplateVariables" - ] + "enum": ["REQUIRED_TEMPLATE_VARIABLES"], + "x-enum-varnames": ["RequiredTemplateVariables"] }, "codersdk.License": { "type": "object", @@ -8045,7 +8042,7 @@ "type": "string" }, "error_code": { - "enum": ["MISSING_TEMPLATE_PARAMETER", "REQUIRED_TEMPLATE_VARIABLES"], + "enum": ["REQUIRED_TEMPLATE_VARIABLES"], "allOf": [ { "$ref": "#/definitions/codersdk.JobErrorCode" diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go index 3b6bcf1ae832b..d0ae33e90ae95 100644 --- a/coderd/wsbuilder/wsbuilder.go +++ b/coderd/wsbuilder/wsbuilder.go @@ -525,7 +525,7 @@ func (b *Builder) getParameters() (names, values []string, err error) { // At this point, we've queried all the data we need from the database, // so the only errors are problems with the request (missing data, failed // validation, immutable parameters, etc.) - return nil, nil, BuildError{http.StatusBadRequest, err.Error(), err} + return nil, nil, BuildError{http.StatusBadRequest, fmt.Sprintf("failed to validate parameter %q", templateVersionParameter.Name), err} } names = append(names, templateVersionParameter.Name) values = append(values, value) diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index 30e0896707d56..4a3e280697f74 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -69,7 +69,6 @@ const ( type JobErrorCode string const ( - MissingTemplateParameter JobErrorCode = "MISSING_TEMPLATE_PARAMETER" RequiredTemplateVariables JobErrorCode = "REQUIRED_TEMPLATE_VARIABLES" ) @@ -81,7 +80,7 @@ type ProvisionerJob struct { CompletedAt *time.Time `json:"completed_at,omitempty" format:"date-time"` CanceledAt *time.Time `json:"canceled_at,omitempty" format:"date-time"` Error string `json:"error,omitempty"` - ErrorCode JobErrorCode `json:"error_code,omitempty" enums:"MISSING_TEMPLATE_PARAMETER,REQUIRED_TEMPLATE_VARIABLES"` + ErrorCode JobErrorCode `json:"error_code,omitempty" enums:"REQUIRED_TEMPLATE_VARIABLES"` Status ProvisionerJobStatus `json:"status" enums:"pending,running,succeeded,canceling,canceled,failed"` WorkerID *uuid.UUID `json:"worker_id,omitempty" format:"uuid"` FileID uuid.UUID `json:"file_id" format:"uuid"` diff --git a/docs/api/builds.md b/docs/api/builds.md index 782e41a6f61aa..f6aa71be7a555 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -39,7 +39,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -201,7 +201,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -759,7 +759,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -926,7 +926,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1163,7 +1163,6 @@ Status Code **200** | Property | Value | | ------------------------- | ----------------------------- | -| `error_code` | `MISSING_TEMPLATE_PARAMETER` | | `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | | `status` | `pending` | | `status` | `running` | @@ -1273,7 +1272,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, diff --git a/docs/api/schemas.md b/docs/api/schemas.md index a615825d266d3..5052f498afff1 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3088,7 +3088,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ## codersdk.JobErrorCode ```json -"MISSING_TEMPLATE_PARAMETER" +"REQUIRED_TEMPLATE_VARIABLES" ``` ### Properties @@ -3097,7 +3097,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | Value | | ----------------------------- | -| `MISSING_TEMPLATE_PARAMETER` | | `REQUIRED_TEMPLATE_VARIABLES` | ## codersdk.License @@ -3626,7 +3625,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -3664,7 +3663,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | Property | Value | | ------------ | ----------------------------- | -| `error_code` | `MISSING_TEMPLATE_PARAMETER` | | `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | | `status` | `pending` | | `status` | `running` | @@ -4656,7 +4654,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -5369,7 +5367,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -5976,7 +5974,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -6503,7 +6501,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, diff --git a/docs/api/templates.md b/docs/api/templates.md index 14ce5dec7d29f..407ab84eba439 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -385,7 +385,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -455,7 +455,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -549,7 +549,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -850,7 +850,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -920,7 +920,6 @@ Status Code **200** | Property | Value | | ------------ | ----------------------------- | -| `error_code` | `MISSING_TEMPLATE_PARAMETER` | | `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | | `status` | `pending` | | `status` | `running` | @@ -1024,7 +1023,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions/{templ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1094,7 +1093,6 @@ Status Code **200** | Property | Value | | ------------ | ----------------------------- | -| `error_code` | `MISSING_TEMPLATE_PARAMETER` | | `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | | `status` | `pending` | | `status` | `running` | @@ -1142,7 +1140,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion} \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1221,7 +1219,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion} "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1347,7 +1345,7 @@ curl -X POST http://coder-server:8080/api/v2/templateversions/{templateversion}/ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1400,7 +1398,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index f5c4aadd729c5..01b85e21b3527 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -67,7 +67,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -255,7 +255,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -446,7 +446,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -631,7 +631,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -953,7 +953,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/lock \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index e0592b966bca4..0a4526291194f 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1660,13 +1660,8 @@ export type InsightsReportInterval = "day" export const InsightsReportIntervals: InsightsReportInterval[] = ["day"] // From codersdk/provisionerdaemons.go -export type JobErrorCode = - | "MISSING_TEMPLATE_PARAMETER" - | "REQUIRED_TEMPLATE_VARIABLES" -export const JobErrorCodes: JobErrorCode[] = [ - "MISSING_TEMPLATE_PARAMETER", - "REQUIRED_TEMPLATE_VARIABLES", -] +export type JobErrorCode = "REQUIRED_TEMPLATE_VARIABLES" +export const JobErrorCodes: JobErrorCode[] = ["REQUIRED_TEMPLATE_VARIABLES"] // From codersdk/provisionerdaemons.go export type LogLevel = "debug" | "error" | "info" | "trace" | "warn" diff --git a/site/src/xServices/createTemplate/createTemplateXService.ts b/site/src/xServices/createTemplate/createTemplateXService.ts index 3d4205fc23f05..c3c7fe2180e61 100644 --- a/site/src/xServices/createTemplate/createTemplateXService.ts +++ b/site/src/xServices/createTemplate/createTemplateXService.ts @@ -519,7 +519,6 @@ export const createTemplateMachine = hasFailed: (_, { data }) => Boolean( data.job.status === "failed" && - !isMissingParameter(data) && !isMissingVariables(data), ), hasNoParametersOrVariables: (_, { data }) => @@ -531,13 +530,6 @@ export const createTemplateMachine = }, ) -const isMissingParameter = (version: TemplateVersion) => { - return Boolean( - version.job.error_code && - version.job.error_code === "MISSING_TEMPLATE_PARAMETER", - ) -} - const isMissingVariables = (version: TemplateVersion) => { return Boolean( version.job.error_code && From e831d242ec62fba8081b68b69ad5acf50c7f19c6 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 13:52:21 +0200 Subject: [PATCH 02/12] Unable: --- coderd/wsbuilder/wsbuilder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go index d0ae33e90ae95..26090a4f1be99 100644 --- a/coderd/wsbuilder/wsbuilder.go +++ b/coderd/wsbuilder/wsbuilder.go @@ -525,7 +525,7 @@ func (b *Builder) getParameters() (names, values []string, err error) { // At this point, we've queried all the data we need from the database, // so the only errors are problems with the request (missing data, failed // validation, immutable parameters, etc.) - return nil, nil, BuildError{http.StatusBadRequest, fmt.Sprintf("failed to validate parameter %q", templateVersionParameter.Name), err} + return nil, nil, BuildError{http.StatusBadRequest, fmt.Sprintf("Unable to validate parameter %q", templateVersionParameter.Name), err} } names = append(names, templateVersionParameter.Name) values = append(values, value) From e5421c4942d7160687022524e92d130a84d4c829 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 15:23:00 +0200 Subject: [PATCH 03/12] site: ask for changed parameter option --- site/src/api/api.ts | 26 ++++++++++++++++++- .../UpdateBuildParametersDialog.tsx | 3 +++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 4d5c88fe74b16..3742623f57bad 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1220,7 +1220,6 @@ const getMissingParameters = ( if (isMutableAndRequired || isImmutable) { requiredParameters.push(p) - return } }) @@ -1243,6 +1242,31 @@ const getMissingParameters = ( missingParameters.push(parameter) } + // Check if parameter "options" changed and we can't use old build parameters. + templateParameters.forEach((templateParameter) => { + if (templateParameter.options.length === 0) { + return + } + + // Check if there is a new value + let buildParameter = newBuildParameters.find( + (p) => p.name === templateParameter.name, + ) + + // If not, get the old one + if (!buildParameter) { + buildParameter = oldBuildParameters.find((p) => p.name === templateParameter.name) + } + + if (!buildParameter) { + return + } + + const matchingOption = templateParameter.options.find(option => option.value === buildParameter?.value); + if (!matchingOption) { + missingParameters.push(templateParameter) + } + }) return missingParameters } diff --git a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx index 33d42b6bc5ae2..fdd813162ec6d 100644 --- a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx +++ b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx @@ -49,6 +49,9 @@ export const UpdateBuildParametersDialog: FC< onUpdate(values.rich_parameter_values) }, }) + form.values = { + rich_parameter_values: initialRichParameterValues, + } const getFieldHelpers = getFormHelpers(form) const { t } = useTranslation("workspacePage") From 349c6ca1d068e52137f5ebd1ffe9f5bd0ad16bcd Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 15:23:33 +0200 Subject: [PATCH 04/12] wip: do not ask for required immutables on update --- cli/parameterresolver.go | 1 - cli/root.go | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/parameterresolver.go b/cli/parameterresolver.go index f4ed240993538..94516fa4dbd62 100644 --- a/cli/parameterresolver.go +++ b/cli/parameterresolver.go @@ -184,7 +184,6 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild if (tvp.Ephemeral && pr.promptBuildOptions) || (action == WorkspaceCreate && tvp.Required) || (action == WorkspaceCreate && !tvp.Ephemeral) || - (action == WorkspaceUpdate && tvp.Required) || (action == WorkspaceUpdate && !tvp.Mutable && firstTimeUse) || (action == WorkspaceUpdate && tvp.Mutable && !tvp.Ephemeral && pr.promptRichParameters) { parameterValue, err := cliui.RichParameter(inv, tvp) diff --git a/cli/root.go b/cli/root.go index 2b4db5eb8b221..fad5625d909c0 100644 --- a/cli/root.go +++ b/cli/root.go @@ -981,6 +981,8 @@ func (p *prettyErrorFormatter) format(err error) { msg = sdkError.Message if sdkError.Helper != "" { msg = msg + "\n" + sdkError.Helper + } else if sdkError.Detail != "" { + msg = msg + "\n" + sdkError.Detail } // The SDK error is usually good enough, and we don't want to overwhelm // the user with output. From 0da0b47095f3f2e0785ec7a49ff2dee913843ef3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 15:52:31 +0200 Subject: [PATCH 05/12] test: ParameterOptionChanged --- cli/update_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/cli/update_test.go b/cli/update_test.go index e799557fcc454..b18b8b03e7734 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -593,12 +593,71 @@ func TestUpdateValidateRichParameters(t *testing.T) { assert.NoError(t, err) }() + pty.ExpectMatch("Planning workspace...") + <-doneChan + }) + + t.Run("ParameterOptionChanged", func(t *testing.T) { + t.Parallel() + + // Create template and workspace + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + + templateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Second option", Description: "This is second option", Value: "2nd"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + }}, + } + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) + clitest.SetupConfig(t, client, root) + err := inv.Run() + require.NoError(t, err) + + // Update template + updatedTemplateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "first_option", Description: "This is first option", Value: "1"}, + {Name: "second_option", Description: "This is second option", Value: "2"}, + {Name: "third_option", Description: "This is third option", Value: "3"}, + }}, + } + + updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) + coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID) + err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: updatedVersion.ID, + }) + require.NoError(t, err) + + // Update the workspace + inv, root = clitest.New(t, "update", "my-workspace") + clitest.SetupConfig(t, client, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + matches := []string{ - "Planning workspace...", "", + "aaaa", "", } for i := 0; i < len(matches); i += 2 { match := matches[i] + value := matches[i+1] pty.ExpectMatch(match) + + if value != "" { + pty.WriteLine(value) + } } <-doneChan }) From 1c64cfe3ccbbb1fea8aed7b27c83200afaa1809c Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 16:28:36 +0200 Subject: [PATCH 06/12] extra test --- cli/update_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/cli/update_test.go b/cli/update_test.go index b18b8b03e7734..48a1d8f7b7c13 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -597,6 +597,73 @@ func TestUpdateValidateRichParameters(t *testing.T) { <-doneChan }) + t.Run("RequiredImmutableParameterAdded", func(t *testing.T) { + t.Parallel() + + // Create template and workspace + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + + templateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Second option", Description: "This is second option", Value: "2nd"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + }}, + } + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) + clitest.SetupConfig(t, client, root) + err := inv.Run() + require.NoError(t, err) + + // Update template: add required, immutable parameter + updatedTemplateParameters := []*proto.RichParameter{ + templateParameters[0], + {Name: immutableParameterName, Type: "string", Mutable: false, Required: true, Options: []*proto.RichParameterOption{ + {Name: "fir", Description: "This is first option for immutable parameter", Value: "I"}, + {Name: "sec", Description: "This is second option for immutable parameter", Value: "II"}, + {Name: "thi", Description: "This is third option for immutable parameter", Value: "III"}, + }}, + } + + updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) + coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID) + err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: updatedVersion.ID, + }) + require.NoError(t, err) + + // Update the workspace + inv, root = clitest.New(t, "update", "my-workspace") + clitest.SetupConfig(t, client, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + + matches := []string{ + immutableParameterName, "thi", + "Planning workspace...", "", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + pty.ExpectMatch(match) + + if value != "" { + pty.WriteLine(value) + } + } + <-doneChan + }) + t.Run("ParameterOptionChanged", func(t *testing.T) { t.Parallel() From 1e41500fdeaaa671e80f69f9d1a4628f50b714a3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 17:25:19 +0200 Subject: [PATCH 07/12] Prompt for changed parameter option --- cli/parameterresolver.go | 29 ++++++++++++++++++++++++++++- cli/update_test.go | 3 ++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cli/parameterresolver.go b/cli/parameterresolver.go index 94516fa4dbd62..bf0c727e1cd35 100644 --- a/cli/parameterresolver.go +++ b/cli/parameterresolver.go @@ -141,6 +141,10 @@ next: continue // immutables should not be passed to consecutive builds } + if len(tvp.Options) > 0 && !isValidTemplateParameterOption(buildParameter, tvp.Options) { + continue // do not propagate invalid options + } + for i, r := range resolved { if r.Name == buildParameter.Name { resolved[i].Value = buildParameter.Value @@ -180,10 +184,11 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild // Parameter has not been resolved yet, so CLI needs to determine if user should input it. firstTimeUse := pr.isFirstTimeUse(tvp.Name) - + promptParameterOption := pr.isLastBuildParameterInvalidOption(tvp) if (tvp.Ephemeral && pr.promptBuildOptions) || (action == WorkspaceCreate && tvp.Required) || (action == WorkspaceCreate && !tvp.Ephemeral) || + (action == WorkspaceUpdate && promptParameterOption) || (action == WorkspaceUpdate && !tvp.Mutable && firstTimeUse) || (action == WorkspaceUpdate && tvp.Mutable && !tvp.Ephemeral && pr.promptRichParameters) { parameterValue, err := cliui.RichParameter(inv, tvp) @@ -206,6 +211,19 @@ func (pr *ParameterResolver) isFirstTimeUse(parameterName string) bool { return findWorkspaceBuildParameter(parameterName, pr.lastBuildParameters) == nil } +func (pr *ParameterResolver) isLastBuildParameterInvalidOption(templateVersionParameter codersdk.TemplateVersionParameter) bool { + if len(templateVersionParameter.Options) == 0 { + return false + } + + for _, buildParameter := range pr.lastBuildParameters { + if buildParameter.Name == templateVersionParameter.Name { + return !isValidTemplateParameterOption(buildParameter, templateVersionParameter.Options) + } + } + return false +} + func findTemplateVersionParameter(workspaceBuildParameter codersdk.WorkspaceBuildParameter, templateVersionParameters []codersdk.TemplateVersionParameter) *codersdk.TemplateVersionParameter { for _, tvp := range templateVersionParameters { if tvp.Name == workspaceBuildParameter.Name { @@ -223,3 +241,12 @@ func findWorkspaceBuildParameter(parameterName string, params []codersdk.Workspa } return nil } + +func isValidTemplateParameterOption(buildParameter codersdk.WorkspaceBuildParameter, options []codersdk.TemplateVersionParameterOption) bool { + for _, opt := range options { + if opt.Value == buildParameter.Value { + return true + } + } + return false +} diff --git a/cli/update_test.go b/cli/update_test.go index 48a1d8f7b7c13..a0b1ae70f2e00 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -715,7 +715,8 @@ func TestUpdateValidateRichParameters(t *testing.T) { }() matches := []string{ - "aaaa", "", + stringParameterName, "second_option", + "Planning workspace...", "", } for i := 0; i < len(matches); i += 2 { match := matches[i] From 8cf4b15014b8110638ca5baa4fd5096b3d70b3f3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 17:29:52 +0200 Subject: [PATCH 08/12] cleanup --- site/src/api/api.ts | 8 ++++++-- .../xServices/createTemplate/createTemplateXService.ts | 5 +---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 3742623f57bad..454a8d43e6922 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1255,14 +1255,18 @@ const getMissingParameters = ( // If not, get the old one if (!buildParameter) { - buildParameter = oldBuildParameters.find((p) => p.name === templateParameter.name) + buildParameter = oldBuildParameters.find( + (p) => p.name === templateParameter.name, + ) } if (!buildParameter) { return } - const matchingOption = templateParameter.options.find(option => option.value === buildParameter?.value); + const matchingOption = templateParameter.options.find( + (option) => option.value === buildParameter?.value, + ) if (!matchingOption) { missingParameters.push(templateParameter) } diff --git a/site/src/xServices/createTemplate/createTemplateXService.ts b/site/src/xServices/createTemplate/createTemplateXService.ts index c3c7fe2180e61..bfff04549fe17 100644 --- a/site/src/xServices/createTemplate/createTemplateXService.ts +++ b/site/src/xServices/createTemplate/createTemplateXService.ts @@ -517,10 +517,7 @@ export const createTemplateMachine = isNotUsingExample: ({ exampleId }) => !exampleId, hasFile: ({ file }) => Boolean(file), hasFailed: (_, { data }) => - Boolean( - data.job.status === "failed" && - !isMissingVariables(data), - ), + Boolean(data.job.status === "failed" && !isMissingVariables(data)), hasNoParametersOrVariables: (_, { data }) => data.variables === undefined, hasParametersOrVariables: (_, { data }) => { From 0635b5c3511e103cb84cb984dac5c4b1b813ccea Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 17:30:36 +0200 Subject: [PATCH 09/12] WIP --- site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx index fdd813162ec6d..33d42b6bc5ae2 100644 --- a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx +++ b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx @@ -49,9 +49,6 @@ export const UpdateBuildParametersDialog: FC< onUpdate(values.rich_parameter_values) }, }) - form.values = { - rich_parameter_values: initialRichParameterValues, - } const getFieldHelpers = getFormHelpers(form) const { t } = useTranslation("workspacePage") From e617d16b1fcd97ea5c1c5a0d7f3520f1900523b2 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 23 Aug 2023 13:09:07 +0200 Subject: [PATCH 10/12] After merge --- cli/update_test.go | 67 ---------------------------------------------- 1 file changed, 67 deletions(-) diff --git a/cli/update_test.go b/cli/update_test.go index baa61a587cc99..adb00d22c863e 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -597,73 +597,6 @@ func TestUpdateValidateRichParameters(t *testing.T) { <-doneChan }) - t.Run("RequiredImmutableParameterAdded", func(t *testing.T) { - t.Parallel() - - // Create template and workspace - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - user := coderdtest.CreateFirstUser(t, client) - - templateParameters := []*proto.RichParameter{ - {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ - {Name: "First option", Description: "This is first option", Value: "1st"}, - {Name: "Second option", Description: "This is second option", Value: "2nd"}, - {Name: "Third option", Description: "This is third option", Value: "3rd"}, - }}, - } - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) - coderdtest.AwaitTemplateVersionJob(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - - inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) - clitest.SetupConfig(t, client, root) - err := inv.Run() - require.NoError(t, err) - - // Update template: add required, immutable parameter - updatedTemplateParameters := []*proto.RichParameter{ - templateParameters[0], - {Name: immutableParameterName, Type: "string", Mutable: false, Required: true, Options: []*proto.RichParameterOption{ - {Name: "fir", Description: "This is first option for immutable parameter", Value: "I"}, - {Name: "sec", Description: "This is second option for immutable parameter", Value: "II"}, - {Name: "thi", Description: "This is third option for immutable parameter", Value: "III"}, - }}, - } - - updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) - coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID) - err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: updatedVersion.ID, - }) - require.NoError(t, err) - - // Update the workspace - inv, root = clitest.New(t, "update", "my-workspace") - clitest.SetupConfig(t, client, root) - doneChan := make(chan struct{}) - pty := ptytest.New(t).Attach(inv) - go func() { - defer close(doneChan) - err := inv.Run() - assert.NoError(t, err) - }() - - matches := []string{ - immutableParameterName, "thi", - "Planning workspace...", "", - } - for i := 0; i < len(matches); i += 2 { - match := matches[i] - value := matches[i+1] - pty.ExpectMatch(match) - - if value != "" { - pty.WriteLine(value) - } - } - <-doneChan - }) - t.Run("ParameterOptionChanged", func(t *testing.T) { t.Parallel() From 75d0f41249a858be3293d55aceec9e8671e9a7e2 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 23 Aug 2023 14:45:01 +0200 Subject: [PATCH 11/12] use enableReinitialize --- site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx index 33d42b6bc5ae2..860969267fb7e 100644 --- a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx +++ b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx @@ -48,6 +48,7 @@ export const UpdateBuildParametersDialog: FC< onSubmit: (values) => { onUpdate(values.rich_parameter_values) }, + enableReinitialize: true, }) const getFieldHelpers = getFormHelpers(form) const { t } = useTranslation("workspacePage") From c03a58109379a4859c4c0616268531fbd382786d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 23 Aug 2023 17:08:53 +0200 Subject: [PATCH 12/12] Add test for disappeared option --- cli/parameterresolver.go | 1 + cli/update_test.go | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/cli/parameterresolver.go b/cli/parameterresolver.go index 64a46f0374e0b..486188d52a27a 100644 --- a/cli/parameterresolver.go +++ b/cli/parameterresolver.go @@ -185,6 +185,7 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild firstTimeUse := pr.isFirstTimeUse(tvp.Name) promptParameterOption := pr.isLastBuildParameterInvalidOption(tvp) + if (tvp.Ephemeral && pr.promptBuildOptions) || (action == WorkspaceCreate && tvp.Required) || (action == WorkspaceCreate && !tvp.Ephemeral) || diff --git a/cli/update_test.go b/cli/update_test.go index adb00d22c863e..57e49d0db3766 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -663,6 +663,72 @@ func TestUpdateValidateRichParameters(t *testing.T) { <-doneChan }) + t.Run("ParameterOptionDisappeared", func(t *testing.T) { + t.Parallel() + + // Create template and workspace + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + + templateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Second option", Description: "This is second option", Value: "2nd"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + }}, + } + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) + clitest.SetupConfig(t, client, root) + err := inv.Run() + require.NoError(t, err) + + // Update template - 2nd option disappeared, 4th option added + updatedTemplateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + {Name: "Fourth option", Description: "This is fourth option", Value: "4th"}, + }}, + } + + updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) + coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID) + err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: updatedVersion.ID, + }) + require.NoError(t, err) + + // Update the workspace + inv, root = clitest.New(t, "update", "my-workspace") + clitest.SetupConfig(t, client, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + + matches := []string{ + stringParameterName, "Third option", + "Planning workspace...", "", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + pty.ExpectMatch(match) + + if value != "" { + pty.WriteLine(value) + } + } + <-doneChan + }) + t.Run("ImmutableRequiredParameterExists_MutableRequiredParameterAdded", func(t *testing.T) { t.Parallel()