Issue #8/#9: add workspace lock lifecycle, run force-execute, and read/list parity flags#10
Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses Issue #8 by adding project metadata (ProjectID and ProjectName) to workspace read output, and makes significant progress on Issue #9 by adding missing workspace lock lifecycle commands and run operation commands, along with extensive filter and include flags for list/read operations.
Changes:
- Added workspace lock, unlock, and force-unlock commands with full test coverage
- Added run force-execute command to support forced run execution
- Enhanced workspace read to include project metadata and support -include flag with automatic project inclusion
- Added comprehensive filter flags to workspace list (search, tags, exclude-tags, wildcard-name, project-id, current-run-status, include, sort)
- Added comprehensive filter flags to run list (user, commit, search, status, source, operation, include)
- Added -include flag support to run show command
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| command/workspace_lock.go | Implements workspace lock command with optional reason parameter |
| command/workspace_lock_test.go | Tests for workspace lock including flag validation and error handling |
| command/workspace_unlock.go | Implements workspace unlock command |
| command/workspace_unlock_test.go | Tests for workspace unlock command |
| command/workspace_force_unlock.go | Implements workspace force-unlock command |
| command/workspace_force_unlock_test.go | Tests for workspace force-unlock command |
| command/workspace_read.go | Adds project metadata to output and -include flag with auto-inclusion of project data |
| command/workspace_read_test.go | Updated tests to verify project metadata and include options |
| command/workspace_list.go | Adds filter flags: search, tags, exclude-tags, wildcard-name, project-id, current-run-status, include, sort |
| command/workspace_list_test.go | Tests filter flag functionality |
| command/run_show.go | Adds -include flag support and nil-safe Plan access |
| command/run_show_test.go | Tests include options functionality |
| command/run_list.go | Adds filter flags: user, commit, search, status, source, operation, include |
| command/run_list_test.go | Tests filter flag functionality |
| command/run_force_execute.go | Implements run force-execute command |
| command/run_force_execute_test.go | Tests for run force-execute command |
| command/run_services.go | Adds runForceExecutor interface |
| command/mocks_test.go | Adds ReadWithOptions support to mockWorkspaceReader and mockRunReadService |
| command/commands.go | Registers new workspace lock/unlock/force-unlock and run force-execute commands |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Read run | ||
| run, err := c.runService(client).Read(client.Context(), c.runID) | ||
| if err != nil { | ||
| c.Ui.Error(fmt.Sprintf("Error reading run: %s", err)) | ||
| return 1 | ||
| var run *tfe.Run | ||
| if c.include != "" { | ||
| if withOptions, ok := any(runSvc).(runReaderWithOptions); ok { | ||
| options := &tfe.RunReadOptions{} | ||
| for _, include := range splitCommaList(c.include) { | ||
| if include == "" { | ||
| continue | ||
| } | ||
| options.Include = append(options.Include, tfe.RunIncludeOpt(include)) | ||
| } | ||
| run, err = withOptions.ReadWithOptions(client.Context(), c.runID, options) | ||
| if err != nil { | ||
| c.Ui.Error(fmt.Sprintf("Error reading run: %s", err)) | ||
| return 1 | ||
| } | ||
| } else { | ||
| run, err = runSvc.Read(client.Context(), c.runID) | ||
| if err != nil { | ||
| c.Ui.Error(fmt.Sprintf("Error reading run: %s", err)) | ||
| return 1 | ||
| } | ||
| } | ||
| } else { | ||
| run, err = runSvc.Read(client.Context(), c.runID) | ||
| if err != nil { | ||
| c.Ui.Error(fmt.Sprintf("Error reading run: %s", err)) | ||
| return 1 | ||
| } | ||
| } |
There was a problem hiding this comment.
The logic for reading a run has unnecessary duplication. When c.include is empty, the same Read call is duplicated in both the outer else block (lines 75-81) and the inner else block (lines 69-73). This creates maintenance overhead.
Consider consolidating the logic so that when include is empty OR when the service doesn't support ReadWithOptions, the fallback Read call is made only once.
| "Name": lockedWorkspace.Name, | ||
| "Organization": c.organization, | ||
| "Locked": lockedWorkspace.Locked, | ||
| "LockReason": c.reason, |
There was a problem hiding this comment.
The output shows the lock reason passed by the user (c.reason) rather than the actual lock reason from the API response (lockedWorkspace). If the API stores and returns additional metadata about the lock (such as who locked it or when), this information will be missing from the output.
Consider using lockedWorkspace.LockReason or a similar field from the API response instead of c.reason to ensure the output reflects the actual state in the API. This is especially important if lock reasons can be modified by the API or if there's other relevant metadata to display.
| "LockReason": c.reason, | |
| "LockReason": lockedWorkspace.LockReason, |
Add workspace lock/unlock/force-unlock, run force-execute, and list/read filter flags to CHANGELOG. Update README command table counts and add usage examples for new commands. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Summary
This PR delivers a high-value parity slice for issues #8 and #9.
Fixes for #8
workspace readnow includes project metadata in output:ProjectIDProjectNameworkspace readnow supports-includeand automatically requestsprojectinclude when possible.Partial parity progress for #9
workspace lockworkspace unlockworkspace force-unlockrun force-executeworkspace list: search/tag/exclude/wildcard/project/status/include/sort flagsrun list: user/commit/search/status/source/operation/include flagsrun show:-includeTests
go test ./commandIssue links