Release

# GitHub Actions Workflow created for handling the release process based on the draft release prepared with the Build workflow.
# Running the publishPlugin task requires all-following secrets to be provided: PUBLISH_TOKEN, PRIVATE_KEY, PRIVATE_KEY_PASSWORD, CERTIFICATE_CHAIN.
# See https://plugins.jetbrains.com/docs/intellij/plugin-signing.html for more information.

name: Release

on:
  release:
    types: [ released ]

env:
  AUTO_SNAPSHOT_VERSION: false

jobs:

  # Prepare and publish the plugin to the Marketplace repository
  release:
    name: Publish Plugin
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:

      # Check out the current repository
      - name: Fetch Sources
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.release.tag_name }}

      # Set up Java environment for the next steps
      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          distribution: zulu
          java-version: 11

      # Setup Gradle
      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v3
        with:
          gradle-home-cache-cleanup: true

      # Set environment variables
      - name: Export Properties
        id: properties
        shell: bash
        run: |
          PROPERTIES="$(./gradlew properties --console=plain -q)"
          PLUGIN_VERSION="$(echo "$PROPERTIES" | grep "^pluginVersion:" | cut -f2- -d ' ')"
          PLUGIN_PRERELEASE_VERSION="$(echo "$PROPERTIES" | grep "^pluginPreReleaseVersion:" | cut -f2- -d ' ')"
          PLUGIN_BUILD_METADATA="$(echo "$PROPERTIES" | grep "^pluginBuildMetadata:" | cut -f2- -d ' ')"
          CHANGELOG="$(cat << 'EOM' | sed -e "/## What's Changed/d" -e 's/^[[:space:]]*$//g' -e '/./,$!d'
          ${{ github.event.release.body }}
          EOM
          )"

          echo "pluginVersion: $PLUGIN_VERSION"
          echo "pluginPrereleaseVersion: $PLUGIN_PRERELEASE_VERSION"
          echo "pluginBuildMetadata: $PLUGIN_BUILD_METADATA"
          echo "changelog:"
          echo "$CHANGELOG"

          echo "pluginVersion=$PLUGIN_VERSION" >> $GITHUB_OUTPUT
          echo "pluginPrereleaseVersion=$PLUGIN_PRERELEASE_VERSION" >> $GITHUB_OUTPUT
          echo "pluginBuildMetadata=$PLUGIN_BUILD_METADATA" >> $GITHUB_OUTPUT

          echo 'changelog<<EOF' >> $GITHUB_OUTPUT
          echo "$CHANGELOG" >> $GITHUB_OUTPUT
          echo 'EOF' >> $GITHUB_OUTPUT

      # Update the unreleased section with the current release note
      - name: Patch Changelog
        if: steps.properties.outputs.pluginBuildMetadata == '' && steps.properties.outputs.changelog != ''
        env:
          CHANGELOG: ${{ steps.properties.outputs.changelog }}
        run: |
          ./gradlew patchChangelog --release-note="$CHANGELOG"

      # Publish the plugin to the Marketplace
      - name: Publish Plugin
        env:
          PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
          CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }}
          PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
          PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }}
        run: ./gradlew publishPlugin

      # Upload artifact as a release asset
      - name: Upload Release Asset
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: gh release upload v${{ steps.properties.outputs.pluginVersion }} ./build/distributions/*

      # Create pull request
      - name: Create Pull Request
        if: steps.properties.outputs.pluginBuildMetadata == '' && steps.properties.outputs.changelog != ''
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          VERSION="${{ github.event.release.tag_name }}"
          BRANCH="changelog-update-$VERSION"
          LABEL="release changelog 📑"

          git config user.email "action@github.com"
          git config user.name "GitHub Action"

          git checkout -b $BRANCH
          git commit -am ":rocket: ${VERSION}" -m "[skip ci]"
          git push --set-upstream origin $BRANCH

          gh label create "$LABEL" \
            --color B39DDB \
            --description "Pull requests with release changelog update" \
            || true

          gh pr create \
            --title ":rocket: \`$VERSION\`" \
            --body "Current pull request contains patched \`CHANGELOG.md\` file for the \`$VERSION\` version." \
            --base "${{ github.event.release.target_commitish }}" \
            --label "$LABEL" \
            --head $BRANCH

      # Close the milestone
      - name: Close Milestone
        if: steps.properties.outputs.pluginPrereleaseVersion == '' && steps.properties.outputs.pluginBuildMetadata == ''
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh api repos/{owner}/{repo}/milestones \
            --jq '.[] | select(.title == "${{ github.event.release.tag_name }}") | .number' \
            | xargs -I '{}' gh api -X PATCH repos/{owner}/{repo}/milestones/{} -F state='closed'

      # Delete the release if the version has build metadata
      - name: Delete Release
        if: steps.properties.outputs.pluginBuildMetadata != ''
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh api repos/{owner}/{repo}/releases \
            --jq ".[] | select(.tag_name == \"${{ github.event.release.tag_name }}\") | .id" \
            | xargs -I '{}' gh api -X DELETE repos/{owner}/{repo}/releases/{}


Snapshot

# GitHub Actions Workflow created for handling the release process based on the draft release prepared
# with the Build workflow. Running the publishPlugin task requires the PUBLISH_TOKEN secret provided.

name: Snapshot

on:
  # Trigger the workflow on pushes to only the 'master' branch (this avoids duplicate checks being run e.g. for dependabot pull requests)
  push:
    branches:
      - '**/snapshot/*'

env:
  AUTO_SNAPSHOT_VERSION: true

jobs:

  # Prepare and publish the plugin to the snapshot channel of the Marketplace repository
  release:
    name: Publish Plugin Snapshot
    runs-on: ubuntu-latest
    steps:

      # Check out current repository
      - name: Fetch Sources
        uses: actions/checkout@v4

      # Setup Java 11 environment for the next steps
      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          distribution: zulu
          java-version: 11
          cache: gradle

      # Set environment variables
      - name: Export Properties
        id: properties
        shell: bash
        run: |
          PROPERTIES="$(./gradlew properties --console=plain -q)"
          PUBLISH_CHANNEL="$(echo "$PROPERTIES" | grep "^publishChannel:" | cut -f2- -d ' ')"

          echo "publishChannel=$PUBLISH_CHANNEL" >> $GITHUB_OUTPUT

      # Publish the plugin to the Marketplace
      - name: Publish Plugin
        if: steps.properties.outputs.publishChannel == 'snapshot'
        env:
          PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
        run: ./gradlew publishPlugin

      # Prepare plugin archive content for creating artifact
      - name: Prepare Plugin Artifact
        id: artifact
        if: steps.properties.outputs.publishChannel == 'snapshot'
        shell: bash
        run: |
          cd ${{ github.workspace }}/build/distributions
          FILENAME=`ls *.zip`
          unzip "$FILENAME" -d content
          echo "filename=${FILENAME:0:-4}" >> $GITHUB_OUTPUT

      # Store already-built plugin as an artifact for downloading
      - name: Upload artifact
        if: steps.properties.outputs.publishChannel == 'snapshot'
        uses: actions/upload-artifact@v4
        with:
          name: ${{ steps.artifact.outputs.filename }}
          path: ./build/distributions/content/*/*


CI

name: CI
on:
    push:
        branches:
            - main
    pull_request:
concurrency:
    # Limit concurrency to 1 for PRs. 'main' concurrency isn't limited.
    group: ${{ github.head_ref || github.run_id }}
    cancel-in-progress: true
env:
    CI: true
    JAVA_VERSION: 17
jobs:
    checks:
        name: Checks
        runs-on: macos-latest
        timeout-minutes: 30
        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-java@v4
              with:
                  distribution: 'zulu'
                  java-version: ${{ env.JAVA_VERSION }}

            - name: Check Gradle wrapper
              uses: gradle/actions/wrapper-validation@v3

            - name: Check style
              run: ./gradlew spotlessCheck

            - name: Check lint
              run: ./gradlew lint

            - name: Check binary compatibility
              run: ./gradlew apiCheck || { exit_code=$?; printf "*** This PR changes Coil's public API. If this change is intentional, run './gradlew apiDump' and commit the new API files. ***"; exit $exit_code; }

    unit-tests:
        name: Unit tests
        runs-on: macos-latest
        timeout-minutes: 60
        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-java@v4
              with:
                  distribution: 'zulu'
                  java-version: ${{ env.JAVA_VERSION }}
            - uses: gradle/actions/setup-gradle@v3
              continue-on-error: true
              timeout-minutes: 5
              with:
                  cache-overwrite-existing: true
                  gradle-home-cache-cleanup: true

            - name: Unit tests
              run: ./gradlew allTests testDebugUnitTest validateDebugScreenshotTest verifyPaparazziDebug verifyRoborazziDebug verifyRoborazziJvm

    instrumentation-tests:
        name: Instrumentation tests
        runs-on: ubuntu-latest
        timeout-minutes: 90
        strategy:
            fail-fast: true
            matrix:
                api-level: [22, 26, 31]
        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-java@v4
              with:
                  distribution: 'zulu'
                  java-version: ${{ env.JAVA_VERSION }}
            - uses: gradle/actions/setup-gradle@v3
              continue-on-error: true
              timeout-minutes: 5
              with:
                  cache-overwrite-existing: true
                  gradle-home-cache-cleanup: true

            # API 30+ emulators only have x86_64 system images.
            - name: Get AVD info
              uses: ./.github/actions/get-avd-info
              id: avd-info
              with:
                  api-level: ${{ matrix.api-level }}

            - name: Enable KVM
              run: |
                  echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
                  sudo udevadm control --reload-rules
                  sudo udevadm trigger --name-match=kvm

            - name: Instrumentation tests
              uses: reactivecircus/android-emulator-runner@v2
              with:
                  api-level: ${{ matrix.api-level }}
                  arch: ${{ steps.avd-info.outputs.arch }}
                  target: ${{ steps.avd-info.outputs.target }}
                  script: mv .github/debug.keystore ~/.android; ./gradlew connectedDebugAndroidTest

    build-samples:
        name: Build samples
        runs-on: macos-latest
        timeout-minutes: 30
        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-java@v4
              with:
                  distribution: 'zulu'
                  java-version: ${{ env.JAVA_VERSION }}
            - uses: gradle/actions/setup-gradle@v3
              continue-on-error: true
              timeout-minutes: 5
              with:
                  cache-overwrite-existing: true
                  gradle-home-cache-cleanup: true

            - name: Build samples
              run: ./gradlew samples:compose:assembleDebug samples:view:assembleDebug

            - name: Build iOS app
              run: xcodebuild -project samples/compose/iosApp/iosApp.xcodeproj -scheme iosApp -destination 'platform=iOS Simulator,name=iPhone 14,OS=latest'

    deploy-snapshot:
        name: Deploy snapshot
        runs-on: macos-latest
        timeout-minutes: 60
        if: github.repository == 'coil-kt/coil' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.x')
        needs: [checks, unit-tests, instrumentation-tests, build-samples]
        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-java@v4
              with:
                  distribution: 'zulu'
                  java-version: ${{ env.JAVA_VERSION }}

            - name: Deploy snapshot
              env:
                  ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
                  ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
              run: ./gradlew publishToMavenCentral

    deploy-docs:
        name: Deploy docs
        runs-on: ubuntu-latest
        timeout-minutes: 60
        if: github.repository == 'coil-kt/coil' && github.ref == 'refs/heads/main'
        needs: [checks, unit-tests, instrumentation-tests, build-samples]
        steps:
            - uses: actions/checkout@v4
            - uses: actions/setup-java@v4
              with:
                  distribution: 'zulu'
                  java-version: ${{ env.JAVA_VERSION }}
            - uses: actions/setup-python@v5
              with:
                  python-version: '3.x'

            - name: Deploy docs
              run: |
                  git config user.name 'github-actions[bot]' && git config user.email 'github-actions[bot]@users.noreply.github.com'
                  pip3 install --upgrade pip && pip3 install mkdocs-material mkdocs-material-extensions mkdocs-minify-plugin
                  ./deploy_docs.sh


