Release

name: Release

on:
  release:
    types: [prereleased, released]

jobs:

  # Prepare and publish the Gradle Plugins repository
  release:
    name: Release to Gradle Plugins
    runs-on: ubuntu-latest
    steps:

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

      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          distribution: zulu
          java-version: 17

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

      # Set environment variables
      - name: Export Properties
        id: properties
        shell: bash
        run: |
          CHANGELOG="$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d'
          ${{ github.event.release.body }}
          EOM
          )"

          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.changelog != '' }}
#        env:
#          CHANGELOG: ${{ steps.properties.outputs.changelog }}
#        run: |
#          ./gradlew patchChangelog --release-note="$CHANGELOG"

      # Publish the plugin to the Gradle Plugins
      - name: Publish Plugin
        env:
          GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
          GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
        run: ./gradlew publishPlugins -Pgradle.publish.key=$GRADLE_PUBLISH_KEY -Pgradle.publish.secret=$GRADLE_PUBLISH_SECRET

      # Upload artifact as a release asset
      - name: Upload Release Asset
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: gh release upload ${{ github.event.release.tag_name }} ./build/libs/*

#      # Create pull request
#      - name: Create Pull Request
#        if: ${{ 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 "Changelog update - $VERSION"
#          git push --set-upstream origin $BRANCH
#
#          gh label create "$LABEL" \
#            --description "Pull requests with release changelog update" \
#            || true
#
#          gh pr create \
#            --title "Changelog update - \`$VERSION\`" \
#            --body "Current pull request contains patched \`CHANGELOG.md\` file for the \`$VERSION\` version." \
#            --label "$LABEL" \
#            --head $BRANCH


Reusable Code Inspection

name: Reusable Code Inspection

on: [ workflow_call ]

jobs:
  codeInspection:
    name: Code Inspection
    runs-on: ubuntu-latest
    steps:

      - name: Fetch Sources
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # only report issues that appeared in a PR

      - name: Qodana - Code Inspection
        uses: JetBrains/qodana-action@v2024.1.5
        if: ${{ false }}  # disable for now


Reusable Gradle Wrapper Test

name: Reusable Gradle Wrapper Test

on: [ workflow_call ]

jobs:
  gradleValidation:
    name: Gradle Wrapper Validation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: gradle/actions/wrapper-validation@v3


Reusable Integration Tests

name: Reusable Integration Tests

on:
  workflow_call:
    inputs:
      platformVersion:
        required: false
        default: 2022.1.4
        type: string

jobs:
  integrationTests:
    name: Gradle ${{ matrix.gradleVersion }} @ ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        gradleVersion:
          - "8.2"
          - "8.8"
        os:
          - windows-latest
          - ubuntu-latest
          - macos-latest

    steps:

      # Free GitHub Actions Environment Disk Space
      - name: Maximize Build Space
        if: runner.os == 'Linux'
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          large-packages: false

      - name: Fetch Sources
        uses: actions/checkout@v4

      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          distribution: zulu
          java-version: 17

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

      - name: Run Tests
        env:
          TEST_NAME: ${{ inputs.testName }}
        shell: bash
        run: |
          [ -z "$TEST_NAME" ] && TESTS="" || TESTS="--tests $TEST_NAME"

          ./gradlew integrationTest \
            -PtestGradleVersion="${{ matrix.gradleVersion }}" \
            ${{ runner.os == 'Windows' && '-PtestGradleUserHome="C:\\testGradleHome"' || '' }} \
            --console=plain

      - name: Test Report
        uses: dorny/test-reporter@v1
        if: ${{ always() }}
        with:
          name: "Integration Tests Report: Gradle ${{ matrix.gradleVersion }} @ ${{ matrix.os }}"
          path: "**/build/test-results/*/TEST-*.xml"
          reporter: java-junit

      - name: Collect Test Results
        if: ${{ always() }}
        uses: actions/upload-artifact@v4
        with:
          name: integration-tests-${{ matrix.gradleVersion }}-${{ runner.name }}
          path: |
            ${{ github.workspace }}/build/reports/tests
            ${{ github.workspace }}/build/reports/configuration-cache


Single Unit Test

name: Single Unit Test

on:
  workflow_call:
    inputs:
      gradleVersion:
        type: string
        required: true
        description: 'Gradle version – see: https://github.com/gradle/gradle/releases'
      os:
        type: string
        required: true
        description: 'Operation system'
      cacheReadOnly:
        type: boolean
        default: false
        required: false
        description: 'Controls writing the Gradle cache'
      testName:
        type: string
        required: false
        description: 'Optional test name to run'

jobs:
  unitTests:
    name: Gradle ${{ inputs.gradleVersion }} @ ${{ inputs.os }}
    runs-on: ${{ inputs.os }}
    permissions:
      checks: write
    steps:

      # Free GitHub Actions Environment Disk Space
      - name: Maximize Build Space
        if: runner.os == 'Linux'
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          large-packages: false

      - name: Fetch Sources
        uses: actions/checkout@v4

      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          distribution: zulu
          java-version: 17

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v3
        with:
          gradle-home-cache-cleanup: true
          cache-read-only: ${{ inputs.cacheReadOnly }}

      - name: Run Tests
        env:
          TEST_NAME: ${{ inputs.testName }}
        shell: bash
        run: |
          [ -z "$TEST_NAME" ] && TESTS="" || TESTS="--tests $TEST_NAME"

          ./gradlew check test \
            -PtestGradleVersion="${{ inputs.gradleVersion }}" \
            ${{ runner.os == 'Windows' && '-PtestGradleUserHome="C:\\testGradleHome"' || '' }} \
            $TESTS \
            --console=plain

      - name: Test Report
        uses: dorny/test-reporter@v1
        if: ${{ always() }}
        with:
          name: "Unit Tests Report: Gradle ${{ inputs.gradleVersion }} @ ${{ inputs.os }}"
          path: "**/build/test-results/*/TEST-*.xml"
          reporter: java-junit

      - name: Collect Test Results
        if: ${{ always() }}
        uses: actions/upload-artifact@v4
        with:
          name: unit-tests-${{ inputs.gradleVersion }}-${{ inputs.os }}
          path: |
            ${{ github.workspace }}/build/reports/tests
            ${{ github.workspace }}/build/reports/configuration-cache


Reusable Unit Tests

name: Reusable Unit Tests

on: [ workflow_call ]

jobs:
  baseUnitTest:
    name: Gradle 8.8 @ ubuntu-latest
    uses: ./.github/workflows/reusable-single-unitTest.yml
    with:
      gradleVersion: "8.8"
      os: ubuntu-latest

  unitTests:
    name: Gradle ${{ matrix.gradleVersion }} @ ${{ matrix.os }}
    needs: baseUnitTest
    uses: ./.github/workflows/reusable-single-unitTest.yml
    with:
      gradleVersion: ${{ matrix.gradleVersion }}
      os: ${{ matrix.os }}
      cacheReadOnly: true

    strategy:
      fail-fast: false
      matrix:
        gradleVersion:
          - "8.2"
          - "8.8"
        os:
          - windows-latest
          - ubuntu-latest
          - macos-latest
        exclude:
          - os: ubuntu-latest
            gradleVersion: "8.8"


Manual Unit Tests

name: Manual Unit Tests
on:
  workflow_dispatch:
    inputs:
      testName:
        description: "Test name"
        required: false
        type: string
      gradleVersion:
        description: "Gradle Version"
        required: true
        type: string
        default: "8.8"
      os:
        description: "Operation System"
        required: true
        type: choice
        options:
          - ubuntu-latest
          - windows-latest
          - macos-latest
  workflow_call:

jobs:
  unitTest:
    name: Gradle ${{ inputs.gradleVersion }} @ ${{ inputs.os }}
    uses: ./.github/workflows/reusable-single-unitTest.yml
    with:
      gradleVersion: ${{ inputs.gradleVersion }}
      os: ${{ inputs.os }}
      testName: ${{ inputs.testName }}


API compatibility verification

name: API compatibility verification

on:
  pull_request:
    types: [ opened, synchronize, reopened, labeled, unlabeled ]
    branches: [ main ]

jobs:
  api-compat-verification:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Check for API compatibility
        if: ${{ !contains(github.event.pull_request.labels.*.name, 'acknowledge-api-break') }}
        run: |
          git fetch origin ${{ github.base_ref }} --depth 1 && \
          git diff remotes/origin/${{ github.base_ref }} --numstat "*.api" | awk '
            BEGIN { s = 0 }

            # git diff numstat shows lines deleted in field 2, hence sum up field 2 across all items
            { s += $2 }

            # exit with the number of lines deleted as the result code so that `if failure()` works below
            END { exit s }
          '
      - name: Error message
        if: ${{ failure() }}
        run: |
          echo "::error ::This change modifies the public API in a way that may be backwards-incompatible. Carefully review this pull request and either:"
          echo "::error ::* Revert the changes which caused the API incompatibility –or–"
          echo "::error ::* Add the 'acknowledge-api-break' label to this PR (in rare cases warranting an API breakage)"
          exit 1


Artifact Size Metrics

name: Artifact Size Metrics
on:
  pull_request:
    types: [ opened, synchronize, reopened, labeled, unlabeled ]
    branches: [ main ]
  release:
    types: [published]

permissions:
  id-token: write
  contents: read
  pull-requests: write

jobs:
  release-metrics:
    if: github.event_name == 'release'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Sources
        uses: actions/checkout@v4
      - name: Configure JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'corretto'
          java-version: 17
          cache: 'gradle'
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }}
          aws-region: us-west-2
      - name: Generate Artifact Size Metrics
        run: ./gradlew artifactSizeMetrics
      - name: Save Artifact Size Metrics
        run: ./gradlew saveArtifactSizeMetrics -Prelease=${{ github.event.release.tag_name }}
      - name: Put Artifact Size Metrics in CloudWatch
        run: ./gradlew putArtifactSizeMetricsInCloudWatch -Prelease=${{ github.event.release.tag_name }}
  size-check:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Sources
        uses: actions/checkout@v4
      - name: Configure JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'corretto'
          java-version: 17
          cache: 'gradle'
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }}
          aws-region: us-west-2
      - name: Generate Artifact Size Metrics
        run: ./gradlew artifactSizeMetrics
      - name: Analyze Artifact Size Metrics
        run: ./gradlew analyzeArtifactSizeMetrics
      - name: Show Results
        uses: actions/github-script@v7
        with:
          script: |
            const getComments =
              `query {
                repository(owner:"${context.repo.owner}", name:"${context.repo.repo}"){
                  pullRequest(number: ${context.issue.number}) {
                    id
                    comments(last:100) {
                      nodes {
                        id
                        body
                        author {
                          login
                        }
                        isMinimized
                      }
                    }
                  }
                }
              }`

            const response = await github.graphql(getComments)
            const comments = response.repository.pullRequest.comments.nodes

            const mutations = comments
              .filter(comment => comment.author.login == 'github-actions' && !comment.isMinimized && comment.body.startsWith('Affected Artifacts'))
              .map(comment =>
                github.graphql(
                  `mutation {
                    minimizeComment(input:{subjectId:"${comment.id}", classifier:OUTDATED}){
                      clientMutationId
                    }
                  }`
                )
              )
            await Promise.all(mutations)

            const fs = require('node:fs')
            const comment = fs.readFileSync('build/reports/metrics/artifact-analysis.md', 'utf8')

            const writeComment =
              `mutation {
                addComment(input:{body:"""${comment}""", subjectId:"${response.repository.pullRequest.id}"}){
                  clientMutationId
                }
              }`

            await github.graphql(writeComment)

      - name: Evaluate
        if: ${{ !contains(github.event.pull_request.labels.*.name, 'acknowledge-artifact-size-increase') }}
        run: |
          cd build/reports/metrics
          cat has-significant-change.txt | grep false || {
            echo An artifact increased in size by more than allowed or a new artifact was created.
            echo If this is expected please add the 'acknowledge-artifact-size-increase' label to this pull request.
            exit 1
          }


Changelog verification

name: Changelog verification

on:
  pull_request:
    types: [ opened, synchronize, reopened, labeled, unlabeled ]
    branches: [ main ]

jobs:
  changelog-verification:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Check for changelog entry
        if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog') }}
        run: |
          git fetch origin ${{ github.base_ref }} --depth 1 && \
          git diff remotes/origin/${{ github.base_ref }} --name-only | grep -P "\.changes/[0-9a-f-]+\.json"
      - name: Error message
        if: ${{ failure() }}
        run: |
          echo "::error ::No new/updated changelog entry found in /.changes directory. Please either:"
          echo "::error ::* Add a changelog entry (see CONTRIBUTING.md for instructions) –or–"
          echo "::error ::* Add the 'no-changelog' label to this PR (in rare cases not warranting a changelog entry)"
          exit 1


CI

name: CI

on:
  push:
    branches: [ main ]
  pull_request:
  workflow_dispatch:

# Allow one instance of this workflow per pull request, and cancel older runs when new changes are pushed
concurrency:
  group: ci-pr-${{ github.ref }}
  cancel-in-progress: true

env:
  RUN: ${{ github.run_id }}-${{ github.run_number }}
  GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dkotlin.incremental=false"

jobs:
  jvm:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        # we build with a specific JDK version but source/target compatibility should ensure the jar is usable by
        # the target versions we want to support
        java-version:
          - 8
          - 11
          - 17
          - 21
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
      - name: Configure JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'corretto'
          java-version: 17
          cache: 'gradle'
      - name: Test
        shell: bash
        run: |
          ./gradlew -Ptest.java.version=${{ matrix.java-version }} jvmTest --stacktrace

  all-platforms:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ ubuntu-latest, macos-latest, windows-latest ]
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
      - name: Configure JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'corretto'
          java-version: 17
          cache: 'gradle'
      - name: Test
        shell: bash
        run: |
          echo "kotlinWarningsAsErrors=true" >> $GITHUB_WORKSPACE/local.properties
          ./gradlew apiCheck
          ./gradlew test jvmTest
      - name: Save Test Reports
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: test-reports-${{ matrix.os }}
          path: '**/build/reports'

  protocol-tests:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
      - name: Configure JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'corretto'
          java-version: 17
          cache: 'gradle'
      - name: Test
        shell: bash
        run: |
          ./gradlew publishToMavenLocal
          ./gradlew testAllProtocols

  downstream:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout sources
      uses: actions/checkout@v4
      with:
        path: 'smithy-kotlin'
    - name: Checkout tools
      uses: actions/checkout@v4
      with:
        path: 'aws-kotlin-repo-tools'
        repository: 'awslabs/aws-kotlin-repo-tools'
        ref: '0.2.3'
        sparse-checkout: |
          .github
    - name: Checkout aws-sdk-kotlin
      uses: ./aws-kotlin-repo-tools/.github/actions/checkout-head
      with:
        # smithy-kotlin is checked out as a sibling dir which will automatically make it an included build
        path: 'aws-sdk-kotlin'
        repository: 'awslabs/aws-sdk-kotlin'
    - name: Configure JDK
      uses: actions/setup-java@v3
      with:
        distribution: 'corretto'
        java-version: 17
        cache: 'gradle'
    - name: Build and Test aws-sdk-kotlin downstream
      run: |
        # TODO - JVM only
        cd $GITHUB_WORKSPACE/smithy-kotlin
        ./gradlew --parallel publishToMavenLocal
        SMITHY_KOTLIN_RUNTIME_VERSION=$(grep sdkVersion= gradle.properties | cut -d = -f 2)
        SMITHY_KOTLIN_CODEGEN_VERSION=$(grep codegenVersion= gradle.properties | cut -d = -f 2)
        cd $GITHUB_WORKSPACE/aws-sdk-kotlin
        # replace smithy-kotlin-runtime-version and smithy-kotlin-codegen-version to be
        # whatever we are testing such that the protocol test projects don't fail with a
        # version that doesn't exist locally or in maven central. Otherwise the generated
        # protocol test projects will use whatever the SDK thinks the version of
        # smithy-kotlin should be
        sed -i "s/smithy-kotlin-runtime-version = .*$/smithy-kotlin-runtime-version = \"$SMITHY_KOTLIN_RUNTIME_VERSION\"/" gradle/libs.versions.toml
        sed -i "s/smithy-kotlin-codegen-version = .*$/smithy-kotlin-codegen-version = \"$SMITHY_KOTLIN_CODEGEN_VERSION\"/" gradle/libs.versions.toml
        ./gradlew --parallel publishToMavenLocal
        ./gradlew test jvmTest
        ./gradlew testAllProtocols


