feat(addons): wire the Discord add-on into packaging, catalog and docs

List the add-on in the package workflow matrix, the package checks, the host
operation policy, the catalog template and index, the case inventories of the
contract and packaging suites, and the architecture guard (two links to the
project community server are not the add-on). Document the connector in the
chats guide.

Assisted-by: Claude:claude-sonnet-5-5 go
This commit is contained in:
Ettore Di Giacinto committed 2026-11-04 15:01:17 +00:00
1 parent 36c405b321
commit 7abbef8236
15 files changed
+236 -22

No files matched your search

+3 -3
View File
@@ -2,7 +2,7 @@
name: 'add-on packages'
# Builds, checks, signs and publishes the first-party add-on OCI packages
# (Tables, Voice, Face, Fine-tuning, Quantization, Studio, Talk, Telegram, Slack, Office and
# (Tables, Voice, Face, Fine-tuning, Quantization, Studio, Talk, Telegram, Slack, Discord, Office and
# Sandbox). This workflow is
# independent of the backend image workflows: it has its own registry input,
# its own signer identity and its own jobs.
@@ -117,7 +117,7 @@ jobs:
strategy:
fail-fast: true
matrix:
addon: [tables, voice, face, fine-tuning, quantization, studio, talk, telegram, slack, github, jellyfin, office, sandbox, web, memory]
addon: [tables, voice, face, fine-tuning, quantization, studio, talk, telegram, slack, discord, github, jellyfin, office, sandbox, web, memory]
steps:
- uses: actions/checkout@v7
with:
@@ -236,7 +236,7 @@ jobs:
# event it fails the plan. A missing or malformed platform file, or
# any other error, fails the plan on every event.
include='[]'
for id in tables voice face fine-tuning quantization studio talk telegram slack github jellyfin office sandbox web memory; do
for id in tables voice face fine-tuning quantization studio talk telegram slack discord github jellyfin office sandbox web memory; do
cfg="addons/$id/package/platforms.yaml"
err="matrix-$id.err"
if rows=$(go run ./scripts/addons/cmd/addonpkg matrix --config "$cfg" 2>"$err"); then
@@ -92,7 +92,7 @@ var _ = Describe("official library index", func() {
Expect(versionPattern.MatchString(r.Version)).To(BeTrue(), r.ID)
Expect(r.Artifact).To(HavePrefix("oci://"), r.ID)
}
Expect(slices.Compact(slices.Sorted(slices.Values(ids)))).To(ConsistOf("fine-tuning", "quantization", "voice", "face", "studio", "talk", "telegram", "slack", "github", "jellyfin", "tables", "office", "sandbox", "web", "memory"))
Expect(slices.Compact(slices.Sorted(slices.Values(ids)))).To(ConsistOf("fine-tuning", "quantization", "voice", "face", "studio", "talk", "telegram", "slack", "discord", "github", "jellyfin", "tables", "office", "sandbox", "web", "memory"))
})
})
@@ -100,6 +100,8 @@ var allowed = []entry{
{"docs/content/reference/cli-reference.md", "(Telegram, Slack)", "help text that gives chat platforms as examples"},
{"docs/content/getting-started/_index.en.md", "Telegram", "a chat platform as an example of where an agent can answer"},
{"docs/content/getting-started/first-agent.md", "Telegram", "a chat platform as an example of where an agent can answer"},
{"docs/content/reference/ai-coding-assistants.md", "Discord channel", "the project community server, not the add-on"},
{"docs/content/getting-started/troubleshooting.md", "LocalAI Discord", "the project community server, not the add-on"},
// Ordinary words that equal an id or a title.
{"core/schema/chats.go", `example:"Sandbox"`, "a sample value in the API documentation"},
+2 -1
View File
@@ -7,6 +7,7 @@
"office": ["workspace."],
"web": ["workspace."],
"telegram": ["connector."],
"slack": ["connector."]
"slack": ["connector."],
"discord": ["connector."]
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ LocalAI provides a comprehensive set of features for running AI models locally.
- **[Chats]({{% relref "features/chats" %}})** - Conversations the server keeps, with files, sub-agents, skills and add-on tools.
- **[Saved agents, triggers and inbox]({{% relref "features/chats-agents" %}})** - Instructions and exact tool grants, schedules, and notifications.
- **[Collections]({{% relref "features/collections" %}})** - The knowledge base: upload documents and search them by meaning.
- **[Telegram and Slack connectors]({{% relref "features/chats-connectors" %}})** - Talk to an agent from Telegram or Slack.
- **[Telegram, Slack and Discord connectors]({{% relref "features/chats-connectors" %}})** - Talk to an agent from Telegram, Slack or Discord.
- **[Moving from the old chat and agents]({{% relref "features/chats-migration" %}})** - What was converted and what was dropped.
- **[Model Context Protocol (MCP)]({{% relref "features/mcp" %}})** - Give a model external tools over MCP.
- **[LocalAI Assistant]({{% relref "features/localai-assistant" %}})** - Chat to administer your LocalAI instance.
+2 -2
View File
@@ -700,7 +700,7 @@ The three roles split the work of a LocalAI process as follows.
| The carrier | Loopback, with keys made at each start | Served on the network | Client only |
| Chat turns | Yes | No | Yes |
| Model loading | In this process, or on workers in distributed mode | No models. In distributed mode it routes loads to workers. | In this process, or on workers in distributed mode |
| Connector polling (Telegram, Slack) | Yes | No | Yes, on the one leader frontend |
| Connector polling (Telegram, Slack, Discord) | Yes | No | Yes, on the one leader frontend |
| Triggers, loops and agent jobs | Yes | No | Yes |
| API and UI for users | Yes | No. Send users to a frontend. | Yes |
@@ -2451,7 +2451,7 @@ The owner process holds everything about host grants. A frontend stores nothing
## Connector add-ons
A connector add-on carries the messages of a messaging platform between the platform
and the [chats](/features/chats-connectors/). The Telegram and Slack add-ons are
and the [chats](/features/chats-connectors/). The Telegram, Slack and Discord add-ons are
first-party connector add-ons. The host keeps what is the same for every platform (the
bindings, the sealed bot token, the allowlist, the sender log, one chat for each sender
or thread, the turn and its notifications), so an add-on holds only the transport and
+118 -3
View File
@@ -7,7 +7,7 @@ url = "/features/chats-connectors/"
The Connectors page of the [chats](/features/chats/) (`/app/chats/connectors`, under **Agents** in the sidebar) lists the messaging bindings you made, and the MCP servers have a page of their own (**MCP connectors** in the sidebar, `/app/mcp-connectors`):
- A **messaging connector** binds a bot of a platform to one of your agents, so a person can talk to it from that platform. The platform comes from a **connector add-on**: the [Telegram add-on](#telegram) or the [Slack add-on](#slack) (the [GitHub add-on](#github) and the [Jellyfin add-on](#jellyfin) hold a token for your agents instead of carrying messages). This is the first half of this page.
- A **messaging connector** binds a bot of a platform to one of your agents, so a person can talk to it from that platform. The platform comes from a **connector add-on**: the [Telegram add-on](#telegram), the [Slack add-on](#slack) or the [Discord add-on](#discord) (the [GitHub add-on](#github) and the [Jellyfin add-on](#jellyfin) hold a token for your agents instead of carrying messages). This is the first half of this page.
- An **MCP server** gives your chats and agents more tools. See [MCP servers](#mcp-servers).
## Connector add-ons
@@ -34,7 +34,7 @@ add-on"; nothing polls and nothing is lost.
Each kind in the add-on manifest declares `delivery`:
- **`pull`** (Slack and Telegram). The host polls the add-on. One process in the
- **`pull`** (Slack, Discord and Telegram). The host polls the add-on. One process in the
cluster, the leader, does the polling. It stores the offset with the binding. When
the leader stops, a new leader continues from that offset.
- **`push`**. The add-on calls the host operation `connector.inbound` for each
@@ -303,6 +303,120 @@ Slack counterpart).
The Events API over HTTP is not supported: it needs a public address, and Socket Mode
gives the same events without one.
## Discord
The Discord add-on binds a Discord bot to one of your [saved agents](/features/chats-agents/).
People talk to the agent in a direct message with the bot, or by mentioning the bot in a
channel or thread of a server. The add-on keeps a Gateway link (a websocket that the
add-on opens to Discord), so LocalAI needs no public address.
### Set up a binding
1. Install the **Discord** add-on from the add-on gallery.
2. At `discord.com/developers/applications` choose **New Application**. On the **Bot**
page choose **Reset Token** and copy the token once.
3. On the same page, under **Privileged Gateway Intents**, turn on **Message Content
Intent**. Without it Discord sends no text from servers. The add-on needs no other
privileged intent. The page **Test** shows a warning when the intent is off.
4. Add the bot to a server with the link that the add-on page builds under **How to make
the Discord bot**: type the Application ID and open the link. It uses the scope
`bot` and the permission number `274878008320`: View Channels, Send Messages,
Send Messages in Threads, Attach Files and Read Message History. Never give the bot
Administrator, Manage Messages or Mention Everyone.
5. On the Discord add-on page make a binding: a name, the token, the agent, and who may use
it. When you save, LocalAI checks the token and shows which bot it belongs to.
6. Mention the bot in a channel it can see, or write to it in a direct message.
```
curl -X POST .../api/chats/connectors -d '{"kind": "discord", "name": "Team bot", "token": "<bot token>", "agent_id": "<id>", "enabled": true, "allow": ["<user ID>", "<channel ID>"]}'
```
### How Discord messages map to chats
- **A direct message** with the bot is a conversation with its sender. The host answers it
only for a listed person, or when the binding accepts anyone (a person can write to a bot
only when they share a server with it).
- **A mention** of the bot in a server channel, or a reply to a message of the bot, addresses
the bot. The answer goes in the same channel. Any other channel message is not read.
`@everyone`, `@here` and role mentions never address the bot.
- **A thread** (also a forum post) is a chat of its own, and its chat is the parent channel,
so listing a channel also covers its threads. The bot follows a thread once it was
mentioned in it: later messages of the thread need no new mention. The setting **Threads
the bot follows** (`followThreads`) is `mentioned` (the default) or `never`. A followed
thread stays followed for 24 hours after its last message; the add-on keeps the list in
its data directory (channel and thread IDs and times, never a token). The bot does not
start threads.
- **Earlier messages of a thread** are read once when a thread chat starts (see
[Earlier messages of a thread](#earlier-messages-of-a-thread)): at most 30, through
`GET /channels/{thread}/messages`, which allows 100 per call. Messages of other bots and of
Discord itself (joins, pins) are left out. This needs Read Message History.
- **Never answered**: every message that a bot or a webhook wrote, the messages of the bot
itself, and system messages. Two bots cannot answer each other.
- **Answers** are plain Markdown (Discord reads it) and split at 2,000 characters, the limit
of Discord, with code blocks closed and opened again at each cut. Every message carries
`allowed_mentions: {"parse": []}`, and the add-on also breaks up `@everyone`, `@here` and
`<@...>` mentions in the text, so the answer can never ping anyone. The bot sets no typing
indicator (Discord discourages it for bots); a working message (see
[How the bot answers](#how-the-bot-answers)) shows that the agent works. Edits are limited
by Discord, so a live answer updates about once a second.
- **Files.** A file attached to a message (at most 8, 10 MB each) is downloaded from the
Discord CDN, with a fresh link read at the moment of the download and without the token,
and attached to the chat. A file that the agent makes is uploaded (10 MB at most).
- **Waits.** When Discord answers `429`, the add-on waits the time it asks for (the
`Retry-After` header, else `retry_after` of the body) once and tries again. An edit is
not repeated.
- **Start a new chat** with `!new`. Discord clients may take a message that starts with a
slash for one of their own commands, so `!new` is the documented form (see
[Start a new chat with `/new`](#start-a-new-chat-with-new)). The add-on has no slash
commands.
### Who may use the bot
The binding has one list of Discord IDs (numbers; turn on Developer Mode in Discord, then
right-click and choose Copy ID):
- **A user ID**: the person may use the bot in a direct message and by mentioning it in any
channel the bot reads. The person is a listed sender, so they hold every capability of the
binding that is not sensitive.
- **A channel ID**: everyone in the channel (and its threads) may mention the bot there.
They get no capability of their own (see
[Capabilities, roles and pairing](#capabilities-roles-and-pairing)).
- **No names and no server IDs.** The page accepts IDs only: names are not unique and
change hands. A server (guild) ID matches nothing, because the chat of a message is a
channel; use the channel IDs. The page names every entry that is not an ID. The add-on
cannot see the allowlist, so a guild ID that is saved over the API is not refused: it is
only inert.
**Anyone** (`accept_any`) is for public channels and read-only agents. Anyone who can message
the bot, or mention it in a channel it can read, is let in. The host gives these senders
**no capability** (only listed or paired senders hold one), so the agent cannot save memory,
propose triggers or take their files for them. The agent's own tools still work for them:
give the agent read-only tools for this connection. Text from a public channel is untrusted
and can try to steer the agent (prompt injection). The host limits each sender to 20 messages
a minute, and the add-on queues at most 30 messages a minute of one sender, so one person
cannot fill the queue of the link. Roles of a server are not mapped: the add-on declares no
role, because a server administrator is not a role ID and the permission check would need
each member's roles and each channel's overwrites.
### Notifications in Discord
Set a channel ID as the notification chat to post them there, or `user:` followed by your
user ID to receive them as direct messages from the bot.
### The token of a bot
The token is sealed like the token of a Telegram bot, and no API returns it. The add-on holds
it in memory while it keeps the Gateway link of the binding open, writes it nowhere and calls
only `discord.com`, the Gateway and the Discord CDN. The link is closed after two minutes
without a poll (the binding was switched off or deleted).
A binding is switched off, with a notification, when Discord refuses the token (HTTP 401, or
Gateway close code 4004) or the intents (4013, 4014: the Message Content Intent is off). The
link reconnects with a backoff after any other failure and resumes the session, so events
are not lost across a short break. Discord allows 1,000 identifies a day for a bot, so the
add-on never retries a refused token. Use one LocalAI instance for one bot: a second process
that uses the same token would receive the same events and answer twice.
## What a message gets
- **The agent's tools only.** A message gets exactly the tools the agent grants,
@@ -479,7 +593,7 @@ declares for its platform, the limit of the platform (the host ceiling that the
administrator sets) and the limit that you set for the bot. The agent reads the same
number in its instructions, so it does not promise a file that cannot go.
An add-on that streams files (Slack and Telegram do) has no inline cap: a file moves in
An add-on that streams files (Slack, Discord and Telegram do) has no inline cap: a file moves in
chunks, by a short-lived handle, so a 6 MB PDF goes both ways. An add-on that does not
stream keeps the inline cap of 1 MiB, whatever you set, and the sentence says "limit of
this add-on version".
@@ -489,6 +603,7 @@ What the platforms allow, from their own documentation:
| Platform | Bot reads a file | Bot sends a file |
|---|---|---|
| Slack | No separate bot limit is documented. The add-on declares the 1 GB per file that a member can add ([Add files to Slack](https://slack.com/help/articles/201330736-Add-files-to-Slack)); the [upload API](https://docs.slack.dev/reference/methods/files.getUploadURLExternal) states none either. | 1 GB per file, by the external upload flow. |
| Discord | 10 MB: the add-on declares a limit that fits every server level; Discord states the limit per server and per person, and a bot may be allowed less. | 10 MB, as one attachment of a message. |
| Telegram | 20 MB with `getFile` on the public Bot API ([Bots FAQ](https://core.telegram.org/bots/faq)). | 50 MB for a document, 10 MB for a photo; a larger picture goes as a document ([Bots FAQ](https://core.telegram.org/bots/faq)). |
A local Bot API server lifts the Telegram limits (no download limit, 2000 MB upload,
+1 -1
View File
@@ -3,7 +3,7 @@
This directory holds the template of the catalog entries of the first-party add-ons.
It holds no live URL, registry or namespace. The registry and repository are inputs of
the release workflow (`ADDON_OCI_REPOSITORY`). Each first-party add-on has its own
template (`tables`, `voice`, `face`, `fine-tuning`, `quantization`, `studio`, `talk`, `office`, `sandbox`, `web`, `memory`, `github`, `jellyfin`) and its own release
template (`tables`, `voice`, `face`, `fine-tuning`, `quantization`, `studio`, `talk`, `office`, `sandbox`, `web`, `memory`, `github`, `jellyfin`, `discord`) and its own release
platform matrix, `addons/<id>/package/platforms.yaml`. The templates differ in `name:` only.
## Generate the index
+18
View File
@@ -0,0 +1,18 @@
id: "{{addon}}"
name: Discord
description: "{{description}}"
version: "{{version}}"
channel: "{{channel}}"
artifact: "oci://{{repository}}:{{tag}}"
digest: "{{digest}}"
os: "{{os}}"
arch: "{{arch}}"
policy:
requireSignature: true
issuer: "{{issuer}}"
identity: "{{identity}}"
ui:
title: "{{title}}"
description: "{{description}}"
mainPage: "{{main_page}}"
icon: "{{icon}}"
+72
View File
@@ -10,6 +10,78 @@
# backend. A tagged release does not read this file for its pins: the release
# workflow renders pinned entries into the asset addons-index.yaml of the
# release (see .agents/preparing-a-release.md).
- id: "discord"
name: Discord
description: "Bind a Discord bot to one of your saved agents, so people can talk to it from Discord."
version: "0.0.0-development"
channel: "development"
artifact: "oci://quay.io/go-skynet/local-ai-addons:discord-development"
mutable: true
os: "darwin"
arch: "amd64"
policy:
requireSignature: true
issuer: "https://token.actions.githubusercontent.com"
identity: "https://github.com/mudler/LocalAI/.github/workflows/addons-package.yml@refs/heads/master"
ui:
title: "Discord"
description: "Bind a Discord bot to one of your saved agents, so people can talk to it from Discord."
mainPage: "bots"
icon: "ui/icon.png"
- id: "discord"
name: Discord
description: "Bind a Discord bot to one of your saved agents, so people can talk to it from Discord."
version: "0.0.0-development"
channel: "development"
artifact: "oci://quay.io/go-skynet/local-ai-addons:discord-development"
mutable: true
os: "darwin"
arch: "arm64"
policy:
requireSignature: true
issuer: "https://token.actions.githubusercontent.com"
identity: "https://github.com/mudler/LocalAI/.github/workflows/addons-package.yml@refs/heads/master"
ui:
title: "Discord"
description: "Bind a Discord bot to one of your saved agents, so people can talk to it from Discord."
mainPage: "bots"
icon: "ui/icon.png"
- id: "discord"
name: Discord
description: "Bind a Discord bot to one of your saved agents, so people can talk to it from Discord."
version: "0.0.0-development"
channel: "development"
artifact: "oci://quay.io/go-skynet/local-ai-addons:discord-development"
mutable: true
os: "linux"
arch: "amd64"
policy:
requireSignature: true
issuer: "https://token.actions.githubusercontent.com"
identity: "https://github.com/mudler/LocalAI/.github/workflows/addons-package.yml@refs/heads/master"
ui:
title: "Discord"
description: "Bind a Discord bot to one of your saved agents, so people can talk to it from Discord."
mainPage: "bots"
icon: "ui/icon.png"
- id: "discord"
name: Discord
description: "Bind a Discord bot to one of your saved agents, so people can talk to it from Discord."
version: "0.0.0-development"
channel: "development"
artifact: "oci://quay.io/go-skynet/local-ai-addons:discord-development"
mutable: true
os: "linux"
arch: "arm64"
policy:
requireSignature: true
issuer: "https://token.actions.githubusercontent.com"
identity: "https://github.com/mudler/LocalAI/.github/workflows/addons-package.yml@refs/heads/master"
ui:
title: "Discord"
description: "Bind a Discord bot to one of your saved agents, so people can talk to it from Discord."
mainPage: "bots"
icon: "ui/icon.png"
- id: "face"
name: Face
description: "Find out whose face is in a photo, or whether two photos show the same person."
+1 -1
View File
@@ -18,7 +18,7 @@ var _ = Describe("official host policy deployment", func() {
grants, err := config.LoadAddonHostPolicy(filepath.Join(root, "deploy", "addons", "host-policy.json"))
Expect(err).NotTo(HaveOccurred())
// Deployment-owned exception: historical identity grants, not core defaults.
legacy := "fine-tuning=finetune.,quantization=quantize.,sandbox=workspace.,sandbox=egress.,office=workspace.,web=workspace.,telegram=connector.,slack=connector."
legacy := "fine-tuning=finetune.,quantization=quantize.,sandbox=workspace.,sandbox=egress.,office=workspace.,web=workspace.,telegram=connector.,slack=connector.,discord=connector."
want, err := config.ParseAddonHostOps(legacy)
Expect(err).NotTo(HaveOccurred())
Expect(grants).To(Equal(want))
@@ -31,10 +31,11 @@ var officialRequests = map[string]contract.HostOperations{
// file operations are optional. Delivery is pull, so neither add-on calls
// connector.inbound.
"slack": {Optional: strings.Fields("connector.file.get connector.file.put")},
"discord": {Optional: strings.Fields("connector.file.get connector.file.put")},
"telegram": {Optional: strings.Fields("connector.file.get connector.file.put")},
}
// connectorRoot holds the file transfer code that slack and telegram share. It
// connectorRoot holds the file transfer code that the connector add-ons share. It
// names its operations by constant, so the audit reads those too.
const connectorRoot = "addons/internal/connector"
@@ -61,7 +62,7 @@ var _ = Describe("official typed package contracts", func() {
// of support; compare independently with the real registered vocabulary.
var used []string
roots := []string{root}
if id == "slack" || id == "telegram" {
if id == "slack" || id == "telegram" || id == "discord" {
roots = append(roots, filepath.Join("../../..", connectorRoot))
}
for _, root := range roots {
+4 -4
View File
@@ -954,7 +954,7 @@ var _ = Describe("catalog template", func() {
Expect(err).To(MatchError(ContainSubstring("development channel only")))
})
It("renders an entry for every first-party add-on from its own template", func() {
for _, id := range []string{"tables", "voice", "face", "fine-tuning", "quantization", "studio", "talk", "telegram", "slack", "github", "jellyfin", "office", "sandbox", "web", "memory"} {
for _, id := range []string{"tables", "voice", "face", "fine-tuning", "quantization", "studio", "talk", "telegram", "slack", "discord", "github", "jellyfin", "office", "sandbox", "web", "memory"} {
p := newProjectFor(id, both)
m := p.merged("1.2.3", rev, nil)
repoName := newRegistry().repo("addons")
@@ -1051,7 +1051,7 @@ var _ = Describe("catalog template", func() {
for _, e := range entries {
got[e.ID] = e.ToolCount
}
Expect(got).To(Equal(map[string]int{"tables": 16, "fine-tuning": 8, "quantization": 6, "voice": 0, "face": 0, "studio": 0, "talk": 0, "telegram": 0, "slack": 0, "github": 14, "jellyfin": 16, "office": 11, "sandbox": 5, "web": 5, "memory": 0}))
Expect(got).To(Equal(map[string]int{"tables": 16, "fine-tuning": 8, "quantization": 6, "voice": 0, "face": 0, "studio": 0, "talk": 0, "telegram": 0, "slack": 0, "discord": 0, "github": 14, "jellyfin": 16, "office": 11, "sandbox": 5, "web": 5, "memory": 0}))
})
It("refuses missing inputs, backend repositories, bad channels and unknown placeholders", func() {
reg := newRegistry()
@@ -1213,7 +1213,7 @@ var _ = Describe("workflow policy", func() {
})
It("rejects a workflow that names one add-on by path or skips an add-on in the matrix", func() {
expect(mutate("node \"addons/$ADDON/ui/build.mjs\"\n - name: Fill", "node addons/tables/ui/build.mjs\n - name: Fill"), "P_ADDON_PATH")
expect(mutate(" addon: [tables, voice, face, fine-tuning, quantization, studio, talk, telegram, slack, github, jellyfin, office, sandbox, web, memory]\n", " platform: [x]\n"), "P_MATRIX")
expect(mutate(" addon: [tables, voice, face, fine-tuning, quantization, studio, talk, telegram, slack, discord, github, jellyfin, office, sandbox, web, memory]\n", " platform: [x]\n"), "P_MATRIX")
})
It("rejects a plan that does not refuse an add-on without an enabled platform", func() {
expect(mutate("has no enabled platform; refusing to publish", "has no platform"), "P_FAIL_CLOSED")
@@ -1263,7 +1263,7 @@ var _ = Describe("workflow policy", func() {
// firstPartyAddons are the add-ons the release workflow packages. The list is
// written out so a new add-on is a deliberate change here and in the workflow.
var firstPartyAddons = []string{"tables", "voice", "face", "fine-tuning", "quantization", "studio", "talk", "telegram", "slack", "github", "jellyfin", "office", "sandbox", "web", "memory"}
var firstPartyAddons = []string{"tables", "voice", "face", "fine-tuning", "quantization", "studio", "talk", "telegram", "slack", "discord", "github", "jellyfin", "office", "sandbox", "web", "memory"}
func readWorkflow() string {
GinkgoHelper()
+3 -3
View File
@@ -89,7 +89,7 @@ test -f "$work/voice/receipt.json"
case_ok 'package.sh builds and verifies the voice add-on for the host'
# A platform other than the host is refused for every add-on, whatever the config.
if [ "$host" = linux/amd64 ]; then foreign=linux/arm64; else foreign=linux/amd64; fi
for id in tables voice face fine-tuning quantization studio talk telegram slack office sandbox web memory; do
for id in tables voice face fine-tuning quantization studio talk telegram slack discord office sandbox web memory; do
expect_fail "package.sh refuses a foreign platform without an executable: $id" 'is not this host' sh scripts/addons/package.sh --config "addons/$id/package/platforms.yaml" --layout "$work/foreign-$id" --version 1.2.3 --platform "$foreign" --local-check
done
case_ok 'package.sh refuses a foreign platform without an executable for every add-on'
@@ -123,11 +123,11 @@ mkdir -p "$work/runtime/gvisor-bin"
for f in runsc gvisor-profile cloud-hypervisor virtiofsd gvisor-bin/checkpointgofer gvisor-bin/gvisor-sentry-prewarmer gvisor-bin/gvisor_sentry gvisor-bin/runsc-fd-parking gvisor-bin/runsc-metric-server; do
printf 'stand-in\n' >"$work/runtime/$f"
done
for id in tables voice face fine-tuning quantization studio talk telegram slack office sandbox web memory; do
for id in tables voice face fine-tuning quantization studio talk telegram slack discord office sandbox web memory; do
PATH="$work/stubbin:$PATH" sh scripts/addons/package.sh --config "addons/$id/package/platforms.yaml" --layout "$work/stub-$id" --version 1.2.3 --platform "$host" --runtime-dir "$work/runtime" --local-check >"$work/out" 2>&1 || { cat "$work/out" >&2; exit 1; }
done
grep -x './addons/tables/cmd/tables CGO_ENABLED=1' "$work/gobuild.log" >/dev/null
for id in voice face fine-tuning quantization studio talk telegram slack office sandbox web memory; do
for id in voice face fine-tuning quantization studio talk telegram slack discord office sandbox web memory; do
grep -x "./addons/$id/cmd/$id CGO_ENABLED=0" "$work/gobuild.log" >/dev/null || { echo "FAIL: $id is not built with CGO_ENABLED=0" >&2; cat "$work/gobuild.log" >&2; exit 1; }
done
case_ok 'package.sh builds Tables with cgo and the pure Go add-ons without'
+5
View File
@@ -482,6 +482,7 @@
"audits actual host calls and strict package contract for quantization",
"audits actual host calls and strict package contract for sandbox",
"audits actual host calls and strict package contract for slack",
"audits actual host calls and strict package contract for discord",
"audits actual host calls and strict package contract for studio",
"audits actual host calls and strict package contract for tables",
"audits actual host calls and strict package contract for talk",
@@ -553,6 +554,7 @@
"has consistent package files for quantization",
"has consistent package files for sandbox",
"has consistent package files for slack",
"has consistent package files for discord",
"has consistent package files for studio",
"has consistent package files for tables",
"has consistent package files for talk",
@@ -4465,6 +4467,7 @@
"package.sh refuses a foreign platform without an executable: talk",
"package.sh refuses a foreign platform without an executable: telegram",
"package.sh refuses a foreign platform without an executable: slack",
"package.sh refuses a foreign platform without an executable: discord",
"package.sh refuses a foreign platform without an executable for every add-on",
"package.sh builds Tables with cgo and the pure Go add-ons without",
"matrix fails closed while no platform is enabled",
@@ -4516,6 +4519,7 @@
"go: has consistent package files for quantization",
"go: has consistent package files for sandbox",
"go: has consistent package files for slack",
"go: has consistent package files for discord",
"go: has consistent package files for studio",
"go: has consistent package files for tables",
"go: has consistent package files for talk",
@@ -4570,6 +4574,7 @@
"go: audits actual host calls and strict package contract for quantization",
"go: audits actual host calls and strict package contract for sandbox",
"go: audits actual host calls and strict package contract for slack",
"go: audits actual host calls and strict package contract for discord",
"go: audits actual host calls and strict package contract for studio",
"go: audits actual host calls and strict package contract for tables",
"go: audits actual host calls and strict package contract for talk",