From 3e1e0335a657ee1daec6d5e42c46fd3404562b5b Mon Sep 17 00:00:00 2001 From: Benjamin Grandfond Date: Mon, 28 Nov 2022 11:46:43 +0100 Subject: [PATCH] Genericize frontend tutorial and make it clear other tech are usable and track it (#16195) * Genericize frontend tutorial and make it clear other tech are usable and track it * Fix linking * Add some space before the callout * Review feedback * Fix link --- _data/toc.yaml | 6 +- .../set-up/frontend-extension-tutorial.md | 321 ++++++++++++++++++ .../build/set-up/minimal-backend-extension.md | 4 +- .../set-up/minimal-frontend-extension.md | 5 +- .../minimal-frontend-using-docker-cli.md | 115 ------- .../build/set-up/react-extension.md | 185 ---------- 6 files changed, 328 insertions(+), 308 deletions(-) create mode 100644 desktop/extensions-sdk/build/set-up/frontend-extension-tutorial.md delete mode 100644 desktop/extensions-sdk/build/set-up/minimal-frontend-using-docker-cli.md delete mode 100644 desktop/extensions-sdk/build/set-up/react-extension.md diff --git a/_data/toc.yaml b/_data/toc.yaml index f0a587a1b0..cc4607c34d 100644 --- a/_data/toc.yaml +++ b/_data/toc.yaml @@ -1331,10 +1331,8 @@ manuals: section: - title: ...a minimal frontend extension path: /desktop/extensions-sdk/build/set-up/minimal-frontend-extension/ - - title: ... a ReactJS-based extension - path: /desktop/extensions-sdk/build/set-up/react-extension/ - - title: ... a minimal extension invoking Docker commands - path: /desktop/extensions-sdk/build/set-up/minimal-frontend-using-docker-cli/ + - title: ... an advanced frontend extension + path: /desktop/extensions-sdk/build/set-up/frontend-extension-tutorial/ - title: ... a minimal backend extension path: /desktop/extensions-sdk/build/set-up/minimal-backend-extension/ - title: "Step two: Build and install" diff --git a/desktop/extensions-sdk/build/set-up/frontend-extension-tutorial.md b/desktop/extensions-sdk/build/set-up/frontend-extension-tutorial.md new file mode 100644 index 0000000000..c12a9c86db --- /dev/null +++ b/desktop/extensions-sdk/build/set-up/frontend-extension-tutorial.md @@ -0,0 +1,321 @@ +--- +title: Set up an advanced frontend extension +description: Advanced frontend extension tutorial +keywords: Docker, extensions, sdk, build +redirect_from: + - /desktop/extensions-sdk/tutorials/react-extension/ + - /desktop/extensions-sdk/build/set-up/react-extension/ + - /desktop/extensions-sdk/build/set-up/minimal-frontend-using-docker-cli/ +--- + +To start creating your extension, you first need a directory with files which range from the extension’s source code to the required extension-specific files. This page provides information on how to set up a simple Docker extension that contains only a UI part. + +> Note +> +> Before you start, make sure you have installed the latest version of [Docker Desktop](https://www.docker.com/products/docker-desktop/). + +## Extension folder structure + +The quickest way to create a new extension is to run `docker extension init my-extension` as in the +[Quickstart](../../quickstart.md). This will create a new directory `my-extension` that contains a fully functional extension. + +> **Tip** +> +> The `docker extension init` generates a React based extension. But you can still use it as a starting point for +> your own extension and use any other frontend framework, like Vue, Angular, Svelte, etc. or event stay with +> vanilla Javascript. +{: .tip } + +Although you can start from an empty directory or from the `react-extension` [sample folder](https://github.com/docker/extensions-sdk/tree/main/samples){:target="_blank" rel="noopener" class="_"}, +it's highly recommended that you start from the `docker extension init` command and change it to suit your needs. + +```bash +. +├── Dockerfile # (1) +├── ui # (2) +│ ├── public # (3) +│ │ └── index.html +│ ├── src # (4) +│ │ ├── App.tsx +│ │ ├── index.tsx +│ ├── package.json +│ └── package-lock.lock +│ ├── tsconfig.json +├── docker.svg # (5) +└── metadata.json # (6) +``` + +1. Contains everything required to build the extension and run it in Docker Desktop. +2. High-level folder containing your front-end app source code. +3. Assets that aren’t compiled or dynamically generated are stored here. These can be static assets like logos or the robots.txt file. +4. The src, or source folder contains all the React components, external CSS files, and dynamic assets that are brought into the component files. +5. The icon that is displayed in the left-menu of the Docker Desktop Dashboard. +6. A file that provides information about the extension such as the name, description, and version. + +## Adapting the Dockerfile + +> **Note** +> +> When using the `docker extension init`, it creates a `Dockerfile` that already contains what is needed for a React +> extension. + +Once the extension is created, you need to configure the `Dockerfile` to build the extension and configure the labels +that are used to populate the extension's card in the Marketplace. Here is an example of a `Dockerfile` for a React +extension: + + + +
+
+ +```Dockerfile +FROM --platform=$BUILDPLATFORM node:18.9-alpine3.15 AS client-builder +WORKDIR /ui +# cache packages in layer +COPY ui/package.json /ui/package.json +COPY ui/package-lock.json /ui/package-lock.json +RUN --mount=type=cache,target=/usr/src/app/.npm \ + npm set cache /usr/src/app/.npm && \ + npm ci +# install +COPY ui /ui +RUN npm run build + +FROM alpine +LABEL org.opencontainers.image.title="My extension" \ + org.opencontainers.image.description="Your Desktop Extension Description" \ + org.opencontainers.image.vendor="Awesome Inc." \ + com.docker.desktop.extension.api.version="0.3.0" \ + com.docker.desktop.extension.icon="https://www.docker.com/wp-content/uploads/2022/03/Moby-logo.png" + com.docker.extension.screenshots="" \ + com.docker.extension.detailed-description="" \ + com.docker.extension.publisher-url="" \ + com.docker.extension.additional-urls="" \ + com.docker.extension.changelog="" + +COPY metadata.json . +COPY docker.svg . +COPY --from=client-builder /ui/build ui + +``` + +
+
+ +
+ +> **Important** +> +> We don't have a working Dockerfile for Vue yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Vue) +> and let us know you'd like a Dockerfile for Vue. +{: .important } + +
+
+ +
+ +> **Important** +> +> We don't have a working Dockerfile for Angular yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Angular) +> and let us know you'd like a Dockerfile for Angular. +{: .important } + +
+
+ +
+ +> **Important** +> +> We don't have a working Dockerfile for Svelte yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Svelte) +> and let us know you'd like a Dockerfile for Svelte. +{: .important } + +
+
+ +## Configure the metadata file + +A `metadata.json` file is required at the root of your extension directory. + +```json +{ + "icon": "docker.svg", + "ui": { + "dashboard-tab": { + "title": "UI Extension", + "root": "/ui", + "src": "index.html" + } + } +} +``` + +## Use the Extension APIs client + +To use the Extension APIs and perform actions with Docker Desktop, the extension must first import the +`@docker/extension-api-client` library. To install it, run the command below: + +```bash +npm install @docker/extension-api-client +``` + +Then call the `createDockerDesktopClient` function to create a client object to call the extension APIs. + +```js +import { createDockerDesktopClient } from '@docker/extension-api-client'; + +const ddClient = createDockerDesktopClient(); +``` + +When using Typescript, you can also install `@docker/extension-api-client-types` as a dev dependency. This will +provide you with type definitions for the extension APIs and auto-completion in your IDE. + +```bash +npm install @docker/extension-api-client-types --save-dev +``` + +![types auto complete](images/types-autocomplete.png) + +For example, you can use the `docker.cli.exec` function to get the list of all the containers via the `docker ps --all` +command and display the result in a table. + + + +
+
+ +Replace the `ui/src/App.tsx` file with the following code: + +```tsx +{% raw %} +// ui/src/App.tsx +import React, { useEffect } from 'react'; +import { + Paper, + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography +} from "@mui/material"; +import { createDockerDesktopClient } from "@docker/extension-api-client"; + +//obtain docker destkop extension client +const ddClient = createDockerDesktopClient(); + +export function App() { + const [containers, setContainers] = React.useState([]); + + useEffect(() => { + // List all containers + ddClient.docker.cli.exec('ps', ['--all', '--format', '"{{json .}}"']).then((result) => { + // result.parseJsonLines() parses the output of the command into an array of objects + setContainers(result.parseJsonLines()); + }); + }, []); + + return ( + + + Container list + + + Simple list of containers using Docker Extensions SDK. + + + + + + Container id + Image + Command + Created + Status + + + + {containers.map((container) => ( + + {container.ID} + {container.Image} + {container.Command} + {container.CreatedAt} + {container.Status} + + ))} + +
+
+
+ ); +} +{% endraw %} +``` + +
+
+ +
+ +> **Important** +> +> We don't have an example for Vue yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Vue) +> and let us know you'd like a sample with Vue. +{: .important } + +
+
+ +
+ +> **Important** +> +> We don't have an example for Angular yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Angular) +> and let us know you'd like a sample with Angular. +{: .important } + +
+
+ +
+ +> **Important** +> +> We don't have an example for Svelte yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Svelte) +> and let us know you'd like a sample with Svelte. +{: .important } + +
+
+ +![Screenshot of the container list.](images/react-extension.png) + +## What's next? + +- Learn how to [build and install your extension](../build-install.md). +- For more information and guidelines on building the UI, see the [Design and UI styling section](../../design/design-guidelines.md). +- If you want to set up user authentication for the extension, see [Authentication](../../dev/oauth2-flow.md). + diff --git a/desktop/extensions-sdk/build/set-up/minimal-backend-extension.md b/desktop/extensions-sdk/build/set-up/minimal-backend-extension.md index 89288504fd..d7a081071c 100644 --- a/desktop/extensions-sdk/build/set-up/minimal-backend-extension.md +++ b/desktop/extensions-sdk/build/set-up/minimal-backend-extension.md @@ -12,7 +12,7 @@ For extensions with a backend service running REST services over sockets or name > Note > -> Before you start, make sure you have installed the latest version of [Docker Desktop](../../../release-notes.md). +> Before you start, make sure you have installed the latest version of [Docker Desktop](https://www.docker.com/products/docker-desktop/). > Note > @@ -48,7 +48,7 @@ If you want to set up user authentication for the extension, see [Authentication ## Invoke the extension backend from your javascript code -Using the [React extension example](./react-extension.md), we can invoke our extension backend from the App.tsx file. +Using the [advanced frontend extension example](./frontend-extension-tutorial.md), we can invoke our extension backend from the `App.tsx` file. Use the Docker Desktop Client object and then invoke a binary provided in our backend container (that lives inside the Docker Desktop VM) with `ddClient.docker.extension.vm.cli.exec()`. In our example, our hello.sh script returns a string as result, we obtain it with `result?.stdout`. diff --git a/desktop/extensions-sdk/build/set-up/minimal-frontend-extension.md b/desktop/extensions-sdk/build/set-up/minimal-frontend-extension.md index ee070a61ed..971ed16afb 100644 --- a/desktop/extensions-sdk/build/set-up/minimal-frontend-extension.md +++ b/desktop/extensions-sdk/build/set-up/minimal-frontend-extension.md @@ -10,7 +10,7 @@ To start creating your extension, you first need a directory with files which ra > Note > -> Before you start, make sure you have installed the latest version of [Docker Desktop](../../../release-notes.md). +> Before you start, make sure you have installed the latest version of [Docker Desktop](https://www.docker.com/products/docker-desktop/). > Note > @@ -77,4 +77,5 @@ For more information on the `metadata.json`, see [Metadata](../../extensions/MET ## What's next? -Learn how to [build and install your extension](../build-install.md). +- Learn how to [build and install your extension](../build-install.md). +- Build a more advanced frontend extension by following the [Advanced frontend extension tutorial](./frontend-extension-tutorial.md). diff --git a/desktop/extensions-sdk/build/set-up/minimal-frontend-using-docker-cli.md b/desktop/extensions-sdk/build/set-up/minimal-frontend-using-docker-cli.md deleted file mode 100644 index 805709493b..0000000000 --- a/desktop/extensions-sdk/build/set-up/minimal-frontend-using-docker-cli.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -title: Set up a minimal extension invoking Docker commands -description: Minimal docker CLI extension tutorial -keywords: Docker, extensions, sdk, build -redirect_from: - - /desktop/extensions-sdk/tutorials/minimal-frontend-using-docker-cli/ ---- - -To start creating your extension, you first need a directory with files which range from the extension’s source code to the required extension-specific files. This page provides information on how to set up a simple Docker extension that invokes Docker CLI commands. - -> Note -> -> Before you start, make sure you have installed the latest version of [Docker Desktop](../../../release-notes.md). - -> Note -> -> If you want to start a codebase for your new extension, our [Quickstart guide](../../quickstart.md) and `docker extension init ` will provide a better base for your extension, more up-to-date and related to your install of Docker Desktop. - -## Extension folder structure - -In the `minimal-docker-cli` [sample folder](https://github.com/docker/extensions-sdk/tree/main/samples){:target="_blank" rel="noopener" class="_"}, you can find a ready-to-go example that represents a UI Extension invoking Docker commands. We will go through this code example in this tutorial. - -Although you can start from an empty directory, it is highly recommended that you start from the template below and change it accordingly to suit your needs. - -```bash -. -├── Dockerfile # (1) -├── metadata.json # (2) -└── client # (3) -│ └── src -│ ├── App.tsx -│ └── ... React application -``` - -1. Contains everything required to build the extension and run it in Docker Desktop. -2. A file that provides information about the extension such as the name, description, and version. -3. The source folder that contains all your HTML, CSS and JS files. In this example we use a React frontend, the main part of th extension is an App.tsx. For more information and guidelines on building the UI, see the [Design and UI styling section](../../design/design-guidelines.md). - -If you want to set up user authentication for the extension, see [Authentication](../../dev/oauth2-flow.md). - -## Invoke docker CLI in your javascript code - -Using the [React extension example](./react-extension.md), we can invoke docker commands from the App.tsx file. - -Use the Docker Desktop Client object to discover extension APIs about `docker`. The application uses `@docker/extension-api-client` in order to obtain a Docker Desktop Client object. Because we have set `@docker/extension-api-client-types` as a dev dependency, we also have auto-completion in our IDE: - -![types auto complete](images/types-autocomplete.png) - -We can invoke a Docker command with `ddClient.docker.cli.exec()`. -For example, to run `docker info` and obtain json formatted results: - -{% raw %}`ddClient.docker.cli.exec("info", ["--format", '"{{ json . }}"'])`{% endraw %}. - -We can use `result.parseJsonObject()` to read results as a json object and use it in our application. - -```typescript -const ddClient = createDockerDesktopClient(); -const [dockerInfo, setDockerInfo] = useState(null); - -async function runDockerInfo() { - const result = await ddClient.docker.cli.exec("info", [ - "--format", - {% raw %}'"{{json .}}"',{% endraw %} - ]); - setDockerInfo(result.parseJsonObject()); -} -``` - -We can then use our `dockerInfo` object in the display part of the application. - -## Create a Dockerfile - -At minimum, your Dockerfile needs: - -- Labels which provide extra information about the extension. -- The source code which in this case is an `index.html` that sits within the `ui` folder. `index.html` refers to javascript code in `script.js`. -- The `metadata.json` file. - -```Dockerfile -FROM node:17.7-alpine3.14 AS client-builder -# ... build React application - -FROM scratch - -LABEL org.opencontainers.image.title="MyExtension" \ - org.opencontainers.image.description="A sample extension to show how easy it's to get started with Desktop Extensions." \ - org.opencontainers.image.vendor="Docker Inc." \ - com.docker.desktop.extension.api.version="1.0.0-beta.1" \ - com.docker.desktop.extension.icon="https://www.docker.com/wp-content/uploads/2022/03/Moby-logo.png" - -COPY ui ./ui -COPY metadata.json . -``` - -## Configure the metadata file - -A `metadata.json` file is required at the root of the image filesystem. - -```json -{ - "ui": { - "dashboard-tab": { - "title": "Docker VM info", - "root": "/ui", - "src": "index.html" - } - } -} -``` - -For more information on the `metadata.json`, see [Metadata](../../extensions/METADATA.md). - -## What's next? - -Learn how to [build and install your extension](../build-install.md). diff --git a/desktop/extensions-sdk/build/set-up/react-extension.md b/desktop/extensions-sdk/build/set-up/react-extension.md deleted file mode 100644 index 74997aa63c..0000000000 --- a/desktop/extensions-sdk/build/set-up/react-extension.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: Set up a minimal react extension -description: Minimal react extension tutorial -keywords: Docker, extensions, sdk, build -redirect_from: - - /desktop/extensions-sdk/tutorials/react-extension/ ---- - -To start creating your extension, you first need a directory with files which range from the extension’s source code to the required extension-specific files. This page provides information on how to set up a simple Docker extension that contains only a UI part and is based on ReactJS. - -> Note -> -> Before you start, make sure you have installed the latest version of [Docker Desktop](../../../release-notes.md). - -> Note -> -> If you want to start a codebase for your new extension, our [Quickstart guide](../../quickstart.md) and `docker extension init ` will provide a better base for your extension, more up-to-date and related to your install of Docker Desktop. - -## Extension folder structure - -In the `react-extension` [sample folder](https://github.com/docker/extensions-sdk/tree/main/samples){:target="_blank" rel="noopener" class="_"}, you can find a ready-to-go example that represents a UI Extension built on ReactJS. We will go through this code example in this tutorial. - -Although you can start from an empty directory, it is highly recommended that you start from the template below and change it accordingly to suit your needs. - -```bash -. -├── Dockerfile # (1) -├── client # (2) -│ ├── package.json -│ ├── public # (3) -│ │ └── index.html -│ ├── src # (4) -│ │ ├── App.tsx -│ │ ├── globals.d.ts -│ │ ├── index.tsx -│ │ └── react-app-env.d.ts -│ ├── tsconfig.json -│ └── yarn.lock -├── docker.svg # (5) -└── metadata.json # (6) -``` - -1. Contains everything required to build the extension and run it in Docker Desktop. -2. High-level folder containing your front-end app source code. -3. Assets that aren’t compiled or dynamically generated are stored here. These can be static assets like logos or the robots.txt file. -4. The src, or source folder contains all the React components, external CSS files, and dynamic assets that are brought into the component files. -5. The icon that is displayed in the left-menu of the Docker Desktop Dashboard. -6. A file that provides information about the extension such as the name, description, and version. - -For more information and guidelines on building the UI, see the [Design and UI styling section](../../design/design-guidelines.md). - -If you want to set up user authentication for the extension, see [Authentication](../../dev/oauth2-flow.md). - -## Create a Dockerfile - -Use the Dockerfile below as a template and change it accordingly to suit your needs. - -```Dockerfile -FROM node:14.17-alpine3.13 AS client-builder -WORKDIR /app/client -# cache packages in layer -COPY client/package.json /app/client/package.json -COPY client/yarn.lock /app/client/yarn.lock -ARG TARGETARCH -RUN yarn config set cache-folder /usr/local/share/.cache/yarn-${TARGETARCH} -RUN --mount=type=cache,target=/usr/local/share/.cache/yarn-${TARGETARCH} yarn -# install -COPY client /app/client -RUN --mount=type=cache,target=/usr/local/share/.cache/yarn-${TARGETARCH} yarn build - -FROM debian:bullseye-slim -LABEL org.opencontainers.image.title="ui-extension" \ - org.opencontainers.image.description="Your Desktop Extension Description" \ - org.opencontainers.image.vendor="Docker Inc." \ - com.docker.desktop.extension.api.version="1.0.0-beta.1" \ - com.docker.desktop.extension.icon="https://www.docker.com/wp-content/uploads/2022/03/Moby-logo.png" - -COPY --from=client-builder /app/client/dist ui -COPY docker.svg . -COPY metadata.json . - -``` - -## Configure the metadata file - -A `metadata.json` file is required at the root of your extension directory. - -```json -{ - "icon": "docker.svg", - "ui": { - "dashboard-tab": { - "title": "UI Extension", - "root": "/ui", - "src": "index.html" - } - } -} -``` - -## Use extension APIs in the application code - -The React application can import `@docker/extension-api-client` and use extension APIs to perform actions with Docker Desktop. -For example, to get the list of containers, call the `docker.cli.exec` function to execute the `docker ps` command -and display the result in a table: -```tsx -{% raw %} -import React, { useEffect } from 'react'; -import { - Paper, - Stack, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Typography -} from "@mui/material"; -import { createDockerDesktopClient } from "@docker/extension-api-client"; - -//obtain docker destkop extension client -const ddClient = createDockerDesktopClient(); - -export function App() { - const [containers, setContainers] = React.useState([]); - - useEffect(() => { - // List all containers - ddClient.docker.cli.exec('ps', ['--all', '--format', '"{{json .}}"']).then((result) => { - setContainers(result.parseJsonLines()); - }); - }, []); - - return ( - - - Container list - - - Simple list of containers using Docker Extensions SDK. - - - - - - Container id - Image - Command - Created - Status - - - - {containers.map((container) => ( - - {container.ID} - {container.Image} - {container.Command} - {container.CreatedAt} - {container.Status} - - ))} - -
-
-
- ); -} -{% endraw %} -``` - -![Screenshot of the container list built above.](images/react-extension.png) - -## What's next? - -Learn how to [build and install your extension](../build-install.md).