# Thanks to https://github.com/coil-kt/coil/blob/master/.github/workflows/ci.yml
name: CI

on:
  push:
    tags:
      - '*'
  pull_request:

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 100

      - uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: |
            17

      - name: Copy CI gradle.properties
        run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v3

      - name: Validate gradle wrapper
        uses: gradle/actions/wrapper-validation@v3

      - name: Build Debug
        run: ./gradlew clean app:assembleDebug

      - name: Run Lint
        if: github.event_name  == 'pull_request'
        run: ./gradlew lintDebug

      - name: Detekt
        if: github.event_name  == 'pull_request'
        run: ./gradlew detekt

      - name: Setup Ruby
        if: github.event_name  == 'pull_request'
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.0'
          bundler-cache: true

      - name: Run Danger
        if: github.event_name  == 'pull_request'
        run: |
          gem install danger
          bundle exec danger --dangerfile=Dangerfile --danger_id=danger-pr
        env:
          DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Prepare Keystore and Local.
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          echo "${{ secrets.KEYSTORE }}" > opensource.jks.asc
          gpg -d --passphrase "${{ secrets.KEYSTORE_PASSPHRASE }}" --batch "opensource.jks.asc" > "app/opensource.jks"
          echo "${{ secrets.SIGNING_GRADLE }}" > signing.gradle.asc
          gpg -d --passphrase "${{ secrets.KEYSTORE_PASSPHRASE }}" --batch "signing.gradle.asc" > "app/signing.gradle"
          echo "openSource.signing.file=signing.gradle" >> local.properties

      - name: Build Release App
        if: startsWith(github.ref, 'refs/tags/')
        run: ./gradlew app:assembleRelease app:bundleRelease

      - name: Prepare Sonatype Gradle properties
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          echo "${{ secrets.SONATYPE_GPG_FILE }}" > opensource.gpg.asc
          gpg -d --passphrase "${{ secrets.KEYSTORE_PASSPHRASE }}" --batch "opensource.gpg.asc" > "$HOME/.gradle/opensource.gpg"
          echo "sonatype.gpg.key=${{ secrets.SONATYPE_GPG_KEY }}" >> ~/.gradle/gradle.properties
          echo "sonatype.gpg.password=${{ secrets.SONATYPE_GPG_PASS }}" >> ~/.gradle/gradle.properties
          echo "sonatype.gpg.secretKeyRingFile=$HOME/.gradle/opensource.gpg" >> ~/.gradle/gradle.properties

      - name: Relase Sonatype
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          ./gradlew build -x test -x lint
          ./gradlew publishReleasePublicationToSonatypeRepository -x test -x lint -Plibrarytypefaceonly --no-configure-on-demand --no-parallel
          ./gradlew publishReleasePublicationToSonatypeRepository -x test -x lint -Plibrarycoreonly --no-configure-on-demand --no-parallel
          ./gradlew publishReleasePublicationToSonatypeRepository -x test -x lint -Plibraryviewsonly --no-configure-on-demand --no-parallel
          ./gradlew publishReleasePublicationToSonatypeRepository -x test -x lint -Plibrarycomposeonly --no-configure-on-demand --no-parallel
        env:
          NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }}
          NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}

      - name: Collect artifacts
        run: |
          COLLECT_PWD=${PWD}
          mkdir -p "artifacts"
          find . -name "*.apk" -type f -exec cp {} "artifacts" \;
          find . -name "*.aab" -type f -exec cp {} "artifacts" \;

      - name: Archive Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: "App-Artifacts"
          path: artifacts/*

      - name: Build Changelog
        id: github_release
        uses: mikepenz/release-changelog-builder-action@v4
        if: startsWith(github.ref, 'refs/tags/')
        with:
          configuration: ".github/config/configuration.json"
          ignorePreReleases: ${{ !contains(github.ref, '-') }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Release
        uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87
        if: startsWith(github.ref, 'refs/tags/')
        with:
          body: ${{steps.github_release.outputs.changelog}}
          prerelease: ${{ contains(github.event.inputs.version, '-rc') || contains(github.event.inputs.version, '-b') || contains(github.event.inputs.version, '-a') }}
          files: artifacts/*
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


detekt

# This workflow performs a static analysis of your Kotlin source code using detekt.
#
# Scans are triggered on every pull request targeting the main branch.

name: detekt

on:
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

  # Triggers the workflow on pull request events targeting the main branch
  pull_request:
    branches:
      - main

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "detekt"
  detekt:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    steps:
      # Checks out your repository under $GITHUB_WORKSPACE, so your job can access it
      - name: "checkout"
        uses: actions/checkout@v2
      # Determines changed files
      - name: "changed-files"
        run: |
          git fetch origin main:refs/remotes/origin/main
          CHANGED_FILES=$(git --no-pager diff --name-only --diff-filter=dr origin/main HEAD | grep '\(.*.kt\|.*.kts\)$' | tr '\n' , | rev | cut -c 2- | rev)
          echo "Changed files: $CHANGED_FILES"
          echo "CHANGED_FILES=$CHANGED_FILES" >> "$GITHUB_ENV"
      # Resolves and installs a specific version of detekt on to a GitHub Actions Runner if not already present in the tool cache
      - name: "set-up-detekt"
        uses: peter-murray/setup-detekt@v2
        with:
          detekt_version: 1.23.5
      # Runs detekt on changed files
      - name: "run-detekt"
        run: |
          if [ -z "$CHANGED_FILES" ]
          then
            echo "No changed Kotlin files found."
            exit 0
          fi

          echo "Running detekt check..."
          DETEKT_ISSUES=$(detekt-cli --build-upon-default-config --config detekt/detekt-config.yml --plugins detekt/detekt-formatting-1.23.5.jar --input $CHANGED_FILES)
          if [ -n "$DETEKT_ISSUES" ]; then
            echo "***********************************************"
            echo "$DETEKT_ISSUES"
            echo "***********************************************"
            echo "                 detekt failed                 "
            echo "        Please fix the above issues            "
            echo "***********************************************"
            exit 1
          fi


Build documentation website

name: Build documentation website

on: [workflow_dispatch]

permissions:
  id-token: write

env:
  INSTANCE: 'Writerside/hi'
  ARTIFACT: 'webHelpHI2-all.zip'
  DOCKER_VERSION: '241.16003'

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
        pull-requests: write
        contents: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Build docs using Writerside Docker builder
        uses: JetBrains/writerside-github-action@v4
        with:
          instance: ${{ env.INSTANCE }}
          artifact: ${{ env.ARTIFACT }}
          docker-version: ${{ env.DOCKER_VERSION }}
          location: documentation-website

      - name: Remove docs folder
        run: rm -rf docs

      - name: Unzip artifact
        run: unzip -O UTF-8 -qq 'artifacts/${{ env.ARTIFACT }}' -d docs

      - name: Revert logos
        run: git checkout docs/logo-dark.png docs/logo-light.png docs/site.webmanifest

      - name: Remove artifacts
        run: rm -rf artifacts

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v6
        with:
          branch: documentation-website
          title: 'docs: Update documentation website'
          commit-message: Update documentation website


Validate Gradle Wrapper

name: "Validate Gradle Wrapper"
on: [push, pull_request]
jobs:
  validation:
    name: "Validation"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: gradle/wrapper-validation-action@v1


name: "Validate Gradle Wrapper"

on: [push, pull_request]

jobs:
  validation:
    name: "Validation"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
      - uses: gradle/actions/wrapper-validation@v3


Publish release build

name: Publish release build

on:
  push:
    tags:
      - '*.*.*'

jobs:
  publish:
    runs-on: ubuntu-latest
    if: github.repository == 'pinterest/ktlint'

    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
        with:
          ref: 'master'

      - uses: ./.github/actions/setup-gradle-build

      - name: Build executable and publish to Maven
        run: ./gradlew clean shadowJarExecutable publishMavenPublicationToMavenCentralRepository --no-daemon --no-parallel --no-configuration-cache
        env:
          SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
          SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
          ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEY }}
          ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEYID }}
          ORG_GRADLE_PROJECT_signingKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }}

      - name: Validate changelog.md
        id: validate_changelog
        if: ${{ success() }}
        run: |
          FIRST_HEADING2=$(grep "^## " CHANGELOG.md | head -n 1)
          echo "First heading2 found: $FIRST_HEADING2"
          if [[ ! "$FIRST_HEADING2" =~ ^##[[:space:]]\[[0-9]+\.[0-9]+\.[0-9]+\][[:space:]]-[[:space:]][0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
            echo "First heading at level 2 should match '## [X.Y.Z] - [YYYY-MM-DD]'"
            exit 1
          fi

      - name: Extract release notes
        id: release_notes
        if: ${{ success() }}
        uses: ffurrer2/extract-release-notes@v2

      - name: Get version
        id: get_version
        if: ${{ success() }}
        run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV

      - name: Create zip for dependency manager(s)
        if: ${{ success() }}
        run: |
          cd ktlint-cli/build/run
          mkdir -p ktlint-${{ env.version }}/bin
          cp ktlint ktlint-${{ env.version }}/bin
          zip -rm ktlint-${{ env.version }}.zip ktlint-${{ env.version }}

      - name: Create release
        id: github_release
        if: ${{ success() }}
        uses: softprops/action-gh-release@v2
        with:
          draft: false
          prerelease: false
          body: ${{ steps.release_notes.outputs.release_notes }}
          files: |
            ktlint-cli/build/run/*
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Bump Homebrew Formula
        if: ${{ success() }}
        uses: mislav/bump-homebrew-formula-action@v2
        env:
          COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TOKEN }}
        with:
          formula-name: ktlint
          formula-path: Formula/k/ktlint.rb
          download-url: https://github.com/pinterest/ktlint/releases/download/${{ env.version }}/ktlint-${{ env.version }}.zip

      - name: Update Release documentation
        if: ${{ success() }}
        run: |
          git config user.email "<>" |
          git config user.name "Ktlint Release Workflow" |
          ./.announce -y
        env:
          VERSION: ${{ env.version }}


Publish release documentation

name: Publish release documentation

on:
  push:
    branches: ['master']
    paths: ['documentation/release-latest/**']

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.repository == 'pinterest/ktlint'
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
        with:
          fetch-depth: 0 # fetch all commits/branches
      - uses: actions/setup-python@v5
        with:
          python-version: 3.x

      - name: Install mkdocs and mike
        run: pip install mkdocs-material mike

      - name: Config git
        run: |
          git config user.email "ktlint@github.com"
          git config user.name "Ktlint Release Workflow"

      - name: Get last released version
        run: echo "version=$(git describe --abbrev=0 --tags)" >> $GITHUB_ENV

      - name: Deploy release docs
        run: |
          echo "Deploy release docs to version ${{ env.version }}"
          cd documentation/release-latest
          # Release docs are versioned so that user can use relevant docs for the ktlint version they use
          mike deploy --push --update-aliases ${{ env.version }} latest

      - name: Update default release docs
        run: |
          cd documentation/release-latest
          mike set-default --push latest


Publish snapshot build

name: Publish snapshot build

on:
  push:
    branches: [master]
    paths: ['**/*.kt', '**/*.kts', '**/*.properties', '**/*.toml']

env:
  SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
  SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
  ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEY }}
  ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEYID }}
  ORG_GRADLE_PROJECT_signingKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }}

jobs:
  publish:
    runs-on: ubuntu-latest
    if: github.repository == 'pinterest/ktlint'
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

      - uses: ./.github/actions/setup-gradle-build

      - name: Publish snapshot to Maven
        run: ./gradlew clean publishMavenPublicationToMavenCentralRepository --no-daemon --no-parallel --no-configuration-cache

      - name: Publish Kotlin-dev snapshot to Maven
        run: ./gradlew -PkotlinDev clean publishMavenPublicationToMavenCentralRepository --no-daemon --no-parallel --no-configuration-cache


Publish snapshot documentation

name: Publish snapshot documentation

on:
  push:
    branches: ['master']
    paths: ['documentation/snapshot/**']

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.repository == 'pinterest/ktlint'
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
        with:
          fetch-depth: 0 # fetch all commits/branches
      - uses: actions/setup-python@v5
        with:
          python-version: 3.x

      - name: Install mkdocs and mike
        run: pip install mkdocs-material mike

      - name: Config git
        run: |
          git config user.email "ktlint@github.com"
          git config user.name "Ktlint Release Workflow"

      - run: |
          cd documentation/snapshot
          # The dev-snapshot version has no release number as the version will only be tagged when an official release is build
          # This also prevents that multiple snapshot versions of the documentation will be published in parallel
          mike deploy --push dev-snapshot


Pull request

# name must be identical to name of 'pull-request-with-code'
name: Pull request

on:
  push:
    branches: ['master']
    paths:
      - '**/*'
      - '!**/*.kt'
      - '!**/*.kts'
      - '!**/*.properties'
      - '!**/*.toml'
  pull_request:
    paths:
      - '**/*'
      - '!**/*.kt'
      - '!**/*.kts'
      - '!**/*.properties'
      - '!**/*.toml'

