Skip to content

Commit 57bc338

Browse files
authored
Merge branch 'main' into mafredri/feat-shutdown-script
2 parents f51eab5 + 136f23f commit 57bc338

32 files changed

+1403
-491
lines changed

cli/testdata/coder_--help.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Workspace Commands:
4545
ssh Start a shell into a workspace
4646
start Start a workspace
4747
stop Stop a workspace
48-
update Update a workspace
48+
update Will update and start a given workspace if it is out of date.
4949

5050
Flags:
5151
--global-config coder Path to the global coder config directory.

cli/testdata/coder_update_--help.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Update a workspace
1+
Will update and start a given workspace if it is out of date. Use --always-prompt to change the parameter values of the workspace.
22

33
Usage:
44
coder update <workspace> [flags]

cli/update.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ func update() *cobra.Command {
2020
Annotations: workspaceCommand,
2121
Use: "update <workspace>",
2222
Args: cobra.ExactArgs(1),
23-
Short: "Update a workspace",
23+
Short: "Will update and start a given workspace if it is out of date.",
24+
Long: "Will update and start a given workspace if it is out of date. Use --always-prompt to change " +
25+
"the parameter values of the workspace.",
2426
RunE: func(cmd *cobra.Command, args []string) error {
2527
client, err := CreateClient(cmd)
2628
if err != nil {
@@ -68,7 +70,7 @@ func update() *cobra.Command {
6870

6971
build, err := client.CreateWorkspaceBuild(cmd.Context(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
7072
TemplateVersionID: template.ActiveVersionID,
71-
Transition: workspace.LatestBuild.Transition,
73+
Transition: codersdk.WorkspaceTransitionStart,
7274
ParameterValues: buildParams.parameters,
7375
RichParameterValues: buildParams.richParameters,
7476
})

docs/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,6 @@ coder [flags]
5050
| [<code>stop</code>](./cli/coder_stop) | Stop a workspace |
5151
| [<code>templates</code>](./cli/coder_templates) | Manage templates |
5252
| [<code>tokens</code>](./cli/coder_tokens) | Manage personal access tokens |
53-
| [<code>update</code>](./cli/coder_update) | Update a workspace |
53+
| [<code>update</code>](./cli/coder_update) | Will update and start a given workspace if it is out of date. |
5454
| [<code>users</code>](./cli/coder_users) | Manage users |
5555
| [<code>version</code>](./cli/coder_version) | Show coder version |

docs/cli/coder_update.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# coder update
44

5-
Update a workspace
5+
Will update and start a given workspace if it is out of date. Use --always-prompt to change the parameter values of the workspace.
66

77
## Usage
88

site/jest.setup.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,30 @@ import { server } from "./src/testHelpers/server"
55
import "jest-location-mock"
66
import { TextEncoder, TextDecoder } from "util"
77
import { Blob } from "buffer"
8+
import { fetch, Request, Response, Headers } from "@remix-run/web-fetch"
89

910
global.TextEncoder = TextEncoder
1011
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Polyfill for jsdom
1112
global.TextDecoder = TextDecoder as any
1213
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Polyfill for jsdom
1314
global.Blob = Blob as any
1415

16+
// From REMIX https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/__tests__/setup.ts
17+
if (!global.fetch) {
18+
// Built-in lib.dom.d.ts expects `fetch(Request | string, ...)` but the web
19+
// fetch API allows a URL so @remix-run/web-fetch defines
20+
// `fetch(string | URL | Request, ...)`
21+
// @ts-expect-error -- Polyfill for jsdom
22+
global.fetch = fetch
23+
// Same as above, lib.dom.d.ts doesn't allow a URL to the Request constructor
24+
// @ts-expect-error -- Polyfill for jsdom
25+
global.Request = Request
26+
// web-std/fetch Response does not currently implement Response.error()
27+
// @ts-expect-error -- Polyfill for jsdom
28+
global.Response = Response
29+
global.Headers = Headers
30+
}
31+
1532
// Polyfill the getRandomValues that is used on utils/random.ts
1633
Object.defineProperty(global.self, "crypto", {
1734
value: {

site/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@material-ui/icons": "4.5.1",
3737
"@material-ui/lab": "4.0.0-alpha.42",
3838
"@monaco-editor/react": "4.4.6",
39+
"@remix-run/web-fetch": "4.3.2",
3940
"@tanstack/react-query": "4.22.4",
4041
"@testing-library/react-hooks": "8.0.1",
4142
"@types/color-convert": "2.0.0",

site/src/components/Dialogs/ConfirmDialog/ConfirmDialog.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ const useStyles = makeStyles((theme) => ({
8787
color: theme.palette.text.primary,
8888
},
8989

90-
"& p": {
90+
"& p:not(.MuiFormHelperText-root)": {
91+
margin: 0,
92+
},
93+
94+
"& > p": {
9195
margin: theme.spacing(1, 0),
9296
},
9397
},

site/src/components/RequireAuth/RequireAuth.test.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { screen } from "@testing-library/react"
22
import { rest } from "msw"
3-
import { Route } from "react-router-dom"
43
import { renderWithAuth } from "testHelpers/renderHelpers"
54
import { server } from "testHelpers/server"
65

@@ -20,7 +19,12 @@ describe("RequireAuth", () => {
2019
)
2120

2221
renderWithAuth(<h1>Test</h1>, {
23-
routes: <Route path="setup" element={<h1>Setup</h1>} />,
22+
nonAuthenticatedRoutes: [
23+
{
24+
path: "setup",
25+
element: <h1>Setup</h1>,
26+
},
27+
],
2428
})
2529

2630
await screen.findByText("Setup")

site/src/components/TemplateLayout/TemplateLayout.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Permissions } from "xServices/auth/authXService"
1414
import { Loader } from "components/Loader/Loader"
1515
import { usePermissions } from "hooks/usePermissions"
1616
import { TemplatePageHeader } from "./TemplatePageHeader"
17+
import { AlertBanner } from "components/AlertBanner/AlertBanner"
1718

1819
const useTemplateName = () => {
1920
const { template } = useParams()
@@ -57,9 +58,21 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
5758
organizationId,
5859
},
5960
})
60-
const { template, permissions: templatePermissions } = templateState.context
61+
const {
62+
template,
63+
permissions: templatePermissions,
64+
getTemplateError,
65+
} = templateState.context
6166
const permissions = usePermissions()
6267

68+
if (getTemplateError) {
69+
return (
70+
<div className={styles.error}>
71+
<AlertBanner severity="error" error={getTemplateError} />
72+
</div>
73+
)
74+
}
75+
6376
if (!template || !templatePermissions) {
6477
return <Loader />
6578
}
@@ -117,6 +130,9 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
117130

118131
export const useStyles = makeStyles((theme) => {
119132
return {
133+
error: {
134+
margin: theme.spacing(2),
135+
},
120136
tabs: {
121137
borderBottom: `1px solid ${theme.palette.divider}`,
122138
marginBottom: theme.spacing(5),

site/src/components/TemplateVersionEditor/FileDialog.tsx

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"
33
import { Stack } from "components/Stack/Stack"
44
import { ChangeEvent, FC, useState } from "react"
55
import Typography from "@material-ui/core/Typography"
6+
import { allowedExtensions, isAllowedFile } from "util/templateVersion"
7+
import { FileTree, validatePath } from "util/filetree"
68

79
export const CreateFileDialog: FC<{
810
onClose: () => void
911
checkExists: (path: string) => boolean
1012
onConfirm: (path: string) => void
1113
open: boolean
12-
}> = ({ checkExists, onClose, onConfirm, open }) => {
14+
fileTree: FileTree
15+
}> = ({ checkExists, onClose, onConfirm, open, fileTree }) => {
1316
const [pathValue, setPathValue] = useState("")
14-
const [error, setError] = useState("")
17+
const [error, setError] = useState<string>()
1518
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
1619
setPathValue(event.target.value)
1720
}
@@ -24,7 +27,20 @@ export const CreateFileDialog: FC<{
2427
setError("File already exists")
2528
return
2629
}
30+
if (!isAllowedFile(pathValue)) {
31+
const extensions = allowedExtensions.join(", ")
32+
setError(
33+
`This extension is not allowed. You only can create files with the following extensions: ${extensions}.`,
34+
)
35+
return
36+
}
37+
const pathError = validatePath(pathValue, fileTree)
38+
if (pathError) {
39+
setError(pathError)
40+
return
41+
}
2742
onConfirm(pathValue)
43+
setError(undefined)
2844
setPathValue("")
2945
}
3046

@@ -33,6 +49,7 @@ export const CreateFileDialog: FC<{
3349
open={open}
3450
onClose={() => {
3551
onClose()
52+
setError(undefined)
3653
setPathValue("")
3754
}}
3855
onConfirm={handleConfirm}
@@ -42,10 +59,10 @@ export const CreateFileDialog: FC<{
4259
confirmText="Create"
4360
title="Create File"
4461
description={
45-
<Stack spacing={1}>
62+
<Stack>
4663
<Typography>
4764
Specify the path to a file to be created. This path can contain
48-
slashes too!
65+
slashes too.
4966
</Typography>
5067
<TextField
5168
autoFocus
@@ -54,14 +71,18 @@ export const CreateFileDialog: FC<{
5471
handleConfirm()
5572
}
5673
}}
74+
error={Boolean(error)}
5775
helperText={error}
5876
name="file-path"
5977
autoComplete="off"
6078
id="file-path"
61-
placeholder="main.tf"
79+
placeholder="example.tf"
6280
value={pathValue}
6381
onChange={handleChange}
6482
label="File Path"
83+
InputLabelProps={{
84+
shrink: true,
85+
}}
6586
/>
6687
</Stack>
6788
}
@@ -82,7 +103,12 @@ export const DeleteFileDialog: FC<{
82103
open={open}
83104
onConfirm={onConfirm}
84105
title="Delete File"
85-
description={`Are you sure you want to delete "${filename}"?`}
106+
description={
107+
<>
108+
Are you sure you want to delete <strong>{filename}</strong>? It will
109+
be deleted permanently.
110+
</>
111+
}
86112
/>
87113
)
88114
}
@@ -93,9 +119,10 @@ export const RenameFileDialog: FC<{
93119
checkExists: (path: string) => boolean
94120
open: boolean
95121
filename: string
96-
}> = ({ checkExists, onClose, onConfirm, open, filename }) => {
122+
fileTree: FileTree
123+
}> = ({ checkExists, onClose, onConfirm, open, filename, fileTree }) => {
97124
const [pathValue, setPathValue] = useState(filename)
98-
const [error, setError] = useState("")
125+
const [error, setError] = useState<string>()
99126
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
100127
setPathValue(event.target.value)
101128
}
@@ -108,7 +135,20 @@ export const RenameFileDialog: FC<{
108135
setError("File already exists")
109136
return
110137
}
138+
if (!isAllowedFile(pathValue)) {
139+
const extensions = allowedExtensions.join(", ")
140+
setError(
141+
`This extension is not allowed. You only can rename files with the following extensions: ${extensions}.`,
142+
)
143+
return
144+
}
145+
const pathError = validatePath(pathValue, fileTree)
146+
if (pathError) {
147+
setError(pathError)
148+
return
149+
}
111150
onConfirm(pathValue)
151+
setError(undefined)
112152
setPathValue("")
113153
}
114154

@@ -117,33 +157,34 @@ export const RenameFileDialog: FC<{
117157
open={open}
118158
onClose={() => {
119159
onClose()
160+
setError(undefined)
120161
setPathValue("")
121162
}}
122163
onConfirm={handleConfirm}
123164
hideCancel={false}
124165
type="success"
125166
cancelText="Cancel"
126-
confirmText="Create"
167+
confirmText="Rename"
127168
title="Rename File"
128169
description={
129-
<Stack spacing={1}>
130-
<Typography>
131-
Rename {`"${filename}"`} to something else. This path can contain
132-
slashes too!
133-
</Typography>
170+
<Stack>
171+
<p>
172+
Rename <strong>{filename}</strong> to something else. This path can
173+
contain slashes too!
174+
</p>
134175
<TextField
135176
autoFocus
136177
onKeyDown={(event) => {
137178
if (event.key === "Enter") {
138179
handleConfirm()
139180
}
140181
}}
182+
error={Boolean(error)}
141183
helperText={error}
142184
name="file-path"
143185
autoComplete="off"
144186
id="file-path"
145-
placeholder="main.tf"
146-
defaultValue={filename}
187+
placeholder={filename}
147188
value={pathValue}
148189
onChange={handleChange}
149190
label="File Path"

site/src/components/TemplateVersionEditor/FileTreeView.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ export const FileTreeView: FC<{
6767
onSelect(currentPath)
6868
}}
6969
onContextMenu={(event) => {
70-
event.preventDefault()
70+
event.preventDefault() // Avoid default browser behavior
71+
event.stopPropagation() // Avoid trigger parent context menu
7172
setContextMenu(
7273
contextMenu
7374
? undefined
@@ -137,7 +138,7 @@ export const FileTreeView: FC<{
137138
setContextMenu(undefined)
138139
}}
139140
>
140-
Rename...
141+
Rename
141142
</MenuItem>
142143
<MenuItem
143144
onClick={() => {
@@ -148,7 +149,7 @@ export const FileTreeView: FC<{
148149
setContextMenu(undefined)
149150
}}
150151
>
151-
Delete Permanently
152+
Delete
152153
</MenuItem>
153154
</Menu>
154155
</TreeView>

0 commit comments

Comments
 (0)