diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..2b7064a9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,104 @@ +name: Release + +on: + push: + tags: + - v[0-9]+.[0-9]+.[0-9]+* + +jobs: + linux: + strategy: + matrix: + build-type: ['', portable] + include: + - build-type: '' + build-flag: '' + suffix: '' + - build-type: portable + build-flag: --portable + suffix: _portable + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: pip install -Ur requirements.txt pyinstaller + - run: pyinstaller tagstudio.spec -- ${{ matrix.build-flag }} + - run: tar czfC dist/tagstudio_linux_x86_64${{ matrix.suffix }}.tar.gz dist tagstudio + - uses: actions/upload-artifact@v4 + with: + name: tagstudio_linux_x86_64${{ matrix.suffix }} + path: dist/tagstudio_linux_x86_64${{ matrix.suffix }}.tar.gz + + macos: + strategy: + matrix: + os-version: ['11', '14'] + include: + - os-version: '11' + arch: x86_64 + - os-version: '14' + arch: aarch64 + runs-on: macos-${{ matrix.os-version }} + env: + MACOSX_DEPLOYMENT_TARGET: '11.0' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: pip install -Ur requirements.txt pyinstaller + - run: pyinstaller tagstudio.spec + - run: tar czfC dist/tagstudio_macos_${{ matrix.arch }}.tar.gz dist TagStudio.app + - uses: actions/upload-artifact@v4 + with: + name: tagstudio_macos_${{ matrix.arch }} + path: dist/tagstudio_macos_${{ matrix.arch }}.tar.gz + + windows: + strategy: + matrix: + build-type: ['', portable] + include: + - build-type: '' + build-flag: '' + suffix: '' + file-end: '' + - build-type: portable + build-flag: --portable + suffix: _portable + file-end: .exe + runs-on: windows-2019 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: pip install -Ur requirements.txt pyinstaller + - run: PyInstaller tagstudio.spec -- ${{ matrix.build-flag }} + - run: Compress-Archive -Path dist/TagStudio${{ matrix.file-end }} -DestinationPath dist/tagstudio_windows_x86_64${{ matrix.suffix }}.zip + - uses: actions/upload-artifact@v4 + with: + name: tagstudio_windows_x86_64${{ matrix.suffix }} + path: dist/tagstudio_windows_x86_64${{ matrix.suffix }}.zip + + publish: + needs: [linux, macos, windows] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + - uses: softprops/action-gh-release@v2 + with: + files: | + tagstudio_linux_x86_64/* + tagstudio_linux_x86_64_portable/* + tagstudio_macos_x86_64/* + tagstudio_macos_x86_64_portable/* + tagstudio_macos_arm64/* + tagstudio_macos_arm64_portable/* + tagstudio_windows_x86_64/* + tagstudio_windows_x86_64_portable/* diff --git a/.gitignore b/.gitignore index 5f5f4002..f20f8b1c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ MANIFEST # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec +!tagstudio.spec # Installer logs pip-log.txt diff --git a/tagstudio.spec b/tagstudio.spec new file mode 100644 index 00000000..d4b9d9fe --- /dev/null +++ b/tagstudio.spec @@ -0,0 +1,86 @@ +# -*- mode: python ; coding: utf-8 -*- +# vi: ft=python + + +from argparse import ArgumentParser +import sys +from PyInstaller.building.api import COLLECT, EXE, PYZ +from PyInstaller.building.build_main import Analysis +from PyInstaller.building.osx import BUNDLE + + +parser = ArgumentParser() +parser.add_argument('--portable', action='store_true') +options = parser.parse_args() + + +name = 'TagStudio' if sys.platform == 'win32' else 'tagstudio' +icon = None +if sys.platform == 'win32': + icon = 'tagstudio/resources/icon.ico' +elif sys.platform == 'darwin': + icon = 'tagstudio/resources/icon.icns' + + +a = Analysis( + ['tagstudio/tag_studio.py'], + pathex=[], + binaries=[], + datas=[('tagstudio/resources', 'resources')], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + excludes=[], + runtime_hooks=[], + noarchive=False, + optimize=0, +) + +pyz = PYZ(a.pure) + +include = [a.scripts] +if options.portable: + include += (a.binaries, a.datas) +exe = EXE( + pyz, + *include, + [], + bootloader_ignore_signals=False, + console=False, + hide_console='hide-early', + disable_windowed_traceback=False, + debug=False, + name=name, + exclude_binaries=not options.portable, + icon=icon, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None +) + +coll = None if options.portable else COLLECT( + exe, + a.binaries, + a.datas, + name=name, + strip=False, + upx=True, + upx_exclude=[], +) + +app = BUNDLE( + exe if coll is None else coll, + name='TagStudio.app', + icon=icon, + bundle_identifier='com.github.tagstudiodev', + version='0.0.0', + info_plist={ + 'NSAppleScriptEnabled': False, + 'NSPrincipalClass': 'NSApplication', + } +)