v1.0.13 (#46)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
import Deployment from '$models/Deployment';
|
||||
import { saveAppLog } from './logging';
|
||||
import * as packs from './packs';
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { docker } from '$lib/api/docker';
|
||||
import Deployment from '$models/Deployment';
|
||||
import { execShellAsync } from '../common';
|
||||
|
||||
export async function deleteSameDeployments(configuration) {
|
||||
@@ -17,29 +18,49 @@ export async function deleteSameDeployments(configuration) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function cleanupStuckedDeploymentsInDB() {
|
||||
// Cleanup stucked deployments.
|
||||
await Deployment.updateMany(
|
||||
{ progress: { $in: ['queued', 'inprogress'] } },
|
||||
{ progress: 'failed' }
|
||||
);
|
||||
}
|
||||
export async function purgeImagesContainers(configuration, deleteAll = false) {
|
||||
const { name, tag } = configuration.build.container;
|
||||
await execShellAsync('docker container prune -f');
|
||||
if (deleteAll) {
|
||||
const IDsToDelete = (
|
||||
await execShellAsync(`docker images ls --filter=reference='${name}' --format '{{json .ID }}'`)
|
||||
)
|
||||
.trim()
|
||||
.replace(/"/g, '')
|
||||
.split('\n');
|
||||
if (IDsToDelete.length > 0)
|
||||
await execShellAsync(`docker rmi -f ${IDsToDelete.toString().replace(',', ' ')}`);
|
||||
} else {
|
||||
const IDsToDelete = (
|
||||
await execShellAsync(
|
||||
`docker images ls --filter=reference='${name}' --filter=before='${name}:${tag}' --format '{{json .ID }}'`
|
||||
)
|
||||
)
|
||||
.trim()
|
||||
.replace(/"/g, '')
|
||||
.split('\n');
|
||||
if (IDsToDelete.length > 1)
|
||||
await execShellAsync(`docker rmi -f ${IDsToDelete.toString().replace(',', ' ')}`);
|
||||
try {
|
||||
await execShellAsync('docker container prune -f');
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
try {
|
||||
if (deleteAll) {
|
||||
const IDsToDelete = (
|
||||
await execShellAsync(
|
||||
`docker images ls --filter=reference='${name}' --format '{{json .ID }}'`
|
||||
)
|
||||
)
|
||||
.trim()
|
||||
.replace(/"/g, '')
|
||||
.split('\n');
|
||||
if (IDsToDelete.length > 0) await execShellAsync(`docker rmi -f ${IDsToDelete.join(' ')}`);
|
||||
} else {
|
||||
const IDsToDelete = (
|
||||
await execShellAsync(
|
||||
`docker images ls --filter=reference='${name}' --filter=before='${name}:${tag}' --format '{{json .ID }}'`
|
||||
)
|
||||
)
|
||||
.trim()
|
||||
.replace(/"/g, '')
|
||||
.split('\n');
|
||||
if (IDsToDelete.length > 1) await execShellAsync(`docker rmi -f ${IDsToDelete.join(' ')}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
try {
|
||||
await execShellAsync('docker image prune -f');
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
await execShellAsync('docker image prune -f');
|
||||
}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import Settings from '$models/Settings';
|
||||
import ServerLog from '$models/Logs/Server';
|
||||
import ApplicationLog from '$models/Logs/Application';
|
||||
import ServerLog from '$models/ServerLog';
|
||||
import ApplicationLog from '$models/ApplicationLog';
|
||||
import dayjs from 'dayjs';
|
||||
import { version } from '../../../../package.json';
|
||||
|
||||
|
||||
function generateTimestamp() {
|
||||
return `${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} `;
|
||||
}
|
||||
|
@@ -1,20 +1,21 @@
|
||||
import { docker, streamEvents } from '$lib/api/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const buildImageNodeDocker = (configuration) => {
|
||||
const buildImageNodeDocker = (configuration, prodBuild) => {
|
||||
return [
|
||||
'FROM node:lts',
|
||||
'WORKDIR /usr/src/app',
|
||||
`COPY ${configuration.build.directory}/package*.json ./`,
|
||||
configuration.build.command.installation && `RUN ${configuration.build.command.installation}`,
|
||||
`COPY ./${configuration.build.directory} ./`,
|
||||
`RUN ${configuration.build.command.build}`
|
||||
`RUN ${configuration.build.command.build}`,
|
||||
prodBuild && `RUN rm -fr node_modules && ${configuration.build.command.installation} --prod`
|
||||
].join('\n');
|
||||
};
|
||||
export async function buildImage(configuration, cacheBuild?: boolean) {
|
||||
export async function buildImage(configuration, cacheBuild?: boolean, prodBuild?: boolean) {
|
||||
await fs.writeFile(
|
||||
`${configuration.general.workdir}/Dockerfile`,
|
||||
buildImageNodeDocker(configuration)
|
||||
buildImageNodeDocker(configuration, prodBuild)
|
||||
);
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
|
@@ -7,6 +7,7 @@ import php from './php';
|
||||
import nuxtjs from './nuxtjs';
|
||||
import nodejs from './nodejs';
|
||||
import nextjs from './nextjs';
|
||||
import nestjs from './nestjs';
|
||||
import gatsby from './gatsby';
|
||||
import docker from './docker';
|
||||
|
||||
@@ -20,6 +21,7 @@ export {
|
||||
nuxtjs,
|
||||
nodejs,
|
||||
nextjs,
|
||||
nestjs,
|
||||
gatsby,
|
||||
docker
|
||||
};
|
||||
|
31
src/lib/api/applications/packs/nestjs/index.ts
Normal file
31
src/lib/api/applications/packs/nestjs/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { docker, streamEvents } from '$lib/api/docker';
|
||||
import { promises as fs } from 'fs';
|
||||
import { buildImage } from '../helpers';
|
||||
// `HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost:${configuration.publish.port}${configuration.publish.path} || exit 1`,
|
||||
const publishNodejsDocker = (configuration) => {
|
||||
return [
|
||||
'FROM node:lts',
|
||||
'WORKDIR /usr/src/app',
|
||||
configuration.build.command.build
|
||||
? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} ./`
|
||||
: `
|
||||
COPY ${configuration.build.directory}/package*.json ./
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
`CMD ${configuration.build.command.start}`
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
export default async function (configuration) {
|
||||
if (configuration.build.command.build) await buildImage(configuration, false, true);
|
||||
await fs.writeFile(
|
||||
`${configuration.general.workdir}/Dockerfile`,
|
||||
publishNodejsDocker(configuration)
|
||||
);
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
);
|
||||
await streamEvents(stream, configuration);
|
||||
}
|
@@ -13,7 +13,7 @@ const publishNodejsDocker = (configuration) => {
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
'CMD [ "yarn", "start" ]'
|
||||
`CMD ${configuration.build.command.start}`
|
||||
].join('\n');
|
||||
};
|
||||
export default async function (configuration) {
|
||||
|
@@ -13,7 +13,7 @@ const publishNodejsDocker = (configuration) => {
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
'CMD [ "yarn", "start" ]'
|
||||
`CMD ${configuration.build.command.start}`
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
|
@@ -13,7 +13,7 @@ const publishNodejsDocker = (configuration) => {
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
'CMD [ "yarn", "start" ]'
|
||||
`CMD ${configuration.build.command.start}`
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
const defaultBuildAndDeploy = {
|
||||
installation: 'yarn install',
|
||||
build: 'yarn build'
|
||||
build: 'yarn build',
|
||||
start: 'yarn start'
|
||||
};
|
||||
|
||||
const templates = {
|
||||
@@ -10,6 +11,13 @@ const templates = {
|
||||
directory: 'public',
|
||||
name: 'Svelte'
|
||||
},
|
||||
'@nestjs/core': {
|
||||
pack: 'nestjs',
|
||||
...defaultBuildAndDeploy,
|
||||
start: 'yarn start:prod',
|
||||
port: 3000,
|
||||
name: 'NestJS'
|
||||
},
|
||||
next: {
|
||||
pack: 'nextjs',
|
||||
...defaultBuildAndDeploy,
|
@@ -1,4 +1,5 @@
|
||||
import Deployment from '$models/Logs/Deployment';
|
||||
|
||||
import Deployment from '$models/Deployment';
|
||||
import dayjs from 'dayjs';
|
||||
import buildContainer from './buildContainer';
|
||||
import { updateServiceLabels } from './configuration';
|
||||
@@ -9,23 +10,21 @@ import { saveAppLog } from './logging';
|
||||
export default async function (configuration, imageChanged) {
|
||||
const { id, organization, name, branch } = configuration.repository;
|
||||
const { domain } = configuration.publish;
|
||||
const { deployId, nickname } = configuration.general;
|
||||
await new Deployment({
|
||||
repoId: id,
|
||||
branch,
|
||||
deployId,
|
||||
domain,
|
||||
organization,
|
||||
name,
|
||||
nickname
|
||||
}).save();
|
||||
await saveAppLog(`${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} Queued.`, configuration);
|
||||
await copyFiles(configuration);
|
||||
await buildContainer(configuration);
|
||||
await deploy(configuration, imageChanged);
|
||||
await Deployment.findOneAndUpdate(
|
||||
{ repoId: id, branch, deployId, organization, name, domain },
|
||||
{ repoId: id, branch, deployId, organization, name, domain, progress: 'done' }
|
||||
);
|
||||
await updateServiceLabels(configuration);
|
||||
const { deployId} = configuration.general;
|
||||
try {
|
||||
await saveAppLog(`${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')} Queued.`, configuration);
|
||||
await copyFiles(configuration);
|
||||
await buildContainer(configuration);
|
||||
await deploy(configuration, imageChanged);
|
||||
await Deployment.findOneAndUpdate(
|
||||
{ repoId: id, branch, deployId, organization, name, domain },
|
||||
{ repoId: id, branch, deployId, organization, name, domain, progress: 'done' }
|
||||
);
|
||||
await updateServiceLabels(configuration);
|
||||
} catch (error) {
|
||||
await Deployment.findOneAndUpdate(
|
||||
{ repoId: id, branch, deployId, organization, name, domain },
|
||||
{ repoId: id, branch, deployId, organization, name, domain, progress: 'failed' }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,17 @@
|
||||
import shell from 'shelljs';
|
||||
import User from '$models/User';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
import { saveServerLog } from './applications/logging';
|
||||
|
||||
export function execShellAsync(cmd, opts = {}) {
|
||||
try {
|
||||
return new Promise(function (resolve, reject) {
|
||||
shell.config.silent = true;
|
||||
shell.exec(cmd, opts, function (code, stdout, stderr) {
|
||||
if (code !== 0) return reject(new Error(stderr));
|
||||
shell.exec(cmd, opts, async function (code, stdout, stderr) {
|
||||
if (code !== 0) {
|
||||
await saveServerLog({ message: JSON.stringify({ cmd, opts, code, stdout, stderr }) })
|
||||
return reject(new Error(stderr));
|
||||
}
|
||||
return resolve(stdout);
|
||||
});
|
||||
});
|
||||
|
23
src/lib/api/github.ts
Normal file
23
src/lib/api/github.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { Request } from '@sveltejs/kit';
|
||||
|
||||
export async function githubAPI(
|
||||
request: Request,
|
||||
resource: string,
|
||||
token?: string,
|
||||
data?: Record<string, unknown>
|
||||
) {
|
||||
const base = 'https://api.github.com';
|
||||
const res = await fetch(`${base}${resource}`, {
|
||||
method: request.method,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json',
|
||||
authorization: token ? `token ${token}` : ''
|
||||
},
|
||||
body: data && JSON.stringify(data)
|
||||
});
|
||||
return {
|
||||
status: res.status,
|
||||
body: await res.json()
|
||||
};
|
||||
}
|
@@ -1,102 +0,0 @@
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { browser } from '$app/env';
|
||||
|
||||
export async function request(
|
||||
url,
|
||||
session,
|
||||
{
|
||||
method,
|
||||
body,
|
||||
customHeaders
|
||||
}: {
|
||||
url?: string;
|
||||
session?: any;
|
||||
fetch?: any;
|
||||
method?: string;
|
||||
body?: any;
|
||||
customHeaders?: Record<string, unknown>;
|
||||
} = {}
|
||||
) {
|
||||
let fetch;
|
||||
if (browser) {
|
||||
fetch = window.fetch;
|
||||
} else {
|
||||
fetch = session.fetch;
|
||||
}
|
||||
let headers = { 'content-type': 'application/json; charset=UTF-8' };
|
||||
if (method === 'DELETE') {
|
||||
delete headers['content-type'];
|
||||
}
|
||||
const isGithub = url.match(/api.github.com/);
|
||||
if (isGithub) {
|
||||
headers = Object.assign(headers, {
|
||||
Authorization: `token ${session.ghToken}`
|
||||
});
|
||||
}
|
||||
const config: any = {
|
||||
method: method || (body ? 'POST' : 'GET'),
|
||||
cache: isGithub ? 'no-cache' : 'default',
|
||||
headers: {
|
||||
...headers,
|
||||
...customHeaders
|
||||
}
|
||||
};
|
||||
if (body) {
|
||||
config.body = JSON.stringify(body);
|
||||
}
|
||||
const response = await fetch(url, config);
|
||||
if (response.status >= 200 && response.status <= 299) {
|
||||
if (response.headers.get('content-type').match(/application\/json/)) {
|
||||
const json = await response.json();
|
||||
if (json?.success === false) {
|
||||
browser && json.showToast !== false && toast.push(json.message);
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
error: json.message
|
||||
});
|
||||
}
|
||||
return json;
|
||||
} else if (response.headers.get('content-type').match(/text\/plain/)) {
|
||||
return await response.text();
|
||||
} else if (response.headers.get('content-type').match(/multipart\/form-data/)) {
|
||||
return await response.formData();
|
||||
} else {
|
||||
console.log(response);
|
||||
if (response.headers.get('content-disposition')) {
|
||||
const blob = await response.blob();
|
||||
console.log(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = response.headers.get('content-disposition').split('=')[1] || 'backup.gz';
|
||||
link.target = '_blank';
|
||||
link.setAttribute('type', 'hidden');
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
return;
|
||||
}
|
||||
return await response.blob();
|
||||
}
|
||||
} else {
|
||||
if (response.status === 401) {
|
||||
browser && toast.push('Unauthorized');
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
error: 'Unauthorized'
|
||||
});
|
||||
} else if (response.status >= 500) {
|
||||
const error = (await response.json()).error;
|
||||
browser && toast.push(error);
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
error: error || 'Oops, something is not okay. Are you okay?'
|
||||
});
|
||||
} else {
|
||||
browser && toast.push(response.statusText);
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
error: response.statusText
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user