Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(site): add create template from scratch#12082

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
BrunoQuaresma merged 14 commits intomainfrombq/add-scratch-template-to-starter
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
14 commits
Select commitHold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletioncli/testdata/coder_templates_init_--help.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ USAGE:
Get started with a templated template.

OPTIONS:
--id aws-devcontainer|aws-linux|aws-windows|azure-linux|do-linux|docker|gcp-devcontainer|gcp-linux|gcp-vm-container|gcp-windows|kubernetes|nomad-docker
--id aws-devcontainer|aws-linux|aws-windows|azure-linux|do-linux|docker|gcp-devcontainer|gcp-linux|gcp-vm-container|gcp-windows|kubernetes|nomad-docker|scratch
Specify a given example template by ID.

———
Expand Down
6 changes: 3 additions & 3 deletionsdocs/cli/templates_init.md
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

9 changes: 9 additions & 0 deletionsexamples/examples.gen.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -156,5 +156,14 @@
"container"
],
"markdown": "\n# Remote Development on Nomad\n\nProvision Nomad Jobs as [Coder workspaces](https://coder.com/docs/v2/latest/workspaces) with this example template. This example shows how to use Nomad service tasks to be used as a development environment using docker and host csi volumes.\n\n\u003c!-- TODO: Add screenshot --\u003e\n\n\u003e **Note**\n\u003e This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case.\n\n## Prerequisites\n\n- [Nomad](https://www.nomadproject.io/downloads)\n- [Docker](https://docs.docker.com/get-docker/)\n\n## Setup\n\n### 1. Start the CSI Host Volume Plugin\n\nThe CSI Host Volume plugin is used to mount host volumes into Nomad tasks. This is useful for development environments where you want to mount persistent volumes into your container workspace.\n\n1. Login to the Nomad server using SSH.\n\n2. Append the following stanza to your Nomad server configuration file and restart the nomad service.\n\n ```hcl\n plugin \"docker\" {\n config {\n allow_privileged = true\n }\n }\n ```\n\n ```shell\n sudo systemctl restart nomad\n ```\n\n3. Create a file `hostpath.nomad` with following content:\n\n ```hcl\n job \"hostpath-csi-plugin\" {\n datacenters = [\"dc1\"]\n type = \"system\"\n\n group \"csi\" {\n task \"plugin\" {\n driver = \"docker\"\n\n config {\n image = \"registry.k8s.io/sig-storage/hostpathplugin:v1.10.0\"\n\n args = [\n \"--drivername=csi-hostpath\",\n \"--v=5\",\n \"--endpoint=${CSI_ENDPOINT}\",\n \"--nodeid=node-${NOMAD_ALLOC_INDEX}\",\n ]\n\n privileged = true\n }\n\n csi_plugin {\n id = \"hostpath\"\n type = \"monolith\"\n mount_dir = \"/csi\"\n }\n\n resources {\n cpu = 256\n memory = 128\n }\n }\n }\n }\n ```\n\n4. Run the job:\n\n ```shell\n nomad job run hostpath.nomad\n ```\n\n### 2. Setup the Nomad Template\n\n1. Create the template by running the following command:\n\n ```shell\n coder template init nomad-docker\n cd nomad-docker\n coder template push\n ```\n\n2. Set up Nomad server address and optional authentication:\n\n3. Create a new workspace and start developing.\n"
},
{
"id": "scratch",
"url": "",
"name": "Scratch",
"description": "A minimal starter template for Coder",
"icon": "/emojis/1f4e6.png",
"tags": [],
"markdown": "\n# A minimal Scaffolding for a Coder Template\n\nUse this starter template as a basis to create your own unique template from scratch.\n"
}
]
1 change: 1 addition & 0 deletionsexamples/examples.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,7 @@ var (
//go:embed templates/gcp-windows
//go:embed templates/kubernetes
//go:embed templates/nomad-docker
//go:embed templates/scratch
files embed.FS

exampleBasePath = "https://github.com/coder/coder/tree/main/examples/templates/"
Expand Down
12 changes: 12 additions & 0 deletionsexamples/templates/scratch/README.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
---
display_name: Scratch
description: A minimal starter template for Coder
icon: ../../../site/static/emojis/1f4e6.png
maintainer_github: coder
verified: true
tags: []
---

# A minimal Scaffolding for a Coder Template

Use this starter template as a basis to create your own unique template from scratch.
62 changes: 62 additions & 0 deletionsexamples/templates/scratch/main.tf
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
}
}

data "coder_provisioner" "me" {}

data "coder_workspace" "me" {}

