name: CI

on:
  push:
    branches: ["master"]
    tags: ["juniper*@*"]
  pull_request:
    branches: ["master"]

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

env:
  RUST_BACKTRACE: 1

jobs:

  ################
  # Pull Request #
  ################

  pr:
    if: ${{ github.event_name == 'pull_request'
            && !contains(github.event.head_commit.message, '[skip ci]') }}
    needs:
      - clippy
      - example
      - feature
      - release-check
      - rustfmt
      - test
      - wasm
    runs-on: ubuntu-latest
    steps:
      - run: true




  ##########################
  # Linting and formatting #
  ##########################

  clippy:
    if: ${{ github.ref == 'refs/heads/master'
            || startsWith(github.ref, 'refs/tags/juniper')
            || !contains(github.event.head_commit.message, '[skip ci]') }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          components: clippy

      - run: make cargo.lint

  rustfmt:
    if: ${{ github.ref == 'refs/heads/master'
            || startsWith(github.ref, 'refs/tags/juniper')
            || !contains(github.event.head_commit.message, '[skip ci]') }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: nightly
          components: rustfmt

      - run: make cargo.fmt check=yes




  ###########
  # Testing #
  ###########

  example:
    if: ${{ github.ref == 'refs/heads/master'
            || startsWith(github.ref, 'refs/tags/juniper')
            || !contains(github.event.head_commit.message, '[skip ci]') }}
    strategy:
      fail-fast: false
      matrix:
        example:
          - actix_subscriptions
          - basic_subscriptions
          - warp_async
          - warp_subscriptions
        os:
          - ubuntu
          - macOS
          - windows
        toolchain:
          - stable
          - beta
          - nightly
    runs-on: ${{ matrix.os }}-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: ${{ matrix.toolchain }}
          override: true

      - run: cargo check -p example_${{ matrix.example }}

  feature:
    if: ${{ github.ref == 'refs/heads/master'
            || startsWith(github.ref, 'refs/tags/juniper')
            || !contains(github.event.head_commit.message, '[skip ci]') }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - { feature: <none>, crate: juniper }
          - { feature: bigdecimal, crate: juniper }
          - { feature: bson, crate: juniper }
          - { feature: chrono, crate: juniper }
          - { feature: chrono-clock, crate: juniper }
          - { feature: chrono-tz, crate: juniper }
          - { feature: expose-test-schema, crate: juniper }
          - { feature: graphql-parser, crate: juniper }
          - { feature: rust_decimal, crate: juniper }
          - { feature: schema-language, crate: juniper }
          - { feature: serde_json, crate: juniper }
          - { feature: time, crate: juniper }
          - { feature: url, crate: juniper }
          - { feature: uuid, crate: juniper }
          - { feature: <none>, crate: juniper_actix }
          - { feature: subscriptions, crate: juniper_actix }
          - { feature: <none>, crate: juniper_warp }
          - { feature: subscriptions, crate: juniper_warp }
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true

      # TODO: Enable once MSRV is supported.
      #- run: cargo +nightly update -Z minimal-versions

      - run: cargo check -p ${{ matrix.crate }} --no-default-features
                   ${{ matrix.feature != '<none>'
                       && format('--features {0}', matrix.feature)
                       || '' }}
        env:
          RUSTFLAGS: -D warnings

  package:
    if: ${{ startsWith(github.ref, 'refs/tags/juniper') }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable

      - name: Parse crate name
        id: crate
        run: echo ::set-output
                  name=NAME::$(printf "$GITHUB_REF" | cut -d '/' -f3
                                                    | cut -d '@' -f1)

      - run: cargo package -p ${{ steps.crate.outputs.NAME }}

  test:
    if: ${{ github.ref == 'refs/heads/master'
            || startsWith(github.ref, 'refs/tags/juniper')
            || !contains(github.event.head_commit.message, '[skip ci]') }}
    strategy:
      fail-fast: false
      matrix:
        crate:
          - juniper_codegen
          - juniper
          - juniper_subscriptions
          - juniper_graphql_ws
          - juniper_integration_tests
          - juniper_codegen_tests
          - juniper_book_tests
          - juniper_actix
          - juniper_hyper
          - juniper_iron
          - juniper_rocket
          - juniper_warp
        os:
          - ubuntu
          - macOS
          - windows
        toolchain:
          - stable
          - beta
          - nightly
        exclude:
          - crate: juniper_codegen_tests
            toolchain: stable
          - crate: juniper_codegen_tests
            toolchain: beta
          - crate: juniper_codegen_tests
            os: macOS
          - crate: juniper_codegen_tests
            os: windows
          - crate: juniper_book_tests
            toolchain: beta
          - crate: juniper_book_tests
            toolchain: nightly
          # TODO: LLVM ERROR: out of memory
          - crate: juniper_integration_tests
            os: windows

    runs-on: ${{ matrix.os }}-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: ${{ matrix.toolchain }}
          override: true

      - run: make test.cargo crate=${{ matrix.crate }}

  wasm:
    if: ${{ github.ref == 'refs/heads/master'
            || startsWith(github.ref, 'refs/tags/juniper')
            || !contains(github.event.head_commit.message, '[skip ci]') }}
    strategy:
      fail-fast: false
      matrix:
        crate:
          - juniper_codegen
          - juniper
        toolchain:
          - stable
          - beta
          - nightly
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: ${{ matrix.toolchain }}
          target: wasm32-unknown-unknown
          override: true

      - run: cargo check --target wasm32-unknown-unknown -p ${{ matrix.crate }}




  #############
  # Releasing #
  #############

  release-check:
    name: Check release automation
    if: ${{ !startsWith(github.ref, 'refs/tags/juniper')
            && (github.ref == 'refs/heads/master'
                || !contains(github.event.head_commit.message, '[skip ci]')) }}
    strategy:
      fail-fast: false
      matrix:
        crate:
          - juniper_codegen
          - juniper
          - juniper_subscriptions
          - juniper_graphql_ws
          - juniper_actix
          - juniper_hyper
          - juniper_iron
          - juniper_rocket
          - juniper_warp
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable

      - run: cargo install cargo-release

      - run: make cargo.release crate=${{ matrix.crate }} ver=minor
                                exec=no install=no

  release-github:
    name: Release on GitHub
    needs:
      - clippy
      - example
      - feature
      - package
      - rustfmt
      - test
      - wasm
    if: ${{ startsWith(github.ref, 'refs/tags/juniper') }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Parse crate name
        id: crate
        run: echo ::set-output
                  name=NAME::$(printf "$GITHUB_REF" | cut -d '/' -f3
                                                    | cut -d '@' -f1)
      - name: Parse release version
        id: release
        run: echo ::set-output
                  name=VERSION::$(printf "$GITHUB_REF" | cut -d '@' -f2)

      - name: Verify release version matches crate's Cargo manifest
        run: >-
          test "${{ steps.release.outputs.VERSION }}" \
            == "$(grep -m1 'version = "' ${{ steps.crate.outputs.NAME }}/Cargo.toml | cut -d '"' -f2)"
      - name: Parse CHANGELOG link
        id: changelog
        run: echo ::set-output
                  name=LINK::https://github.com/${{ github.repository }}/blob/${{ steps.crate.outputs.NAME }}%40${{ steps.release.outputs.VERSION }}//${{ steps.crate.outputs.NAME }}/CHANGELOG.md#$(sed -n '/^## \[${{ steps.release.outputs.VERSION }}\]/{s/^## \[\(.*\)\][^0-9]*\([0-9].*\)/\1--\2/;s/[^0-9a-z-]*//g;p;}' ${{ steps.crate.outputs.NAME }}/CHANGELOG.md)

      - uses: softprops/action-gh-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          name: ${{ steps.crate.outputs.NAME }} ${{ steps.release.outputs.VERSION }}
          body: |
            [API docs](https://docs.rs/${{ steps.crate.outputs.NAME }}/${{ steps.release.outputs.VERSION }})
            [Changelog](${{ steps.changelog.outputs.LINK }})
          prerelease: ${{ contains(steps.release.outputs.VERSION, '-') }}

  release-crate:
    name: Release on crates.io
    needs: ["release-github"]
    if: ${{ startsWith(github.ref, 'refs/tags/juniper') }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable

      - name: Parse crate name
        id: crate
        run: echo ::set-output
                  name=NAME::$(printf "$GITHUB_REF" | cut -d '/' -f3
                                                    | cut -d '@' -f1)

      - name: Publish crate
        run: cargo publish -p ${{ steps.crate.outputs.NAME }}
                           --token ${{ secrets.CRATESIO_TOKEN }}




  ##########
  # Deploy #
  ##########

  deploy-book:
    name: deploy Book
    needs: ["test"]
    if: ${{ github.ref == 'refs/heads/master'
            || startsWith(github.ref, 'refs/tags/juniper@') }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: peaceiris/actions-mdbook@v1

      - run: make book.build out=gh-pages/master
        if: ${{ github.ref == 'refs/heads/master' }}

      - run: make book.build out=gh-pages
        if: ${{ startsWith(github.ref, 'refs/tags/juniper@') }}

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          keep_files: true
          publish_dir: book/gh-pages