Skip to content

Commit 5555ddb

Browse files
authored
Merge branch 'main' into mafredri/recreate-template
2 parents 7b6af6e + 18b0eff commit 5555ddb

File tree

9 files changed

+154
-55
lines changed

9 files changed

+154
-55
lines changed

cli/configssh.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ func configSSH() *cobra.Command {
138138
var (
139139
sshConfigFile string
140140
sshConfigOpts sshConfigOptions
141+
usePreviousOpts bool
141142
coderConfigFile string
142143
showDiff bool
143144
skipProxyCommand bool
@@ -218,7 +219,9 @@ func configSSH() *cobra.Command {
218219

219220
// Avoid prompting in diff mode (unexpected behavior)
220221
// or when a previous config does not exist.
221-
if !showDiff && lastConfig != nil && !sshConfigOpts.equal(*lastConfig) {
222+
if usePreviousOpts && lastConfig != nil {
223+
sshConfigOpts = *lastConfig
224+
} else if !showDiff && lastConfig != nil && !sshConfigOpts.equal(*lastConfig) {
222225
newOpts := sshConfigOpts.asList()
223226
newOptsMsg := "\n\n New options: none"
224227
if len(newOpts) > 0 {
@@ -394,11 +397,14 @@ func configSSH() *cobra.Command {
394397
cmd.Flags().BoolVarP(&showDiff, "diff", "D", false, "Show diff of changes that will be made.")
395398
cmd.Flags().BoolVarP(&skipProxyCommand, "skip-proxy-command", "", false, "Specifies whether the ProxyCommand option should be skipped. Useful for testing.")
396399
_ = cmd.Flags().MarkHidden("skip-proxy-command")
400+
cliflag.BoolVarP(cmd.Flags(), &usePreviousOpts, "use-previous-options", "", "CODER_SSH_USE_PREVIOUS_OPTIONS", false, "Specifies whether or not to keep options from previous run of config-ssh.")
397401

398402
// Deprecated: Remove after migration period.
399403
cmd.Flags().StringVar(&coderConfigFile, "test.ssh-coder-config-file", sshDefaultCoderConfigFileName, "Specifies the path to an Coder SSH config file. Useful for testing.")
400404
_ = cmd.Flags().MarkHidden("test.ssh-coder-config-file")
401405

406+
cliui.AllowSkipPrompt(cmd)
407+
402408
return cmd
403409
}
404410

cli/configssh_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,54 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
446446
{match: "Continue?", write: "no"},
447447
},
448448
},
449+
{
450+
name: "Do not prompt when using --yes",
451+
writeConfig: writeConfig{
452+
ssh: strings.Join([]string{
453+
headerStart,
454+
"# Last config-ssh options:",
455+
"# :ssh-option=ForwardAgent=yes",
456+
"#",
457+
headerEnd,
458+
"",
459+
}, "\n"),
460+
},
461+
wantConfig: wantConfig{
462+
ssh: strings.Join([]string{
463+
// Last options overwritten.
464+
baseHeader,
465+
"",
466+
}, "\n"),
467+
},
468+
args: []string{"--yes"},
469+
},
470+
{
471+
name: "Do not prompt for new options when prev opts flag is set",
472+
writeConfig: writeConfig{
473+
ssh: strings.Join([]string{
474+
headerStart,
475+
"# Last config-ssh options:",
476+
"# :ssh-option=ForwardAgent=yes",
477+
"#",
478+
headerEnd,
479+
"",
480+
}, "\n"),
481+
},
482+
wantConfig: wantConfig{
483+
ssh: strings.Join([]string{
484+
headerStart,
485+
"# Last config-ssh options:",
486+
"# :ssh-option=ForwardAgent=yes",
487+
"#",
488+
headerEnd,
489+
"",
490+
}, "\n"),
491+
},
492+
args: []string{
493+
"--use-previous-options",
494+
"--yes",
495+
},
496+
},
449497

450498
// Tests for deprecated split coder config.
451499
{

cli/server.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func server() *cobra.Command {
8383
oauth2GithubClientSecret string
8484
oauth2GithubAllowedOrganizations []string
8585
oauth2GithubAllowSignups bool
86+
telemetryEnable bool
8687
telemetryURL string
8788
tlsCertFile string
8889
tlsClientCAFile string
@@ -311,7 +312,11 @@ func server() *cobra.Command {
311312
if err != nil {
312313
return xerrors.Errorf("parse telemetry url: %w", err)
313314
}
314-
if !inMemoryDatabase || cmd.Flags().Changed("telemetry-url") {
315+
// Disable telemetry if the in-memory database is used unless explicitly defined!
316+
if inMemoryDatabase && !cmd.Flags().Changed("telemetry") {
317+
telemetryEnable = false
318+
}
319+
if telemetryEnable {
315320
options.Telemetry, err = telemetry.New(telemetry.Options{
316321
BuiltinPostgres: builtinPostgres,
317322
DeploymentID: deploymentID,
@@ -403,11 +408,6 @@ func server() *cobra.Command {
403408
errCh <- wg.Wait()
404409
}()
405410

406-
// This is helpful for tests, but can be silently ignored.
407-
// Coder may be ran as users that don't have permission to write in the homedir,
408-
// such as via the systemd service.
409-
_ = config.URL().Write(client.URL.String())
410-
411411
hasFirstUser, err := client.HasFirstUser(cmd.Context())
412412
if !hasFirstUser && err == nil {
413413
cmd.Println()
@@ -437,6 +437,12 @@ func server() *cobra.Command {
437437
stopChan := make(chan os.Signal, 1)
438438
defer signal.Stop(stopChan)
439439
signal.Notify(stopChan, os.Interrupt)
440+
441+
// This is helpful for tests, but can be silently ignored.
442+
// Coder may be ran as users that don't have permission to write in the homedir,
443+
// such as via the systemd service.
444+
_ = config.URL().Write(client.URL.String())
445+
440446
select {
441447
case <-cmd.Context().Done():
442448
coderAPI.Close()
@@ -531,6 +537,7 @@ func server() *cobra.Command {
531537
"Specifies organizations the user must be a member of to authenticate with GitHub.")
532538
cliflag.BoolVarP(root.Flags(), &oauth2GithubAllowSignups, "oauth2-github-allow-signups", "", "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS", false,
533539
"Specifies whether new users can sign up with GitHub.")
540+
cliflag.BoolVarP(root.Flags(), &telemetryEnable, "telemetry", "", "CODER_TELEMETRY", true, "Specifies whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.")
534541
cliflag.StringVarP(root.Flags(), &telemetryURL, "telemetry-url", "", "CODER_TELEMETRY_URL", "https://telemetry.coder.com", "Specifies a URL to send telemetry to.")
535542
_ = root.Flags().MarkHidden("telemetry-url")
536543
cliflag.BoolVarP(root.Flags(), &tlsEnable, "tls-enable", "", "CODER_TLS_ENABLE", false, "Specifies if TLS will be enabled")

cli/server_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ func TestServer(t *testing.T) {
259259
server := httptest.NewServer(r)
260260
t.Cleanup(server.Close)
261261

262-
root, _ := clitest.New(t, "server", "--in-memory", "--address", ":0", "--telemetry-url", server.URL)
262+
root, _ := clitest.New(t, "server", "--in-memory", "--address", ":0", "--telemetry", "--telemetry-url", server.URL)
263263
errC := make(chan error)
264264
go func() {
265265
errC <- root.ExecuteContext(ctx)

coderd/provisionerdaemons.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func (api *API) ListenProvisionerDaemon(ctx context.Context) (client proto.DRPCP
9696
},
9797
})
9898
go func() {
99-
err = server.Serve(ctx, serverSession)
99+
err := server.Serve(ctx, serverSession)
100100
if err != nil && !xerrors.Is(err, io.EOF) {
101101
api.Logger.Debug(ctx, "provisioner daemon disconnected", slog.Error(err))
102102
}

site/src/components/AppLink/AppLink.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,15 @@ export const AppLink: FC<AppLinkProps> = ({ userName, workspaceName, appName, ap
1616
const href = `/@${userName}/${workspaceName}/apps/${appName}`
1717

1818
return (
19-
<Link href={href} target="_blank" className={styles.link}>
19+
<Link
20+
href={href}
21+
target="_blank"
22+
className={styles.link}
23+
onClick={(event) => {
24+
event.preventDefault()
25+
window.open(href, appName, "width=900,height=600")
26+
}}
27+
>
2028
<img
2129
className={combineClasses([styles.icon, appIcon === "" ? "empty" : ""])}
2230
alt={`${appName} Icon`}

site/src/components/Tooltips/HelpTooltip/HelpTooltip.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export default {
1616
const Template: Story<HelpTooltipProps> = (args) => (
1717
<HelpTooltip {...args}>
1818
<HelpTooltipTitle>What is a template?</HelpTooltipTitle>
19-
<HelpTooltipText>A template is a common configuration for your team`&apos;`s workspaces.</HelpTooltipText>
19+
<HelpTooltipText>A template is a common configuration for your team&apos;s workspaces.</HelpTooltipText>
2020
<HelpTooltipLinksGroup>
2121
<HelpTooltipLink href="https://github.com/coder/coder/">Creating a template</HelpTooltipLink>
2222
<HelpTooltipLink href="https://github.com/coder/coder/">Updating a template</HelpTooltipLink>

site/src/components/Tooltips/HelpTooltip/HelpTooltip.tsx

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Popover from "@material-ui/core/Popover"
33
import { makeStyles } from "@material-ui/core/styles"
44
import HelpIcon from "@material-ui/icons/HelpOutline"
55
import OpenInNewIcon from "@material-ui/icons/OpenInNew"
6-
import React, { createContext, useContext, useState } from "react"
6+
import React, { createContext, useContext, useRef, useState } from "react"
77
import { Stack } from "../../Stack/Stack"
88

99
type Icon = typeof HelpIcon
@@ -29,31 +29,36 @@ const useHelpTooltip = () => {
2929

3030
export const HelpTooltip: React.FC<HelpTooltipProps> = ({ children, open, size = "medium" }) => {
3131
const styles = useStyles({ size })
32-
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)
33-
open = open ?? Boolean(anchorEl)
34-
const id = open ? "help-popover" : undefined
32+
const anchorRef = useRef<HTMLButtonElement>(null)
33+
const [isOpen, setIsOpen] = useState(!!open)
34+
const id = isOpen ? "help-popover" : undefined
3535

3636
const onClose = () => {
37-
setAnchorEl(null)
37+
setIsOpen(false)
3838
}
3939

4040
return (
4141
<>
4242
<button
43+
ref={anchorRef}
4344
aria-describedby={id}
4445
className={styles.button}
4546
onClick={(event) => {
4647
event.stopPropagation()
47-
setAnchorEl(event.currentTarget)
48+
setIsOpen(true)
49+
}}
50+
onMouseEnter={() => {
51+
setIsOpen(true)
4852
}}
4953
>
5054
<HelpIcon className={styles.icon} />
5155
</button>
5256
<Popover
57+
className={styles.popover}
5358
classes={{ paper: styles.popoverPaper }}
5459
id={id}
55-
open={open}
56-
anchorEl={anchorEl}
60+
open={isOpen}
61+
anchorEl={anchorRef.current}
5762
onClose={onClose}
5863
anchorOrigin={{
5964
vertical: "bottom",
@@ -63,8 +68,16 @@ export const HelpTooltip: React.FC<HelpTooltipProps> = ({ children, open, size =
6368
vertical: "top",
6469
horizontal: "left",
6570
}}
71+
PaperProps={{
72+
onMouseEnter: () => {
73+
setIsOpen(true)
74+
},
75+
onMouseLeave: () => {
76+
setIsOpen(false)
77+
},
78+
}}
6679
>
67-
<HelpTooltipContext.Provider value={{ open, onClose }}>{children}</HelpTooltipContext.Provider>
80+
<HelpTooltipContext.Provider value={{ open: isOpen, onClose }}>{children}</HelpTooltipContext.Provider>
6881
</Popover>
6982
</>
7083
)
@@ -166,11 +179,16 @@ const useStyles = makeStyles((theme) => ({
166179
height: ({ size }: { size?: Size }) => theme.spacing(getIconSpacingFromSize(size)),
167180
},
168181

182+
popover: {
183+
pointerEvents: "none",
184+
},
185+
169186
popoverPaper: {
170187
marginTop: theme.spacing(0.5),
171188
width: theme.spacing(38),
172189
padding: theme.spacing(2.5),
173190
color: theme.palette.text.secondary,
191+
pointerEvents: "auto",
174192
},
175193

176194
title: {

site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,68 @@
11
import { ComponentMeta, Story } from "@storybook/react"
22
import { spawn } from "xstate"
3-
import { ProvisionerJobStatus, Workspace, WorkspaceTransition } from "../../api/typesGenerated"
3+
import { ProvisionerJobStatus, WorkspaceTransition } from "../../api/typesGenerated"
44
import { MockWorkspace } from "../../testHelpers/entities"
55
import { workspaceFilterQuery } from "../../util/workspace"
6-
import { workspaceItemMachine } from "../../xServices/workspaces/workspacesXService"
6+
import { workspaceItemMachine, WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
77
import { WorkspacesPageView, WorkspacesPageViewProps } from "./WorkspacesPageView"
88

9-
export default {
10-
title: "pages/WorkspacesPageView",
11-
component: WorkspacesPageView,
12-
} as ComponentMeta<typeof WorkspacesPageView>
13-
14-
const Template: Story<WorkspacesPageViewProps> = (args) => <WorkspacesPageView {...args} />
15-
16-
const createWorkspaceWithStatus = (
9+
const createWorkspaceItemRef = (
1710
status: ProvisionerJobStatus,
1811
transition: WorkspaceTransition = "start",
1912
outdated = false,
20-
): Workspace => {
21-
return {
22-
...MockWorkspace,
23-
outdated,
24-
latest_build: {
25-
...MockWorkspace.latest_build,
26-
transition,
27-
job: {
28-
...MockWorkspace.latest_build.job,
29-
status: status,
13+
): WorkspaceItemMachineRef => {
14+
return spawn(
15+
workspaceItemMachine.withContext({
16+
data: {
17+
...MockWorkspace,
18+
outdated,
19+
latest_build: {
20+
...MockWorkspace.latest_build,
21+
transition,
22+
job: {
23+
...MockWorkspace.latest_build.job,
24+
status: status,
25+
},
26+
},
3027
},
31-
},
32-
}
28+
}),
29+
)
3330
}
3431

3532
// This is type restricted to prevent future statuses from slipping
3633
// through the cracks unchecked!
37-
const workspaces: { [key in ProvisionerJobStatus]: Workspace } = {
38-
canceled: createWorkspaceWithStatus("canceled"),
39-
canceling: createWorkspaceWithStatus("canceling"),
40-
failed: createWorkspaceWithStatus("failed"),
41-
pending: createWorkspaceWithStatus("pending"),
42-
running: createWorkspaceWithStatus("running"),
43-
succeeded: createWorkspaceWithStatus("succeeded"),
34+
const workspaces: { [key in ProvisionerJobStatus]: WorkspaceItemMachineRef } = {
35+
canceled: createWorkspaceItemRef("canceled"),
36+
canceling: createWorkspaceItemRef("canceling"),
37+
failed: createWorkspaceItemRef("failed"),
38+
pending: createWorkspaceItemRef("pending"),
39+
running: createWorkspaceItemRef("running"),
40+
succeeded: createWorkspaceItemRef("succeeded"),
41+
}
42+
43+
const additionalWorkspaces: Record<string, WorkspaceItemMachineRef> = {
44+
runningAndStop: createWorkspaceItemRef("running", "stop"),
45+
succeededAndStop: createWorkspaceItemRef("succeeded", "stop"),
46+
runningAndDelete: createWorkspaceItemRef("running", "delete"),
47+
outdated: createWorkspaceItemRef("running", "delete", true),
4448
}
4549

50+
export default {
51+
title: "pages/WorkspacesPageView",
52+
component: WorkspacesPageView,
53+
argTypes: {
54+
workspaceRefs: {
55+
options: [...Object.keys(workspaces), ...Object.keys(additionalWorkspaces)],
56+
mapping: { ...workspaces, ...additionalWorkspaces },
57+
},
58+
},
59+
} as ComponentMeta<typeof WorkspacesPageView>
60+
61+
const Template: Story<WorkspacesPageViewProps> = (args) => <WorkspacesPageView {...args} />
62+
4663
export const AllStates = Template.bind({})
4764
AllStates.args = {
48-
workspaceRefs: [
49-
...Object.values(workspaces),
50-
createWorkspaceWithStatus("running", "stop"),
51-
createWorkspaceWithStatus("succeeded", "stop"),
52-
createWorkspaceWithStatus("running", "delete"),
53-
].map((data) => spawn(workspaceItemMachine.withContext({ data }))),
65+
workspaceRefs: [...Object.values(workspaces), ...Object.values(additionalWorkspaces)],
5466
}
5567

5668
export const OwnerHasNoWorkspaces = Template.bind({})

0 commit comments

Comments
 (0)