# Add a dummy job that return true so that a PR not containing any code can be merged to master
# Note that all "jobs" (build, tests) including "jobs.*.runs-on" should be kept in sync with "pull-request-with-code"
jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    name: "[build] OS=${{ matrix.os }} Kotlin=stable"
    steps:
      - run: 'echo "No build required"'

  build-dev:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    name: "[build] OS=${{ matrix.os }}, Kotlin=dev"
    steps:
      - run: 'echo "No build required"'

  tests:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        jdk: [ 8, 11, 17 ]
    runs-on: ${{ matrix.os }}
    name: "[tests] OS=${{ matrix.os }}, Java=${{ matrix.jdk }}"
    steps:
      - run: 'echo "No build required"'


# name must be identical to name of 'pull-request-without-code'
name: Pull request

on:
  push:
    branches: ['master']
    paths:
      - '**/*.kt'
      - '**/*.kts'
      - '**/*.properties'
      - '**/*.toml'
  pull_request:
    paths:
      - '**/*.kt'
      - '**/*.kts'
      - '**/*.properties'
      - '**/*.toml'
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}

env:
  ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEY }}
  ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEYID }}
  ORG_GRADLE_PROJECT_signingKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }}
  CLI_TEST_MAX_DURATION_IN_SECONDS: 10

# Note that all "jobs" (build, tests) including "jobs.*.runs-on" should be kept in sync with "pull-request-without-code"
jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    name: "[build] OS=${{ matrix.os }} Kotlin=stable"
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

      - uses: ./.github/actions/setup-gradle-build

      - name: Build with release Kotlin version
        run: ./gradlew ktlintCheck build

      - name: Check `data class`es are not part of public API
        run: |
          found=0

          # Loop through all .api files in the current directory and subdirectories
          for file in $(find . -type f -name "*.api" ! -path "*/build/*"); do
              # Check if the file contains the 'data class' specific text
              if grep -q "public static synthetic fun copy$default" "$file"; then
                  echo "public 'data class' found in: $file"
                  found=1
              fi
          done

          if [ $found -eq 0 ]; then
              exit 0
          else
              echo "data classes found in one or more .api files. Visit https://github.com/pinterest/ktlint/issues/2133 for more details"
              exit 1
          fi
        shell: bash

  build-dev:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    name: "[build] OS=${{ matrix.os }}, Kotlin=dev"
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

      - uses: ./.github/actions/setup-gradle-build

      - name: Build with assemble Kotlin version
        run: ./gradlew -PkotlinDev ktlintCheck build

  tests:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest]
        jdk: [8, 11, 17] # list of Java versions to run tests against (excluding `java-compilation` version for which tests already have run during `build` job)
    runs-on: ${{ matrix.os }}
    name: "[tests] OS=${{ matrix.os }}, Java=${{ matrix.jdk }}"
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

      - uses: ./.github/actions/setup-gradle-build
        with:
          additional-java-versions: ${{ matrix.jdk }}

      - run: ./gradlew testOnJdk${{ matrix.jdk }} -PtestJdkVersion=${{ matrix.jdk }}


Generate changelog

name: Generate changelog

on: [ workflow_dispatch ]

jobs:
  publish:
    runs-on: ubuntu-latest
    if: github.repository == 'pinterest/ktlint'
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
        with:
          fetch-depth: 0 # fetch all commits/branches/tags

      - uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 21

      - uses: gradle/actions/setup-gradle@v3
        with:
          gradle-home-cache-cleanup: true

      - name: Get last released version
        run: echo "version=$(git describe --abbrev=0 --tags)" >> $GITHUB_ENV

      - name: "Build Changelog"
        id: build_changelog
        uses: mikepenz/release-changelog-builder-action@v4
        with:
          fromTag: "${{ env.version }}"
          # This Github action is run before building a new release. All PR's created after last release and between current HEAD are to be
          # included
          toTag: "HEAD"
          configurationJson: |
            {
              "template": "## [Unreleased]\n\n#{{CHANGELOG}}",
              "categories": [
                {
                  "title": "### 💔 Breaking changes",
                  "labels": ["api changelog"],
                  "exclude_labels": ["exclude from changelog"]
                },
                {
                  "title": "### 🆕 Features",
                  "labels": ["feature changelog"],
                  "exclude_labels": ["exclude from changelog"],
                  "empty_content": "None"
                },
                {
                  "title": "### 🔧 Fixes",
                  "labels": ["fix changelog"],
                  "exclude_labels": ["exclude from changelog"],
                  "empty_content": "None"
                },
                {
                  "title": "### 📦 Dependencies",
                  "exhaustive_rules": false,
                  "rules": [
                    {
                      "pattern": "dependency changelog",
                      "on_property": "labels"
                    },
                    {
                      "pattern": "renovate\\[bot\\]",
                      "on_property": "author"
                    }
                  ],
                  "exclude_labels": ["exclude from changelog"]
                },
                {
                  "title": "### 💬 Other",
                  "labels": [],
                  "exclude_labels": ["documentation", "exclude from changelog"]
                }
              ],
              "pr_template": "\n* #{{TITLE}} - [##{{NUMBER}}](#{{URL}}), by @#{{AUTHOR}}",
              "empty_template": "- no changes",
              "sort": {
                "order": "ASC",
                "on_property": "mergedAt"
              },
              "max_pull_requests": 1000,
              "max_back_track_time_days": 1000
            }
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Echo generated changelog
        env:
          CHANGELOG: ${{ steps.build_changelog.outputs.changelog }}
        run: echo -e "$CHANGELOG"


