169 lines
4.7 KiB
JavaScript
169 lines
4.7 KiB
JavaScript
const Sentry = require("@sentry/node");
|
|
|
|
const {
|
|
SERVICE = "unknown-service",
|
|
COMPONENT = "deployment",
|
|
SENTRY_DSN = "",
|
|
AUTH = "",
|
|
DEPLOY_IMAGE = "git.makecodes.dev/viaartistica/crm-backend",
|
|
DEPLOY_VERSION = "qa",
|
|
DEPLOY_WEBHOOK_URL = "https://n8n.ops.makecodes.dev/webhook/viaartistica-crm-qa-deployments",
|
|
DEPLOY_TIMEOUT_MS = "3600000",
|
|
} = process.env;
|
|
|
|
const fetchFn = global.fetch;
|
|
const { AbortSignal } = global;
|
|
|
|
if (typeof fetchFn !== "function" || !AbortSignal?.timeout) {
|
|
throw new Error("Required web APIs (fetch, AbortSignal.timeout) are unavailable.");
|
|
}
|
|
|
|
Sentry.init({
|
|
dsn: SENTRY_DSN,
|
|
sendDefaultPii: false,
|
|
enableLogs: true,
|
|
environment: "ci",
|
|
_experiments: {
|
|
enableMetrics: true,
|
|
},
|
|
});
|
|
|
|
const logger = Sentry.logger;
|
|
const baseMetricAttributes = {
|
|
environment: "ci",
|
|
service: SERVICE,
|
|
component: COMPONENT,
|
|
};
|
|
|
|
const METRIC_NAMES = {
|
|
WEBHOOK_ATTEMPT: "deploy.webhook_attempt",
|
|
WEBHOOK_FAILURE: "deploy.webhook_failure",
|
|
WEBHOOK_SUCCESS: "deploy.webhook_success",
|
|
WEBHOOK_DURATION: "deploy.webhook_duration_ms",
|
|
RUN_STARTED: "deploy.run_started",
|
|
RUN_SUCCEEDED: "deploy.run_succeeded",
|
|
RUN_FAILED: "deploy.run_failed",
|
|
RUN_DURATION: "deploy.run_duration_ms",
|
|
};
|
|
|
|
const metrics = {
|
|
count: (name, value = 1, attributes = {}) => {
|
|
Sentry.metrics?.count?.(name, value, {
|
|
attributes: { ...baseMetricAttributes, ...attributes },
|
|
});
|
|
},
|
|
distribution: (name, value, { unit, attributes = {} } = {}) => {
|
|
Sentry.metrics?.distribution?.(name, value, {
|
|
...(unit && { unit }),
|
|
attributes: { ...baseMetricAttributes, ...attributes },
|
|
});
|
|
},
|
|
};
|
|
|
|
const requireEnv = (value, name) => {
|
|
if (!value) {
|
|
logger.error(logger.fmt`Missing required environment variable '${name}'.`);
|
|
throw new Error(`Missing required environment variable '${name}'.`);
|
|
}
|
|
return value;
|
|
};
|
|
|
|
const triggerDeployment = async () => {
|
|
requireEnv(AUTH, "AUTH");
|
|
requireEnv(DEPLOY_WEBHOOK_URL, "DEPLOY_WEBHOOK_URL");
|
|
|
|
const payload = {
|
|
service: SERVICE,
|
|
image: DEPLOY_IMAGE,
|
|
version: DEPLOY_VERSION,
|
|
component: COMPONENT,
|
|
};
|
|
|
|
const signal = AbortSignal.timeout(Number(DEPLOY_TIMEOUT_MS) || 3600000);
|
|
|
|
logger.info(
|
|
logger.fmt`Starting deployment for '${SERVICE}' using version '${DEPLOY_VERSION}'.`,
|
|
payload
|
|
);
|
|
|
|
metrics.count(METRIC_NAMES.WEBHOOK_ATTEMPT, 1);
|
|
|
|
const webhookStart = Date.now();
|
|
const response = await fetchFn(DEPLOY_WEBHOOK_URL, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Basic ${AUTH}`,
|
|
},
|
|
body: JSON.stringify(payload),
|
|
signal,
|
|
});
|
|
|
|
metrics.distribution(METRIC_NAMES.WEBHOOK_DURATION, Date.now() - webhookStart, {
|
|
unit: "millisecond",
|
|
attributes: { status: response.status },
|
|
});
|
|
|
|
const bodyText = await response.text();
|
|
|
|
logger.debug("Deployment webhook response body", {
|
|
service: SERVICE,
|
|
status: response.status,
|
|
body: bodyText,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
metrics.count(METRIC_NAMES.WEBHOOK_FAILURE, 1, { status: response.status });
|
|
logger.error(
|
|
logger.fmt`Deployment failed for '${SERVICE}' with status '${response.status}'.`,
|
|
{ body: bodyText }
|
|
);
|
|
throw new Error(
|
|
`Deployment failed for '${SERVICE}' with status '${response.status}'.`
|
|
);
|
|
}
|
|
|
|
metrics.count(METRIC_NAMES.WEBHOOK_SUCCESS, 1, { status: response.status });
|
|
logger.info(
|
|
logger.fmt`Deployment succeeded for '${SERVICE}' with status '${response.status}'.`
|
|
);
|
|
};
|
|
|
|
async function main() {
|
|
if (!SENTRY_DSN) {
|
|
throw new Error("Missing required environment variable 'SENTRY_DSN'.");
|
|
}
|
|
|
|
metrics.count(METRIC_NAMES.RUN_STARTED, 1);
|
|
const startedAt = Date.now();
|
|
let success = false;
|
|
|
|
try {
|
|
await triggerDeployment();
|
|
success = true;
|
|
|
|
metrics.count(METRIC_NAMES.RUN_SUCCEEDED, 1);
|
|
logger.info(logger.fmt`Deployment script completed for '${SERVICE}'.`);
|
|
} catch (error) {
|
|
metrics.count(METRIC_NAMES.RUN_FAILED, 1, {
|
|
reason: error?.name || "Error",
|
|
});
|
|
logger.error("Failed to run deployment script", {
|
|
error: error.stack || error,
|
|
});
|
|
} finally {
|
|
metrics.distribution(METRIC_NAMES.RUN_DURATION, Date.now() - startedAt, {
|
|
unit: "millisecond",
|
|
attributes: { status: success ? "success" : "failure" },
|
|
});
|
|
|
|
await Sentry.flush(5000);
|
|
|
|
if (!success) {
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
main();
|