mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
Merge pull request #2513 from divaltor/docker-image-tag
feat(tags): Add support for tags from Github Packages
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { extractCommitMessage } from "@/pages/api/deploy/[refreshToken]";
|
||||
import {
|
||||
extractCommitMessage,
|
||||
extractImageName,
|
||||
extractImageTag,
|
||||
extractImageTagFromRequest,
|
||||
} from "@/pages/api/deploy/[refreshToken]";
|
||||
|
||||
describe("GitHub Webhook Skip CI", () => {
|
||||
const mockGithubHeaders = {
|
||||
@@ -96,3 +101,308 @@ describe("GitHub Webhook Skip CI", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GitHub Packages Docker Image Tag Extraction", () => {
|
||||
it("should extract tag from container_metadata", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
container_metadata: {
|
||||
tag: {
|
||||
name: "v1.0.0",
|
||||
digest: "sha256:abc123...",
|
||||
},
|
||||
},
|
||||
package_url: "ghcr.io/owner/repo:v1.0.0",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBe("v1.0.0");
|
||||
});
|
||||
|
||||
it("should extract tag from package_url when container_metadata tag matches version", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
container_metadata: {
|
||||
tag: {
|
||||
name: "sha256:abc123...",
|
||||
digest: "sha256:abc123...",
|
||||
},
|
||||
},
|
||||
package_url: "ghcr.io/owner/repo:latest",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBe("latest");
|
||||
});
|
||||
|
||||
it("should extract tag from package_url when container_metadata is missing", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
package_url: "ghcr.io/owner/repo:1.2.3",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBe("1.2.3");
|
||||
});
|
||||
|
||||
it("should handle different tag formats in package_url", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const testCases = [
|
||||
{ url: "ghcr.io/owner/repo:latest", expected: "latest" },
|
||||
{ url: "ghcr.io/owner/repo:v1.0.0", expected: "v1.0.0" },
|
||||
{ url: "ghcr.io/owner/repo:1.2.3", expected: "1.2.3" },
|
||||
{ url: "ghcr.io/owner/repo:dev", expected: "dev" },
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
package_url: testCase.url,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBe(testCase.expected);
|
||||
}
|
||||
});
|
||||
|
||||
it("should return null for non-registry_package events", () => {
|
||||
const headers = { "x-github-event": "push" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
package_url: "ghcr.io/owner/repo:latest",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null when package_version is missing", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null when package_url has no tag", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
package_url: "ghcr.io/owner/repo",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null when package_url ends with colon (no tag)", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
package_url: "ghcr.io/owner/repo:",
|
||||
container_metadata: {
|
||||
tag: {
|
||||
name: "",
|
||||
digest: "sha256:abc123...",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null when tag name is empty string", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
container_metadata: {
|
||||
tag: {
|
||||
name: "",
|
||||
digest: "sha256:abc123...",
|
||||
},
|
||||
},
|
||||
package_url: "ghcr.io/owner/repo:",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBeNull();
|
||||
});
|
||||
|
||||
it("should ignore tag if it matches the version (digest)", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
container_metadata: {
|
||||
tag: {
|
||||
name: "sha256:abc123...",
|
||||
digest: "sha256:abc123...",
|
||||
},
|
||||
},
|
||||
package_url: "ghcr.io/owner/repo:latest",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tag = extractImageTagFromRequest(headers, body);
|
||||
expect(tag).toBe("latest");
|
||||
});
|
||||
|
||||
it("should handle registry_package commit message with package_url", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
package_url: "ghcr.io/owner/repo:latest",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const message = extractCommitMessage(headers, body);
|
||||
expect(message).toBe("Docker GHCR image pushed: ghcr.io/owner/repo:latest");
|
||||
});
|
||||
|
||||
it("should handle registry_package commit message when package_url is missing", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {
|
||||
package_version: {
|
||||
version: "sha256:abc123...",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const message = extractCommitMessage(headers, body);
|
||||
expect(message).toBe("Docker GHCR image pushed");
|
||||
});
|
||||
|
||||
it("should handle registry_package commit message when package_version is missing", () => {
|
||||
const headers = { "x-github-event": "registry_package" };
|
||||
const body = {
|
||||
registry_package: {},
|
||||
};
|
||||
|
||||
const message = extractCommitMessage(headers, body);
|
||||
expect(message).toBe("NEW COMMIT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Docker Image Name and Tag Extraction", () => {
|
||||
describe("extractImageName", () => {
|
||||
it("should return image name without tag", () => {
|
||||
expect(extractImageName("my-image:latest")).toBe("my-image");
|
||||
expect(extractImageName("my-image:1.0.0")).toBe("my-image");
|
||||
expect(extractImageName("ghcr.io/owner/repo:latest")).toBe(
|
||||
"ghcr.io/owner/repo",
|
||||
);
|
||||
});
|
||||
|
||||
it("should return full image name when no tag is present", () => {
|
||||
expect(extractImageName("my-image")).toBe("my-image");
|
||||
expect(extractImageName("ghcr.io/owner/repo")).toBe("ghcr.io/owner/repo");
|
||||
});
|
||||
|
||||
it("should handle images with port numbers correctly", () => {
|
||||
expect(extractImageName("registry:5000/image:tag")).toBe(
|
||||
"registry:5000/image",
|
||||
);
|
||||
expect(extractImageName("localhost:5000/my-app:latest")).toBe(
|
||||
"localhost:5000/my-app",
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle complex image paths", () => {
|
||||
expect(
|
||||
extractImageName("myregistryhost:5000/fedora/httpd:version1.0"),
|
||||
).toBe("myregistryhost:5000/fedora/httpd");
|
||||
expect(extractImageName("registry.example.com:8080/ns/app:v1.2.3")).toBe(
|
||||
"registry.example.com:8080/ns/app",
|
||||
);
|
||||
});
|
||||
|
||||
it("should return null for invalid inputs", () => {
|
||||
expect(extractImageName(null)).toBeNull();
|
||||
expect(extractImageName("")).toBeNull();
|
||||
});
|
||||
|
||||
it("should handle edge cases with multiple colons", () => {
|
||||
expect(extractImageName("image:tag:extra")).toBe("image:tag");
|
||||
expect(extractImageName("registry:5000:invalid")).toBe("registry:5000");
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractImageTag", () => {
|
||||
it("should extract tag from image with tag", () => {
|
||||
expect(extractImageTag("my-image:latest")).toBe("latest");
|
||||
expect(extractImageTag("my-image:1.0.0")).toBe("1.0.0");
|
||||
expect(extractImageTag("ghcr.io/owner/repo:v1.2.3")).toBe("v1.2.3");
|
||||
});
|
||||
|
||||
it("should return 'latest' when no tag is present", () => {
|
||||
expect(extractImageTag("my-image")).toBe("latest");
|
||||
expect(extractImageTag("ghcr.io/owner/repo")).toBe("latest");
|
||||
});
|
||||
|
||||
it("should handle complex image paths with tags", () => {
|
||||
expect(
|
||||
extractImageTag("myregistryhost:5000/fedora/httpd:version1.0"),
|
||||
).toBe("version1.0");
|
||||
expect(extractImageTag("registry.example.com:8080/ns/app:v1.2.3")).toBe(
|
||||
"v1.2.3",
|
||||
);
|
||||
});
|
||||
|
||||
it("should return null for invalid inputs", () => {
|
||||
expect(extractImageTag(null)).toBeNull();
|
||||
expect(extractImageTag("")).toBeNull();
|
||||
});
|
||||
|
||||
it("should handle edge cases with multiple colons", () => {
|
||||
expect(extractImageTag("image:tag:extra")).toBe("extra");
|
||||
expect(extractImageTag("registry:5000/image:tag")).toBe("tag");
|
||||
});
|
||||
|
||||
it("should handle numeric tags", () => {
|
||||
expect(extractImageTag("my-image:123")).toBe("123");
|
||||
expect(extractImageTag("my-image:1")).toBe("1");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { findEnvironmentById } from "@dokploy/server/index";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -27,12 +26,10 @@ import {
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { api } from "@/utils/api";
|
||||
import { api, type RouterOutputs } from "@/utils/api";
|
||||
|
||||
type Environment = Omit<
|
||||
Awaited<ReturnType<typeof findEnvironmentById>>,
|
||||
"project"
|
||||
>;
|
||||
type Project = RouterOutputs["project"]["all"][number];
|
||||
type Environment = Project["environments"][number];
|
||||
|
||||
export type Services = {
|
||||
appName: string;
|
||||
@@ -53,17 +50,16 @@ export type Services = {
|
||||
};
|
||||
|
||||
export const extractServices = (data: Environment | undefined) => {
|
||||
const applications: Services[] =
|
||||
data?.applications.map((item) => ({
|
||||
appName: item.appName,
|
||||
name: item.name,
|
||||
type: "application",
|
||||
id: item.applicationId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.applicationStatus,
|
||||
description: item.description,
|
||||
serverId: item.serverId,
|
||||
})) || [];
|
||||
const applications: Services[] = (data?.applications?.map((item) => ({
|
||||
appName: item.appName,
|
||||
name: item.name,
|
||||
type: "application",
|
||||
id: item.applicationId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.applicationStatus,
|
||||
description: item.description,
|
||||
serverId: item.serverId,
|
||||
})) ?? []) as Services[];
|
||||
|
||||
const mariadb: Services[] =
|
||||
data?.mariadb.map((item) => ({
|
||||
@@ -125,17 +121,16 @@ export const extractServices = (data: Environment | undefined) => {
|
||||
serverId: item.serverId,
|
||||
})) || [];
|
||||
|
||||
const compose: Services[] =
|
||||
data?.compose.map((item) => ({
|
||||
appName: item.appName,
|
||||
name: item.name,
|
||||
type: "compose",
|
||||
id: item.composeId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.composeStatus,
|
||||
description: item.description,
|
||||
serverId: item.serverId,
|
||||
})) || [];
|
||||
const compose: Services[] = (data?.compose?.map((item) => ({
|
||||
appName: item.appName,
|
||||
name: item.name,
|
||||
type: "compose",
|
||||
id: item.composeId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.composeStatus,
|
||||
description: item.description,
|
||||
serverId: item.serverId,
|
||||
})) ?? []) as Services[];
|
||||
|
||||
applications.push(
|
||||
...mysql,
|
||||
|
||||
@@ -12,6 +12,17 @@ import type { DeploymentJob } from "@/server/queues/queue-types";
|
||||
import { myQueue } from "@/server/queues/queueSetup";
|
||||
import { deploy } from "@/server/utils/deploy";
|
||||
|
||||
/**
|
||||
* Helper function to get package_version from registry_package events
|
||||
*/
|
||||
const getPackageVersion = (headers: any, body: any) => {
|
||||
const event = headers["x-github-event"];
|
||||
if (event === "registry_package") {
|
||||
return body.registry_package?.package_version;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
@@ -46,21 +57,60 @@ export default async function handler(
|
||||
}
|
||||
|
||||
const deploymentTitle = extractCommitMessage(req.headers, req.body);
|
||||
const deploymentHash = extractHash(req.headers, req.body);
|
||||
|
||||
const deploymentHash = extractHash(req.headers, req.body);
|
||||
const sourceType = application.sourceType;
|
||||
|
||||
if (sourceType === "docker") {
|
||||
const applicationImageName = extractImageName(application.dockerImage);
|
||||
const applicationDockerTag = extractImageTag(application.dockerImage);
|
||||
|
||||
const webhookImageName = extractImageNameFromRequest(
|
||||
req.headers,
|
||||
req.body,
|
||||
);
|
||||
const webhookDockerTag = extractImageTagFromRequest(
|
||||
req.headers,
|
||||
req.body,
|
||||
);
|
||||
if (
|
||||
applicationDockerTag &&
|
||||
webhookDockerTag &&
|
||||
webhookDockerTag !== applicationDockerTag
|
||||
) {
|
||||
|
||||
if (!applicationImageName) {
|
||||
res.status(301).json({
|
||||
message: "Application Docker Image Name Not Found",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!webhookImageName) {
|
||||
res.status(301).json({
|
||||
message: "Webhook Docker Image Name Not Found",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate image name matches
|
||||
if (webhookImageName !== applicationImageName) {
|
||||
res.status(301).json({
|
||||
message: `Application Image Name (${applicationImageName}) doesn't match request event payload Image Name (${webhookImageName}).`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!applicationDockerTag) {
|
||||
res.status(301).json({
|
||||
message: "Application Docker Tag Not Found",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!webhookDockerTag) {
|
||||
res.status(301).json({
|
||||
message: "Webhook Docker Tag Not Found",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (webhookDockerTag !== applicationDockerTag) {
|
||||
res.status(301).json({
|
||||
message: `Application Image Tag (${applicationDockerTag}) doesn't match request event payload Image Tag (${webhookDockerTag}).`,
|
||||
});
|
||||
@@ -191,7 +241,7 @@ export default async function handler(
|
||||
const jobData: DeploymentJob = {
|
||||
applicationId: application.applicationId as string,
|
||||
titleLog: deploymentTitle,
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
...(deploymentHash && { descriptionLog: `Hash: ${deploymentHash}` }),
|
||||
type: "deploy",
|
||||
applicationType: "application",
|
||||
server: !!application.serverId,
|
||||
@@ -222,6 +272,39 @@ export default async function handler(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image name without the tag
|
||||
* Example: "my-image" => "my-image"
|
||||
* Example: "my-image:latest" => "my-image"
|
||||
* Example: "my-image:1.0.0" => "my-image"
|
||||
* Example: "myregistryhost:5000/fedora/httpd:version1.0" => "myregistryhost:5000/fedora/httpd"
|
||||
* @link https://docs.docker.com/reference/cli/docker/image/tag/
|
||||
*/
|
||||
export function extractImageName(dockerImage: string | null): string | null {
|
||||
if (!dockerImage || typeof dockerImage !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Handle case where there's no tag (no colon or colon is part of port number)
|
||||
const lastColonIndex = dockerImage.lastIndexOf(":");
|
||||
if (lastColonIndex === -1) {
|
||||
return dockerImage;
|
||||
}
|
||||
|
||||
// Check if the part after the last colon looks like a tag (not a port number)
|
||||
// Port numbers are typically 1-5 digits, tags are usually longer or contain letters
|
||||
const afterColon = dockerImage.substring(lastColonIndex + 1);
|
||||
const isPortNumber = /^\d{1,5}$/.test(afterColon);
|
||||
|
||||
// If it's a port number (like registry:5000/image), don't split
|
||||
if (isPortNumber) {
|
||||
return dockerImage;
|
||||
}
|
||||
|
||||
// Otherwise, split at the last colon to get image name
|
||||
return dockerImage.substring(0, lastColonIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last part of the image name, which is the tag
|
||||
* Example: "my-image" => null
|
||||
@@ -230,7 +313,7 @@ export default async function handler(
|
||||
* Example: "myregistryhost:5000/fedora/httpd:version1.0" => "version1.0"
|
||||
* @link https://docs.docker.com/reference/cli/docker/image/tag/
|
||||
*/
|
||||
function extractImageTag(dockerImage: string | null) {
|
||||
export function extractImageTag(dockerImage: string | null) {
|
||||
if (!dockerImage || typeof dockerImage !== "string") {
|
||||
return null;
|
||||
}
|
||||
@@ -240,12 +323,78 @@ function extractImageTag(dockerImage: string | null) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the image name (without tag) from webhook request
|
||||
* @link https://docs.docker.com/docker-hub/webhooks/#example-webhook-payload
|
||||
* @link https://docs.github.com/en/webhooks/webhook-events-and-payloads#registry_package
|
||||
*/
|
||||
export const extractImageNameFromRequest = (
|
||||
headers: any,
|
||||
body: any,
|
||||
): string | null => {
|
||||
// GitHub Packages: registry_package events (container registry)
|
||||
const packageVersion = getPackageVersion(headers, body);
|
||||
if (packageVersion?.package_url) {
|
||||
const packageUrl = packageVersion.package_url;
|
||||
// Remove tag if present (everything after the last colon)
|
||||
if (packageUrl.includes(":")) {
|
||||
const lastColonIndex = packageUrl.lastIndexOf(":");
|
||||
// Check if it's a port number (like registry:5000/image)
|
||||
const afterColon = packageUrl.substring(lastColonIndex + 1);
|
||||
const isPortNumber = /^\d{1,5}$/.test(afterColon);
|
||||
if (isPortNumber) {
|
||||
return packageUrl;
|
||||
}
|
||||
return packageUrl.substring(0, lastColonIndex);
|
||||
}
|
||||
return packageUrl;
|
||||
}
|
||||
|
||||
// Docker Hub
|
||||
if (headers["user-agent"]?.includes("Go-http-client")) {
|
||||
if (body.repository) {
|
||||
const repoName = body.repository.repo_name;
|
||||
return `${repoName}`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @link https://docs.docker.com/docker-hub/webhooks/#example-webhook-payload
|
||||
* @link https://docs.github.com/en/webhooks/webhook-events-and-payloads#registry_package
|
||||
*/
|
||||
export const extractImageTagFromRequest = (
|
||||
headers: any,
|
||||
body: any,
|
||||
): string | null => {
|
||||
// GitHub Packages: registry_package events (container registry)
|
||||
const packageVersion = getPackageVersion(headers, body);
|
||||
if (packageVersion) {
|
||||
// Try to get tag from container_metadata first (most reliable)
|
||||
// Only use it if it's not empty and not the same as the version (digest)
|
||||
const tagName = packageVersion.container_metadata?.tag?.name?.trim() || "";
|
||||
if (
|
||||
tagName &&
|
||||
tagName !== packageVersion.version &&
|
||||
!tagName.startsWith("sha256:")
|
||||
) {
|
||||
return tagName;
|
||||
}
|
||||
// Fallback: extract tag from package_url (e.g., "ghcr.io/owner/repo:tag")
|
||||
if (packageVersion.package_url) {
|
||||
const packageUrl = packageVersion.package_url;
|
||||
// Handle case where package_url ends with colon (no tag)
|
||||
if (packageUrl.endsWith(":")) {
|
||||
return null;
|
||||
}
|
||||
const tagMatch = packageUrl.match(/:([^:]+)$/);
|
||||
if (tagMatch?.[1]?.trim()) {
|
||||
return tagMatch[1].trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Docker Hub
|
||||
if (headers["user-agent"]?.includes("Go-http-client")) {
|
||||
if (body.push_data && body.repository) {
|
||||
return body.push_data.tag;
|
||||
@@ -255,6 +404,18 @@ export const extractImageTagFromRequest = (
|
||||
};
|
||||
|
||||
export const extractCommitMessage = (headers: any, body: any) => {
|
||||
// GitHub Packages: registry_package events (container tags)
|
||||
const githubEvent = headers["x-github-event"];
|
||||
if (githubEvent === "registry_package") {
|
||||
const packageVersion = getPackageVersion(headers, body);
|
||||
if (packageVersion) {
|
||||
if (packageVersion.package_url) {
|
||||
return `Docker GHCR image pushed: ${packageVersion.package_url}`;
|
||||
}
|
||||
return "Docker GHCR image pushed";
|
||||
}
|
||||
// If package_version is missing, fall through to default behavior
|
||||
}
|
||||
// GitHub
|
||||
if (headers["x-github-event"]) {
|
||||
return body.head_commit ? body.head_commit.message : "NEW COMMIT";
|
||||
@@ -283,7 +444,7 @@ export const extractCommitMessage = (headers: any, body: any) => {
|
||||
|
||||
if (headers["user-agent"]?.includes("Go-http-client")) {
|
||||
if (body.push_data && body.repository) {
|
||||
return `Docker image pushed: ${body.repository.repo_name}:${body.push_data.tag} by ${body.push_data.pusher}`;
|
||||
return `DockerHub image pushed: ${body.repository.repo_name}:${body.push_data.tag} by ${body.push_data.pusher}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user