chore: unify pipeline with klz-2026 standards and harden registry auth
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 41s
Build & Deploy / 🧪 QA (push) Failing after 1m2s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s

This commit is contained in:
2026-05-12 11:13:28 +02:00
parent e1eff99889
commit 0bcc780230
6 changed files with 181 additions and 189 deletions

View File

@@ -1,89 +1,90 @@
#!/bin/bash
set -e
# Configuration
# --- Configuration ---
REGISTRY_URL="https://git.infra.mintel.me/api/packages/mmintel/npm/"
REGISTRY_DOMAIN="git.infra.mintel.me"
REGISTRY_PATH="/api/packages/mmintel/npm/"
SCOPE="@mintel"
TEST_PACKAGE="@mintel/mail"
echo "🔍 Starting functional registry discovery..."
# Discover token
token=""
token_src=""
# Function to test a specific header
test_auth() {
local label=$1
local header=$2
local url=$3
echo "Trying $label..."
# Capture both status code and body
# Using -k because sometimes internal registries have cert issues in CI
RESPONSE=$(curl -s -k -w "\n%{http_code}" -H "$header" "$url")
STATUS=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | head -n -1)
echo "$label status: $STATUS"
if [ "$STATUS" == "200" ]; then
return 0
else
if [ -n "$BODY" ]; then
echo "Response snippet: $(echo "$BODY" | head -c 200)"
fi
return 1
fi
}
# 1. Discover Tokens
TOKEN=""
TOKEN_SRC=""
if [ -n "$NPM_TOKEN" ]; then
token="$NPM_TOKEN"
token_src="NPM_TOKEN"
TOKEN=$NPM_TOKEN
TOKEN_SRC="NPM_TOKEN"
elif [ -n "$GITEA_PAT" ]; then
token="$GITEA_PAT"
token_src="GITEA_PAT"
TOKEN=$GITEA_PAT
TOKEN_SRC="GITEA_PAT"
elif [ -n "$MINTEL_PRIVATE_TOKEN" ]; then
token="$MINTEL_PRIVATE_TOKEN"
token_src="MINTEL_PRIVATE_TOKEN"
TOKEN=$MINTEL_PRIVATE_TOKEN
TOKEN_SRC="MINTEL_PRIVATE_TOKEN"
fi
if [ -z "$token" ]; then
echo "❌ No registry token found (checked NPM_TOKEN, GITEA_PAT, MINTEL_PRIVATE_TOKEN)"
if [ -z "$TOKEN" ]; then
echo "❌ No token found (NPM_TOKEN, GITEA_PAT, MINTEL_PRIVATE_TOKEN are empty)."
exit 1
fi
echo "Found token from $token_src (length: ${#token})"
echo "Found token from $TOKEN_SRC (length: ${#TOKEN})"
# Functional check against the registry
test_pkg="@mintel/mail"
test_url="${REGISTRY_URL}${test_pkg//\//@}"
echo "Testing registry access for $test_pkg..."
# 2. Functional Test
# In Gitea, the NPM package URL for discovery can be different.
# Try both @mintel/mail and mmintel/mail just in case.
TEST_URL="${REGISTRY_URL}@mintel/mail"
# Try Bearer (standard for _authToken)
echo "Trying Bearer auth..."
status_bearer=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" "$test_url")
echo "Bearer status: $status_bearer"
# Try Basic (standard for _auth or user:pass)
echo "Trying Basic auth..."
status_basic=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Basic $token" "$test_url")
echo "Basic status: $status_basic"
# Try Token (Gitea specific sometimes)
echo "Trying Token auth..."
status_token=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $token" "$test_url")
echo "Token status: $status_token"
best_auth=""
if [ "$status_bearer" -eq 200 ]; then
echo "✅ Bearer auth worked!"
best_auth="authToken"
elif [ "$status_basic" -eq 200 ]; then
echo "✅ Basic auth worked!"
best_auth="auth"
elif [ "$status_token" -eq 200 ]; then
echo "✅ Token auth worked! (Using Bearer for pnpm)"
best_auth="authToken"
else
echo "❌ All auth methods failed."
# Check if it's base64 encoded user:pass and maybe we should decode it then use Bearer?
# Or maybe it's a raw PAT and we should base64 it for Basic?
AUTH_TYPE=""
if test_auth "Bearer" "Authorization: Bearer $TOKEN" "$TEST_URL"; then
AUTH_TYPE="Bearer"
elif test_auth "Basic (raw)" "Authorization: Basic $(echo -n "token:$TOKEN" | base64)" "$TEST_URL"; then
AUTH_TYPE="Basic"
elif test_auth "Basic (token:pass)" "Authorization: Basic $(echo -n "$TOKEN:" | base64)" "$TEST_URL"; then
AUTH_TYPE="Basic"
elif test_auth "Gitea Token" "Authorization: token $TOKEN" "$TEST_URL"; then
AUTH_TYPE="Token"
fi
# Generate .npmrc
if [ -z "$AUTH_TYPE" ]; then
echo "❌ All auth methods failed for $TEST_URL."
echo "Trying alternative URL format..."
TEST_URL_ALT="${REGISTRY_URL}mmintel/mail"
if test_auth "Bearer (Alt)" "Authorization: Bearer $TOKEN" "$TEST_URL_ALT"; then
AUTH_TYPE="Bearer"
fi
fi
# 3. Generate .npmrc
echo "Generating .npmrc..."
{
echo "always-auth=true"
echo "@mintel:registry=https://git.infra.mintel.me/api/packages/mmintel/npm/"
echo "//git.infra.mintel.me/api/packages/mmintel/npm/:_authToken=$TOKEN"
# Fallback without trailing slash
echo "//git.infra.mintel.me/api/packages/mmintel/npm:_authToken=$TOKEN"
} > .npmrc
cat << EOF > .npmrc
$SCOPE:registry=$REGISTRY_URL
always-auth=true
EOF
# If none worked, default to authToken as it's most common
if [ -z "$best_auth" ] || [ "$best_auth" = "authToken" ]; then
echo "//${REGISTRY_DOMAIN}${REGISTRY_PATH}:_authToken=$token" >> .npmrc
echo "//${REGISTRY_DOMAIN}${REGISTRY_PATH%/}:_authToken=$token" >> .npmrc
else
echo "//${REGISTRY_DOMAIN}${REGISTRY_PATH}:_auth=$token" >> .npmrc
echo "//${REGISTRY_DOMAIN}${REGISTRY_PATH%/}:_auth=$token" >> .npmrc
fi
echo "progress=false" >> .npmrc
echo "verify-store-integrity=false" >> .npmrc
echo "✅ .npmrc hardened and generated."
echo "✅ .npmrc hardened and generated (Detected Type: ${AUTH_TYPE:-None})."

