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
This commit is contained in:
Benjamin Grandfond 2022-11-28 11:46:43 +01:00 committed by GitHub
parent e2a3dad4e2
commit 3e1e0335a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 328 additions and 308 deletions

View File

@ -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"

View File

@ -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 extensions 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 arent 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:
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" data-target="#react-dockerfile" data-group="react">For React</a></li>
<li><a data-toggle="tab" data-target="#vue-dockerfile" data-group="vue">For Vue</a></li>
<li><a data-toggle="tab" data-target="#angular-dockerfile" data-group="angular">For Angular</a></li>
<li><a data-toggle="tab" data-target="#svelte-dockerfile" data-group="svelte">For Svelte</a></li>
</ul>
<div class="tab-content">
<div id="react-dockerfile" class="tab-pane fade in active" markdown="1">
```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
```
</div>
<div id="vue-dockerfile" class="tab-pane fade" markdown="1">
<br/>
> **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 }
</div>
<div id="angular-dockerfile" class="tab-pane fade" markdown="1">
<br/>
> **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 }
</div>
<div id="svelte-dockerfile" class="tab-pane fade" markdown="1">
<br/>
> **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 }
</div>
</div>
## 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.
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" data-target="#react-app" data-group="react">React</a></li>
<li><a data-toggle="tab" data-target="#vue-app" data-group="vue">Vue</a></li>
<li><a data-toggle="tab" data-target="#angular-app" data-group="angular">Angular</a></li>
<li><a data-toggle="tab" data-target="#svelte-app" data-group="svelte">Svelte</a></li>
</ul>
<div class="tab-content">
<div id="react-app" class="tab-pane fade in active" markdown="1">
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 (
<Stack>
<Typography data-testid="heading" variant="h3" role="title">
Container list
</Typography>
<Typography
data-testid="subheading"
variant="body1"
color="text.secondary"
sx={{ mt: 2 }}
>
Simple list of containers using Docker Extensions SDK.
</Typography>
<TableContainer sx={{mt:2}}>
<Table>
<TableHead>
<TableRow>
<TableCell>Container id</TableCell>
<TableCell>Image</TableCell>
<TableCell>Command</TableCell>
<TableCell>Created</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{containers.map((container) => (
<TableRow
key={container.ID}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell>{container.ID}</TableCell>
<TableCell>{container.Image}</TableCell>
<TableCell>{container.Command}</TableCell>
<TableCell>{container.CreatedAt}</TableCell>
<TableCell>{container.Status}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Stack>
);
}
{% endraw %}
```
</div>
<div id="vue-app" class="tab-pane fade" markdown="1">
<br/>
> **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 }
</div>
<div id="angular-app" class="tab-pane fade" markdown="1">
<br/>
> **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 }
</div>
<div id="svelte-app" class="tab-pane fade" markdown="1">
<br/>
> **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 }
</div>
</div>
![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).

View File

@ -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`.

View File

@ -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).

View File

@ -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 extensions 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 <my-extension>` 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<any>(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).

View File

@ -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 extensions 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 <my-extension>` 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 arent 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 (
<Stack>
<Typography data-testid="heading" variant="h3" role="title">
Container list
</Typography>
<Typography
data-testid="subheading"
variant="body1"
color="text.secondary"
sx={{ mt: 2 }}
>
Simple list of containers using Docker Extensions SDK.
</Typography>
<TableContainer sx={{mt:2}}>
<Table>
<TableHead>
<TableRow>
<TableCell>Container id</TableCell>
<TableCell>Image</TableCell>
<TableCell>Command</TableCell>
<TableCell>Created</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{containers.map((container) => (
<TableRow
key={container.ID}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell>{container.ID}</TableCell>
<TableCell>{container.Image}</TableCell>
<TableCell>{container.Command}</TableCell>
<TableCell>{container.CreatedAt}</TableCell>
<TableCell>{container.Status}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Stack>
);
}
{% 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).