Skip to content
Blog Programming

Bagaimana Saya Mengubah Dokumen Office Jadi PDF Tanpa Membuka Office

By Romi Fadli, S.Kom. 18 min read Tooling & Automation
On this page
One Office document flowing to two results: one with shifted lines, one with lines kept flush

Pendahuluan

A. Konversi yang mestinya tak butuh aplikasi

Mengubah dokumen Word jadi PDF itu persoalan yang sudah selesai, dalam arti tiap office suite punya menunya. Ia berhenti selesai begitu permintaannya datang dari tempat lain, bukan dari orang yang sedang duduk di depan menu tersebut — sebuah script yang baru saja membuat dokumen dari template, laporan terjadwal yang berakhir sebagai PDF, atau satu folder file yang semuanya butuh perlakuan sama. Ketiganya berbagi satu ciri: yang memicu konversi bukan seseorang yang sedang menatap dokumen itu, melainkan langkah sebelumnya yang baru saja selesai.

Yang sama dari ketiga situasi itu: tak ada yang bisa diklik. Konversinya wajib berjalan tanpa antarmuka, tanpa prompt yang menunggu jawaban, dan tanpa jendela yang muncul lalu harus ditutup. Ia juga wajib menyatakan berhasil atau tidak, dengan cara yang bisa diperiksa program pemanggilnya, sebab satu tahap pipeline yang gagal diam-diam lebih buruk ketimbang yang gagal terang-terangan. Kegagalan yang berteriak berhenti di tempatnya; kegagalan yang bungkam diteruskan ke tahap sesudahnya sebagai seolah-olah keberhasilan.

Tantangannya: mengubah dokumen Office jadi PDF tanpa membuka aplikasi Office-nya sama sekali.

Tool ini dipakai dengan kedua cara, dan itu pantas dikatakan terus terang ketimbang berpura-pura ia semata infrastruktur. Sehari-hari ia diketik di terminal, satu file sekali jalan, sebab mengetik satu perintah lebih cepat ketimbang membuka dokumen lalu meng-export-nya. Tapi ia dibangun supaya bisa dipanggil program lain, dan kedua pemakaian itu tidak bertentangan — sifat yang membuatnya aman di dalam pipeline justru sifat yang membuatnya enak dipakai manual. Tanpa antarmuka, status keluar yang jelas, dan path hasil yang bisa ditebak sama bergunanya bagi orang maupun bagi script. Yang berbeda cuma siapa yang membaca hasilnya: mata saya, atau baris berikutnya di sebuah program.

Gampang dirumuskan, canggung dibangun. Kecanggungannya dimulai seketika, pada langkah ketika jawaban yang paling kentara ternyata jawaban yang keliru. Dan keliru di sini bukan berarti tak jalan — ia jalan, lalu menghasilkan dokumen yang sedikit berbeda dari yang dimaksud.

B. Kenapa tampilannya wajib selamat sampai tujuan

Dokumen hasil konversi tidak dinilai dari bisa dibuka atau tidak. Ia dinilai dari masih menyerupai dokumennya atau tidak. Laporan yang dikirim ke orang lain punya layout: tabel yang wajib muat sehalaman, heading yang duduk di tempat ia ditaruh, slide yang text box-nya tak bergeser. Kalau konversi memindahkan salah satunya, PDF itu bukan salinan dokumennya melainkan pendekatan atasnya — dan orang yang menerimanya tak punya cara tahu bagian mana yang bergeser. Dokumen aslinya ada di mesin saya, bukan di mesin mereka, jadi tak ada pembanding yang bisa mereka buka.

Batasan itu lebih ketat ketimbang kedengarannya, sebab dokumen yang dimaksud buatan Microsoft Office. Kesetiaan pada format itu bukan klaim umum soal mutu; ia pertanyaan tentang bagaimana satu engine tertentu membaca file satu vendor tertentu, dan jawabannya berbeda antar engine dengan cara yang cuma muncul pada layout rumit. Dokumen sederhana terkonversi sama baiknya di mana pun, dan justru itulah yang membuat perbedaannya gampang terlewat saat menguji.

