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 # ---- Frontend Build ---- - name: Install pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.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@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 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@6db134d15f3909ccc9eefd369f02bd1e9cffdf97 # v6.2.0 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@8f6863c6c894ba46f9e676ef5cccec4752723c1e # v1.9.2 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 - 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'] });