feat: github allow fual branches

This commit is contained in:
Andras Bacsai 2022-09-01 12:14:06 +02:00
parent 4e2dad7720
commit 582170f26e
3 changed files with 157 additions and 150 deletions

View File

@ -183,7 +183,7 @@ export async function getApplicationFromDB(id: string, teamId: string) {
} }
export async function getApplicationFromDBWebhook(projectId: number, branch: string) { export async function getApplicationFromDBWebhook(projectId: number, branch: string) {
try { try {
let application = await prisma.application.findFirst({ let applications = await prisma.application.findMany({
where: { projectId, branch, settings: { autodeploy: true } }, where: { projectId, branch, settings: { autodeploy: true } },
include: { include: {
destinationDocker: true, destinationDocker: true,
@ -193,22 +193,28 @@ export async function getApplicationFromDBWebhook(projectId: number, branch: str
persistentStorage: true persistentStorage: true
} }
}); });
if (!application) { if (applications.length === 0) {
throw { status: 500, message: 'Application not configured.' } throw { status: 500, message: 'Application not configured.' }
} }
application = decryptApplication(application); applications = applications.map((application: any) => {
const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage( application = decryptApplication(application);
application.buildPack const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage(
); application.buildPack
);
// Set default build images // Set default build images
if (!application.baseImage) { if (!application.baseImage) {
application.baseImage = baseImage; application.baseImage = baseImage;
} }
if (!application.baseBuildImage) { if (!application.baseBuildImage) {
application.baseBuildImage = baseBuildImage; application.baseBuildImage = baseBuildImage;
} }
return { ...application, baseBuildImages, baseImages }; application.baseBuildImages = baseBuildImages;
application.baseImages = baseImages;
return application
})
return applications;
} catch ({ status, message }) { } catch ({ status, message }) {
return errorHandler({ status, message }) return errorHandler({ status, message })
@ -284,11 +290,11 @@ export async function saveApplicationSettings(request: FastifyRequest<SaveApplic
try { try {
const { id } = request.params const { id } = request.params
const { debug, previews, dualCerts, autodeploy, branch, projectId, isBot } = request.body const { debug, previews, dualCerts, autodeploy, branch, projectId, isBot } = request.body
const isDouble = await checkDoubleBranch(branch, projectId); // const isDouble = await checkDoubleBranch(branch, projectId);
if (isDouble && autodeploy) { // if (isDouble && autodeploy) {
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } }) // await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
throw { status: 500, message: 'Cannot activate automatic deployments until only one application is defined for this repository / branch.' } // throw { status: 500, message: 'Cannot activate automatic deployments until only one application is defined for this repository / branch.' }
} // }
await prisma.application.update({ await prisma.application.update({
where: { id }, where: { id },
data: { fqdn: isBot ? null : undefined, settings: { update: { debug, previews, dualCerts, autodeploy, isBot } } }, data: { fqdn: isBot ? null : undefined, settings: { update: { debug, previews, dualCerts, autodeploy, isBot } } },
@ -672,12 +678,12 @@ export async function saveRepository(request, reply) {
data: { repository, branch, projectId, settings: { update: { autodeploy, isPublicRepository } } } data: { repository, branch, projectId, settings: { update: { autodeploy, isPublicRepository } } }
}); });
} }
if (!isPublicRepository) { // if (!isPublicRepository) {
const isDouble = await checkDoubleBranch(branch, projectId); // const isDouble = await checkDoubleBranch(branch, projectId);
if (isDouble) { // if (isDouble) {
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false, isPublicRepository } }) // await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false, isPublicRepository } })
} // }
} // }
return reply.code(201).send() return reply.code(201).send()
} catch ({ status, message }) { } catch ({ status, message }) {
return errorHandler({ status, message }) return errorHandler({ status, message })
@ -974,9 +980,12 @@ export async function getBuildIdLogs(request: FastifyRequest<GetBuildIdLogs>) {
where: { buildId, time: { gt: sequence } }, where: { buildId, time: { gt: sequence } },
orderBy: { time: 'asc' } orderBy: { time: 'asc' }
}); });
const data = await prisma.build.findFirst({ where: { id: buildId } }); const data = await prisma.build.findFirst({ where: { id: buildId } });
const createdAt = day(data.createdAt).utc();
return { return {
logs, logs,
took: day().diff(createdAt) / 1000,
status: data?.status || 'queued' status: data?.status || 'queued'
} }
} catch ({ status, message }) { } catch ({ status, message }) {

View File

@ -67,7 +67,7 @@ export async function configureGitHubApp(request, reply) {
} }
export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promise<any> { export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promise<any> {
try { try {
const buildId = cuid();
const allowedGithubEvents = ['push', 'pull_request']; const allowedGithubEvents = ['push', 'pull_request'];
const allowedActions = ['opened', 'reopened', 'synchronize', 'closed']; const allowedActions = ['opened', 'reopened', 'synchronize', 'closed'];
const githubEvent = request.headers['x-github-event']?.toString().toLowerCase(); const githubEvent = request.headers['x-github-event']?.toString().toLowerCase();
@ -87,126 +87,124 @@ export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promi
if (!projectId || !branch) { if (!projectId || !branch) {
throw { status: 500, message: 'Cannot parse projectId or branch from the webhook?!' } throw { status: 500, message: 'Cannot parse projectId or branch from the webhook?!' }
} }
const applicationFound = await getApplicationFromDBWebhook(projectId, branch); const applicationsFound = await getApplicationFromDBWebhook(projectId, branch);
if (applicationFound) { if (applicationsFound && applicationsFound.length > 0) {
const webhookSecret = applicationFound.gitSource.githubApp.webhookSecret || null; for (const application of applicationsFound) {
//@ts-ignore const buildId = cuid();
const hmac = crypto.createHmac('sha256', webhookSecret); const webhookSecret = application.gitSource.githubApp.webhookSecret || null;
const digest = Buffer.from(
'sha256=' + hmac.update(JSON.stringify(body)).digest('hex'),
'utf8'
);
if (!isDev) {
const checksum = Buffer.from(githubSignature, 'utf8');
//@ts-ignore //@ts-ignore
if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) { const hmac = crypto.createHmac('sha256', webhookSecret);
throw { status: 500, message: 'SHA256 checksum failed. Are you doing something fishy?' } const digest = Buffer.from(
}; 'sha256=' + hmac.update(JSON.stringify(body)).digest('hex'),
} 'utf8'
);
if (!isDev) {
if (githubEvent === 'push') { const checksum = Buffer.from(githubSignature, 'utf8');
if (!applicationFound.configHash) { //@ts-ignore
const configHash = crypto if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) {
//@ts-ignore console.log('SHA256 checksum failed. Are you doing something fishy?')
.createHash('sha256') // throw { status: 500, message: 'SHA256 checksum failed. Are you doing something fishy?'
.update( };
JSON.stringify({
buildPack: applicationFound.buildPack,
port: applicationFound.port,
exposePort: applicationFound.exposePort,
installCommand: applicationFound.installCommand,
buildCommand: applicationFound.buildCommand,
startCommand: applicationFound.startCommand
})
)
.digest('hex');
await prisma.application.updateMany({
where: { branch, projectId },
data: { configHash }
});
}
await prisma.application.update({
where: { id: applicationFound.id },
data: { updatedAt: new Date() }
});
await prisma.build.create({
data: {
id: buildId,
applicationId: applicationFound.id,
destinationDockerId: applicationFound.destinationDocker.id,
gitSourceId: applicationFound.gitSource.id,
githubAppId: applicationFound.gitSource.githubApp?.id,
gitlabAppId: applicationFound.gitSource.gitlabApp?.id,
status: 'queued',
type: 'webhook_commit'
}
});
return {
message: 'Queued. Thank you!'
};
} else if (githubEvent === 'pull_request') {
const pullmergeRequestId = body.number.toString();
const pullmergeRequestAction = body.action;
const sourceBranch = body.pull_request.head.ref.includes('/') ? body.pull_request.head.ref.split('/')[2] : body.pull_request.head.ref;
if (!allowedActions.includes(pullmergeRequestAction)) {
throw { status: 500, message: 'Action not allowed.' }
} }
if (applicationFound.settings.previews) { if (githubEvent === 'push') {
if (applicationFound.destinationDockerId) { if (!application.configHash) {
const isRunning = await checkContainer( const configHash = crypto
{ //@ts-ignore
dockerId: applicationFound.destinationDocker.id, .createHash('sha256')
container: applicationFound.id .update(
} JSON.stringify({
); buildPack: application.buildPack,
if (!isRunning) { port: application.port,
throw { status: 500, message: 'Application not running.' } exposePort: application.exposePort,
} installCommand: application.installCommand,
} buildCommand: application.buildCommand,
if ( startCommand: application.startCommand
pullmergeRequestAction === 'opened' || })
pullmergeRequestAction === 'reopened' || )
pullmergeRequestAction === 'synchronize' .digest('hex');
) {
await prisma.application.update({ await prisma.application.update({
where: { id: applicationFound.id }, where: { id: application.id },
data: { updatedAt: new Date() } data: { configHash }
}); });
await prisma.build.create({
data: {
id: buildId,
pullmergeRequestId,
sourceBranch,
applicationId: applicationFound.id,
destinationDockerId: applicationFound.destinationDocker.id,
gitSourceId: applicationFound.gitSource.id,
githubAppId: applicationFound.gitSource.githubApp?.id,
gitlabAppId: applicationFound.gitSource.gitlabApp?.id,
status: 'queued',
type: 'webhook_pr'
}
});
return {
message: 'Queued. Thank you!'
};
} else if (pullmergeRequestAction === 'closed') {
if (applicationFound.destinationDockerId) {
const id = `${applicationFound.id}-${pullmergeRequestId}`;
await removeContainer({ id, dockerId: applicationFound.destinationDocker.id });
}
return {
message: 'Removed preview. Thank you!'
};
} }
} else {
throw { status: 500, message: 'Pull request previews are not enabled.' } await prisma.application.update({
where: { id: application.id },
data: { updatedAt: new Date() }
});
console.log(application.id)
await prisma.build.create({
data: {
id: buildId,
applicationId: application.id,
destinationDockerId: application.destinationDocker.id,
gitSourceId: application.gitSource.id,
githubAppId: application.gitSource.githubApp?.id,
gitlabAppId: application.gitSource.gitlabApp?.id,
status: 'queued',
type: 'webhook_commit'
}
});
console.log(`Webhook for ${application.name} queued.`)
} else if (githubEvent === 'pull_request') {
const pullmergeRequestId = body.number.toString();
const pullmergeRequestAction = body.action;
const sourceBranch = body.pull_request.head.ref.includes('/') ? body.pull_request.head.ref.split('/')[2] : body.pull_request.head.ref;
if (!allowedActions.includes(pullmergeRequestAction)) {
throw { status: 500, message: 'Action not allowed.' }
}
if (application.settings.previews) {
if (application.destinationDockerId) {
const isRunning = await checkContainer(
{
dockerId: application.destinationDocker.id,
container: application.id
}
);
if (!isRunning) {
throw { status: 500, message: 'Application not running.' }
}
}
if (
pullmergeRequestAction === 'opened' ||
pullmergeRequestAction === 'reopened' ||
pullmergeRequestAction === 'synchronize'
) {
await prisma.application.update({
where: { id: application.id },
data: { updatedAt: new Date() }
});
await prisma.build.create({
data: {
id: buildId,
pullmergeRequestId,
sourceBranch,
applicationId: application.id,
destinationDockerId: application.destinationDocker.id,
gitSourceId: application.gitSource.id,
githubAppId: application.gitSource.githubApp?.id,
gitlabAppId: application.gitSource.gitlabApp?.id,
status: 'queued',
type: 'webhook_pr'
}
});
} else if (pullmergeRequestAction === 'closed') {
if (application.destinationDockerId) {
const id = `${application.id}-${pullmergeRequestId}`;
await removeContainer({ id, dockerId: application.destinationDocker.id });
}
}
}
} }
} }
} }
throw { status: 500, message: 'Not handled event.' }
} catch ({ status, message }) { } catch ({ status, message }) {
return errorHandler({ status, message }) return errorHandler({ status, message })
} }

View File

@ -95,19 +95,19 @@
async function isBranchAlreadyUsed(event: any) { async function isBranchAlreadyUsed(event: any) {
selected.branch = event.detail.value; selected.branch = event.detail.value;
try { try {
const data = await get( // const data = await get(
`/applications/${id}/configuration/repository?repository=${selected.repository}&branch=${selected.branch}` // `/applications/${id}/configuration/repository?repository=${selected.repository}&branch=${selected.branch}`
); // );
if (data.used) { // if (data.used) {
const sure = confirm($t('application.configuration.branch_already_in_use')); // const sure = confirm($t('application.configuration.branch_already_in_use'));
if (sure) { // if (sure) {
selected.autodeploy = false; // selected.autodeploy = false;
showSave = true; // showSave = true;
return true; // return true;
} // }
showSave = false; // showSave = false;
return true; // return true;
} // }
showSave = true; showSave = true;
} catch (error) { } catch (error) {
showSave = false; showSave = false;