Jika tim Anda menjalankan writing challenge internal atau menerima kontribusi konten teknis dari banyak penulis, masalah utamanya biasanya bukan ide artikel, melainkan konsistensi submission. Front matter sering tidak lengkap, slug bentrok, Markdown tidak rapi, link rusak lolos review, dan reviewer harus mengulang pemeriksaan yang sama di setiap pull request.
Template GitHub Actions untuk writing challenge tim yang konsisten membantu memindahkan pemeriksaan berulang tersebut ke pipeline otomatis. Inspirasi budaya challenge komunitas seperti pengumuman pemenang Google I/O 2026 Writing Challenge di dev.to menunjukkan bahwa format challenge bisa mendorong partisipasi; namun agar skalanya nyaman untuk tim engineering, pengalaman kontributor harus ditopang oleh otomasi yang jelas, dapat diprediksi, dan mudah di-debug.
Mengapa workflow writing challenge perlu diotomasi
Untuk repository konten teknis, CI tidak hanya berfungsi sebagai penjaga kualitas kode. Ia juga menjadi kontrak editorial yang dapat diverifikasi. Setiap PR sebaiknya menjawab pertanyaan berikut sebelum reviewer membaca substansi artikel:
- Apakah metadata artikel valid dan lengkap?
- Apakah slug unik sehingga URL tidak bentrok?
- Apakah format Markdown cukup konsisten?
- Apakah link eksternal dan internal masih aktif?
- Apakah hasil build preview dapat dirender?
- Apakah PR sudah diberi label yang memudahkan triase?
Dengan pendekatan ini, reviewer manusia fokus pada akurasi teknis, struktur argumen, dan kualitas penjelasan, bukan hal-hal mekanis yang mudah diotomasi.
Struktur repository yang disarankan
Struktur berikut cukup umum untuk tim yang menyimpan artikel Markdown dan membangun situs dokumentasi atau blog statis:
repo-content/
├─ .github/
│ ├─ workflows/
│ │ ├─ content-ci.yml
│ │ ├─ preview-build.yml
│ │ └─ auto-label.yml
│ └─ pull_request_template.md
├─ content/
│ ├─ 2026-06-01-template-github-actions-writing-challenge.md
│ └─ 2026-06-02-observability-basics.md
├─ scripts/
│ ├─ validate-frontmatter.js
│ ├─ check-duplicate-slug.js
│ └─ changed-content-files.js
├─ site/
│ └─ ...
├─ package.json
└─ .markdownlint.jsonPrinsipnya sederhana:
- content/ menyimpan artikel sumber.
- scripts/ berisi logika validasi yang spesifik terhadap aturan editorial tim Anda.
- .github/workflows/ memecah workflow berdasarkan tanggung jawab, agar mudah dirawat.
Memisahkan validasi ke script kecil lebih baik daripada menulis seluruh logika dalam shell satu baris di YAML. Alasannya: lebih mudah diuji, lebih jelas pesan error-nya, dan lebih mudah diubah saat aturan editorial berkembang.
Konvensi artikel dan front matter
Sebelum menulis workflow, tentukan kontrak yang akan divalidasi. Misalnya setiap file di content/ harus memiliki front matter YAML seperti ini:
---
title: "Template GitHub Actions untuk Writing Challenge Tim yang Konsisten"
description: "Panduan membuat workflow CI untuk validasi konten teknis tim."
slug: "template-github-actions-writing-challenge"
authors:
- "nama-penulis"
tags:
- "github-actions"
- "markdown"
status: "draft"
---Field yang umum divalidasi:
titletidak kosongdescriptionada dan panjangnya wajarslughanya berisi huruf kecil, angka, dan tanda hubungauthorsberbentuk arraytagsberbentuk array dan tidak kosongstatustermasuk nilai yang diizinkan, misalnyadraft,review, atauready
Jangan terlalu agresif di awal. Aturan yang terlalu kaku akan membuat kontributor frustrasi. Mulailah dari validasi yang benar-benar mencegah masalah produksi, lalu tambahkan aturan editorial secara bertahap.
Workflow utama: validasi konten di pull request
Berikut contoh workflow inti untuk menjalankan validasi front matter, cek slug duplikat, lint Markdown, dan pemeriksaan link. Contoh ini menggunakan Node.js karena praktis untuk mem-parsing YAML front matter dan mengolah file yang berubah, tetapi idenya tetap sama jika tim Anda lebih nyaman dengan Python atau shell script.
name: Content CI
on:
pull_request:
paths:
- 'content/**/*.md'
- 'scripts/**'
- '.github/workflows/content-ci.yml'
- '.markdownlint.json'
jobs:
validate-content:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Get changed content files
id: changed
run: |
node scripts/changed-content-files.js > changed-files.txt
echo "count=$(wc -l < changed-files.txt | tr -d ' ')" >> $GITHUB_OUTPUT
- name: Validate front matter
if: steps.changed.outputs.count != '0'
run: xargs -a changed-files.txt node scripts/validate-frontmatter.js
- name: Check duplicate slugs
if: steps.changed.outputs.count != '0'
run: node scripts/check-duplicate-slug.js
- name: Lint Markdown
if: steps.changed.outputs.count != '0'
run: npx markdownlint-cli "content/**/*.md"
- name: Check links
if: steps.changed.outputs.count != '0'
run: npx lychee --no-progress --include-fragments content/**/*.mdKenapa workflow ini bekerja dengan baik:
- Trigger dibatasi ke path yang relevan, sehingga CI tidak berjalan saat file non-konten berubah.
fetch-depth: 0berguna bila script perlu membandingkan branch atau membaca riwayat penuh.- Perubahan file dihitung dulu, sehingga job bisa dilewati jika tidak ada artikel yang berubah.
- Setiap tahap fokus pada satu jenis validasi, sehingga pesan gagal lebih mudah dipahami kontributor.
Contoh validasi front matter
Script berikut hanya contoh minimal. Tujuannya menunjukkan pola validasi, bukan memaksakan library tertentu.
#!/usr/bin/env node
const fs = require('fs');
const matter = require('gray-matter');
const files = process.argv.slice(2);
let failed = false;
for (const file of files) {
const raw = fs.readFileSync(file, 'utf8');
const { data } = matter(raw);
const errors = [];
if (!data.title || typeof data.title !== 'string') {
errors.push('title wajib diisi');
}
if (!data.description || typeof data.description !== 'string') {
errors.push('description wajib diisi');
}
if (!data.slug || !/^[a-z0-9-]+$/.test(data.slug)) {
errors.push('slug wajib lowercase-kebab-case');
}
if (!Array.isArray(data.authors) || data.authors.length === 0) {
errors.push('authors wajib berupa array dan tidak kosong');
}
if (!Array.isArray(data.tags) || data.tags.length === 0) {
errors.push('tags wajib berupa array dan tidak kosong');
}
if (data.status && !['draft', 'review', 'ready'].includes(data.status)) {
errors.push('status harus draft, review, atau ready');
}
if (errors.length > 0) {
failed = true;
console.error(`\n${file}`);
for (const err of errors) {
console.error(`- ${err}`);
}
}
}
if (failed) process.exit(1);Hal penting di sini adalah pesan error yang spesifik. CI yang gagal tanpa menjelaskan field mana yang salah akan memperlambat kontribusi lebih dari tidak adanya CI sama sekali.
Cek slug duplikat
Slug duplikat adalah masalah klasik saat banyak orang menulis topik yang mirip atau membuat draft paralel. Validasi ini sebaiknya membaca seluruh direktori content/, bukan hanya file yang berubah, karena bentrok bisa terjadi dengan artikel lama.
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const matter = require('gray-matter');
const dir = path.join(process.cwd(), 'content');
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md'));
const seen = new Map();
let failed = false;
for (const file of files) {
const fullPath = path.join(dir, file);
const raw = fs.readFileSync(fullPath, 'utf8');
const { data } = matter(raw);
const slug = data.slug;
if (!slug) continue;
if (seen.has(slug)) {
failed = true;
console.error(`Slug duplikat: "${slug}" ditemukan pada ${seen.get(slug)} dan ${file}`);
} else {
seen.set(slug, file);
}
}
if (failed) process.exit(1);Jika repository Anda menggunakan folder bersarang atau nama file tidak unik, ubah pembacaan file menjadi rekursif. Jangan mengandalkan nama file sebagai pengganti slug kecuali memang itu kontrak arsitektur konten Anda.
Lint Markdown dan pemeriksaan link rusak
Markdown lint berguna untuk konsistensi struktur heading, spasi, list, dan blok kode. Ia tidak menjamin kualitas tulisan, tetapi sangat efektif mengurangi perbedaan format antar penulis. Simpan aturan di .markdownlint.json agar mudah dinegosiasikan oleh tim.
{
"default": true,
"MD013": false,
"MD022": true,
"MD032": true,
"MD041": false
}Contoh di atas sengaja tidak terlalu ketat. Misalnya aturan panjang baris sering memicu friksi pada paragraf biasa dan URL panjang.
Untuk link checker, perlu dipahami trade-off berikut:
- Link eksternal dapat gagal karena rate limiting, firewall, atau situs tujuan sedang lambat.
- Link dengan anchor fragmen sering valid secara dokumen tetapi sulit diverifikasi jika halaman dirender dinamis.
- Pemeriksaan semua link di setiap PR bisa membuat CI lambat.
Praktik yang cukup aman adalah memeriksa link pada file yang berubah untuk PR, lalu menjalankan pemeriksaan penuh terjadwal, misalnya sekali sehari atau mingguan.
name: Scheduled Link Check
on:
schedule:
- cron: '0 2 * * 1'
workflow_dispatch:
jobs:
links:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check all content links
run: npx lychee --no-progress --include-fragments content/**/*.mdJika Anda sering menerima false positive, tambahkan daftar pengecualian yang ditinjau secara berkala. Jangan langsung menonaktifkan pemeriksaan link seluruhnya.
Preview build agar reviewer melihat hasil akhir
Review artikel akan lebih cepat jika reviewer bisa membuka hasil render, bukan hanya membaca Markdown mentah. Workflow preview build biasanya melakukan tiga hal:
- Membangun situs atau halaman preview.
- Mengunggah artefak build atau menerbitkan preview environment.
- Menulis tautan preview ke pull request.
Contoh generik berikut hanya sampai tahap build dan upload artefak, karena mekanisme deployment preview berbeda-beda tergantung stack situs Anda.
name: Preview Build
on:
pull_request:
paths:
- 'content/**/*.md'
- 'site/**'
- 'package.json'
jobs:
build-preview:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Build site
run: npm run build
- name: Upload preview artifact
uses: actions/upload-artifact@v4
with:
name: site-preview
path: dist/Jika tim Anda menggunakan platform yang mendukung preview deployment otomatis, Anda bisa menambahkan komentar PR berisi URL preview. Nilai utamanya adalah reviewer dapat mengecek:
- render heading dan daftar isi,
- blok kode dan syntax highlighting,
- gambar atau aset yang hilang,
- broken internal routes,
- perbedaan antara Markdown source dan tampilan final.
Preview build tidak menggantikan lint dan validasi metadata. Ia menutup kelas masalah yang berbeda: masalah render dan integrasi dengan generator situs.
Auto-label PR untuk triase yang lebih cepat
Pada writing challenge tim, label membantu editor atau maintainer memprioritaskan review. Misalnya:
contentuntuk semua PR artikelneeds-reviewsaat validasi lulusdraftbila front matter status masih draftdocs-sitejika PR juga mengubah generator situs
Cara paling sederhana adalah memakai workflow berbasis path:
name: Auto Label Content PR
on:
pull_request_target:
types: [opened, synchronize, reopened]
jobs:
label:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Label content PR
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const files = await github.paginate(
github.rest.pulls.listFiles,
{
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
per_page: 100
}
);
const names = files.map(f => f.filename);
const labels = new Set();
if (names.some(name => name.startsWith('content/'))) labels.add('content');
if (names.some(name => name.startsWith('site/'))) labels.add('docs-site');
if (labels.size > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: [...labels]
});
}Catatan keamanan: pull_request_target memiliki konteks permission yang lebih tinggi dibanding pull_request. Karena itu, gunakan hanya untuk operasi aman seperti membaca metadata PR dan menambahkan label. Hindari mengeksekusi kode dari branch kontributor dalam workflow ini.
Strategi branch protection yang realistis
Workflow yang bagus akan kurang efektif jika branch utama tetap menerima merge tanpa syarat. Untuk repository konten, strategi branch protection yang praktis biasanya mencakup:
- Require pull request before merging
- Require status checks to pass untuk
Content CIdanPreview Build - Require at least 1 review dari editor teknis atau maintainer
- Dismiss stale reviews bila ada perubahan besar setelah review
- Restrict direct pushes ke branch utama
Kalau tim Anda kecil dan ritme publikasinya cepat, jangan terlalu banyak menambahkan status check wajib. Misalnya pemeriksaan link penuh mingguan tidak perlu dijadikan syarat merge PR harian. Pisahkan antara:
- gating checks: wajib lulus untuk merge,
- informational checks: memberi sinyal kualitas, tetapi tidak memblokir merge.
Pemisahan ini penting agar branch protection menjaga kualitas tanpa menciptakan antrean merge yang tidak perlu.
Checklist review untuk submission yang konsisten
CI membantu banyak, tetapi reviewer tetap memegang keputusan editorial dan teknis. Simpan checklist di .github/pull_request_template.md agar penulis dan reviewer memakai kriteria yang sama.
## Checklist penulis
- [ ] Front matter lengkap dan valid
- [ ] Slug unik dan sesuai format
- [ ] Artikel sudah di-lint tanpa error
- [ ] Link internal dan eksternal sudah dicek
- [ ] Preview build berhasil
- [ ] Contoh kode sudah diuji atau diverifikasi
- [ ] Istilah teknis konsisten
## Checklist reviewer
- [ ] Judul sesuai isi artikel
- [ ] Penjelasan teknis akurat
- [ ] Tidak ada klaim yang tidak didukung
- [ ] Struktur heading jelas
- [ ] Kode dan command realistis
- [ ] Layak dipublikasikan atau perlu revisiChecklist seperti ini efektif karena mengurangi diskusi berulang. Penulis tahu definisi “siap direview”, dan reviewer punya standar yang konsisten antar artikel.
Kesalahan umum dan tips debugging
1. CI lambat karena memeriksa seluruh repository
Gunakan filter path dan, bila perlu, hanya proses file yang berubah pada PR. Validasi yang memang perlu melihat seluruh koleksi, seperti slug unik, tetap bisa berjalan global karena biayanya biasanya kecil.
2. Link checker sering gagal acak
Jangan langsung menyimpulkan semua link rusak. Cek apakah kegagalan berasal dari timeout, redirect, atau pembatasan akses. Pertimbangkan retry terbatas atau pengecualian untuk domain tertentu yang memang tidak stabil.
3. Workflow sulit dirawat karena semua logika ada di YAML
Pindahkan logika ke script terpisah. YAML sebaiknya menjadi orkestrator, bukan tempat utama logika bisnis editorial.
4. Pesan gagal tidak membantu kontributor
Pastikan setiap validasi menyebut file dan alasan gagal secara eksplisit. Semakin sedikit tebakan yang harus dilakukan penulis, semakin cepat PR diperbaiki.
5. Auto-label atau komentar PR tidak jalan pada fork
Periksa event yang digunakan dan permission workflow. Untuk operasi metadata, pull_request_target sering lebih tepat, tetapi harus dipakai dengan batasan keamanan yang ketat.
Template minimum yang layak dipakai tim
Jika Anda ingin memulai tanpa terlalu kompleks, kombinasi minimum berikut sudah cukup kuat:
- Content CI: validasi front matter, slug unik, Markdown lint
- Preview Build: build situs dan unggah artefak preview
- Auto Label: beri label berdasarkan path
- Branch protection: wajib lulus CI dan minimal satu review
- PR checklist: samakan ekspektasi penulis dan reviewer
Setelah stabil, baru tambahkan pemeriksaan link penuh, aturan editorial tambahan, atau automasi komentar preview.
Penutup
Template GitHub Actions untuk writing challenge tim yang konsisten bukan sekadar rangkaian job CI. Ia adalah cara memformalkan standar kontribusi konten teknis agar partisipasi tetap tinggi tanpa mengorbankan kualitas. Dengan validasi front matter, cek slug duplikat, lint Markdown, pemeriksaan link, preview build, dan auto-label PR, tim bisa mengurangi beban review manual dan mempercepat rilis artikel secara signifikan.
Mulailah dari aturan yang benar-benar menyelesaikan masalah harian tim Anda. Jika workflow mudah dipahami, pesan error jelas, dan branch protection disusun dengan realistis, writing challenge tidak hanya seru di permukaan, tetapi juga efisien secara operasional untuk maintainer dan kontributor.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!