Batasan kedua soal perilaku, bukan soal keluaran. Konversi yang dipanggil dari script bisa saja dipanggil dua kali sekaligus, dan bisa jadi dipanggil di mesin yang tak seorang pun akan login ke sana untuk merapikan. Apa pun yang meninggalkan proses tetap hidup, atau menyimpan state per-user yang bisa diperebutkan dua pemanggilan, adalah persoalan yang muncul saat beban naik dan bukan saat diuji — waktu terburuk untuk menemukannya, sebab saat itu program yang memanggil konverter sudah berjalan di produksi.

C. Enam hambatan yang menghalangi

Jawaban yang paling kentara justru yang saya tolak. Mencari cara mengerjakan ini mengembalikan satu perintah, dan perintah itu pemanggilan LibreOffice. Ia gratis, terpasang di mana-mana, dan hampir semua orang menjawabnya begitu. Saya memakai yang lain, dan alasannya spesifik ketimbang soal selera — dan itulah yang membuatnya jadi hal pertama yang wajib dibenarkan artikel ini, bukan diandaikan. Menolak jawaban yang diberikan semua orang menuntut alasan yang lebih kuat ketimbang menerimanya.

Engine-nya tak menerima argumen — ia menerima file tugas. Konverter yang saya pilih tidak dikemudikan flag. Ia diberi satu dokumen XML yang menjelaskan pekerjaannya, dan itu antarmuka tak lazim sekaligus perkara paling aneh dari tool ini. Bentuk tersebut berakibat pada bagaimana pembungkusnya harus ditulis.

Environment variable yang disusun sembarangan bisa menunjuk ke tempat yang keliru. Engine-nya dipanggil dengan library path yang ditempelkan di depan apa pun yang sudah dimiliki pemanggilnya. Menyambung keduanya dengan keliru sedikit saja menghasilkan nilai yang bukan sekadar tak berguna melainkan berbahaya, dan bahayanya tak terlihat sebab kasus yang paling seringnya tetap bekerja.

Engine-nya tak menyelesaikan path relatif dengan cara yang sama seperti pemanggilnya. Program yang dinyalakan sebuah script belum tentu berbagi working directory dengan script itu, dan path semacam output.pdf tak berarti apa-apa tanpanya. Apa pun yang relatif wajib dijadikan absolut sebelum ditulis ke file tugasnya.

Selesai tanpa error tidak sama dengan menghasilkan file. Untuk tahap yang digantungi tahap berikutnya, pertanyaannya bukan konverternya senang atau tidak melainkan barangnya ada atau tidak. Keduanya pertanyaan berbeda, dan cuma satu yang berarti bagi tahap berikutnya.

Ada tiga file, dan ketiganya file yang sama. Word, Excel, dan PowerPoint masing-masing dapat perintah sendiri, berbeda pada satu baris fungsional. Itu duplikasi, sengaja dipertahankan, dan alasan ia lebih murah ketimbang alternatifnya pantas diuraikan ketimbang diminta maaf. Pengulangan code biasanya dianggap cacat secara otomatis, padahal ongkosnya bergantung pada satu pertanyaan yang jarang diajukan lebih dulu, dan jawabannya di sini kebetulan sangat jelas.

Urutannya berjalan dari keputusan yang membentuk segalanya, melewati antarmuka yang dipaksakan keputusan itu, lalu ke tiga cara sebuah pembungkus bisa keliru dengan bentuk yang cuma muncul ketika ada program lain yang menggantunginya — dan berakhir pada pertanyaan tentang pengulangan.

Metode

A. Memilih engine yang bukan pilihan kentara

Inilah jawaban atas hambatan pertama. Perintah yang disarankan tiap hasil pencarian adalah ini:

bash
libreoffice --headless --convert-to pdf document.docx

Yang saya pakai justru engine konversi milik OnlyOffice, office suite open-source, dipanggil langsung. Bukan aplikasi OnlyOffice-nya, dan bukan pula document server — cuma binary yang mengerjakan penerjemahan formatnya, dipanggil sebagai program.