Dependabot Dependency Submission

name: Dependabot Dependency Submission

on:
  push:
    branches: [ main ]

permissions:
  contents: write

jobs:
  dependency-submission:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
      - name: Generate and submit dependency graph
        uses: gradle/actions/dependency-submission@v3


Lint

name: Lint

on:
  push:
    branches:
      - '**'
      - '!main'
  pull_request:
    branches: [ main ]
  workflow_dispatch:

env:
  PACKAGE_NAME: smithy-kotlin

jobs:
  ktlint:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout sources
      uses: actions/checkout@v2
    - name: Lint ${{ env.PACKAGE_NAME }}
      run: |
        ./gradlew ktlint


Close stale issues

name: "Close stale issues"

# Controls when the action will run.
on:
  schedule:
  - cron: "0 0/3 * * *"

jobs:
  cleanup:
    name: Stale issue job
    runs-on: ubuntu-latest
    steps:
    - uses: aws-actions/stale-issue-cleanup@v4
      with:
        # Setting messages to an empty string will cause the automation to skip
        # that category
        ancient-issue-message: This is a very old issue that is probably not getting as much
          attention as it deserves. We encourage you to check if this is still an issue in
          the latest release and if you find that this is still a problem, please feel free
          to provide a comment or open a new issue.
        stale-issue-message: It looks like this issue has not been active for more than 5 days.
          In the absence of more information, we will be closing this issue soon. If you find
          that this is still a problem, please add a comment to prevent automatic closure, or
          if the issue is already closed please feel free to reopen it.
        stale-pr-message: It looks like this PR has not been active for more than five days. In
          the absence of more information, we will be closing this PR soon. Please add a
          comment to prevent automatic closure, or if the PR is already closed please feel
          free to open a new one.

        # These labels are required
        stale-issue-label: closing-soon
        exempt-issue-labels: no-auto-closure,feature-request
        stale-pr-label: closing-soon
        exempt-pr-labels: pr/needs-review
        response-requested-label: response-requested

        # Don't set closed-for-staleness label to skip closing very old issues
        # regardless of label
        closed-for-staleness-label: closed-for-staleness

        # Issue timing
        days-before-stale: 10
        days-before-close: 4
        days-before-ancient: 365

        # If you don't want to mark a issue as being ancient based on a
        # threshold of "upvotes", you can set this here. An "upvote" is
        # the total number of +1, heart, hooray, and rocket reactions
        # on an issue.
        minimum-upvotes-to-exempt: 1

        repo-token: ${{ secrets.GITHUB_TOKEN }}
        loglevel: DEBUG
        # Set dry-run to true to not perform label or close actions.
        #dry-run: true


