ข้ามไปยังเนื้อหาหลัก

Migrate from GitHub Actions

.loha.yml is intentionally close to GitHub Actions syntax. Most workflows port over with no structural changes, just a few keyword and action substitutions.

Keyword and action mapping​

GitHub ActionsLoha
runs-on: ubuntu-latestresource_class: small (or larger)
actions/checkout@v4loha/git-clone@v1
actions/cache@v4loha/cache@v1
actions/upload-artifact@v4artifacts.paths on the job (no step)
actions/download-artifact@v4nothing: a job receives the files of the jobs in its needs:
docker/build-push-action@v5loha/buildah-build@v1
aquasecurity/trivy-actionloha/trivy-scan@v1
${{ secrets.FOO }} (env injection)secrets: [FOO] on the step
${{ github.sha }}${COMMIT_SHA} (or ${SHORT_SHA})
${{ github.ref_name }}no equivalent: filter on the trigger instead
if: on a jobif: on the job, written by hand: the translator removes a job's if: with a warning. Loha's if: compares only branch, event, github.ref and github.event_name with == / !=
strategy.matrixstrategy.matrix
strategy.max-parallelstrategy.max_parallel
${{ matrix.node }}${{ matrix.node }} (unchanged) or $MATRIX_NODE in a run: script
${{ ... }} is matrix-only in Loha

Loha substitutes three tokens, and only inside with: values: ${COMMIT_SHA}, ${SHORT_SHA}, ${REPO_URL}. The one exception is ${{ matrix.<axis> }}, which works inside a job that declares a matrix. Any other ${{ ... }} copied over from a GitHub workflow is not rejected; it is passed through as literal text. See Context tokens.

Artifacts between jobs​

GitHub Actions passes files between jobs with actions/upload-artifact in one job and actions/download-artifact in another. Loha has the same idea as a block on the job, artifacts:, and the translator converts it:

  • actions/upload-artifact becomes artifacts.paths on the job, with no step. The path: input is split one entry per line. An entry Loha cannot express, such as a negation (!dist/*.map), an absolute path, a .. path or one containing a ${{ ... }} expression, is dropped with a warning. More than 20 entries are cut to 20 with a warning. The name:, retention-days and if-no-files-found inputs have no equivalent: a declared path that matches no files fails the job.
  • actions/download-artifact becomes nothing. Every job that lists another job in needs: receives that job's files at the same paths, and a job needing a matrix job receives every shard's. The translator only warns when the job has no needs:, because then there is nothing for it to receive: add needs: [<job>].
.github/workflows/build.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: npm ci && npm run build
- name: Save dist
uses: actions/upload-artifact@v4
with: { name: dist, path: dist/ }
test:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get dist
uses: actions/download-artifact@v4
with: { name: dist }
- name: Test
run: npm test

Becomes:

.loha.yml
jobs:
build:
steps:
- name: Checkout
uses: loha/git-clone@v1
- name: Build
run: npm ci && npm run build
artifacts:
paths: [dist/]
test:
needs: [build]
steps:
- name: Checkout
uses: loha/git-clone@v1
- name: Test
run: npm test

If the upload was meant as a download for people (a release binary, a test report) rather than input for a later job, use loha/upload-artifact instead.

What doesn't port over (yet)​

  • Matrix builds port, with limits. The translator converts a strategy.matrix of plain value lists into Loha's strategy.matrix (1–5 axes, at most 20 values per axis and 20 combinations, values are strings), maps max-parallel to max_parallel, and leaves ${{ matrix.<axis> }} references as they are. It warns and drops fail-fast, include: and exclude:, which have no equivalent: without exclude: the excluded combinations run. A matrix built from an expression (${{ fromJSON(...) }}), an axis of objects, or one over the limits is dropped with a warning, and the job runs once until you rewrite the matrix by hand. A matrix value in runs-on: does not change the runner: every instance runs in ubuntu:latest, so vary container.image instead.
  • Reusable workflows / extends: are planned for v0.7.0.
  • Approval gates / manual triggers between jobs are planned for v0.7.0.
  • OIDC token publishing to AWS / GCP / Azure is planned for v0.8.0.

If your workflow depends on any of these, the roadmap tells you when to expect parity.

Worked example​

A common Node.js workflow:

.github/workflows/build.yml
on:
push:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npm test
- uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

Equivalent .loha.yml:

.loha.yml
version: '1'
on:
push:
branches: [main]
jobs:
build:
resource_class: small
steps:
- name: Checkout
uses: loha/git-clone@v1
- name: Install and test
image: node:20
run: |
npm ci
npm test
- name: Build and push image
uses: loha/buildah-build@v1
with:
IMAGE: registry.example.com/acme/web:${COMMIT_SHA}

Two differences worth noting. Every step needs a name:, which is how steps are identified in the build log. And there's no setup-node equivalent: pick the runtime with the step's image: instead of installing it at run time.