v1.0.16 (#51)
This commit is contained in:
@@ -11,7 +11,8 @@ export async function deleteSameDeployments(configuration) {
|
||||
const running = JSON.parse(s.Spec.Labels.configuration);
|
||||
if (
|
||||
running.repository.id === configuration.repository.id &&
|
||||
running.repository.branch === configuration.repository.branch
|
||||
running.repository.branch === configuration.repository.branch &&
|
||||
running.publish.domain === configuration.publish.domain
|
||||
) {
|
||||
await execShellAsync(`docker stack rm ${s.Spec.Labels['com.docker.stack.namespace']}`);
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import { execShellAsync } from '../common';
|
||||
export default async function (configuration) {
|
||||
try {
|
||||
const { GITHUB_APP_PRIVATE_KEY } = process.env;
|
||||
const { workdir } = configuration.general;
|
||||
const { workdir, isPreviewDeploymentEnabled, pullRequest } = configuration.general;
|
||||
const { organization, name, branch } = configuration.repository;
|
||||
const github = configuration.github;
|
||||
if (!github.installation.id || !github.app.id) {
|
||||
@@ -37,8 +37,12 @@ export default async function (configuration) {
|
||||
await execShellAsync(
|
||||
`mkdir -p ${workdir} && git clone -q -b ${branch} https://x-access-token:${token}@github.com/${organization}/${name}.git ${workdir}/`
|
||||
);
|
||||
|
||||
if (isPreviewDeploymentEnabled && pullRequest && pullRequest !== 0) {
|
||||
await execShellAsync(`cd ${workdir} && git fetch origin pull/${pullRequest}/head:pull_${pullRequest} && git checkout pull_${pullRequest}`)
|
||||
}
|
||||
configuration.build.container.tag = (
|
||||
await execShellAsync(`cd ${configuration.general.workdir}/ && git rev-parse HEAD`)
|
||||
await execShellAsync(`cd ${workdir}/ && git rev-parse HEAD`)
|
||||
)
|
||||
.replace('\n', '')
|
||||
.slice(0, 7);
|
||||
|
@@ -10,9 +10,8 @@ function getUniq() {
|
||||
}
|
||||
|
||||
export function setDefaultConfiguration(configuration) {
|
||||
const nickname = getUniq();
|
||||
const nickname = configuration.general.nickname || getUniq();
|
||||
const deployId = cuid();
|
||||
|
||||
const shaBase = JSON.stringify({ repository: configuration.repository });
|
||||
const sha256 = crypto.createHash('sha256').update(shaBase).digest('hex');
|
||||
|
||||
@@ -21,12 +20,14 @@ export function setDefaultConfiguration(configuration) {
|
||||
configuration.general.nickname = nickname;
|
||||
configuration.general.deployId = deployId;
|
||||
configuration.general.workdir = `/tmp/${deployId}`;
|
||||
|
||||
if (configuration.general.isPreviewDeploymentEnabled && configuration.general.pullRequest !== 0) {
|
||||
configuration.build.container.name = `pr${configuration.general.pullRequest}-${sha256.slice(0, 8)}`
|
||||
configuration.publish.domain = `pr${configuration.general.pullRequest}.${configuration.publish.domain}`
|
||||
}
|
||||
if (!configuration.publish.path) configuration.publish.path = '/';
|
||||
if (!configuration.publish.port) {
|
||||
if (
|
||||
configuration.build.pack === 'nodejs' ||
|
||||
configuration.build.pack === 'vuejs' ||
|
||||
configuration.build.pack === 'nuxtjs' ||
|
||||
configuration.build.pack === 'rust' ||
|
||||
configuration.build.pack === 'nextjs' ||
|
||||
@@ -74,20 +75,23 @@ export function setDefaultConfiguration(configuration) {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
export async function precheckDeployment({ services, configuration }) {
|
||||
export async function precheckDeployment(configuration) {
|
||||
const services = (await docker.engine.listServices()).filter(
|
||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application' && JSON.parse(r.Spec.Labels.configuration).publish.domain === configuration.publish.domain
|
||||
);
|
||||
let foundService = false;
|
||||
let configChanged = false;
|
||||
let imageChanged = false;
|
||||
|
||||
let forceUpdate = false;
|
||||
|
||||
for (const service of services) {
|
||||
const running = JSON.parse(service.Spec.Labels.configuration);
|
||||
if (running) {
|
||||
|
||||
if (
|
||||
running.repository.id === configuration.repository.id &&
|
||||
running.repository.branch === configuration.repository.branch
|
||||
) {
|
||||
foundService = true;
|
||||
// Base service configuration changed
|
||||
if (
|
||||
!running.build.container.baseSHA ||
|
||||
@@ -107,9 +111,32 @@ export async function precheckDeployment({ services, configuration }) {
|
||||
(n) =>
|
||||
n.DesiredState !== 'Running' && n.Image.split(':')[1] === running.build.container.tag
|
||||
);
|
||||
if (isError.length > 0) forceUpdate = true;
|
||||
foundService = true;
|
||||
if (isError.length > 0) {
|
||||
forceUpdate = true;
|
||||
}
|
||||
|
||||
const compareObjects = (a, b) => {
|
||||
if (a === b) return true;
|
||||
|
||||
if (typeof a != 'object' || typeof b != 'object' || a == null || b == null) return false;
|
||||
|
||||
let keysA = Object.keys(a), keysB = Object.keys(b);
|
||||
|
||||
if (keysA.length != keysB.length) return false;
|
||||
|
||||
for (let key of keysA) {
|
||||
if (!keysB.includes(key)) return false;
|
||||
|
||||
if (typeof a[key] === 'function' || typeof b[key] === 'function') {
|
||||
if (a[key].toString() != b[key].toString()) return false;
|
||||
} else {
|
||||
if (!compareObjects(a[key], b[key])) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const runningWithoutContainer = JSON.parse(JSON.stringify(running));
|
||||
delete runningWithoutContainer.build.container;
|
||||
|
||||
@@ -118,16 +145,19 @@ export async function precheckDeployment({ services, configuration }) {
|
||||
|
||||
// If only the configuration changed
|
||||
if (
|
||||
JSON.stringify(runningWithoutContainer.build) !==
|
||||
JSON.stringify(configurationWithoutContainer.build) ||
|
||||
JSON.stringify(runningWithoutContainer.publish) !==
|
||||
JSON.stringify(configurationWithoutContainer.publish)
|
||||
)
|
||||
!compareObjects(runningWithoutContainer.build,configurationWithoutContainer.build) ||
|
||||
!compareObjects(runningWithoutContainer.publish,configurationWithoutContainer.publish) ||
|
||||
runningWithoutContainer.general.isPreviewDeploymentEnabled !==
|
||||
configurationWithoutContainer.general.isPreviewDeploymentEnabled
|
||||
){
|
||||
configChanged = true;
|
||||
}
|
||||
|
||||
// If only the image changed
|
||||
if (running.build.container.tag !== configuration.build.container.tag) imageChanged = true;
|
||||
// If build pack changed, forceUpdate the service
|
||||
if (running.build.pack !== configuration.build.pack) forceUpdate = true;
|
||||
if (configuration.general.isPreviewDeploymentEnabled && configuration.general.pullRequest !== 0) forceUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,28 +174,5 @@ export async function precheckDeployment({ services, configuration }) {
|
||||
}
|
||||
|
||||
export async function updateServiceLabels(configuration) {
|
||||
// In case of any failure during deployment, still update the current configuration.
|
||||
const services = (await docker.engine.listServices()).filter(
|
||||
(r) => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application'
|
||||
);
|
||||
const found = services.find((s) => {
|
||||
const config = JSON.parse(s.Spec.Labels.configuration);
|
||||
if (
|
||||
config.repository.id === configuration.repository.id &&
|
||||
config.repository.branch === configuration.repository.branch
|
||||
) {
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if (found) {
|
||||
const { ID } = found;
|
||||
const Labels = { ...JSON.parse(found.Spec.Labels.configuration), ...configuration };
|
||||
await execShellAsync(
|
||||
`docker service update --label-add configuration='${JSON.stringify(
|
||||
Labels
|
||||
)}' --label-add com.docker.stack.image='${configuration.build.container.name}:${configuration.build.container.tag
|
||||
}' ${ID}`
|
||||
);
|
||||
}
|
||||
return await execShellAsync(`docker service update --label-add configuration='${JSON.stringify(configuration)}' ${configuration.build.container.name}_${configuration.build.container.name}`)
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { docker } from '$lib/api/docker';
|
||||
import { saveAppLog } from './logging';
|
||||
import { promises as fs } from 'fs';
|
||||
import { deleteSameDeployments } from './cleanup';
|
||||
import { deleteSameDeployments, purgeImagesContainers } from './cleanup';
|
||||
import yaml from 'js-yaml';
|
||||
import { execShellAsync } from '../common';
|
||||
import { delay, execShellAsync } from '../common';
|
||||
|
||||
export default async function (configuration, imageChanged) {
|
||||
const generateEnvs = {};
|
||||
@@ -11,6 +11,7 @@ export default async function (configuration, imageChanged) {
|
||||
generateEnvs[secret.name] = secret.value;
|
||||
}
|
||||
const containerName = configuration.build.container.name;
|
||||
const containerTag = configuration.build.container.tag;
|
||||
|
||||
// Only save SHA256 of it in the configuration label
|
||||
const baseServiceConfiguration = configuration.baseServiceConfiguration;
|
||||
@@ -20,7 +21,7 @@ export default async function (configuration, imageChanged) {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[containerName]: {
|
||||
image: `${configuration.build.container.name}:${configuration.build.container.tag}`,
|
||||
image: `${containerName}:${containerTag}`,
|
||||
networks: [`${docker.network}`],
|
||||
environment: generateEnvs,
|
||||
deploy: {
|
||||
@@ -31,21 +32,21 @@ export default async function (configuration, imageChanged) {
|
||||
'configuration=' + JSON.stringify(configuration),
|
||||
'traefik.enable=true',
|
||||
'traefik.http.services.' +
|
||||
configuration.build.container.name +
|
||||
containerName +
|
||||
`.loadbalancer.server.port=${configuration.publish.port}`,
|
||||
'traefik.http.routers.' + configuration.build.container.name + '.entrypoints=websecure',
|
||||
'traefik.http.routers.' + containerName + '.entrypoints=websecure',
|
||||
'traefik.http.routers.' +
|
||||
configuration.build.container.name +
|
||||
containerName +
|
||||
'.rule=Host(`' +
|
||||
configuration.publish.domain +
|
||||
'`) && PathPrefix(`' +
|
||||
configuration.publish.path +
|
||||
'`)',
|
||||
'traefik.http.routers.' +
|
||||
configuration.build.container.name +
|
||||
containerName +
|
||||
'.tls.certresolver=letsencrypt',
|
||||
'traefik.http.routers.' +
|
||||
configuration.build.container.name +
|
||||
containerName +
|
||||
'.middlewares=global-compress'
|
||||
]
|
||||
}
|
||||
@@ -62,7 +63,7 @@ export default async function (configuration, imageChanged) {
|
||||
if (imageChanged) {
|
||||
// console.log('image changed')
|
||||
await execShellAsync(
|
||||
`docker service update --image ${configuration.build.container.name}:${configuration.build.container.tag} ${configuration.build.container.name}_${configuration.build.container.name}`
|
||||
`docker service update --image ${containerName}:${containerTag} ${containerName}_${containerName}`
|
||||
);
|
||||
} else {
|
||||
// console.log('new deployment or force deployment or config changed')
|
||||
@@ -71,6 +72,11 @@ export default async function (configuration, imageChanged) {
|
||||
`cat ${configuration.general.workdir}/stack.yml | docker stack deploy --prune -c - ${containerName}`
|
||||
);
|
||||
}
|
||||
async function purgeImagesAsync(found) {
|
||||
await delay(10000);
|
||||
await purgeImagesContainers(found, true);
|
||||
}
|
||||
purgeImagesAsync(configuration)
|
||||
|
||||
await saveAppLog('### Published done!', configuration);
|
||||
}
|
||||
|
@@ -46,6 +46,7 @@ const templates = {
|
||||
pack: 'vuejs',
|
||||
...defaultBuildAndDeploy,
|
||||
directory: 'dist',
|
||||
port: 80,
|
||||
name: 'Vue'
|
||||
},
|
||||
gatsby: {
|
||||
|
@@ -10,7 +10,7 @@ import { saveAppLog } from './logging';
|
||||
export default async function (configuration, imageChanged) {
|
||||
const { id, organization, name, branch } = configuration.repository;
|
||||
const { domain } = configuration.publish;
|
||||
const { deployId} = configuration.general;
|
||||
const { deployId } = configuration.general;
|
||||
try {
|
||||
await saveAppLog(`${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} Queued.`, configuration);
|
||||
await copyFiles(configuration);
|
||||
@@ -20,6 +20,7 @@ export default async function (configuration, imageChanged) {
|
||||
{ repoId: id, branch, deployId, organization, name, domain },
|
||||
{ repoId: id, branch, deployId, organization, name, domain, progress: 'done' }
|
||||
);
|
||||
|
||||
await updateServiceLabels(configuration);
|
||||
} catch (error) {
|
||||
await Deployment.findOneAndUpdate(
|
||||
|
Reference in New Issue
Block a user