Issue moderator

name: Issue moderator

on:
  issues:
    types: [opened, edited, reopened]
  issue_comment:
    types: [created]

jobs:
  moderate:
    runs-on: ubuntu-latest
    steps:
      - name: Moderate issues
        uses: tachiyomiorg/issue-moderator-action@39b363606e00c4ae39a15b3705a63535cd428c15 # v2.6.0
        # uses: keiyoushi/issue-moderator-action@a017be83547db6e107431ce7575f53c1dfa3296a
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          duplicate-label: Duplicate

          auto-close-rules: |
            [
              {
                "type": "body",
                "regex": ".*\\* (Komikku version|Android version|Device): \\?.*",
                "message": "Requested information in the template was not filled out."
              },
              {
                "type": "both",
                "regex": "^(?!.*myanimelist.*).*(aniyomi|anime).*$",
                "ignoreCase": true,
                "message": "Komikku does not support anime, and has no plans to support anime. In addition Komikku is not affiliated with Aniyomi https://github.com/jmir1/aniyomi"
              },
              {
                "type": "both",
                "regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(?<!n[o']?t )blocked by|error) (?:to )?(?:get past|by ?pass|penetrate)?.*cloud ?fl?are.*",
                "ignoreCase": true,
                "labels": ["Cloudflare protected"],
                "message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
              },
              {
                "type": "both",
                "regex": "^.*(myanimelist|mal).*$",
                "ignoreCase": true,
                "message": "For issues with linking MyAnimeList, please follow these steps:\n1. Update Komikku to latest version\n2. Change your default User-Agent (`More → Settings → Advanced → Default user agent string`)\n3. Close and restart App\n4. Attempt to link MyAnimeList again\n\nIf you had MyAnimeList linked before, try to unlink it first before trying these steps."
              }
            ]
          auto-close-ignore-label: do-not-autoclose


CI

name: CI
on:
  push:
    branches:
      - develop
      # - master
    paths-ignore:
      - '**.md'
  # pull_request:
  #   paths-ignore:
  #     - '**.md'
  # Dispatch or Manual triggers
  workflow_dispatch:

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

permissions:
  contents: read

jobs:
  build:
    name: Build app
    runs-on: ubuntu-latest

    steps:
      - name: Clone repo
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Setup Android SDK
        run: |
          ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
        with:
          java-version: 17
          distribution: temurin

      - name: Write google-services.json
        uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 # v1.3
        with:
          path: app/google-services.json
          contents: ${{ secrets.GOOGLE_SERVICES_JSON }}
          write-mode: overwrite

      - name: Write client_secrets.json
        uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 # v1.3
        with:
          path: app/src/main/assets/client_secrets.json
          contents: ${{ secrets.GOOGLE_CLIENT_SECRETS_JSON }}
          write-mode: overwrite

      - name: Set up gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Build app and run unit tests
        run: ./gradlew assembleStandardPreview testStandardPreviewUnitTest testReleaseUnitTest --stacktrace

      - name: Sign APK
        uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # v1
        with:
          releaseDirectory: app/build/outputs/apk/standard/preview
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}

      - name: Rename apk
        run: |
          set -x
          version_tag=${GITHUB_REF/refs\/heads\//}
          commit_count=$(git rev-list --count HEAD)
          echo "VERSION_TAG=$version_tag" >> $GITHUB_ENV
          echo "COMMIT_COUNT=$commit_count" >> $GITHUB_ENV
          mv app/build/outputs/apk/standard/preview/app-standard-universal-preview-signed.apk Komikku-$version_tag-r$commit_count.apk

      - name: Upload APK
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          name: Komikku-${{ env.VERSION_TAG }}-r${{ env.COMMIT_COUNT }}.apk
          path: ./*.apk
          retention-days: 1


Preview Builder

name: Preview Builder
# Manually build a standard preview

on:
  # Dispatch or Manual triggers
  workflow_dispatch:

#  push:
#    branches:
#      - master
#    paths:
#      - '**'
#      - '!**.md'
#      - '!.github/**'
#      - '.github/scripts/**'
#      - '.github/workflows/**'

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

jobs:
  prepare-build:
    if: github.ref == 'refs/tags/master' && github.repository == 'komikku-app/komikku'
    runs-on: ubuntu-latest
    outputs:
      COMMIT_COUNT: ${{ steps.prepare_env.outputs.COMMIT_COUNT }}
      CURRENT_SHA: ${{ steps.prepare_env.outputs.CURRENT_SHA }}
      PREV_RELEASE_SHA: ${{ steps.prepare_env.outputs.PREV_RELEASE_SHA }}
      COMMIT_LOGS: ${{ steps.prepare_env.outputs.COMMIT_LOGS }}
      TAG_PREFIX: r
      ARTIFACTS_PREFIX: standard/preview/app-standard
    steps:
      - name: Get tag name
        id: get_tag
        run: |
          set -x
          echo "VERSION_TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT

      - name: Clone Repository (${{ steps.get_tag.outputs.VERSION_TAG }})
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Prepare env
        id: prepare_env
        run: |
          set -e

          commit_count=$(git rev-list --count HEAD)
          echo "COMMIT_COUNT=$commit_count" >> $GITHUB_OUTPUT
          current_sha=$(git rev-parse --short HEAD)
          echo "CURRENT_SHA=$current_sha" >> $GITHUB_OUTPUT

          prev_release_sha=$(git log --oneline --decorate --tags --no-walk --pretty='%C(auto)%h' | head -n 1)
          echo "PREV_RELEASE_SHA=$prev_release_sha" >> $GITHUB_OUTPUT

          echo "COMMIT_LOGS<<{delimiter}
          $(curl -H "Accept: application/vnd.github.v3+json" \
            "https://api.github.com/repos/komikku-app/komikku/compare/$prev_release_sha...$current_sha" \
            | jq '[.commits[]|{message:(.commit.message | split("\n")), username:.author.login}]' \
            | jq -r '.[]|"- \(.message | first) (@\(.username))"')
          {delimiter}" >> $GITHUB_OUTPUT

  build-app:
    runs-on: ubuntu-latest
    needs: prepare-build
    steps:
      - name: Clone Repository (${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }})
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0

      - name: Setup Android SDK
        run: |
          ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
        with:
          java-version: 17
          distribution: temurin

      - name: Write google-services.json
        uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 # v1.3
        with:
          path: app/google-services.json
          contents: ${{ secrets.GOOGLE_SERVICES_JSON }}
          write-mode: overwrite

      - name: Write client_secrets.json
        uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 # v1.3
        with:
          path: app/src/main/assets/client_secrets.json
          contents: ${{ secrets.GOOGLE_CLIENT_SECRETS_JSON }}
          write-mode: overwrite

      - name: Set up gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Build Preview app and run unit tests
        run: ./gradlew assembleStandardPreview testStandardPreviewUnitTest testReleaseUnitTest --stacktrace

      - name: Upload artifacts
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          path: "**/*.apk"
          retention-days: 1

  release-app:
    runs-on: ubuntu-latest
    needs:
      - prepare-build
      - build-app
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
        with:
          merge-multiple: true

      - name: Setup Android SDK
        run: |
          ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"

      - name: Sign APK
        uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # v1
        with:
          releaseDirectory: app/build/outputs/apk/standard/preview
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}

      - name: Get SHA & clean up build artifacts
        id: get_sha
        run: |
          set -e

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-universal-preview-signed.apk Komikku-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_UNIVERSAL_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-arm64-v8a-preview-signed.apk Komikku-arm64-v8a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-arm64-v8a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_ARM64_V8A_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-armeabi-v7a-preview-signed.apk Komikku-armeabi-v7a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-armeabi-v7a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_ARMEABI_V7A_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-x86-preview-signed.apk Komikku-x86-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-x86-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_X86_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-x86_64-preview-signed.apk Komikku-x86_64-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-x86_64-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_X86_64_SHA=$sha" >> $GITHUB_OUTPUT

      - name: Create release
        uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # v2.0.6
        with:
          tag_name: ${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}
          name: Komikku Preview ${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}
          body: |
            ### Commits

            https://github.com/komikku-app/komikku/compare/${{ needs.prepare-build.outputs.PREV_RELEASE_SHA }}...${{ needs.prepare-build.outputs.CURRENT_SHA }}

            ${{ needs.prepare-build.outputs.COMMIT_LOGS }}

            ---

            ### Checksums

            | Variant | SHA-256 |
            | ------- | ------- |
            | Universal | ${{ steps.get_sha.outputs.APK_UNIVERSAL_SHA }} |
            | arm64-v8a | ${{ steps.get_sha.outputs.APK_ARM64_V8A_SHA }} |
            | armeabi-v7a | ${{ steps.get_sha.outputs.APK_ARMEABI_V7A_SHA }} |
            | x86 | ${{ steps.get_sha.outputs.APK_X86_SHA }} |
            | x86_64 | ${{ steps.get_sha.outputs.APK_X86_64_SHA }} |

            ## If you are unsure which apk to download then go with Komikku-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          files: |
            Komikku-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
            Komikku-arm64-v8a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
            Komikku-armeabi-v7a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
            Komikku-x86-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
            Komikku-x86_64-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          draft: true
          prerelease: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


