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
120 changes: 96 additions & 24 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,107 @@ name: Release
#
# git tag v1.1.0 && git push origin v1.1.0
#
# which builds a standalone Windows .exe, builds the sdist and wheel, and
# attaches all three to a GitHub release. Publishing to PyPI is opt-in: set the
# repository variable PUBLISH_TO_PYPI to "true" once trusted publishing is
# configured at https://pypi.org/manage/account/publishing/ .
# which builds a Windows .msi, macOS .dmg for both architectures, the sdist and
# the wheel, and attaches them all to a GitHub release. Each packaged build runs
# its own self-test first, so an incomplete bundle fails here rather than in
# somebody's dialog box.
#
# The same jobs run on a pull request that touches the packaging, without
# publishing anything - packaging breaks quietly and only on the platform you
# are not using.
#
# PyPI publishing is opt-in: set the repository variable PUBLISH_TO_PYPI to
# "true" once trusted publishing is configured at
# https://pypi.org/manage/account/publishing/ .

on:
push:
tags: ["v*"]
pull_request:
paths:
- "packaging/**"
- "tools/build_app.py"
- "tools/fetch_platform_tools.py"
- "tools/pyinstaller_entry.py"
- "tools/make_icon.py"
- ".github/workflows/release.yml"
workflow_dispatch:

permissions:
contents: write

env:
# WiX 6 and later require accepting a separate maintenance-fee licence; 5 is
# the last version that builds unattended.
WIX_VERSION: "5.0.2"

jobs:
windows-exe:
name: Windows executable
windows:
name: Windows installer
runs-on: windows-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[gui]" pyinstaller
dotnet tool install --global wix --version ${{ env.WIX_VERSION }}

- name: Build AutoDraw.exe
run: >
pyinstaller --onefile --windowed --name AutoDraw
--collect-all customtkinter
tools/pyinstaller_entry.py
- name: Build app, installer and archive
run: python tools/build_app.py --msi --archive

- uses: actions/upload-artifact@v4
with:
name: AutoDraw-windows
path: dist/AutoDraw.exe
name: windows
path: |
dist/*.msi
dist/*.zip

macos:
name: macOS app (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
# A retired runner label does not fail - it queues for ever, and the release
# job waits behind it. macos-13 did exactly that.
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- runner: macos-latest
arch: arm64
- runner: macos-15-intel
arch: x86_64

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install build dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[gui]" pyinstaller

- name: Build app and disk image
run: python tools/build_app.py --dmg

- uses: actions/upload-artifact@v4
with:
name: macos-${{ matrix.arch }}
path: dist/*.dmg

distribution:
name: Build distribution
name: Python distribution
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4

Expand All @@ -67,26 +125,40 @@ jobs:

release:
name: Publish GitHub release
needs: [windows-exe, distribution]
needs: [windows, macos, distribution]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/download-artifact@v4
with:
name: AutoDraw-windows
path: artifacts

- uses: actions/download-artifact@v4
with:
name: dist
path: artifacts
merge-multiple: true

- uses: softprops/action-gh-release@v2
with:
files: artifacts/*
generate_release_notes: true
draft: false
prerelease: false
body: |
### Which file do I want?

| | |
|---|---|
| **Windows** | `AutoDraw-*-x64.msi` — installs like any other program, Start Menu entry included. The `.zip` is the same app with no installer. |
| **macOS** | `AutoDraw-*-arm64.dmg` for Apple Silicon, `AutoDraw-*-x64.dmg` for Intel. Drag it to Applications. |
| **Linux** | Run from source — see the [README](https://github.com/MAXAWER/AutoDraw-Sim#install-variants). |

Everything is included: Python, OpenCV and **adb** all travel inside
the application. There is nothing else to install and no Android SDK
to set up.

The builds are not code-signed, because signing certificates cost
money this project does not take. Windows SmartScreen will say
"unknown publisher" — *More info* → *Run anyway*. On macOS, the first
launch needs a right-click → **Open**, or:

```
xattr -dr com.apple.quarantine /Applications/AutoDraw.app
```

pypi:
name: Publish to PyPI
Expand Down
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Build artifacts
/dist/
/build/
*.spec
# PyInstaller drops a generated spec beside whatever script it was given; the
# one we maintain lives in packaging/ and must stay tracked.
/*.spec

# Python
__pycache__/
Expand All @@ -22,6 +24,9 @@ __pycache__/
screen_tmp.png
*.log

# Downloaded by tools/fetch_platform_tools.py
/platform-tools/

# Environment
.env
venv/
Expand All @@ -32,3 +37,6 @@ screen_tmp.png

# Local virtualenvs
.venv/

# Superseded by adbtouch/injector.jar, which ships inside the package
/injector/*.jar
29 changes: 27 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
# Changelog

## Unreleased

- **Drawing works on devices that refuse raw touch events** - every recent
Pixel, where SELinux denies the shell domain write access to `/dev/input`.
`Device.supports_raw_touch` probes for it and the drawing path is picked
accordingly, instead of reporting success and doing nothing.
- **An on-device injector** (`adbtouch/injector.py`, built from `injector/`),
run once through `app_process` and fed points over stdin. One process for a
whole drawing rather than one per point, which is what makes realistic timing
possible at all. Measured on a Pixel 8 Pro: a 136-stroke, 1,679-point drawing
in about 19 seconds, against 3 minutes 40 through `input`.
- **`adbtouch.hand`** simulates hand drawing: rounded corners, a velocity
profile, tremor, overshoot and a sensible stroke order.
- **`adbtouch.paths`** joins the fragments `findContours` returns back into
strokes and drops specks - 215 strokes became 136 on the sample image, which
is both faster and cleaner.
- `tools/test_canvas.py`: a drawing canvas served to the device over
`adb reverse`, with a calibration pattern, for checking any of this.
- Speed and hand-drawing controls in the desktop app, with a time estimate.

## 1.1.0

Packaging and presentation release; no behavioural changes to drawing or replay.

- Tagged releases now build a standalone `AutoDraw.exe` and the Python
distribution automatically, and attach both to the GitHub release.
- **Installers.** A Windows `.msi` that installs like any other program, and a
macOS `.dmg` for both Apple Silicon and Intel, built automatically from a tag.
- **Self-contained.** The packaged builds carry their own `adb`: no Android SDK,
no platform-tools download, no `PATH` to edit. `find_adb` looks inside the
bundle first, so an installed AutoDraw behaves the same everywhere.
- Packaged builds run a self-test before shipping, which imports the whole
application and runs the bundled adb.
- Optional PyPI publishing through trusted publishing — see
[docs/RELEASING.md](docs/RELEASING.md).
- `run.bat` and `run.sh`: one-click launchers that create the virtual
Expand Down
Loading
Loading