name: Release
on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'
      - 'v[0-9]+.[0-9]+.[0-9]+-beta.rc[0-9]+'
concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false
env:
  DEFAULT_UV_VERSION: "0.10.x"
  DEFAULT_PYTHON_VERSION: "3.12"
jobs:
  wait-for-docker:
    name: Wait for Docker Build
    runs-on: ubuntu-24.04
    steps:
      - name: Wait for Docker build
        uses: lewagon/wait-on-check-action@74049309dfeff245fe8009a0137eacf28136cb3c # v1.5.0
        with:
          ref: ${{ github.sha }}
          check-name: 'Build Docker Image'
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          wait-interval: 60
  build-release:
    name: Build Release
    needs: wait-for-docker
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      # ---- Frontend Build ----
      - name: Install pnpm
        uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
        with:
          version: 10
      - name: Use Node.js 24
        uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
        with:
          node-version: 24.x
          cache: 'pnpm'
          cache-dependency-path: 'src-ui/pnpm-lock.yaml'
      - name: Install frontend dependencies
        run: cd src-ui && pnpm install
      - name: Build frontend
        run: cd src-ui && pnpm run build --configuration production
      # ---- Backend Setup ----
      - name: Set up Python
        id: setup-python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
      - name: Install uv
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
        with:
          version: ${{ env.DEFAULT_UV_VERSION }}
          enable-cache: true
          python-version: ${{ steps.setup-python.outputs.python-version }}
      - name: Install Python dependencies
        run: |
          uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
      - name: Install system dependencies
        run: |
          sudo apt-get update -qq
          sudo apt-get install -qq --no-install-recommends gettext liblept5
      # ---- Build Documentation ----
      - name: Build documentation
        run: |
          uv run \
            --python ${{ steps.setup-python.outputs.python-version }} \
            --dev \
            --frozen \
            zensical build --clean
      # ---- Prepare Release ----
      - name: Generate requirements file
        run: |
          uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt
      - name: Compile messages
        run: |
          cd src/
          uv run \
            --python ${{ steps.setup-python.outputs.python-version }} \
            manage.py compilemessages
      - name: Collect static files
        run: |
          cd src/
          uv run \
            --python ${{ steps.setup-python.outputs.python-version }} \
            manage.py collectstatic --no-input --clear
      - name: Assemble release package
        run: |
          mkdir -p dist/paperless-ngx/scripts

          for file_name in .dockerignore \
                          .env \
                          Dockerfile \
                          pyproject.toml \
                          uv.lock \
                          requirements.txt \
                          LICENSE \
                          README.md \
                          paperless.conf.example
          do
            cp --verbose ${file_name} dist/paperless-ngx/
          done
          mv dist/paperless-ngx/paperless.conf.example dist/paperless-ngx/paperless.conf

          cp --recursive docker/ dist/paperless-ngx/docker
          cp scripts/*.service scripts/*.sh scripts/*.socket dist/paperless-ngx/scripts/
          cp --recursive src/ dist/paperless-ngx/src
          cp --recursive site/ dist/paperless-ngx/docs
          mv static dist/paperless-ngx/

          find dist/paperless-ngx -name "__pycache__" -type d -exec rm -rf {} +
      - name: Create release archive
        run: |
          cd dist
          sudo chown -R 1000:1000 paperless-ngx/
          tar -cJf paperless-ngx.tar.xz paperless-ngx/
      - name: Upload release artifact
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: release
          path: dist/paperless-ngx.tar.xz
          retention-days: 7
  publish-release:
    name: Publish Release
    needs: build-release
    runs-on: ubuntu-24.04
    outputs:
      prerelease: ${{ steps.get-version.outputs.prerelease }}
      changelog: ${{ steps.create-release.outputs.body }}
      version: ${{ steps.get-version.outputs.version }}
    steps:
      - name: Download release artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: release
          path: ./
      - name: Get version info
        id: get-version
        run: |
          echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
          if [[ "${{ github.ref_name }}" == *"-beta.rc"* ]]; then
            echo "prerelease=true" >> $GITHUB_OUTPUT
          else
            echo "prerelease=false" >> $GITHUB_OUTPUT
          fi
      - name: Create release and changelog
        id: create-release
        uses: release-drafter/release-drafter@139054aeaa9adc52ab36ddf67437541f039b88e2 # v7.1.1
        with:
          name: Paperless-ngx ${{ steps.get-version.outputs.version }}
          tag: ${{ steps.get-version.outputs.version }}
          version: ${{ steps.get-version.outputs.version }}
          prerelease: ${{ steps.get-version.outputs.prerelease }}
          publish: true
          commitish: main
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Upload release archive
        uses: shogo82148/actions-upload-release-asset@96bc1f0cb850b65efd58a6b5eaa0a69f88d38077 # v1.10.0
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          upload_url: ${{ steps.create-release.outputs.upload_url }}
          asset_path: ./paperless-ngx.tar.xz
          asset_name: paperless-ngx-${{ steps.get-version.outputs.version }}.tar.xz
          asset_content_type: application/x-xz
  # ---------------------------------------------------------------------------
  # Append changelog to docs (only on non-prerelease)
  # ---------------------------------------------------------------------------
  append-changelog:
    name: Append Changelog
    needs: publish-release
    if: needs.publish-release.outputs.prerelease == 'false'
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          ref: main
          persist-credentials: true # for pushing changelog branch
      - name: Set up Python
        id: setup-python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
      - name: Install uv
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
        with:
          version: ${{ env.DEFAULT_UV_VERSION }}
          enable-cache: true
          python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
      - name: Update changelog
        working-directory: docs
        run: |
          git branch ${{ needs.publish-release.outputs.version }}-changelog
          git checkout ${{ needs.publish-release.outputs.version }}-changelog

          echo -e "# Changelog\n\n${{ needs.publish-release.outputs.changelog }}\n" > changelog-new.md

          echo "Manually linking usernames"
          sed -i -r 's|@([a-zA-Z0-9_]+) \(\[#|[@\1](https://github.com/\1) ([#|g' changelog-new.md

          echo "Removing unneeded comment tags"
          sed -i -r 's|@<!---->|@|g' changelog-new.md

          CURRENT_CHANGELOG=$(tail --lines +2 changelog.md)
          echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
          mv changelog-new.md changelog.md

          uv run \
            --python ${{ steps.setup-python.outputs.python-version }} \
            --dev \
            prek run --files changelog.md || true

          git config --global user.name "github-actions"
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"
          git push origin ${{ needs.publish-release.outputs.version }}-changelog
      - name: Create pull request
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
        with:
          script: |
            const { repo, owner } = context.repo;
            const result = await github.rest.pulls.create({
              title: 'Documentation: Add ${{ needs.publish-release.outputs.version }} changelog',
              owner,
              repo,
              head: '${{ needs.publish-release.outputs.version }}-changelog',
              base: 'main',
              body: 'This PR is auto-generated by CI.'
            });
            github.rest.issues.addLabels({
              owner,
              repo,
              issue_number: result.data.number,
              labels: ['documentation', 'skip-changelog']
            });
