refactor(examples): pre-parse frontmatter via scripts/examplegen (#9514)

* refactor(examples): pre-parse frontmatter via scripts/examplegen

This removes 2 MB from the slim binary.

Ref: #9380
This commit is contained in:
Mathias Fredriksson 2023-09-04 20:15:29 +03:00 committed by GitHub
parent 6fc1f5276d
commit a1025f92af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 380 additions and 101 deletions

View File

@ -83,3 +83,6 @@ scripts/apitypings/testdata/**/*.ts
site/e2e/provisionerGenerated.ts
**/pnpm-lock.yaml
# Ignore generated JSON (e.g. examples/examples.gen.json).
**/*.gen.json

View File

@ -13,3 +13,6 @@ scripts/apitypings/testdata/**/*.ts
site/e2e/provisionerGenerated.ts
**/pnpm-lock.yaml
# Ignore generated JSON (e.g. examples/examples.gen.json).
**/*.gen.json

View File

@ -471,7 +471,8 @@ gen: \
site/.prettierrc.yaml \
site/.prettierignore \
site/.eslintignore \
site/e2e/provisionerGenerated.ts
site/e2e/provisionerGenerated.ts \
examples/examples.gen.json
.PHONY: gen
# Mark all generated files as fresh so make thinks they're up-to-date. This is
@ -494,6 +495,7 @@ gen/mark-fresh:
site/.prettierignore \
site/.eslintignore \
site/e2e/provisionerGenerated.ts \
examples/examples.gen.json \
"
for file in $$files; do
echo "$$file"
@ -545,6 +547,10 @@ site/e2e/provisionerGenerated.ts:
../scripts/pnpm_install.sh
pnpm run gen:provisioner
examples/examples.gen.json: scripts/examplegen/main.go examples/examples.go $(shell find ./examples/templates)
go run ./scripts/examplegen/main.go > examples/examples.gen.json
coderd/rbac/object_gen.go: scripts/rbacgen/main.go coderd/rbac/object.go
go run scripts/rbacgen/main.go ./coderd/rbac > coderd/rbac/object_gen.go
@ -552,7 +558,7 @@ docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/me
go run scripts/metricsdocgen/main.go
pnpm run format:write:only ./docs/admin/prometheus.md
docs/cli.md: scripts/clidocgen/main.go $(GO_SRC_FILES)
docs/cli.md: scripts/clidocgen/main.go examples/examples.gen.json $(GO_SRC_FILES)
BASE_PATH="." go run ./scripts/clidocgen
pnpm run format:write:only ./docs/cli.md ./docs/cli/*.md ./docs/manifest.json
@ -605,7 +611,7 @@ site/.prettierrc.yaml: .prettierrc.yaml
# - ./ -> ../
# - ./site -> ./
yq \
'.overrides[].files |= map(. | sub("^./"; "") | sub("^"; "../") | sub("../site/"; "./"))' \
'.overrides[].files |= map(. | sub("^./"; "") | sub("^"; "../") | sub("../site/"; "./") | sub("../!"; "!../"))' \
"$<" >> "$@"
# Combine .gitignore with .prettierignore.include to generate .prettierignore.

149
examples/examples.gen.json Normal file
View File

@ -0,0 +1,149 @@
// Code generated by examplegen. DO NOT EDIT.
[
{
"id": "aws-ecs-container",
"url": "",
"name": "Develop in an ECS-hosted container",
"description": "Get started with Linux development on AWS ECS.",
"icon": "/icon/aws.png",
"tags": [
"cloud",
"aws"
],
"markdown": "\n# aws-ecs\n\nThis is a sample template for running a Coder workspace on ECS. It assumes there\nis a pre-existing ECS cluster with EC2-based compute to host the workspace.\n\n## Architecture\n\nThis workspace is built using the following AWS resources:\n\n- Task definition - the container definition, includes the image, command, volume(s)\n- ECS service - manages the task definition\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n"
},
{
"id": "aws-linux",
"url": "",
"name": "Develop in Linux on AWS EC2",
"description": "Get started with Linux development on AWS EC2.",
"icon": "/icon/aws.png",
"tags": [
"cloud",
"aws"
],
"markdown": "\n# aws-linux\n\nTo get started, run `coder templates init`. When prompted, select this template.\nFollow the on-screen instructions to proceed.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith AWS. For example, run `aws configure import` to import credentials on the\nsystem and user running coderd. For other ways to authenticate [consult the\nTerraform docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration).\n\n## Required permissions / policy\n\nThe following sample policy allows Coder to create EC2 instances and modify\ninstances provisioned by Coder:\n\n```json\n{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Sid\": \"VisualEditor0\",\n \"Effect\": \"Allow\",\n \"Action\": [\n \"ec2:GetDefaultCreditSpecification\",\n \"ec2:DescribeIamInstanceProfileAssociations\",\n \"ec2:DescribeTags\",\n \"ec2:DescribeInstances\",\n \"ec2:DescribeInstanceTypes\",\n \"ec2:CreateTags\",\n \"ec2:RunInstances\",\n \"ec2:DescribeInstanceCreditSpecifications\",\n \"ec2:DescribeImages\",\n \"ec2:ModifyDefaultCreditSpecification\",\n \"ec2:DescribeVolumes\"\n ],\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"CoderResources\",\n \"Effect\": \"Allow\",\n \"Action\": [\n \"ec2:DescribeInstanceAttribute\",\n \"ec2:UnmonitorInstances\",\n \"ec2:TerminateInstances\",\n \"ec2:StartInstances\",\n \"ec2:StopInstances\",\n \"ec2:DeleteTags\",\n \"ec2:MonitorInstances\",\n \"ec2:CreateTags\",\n \"ec2:RunInstances\",\n \"ec2:ModifyInstanceAttribute\",\n \"ec2:ModifyInstanceCreditSpecification\"\n ],\n \"Resource\": \"arn:aws:ec2:*:*:instance/*\",\n \"Condition\": {\n \"StringEquals\": {\n \"aws:ResourceTag/Coder_Provisioned\": \"true\"\n }\n }\n }\n ]\n}\n```\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n"
},
{
"id": "aws-windows",
"url": "",
"name": "Develop in Windows on AWS",
"description": "Get started with Windows development on AWS.",
"icon": "/icon/aws.png",
"tags": [
"cloud",
"aws"
],
"markdown": "\n# aws-windows\n\n## Getting started\n\nTo get started, run `coder templates init`. When prompted, select this template.\nFollow the on-screen instructions to proceed.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith AWS. For example, run `aws configure import` to import credentials on the\nsystem and user running coderd. For other ways to authenticate [consult the\nTerraform docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration).\n\n## Required permissions / policy\n\nThe following sample policy allows Coder to create EC2 instances and modify\ninstances provisioned by Coder:\n\n```json\n{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Sid\": \"VisualEditor0\",\n \"Effect\": \"Allow\",\n \"Action\": [\n \"ec2:GetDefaultCreditSpecification\",\n \"ec2:DescribeIamInstanceProfileAssociations\",\n \"ec2:DescribeTags\",\n \"ec2:DescribeInstances\",\n \"ec2:DescribeInstanceTypes\",\n \"ec2:CreateTags\",\n \"ec2:RunInstances\",\n \"ec2:DescribeInstanceCreditSpecifications\",\n \"ec2:DescribeImages\",\n \"ec2:ModifyDefaultCreditSpecification\",\n \"ec2:DescribeVolumes\"\n ],\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"CoderResources\",\n \"Effect\": \"Allow\",\n \"Action\": [\n \"ec2:DescribeInstanceAttribute\",\n \"ec2:UnmonitorInstances\",\n \"ec2:TerminateInstances\",\n \"ec2:StartInstances\",\n \"ec2:StopInstances\",\n \"ec2:DeleteTags\",\n \"ec2:MonitorInstances\",\n \"ec2:CreateTags\",\n \"ec2:RunInstances\",\n \"ec2:ModifyInstanceAttribute\",\n \"ec2:ModifyInstanceCreditSpecification\"\n ],\n \"Resource\": \"arn:aws:ec2:*:*:instance/*\",\n \"Condition\": {\n \"StringEquals\": {\n \"aws:ResourceTag/Coder_Provisioned\": \"true\"\n }\n }\n }\n ]\n}\n```\n"
},
{
"id": "azure-linux",
"url": "",
"name": "Develop in Linux on Azure",
"description": "Get started with Linux development on Microsoft Azure.",
"icon": "/icon/azure.png",
"tags": [
"cloud",
"azure",
"linux"
],
"markdown": "\n# azure-linux\n\nTo get started, run `coder templates init`. When prompted, select this template.\nFollow the on-screen instructions to proceed.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith Azure. For example, run `az login` then `az account set --subscription=\u003cid\u003e`\nto import credentials on the system and user running coderd. For other ways to\nauthenticate [consult the Terraform docs](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs#authenticating-to-azure).\n"
},
{
"id": "do-linux",
"url": "",
"name": "Develop in Linux on a Digital Ocean Droplet",
"description": "Get started with Linux development on a Digital Ocean Droplet.",
"icon": "/icon/do.png",
"tags": [
"cloud",
"digitalocean"
],
"markdown": "\n# do-linux\n\nTo deploy workspaces as DigitalOcean Droplets, you'll need:\n\n- DigitalOcean [personal access token\n (PAT)](https://docs.digitalocean.com/reference/api/create-personal-access-token/)\n\n- DigitalOcean project ID (you can get your project information via the `doctl`\n CLI by running `doctl projects list`)\n\n - Remove the following sections from the `main.tf` file if you don't want to\n associate your workspaces with a project:\n\n - `variable \"step2_do_project_id\"`\n - `resource \"digitalocean_project_resources\" \"project\"`\n\n- **Optional:** DigitalOcean SSH key ID (obtain via the `doctl` CLI by running\n `doctl compute ssh-key list`)\n\n - Note that this is only required for Fedora images to work.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith Digital Ocean. Obtain a [Digital Ocean Personal Access\nToken](https://cloud.digitalocean.com/account/api/tokens) and set the\nenvironment variable `DIGITALOCEAN_TOKEN` to the access token before starting\ncoderd. For other ways to authenticate [consult the Terraform\ndocs](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs).\n"
},
{
"id": "docker",
"url": "",
"name": "Develop in Docker",
"description": "Develop inside Docker containers using your local daemon",
"icon": "/icon/docker.png",
"tags": [
"local",
"docker"
],
"markdown": "\n# docker\n\nTo get started, run `coder templates init`. When prompted, select this template.\nFollow the on-screen instructions to proceed.\n\n## Editing the image\n\nEdit the `Dockerfile` and run `coder templates push` to update workspaces.\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n\n## Extending this template\n\nSee the [kreuzwerker/docker](https://registry.terraform.io/providers/kreuzwerker/docker) Terraform provider documentation to\nadd the following features to your Coder template:\n\n- SSH/TCP docker host\n- Registry authentication\n- Build args\n- Volume mounts\n- Custom container spec\n- More\n\nWe also welcome contributions!\n"
},
{
"id": "docker-with-dotfiles",
"url": "",
"name": "Develop in Docker with a dotfiles URL",
"description": "Develop inside Docker containers using your local daemon",
"icon": "/icon/docker.png",
"tags": [
"local",
"docker"
],
"markdown": "\n# docker-with-dotfiles\n\nThis is an example for deploying workspaces with a prompt for the users' dotfiles repo URI.\n\n## Getting started\n\nRun `coder templates init` and select this template. Follow the instructions that appear.\n\n## How it works\n\nDuring workspace creation, Coder prompts you to specify a dotfiles URL via a Terraform variable. Once the workspace starts, the Coder agent runs `coder dotfiles` via the startup script:\n\n```hcl\nvariable \"dotfiles_uri\" {\n description = \u003c\u003c-EOF\n Dotfiles repo URI (optional)\n\n see https://dotfiles.github.io\n EOF\n # The codercom/enterprise-* images are only built for amd64\n default = \"\"\n}\n\nresource \"coder_agent\" \"main\" {\n ...\n startup_script = var.dotfiles_uri != \"\" ? \"/tmp/tmp.coder*/coder dotfiles -y ${var.dotfiles_uri}\" : null\n}\n```\n\n# Managing images and workspaces\n\nRefer to the documentation in the [Docker template](../docker/README.md).\n"
},
{
"id": "gcp-linux",
"url": "",
"name": "Develop in Linux on Google Cloud",
"description": "Get started with Linux development on Google Cloud.",
"icon": "/icon/gcp.png",
"tags": [
"cloud",
"google"
],
"markdown": "\n# gcp-linux\n\nTo get started, run `coder templates init`. When prompted, select this template,\nand follow the on-screen instructions to proceed.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith Google Cloud. For example, run `gcloud auth application-default login` to\nimport credentials on the system and user running coderd. For other ways to\nauthenticate [consult the Terraform\ndocs](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started#adding-credentials).\n\n## Service account\n\nCoder requires a Google Cloud Service Account to provision workspaces. To create\na service account:\n\n1. Navigate to the [CGP\n console](https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create),\n and select your Cloud project (if you have more than one project associated\n with your account)\n\n1. Provide a service account name (this name is used to generate the service\n account ID)\n\n1. Click **Create and continue**, and choose the following IAM roles to grant to\n the service account:\n\n - Compute Admin\n - Service Account User\n\n Click **Continue**.\n\n1. Click on the created key, and navigate to the **Keys** tab.\n\n1. Click **Add key** \u003e **Create new key**.\n\n1. Generate a **JSON private key**, which will be what you provide to Coder\n during the setup process.\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n"
},
{
"id": "gcp-vm-container",
"url": "",
"name": "Develop in a container on a Google Cloud VM",
"description": "Get started with Linux development on Google Cloud.",
"icon": "/icon/gcp.png",
"tags": [
"cloud",
"google",
"container"
],
"markdown": "\n# gcp-vm-container\n\nTo get started, run `coder templates init`. When prompted, select this template,\nand follow the on-screen instructions to proceed.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith Google Cloud. For example, run `gcloud auth application-default login` to\nimport credentials on the system and user running coderd. For other ways to\nauthenticate [consult the Terraform\ndocs](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started#adding-credentials).\n\n## Service account\n\nCoder requires a Google Cloud Service Account to provision workspaces. To create\na service account:\n\n1. Navigate to the [CGP\n console](https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create),\n and select your Cloud project (if you have more than one project associated\n with your account)\n\n1. Provide a service account name (this name is used to generate the service\n account ID)\n\n1. Click **Create and continue**, and choose the following IAM roles to grant to\n the service account:\n\n - Compute Admin\n - Service Account User\n\n Click **Continue**.\n\n1. Click on the created key, and navigate to the **Keys** tab.\n\n1. Click **Add key** \u003e **Create new key**.\n\n1. Generate a **JSON private key**, which will be what you provide to Coder\n during the setup process.\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n"
},
{
"id": "gcp-windows",
"url": "",
"name": "Develop in Windows on Google Cloud",
"description": "Get started with Windows development on Google Cloud.",
"icon": "/icon/gcp.png",
"tags": [
"cloud",
"google"
],
"markdown": "\n# gcp-windows\n\nTo get started, run `coder templates init`. When prompted, select this template,\nand follow the on-screen instructions to proceed.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith Google Cloud. For example, run `gcloud auth application-default login` to\nimport credentials on the system and user running coderd. For other ways to\nauthenticate [consult the Terraform\ndocs](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started#adding-credentials).\n\n## Service account\n\nCoder requires a Google Cloud Service Account to provision workspaces. To create\na service account:\n\n1. Navigate to the [CGP\n console](https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create),\n and select your Cloud project (if you have more than one project associated\n with your account)\n\n1. Provide a service account name (this name is used to generate the service\n account ID)\n\n1. Click **Create and continue**, and choose the following IAM roles to grant to\n the service account:\n\n - Compute Admin\n - Service Account User\n\n Click **Continue**.\n\n1. Click on the created key, and navigate to the **Keys** tab.\n\n1. Click **Add key** \u003e **Create new key**.\n\n1. Generate a **JSON private key**, which will be what you provide to Coder\n during the setup process.\n"
},
{
"id": "kubernetes",
"url": "",
"name": "Develop in Kubernetes",
"description": "Get started with Kubernetes development.",
"icon": "/icon/k8s.png",
"tags": [
"cloud",
"kubernetes"
],
"markdown": "\n# Getting started\n\nThis template creates a deplyment running the `codercom/enterprise-base:ubuntu` image.\n\n## Prerequisites\n\nThis template uses [`kubernetes_deployment`](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/deployment) terraform resource, which requires the `coder` service account to have permission to create deploymnets. For example if you are using [helm](https://coder.com/docs/v2/latest/install/kubernetes#install-coder-with-helm) to install Coder, you should set `coder.serviceAccount.enableDeployments=true` in your `values.yaml`\n\n```diff\ncoder:\nserviceAccount:\n workspacePerms: true\n- enableDeployments: false\n+ enableDeployments: true\n annotations: {}\n name: coder\n```\n\n\u003e Note: This is only required for Coder versions \u003c 0.28.0, as this will be the default value for Coder versions \u003e= 0.28.0\n\n## Authentication\n\nThis template can authenticate using in-cluster authentication, or using a kubeconfig local to the\nCoder host. For additional authentication options, consult the [Kubernetes provider\ndocumentation](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs).\n\n### kubeconfig on Coder host\n\nIf the Coder host has a local `~/.kube/config`, you can use this to authenticate\nwith Coder. Make sure this is done with same user that's running the `coder` service.\n\nTo use this authentication, set the parameter `use_kubeconfig` to true.\n\n### In-cluster authentication\n\nIf the Coder host runs in a Pod on the same Kubernetes cluster as you are creating workspaces in,\nyou can use in-cluster authentication.\n\nTo use this authentication, set the parameter `use_kubeconfig` to false.\n\nThe Terraform provisioner will automatically use the service account associated with the pod to\nauthenticate to Kubernetes. Be sure to bind a [role with appropriate permission](#rbac) to the\nservice account. For example, assuming the Coder host runs in the same namespace as you intend\nto create workspaces:\n\n```yaml\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: coder\n\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n name: coder\nsubjects:\n - kind: ServiceAccount\n name: coder\nroleRef:\n kind: Role\n name: coder\n apiGroup: rbac.authorization.k8s.io\n```\n\nThen start the Coder host with `serviceAccountName: coder` in the pod spec.\n\n### Authenticate against external clusters\n\nYou may want to deploy workspaces on a cluster outside of the Coder control plane. Refer to the [Coder docs](https://coder.com/docs/v2/latest/platforms/kubernetes/additional-clusters) to learn how to modify your template to authenticate against external clusters.\n\n## Namespace\n\nThe target namespace in which the deployment will be deployed is defined via the `coder_workspace`\nvariable. The namespace must exist prior to creating workspaces.\n\n## Persistence\n\nThe `/home/coder` directory in this example is persisted via the attached PersistentVolumeClaim.\nAny data saved outside of this directory will be wiped when the workspace stops.\n\nSince most binary installations and environment configurations live outside of\nthe `/home` directory, we suggest including these in the `startup_script` argument\nof the `coder_agent` resource block, which will run each time the workspace starts up.\n\nFor example, when installing the `aws` CLI, the install script will place the\n`aws` binary in `/usr/local/bin/aws`. To ensure the `aws` CLI is persisted across\nworkspace starts/stops, include the following code in the `coder_agent` resource\nblock of your workspace template:\n\n```terraform\nresource \"coder_agent\" \"main\" {\n startup_script = \u003c\u003c-EOT\n set -e\n # install AWS CLI\n curl \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\" -o \"awscliv2.zip\"\n unzip awscliv2.zip\n sudo ./aws/install\n EOT\n}\n```\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n\n## Deployment logs\n\nTo stream kubernetes pods events from the deployment, you can use Coder's [`coder-logstream-kube`](https://github.com/coder/coder-logstream-kube) tool. This can stream logs from the deployment to Coder's workspace startup logs. You just need to install the `coder-logstream-kube` helm chart on the cluster where the deployment is running.\n\n```shell\nhelm repo add coder-logstream-kube https://helm.coder.com/logstream-kube\nhelm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \\\n --namespace coder \\\n --set url=\u003cyour-coder-url-including-http-or-https\u003e\n```\n\nFor detailed instructions, see [Deployment logs](https://coder.com/docs/v2/latest/platforms/kubernetes/deployment-logs)\n"
},
{
"id": "fly-docker-image",
"url": "",
"name": "Develop on a Fly.io container",
"description": "Run workspaces as Firecracker VMs on Fly.io",
"icon": "/icon/fly.io.svg",
"tags": [
"docker",
"fly.io"
],
"markdown": "\n# Coder Fly.io Template\n\nThis template provisions a [code-server](https://github.com/coder/code-server) instance on [fly.io](https://fly.io) using the [codercom/code-server](https://hub.docker.com/r/codercom/code-server) image.\n\n## Prerequisites\n\n- [flyctl](https://fly.io/docs/getting-started/installing-flyctl/) installed.\n- [Coder](https://coder.com/) already setup and running with coder-cli installed locally.\n\n## Getting started\n\n1. Run `coder templates init` and select this template. Follow the instructions that appear.\n2. cd into the directory that was created. (e.g. `cd fly-docker-image`)\n3. Create the new template by running the following command from the `fly-docker-image` directory:\n\n```bash\ncoder templates create fly-docker-image \\\n --var fly_api_token=$(flyctl auth token) \\\n --var fly_org=personal\n```\n\n\u003e If the Coder server is also running as a fly.io app, then instead of setting variable `fly_api_token` you can also set a fly.io secret with the name `FLY_API_TOKEN`\n\u003e\n\u003e ```bash\n\u003e flyctl secrets set FLY_API_TOKEN=$(flyctl auth token) --app \u003cyour-coder-app-name\u003e\n\u003e ```\n\n\u003e Read our blog [post](coder.com/blog/deploying-coder-on-fly-io) to learn more about how to deploy Coder on fly.io.\n\n4. Navigate to the Coder dashboard and create a new workspace using the template.\n\nThis is all. You should now have a code-server instance running on fly.io.\n"
}
]