resource "coder_agent" "main" {
arch = data.coder_provisioner.me.arch
os = data.coder_provisioner.me.os

metadata {
display_name = "CPU Usage"
key = "0_cpu_usage"
script = "coder stat cpu"
interval = 10
timeout = 1
}

metadata {
display_name = "RAM Usage"
key = "1_ram_usage"
script = "coder stat mem"
interval = 10
timeout = 1
}
}

# Use this to set environment variables in your workspace
# details: https://registry.terraform.io/providers/coder/coder/latest/docs/resources/env
resource "coder_env" "welcome_message" {
agent_id = coder_agent.main.id
name = "WELCOME_MESSAGE"
value = "Welcome to your Coder workspace!"
}

# Adds code-server
# See all available modules at https://registry.coder.com
module "code-server" {
source = "registry.coder.com/modules/code-server/coder"
version = "1.0.2"
agent_id = coder_agent.main.id
}

# Runs a script at workspace start/stop or on a cron schedule
# details: https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script
resource "coder_script" "startup_script" {
agent_id = coder_agent.main.id
display_name = "Startup Script"
script = <<-EOF
#!/bin/sh
set -e
# Run programs at workspace startup
EOF
run_on_start = true
start_blocks_login = true
}
2 changes: 1 addition & 1 deletionsite/e2e/helpers.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -134,7 +134,7 @@ export const createTemplate = async (
const name = randomName();
await page.getByLabel("Name *").fill(name);
await page.getByTestId("form-submit").click();
await expect(page).toHaveURL("/templates/" +name, {
await expect(page).toHaveURL(`/templates/${name}/files`, {
timeout: 30000,
});
return name;
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ test("Create template from starter template", async () => {
);
await waitFor(() => expect(API.createTemplate).toBeCalledTimes(1));
expect(router.state.location.pathname).toEqual(
`/templates/${MockTemplate.name}`,
`/templates/${MockTemplate.name}/files`,
);
expect(API.createTemplateVersion).toHaveBeenCalledWith(MockOrganization.id, {
example_id: "aws-windows",
Expand DownExpand Up@@ -138,7 +138,7 @@ test("Create template from duplicating a template", async () => {
);
await waitFor(() => {
expect(router.state.location.pathname).toEqual(
`/templates/${MockTemplate.name}`,
`/templates/${MockTemplate.name}/files`,
);
});
});
11 changes: 8 additions & 3 deletionssite/src/pages/CreateTemplatePage/CreateTemplatePage.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,11 +6,16 @@ import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizont
import { DuplicateTemplateView } from "./DuplicateTemplateView";
import { ImportStarterTemplateView } from "./ImportStarterTemplateView";
import { UploadTemplateView } from "./UploadTemplateView";
import { Template } from "api/typesGenerated";

const CreateTemplatePage: FC = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();

const onSuccess = (template: Template) => {
navigate(`/templates/${template.name}/files`);
};

const onCancel = () => {
navigate(-1);
};
Expand All@@ -23,11 +28,11 @@ const CreateTemplatePage: FC = () => {

<FullPageHorizontalForm title="Create Template" onCancel={onCancel}>
{searchParams.has("fromTemplate") ? (
<DuplicateTemplateView />
<DuplicateTemplateViewonSuccess={onSuccess}/>
) : searchParams.has("exampleId") ? (
<ImportStarterTemplateView />
<ImportStarterTemplateViewonSuccess={onSuccess}/>
) : (
<UploadTemplateView />
<UploadTemplateViewonSuccess={onSuccess}/>
)}
</FullPageHorizontalForm>
</>
Expand Down
11 changes: 9 additions & 2 deletionssite/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,15 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { CreateTemplateForm } from "./CreateTemplateForm";
import { firstVersionFromFile, getFormPermissions, newTemplate } from "./utils";
import { Template } from "api/typesGenerated";

export const DuplicateTemplateView: FC = () => {
type DuplicateTemplateViewProps = {
onSuccess: (template: Template) => void;
};

export const DuplicateTemplateView: FC<DuplicateTemplateViewProps> = ({
onSuccess,
}) => {
const navigate = useNavigate();
const organizationId = useOrganizationId();
const [searchParams] = useSearchParams();
Expand DownExpand Up@@ -79,7 +86,7 @@ export const DuplicateTemplateView: FC = () => {
),
template: newTemplate(formData),
});
navigate(`/templates/${template.name}`);
onSuccess(template);
}}
/>
);
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,8 +18,15 @@ import {
getFormPermissions,
newTemplate,
} from "./utils";
import { Template } from "api/typesGenerated";

export const ImportStarterTemplateView: FC = () => {
type ImportStarterTemplateViewProps = {
onSuccess: (template: Template) => void;
};

export const ImportStarterTemplateView: FC<ImportStarterTemplateViewProps> = ({
onSuccess,
}) => {
const navigate = useNavigate();
const organizationId = useOrganizationId();
const [searchParams] = useSearchParams();
Expand DownExpand Up@@ -76,7 +83,7 @@ export const ImportStarterTemplateView: FC = () => {
),
template: newTemplate(formData),
});
navigate(`/templates/${template.name}`);
onSuccess(template);
}}
/>
);
Expand Down
12 changes: 10 additions & 2 deletionssite/src/pages/CreateTemplatePage/UploadTemplateView.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,8 +11,16 @@ import { useOrganizationId } from "contexts/auth/useOrganizationId";
import { useDashboard } from "modules/dashboard/useDashboard";
import { CreateTemplateForm } from "./CreateTemplateForm";
import { firstVersionFromFile, getFormPermissions, newTemplate } from "./utils";
import { Template } from "api/typesGenerated";
import { FC } from "react";

