terrably
Distribution

CI release workflow

Multi-platform GitHub Actions workflow that builds a native binary for each target platform, then packages and publishes a signed GitHub Release.

Node.js vs Bun

Node.jsBun
Cross-compilation✗ — one native runner per platform✓ — single runner, all targets
CI jobs for a full release5–7 build jobs + 1 release job1 build job + 1 release job
Binary runtimeNode.js 25.5+Bun 0.6.0+

GitHub Actions workflow

Node.js SEA does not support cross-compilation — a binary built on macOS will not run on Linux. The solution is a CI matrix that builds natively on each platform, then a single packaging step that assembles all binaries into the release assets the Terraform Registry requires.

CI matrix (one job per platform)
  └─ terrably build  →  bin/terraform-provider-mycloud_{os}_{arch}

Packaging job
  └─ terrably publish  →  release/
       terraform-provider-mycloud_1.0.0_linux_amd64.zip
       terraform-provider-mycloud_1.0.0_linux_arm64.zip
       terraform-provider-mycloud_1.0.0_darwin_amd64.zip
       terraform-provider-mycloud_1.0.0_darwin_arm64.zip
       terraform-provider-mycloud_1.0.0_windows_amd64.zip
       terraform-provider-mycloud_1.0.0_manifest.json
       terraform-provider-mycloud_1.0.0_SHA256SUMS
       terraform-provider-mycloud_1.0.0_SHA256SUMS.sig

Create .github/workflows/release.yml in your provider repository. Replace mycloud with your provider's short name.

.github/workflows/release.yml
name: Release

# Triggers on any tag matching v* (e.g. v1.0.0, v1.2.3-beta)
on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write   # needed to create the GitHub Release

env:
  PROVIDER_NAME: mycloud   # ← change this

jobs:
  # ── Build: one job per OS/arch, runs terrably build natively ─────────────
  build:
    name: Build (${{ matrix.os }}_${{ matrix.arch }})
    runs-on: ${{ matrix.runner }}
    strategy:
      matrix:
        include:
          # Primary targets (required for HCP Terraform compatibility)
          - { os: linux,   arch: amd64, runner: ubuntu-slim      }
          - { os: darwin,  arch: amd64, runner: macos-15 intel   }
          - { os: darwin,  arch: arm64, runner: macos-latest     }
          - { os: windows, arch: amd64, runner: windows-latest   }

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v6
        with:
          node-version: '25'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build SEA binary
        shell: bash
        run: npx terrably build --name "$PROVIDER_NAME"

      - name: Rename binary with platform suffix
        shell: bash
        run: |
          EXT=""
          if [ "${{ matrix.os }}" = "windows" ]; then EXT=".exe"; fi
          mv "bin/terraform-provider-${PROVIDER_NAME}${EXT}" \
             "bin/terraform-provider-${PROVIDER_NAME}_${{ matrix.os }}_${{ matrix.arch }}${EXT}"

      - uses: actions/upload-artifact@v4
        with:
          name: binary-${{ matrix.os }}-${{ matrix.arch }}
          path: bin/terraform-provider-${{ env.PROVIDER_NAME }}_${{ matrix.os }}_${{ matrix.arch }}*
          if-no-files-found: error
          retention-days: 1

  # ── Release: collect all binaries, package, sign, publish ────────────────
  release:
    name: Publish to GitHub Releases
    needs: build
    runs-on: ubuntu-slim

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v6
        with:
          node-version: '25'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - uses: actions/download-artifact@v8
        with:
          path: bin/
          pattern: binary-*
          merge-multiple: true

      - name: Import GPG key
        run: |
          echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --import
          FP=$(gpg --list-secret-keys --with-colons | awk -F: '/^fpr/{print $10; exit}')
          echo "GPG_FINGERPRINT=${FP}" >> "$GITHUB_ENV"

      - name: Package, sign, and publish release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          npx terrably publish \
            --release-version "${{ github.ref_name }}" \
            --gpg-key         "${GPG_FINGERPRINT}" \
            --github-release

Cross-compilation is not supported with Node.js SEA. Each platform target requires its own native runner in the CI matrix. Use Bun to cross-compile from a single job instead.

Bun supports cross-compilation natively — a single runner can produce binaries for Linux, macOS, and Windows in one job, eliminating the need for a build matrix. Pass any target from Bun's supported targets list to --target.