View File

@ -2,15 +2,17 @@ package examples
import (
"archive/tar"
"bufio"
"bytes"
"embed"
"encoding/json"
"io"
"io/fs"
"path"
"sort"
"strings"
"sync"
"github.com/gohugoio/hugo/parser/pageparser"
"golang.org/x/sync/singleflight"
"golang.org/x/xerrors"
@ -19,6 +21,8 @@ import (
var (
// Only some templates are embedded that we want to display inside the UI.
// The metadata in examples.gen.json is generated via scripts/examplegen.
//go:embed examples.gen.json
//go:embed templates/aws-ecs-container
//go:embed templates/aws-linux
//go:embed templates/aws-windows
@ -34,9 +38,10 @@ var (
files embed.FS
exampleBasePath = "https://github.com/coder/coder/tree/main/examples/templates/"
examples = make([]codersdk.TemplateExample, 0)
examplesJSON = "examples.gen.json"
parsedExamples []codersdk.TemplateExample
parseExamples sync.Once
archives = singleflight.Group{}
archives singleflight.Group
ErrNotFound = xerrors.New("example not found")
)
@ -44,102 +49,65 @@ const rootDir = "templates"
// List returns all embedded examples.
func List() ([]codersdk.TemplateExample, error) {
var returnError error
var err error
parseExamples.Do(func() {
files, err := fs.Sub(files, rootDir)
if err != nil {
returnError = xerrors.Errorf("get example fs: %w", err)
}
dirs, err := fs.ReadDir(files, ".")
if err != nil {
returnError = xerrors.Errorf("read dir: %w", err)
return
}
for _, dir := range dirs {
if !dir.IsDir() {
continue
}
exampleID := dir.Name()
exampleURL := exampleBasePath + exampleID
// Each one of these is a example!
readme, err := fs.ReadFile(files, path.Join(dir.Name(), "README.md"))
if err != nil {
returnError = xerrors.Errorf("example %q does not contain README.md", exampleID)
return
}
frontMatter, err := pageparser.ParseFrontMatterAndContent(bytes.NewReader(readme))
if err != nil {
returnError = xerrors.Errorf("parse example %q front matter: %w", exampleID, err)
return
}
nameRaw, exists := frontMatter.FrontMatter["name"]
if !exists {
returnError = xerrors.Errorf("example %q front matter does not contain name", exampleID)
return
}
name, valid := nameRaw.(string)
if !valid {
returnError = xerrors.Errorf("example %q name isn't a string", exampleID)
return
}
descriptionRaw, exists := frontMatter.FrontMatter["description"]
if !exists {
returnError = xerrors.Errorf("example %q front matter does not contain name", exampleID)
return
}
description, valid := descriptionRaw.(string)
if !valid {
returnError = xerrors.Errorf("example %q description isn't a string", exampleID)
return
}
tags := []string{}
tagsRaw, exists := frontMatter.FrontMatter["tags"]
if exists {
tagsI, valid := tagsRaw.([]interface{})
if !valid {
returnError = xerrors.Errorf("example %q tags isn't a slice: type %T", exampleID, tagsRaw)
return
}
for _, tagI := range tagsI {
tag, valid := tagI.(string)
if !valid {
returnError = xerrors.Errorf("example %q tag isn't a string: type %T", exampleID, tagI)
return
}
tags = append(tags, tag)
}
}
var icon string
iconRaw, exists := frontMatter.FrontMatter["icon"]
if exists {
icon, valid = iconRaw.(string)
if !valid {
returnError = xerrors.Errorf("example %q icon isn't a string", exampleID)
return
}
}
examples = append(examples, codersdk.TemplateExample{
ID: exampleID,
URL: exampleURL,
Name: name,
Description: description,
Icon: icon,
Tags: tags,
Markdown: string(frontMatter.Content),
})
}
parsedExamples, err = parseAndVerifyExamples()
})
return examples, returnError
return parsedExamples, err
}
func parseAndVerifyExamples() (examples []codersdk.TemplateExample, err error) {
f, err := files.Open(examplesJSON)
if err != nil {
return nil, xerrors.Errorf("open %s: %w", examplesJSON, err)
}
defer f.Close()
b := bufio.NewReader(f)
// Discard the first line (code generated by-comment).
_, _, err = b.ReadLine()
if err != nil {
return nil, xerrors.Errorf("read %s: %w", examplesJSON, err)
}
err = json.NewDecoder(b).Decode(&examples)
if err != nil {
return nil, xerrors.Errorf("decode %s: %w", examplesJSON, err)
}
// Sanity-check: Verify that the examples in the JSON file match the
// embedded files.
var wantEmbedFiles []string
for i, example := range examples {
examples[i].URL = exampleBasePath + example.ID
wantEmbedFiles = append(wantEmbedFiles, example.ID)
}
files, err := fs.Sub(files, rootDir)
if err != nil {
return nil, xerrors.Errorf("get templates fs: %w", err)
}
dirs, err := fs.ReadDir(files, ".")
if err != nil {
return nil, xerrors.Errorf("read templates dir: %w", err)
}
var gotEmbedFiles []string
for _, dir := range dirs {
if dir.IsDir() {
gotEmbedFiles = append(gotEmbedFiles, dir.Name())
}
}
sort.Strings(wantEmbedFiles)
sort.Strings(gotEmbedFiles)
want := strings.Join(wantEmbedFiles, ", ")
got := strings.Join(gotEmbedFiles, ", ")
if want != got {
return nil, xerrors.Errorf("mismatch between %s and embedded files: want %q, got %q", examplesJSON, want, got)
}
return examples, nil
}
// Archive returns a tar by example ID.

View File

@ -16,7 +16,7 @@ import (
func TestTemplate(t *testing.T) {
t.Parallel()
list, err := examples.List()
require.NoError(t, err)
require.NoError(t, err, "error listing examples, run \"make gen\" to ensure examples are up to date")
require.NotEmpty(t, list)
for _, eg := range list {
eg := eg

144
scripts/examplegen/main.go Normal file
View File

@ -0,0 +1,144 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"go/parser"
"go/token"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"github.com/gohugoio/hugo/parser/pageparser"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/codersdk"
)
const (
examplesDir = "examples"
examplesSrc = "examples.go"
)
func main() {
if err := run(); err != nil {
panic(err)
}
}
func run() error {
fset := token.NewFileSet()
src, err := parser.ParseFile(fset, filepath.Join(examplesDir, examplesSrc), nil, parser.ParseComments)
if err != nil {
return err
}
var paths []string
for _, comment := range src.Comments {
for _, line := range comment.List {
if s, ok := parseEmbedTag(line.Text); ok && !strings.HasSuffix(s, ".json") {
paths = append(paths, s)
}
}
}
var examples []codersdk.TemplateExample
files := os.DirFS(examplesDir)
for _, name := range paths {
dir, err := fs.Stat(files, name)
if err != nil {
return err
}
if !dir.IsDir() {
continue
}
exampleID := dir.Name()
// Each one of these is a example!
readme, err := fs.ReadFile(files, path.Join(name, "README.md"))
if err != nil {
return xerrors.Errorf("example %q does not contain README.md", exampleID)
}
frontMatter, err := pageparser.ParseFrontMatterAndContent(bytes.NewReader(readme))
if err != nil {
return xerrors.Errorf("parse example %q front matter: %w", exampleID, err)
}
nameRaw, exists := frontMatter.FrontMatter["name"]
if !exists {
return xerrors.Errorf("example %q front matter does not contain name", exampleID)
}
name, valid := nameRaw.(string)
if !valid {
return xerrors.Errorf("example %q name isn't a string", exampleID)
}
descriptionRaw, exists := frontMatter.FrontMatter["description"]
if !exists {
return xerrors.Errorf("example %q front matter does not contain name", exampleID)
}
description, valid := descriptionRaw.(string)
if !valid {
return xerrors.Errorf("example %q description isn't a string", exampleID)
}
tags := []string{}
tagsRaw, exists := frontMatter.FrontMatter["tags"]
if exists {
tagsI, valid := tagsRaw.([]interface{})
if !valid {
return xerrors.Errorf("example %q tags isn't a slice: type %T", exampleID, tagsRaw)
}
for _, tagI := range tagsI {
tag, valid := tagI.(string)
if !valid {
return xerrors.Errorf("example %q tag isn't a string: type %T", exampleID, tagI)
}
tags = append(tags, tag)
}
}
var icon string
iconRaw, exists := frontMatter.FrontMatter["icon"]
if exists {
icon, valid = iconRaw.(string)
if !valid {
return xerrors.Errorf("example %q icon isn't a string", exampleID)
}
}
examples = append(examples, codersdk.TemplateExample{
ID: exampleID,
Name: name,
Description: description,
Icon: icon,
Tags: tags,
Markdown: string(frontMatter.Content),
// URL is set by examples/examples.go.
})
}
w := os.Stdout
_, err = fmt.Fprint(w, "// Code generated by examplegen. DO NOT EDIT.\n")
if err != nil {
return err
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(examples)
}
func parseEmbedTag(s string) (string, bool) {
if !strings.HasPrefix(s, "//go:embed") {
return "", false
}
return strings.TrimSpace(strings.TrimPrefix(s, "//go:embed")), true
}

View File

@ -83,3 +83,6 @@ result
e2e/provisionerGenerated.ts
**/pnpm-lock.yaml
# Ignore generated JSON (e.g. examples/examples.gen.json).
**/*.gen.json

View File

@ -83,3 +83,6 @@ result
e2e/provisionerGenerated.ts
**/pnpm-lock.yaml
# Ignore generated JSON (e.g. examples/examples.gen.json).
**/*.gen.json