Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/actions/setup-python/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: (c) TagStudio Contributors
# SPDX-License-Identifier: CC0-1.0
---
name: Setup Python
description: Combination of Python setup actions

inputs:
skip-setup:
default: 'false'
description: Whether to skip setup actions and only execute installs
groups:
description: Newline-separated list of dependency groups to install

runs:
using: composite
steps:
- name: Setup Python install
if: inputs.skip-setup != 'true'
uses: actions/setup-python@v6
with:
python-version: '3.12'

- name: Setup uv install
if: inputs.skip-setup != 'true'
uses: astral-sh/setup-uv@v8.2.0

- name: Run pip install (Windows)
if: runner.os == 'Windows'
env:
INPUT_GROUPS: ${{ inputs.groups }}
UV_PYTHON_DOWNLOADS: never
UV_SYSTEM_PYTHON: '1'
shell: pwsh
run: |
if (![string]::IsNullOrEmpty(${env:INPUT_GROUPS})) {
$groupArgs = -split ${env:INPUT_GROUPS} -replace '^', '--group='
}

uv pip install . ${groupArgs}

- name: Run pip install (non-Windows)
if: runner.os != 'Windows'
env:
INPUT_GROUPS: ${{ inputs.groups }}
UV_PYTHON_DOWNLOADS: never
UV_SYSTEM_PYTHON: '1'
shell: bash
run: |
extra_args=()

if [ -n "${INPUT_GROUPS}" ]; then
while IFS= read -r group; do
if [ -n "${group}" ]; then
extra_args+=("--group=${group}")
fi
done <<<"${INPUT_GROUPS}"
fi

uv pip install . "${extra_args[@]}"
203 changes: 203 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# SPDX-FileCopyrightText: (c) TagStudio Contributors
# SPDX-License-Identifier: CC0-1.0

# TODO: This will be invoked by a 'Release' workflow in the future.
---
name: Build

on:
push:
tags:
- v*
schedule:
# From: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule
# To decrease the chance of delay, schedule your workflow to run at a different time of the hour.
- cron: 42 3 * * *
timezone: America/Los_Angeles
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

defaults:
run:
shell: sh

jobs:
meta:
name: Metadata
runs-on: ubuntu-latest
outputs:
type: ${{ steps.meta.outputs.type }}
version: ${{ steps.meta.outputs.version }}
steps:
- name: Set metadata
id: meta
run: |
type=Nightly
if [ "${GITHUB_REF_TYPE}" = tag ]; then
case ${GITHUB_REF_NAME} in
v*)
type=Release
case ${GITHUB_REF_NAME} in
*-pr*) type=Pre-${type} ;;
esac
;;
esac
fi

if [ ${type} = Nightly ]; then
version=dev+$(date --iso-8601 | tr -d -)
else
version=${GITHUB_REF_NAME}
fi

cat <<EOF >>"${GITHUB_OUTPUT}"
type=${type}
version=${version}
EOF

build:
strategy:
fail-fast: ${{ needs.meta.outputs.type != 'Nightly' }}
matrix:
include:
- os: ubuntu-22.04
os-label: Linux x86

- os: macos-15-intel
os-label: macOS x86

- os: macos-15
os-label: macOS ARM

- os: windows-2022
os-label: Windows x86