All tests

name: All tests

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
  group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
  cancel-in-progress: true

jobs:
  tests:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        java-version: [ 17, 19 ] # test LTS versions, and the newest
        kotlin-version: [ 1.5.31, 1.6.21, 1.7.22, 1.8.20, 1.9.10, 2.0.0 ]
      fail-fast: false # in case one JDK fails, we still want to see results from others
    name: "[java=${{ matrix.java-version }}, kotlin=${{ matrix.kotlin-version }}]"
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v3

      - name: Set up JDK ${{ matrix.java-version }}
        uses: actions/setup-java@v3
        with:
          distribution: temurin
          java-version: ${{ matrix.java-version }}

      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2.4.2

      - name: Run tests with Gradle
        run: >
          ./gradlew check
          --stacktrace
          -Pio_mockk_kotlin_version=${{ matrix.kotlin-version }}
          -Pio_mockk_java_toolchain_test_version=${{ matrix.java-version }}

  android-instrumented-tests:
    runs-on: macos-12
    strategy:
      matrix:
        api-level: [ 30 ] # Relevant SDK versions to test for MockK Android projects
      fail-fast: false # in case one API-level fails, we still want to see results from others
    name: "[api-level=${{ matrix.api-level }}]"
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-java@v3
        with:
          distribution: temurin
          java-version: 17

      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2

      - name: Set AVD Architecture Based on API Level
        run: echo "ARCH=$(if [ ${{ matrix.api-level }} -eq 27 ]; then echo 'x86'; else echo 'x86_64'; fi)" >> $GITHUB_ENV

      - name: AVD cache
        uses: actions/cache@v3
        id: avd-cache
        with:
          path: |
            ~/.android/avd/*
            ~/.android/adb*
          key: ${{ runner.os }}-avd-${{ matrix.api-level }}

      - name: create AVD and generate snapshot for caching
        if: steps.avd-cache.outputs.cache-hit != 'true'
        uses: reactivecircus/android-emulator-runner@v2
        with:
          arch: ${{ env.ARCH }}
          api-level: ${{ matrix.api-level }}
          force-avd-creation: false
          emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
          disable-animations: false
          script: echo "Generated AVD snapshot for caching."
          emulator-build: 7425822  # workaround for emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160

      - name: run tests
        uses: reactivecircus/android-emulator-runner@v2
        with:
          arch: ${{ env.ARCH }}
          api-level: ${{ matrix.api-level }}
          script: ./gradlew connectedCheck
          emulator-build: 7425822  # workaround for emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160


PR title check

name: 'PR title check'
on:
  pull_request:
    types: [opened, edited, reopened]

jobs:
  check-pr-title:
    runs-on: ubuntu-latest
    steps:
      - uses: morrisoncole/pr-lint-action@v1.7.1
        with:
          title-regex: '^((CORDA|AG|EG|ENT|INFRA|ES|DOC)-\d+)(.*)'
          on-failed-regex-comment: "PR title failed to match regex -> `%regex%`"
          repo-token: "${{ secrets.GITHUB_TOKEN }}"


Sync assigned jira issues

name: Sync assigned jira issues

on:
  schedule:
    - cron: '15 * * * *'

jobs:
  sync_assigned:
    runs-on: ubuntu-latest
    steps:
      - name: Generate a token
        id: generate_token
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.AUTH_APP_ID }}
          private-key: ${{ secrets.AUTH_APP_PK }}
      - name: Assign
        uses: corda/jira-sync-assigned-action@master
        with:
          jiraBaseUrl: ${{ secrets.JIRA_BASE_URL }}
          jiraEmail: ${{ secrets.JIRA_USER_EMAIL }}
          jiraToken: ${{ secrets.JIRA_API_TOKEN }}
          token: ${{ steps.generate_token.outputs.token }}
          owner: corda
          repository: corda


Sync closed jira issues

name: Sync closed jira issues

on:
  schedule:
    - cron: '30 * * * *'

jobs:
  sync_closed:
    runs-on: ubuntu-latest
    steps:
      - name: Generate a token
        id: generate_token
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.AUTH_APP_ID }}
          private-key: ${{ secrets.AUTH_APP_PK }}
      - name: Close
        uses: corda/jira-sync-closed-action@master
        with:
          jiraBaseUrl: https://r3-cev.atlassian.net
          jiraEmail: ${{ secrets.JIRA_USER_EMAIL }}
          jiraToken: ${{ secrets.JIRA_API_TOKEN }}
          token: ${{ steps.generate_token.outputs.token }}
          owner: corda
          repository: corda


Create jira issue from github issue

name: Create jira issue from github issue

on:
  issues:
    types: [opened]

jobs:
  update_jira:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4.1.1

      - name: Generate a token
        id: generate_token
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.AUTH_APP_ID }}
          private-key: ${{ secrets.AUTH_APP_PK }}

      - name: Jira Create issue
        id: create
        uses: corda/jira-create-issue-action@master
        with:
          jiraBaseUrl: https://r3-cev.atlassian.net
          project: CORDA
          squad: Corda
          issuetype: Bug
          summary: ${{ github.event.issue.title }}
          labels: community
          jiraEmail: ${{ secrets.JIRA_USER_EMAIL }}
          jiraToken: ${{ secrets.JIRA_API_TOKEN }}
          description: |
            ${{ github.event.issue.body }}

            Created by github action.

      - name: Create comment
        uses: peter-evans/create-or-update-comment@v4.0.0
        with:
          token: ${{ steps.generate_token.outputs.token }}
          issue-number: ${{ github.event.issue.number }}
          body: |
            Automatically created Jira issue: ${{ steps.create.outputs.issue }}
          reaction-type: '+1'


build

name: build

on:
  pull_request: {}
  workflow_dispatch: {}
  push:
    branches:
      - 'master'
    tags-ignore:
      - '**'

env:
  GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false"

jobs:
  jvm:
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        java-version:
          - 17
          - 18
          - 19

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: ${{ matrix.java-version }}

      - name: Check test files
        run: |
          ./gradlew generateTests --stacktrace --warning-mode all
          if [ ! -z "$(git status --porcelain)" ]; then git diff; echo -e "\nTest files changed. Did you run ./gradlew generateTests?"; exit 1; fi

      - name: Test Native and JS
        run: |
          ./gradlew -Dkjs=true -Dknative=true -Pswift=false samples:native:build samples:js:build samples:multi-target:build --stacktrace --warning-mode all

      - name: Test
        run: |
          ./gradlew -Dkjs=false -Dknative=false -Pswift=false build --stacktrace --warning-mode all -x samples:native:build -x samples:js:build -x samples:multi-target:build

  multiplatform:
    runs-on: macos-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 19

      - name: Setup Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: latest-stable

      # We set `SDKROOT` as a workaround for https://github.com/gradle/gradle/pull/29227
      - name: Check test files
        run: |
          SDKROOT=$(xcrun -sdk macosx --show-sdk-path) ./gradlew generateTests --stacktrace --warning-mode all
          if [ ! -z "$(git status --porcelain)" ]; then git diff; echo -e "\nTest files changed. Did you run ./gradlew generateTests?"; exit 1; fi

      - name: Install Chrome
        uses: browser-actions/setup-chrome@latest

      - name: Test
        run: |
          ./gradlew -Pswift=false build --stacktrace --warning-mode all

  windows:
    runs-on: windows-latest

    steps:
      - name: Set git to use LF and not automatically replace them with CRLF.
        run: |
          git config --global core.autocrlf false

      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 19

      - name: Check test files
        run: |
          ./gradlew generateTests --stacktrace --warning-mode all
          if [ ! -z "$(git status --porcelain)" ]; then git diff; echo -e "\nTest files changed. Did you run ./gradlew generateTests?"; exit 1; fi
        shell: bash

      - name: Install Chrome
        uses: browser-actions/setup-chrome@latest

      - name: Test
        uses: nick-fields/retry@v3.0.0
        with:
          max_attempts: 5
          timeout_minutes: 90
          shell: bash
          command: |
            ./gradlew -Pswift=false build --stacktrace --warning-mode all

  swift:
    runs-on: macos-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 19

      - name: Setup Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: latest-stable

      # We set `SDKROOT` as a workaround for https://github.com/gradle/gradle/pull/29227
      - name: Test Swift Runtime
        # This builds the wire-swift-runtime, verifies that generated protos match what's currently
        # checked into the repo both for the runtime itself and for its tests, and runs the tests.
        run: |
          SDKROOT=$(xcrun -sdk macosx --show-sdk-path) ./gradlew :wire-runtime-swift:build --warning-mode all
          if [ ! -z "$(git status --porcelain)" ]; then git diff; echo -e "\nGenerated files changed. Did you run ./gradlew generateSwiftProtos?"; exit 1; fi

      - name: Generate Swift from golden proto files
        # This compiles the golden proto files into Swift files and ensures that those outputs match
        # what's currently checked into the repo. These generated files are compiled during the
        # wire-tests-swift build but should still be reviewed manually. Finally, run the associated
        # unit tests.
        run: |
          SDKROOT=$(xcrun -sdk macosx --show-sdk-path) ./gradlew generateSwiftTests --warning-mode all
          if [ ! -z "$(git status --porcelain)" ]; then git diff; echo -e "\nGenerated files changed. Did you run ./gradlew generateSwiftProtos and generateSwiftTests?"; exit 1; fi
          SDKROOT=$(xcrun -sdk macosx --show-sdk-path) ./gradlew -p wire-tests-swift build --warning-mode all

      - name: Test Swift Package Manager
        run: swift test

  android:
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        api-level:
          - 21
          - 24
          - 26
          - 29

    steps:
      - name: Enable KVM group perms
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm
          ls /dev/kvm

      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 19

      - uses: gradle/actions/setup-gradle@v3

      - name: Run Tests
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: ${{ matrix.api-level }}
          script: ./gradlew --stacktrace -Pswift=false connectedCheck
        env:
          API_LEVEL: ${{ matrix.api-level }}

  publish-snapshot:
    runs-on: macos-latest
    if: github.repository == 'square/wire' && github.ref == 'refs/heads/master'
    needs: [jvm, multiplatform, swift]

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 19

      - name: Setup Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: latest-stable

      # We set `SDKROOT` as a workaround for https://github.com/gradle/gradle/pull/29227
      - name: Upload Artifacts
        run: |
          SDKROOT=$(xcrun -sdk macosx --show-sdk-path) ./gradlew publish --stacktrace --warning-mode all --no-daemon
        env:
          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
          ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ARTIFACT_SIGNING_PRIVATE_KEY }}


gradle-wrapper

name: gradle-wrapper

on:
  pull_request:
    paths:
    - 'gradlew'
    - 'gradlew.bat'
    - 'gradle/wrapper/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: gradle/actions/wrapper-validation@v3


release

name: release

on:
  push:
    tags:
      - '**'

env:
  GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false"

jobs:
  publish-artifacts:
    runs-on: macos-latest

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 19

      - name: Setup Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: latest-stable

      # We set `SDKROOT` as a workaround for https://github.com/gradle/gradle/pull/29227
      - name: Upload Artifacts
        run: |
          SDKROOT=$(xcrun -sdk macosx --show-sdk-path) ./gradlew publish --stacktrace --warning-mode all --no-daemon
        env:
          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
          ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ARTIFACT_SIGNING_PRIVATE_KEY }}

      # We set `SDKROOT` as a workaround for https://github.com/gradle/gradle/pull/29227
      - name: Publish plugin to Gradle portal
        run: |
          SDKROOT=$(xcrun -sdk macosx --show-sdk-path) ./gradlew publishPluginToGradlePortalIfRelease --stacktrace --warning-mode all
        env:
          GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
          GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}

  publish-website:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Configure JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 19

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: 3.8

      - name: Prepare docs
        run: .buildscript/prepare_mkdocs.sh

      - name: Build mkdocs
        run: |
          pip3 install mkdocs-material mkdocs-macros-plugin
          mkdocs build

      - name: Restore 2.x docs
        run: .buildscript/restore_v2_docs.sh

      - name: Deploy docs
        if: success()
        uses: JamesIves/github-pages-deploy-action@releases/v3
        with:
          GITHUB_TOKEN: ${{ secrets.GH_CLIPPY_TOKEN }}
          BRANCH: gh-pages
          FOLDER: site
          SINGLE_COMMIT: true


auto updates

name: auto updates

on:
    workflow_dispatch:
    schedule:
        - cron: '0 0 * * 1' # try each Monday

jobs:
    update-nightly:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3
              with:
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - name: Prepare for updates
              uses: ./.github/actions/preparations-for-updates

            - name: Update nightly
              run: python scripts/update_nightly.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

    update-stable:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3
              with:
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - name: Prepare for updates
              uses: ./.github/actions/preparations-for-updates
              with:
                  toolchain: stable

            - name: Update stable
              run: python scripts/update_stable.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

    update-compiler-features:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3
              with:
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - name: Prepare for updates
              uses: ./.github/actions/preparations-for-updates

            - name: Update compiler features
              run: python scripts/update_compiler_features.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

    update-lints:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3
              with:
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - name: Prepare for updates
              uses: ./.github/actions/preparations-for-updates

            - name: Update lints
              run: python scripts/update_lints.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

    update-evcxr-repl:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3
              with:
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - name: Prepare for updates
              uses: ./.github/actions/preparations-for-updates

            - name: Update evcxr_repl
              run: python scripts/update_repl.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }}


build native code

name: build native code

on:
    workflow_call:
        inputs:
            rust-version:
                required: true
                type: string
            cache:
                required: true
                type: boolean

env:
    CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc

jobs:
    build-native-code:
        strategy:
            fail-fast: true
            matrix:
                config:
                    - name: linux
                      arch: x86-64
                      os: ubuntu-latest
                      container: ubuntu:18.04
                      target: x86_64-unknown-linux-gnu
                      artifact_path: native-helper/target/x86_64-unknown-linux-gnu/release/intellij-rust-native-helper
                    - name: linux
                      arch: arm64
                      os: ubuntu-latest
                      container: ubuntu:18.04
                      target: aarch64-unknown-linux-gnu
                      artifact_path: native-helper/target/aarch64-unknown-linux-gnu/release/intellij-rust-native-helper
                    - name: windows
                      arch: x86-64
                      os: windows-latest
                      target: x86_64-pc-windows-msvc
                      artifact_path: native-helper/target/x86_64-pc-windows-msvc/release/intellij-rust-native-helper.exe
                    - name: windows
                      arch: arm64
                      os: windows-latest
                      target: aarch64-pc-windows-msvc
                      artifact_path: native-helper/target/aarch64-pc-windows-msvc/release/intellij-rust-native-helper.exe
                    - name: macos
                      arch: x86-64
                      os: macos-latest
                      target: x86_64-apple-darwin
                      artifact_path: native-helper/target/x86_64-apple-darwin/release/intellij-rust-native-helper
                    - name: macos
                      arch: arm64
                      os: macos-11
                      target: aarch64-apple-darwin
                      artifact_path: native-helper/target/aarch64-apple-darwin/release/intellij-rust-native-helper

        name: ${{ matrix.config.name }}-${{ matrix.config.arch }}
        runs-on: ${{ matrix.config.os }}
        container: ${{ matrix.config.container }}
        steps:
            - name: Checkout
              uses: actions/checkout@v3

            - name: Cache native code artifact
              id: cache-native-code-artifact
              if: inputs.cache
              uses: actions/cache@v3
              with:
                  path: ${{ matrix.config.artifact_path }}
                  key: ${{ matrix.config.arch }}-${{ matrix.config.os }}-native-helper-${{ inputs.rust-version }}-${{ hashFiles('native-helper/src/**', 'native-helper/Cargo.toml', 'native-helper/Cargo.lock') }}

            # Since we use pure ubuntu container, it doesn't contain `curl` and `rustup`.
            # So let's install them manually
            - name: Install Rust toolchain dependencies
              if: ${{ matrix.config.container == 'ubuntu:18.04' && (!inputs.cache || !steps.cache-native-code-artifact.outputs.cache-hit) }}
              shell: bash
              run: |
                  apt-get update && apt-get install -y build-essential curl
                  curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y
                  echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH

            - name: Set up Rust
              if: ${{ !inputs.cache || !steps.cache-native-code-artifact.outputs.cache-hit }}
              uses: actions-rs/toolchain@v1
              with:
                  profile: minimal
                  toolchain: ${{ inputs.rust-version }}
                  target: ${{ matrix.config.target }}
                  default: true

            - name: Install Linux arm64 linker
              if: matrix.config.target == 'aarch64-unknown-linux-gnu'
              run: |
                apt-get update
                apt-get -y install gcc-aarch64-linux-gnu

            - name: Build
              if: ${{ !inputs.cache || !steps.cache-native-code-artifact.outputs.cache-hit }}
              uses: actions-rs/cargo@v1
              with:
                  command: build
                  args: --manifest-path native-helper/Cargo.toml --target ${{ matrix.config.target }} --release

            - name: Publish
              uses: actions/upload-artifact@v3
              with:
                  name: ${{ matrix.config.name }}-${{ matrix.config.arch }}
                  path: ${{ matrix.config.artifact_path }}


build plugin

name: build plugin

on:
    workflow_dispatch:

jobs:
    generate-build-number:
        runs-on: ubuntu-latest
        outputs:
            build_number: ${{ steps.buildnumber.outputs.build_number }}
        steps:
            - name: Generate build number
              uses: einaregilsson/build-number@v3
              id: buildnumber
              with:
                  token: ${{ secrets.github_token }}
                  prefix: rust

    build-native-code:
        uses: ./.github/workflows/build-native-code.yml
        with:
            rust-version: stable
            cache: false

    build:
        needs: [ generate-build-number, build-native-code ]
        runs-on: ubuntu-latest
        strategy:
            fail-fast: true
            matrix:
                platform-version: [ 232, 233 ]
        env:
            ORG_GRADLE_PROJECT_buildNumber: ${{ needs.generate-build-number.outputs.build_number }}
            ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }}
            ORG_GRADLE_PROJECT_enableBuildSearchableOptions: true
            ORG_GRADLE_PROJECT_compileNativeCode: false

        steps:
            - name: Checkout
              uses: actions/checkout@v3

            - name: Set up JDK 17
              uses: actions/setup-java@v3
              with:
                  distribution: corretto
                  java-version: 17

            - name: Load native binaries
              uses: ./.github/actions/load-native-binaries

            - name: Build rust plugin
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: ":plugin:buildPlugin"
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

            - name: Save artifacts
              uses: actions/upload-artifact@v3
              with:
                  name: intellij-rust
                  path: plugin/build/distributions/*.zip


check

name: check

on:
    push:
        branches:
            - master
            - staging
            - trying
    pull_request:
    # Trigger workflow every day to create/update caches for other builds
    # See https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache
    # to get more details about cache access
    schedule:
        -   cron: '0 3 * * *'

# Allow cancelling all previous runs for the same branch
# See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency
concurrency:
    group: ${{ github.workflow }}-${{ github.ref }}
    cancel-in-progress: true

env:
    OLD_IDEA_VERSION: IU-2023.2
    OLD_CLION_VERSION: CL-2023.2
    OLD_NATIVE_DEBUG_PLUGIN: "com.intellij.nativeDebug:232.8660.142"

jobs:
    get-rust-versions:
        uses: ./.github/workflows/get-rust-versions.yml

    calculate-git-info:
        runs-on: ubuntu-latest
        outputs:
            is_bors_branch: ${{ steps.calculate-git-info.outputs.is_bors_branch }}
            is_master_branch: ${{ steps.calculate-git-info.outputs.is_master_branch }}
            checked: ${{ steps.calculate-git-info.outputs.checked }}
        steps:
            - uses: actions/checkout@v3
              with:
                  fetch-depth: 0

            - name: Calculate git info
              id: calculate-git-info
              run: |
                  echo "is_bors_branch=${{ github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/trying' }}" >> $GITHUB_OUTPUT
                  echo "is_master_branch=${{ github.ref == 'refs/heads/master'}}" >> $GITHUB_OUTPUT
                  echo "checked=$(python scripts/has_successful_status.py --token ${{ github.token }} --ref ${{ github.sha }} --check_name check)" >> $GITHUB_OUTPUT

            - name: Check git info
              run: |
                  echo "is_bors_branch: ${{ steps.calculate-git-info.outputs.is_bors_branch }}"
                  echo "is_master_branch: ${{ steps.calculate-git-info.outputs.is_master_branch }}"
                  echo "checked: ${{ steps.calculate-git-info.outputs.checked }}"

    check-license:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3

            - name: Check license
              run: python scripts/check_license.py

    build-native-code:
        needs: [ calculate-git-info, get-rust-versions ]
        # `fromJSON` is used here to convert string output to boolean
        # We always want to trigger all workflow jobs on `schedule` event because it creates/updates caches for other builds.
        # See https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache
        # to get more details about cache access
        if: ${{ github.event_name == 'schedule' || !fromJSON(needs.calculate-git-info.outputs.is_master_branch) || !fromJSON(needs.calculate-git-info.outputs.checked) }}
        uses: ./.github/workflows/build-native-code.yml
        with:
            rust-version:  ${{ needs.get-rust-versions.outputs.stable }}
            cache: true

    check-pretty-printers:
        needs: [ calculate-git-info, get-rust-versions ]
        strategy:
            # `fromJSON` is used here to convert string output to boolean
            fail-fast: ${{ fromJSON(needs.calculate-git-info.outputs.is_bors_branch) }}
            matrix:
                # TODO: check on macos as well
                os: [ ubuntu-latest, windows-latest ]
                # `fromJSON` is used here to convert string output to sequence.
                # `matrix` is a string with list of stable and nightly versions.
                # Make sequence from two outputs with version is not possible here.
                # TODO: check on more rustc versions
                rust-version: ${{ fromJSON(needs.get-rust-versions.outputs.matrix) }}
                platform-version: [ 232, 233 ]
                include:
                    - os: ubuntu-latest
                      rust-version: ${{ needs.get-rust-versions.outputs.old }}
                      platform-version: 232
                    - os: windows-latest
                      rust-version: ${{ needs.get-rust-versions.outputs.old }}
                      platform-version: 232

        runs-on: ${{ matrix.os }}
        env:
            ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }}
            ORG_GRADLE_PROJECT_compileNativeCode: false
            RUST_BACKTRACE: 1

        steps:
            - uses: actions/checkout@v3

            - name: Set up Rust
              uses: actions-rs/toolchain@v1
              with:
                  profile: minimal
                  toolchain: ${{ matrix.rust-version }}
                  default: true

            - name: Set up additional env variables
              if: matrix.rust-version == needs.get-rust-versions.outputs.old
                # see https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
              run: |
                  echo "ORG_GRADLE_PROJECT_ideaVersion=${{ env.OLD_IDEA_VERSION }}" >> $GITHUB_ENV
                  echo "ORG_GRADLE_PROJECT_clionVersion=${{ env.OLD_CLION_VERSION }}" >> $GITHUB_ENV
                  echo "ORG_GRADLE_PROJECT_nativeDebugPlugin=${{ env.OLD_NATIVE_DEBUG_PLUGIN }}" >> $GITHUB_ENV

            - name: Retrieve CLion version
              id: clion-version
              shell: bash
              run: echo "version=$(python scripts/retrieve_clion_version.py --path gradle-${{ matrix.platform-version }}.properties)" >> $GITHUB_OUTPUT

            - name: Cache debuggers
              id: cache-debuggers
              uses: actions/cache@v3
              with:
                  # Cache only GDB for Linux because we run gdb pretty-printer tests only on Linux for now
                  path: |
                      deps/clion-${{ steps.clion-version.outputs.version }}/bin/lldb
                      deps/clion-${{ steps.clion-version.outputs.version }}/bin/gdb/linux
                  key: debuggers-${{ steps.clion-version.outputs.version }}
                  enableCrossOsArchive: true

            - name: Set up JDK 17
              if: ${{ !steps.cache-debuggers.outputs.cache-hit }}
              uses: actions/setup-java@v3
              with:
                  distribution: corretto
                  java-version: 17

            - name: Download debuggers
              if: ${{ !steps.cache-debuggers.outputs.cache-hit }}
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: ":resolveDependencies -Pkotlin.incremental=false --no-daemon"
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

            - name: Cache pretty printer test binaries
              uses: actions/cache@v3
              with:
                  path: |
                      ~/.cargo/bin/
                      ~/.cargo/registry/index/
                      ~/.cargo/registry/cache/
                      ~/.cargo/git/db/
                      pretty_printers_tests/target
                  key: pretty_printers_tests-${{ runner.os }}-${{ matrix.rust-version }}-${{ hashFiles('pretty_printers_tests/src/**', 'pretty_printers_tests/examples/**', 'pretty_printers_tests/Cargo.toml', 'pretty_printers_tests/Cargo.lock') }}

            - name: Check bundled Rust formatters
              run: python3 scripts/run_pretty_printer_tests.py --clion_version ${{ steps.clion-version.outputs.version }}

    check-plugin:
        needs: [ calculate-git-info, build-native-code, get-rust-versions ]
        strategy:
            # `fromJSON` is used here to convert string output to boolean
            fail-fast: ${{ fromJSON(needs.calculate-git-info.outputs.is_bors_branch) }}
            matrix:
                os: [ ubuntu-latest, windows-latest ]
                # `fromJSON` is used here to convert string output to sequence.
                # `matrix` is a string with list of stable and nightly versions.
                # Make sequence from two outputs with version is not possible here.
                rust-version: ${{ fromJSON(needs.get-rust-versions.outputs.matrix) }}
                base-ide: [ idea, clion ]
                platform-version: [ 232, 233 ]
                # it's enough to verify plugin structure only once per platform version
                verify-plugin: [ false ]
                default-edition-for-tests: [ 2021 ]
                include:
                    - os: ubuntu-latest
                      rust-version: ${{ needs.get-rust-versions.outputs.old }}
                      base-ide: idea
                      platform-version: 232
                      verify-plugin: true
                      default-edition-for-tests: 2021

        runs-on: ${{ matrix.os }}
        timeout-minutes: 120
        env:
            ORG_GRADLE_PROJECT_baseIDE: ${{ matrix.base-ide }}
            ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }}
            ORG_GRADLE_PROJECT_compileNativeCode: false
            DEFAULT_EDITION_FOR_TESTS: ${{ matrix.default-edition-for-tests }}
            EVCXR_REPL_VERSION: 0.14.2

        steps:
            - uses: actions/checkout@v3

            - name: Set up JDK 17
              uses: actions/setup-java@v3
              with:
                  distribution: corretto
                  java-version: 17

            - name: Set up Rust
              uses: actions-rs/toolchain@v1
              with:
                  profile: minimal
                  toolchain: ${{ matrix.rust-version }}
                  components: rust-src, rustfmt, clippy
                  default: true

            # Requires for tests with overridden toolchain
            - name: Set up nightly Rust
              uses: actions-rs/toolchain@v1
              with:
                  profile: minimal
                  toolchain: nightly
                  components: rust-src, rustfmt
                  default: false

            - name: Cache cargo binaries
              uses: actions/cache@v3
              with:
                  path: |
                      ~/.cargo/bin/evcxr
                      ~/.cargo/bin/evcxr.exe
                      ~/.cargo/bin/cargo-generate
                      ~/.cargo/bin/cargo-generate.exe
                      ~/.cargo/.crates.toml
                      ~/.cargo/.crates2.json
                  key: ${{ runner.os }}-cache-cargo-binaries-${{ matrix.rust-version }}-${{ github.run_id }} # https://github.com/actions/cache/issues/342#issuecomment-673371329
                  restore-keys: |
                      ${{ runner.os }}-cache-cargo-binaries-${{ matrix.rust-version }}-

            - name: Install evcxr
              # BACKCOMPAT: Evcxr 0.13 requires at least stable-1.59
              if: matrix.os != 'windows-latest' && matrix.rust-version >= '1.59.0'
              uses: actions-rs/cargo@v1
              with:
                  command: install
                  # https://github.com/intellij-rust/intellij-rust/issues/9406
                  args: evcxr_repl --locked --version ${{ env.EVCXR_REPL_VERSION }}

            - name: Install cargo-generate
              # BACKCOMPAT: cargo-generate 0.15.2 requires Rust 1.61.0 or newer
              if: matrix.rust-version >= '1.61.0'
              uses: actions-rs/cargo@v1
              with:
                  command: install
                  args: cargo-generate --locked

            - name: Check environment
              run: |
                  rustc --version
                  rustup component list --installed
                  cargo install --list

            - name: Set up additional env variables
              if: matrix.rust-version == needs.get-rust-versions.outputs.old
              # see https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
              run: |
                  echo "ORG_GRADLE_PROJECT_ideaVersion=${{ env.OLD_IDEA_VERSION }}" >> $GITHUB_ENV
                  echo "ORG_GRADLE_PROJECT_clionVersion=${{ env.OLD_CLION_VERSION }}" >> $GITHUB_ENV
                  echo "ORG_GRADLE_PROJECT_nativeDebugPlugin=${{ env.OLD_NATIVE_DEBUG_PLUGIN }}" >> $GITHUB_ENV

            - name: Set up test env variables
              run: echo "RUST_SRC_WITH_SYMLINK=$HOME/.rust-src" >> $GITHUB_ENV

            - name: Create symlink for Rust stdlib Unix
              if: matrix.os != 'windows-latest'
              run: ln -s $(rustc --print sysroot)/lib/rustlib/src/rust $RUST_SRC_WITH_SYMLINK

            # FIXME: find out why it doesn't work on CI
            # - name: Create symlink for Rust stdlib Windows
            #  if: matrix.os == 'windows-latest'
            #  run: New-Item -ItemType Junction -Path "$env:RUST_SRC_WITH_SYMLINK" -Target "$(rustc --print sysroot)/lib/rustlib/src/rust"

            - name: Load native binaries
              uses: ./.github/actions/load-native-binaries

            - name: Download
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: ":resolveDependencies -Pkotlin.incremental=false --no-daemon"
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

            - name: Build
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: "assemble testClasses -Pkotlin.incremental=false --no-daemon"
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

            - name: Check
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: "check -PexcludeTests=org/rustPerformanceTests/** --continue -Pkotlin.incremental=false --no-daemon"
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

            - name: Collect fail reports
              if: failure()
              uses: actions/upload-artifact@v3
              with:
                  name: tests-report-${{ matrix.os }}-${{ matrix.rust-version }}-${{ matrix.base-ide }}-${{ matrix.platform-version }}
                  path: |
                      build/reports/tests
                      */build/reports/tests
                      build/*sandbox*/system-test/testlog/idea.log
                      */build/*sandbox*/system-test/testlog/idea.log

            - name: Verify plugin
              if: matrix.verify-plugin
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: ":plugin:verifyPlugin"
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

    check:
        needs: [ check-license, check-plugin, check-pretty-printers ]
        runs-on: ubuntu-latest
        steps:
            - name: Finish check
              run: echo "Check finished successfully!"


