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
4 changes: 2 additions & 2 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ jobs:
python_version: ['3.12']
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4 # persist-credentials must be false so the deploy step below uses its own token, not the checkout credentials.
uses: actions/checkout@v5 # persist-credentials must be false so the deploy step below uses its own token, not the checkout credentials.
with:
submodules: 'recursive'
persist-credentials: false

- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python_version }}

Expand Down
94 changes: 60 additions & 34 deletions _data/downloads.yml
Original file line number Diff line number Diff line change
@@ -1,42 +1,68 @@
version: v0.12.2

platforms:
# Generated by scripts/update-downloads.py — do not edit by hand.
stable:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update remaining download data consumers

Because the downloads data now lives under stable, the existing consumers that still read site.data.downloads.version and site.data.downloads.platforms lose their inputs. On the homepage download button, _includes/download-url.js parses the missing platforms value and then calls .filter(...), so visitors with a detected OS hit a JS error instead of getting a direct platform download; _includes/schema-index.html also emits an empty software version and no per-platform entry points. Please either preserve the old top-level aliases or update those includes to read site.data.downloads.stable.*.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e4749b4. Updated _includes/download-url.js (download button) and _includes/schema-index.html (JSON-LD) to read site.data.downloads.stable.*. Verified no flat downloads.version/downloads.platforms references remain anywhere in the repo.

version: v0.13.2
platforms:
- name: Windows
assets:
- title: Installer
description: " (recommended)"
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.12.2/activitywatch-v0.12.2-windows-x86_64-setup.exe
- title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.12.2/activitywatch-v0.12.2-windows-x86_64.zip
- title: Chocolatey
url: https://chocolatey.org/packages/activitywatch

- title: Installer
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64-setup.exe
description: ' (recommended)'
- title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip
- title: Chocolatey
url: https://chocolatey.org/packages/activitywatch
- name: Linux
assets:
- title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.12.2/activitywatch-v0.12.2-linux-x86_64.zip
- title: .AppImage
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.12.2/activitywatch-linux-x86_64.AppImage
- title: AUR
description: ", for Arch Linux and Manjaro"
url: https://aur.archlinux.org/packages/activitywatch-bin/
- title: nixpkgs
description: ", for NixOS"
url: https://search.nixos.org/packages?query=activitywatch&show=activitywatch

- title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-linux-x86_64.zip
- title: .AppImage
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-linux-x86_64.AppImage
- title: .deb
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-linux-x86_64.deb
- title: AUR
description: ', for Arch Linux and Manjaro'
url: https://aur.archlinux.org/packages/activitywatch-bin/
- title: nixpkgs
description: ', for NixOS'
url: https://search.nixos.org/packages?query=activitywatch&show=activitywatch
- name: macOS
assets:
- title: .dmg
description: " (recommended)"
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.12.2/activitywatch-v0.12.2-macos-x86_64.dmg
- type: zip
title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.12.2/activitywatch-v0.12.2-macos-x86_64.zip
- title: Homebrew
url: https://formulae.brew.sh/cask/activitywatch

