เวลาอ่าน: 10 minutes

คุณเคยเจอช่วงเวลาที่เว็บไซต์ของคุณบนเครื่องโลคอลดูไม่ดีเอาเสียเลย แต่พอดีพลอยขึ้นออนไลน์แล้วทุกอย่างกลับสมบูรณ์แบบไหม? นั่นคือสิ่งที่เกิดขึ้นกับปลั๊กอิน Gradle JBake ของฉัน งาน`serve`แสดงปุ่มสี่เหลี่ยม ข้อความสีเข้มบนพื้นหลังสีดำ และ CSS ที่หายไปอย่างน่าประหลาด แต่กระนั้น`publishSite`สร้างเว็บไซต์เดียวกันได้อย่างไร้ที่ติ ตัวการไม่ใช่ CSS ไม่ใช่เบราว์เซอร์ และไม่ใช่แคช แต่เป็นโค้ด Kotlin เพียงบรรทัดเดียว — และนิสัยที่ไม่ดีในการจัดการอาร์กิวเมนต์บนบรรทัดคำสั่ง

ฉาก: สกรีนช็อตสี่ภาพ, เรนเดอร์สองแบบ

มันเกิดขึ้นหลังระบบล่ม ฉันได้จับภาพหน้าจอไว้สี่ภาพเพื่อเปรียบเทียบการแสดงผลในเครื่อง (./gradlew serve`บน`localhost:8820) ด้วยการแสดงผลแบบออนไลน์ (`publishSite`บน GitHub Pages) สองภาพหน้าจอต่อหน้า : ด้านบนและด้านล่าง

การเรนเดอร์ภายในเครื่อง

กลับขึ้นด้านบน— ปุ่ม Project และ Template เป็นเล็ก, เป็นรูปสี่เหลี่ยมผืนผ้า, ด้วยสีมาตรฐาน (สีน้ำเงินและสีเขียว) การ์ด Start Writing มีพื้นหลังสีดำเรียบ, โดยไม่มีกรอบ

Rendu local - haut de page

ส่วนท้ายของหน้า— คำบรรยายใต้หัวข้อ "บทความและแหล่งข้อมูลล่าสุด" คือแทบจะอ่านไม่ออก, มืดเกินไปบนพื้นหลังสีดำ วันที่ใต้รูปภาพบล็อก ("17 October 2013" ฯลฯ) เป็นที่ไม่อยู่No text was provided to translate.

Rendu local - bas de page

แสดงผลออนไลน์

กลับขึ้นด้านบน— ปุ่มเดียวกันนั้นเป็นใหญ่, รูปไข่, สีเขียวนีออนฉูดฉาด, ข้อความเป็นตัวพิมพ์ใหญ่ การ์ดมีกรอบสีขาวมุมมนพร้อมเงาตกกระทบ.

Rendu en ligne - haut de page

ส่วนท้ายของหน้า— คำบรรยายคืออ่านได้ชัดเจน. วันที่คือที่มองเห็นได้. การ์ดของบทความมีมุมโค้งและชื่อเรื่องคือตัวหนา.

Rendu en ligne - bas de page

สิ่งแรกที่นึกถึงคือธีม ฉันใช้ระบบธีมแบบไดนามิก (สว่าง มืด คอนทราสต์สูง) โดยอิงจาก`localStorage`. บางที local store อาจทำให้ธีม high-contrast ถูกเรียกใช้งานหรือเปล่า? ไม่ ฉันเขียนไว้แล้วhttps://cheroliv.com/blog/2025/0098_preloading_css_variable_post.html[บทความเต็มเกี่ยวกับการโหลดธีมล่วงหน้า], ฉันรู้ว่ามันทำงานอย่างไร และอีกอย่าง แม้ในโหมด high-contrast โครงสร้าง CSS (ปุ่มวงรี เงา) ก็ควรจะอยู่ตรงนั้น แต่มันกลับไม่อยู่

ความคิดที่สอง: แคชของเบราว์เซอร์ ไม่ ฉันได้ทดสอบด้วยโปรไฟล์ที่สะอาดแล้ว diff ยังคงอยู่

รีเฟล็กซ์ที่สาม:`serve`ไม่ได้ให้บริการไฟล์เดียวกันกับ`publishSite`.

การวินิจฉัย: serve ไม่ได้ชี้ไปยังโฟลเดอร์ที่ถูกต้อง

ปลั๊กอิน Gradle ของฉัน`bakery`มีสองภารกิจหลัก:

  • bake: สร้างเว็บไซต์แบบสแตติกใน`build/bake/`

  • serve: เริ่มเซิร์ฟเวอร์เว็บภายในเครื่อง

  • publishSite: หน่อ`build/bake/`ไปยัง GitHub Pages

