From 3bab2edc414e1ced9a09fb25acdbf1a28f9f2a53 Mon Sep 17 00:00:00 2001 From: Vlad Mocanu Date: Wed, 25 Feb 2026 19:26:18 +0000 Subject: [PATCH] fix: build image URL regexes at call time, not module load time --- src/github/utils/image-downloader.ts | 34 +++++++++++++++++----------- test/image-downloader.test.ts | 13 +++++++++++ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/github/utils/image-downloader.ts b/src/github/utils/image-downloader.ts index 1e819ff..0b2c0c7 100644 --- a/src/github/utils/image-downloader.ts +++ b/src/github/utils/image-downloader.ts @@ -1,18 +1,24 @@ import fs from "fs/promises"; import path from "path"; import type { Octokits } from "../api/client"; -import { GITHUB_SERVER_URL } from "../api/config"; -const escapedUrl = GITHUB_SERVER_URL.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -const IMAGE_REGEX = new RegExp( - `!\\[[^\\]]*\\]\\((${escapedUrl}\\/user-attachments\\/assets\\/[^)]+)\\)`, - "g", -); - -const HTML_IMG_REGEX = new RegExp( - `]+src=["']([^"']*${escapedUrl}\\/user-attachments\\/assets\\/[^"']+)["'][^>]*>`, - "gi", -); +// Build regexes at call time, reading GITHUB_SERVER_URL from process.env +// directly. The config.ts export freezes the value at module load time, +// which can be wrong when the Forgejo CI runner injects its own +// GITHUB_SERVER_URL that the shell export cannot override. +function buildImageRegexes(): { imageRegex: RegExp; htmlImgRegex: RegExp } { + const serverUrl = process.env.GITHUB_SERVER_URL || "https://github.com"; + const escapedUrl = serverUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const imageRegex = new RegExp( + `!\\[[^\\]]*\\]\\((${escapedUrl}\\/user-attachments\\/assets\\/[^)]+)\\)`, + "g", + ); + const htmlImgRegex = new RegExp( + `]+src=["']([^"']*${escapedUrl}\\/user-attachments\\/assets\\/[^"']+)["'][^>]*>`, + "gi", + ); + return { imageRegex, htmlImgRegex }; +} type IssueComment = { type: "issue_comment"; @@ -63,6 +69,8 @@ export async function downloadCommentImages( await fs.mkdir(downloadsDir, { recursive: true }); + const { imageRegex, htmlImgRegex } = buildImageRegexes(); + const commentsWithImages: Array<{ comment: CommentWithImages; urls: string[]; @@ -70,11 +78,11 @@ export async function downloadCommentImages( for (const comment of comments) { // Extract URLs from Markdown format - const markdownMatches = [...comment.body.matchAll(IMAGE_REGEX)]; + const markdownMatches = [...comment.body.matchAll(imageRegex)]; const markdownUrls = markdownMatches.map((match) => match[1] as string); // Extract URLs from HTML format - const htmlMatches = [...comment.body.matchAll(HTML_IMG_REGEX)]; + const htmlMatches = [...comment.body.matchAll(htmlImgRegex)]; const htmlUrls = htmlMatches.map((match) => match[1] as string); // Combine and deduplicate URLs diff --git a/test/image-downloader.test.ts b/test/image-downloader.test.ts index e00b6d0..124413a 100644 --- a/test/image-downloader.test.ts +++ b/test/image-downloader.test.ts @@ -20,8 +20,14 @@ describe("downloadCommentImages", () => { let fsMkdirSpy: any; let fsWriteFileSpy: any; let fetchSpy: any; + let savedServerUrl: string | undefined; beforeEach(() => { + // Pin GITHUB_SERVER_URL so tests pass regardless of runner environment. + // The image-downloader reads process.env directly at call time. + savedServerUrl = process.env.GITHUB_SERVER_URL; + process.env.GITHUB_SERVER_URL = "https://github.com"; + // Spy on console methods consoleLogSpy = spyOn(console, "log").mockImplementation(() => {}); consoleWarnSpy = spyOn(console, "warn").mockImplementation(() => {}); @@ -36,6 +42,13 @@ describe("downloadCommentImages", () => { }); afterEach(() => { + // Restore GITHUB_SERVER_URL + if (savedServerUrl !== undefined) { + process.env.GITHUB_SERVER_URL = savedServerUrl; + } else { + delete process.env.GITHUB_SERVER_URL; + } + consoleLogSpy.mockRestore(); consoleWarnSpy.mockRestore(); consoleErrorSpy.mockRestore();