From 163ed2b9d1ebada3464bcb020e37eab3418b17d6 Mon Sep 17 00:00:00 2001 From: crankshift Date: Thu, 28 May 2026 07:49:28 +0200 Subject: [PATCH 1/3] feat: add Telegram forwarded author context --- FEATURES.md | 1 + README.md | 2 + src/adapters/telegram/author.js | 52 +++++++ src/adapters/telegram/bot.js | 12 +- src/core/opencode/client.js | 37 ++++- tests/adapters/telegramAuthor.test.js | 92 +++++++++++++ tests/adapters/telegramBot.test.js | 191 +++++++++++++++++++++++--- tests/core/opencodeClient.test.js | 72 ++++++++++ 8 files changed, 439 insertions(+), 20 deletions(-) create mode 100644 src/adapters/telegram/author.js create mode 100644 tests/adapters/telegramAuthor.test.js diff --git a/FEATURES.md b/FEATURES.md index 949dfd0..14ff329 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -31,6 +31,7 @@ OpenCode Remote is currently a Telegram gateway for OpenCode with text, image, a - `/help` shows the available bot commands. - The Telegram slash-command menu is refreshed on gateway startup. - Non-command text from the authorized user is sent to OpenCode as a prompt. +- Forwarded Telegram text, photo, album, and voice prompts include safe original-author context when Telegram provides it, with a safe fallback to the authorized user. - The bot shows Telegram typing activity while a prompt is running. - The bot can show an editable `Activity` message with OpenCode tools and skills used during a prompt. - OpenCode permission requests are sent as text with `Allow once`, `Always allow`, and `Deny` buttons, even when voice replies are enabled. diff --git a/README.md b/README.md index 4228851..5cc9290 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,8 @@ The bot currently supports: Any non-command text message from the authorized Telegram user is sent to OpenCode as a prompt. If no active session is selected, the gateway creates one automatically. +Forwarded Telegram text, photo, album, and voice prompts include safe author context for OpenCode when Telegram provides the original author. If Telegram hides or omits the forwarded author, the prompt falls back to the authorized Telegram user without exposing raw Telegram payloads or numeric user IDs. + When a new OpenCode session starts, OpenCode Remote sends hidden gateway context with no assistant reply. This helps the agent understand that voice input may arrive as transcripts and that final text can be delivered as voice notes when voice mode is enabled. When OpenCode requests permission during a prompt, the bot sends a text message with `Allow once`, `Always allow`, and `Deny` buttons. Permission prompts are always text, including when `/voice on` or `/voice all` would make normal assistant replies voice-only. diff --git a/src/adapters/telegram/author.js b/src/adapters/telegram/author.js new file mode 100644 index 0000000..6b495c5 --- /dev/null +++ b/src/adapters/telegram/author.js @@ -0,0 +1,52 @@ +export function authorContextFromTelegramMessage(message) { + const forwardedName = forwardedAuthorName(message?.forward_origin) + if (forwardedName) { + return { name: forwardedName, source: "forwarded" } + } + + return { + name: telegramUserDisplayName(message?.from) ?? "Authorized Telegram user", + source: "sender", + } +} + +function forwardedAuthorName(origin) { + switch (origin?.type) { + case "user": + return telegramUserDisplayName(origin.sender_user) + case "hidden_user": + return safeDisplayName(origin.sender_user_name) + case "chat": + return telegramChatDisplayName(origin.sender_chat) + case "channel": + return telegramChatDisplayName(origin.chat) + default: + return null + } +} + +function telegramUserDisplayName(user) { + const fullName = safeDisplayName([user?.first_name, user?.last_name].filter(Boolean).join(" ")) + if (fullName) { + return fullName + } + const username = safeDisplayName(user?.username) + return username ? `@${username.replace(/^@/u, "")}` : null +} + +function telegramChatDisplayName(chat) { + const title = safeDisplayName(chat?.title) + if (title) { + return title + } + const username = safeDisplayName(chat?.username) + return username ? `@${username.replace(/^@/u, "")}` : null +} + +function safeDisplayName(value) { + if (typeof value !== "string") { + return null + } + const name = value.replace(/\s+/gu, " ").trim() + return name || null +} diff --git a/src/adapters/telegram/bot.js b/src/adapters/telegram/bot.js index 1f8a2c3..80fbdf3 100644 --- a/src/adapters/telegram/bot.js +++ b/src/adapters/telegram/bot.js @@ -7,6 +7,7 @@ import { recordProgressEvent, } from "../../core/formatting/progressText.js" import { isAuthorizedTelegramUser } from "./auth.js" +import { authorContextFromTelegramMessage } from "./author.js" import { captionFromMessages, cleanupAttachments as defaultCleanupMediaAttachments, @@ -275,7 +276,10 @@ export function createTelegramBot({ try { await setEmojiReaction(ctx, chatId, messageId, "👀", logger) const response = await sendPromptWithProgress( - formatPromptWithTelegramReactionInstruction(ctx.message.text), + formatPromptWithTelegramReactionInstruction({ + text: ctx.message.text, + author: authorContextFromTelegramMessage(ctx.message), + }), progress, ctx, ) @@ -337,6 +341,7 @@ export function createTelegramBot({ const response = await sendPromptWithProgress( formatPromptWithTelegramReactionInstruction({ text: captionFromMessages(messages), + author: authorContextFromTelegramMessage(messages[0]), attachments, }), progress, @@ -398,7 +403,10 @@ export function createTelegramBot({ const transcript = await voiceService.transcribe(attachment.filePath) const progress = await createPromptProgressRenderer(ctx) const response = await sendPromptWithProgress( - formatPromptWithTelegramReactionInstruction(transcript), + formatPromptWithTelegramReactionInstruction({ + text: transcript, + author: authorContextFromTelegramMessage(ctx.message), + }), progress, ctx, ) diff --git a/src/core/opencode/client.js b/src/core/opencode/client.js index 2dc4b9f..4b4fbe7 100644 --- a/src/core/opencode/client.js +++ b/src/core/opencode/client.js @@ -368,10 +368,45 @@ function toPromptParts(prompt) { mime: attachment.mime, url: attachment.url, })), - { type: "text", text: String(prompt?.text ?? "") }, + { type: "text", text: formatPromptText(prompt) }, ] } +function formatPromptText(prompt) { + const text = String(prompt?.text ?? "") + const author = normalizePromptAuthor(prompt?.author) + if (!author) { + return text + } + + return [ + "Message author context:", + `- Author: ${author.name}`, + `- Attribution: ${formatAuthorAttribution(author.source)}`, + "", + "Message:", + text, + ].join("\n") +} + +function normalizePromptAuthor(author) { + if (!author || typeof author !== "object") { + return null + } + const name = firstString(author.name) + if (!name) { + return null + } + return { name, source: firstString(author.source) ?? "sender" } +} + +function formatAuthorAttribution(source) { + if (source === "forwarded") { + return "forwarded original author" + } + return "message sender" +} + function toData(result) { if (result && typeof result === "object" && "data" in result) { return result.data diff --git a/tests/adapters/telegramAuthor.test.js b/tests/adapters/telegramAuthor.test.js new file mode 100644 index 0000000..669c4e7 --- /dev/null +++ b/tests/adapters/telegramAuthor.test.js @@ -0,0 +1,92 @@ +import { describe, expect, test } from "vitest" +import { authorContextFromTelegramMessage } from "../../src/adapters/telegram/author.js" + +describe("telegram author context", () => { + test("uses the known forwarded user as the forwarded author", () => { + const author = authorContextFromTelegramMessage({ + from: { id: 123, is_bot: false, first_name: "Forwarder" }, + forward_origin: { + type: "user", + sender_user: { + id: 999, + is_bot: false, + first_name: "Ada", + last_name: "Lovelace", + username: "ada_private", + }, + }, + }) + + expect(author).toEqual({ name: "Ada Lovelace", source: "forwarded" }) + }) + + test("uses hidden forwarded sender names without raw Telegram payloads", () => { + const author = authorContextFromTelegramMessage({ + from: { id: 123, is_bot: false, first_name: "Forwarder" }, + forward_origin: { + type: "hidden_user", + sender_user_name: "Private Sender", + }, + }) + + expect(author).toEqual({ name: "Private Sender", source: "forwarded" }) + }) + + test("uses forwarded chat titles as forwarded author context", () => { + const author = authorContextFromTelegramMessage({ + from: { id: 123, is_bot: false, first_name: "Forwarder" }, + forward_origin: { + type: "chat", + sender_chat: { id: -1001, type: "supergroup", title: "Private Group" }, + }, + }) + + expect(author).toEqual({ name: "Private Group", source: "forwarded" }) + }) + + test("uses forwarded channel titles as forwarded author context", () => { + const author = authorContextFromTelegramMessage({ + from: { id: 123, is_bot: false, first_name: "Forwarder" }, + forward_origin: { + type: "channel", + chat: { id: -1002, type: "channel", title: "Release Notes" }, + }, + }) + + expect(author).toEqual({ name: "Release Notes", source: "forwarded" }) + }) + + test("falls back to the authorized sender when forwarded author data is unavailable", () => { + const author = authorContextFromTelegramMessage({ + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + forward_origin: { + type: "hidden_user", + sender_user_name: " ", + }, + }) + + expect(author).toEqual({ name: "Authorized User", source: "sender" }) + }) + + test("defaults normal messages to the authorized sender", () => { + const author = authorContextFromTelegramMessage({ + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + }) + + expect(author).toEqual({ name: "Authorized User", source: "sender" }) + }) + + test("does not expose numeric Telegram IDs as author names", () => { + const author = authorContextFromTelegramMessage({ + from: { id: 123, is_bot: false, first_name: "Authorized" }, + forward_origin: { + type: "user", + sender_user: { id: 999, is_bot: false, first_name: " " }, + }, + }) + + expect(author).toEqual({ name: "Authorized", source: "sender" }) + expect(author.name).not.toContain("999") + expect(author.name).not.toContain("123") + }) +}) diff --git a/tests/adapters/telegramBot.test.js b/tests/adapters/telegramBot.test.js index 8cc6971..fd6dd11 100644 --- a/tests/adapters/telegramBot.test.js +++ b/tests/adapters/telegramBot.test.js @@ -510,20 +510,143 @@ describe("createTelegramBot", () => { const setMessageReaction = vi.fn(async () => true) await bot.messageHandlers.get("message:text")({ - message: { message_id: 10, text: "hello", chat: { id: 456 } }, + message: { + message_id: 10, + text: "hello", + chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + }, api: { sendChatAction, setMessageReaction }, reply, }) expect(setMessageReaction).toHaveBeenNthCalledWith(1, 456, 10, [{ type: "emoji", emoji: "👀" }]) expect(controller.sendPrompt).toHaveBeenCalledWith( - expect.stringContaining("hello"), + expect.objectContaining({ + text: expect.stringContaining("hello"), + author: { name: "Authorized User", source: "sender" }, + }), expect.objectContaining({ onProgress: expect.any(Function) }), ) expect(reply).toHaveBeenCalledWith("answer") expect(setMessageReaction).toHaveBeenNthCalledWith(2, 456, 10, []) }) + test("forwarded text prompts include forwarded author context", async () => { + const controller = { + sendPrompt: vi.fn(async () => "answer"), + } + const bot = createTelegramBot({ + token: "token", + allowedUserId: 123, + controller, + logger: { warn: vi.fn(), error: vi.fn() }, + botFactory: FakeBot, + }) + + await bot.messageHandlers.get("message:text")({ + message: { + message_id: 10, + text: "please summarize this", + chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Forwarder" }, + forward_origin: { + type: "user", + sender_user: { + id: 999, + is_bot: false, + first_name: "Ada", + last_name: "Lovelace", + }, + }, + }, + api: { + sendChatAction: vi.fn(async () => undefined), + setMessageReaction: vi.fn(async () => true), + }, + reply: vi.fn(async () => ({ message_id: 11, chat: { id: 456 }, text: "answer" })), + }) + + expect(controller.sendPrompt).toHaveBeenCalledWith( + expect.objectContaining({ + text: expect.stringContaining("please summarize this"), + author: { name: "Ada Lovelace", source: "forwarded" }, + }), + expect.objectContaining({ onProgress: expect.any(Function) }), + ) + }) + + test("forwarded text prompts without author data fall back to the sender", async () => { + const controller = { + sendPrompt: vi.fn(async () => "answer"), + } + const bot = createTelegramBot({ + token: "token", + allowedUserId: 123, + controller, + logger: { warn: vi.fn(), error: vi.fn() }, + botFactory: FakeBot, + }) + + await bot.messageHandlers.get("message:text")({ + message: { + message_id: 10, + text: "please summarize this", + chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + forward_origin: { type: "hidden_user", sender_user_name: " " }, + }, + api: { + sendChatAction: vi.fn(async () => undefined), + setMessageReaction: vi.fn(async () => true), + }, + reply: vi.fn(async () => ({ message_id: 11, chat: { id: 456 }, text: "answer" })), + }) + + expect(controller.sendPrompt).toHaveBeenCalledWith( + expect.objectContaining({ + text: expect.stringContaining("please summarize this"), + author: { name: "Authorized User", source: "sender" }, + }), + expect.objectContaining({ onProgress: expect.any(Function) }), + ) + }) + + test("normal text prompts include the sender as author context", async () => { + const controller = { + sendPrompt: vi.fn(async () => "answer"), + } + const bot = createTelegramBot({ + token: "token", + allowedUserId: 123, + controller, + logger: { warn: vi.fn(), error: vi.fn() }, + botFactory: FakeBot, + }) + + await bot.messageHandlers.get("message:text")({ + message: { + message_id: 10, + text: "hello", + chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + }, + api: { + sendChatAction: vi.fn(async () => undefined), + setMessageReaction: vi.fn(async () => true), + }, + reply: vi.fn(async () => ({ message_id: 11, chat: { id: 456 }, text: "answer" })), + }) + + expect(controller.sendPrompt).toHaveBeenCalledWith( + expect.objectContaining({ + text: expect.stringContaining("hello"), + author: { name: "Authorized User", source: "sender" }, + }), + expect.objectContaining({ onProgress: expect.any(Function) }), + ) + }) + test("text prompts in voice all mode send voice replies without text", async () => { const controller = { sendPrompt: vi.fn(async () => "answer"), @@ -545,7 +668,12 @@ describe("createTelegramBot", () => { const reply = vi.fn(async (text) => ({ message_id: 11, chat: { id: 456 }, text })) await bot.messageHandlers.get("message:text")({ - message: { message_id: 10, text: "hello", chat: { id: 456 } }, + message: { + message_id: 10, + text: "hello", + chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + }, api: { sendChatAction: vi.fn(async () => undefined), setMessageReaction: vi.fn(async () => true), @@ -600,7 +728,12 @@ describe("createTelegramBot", () => { })) await bot.messageHandlers.get("message:text")({ - message: { message_id: 10, text: "hello", chat: { id: 456 } }, + message: { + message_id: 10, + text: "hello", + chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + }, api: { sendChatAction: vi.fn(async () => undefined), setMessageReaction: vi.fn(async () => true), @@ -997,7 +1130,12 @@ describe("createTelegramBot", () => { }) await bot.messageHandlers.get("message:text")({ - message: { message_id: 10, text: "hello", chat: { id: 456 } }, + message: { + message_id: 10, + text: "hello", + chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + }, api: { sendChatAction: vi.fn(async () => undefined), setMessageReaction: vi.fn(async () => true), @@ -1006,15 +1144,18 @@ describe("createTelegramBot", () => { }) expect(controller.sendPrompt).toHaveBeenCalledWith( - [ - "hello", - "", - "Telegram gateway note:", - "The gateway shows tool and skill usage separately in an Activity message. Do not include tool or skill usage announcements in your final response.", - "If a short emoji reaction to the user's message is appropriate, include exactly one hidden marker anywhere in your response:", - "[telegram_reaction: 👍]", - "Use only one standard Telegram emoji, and omit the marker when no reaction is useful. The marker will be removed before the user sees the reply.", - ].join("\n"), + { + text: [ + "hello", + "", + "Telegram gateway note:", + "The gateway shows tool and skill usage separately in an Activity message. Do not include tool or skill usage announcements in your final response.", + "If a short emoji reaction to the user's message is appropriate, include exactly one hidden marker anywhere in your response:", + "[telegram_reaction: 👍]", + "Use only one standard Telegram emoji, and omit the marker when no reaction is useful. The marker will be removed before the user sees the reply.", + ].join("\n"), + author: { name: "Authorized User", source: "sender" }, + }, expect.objectContaining({ onProgress: expect.any(Function) }), ) }) @@ -1076,6 +1217,11 @@ describe("createTelegramBot", () => { message_id: 10, chat: { id: 456 }, caption: "What changed?", + from: { id: 123, is_bot: false, first_name: "Forwarder" }, + forward_origin: { + type: "hidden_user", + sender_user_name: "Screenshot Author", + }, photo: [small, large], }, api: { sendChatAction: vi.fn(async () => undefined) }, @@ -1093,6 +1239,7 @@ describe("createTelegramBot", () => { text: expect.stringContaining( "The gateway shows tool and skill usage separately in an Activity message.", ), + author: { name: "Screenshot Author", source: "forwarded" }, attachments: [attachment], }, expect.objectContaining({ onProgress: expect.any(Function) }), @@ -1181,7 +1328,12 @@ describe("createTelegramBot", () => { const reply = vi.fn(async (text) => ({ message_id: 11, chat: { id: 456 }, text })) await bot.messageHandlers.get("message:voice")({ - message: { message_id: 10, chat: { id: 456 }, voice: { file_id: "voice-1" } }, + message: { + message_id: 10, + chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, + voice: { file_id: "voice-1" }, + }, api: { sendChatAction: vi.fn(async () => undefined) }, reply, }) @@ -1194,7 +1346,10 @@ describe("createTelegramBot", () => { }) expect(voiceService.transcribe).toHaveBeenCalledWith("/tmp/voice.ogg") expect(controller.sendPrompt).toHaveBeenCalledWith( - expect.stringContaining("transcribed prompt"), + expect.objectContaining({ + text: expect.stringContaining("transcribed prompt"), + author: { name: "Authorized User", source: "sender" }, + }), expect.objectContaining({ onProgress: expect.any(Function) }), ) expect(reply).not.toHaveBeenCalled() @@ -1252,6 +1407,7 @@ describe("createTelegramBot", () => { text: expect.stringContaining( "The gateway shows tool and skill usage separately in an Activity message.", ), + author: { name: "Authorized User", source: "sender" }, attachments: [ { mime: "image/jpeg", url: "file:///tmp/photo-10.jpg", filePath: "/tmp/photo-10.jpg" }, { mime: "image/jpeg", url: "file:///tmp/photo-11.jpg", filePath: "/tmp/photo-11.jpg" }, @@ -1309,7 +1465,7 @@ describe("createTelegramBot", () => { test("user reaction to a known bot message sends a feedback prompt and reply", async () => { const controller = { sendPrompt: vi.fn(async (prompt) => { - if (prompt.startsWith("hello")) { + if (String(prompt?.text ?? prompt).startsWith("hello")) { return "answer" } return "feedback response" @@ -1431,6 +1587,7 @@ function photoContext({ messageId, fileId, caption = "", reply }) { message: { message_id: messageId, chat: { id: 456 }, + from: { id: 123, is_bot: false, first_name: "Authorized", last_name: "User" }, media_group_id: "album-1", caption, photo: [{ file_id: fileId, width: 1280, height: 720, file_size: 3000 }], diff --git a/tests/core/opencodeClient.test.js b/tests/core/opencodeClient.test.js index 68a9fab..feabeb2 100644 --- a/tests/core/opencodeClient.test.js +++ b/tests/core/opencodeClient.test.js @@ -68,6 +68,78 @@ describe("createOpenCodeClient", () => { }) }) + test("adds messenger-neutral author context to object prompts", async () => { + const sdkClient = { + session: { + prompt: vi.fn(async () => ({ parts: [{ type: "text", text: "answer" }] })), + }, + } + const client = createOpenCodeClient({ sdkClient }) + + await expect( + client.sendPrompt("ses_1", { + text: "please summarize this", + author: { name: "Ada Lovelace", source: "forwarded" }, + }), + ).resolves.toBe("answer") + + expect(sdkClient.session.prompt).toHaveBeenCalledWith({ + path: { id: "ses_1" }, + body: { + parts: [ + { + type: "text", + text: [ + "Message author context:", + "- Author: Ada Lovelace", + "- Attribution: forwarded original author", + "", + "Message:", + "please summarize this", + ].join("\n"), + }, + ], + }, + }) + }) + + test("keeps attachments before author-context text prompts", async () => { + const sdkClient = { + session: { + prompt: vi.fn(async () => ({ parts: [{ type: "text", text: "answer" }] })), + }, + } + const client = createOpenCodeClient({ sdkClient }) + + await expect( + client.sendPrompt("ses_1", { + text: "What changed?", + author: { name: "Grace Hopper", source: "sender" }, + attachments: [{ mime: "image/jpeg", url: "file:///tmp/photo.jpg" }], + }), + ).resolves.toBe("answer") + + expect(sdkClient.session.prompt).toHaveBeenCalledWith({ + path: { id: "ses_1" }, + body: { + parts: [ + { type: "file", mime: "image/jpeg", url: "file:///tmp/photo.jpg" }, + { + type: "text", + text: [ + "Message author context:", + "- Author: Grace Hopper", + "- Attribution: message sender", + "", + "Message:", + "What changed?", + ].join("\n"), + }, + ], + }, + }) + }) + test("streams normalized skill progress while a prompt is running", async () => { const stream = createEventStream([ { From 9e5a7e7ec97b10a90c6052ed69585e2c9470bdec Mon Sep 17 00:00:00 2001 From: crankshift Date: Thu, 28 May 2026 07:51:30 +0200 Subject: [PATCH 2/3] chore: remove Dependabot config --- .github/dependabot.yml | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 5ed085a..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: 2 -updates: - - package-ecosystem: github-actions - directory: "/" - schedule: - interval: weekly - - - package-ecosystem: npm - directory: "/" - schedule: - interval: weekly From 4696000f8836e1e71cb85941ca3955ab2ff74452 Mon Sep 17 00:00:00 2001 From: crankshift Date: Thu, 28 May 2026 07:54:51 +0200 Subject: [PATCH 3/3] test: stop requiring Dependabot workflow config --- CHANGELOG.md | 4 ++++ DEVELOPMENT.md | 2 +- tests/smoke/workflowSmoke.js | 4 ---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d5bf1..7cc3deb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ This project follows Semantic Versioning. ## Unreleased +### Removed + +- Removed Dependabot configuration to stop automated dependency update pull requests. + ## [0.5.6] - 2026-05-27 ### Added diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 9b9a601..d62ef86 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -121,7 +121,7 @@ Default tests mock external systems. They do not require live Telegram, live Ope GitHub Actions runs the `Check` workflow on pull requests and pushes to `main`. It installs dependencies with `pnpm install --frozen-lockfile` on Node.js 24 and runs `pnpm run check`. -Maintainers should configure branch protection for `main` to require the `Check` workflow before merging. Dependabot checks GitHub Actions and npm dependencies weekly. +Maintainers should configure branch protection for `main` to require the `Check` workflow before merging. ## Release diff --git a/tests/smoke/workflowSmoke.js b/tests/smoke/workflowSmoke.js index 445d62a..cc7e2d2 100644 --- a/tests/smoke/workflowSmoke.js +++ b/tests/smoke/workflowSmoke.js @@ -3,7 +3,6 @@ import { readFile } from "node:fs/promises" const checkWorkflow = await readRequiredFile(".github/workflows/check.yml") const publishWorkflow = await readRequiredFile(".github/workflows/publish.yml") const releaseTagWorkflow = await readRequiredFile(".github/workflows/release-tag.yml") -const dependabotConfig = await readRequiredFile(".github/dependabot.yml") assertMatches(checkWorkflow, /^name:\s*Check$/m, "check workflow is named Check") assertIncludes(checkWorkflow, "pull_request:", "check workflow runs on pull requests") @@ -123,9 +122,6 @@ assertDoesNotMatch( "release tag workflow does not publish npm packages directly", ) -assertIncludes(dependabotConfig, "package-ecosystem: github-actions", "Dependabot updates actions") -assertIncludes(dependabotConfig, "package-ecosystem: npm", "Dependabot updates npm dependencies") - async function readRequiredFile(path) { try { return await readFile(path, "utf8")