- Dalam proses mengembalikan CI yang kompleks ke GitHub Actions, merge queue, beberapa runner, build Rust, image Docker, dan pengujian integrasi saling terkait, sehingga biaya debugging tampak lebih besar daripada konfigurasi itu sendiri
- Semua perubahan yang masuk ke
main harus lulus pengujian, dan kesalahan kecil diperbaiki otomatis seperti format, dependensi yang tidak digunakan, serta lint; artefak CI juga harus sama dengan artefak rilis
- Untuk mewajibkan validasi sebelum dan sesudah merge queue, nama job harus dibuat sama pada kedua tahap; jika tidak, check bisa berhenti atau perubahan yang gagal dapat ter-merge
GITHUB_TOKEN, permissions workflow, token kustom, serta pengecualian fork dan self-hosted runner saling bertaut, membuat model keamanan sulit dipahami dan memperbesar peluang kesalahan
- Eksekusi container Docker, workflow YAML, dan pengujian lokal yang terbatas memperlambat pengembangan, tetapi skrip CI baru memangkas waktu merge secara signifikan
CI kompleks yang kembali ke GitHub Actions
- Selama 2 minggu terakhir, skrip CI ditulis ulang kembali ke GitHub Actions
- Ini adalah perombakan ketiga untuk konfigurasi CI
- Awalnya menggunakan GitHub Actions
- Lalu pindah ke Earthly
- Karena Earthly dihentikan, kembali lagi ke GitHub Actions
- CI saat ini menangani merge queue, beberapa runner, build Rust, image Docker, dan pengujian integrasi berat secara bersamaan
- Runner yang digunakan mencakup self-hosted, blacksmith.sh, dan GitHub-hosted
- Saat menggabungkan satu PR, sekitar 1 jam waktu CI terpakai di beberapa runner paralel
Syarat yang harus dipenuhi CI
- Semua perubahan yang masuk ke
main harus lulus semua pengujian
- Kesalahan kecil seperti masalah format, dependensi yang tidak digunakan, dan lint sebaiknya diperbaiki otomatis alih-alih berakhir sebagai kegagalan
- Artefak yang diuji di CI harus sama dengan artefak rilis sebenarnya
- Demi pengalaman developer, CI harus selesai dengan cepat
- GitHub Actions secara teknis mendukung syarat-syarat ini, tetapi proses konfigurasinya disertai jebakan tersembunyi, perilaku yang tidak konsisten, dan debugging yang sulit
Merge queue dan status check
- Kunci untuk menjaga branch
main tetap bersih adalah merge queue GitHub
- merge queue melakukan rebase PR di atas
main sebelum menjalankan CI
- Eksekusi CI yang diperlukan terbagi menjadi dua tahap
- Menjalankan CI sebelum masuk ke queue untuk memperbaiki masalah kecil secara otomatis
- Menjalankan CI lagi di dalam queue untuk memvalidasi status merge final
- Agar kedua eksekusi wajib di GitHub Actions, nama job harus ditetapkan sama pada kedua tahap
- GitHub memperlakukan kedua eksekusi sebagai check yang sama, sehingga keduanya harus berhasil sebelum merge dapat dilakukan
- Cara ini ditemukan melalui jawaban Stack Overflow setelah beberapa jam debugging
- Cara lain dapat membuat status check tertahan sebelum masuk ke queue sehingga job tidak dimulai, atau membuat job yang seharusnya gagal di dalam merge queue tetap berujung pada merge
Model keamanan GitHub Actions yang sulit dipahami
- Setelah sebuah GitHub Action populer baru-baru ini diretas, muncul respons “kunci dependensi ke hash”, tetapi di komentar banyak yang mengatakan hampir tidak ada orang yang melakukannya
- GitHub Actions memiliki token default bernama
GITHUB_TOKEN
- Token ini diinisialisasi dengan izin default
- Izin default dapat diatur di pengaturan repository melalui Actions → General → Workflow Permissions
- Jika izin default
GITHUB_TOKEN bersifat terbatas, izin harus dinaikkan agar action dan perintah yang diperlukan dapat berjalan; jika izin default bersifat permisif, sebagian izin dapat dihapus dari file workflow
- Default yang lebih baik adalah memulai dari tanpa izin, lalu pengguna hanya menambahkan izin yang diperlukan
- Jenis izinnya banyak, dan bagi yang bukan ahli GitHub, sulit memahami apa yang dilindungi masing-masing izin
Pengecualian token dan izin
- Saat membuat rilis GitHub secara otomatis dengan
softprops/action-gh-release, token kustom CI_RELEASE digunakan
- name: Release on GitHub
if: env.version_exists == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ env.CURRENT_VERSION }}
generate_release_notes: true
make_latest: true
token: ${{ secrets.CI_RELEASE }}
- Rilis itu sendiri tetap selesai dengan token default, tetapi workflow setelah rilis tidak terpicu
- Tidak ada indikasi khusus, sehingga penyebabnya baru diketahui setelah menemukan issue dari orang yang mengalami masalah sama
- Izin juga dapat dinaikkan di dalam YAML workflow
- Struktur menaikkan izin di dalam kode yang hendak dilindungi terasa asing
- Menurut dokumentasi GitHub, kunci
permissions dapat menambah atau menghapus izin baca untuk repository fork, tetapi biasanya tidak dapat memberikan izin tulis
- Pengecualiannya adalah ketika admin memilih opsi Send write tokens to workflows from pull requests di pengaturan GitHub Actions
- Karena banyak pengecualian dan jebakan, model keamanan GitHub Actions kuat sekaligus memperbesar permukaan serangan dan peluang kesalahan
Ketidakpastian self-hosted runner
- Dokumentasi GitHub tidak merekomendasikan penggunaan self-hosted runner pada repository publik
- Alasannya, fork dari repository publik dapat menjalankan kode berbahaya di mesin self-hosted runner melalui PR
- GitHub juga memiliki pengaturan self-hosted runner yang mewajibkan persetujuan untuk menjalankan PR dari kontributor eksternal
- Tidak ada jawaban yang jelas dalam dokumentasi apakah penggunaan pengaturan ini bersama self-hosted runner aman, dan di internet pun tidak ada konsensus
- Kompleksitasnya tinggi, sehingga tetap sulit untuk yakin 100%
Benturan Docker dan GitHub Actions
- GitHub Actions dapat menjalankan job di dalam container
- Keuntungannya, dependensi dapat dipaketkan terlebih dahulu dalam dev container tanpa perlu menginstalnya setiap kali
- Dalam penggunaan nyata, masalah izin file terus berulang
- Container membangun file sebagai satu user, tetapi GitHub runner dapat berjalan dengan uid/gid yang berbeda
- Akibatnya, file di dalam container, GitHub workspace, atau direktori host sementara mungkin tidak dapat diakses
- Direktori
$HOME juga bisa tidak selaras
- Dev container dapat menginstal tool di
/home/ubuntu
- Di dalam GitHub Actions,
$HOME bisa menjadi /github/home
- Tool yang bergantung pada file di bawah
$HOME mungkin tidak dapat menemukan file yang diperlukan
- Action yang berinteraksi dengan sistem host juga dapat rusak
- Cache GitHub dibatasi 10GB, sehingga action sticky disk dari blacksmith digunakan untuk me-mount drive NVMe untuk caching
- Ini tidak berfungsi di dalam container, lalu terselesaikan setelah perbaikan dari blacksmith.sh
- Field
container sendiri juga memiliki batasan
- entrypoint tidak dapat di-override
- Tidak mungkin menjalankan hanya beberapa step di dalam container dan sisanya di luar
Pengembangan dan debugging workflow YAML
- Logika GitHub Actions ditulis dalam YAML, dan semakin kompleks, semakin mudah terjadi kesalahan
- Pemeriksaan GitHub YAML linter di RustRover membantu, tetapi diperlukan pemeriksaan statis yang lebih baik
- Di lokal, sulit menguji perilaku CI sebenarnya secara memadai
- act adalah tool yang dikenal, tetapi hanya mendukung sebagian kecil dari hal yang ingin dilakukan di CI
- Cara debugging terbaik adalah membuat satu repository lain yang sama, lalu mengulang
git commit -a -m "wip" && git push test-ci branch sampai CI berjalan sesuai harapan
Pemisahan workflow dan penggunaan ulang artefak
- Agar tidak menjalankan seluruh pipeline CI setiap kali, tiap workflow dijaga tetap kecil
- Di akhir setiap workflow, artefak diunggah, lalu workflow berikutnya mengunduhnya agar tidak perlu build ulang dari awal
- Workflow dapat diuji secara terisolasi dengan mengunduh artefak dari eksekusi sebelumnya
- Namun saat mengunduh dari eksekusi sebelumnya, token harus diberikan ke action
download-artifact
- Token ini boleh token default, tetapi mengapa masih harus dinyatakan secara eksplisit tetap menjadi pertanyaan yang belum terjawab
- File workflow utama menjadi rantai yang memanggil file YAML lain
jobs:
invoke-build-rust:
name: Build Rust
uses: ./.github/workflows/build-rust.yml
invoke-build-java:
name: Build Java
uses: ./.github/workflows/build-java.yml
invoke-tests-unit:
name: Unit Tests
needs: [invoke-build-rust, invoke-build-java]
uses: ./.github/workflows/test-unit.yml
invoke-tests-adapter:
name: Adapter Tests
needs: [invoke-build-rust]
uses: ./.github/workflows/test-adapters.yml
secrets: inherit
invoke-build-docker:
name: Build Docker
needs: [invoke-build-rust, invoke-build-java]
uses: ./.github/workflows/build-docker.yml
invoke-tests-integration:
name: Integration Tests
needs: [invoke-build-docker]
uses: ./.github/workflows/test-integration.yml
invoke-tests-java:
name: Java Tests
needs: [invoke-build-java]
uses: ./.github/workflows/test-java.yml
- Beberapa job memerlukan
secrets: inherit
- Saat satu workflow memanggil workflow lain, secret tidak dibagikan secara default
- Ini adalah penyebab masalah di mana seluruh pipeline CI gagal, tetapi masing-masing step berjalan jika dieksekusi terpisah
Merge makin cepat, debugging masih mahal
- Skrip CI baru secara signifikan mengurangi waktu merge, dan hasilnya memuaskan
- Namun terlalu banyak waktu diperlukan untuk mencapai kondisi itu, dan debugging perlu dibuat lebih mudah saat masalah muncul
Belum ada komentar.