PR build check

name: PR build check
on:
  #  push:
  #    branches:
  #      - develop
  #    paths-ignore:
  #      - '**.md'
  pull_request:
    paths-ignore:
      - '**.md'
  # Dispatch or Manual triggers
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  build:
    name: Build app
    runs-on: ubuntu-latest

    steps:
      - name: Clone repo
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Dependency Review
        uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3

      - name: Setup Android SDK
        run: |
          ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
        with:
          java-version: 17
          distribution: temurin

      - name: Write google-services.json
        uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 # v1.3
        with:
          path: app/google-services.json
          contents: ${{ secrets.GOOGLE_SERVICES_JSON }}
          write-mode: overwrite

      - name: Write client_secrets.json
        uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 # v1.3
        with:
          path: app/src/main/assets/client_secrets.json
          contents: ${{ secrets.GOOGLE_CLIENT_SECRETS_JSON }}
          write-mode: overwrite

      - name: Set up gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Build app and run unit tests
        run: ./gradlew assembleStandardPreview testStandardPreviewUnitTest testReleaseUnitTest --stacktrace


Benchmark Builder

name: Benchmark Builder
# Manually build a dev banchmark

on:
  # Dispatch or Manual triggers
  workflow_dispatch:

#  push:
#    branches:
#      - dev-benchmark

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

jobs:
  prepare-build:
    if: github.ref == 'refs/tags/master' && github.repository == 'komikku-app/komikku'
    runs-on: ubuntu-latest
    outputs:
      COMMIT_COUNT: ${{ steps.prepare_env.outputs.COMMIT_COUNT }}
      CURRENT_SHA: ${{ steps.prepare_env.outputs.CURRENT_SHA }}
      PREV_RELEASE_SHA: ${{ steps.prepare_env.outputs.PREV_RELEASE_SHA }}
      COMMIT_LOGS: ${{ steps.prepare_env.outputs.COMMIT_LOGS }}
      TAG_PREFIX: benchmark-r
      ARTIFACTS_PREFIX: dev/benchmark/app-dev
    steps:
      - name: Get tag name
        id: get_tag
        run: |
          set -x
          echo "VERSION_TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT

      - name: Clone Repository (${{ steps.get_tag.outputs.VERSION_TAG }})
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Prepare env
        id: prepare_env
        run: |
          set -e

          commit_count=$(git rev-list --count HEAD)
          echo "COMMIT_COUNT=$commit_count" >> $GITHUB_OUTPUT
          current_sha=$(git rev-parse --short HEAD)
          echo "CURRENT_SHA=$current_sha" >> $GITHUB_OUTPUT

          prev_release_sha=$(git log --oneline --decorate --tags --no-walk --pretty='%C(auto)%h' | head -n 1)
          echo "PREV_RELEASE_SHA=$prev_release_sha" >> $GITHUB_OUTPUT

          echo "COMMIT_LOGS<<{delimiter}
          $(curl -H "Accept: application/vnd.github.v3+json" \
            "https://api.github.com/repos/komikku-app/komikku/compare/$prev_release_sha...$current_sha" \
            | jq '[.commits[]|{message:(.commit.message | split("\n")), username:.author.login}]' \
            | jq -r '.[]|"- \(.message | first) (@\(.username))"')
          {delimiter}" >> $GITHUB_OUTPUT

  build-app:
    runs-on: ubuntu-latest
    needs: prepare-build
    steps:
      - name: Clone Repository (${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }})
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0

      - name: Setup Android SDK
        run: |
          ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
        with:
          java-version: 17
          distribution: temurin

      - name: Set up gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Build Benchmark app and run unit tests
        run: ./gradlew assembleDevBenchmark testDevBenchmarkUnitTest testReleaseUnitTest --stacktrace

      - name: Upload artifacts
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          path: "**/*.apk"
          retention-days: 1

  release-app:
    runs-on: ubuntu-latest
    needs:
      - prepare-build
      - build-app
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
        with:
          merge-multiple: true

      - name: Setup Android SDK
        run: |
          ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"

      - name: Sign APK
        uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # v1
        with:
          releaseDirectory: app/build/outputs/apk/dev/benchmark
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}

      - name: Get SHA & clean up build artifacts
        id: get_sha
        run: |
          set -e

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-universal-benchmark-signed.apk Komikku-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_UNIVERSAL_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-arm64-v8a-benchmark-signed.apk Komikku-arm64-v8a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-arm64-v8a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_ARM64_V8A_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-armeabi-v7a-benchmark-signed.apk Komikku-armeabi-v7a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-armeabi-v7a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_ARMEABI_V7A_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-x86-benchmark-signed.apk Komikku-x86-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-x86-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_X86_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/${{ needs.prepare-build.outputs.ARTIFACTS_PREFIX }}-x86_64-benchmark-signed.apk Komikku-x86_64-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          sha=`sha256sum Komikku-x86_64-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk | awk '{ print $1 }'`
          echo "APK_X86_64_SHA=$sha" >> $GITHUB_OUTPUT

      - name: Create release
        uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # v2.0.6
        with:
          tag_name: ${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}
          name: Komikku Benchmark ${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}
          body: |
            ### Commits

            https://github.com/komikku-app/komikku/compare/${{ needs.prepare-build.outputs.PREV_RELEASE_SHA }}...${{ needs.prepare-build.outputs.CURRENT_SHA }}

            ${{ needs.prepare-build.outputs.COMMIT_LOGS }}

            ---

            ### Checksums

            | Variant | SHA-256 |
            | ------- | ------- |
            | Universal | ${{ steps.get_sha.outputs.APK_UNIVERSAL_SHA }} |
            | arm64-v8a | ${{ steps.get_sha.outputs.APK_ARM64_V8A_SHA }} |
            | armeabi-v7a | ${{ steps.get_sha.outputs.APK_ARMEABI_V7A_SHA }} |
            | x86 | ${{ steps.get_sha.outputs.APK_X86_SHA }} |
            | x86_64 | ${{ steps.get_sha.outputs.APK_X86_64_SHA }} |

            ## If you are unsure which apk to download then go with Komikku-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          files: |
            Komikku-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
            Komikku-arm64-v8a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
            Komikku-armeabi-v7a-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
            Komikku-x86-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
            Komikku-x86_64-${{ needs.prepare-build.outputs.TAG_PREFIX }}${{ needs.prepare-build.outputs.COMMIT_COUNT }}.apk
          draft: true
          prerelease: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


Remote Dispatch Preview Build

name: Remote Dispatch Preview Build
# Manually send a dispatch to build standard preview (master) or dev preview (other refs)

on:
  # Dispatch or Manual triggers
  workflow_dispatch:

#  push:
#    branches:
#      - master
#      # - develop
#    paths:
#      - '**'
#      - '!**.md'
#      - '!.github/**'
#      - '.github/scripts/**'
#      - '.github/workflows/**'

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

jobs:
  trigger_preview_build:
    name: Trigger preview build
    runs-on: ubuntu-latest

    steps:
      - name: Clone repo
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Prepare build
        id: prepare_build
        run: |
          set -e

          commit_count=$(git rev-list --count HEAD)
          echo "COMMIT_COUNT=$commit_count" >> $GITHUB_OUTPUT

      - name: Create Tag 'r${{ steps.prepare_build.outputs.COMMIT_COUNT }}'
        if: ${{ github.ref }} == "refs/heads/master"
        run: |
          git tag "r${{ steps.prepare_build.outputs.COMMIT_COUNT }}"
          git push origin "r${{ steps.prepare_build.outputs.COMMIT_COUNT }}"

      - name: Get branch names
        id: branch_names
        uses: tj-actions/branch-names@6871f53176ad61624f978536bbf089c574dc19a2 # v8.0.1

      - name: Invoke workflow in preview repo
        uses: benc-uk/workflow-dispatch@25b02cc069be46d637e8fe2f1e8484008e9e9609 # v1.2.3
        with:
          workflow: build_app.yml
          repo: komikku-app/komikku-preview
          ref: "refs/heads/main"
          token: "${{ secrets.BOT_PAT }}"
          inputs: '{ "git-ref": "${{ steps.branch_names.outputs.current_branch }}" }'


Release Builder

name: Release Builder
# Build a stable release on a tag newly pushed

on:
  push:
    tags:
      - v*
  # Dispatch or Manual triggers
  workflow_dispatch:

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