74
scripts/smoke.ts Normal file
View File

@@ -0,0 +1,74 @@
import puppeteer from 'puppeteer';
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
async function run() {
console.log(`🚀 Smoke Test: ${targetUrl}`);
const browser = await puppeteer.launch({
headless: true,
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
try {
// 1. Login via Gatekeeper
console.log('🔑 Authenticating...');
await page.goto(targetUrl, { waitUntil: 'networkidle2' });
const isGatekeeper = await page.$('input[name="password"]');
if (isGatekeeper) {
await page.type('input[name="password"]', gatekeeperPassword);
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle2' }),
page.click('button[type="submit"]'),
]);
}
// 2. Check Key Pages
const pages = ['/', '/de/kontakt', '/en/contact', '/de/blog'];
for (const p of pages) {
console.log(`📄 Checking ${p}...`);
const res = await page.goto(`${targetUrl}${p}`, { waitUntil: 'domcontentloaded' });
if (res?.status() !== 200) throw new Error(`Page ${p} returned ${res?.status()}`);
// Check for broken images
const images = await page.$$eval('img', (imgs) => imgs.map((img) => img.src).filter(Boolean));
console.log(` Checking ${images.length} images...`);
const brokenImages = await page.evaluate(async (urls) => {
const broken: string[] = [];
await Promise.all(
urls.map(async (url) => {
if (url.includes('openstreetmap.org') || url.includes('unpkg.com')) return;
try {
const res = await fetch(url, { method: 'HEAD' });
if (res.status >= 400) {
broken.push(`${url} (Status: ${res.status})`);
}
} catch (e) {
broken.push(`${url} (Fetch failed)`);
}
}),
);
return broken;
}, images);
if (brokenImages.length > 0) {
console.warn(`⚠️ Found ${brokenImages.length} broken images on ${p}`);
}
}
console.log('✅ Smoke test passed!');
process.exit(0);
} catch (err: any) {
console.error(`❌ Smoke test failed: ${err.message}`);
process.exit(1);
} finally {
await browser.close();
}
}
run();