name: Build${{ needs.meta.outputs.type && format(' {0}', needs.meta.outputs.type) }}${{ matrix.os-label && format(' ({0})', matrix.os-label) }}
needs: meta
runs-on: ${{ matrix.os }}
env:
BUILD_PREFIX: tagstudio_${{ needs.meta.outputs.version }}
steps:
- name: Prepare build (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$dir = "${env:RUNNER_TEMP}/upload"
New-Item -Path ${dir} -ItemType Directory -Force

$os = ${env:RUNNER_OS}.ToLower()
$arch = uname -m

$commonPath = "${dir}/${env:BUILD_PREFIX}_${os}_${arch}"
Add-Content -Path ${env:GITHUB_ENV} -Value @"
BUILD_ARTIFACT_DEFAULT=${commonPath}.zip
BUILD_ARTIFACT_PORTABLE=${commonPath}_portable.zip
"@ -Encoding utf8

- name: Prepare build (non-Windows)
if: runner.os != 'Windows'
run: |
dir=${RUNNER_TEMP}/upload
mkdir -p "${dir}"
os=$(
tr '[:upper:]' '[:lower:]' <<EOF
${RUNNER_OS}
EOF
)
arch=$(uname -m)

common_path=${dir}/${BUILD_PREFIX}_${os}_${arch}
artifact_default=${common_path}.tar.gz
if [ "${RUNNER_OS}" = macOS ]; then
artifact_portable=
else
artifact_portable=${common_path}_portable.tar.gz
fi
cat <<EOF >>"${GITHUB_ENV}"
BUILD_ARTIFACT_DEFAULT=${artifact_default}
${artifact_portable:+BUILD_ARTIFACT_PORTABLE=${artifact_portable}}
EOF

- name: Checkout repository
uses: actions/checkout@v7

- name: Setup Python
uses: ./.github/actions/setup-python
with:
groups: build

- parallel:
- name: Run PyInstaller (Linux)
if: runner.os == 'Linux'
run: |
pyinstaller --distpath dist/default --workpath build/default scripts/tagstudio.spec
tar czf "${BUILD_ARTIFACT_DEFAULT}" -C dist/default/tagstudio .

- name: Run PyInstaller (Linux portable)
if: runner.os == 'Linux'
run: |
pyinstaller --distpath dist/portable --workpath build/portable scripts/tagstudio.spec -- --portable
tar czf "${BUILD_ARTIFACT_PORTABLE}" -C dist/portable tagstudio

- name: Run PyInstaller (macOS)
if: runner.os == 'macOS'
run: |
scripts/tagstudio.spec
tar czf "${BUILD_ARTIFACT_DEFAULT}" -C dist TagStudio.app

- name: Run PyInstaller (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
PyInstaller --distpath dist/default --workpath build/default scripts/tagstudio.spec
Compress-Archive -Path dist/default/TagStudio -DestinationPath ${env:BUILD_ARTIFACT_DEFAULT}

- name: Run PyInstaller (Windows portable)
if: runner.os == 'Windows'
shell: pwsh
run: |
PyInstaller --distpath dist/portable --workpath build/portable scripts/tagstudio.spec -- --portable
Compress-Archive -Path dist/portable/TagStudio.exe -DestinationPath ${env:BUILD_ARTIFACT_PORTABLE}

- parallel:
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
path: ${{ env.BUILD_ARTIFACT_DEFAULT }}
if-no-files-found: error
# Short retention for 'Pre-Release' and 'Release' because they get uploaded to a GitHub release.
retention-days: &upload-artifact-retention-days ${{ case(needs.meta.outputs.type == 'Nightly', '7', '1') }}
archive: false

- name: Upload build artifact (portable)
if: env.BUILD_ARTIFACT_PORTABLE != ''
uses: actions/upload-artifact@v7
with:
path: ${{ env.BUILD_ARTIFACT_PORTABLE }}
if-no-files-found: error
retention-days: *upload-artifact-retention-days
archive: false

upload:
permissions:
contents: write

name: Upload Builds to Release
needs: [meta, build]
if: needs.meta.outputs.type != 'Nightly'
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v8
with:
path: ${{ runner.temp }}/download
merge-multiple: 'true'
skip-decompress: 'true'

- name: Upload build artifacts to release
uses: softprops/action-gh-release@v3
with:
files: ${{ runner.temp }}/download/*
56 changes: 56 additions & 0 deletions .github/workflows/build_docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# SPDX-FileCopyrightText: (c) TagStudio Contributors
# SPDX-License-Identifier: CC0-1.0

# TODO: In the future, docs will be built (but not published) for PRs.
---
name: Build Docs

on:
push:
branches:
- main
paths:
- .github/actions/setup-python/action.yml
- .github/workflows/build_docs.yml
- docs/**
- mkdocs.yml
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

defaults:
run:
shell: sh

jobs:
publish:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
permissions:
contents: write

name: Publish Docs
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v7

- name: Setup Python
uses: ./.github/actions/setup-python
with:
groups: docs

- name: Use cache
uses: actions/cache@v6
with:
key: mkdocs-material-${{ github.run_id }}
path: .cache
restore-keys: |
mkdocs-material-
- name: Run mkdocs
env:
DISABLE_MKDOCS_2_WARNING: 'true'
run: mkdocs gh-deploy --force
Loading