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
21 changes: 19 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,36 @@ jobs:
timeout-minutes: 5
permissions:
contents: write
concurrency:
group: fontara-release-assets-${{ github.ref_name }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 24.x
- name: Download the packages tested by verification
uses: actions/download-artifact@v8
with:
name: fontara-verified-packages
path: build
- name: Prepare versioned downloads, integrity checks, and release notes
env:
GH_TOKEN: ${{ github.token }}
run: node tasks/prepare-release-assets.js --tag "$GITHUB_REF_NAME" --commit "$GITHUB_SHA" --directory build --output release-assets --check-existing
- name: Publish GitHub release
uses: softprops/action-gh-release@v3
with:
fail_on_unmatched_files: true
overwrite_files: false
name: FontARA ${{ github.ref_name }}
body_path: release-assets/release-body.md
files: |
build/*-prod.zip
build/firefox-mv3-source-*.zip
release-assets/*.zip
release-assets/build-info.json
release-assets/SHA256SUMS

chrome-store:
name: Publish to Chrome Web Store
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/store-assets-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Verify and publish signed store assets

on:
workflow_call:
inputs:
release_tag:
type: string
required: true

permissions:
contents: read

jobs:
collect:
if: github.repository == 'mimalef70/fontara' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
actions: read
outputs:
complete: ${{ steps.release.outputs.complete }}
release_tag: ${{ steps.release.outputs.tag }}
publisher_sha: ${{ steps.publisher.outputs.sha }}
steps:
- uses: actions/checkout@v7
with:
ref: master
persist-credentials: false
- id: publisher
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- uses: actions/setup-node@v7
with:
node-version: 24.x
- name: Resolve original verified release
id: release
env:
GITHUB_TOKEN: ${{ github.token }}
STORE_RELEASE_TAG: ${{ inputs.release_tag }}
run: node tasks/sync-store-assets.js --action prepare --metadata "$RUNNER_TEMP/store-release.json"
- name: Download exact packages from original successful CI
if: steps.release.outputs.complete != 'true'
uses: actions/download-artifact@v8
with:
artifact-ids: ${{ steps.release.outputs.artifact_id }}
run-id: ${{ steps.release.outputs.run_id }}
github-token: ${{ github.token }}
path: verified-packages
merge-multiple: true
- name: Verify official store packages without store credentials
if: steps.release.outputs.complete != 'true'
env:
GITHUB_TOKEN: ${{ github.token }}
run: node tasks/sync-store-assets.js --action collect --metadata "$RUNNER_TEMP/store-release.json" --directory verified-packages --output store-assets
- name: Upload verified store files and result
if: steps.release.outputs.complete != 'true'
uses: actions/upload-artifact@v7
with:
name: verified-store-assets-${{ inputs.release_tag }}
path: store-assets/
if-no-files-found: error
retention-days: 7

publish:
needs: collect
if: needs.collect.outputs.complete != 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: fontara-release-assets-${{ needs.collect.outputs.release_tag }}
cancel-in-progress: false
permissions:
contents: write
actions: read
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.collect.outputs.publisher_sha }}
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 24.x
- uses: actions/download-artifact@v8
with:
name: verified-store-assets-${{ inputs.release_tag }}
path: store-assets
- name: Add immutable signed assets and refresh store availability
env:
GH_TOKEN: ${{ github.token }}
run: node tasks/sync-store-assets.js --action publish --directory store-assets
58 changes: 58 additions & 0 deletions .github/workflows/store-assets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Store Release Assets

on:
workflow_run:
workflows: [Release, Firefox Release]
types: [completed]
schedule:
- cron: '17 */6 * * *'
workflow_dispatch:
inputs:
release_tag:
description: Existing stable release tag (blank checks recent releases)
type: string
default: ''

permissions:
contents: read

jobs:
plan:
if: >-
github.repository == 'mimalef70/fontara' && github.ref == 'refs/heads/master' &&
(github.event_name != 'workflow_run' ||
(github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.repository.id == 141708904 &&
github.event.workflow_run.head_repository.id == 141708904))
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
tags: ${{ steps.queue.outputs.tags }}
steps:
- uses: actions/checkout@v7
with:
ref: master
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 24.x
- id: queue
env:
GITHUB_TOKEN: ${{ github.token }}
STORE_RELEASE_TAG: ${{ inputs.release_tag || (github.event.workflow_run.name == 'Release' && startsWith(github.event.workflow_run.head_branch, 'v') && github.event.workflow_run.head_branch) || '' }}
run: node tasks/sync-store-assets.js --action plan

sync:
needs: plan
if: needs.plan.outputs.tags != '[]'
strategy:
fail-fast: false
max-parallel: 3
matrix:
tag: ${{ fromJSON(needs.plan.outputs.tags) }}
uses: ./.github/workflows/store-assets-release.yml
with:
release_tag: ${{ matrix.tag }}
permissions:
contents: write
actions: read
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ This project follows a practical release-note format:

## Unreleased

- Added versioned browser downloads, SHA-256 manifests, build metadata, and
installation guidance to GitHub releases without rebuilding verified packages.
- Added automatic collection of matching store-signed Chrome CRX3 and Firefox
XPI downloads, with signature/content checks and retries while store approval
is pending. Existing release packages remain immutable.

- Fixed ChatGPT Canvas font application when page styles contain font-size
variables or Unicode bidi utilities. Protected authored fonts and code editors,
kept titlebars out of smart RTL without clipping their menus, and restored
Expand Down
84 changes: 84 additions & 0 deletions docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,88 @@ Release archives are written to `build/*-prod.zip`. When Firefox is part of the
build, the Firefox source review package is also written to
`build/firefox-mv3-source-<version>.zip`.

## Public release downloads

Every new release publishes a browser download table, installation links, the
tag's changelog, and versioned filenames. The publishing job copies the exact
verified archives; it never rebuilds extension code. Original filenames remain
available because store submission and provenance checks depend on them.

| Download | Purpose |
| --- | --- |
| `fontara-<version>-chrome.zip` | Chrome unpacked/developer package. |
| `fontara-<version>-edge.zip`, `-brave.zip`, `-opera.zip` | Corresponding Chromium packages; these do not imply a separate store listing. |
| `fontara-<version>-firefox.zip` | Unsigned Firefox developer/review package; ordinary Firefox installation uses the signed XPI or AMO. |
| `fontara-<version>-source.zip` | Reproducible reviewer source and build instructions. |
| `fontara-<version>-safari-experimental.zip` | Experimental web-extension resources; not an installable Safari app. |
| `build-info.json` | Release tag/commit, declared toolchain, and archive hashes. |
| `SHA256SUMS` | Integrity manifest for the original ZIPs, versioned copies, and build information. |

For a normal installation, prefer the
[Chrome Web Store](https://chromewebstore.google.com/detail/dcjdhicepiklefpimapdkbaeoocniemc)
or [Mozilla Add-ons](https://addons.mozilla.org/firefox/addon/fontara-font-changer/).
For a Chromium development installation, extract the matching ZIP and choose
**Load unpacked** on the browser's extensions page with Developer mode enabled.
The Firefox source ZIP is source code, not the installable extension.

### Store-signed CRX and XPI

The separate `Store Release Assets` workflow obtains already published store
packages after release workflows complete, periodically, or on manual request.
It does not upload a new store version, generate a signing key, or re-sign an
extension. This preserves the existing extension identity and store update path.

Only packages matching the requested version and the verified release contents
are attached. Chrome CRX3 signatures and extension identity are checked; Firefox
downloads are checked against the AMO hash, add-on identity, and signing files.
Package contents are compared with the original release ZIPs, allowing only
the explicitly checked store metadata and signature additions. A store still
serving an older version is reported as pending. The next scheduled run retries;
it never labels an old CRX as a new release.

To refresh a published release manually, use:

```sh
gh workflow run store-assets.yml --ref master -f release_tag=v5.1.0
```

Leaving `release_tag` empty scans recent eligible stable releases and the latest
release, so an older release still awaiting store approval is revisited when a
newer tag appears. The scan is bounded to 50 releases and a 30-day window, plus
the latest eligible release. The workflow runs after successful release workflows
and every six hours. Collection uses a
read-only token; a separate job can attach verified files to GitHub Releases.
Neither job needs a Chrome or Mozilla account credential. Pending store approval
does not fail the workflow. Invalid signatures, unexpected contents, and network
errors do fail it rather than being mislabeled as approval delays.

The signed files use `fontara-<version>-chrome.crx` and
`fontara-<version>-firefox.xpi`. Their separate checksum manifest and managed
release-note section can be updated as store approvals complete; original
release ZIPs and their checksums are preserved. Existing signed assets with
different bytes cause an error rather than being replaced.

Incomplete collections require the original CI artifact, retained for 30 days;
an expired artifact stops collection rather than permitting a rebuild. Once both
signed packages are collected, a receipt binds their hashes, the original ZIP
hashes, checksums and tag commit, so completed releases do not depend on artifact
retention. `store-assets.json` records those results and
`SHA256SUMS-store.txt` covers the signed downloads.

A CRX download is useful for supported Chromium distribution and archival use;
it is not a promise of ordinary drag-and-drop installation on every platform.
Chrome restricts external installations on Windows and macOS. Use the store for
normal installations and follow Google's
[installation policy](https://developer.chrome.com/docs/extensions/how-to/distribute/install-extensions)
and [CRX hosting guidance](https://developer.chrome.com/docs/extensions/how-to/distribute/host-on-linux).
Firefox users can choose **Install Add-on From File** in `about:addons` for the
Mozilla-signed XPI. Browser restrictions and review decisions still apply.

The layout is inspired by
[AdGuard's browser-specific release packages](https://github.com/AdguardTeam/AdguardBrowserExtension/releases/tag/v5.5.3.3).
FontARA publishes only its own supported targets; it does not add unsupported
MV2 variants or claim a Safari app distribution.

## Firefox Review Package

```sh
Expand Down Expand Up @@ -126,6 +208,8 @@ Known warning category:
| Verify release packages | reusable workflow call | Runs `pnpm verify`, uploads production/source ZIPs, then runs stable/minimum browser suites and production smoke tests. |
| Browser Tests | manual, nightly | Browser matrix across Chrome and Firefox channels. |
| Release | `v*` tags, manual | Verifies and publishes the exact packages to GitHub Releases, then submits Chrome to the Web Store. Manual status and rollout operations are also available. |
| Firefox Release | successful tag Release, manual | Submits the verified Firefox package and reviewer source through AMO. |
| Store Release Assets | successful release workflows, every six hours, manual | Revisits eligible releases and attaches matching official CRX/XPI packages as store approvals finish. |

[verify.yml](../.github/workflows/verify.yml) is the common gate for
[ci.yml](../.github/workflows/ci.yml) and
Expand Down
Loading
Loading