fix: build image URL regexes at call time, not module load time

This commit is contained in:
Vlad Mocanu committed 2026-02-25 19:26:18 +00:00
1 parent 2f7d0a7fb2
commit 3bab2edc41
2 files changed
+34 -13

No files matched your search

+21 -13
View File
@@ -1,18 +1,24 @@
import fs from "fs/promises"; import fs from "fs/promises";
import path from "path"; import path from "path";
import type { Octokits } from "../api/client"; import type { Octokits } from "../api/client";
import { GITHUB_SERVER_URL } from "../api/config";
const escapedUrl = GITHUB_SERVER_URL.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Build regexes at call time, reading GITHUB_SERVER_URL from process.env
const IMAGE_REGEX = new RegExp( // directly. The config.ts export freezes the value at module load time,
`!\\[[^\\]]*\\]\\((${escapedUrl}\\/user-attachments\\/assets\\/[^)]+)\\)`, // which can be wrong when the Forgejo CI runner injects its own
"g", // 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 HTML_IMG_REGEX = new RegExp( const escapedUrl = serverUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
`<img[^>]+src=["']([^"']*${escapedUrl}\\/user-attachments\\/assets\\/[^"']+)["'][^>]*>`, const imageRegex = new RegExp(
"gi", `!\\[[^\\]]*\\]\\((${escapedUrl}\\/user-attachments\\/assets\\/[^)]+)\\)`,
); "g",
);
const htmlImgRegex = new RegExp(
`<img[^>]+src=["']([^"']*${escapedUrl}\\/user-attachments\\/assets\\/[^"']+)["'][^>]*>`,
"gi",
);
return { imageRegex, htmlImgRegex };
}
type IssueComment = { type IssueComment = {
type: "issue_comment"; type: "issue_comment";
@@ -63,6 +69,8 @@ export async function downloadCommentImages(
await fs.mkdir(downloadsDir, { recursive: true }); await fs.mkdir(downloadsDir, { recursive: true });
const { imageRegex, htmlImgRegex } = buildImageRegexes();
const commentsWithImages: Array<{ const commentsWithImages: Array<{
comment: CommentWithImages; comment: CommentWithImages;
urls: string[]; urls: string[];
@@ -70,11 +78,11 @@ export async function downloadCommentImages(
for (const comment of comments) { for (const comment of comments) {
// Extract URLs from Markdown format // 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); const markdownUrls = markdownMatches.map((match) => match[1] as string);
// Extract URLs from HTML format // 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); const htmlUrls = htmlMatches.map((match) => match[1] as string);
// Combine and deduplicate URLs // Combine and deduplicate URLs
+13
View File
@@ -20,8 +20,14 @@ describe("downloadCommentImages", () => {
let fsMkdirSpy: any; let fsMkdirSpy: any;
let fsWriteFileSpy: any; let fsWriteFileSpy: any;
let fetchSpy: any; let fetchSpy: any;
let savedServerUrl: string | undefined;
beforeEach(() => { 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 // Spy on console methods
consoleLogSpy = spyOn(console, "log").mockImplementation(() => {}); consoleLogSpy = spyOn(console, "log").mockImplementation(() => {});
consoleWarnSpy = spyOn(console, "warn").mockImplementation(() => {}); consoleWarnSpy = spyOn(console, "warn").mockImplementation(() => {});
@@ -36,6 +42,13 @@ describe("downloadCommentImages", () => {
}); });
afterEach(() => { afterEach(() => {
// Restore GITHUB_SERVER_URL
if (savedServerUrl !== undefined) {
process.env.GITHUB_SERVER_URL = savedServerUrl;
} else {
delete process.env.GITHUB_SERVER_URL;
}
consoleLogSpy.mockRestore(); consoleLogSpy.mockRestore();
consoleWarnSpy.mockRestore(); consoleWarnSpy.mockRestore();
consoleErrorSpy.mockRestore(); consoleErrorSpy.mockRestore();