Examples

There is a separate directory with examples called firmware-action-example. It is a great source of information to get started.

In addition, the main repository also contains multiple examples (even though rather simple ones) defined in .github/workflows/example.yml. These are there to function as tests to verify the functionality, as such they are made with this specific task in mind. Please take that into account when going though them.

Coreboot

Coreboot - GitHub CI

  build-coreboot:
    needs:
      - changes
      - skip-check
    strategy:
      fail-fast: false
      matrix:
        coreboot-version: ['24.02.01']
        arch: ['amd64', 'arm64']
    runs-on: ${{ matrix.arch == 'arm64' && 'ARM64' || 'ubuntu-latest' }}
    if: ${{ ! (github.event_name == 'pull_request_review' && github.actor != 'github-actions[bot]') && needs.skip-check.outputs.changes == 'true' }}
    # Skip if pull_request_review on PR not made by a bot
    steps:
      - name: Cleanup
        run: |
          rm -rf ./* || true
          rm -rf ./.??* || true
      - name: Checkout
        uses: actions/checkout@v4

      - name: Restore cached coreboot repo
        uses: actions/cache/restore@v4
        id: cache-repo
        with:
          path: ./my_super_dooper_awesome_coreboot
          key: coreboot-${{ matrix.coreboot-version }}-example
      - name: Clone coreboot repo
        if: steps.cache-repo.outputs.cache-hit != 'true'
        run: |
          git clone --branch "${{ matrix.coreboot-version }}" --depth 1 https://review.coreboot.org/coreboot my_super_dooper_awesome_coreboot
      - name: Store coreboot repo in cache
        uses: actions/cache/save@v4
        if: steps.cache-repo.outputs.cache-hit != 'true'
        with:
          path: ./my_super_dooper_awesome_coreboot
          key: coreboot-${{ matrix.coreboot-version }}-example

      - name: Move my defconfig into place (filename must not contain '.defconfig')
        run: |
          mv "tests/coreboot_${{ matrix.coreboot-version }}/seabios.defconfig" "seabios_defconfig"

      - name: firmware-action
        uses: ./
        # uses: 9elements/firmware-action
        with:
          config: |-
            tests/example_config__coreboot.json
            tests/example_config__uroot.json
          target: 'coreboot-example'
          recursive: 'false'
          compile: ${{ needs.changes.outputs.compile }}
          debug: 'true'
          enable-cache: 'true'
          auto-download-artifacts: 'true'
          auto-upload-artifacts: 'true'
        env:
          COREBOOT_VERSION: ${{ matrix.coreboot-version }}
          UROOT_VERSION: 'dummy'

Coreboot - Configuration file

{
  "coreboot": {
    "coreboot-example": {
      "depends": null,
      "sdk_url": "ghcr.io/9elements/firmware-action/coreboot_${COREBOOT_VERSION}:main",
      "repo_path": "my_super_dooper_awesome_coreboot/",
      "defconfig_path": "seabios_defconfig",
      "output_dir": "output-coreboot/",
      "container_output_dirs": null,
      "container_output_files": ["build/coreboot.rom", "defconfig"],
      "blobs": {},
      "container_input_dir": "inputs/",
      "input_dirs": null,
      "input_files": null
    }
  }
}

Linux Kernel

Linux Kernel - GitHub CI

  build-linux:
    needs:
      - changes
      - skip-check
    strategy:
      fail-fast: false
      matrix:
        linux-version: ['6.12']
        arch: ['amd64', 'arm64']
    runs-on: ${{ matrix.arch == 'arm64' && 'ARM64' || 'ubuntu-latest' }}
    if: ${{ ! (github.event_name == 'pull_request_review' && github.actor != 'github-actions[bot]') && needs.skip-check.outputs.changes == 'true' }}
    # Skip if pull_request_review on PR not made by a bot
    steps:
      - name: Cleanup
        run: |
          rm -rf ./* || true
          rm -rf ./.??* || true
      - name: Checkout
        uses: actions/checkout@v4

      - name: Restore cached linux source
        id: cache-repo
        uses: actions/cache/restore@v4
        with:
          path: ./linux-${{ matrix.linux-version }}.tar.xz
          key: linux-${{ matrix.linux-version }}-example
      - name: Prepare linux kernel
        run: |
          # Download source files
          wget --quiet --continue "https://cdn.kernel.org/pub/linux/kernel/v${LINUX_MAJOR_VERSION}.x/linux-${{ matrix.linux-version }}.tar.xz"
          wget --quiet "https://cdn.kernel.org/pub/linux/kernel/v${LINUX_MAJOR_VERSION}.x/linux-${{ matrix.linux-version }}.tar.sign"
          unxz --keep "linux-${{ matrix.linux-version }}.tar.xz" >/dev/null
          # Verify GPG signature
          gpg2 --locate-keys torvalds@kernel.org gregkh@kernel.org
          gpg2 --verify "linux-${{ matrix.linux-version }}.tar.sign"
          # Extract
          tar -xvf "linux-${{ matrix.linux-version }}.tar"
        env:
          LINUX_MAJOR_VERSION: 6
      - name: Store linux source in cache
        uses: actions/cache/save@v4
        if: steps.cache-repo.outputs.cache-hit != 'true'
        with:
          path: ./linux-${{ matrix.linux-version }}.tar.xz
          key: linux-${{ matrix.linux-version }}-example

      - name: Move my defconfig into place (filename must not contain '.defconfig')
        run: |
          mv "tests/linux_${{ matrix.linux-version }}/linux.defconfig" "ci_defconfig"

      - name: firmware-action
        uses: ./
        # uses: 9elements/firmware-action
        with:
          config: |-
            tests/example_config__uroot.json
            tests/example_config__linux.json
          target: 'linux-example'
          recursive: 'false'
          compile: ${{ needs.changes.outputs.compile }}
          debug: 'true'
          enable-cache: 'true'
          auto-download-artifacts: 'true'
          auto-upload-artifacts: 'true'
        env:
          LINUX_VERSION: ${{ matrix.linux-version }}
          SYSTEM_ARCH: ${{ matrix.arch }}
          UROOT_VERSION: 'dummy'

Linux Kernel - Configuration file

{
  "linux": {
    "linux-example": {
      "depends": null,
      "sdk_url": "ghcr.io/9elements/firmware-action/linux_${LINUX_VERSION}:main",
      "arch": "${SYSTEM_ARCH}",
      "repo_path": "linux-${LINUX_VERSION}/",
      "defconfig_path": "ci_defconfig",
      "output_dir": "output-linux/",
      "container_output_dirs": null,
      "container_output_files": ["vmlinux", "defconfig"],
      "gcc_version": "",
      "container_input_dir": "inputs/",
      "input_dirs": null,
      "input_files": null
    }
  }
}

Edk2

Edk2 - GitHub CI

  build-edk2:
    runs-on: ubuntu-latest
    needs:
      - changes
      - skip-check
    strategy:
      fail-fast: false
      matrix:
        edk2-version: ['edk2-stable202211']
    # TODO
    if: ${{ ! (github.event_name == 'pull_request_review' && github.actor != 'github-actions[bot]') && needs.skip-check.outputs.changes == 'true' }}
    # Skip if pull_request_review on PR not made by a bot
    steps:
      - name: Cleanup
        run: |
          rm -rf ./* || true
          rm -rf ./.??* || true
      - name: Checkout
        uses: actions/checkout@v4

      - name: Restore cached edk2 repo
        uses: actions/cache/restore@v4
        id: cache-repo
        with:
          path: ./Edk2
          key: edk2-${{ matrix.edk2-version }}-example
      - name: Clone edk2 repo
        if: steps.cache-repo.outputs.cache-hit != 'true'
        run: |
          git clone --recurse-submodules --branch "${{ matrix.edk2-version }}" --depth 1 https://github.com/tianocore/edk2.git Edk2
      - name: Prepare file with build arguments
        run: |
          echo "-D BOOTLOADER=COREBOOT -D TPM_ENABLE=TRUE -D NETWORK_IPXE=TRUE" > "edk2_config.cfg"
      - name: Store edk2 repo in cache
        uses: actions/cache/save@v4
        if: steps.cache-repo.outputs.cache-hit != 'true'
        with:
          path: ./Edk2
          key: edk2-${{ matrix.edk2-version }}-example

      - name: Get versions of edk2
        id: edk2_versions
        run: |
          echo "ver_current=$( echo ${{ matrix.edk2-version }} | tr -cd '0-9' )" >> "${GITHUB_OUTPUT}"
          echo "ver_breaking=$( echo 'edk2-stable202305' | tr -cd '0-9' )" >> "${GITHUB_OUTPUT}"

      - name: Use GCC5 for old edk2
        id: gcc_toolchain
        # GCC5 is deprecated since edk2-stable202305
        # For more information see https://github.com/9elements/firmware-action/issues/340
        run: |
          if [[ ! ${{ steps.edk2_versions.outputs.ver_current }} < ${{ steps.edk2_versions.outputs.ver_breaking }} ]]; then
            echo "gcc_toolchain_version=GCC" >> "${GITHUB_OUTPUT}"
          else
            echo "gcc_toolchain_version=GCC5" >> "${GITHUB_OUTPUT}"
          fi

      - name: firmware-action
        uses: ./
        # uses: 9elements/firmware-action
        with:
          config: 'tests/example_config__edk2.json'
          target: 'edk2-example'
          recursive: 'false'
          compile: ${{ needs.changes.outputs.compile }}
          debug: 'true'
          enable-cache: 'true'
          auto-download-artifacts: 'true'
          auto-upload-artifacts: 'true'
        env:
          EDK2_VERSION: ${{ matrix.edk2-version }}
          GCC_TOOLCHAIN_VERSION: ${{ steps.gcc_toolchain.outputs.gcc_toolchain_version }}

Edk2 - Configuration file

{
  "edk2": {
    "edk2-example": {
      "depends": null,
      "sdk_url": "ghcr.io/9elements/firmware-action/${EDK2_VERSION}:main",
      "arch": "X64",
      "repo_path": "Edk2/",
      "defconfig_path": "edk2_config.cfg",
      "output_dir": "output-edk2/",
      "container_output_dirs": ["Build/"],
      "container_output_files": null,
      "build_command": "source ./edksetup.sh; build -a X64 -p UefiPayloadPkg/UefiPayloadPkg.dsc -b DEBUG -t ${GCC_TOOLCHAIN_VERSION}",
      "container_input_dir": "inputs/",
      "input_dirs": null,
      "input_files": null
    }
  }
}

Firmware Stitching

Firmware Stitching - GitHub CI

  build-stitching:
    needs:
      - changes
      - skip-check
    strategy:
      fail-fast: false
      matrix:
        coreboot-version: ['4.19']
        arch: ['amd64', 'arm64']
    runs-on: ${{ matrix.arch == 'arm64' && 'ARM64' || 'ubuntu-latest' }}
    if: ${{ ! (github.event_name == 'pull_request_review' && github.actor != 'github-actions[bot]') && needs.skip-check.outputs.changes == 'true' }}
    # Skip if pull_request_review on PR not made by a bot
    steps:
      - name: Cleanup
        run: |
          rm -rf ./* || true
          rm -rf ./.??* || true
      - name: Checkout
        uses: actions/checkout@v4

      - name: Restore cached coreboot-blobs repo
        uses: actions/cache/restore@v4
        id: cache-repo
        with:
          path: ./stitch
          key: coreboot-blobs-${{ matrix.coreboot-version }}-example
      - name: Clone blobs repo
        if: steps.cache-repo.outputs.cache-hit != 'true'
        run: |
          git clone --depth 1 https://review.coreboot.org/blobs stitch
      - name: Store coreboot-blobs repo in cache
        uses: actions/cache/save@v4
        if: steps.cache-repo.outputs.cache-hit != 'true'
        with:
          path: ./stitch
          key: coreboot-blobs-${{ matrix.coreboot-version }}-example

      - name: firmware-action
        uses: ./
        # uses: 9elements/firmware-action
        with:
          config: 'tests/example_config__firmware_stitching.json'
          target: 'stitching-example'
          recursive: 'false'
          compile: ${{ needs.changes.outputs.compile }}
          debug: 'true'
          enable-cache: 'true'
          auto-download-artifacts: 'true'
          auto-upload-artifacts: 'true'
        env:
          COREBOOT_VERSION: ${{ matrix.coreboot-version }}

Firmware Stitching - Configuration file

{
  "firmware_stitching": {
    "stitching-example": {
      "depends": null,
      "sdk_url": "ghcr.io/9elements/firmware-action/coreboot_${COREBOOT_VERSION}:main",
      "repo_path": "stitch/",
      "container_output_dirs": null,
      "container_output_files": ["new_descriptor.bin"],
      "output_dir": "output-stitch/",
      "base_file_path": "stitch/mainboard/intel/emeraldlake2/descriptor.bin",
      "platform": "",
      "ifdtool_entries": [
        {
          "path": "stitch/mainboard/intel/emeraldlake2/me.bin",
          "target_region": "ME",
          "optional_arguments": null
        }
      ],
      "container_input_dir": "inputs/",
      "input_dirs": null,
      "input_files": null
    }
  }
}

u-root

u-root - GitHub CI

  build-uroot:
    needs:
      - changes
      - skip-check
    strategy:
      fail-fast: false
      matrix:
        uroot-version: ['0.14.0']
        arch: ['amd64', 'arm64']
    runs-on: ${{ matrix.arch == 'arm64' && 'ARM64' || 'ubuntu-latest' }}
    if: ${{ ! (github.event_name == 'pull_request_review' && github.actor != 'github-actions[bot]') && needs.skip-check.outputs.changes == 'true' }}
    # Skip if pull_request_review on PR not made by a bot
    steps:
      - name: Cleanup
        run: |
          rm -rf ./* || true
          rm -rf ./.??* || true
      - name: Checkout
        uses: actions/checkout@v4

      - name: Restore cached u-root repo
        uses: actions/cache/restore@v4
        id: cache-repo
        with:
          path: ./u-root
          key: u-root-${{ matrix.uroot-version }}-example
      - name: Clone u-root repo
        if: steps.cache-repo.outputs.cache-hit != 'true'
        run: |
          git clone --depth 1 --branch v${{ matrix.uroot-version }} https://github.com/u-root/u-root.git || true
      - name: Store u-root repo in cache
        uses: actions/cache/save@v4
        if: steps.cache-repo.outputs.cache-hit != 'true'
        with:
          path: ./u-root
          key: u-root-${{ matrix.uroot-version }}-example

      - name: firmware-action
        uses: ./
        # uses: 9elements/firmware-action
        with:
          config: 'tests/example_config__uroot.json'
          target: 'u-root-example'
          recursive: 'false'
          compile: ${{ needs.changes.outputs.compile }}
          debug: 'true'
          enable-cache: 'true'
          auto-download-artifacts: 'true'
          auto-upload-artifacts: 'true'
        env:
          UROOT_VERSION: ${{ matrix.uroot-version }}

u-root - Configuration file

{
  "u-root": {
    "u-root-example": {
      "depends": null,
      "sdk_url": "ghcr.io/9elements/firmware-action/uroot_${UROOT_VERSION}:main",
      "repo_path": "u-root/",
      "output_dir": "output-uroot/",
      "container_output_dirs": null,
      "container_output_files": ["initramfs.cpio"],
      "build_command": "go build; GOARCH=amd64 ./u-root -defaultsh gosh -o initramfs.cpio boot coreboot-app ./cmds/core/* ./cmds/boot/*",
      "container_input_dir": "inputs/",
      "input_dirs": null,
      "input_files": null
    }
  }
}

u-boot

u-boot - GitHub CI

  build-uboot:
    needs:
      - changes
      - skip-check
    strategy:
      fail-fast: false
      matrix:
        uboot-version: ['2025.01']
        arch: ['amd64', 'arm64']
    runs-on: ${{ matrix.arch == 'arm64' && 'ARM64' || 'ubuntu-latest' }}
    if: ${{ ! (github.event_name == 'pull_request_review' && github.actor != 'github-actions[bot]') && needs.skip-check.outputs.changes == 'true' }}
    # Skip if pull_request_review on PR not made by a bot
    steps:
      - name: Cleanup
        run: |
          rm -rf ./* || true
          rm -rf ./.??* || true
      - name: Checkout
        uses: actions/checkout@v4

      - name: Restore cached u-boot repo
        uses: actions/cache/restore@v4
        id: cache-repo
        with:
          path: ./u-boot
          key: u-boot-${{ matrix.uboot-version }}-example
      - name: Clone u-boot repo
        if: steps.cache-repo.outputs.cache-hit != 'true'
        run: |
          git clone https://source.denx.de/u-boot/u-boot.git
          cd u-boot
          git fetch -a
          git checkout "v${{ matrix.uboot-version }}"
      - name: Store u-boot repo in cache
        uses: actions/cache/save@v4
        if: steps.cache-repo.outputs.cache-hit != 'true'
        with:
          path: ./u-boot
          key: u-boot-${{ matrix.uboot-version }}-example

      - name: Move my defconfig into place (filename must not contain '.defconfig')
        run: |
          mv "tests/uboot_${{ matrix.uboot-version }}/uboot.defconfig" "uboot_defconfig"

      - name: firmware-action
        uses: ./
        # uses: 9elements/firmware-action
        with:
          config: 'tests/example_config__uboot.json'
          target: 'u-boot-example'
          recursive: 'false'
          compile: ${{ needs.changes.outputs.compile }}
          debug: 'true'
          enable-cache: 'true'
          auto-download-artifacts: 'true'
          auto-upload-artifacts: 'true'
        env:
          UBOOT_VERSION: ${{ matrix.uboot-version }}

u-boot - Configuration file

{
  "u-boot": {
    "u-boot-example": {
      "depends": null,
      "sdk_url": "ghcr.io/9elements/firmware-action/uboot_${UBOOT_VERSION}:main",
      "arch": "arm64",
      "repo_path": "u-boot/",
      "defconfig_path": "uboot_defconfig",
      "output_dir": "output-uboot/",
      "container_output_dirs": null,
      "container_output_files": ["u-boot", "u-boot.cfg", "u-boot.elf"],
      "container_input_dir": "inputs/",
      "input_dirs": null,
      "input_files": null
    }
  }
}

Universal

Universal - GitHub CI

  build-universal:
    needs:
      - changes
      - skip-check
    runs-on: ubuntu-latest
    if: ${{ ! (github.event_name == 'pull_request_review' && github.actor != 'github-actions[bot]') && needs.skip-check.outputs.changes == 'true' }}
    # Skip if pull_request_review on PR not made by a bot
    steps:
      - name: Cleanup
        run: |
          rm -rf ./* || true
          rm -rf ./.??* || true
      - name: Checkout
        uses: actions/checkout@v4

      - name: firmware-action
        uses: ./
        # uses: 9elements/firmware-action
        with:
          config: 'tests/example_config__universal.json'
          target: 'universal-example'
          recursive: 'false'
          compile: ${{ needs.changes.outputs.compile }}

      - name: Get artifacts
        uses: actions/upload-artifact@v4
        with:
          name: universal
          path: output-universal
          retention-days: 14

Universal - Configuration file

{
  "universal": {
    "universal-example": {
      "depends": null,
      "sdk_url": "golang:latest",
      "repo_path": "./",
      "container_output_dirs": null,
      "container_output_files": [
        "test.txt"
      ],
      "output_dir": "output-universal/",
      "input_dirs": null,
      "input_files": null,
      "container_input_dir": "inputs/",
      "build_commands": [
        "echo 'hello world'",
        "touch test.txt"
      ]
    }
  }
}