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
99 changes: 83 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,69 @@
name: CI

# Only PRs are checked: `main` requires these checks with "branch must be up
# to date", so a merged PR was already tested against the exact tree that
# lands on main and re-running CI on push would only repeat that work.
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
branches: [main]
workflow_dispatch:

concurrency:
group: ci-${{ github.ref }}
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
outputs:
code: ${{ steps.filter.outputs.code }}
native: ${{ steps.filter.outputs.native }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Classify changed files
id: filter
shell: bash
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_REF: ${{ github.head_ref }}
run: |
if [ "${{ github.event_name }}" != "pull_request" ]; then
echo "code=true" >> "$GITHUB_OUTPUT"
echo "native=true" >> "$GITHUB_OUTPUT"
exit 0
fi

files=$(git diff --name-only "$BASE_SHA"...HEAD)
echo "Changed files:"; echo "$files"

# Anything besides docs needs lint / typecheck / tests.
code=false
if echo "$files" | grep -qvE '(\.md$|^docs/|^LICENSE$|^\.github/(ISSUE_TEMPLATE/|PULL_REQUEST_TEMPLATE))'; then
code=true
fi

# Native builds only matter when the Rust side, the sidecar, the
# dependency set or the workflows change. release-please PRs only
# bump versions, and the release build runs right after they merge.
native=false
if echo "$files" | grep -qE '^(src-tauri/|scripts/|package(-lock)?\.json$|\.github/workflows/)'; then
native=true
fi
case "$HEAD_REF" in release-please--*) native=false ;; esac

echo "code=$code" >> "$GITHUB_OUTPUT"
echo "native=$native" >> "$GITHUB_OUTPUT"

# Skipped jobs still report their (required) check as successful.
quality:
name: Quality Checks
needs: changes
if: ${{ needs.changes.outputs.code == 'true' }}
runs-on: macos-14
steps:
- name: Checkout
Expand All @@ -34,24 +84,22 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri -> target
key: quality
shared-key: quality

- name: Cache aria2 sidecar
uses: actions/cache@v6
with:
path: src-tauri/binaries
key: aria2-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('scripts/prepare-aria2.mjs') }}
key: aria2-aarch64-apple-darwin-${{ hashFiles('scripts/prepare-aria2.mjs') }}

- name: Install npm dependencies
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm ci

- name: Lint frontend
run: npm run lint

- name: Type-check frontend
run: npm run typecheck
- name: Type-check and build frontend
run: npm run build

- name: Check Rust formatting
working-directory: src-tauri
Expand All @@ -63,7 +111,11 @@ jobs:

build:
name: Build (${{ matrix.label }})
needs: quality
needs: [changes, quality]
# A skipped matrix job would report an unexpanded check name, which never
# satisfies the required checks; so the job always runs and its steps are
# skipped instead (on a cheap Linux runner) when no native build is needed.
if: ${{ !failure() && !cancelled() }}
strategy:
fail-fast: false
matrix:
Expand All @@ -72,48 +124,63 @@ jobs:
os: macos-14
rust-target: aarch64-apple-darwin
tauri-args: --target aarch64-apple-darwin
# Cross-compiled on Apple Silicon: far faster than the Intel runners.
- label: macos-x64
os: macos-15-intel
os: macos-14
rust-target: x86_64-apple-darwin
tauri-args: --target x86_64-apple-darwin
- label: windows-x64
os: windows-latest
rust-target: x86_64-pc-windows-msvc
tauri-args: ""

runs-on: ${{ matrix.os }}
runs-on: ${{ needs.changes.outputs.native == 'true' && matrix.os || 'ubuntu-latest' }}

steps:
- name: Skip (no native changes)
if: ${{ needs.changes.outputs.native != 'true' }}
run: echo "No changes affecting native builds; skipping."

- name: Checkout
if: ${{ needs.changes.outputs.native == 'true' }}
uses: actions/checkout@v6

- name: Setup Node.js
if: ${{ needs.changes.outputs.native == 'true' }}
uses: actions/setup-node@v6
with:
node-version: 24
cache: npm

- name: Setup Rust
if: ${{ needs.changes.outputs.native == 'true' }}
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust-target }}

# Shared with release.yml. Only runs on main (releases) save, so PR
# caches don't evict main's from the repo's 10 GB cache quota.
- name: Cache Cargo registry & build
if: ${{ needs.changes.outputs.native == 'true' }}
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri -> target
key: ${{ matrix.label }}
shared-key: ${{ matrix.label }}
save-if: ${{ github.ref == 'refs/heads/main' }}

- name: Cache aria2 sidecar
if: ${{ needs.changes.outputs.native == 'true' }}
uses: actions/cache@v6
with:
path: src-tauri/binaries
key: aria2-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('scripts/prepare-aria2.mjs') }}
key: aria2-${{ matrix.rust-target }}-${{ hashFiles('scripts/prepare-aria2.mjs') }}