create changelog

name: create changelog

on:
    workflow_dispatch:
    schedule:
        - cron: '0 3 * * *' # try everyday

jobs:
    create-changelog:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3

            - name: Checkout intellij-rust.github.io repo
              uses: actions/checkout@v3
              with:
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}
                  repository: ${{ github.repository_owner }}/intellij-rust.github.io
                  path: intellij-rust.github.io

            - name: Install python packages
              run: pip install -r intellij-rust.github.io/requirements.txt

            - name: Set up git user
              run: |
                  (cd intellij-rust.github.io; git config --local user.email "intellij.rust@gmail.com")
                  (cd intellij-rust.github.io; git config --local user.name "intellij-rust-bot")

            - name: Make pull request
              run: python scripts/make_changelog_pull_request.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }} --repo_owner ${{ github.repository_owner }} --repo_name intellij-rust.github.io


get rust versions

# This workflow exists to avoid using hardcoded versions of the toolchain in different places.
# In an ideal world, top-level constants (not yet implemented) or workflow-level environment variables
# would be used, but they are not supported in all needed places.

name: get rust versions
on:
    workflow_call:
        outputs:
            stable:
                value: ${{ jobs.get_versions.outputs.stable }}
            nightly:
                value: ${{ jobs.get_versions.outputs.nightly }}
            old:
                value: ${{ jobs.get_versions.outputs.old }}
            matrix:
                description: 'String like ["%stable%", "%nightly-yyyy-mm-dd%"].
                         Added for `check` workflow, because there is no way to make a sequence from two outputs.'
                value: ${{ jobs.get_versions.outputs.matrix }}