jobs:
  build-app:
    if: startsWith(github.ref, 'refs/tags/') && github.repository == 'komikku-app/komikku'
    name: Build release app
    runs-on: ubuntu-latest
    outputs:
      VERSION_TAG: ${{ steps.get_tag.outputs.VERSION_TAG }}
    steps:
      - name: Get tag name
        id: get_tag
        run: |
          set -x
          echo "VERSION_TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT

      - name: Clone Repository (${{ steps.get_tag.outputs.VERSION_TAG }})
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Setup Android SDK
        run: |
          ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
        with:
          java-version: 17
          distribution: temurin

      - name: Write google-services.json
        uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 # v1.3
        with:
          path: app/google-services.json
          contents: ${{ secrets.GOOGLE_SERVICES_JSON }}
          write-mode: overwrite

      - name: Write client_secrets.json
        uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 # v1.3
        with:
          path: app/src/main/assets/client_secrets.json
          contents: ${{ secrets.GOOGLE_CLIENT_SECRETS_JSON }}
          write-mode: overwrite

      - name: Set up gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Build app and run unit tests
        run: ./gradlew assembleStandardRelease testStandardReleaseUnitTest testReleaseUnitTest --stacktrace

      - name: Upload artifacts
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          path: "**/*.apk"
          retention-days: 1

  release-app:
    runs-on: ubuntu-latest
    needs:
      - build-app
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
        with:
          merge-multiple: true

      - name: Setup Android SDK
        run: |
          ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"

      - name: Sign APK
        uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # v1
        with:
          releaseDirectory: app/build/outputs/apk/standard/release
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}

      - name: Get SHA & clean up build artifacts
        id: get_sha
        run: |
          set -e

          mv app/build/outputs/apk/standard/release/app-standard-universal-release-unsigned-signed.apk Komikku-${{ needs.build-app.outputs.VERSION_TAG }}.apk
          sha=`sha256sum Komikku-${{ needs.build-app.outputs.VERSION_TAG }}.apk | awk '{ print $1 }'`
          echo "APK_UNIVERSAL_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned-signed.apk Komikku-arm64-v8a-${{ needs.build-app.outputs.VERSION_TAG }}.apk
          sha=`sha256sum Komikku-arm64-v8a-${{ needs.build-app.outputs.VERSION_TAG }}.apk | awk '{ print $1 }'`
          echo "APK_ARM64_V8A_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/standard/release/app-standard-armeabi-v7a-release-unsigned-signed.apk Komikku-armeabi-v7a-${{ needs.build-app.outputs.VERSION_TAG }}.apk
          sha=`sha256sum Komikku-armeabi-v7a-${{ needs.build-app.outputs.VERSION_TAG }}.apk | awk '{ print $1 }'`
          echo "APK_ARMEABI_V7A_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/standard/release/app-standard-x86-release-unsigned-signed.apk Komikku-x86-${{ needs.build-app.outputs.VERSION_TAG }}.apk
          sha=`sha256sum Komikku-x86-${{ needs.build-app.outputs.VERSION_TAG }}.apk | awk '{ print $1 }'`
          echo "APK_X86_SHA=$sha" >> $GITHUB_OUTPUT

          mv app/build/outputs/apk/standard/release/app-standard-x86_64-release-unsigned-signed.apk Komikku-x86_64-${{ needs.build-app.outputs.VERSION_TAG }}.apk
          sha=`sha256sum Komikku-x86_64-${{ needs.build-app.outputs.VERSION_TAG }}.apk | awk '{ print $1 }'`
          echo "APK_X86_64_SHA=$sha" >> $GITHUB_OUTPUT

      - name: Create release
        uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # v2.0.6
        with:
          tag_name: ${{ needs.build-app.outputs.VERSION_TAG }}
          name: Komikku ${{ needs.build-app.outputs.VERSION_TAG }}
          body: |
            ---

            ### Checksums

            | Variant | SHA-256 |
            | ------- | ------- |
            | Universal | ${{ steps.get_sha.outputs.APK_UNIVERSAL_SHA }} |
            | arm64-v8a | ${{ steps.get_sha.outputs.APK_ARM64_V8A_SHA }} |
            | armeabi-v7a | ${{ steps.get_sha.outputs.APK_ARMEABI_V7A_SHA }} |
            | x86 | ${{ steps.get_sha.outputs.APK_X86_SHA }} |
            | x86_64 | ${{ steps.get_sha.outputs.APK_X86_64_SHA }} |

            ## If you are unsure which apk to download then go with Komikku-${{ needs.build-app.outputs.VERSION_TAG }}.apk
          files: |
            Komikku-${{ needs.build-app.outputs.VERSION_TAG }}.apk
            Komikku-arm64-v8a-${{ needs.build-app.outputs.VERSION_TAG }}.apk
            Komikku-armeabi-v7a-${{ needs.build-app.outputs.VERSION_TAG }}.apk
            Komikku-x86-${{ needs.build-app.outputs.VERSION_TAG }}.apk
            Komikku-x86_64-${{ needs.build-app.outputs.VERSION_TAG }}.apk
          draft: true
          prerelease: false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


Mirror Sync

# Sync repo to the Codeberg mirror
name: Mirror Sync

on:
  push:
    branches: [ "master" ]
  workflow_dispatch: # Manual dispatch
  # schedule:
  #   - cron: "0 */8 * * *"

jobs:
  codeberg:
    if: github.repository == 'komikku-app/komikku'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0
      - uses: pixta-dev/repository-mirroring-action@674e65a7d483ca28dafaacba0d07351bdcc8bd75 # v1.1.1
        with:
          target_repo_url: "git@codeberg.org:cuong-tran/komikku.git"
          ssh_private_key: ${{ secrets.CODEBERG_SSH }}


Lock threads

name: Lock threads

on:
  # Daily
  schedule:
    - cron: '0 0 * * *'
  # Manual trigger
  workflow_dispatch:
    inputs:

jobs:
  lock:
    runs-on: ubuntu-latest
    steps:
      - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5
        with:
          github-token: ${{ github.token }}
          issue-inactive-days: '2'
          pr-inactive-days: '2'


Auto cherry-pick Keiyoushi

name: Auto cherry-pick Keiyoushi

on:
  schedule:
    - cron: "0 */8 * * *"
  workflow_dispatch: # Manual dispatch

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

