reconcile 0.3.0

A reconciliation storage service to sync a key-value map over multiple instances
name: Devcontainer CI

on:
  push:
    branches: ["main"]
  pull_request:
  merge_group:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # The heavy devcontainer build/smoke only validates the dev *image*, so it is
  # pointless on the vast majority of changes that don't touch it. We still let the
  # workflow trigger on every PR/push (so the required check always reports), but gate
  # the expensive job behind a fast paths check: when nothing devcontainer-related
  # changed, `devcontainer-ci` is skipped — and a skipped job counts as a passing
  # required check, whereas filtering the whole workflow via `on.*.paths` would leave
  # the check stuck "pending" and block merges.
  changes:
    name: Detect devcontainer changes
    runs-on: ubuntu-latest
    outputs:
      devcontainer: ${{ steps.filter.outputs.devcontainer }}
    steps:
      - uses: actions/checkout@v3
      # Not on `merge_group`: that event gives `paths-filter` no base to diff against, and a throw
      # here would fail this job, which would *skip* `devcontainer-ci` rather than run it -- green
      # having built nothing. The queue therefore builds unconditionally, which is also the right
      # answer on its own terms: it validates a combination no single pull request tested.
      - uses: dorny/paths-filter@v3
        id: filter
        if: github.event_name != 'merge_group'
        with:
          filters: |
            devcontainer:
              - '.devcontainer/**'
              - '.github/workflows/devcontainer.yml'

  devcontainer-ci:
    name: Build & Test in Devcontainer
    needs: changes
    if: github.event_name == 'merge_group' || needs.changes.outputs.devcontainer == 'true'
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Cache devcontainer build layers
        uses: actions/cache@v3
        with:
          path: /tmp/.devcontainer-cache
          key: ${{ runner.os }}-devcontainer-${{ hashFiles('.devcontainer/Dockerfile.dev') }}
          restore-keys: |
            ${{ runner.os }}-devcontainer-

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
        with:
          driver: docker-container

      - name: Setup Node.js (for Devcontainer CLI)
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install Devcontainer CLI
        run: |
          npm config set prefix ~/.npm-global
          npm install -g @devcontainers/cli

      - name: Add npm-global bin to PATH
        run: echo "$HOME/.npm-global/bin" >> $GITHUB_PATH

      - name: Build devcontainer image
        run: |
          devcontainer build \
            --workspace-folder . \
            --cache-from type=local,src=/tmp/.devcontainer-cache \
            --cache-to type=local,dest=/tmp/.devcontainer-cache,mode=max \
            --log-level info

      - name: Start devcontainer
        run: |
          devcontainer up \
            --workspace-folder . \
            --skip-post-create \
            --log-level info \
            --cache-from type=local,src=/tmp/.devcontainer-cache

      # The point of this job is to verify the devcontainer image itself builds and
      # works, not to re-run the full suite — fmt, clippy and the complete test matrix
      # already run (faster) in the `main` workflow's lint-and-test job. So we only
      # smoke the dev image: compile every target and run the fast lib unit tests.
      - name: Smoke-build inside devcontainer
        run: |
          devcontainer exec --workspace-folder . -- bash -lc "\
            export CARGO_HOME=\$HOME/.cargo && \
            mkdir -p \$CARGO_HOME && \
            cargo build --all-targets --all-features && \
            cargo test --lib --all-features \
          "