Langsung ke konten
KamusNgoding
Mahir Github-actions 4 menit baca

Panduan Lengkap Membuat Action Kustom di GitHub Actions

#github-actions #custom-action #javascript #docker #advanced

Panduan Lengkap Membuat Action Kustom di GitHub Actions

Pendahuluan

Bayangkan kamu sedang membangun sistem deployment otomatis seperti yang digunakan platform e-commerce besar — setiap kali ada kode baru masuk, sistem langsung memvalidasi, menguji, dan mendeploy tanpa campur tangan manusia. Di sinilah action kustom GitHub Actions menjadi senjata andalan.

Action bawaan dari GitHub Marketplace memang banyak, tapi ada kalanya tidak ada satu pun yang pas dengan kebutuhan proyekmu. Mungkin kamu perlu memvalidasi format file Markdown sebelum publish, atau mengirim notifikasi ke sistem internal tim. Itulah saatnya kamu membuat action sendiri.

Artikel ini membahas cara membuat action kustom dari nol: memahami strukturnya, memilih tipe yang tepat, hingga contoh kasus nyata yang langsung bisa kamu adaptasi. Jika kamu belum familiar dengan konsep dasar workflow, pastikan kamu sudah membaca artikel tentang komponen dasar GitHub Actions terlebih dahulu.


Memahami Anatomi Action Kustom: action.yml

Setiap action kustom wajib memiliki file action.yml di root repositori. File ini adalah “kontrak” yang mendeklarasikan apa yang bisa dilakukan action-mu, input apa yang diterima, dan output apa yang dihasilkan.

# action.yml
name: "Validasi Markdown"
description: "Memvalidasi format dan struktur file Markdown"
author: "KamusNgoding"

inputs:
  target-directory:
    description: "Direktori yang berisi file Markdown"
    required: true
    default: "."
  strict-mode:
    description: "Aktifkan validasi ketat (gagal jika ada warning)"
    required: false
    default: "false"

outputs:
  total-files:
    description: "Jumlah file Markdown yang divalidasi"
  error-count:
    description: "Jumlah error yang ditemukan"

runs:
  using: "node20"
  main: "dist/index.js"

Beberapa bagian penting yang perlu dipahami:

  • inputs: Parameter yang bisa diisi pengguna action-mu saat memakainya di workflow. Setiap input bisa punya nilai default.
  • outputs: Data yang bisa diakses step berikutnya setelah action selesai berjalan.
  • runs: Menentukan tipe action dan entry point-nya.

Tipe-Tipe Action Kustom: JavaScript, Docker, dan Composite

GitHub Actions mendukung tiga tipe action kustom, masing-masing dengan karakteristik berbeda:

1. JavaScript Action

Action berbasis Node.js yang berjalan langsung di runner. Ini adalah pilihan tercepat karena tidak perlu build container.

runs:
  using: "node20"
  main: "dist/index.js"
  pre: "dist/setup.js"    # opsional: dijalankan sebelum main
  post: "dist/cleanup.js" # opsional: dijalankan setelah main

Cocok untuk: Manipulasi file, panggilan API, logika sederhana hingga menengah.

2. Docker Action

Menjalankan action di dalam container Docker. Lebih lambat tapi memberikan kontrol penuh atas environment — cocok jika action-mu butuh tool sistem seperti pandoc, imagemagick, atau runtime selain Node.js.

# action.yml untuk Docker Action
name: "Konversi Dokumen"
description: "Konversi Markdown ke PDF menggunakan Pandoc"
author: "KamusNgoding"

inputs:
  input-file:
    description: "Path ke file Markdown"
    required: true
  output-file:
    description: "Nama file PDF output"
    required: false
    default: "output.pdf"

runs:
  using: "docker"
  image: "Dockerfile"
  args:
    - ${{ inputs.input-file }}
    - ${{ inputs.output-file }}
# Dockerfile
FROM pandoc/core:3.1

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
#!/bin/bash
# entrypoint.sh
set -e

INPUT_FILE=$1
OUTPUT_FILE=$2

echo "Mengkonversi $INPUT_FILE ke $OUTPUT_FILE..."
pandoc "$INPUT_FILE" -o "$OUTPUT_FILE" --pdf-engine=xelatex

echo "Konversi selesai: $OUTPUT_FILE"

Cocok untuk: Action yang butuh dependensi sistem spesifik, atau bahasa selain JavaScript.

3. Composite Action

Menggabungkan beberapa step workflow menjadi satu action yang bisa dipakai ulang — tanpa menulis kode.

runs:
  using: "composite"
  steps:
    - name: Install dependencies
      shell: bash
      run: npm ci
    - name: Run linter
      shell: bash
      run: npm run lint

Cocok untuk: Mengemas urutan langkah yang sering diulang di banyak workflow.

Untuk artikel ini, kita akan fokus pada JavaScript Action karena paling umum digunakan dan performanya optimal.