Single build job (one runner, all platforms)
  └─ terrably build --target bun-linux-x64    →  bin/terraform-provider-mycloud
  └─ terrably build --target bun-linux-arm64  →  bin/terraform-provider-mycloud
  └─ terrably build --target bun-darwin-arm64 →  bin/terraform-provider-mycloud
  └─ terrably build --target bun-darwin-x64   →  bin/terraform-provider-mycloud
  └─ terrably build --target bun-windows-x64  →  bin/terraform-provider-mycloud.exe

Packaging job
  └─ terrably publish  →  release/
       terraform-provider-mycloud_1.0.0_linux_amd64.zip
       ...

Create .github/workflows/release.yml in your provider repository. Replace mycloud with your provider's short name.

.github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write

env:
  PROVIDER_NAME: mycloud   # ← change this

jobs:
  # ── Build: single job, Bun cross-compiles all targets ────────────────────
  build:
    name: Build (all platforms)
    runs-on: ubuntu-slim

    steps:
      - uses: actions/checkout@v6

      - uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Install dependencies
        run: bun install

      - name: Build for all targets
        shell: bash
        run: |
          TARGETS=(
            "bun-linux-x64    linux   amd64"
            "bun-linux-arm64  linux   arm64"
            "bun-darwin-arm64 darwin  arm64"
            "bun-darwin-x64   darwin  amd64"
            "bun-windows-x64  windows amd64"
          )
          for row in "${TARGETS[@]}"; do
            read -r TARGET OS ARCH <<< "$row"
            EXT=""
            if [ "$OS" = "windows" ]; then EXT=".exe"; fi
            bun node_modules/terrably/dist/src/cli/index.js build \
              --name "$PROVIDER_NAME" \
              --target "$TARGET"
            mv "bin/terraform-provider-${PROVIDER_NAME}${EXT}" \
               "bin/terraform-provider-${PROVIDER_NAME}_${OS}_${ARCH}${EXT}"
          done

      - uses: actions/upload-artifact@v4
        with:
          name: binaries
          path: bin/terraform-provider-${{ env.PROVIDER_NAME }}_*
          if-no-files-found: error
          retention-days: 1

  # ── Release: package, sign, publish ──────────────────────────────────────
  release:
    name: Publish to GitHub Releases
    needs: build
    runs-on: ubuntu-slim

    steps:
      - uses: actions/checkout@v6

      - uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Install dependencies
        run: bun install

      - uses: actions/download-artifact@v8
        with:
          name: binaries
          path: bin/

      - name: Import GPG key
        run: |
          echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --import
          FP=$(gpg --list-secret-keys --with-colons | awk -F: '/^fpr/{print $10; exit}')
          echo "GPG_FINGERPRINT=${FP}" >> "$GITHUB_ENV"

      - name: Package, sign, and publish release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          bun node_modules/terrably/dist/src/cli/index.js publish \
            --release-version "${{ github.ref_name }}" \
            --gpg-key         "${GPG_FINGERPRINT}" \
            --github-release

macOS binaries built via cross-compilation are not ad-hoc codesigned (codesign requires running on macOS). If you need signed macOS binaries, add a macos-latest runner step for the Darwin targets, or sign them as a post-processing step with your Apple Developer certificate.


Required GitHub Actions secrets

SecretHow to generate
GPG_PRIVATE_KEYgpg --armor --export-secret-keys your@email.com
(passphrase)GPG key passphrase — omit the import step if the key has no passphrase

GITHUB_TOKEN is injected automatically — no setup required.

To trigger a release: git tag v1.0.0 && git push origin v1.0.0


Platform support matrix

PlatformBun targetRunner
Linux x86-64 (linux_amd64)bun-linux-x64ubuntu-slim
Linux arm64 (linux_arm64)bun-linux-arm64
macOS arm64 (darwin_arm64)bun-darwin-arm64macos-latest
macOS x86-64 (darwin_amd64)bun-darwin-x64macos-15-intel
Windows x86-64 (windows_amd64)bun-windows-x64windows-latest
Windows arm64 (windows_arm64)bun-windows-arm64

This list is not exhaustive, and might be out of date. See https://github.com/actions/runner-images#available-images for the latest runner images, and Bun's supported targets list for the targets supported by Bun.

Last updated on

On this page