export const UploadTemplateView = () => {
type UploadTemplateViewProps = {
onSuccess: (template: Template) => void;
};

export const UploadTemplateView: FC<UploadTemplateViewProps> = ({
onSuccess,
}) => {
const navigate = useNavigate();
const organizationId = useOrganizationId();

Expand DownExpand Up@@ -61,7 +69,7 @@ export const UploadTemplateView = () => {
),
template: newTemplate(formData),
});
navigate(`/templates/${template.name}`);
onSuccess(template);
}}
/>
);
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
import { render, screen } from "@testing-library/react";
import StarterTemplatesPage from "./StarterTemplatesPage";
import { AppProviders } from "App";
import { RouterProvider, createMemoryRouter } from "react-router-dom";
import { RequireAuth } from "contexts/auth/RequireAuth";
import { rest } from "msw";
import {
MockTemplateExample,
MockTemplateExample2,
} from "testHelpers/entities";
import { server } from "testHelpers/server";

test("does not display the scratch template", async () => {
server.use(
rest.get(
"api/v2/organizations/:organizationId/templates/examples",
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json([
MockTemplateExample,
MockTemplateExample2,
{
...MockTemplateExample,
id: "scratch",
name: "Scratch",
description: "Create a template from scratch",
},
]),
);
},
),
);

render(
<AppProviders>
<RouterProvider
router={createMemoryRouter(
[
{
element: <RequireAuth />,
children: [
{
path: "/starter-templates",
element: <StarterTemplatesPage />,
},
],
},
],
{ initialEntries: ["/starter-templates"] },
)}
/>
</AppProviders>,
);

await screen.findByText(MockTemplateExample.name);
screen.getByText(MockTemplateExample2.name);
expect(screen.queryByText("Scratch")).not.toBeInTheDocument();
});
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,12 +6,14 @@ import { useOrganizationId } from "contexts/auth/useOrganizationId";
import { pageTitle } from "utils/page";
import { getTemplatesByTag } from "utils/starterTemplates";
import { StarterTemplatesPageView } from "./StarterTemplatesPageView";
import { TemplateExample } from "api/typesGenerated";

const StarterTemplatesPage: FC = () => {
const organizationId = useOrganizationId();
const templateExamplesQuery = useQuery(templateExamples(organizationId));
const starterTemplatesByTag = templateExamplesQuery.data
? getTemplatesByTag(templateExamplesQuery.data)
? // Currently, the scratch template should not be displayed on the starter templates page.
getTemplatesByTag(removeScratchExample(templateExamplesQuery.data))
: undefined;

return (
Expand All@@ -28,4 +30,8 @@ const StarterTemplatesPage: FC = () => {
);
};

const removeScratchExample = (data: TemplateExample[]) => {
return data.filter((example) => example.id !== "scratch");
};

export default StarterTemplatesPage;
22 changes: 22 additions & 0 deletionssite/src/pages/TemplatesPage/CreateTemplateButton.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
import { CreateTemplateButton } from "./CreateTemplateButton";
import type { Meta, StoryObj } from "@storybook/react";
import { userEvent, screen } from "@storybook/test";

const meta: Meta<typeof CreateTemplateButton> = {
title: "pages/TemplatesPage/CreateTemplateButton",
component: CreateTemplateButton,
};

export default meta;
type Story = StoryObj<typeof CreateTemplateButton>;

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"));
});
},
};
Loading

[8]ページ先頭

©2009-2025 Movatter.jp