- name: Install npm dependencies
if: ${{ needs.changes.outputs.native == 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARIA2_TARGET_TRIPLE: ${{ matrix.rust-target }}
run: npm ci

- name: Build Tauri app
if: ${{ needs.changes.outputs.native == 'true' }}
run: npx tauri build ${{ matrix.tauri-args }}
15 changes: 12 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ jobs:
os: macos-14
rust-target: aarch64-apple-darwin
tauri-args: --target aarch64-apple-darwin
# Cross-compiled on Apple Silicon: far faster than the Intel runners.
- label: macos-x64
os: macos-15-intel
os: macos-14
rust-target: x86_64-apple-darwin
tauri-args: --target x86_64-apple-darwin
- label: windows-x64
Expand All @@ -65,15 +66,23 @@ jobs:
with:
targets: ${{ matrix.rust-target }}

# Same keys as ci.yml: releases run on main, so the caches they save are
# the ones PR builds restore.
- name: Cache Cargo registry & build
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri -> target
key: ${{ matrix.label }}-release
shared-key: ${{ matrix.label }}

- name: Cache aria2 sidecar
uses: actions/cache@v6
with:
path: src-tauri/binaries
key: aria2-${{ matrix.rust-target }}-${{ hashFiles('scripts/prepare-aria2.mjs') }}

- name: Install npm dependencies
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARIA2_TARGET_TRIPLE: ${{ matrix.rust-target }}
run: npm ci

- name: Resolve release tag
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

## [0.1.1](https://github.com/mogurt/ComfyDownloader/compare/comfy-downloader-v0.1.0...comfy-downloader-v0.1.1) (2026-09-28)
## [0.1.1](https://github.com/mogurt/ComfyDownloader/compare/v0.1.0...comfy-downloader-v0.1.1) (2026-09-28)


### Bug Fixes
Expand Down
1 change: 1 addition & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
}
],
"include-v-in-tag": true,
"include-component-in-tag": false,
"pull-request-title-pattern": "chore${scope}: release${component} ${version}",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true
Expand Down
24 changes: 22 additions & 2 deletions scripts/prepare-aria2.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ function getFallbackTargetTriple() {
}

async function getTargetTriple() {
// Set when building for another target than the host (e.g. x86_64 macOS
// bundles built on an Apple Silicon CI runner).
if (process.env.ARIA2_TARGET_TRIPLE) {
return process.env.ARIA2_TARGET_TRIPLE;
}

try {
const triple = await execFileText("rustc", ["--print", "host-tuple"]);
return triple || getFallbackTargetTriple();
Expand Down Expand Up @@ -185,6 +191,13 @@ function isSystemLibrary(path) {
return path.startsWith("/usr/lib/") || path.startsWith("/System/Library/");
}

async function assertArchitecture(binary, arch) {
const archs = await execFileText("lipo", ["-archs", binary]);
if (archs !== arch) {
throw new Error(`aria2c was built for "${archs}", expected "${arch}"`);
}
}

async function assertOnlySystemLibraries(binary) {
const output = await execFileText("otool", ["-L", binary]);
const libraries = output
Expand All @@ -202,7 +215,9 @@ async function assertOnlySystemLibraries(binary) {
* aria2 publishes no macOS binaries, so build the pinned release from source,
* linking only against system frameworks (AppleTLS) to keep it portable.
*/
async function prepareMacBinary(targetBinary) {
async function prepareMacBinary(targetBinary, targetTriple) {
const arch = targetTriple.startsWith("x86_64") ? "x86_64" : "arm64";

await withTempDir("comfy-aria2-macos-", async (tempDir) => {
const tarball = join(tempDir, ARIA2_SOURCE.name);
await downloadVerified(ARIA2_SOURCE, tarball);
Expand All @@ -215,6 +230,10 @@ async function prepareMacBinary(targetBinary) {
const env = {
...process.env,
MACOSX_DEPLOYMENT_TARGET: MACOS_DEPLOYMENT_TARGET,
// Build for the target arch. When it differs from the host, configure's
// test programs run under Rosetta, so no cross-compile setup is needed.
CC: `clang -arch ${arch}`,
CXX: `clang++ -arch ${arch}`,
PKG_CONFIG_PATH: "",
PKG_CONFIG_LIBDIR: emptyPkgConfigDir,
};
Expand Down Expand Up @@ -245,6 +264,7 @@ async function prepareMacBinary(targetBinary) {

const builtBinary = join(sourceDir, "src", "aria2c");
await runCommand("strip", [builtBinary]);
await assertArchitecture(builtBinary, arch);
await assertOnlySystemLibraries(builtBinary);

await copyFile(builtBinary, targetBinary);
Expand Down Expand Up @@ -281,7 +301,7 @@ async function main() {
}

if (process.platform === "darwin") {
await prepareMacBinary(targetBinary);
await prepareMacBinary(targetBinary, targetTriple);
} else if (process.platform === "win32" && process.arch === "x64") {
await prepareWindowsBinary(targetBinary);
} else {
Expand Down
Loading