ความแตกต่างนั้นสามารถมาจากเพียง`serve`. Si publishSite`เผยแพร่เนื้อหาที่ถูกต้อง นั่นก็คือว่า`build/bake/`ดี. ถ้า`serve`แสดงอย่างอื่น นั่นคือมันไม่ได้ถูกใช้งาน`build/bake/.

มาดูโค้ดกัน ใน`SiteManager.kt`, งาน`serve`ถูกกำหนดไว้ดังนี้:

SiteManager.kt — La tâche serve (version buggy)
tasks.register("serve", JavaExec::class.java) { task ->
    task.apply {
        mainClass.set("org.jbake.launcher.Main")
        classpath = jbakeRuntime
        environment("GEM_PATH", jbakeRuntime.asPath)
        jvmArgs(/* ... */)
        args = listOf(
            "-b", file(site.bake.srcPath).absolutePath,
            "-s", layout.buildDirectory.get()
                .asFile.resolve(site.bake.destDirPath)
                .absolutePath
        )
    }
}

org.jbake.launcher.Main`เป็นจุดเริ่มต้นของ CLI JBake 2.7.0 แนวคิดคือ: ส่ง-b`สำหรับโฟลเดอร์ต้นทางและ`-s`สำหรับโฟลเดอร์ปลายทาง จากนั้นเปิดโหมดเซิร์ฟเวอร์ แต่ทว่า…​

สาเหตุราก: JBake CLI ไม่ทำงานแบบนั้น

JBake 2.7.0 รอ…​อาร์กิวเมนต์ตามตำแหน่งสำหรับ source และ destination แล้วตามด้วย flags ที่ไม่บังคับ ซิกเนเจอร์คือ :

jbake <source> <destination> [options]

แต่ในโค้ดของฉัน ฉันเขียนว่า:

args = listOf("-b", "/path/to/site", "-s", "/path/to/build/bake")

JBake ตีความสิ่งนี้ว่า:

  1. -b→ พาร์สแฟล็ก bake (บูลีน) ซึ่งถูกใช้ไปแล้ว

  2. /path/to/site→ กลายเป็นอาร์กิวเมนต์ตามตำแหน่งลำดับที่ 1 (แหล่งที่มา)

  3. -s→ แยกวิเคราะห์แฟล็ก "serve" (บูลีน), เซิร์ฟเวอร์เริ่มทำงานแล้วทันที

  4. /path/to/build/bake→ กลายเป็นอาร์กิวเมนต์กำพร้า ซึ่งถูกละเลยหรือตีความผิด

ผลลัพธ์: JBake ใช้เวลา`/path/to/site`เป็นแหล่งที่มา,ไม่ได้คำนึงถึงจุดหมายปลายทางที่จัดหาให้ (build/bake), และใช้ไดเรกทอรีเริ่มต้นของตน (มักจะ`./output`หรือสำเนาชั่วคราว) ไฟล์`css/styles.css`ดังนั้นบิลด์จึงถูกเขียนทับหรือถูกละเลย และเป็น CSS เริ่มต้นเก่า (ที่ไม่มีตัวแปร custom ไม่มีมุมโค้ง ไม่มีเงา) ที่ถูกเสิร์ฟ

เมื่อเปรียบเทียบแล้ว`bake` et publishSite`ใช้ปลั๊กอิน Gradle JBake อย่างเป็นทางการ(`jbake-gradle-plugin) ที่เขียนอย่างเรียบร้อยใน`build/bake/`ผ่าน Gradle API พวกเขาไม่ได้ผ่าน CLI

วิธีแก้: อาร์กิวเมนต์แบบตำแหน่ง ไม่ใช่แฟล็ก

การแก้ไขเป็นเรื่องง่ายดาย — เมื่อรู้แล้ว ก็เพียงส่ง source และ destination เป็นอาร์กิวเมนต์แบบตำแหน่ง แล้ว`-s`สุดท้ายนี้:

SiteManager.kt — La tâche serve (version corrigée)
args = listOf(
    file(site.bake.srcPath).absolutePath,
    layout.buildDirectory.get()
        .asFile.resolve(site.bake.destDirPath)
        .absolutePath,
    "-s"
)

แค่นั้นเอง เปลี่ยนแค่สามบรรทัด บั๊กก็แก้แล้ว

หลังจากเผยแพร่ปลั๊กอินในเครื่องแล้ว (publishToMavenLocal), un `./gradlew serve`เรียกใช้ JBake ด้วยไวยากรณ์ที่ถูกต้อง:

jbake /home/user/project/site /home/user/project/build/bake -s

