;;; build-runner.el --- Build orchestration -*- lexical-binding: t; -*- ;;; Code: (require 'ox-publish) (defun z/wipe-build-dirs () "Delete output/ and .org-timestamps/ together." (dolist (dir (list z/output-root (site-path ".org-timestamps/"))) (if (file-directory-p dir) (progn (z/with-timing (format "delete %s" (file-name-nondirectory (directory-file-name dir))) (delete-directory dir t)) (z/log 'done "Deleted %s" dir)) (z/log 'info "Already absent: %s" dir)))) (defun z/output-integrity-ok-p () "Return t if critical output paths all exist." (let ((missing '())) (unless (file-directory-p (output-path "assets/")) (push "output/assets/" missing)) (unless (or (file-exists-p (output-path "index.html")) (file-exists-p (output-path "sitemap.html"))) (push "output/index.html (or sitemap.html)" missing)) (dolist (m missing) (z/log 'warn "Integrity check: missing %s" m)) (null missing))) (defun z/prepare-output-cache () "Keep org-publish output and timestamp cache in sync." (z/log-separator "Pre-build") (z/log 'info "Site source root : %s" z/site-root) (z/log 'info "Site output root : %s" z/output-root) (cond ((getenv "SITE_CLEAN") (z/log 'warn "SITE_CLEAN=1 - wiping output/ and .org-timestamps/") (z/wipe-build-dirs) (setenv "SITE_FORCE" "1")) ((not (z/output-integrity-ok-p)) (z/log 'warn "Output integrity check failed - timestamp cache is stale") (z/log 'warn "Clearing .org-timestamps/ and forcing full republish") (let ((ts-dir (site-path ".org-timestamps/"))) (when (file-directory-p ts-dir) (z/with-timing "delete .org-timestamps" (delete-directory ts-dir t)) (z/log 'done "Deleted .org-timestamps/"))) (setenv "SITE_FORCE" "1")) (t (z/log 'info "Incremental build - output/ looks complete")))) (defun z/generate-build-inputs () "Generate source-side data files required before publishing." (z/log-separator "Generated inputs") (z/with-timing "tag page generation" (condition-case err (let ((n (z/write-tag-pages))) (z/log 'done "Tag pages written%s" (if (numberp n) (format " (%d tags)" n) ""))) (error (z/log 'error "Tag generation failed: %s" (error-message-string err)) (error "Aborting build: tag generation error")))) (z/with-timing "comment page manifest" (condition-case err (z/generate-comment-pages-json) (error (z/log 'error "Comment page manifest failed: %s" (error-message-string err)) (error "Aborting build: comment page manifest error"))))) (defun z/publish-site () "Publish the configured site." (z/log-separator "Publishing") (if (getenv "SITE_DRY") (z/log 'warn "SITE_DRY=1 - skipping org-publish-all (dry run)") (let ((force (not (not (getenv "SITE_FORCE"))))) (when force (z/log 'info "SITE_FORCE=1 - forcing republish of all files")) (condition-case err (progn (z/with-timing "org-publish-all" (org-publish-all force)) (z/with-timing "asset sync" (z/sync-assets-to-output))) (error (z/log 'error "org-publish-all failed: %s" (error-message-string err)) (error "Build failed")))))) (defun z/log-build-summary () "Log a summary of output artifacts." (z/log-separator "Build summary") (let* ((elapsed (- (float-time) z/build-start-time)) (out-dir z/output-root) (html-files (when (file-directory-p out-dir) (length (directory-files-recursively out-dir "\\.html\\'")))) (css-files (when (file-directory-p out-dir) (length (directory-files-recursively out-dir "\\.css\\'")))) (js-files (when (file-directory-p out-dir) (length (directory-files-recursively out-dir "\\.js\\'")))) (tag-files (let ((td (output-path "tags/"))) (when (file-directory-p td) (length (directory-files td nil "\\.html\\'")))))) (z/log 'metric "Total elapsed : %.2fs" elapsed) (z/log 'metric "HTML files output: %d" (or html-files 0)) (z/log 'metric "CSS files output: %d" (or css-files 0)) (z/log 'metric "JS files output: %d" (or js-files 0)) (z/log 'metric "Tag pages output: %d" (or tag-files 0)) (z/log 'done "Build complete"))) (defun z/run-build () "Run the full site build." (z/prepare-output-cache) (z/generate-build-inputs) (z/publish-site) (z/log-build-summary)) (provide 'build-runner) ;;; build-runner.el ends here