# XIOM registry publish -- OIDC trusted publishing for a community package.
#
# WHAT THIS IS
#   A copy-paste GitHub Actions workflow that publishes your package to the
#   XIOM registry with no long-lived secret: GitHub mints a short-lived OIDC
#   token for this repo/workflow/ref, and the registry only accepts it if a
#   maintainer approved that exact combination.
#
# SETUP (once, ~5 minutes)
#   1. Save this file as .github/workflows/publish-registry.yml in your repo.
#   2. Request access on the registry website (Sign in -> Requests):
#        - Repository: <your-account>/<your-repo>
#        - Workflow:   publish-registry.yml
#        - Refs:       refs/tags/v*        (or refs/heads/main for branch runs)
#        - Scopes:     the `name` from package.xi (e.g. my-lib)
#   3. Wait for the approval (a maintainer reviews it; you get a notice).
#
# HOW TO RELEASE (recommended: tags)
#   git tag v0.1.0
#   git push origin v0.1.0
#   -> uncomment the `push: tags: ["v*"]` trigger below once, then every tag
#      publishes automatically and the workflow verifies the tag matches
#      `version:` in package.xi before publishing.
#
# HOW TO RELEASE (manual)
#   Actions -> "Publish to XIOM registry" -> Run workflow.
#   Use mode=publish, or mode=yank with the version to withdraw.
#
# Good to know
#   - The package is signed with the ed25519 key in the XIOM_SIGNING_KEY
#     secret if you set one; otherwise a fresh key is generated per run.
#     Set the secret for a stable "verified" badge on every release.
#   - `xiom pkg publish` packages the working directory. Keep build output,
#     test fixtures, and the downloaded toolchain out of the repository (this
#     workflow downloads the compiler into $RUNNER_TEMP for that reason).
#   - The registry refuses a version that already exists (409). Bump
#     `version:` in package.xi for every release.
name: Publish to XIOM registry

on:
  workflow_dispatch:
    inputs:
      registry:
        description: "Registry base URL"
        required: true
        default: "https://registry.xiom-lang.org"
      mode:
        description: "publish, or yank the version below"
        required: true
        type: choice
        options: [publish, yank]
        default: publish
      version:
        description: "Version to yank (mode=yank only)"
        required: false
  # Recommended for releases: uncomment to publish when a version tag is
  # pushed. The tag must match `version:` in package.xi (checked below).
  # push:
  #   tags: ["v*"]

permissions:
  contents: read