Panduan Langkah-demi-Langkah Membuat Action JavaScript

Langkah 1: Siapkan Struktur Repositori

my-custom-action/
├── action.yml
├── package.json
├── src/
│   └── index.js
└── dist/
    └── index.js  (dibuat oleh bundler)

Langkah 2: Inisialisasi Project Node.js

npm init -y
npm install @actions/core @actions/github
npm install --save-dev @vercel/ncc

Package @actions/core menyediakan fungsi penting seperti getInput(), setOutput(), dan setFailed(). Jika kamu terbiasa dengan Pengenalan TypeScript: Superset JavaScript yang Lebih Aman, kamu bisa menggunakan TypeScript untuk action-mu dengan menambahkan @actions/core type definitions.

Langkah 3: Tulis Logika Action

// src/index.js
const core = require("@actions/core");
const fs = require("fs");
const path = require("path");

async function run() {
  try {
    // Ambil input dari action.yml
    const targetDir = core.getInput("target-directory", { required: true });
    const strictMode = core.getInput("strict-mode") === "true";

    core.info(`Memulai validasi di direktori: ${targetDir}`);

    // Cari semua file Markdown
    const files = findMarkdownFiles(targetDir);
    let errorCount = 0;

    for (const file of files) {
      const content = fs.readFileSync(file, "utf-8");
      const errors = validateMarkdown(content, file);

      if (errors.length > 0) {
        errors.forEach((err) => core.error(err, { file }));
        errorCount += errors.length;
      }
    }

    // Set output untuk step berikutnya
    core.setOutput("total-files", files.length.toString());
    core.setOutput("error-count", errorCount.toString());

    // Gagalkan workflow jika ada error
    if (errorCount > 0 || (strictMode && errorCount > 0)) {
      core.setFailed(`Validasi gagal: ditemukan ${errorCount} error`);
    } else {
      core.info(`✅ Semua ${files.length} file valid!`);
    }
  } catch (error) {
    core.setFailed(`Action gagal: ${error.message}`);
  }
}

function findMarkdownFiles(dir) {
  const results = [];
  const items = fs.readdirSync(dir, { withFileTypes: true });

  for (const item of items) {
    const fullPath = path.join(dir, item.name);
    if (item.isDirectory() && item.name !== "node_modules") {
      results.push(...findMarkdownFiles(fullPath));
    } else if (item.isFile() && item.name.endsWith(".md")) {
      results.push(fullPath);
    }
  }
  return results;
}

