cmd/telemetrygodev: create the telemetrygodev server

The CL creates the skeleton of the telemetry.go.dev server. This service
will eventually handle requests for documentation, uploading and downloading
telemetry reports, and serving telemetry data charts and graphs.

Change-Id: I8fd841157976c4ce0d9defb27630134ac92c0d09
Reviewed-on: https://go-review.googlesource.com/c/telemetry/+/495756
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Peter Weinberger <pjw@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
diff --git a/godev/cmd/telemetrygodev/Dockerfile b/godev/cmd/telemetrygodev/Dockerfile
new file mode 100644
index 0000000..f027d5a
--- /dev/null
+++ b/godev/cmd/telemetrygodev/Dockerfile
@@ -0,0 +1,49 @@
+# Copyright 2023 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# This Dockerfile expects the build context to be the repo root.
+
+# NOTE: don't put anything in /tmp here. It will work locally,
+# but Cloud Run mounts something else to /tmp, so anything
+# installed here will be shadowed.
+
+FROM golang:1.20
+
+LABEL maintainer="Go Telemetry Team <go-telemetry-team@google.com>"
+
+#### Preliminaries
+
+WORKDIR /
+
+# Create some directories.
+
+# The telemetrygodev binary and related files live here.
+RUN mkdir /app
+
+#### Building binaries
+
+# Set the working directory outside $GOPATH to ensure module mode is enabled.
+WORKDIR /src
+
+# Copy go.mods and go.sums into the container.
+# If they don't change, which is the common case, then docker can
+# cache these COPYs and the subsequent RUN.
+COPY go.mod go.sum ./
+
+# Download the dependencies.
+RUN go mod download
+
+# Copy the repo from local machine into Docker client’s current working
+# directory, so that we can use it to build the binary.
+# See .dockerignore at the repo root for excluded files.
+COPY . /src
+
+# Build the telemetrygodev binary and put it in /app.
+RUN go build -mod=readonly -o /app/telemetrygodev ./cmd/telemetrygodev
+
+#### telemetrygodev init
+
+WORKDIR /app
+
+CMD ["./telemetrygodev"]
diff --git a/godev/cmd/telemetrygodev/cloudbuild.yaml b/godev/cmd/telemetrygodev/cloudbuild.yaml
new file mode 100644
index 0000000..d60c2a1
--- /dev/null
+++ b/godev/cmd/telemetrygodev/cloudbuild.yaml
@@ -0,0 +1,31 @@
+# Copyright 2023 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+steps:
+  # Build the container image
+  - name: "gcr.io/cloud-builders/docker"
+    dir: 'godev'
+    args:
+      - "build"
+      - "-t"
+      - "gcr.io/$PROJECT_ID/telemetrygodev:$COMMIT_SHA"
+      - "-f"
+      - "cmd/telemetrygodev/Dockerfile"
+      - "."
+  # Push the container image to Container Registry
+  - name: "gcr.io/cloud-builders/docker"
+    args: ["push", "gcr.io/$PROJECT_ID/telemetrygodev:$COMMIT_SHA"]
+  # Deploy container image to Cloud Run
+  - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
+    entrypoint: gcloud
+    args:
+      - "run"
+      - "deploy"
+      - "$_SERVICE"
+      - "--image"
+      - "gcr.io/$PROJECT_ID/telemetrygodev:$COMMIT_SHA"
+      - "--region"
+      - "us-central1"
+images:
+  - "gcr.io/$PROJECT_ID/telemetrygodev:$COMMIT_SHA"
diff --git a/godev/cmd/telemetrygodev/main.go b/godev/cmd/telemetrygodev/main.go
new file mode 100644
index 0000000..684dfbf
--- /dev/null
+++ b/godev/cmd/telemetrygodev/main.go
@@ -0,0 +1,45 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Telemetrygodev serves the telemetry.go.dev website.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/fs"
+	"log"
+	"net/http"
+	"os"
+
+	"golang.org/x/telemetry/godev"
+	"golang.org/x/telemetry/godev/internal/content"
+	"golang.org/x/telemetry/godev/internal/middleware"
+	"golang.org/x/telemetry/godev/internal/unionfs"
+)
+
+var (
+	addr = flag.String("addr", ":8080", "server listens on the given TCP network address")
+	dev  = flag.Bool("dev", false, "load static content and templates from the filesystem")
+)
+
+func main() {
+	flag.Parse()
+	s := content.Server(fsys(*dev))
+	mw := middleware.Default
+	fmt.Printf("server listening at http://%s\n", *addr)
+	log.Fatal(http.ListenAndServe(*addr, mw(s)))
+}
+
+func fsys(fromOS bool) fs.FS {
+	var f fs.FS = godev.FS
+	if fromOS {
+		f = os.DirFS(".")
+	}
+	f, err := unionfs.Sub(f, "content/telemetrygodev", "content/shared")
+	if err != nil {
+		log.Fatal(err)
+	}
+	return f
+}
diff --git a/godev/content.go b/godev/content.go
new file mode 100644
index 0000000..d9c1b06
--- /dev/null
+++ b/godev/content.go
@@ -0,0 +1,13 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package godev exports the content dir as an embed.FS.
+package godev
+
+import (
+	"embed"
+)
+
+//go:embed content
+var FS embed.FS
diff --git a/godev/content/shared/base.css b/godev/content/shared/base.css
new file mode 100644
index 0000000..f53468a
--- /dev/null
+++ b/godev/content/shared/base.css
@@ -0,0 +1,24 @@
+/*!
+ * Copyright 2023 The Go Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+@import url("./normalize.css");
+@import url("./color.css");
+@import url("./typography.css");
+
+:root {
+  --border: 0.0625rem solid var(--color-border);
+  --border-radius: 0.25rem;
+}
+
+.Container {
+  margin: 2rem 0 5rem;
+}
+
+.Content {
+  margin: 0 auto;
+  max-width: 50rem;
+  padding: 0 1rem;
+}
diff --git a/godev/content/shared/base.html b/godev/content/shared/base.html
new file mode 100644
index 0000000..ab01ea7
--- /dev/null
+++ b/godev/content/shared/base.html
@@ -0,0 +1,7 @@
+<!--
+  Copyright 2023 The Go Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style
+  license that can be found in the LICENSE file.
+-->
+
+{{template "base" .}}
diff --git a/godev/content/shared/base.tmpl b/godev/content/shared/base.tmpl
new file mode 100644
index 0000000..9c13b62
--- /dev/null
+++ b/godev/content/shared/base.tmpl
@@ -0,0 +1,25 @@
+<!--
+  Copyright 2023 The Go Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style
+  license that can be found in the LICENSE file.
+-->
+
+{{define "base"}}
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <title>{{block "title" .}}{{.Title}}{{end}}</title>
+  <link rel="icon" type="image/x-icon" href="/favicon.ico">
+  <link rel="stylesheet" href="/base.css">
+</head>
+<body>
+  <div class="Container">
+    <div class="Content">{{block "content" .}}{{.Content}}{{end}}</div>
+  </div>
+</body>
+</html>
+{{end}}
diff --git a/godev/content/shared/color.css b/godev/content/shared/color.css
new file mode 100644
index 0000000..382effd
--- /dev/null
+++ b/godev/content/shared/color.css
@@ -0,0 +1,85 @@
+/*!
+ * Copyright 2021 The Go Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+:root {
+  /* Colors */
+  --gray-1: #202224;
+  --gray-2: #3e4042;
+  --gray-3: #555759;
+  --gray-4: #6e7072;
+  --gray-5: #848688;
+  --gray-6: #aaacae;
+  --gray-7: #c6c8ca;
+  --gray-8: #dcdee0;
+  --gray-9: #f0f1f2;
+  --gray-10: #f8f8f8;
+  --turq-light: #5dc9e2;
+  --turq-med: #50b7e0;
+  --turq-dark: #007d9c;
+  --blue: #bfeaf4;
+  --blue-light: #f2fafd;
+  --black: #000;
+  --green: #3a6e11;
+  --green-light: #5fda64;
+  --pink: #c85e7a;
+  --pink-light: #fdecf1;
+  --purple: #542c7d;
+  --slate: #253443; /* Footer background. */
+  --white: #fff;
+  --yellow: #fceea5;
+  --yellow-light: #fff8cc;
+
+  /* Color Intents */
+  --color-brand-primary: var(--turq-dark);
+  --color-background: var(--white);
+  --color-background-inverted: var(--slate);
+  --color-background-accented: var(--gray-10);
+  --color-background-highlighted: var(--blue);
+  --color-background-highlighted-link: var(--blue-light);
+  --color-background-info: var(--gray-9);
+  --color-background-warning: var(--yellow-light);
+  --color-background-alert: var(--pink-light);
+  --color-border: var(--gray-7);
+  --color-text: var(--gray-1);
+  --color-text-subtle: var(--gray-4);
+  --color-text-link: var(--turq-dark);
+  --color-text-inverted: var(--white);
+  --color-code-comment: var(--green);
+
+  /* Interactive Colors */
+  --color-input: var(--color-background);
+  --color-input-text: var(--color-text);
+  --color-button: var(--turq-dark);
+  --color-button-disabled: var(--gray-9);
+  --color-button-text: var(--white);
+  --color-button-text-disabled: var(--gray-3);
+  --color-button-inverted: var(--color-background);
+  --color-button-inverted-disabled: var(--color-background);
+  --color-button-inverted-text: var(--color-brand-primary);
+  --color-button-inverted-text-disabled: var(--color-text-subtle);
+  --color-button-accented: var(--yellow);
+  --color-button-accented-disabled: var(--gray-9);
+  --color-button-accented-text: var(--gray-1);
+  --color-button-accented-text-disabled: var(--gray-3);
+}
+
+@media (prefers-color-scheme: dark) {
+  :root:not([data-theme="light"]) {
+    --color-brand-primary: var(--turq-med);
+    --color-background: var(--gray-1);
+    --color-background-accented: var(--gray-2);
+    --color-background-highlighted: var(--gray-2);
+    --color-background-highlighted-link: var(--gray-2);
+    --color-background-info: var(--gray-3);
+    --color-background-warning: var(--yellow);
+    --color-background-alert: var(--pink);
+    --color-border: var(--gray-4);
+    --color-text: var(--gray-9);
+    --color-text-link: var(--turq-med);
+    --color-text-subtle: var(--gray-7);
+    --color-code-comment: var(--green-light);
+  }
+}
diff --git a/godev/content/shared/favicon.ico b/godev/content/shared/favicon.ico
new file mode 100644
index 0000000..8d22584
--- /dev/null
+++ b/godev/content/shared/favicon.ico
Binary files differ
diff --git a/godev/content/shared/normalize.css b/godev/content/shared/normalize.css
new file mode 100644
index 0000000..b6eb821
--- /dev/null
+++ b/godev/content/shared/normalize.css
@@ -0,0 +1,349 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+   ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+
+ html {
+  line-height: 1.15; /* 1 */
+  -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+   ========================================================================== */
+
+/**
+ * Remove the margin in all browsers.
+ */
+
+body {
+  margin: 0;
+}
+
+/**
+ * Render the `main` element consistently in IE.
+ */
+
+main {
+  display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+/* Grouping content
+   ========================================================================== */
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+  box-sizing: content-box; /* 1 */
+  height: 0; /* 1 */
+  overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+   ========================================================================== */
+
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+
+a {
+  background-color: transparent;
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+  border-bottom: none; /* 1 */
+  text-decoration: underline; /* 2 */
+  text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+  font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+  font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.5em;
+}
+
+/* Embedded content
+   ========================================================================== */
+
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+
+img {
+  border-style: none;
+}
+
+/* Forms
+   ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+  font-family: inherit; /* 1 */
+  font-size: 100%; /* 1 */
+  line-height: 1.15; /* 1 */
+  margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input { /* 1 */
+  overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select { /* 1 */
+  text-transform: none;
+}
+
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+  -webkit-appearance: button;
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+  border-style: none;
+  padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+  outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+  padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ *    `fieldset` elements in all browsers.
+ */
+
+legend {
+  box-sizing: border-box; /* 1 */
+  color: inherit; /* 2 */
+  display: table; /* 1 */
+  max-width: 100%; /* 1 */
+  padding: 0; /* 3 */
+  white-space: normal; /* 1 */
+}
+
+/**
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+  vertical-align: baseline;
+}
+
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+
+textarea {
+  overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+  box-sizing: border-box; /* 1 */
+  padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+  -webkit-appearance: textfield; /* 1 */
+  outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+  -webkit-appearance: button; /* 1 */
+  font: inherit; /* 2 */
+}
+
+/* Interactive
+   ========================================================================== */
+
+/*
+ * Add the correct display in Edge, IE 10+, and Firefox.
+ */
+
+details {
+  display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+  display: list-item;
+}
+
+/* Misc
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 10+.
+ */
+
+template {
+  display: none;
+}
+
+/**
+ * Add the correct display in IE 10.
+ */
+
+[hidden] {
+  display: none;
+}
\ No newline at end of file
diff --git a/godev/content/shared/typography.css b/godev/content/shared/typography.css
new file mode 100644
index 0000000..9081a99
--- /dev/null
+++ b/godev/content/shared/typography.css
@@ -0,0 +1,69 @@
+/*!
+ * Copyright 2021 The Go Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+body {
+  background-color: var(--color-background);
+  color: var(--color-text);
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
+    sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
+  font-size: 1rem;
+  line-height: normal;
+}
+
+p {
+  line-height: 1.4375;
+  max-width: 75ch;
+}
+
+hr {
+  border: none;
+  border-bottom: var(--border);
+  margin: 0;
+  width: 100%;
+}
+
+code,
+pre,
+textarea.code {
+  font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
+  font-size: 0.875rem;
+  line-height: 1.5em;
+}
+
+pre,
+textarea.code {
+  background-color: var(--color-background-accented);
+  border: var(--border);
+  border-radius: var(--border-radius);
+  color: var(--color-text);
+  overflow-x: auto;
+  padding: 0.625rem;
+  tab-size: 4;
+  white-space: pre;
+}
+
+button,
+input,
+select,
+textarea {
+  font: inherit;
+}
+
+a,
+a:link,
+a:visited {
+  color: var(--color-brand-primary);
+  text-decoration: none;
+}
+
+a:hover {
+  color: var(--color-brand-primary);
+  text-decoration: underline;
+}
+
+a:hover > * {
+  text-decoration: underline;
+}
diff --git a/godev/content/telemetrygodev/index.md b/godev/content/telemetrygodev/index.md
new file mode 100644
index 0000000..9ea1e9f
--- /dev/null
+++ b/godev/content/telemetrygodev/index.md
@@ -0,0 +1,9 @@
+---
+Title: Go Telemetry
+---
+
+# Go Telemetry
+
+## Overview
+This page will provide information about telemetry collection in the Go
+toolchain.
diff --git a/godev/content/telemetrygodev/privacy.md b/godev/content/telemetrygodev/privacy.md
new file mode 100644
index 0000000..5b61d77
--- /dev/null
+++ b/godev/content/telemetrygodev/privacy.md
@@ -0,0 +1,16 @@
+---
+Title: Go Telemetry Privacy Policy
+---
+
+# Privacy Policy
+
+*Last updated: April 27, 2023*
+
+When you enable Go toolchain telemetry using `go telemetry on`, Go toolchain programs such as the go command and gopls record usage and performance data about their own execution to local files on your computer stored in `os.UserConfigDir()/go/telemetry`. The files contain event counters, stack traces for the Go toolchain programs, and basic version information about your operating system, CPU architecture, and dependency tools such as the host C compiler and version control tools. The files do not contain any user data that may be potentially identifying or any kind of system identifier.
+
+You can view the locally collected data using `go telemetry view`.
+
+Once a week, the Go toolchain will randomly decide whether to upload that week's reports to a server at Google. The random choice is set so that a representative sample of systems upload reports each week. As more systems participate, each system uploads less often. This data is collected in accordance with the Google Privacy Policy (https://policies.google.com/privacy).
+The uploaded reports are republished in full as part of a public dataset. Developers working on Go itself, both inside and outside Google, will use that dataset to better understand how the toolchain is being used and whether it is performing as expected. 
+
+You can collect telemetry information for local viewing without sending it to Google by using `go telemetry local`. If you later switch from `go telemetry local` to `go telemetry on`, the current week’s telemetry data may be uploaded. You may clear your local telemetry data by running `go telemetry clear` at any time.