gridoxide 0.0.2

AC power flow and state estimation solver with a sparse Newton-Raphson core
Documentation
name: Publish to crates.io

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      dry_run:
        description: 'Package and verify everything without uploading'
        type: boolean
        default: true

env:
  CARGO_TERM_COLOR: always

jobs:
  publish:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    # Same tag trigger as pypi.yml, so one `v*` tag cuts both the wheels and
    # the crate. Nothing here depends on that workflow, though: a PyPI failure
    # shouldn't hold back the crate or vice versa.
    #
    # Holds the CARGO_REGISTRY_TOKEN secret, which is the point of the
    # environment — scoping the token to this one job, and letting a required
    # reviewer be added later without touching this file.
    environment: crate-release
    steps:
      # No `submodules:` — the two git submodules under tests/data/ hold only
      # test fixtures, and Cargo.toml's `exclude` keeps that whole directory
      # out of the packaged .crate anyway.
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@stable

      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-publish-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-publish-

      # A version published to crates.io can never be replaced, so catch a
      # mistyped tag here rather than by burning the wrong version number.
      # Skipped on workflow_dispatch, which has no tag to compare against.
      - name: Check tag matches crate version
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          set -euo pipefail
          version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
          tag="${GITHUB_REF_NAME#v}"
          if [ "$tag" != "$version" ]; then
            echo "::error::Tag $GITHUB_REF_NAME does not match Cargo.toml version $version"
            exit 1
          fi
          echo "Tag $GITHUB_REF_NAME matches Cargo.toml version $version"

      # Catches the two ways a .crate can be broken without any local build
      # noticing: a file the package needs that `exclude` dropped, and a
      # dependency that resolves locally but not from the registry. Verifies on
      # default features only, matching build.yml — `klu`/`pardiso` need a C
      # toolchain, libclang, or a local oneMKL install. That's a limit on what
      # this job *compiles*, not on what ships: the crate is source-only, so
      # every feature's source is published either way.
      - name: Package
        run: cargo package

      # The `cgmes` feature is the one whose dependencies come from outside
      # this repo (cimoxide's `cimoxide-decoder`/`cimoxide-structs`), so build
      # it from the packaged .crate rather than the working tree — that is what
      # a crates.io consumer actually gets. Pure Rust, so unlike `klu` it needs
      # nothing extra on the runner.
      - name: Check the packaged crate builds with --features cgmes
        run: |
          set -euo pipefail
          version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
          tar xzf "target/package/gridoxide-$version.crate" -C target/package
          cargo build --features cgmes --manifest-path "target/package/gridoxide-$version/Cargo.toml"

      # Everything above is the dry run: a `workflow_dispatch` with dry_run
      # left on exercises the whole packaging path and stops here, without
      # spending a version number.
      #
      # `--no-verify` because the `cargo package` step above already built the
      # very same .crate; re-verifying would just compile faer twice.
      - name: Publish
        if: ${{ github.event_name != 'workflow_dispatch' || !inputs.dry_run }}
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: cargo publish --no-verify