env:
    STABLE: "1.71.0"
    NIGHTLY: "nightly-2023-04-15"
    OLD: "1.56.0"

jobs:
    get_versions:
        runs-on: ubuntu-latest
        outputs:
            stable: ${{ steps.versions.outputs.stable }}
            nightly: ${{ steps.versions.outputs.nightly }}
            old: ${{ steps.versions.outputs.old }}
            matrix: ${{ steps.versions.outputs.matrix }}
        steps:
            -   name: setup all version
                id: versions
                run: |
                    echo "stable=${{ env.STABLE }}" >> $GITHUB_OUTPUT
                    echo "nightly=${{ env.NIGHTLY }}" >> $GITHUB_OUTPUT
                    echo "old=${{ env.OLD }}" >> $GITHUB_OUTPUT
                    echo "matrix=[\"${{ env.STABLE }}\", \"${{ env.NIGHTLY }}\"]" >> $GITHUB_OUTPUT


milestones

name: milestones

on:
    pull_request_target:
        types: [ closed ]

jobs:
    set_milestone:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3

            - name: Set milestone
              if: github.event.pull_request.merged && github.event.pull_request.milestone == null
              run: python scripts/set_milestone.py --token ${{ github.token }} --pull-request ${{ github.event.pull_request.number }}


docteam notifier