jobs:
  auto-merge:
    name: Auto cherry-pick Keiyoushi
    if: github.repository == 'komikku-app/komikku-extensions'
    runs-on: ubuntu-latest

    steps:
      - name: Clone master
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
        with:
          ref: master
          path: master
          fetch-depth: 100
          token: ${{ secrets.BOT_PAT }}

      - name: Import GPG key
        id: import-gpg
        uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6
        with:
          workdir: master
          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
          passphrase: ${{ secrets.PASSPHRASE }}
          git_user_signingkey: true
          git_commit_gpgsign: true

      - name: GPG user IDs
        run: |
          echo "fingerprint: ${{ steps.import-gpg.outputs.fingerprint }}"
          echo "keyid:       ${{ steps.import-gpg.outputs.keyid }}"
          echo "name:        ${{ steps.import-gpg.outputs.name }}"
          echo "email:       ${{ steps.import-gpg.outputs.email }}"

      - name: Cherry-picking
        run: |
          cd master

          git fetch origin keiyoushi
          git checkout keiyoushi
          echo "Last commit: `git log -1 --oneline`"
          git remote add keiyoushi-remote https://github.com/keiyoushi/extensions-source
          git fetch keiyoushi-remote
          echo "Last commit: `git log -1 --oneline keiyoushi-remote/main`"

          echo "Incoming commits:"
          echo "`git log keiyoushi..keiyoushi-remote/main --oneline`"
          upcoming_changes=`git log keiyoushi..keiyoushi-remote/main --pretty="%h"`

          # Read the upcoming changes' hash into an array
          upcoming_changes_hash=()
          while read -r line; do
            upcoming_changes_hash+=("$line")
          done <<< "$upcoming_changes"

          git checkout master
          echo "Last commit: `git log -1 --oneline`"
          # Loop through indices in reverse order
          for (( i=${#upcoming_changes_hash[@]}; i>=0; i-- )); do
            if [ ! -z "${upcoming_changes_hash[i]}" -a "${upcoming_changes_hash[i]}" != " " ]; then
              echo "[$i]: Cherry picking '${upcoming_changes_hash[i]}'"
              git cherry-pick "${upcoming_changes_hash[i]}"
              latest_hash=${upcoming_changes_hash[i]}
            else
              echo "[$i]: skip '${upcoming_changes_hash[i]}'"
            fi
          done

          echo "Last commit: `git log -1 --oneline`"

          echo "LATEST_HASH=${latest_hash}" >> $GITHUB_ENV

      - name: Merging keiyoushi-remote to keiyoushi
        run: |
          cd master
          git checkout keiyoushi
          echo "Last commit: `git log -1 --oneline`"
          latest_commit_hash=${{ env.LATEST_HASH }}
          if [ ! -z "$latest_commit_hash" -a "$latest_commit_hash" != " " ]; then
            echo "Merging commit '$latest_commit_hash' from 'keiyoushi-remote' into keiyoushi"
            git merge "$latest_commit_hash" --no-ff -m "Merge branch 'keiyoushi-remote' into keiyoushi"
          fi

          echo "Last commit: `git log -1 --oneline`"

      - name: Merging master to merge-keiyoushi
        run: |
          cd master
          git fetch origin merge-keiyoushi
          git checkout merge-keiyoushi
          echo "Last commit: `git log -1 --oneline`"
          echo "Merging 'master' into merge-keiyoushi"
          git merge --no-edit master
          echo "Last commit: `git log -1 --oneline`"

      - name: Merging keiyoushi-remote to merge-keiyoushi
        run: |
          cd master
          echo "Merging 'keiyoushi-remote/main' into merge-keiyoushi"
          git merge -S --no-edit keiyoushi-remote/main
          echo "Last commit: `git log -1 --oneline`"

      - name: Pushing to repo
        run: |
          cd master
          git checkout master
          echo "Last commit: `git log -1 --oneline`"
          echo "Pushing 'master' to repo"
          git push
          git checkout keiyoushi
          echo "Last commit: `git log -1 --oneline`"
          echo "Pushing 'keiyoushi' to repo"
          git push
          git checkout merge-keiyoushi
          echo "Last commit: `git log -1 --oneline`"
          echo "Pushing 'merge-keiyoushi' to repo"
          git push


Batch close stale issues

name: "Batch close stale issues"

on:
  # Monthly
  schedule:
    - cron: '0 0 1 * *'
  # Manual trigger
  workflow_dispatch:
    inputs:

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          # Close everything older than a year
          days-before-issue-stale: 365
          days-before-issue-close: 0
          exempt-issue-labels: "do-not-autoclose,Meta request"
          close-issue-message: "In an effort to have a more manageable issue backlog, we're closing older requests that weren't addressed since there's a low chance of it being addressed if it hasn't already. If your request is still relevant, please [open a new request](https://github.com/komikku-app/komikku-extensions/issues/new/choose)."
          close-issue-reason: not_planned
          ascending: true
          operations-per-run: 250


PR build check

name: PR build check

on:
  pull_request:
    paths:
      - '**'
      - '!**.md'
      - '!.github/**'
      - '.github/workflows/build_pull_request.yml'

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
  cancel-in-progress: true

env:
  CI_CHUNK_SIZE: 65

jobs:
  prepare:
    name: Prepare job
    runs-on: ubuntu-latest
    outputs:
      individualMatrix: ${{ steps.generate-matrices.outputs.individualMatrix }}
    steps:
      - name: Clone repo
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Get number of modules
        run: |
          set -x
          projects=(src/*/*)

          echo "NUM_INDIVIDUAL_MODULES=${#projects[@]}" >> $GITHUB_ENV

      - id: generate-matrices
        name: Create output matrices
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          script: |
            const numIndividualModules = process.env.NUM_INDIVIDUAL_MODULES;
            const chunkSize = process.env.CI_CHUNK_SIZE;

            const numIndividualChunks = Math.ceil(numIndividualModules / chunkSize);

            console.log(`Individual modules: ${numIndividualModules} (${numIndividualChunks} chunks of ${chunkSize})`);

            core.setOutput('individualMatrix', { 'chunk': [...Array(numIndividualChunks).keys()] });

  build_individual:
    name: Build individual modules
    needs: prepare
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.prepare.outputs.individualMatrix) }}
    steps:
      - name: Checkout PR
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
        with:
          java-version: 17
          distribution: temurin

      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2
        with:
          cache-read-only: true

      - name: Build extensions (chunk ${{ matrix.chunk }})
        env:
          CI_CHUNK_NUM: ${{ matrix.chunk }}
        run: ./gradlew -p src assembleDebug


CI

name: CI

on:
  push:
    branches:
      - main
    paths:
      - '**'
      - '!**.md'
      - '!.github/**'
      - '.github/scripts/**'
      - '.github/workflows/build_push.yml'

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

env:
  CI_CHUNK_SIZE: 65

jobs:
  prepare:
    name: Prepare job
    runs-on: ubuntu-latest
    outputs:
      individualMatrix: ${{ steps.generate-matrices.outputs.individualMatrix }}
    steps:
      - name: Clone repo
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2

      - name: Get number of modules
        run: |
          set -x
          projects=(src/*/*)

          echo "NUM_INDIVIDUAL_MODULES=${#projects[@]}" >> $GITHUB_ENV

      - id: generate-matrices
        name: Create output matrices
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          script: |
            const numIndividualModules = process.env.NUM_INDIVIDUAL_MODULES;
            const chunkSize = process.env.CI_CHUNK_SIZE;

            const numIndividualChunks = Math.ceil(numIndividualModules / chunkSize);

            console.log(`Individual modules: ${numIndividualModules} (${numIndividualChunks} chunks of ${chunkSize})`);

            core.setOutput('individualMatrix', { 'chunk': [...Array(numIndividualChunks).keys()] });

  build_individual:
    name: Build individual modules
    needs: prepare
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.prepare.outputs.individualMatrix) }}
    steps:
      - name: Checkout main branch
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
        with:
          java-version: 17
          distribution: temurin

      - name: Prepare signing key
        run: |
          echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks

      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2
        with:
          cache-read-only: ${{ matrix.chunk != 0 }}

      - name: Build extensions (chunk ${{ matrix.chunk }})
        env:
          CI_CHUNK_NUM: ${{ matrix.chunk }}
          ALIAS: ${{ secrets.ALIAS }}
          KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }}
          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
        run: ./gradlew -p src assembleRelease

      - name: Upload APKs (chunk ${{ matrix.chunk }})
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        if: "github.repository == 'keiyoushi/extensions-source'"
        with:
          name: "individual-apks-${{ matrix.chunk }}"
          path: "**/*.apk"
          retention-days: 1

      - name: Clean up CI files
        run: rm signingkey.jks

  publish_repo:
    name: Publish repo
    needs:
      - build_individual
    if: "github.repository == 'keiyoushi/extensions-source'"
    runs-on: ubuntu-latest
    steps:
      - name: Download APK artifacts
        uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
        with:
          path: ~/apk-artifacts

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
        with:
          java-version: 17
          distribution: temurin

      - name: Checkout main branch
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          ref: main
          path: main

      - name: Create repo artifacts
        run: |
          cd main
          python ./.github/scripts/move-apks.py
          INSPECTOR_LINK="$(curl -s "https://api.github.com/repos/keiyoushi/extensions-inspector/releases/latest" | jq -r '.assets[0].browser_download_url')"
          curl -L "$INSPECTOR_LINK" -o ./Inspector.jar
          java -jar ./Inspector.jar "repo/apk" "output.json" "tmp"
          python ./.github/scripts/create-repo.py

      - name: Checkout repo branch
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          repository: keiyoushi/extensions
          token: ${{ secrets.BOT_PAT }}
          ref: repo
          path: repo

      - name: Deploy repo
        run: |
          cd repo
          ../main/.github/scripts/commit-repo.sh


CI-komikku

name: CI-komikku

on:
  push:
    branches:
      # - master
      - merge-keiyoushi
    paths:
      - '**'
      - '!**.md'
      - '!.github/**'
      - '.github/scripts/**'
      - '.github/workflows/build_push_komikku.yml'
  # Manual trigger
  workflow_dispatch:

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

env:
  CI_CHUNK_SIZE: 65