และครั้งนี้ เซิร์ฟเวอร์ Jetty ที่ฝังอยู่ใน JBake ให้บริการเนื้อหาของ`build/bake/`— เหมือนกับสิ่งที่เผยแพร่ทางออนไลน์

@startuml
!define RECTANGLE class

package "AVANT (bug)" {
  RECTANGLE JBakeCLI1 {
    + args = ["-b", "site", "-s", "build/bake"]
  }

  RECTANGLE Source1 {
    + site/
  }

  RECTANGLE Dest1 {
    + ??? (dossier temporaire)
  }

  RECTANGLE Jetty1 {
    + sert mauvais fichiers
  }

  JBakeCLI1 --> Source1 : lit
  JBakeCLI1 -[#red]x Dest1 : destination ignorée
  Jetty1 --> Dest1 : sert
}

package "APRÈS (fix)" {
  RECTANGLE JBakeCLI2 {
    + args = ["site", "build/bake", "-s"]
  }

  RECTANGLE Source2 {
    + site/
  }

  RECTANGLE Dest2 {
    + build/bake/
  }

  RECTANGLE Jetty2 {
    + sert les bons fichiers
  }

  JBakeCLI2 --> Source2 : lit
  JBakeCLI2 --> Dest2 : écrit dans
  Jetty2 --> Dest2 : sert OK
}
@enduml

ตรวจสอบอย่างรวดเร็วด้วย curl

เพื่อให้แน่ใจว่าเซิร์ฟเวอร์ให้บริการเนื้อหาที่ถูกต้อง เพียงแค่`curl`ยืนยันว่า`index.html`มี`data-bs-theme="light"`และว่า`build/bake/css/styles.css`มีขนาด 31 402 ไบต์พอดี — เหมือนกับไฟล์ที่สร้างโดย`bake`.

$ ./gradlew serve
# Dans un autre terminal :
$ curl -s http://localhost:8820/ | grep data-bs-theme
<html ... data-bs-theme="light" ...>

$ curl -I http://localhost:8820/css/styles.css
HTTP/1.1 200 OK
Content-Length: 31402
Content-Type: text/css

การเรนเดอร์ในเครื่องขณะนี้คือเหมือนกันทุกพิกเซลไปยังการเรนเดอร์ที่ปรับใช้แล้ว

ทำไมบั๊กนี้ถึงร้ายกาจ

คุณเห็นไหมว่าทำไมมันถึงจับได้ยาก

  1. ไม่มีข้อผิดพลาดที่ชัดเจน: JBake ไม่ได้แครช มัน "ทำงาน" อยู่ เพียงแต่ใช้โฟลเดอร์ผิด

  2. บิลด์ทำงานได้ : ./gradlew bake`สร้างไฟล์อย่างถูกต้องใน`build/bake/.

  3. การปรับใช้ทำงานอยู่:`publishSite`ผลักดันเนื้อหาที่ดีไปยังออนไลน์

  4. มีเพียง serve เท่านั้นที่เสีย: งานพัฒนา งานที่เราใช้อย่างต่อเนื่องเพื่อวนซ้ำ

  5. ความเคยชินกับ -key value: ในฐานะนักพัฒนา พวกเราถูกหล่อหลอมจากประสบการณ์หลายปีกับ CLI Unix (-o output`, -i input). JBake CLI เป็นข้อยกเว้น — source และ destination เป็นอาร์กิวเมนต์แบบระบุตำแหน่ง

บทสรุป

หากคุณดูแลปลั๊กอิน Gradle ที่ครอบ JBake (หรือเครื่องมือ CLI ใด ๆ)อ่านเอกสารของอาร์กิวเมนต์แม้ว่าคุณจะคิดว่าคุณรู้จักพวกเขาแล้วก็ตาม สมมติฐานโดยนัย (-s= "set destination") อาจทำให้คุณเสียเวลาหลายชั่วโมงในการดีบักด้วยภาพ

ตรงนี้ การแก้ไขคือการเปลี่ยน:

args = listOf("-b", src, "-s", dest)   // ❌ BUG

en :

args = listOf(src, dest, "-s")         // ✅ FIX

ย้ายโทเคนไปสามตัว แล้วเว็บไซต์โลคอลของฉันก็กลับมาสวยเหมือนเว็บไซต์ในโปรดักชันอีกครั้ง

บทเรียน: เมื่อผลลัพธ์การเรนเดอร์แตกต่างกันระหว่าง local กับ prod โดยไม่มีเหตุผลที่ชัดเจน ให้สงสัย pipeline ก่อน — ไม่ใช่ CSS ไม่ใช่เบราว์เซอร์ และยิ่งไม่ใช่เฟรมเวิร์ก มักเป็นขั้นตอนก่อนการเรนเดอร์โดยตรงที่โกง

บทความที่เกี่ยวข้อง