release.ymlyaml · 98 linesall
name: Release

# Runs when a GitHub Release is published:
#
#   * pokes code.dlang.org to ingest the new tag immediately, and
#   * builds `.#all-desktop`, pushes it to the binary cache, and pins the release
#     closure there so it survives cache garbage collection.
on:
  # Trigger on a *published* GitHub Release, not a bare tag push. This leaves a
  # grace window between pushing the `vX.Y.Z` tag and publishing the Release in
  # which a mistaken tag can be deleted before we nudge the registry. (Best
  # effort: the registry's background scan can still ingest a pushed tag on its
  # own — pushing the tag is the real point of no return.)
  release:
    types: [published]

  # Allow re-running by hand: re-notify if the release-time call failed, or
  # re-push/re-pin the cache (dispatching from a tag ref moves the pin;
  # dispatching from a branch only warms the cache).
  workflow_dispatch:

concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false

jobs:
  # Pokes code.dlang.org to ingest the new release immediately. The registry
  # also background-scans registered repos on its own schedule, so this only
  # *speeds up* ingestion — it is not what makes a tag public.
  notify-dub-registry:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Checkout
        uses: actions/checkout@v7

      - name: Trigger code.dlang.org update
        env:
          # Optional. Set this repo secret only if the package was registered
          # with an update secret; otherwise the empty default is accepted.
          DUB_REGISTRY_SECRET: ${{ secrets.DUB_REGISTRY_SECRET }}
        run: ci/notify-dub-registry.sh

  # Build `.#all-desktop` (the aggregate from nix/packages/all.nix: the full dev
  # shell, every package, and every standalone example), push it to the binary
  # cache, and pin the closure under a stable per-system name so it survives
  # cache garbage collection. Cachix has no unpin command (the public API only
  # creates pins), so retention works by re-pinning: each release adds a new
  # revision of the same pin and --keep-revisions drops the oldest, whose
  # closure becomes garbage-collectable.
  nix-build-pin:
    name: Nix build, push & pin (${{ matrix.runner }})
    strategy:
      fail-fast: false
      matrix:
        include:
          - runner: ubuntu-latest
            system: x86_64-linux
          - runner: macos-latest
            system: aarch64-darwin
    runs-on: ${{ matrix.runner }}
    # Usually cache hits (~2-4m); allow margin for a cold-cache `.#all-desktop` rebuild.
    timeout-minutes: 20
    steps:
      - name: Checkout
        uses: actions/checkout@v7
        with:
          # Full history so the highest-tag guard below sees every tag.
          fetch-depth: 0

      - name: Setup Nix
        uses: ./.github/actions/setup-nix
        with:
          push-to-cache: true
          cachix-cache: ${{ vars.CACHIX_CACHE }}
          cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}
          trusted-public-keys: ${{ vars.TRUSTED_PUBLIC_KEYS }}
          substituters: ${{ vars.SUBSTITUTERS }}
          use-nix-cache: true
          nix-cache-key: nix-${{ runner.os }}-nixbuild-${{ hashFiles('**/flake.lock') }}
          nix-cache-restore-prefixes: |
            nix-${{ runner.os }}-nixbuild-
            nix-${{ runner.os }}-

      # Usually all cache hits: CI's nix-build job already built and pushed
      # this commit's outputs before it reached main.
      - name: Build devShells.full, all packages, and all examples
        env:
          CACHIX_CACHE: ${{ vars.CACHIX_CACHE }}
          CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
        run: ci/with-cachix.sh nix build --print-build-logs .#all-desktop

      - name: Push & pin the release closure
        env:
          CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
          CACHIX_CACHE: ${{ vars.CACHIX_CACHE }}
          CI_SYSTEM: ${{ matrix.system }}
        run: ci/release-pin.sh