jobs:
  prepare:
    name: Prepare job
    runs-on: ubuntu-latest
    outputs:
      individualMatrix: ${{ steps.generate-matrices.outputs.individualMatrix }}
    steps:
      - name: Clone repo
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

      - name: Validate Gradle Wrapper
        uses: gradle/wrapper-validation-action@9ba54b687bf0f59b62abbf91e0d60de082e8ebc4 # v3

      - name: Get number of modules
        run: |
          set -x
          projects=(src/*/*)

          echo "NUM_INDIVIDUAL_MODULES=${#projects[@]}" >> $GITHUB_ENV

      - id: generate-matrices
        name: Create output matrices
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
        with:
          script: |
            const numIndividualModules = process.env.NUM_INDIVIDUAL_MODULES;
            const chunkSize = process.env.CI_CHUNK_SIZE;

            const numIndividualChunks = Math.ceil(numIndividualModules / chunkSize);

            console.log(`Individual modules: ${numIndividualModules} (${numIndividualChunks} chunks of ${chunkSize})`);

            core.setOutput('individualMatrix', { 'chunk': [...Array(numIndividualChunks).keys()] });

  build_individual:
    name: Build individual modules
    needs: prepare
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.prepare.outputs.individualMatrix) }}
    steps:
      - name: Checkout ${{ github.ref_name }} branch
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4
        with:
          java-version: 17
          distribution: temurin

      - name: Prepare signing key
        run: |
          echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks

      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3
        with:
          cache-read-only: ${{ matrix.chunk != 0 }}

      - name: Build extensions (chunk ${{ matrix.chunk }})
        env:
          CI_CHUNK_NUM: ${{ matrix.chunk }}
          ALIAS: ${{ secrets.ALIAS }}
          KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }}
          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
        run: ./gradlew -p src assembleRelease

      - name: Upload APKs (chunk ${{ matrix.chunk }})
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
        if: github.repository == 'komikku-app/komikku-extensions'
        with:
          name: "individual-apks-${{ matrix.chunk }}"
          path: "**/*.apk"
          retention-days: 1

      - name: Clean up CI files
        run: rm signingkey.jks

  publish_repo:
    name: Publish repo
    needs:
      - build_individual
    if: github.repository == 'komikku-app/komikku-extensions'
    runs-on: ubuntu-latest
    steps:
      - name: Download APK artifacts
        uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4
        with:
          path: ~/apk-artifacts

      - name: Set up JDK
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4
        with:
          java-version: 17
          distribution: temurin

      - name: Checkout ${{ github.ref_name }} branch
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
        with:
          ref: ${{ github.ref_name }}
          path: ${{ github.ref_name }}

      - name: Create repo artifacts
        run: |
          cd ${{ github.ref_name }}
          python ./.github/scripts/move-apks.py
          INSPECTOR_LINK="$(curl -s "https://api.github.com/repos/keiyoushi/extensions-inspector/releases/latest" | jq -r '.assets[0].browser_download_url')"
          curl -L "$INSPECTOR_LINK" -o ./Inspector.jar
          java -jar ./Inspector.jar "repo/apk" "output.json" "tmp"
          python ./.github/scripts/create-repo.py

      - name: Checkout repo branch
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
        with:
          repository: komikku-app/extensions
          token: ${{ secrets.BOT_PAT }}
          ref: repo
          path: repo

      - name: Import GPG key
        id: import-gpg
        uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6
        with:
          workdir: repo
          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
          passphrase: ${{ secrets.PASSPHRASE }}
          git_user_signingkey: true
          git_commit_gpgsign: true

      - name: GPG user IDs
        run: |
          echo "fingerprint: ${{ steps.import-gpg.outputs.fingerprint }}"
          echo "keyid:       ${{ steps.import-gpg.outputs.keyid }}"
          echo "name:        ${{ steps.import-gpg.outputs.name }}"
          echo "email:       ${{ steps.import-gpg.outputs.email }}"

      - name: Deploy repo
        run: |
          cd repo
          ../${{ github.ref_name }}/.github/scripts/commit-repo.sh ../${{ github.ref_name }}/repo/


Mirror Sync

# Sync repo to the Codeberg mirror
name: Mirror Sync

on:
  push:
    branches: [ "master" ]
  workflow_dispatch: # Manual dispatch
  # schedule:
  #   - cron: "0 */8 * * *"

jobs:
  codeberg:
    if: github.repository == 'komikku-app/komikku-extensions'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0
      - uses: pixta-dev/repository-mirroring-action@674e65a7d483ca28dafaacba0d07351bdcc8bd75 # v1.1.1
        with:
          target_repo_url: "git@codeberg.org:cuong-tran/komikku-extensions.git"
          ssh_private_key: ${{ secrets.CODEBERG_SSH }}


Issue moderator

name: Issue moderator

on:
  issues:
    types: [ opened, edited, reopened ]
  issue_comment:
    types: [ created ]

jobs:
  autoclose:
    runs-on: ubuntu-latest
    steps:
      - name: Moderate issues
        uses: keiyoushi/issue-moderator-action@a017be83547db6e107431ce7575f53c1dfa3296a
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          duplicate-label: Duplicate

          blurbs: |
            [
              {
                "keywords": ["cf", "cloudflare"],
                "message": "Refer to the **Solving Cloudflare issues** section at https://keiyoushi.github.io/docs/guides/troubleshooting#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
              },
              {
                "keywords": ["uninstall"],
                "message": "Uninstall the extension before updating. If that does not work, uninstall it from your device's settings by navigating to your device's Settings -> Apps and uninstall it from there by looking for `Tachiyomi: <extension name>`.\n\nThis is usually caused your previous extension having a different signature from the updated extension, making Android refuse to update it."
              }
            ]

          duplicate-check-enabled: true
          duplicate-check-labels: |
            ["Source request", "Domain changed"]

          existing-check-enabled: true
          existing-check-labels: |
            ["Source request", "Domain changed"]
          existing-check-repo-url: https://raw.githubusercontent.com/komikku-app/komikku-extensions/repo/index.min.json

          auto-close-rules: |
            [
              {
                "type": "both",
                "regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(?<!n[o']?t )blocked by|error) (?:to )?(?:get past|by ?pass|penetrate)?.*cl[oa]ud ?fl?[ai]re.*",
                "ignoreCase": true,
                "labels": ["Cloudflare protected"],
                "message": "Refer to the **Solving Cloudflare issues** section at https://keiyoushi.github.io/docs/guides/troubleshooting#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
              },
              {
                "type": "both",
                "regex": "remanga\\.org",
                "ignoreCase": true,
                "labels": ["invalid"],
                "message": "ReManga (Russian) will not be added back as it has been removed [due to legal reasons](https://github.com/github/dmca)."
              },
              {
                "type": "both",
                "regex": "(kumanga\\.com)",
                "ignoreCase": true,
                "labels": ["invalid"],
                "message": "{match} will not be added back as it is too difficult to maintain."
              }
            ]
          auto-close-ignore-label: do-not-autoclose


Lock threads

name: Lock threads

on:
  # Daily
  schedule:
    - cron: '0 0 * * *'
  # Manual trigger
  workflow_dispatch:
    inputs:

jobs:
  lock:
    runs-on: ubuntu-latest
    steps:
      - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
        with:
          github-token: ${{ github.token }}
          issue-inactive-days: '2'
          pr-inactive-days: '2'


Merge keiyoushi

name: Merge keiyoushi

on:
  schedule:
    - cron: "0 */8 * * *"
  workflow_dispatch: # Manual dispatch

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

jobs:
  auto-merge:
    name: Merge keiyoushi
    if: github.repository == 'komikku-app/komikku-extensions'
    runs-on: ubuntu-latest

    steps:
      - name: Clone repo
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
        with:
          ref: merge-keiyoushi
          path: merge-keiyoushi
          fetch-depth: 0
          token: ${{ secrets.BOT_PAT }}

      - name: Import GPG key
        id: import-gpg
        uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6
        with:
          workdir: merge-keiyoushi
          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
          passphrase: ${{ secrets.PASSPHRASE }}
          git_user_signingkey: true
          git_commit_gpgsign: true

      - name: GPG user IDs
        run: |
          echo "fingerprint: ${{ steps.import-gpg.outputs.fingerprint }}"
          echo "keyid:       ${{ steps.import-gpg.outputs.keyid }}"
          echo "name:        ${{ steps.import-gpg.outputs.name }}"
          echo "email:       ${{ steps.import-gpg.outputs.email }}"

      - name: Merging Master
        run: |
          cd merge-keiyoushi
          git merge --no-edit origin/master

      - name: Merging Keiyoushi
        run: |
          cd merge-keiyoushi
          git remote add keiyoushi https://github.com/keiyoushi/extensions-source
          git fetch keiyoushi
          # git config --global user.email "github-actions[bot]@users.noreply.github.com"
          # git config --global user.name "github-actions[bot]"
          git merge -S --no-edit keiyoushi/main

      - name: Push to repo
        run: |
          cd merge-keiyoushi
          git push


Build and Test

name: Build and Test

on:
  merge_group:
    types: [ checks_requested ]
  pull_request:
    branches:
    - main
  push:
    branches:
    - main

env:
  GRADLE_OPTS: -Dorg.gradle.daemon=false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Setup Java
      uses: actions/setup-java@v4
      with:
        distribution: temurin
        java-version: 17

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

    - name: Build all classes
      run: ./gradlew --stacktrace classes

  build-ui:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Setup Java
      uses: actions/setup-java@v4
      with:
        distribution: temurin
        java-version: 17

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

    - name: Generate OpenAPI spec
      run: ./gradlew --stacktrace :core:generateOpenApiSpec

    - name: Install Node
      uses: actions/setup-node@v4
      with:
        node-version: 20

    - uses: pnpm/action-setup@v4
      name: Install pnpm
      with:
        version: 9

    - name: Get pnpm store directory
      shell: bash
      run: |
        echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

    - uses: actions/cache@v4
      name: Setup pnpm cache
      with:
        path: ${{ env.STORE_PATH }}
        key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
        restore-keys: |
          ${{ runner.os }}-pnpm-store-

    - name: Install dependencies
      run: pnpm -C ui install

    - name: Build UI
      run: pnpm -C ui build

  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Setup Java
      uses: actions/setup-java@v4
      with:
        distribution: temurin
        java-version: 17

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

    - name: Run tests
      run: ./gradlew test -Dkotest.tags='!Integration'

    - name: Upload test results
      uses: actions/upload-artifact@v4
      if: success() || failure()
      with:
        name: test-results
        path: "**/build/reports/tests"
        retention-days: 7

  integration-test:
    needs: build
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Setup Java
      uses: actions/setup-java@v4
      with:
        distribution: temurin
        java-version: 17

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

    - name: Run integration tests
      run: ./gradlew test -Dkotest.tags='Integration'

    - name: Upload test results
      uses: actions/upload-artifact@v4
      if: success() || failure()
      with:
        name: integration-test-results
        path: "**/build/reports/tests"
        retention-days: 7


Docker Build

name: Docker Build

on:
  push:
    branches:
      - main
    tags:
      - "[0-9]+.[0-9]+.[0-9]+"

env:
  REGISTRY: ghcr.io

jobs:
  build:
    name: Build Docker Image
    runs-on: ubuntu-22.04
    permissions:
      packages: write
    strategy:
      matrix:
        # Define the Docker images to build. The job first builds an image using Docker which is used for the ui and for
        # worker base images. Then the job builds an image using Jib. Both steps are optional, e.g., not all Jib builds
        # need a base image, and the ui build does not need a Jib build.
        #
        # If the image is built with Docker:
        # preparationTask (optional): Gradle task to run before building the image.
        # image: Name of the image, used for the Docker image name.
        # context: Path to the Docker context directory.
        # dockerfile: Path of the Dockerfile to use, relative to context.
        #
        # If the image is built with Jib:
        # jibImage: Name of the image built by Jib.
        # task: Gradle task to build the image.
        #
        # Optional properties:
        # freeDiskSpace: Whether to free disk space before building the image.
        docker:
        - jibImage: core
          task: :core:jibDockerBuild
        - jibImage: orchestrator
          task: :orchestrator:jibDockerBuild
        - jibImage: kubernetes-jobmonitor
          task: :transport:kubernetes-jobmonitor:jibDockerBuild
        - jibImage: advisor-worker
          task: :workers:advisor:jibDockerBuild
        - image: analyzer-worker-base-image
          context: workers/analyzer/docker
          dockerfile: Analyzer.Dockerfile
          jibImage: analyzer-worker
          task: :workers:analyzer:jibDockerBuild
          freeDiskSpace: true
        - image: config-worker-base-image
          context: workers/config/docker
          dockerfile: Config.Dockerfile
          jibImage: config-worker
          task: :workers:config:jibDockerBuild
        - image: evaluator-worker-base-image
          context: workers/evaluator/docker
          dockerfile: Evaluator.Dockerfile
          jibImage: evaluator-worker
          task: :workers:evaluator:jibDockerBuild
        - image: notifier-worker-base-image
          context: workers/notifier/docker
          dockerfile: Notifier.Dockerfile
          jibImage: notifier-worker
          task: :workers:notifier:jibDockerBuild
        - image: reporter-worker-base-image
          context: workers/reporter/docker
          dockerfile: Reporter.Dockerfile
          jibImage: reporter-worker
          task: :workers:reporter:jibDockerBuild
        - image: scanner-worker-base-image
          context: workers/scanner/docker
          dockerfile: Scanner.Dockerfile
          jibImage: scanner-worker
          task: :workers:scanner:jibDockerBuild
        - preparationTask: :core:generateOpenApiSpec
          image: ui
          context: ui
          dockerfile: docker/UI.Dockerfile

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

    - name: Free Disk Space
      if: ${{ matrix.docker.freeDiskSpace }}
      uses: ./.github/actions/free-disk-space

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3

    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}

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

    - name: Get ORT-Server Version
      run: |
        ORT_SERVER_VERSION=$(./gradlew -q properties --property version | sed -nr 's/version: (.+)/\1/p' | sed 's/+/-/')
        echo "ORT_SERVER_VERSION=${ORT_SERVER_VERSION}" >> $GITHUB_ENV

    - name: Run Preparation Task ${{ matrix.docker.preparationTask }}
      if: ${{ matrix.docker.preparationTask != '' }}
      run: ./gradlew ${{ matrix.docker.preparationTask }}

    - name: Extract Docker Metadata for ${{ matrix.docker.image }} Image
      if: ${{ matrix.docker.dockerfile != '' }}
      id: meta-base
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/ort-server-${{ matrix.docker.image }}
        tags: |
          type=raw,value=${{ env.ORT_SERVER_VERSION }}
          type=ref,event=branch
          type=sha

    - name: Build ${{ matrix.docker.image }} Image
      if: ${{ matrix.docker.dockerfile != '' }}
      uses: docker/build-push-action@v5
      with:
        context: ${{ matrix.docker.context }}
        file: ${{ matrix.docker.context }}/${{ matrix.docker.dockerfile }}
        push: true
        tags: ${{ steps.meta-base.outputs.tags }}
        labels: ${{ steps.meta-base.outputs.labels }}
        cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository_owner }}/ort-server-${{ matrix.docker.image }}:cache
        cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository_owner }}/ort-server-${{ matrix.docker.image }}:cache,mode=max

    - name: Extract Docker Metadata for ${{ matrix.docker.jibImage }} Image
      if: ${{ matrix.docker.task != '' }}
      id: meta
      uses: docker/metadata-action@v5
      with:
        tags: |
          type=raw,value=${{ env.ORT_SERVER_VERSION }}
          type=ref,event=branch
          type=sha

    - name: Build ${{ matrix.docker.jibImage }} Image
      if: ${{ matrix.docker.task != '' }}
      run: |
        ./gradlew \
          -PdockerBaseImagePrefix=${{ env.REGISTRY }}/${{ github.repository_owner }}/ \
          -PdockerBaseImageTag=${{ env.ORT_SERVER_VERSION }} \
          -PdockerImagePrefix=${{ env.REGISTRY }}/${{ github.repository_owner }}/ \
          -PdockerImageTag=${{ env.ORT_SERVER_VERSION }} \
          ${{ matrix.docker.task }} \
          -Djib.console=plain \
          -Djib.container.labels="$(echo "${{ steps.meta.outputs.labels }}" | tr '\n' ',' | sed 's/,$//')" \
          -Djib.to.tags="$(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ',' | sed 's/,$//')"
        docker push ${{ env.REGISTRY }}/${{ github.repository_owner }}/ort-server-${{ matrix.docker.jibImage }} --all-tags


