codex-multi-workspace 0.3.1

Run Codex CLI in Docker across saved single-folder or multi-folder workspaces.
Documentation
name: Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: read
  packages: write

jobs:
  validate-release:
    runs-on: ubuntu-latest
    outputs:
      crate_version: ${{ steps.version.outputs.crate_version }}
      image_changed: ${{ steps.image.outputs.changed }}
      image_version: ${{ steps.image.outputs.current_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check tag matches Cargo.toml version
        id: version
        run: |
          crate_version="$(python3 -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["package"]["version"])')"
          tag_version="${GITHUB_REF_NAME#v}"

          if [ "$crate_version" != "$tag_version" ]; then
            echo "Cargo.toml version ${crate_version} does not match tag ${GITHUB_REF_NAME}." >&2
            exit 1
          fi

          echo "crate_version=${crate_version}" >> "$GITHUB_OUTPUT"

      - name: Check Docker image version
        id: image
        run: |
          label_pattern='s/^LABEL[[:space:]]+org\.openai\.codex-ws\.image-version="([^"]+)".*/\1/p'
          current_version="$(sed -nE "$label_pattern" Dockerfile.codex-ws)"

          if [ -z "$current_version" ]; then
            echo "Dockerfile.codex-ws is missing LABEL org.openai.codex-ws.image-version." >&2
            exit 1
          fi

          current_commit="$(git rev-list -n 1 "$GITHUB_REF_NAME")"
          previous_tag="$(git describe --tags --abbrev=0 "${current_commit}^" 2>/dev/null || true)"

          if [ -z "$previous_tag" ]; then
            changed=true
          else
            previous_version="$(git show "${previous_tag}:Dockerfile.codex-ws" 2>/dev/null | sed -nE "$label_pattern" || true)"
            if [ "$current_version" = "$previous_version" ]; then
              changed=false
            else
              changed=true
            fi
          fi

          echo "changed=${changed}" >> "$GITHUB_OUTPUT"
          echo "current_version=${current_version}" >> "$GITHUB_OUTPUT"

  publish-image:
    runs-on: ubuntu-latest
    needs: validate-release
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Log in to GHCR
        if: needs.validate-release.outputs.image_changed == 'true'
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        if: needs.validate-release.outputs.image_changed == 'true'
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/honahec/codex-multi-workspace
          tags: |
            type=raw,value=latest
            type=ref,event=tag

      - name: Build and push image
        if: needs.validate-release.outputs.image_changed == 'true'
        uses: docker/build-push-action@v6
        with:
          context: .
          file: Dockerfile.codex-ws
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Skip unchanged image
        if: needs.validate-release.outputs.image_changed == 'false'
        run: |
          echo "Docker image version ${{ needs.validate-release.outputs.image_version }} is unchanged; skipping GHCR publish."

  publish-crate:
    runs-on: ubuntu-latest
    needs:
      - validate-release
      - publish-image
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Check crates.io version
        id: crates
        run: |
          status="$(curl -sS -o /tmp/codex-multi-workspace-version.json -w "%{http_code}" \
            -H "User-Agent: Honahec/codex-multi-workspace release workflow (https://github.com/Honahec/codex-multi-workspace)" \
            "https://crates.io/api/v1/crates/codex-multi-workspace/${{ needs.validate-release.outputs.crate_version }}")"
          if [ "$status" = "200" ]; then
            echo "exists=true" >> "$GITHUB_OUTPUT"
          elif [ "$status" = "404" ]; then
            echo "exists=false" >> "$GITHUB_OUTPUT"
          else
            echo "Unexpected crates.io response: $status" >&2
            exit 1
          fi

      - name: Publish crate
        if: steps.crates.outputs.exists == 'false'
        run: cargo publish --locked
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}