All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 3s
Build & Deploy / 🧪 QA (push) Successful in 2m1s
Build & Deploy / 🏗️ Build (push) Successful in 2m38s
Build & Deploy / 🚀 Deploy (push) Successful in 12s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 3m23s
Build & Deploy / 🔔 Notify (push) Successful in 1s
137 lines
4.3 KiB
TypeScript
137 lines
4.3 KiB
TypeScript
import axios from "axios";
|
||
import dns from "dns";
|
||
import { promisify } from "util";
|
||
import url from "url";
|
||
|
||
const resolve4 = promisify(dns.resolve4);
|
||
|
||
// This script verifies that external logging and analytics APIs are reachable
|
||
// from the deployment environment (which could be behind corporate firewalls or VPNs).
|
||
|
||
const umamiEndpoint =
|
||
process.env.UMAMI_API_ENDPOINT || "https://analytics.infra.mintel.me";
|
||
const sentryDsn = process.env.SENTRY_DSN || "";
|
||
|
||
async function checkUmami() {
|
||
console.log(`\n🔍 Checking Umami Analytics API Availability...`);
|
||
console.log(` Endpoint: ${umamiEndpoint}`);
|
||
|
||
try {
|
||
// Umami usually exposes a /api/heartbeat or /api/health if we know the route.
|
||
// Trying root or /api/auth/verify (which will give 401 but proves routing works).
|
||
// A simple GET to the configured endpoint should return a 200 or 401, not a 5xx/timeout.
|
||
const response = await axios.get(
|
||
`${umamiEndpoint.replace(/\/$/, "")}/api/health`,
|
||
{
|
||
timeout: 5000,
|
||
validateStatus: () => true, // Accept any status, we just want to know it's reachable and not 5xx
|
||
},
|
||
);
|
||
|
||
// As long as it's not a 502/503/504 Bad Gateway/Timeout, the service is "up" from our perspective
|
||
if (response.status >= 500) {
|
||
throw new Error(
|
||
`Umami API responded with server error HTTP ${response.status}`,
|
||
);
|
||
}
|
||
|
||
console.log(` ✅ Umami Analytics is reachable (HTTP ${response.status})`);
|
||
return true;
|
||
} catch (error) {
|
||
const err = error as Error;
|
||
// If /api/health fails completely, maybe try a DNS check as a fallback
|
||
try {
|
||
console.warn(
|
||
` ⚠️ HTTP check failed, falling back to DNS resolution...`,
|
||
);
|
||
const umamiHost = new url.URL(umamiEndpoint).hostname;
|
||
await resolve4(umamiHost);
|
||
console.log(
|
||
` ✅ Umami Analytics DNS resolved successfully (${umamiHost})`,
|
||
);
|
||
return true;
|
||
} catch (error) {
|
||
const dnsErr = error as Error;
|
||
console.error(
|
||
` ❌ CRITICAL: Umami Analytics is completely unreachable! ${err.message} | DNS: ${dnsErr.message}`,
|
||
);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
async function checkSentry() {
|
||
console.log(`\n🔍 Checking Glitchtip/Sentry Error Tracking Availability...`);
|
||
|
||
if (!sentryDsn) {
|
||
console.log(` ℹ️ No SENTRY_DSN provided in environment. Skipping.`);
|
||
return true;
|
||
}
|
||
|
||
try {
|
||
const parsedDsn = new url.URL(sentryDsn);
|
||
const host = parsedDsn.hostname;
|
||
console.log(` Host: ${host}`);
|
||
|
||
// We do a DNS lookup to ensure the runner can actually resolve the tracking server
|
||
const addresses = await resolve4(host);
|
||
|
||
if (addresses && addresses.length > 0) {
|
||
console.log(` ✅ Glitchtip/Sentry domain resolved: ${addresses[0]}`);
|
||
|
||
// Optional: Quick TCP/HTTP check to the host root (Glitchtip usually runs on 80/443 root)
|
||
try {
|
||
const proto = parsedDsn.protocol || "https:";
|
||
await axios.get(`${proto}//${host}/api/0/`, {
|
||
timeout: 5000,
|
||
validateStatus: () => true,
|
||
});
|
||
console.log(` ✅ Glitchtip/Sentry API root responds to HTTP.`);
|
||
} catch {
|
||
console.log(
|
||
` ⚠️ Glitchtip/Sentry HTTP ping failed or timed out, but DNS is valid. Proceeding.`,
|
||
);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
throw new Error("No IP addresses found for DSN host");
|
||
} catch (error) {
|
||
const err = error as Error;
|
||
console.error(
|
||
` ❌ CRITICAL: Glitchtip/Sentry DSN is invalid or hostname is unresolvable! ${err.message}`,
|
||
);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
console.log("🚀 Starting External API Connectivity Smoke Test...");
|
||
|
||
let hasErrors = false;
|
||
|
||
const umamiOk = await checkUmami();
|
||
if (!umamiOk) hasErrors = true;
|
||
|
||
const sentryOk = await checkSentry();
|
||
if (!sentryOk) hasErrors = true;
|
||
|
||
if (hasErrors) {
|
||
console.error(
|
||
`\n🚨 POST-DEPLOY CHECK FAILED: One or more critical external APIs are unreachable.`,
|
||
);
|
||
console.error(
|
||
` This might mean the deployment environment lacks outbound internet access, `,
|
||
);
|
||
console.error(
|
||
` DNS is misconfigured, or the upstream services are down.`,
|
||
);
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(`\n🎉 SUCCESS: All required external APIs are reachable!`);
|
||
process.exit(0);
|
||
}
|
||
|
||
main();
|