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
180 changes: 175 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ on:
- "v*"

jobs:
build:
build-python:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout repository
Expand Down Expand Up @@ -38,14 +36,186 @@ jobs:
)
print("Validated release tag {} against package version {}".format(tag, __version__))

- name: Build distributions
- name: Run unit tests
run: python -m unittest discover -v

- name: Build Python distributions
run: |
python -m pip install --upgrade pip
python -m pip install build twine
python -m build
python -m twine check dist/*

- name: Upload Python distribution artifacts
uses: actions/upload-artifact@v4
with:
name: python-dist
path: dist/*

build-windows-portable:
runs-on: windows-latest

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Validate tag matches package version
shell: python
run: |
import os
import pathlib
import sys

sys.path.insert(0, str(pathlib.Path("src").resolve()))
from brave_updater import __version__

tag = os.environ["GITHUB_REF_NAME"]
expected = tag[1:] if tag.startswith("v") else tag
if __version__ != expected:
raise SystemExit(
"Release tag {} does not match brave_updater.__version__ {}".format(tag, __version__)
)
print("Validated release tag {} against package version {}".format(tag, __version__))

- name: Build Windows portable executable
run: |
python -m pip install --upgrade pip
python -m pip install pyinstaller
python -m PyInstaller --noconfirm --clean --onefile --name brave-updater --paths src packaging/windows/entrypoint.py

- name: Package Windows portable zip
shell: python
run: |
import pathlib
import zipfile

output_dir = pathlib.Path("dist/windows")
output_dir.mkdir(parents=True, exist_ok=True)
zip_path = output_dir / "brave-updater-windows-x64.zip"

with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
archive.write("dist/brave-updater.exe", "brave-updater.exe")
archive.write("LICENSE", "LICENSE")
archive.write("README.md", "README.md")

print("Created {}".format(zip_path))

- name: Upload Windows portable artifact
uses: actions/upload-artifact@v4
with:
name: windows-portable
path: dist/windows/brave-updater-windows-x64.zip

publish-pypi:
needs: build-python
runs-on: ubuntu-latest
permissions:
id-token: write

steps:
- name: Download Python distribution artifacts
uses: actions/download-artifact@v4
with:
name: python-dist
path: dist

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist

publish-release:
needs: [build-python, build-windows-portable]
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Download Python distribution artifacts
uses: actions/download-artifact@v4
with:
name: python-dist
path: release-assets/python

- name: Download Windows portable artifact
uses: actions/download-artifact@v4
with:
name: windows-portable
path: release-assets/windows

- name: Generate SHA256SUMS
shell: python
run: |
import hashlib
import pathlib

root = pathlib.Path("release-assets")
outputs = sorted(path for path in root.rglob("*") if path.is_file())
lines = []
for path in outputs:
digest = hashlib.sha256(path.read_bytes()).hexdigest()
lines.append("{} {}".format(digest, path.relative_to(root)))
sums_path = root / "SHA256SUMS.txt"
sums_path.write_text("\n".join(lines) + "\n")
print("Wrote {}".format(sums_path))

- name: Render Homebrew and Scoop files
run: |
python scripts/render_distribution_files.py \
--version "${GITHUB_REF_NAME}" \
--repo "HackEAC/braver-tz" \
--source-archive "release-assets/python/brave_updater-${GITHUB_REF_NAME#v}.tar.gz" \
--windows-zip "release-assets/windows/brave-updater-windows-x64.zip" \
--output-dir "release-assets/manifests"

- name: Upload release assets
uses: softprops/action-gh-release@v2
with:
files: dist/*
files: |
release-assets/python/*
release-assets/windows/*
release-assets/SHA256SUMS.txt
release-assets/manifests/homebrew/brave-updater.rb
release-assets/manifests/scoop/brave-updater.json

dispatch-packaging-sync:
needs: publish-release
runs-on: ubuntu-latest

steps:
- name: Dispatch tap and bucket sync workflows
shell: bash
env:
PACKAGING_SYNC_TOKEN: ${{ secrets.PACKAGING_SYNC_TOKEN }}
run: |
if [ -z "${PACKAGING_SYNC_TOKEN}" ]; then
echo "PACKAGING_SYNC_TOKEN is not configured; skipping tap and bucket dispatch."
exit 0
fi

curl --fail-with-body --silent --show-error \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${PACKAGING_SYNC_TOKEN}" \
https://api.github.com/repos/HackEAC/scoop-bucket/dispatches \
-d "{\"event_type\":\"sync-from-release\",\"client_payload\":{\"source_repository\":\"${GITHUB_REPOSITORY}\",\"tag\":\"${GITHUB_REF_NAME}\"}}"

curl --fail-with-body --silent --show-error \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${PACKAGING_SYNC_TOKEN}" \
https://api.github.com/repos/HackEAC/homebrew-brave-updater/dispatches \
-d "{\"event_type\":\"sync-from-release\",\"client_payload\":{\"source_repository\":\"${GITHUB_REPOSITORY}\",\"tag\":\"${GITHUB_REF_NAME}\"}}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ __pycache__/
build/
dist/
test-downloads/
*.spec
95 changes: 19 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,14 @@

`brave-updater` is an unofficial cross-platform CLI for checking, downloading, and updating Brave Browser from Brave-controlled release sources.

This project is intended for environments where the usual Brave installation flow is inconvenient or blocked, but GitHub-hosted Brave releases are still reachable.
It is meant for situations where the normal Brave install flow is inconvenient, blocked, or harder to automate.

Important:
- This project is not affiliated with or endorsed by Brave Software.
- It does not rehost Brave binaries.
- It downloads Brave installers directly from Brave-controlled GitHub release assets at runtime.
- Homebrew already ships the official macOS `brave-browser` cask. This project is a separate updater/downloader CLI, not a replacement for that cask.

## Current status

- Packaged Python CLI with a `src/` layout and console entrypoint: `brave-updater`
- Backward-compatible wrapper retained: `python3 braver.py ...`
- True updater behavior:
- Detects installed Brave version when possible
- Compares it to the latest stable release
- Skips update work when the installed version is already current
- Cross-platform installer selection:
- macOS: `.dmg` / `.pkg`
- Windows: `.exe`
- Linux: `.deb` / `.rpm` on Debian-like and RHEL-like systems
- Safer downloads:
- SHA256 verification
- redirect host validation
- temp-file download + atomic replace
- safe recovery when resume requests are ignored

## Repository transfer

The canonical repository is now `HackEAC/braver-tz`.

Post-transfer notes:
- keep `origin` pointed at `git@github.com:HackEAC/braver-tz.git`
- do not recreate the previous repository path after the transfer, because that would break GitHub's redirect
- follow the post-transfer checklist in [`docs/repository-transfer.md`](docs/repository-transfer.md)

## Installation
- It does not bundle or mirror Brave binaries.
- It downloads Brave installers directly from Brave-controlled release assets at runtime.

## Install

### From source

Expand All @@ -47,30 +19,24 @@ cd braver-tz
python3 -m pip install .
```

After installation:

```bash
brave-updater check
```

### Legacy wrapper

You can still use the old entrypoint directly from the repo:
The original entrypoint still works from the repository:

```bash
python3 braver.py --print-only
python3 braver.py --install
```

### Homebrew tap
### Release channels

The intended tap command once the tap is published is:
This repository now includes release packaging for:

```bash
brew install HackEAC/brave-updater/brave-updater
```
- `PyPI` / `pipx`
- `Homebrew`
- Windows portable releases for `Scoop` and `WinGet`

The starter formula and tap notes live under [`packaging/homebrew`](packaging/homebrew).
Release-channel notes and packaging assets live in [packaging/README.md](/Users/maotora/Projects/brave-updater/packaging/README.md).

## Usage

Expand Down Expand Up @@ -101,24 +67,13 @@ brave-updater update --yes
brave-updater install /path/to/Brave-Browser-universal.dmg
```

### Source strategy
## Platform notes

`brave-updater` currently defaults to GitHub Releases because that is the supported product direction for this project.
- macOS: `.dmg` and `.pkg` installers are supported
- Windows: `.exe` installers are supported
- Linux: `.deb` and `.rpm` installers are supported on Debian-like and RHEL-like systems

Available source modes:
- `github`
- `official`
- `auto`

At the moment, `official` and `auto` resolve through Brave's official GitHub release feed. The code is structured so distinct official non-GitHub providers can be added later without redesigning the CLI.

### Linux support

Linux installs are currently supported on:
- Debian-like distributions
- RHEL-like distributions

Arch-like and other unsupported Linux families fail fast with a clear error instead of downloading the wrong package format.
Unsupported Linux families fail fast instead of downloading the wrong package type.

## Development

Expand All @@ -128,25 +83,13 @@ Run the test suite:
python3 -m unittest discover -v
```

Build distributions:
Build Python distributions:

```bash
python3 -m pip install build
python3 -m build
```

## CI and release flow

- CI runs deterministic unit tests on Linux, macOS, and Windows.
- A scheduled smoke workflow hits the live Brave release endpoint with read-only checks.
- Tagging `v*` builds and uploads sdist/wheel artifacts to GitHub Releases.

## Legal and policy notes

- This project should avoid Brave logos and branded artwork unless you have explicit permission.
- The project should continue to present itself as unofficial and non-affiliated.
- The project should continue to download installers from Brave-controlled endpoints rather than mirror them.

## License

MIT. See [`LICENSE`](LICENSE).
MIT. See [LICENSE](/Users/maotora/Projects/brave-updater/LICENSE).
41 changes: 41 additions & 0 deletions packaging/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Packaging

This directory contains packaging assets and release-channel notes for `brave-updater`.

## Current channels

- `PyPI`
- `Homebrew`
- `Windows portable zip`
- `Scoop`
- `WinGet` submission notes

Linux users can use Homebrew on Linux or Python package managers such as `pipx`, so there is no separate distro-specific Linux packaging here right now.

## Layout

- `homebrew/`: tap template and notes
- `pypi/`: PyPI release notes
- `scoop/`: Scoop manifest template and notes
- `winget/`: WinGet submission notes
- `windows/`: Windows portable build entrypoint and notes

## Release outputs

On each tagged release, the release workflow should publish:

- `brave_updater-<version>.tar.gz`
- `brave_updater-<version>-py3-none-any.whl`
- `brave-updater-windows-x64.zip`
- `SHA256SUMS.txt`
- rendered Homebrew formula
- rendered Scoop manifest

## Cross-repo sync

The Homebrew tap and Scoop bucket live in separate repositories:

- `HackEAC/homebrew-brave-updater`
- `HackEAC/scoop-bucket`

`HackEAC/braver-tz` can notify both repos immediately after a tagged release by using the `PACKAGING_SYNC_TOKEN` repository secret. Without that secret, the external repos still poll the latest release on a 6-hour schedule.
Loading