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
19 changes: 19 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.py]
indent_style = space
indent_size = 4
max_line_length = 100

[*.{json,yml,yaml}]
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Default owner for everything in the repo
* @konverga
61 changes: 61 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Bug Report
description: Report a problem with KayakFit
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report a bug!

- type: textarea
id: description
attributes:
label: Description
description: What happened? What did you expect to happen instead?
validations:
required: true

- type: input
id: os
attributes:
label: Operating System
placeholder: "e.g. macOS 15.2, Windows 11"
validations:
required: true

- type: input
id: python-version
attributes:
label: Python version
placeholder: "e.g. 3.14.5"
validations:
required: false

- type: input
id: device
attributes:
label: Ergometer / firmware version
placeholder: "e.g. KayakFirst Bull, firmware X.Y"
validations:
required: false

- type: textarea
id: steps
attributes:
label: Steps to reproduce
value: |
1.
2.
3.
validations:
required: true

- type: textarea
id: logs
attributes:
label: Relevant log output
description: Paste logs from the app logger, if available. This will be auto-formatted as code.
render: shell
validations:
required: false
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions / Discussion
url: https://github.com/konverga/KayakFit/discussions
about: Ask general questions or discuss ideas here instead of opening an issue.
36 changes: 36 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Feature Request
description: Suggest an idea or improvement for KayakFit
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: Problem / motivation
description: What problem does this solve, or what's currently missing?
validations:
required: true

- type: textarea
id: solution
attributes:
label: Proposed solution
description: What would you like to happen?
validations:
required: true

- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Any other approaches you thought about?
validations:
required: false

- type: textarea
id: additional
attributes:
label: Additional context
description: Screenshots, related issues, links, etc.
validations:
required: false
26 changes: 26 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## Summary

<!-- What does this PR do and why? -->

## Changes

-
-

## Testing

<!-- How was this tested? e.g. ran against physical KayakFirst Bull, unit tests, manual GUI check -->

- [ ] Tested locally against the ergometer
- [ ] Existing tests pass
- [ ] Added/updated tests where relevant

## Checklist

- [ ] `ruff check .` and `mypy . --strict` pass with no errors (CI's `lint` job enforces this)
- [ ] Code follows project style (type hints, docstrings, double quotes)
- [ ] No secrets, tokens, or personal data included
- [ ] Updated README/docs if behavior changed
- [ ] Linked related issue(s), if any

Closes #
21 changes: 21 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: 2
updates:
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 1
groups:
python-dependencies:
patterns:
- "*"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 1
groups:
github-actions:
patterns:
- "*"
207 changes: 207 additions & 0 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
name: Build & Release

# Builds the macOS .app (Apple Silicon/arm64) and the Windows .exe with
# PyInstaller and attaches them to a GitHub Release.
#
# Triggers:
# - push a tag like "1.2.0" -> builds + creates/updates that release
# - manual run (Actions tab) -> builds only, no release (for testing)

on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch: {}

permissions:
contents: read

# Serialize release builds per ref, but never cancel an in-flight one: a
# half-finished release publish should complete rather than be interrupted.
concurrency:
group: build-release-${{ github.ref }}
cancel-in-progress: false

env:
# The interpreter every build leg uses. uv downloads a managed CPython for it.
UV_PYTHON: "3.14"

jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
- name: Verify release tag is on main
if: startsWith(github.ref, 'refs/tags/')
run: git merge-base --is-ancestor "$GITHUB_SHA" origin/main
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Validate source and tests
run: |
uv sync --locked
uv run ruff check .
uv run mypy . --strict
uv run pytest

build-macos:
needs: validate
runs-on: macos-14
timeout-minutes: 35

steps:
- uses: actions/checkout@v6
with:
persist-credentials: false

# Use the official CPython build that includes a working Tcl/Tk.
- name: Set up Python with Tk
uses: actions/setup-python@v5
with:
python-version: "3.14"

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Install dependencies
run: |
uv sync \
--locked \
--no-default-groups \
--group build \
--python "$(which python3.14)"

- name: Determine version
shell: bash
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "KAYAKFIT_VERSION=${GITHUB_REF_NAME}" >> "$GITHUB_ENV"
else
echo "KAYAKFIT_VERSION=0.0.0-dev" >> "$GITHUB_ENV"
fi

- name: Build with PyInstaller
run: |
uv run \
--python "$(which python3.14)" \
pyinstaller KayakFit.spec \
--distpath dist-mac \
--workpath build-mac

- name: Verify Tcl/Tk bundle
shell: bash
run: |
APP="dist-mac/KayakFit.app"

required=(
"Contents/Frameworks/Tcl"
"Contents/Frameworks/Tk"
"Contents/Resources/tcl9"
"Contents/Resources/_tcl_data"
"Contents/Resources/_tk_data"
)

missing=0
for path in "${required[@]}"; do
if [[ ! -e "$APP/$path" ]]; then
echo "::error::Missing $path"
missing=1
fi
done

if [[ "$missing" -ne 0 ]]; then
exit 1
fi

echo "✓ Tcl/Tk bundle verified."

- name: Package .app bundle
run: |
cd dist-mac
ditto -c -k --sequesterRsrc --keepParent \
KayakFit.app \
../KayakFit-macOS-arm64.zip

- uses: actions/upload-artifact@v7
with:
name: KayakFit-macOS-arm64
path: KayakFit-macOS-arm64.zip
if-no-files-found: error
retention-days: 7

build-windows:
needs: validate
runs-on: windows-latest
timeout-minutes: 35
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Install dependencies
# Runtime deps + the PyInstaller build group, but not the dev tools
# (ruff/mypy/pytest) — they are never part of the shipped bundle.
run: uv sync --locked --no-default-groups --group build

- name: Determine version
shell: bash
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "KAYAKFIT_VERSION=${GITHUB_REF_NAME}" >> "$GITHUB_ENV"
else
echo "KAYAKFIT_VERSION=0.0.0-dev" >> "$GITHUB_ENV"
fi

- name: Build with PyInstaller
run: uv run pyinstaller KayakFit.spec --distpath dist-win --workpath build-win

- name: Package exe
run: Compress-Archive -Path dist-win\KayakFit.exe -DestinationPath KayakFit-Windows.zip

- uses: actions/upload-artifact@v7
with:
name: KayakFit-Windows
path: KayakFit-Windows.zip
if-no-files-found: error
retention-days: 7

release:
needs: [build-macos, build-windows]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- uses: actions/download-artifact@v8
with:
path: artifacts

- name: Generate release checksums
working-directory: artifacts
run: |
sha256sum \
KayakFit-macOS-arm64/KayakFit-macOS-arm64.zip \
KayakFit-Windows/KayakFit-Windows.zip \
> SHA256SUMS.txt

- name: Attach builds to release
uses: softprops/action-gh-release@v3
with:
generate_release_notes: true
files: |
artifacts/KayakFit-macOS-arm64/KayakFit-macOS-arm64.zip
artifacts/KayakFit-Windows/KayakFit-Windows.zip
artifacts/SHA256SUMS.txt
Loading
Loading