Upload files to "src"
This commit is contained in:
parent
3a46ac8667
commit
c03d135ae3
347
src/index.ts
Normal file
347
src/index.ts
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
import { error, getInput, getMultilineInput, info } from "@actions/core";
|
||||||
|
|
||||||
|
enum Input {
|
||||||
|
CoolifyApiKey = "coolify-api-key",
|
||||||
|
CoolifyUrl = "coolify-url",
|
||||||
|
ProjectId = "project-id",
|
||||||
|
ServerId = "server-id",
|
||||||
|
Name = "name",
|
||||||
|
Domains = "domains",
|
||||||
|
EnvironmentId = "environment-id",
|
||||||
|
EnvironmentName = "environment-name",
|
||||||
|
DockerCompose = "docker-compose",
|
||||||
|
Env = "env",
|
||||||
|
ImageName = "image-name",
|
||||||
|
ImageTag = "image-tag",
|
||||||
|
Ports = "ports",
|
||||||
|
Override = "override",
|
||||||
|
}
|
||||||
|
|
||||||
|
type CoolifyFetch = (path: string, init?: RequestInit) => Promise<Response>;
|
||||||
|
|
||||||
|
type ParamResult<P> = P extends { list: true } ? string[] : string;
|
||||||
|
|
||||||
|
type OptionalIfNotRequiredOrFallback<T> = {
|
||||||
|
[K in keyof T as T[K] extends { required: true }
|
||||||
|
? K
|
||||||
|
: T[K] extends { fallback: infer F }
|
||||||
|
? F extends undefined
|
||||||
|
? never
|
||||||
|
: K
|
||||||
|
: never]-?: ParamResult<T[K]>;
|
||||||
|
} & {
|
||||||
|
[K in keyof T as T[K] extends { required: true }
|
||||||
|
? never
|
||||||
|
: T[K] extends { fallback: infer F }
|
||||||
|
? F extends undefined
|
||||||
|
? K
|
||||||
|
: never
|
||||||
|
: K]?: ParamResult<T[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getParameters<
|
||||||
|
T extends {
|
||||||
|
[key: string]: {
|
||||||
|
name: string;
|
||||||
|
required?: boolean;
|
||||||
|
fallback?: string;
|
||||||
|
list?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>(parameters: T) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(parameters).map(([name, _parameter]) => {
|
||||||
|
const parameter = _parameter as {
|
||||||
|
name: string;
|
||||||
|
required?: boolean;
|
||||||
|
fallback?: string;
|
||||||
|
list?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
name,
|
||||||
|
(parameter.list
|
||||||
|
? getMultilineInput(parameter.name, parameter)
|
||||||
|
: getInput(parameter.name, parameter)) ||
|
||||||
|
parameter.fallback ||
|
||||||
|
undefined,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
) as OptionalIfNotRequiredOrFallback<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUuid(
|
||||||
|
coolifyFetch: CoolifyFetch,
|
||||||
|
parameters: {
|
||||||
|
projectId: string;
|
||||||
|
environmentName?: string;
|
||||||
|
environmentId?: string;
|
||||||
|
serverId: string;
|
||||||
|
name: string;
|
||||||
|
domains: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
checkEnvironment(parameters);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const environment = await coolifyFetch(
|
||||||
|
`/projects/${parameters.projectId}/${
|
||||||
|
parameters.environmentId ?? parameters.environmentName
|
||||||
|
}`
|
||||||
|
).then((res) => res.json() as Promise<{ id: number }>);
|
||||||
|
|
||||||
|
const response = await coolifyFetch("/applications").then(
|
||||||
|
(res) =>
|
||||||
|
res.json() as Promise<
|
||||||
|
{
|
||||||
|
uuid: string;
|
||||||
|
name: string;
|
||||||
|
environment_id: number;
|
||||||
|
destination: {
|
||||||
|
server: { uuid: string };
|
||||||
|
};
|
||||||
|
fqdn: string;
|
||||||
|
}[]
|
||||||
|
>
|
||||||
|
);
|
||||||
|
|
||||||
|
const matches = response.filter((application) => {
|
||||||
|
if (application.name != parameters.name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (application.environment_id != environment.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (application.destination.server.uuid != parameters.serverId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (application.fqdn != parameters.domains) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matches.length > 1) {
|
||||||
|
throw new Error("Multiple applications match the parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches.length == 1 ? matches[0].uuid : null;
|
||||||
|
} catch (err) {
|
||||||
|
error(err as string | Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deploy(coolifyFetch: CoolifyFetch, uuid: string) {
|
||||||
|
return await coolifyFetch(`/deploy?uuid=${uuid}&force=false`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonParameters = {
|
||||||
|
projectId: { name: Input.ProjectId, required: true },
|
||||||
|
serverId: { name: Input.ServerId, required: true },
|
||||||
|
name: { name: Input.Name, required: true },
|
||||||
|
domains: { name: Input.Domains, required: true },
|
||||||
|
environmentName: { name: Input.EnvironmentName },
|
||||||
|
environmentId: { name: Input.EnvironmentId },
|
||||||
|
} satisfies Parameters<typeof getParameters>[0];
|
||||||
|
|
||||||
|
function checkEnvironment(parameters: {
|
||||||
|
environmentName?: string;
|
||||||
|
environmentId?: string;
|
||||||
|
}) {
|
||||||
|
if (!parameters.environmentName && !parameters.environmentId) {
|
||||||
|
throw new Error(
|
||||||
|
"'environment-name' or 'environment-id' must be defined"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createEnv(
|
||||||
|
coolifyFetch: CoolifyFetch,
|
||||||
|
uuid: string,
|
||||||
|
envs: string[]
|
||||||
|
) {
|
||||||
|
for (const env of envs) {
|
||||||
|
const [key, value] = env.split(/=(.*)/s).map((x) => x.trim());
|
||||||
|
|
||||||
|
await coolifyFetch(`/applications/${uuid}/envs`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
is_preview: false,
|
||||||
|
is_literal: true,
|
||||||
|
is_multiline: false,
|
||||||
|
is_shown_once: false,
|
||||||
|
}),
|
||||||
|
}).then((res) => res.json() as Promise<{ uuid: string }>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createDockerCompose(coolifyFetch: CoolifyFetch) {
|
||||||
|
const parameters = getParameters({
|
||||||
|
...commonParameters,
|
||||||
|
dockerCompose: { name: Input.DockerCompose, required: true },
|
||||||
|
overrideString: { name: Input.Override, fallback: "{}" },
|
||||||
|
env: { name: Input.Env, list: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
checkEnvironment(parameters);
|
||||||
|
|
||||||
|
const override = JSON.parse(parameters.overrideString);
|
||||||
|
|
||||||
|
const hasEnv = parameters.env && parameters.env.length > 0;
|
||||||
|
|
||||||
|
const { uuid } = await coolifyFetch("/applications/dockercompose", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
project_uuid: parameters.projectId,
|
||||||
|
server_uuid: parameters.serverId,
|
||||||
|
environment_name: parameters.environmentName,
|
||||||
|
environment_uuid: parameters.environmentId,
|
||||||
|
docker_compose_raw: parameters.dockerCompose,
|
||||||
|
name: parameters.name,
|
||||||
|
domains: parameters.domains,
|
||||||
|
instant_deploy: false,
|
||||||
|
...override,
|
||||||
|
}),
|
||||||
|
}).then((res) => res.json() as Promise<{ uuid: string }>);
|
||||||
|
|
||||||
|
if (parameters.env && parameters.env.length > 0) {
|
||||||
|
createEnv(coolifyFetch, uuid, parameters.env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createDockerImage(coolifyFetch: CoolifyFetch) {
|
||||||
|
const parameters = getParameters({
|
||||||
|
...commonParameters,
|
||||||
|
imageName: { name: Input.ImageName, required: true },
|
||||||
|
imageTag: { name: Input.ImageTag, required: true },
|
||||||
|
ports: { name: Input.Ports, fallback: "80" },
|
||||||
|
overrideString: { name: Input.Override, fallback: "{}" },
|
||||||
|
env: { name: Input.Env, list: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
checkEnvironment(parameters);
|
||||||
|
|
||||||
|
const override = JSON.parse(parameters.overrideString);
|
||||||
|
|
||||||
|
const { uuid } = await coolifyFetch("/applications/dockerimage", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
project_uuid: parameters.projectId,
|
||||||
|
server_uuid: parameters.serverId,
|
||||||
|
environment_name: parameters.environmentName,
|
||||||
|
environment_uuid: parameters.environmentId,
|
||||||
|
docker_registry_image_name: parameters.imageName,
|
||||||
|
docker_registry_image_tag: parameters.imageTag,
|
||||||
|
name: parameters.name,
|
||||||
|
domains: parameters.domains,
|
||||||
|
ports_exposes: parameters.ports,
|
||||||
|
instant_deploy: false,
|
||||||
|
...override,
|
||||||
|
}),
|
||||||
|
}).then((res) => res.json() as Promise<{ uuid: string }>);
|
||||||
|
|
||||||
|
if (parameters.env && parameters.env.length > 0) {
|
||||||
|
createEnv(coolifyFetch, uuid, parameters.env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function update(coolifyFetch: CoolifyFetch) {
|
||||||
|
const parameters = getParameters(commonParameters);
|
||||||
|
|
||||||
|
checkEnvironment(parameters);
|
||||||
|
|
||||||
|
const existingApplication = await getUuid(coolifyFetch, parameters);
|
||||||
|
|
||||||
|
if (!existingApplication) {
|
||||||
|
throw new Error("Application doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await deploy(coolifyFetch, existingApplication);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteApplication(coolifyFetch: CoolifyFetch) {
|
||||||
|
const parameters = getParameters(commonParameters);
|
||||||
|
|
||||||
|
checkEnvironment(parameters);
|
||||||
|
|
||||||
|
const existingApplication = await getUuid(coolifyFetch, parameters);
|
||||||
|
|
||||||
|
if (!existingApplication) {
|
||||||
|
throw new Error("No application found matching inputs");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await coolifyFetch(`/applications/${existingApplication}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions: Record<
|
||||||
|
string,
|
||||||
|
{ action: (coolifyFetch: CoolifyFetch) => Promise<unknown> }
|
||||||
|
> = {
|
||||||
|
"create-docker-compose": {
|
||||||
|
action: createDockerCompose,
|
||||||
|
},
|
||||||
|
"create-docker-image": {
|
||||||
|
action: createDockerImage,
|
||||||
|
},
|
||||||
|
update: { action: update },
|
||||||
|
delete: { action: deleteApplication },
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function run() {
|
||||||
|
const actionName = getInput("action", { required: true });
|
||||||
|
const action = actions[actionName];
|
||||||
|
|
||||||
|
if (action == undefined) {
|
||||||
|
throw new Error(
|
||||||
|
`"${actionName} is not a valid action. Valid options are:\n\n\t${Object.keys(
|
||||||
|
actions
|
||||||
|
).join("\n\t")}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiKey = getInput("coolify-api-key", { required: true });
|
||||||
|
const coolifyUrl = (
|
||||||
|
getInput("coolify-url") || "https://coolify.skytechab.se"
|
||||||
|
).replace(/\/+$/, "");
|
||||||
|
|
||||||
|
info(`Executing action '${actionName}' to url ${coolifyUrl}`);
|
||||||
|
|
||||||
|
await action.action((input: string, init?: RequestInit) =>
|
||||||
|
fetch(`${coolifyUrl}/api/v1/${input.replace(/^\//, "")}`, {
|
||||||
|
...init,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiKey}`,
|
||||||
|
...init?.headers,
|
||||||
|
},
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (!res.ok) {
|
||||||
|
const response = (await res.json()) as { message: string };
|
||||||
|
throw new Error(response.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Action completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
Loading…
x
Reference in New Issue
Block a user