jobs:
  registry:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      id-token: write   # required: lets the job request the OIDC token
    env:
      # `github.event.inputs` is empty on tag runs; the defaults cover both.
      XIOM_REGISTRY: ${{ github.event.inputs.registry || 'https://registry.xiom-lang.org' }}
      XIOM_MODE: ${{ github.event.inputs.mode || 'publish' }}
      YANK_VERSION: ${{ github.event.inputs.version || '' }}
    steps:
      - name: Checkout
        uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4

      - name: Show target
        run: |
          set -euo pipefail
          if [ -z "${XIOM_REGISTRY:-}" ]; then
            echo "::error::the registry is empty; set it (production: https://registry.xiom-lang.org)"
            exit 1
          fi
          echo "TARGET registry: ${XIOM_REGISTRY}"
          echo "mode:            ${XIOM_MODE}"
          echo "repo:            ${GITHUB_REPOSITORY}"
          echo "ref:             ${GITHUB_REF}"
          echo "package.xi name: $(sed -n 's/.*name:[[:space:]]*"\([^"]*\)".*/\1/p' package.xi | head -n1)"

      - name: Check tag matches the package version
        run: |
          set -euo pipefail
          if [[ "${GITHUB_REF}" != refs/tags/* ]]; then
            echo "not a tag run; skipping the tag/version check"
            exit 0
          fi
          tag="${GITHUB_REF_NAME#v}"
          version="$(sed -n 's/.*version:[[:space:]]*"\([^"]*\)".*/\1/p' package.xi | head -n1)"
          if [ -z "$version" ]; then
            echo "::error::no version: found in package.xi"
            exit 1
          fi
          if [ "$tag" != "$version" ]; then
            echo "::error::tag v${tag} does not match package.xi version ${version}; bump the manifest or re-tag"
            exit 1
          fi
          echo "tag v${tag} matches package.xi version ${version}"

      - name: Mint registry token (GitHub OIDC)
        run: |
          set -euo pipefail
          if [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ]; then
            echo "OIDC request env is missing; the job needs id-token: write" >&2
            exit 1
          fi
          response="$(curl -fsS \
            -H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
            "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=xiom-registry")"
          token="$(printf '%s' "$response" | python3 -c 'import json,sys; print(json.load(sys.stdin)["value"])')"
          if [ -z "$token" ]; then
            echo "empty OIDC token in response" >&2
            exit 1
          fi
          echo "::add-mask::$token"
          echo "XIOM_REGISTRY_TOKEN=$token" >> "$GITHUB_ENV"
          echo "minted registry OIDC token (audience xiom-registry)"

      - name: Read package name
        id: pkg
        run: |
          set -euo pipefail
          name="$(sed -n 's/.*name:[[:space:]]*"\([^"]*\)".*/\1/p' package.xi | head -n1)"
          if [ -z "$name" ]; then
            echo "no name: found in package.xi" >&2
            exit 1
          fi
          echo "name=$name" >> "$GITHUB_OUTPUT"
          echo "package: $name"

      - name: Install toolchain
        if: env.XIOM_MODE == 'publish'
        run: |
          set -euo pipefail
          pin="v0.64.0"
          pv="${pin#v}"
          # Keep the toolchain OUT of the repository: `xiom pkg publish`
          # packages the working directory, so anything downloaded here would
          # be bundled into the package and blow the registry size limit.
          curl -fsSL -o "$RUNNER_TEMP/xiom-compiler.tar.gz" \
            "https://github.com/xiom-lang/xiom/releases/download/${pin}/xiom-${pv}-linux-x64.tar.gz"
          mkdir -p "$RUNNER_TEMP/compiler"
          tar -xzf "$RUNNER_TEMP/xiom-compiler.tar.gz" -C "$RUNNER_TEMP/compiler"
          test -x "$RUNNER_TEMP/compiler/bin/xiom"
          echo "$RUNNER_TEMP/compiler/bin" >> "$GITHUB_PATH"

      - name: Prepare signing key
        if: env.XIOM_MODE == 'publish'
        env:
          XIOM_SIGNING_KEY: ${{ secrets.XIOM_SIGNING_KEY }}
        run: |
          set -euo pipefail
          mkdir -p "$HOME/.xiom/keys"
          if [ -n "${XIOM_SIGNING_KEY:-}" ]; then
            printf '%s' "$XIOM_SIGNING_KEY" | tr -d '\r\n' > "$HOME/.xiom/keys/default.key"
            printf '\n' >> "$HOME/.xiom/keys/default.key"
            chmod 600 "$HOME/.xiom/keys/default.key"
            key_len="$(tr -d '\r\n' < "$HOME/.xiom/keys/default.key" | wc -c)"
            if [ "$key_len" -ne 64 ]; then
              echo "XIOM_SIGNING_KEY must be 64 hex chars (got $key_len)" >&2
              exit 1
            fi
            echo "using the XIOM_SIGNING_KEY secret"
          else
            echo "::warning::no XIOM_SIGNING_KEY secret; generating an ephemeral key for this run"
            xiom pkg keygen
          fi

      - name: Publish
        if: env.XIOM_MODE == 'publish'
        run: |
          set -euo pipefail
          echo "publishing to ${XIOM_REGISTRY}"
          xiom pkg publish
          echo "published ${{ steps.pkg.outputs.name }} -- see ${XIOM_REGISTRY}/packages/${{ steps.pkg.outputs.name }}"

      - name: Yank
        if: env.XIOM_MODE == 'yank'
        run: |
          set -euo pipefail
          version="${YANK_VERSION:-${GITHUB_REF_NAME#v}}"
          if [ -z "${version}" ]; then
            echo "mode=yank requires the version input (e.g. 0.1.0)" >&2
            exit 1
          fi
          curl -fsS -X POST \
            -H "Authorization: Bearer ${XIOM_REGISTRY_TOKEN}" \
            -H "Content-Type: application/json" \
            -d '{"reason":"withdrawn"}' \
            "${XIOM_REGISTRY}/packages/${{ steps.pkg.outputs.name }}/${version}/yank"
          echo "yanked ${{ steps.pkg.outputs.name }}@${version} on ${XIOM_REGISTRY}"
