feat(logging): erweitertes Workflow-Logging in App und TeamCity-Skripten

- detaillierte Schritt-Logs mit Timestamp in `src/index.js` ergänzt
- Logging für Laufkontext hinzugefügt (Dry-Run, JQL, Zielprojekt/Board, Wiki/SMTP/Output)
- Logging für Jira-Fetch, Report-Erstellung, Ticket-/Wiki-Erstellung, HTML-Write und Mail-Versand erweitert
- TeamCity Wrapper (`run-workflow.sh` und `run-workflow.ps1`) um Kontext-Logs erweitert
- Volume-Mapping in TeamCity-Skripten als explizite Log-Ausgabe ergänzt
This commit is contained in:
2026-03-23 06:59:51 +00:00
parent eed6681329
commit 18f964e6aa
3 changed files with 58 additions and 2 deletions
+48
View File
@@ -15,25 +15,46 @@ function hasFlag(flag) {
return process.argv.includes(flag); return process.argv.includes(flag);
} }
function logStep(message) {
const timestamp = dayjs().format("YYYY-MM-DD HH:mm:ss");
console.log(`[${timestamp}] ${message}`);
}
async function run() { async function run() {
if (hasFlag("--help")) { if (hasFlag("--help")) {
printHelp(); printHelp();
return; return;
} }
logStep("Workflow gestartet.");
const config = getConfig(); const config = getConfig();
const dryRun = config.jira.dryRun || hasFlag("--dry-run"); const dryRun = config.jira.dryRun || hasFlag("--dry-run");
logStep(`Dry-Run: ${dryRun}`);
logStep(
`Jira Quelle: maxResults=${config.jira.sourceMaxResults}, JQL="${config.jira.sourceJql}"`
);
logStep(
`Jira Ziel: projectKey=${config.jira.targetProjectKey || "-"}, boardId=${
config.jira.targetBoardId || "-"
}`
);
logStep(
`Optionen: Wiki=${config.wiki.enabled}, SMTP=${config.smtp.enabled}, Output=${config.outputHtmlPath}`
);
const jira = createJiraClient(config.jira); const jira = createJiraClient(config.jira);
logStep("Lade Jira Tickets...");
const issues = await jira.getRecentIssues( const issues = await jira.getRecentIssues(
config.jira.sourceJql, config.jira.sourceJql,
config.jira.sourceMaxResults config.jira.sourceMaxResults
); );
logStep(`Jira Tickets geladen: ${issues.length}`);
if (issues.length === 0) { if (issues.length === 0) {
throw new Error("Keine Jira Tickets fuer die konfigurierte JQL gefunden."); throw new Error("Keine Jira Tickets fuer die konfigurierte JQL gefunden.");
} }
logStep("Erzeuge Report-Daten...");
const report = buildReport({ const report = buildReport({
issues, issues,
jiraBaseUrl: config.jira.baseUrl, jiraBaseUrl: config.jira.baseUrl,
@@ -42,18 +63,26 @@ async function run() {
xrayReportUrl: config.jira.xrayReportUrl, xrayReportUrl: config.jira.xrayReportUrl,
sonarReportUrl: config.jira.sonarReportUrl, sonarReportUrl: config.jira.sonarReportUrl,
}); });
logStep(
`Report erstellt: ${report.totalIssues} Tickets, ${report.statusCounts.length} Statusgruppen`
);
let targetProjectKey = config.jira.targetProjectKey; let targetProjectKey = config.jira.targetProjectKey;
if (!targetProjectKey && config.jira.targetBoardId) { if (!targetProjectKey && config.jira.targetBoardId) {
logStep(
`Kein targetProjectKey gesetzt, loese Projekt aus Board ${config.jira.targetBoardId} auf...`
);
targetProjectKey = await jira.resolveProjectKeyFromBoardId( targetProjectKey = await jira.resolveProjectKeyFromBoardId(
config.jira.targetBoardId config.jira.targetBoardId
); );
logStep(`Aufgeloester targetProjectKey: ${targetProjectKey}`);
} }
const ticketSummary = `${config.jira.targetSummaryPrefix} ${dayjs().format("YYYY-MM-DD")} (${report.totalIssues} Tickets)`; const ticketSummary = `${config.jira.targetSummaryPrefix} ${dayjs().format("YYYY-MM-DD")} (${report.totalIssues} Tickets)`;
let createdIssue = null; let createdIssue = null;
if (!dryRun) { if (!dryRun) {
logStep("Erstelle Jira Ziel-Ticket...");
createdIssue = await jira.createIssue({ createdIssue = await jira.createIssue({
projectKey: targetProjectKey, projectKey: targetProjectKey,
issueType: config.jira.targetIssueType, issueType: config.jira.targetIssueType,
@@ -62,6 +91,9 @@ async function run() {
labels: config.jira.targetLabels, labels: config.jira.targetLabels,
solutionVersion: config.jira.solutionVersion, solutionVersion: config.jira.solutionVersion,
}); });
logStep(`Jira Ziel-Ticket erstellt: ${createdIssue?.key || "-"}`);
} else {
logStep("Jira Ziel-Ticket uebersprungen (Dry-Run).");
} }
const createdIssueKey = createdIssue?.key || null; const createdIssueKey = createdIssue?.key || null;
@@ -71,6 +103,13 @@ async function run() {
let createdWikiPage = null; let createdWikiPage = null;
if (config.wiki.enabled && !dryRun) { if (config.wiki.enabled && !dryRun) {
logStep(
`Erstelle Wiki-Seite in Space ${config.wiki.spaceKey}${
config.wiki.parentPageId
? ` (Parent ${config.wiki.parentPageId})`
: ""
}...`
);
const confluence = createConfluenceClient(config.wiki); const confluence = createConfluenceClient(config.wiki);
const wikiTitle = `${config.wiki.titlePrefix} ${dayjs().format( const wikiTitle = `${config.wiki.titlePrefix} ${dayjs().format(
"YYYY-MM-DD" "YYYY-MM-DD"
@@ -83,12 +122,19 @@ async function run() {
spaceKey: config.wiki.spaceKey, spaceKey: config.wiki.spaceKey,
parentPageId: config.wiki.parentPageId, parentPageId: config.wiki.parentPageId,
}); });
logStep(`Wiki-Seite erstellt: ${createdWikiPage?.id || "-"}`);
} else if (config.wiki.enabled) {
logStep("Wiki-Seite uebersprungen (Dry-Run).");
} else {
logStep("Wiki-Seite deaktiviert (ATLASSIAN_WIKI_ENABLED=false).");
} }
logStep("Erzeuge HTML-Zusammenfassung...");
const html = buildHtmlSummary(report, createdIssueUrl); const html = buildHtmlSummary(report, createdIssueUrl);
const outputDir = path.dirname(config.outputHtmlPath); const outputDir = path.dirname(config.outputHtmlPath);
fs.mkdirSync(outputDir, { recursive: true }); fs.mkdirSync(outputDir, { recursive: true });
fs.writeFileSync(config.outputHtmlPath, html, "utf8"); fs.writeFileSync(config.outputHtmlPath, html, "utf8");
logStep(`HTML gespeichert: ${config.outputHtmlPath}`);
const mailSubject = `${config.smtp.subjectPrefix} - ${dayjs().format( const mailSubject = `${config.smtp.subjectPrefix} - ${dayjs().format(
"YYYY-MM-DD" "YYYY-MM-DD"
@@ -105,12 +151,14 @@ async function run() {
createdIssueUrl || "nicht erstellt (Dry-Run)" createdIssueUrl || "nicht erstellt (Dry-Run)"
}\nWiki Seite: ${wikiStatusText}`; }\nWiki Seite: ${wikiStatusText}`;
logStep("Starte E-Mail-Versand...");
const mailResult = await sendReportMail({ const mailResult = await sendReportMail({
smtpConfig: config.smtp, smtpConfig: config.smtp,
subject: mailSubject, subject: mailSubject,
html, html,
text: mailText, text: mailText,
}); });
logStep("E-Mail-Versand abgeschlossen.");
console.log("Workflow abgeschlossen."); console.log("Workflow abgeschlossen.");
console.log(`Issues verarbeitet: ${report.totalIssues}`); console.log(`Issues verarbeitet: ${report.totalIssues}`);
+5 -1
View File
@@ -2,11 +2,15 @@ $ErrorActionPreference = "Stop"
$imageTag = if ($env:TEAMCITY_BUILD_NUMBER) { $env:TEAMCITY_BUILD_NUMBER } else { "local" } $imageTag = if ($env:TEAMCITY_BUILD_NUMBER) { $env:TEAMCITY_BUILD_NUMBER } else { "local" }
$imageName = "its-workflow-npm:$imageTag" $imageName = "its-workflow-npm:$imageTag"
$hostReportDir = "${PWD}\report-output"
$containerReportDir = "/opt/app-root/src/report-output"
Write-Host "Builde Docker Image $imageName" Write-Host "Builde Docker Image $imageName"
docker build -t $imageName . docker build -t $imageName .
Write-Host "Starte Workflow Container" Write-Host "Starte Workflow Container"
Write-Host "Kontext: IMAGE_TAG=$imageTag, JIRA_DRY_RUN=$($env:JIRA_DRY_RUN), ATLASSIAN_WIKI_ENABLED=$($env:ATLASSIAN_WIKI_ENABLED), SMTP_ENABLED=$($env:SMTP_ENABLED)"
Write-Host "Volume Mapping: $hostReportDir -> $containerReportDir"
docker run --rm ` docker run --rm `
-e JIRA_BASE_URL ` -e JIRA_BASE_URL `
-e JIRA_USER_EMAIL ` -e JIRA_USER_EMAIL `
@@ -39,5 +43,5 @@ docker run --rm `
-e SMTP_FROM ` -e SMTP_FROM `
-e SMTP_TO ` -e SMTP_TO `
-e SMTP_SUBJECT_PREFIX ` -e SMTP_SUBJECT_PREFIX `
-v "${PWD}\report-output:/opt/app-root/src/report-output" ` -v "${hostReportDir}:$containerReportDir" `
$imageName $imageName
+5 -1
View File
@@ -3,11 +3,15 @@ set -eu
IMAGE_TAG="${TEAMCITY_BUILD_NUMBER:-local}" IMAGE_TAG="${TEAMCITY_BUILD_NUMBER:-local}"
IMAGE_NAME="its-workflow-npm:${IMAGE_TAG}" IMAGE_NAME="its-workflow-npm:${IMAGE_TAG}"
HOST_REPORT_DIR="$(pwd)/report-output"
CONTAINER_REPORT_DIR="/opt/app-root/src/report-output"
echo "Builde Docker Image ${IMAGE_NAME}" echo "Builde Docker Image ${IMAGE_NAME}"
docker build -t "${IMAGE_NAME}" . docker build -t "${IMAGE_NAME}" .
echo "Starte Workflow Container" echo "Starte Workflow Container"
echo "Kontext: IMAGE_TAG=${IMAGE_TAG}, JIRA_DRY_RUN=${JIRA_DRY_RUN:-false}, ATLASSIAN_WIKI_ENABLED=${ATLASSIAN_WIKI_ENABLED:-false}, SMTP_ENABLED=${SMTP_ENABLED:-false}"
echo "Volume Mapping: ${HOST_REPORT_DIR} -> ${CONTAINER_REPORT_DIR}"
docker run --rm \ docker run --rm \
-e JIRA_BASE_URL \ -e JIRA_BASE_URL \
-e JIRA_USER_EMAIL \ -e JIRA_USER_EMAIL \
@@ -40,5 +44,5 @@ docker run --rm \
-e SMTP_FROM \ -e SMTP_FROM \
-e SMTP_TO \ -e SMTP_TO \
-e SMTP_SUBJECT_PREFIX \ -e SMTP_SUBJECT_PREFIX \
-v "$(pwd)/report-output:/opt/app-root/src/report-output" \ -v "${HOST_REPORT_DIR}:${CONTAINER_REPORT_DIR}" \
"${IMAGE_NAME}" "${IMAGE_NAME}"