name: docteam notifier

on:
    pull_request_target:
        types: [ labeled, closed ]

jobs:
    notify-docteam:
        if: |
            github.event.action == 'closed' && github.event.pull_request.merged && contains(github.event.pull_request.labels.*.name, 'to be documented') ||
            github.event.label.name == 'to be documented' && github.event.pull_request.merged
        runs-on: ubuntu-latest
        steps:
            - name: Checkout repo content
              uses: actions/checkout@v3

            - name: Install python packages
              run: pip install -r scripts/requirements.txt

            - name: Write comment
              run: python scripts/notify_docteam.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }} --pull_request ${{ github.event.pull_request.number }}


regressions-extended

name: regressions-extended

on: [ workflow_dispatch ]

jobs:
    main:
        uses: ./.github/workflows/regressions-template.yml
        with:
            projects-matrix-file: .github/workflows/regressions-extended.json
            analyze-dependencies: true


regressions-template

name: regressions-template

on:
    workflow_call:
        inputs:
            projects-matrix-file:
                required: true
                type: string
            analyze-dependencies:
                required: true
                type: boolean

jobs:
    prepare:
        runs-on: ubuntu-latest
        outputs:
            base-commit: ${{ steps.base-commit.outputs.base-commit }}
            projects-matrix: ${{ steps.projects-matrix.outputs.projects-matrix }}
        steps:
            - uses: actions/checkout@v3
              with:
                  fetch-depth: 0

            # In case of `workflow_dispatch` event, `github.sha` points to latest commit of chosen branch
            # So, we need to find the latest common commit for master branch and chosen one
            # to calculate difference only for branch changes
            - name: Calculate base commit for workflow_dispatch or push event
              if: github.event_name == 'workflow_dispatch' || github.event_name == 'push'
              run: echo "BASE_COMMIT=$(git merge-base origin/master ${{ github.sha }})" >> $GITHUB_ENV

            # For pull request event, GitHub produces additional merge commit with `master` branch and PR branch as parents
            # In this case, we want to check difference between master branch and merge commit
            # so emit hash of `origin/master` branch itself as base commit
            - name: Calculate base commit for pull_request event
              if: github.event_name == 'pull_request'
              run: echo "BASE_COMMIT=$(git rev-parse origin/master)" >> $GITHUB_ENV

            - name: Emit base commit
              id: base-commit
              run: echo "base-commit=$BASE_COMMIT" >> $GITHUB_OUTPUT

            - name: Show commits
              run: |
                  echo "current commit:"
                  git log ${{ github.sha }} -n 1 --pretty=short
                  echo "base commit:"
                  git log ${{ steps.base-commit.outputs.base-commit }} -n 1 --pretty=short

            - name: Emit projects matrix
              id: projects-matrix
              run: |
                projects=$(cat ${{ inputs.projects-matrix-file }})
                projects=${projects//$'\n'/}  # remove newline characters
                echo "projects-matrix=$projects" >> $GITHUB_OUTPUT

    check:
        needs: [ prepare ]
        name: ${{ matrix.batch.name }}
        runs-on: ubuntu-latest
        strategy:
            fail-fast: false
            matrix:
                batch: ${{ fromJson(needs.prepare.outputs.projects-matrix) }}
        env:
            ORG_GRADLE_PROJECT_showStandardStreams: true
            PROJECTS: ${{ toJSON(matrix.batch.projects) }}
            ANALYZE_DEPENDENCIES: ${{ inputs.analyze-dependencies }}
        steps:
            - uses: actions/checkout@v3
              with:
                  fetch-depth: 0

            - name: Set up JDK 17
              uses: actions/setup-java@v3
              with:
                  distribution: corretto
                  java-version: 17

            - name: Set up Rust
              uses: actions-rs/toolchain@v1
              with:
                  profile: minimal
                  toolchain: stable
                  components: rust-src
                  default: true

            - name: Install bevy dependencies
              run: |
                  sudo apt-get update
                  sudo apt-get install -y libasound2-dev libudev-dev

            - name: Checkout projects
              run: python scripts/fetch_projects.py --projects $'${{ toJSON(matrix.batch.projects) }}'

            - name: Download
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: ":resolveDependencies -Pkotlin.incremental=false"
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

            - name: Check with changes
              uses: gradle/gradle-build-action@v2
              env:
                  RESULT_SUFFIX: _with_changes
              with:
                  arguments: "clean :test --tests \"org.rustPerformanceTests.CustomRealProjectAnalysisTest\""
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

            - name: Checkout base version
              run: git checkout ${{ needs.prepare.outputs.base-commit }}

            - name: Check without changes
              uses: gradle/gradle-build-action@v2
              env:
                  RESULT_SUFFIX: _without_changes
              with:
                  arguments: "clean :test --tests \"org.rustPerformanceTests.CustomRealProjectAnalysisTest\""
                  gradle-home-cache-excludes: |
                      caches/modules-2/files-2.1/com.jetbrains.intellij.idea
                      caches/modules-2/files-2.1/com.jetbrains.intellij.clion

            - name: Checkout current version
              run: git checkout ${{ github.sha }}

            - name: Calculate regressions
              run: python scripts/calculate_regressions.py --projects $'${{ toJSON(matrix.batch.projects) }}'

            - name: Upload results
              if: ${{ always() }}
              uses: actions/upload-artifact@v3
              with:
                  name: ${{ matrix.batch.name }}
                  path: regressions/


regressions

name: regressions

on: [ workflow_dispatch, pull_request ]

# Allow cancelling all previous runs for the same branch
# See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency
concurrency:
    group: ${{ github.workflow }}-${{ github.ref }}
    cancel-in-progress: true

jobs:
    main:
        uses: ./.github/workflows/regressions-template.yml
        with:
            projects-matrix-file: .github/workflows/regressions.json
            analyze-dependencies: false


release branch

name: release branch

on:
    workflow_dispatch:
    schedule:
        - cron: '0 1 * * *' # try everyday

jobs:
    make-release-branch:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3
              with:
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - name: Set up git user
              run: |
                  git config --local user.email "intellij.rust@gmail.com"
                  git config --local user.name "intellij-rust-bot"

            - name: Check date
              if: github.event_name == 'schedule'
              id: check-date
              run: echo "result=$(python scripts/check_release_branch_date.py)" >> $GITHUB_OUTPUT

            - name: Make release branch
              if: github.event_name != 'schedule' || fromJSON(steps.check-date.outputs.result).create_release_branch
              run: python scripts/make_release_branch.py --slack_webhook ${{ secrets.SLACK_WEBHOOK_URL }} --slack_ids ${{ secrets.SLACK_USERNAMES}}

            - name: Create new milestone
              if: github.event_name != 'schedule' || fromJSON(steps.check-date.outputs.result).create_release_branch
              run: python scripts/make_next_milestone.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }}