bash
/opt/onlyoffice/documentbuilder/x2t task.xml

Empat alasan, dan yang pertama itulah yang memutuskan. Cara LibreOffice merender file Microsoft Office melenceng pada dokumen berlayout tak sederhana — yang tabelnya wajib muat, slide-nya punya teks berposisi, dan penataannya berasal dari Word ketimbang dari sumber netral. Untuk PDF yang akan dibaca orang lain sebagai dokumen jadi, layout yang bergeser itu persoalan kebenaran dan bukan persoalan kosmetik.

LibreOffice dalam mode headless menyimpan profil user, dan dua konversi yang berjalan bersamaan memperebutkannya. Itulah kegagalan klasik pendekatan tersebut: ia bekerja sempurna saat diuji, ketika konversinya terjadi satu per satu, lalu berulah di bawah pipeline yang menjalankan beberapa sekaligus.

Engine OnlyOffice satu proses yang mulai, mengonversi, lalu selesai. LibreOffice headless punya kebiasaan meninggalkan sesuatu tetap berjalan, yang di mesin tanpa penunggu berarti proses yang takkan diperhatikan maupun dirapikan siapa pun.

Dan engine-nya memang sudah ada di sana, sebab OnlyOffice terpasang untuk keperluan lain. Memilihnya tak menambah apa pun ke mesin, yang jadi argumen kecil bila berdiri sendiri sekaligus penentu saat disandingkan dengan ketiga alasan lainnya.

B. Antarmuka yang terbuat dari XML

Ini menjawab hambatan kedua. Engine-nya tak menerima flag yang menjelaskan pekerjaannya. Ia menerima path ke sebuah file XML yang menjelaskannya, dan segala yang perlu diketahui konversi itu masuk ke dalam dokumen tersebut.

bash
<TaskQueueDataConvert>
  <m_sFileFrom>$IN</m_sFileFrom>
  <m_sFileTo>$OUT</m_sFileTo>
  <m_sFontDir>/usr/share/fonts</m_sFontDir>
  <m_sThemeDir>$BDIR/sdkjs/slide/themes</m_sThemeDir>
  <m_bIsNoBase64>true</m_bIsNoBase64>
</TaskQueueDataConvert>

Nama elemennya membocorkan asal antarmuka ini: ia format tugas internal sebuah document server, gambaran pekerjaan yang sedang diantre ketimbang sebuah command line. Format keluarannya tak disebut di mana pun — ia disimpulkan dari ekstensi path tujuannya, dan itulah sebabnya pembungkusnya tak pernah mengoper argumen format.

Dua dari ruas itu ada karena konversinya butuh sumber daya yang tak ada di dalam dokumen. m_sFontDir menunjuk ke font sistem, sebab teks wajib diukur untuk bisa di-layout, dan font yang hilang mengubah tempat tiap baris terpotong. m_sThemeDir menunjuk ke dalam instalasi engine-nya sendiri, dan berarti bagi presentasi, yang membawa rujukan theme ketimbang penataan yang tertanam.

Karena file tugasnya dibuat tiap kali jalan dan tak berguna sesudahnya, ia ditaruh di folder sementara yang membersihkan dirinya sendiri:

bash
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT

Trap itulah yang membuatnya aman ketimbang sekadar rapi. Ia menyala pada tiap jalan keluar, termasuk konversi yang gagal, jadi tak ada satu pun jalur di script yang meninggalkan folder terlantar.

C. Titik dua yang berarti "di sini"

Ini menjawab hambatan ketiga. Engine-nya dipanggil dengan foldernya sendiri ditempelkan di depan jalur pencarian library:

bash
LD_LIBRARY_PATH="$BDIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" "$BDIR/x2t" "$TMP/params.xml"

Bagian menariknya :+, dan apa yang ia cegah. Artinya "sisipkan titik dua ini cuma bila variabelnya tak kosong", dan alternatifnya memperlihatkan kenapa itu berarti:

bash
# variable empty, with :+      ->  /opt/onlyoffice/documentbuilder
# variable empty, without :+   ->  /opt/onlyoffice/documentbuilder:
# variable set,   with :+      ->  /opt/onlyoffice/documentbuilder:/existing/path