function validateMarkdown(content, filePath) {
  const errors = [];

  // Cek apakah ada heading H1
  if (!content.match(/^# .+/m)) {
    errors.push(`${filePath}: Tidak ada heading H1`);
  }

  // Cek code block tanpa bahasa
  const codeBlocksWithoutLang = content.match(/```\n/g);
  if (codeBlocksWithoutLang) {
    errors.push(
      `${filePath}: Ada ${codeBlocksWithoutLang.length} code block tanpa bahasa`
    );
  }

  return errors;
}

run();

Langkah 4: Bundle Action dengan ncc

GitHub Actions membutuhkan semua dependensi dalam satu file. Gunakan ncc untuk bundling:

// package.json
{
  "scripts": {
    "build": "ncc build src/index.js -o dist --license licenses.txt",
    "prepare": "npm run build"
  }
}
npm run build

Folder dist/ yang dihasilkan harus di-commit ke repositori, karena runner GitHub akan langsung mengeksekusi file tersebut.

Langkah 5: Gunakan Action di Workflow

# .github/workflows/validate.yml
name: Validasi Konten

on:
  pull_request:
    paths:
      - "src/content/**/*.md"

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Jalankan validasi Markdown
        id: validator
        uses: username/my-custom-action@v1
        with:
          target-directory: "src/content"
          strict-mode: "true"

      - name: Tampilkan hasil
        run: |
          echo "Total file: ${{ steps.validator.outputs.total-files }}"
          echo "Total error: ${{ steps.validator.outputs.error-count }}"

Contoh Kasus Nyata: Otomatisasi Validasi Markdown

Berikut workflow lengkap yang mengintegrasikan action kustom di atas dengan konteks nyata — misalnya untuk platform dokumentasi seperti KamusNgoding:

# .github/workflows/content-pipeline.yml
name: Content Quality Gate

on:
  pull_request:
    branches: [main]

jobs:
  quality-check:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: read

    steps:
      - uses: actions/checkout@v4

      - name: Validasi struktur Markdown
        id: md-check
        uses: username/my-custom-action@v1
        with:
          target-directory: "src/content"
          strict-mode: "true"

      - name: Komentar hasil di PR
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const totalFiles = '${{ steps.md-check.outputs.total-files }}';
            const errorCount = '${{ steps.md-check.outputs.error-count }}';
            const status = errorCount === '0' ? '✅ Lulus' : '❌ Gagal';

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Hasil Validasi Konten\n\n${status}\n- File diperiksa: ${totalFiles}\n- Error ditemukan: ${errorCount}`
            });

Pola ini mirip dengan konsep Mastering Factory Method Pattern: Membuat Sistem Plugin yang Fleksibel — kamu mendefinisikan antarmuka standar (action.yml), lalu implementasinya bisa diganti sesuai kebutuhan tanpa mengubah workflow yang memanggilnya.


Troubleshooting: Error yang Sering Muncul

Error: Cannot find module '@actions/core'

Penyebab: File dist/index.js tidak di-bundle dengan benar, atau folder dist/ tidak ikut di-commit ke repositori.

Solusi:

# Pastikan ncc sudah terinstall dan jalankan build
npm install --save-dev @vercel/ncc
npm run build

# Commit folder dist/
git add dist/
git commit -m "chore: bundle action dependencies"
git push

Error: Input required and not supplied: target-directory

Penyebab: Pengguna action tidak menyertakan input yang wajib (required: true) saat memanggil action di workflow mereka.

Solusi:

# Pastikan semua input required tersedia di workflow
- uses: username/my-custom-action@v1
  with:
    target-directory: "src/content"  # ← wajib diisi

Atau ubah required: false dengan nilai default di action.yml jika input memang opsional.


Error: Error: ENOENT: no such file or directory

Penyebab: Action mencoba membaca direktori yang tidak ada, biasanya karena actions/checkout belum dipanggil sebelum action kustom dijalankan.

Solusi:

steps:
  - uses: actions/checkout@v4  # ← WAJIB sebelum action yang akses file

  - uses: username/my-custom-action@v1
    with:
      target-directory: "src/content"

Warning: Node.js 16 actions are deprecated

Penyebab: action.yml masih menggunakan using: "node16" yang sudah deprecated oleh GitHub.

Solusi:

# action.yml — ubah ke node20
runs:
  using: "node20"  # ← ganti dari node16
  main: "dist/index.js"

Pertanyaan yang Sering Diajukan

Apa perbedaan action kustom dengan reusable workflow?

Action kustom adalah unit logika yang dikemas dalam repositori tersendiri dan dipanggil dengan uses: — fokusnya pada satu tugas spesifik. Reusable workflow adalah workflow lengkap (dengan jobs dan steps) yang bisa dipanggil dari workflow lain. Gunakan action kustom untuk logika yang sering dipakai ulang di banyak proyek, dan reusable workflow untuk urutan proses CI/CD yang standar di seluruh tim.

Bagaimana cara mempublish action ke GitHub Marketplace?

Repositori action-mu harus bersifat public, dan file action.yml harus ada di root repositori. Setelah itu, buka tab “Releases” di GitHub, buat release baru, dan centang opsi “Publish this Action to the GitHub Marketplace”. Pastikan nama dan deskripsi action jelas agar mudah ditemukan developer lain.

Apakah action kustom bisa digunakan di repositori private?

Ya. Untuk repositori private, kamu bisa merujuk action dari repositori lain di organisasi yang sama menggunakan format uses: org/repo-action@v1. Pastikan pengaturan akses “Actions” di organization settings mengizinkan akses lintas repositori.

Mengapa saya harus meng-commit folder dist/?

Berbeda dengan project Node.js biasa, GitHub Actions tidak menjalankan npm install sebelum mengeksekusi action. Runner langsung membaca file yang ada di repositori. Karena itu, semua dependensi harus sudah di-bundle ke dalam dist/index.js dan file tersebut harus ada di repositori.

Bagaimana cara menguji action kustom secara lokal sebelum di-push?

Gunakan package act (GitHub Actions local runner) atau buat file pengujian sederhana yang menyimulasikan environment GitHub Actions:

# Simulasi environment GitHub Actions secara lokal
INPUT_TARGET_DIRECTORY="./src/content" \
INPUT_STRICT_MODE="false" \
node dist/index.js

Kesimpulan

Membuat action kustom di GitHub Actions membuka peluang otomatisasi yang jauh lebih luas dari sekadar mengandalkan action pihak ketiga. Dengan memahami struktur action.yml, memilih tipe action yang tepat (JavaScript untuk kecepatan, Docker untuk fleksibilitas, Composite untuk penyederhanaan), dan mengikuti alur bundling yang benar, kamu bisa membangun blok otomatisasi yang dapat dipakai ulang di seluruh proyek timmu.

Kunci utamanya: selalu bundle dependensi dengan ncc, commit folder dist/, dan uji action-mu di workflow nyata sebelum mempublishnya. Selamat bereksperimen dan terus kembangkan pipeline otomatisasimu — jika ada pertanyaan atau ingin berbagi action kustom yang sudah kamu buat, jangan ragu untuk eksplorasi artikel-artikel lainnya di KamusNgoding!

Artikel Terkait