rust nightly

name: rust nightly

on:
    workflow_dispatch:
    schedule:
        - cron: '0 2 * * *'

jobs:
    generate-build-number:
        runs-on: ubuntu-latest
        outputs:
            build_number: ${{ steps.buildnumber.outputs.build_number }}
        steps:
            - name: Generate build number
              uses: einaregilsson/build-number@v3
              id: buildnumber
              with:
                  token: ${{ secrets.github_token }}
                  prefix: rust

    fetch-latest-changes:
        runs-on: ubuntu-latest
        outputs:
            rust-commit: ${{ steps.fetch-commits.outputs.rust-commit }}
            rust-nightly: ${{ steps.fetch-commits.outputs.rust-nightly }}
        steps:
            - name: Checkout
              uses: actions/checkout@v3
              with:
                  fetch-depth: 0

            - name: Fetch latest commits
              id: fetch-commits
              run: |
                  echo "rust-commit=$(git log -n 1 --format=format:%H)" >> $GITHUB_OUTPUT
                  echo "rust-nightly=$(python scripts/get_tag_commit.py --tag "rust-nightly")" >> $GITHUB_OUTPUT

            - name: Show commits
              run: |
                  echo "rust-commit: ${{ steps.fetch-commits.outputs.rust-commit }}"
                  echo "rust-nightly: ${{ steps.fetch-commits.outputs.rust-nightly }}"

    build-native-code:
        if: needs.fetch-latest-changes.outputs.rust-commit != needs.fetch-latest-changes.outputs.rust-nightly
        needs: [ fetch-latest-changes ]
        uses: ./.github/workflows/build-native-code.yml
        with:
            rust-version: stable
            cache: false

    build:
        if: needs.fetch-latest-changes.outputs.rust-commit != needs.fetch-latest-changes.outputs.rust-nightly
        needs: [ generate-build-number, fetch-latest-changes, build-native-code ]
        runs-on: ubuntu-latest
        strategy:
            fail-fast: true
            matrix:
                platform-version: [ 232, 233 ]
        env:
            ORG_GRADLE_PROJECT_buildNumber: ${{ needs.generate-build-number.outputs.build_number }}
            ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }}
            ORG_GRADLE_PROJECT_enableBuildSearchableOptions: true
            ORG_GRADLE_PROJECT_publishChannel: nightly
            ORG_GRADLE_PROJECT_publishToken: ${{ secrets.plugin_bot_token }}
            ORG_GRADLE_PROJECT_compileNativeCode: false

        steps:
            - name: Checkout
              uses: actions/checkout@v3

            - name: Set up JDK 17
              uses: actions/setup-java@v3
              with:
                  distribution: corretto
                  java-version: 17

            - name: Load native binaries
              uses: ./.github/actions/load-native-binaries

            - name: Publish rust plugin
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: ":plugin:publishPlugin"
                  cache-disabled: true

    save-commit:
        needs: [ build, fetch-latest-changes ]
        runs-on: ubuntu-latest
        steps:
            - name: Checkout
              uses: actions/checkout@v3
              with:
                  fetch-depth: 0
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - name: Save commits
              run: |
                  python scripts/save_tag.py --tag rust-nightly --commit ${{ needs.fetch-latest-changes.outputs.rust-commit }}


rust release

name: rust release

on:
    workflow_dispatch:
        inputs:
            type:
                required: true
                default: 'stable'
                description: 'Available values: `beta` and `stable`'
            update_changelog:
                required: false
                description: 'Enables/disables Rust changelog update'
    schedule:
        - cron: '0 3 * * *'

jobs:
    generate-build-number:
        runs-on: ubuntu-latest
        outputs:
            build_number: ${{ steps.buildnumber.outputs.build_number }}
        steps:
            - name: Generate build number
              uses: einaregilsson/build-number@v3
              id: buildnumber
              with:
                  token: ${{ secrets.github_token }}
                  prefix: rust

    get-release-branch:
        runs-on: ubuntu-latest
        outputs:
            release-branch: ${{ steps.get-release-branch.outputs.release-branch }}
        steps:
            - name: Checkout
              uses: actions/checkout@v3

            - id: get-release-branch
              run: |
                  branch=$(python scripts/get_release_branch.py)
                  echo "release-branch=$branch" >> $GITHUB_OUTPUT

    update-changelog-link:
        runs-on: ubuntu-latest
        steps:
            - name: Checkout
              uses: actions/checkout@v3
              with:
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - run: git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*

            - name: Set up git user
              run: |
                  git config --local user.email "intellij.rust@gmail.com"
                  git config --local user.name "intellij-rust-bot"

            - name: Update changelog link
              if: github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'stable' && github.event.inputs.update_changelog != 'false'
              run: python scripts/update_changelog_link.py

    get-channel:
        runs-on: ubuntu-latest
        outputs:
            channel: ${{ steps.get-channel.outputs.channel }}
        steps:
            - id: get-channel
              run: |
                if ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'stable' }}
                then
                  echo "channel=stable" >> $GITHUB_OUTPUT
                else
                  echo "channel=beta" >> $GITHUB_OUTPUT
                fi

    fetch-latest-changes:
        runs-on: ubuntu-latest
        needs: [ get-release-branch, update-changelog-link, get-channel ]
        outputs:
            rust-commit: ${{ steps.fetch-commits.outputs.rust-commit }}
            rust-release: ${{ steps.fetch-commits.outputs.rust-release }}
        steps:
            - name: Checkout
              uses: actions/checkout@v3
              with:
                  ref: ${{ needs.get-release-branch.outputs.release-branch }}
                  fetch-depth: 0

            - name: Fetch latest commits
              id: fetch-commits
              run: |
                  echo "rust-commit=$(git log -n 1 --format=format:%H)" >> $GITHUB_OUTPUT
                  echo "rust-release=$(python scripts/get_tag_commit.py --tag "rust-${{ needs.get-channel.outputs.channel }}")" >> $GITHUB_OUTPUT

            - name: Show commits
              run: |
                  echo "rust-commit: ${{ steps.fetch-commits.outputs.rust-commit }}"
                  echo "rust-release: ${{ steps.fetch-commits.outputs.rust-release }}"

    build-native-code:
        if: needs.fetch-latest-changes.outputs.rust-commit != needs.fetch-latest-changes.outputs.rust-release
        needs: [ fetch-latest-changes ]
        uses: ./.github/workflows/build-native-code.yml
        with:
            rust-version: stable
            cache: false

    build:
        if: needs.fetch-latest-changes.outputs.rust-commit != needs.fetch-latest-changes.outputs.rust-release
        needs: [ generate-build-number, get-release-branch, update-changelog-link, get-channel, fetch-latest-changes, build-native-code ]
        runs-on: ubuntu-latest
        strategy:
            fail-fast: true
            matrix:
                platform-version: [ 232, 233 ]
        env:
            ORG_GRADLE_PROJECT_buildNumber: ${{ needs.generate-build-number.outputs.build_number }}
            ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }}
            ORG_GRADLE_PROJECT_enableBuildSearchableOptions: true
            ORG_GRADLE_PROJECT_publishChannel: ${{ needs.get-channel.outputs.channel }}
            ORG_GRADLE_PROJECT_publishToken: ${{ secrets.plugin_bot_token }}
            ORG_GRADLE_PROJECT_compileNativeCode: false

        steps:
            - name: Checkout
              uses: actions/checkout@v3
              with:
                  ref: ${{ needs.get-release-branch.outputs.release-branch }}

            - name: Set up JDK 17
              uses: actions/setup-java@v3
              with:
                  distribution: corretto
                  java-version: 17

            - name: Load native binaries
              uses: ./.github/actions/load-native-binaries

            - name: Publish rust plugin
              uses: gradle/gradle-build-action@v2
              with:
                  arguments: ":plugin:publishPlugin"
                  cache-disabled: true

    save-commit:
        needs: [ get-channel, fetch-latest-changes, build ]
        runs-on: ubuntu-latest
        steps:
            - name: Checkout
              uses: actions/checkout@v3
              with:
                  fetch-depth: 0
                  token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }}

            - name: Save commits
              run: |
                  python scripts/save_tag.py --tag rust-${{ needs.get-channel.outputs.channel }} --commit ${{ needs.fetch-latest-changes.outputs.rust-commit }}


update PR project state

name: update PR project state

on:
    pull_request_target:
        types: [ labeled ]

jobs:
    update-to-test-board:
        if: github.event.label.name == 'fix' || github.event.label.name == 'feature'
        runs-on: ubuntu-latest
        steps:
            -   name: Checkout repo content
                uses: actions/checkout@v3

            -   name: Install python packages
                run: pip install -r scripts/requirements.txt

            -   name: Add to board
                run: python scripts/add_to_board.py --token ${{ secrets.WORKFLOW_GITHUB_TOKEN }} --pull_request ${{ github.event.pull_request.number }}