name: Release on: workflow_dispatch: permissions: contents: write jobs: validate-release: name: Validate Release runs-on: ubuntu-latest steps: - name: Checkout Source Code uses: actions/checkout@v5 with: fetch-depth: 0 - name: Check release tag is new env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | version="$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")" if [ "${{ github.ref }}" = "refs/heads/main" ]; then tag="v${version}" else safe_ref="$(echo "${{ github.ref_name }}" | tr '/_' '--' | tr -cd '[:alnum:].-')" tag="v${version}-${safe_ref}.${GITHUB_RUN_NUMBER}" fi if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then echo "::error title=Release tag already exists::${tag} already exists. Bump project.version in pyproject.toml before running the release workflow." exit 1 fi if gh release view "$tag" >/dev/null 2>&1; then echo "::error title=GitHub release already exists::${tag} already has a GitHub release. Bump project.version in pyproject.toml before running the release workflow." exit 1 fi build-windows: name: Build Windows needs: validate-release runs-on: windows-latest steps: - name: Checkout Source Code uses: actions/checkout@v5 - name: Get Python uses: actions/setup-python@v6 with: python-version: "3.12" - name: Install Dependencies run: | python -m pip install --upgrade pip python -m pip install -e ".[build]" - name: Test run: python -m unittest discover -s tests - name: Build run: python -m PyInstaller --clean --noconfirm "packaging/NTE History Exporter.spec" - name: Smoke Test run: .\dist\nte-history-exporter.exe --help - name: Upload Artifact uses: actions/upload-artifact@v6 with: name: windows path: dist/nte-history-exporter.exe build-linux: name: Build Linux needs: validate-release runs-on: ubuntu-latest steps: - name: Checkout Source Code uses: actions/checkout@v5 - name: Install libpcap run: sudo apt-get update && sudo apt-get install -y libpcap-dev - name: Get Python uses: actions/setup-python@v6 with: python-version: "3.12" - name: Install Dependencies run: | python -m pip install --upgrade pip python -m pip install -e ".[build]" - name: Test run: python -m unittest discover -s tests - name: Build run: python -m PyInstaller --clean --noconfirm "packaging/NTE History Exporter.spec" - name: Smoke Test run: ./dist/nte-history-exporter --help - name: Upload Artifact uses: actions/upload-artifact@v6 with: name: linux path: dist/nte-history-exporter build-macos: name: Build Macos needs: validate-release runs-on: macos-15-intel steps: - name: Checkout Source Code uses: actions/checkout@v5 - name: Get Python uses: actions/setup-python@v6 with: python-version: "3.12" - name: Install Dependencies run: | python -m pip install --upgrade pip python -m pip install -e ".[build]" - name: Test run: python -m unittest discover -s tests - name: Build run: python -m PyInstaller --clean --noconfirm "packaging/NTE History Exporter.spec" - name: Smoke Test run: ./dist/nte-history-exporter --help - name: Upload Artifact uses: actions/upload-artifact@v6 with: name: macos path: dist/nte-history-exporter release: name: Release needs: [build-windows, build-linux, build-macos] runs-on: ubuntu-latest steps: - name: Checkout Source Code uses: actions/checkout@v5 with: fetch-depth: 0 - name: Get package version id: package-version run: | version="$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")" echo "version=$version" >> "$GITHUB_OUTPUT" - name: Build Release Notes run: | version="${{ steps.package-version.outputs.version }}" if [ "${{ github.ref }}" = "refs/heads/main" ]; then tag="v${version}" else safe_ref="$(echo "${{ github.ref_name }}" | tr '/_' '--' | tr -cd '[:alnum:].-')" tag="v${version}-${safe_ref}.${GITHUB_RUN_NUMBER}" fi repo_url="${{ github.server_url }}/${{ github.repository }}" previous_tag="$(git tag --merged HEAD --sort=-v:refname | grep -E '^v[0-9]+(\.[0-9]+)*$' | grep -vx "$tag" | head -n 1 || true)" { echo "Release ${tag}" echo echo "## Commits included in this release" echo if [ -n "$previous_tag" ]; then echo "Changes since ${previous_tag}:" commit_range="${previous_tag}..HEAD" else commit_range="HEAD" fi echo while IFS= read -r commit; do short_hash="$(git show -s --format='%h' "$commit")" title="$(git show -s --format='%s' "$commit")" description="$(git show -s --format='%b' "$commit")" echo "- [\`${short_hash}\`](${repo_url}/commit/${commit}) ${title}" if [ -n "$description" ]; then echo "$description" | sed 's/^/ /' fi echo done < <(git rev-list "$commit_range") } > release-notes.md - name: Download Artifacts uses: actions/download-artifact@v7 - name: Rename run: | version="${{ steps.package-version.outputs.version }}" cp windows/nte-history-exporter.exe nte-history-exporter.exe zip nte-history-exporter-v${version}-x86_64-pc-windows-msvc.zip nte-history-exporter.exe cp linux/nte-history-exporter nte-history-exporter chmod +x nte-history-exporter cp nte-history-exporter nte-history-exporter-linux zip nte-history-exporter-v${version}-x86_64-unknown-linux-gnu.zip nte-history-exporter cp macos/nte-history-exporter nte-history-exporter chmod +x nte-history-exporter cp nte-history-exporter nte-history-exporter-macos zip nte-history-exporter-v${version}-x86_64-apple-darwin.zip nte-history-exporter - name: Release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | version="${{ steps.package-version.outputs.version }}" if [ "${{ github.ref }}" = "refs/heads/main" ]; then tag="v${version}" prerelease_args=() else safe_ref="$(echo "${{ github.ref_name }}" | tr '/_' '--' | tr -cd '[:alnum:].-')" tag="v${version}-${safe_ref}.${GITHUB_RUN_NUMBER}" prerelease_args=(--prerelease) fi files=( nte-history-exporter.exe nte-history-exporter-linux nte-history-exporter-macos nte-history-exporter-v${version}-x86_64-pc-windows-msvc.zip nte-history-exporter-v${version}-x86_64-unknown-linux-gnu.zip nte-history-exporter-v${version}-x86_64-apple-darwin.zip ) if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then echo "::error title=Release tag already exists::${tag} already exists. Bump project.version in pyproject.toml before running the release workflow." exit 1 fi if gh release view "$tag" >/dev/null 2>&1; then echo "::error title=GitHub release already exists::${tag} already has a GitHub release. Bump project.version in pyproject.toml before running the release workflow." exit 1 fi gh release create "$tag" "${files[@]}" --title "${tag} Release" --notes-file release-notes.md "${prerelease_args[@]}"