Titik dua di ekor sebuah jalur pencarian tidak diabaikan. Ia entri kosong, dan entri kosong berarti working directory saat itu — jadi versi yang sembarangan tadi menyuruh dynamic linker mencari library di mana pun pemanggilnya kebetulan sedang berdiri. Itu persoalan keamanan pada kasus umum dan persoalan ketakterdugaan pada tiap kasus, dan ia tak terlihat saat diuji sebab library-nya toh ketemu di lokasi pertama.

Pantas dijelaskan persis apa yang baris ini kerjakan dan tidak kerjakan di sini. Build engine yang ini membawa RPATH bernilai $ORIGIN, artinya ia memang sudah mencari library di sebelah dirinya sendiri, dan konversi tetap berhasil di mesin ini meski variabelnya tak diset. Baris tersebut asuransi ketimbang syarat yang menanggung beban — tapi :+ di dalamnya pantas disalin ke tiap tempat sebuah variabel path diperpanjang, sebab kegagalan yang ia cegah bersifat senyap di mana pun.

D. Menjadikan tiap path absolut sebelum diserahkan

Ini menjawab hambatan keempat. Kedua path diselesaikan sebelum sampai ke file tugas, lewat dua mekanisme berbeda sebab kedua kasusnya memang berbeda.

bash
IN="$(realpath "$IN")"
case "$OUT" in /*) : ;; *) OUT="$PWD/$OUT" ;; esac

Input-nya sudah ada, jadi realpath bisa menyelesaikannya sepenuhnya — mengikuti symlink dan meratakan .. sepanjang jalan. Output-nya belum ada, dan realpath pada path yang belum ada itu canggung, jadi ia mendapat perlakuan yang lebih sederhana: kalau sudah diawali garis miring, biarkan; kalau tidak, tempelkan folder saat ini di depannya.

Alasan ini tak boleh dilewati: engine-nya proses terpisah yang membaca sebuah file, dan tak ada yang menjamin ia menafsirkan path relatif terhadap folder yang sama dengan tempat pemanggilnya berada. Menulis report.pdf yang belum diselesaikan ke dalam file tugas sama dengan meminta program lain menebak di mana "sini" itu.

Perilaku yang pantas diperiksa adalah ketika output relatif diberikan dari tempat selain folder input-nya:

bash
$ cd subdir
$ docx2pdf ../letter.docx out.pdf
OK -> /path/to/subdir/out.pdf

File-nya mendarat di sebelah pemanggilnya, bukan di sebelah input-nya — dan itulah yang dimaksud orang yang mengetik perintah tersebut, sekaligus yang dibutuhkan script yang menyusun path relatif terhadap working directory-nya sendiri.

E. Menanyakan ada atau tidaknya file, bukan senang atau tidaknya engine

Ini menjawab hambatan kelima. Sesudah engine-nya kembali, pembungkusnya memeriksa barangnya sendiri:

bash
if [[ -f "$OUT" ]]; then
  echo "OK -> $OUT"
else
  echo "FAILED: output was not produced" >&2
  exit 1
fi

Ini pagar terhadap engine yang melaporkan sukses tanpa menghasilkan apa pun, dan itulah bentuk kegagalan yang paling menyakitkan di dalam pipeline: tahap berikutnya berjalan dengan andaian sebuah file ada di sana.

Pantas jujur soal seberapa sering itu terjadi. Di seluruh kasus kegagalan yang sanggup saya susun — input yang hilang, file rusak berekstensi benar, tujuan yang tak bisa ditulisi, folder tujuan yang tak ada — engine ini selalu mengembalikan status bukan nol, dan tak menghasilkan file. Saya tak berhasil membuatnya mengaku sukses sambil tak menghasilkan apa pun.

Pemeriksaan itu tetap dipertahankan, dan alasannya ketimpangan ongkos. Ia cuma satu perbandingan. Yang ia cegah adalah tahap hilir yang menelan file yang sebenarnya tak ada, dan itu menampakkan diri jauh dari sebabnya. Pagar terhadap kebohongan yang belum pernah kita tangkap sendiri tetap layak dipasang ketika kebohongan itu mahal sementara pagarnya nyaris gratis.

Ada efek kedua dari set -e yang pantas diketahui, sebab ia mengubah kapan cabang tersebut bisa tercapai sama sekali. Bila engine-nya keluar dengan status bukan nol, script berhenti di baris itu dan tak pernah sampai ke pemeriksaannya — kegagalannya dilaporkan lewat status keluar, bukan lewat pesan. Cabang else itu ada persis untuk satu skenario: keluar nol tanpa file.

F. Tiga perintah ketimbang satu perintah berflag

Ini menjawab hambatan keenam. Ada tiga script, dan ketiganya script yang sama. Membandingkan dua di antaranya memperlihatkan betapa sedikit yang berbeda:

bash
$ diff docx2pdf xlsx2pdf
EXT="docx"     ->  EXT="xlsx"
# plus three comment lines naming the format

Satu baris fungsional. Selebihnya — validasi, penanganan path, XML-nya, library path, pemeriksaan output — diduplikasi tiga kali, dan menambah format keempat berarti menyalin file-nya lagi.

Alternatifnya satu perintah dengan argumen format, dan alasan saya tak menulisnya: duplikasinya lebih murah ketimbang kelihatannya sementara abstraksinya lebih mahal ketimbang kelihatannya. Tiga perintah bernama jelas justru tiga nama yang tak perlu diingat, sebab namanya sudah menyatakan ia menerima apa. Satu office2pdf --format=xlsx berarti satu hal untuk diingat plus satu flag untuk dicari tahu, dan flag yang keliru adalah kasus error baru yang tak ada ketika formatnya berada di nama perintahnya.

Bagian yang terduplikasi juga bagian yang tak berubah. Script ini tak pernah disunting sejak ditulis, jadi ongkos duplikasi — harus memperbaiki bug di tiga tempat — belum pernah dibayar. Itulah uji sebenarnya untuk pengulangan semacam ini: bukan berapa banyak salinannya, melainkan seberapa sering mereka berubah.

Pemeriksaan ekstensi itulah yang membuat pemisahannya berguna ketimbang sekadar rapi, sebab tiap perintah menolak masukan yang bukan untuknya:

bash
$ xlsx2pdf letter.docx
Error: input must have extension .xlsx (got: letter.docx)

Itu pagar sungguhan, bukan kecerewetan. Engine-nya menyimpulkan format sumber dari file-nya, jadi menyerahkan file Word ke perintah spreadsheet belum tentu gagal dengan keras — ia bisa saja menghasilkan keluaran yang keliru secara halus. Menolak di pintu masuk mengubah keanehan yang mungkin senyap jadi error yang segera dan terbaca.

Hasil dan Pembahasan

A. Apa yang dijumlahkan keenam keputusan itu

Ketiga perintah itu tak pernah disunting sejak ditulis. Ia dipakai manual hampir tiap hari dan dipakai di dalam script ketika sebuah dokumen baru saja dibuat lalu perlu keluar sebagai PDF.

KeputusanYang dibeliAlternatif yang ditolak
OnlyOffice x2tkesetiaan layout pada file Microsoft OfficeLibreOffice headless — melenceng pada layout rumit
satu proses, tanpa profilaman dijalankan beberapa sekaligus, tak meninggalkan apa pundaemon headless — profil diperebutkan, proses terlantar
file tugas XMLantarmuka yang memang dimiliki engine-nyatak ada — ini bukan pilihan pembungkusnya
mktemp -d + traptak ada folder terlantar di jalur keluar mana punnama temp tetap — bertabrakan saat dijalankan dua kali
${VAR:+:$VAR}tak pernah ada titik dua di ekor, tak pernah "cari di sini"sambung polos — diam-diam menambahkan cwd
realpath + output absolutpath berarti sama di kedua prosesmengoper path relatif — program lain menebak
[[ -f "$OUT" ]]jawabannya soal file-nya, bukan soal suasana hati enginemempercayai status keluarnya saja
tiga perintah, berjarak satu barisnamanya membawa formatnya; masukan keliru ditolaksatu perintah ber---format — satu flag untuk dicari

B. Apa yang membuatnya layak dicatat

Sebagian besar tool ini biasa saja: validasi masukannya, bangun sebuah file, panggil sebuah program, periksa hasilnya. Yang pantas dicatat justru keputusan pertamanya, sebab itulah yang bertentangan dengan apa yang dikatakan tiap hasil pencarian, dan sebab penalarannya tak terlihat di dalam code. Pembaca yang melihat enam puluh baris ini menemukan sebuah path ke binary OnlyOffice tanpa penjelasan kenapa path itu dan bukan sebuah perintah LibreOffice.

Itulah bentuk umum perkaranya. Code mencatat apa yang dipilih dan nyaris bungkam soal apa yang ditolak, sementara opsi yang ditolak justru sering yang akan diraih pembaca di masa depan — termasuk saya sendiri versi nanti. Empat alasan memilih satu engine ketimbang engine lain, yang tak satu pun muncul di dalam script, adalah bagian paling gampang menguap dari seluruh tool ini.

Pantas disebut sebagai batas ketimbang kemenangan: ini tiga shell script yang membungkus binary milik orang lain, dan ia mewarisi tiap sifat binary tersebut. Kalau kesetiaan engine-nya pada suatu dokumen di masa depan ternyata meleset, sehati-hati apa pun pembungkusnya takkan menolong. Yang disumbang pembungkus lebih kecil dan lebih membosankan — bahwa path berarti seperti yang tertulis, bahwa file sementara lenyap, dan bahwa hasil yang dilaporkan merujuk ke barangnya ketimbang ke prosesnya.

C. Ketika pendekatan ini berhenti jadi yang tepat

Konversinya dipatok. Ketiga perintah ini menerima sebuah dokumen lalu menghasilkan PDF pada setelan default, tanpa kendali atas rentang halaman, mutu, font yang ditanam, maupun metadata. Apa pun yang menuntut itu semua adalah pekerjaan berbeda, dan tempatnya di tool yang dibangun mengelilingi antarmuka engine yang lebih penuh, bukan di pembungkus yang seluruh tugasnya membuat satu kasus umum jadi praktis.

Hal yang sama berlaku pada volume. Tiap pemanggilan menyalakan engine-nya, mengonversi satu file, lalu selesai, dan ongkos menyalakan proses itu ongkos tetap yang dibayar sekali per dokumen. Untuk segelintir file, atau satu loop atas satu folder, ia tak berarti apa-apa. Untuk ribuan, bentuk yang benar adalah layanan berumur panjang yang menjaga engine-nya tetap hangat — yang kebetulan persis definisi sebuah document server, dan persis yang sengaja dihindari tool ini.

Simpulan

Mengubah dokumen Office jadi PDF tanpa membuka Office cuma satu perintah di permukaannya, dan permukaan itu menyesatkan dengan cara yang spesifik: perintah yang disarankan semua orang bukan perintah yang saya pakai. Memilih engine OnlyOffice ketimbang LibreOffice berujung pada empat hal — kesetiaan layout pada file Microsoft Office, tak ada profil user yang diperebutkan pemanggilan paralel, proses yang selesai ketimbang berlama-lama, dan engine yang memang sudah terpasang.

Selebihnya adalah ongkos berbicara dengan engine yang dibangun untuk document server ketimbang untuk command line: antarmuka yang terbuat dari XML, path yang wajib absolut sebab proses terpisah tak berbagi working directory, library path yang disusun supaya tak pernah berakhir dengan titik dua, dan hasil yang diukur dari ada atau tidaknya file ketimbang dari puas atau tidaknya konverternya.

Ketiga script yang nyaris identik adalah bagian yang paling mungkin dikritik sekaligus bagian yang paling tak ingin saya ubah. Code yang terduplikasi tak pernah butuh disunting, formatnya tinggal di nama perintah tempat ia tak bisa salah ketik, dan tiap perintah menolak dokumen yang bukan untuknya. Pengulangan itu mahal sebanding dengan seberapa sering ia berubah — dan yang ini tak berubah sama sekali.

Istilah

x2t
Engine konversi format milik OnlyOffice Document Builder, dipanggil sebagai binary yang berdiri sendiri. Ia menerima path ke file tugas XML ketimbang opsi command line.
headless
Menjalankan program yang biasanya punya antarmuka grafis tanpa menampilkannya, supaya bisa dikemudikan script. Flag --headless milik LibreOffice contoh yang paling lazim.
LD_LIBRARY_PATH
Daftar folder berpemisah titik dua yang ditelusuri dynamic linker untuk mencari shared library sebelum lokasi default sistem. Entri kosong di dalam daftar itu — seperti yang dibuat titik dua di ekor — berarti working directory saat itu.
RPATH
Jalur pencarian library yang dicatat di dalam sebuah executable saat ia di-build. Dengan nilai $ORIGIN ia berarti "folder tempat executable ini berada", sehingga program bisa menemukan library miliknya tanpa environment variable.
realpath
Menyelesaikan sebuah path jadi bentuk absolut kanoniknya, mengikuti symlink dan membuang komponen . serta ... Menuntut path-nya sudah ada.
set -e
Opsi shell yang menghentikan script begitu ada perintah mengembalikan status bukan nol, ketimbang melanjutkan ke baris berikutnya.

Technologies covered

  • Bash
  • LibreOffice
  • ONLYOFFICE

About the author

Photo of Romi Fadli

Romi Fadli, S.Kom.

Solution Architect · Backend Engineer · Embedded Systems

In my day job I work as a solution architect. I design the system, lead the team that builds it — quality assurance, backend developers, a UI developer, DevOps and DevSecOps — and write the most critical modules myself, such as a certificate authority, a long-term PDF signer, and the core API of a personnel system.

I work across the whole stack that a running system actually needs. On the hardware side that means PCB design, microcontrollers, and embedded firmware — including RISC-V boards running FreeRTOS and Embassy, speaking MQTT over mutually authenticated TLS on a microcontroller with 64 KB of SRAM.

Most of my day-to-day work is software: Rust backends and API modules built on Clean Architecture, an OAuth2/OpenID Connect authorization server written from scratch, PAdES document signing, and certificate authorities — root CAs, timestamping authorities, and OCSP responders. I also build the frontends that sit on top of them.

Running all of that means infrastructure: Docker Compose stacks, networking, Mikrotik and RADIUS, observability with OpenTelemetry, and a steady habit of writing command-line tools and utilities for Linux when the existing ones do not fit.

Share

Enjoyed this?

Get an email when a new article is published.

Referenced by

Related Articles

A stack of Markdown documents and an orange arrow leading to a browser page with a side menu, a heading, paragraphs, a dark code panel, and a table of contents with its active entry highlighted

Satu script yang mengubah dokumentasi Markdown satu project menjadi situs statis yang terbuka dari folder tanpa server maupun internet: menu, daftar isi yang mengikuti pembaca, tabel yang tak pernah terpotong, kode dengan tombol salin, dan tautan yang tetap berfungsi.

7 min read Read more →
An old layout with a grey banner and a new structure with a green banner, each holding a blue database, a purple document store, and an orange telemetry store; three coloured paths pass through one export package marked with a green check

Empat perintah yang memindahkan stack dari layout lama ke struktur baru melintasi versi mayor: export tanpa mengubah data sumber, import tiap store sesuai cara yang diizinkan versinya, verifikasi apa yang benar-benar tiba, dan rename modul sambil memindahkan datanya.

11 min read Read more →
Three image layer stacks, each with a version tag of its own colour, blue, orange, and green, each leading to an archive file banded in the same colour

Perintah yang memberi versi tersendiri pada tiap modul stack, mem-build image-nya di bawah tag yang berarti tepat satu build, menolak memakai ulang versi atau menelan compile yang gagal, dan mengarsipkan image menurut nama dan tag supaya versi tak saling menimpa.

7 min read Read more →