Build A Runtime In GitHub Actions#

This tutorial takes a committed conda-ship project and builds runtime artifacts with the composite GitHub Action.

You will add a workflow that downloads released conda-ship build tools, verifies them, runs cs build --dry-run, builds the runtime, and uploads the generated dist directory as a workflow artifact.

Before You Start#

You need a repository that already contains one supported manifest and lockfile pair:

  • conda.toml and conda.lock

  • pixi.toml and pixi.lock

  • pyproject.toml with [tool.conda] and conda.lock

  • pyproject.toml with [tool.pixi] and pixi.lock

The manifest must contain [tool.conda-ship] with at least:

[tool.conda-ship]
runtime = "demo"
runtime-version = "0.1.0"
delegate = "conda"
source-environment = "ship"

Run the local preflight before committing:

cs inspect
cs build --dry-run

Add The Workflow#

Create .github/workflows/build-runtime.yml:

name: Build runtime

on:
  workflow_dispatch:
  push:
    branches: [main]

permissions:
  contents: read
  id-token: write
  attestations: write
  artifact-metadata: write

jobs:
  build:
    name: Build ${{ matrix.os }} ${{ matrix.layout }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            layout: online
            install-method: standalone
          - os: macos-15-intel
            layout: embedded
            install-method: homebrew
          - os: macos-15
            layout: embedded
            install-method: homebrew
          - os: windows-latest
            layout: online
            install-method: standalone

    steps:
      - uses: actions/checkout@v4

      - uses: jezdez/conda-ship@0.2.1
        id: cs
        with:
          layout: ${{ matrix.layout }}
          install-method: ${{ matrix.install-method }}

      - uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
        with:
          subject-path: ${{ steps.cs.outputs.dist-path }}/*

      - uses: actions/upload-artifact@v4
        with:
          name: ${{ steps.cs.outputs.asset-name }}
          path: ${{ steps.cs.outputs.dist-path }}

Pin the action to a conda-ship release tag. Branch refs do not have matching release assets for cs and cs-template.

Warning

Use the latest reviewed actions/attest release in your workflow and pin it by commit SHA. The SHA above is an example, not a recommendation to keep using that exact revision indefinitely.

Run It#

Push the workflow and start it from the GitHub Actions tab, or wait for the next push to main.

The action downloads these release assets for the current runner:

  • cs-<target>

  • cs-template-<target>

  • SHA256SUMS

It verifies GitHub artifact attestations and the checksums before running the downloaded cs binary.

The workflow also attests the generated runtime output directory before uploading it. That downstream attestation covers the runtime binary, .runtime.lock, .packages.txt, .info.json, .sha256, and any external bundle produced by that job.

Inspect The Artifact#

Each job uploads the full generated output directory. Download one artifact and inspect the files:

demo-x86_64-unknown-linux-gnu
demo-x86_64-unknown-linux-gnu.info.json
demo-x86_64-unknown-linux-gnu.packages.txt
demo-x86_64-unknown-linux-gnu.runtime.lock
demo-x86_64-unknown-linux-gnu.sha256

For an external build, the directory also contains demo-<target>.bundle.tar.zst. For an embedded build, the runtime name gets the z suffix, for example demoz-aarch64-apple-darwin.

Override Runtime Metadata#

Keep package and channel choices in the manifest and lockfile. Use action inputs for release-job metadata that may vary across a matrix:

- uses: jezdez/conda-ship@0.2.1
  id: cs
  with:
    runtime: demo
    delegate: conda
    layout: ${{ matrix.layout }}
    docs-url: https://example.com/demo/
    install-scheme: conda-home
    install-name: demo
    install-method: ${{ matrix.install-method }}

The action does not validate those values itself. It passes them to cs build --dry-run; invalid values fail in conda-ship before artifact files are written.

What You Learned#

You added a release-style workflow that builds conda-ship runtime artifacts from committed project input. The solve still belongs to conda-workspaces or Pixi; the action consumes the committed lockfile and stamps a runtime with release-specific metadata.