This is an automated email from the ASF dual-hosted git repository.

young pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-website.git


The following commit(s) were added to refs/heads/master by this push:
     new 49bc0a78617 chore: show failed logs in ci (#1968)
49bc0a78617 is described below

commit 49bc0a786177ca1eb86bd9b697e9290c6ae7ac76
Author: YYYoung <[email protected]>
AuthorDate: Wed Oct 29 14:39:37 2025 +0800

    chore: show failed logs in ci (#1968)
---
 .github/workflows/deploy.yml |  26 ++++++++
 scripts/generate-website.js  | 137 ++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 154 insertions(+), 9 deletions(-)

diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 2cb5edb2f50..ba4e2b39827 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -108,6 +108,32 @@ jobs:
         run: |
           yarn build
 
+      - name: Show build logs on failure
+        if: failure()
+        run: |
+          LOG_DIR="/tmp/apisix-website-build-logs"
+          if [ -d "$LOG_DIR" ]; then
+            echo "Build logs:"
+            for logfile in "$LOG_DIR"/*.log; do
+              if [ -f "$logfile" ]; then
+                echo ""
+                echo "========== $(basename $logfile .log) =========="
+                cat "$logfile"
+              fi
+            done
+          else
+            echo "No log directory found at $LOG_DIR"
+          fi
+
+      - name: Upload build logs as artifact
+        if: failure()
+        uses: actions/upload-artifact@v4
+        with:
+          name: build-logs
+          path: /tmp/apisix-website-build-logs/
+          retention-days: 7
+          if-no-files-found: warn
+
       - name: Update sitemap.xml
         run: |
           yarn update-sitemap && git status
diff --git a/scripts/generate-website.js b/scripts/generate-website.js
index 0efa4371ccc..dee5122a0b2 100644
--- a/scripts/generate-website.js
+++ b/scripts/generate-website.js
@@ -1,8 +1,43 @@
 const Listr = require('listr');
 const util = require('util');
+const fs = require('fs');
+const path = require('path');
 const { chdir } = require('node:process');
 const exec = util.promisify(require('node:child_process').exec);
 
+// Detect CI environment
+const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 
'true';
+
+// Control whether to show logs locally (default: true for convenience)
+// Set SHOW_BUILD_LOGS=false to disable local log output
+const showLogsLocally = process.env.SHOW_BUILD_LOGS !== 'false';
+
+// Log directory for build outputs
+const logDir = '/tmp/apisix-website-build-logs';
+
+// Ensure log directory exists
+if (!fs.existsSync(logDir)) {
+  fs.mkdirSync(logDir, { recursive: true });
+}
+
+// Helper function to print logs from files
+function printLogFiles(logFiles, showAll = false) {
+  /* eslint-disable no-console */
+  logFiles.forEach((file) => {
+    // Only print failed logs, or all logs if showAll is true
+    if (showAll || file.failed) {
+      console.error(`\n========== ${file.step} ==========`);
+      try {
+        const content = fs.readFileSync(file.path, 'utf8');
+        console.error(content);
+      } catch (err) {
+        console.error(`Error reading log file: ${err.message}`);
+      }
+    }
+  });
+  /* eslint-enable no-console */
+}
+
 const tasks = new Listr([
   {
     title: `Change working dir`,
@@ -17,34 +52,118 @@ const tasks = new Listr([
   },
   {
     title: `Build website's all parts`,
-    task: () => Promise.allSettled([
-      exec('yarn run build:blog:zh', { stdio: 'ignore' }),
-      exec('yarn run build:blog:en', { stdio: 'ignore' }),
-      exec('yarn run build:doc', { stdio: 'ignore' }),
-      exec('yarn run build:website', { stdio: 'ignore' }),
-    ]),
+    task: async (ctx) => {
+      const buildSteps = [
+        { name: 'blog-zh', cmd: 'yarn run build:blog:zh' },
+        { name: 'blog-en', cmd: 'yarn run build:blog:en' },
+        { name: 'doc', cmd: 'yarn run build:doc' },
+        { name: 'website', cmd: 'yarn run build:website' },
+      ];
+
+      // Execute all builds in parallel, capturing output
+      const results = await Promise.allSettled(
+        buildSteps.map((step) => exec(step.cmd)),
+      );
+
+      // Check for failures and save logs to files
+      const failures = [];
+      const logFiles = [];
+
+      results.forEach((result, index) => {
+        const step = buildSteps[index];
+        const logFilePath = path.join(logDir, `${step.name}.log`);
+        let logContent = '';
+
+        if (result.status === 'fulfilled') {
+          // Write raw stdout and stderr without extra formatting
+          if (result.value.stdout) {
+            logContent += result.value.stdout;
+          }
+          if (result.value.stderr) {
+            if (logContent) logContent += '\n';
+            logContent += result.value.stderr;
+          }
+        } else {
+          failures.push(step.name);
+          // Write raw stdout and stderr for failed builds
+          if (result.reason.stdout) {
+            logContent += result.reason.stdout;
+          }
+          if (result.reason.stderr) {
+            if (logContent) logContent += '\n';
+            logContent += result.reason.stderr;
+          }
+          // Add error message at the end if available
+          if (result.reason.message && 
!result.reason.stderr?.includes(result.reason.message)) {
+            if (logContent) logContent += '\n';
+            logContent += `Error: ${result.reason.message}\n`;
+          }
+        }
+
+        // Write to log file
+        fs.writeFileSync(logFilePath, logContent);
+        logFiles.push({ path: logFilePath, step: step.name, failed: 
result.status === 'rejected' });
+      });
+
+      // Store in context for later use
+      ctx.buildLogFiles = logFiles;
+      ctx.buildFailures = failures;
+
+      // If any build failed, throw error
+      if (failures.length > 0) {
+        throw new Error(`Build failed for: ${failures.join(', ')}`);
+      }
+    },
   },
   {
     title: `Copy website's all parts to website's root`,
     task: () => Promise.allSettled([
       exec(
         'cp ./.asf.yaml ./.htaccess ./blog/en/build/blog 
./blog/en/build/assets ./doc/build/assets ./doc/build/docs ./website/build/ -r',
-        { stdio: 'ignore' },
       ),
       exec(
         'cp ./blog/zh/build/blog ./blog/zh/build/assets ./doc/build/zh/docs 
./doc/build/zh/assets ./website/build/zh/ -r',
-        { stdio: 'ignore' },
       ),
     ]),
   },
-]);
+], {
+  renderer: isCI ? 'verbose' : 'default',
+});
 
 tasks
   .run()
   .then(() => {
+    /* eslint-disable-next-line no-console */
     console.log(`[Finish] Generate website`);
+
+    // In local environment, show log location after successful build
+    if (!isCI && showLogsLocally && fs.existsSync(logDir)) {
+      /* eslint-disable-next-line no-console */
+      console.log(`\nBuild logs: ${logDir}/*.log`);
+    }
   })
   .catch((err) => {
+    /* eslint-disable-next-line no-console */
     console.error(err);
+
+    // Print information about log files and their content
+    if (err.context && err.context.buildLogFiles) {
+      /* eslint-disable no-console */
+      console.error(`\nBuild logs saved to: ${logDir}`);
+      err.context.buildLogFiles.forEach((file) => {
+        const status = file.failed ? '❌' : '✓';
+        console.error(`  ${status} ${file.step}.log`);
+      });
+      /* eslint-enable no-console */
+
+      // In local environment, automatically print failed logs
+      if (!isCI && showLogsLocally) {
+        printLogFiles(err.context.buildLogFiles, false);
+      } else if (!isCI) {
+        /* eslint-disable-next-line no-console */
+        console.error(`\nView logs: cat ${logDir}/*.log`);
+      }
+    }
+
     process.exit(1);
   });

Reply via email to