diff --git a/scripts/build_windows_installer.sh b/scripts/build_windows_installer.sh index 3b4d15a3cee9c..1a20a2cca3fb3 100755 --- a/scripts/build_windows_installer.sh +++ b/scripts/build_windows_installer.sh @@ -19,6 +19,7 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" agpl="${CODER_BUILD_AGPL:-0}" output_path="" version="" +sign_windows="${CODER_SIGN_WINDOWS:-0}" args="$(getopt -o "" -l agpl,output:,version: -- "$@")" eval set -- "$args" @@ -51,6 +52,11 @@ if [[ "$output_path" == "" ]]; then error "--output is a required parameter" fi +if [[ "$sign_windows" == 1 ]]; then + dependencies java + requiredenvs JSIGN_PATH EV_KEYSTORE EV_KEY EV_CERTIFICATE_PATH EV_TSA_URL GCLOUD_ACCESS_TOKEN +fi + if [[ "$#" != 1 ]]; then error "Exactly one argument must be provided to this script, $# were supplied" fi @@ -125,3 +131,7 @@ popd cp "$temp_dir/installer.exe" "$output_path" rm -rf "$temp_dir" + +if [[ "$sign_windows" == 1 ]]; then + execrelative ./sign_windows.sh "$output_path" 1>&2 +fi diff --git a/site/src/pages/TemplatesPage/CreateTemplateButton.stories.tsx b/site/src/pages/TemplatesPage/CreateTemplateButton.stories.tsx new file mode 100644 index 0000000000000..f79e3e398b586 --- /dev/null +++ b/site/src/pages/TemplatesPage/CreateTemplateButton.stories.tsx @@ -0,0 +1,22 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { screen, userEvent } from "@storybook/test"; +import { CreateTemplateButton } from "./CreateTemplateButton"; + +const meta: Meta = { + title: "pages/TemplatesPage/CreateTemplateButton", + component: CreateTemplateButton, +}; + +export default meta; +type Story = StoryObj; + +export const Close: Story = {}; + +export const Open: Story = { + play: async ({ step }) => { + const user = userEvent.setup(); + await step("click on trigger", async () => { + await user.click(screen.getByRole("button")); + }); + }, +}; diff --git a/site/src/pages/TemplatesPage/CreateTemplateButton.tsx b/site/src/pages/TemplatesPage/CreateTemplateButton.tsx new file mode 100644 index 0000000000000..d3e2dc5ef2d64 --- /dev/null +++ b/site/src/pages/TemplatesPage/CreateTemplateButton.tsx @@ -0,0 +1,56 @@ +import AddIcon from "@mui/icons-material/AddOutlined"; +import Inventory2 from "@mui/icons-material/Inventory2"; +import NoteAddOutlined from "@mui/icons-material/NoteAddOutlined"; +import UploadOutlined from "@mui/icons-material/UploadOutlined"; +import Button from "@mui/material/Button"; +import type { FC } from "react"; +import { + MoreMenu, + MoreMenuContent, + MoreMenuItem, + MoreMenuTrigger, +} from "components/MoreMenu/MoreMenu"; + +type CreateTemplateButtonProps = { + onNavigate: (path: string) => void; +}; + +export const CreateTemplateButton: FC = ({ + onNavigate, +}) => { + return ( + + + + + + { + onNavigate("/templates/new?exampleId=scratch"); + }} + > + + From scratch + + { + onNavigate("/templates/new"); + }} + > + + Upload template + + { + onNavigate("/starter-templates"); + }} + > + + Choose a starter template + + + + ); +}; diff --git a/site/src/pages/TemplatesPage/TemplatesPage.test.tsx b/site/src/pages/TemplatesPage/TemplatesPage.test.tsx index 535949b6d9dee..670362416338b 100644 --- a/site/src/pages/TemplatesPage/TemplatesPage.test.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPage.test.tsx @@ -17,7 +17,7 @@ test("create template from scratch", async () => { element: , }, { - path: "/starter-templates", + path: "/templates/new", element:
, }, ], @@ -34,6 +34,9 @@ test("create template from scratch", async () => { name: "Create Template", }); await user.click(createTemplateButton); + const fromScratchMenuItem = await screen.findByText("From scratch"); + await user.click(fromScratchMenuItem); await screen.findByTestId("new-template-page"); - expect(router.state.location.pathname).toBe("/starter-templates"); + expect(router.state.location.pathname).toBe("/templates/new"); + expect(router.state.location.search).toBe("?exampleId=scratch"); }); diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx index 17092e2705ee7..e7134ac65f9a6 100644 --- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx @@ -38,6 +38,7 @@ import { TableRowSkeleton, } from "components/TableLoader/TableLoader"; import { useClickableTableRow } from "hooks/useClickableTableRow"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { linkToTemplate, useLinks } from "modules/navigation"; import { createDayString } from "utils/createDayString"; import { docs } from "utils/docs"; @@ -45,6 +46,7 @@ import { formatTemplateBuildTime, formatTemplateActiveDevelopers, } from "utils/templates"; +import { CreateTemplateButton } from "./CreateTemplateButton"; import { EmptyTemplates } from "./EmptyTemplates"; export const Language = { @@ -167,38 +169,40 @@ export const TemplatesPageView: FC = ({ examples, canCreateTemplates, }) => { + const { experiments } = useDashboard(); const isLoading = !templates; const isEmpty = templates && templates.length === 0; const navigate = useNavigate(); + const multiOrgExperimentEnabled = experiments.includes("multi-organization"); + + const createTemplateAction = () => { + return multiOrgExperimentEnabled ? ( + + ) : ( + + ); + }; return ( - } - variant="contained" - onClick={() => { - navigate("/starter-templates"); - }} - > - Create Template - - ) - } - > + Templates - {templates && templates.length > 0 && ( - - Select a template to create a workspace. - - )} + + Select a template to create a workspace. + {error ? ( @@ -212,7 +216,7 @@ export const TemplatesPageView: FC = ({ {Language.usedByLabel} {Language.buildTimeLabel} {Language.lastUpdatedLabel} - +