Validate Gradle Wrapper

name: Validate Gradle Wrapper

on:
  merge_group:
    types: [ checks_requested ]
  pull_request:
    branches:
    - main
  push:
    branches:
    - main

jobs:
  wrapper-validation:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Validate Wrapper
      uses: gradle/wrapper-validation-action@v1


Publish Artifacts

name: Publish Artifacts

on:
  push:
    branches:
    - main

env:
  GRADLE_OPTS: -Dorg.gradle.daemon=false

jobs:
  publish:
    runs-on: ubuntu-22.04
    permissions:
      packages: write
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4
      with:
        ref: ${{ env.ORT_VERSION }}
        fetch-depth: 0

    - name: Setup Java
      uses: actions/setup-java@v4
      with:
        distribution: temurin
        java-version: 17
        cache: gradle

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

    - name: Publish Artifacts to GitHub Packages
      env:
        ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ env.GITHUB_ACTOR }}
        ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }}
      run: ./gradlew publishAllPublicationsToGithubPackagesRepository


Renovate

name: Renovate

on:
  schedule:
  - cron: '0/15 * * * *'
  workflow_dispatch:

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Renovate
      uses: renovatebot/github-action@v40.1.8
      with:
        configurationFile: .github/renovate-global.json
        token: ${{ secrets.RENOVATE_TOKEN }}


Static Analysis

name: Static Analysis

on:
  merge_group:
    types: [ checks_requested ]
  pull_request:
    branches:
    - main
  push:
    branches:
    - main

jobs:
  commit-lint:
    runs-on: ubuntu-22.04
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - uses: wagoid/commitlint-github-action@v5
      with:
        configFile: .commitlintrc.yml

  detekt-issues:
    runs-on: ubuntu-latest
    env:
      GRADLE_OPTS: -Dorg.gradle.daemon=false
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Setup Java
      uses: actions/setup-java@v4
      with:
        distribution: temurin
        java-version: 17

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

    - name: Check for Detekt Issues
      run: ./gradlew --stacktrace detekt

    - name: Check for Detekt Issues with Type Resolution
      run: ./gradlew --stacktrace detektMain detektTest

  eslint:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Install Node
      uses: actions/setup-node@v4
      with:
        node-version: 20

    - uses: pnpm/action-setup@v4
      name: Install pnpm
      with:
        version: 9

    - name: Get pnpm store directory
      shell: bash
      run: |
        echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

    - uses: actions/cache@v4
      name: Setup pnpm cache
      with:
        path: ${{ env.STORE_PATH }}
        key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
        restore-keys: |
          ${{ runner.os }}-pnpm-store-

    - name: Install dependencies
      run: pnpm -C ui install --dev

    - name: Run ESLint
      run: pnpm -C ui lint

  prettier:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Install Node
      uses: actions/setup-node@v4
      with:
        node-version: 20

    - uses: pnpm/action-setup@v4
      name: Install pnpm
      with:
        version: 9

    - name: Get pnpm store directory
      shell: bash
      run: |
        echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

    - uses: actions/cache@v4
      name: Setup pnpm cache
      with:
        path: ${{ env.STORE_PATH }}
        key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
        restore-keys: |
          ${{ runner.os }}-pnpm-store-

    - name: Install dependencies
      run: pnpm -C ui install --dev

    - name: Run Prettier
      run: pnpm -C ui format:check

  renovate-validation:
    runs-on: ubuntu-22.04
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Validate Repository Renovate Config
      run: npx -y --package renovate -- renovate-config-validator .github/renovate.json

    - name: Validate Global Renovate Config
      run: npx -y --package renovate -- renovate-config-validator .github/renovate-global.json

  reuse-tool:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Check REUSE Compliance
      run: |
        pip install --user reuse
        ~/.local/bin/reuse lint