chore: adopt markdownlint and markdown-table-formatter for *.md (#15831)

Co-authored-by: Edward Angert <EdwardAngert@users.noreply.github.com>
This commit is contained in:
Muhammad Atif Ali
2025-01-03 13:12:59 +00:00
committed by GitHub
co-authored by Edward Angert
parent 08463c27d8
commit 94f5d52fdc
255 changed files with 18279 additions and 16786 deletions
+48 -45
View File
@@ -64,7 +64,7 @@ You can modify the notification delivery behavior using the following server
flags.
| Required | CLI | Env | Type | Description | Default |
| :------: | ----------------------------------- | --------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------- | ------- |
|:--------:|-------------------------------------|-----------------------------------------|------------|-----------------------------------------------------------------------------------------------------------------------|---------|
| ✔️ | `--notifications-dispatch-timeout` | `CODER_NOTIFICATIONS_DISPATCH_TIMEOUT` | `duration` | How long to wait while a notification is being sent before giving up. | 1m |
| ✔️ | `--notifications-method` | `CODER_NOTIFICATIONS_METHOD` | `string` | Which delivery method to use (available options: 'smtp', 'webhook'). See [Delivery Methods](#delivery-methods) below. | smtp |
| -️ | `--notifications-max-send-attempts` | `CODER_NOTIFICATIONS_MAX_SEND_ATTEMPTS` | `int` | The upper limit of attempts to send a notification. | 5 |
@@ -90,15 +90,15 @@ existing one.
**Server Settings:**
| Required | CLI | Env | Type | Description | Default |
| :------: | ------------------- | ----------------------- | -------- | ----------------------------------------- | --------- |
|:--------:|---------------------|-------------------------|----------|-------------------------------------------|-----------|
| ✔️ | `--email-from` | `CODER_EMAIL_FROM` | `string` | The sender's address to use. | |
| ✔️ | `--email-smarthost` | `CODER_EMAIL_SMARTHOST` | `string` | The SMTP relay to send messages |
| ✔️ | `--email-smarthost` | `CODER_EMAIL_SMARTHOST` | `string` | The SMTP relay to send messages | |
| ✔️ | `--email-hello` | `CODER_EMAIL_HELLO` | `string` | The hostname identifying the SMTP server. | localhost |
**Authentication Settings:**
| Required | CLI | Env | Type | Description |
| :------: | ---------------------------- | -------------------------------- | -------- | ------------------------------------------------------------------------- |
|:--------:|------------------------------|----------------------------------|----------|---------------------------------------------------------------------------|
| - | `--email-auth-username` | `CODER_EMAIL_AUTH_USERNAME` | `string` | Username to use with PLAIN/LOGIN authentication. |
| - | `--email-auth-password` | `CODER_EMAIL_AUTH_PASSWORD` | `string` | Password to use with PLAIN/LOGIN authentication. |
| - | `--email-auth-password-file` | `CODER_EMAIL_AUTH_PASSWORD_FILE` | `string` | File from which to load password for use with PLAIN/LOGIN authentication. |
@@ -106,14 +106,14 @@ existing one.
**TLS Settings:**
| Required | CLI | Env | Type | Description | Default |
| :------: | --------------------------- | ----------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| - | `--email-force-tls` | `CODER_EMAIL_FORCE_TLS` | `bool` | Force a TLS connection to the configured SMTP smarthost. If port 465 is used, TLS will be forced. See https://datatracker.ietf.org/doc/html/rfc8314#section-3.3. | false |
| - | `--email-tls-starttls` | `CODER_EMAIL_TLS_STARTTLS` | `bool` | Enable STARTTLS to upgrade insecure SMTP connections using TLS. Ignored if `CODER_NOTIFICATIONS_EMAIL_FORCE_TLS` is set. | false |
| - | `--email-tls-skip-verify` | `CODER_EMAIL_TLS_SKIPVERIFY` | `bool` | Skip verification of the target server's certificate (**insecure**). | false |
| - | `--email-tls-server-name` | `CODER_EMAIL_TLS_SERVERNAME` | `string` | Server name to verify against the target certificate. | |
| - | `--email-tls-cert-file` | `CODER_EMAIL_TLS_CERTFILE` | `string` | Certificate file to use. | |
| - | `--email-tls-cert-key-file` | `CODER_EMAIL_TLS_CERTKEYFILE` | `string` | Certificate key file to use. | |
| Required | CLI | Env | Type | Description | Default |
|:--------:|-----------------------------|-------------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
| - | `--email-force-tls` | `CODER_EMAIL_FORCE_TLS` | `bool` | Force a TLS connection to the configured SMTP smarthost. If port 465 is used, TLS will be forced. See <https://datatracker.ietf.org/doc/html/rfc8314#section-3.3>. | false |
| - | `--email-tls-starttls` | `CODER_EMAIL_TLS_STARTTLS` | `bool` | Enable STARTTLS to upgrade insecure SMTP connections using TLS. Ignored if `CODER_NOTIFICATIONS_EMAIL_FORCE_TLS` is set. | false |
| - | `--email-tls-skip-verify` | `CODER_EMAIL_TLS_SKIPVERIFY` | `bool` | Skip verification of the target server's certificate (**insecure**). | false |
| - | `--email-tls-server-name` | `CODER_EMAIL_TLS_SERVERNAME` | `string` | Server name to verify against the target certificate. | |
| - | `--email-tls-cert-file` | `CODER_EMAIL_TLS_CERTFILE` | `string` | Certificate file to use. | |
| - | `--email-tls-cert-key-file` | `CODER_EMAIL_TLS_CERTKEYFILE` | `string` | Certificate key file to use. | |
**NOTE:** you _MUST_ use `CODER_EMAIL_FORCE_TLS` if your smarthost supports TLS
on a port other than `465`.
@@ -123,9 +123,11 @@ on a port other than `465`.
After setting the required fields above:
1. Create an [App Password](https://myaccount.google.com/apppasswords) using the
account you wish to send from
2. Set the following configuration options:
```
account you wish to send from.
1. Set the following configuration options:
```text
CODER_EMAIL_SMARTHOST=smtp.gmail.com:465
CODER_EMAIL_AUTH_USERNAME=<user>@<domain>
CODER_EMAIL_AUTH_PASSWORD="<app password created above>"
@@ -140,8 +142,9 @@ for more options.
After setting the required fields above:
1. Setup an account on Microsoft 365 or outlook.com
2. Set the following configuration options:
```
1. Set the following configuration options:
```text
CODER_EMAIL_SMARTHOST=smtp-mail.outlook.com:587
CODER_EMAIL_TLS_STARTTLS=true
CODER_EMAIL_AUTH_USERNAME=<user>@<domain>
@@ -161,40 +164,40 @@ systems.
**Settings**:
| Required | CLI | Env | Type | Description |
| :------: | ---------------------------------- | -------------------------------------- | ----- | --------------------------------------- |
|:--------:|------------------------------------|----------------------------------------|-------|-----------------------------------------|
| ✔️ | `--notifications-webhook-endpoint` | `CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT` | `url` | The endpoint to which to send webhooks. |
Here is an example payload for Coder's webhook notification:
```json
{
"_version": "1.0",
"msg_id": "88750cad-77d4-4663-8bc0-f46855f5019b",
"payload": {
"_version": "1.0",
"notification_name": "Workspace Deleted",
"user_id": "4ac34fcb-8155-44d5-8301-e3cd46e88b35",
"user_email": "danny@coder.com",
"user_name": "danny",
"user_username": "danny",
"actions": [
{
"label": "View workspaces",
"url": "https://et23ntkhpueak.pit-1.try.coder.app/workspaces"
},
{
"label": "View templates",
"url": "https://et23ntkhpueak.pit-1.try.coder.app/templates"
}
],
"labels": {
"initiator": "danny",
"name": "my-workspace",
"reason": "initiated by user"
}
},
"title": "Workspace \"my-workspace\" deleted",
"body": "Hi danny\n\nYour workspace my-workspace was deleted.\nThe specified reason was \"initiated by user (danny)\"."
"_version": "1.0",
"msg_id": "88750cad-77d4-4663-8bc0-f46855f5019b",
"payload": {
"_version": "1.0",
"notification_name": "Workspace Deleted",
"user_id": "4ac34fcb-8155-44d5-8301-e3cd46e88b35",
"user_email": "danny@coder.com",
"user_name": "danny",
"user_username": "danny",
"actions": [
{
"label": "View workspaces",
"url": "https://et23ntkhpueak.pit-1.try.coder.app/workspaces"
},
{
"label": "View templates",
"url": "https://et23ntkhpueak.pit-1.try.coder.app/templates"
}
],
"labels": {
"initiator": "danny",
"name": "my-workspace",
"reason": "initiated by user"
}
},
"title": "Workspace \"my-workspace\" deleted",
"body": "Hi danny\n\nYour workspace my-workspace was deleted.\nThe specified reason was \"initiated by user (danny)\"."
}
```
+100 -100
View File
@@ -34,9 +34,9 @@ To integrate Slack with Coder, follow these steps to create a Slack application:
3. Under "OAuth & Permissions", add the following OAuth scopes:
- `chat:write`: To send messages as the app.
- `users:read`: To find the user details.
- `users:read.email`: To find user emails.
- `chat:write`: To send messages as the app.
- `users:read`: To find the user details.
- `users:read.email`: To find user emails.
4. Install the app to your workspace and note down the **Bot User OAuth Token**
from the "OAuth & Permissions" section.
@@ -52,128 +52,128 @@ To build the server to receive webhooks and interact with Slack:
1. Initialize your project by running:
```bash
npm init -y
```
```bash
npm init -y
```
2. Install the Bolt library:
```bash
npm install @slack/bolt
```
```bash
npm install @slack/bolt
```
3. Create and edit the `app.js` file. Below is an example of the basic
structure:
```js
const { App, LogLevel, ExpressReceiver } = require("@slack/bolt");
const bodyParser = require("body-parser");
```js
const { App, LogLevel, ExpressReceiver } = require("@slack/bolt");
const bodyParser = require("body-parser");
const port = process.env.PORT || 6000;
const port = process.env.PORT || 6000;
// Create a Bolt Receiver
const receiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
receiver.router.use(bodyParser.json());
// Create a Bolt Receiver
const receiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
receiver.router.use(bodyParser.json());
// Create the Bolt App, using the receiver
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
logLevel: LogLevel.DEBUG,
receiver,
});
// Create the Bolt App, using the receiver
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
logLevel: LogLevel.DEBUG,
receiver,
});
receiver.router.post("/v1/webhook", async (req, res) => {
try {
if (!req.body) {
return res.status(400).send("Error: request body is missing");
}
receiver.router.post("/v1/webhook", async (req, res) => {
try {
if (!req.body) {
return res.status(400).send("Error: request body is missing");
}
const { title, body } = req.body;
if (!title || !body) {
return res.status(400).send('Error: missing fields: "title", or "body"');
}
const { title, body } = req.body;
if (!title || !body) {
return res.status(400).send('Error: missing fields: "title", or "body"');
}
const payload = req.body.payload;
if (!payload) {
return res.status(400).send('Error: missing "payload" field');
}
const payload = req.body.payload;
if (!payload) {
return res.status(400).send('Error: missing "payload" field');
}
const { user_email, actions } = payload;
if (!user_email || !actions) {
return res
.status(400)
.send('Error: missing fields: "user_email", "actions"');
}
const { user_email, actions } = payload;
if (!user_email || !actions) {
return res
.status(400)
.send('Error: missing fields: "user_email", "actions"');
}
// Get the user ID using Slack API
const userByEmail = await app.client.users.lookupByEmail({
email: user_email,
});
// Get the user ID using Slack API
const userByEmail = await app.client.users.lookupByEmail({
email: user_email,
});
const slackMessage = {
channel: userByEmail.user.id,
text: body,
blocks: [
{
type: "header",
text: { type: "plain_text", text: title },
},
{
type: "section",
text: { type: "mrkdwn", text: body },
},
],
};
const slackMessage = {
channel: userByEmail.user.id,
text: body,
blocks: [
{
type: "header",
text: { type: "plain_text", text: title },
},
{
type: "section",
text: { type: "mrkdwn", text: body },
},
],
};
// Add action buttons if they exist
if (actions && actions.length > 0) {
slackMessage.blocks.push({
type: "actions",
elements: actions.map((action) => ({
type: "button",
text: { type: "plain_text", text: action.label },
url: action.url,
})),
});
}
// Add action buttons if they exist
if (actions && actions.length > 0) {
slackMessage.blocks.push({
type: "actions",
elements: actions.map((action) => ({
type: "button",
text: { type: "plain_text", text: action.label },
url: action.url,
})),
});
}
// Post message to the user on Slack
await app.client.chat.postMessage(slackMessage);
// Post message to the user on Slack
await app.client.chat.postMessage(slackMessage);
res.status(204).send();
} catch (error) {
console.error("Error sending message:", error);
res.status(500).send();
}
});
res.status(204).send();
} catch (error) {
console.error("Error sending message:", error);
res.status(500).send();
}
});
// Acknowledge clicks on link_button, otherwise Slack UI
// complains about missing events.
app.action("button_click", async ({ body, ack, say }) => {
await ack(); // no specific action needed
});
// Acknowledge clicks on link_button, otherwise Slack UI
// complains about missing events.
app.action("button_click", async ({ body, ack, say }) => {
await ack(); // no specific action needed
});
// Start the Bolt app
(async () => {
await app.start(port);
console.log("⚡️ Coder Slack bot is running!");
})();
```
// Start the Bolt app
(async () => {
await app.start(port);
console.log("⚡️ Coder Slack bot is running!");
})();
```
3. Set environment variables to identify the Slack app:
4. Set environment variables to identify the Slack app:
```bash
export SLACK_BOT_TOKEN=xoxb-...
export SLACK_SIGNING_SECRET=0da4b...
```
```bash
export SLACK_BOT_TOKEN=xoxb-...
export SLACK_SIGNING_SECRET=0da4b...
```
4. Start the web application by running:
5. Start the web application by running:
```bash
node app.js
```
```bash
node app.js
```
## Enable Interactivity in Slack
+94 -94
View File
@@ -21,115 +21,115 @@ following:
The process of setting up a Teams workflow consists of three key steps:
1. Configure the Webhook Trigger.
1. Configure the Webhook Trigger.
Begin by configuring the trigger: **"When a Teams webhook request is
received"**.
Begin by configuring the trigger: **"When a Teams webhook request is
received"**.
Ensure the trigger access level is set to **"Anyone"**.
Ensure the trigger access level is set to **"Anyone"**.
2. Setup the JSON Parsing Action.
1. Setup the JSON Parsing Action.
Next, add the **"Parse JSON"** action, linking the content to the **"Body"**
of the received webhook request. Use the following schema to parse the
notification payload:
Add the **"Parse JSON"** action, linking the content to the **"Body"** of the
received webhook request. Use the following schema to parse the notification
payload:
```json
{
"type": "object",
"properties": {
"_version": {
"type": "string"
},
"payload": {
"type": "object",
"properties": {
"_version": {
"type": "string"
},
"user_email": {
"type": "string"
},
"actions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"url": {
"type": "string"
}
},
"required": ["label", "url"]
}
}
}
},
"title": {
"type": "string"
},
"body": {
"type": "string"
}
}
}
```
```json
{
"type": "object",
"properties": {
"_version": {
"type": "string"
},
"payload": {
"type": "object",
"properties": {
"_version": {
"type": "string"
},
"user_email": {
"type": "string"
},
"actions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"url": {
"type": "string"
}
},
"required": ["label", "url"]
}
}
}
},
"title": {
"type": "string"
},
"body": {
"type": "string"
}
}
}
```
This action parses the notification's title, body, and the recipient's email
address.
This action parses the notification's title, body, and the recipient's email
address.
3. Configure the Adaptive Card Action.
1. Configure the Adaptive Card Action.
Finally, set up the **"Post Adaptive Card in a chat or channel"** action
with the following recommended settings:
Finally, set up the **"Post Adaptive Card in a chat or channel"** action with
the following recommended settings:
**Post as**: Flow Bot
**Post as**: Flow Bot
**Post in**: Chat with Flow Bot
**Post in**: Chat with Flow Bot
**Recipient**: `user_email`
**Recipient**: `user_email`
Use the following _Adaptive Card_ template:
Use the following _Adaptive Card_ template:
```json
{
"$schema": "https://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Image",
"url": "https://coder.com/coder-logo-horizontal.png",
"height": "40px",
"altText": "Coder",
"horizontalAlignment": "center"
},
{
"type": "TextBlock",
"text": "**@{replace(body('Parse_JSON')?['title'], '"', '\"')}**"
},
{
"type": "TextBlock",
"text": "@{replace(body('Parse_JSON')?['body'], '"', '\"')}",
"wrap": true
},
{
"type": "ActionSet",
"actions": [@{replace(replace(join(body('Parse_JSON')?['payload']?['actions'], ','), '{', '{"type": "Action.OpenUrl",'), '"label"', '"title"')}]
}
]
}
```
```json
{
"$schema": "https://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Image",
"url": "https://coder.com/coder-logo-horizontal.png",
"height": "40px",
"altText": "Coder",
"horizontalAlignment": "center"
},
{
"type": "TextBlock",
"text": "**@{replace(body('Parse_JSON')?['title'], '"', '\"')}**"
},
{
"type": "TextBlock",
"text": "@{replace(body('Parse_JSON')?['body'], '"', '\"')}",
"wrap": true
},
{
"type": "ActionSet",
"actions": [@{replace(replace(join(body('Parse_JSON')?['payload']?['actions'], ','), '{', '{"type": "Action.OpenUrl",'), '"label"', '"title"')}]
}
]
}
```
_Notice_: The Coder `actions` format differs from the `ActionSet` schema, so
its properties need to be modified: include `Action.OpenUrl` type, rename
`label` to `title`. Unfortunately, there is no straightforward solution for
`for-each` pattern.
_Notice_: The Coder `actions` format differs from the `ActionSet` schema, so
its properties need to be modified: include `Action.OpenUrl` type, rename
`label` to `title`. Unfortunately, there is no straightforward solution for
`for-each` pattern.
Feel free to customize the payload to modify the logo, notification title,
or body content to suit your needs.
Feel free to customize the payload to modify the logo, notification title, or
body content to suit your needs.
## Enable Webhook Integration