# Checks for each PR / push

name: QC

on:
  push:
    branches-ignore: ["l10*", "dependabot/**", "backport/**"]
  pull_request:
    branches-ignore: ["l10*"]

env:
  python_version: 3.12
  node_version: 24
  # The OS version must be set per job
  server_start_sleep: 60

  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  INVENTREE_DB_ENGINE: sqlite3
  INVENTREE_DB_NAME: inventree
  INVENTREE_MEDIA_ROOT: /home/runner/work/InvenTree/test_inventree_media
  INVENTREE_STATIC_ROOT: /home/runner/work/InvenTree/test_inventree_static
  INVENTREE_BACKUP_DIR: /home/runner/work/InvenTree/test_inventree_backup
  INVENTREE_SITE_URL: http://localhost:8000
  INVENTREE_DEBUG: true

permissions:
  contents: read

jobs:
  paths-filter:
    name: Filter
    runs-on: ubuntu-latest

    outputs:
      server: ${{ steps.filter.outputs.server }}
      migrations: ${{ steps.filter.outputs.migrations }}
      frontend: ${{ steps.filter.outputs.frontend }}
      api: ${{ steps.filter.outputs.api }}
      force: ${{ steps.force.outputs.force }}
      cicd: ${{ steps.filter.outputs.cicd }}
      requirements: ${{ steps.filter.outputs.requirements }}
      runner-perf: ${{ steps.runner-perf.outputs.runner }}
      performance: ${{ steps.performance.outputs.force-performance }}
      submit-performance: ${{ steps.runner-perf.outputs.submit-performance }}

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
        id: filter
        with:
          filters: |
            server:
              - 'src/backend/InvenTree/**'
              - 'src/backend/requirements.txt'
              - 'src/backend/requirements-dev.txt'
            migrations:
              - '**/test_migrations.py'
              - '**/migrations/**'
              - '.github/workflows**'
              - 'src/backend/requirements.txt'
            api:
              - 'src/backend/InvenTree/InvenTree/api_version.py'
            frontend:
              - 'src/frontend/**'
            cicd:
              - '.github/workflows/**'
            requirements:
              - 'src/backend/requirements.txt'
              - 'src/backend/requirements-dev.txt'
              - 'docs/requirements.txt'
              - 'contrib/dev_reqs/requirements.txt'
      - name: Is CI being forced?
        run: echo "force=true" >> $GITHUB_OUTPUT
        id: force
        if: |
          contains(github.event.pull_request.labels.*.name, 'dependency') ||
          contains(github.event.pull_request.labels.*.name, 'full-run')
      - name: Is performance testing being forced?
        run: echo "force-performance=true" >> $GITHUB_OUTPUT
        id: performance
        if: |
          contains(github.event.pull_request.labels.*.name, 'performance-run')
      - name: Which runner to use?
        env:
          GITHUB_REF: ${{ github.ref }}
          PERFORMANCE: ${{ steps.performance.outputs.force-performance }}
        id: runner-perf
        # decide if we are running in inventree/inventree -> use codspeed-macro runner else ubuntu-24.04
        run: |
          is_main_push=false
          if [[ '${{ github.event_name }}' == 'push' && "$GITHUB_REF" == 'refs/heads/master' ]]; then
            is_main_push=true
          fi
          if [[ '${{ github.repository }}' == 'inventree/InvenTree' && ( "$is_main_push" == 'true' || "$PERFORMANCE" == 'true' ) ]]; then
            echo "runner=codspeed-macro" >> "$GITHUB_OUTPUT"
            echo "submit-performance=true" >> "$GITHUB_OUTPUT"
          else
            echo "runner=ubuntu-24.04" >> "$GITHUB_OUTPUT"
            echo "submit-performance=false" >> "$GITHUB_OUTPUT"
          fi

  code-style:
    name: Style [prek]
    runs-on: ubuntu-24.04
    needs: paths-filter
    if: needs.paths-filter.outputs.cicd == 'true' || needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.requirements == 'true' || needs.paths-filter.outputs.force == 'true'

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Set up Python ${{ env.python_version }}
        uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
        with:
          python-version: ${{ env.python_version }}
          cache: "pip"
      - name: Run pre commit hook Checks
        uses: j178/prek-action@e98a699c41eb69ab013a45817a0406469a748f8d # v2.0.5
      - name: Check Version
        run: |
          pip install --require-hashes -r contrib/dev_reqs/requirements.txt
          python3 .github/scripts/version_check.py

  typecheck:
    name: Style [Typecheck]
    runs-on: ubuntu-24.04
    needs: [code-style, paths-filter]
    if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.requirements == 'true' || needs.paths-filter.outputs.force == 'true'

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        id: setup
        uses: ./.github/actions/setup
        with:
          apt-dependency: gettext poppler-utils
          dev-install: true
          update: true
      - name: Check types
        run: |
          ty check --python ${Python_ROOT_DIR}/bin/python3 src/backend

  mkdocs:
    name: Style [Documentation]
    runs-on: ubuntu-24.04

    needs: paths-filter

    steps:
      - name: Checkout Code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Set up Python ${{ env.python_version }}
        uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
        with:
          python-version: ${{ env.python_version }}
      - name: Check Config
        run: |
          pip install --require-hashes -r contrib/dev_reqs/requirements.txt
          pip install --require-hashes -r docs/requirements.txt
          python docs/ci/check_mkdocs_config.py
      - name: Check Links
        uses: tcort/github-action-markdown-link-check@e7c7a18363c842693fadde5d41a3bd3573a7a225 # v1
        with:
          folder-path: docs
          config-file: docs/mlc_config.json
          check-modified-files-only: "yes"
          use-quiet-mode: "yes"

  schema:
    name: Tests - API Schema Documentation
    runs-on: ubuntu-24.04
    needs: paths-filter
    if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
    env:
      INVENTREE_DB_ENGINE: django.db.backends.sqlite3
      INVENTREE_DB_NAME: ../inventree_unit_test_db.sqlite3
      INVENTREE_ADMIN_USER: testuser
      INVENTREE_ADMIN_PASSWORD: testpassword
      INVENTREE_ADMIN_EMAIL: test@test.com
      INVENTREE_PYTHON_TEST_SERVER: http://localhost:12345
      INVENTREE_PYTHON_TEST_USERNAME: testuser
      INVENTREE_PYTHON_TEST_PASSWORD: testpassword
    outputs:
      version: ${{ steps.version.outputs.version }}

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          apt-dependency: gettext poppler-utils
          dev-install: true
          update: true
      - name: Export API Documentation
        run: invoke dev.schema --ignore-warnings --filename src/backend/InvenTree/schema.yml
      - name: Upload schema
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: schema.yml
          path: src/backend/InvenTree/schema.yml
      - name: Download public schema
        env:
          API: ${{ needs.paths-filter.outputs.api }}
        run: |
          pip install --require-hashes -r contrib/dev_reqs/requirements.txt >/dev/null 2>&1
          version="$(python3 .github/scripts/version_check.py --show-api-version --decrement-api=${API} 2>&1)"
          echo "API Version: $version"
          url="https://raw.githubusercontent.com/inventree/schema/main/export/${version}/api.yaml"
          echo "URL: $url"
          code=$(curl -s -o api.yaml $url --write-out '%{http_code}' --silent)
          if [ "$code" != "200" ]; then
            exit 1
          fi
          echo "Downloaded api.yaml"
      - name: Running OpenAPI Spec diff action
        id: breaking_changes
        uses: oasdiff/oasdiff-action/diff@3e9d440d37f468355457604348009f50e0cddbf3 # v0.1.4
        with:
          base: "api.yaml"
          revision: "src/backend/InvenTree/schema.yml"
          format: "html"
      - name: Echoing diff to step
        continue-on-error: true
        env:
          DIFF: ${{ steps.breaking_changes.outputs.diff }}
        run: echo "${DIFF}" >> $GITHUB_STEP_SUMMARY

      - name: Check for differences in API Schema
        if: needs.paths-filter.outputs.api == 'false'
        run: |
          diff --color -u src/backend/InvenTree/schema.yml api.yaml
          diff -u src/backend/InvenTree/schema.yml api.yaml && echo "no difference in API schema " || exit 2
      - name: Check schema - including warnings
        run: invoke dev.schema
      - name: Extract version for publishing
        id: version
        if: github.ref == 'refs/heads/master' && needs.paths-filter.outputs.api == 'true'
        run: |
          pip install --require-hashes -r contrib/dev_reqs/requirements.txt >/dev/null 2>&1
          version="$(python3 .github/scripts/version_check.py --show-api-version 2>&1)"
          echo "API Version: $version"
          echo "version=$version" >> "$GITHUB_OUTPUT"
      - name: Extract settings / tags
        run: invoke int.export-definitions --basedir docs
      - name: Upload settings
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: inventree_settings.json
          path: docs/generated/inventree_settings.json
      - name: Upload tags
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: inventree_tags.yml
          path: docs/generated/inventree_tags.yml
      - name: Upload filters
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: inventree_filters.yml
          path: docs/generated/inventree_filters.yml

  schema-push:
    name: Push new schema
    runs-on: ubuntu-24.04
    needs: [paths-filter, schema]
    if: needs.schema.result == 'success' && github.ref == 'refs/heads/master' && needs.paths-filter.outputs.api == 'true' && github.repository_owner == 'inventree'
    env:
      version: ${{ needs.schema.outputs.version }}

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        name: Checkout Code
        with:
          repository: inventree/schema
          token: ${{ secrets.SCHEMA_PAT }}
          persist-credentials: true
      - name: Create artifact directory
        run: mkdir -p artifact
      - name: Download schema artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: artifact
          merge-multiple: true
      - name: Move files to correct location
        run: |
          echo "Version: ${version}"
          echo "before move"
          ls -la artifact
          mkdir export/${version}
          mv artifact/schema.yml export/${version}/api.yaml
          mv artifact/inventree_settings.json export/${version}/inventree_settings.json
          mv artifact/inventree_tags.yml export/${version}/inventree_tags.yml
          mv artifact/inventree_filters.yml export/${version}/inventree_filters.yml
          echo "after move"
          ls -la artifact
          rm -rf artifact
      - uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0
        name: Commit schema changes
        with:
          commit_message: "Update API schema for ${{ env.version }} / ${{ github.sha }}"

  python:
    name: Tests - inventree-python
    runs-on: ${{ needs.paths-filter.outputs.runner-perf }}

    needs: ["code-style", "paths-filter"]
    if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true' || needs.paths-filter.outputs.performance == 'true'
    permissions:
      contents: read
      id-token: write

    env:
      WRAPPER_NAME: inventree-python
      INVENTREE_DB_ENGINE: django.db.backends.sqlite3
      INVENTREE_DB_NAME: ../inventree_unit_test_db.sqlite3
      INVENTREE_ADMIN_USER: testuser
      INVENTREE_ADMIN_PASSWORD: testpassword
      INVENTREE_ADMIN_EMAIL: test@test.com
      INVENTREE_PYTHON_TEST_SERVER: http://127.0.0.1:12345
      INVENTREE_PYTHON_TEST_USERNAME: testuser
      INVENTREE_PYTHON_TEST_PASSWORD: testpassword
      INVENTREE_SITE_URL: http://127.0.0.1:12345
      INVENTREE_DEBUG: true
      INVENTREE_LOG_LEVEL: WARNING
      node_version: '>=24'

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          apt-dependency: gettext poppler-utils
          dev-install: true
          update: true
          static: true
          npm: true
      - name: Download Python Code For `${WRAPPER_NAME}`
        run: git clone --depth 1 https://github.com/inventree/${WRAPPER_NAME} ./${WRAPPER_NAME}
      - name: Start InvenTree Server
        run: |
          invoke dev.delete-data -f
          invoke dev.import-fixtures
          invoke dev.server -a 127.0.0.1:12345 &
          invoke wait
      - name: Run Tests For `${WRAPPER_NAME}`
        run: |
          cd ${WRAPPER_NAME}
          invoke check-server
          coverage run -m unittest discover -s test/
      - name: Prepare environment for performance tests
        run: |
          pip uninstall pytest-django -y
          cd ${WRAPPER_NAME}
          pip install .
        if: needs.paths-filter.outputs.submit-performance == 'true'
      - name: Performance Reporting
        uses: CodSpeedHQ/action@a4a36bb07c0638b0b4ca52bf1f3dad1b4289e52f # v4.18.1
        # check if we are in inventree/inventree - reporting only works in that OIDC context
        if: github.repository == 'inventree/InvenTree' && needs.paths-filter.outputs.submit-performance == 'true'
        with:
          mode: walltime
          run: pytest ./src/performance --codspeed

  coverage:
    name: Tests - DB [SQLite] + Coverage ${{ matrix.python_version }}
    runs-on: ubuntu-24.04

    needs: ["code-style", "paths-filter"]
    if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
    continue-on-error: true # continue if a step fails so that coverage gets pushed
    strategy:
      matrix:
        python_version: [3.12, 3.14]

    env:
      INVENTREE_DB_NAME: ./inventree.sqlite
      INVENTREE_DB_ENGINE: sqlite3
      INVENTREE_PLUGINS_ENABLED: true
      INVENTREE_CONSOLE_LOG: false
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      python_version: ${{ matrix.python_version }}

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          apt-dependency: gettext poppler-utils
          dev-install: true
          update: true
          static: true
      - name: Data Export Test
        uses: ./.github/actions/migration
      - name: Test Translations
        run: invoke dev.translate
      - name: Check Migration Files
        run: |
          invoke migrate --detect
          python3 .github/scripts/check_migration_files.py
      - name: Coverage Tests
        run: invoke dev.test --check --coverage --translations
      - name: Upload raw coverage to artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: coverage
          path: .coverage
          retention-days: 14
      - name: Upload coverage reports to Codecov
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
        if: always()
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: inventree/InvenTree
          flags: backend

  performance:
    name: Tests - Performance
    runs-on: ${{ needs.paths-filter.outputs.runner-perf }}

    needs: ["code-style", "paths-filter"]
    # check if we are in inventree/inventree - reporting only works in that OIDC context
    if: (needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true') && github.repository == 'inventree/InvenTree' && needs.paths-filter.outputs.submit-performance == 'true'
    permissions:
      contents: read
      id-token: write

    env:
      INVENTREE_DB_NAME: inventree_unit_test_db.sqlite
      INVENTREE_DB_ENGINE: sqlite3
      INVENTREE_PLUGINS_ENABLED: true
      INVENTREE_CONSOLE_LOG: false
      INVENTREE_AUTO_UPDATE: true

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          apt-dependency: gettext poppler-utils
          dev-install: true
          update: true
          npm: true
        env:
          node_version: '>=24'
      - name: Performance Reporting
        uses: CodSpeedHQ/action@a4a36bb07c0638b0b4ca52bf1f3dad1b4289e52f # v4.18.1
        with:
          mode: walltime
          run: inv dev.test --pytest

  postgres:
    name: Tests - DB [PostgreSQL]
    runs-on: ubuntu-24.04
    needs: ["code-style", "paths-filter"]
    if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'

    env:
      INVENTREE_DB_ENGINE: django.db.backends.postgresql
      INVENTREE_DB_USER: inventree
      INVENTREE_DB_PASSWORD: password
      INVENTREE_DB_HOST: "127.0.0.1"
      INVENTREE_DB_PORT: 5432
      INVENTREE_DEBUG: true
      INVENTREE_LOG_LEVEL: INFO
      INVENTREE_CONSOLE_LOG: false
      INVENTREE_CACHE_HOST: localhost
      INVENTREE_PLUGINS_ENABLED: true

    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_USER: inventree
          POSTGRES_PASSWORD: password
        ports:
          - 5432:5432

      redis:
        image: redis:8
        ports:
          - 6379:6379

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          apt-dependency: gettext poppler-utils libpq-dev
          pip-dependency: psycopg django-redis>=5.0.0
          dev-install: true
          update: true
          static: true
      - name: Run Tests
        run: invoke dev.test --check --translations
      - name: Data Export Test
        uses: ./.github/actions/migration

  mysql:
    name: Tests - DB [MySQL]
    runs-on: ubuntu-24.04

    needs: ["code-style", "paths-filter"]
    if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'

    env:
      # Database backend configuration
      INVENTREE_DB_ENGINE: django.db.backends.mysql
      INVENTREE_DB_USER: root
      INVENTREE_DB_PASSWORD: password
      INVENTREE_DB_HOST: "127.0.0.1"
      INVENTREE_DB_PORT: 3306
      INVENTREE_DEBUG: true
      INVENTREE_LOG_LEVEL: WARNING
      INVENTREE_CONSOLE_LOG: false
      INVENTREE_PLUGINS_ENABLED: true

    services:
      mysql:
        image: mysql:9
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
          MYSQL_DATABASE: ${{ env.INVENTREE_DB_NAME }}
          MYSQL_USER: inventree
          MYSQL_PASSWORD: password
          MYSQL_ROOT_PASSWORD: password
        options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
        ports:
          - 3306:3306

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          apt-dependency: gettext poppler-utils libmysqlclient-dev
          pip-dependency: mysqlclient
          dev-install: true
          update: true
          static: true
      - name: Run Tests
        run: invoke dev.test --check --translations
      - name: Data Export Test
        uses: ./.github/actions/migration

  migration-tests:
    name: Tests - Migrations [PostgreSQL]
    runs-on: ubuntu-latest
    needs: paths-filter
    if: ${{ (needs.paths-filter.outputs.force == 'true') || (github.ref == 'refs/heads/master' && needs.paths-filter.outputs.migrations == 'true') }}

    env:
      INVENTREE_DB_ENGINE: django.db.backends.postgresql
      INVENTREE_DB_NAME: inventree
      INVENTREE_DB_USER: inventree
      INVENTREE_DB_PASSWORD: password
      INVENTREE_DB_HOST: "127.0.0.1"
      INVENTREE_DB_PORT: 5432
      INVENTREE_DEBUG: False
      INVENTREE_LOG_LEVEL: WARNING
      INVENTREE_PLUGINS_ENABLED: false

    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_USER: inventree
          POSTGRES_PASSWORD: password
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          apt-dependency: gettext poppler-utils libpq-dev
          pip-dependency: psycopg
          dev-install: true
          update: true
      - name: Run Tests
        run: invoke dev.test --check --migrations --report --coverage --translations
      - name: Upload coverage reports to Codecov
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
        if: always()
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: inventree/InvenTree
          flags: migrations

  migrations-checks:
    name: Tests - Full Migration [SQLite]
    runs-on: ubuntu-latest
    needs: paths-filter
    if: ${{ (needs.paths-filter.outputs.force == 'true') || (github.ref == 'refs/heads/master' && needs.paths-filter.outputs.migrations == 'true') }}

    env:
      INVENTREE_DB_ENGINE: sqlite3
      INVENTREE_DB_NAME: /home/runner/work/InvenTree/db.sqlite3
      INVENTREE_DEBUG: true
      INVENTREE_LOG_LEVEL: WARNING
      INVENTREE_PLUGINS_ENABLED: false

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
        name: Checkout Code
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          install: true
      - name: Fetch Database
        run: git clone --depth 1 https://github.com/inventree/test-db ./test-db

      - name: 0.10.0 Database
        run: |
          rm /home/runner/work/InvenTree/db.sqlite3
          cp test-db/stable_0.10.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3
          chmod +rw /home/runner/work/InvenTree/db.sqlite3
          invoke migrate

      - name: 0.11.0 Database
        run: |
          rm /home/runner/work/InvenTree/db.sqlite3
          cp test-db/stable_0.11.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3
          chmod +rw /home/runner/work/InvenTree/db.sqlite3
          invoke migrate

      - name: 0.13.5 Database
        run: |
          rm /home/runner/work/InvenTree/db.sqlite3
          cp test-db/stable_0.13.5.sqlite3 /home/runner/work/InvenTree/db.sqlite3
          chmod +rw /home/runner/work/InvenTree/db.sqlite3
          invoke migrate

      - name: 0.16.0 Database
        run: |
          rm /home/runner/work/InvenTree/db.sqlite3
          cp test-db/stable_0.16.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3
          chmod +rw /home/runner/work/InvenTree/db.sqlite3
          invoke migrate

      - name: 0.17.0 Database
        run: |
          rm /home/runner/work/InvenTree/db.sqlite3
          cp test-db/stable_0.17.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3
          chmod +rw /home/runner/work/InvenTree/db.sqlite3
          invoke migrate

  zizmor:
    name: Security [Zizmor]
    runs-on: ubuntu-24.04
    needs: ["code-style", "paths-filter"]
    if: needs.paths-filter.outputs.cicd == 'true' || needs.paths-filter.outputs.force == 'true'

    permissions:
      security-events: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Run zizmor 🌈
        uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7
