Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node AS builder
FROM node:24 AS builder

WORKDIR /app
COPY package.json .
Expand All @@ -11,9 +11,12 @@ FROM node:24.11.0

RUN groupadd -r devtron && useradd -r -g devtron devtron

ENV TINI_VERSION v0.18.0
RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && echo $arch && wget https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${arch} -O /tini
RUN chmod +x /tini
ENV TINI_VERSION=v0.18.0
RUN apt-get update && apt-get install -y wget && \
arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && echo $arch && \
wget https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${arch} -O /tini && \
chmod +x /tini && \
apt-get purge -y wget && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["/tini", "--"]

WORKDIR /app
Expand Down
25 changes: 11 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "1.0.0",
"description": "app for notification",
"main": "dist/server.js",
"engines": {
"node": ">=20.0.0"
},
"scripts": {
"start": "ts-node src/server.ts",
"dev": "nodemon src/server.ts",
Expand All @@ -29,7 +32,7 @@
"dependencies": {
"@types/express": "^4.17.21",
"@types/mustache": "^0.8.32",
"@types/node": "^12.0.2",
"@types/node": "^20.0.0",
"@types/request": "^2.48.1",
"axios": "^1.7.7",
"body-parser": "^1.20.3",
Expand Down
111 changes: 59 additions & 52 deletions src/common/mustacheHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,65 @@ export class MustacheHelper {
if(event.eventTypeId===EVENT_TYPE.ScoopNotification){
return this.parseScoopNotification(event)
}

const date = moment(event.eventTime);
const timestamp = isSlackNotification
? date.unix()
: date.format('dddd, MMMM Do YYYY hh:mm A [GMT]Z');

let baseURL = event.baseUrl;

// Handle approval events FIRST (before checking pipelineType)
if (event.eventTypeId===EVENT_TYPE.Approval){
let imageTagNames,imageComment,imageLink,approvalLink;
let index = -1;
if (event.payload.dockerImageUrl) index = event.payload.dockerImageUrl.lastIndexOf(":");
if (event.payload.imageTagNames) imageTagNames = event.payload.imageTagNames;
if (event.payload.imageComment) imageComment = event.payload.imageComment;
if (baseURL && event.payload.imageApprovalLink) imageLink =`${baseURL}${event.payload.imageApprovalLink}`;
if (baseURL && event.payload.approvalLink) approvalLink = `${baseURL}${event.payload.approvalLink}`;

return {
eventTime: timestamp,
triggeredBy: event.payload.triggeredBy || "NA",
appName: event.payload.appName || "NA",
envName: event.payload.envName || "NA",
pipelineName: event.payload.pipelineName || "NA",
imageTag: index >= 0 ? event.payload.dockerImageUrl.substring(index + 1) : "NA",
comment:imageComment,
tags:imageTagNames,
imageApprovalLink:imageLink,
approvalLink:approvalLink,
}
}

if (event.eventTypeId===EVENT_TYPE.ConfigApproval){
let protectConfigFileType,protectConfigFileName,protectConfigComment,protectConfigLink,envName,approvalLink;
if (event.payload.protectConfigFileType) protectConfigFileType = event.payload.protectConfigFileType;
if (event.payload.protectConfigFileName) protectConfigFileName = event.payload.protectConfigFileName;
if (event.payload.protectConfigComment) protectConfigComment = event.payload.protectConfigComment.split("\n");
if (baseURL && event.payload.protectConfigLink) protectConfigLink =`${baseURL}${event.payload.protectConfigLink}`;
if (baseURL && event.payload.approvalLink) approvalLink = `${baseURL}${event.payload.approvalLink}`;
if (!event.payload.envName){
envName="Base configuration"
}
else{
envName=event.payload.envName
}
return {
eventTime: timestamp,
triggeredBy: event.payload.triggeredBy || "NA",
appName: event.payload.appName || "NA",
envName: envName,
protectConfigFileType:protectConfigFileType,
protectConfigFileName:protectConfigFileName,
protectConfigComment:protectConfigComment,
protectConfigLink:protectConfigLink,
approvalLink:approvalLink,
}
}

// Now handle CI/CD events (which need material parsing)
let material = event.payload.material;
let ciMaterials;
if (event.eventTypeId!==EVENT_TYPE.Approval && event.eventTypeId!==EVENT_TYPE.ConfigApproval && event.eventTypeId!=EVENT_TYPE.ImagePromotion){
Expand Down Expand Up @@ -116,12 +174,6 @@ export class MustacheHelper {
}) : [];
}


const date = moment(event.eventTime);
const timestamp = isSlackNotification
? date.unix()
: date.format('dddd, MMMM Do YYYY hh:mm A [GMT]Z');

if (event.pipelineType === "CI") {
let buildHistoryLink;
if (baseURL && event.payload.buildHistoryLink) buildHistoryLink = `${baseURL}${event.payload.buildHistoryLink}`;
Expand Down Expand Up @@ -163,52 +215,7 @@ export class MustacheHelper {
triggeredWithoutApprovalStyle: event.isDeploymentDoneWithoutApproval ? 'block' : 'none'
}
}
else if (event.eventTypeId===EVENT_TYPE.Approval){
let imageTagNames,imageComment,imageLink,approvalLink;
let index = -1;
if (event.payload.dockerImageUrl) index = event.payload.dockerImageUrl.lastIndexOf(":");
if (event.payload.imageTagNames) imageTagNames = event.payload.imageTagNames;
if (event.payload.imageComment) imageComment = event.payload.imageComment;
if (baseURL && event.payload.imageApprovalLink) imageLink =`${baseURL}${event.payload.imageApprovalLink}`;
if (baseURL && event.payload.approvalLink) approvalLink = `${baseURL}${event.payload.approvalLink}`;

return {
eventTime: timestamp,
triggeredBy: event.payload.triggeredBy || "NA",
appName: event.payload.appName || "NA",
envName: event.payload.envName || "NA",
pipelineName: event.payload.pipelineName || "NA",
imageTag: index >= 0 ? event.payload.dockerImageUrl.substring(index + 1) : "NA",
comment:imageComment,
tags:imageTagNames,
imageApprovalLink:imageLink,
approvalLink:approvalLink,
}


}
else if (event.eventTypeId===EVENT_TYPE.ConfigApproval){
let protectConfigFileType,protectConfigFileName,protectConfigComment,protectConfigLink,envName,approvalLink;
if (event.payload.protectConfigFileType) protectConfigFileType = event.payload.protectConfigFileType;
if (event.payload.protectConfigFileName) protectConfigFileName = event.payload.protectConfigFileName;
if (event.payload.protectConfigComment) protectConfigComment = event.payload.protectConfigComment.split("\n");
if (baseURL && event.payload.protectConfigLink) protectConfigLink =`${baseURL}${event.payload.protectConfigLink}`;
if (baseURL && event.payload.approvalLink) approvalLink = `${baseURL}${event.payload.approvalLink}`;
if (!event.payload.envName){
envName="Base configuration"
}
return {
eventTime: timestamp,
triggeredBy: event.payload.triggeredBy || "NA",
appName: event.payload.appName || "NA",
envName: event.payload.envName || envName,
protectConfigFileType:protectConfigFileType || "NA",
protectConfigFileName:protectConfigFileName || "NA",
protectConfigComment:protectConfigComment || [],
protectConfigLink:protectConfigLink,
approvalLink:approvalLink,
}
}
// Note: Approval and ConfigApproval events are now handled at the top of this function
else if (event.eventTypeId === EVENT_TYPE.ImagePromotion ){

let artifactPromotionRequestViewLink : string = `${baseURL}${event.payload?.artifactPromotionRequestViewLink}`
Expand Down
1 change: 0 additions & 1 deletion src/destination/destinationHandlers/slackHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ export class SlackService implements Handler {
let parsedEvent = this.mh.parseEvent(event as Event, true);
jsons = Mustache.render(template, parsedEvent);
}

let j = JSON.parse(jsons)
const res = await sdk.send(
{
Expand Down
25 changes: 19 additions & 6 deletions src/notification/service/notificationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,15 @@ class NotificationService {
settings.pipeline_id = event.pipelineId
settings.event_type_id = event.eventTypeId
for (let h of this.handlers) {
// Handle email notifications (SES and SMTP)
if ((h instanceof SESService) || (h instanceof SMTPService)) {
await h.handle(event, templateResults, settings, configsMap, destinationMap)
}
// Handle Slack notifications for approval events
if (h instanceof SlackService) {
this.logger.info("Processing Slack approval notification");
await h.handle(event, templateResults, settings, configsMap, destinationMap)
}
}}
catch(err) {
this.logger.error("err" + err)
Expand Down Expand Up @@ -145,12 +151,19 @@ class NotificationService {
this.logger.info(`Processing notification V2 for event type: ${event.eventTypeId}, correlationId: ${event.correlationId}`);
this.logger.info(`Using ${notificationSettings.length} pre-provided notification settings`);

// Handle approval notifications
if (event.payload.providers && event.payload.providers.length > 0) {
this.logger.info(`Processing approval notification with ${event.payload.providers.length} providers`);
await this.sendApprovalNotification(event);
this.logger.info(`Approval notification sent successfully`);
return new CustomResponse("notification sent", 200);
// Handle approval notifications (eventTypeId 4 = Approval, 5 = ConfigApproval)
// Approval events use event.payload.providers instead of notificationSettings
if (event.eventTypeId === EVENT_TYPE.Approval || event.eventTypeId === EVENT_TYPE.ConfigApproval) {
this.logger.info(`Detected approval event type: ${event.eventTypeId}`);
if (event.payload.providers && event.payload.providers.length > 0) {
this.logger.info(`Processing approval notification with ${event.payload.providers.length} providers`);
await this.sendApprovalNotification(event);
this.logger.info(`Approval notification sent successfully`);
return new CustomResponse("notification sent", 200);
} else {
this.logger.warn(`Approval event received but no providers found in payload for event ${event.correlationId}`);
return new CustomResponse("", 0, new CustomError("no providers found in payload for approval event", 400));
}
}

// Handle scoop notification events
Expand Down
Loading
Loading