- title: .dmg
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-macos-x86_64.dmg
description: ' (recommended) (Intel)'
- title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-macos-x86_64.zip
description: ' (Intel)'
- title: Homebrew
url: https://formulae.brew.sh/cask/activitywatch
- name: Android
assets:
- title: Play Store
description: " (beta)"
url: https://play.google.com/store/apps/details?id=net.activitywatch.android
- title: Play Store
description: ' (beta)'
url: https://play.google.com/store/apps/details?id=net.activitywatch.android
prerelease:
version: v0.14.0b1
platforms:
- name: Windows
assets:
- title: Installer
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.14.0b1/activitywatch-v0.14.0b1-windows-x86_64-setup.exe
description: ' (recommended)'
- title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.14.0b1/activitywatch-v0.14.0b1-windows-x86_64.zip
- name: Linux
assets:
- title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.14.0b1/activitywatch-v0.14.0b1-linux-x86_64.zip
- title: .AppImage
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.14.0b1/activitywatch-linux-x86_64.AppImage
- title: .deb
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.14.0b1/activitywatch-v0.14.0b1-linux-x86_64.deb
- name: macOS
assets:
- title: .dmg
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.14.0b1/activitywatch-v0.14.0b1-macos-arm64.dmg
description: ' (recommended) (Apple Silicon)'
- title: .zip
url: https://github.com/ActivityWatch/activitywatch/releases/download/v0.14.0b1/activitywatch-v0.14.0b1-macos-arm64.zip
description: ' (Apple Silicon)'
4 changes: 2 additions & 2 deletions _includes/download-url.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ function getOS() {
function getDownloadInfo() {
let os = getOS();
if (os !== null) {
let version = "{{ site.data.downloads.version }}";
let platforms = JSON.parse(`{{ site.data.downloads.platforms | jsonify }}`);
let version = "{{ site.data.downloads.stable.version }}";
let platforms = JSON.parse(`{{ site.data.downloads.stable.platforms | jsonify }}`);
let p = platforms.filter((p) => p.name === os)[0] || null;
if (p !== null) {
return {
Expand Down
8 changes: 4 additions & 4 deletions _includes/schema-index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"ratingValue": "{{ site.data.ratings.alternativeto.average }}",
"reviewCount": "{{ site.data.ratings.alternativeto.count }}"
},
"softwareVersion": "{{ site.data.downloads.version }}",
"softwareVersion": "{{ site.data.downloads.stable.version }}",
"datePublished": "2024-06-12",
"dateCreated": "2016-06-10",
"downloadUrl": "https://activitywatch.net/downloads/",
Expand Down Expand Up @@ -53,15 +53,15 @@
"target": [
"https://activitywatch.net/downloads/",
"android-app:https://play.google.com/store/apps/details?id=net.activitywatch.android",
{% for platform in site.data.downloads.platforms %}
{% for platform in site.data.downloads.stable.platforms %}
{% for asset in platform.assets %}
{
"@type": "EntryPoint",
"urlTemplate": "{{ asset.url }}",
"actionApplication": {
"@type": "SoftwareApplication",
"@id": "activitywatch-{{ site.data.downloads.version }}-{{ platform.name | downcase }}-{{ asset.title | replace: ".", "" | downcase }}",
"name": "ActivityWatch {{ site.data.downloads.version }} {{ platform.name }} {{ asset.title }}",
"@id": "activitywatch-{{ site.data.downloads.stable.version }}-{{ platform.name | downcase }}-{{ asset.title | replace: ".", "" | downcase }}",
"name": "ActivityWatch {{ site.data.downloads.stable.version }} {{ platform.name }} {{ asset.title }}",
"operatingSystem": "{{ platform.name }}"
}
},
Expand Down
21 changes: 19 additions & 2 deletions downloads.pug
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ article.container
header
h1 Downloads

h3.mt-4.mb-2 ActivityWatch {{ site.data.downloads.version }}
h3.mt-4.mb-2 ActivityWatch {{ site.data.downloads.stable.version }}

div.row.mt-4
| {% for platform in site.data.downloads.platforms %}
| {% for platform in site.data.downloads.stable.platforms %}
div.col-md-6(id="{{ platform.name }}")
b {{ platform.name }}
ul
Expand All @@ -23,6 +23,23 @@ article.container
| {% endfor %}
| {% endfor %}

| {% if site.data.downloads.prerelease %}
h3.mt-5.mb-2 Pre-release: ActivityWatch {{ site.data.downloads.prerelease.version }}
p.small.dim
| A newer pre-release is available for testing new features and fixes. It may be unstable — for everyday use, prefer the stable release above.
div.row.mt-2
| {% for platform in site.data.downloads.prerelease.platforms %}
div.col-md-6(id="prerelease-{{ platform.name }}")
b {{ platform.name }}
ul
| {% for asset in platform.assets %}
li
a(href="{{asset.url}}" style="{% if forloop.first %} font-weight: bold {% endif %}") {{ asset.title }}
| {{ asset.description }}
| {% endfor %}
| {% endfor %}
| {% endif %}

div For help on how to get started, see the #[a(href="https://docs.activitywatch.net/en/latest/getting-started.html") Getting Started guide] in the documentation.

div Looking for an older or pre-release version? You can find all #[a(href="https://github.com/ActivityWatch/activitywatch/releases") releases on GitHub].
Expand Down
160 changes: 135 additions & 25 deletions scripts/update-downloads.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,153 @@
#!/usr/bin/env python3
"""Regenerate _data/downloads.yml from the ActivityWatch GitHub releases.

Writes the latest stable release, plus — when it is newer than that stable —
the latest pre-release, so betas/nightlies newer than stable are surfaced too.

Download links are built from each release's *actual* assets rather than by
substituting a version number into a fixed template, so it survives asset
naming changes between versions (e.g. the v0.14 `tauri` builds and macOS
moving from x86_64 to arm64).
"""

import os
import re
from datetime import datetime, timedelta, timezone
from pathlib import Path

import requests
import yaml

API = "https://api.github.com/repos/ActivityWatch/activitywatch/releases"

def is_older(dt: str, td: timedelta):
now = datetime.now(tz=timezone.utc)
created_at = datetime.fromisoformat(dt.strip("Z")).replace(tzinfo=timezone.utc)
return created_at < now - td
# Static (non-GitHub) distribution links, appended per platform for the stable
# release only — package managers and the Play Store track stable, not betas.
PACKAGE_LINKS = {
"Windows": [
{"title": "Chocolatey", "url": "https://chocolatey.org/packages/activitywatch"},
],
"Linux": [
{"title": "AUR", "description": ", for Arch Linux and Manjaro",
"url": "https://aur.archlinux.org/packages/activitywatch-bin/"},
{"title": "nixpkgs", "description": ", for NixOS",
"url": "https://search.nixos.org/packages?query=activitywatch&show=activitywatch"},
],
"macOS": [
{"title": "Homebrew", "url": "https://formulae.brew.sh/cask/activitywatch"},
],
}

# Android has no GitHub release assets; it's always just the Play Store link.
ANDROID = {
"name": "Android",
"assets": [
{"title": "Play Store", "description": " (beta)",
"url": "https://play.google.com/store/apps/details?id=net.activitywatch.android"},
],
}

if __name__ == "__main__":
r = requests.get(
"https://api.github.com/repos/ActivityWatch/activitywatch/releases"
)
data = r.json()
# Per platform: (asset-name regex, button title, extra description).
# description may be a callable taking the matched asset.
PLATFORM_SPECS = {
"Windows": [
(r"windows.*setup\.exe$", "Installer", " (recommended)"),
(r"windows.*\.zip$", ".zip", ""),
],
"Linux": [
(r"linux.*x86_64\.zip$", ".zip", ""),
(r"\.AppImage$", ".AppImage", ""),
(r"\.deb$", ".deb", ""),
],
"macOS": [
(r"macos.*\.dmg$", ".dmg", lambda a: " (recommended)" + _arch(a)),
(r"macos.*\.zip$", ".zip", lambda a: _arch(a)),
],
}

# Filter away prereleases
data = filter(lambda r: not r["prerelease"] and not r["draft"], data)

# Filter away very recent releases (<1h old)
data = filter(lambda r: is_older(r["created_at"], timedelta(hours=1)), data)
def _arch(asset: dict) -> str:
name = asset["name"].lower()
if "arm64" in name or "aarch64" in name:
return " (Apple Silicon)"
if "x86_64" in name or "amd64" in name:
return " (Intel)"
return ""

data = sorted(data, key=lambda r: r["created_at"])
latest_release = data[-1]["tag_name"]
print(f"Latest release: {latest_release}")

p = Path("_data/downloads.yml")
def _pick(assets: list, pattern: str):
"""First asset matching pattern, preferring the plain (non-tauri) build."""
cands = [a for a in assets if re.search(pattern, a["name"], re.I)]
cands.sort(key=lambda a: ("tauri" in a["name"].lower(), len(a["name"])))
return cands[0] if cands else None


def platforms_for(release: dict, include_packages: bool) -> list:
assets = release["assets"]
platforms = []
for name, specs in PLATFORM_SPECS.items():
entries = []
for pattern, title, desc in specs:
a = _pick(assets, pattern)
if not a:
continue
entry = {"title": title, "url": a["browser_download_url"]}
d = desc(a) if callable(desc) else desc
if d:
entry["description"] = d
entries.append(entry)
if include_packages:
entries += PACKAGE_LINKS.get(name, [])
if entries:
platforms.append({"name": name, "assets": entries})
return platforms


def build_block(release: dict, include_packages: bool, with_android: bool) -> dict:
platforms = platforms_for(release, include_packages)
if with_android:
platforms.append(ANDROID)
return {"version": release["tag_name"], "platforms": platforms}


with open(p, "r") as f:
orig = f.read()
new = re.sub(r"v[0-9.]+[abrc]*[0-9]*", latest_release, orig)
def is_older(dt: str, td: timedelta) -> bool:
created = datetime.fromisoformat(dt.replace("Z", "+00:00"))
return created < datetime.now(tz=timezone.utc) - td

if new == orig:
print("No change")

if __name__ == "__main__":
headers = {"Accept": "application/vnd.github+json"}
token = os.getenv("GITHUB_TOKEN")
if token:
headers["Authorization"] = f"Bearer {token}"
resp = requests.get(API, headers=headers, timeout=30)
resp.raise_for_status() # fail loudly on rate-limit/auth/5xx, don't parse an error body
releases = resp.json()
if not isinstance(releases, list):
raise SystemExit(f"Unexpected GitHub API response (not a release list): {releases}")

# Ignore drafts and anything <1h old (assets may still be uploading).
releases = [r for r in releases
if not r["draft"] and is_older(r["created_at"], timedelta(hours=1))]
releases.sort(key=lambda r: r["created_at"])

stable = [r for r in releases if not r["prerelease"]]
if not stable:
raise SystemExit("No stable release found")
latest_stable = stable[-1]

data = {"stable": build_block(latest_stable, include_packages=True, with_android=True)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Nested Downloads Break Consumers

When this script writes only stable and prerelease, the existing homepage schema and download-button include still read site.data.downloads.version and site.data.downloads.platforms. After regeneration, those old keys are missing, so the JSON-LD loses its version/download entries and the site-wide download CTA can fail when it tries to filter a null platform list.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e4749b4. Updated _includes/download-url.js (download button) and _includes/schema-index.html (JSON-LD) to read site.data.downloads.stable.*. Verified no flat downloads.version/downloads.platforms references remain anywhere in the repo.


# Surface the latest pre-release only if it's newer than the latest stable.
newer_pre = [r for r in releases
if r["prerelease"] and r["created_at"] > latest_stable["created_at"]]
if newer_pre:
latest_pre = newer_pre[-1]
data["prerelease"] = build_block(latest_pre, include_packages=False, with_android=False)
print(f"Stable: {latest_stable['tag_name']}; newer pre-release: {latest_pre['tag_name']}")
else:
with open(p, "w") as f:
f.write(new)
print(f"Wrote to {p}")
print(f"Stable: {latest_stable['tag_name']} (no newer pre-release)")

p = Path("_data/downloads.yml")
header = "# Generated by scripts/update-downloads.py — do not edit by hand.\n"
p.write_text(header + yaml.safe_dump(data, sort_keys=False, allow_unicode=True))
print(f"Wrote {p}")