sql-forge 0.3.2

Proc-macro combining compile-time SQL validation with a runtime QueryBuilder for dynamic queries using named parameters.
Documentation
name: sql-forge CI

on:
  pull_request:
    paths-ignore:
      - 'README.md'
      - '.gitignore'
  push:
    branches: [main]
    tags: ['v*']
    paths-ignore:
      - 'README.md'
      - '.gitignore'

env:
  CARGO_TERM_COLOR: always
  SQLX_OFFLINE: "false"

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        db-type: [mysql, postgres, sqlite]

    services:
      mysql:
        image: ${{ matrix.db-type == 'mysql' && 'mysql:8.0.45' || '' }}
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: sql_forge_test
        ports:
          - 3306:3306
      postgres:
        image: ${{ matrix.db-type == 'postgres' && 'postgres:18.4' || '' }}
        env:
          POSTGRES_PASSWORD: root
          POSTGRES_DB: sql_forge_test
        ports:
          - 5432:5432

    env:
      ENV_DB_TYPE: ${{ matrix.db-type }}
      DATABASE_URL: ${{ matrix.db-type == 'mysql' && 'mysql://root:root@127.0.0.1:3306/sql_forge_test' || matrix.db-type == 'postgres' && 'postgres://postgres:root@127.0.0.1:5432/sql_forge_test' || format('sqlite:{0}/sql_forge_test.db', github.workspace) }}
      SQL_FORGE_DB_TYPE: ${{ matrix.db-type == 'mysql' && 'sqlx::MySql' || matrix.db-type == 'postgres' && 'sqlx::Postgres' || 'sqlx::Sqlite' }}
      SQLX_ENV_DIR: tests/${{ matrix.db-type }}/.sqlx

    steps:
      - uses: actions/checkout@v6

      - name: Rust Cache
        uses: swatinem/rust-cache@v2
        with:
          cache-targets: "false"

      - name: Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
            default-libmysqlclient-dev \
            libpq-dev \
            mysql-client \
            pkg-config \
            postgresql-client

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: 1.95.0
          components: rustfmt, clippy

      - name: Wait for database and init
        run: |
          if [ "${{ matrix.db-type }}" = "mysql" ]; then
            until mysqladmin ping -h 127.0.0.1 -uroot -proot --silent; do
              echo "waiting for mysql..."
              sleep 2
            done
            mysql -h 127.0.0.1 -uroot -proot sql_forge_test < tests/init.sql
          elif [ "${{ matrix.db-type }}" = "postgres" ]; then
            until pg_isready -h 127.0.0.1 -U postgres; do
              echo "waiting for postgres..."
              sleep 2
            done
            PGPASSWORD=root psql -h 127.0.0.1 -U postgres -d sql_forge_test -f tests/init-pg.sql
          else
            sqlite3 sql_forge_test.db < tests/init-sqlite.sql
          fi

      - name: Copy .sqlx metadata for this database type
        run: cp -r tests/${{ matrix.db-type }}/.sqlx .sqlx

      - name: Install sqlx-cli
        run: cargo install sqlx-cli --no-default-features --features native-tls,mysql,postgres,sqlite

      - name: Install cargo-expand
        run: cargo install cargo-expand

      - name: cargo expand and compare with repo copy
        run: |
          cargo expand --test tests > tests/${{ matrix.db-type }}/tests_expanded.ci.rs
          if ! diff -q tests/${{ matrix.db-type }}/tests_expanded.rs tests/${{ matrix.db-type }}/tests_expanded.ci.rs; then
            echo "ERROR: tests/${{ matrix.db-type }}/tests_expanded.rs is outdated."
            echo "Regenerate it by running in the rust service from the docker-compose file: sql-forge-test"
            diff tests/${{ matrix.db-type }}/tests_expanded.rs tests/${{ matrix.db-type }}/tests_expanded.ci.rs
            exit 1
          fi

      - name: cargo check
        run: cargo check --tests

      - name: cargo fmt
        run: cargo fmt --all -- --check

      - name: cargo clippy
        run: cargo clippy --tests -- -D warnings

      - name: Prepare trybuild tmp-ui fixtures
        run: |
          TMP_UI_DIR="tests/${{ matrix.db-type }}/tmp-ui"
          UI_SRC_DIR="tests/ui"
          UI_COMMON_DIR="tests/${{ matrix.db-type }}/ui-common"

          rm -rf "$TMP_UI_DIR"
          mkdir -p "$TMP_UI_DIR"

          cp -r "$UI_SRC_DIR/." "$TMP_UI_DIR/"

          if [ -d "$UI_COMMON_DIR" ]; then
            (
              cd "$UI_COMMON_DIR"
              find . -type f -name '*.stderr' -exec cp --parents {} "$GITHUB_WORKSPACE/$TMP_UI_DIR" \;
            )
          fi

      - name: cargo test
        run: cargo test

      - name: Verify that .sqlx/ files are up to date
        run: cargo sqlx prepare --check -- --tests

  publish:
    runs-on: ubuntu-latest
    needs: test
    if: startsWith(github.ref, 'refs/tags/v')

    steps:
      - uses: actions/checkout@v6

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

      - name: Verify all crate versions match the tag
        run: |
          TAG_VERSION="${GITHUB_REF_NAME#v}"
          WORKSPACE_VERSION=$(awk '
            $0 == "[workspace.package]" { in_workspace_package = 1; next }
            /^\[/ { in_workspace_package = 0 }
            in_workspace_package && /^version = / {
              gsub(/^version = "/, "")
              gsub(/"$/, "")
              print
              exit
            }
          ' Cargo.toml)

          for manifest in Cargo.toml sql-forge-macro/Cargo.toml; do
            if ! grep -q '^version\.workspace = true$' "$manifest"; then
              echo "ERROR: $manifest must inherit the workspace version."
              exit 1
            fi
          done

          if [ "$TAG_VERSION" != "$WORKSPACE_VERSION" ]; then
            echo "ERROR: Tag version ($TAG_VERSION) does not match workspace version ($WORKSPACE_VERSION)."
            exit 1
          fi

      - name: Dry run publish sql-forge-macro
        run: cargo publish -p sql-forge-macro --dry-run

      - name: Publish sql-forge-macro
        run: cargo publish -p sql-forge-macro
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for sql-forge-macro to be indexed
        run: sleep 30

      - name: Dry run publish sql-forge
        run: cargo publish -p sql-forge --dry-run --no-verify

      - name: Publish sql-forge
        run: cargo publish -p sql-forge --no-verify
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}