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
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,14 @@ jobs:
- run: npm ci
- run: npm test
- run: npm run test:rust

rust-check:
needs: lint
strategy:
matrix:
os: [windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --manifest-path src-tauri/Cargo.toml
478 changes: 239 additions & 239 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zinnia",
"version": "0.2.0",
"version": "0.2.1",
"private": true,
"description": "Cross-platform 7z GUI built with Tauri.",
"type": "module",
Expand Down Expand Up @@ -32,6 +32,9 @@
"dist:clean-all": "node scripts/dist-tools.js clean-all",
"wc": "npm run win-compiler:x64",
"vi": "node scripts/vi.js",
"b": "git fetch && git switch beta && npm run vi",
"r": "git switch main && npm run vi && npm run gitprune:force",
"u": "npm update && npm run release:prepare && npm run format && npm run test:all",
"win-compiler:x64": "powershell -NoProfile -ExecutionPolicy Bypass -NoExit -Command \"& 'C:\\Program Files\\Microsoft Visual Studio\\18\\Community\\Common7\\Tools\\Launch-VsDevShell.ps1' -SkipAutomaticLocation",
"win-compiler:arm64": "powershell -NoProfile -ExecutionPolicy Bypass -NoExit -Command \"& 'C:\\Program Files\\Microsoft Visual Studio\\18\\Community\\Common7\\Tools\\Launch-VsDevShell.ps1' -SkipAutomaticLocation -Arch arm64 -HostArch amd64\"",
"build:win:x64": "npm run sync-version && npm run licenses && npm run prepare:7z && dotenv -e .env -- tauri build --target x86_64-pc-windows-msvc --bundles nsis",
Expand All @@ -46,16 +49,19 @@
"flatpak:bundle": "flatpak-builder --repo=flatpak-repo --force-clean flatpak-build run.rosie.zinnia.yml && flatpak build-bundle flatpak-repo dist/Zinnia-Linux.flatpak run.rosie.zinnia",
"flatpak:clean": "rm -rf flatpak-build flatpak-repo",
"release:prepare": "npm i && npm run sync-version && cargo check --manifest-path src-tauri/Cargo.toml -q && npm run dist:clean-release-artifacts && node scripts/update-metainfo.js",
"prerelease:prepare": "node scripts/release-warning.js && git fetch origin && git reset --hard @{u} && git clean -fd",
"release:finalize": "dotenv -e .env -- node scripts/post-release-assets.js",
"prerelease:prepare": "node scripts/release-warning.js",
"release:finalize": "dotenv -e .env -- node scripts/post-release-assets.js && git fetch origin && git reset --hard @{u} && git clean -fd",
"release:win": "npm run release:prepare && npm run build:win && npm run release:sign:gpg && npm run release:finalize",
"release:mac": "npm run release:prepare && npm run build:mac:universal && npm run build:mac:zip && npm run release:sign:gpg && npm run release:finalize",
"release:mac:ssh": "npm run mac:ssh:keychain && npm run release:mac",
"release:linux": "npm run release:prepare && npm run build:linux && npm run flatpak:clean && npm run flatpak:bundle && npm run release:sign:gpg && npm run release:finalize",
"release:linux:x64": "npm run release:prepare && npm run build:linux:x64 && npm run flatpak:clean && npm run flatpak:bundle && npm run release:sign:gpg && npm run release:finalize",
"release:linux:arm64": "npm run release:prepare && npm run build:linux:arm64 && npm run flatpak:clean && npm run flatpak:bundle && npm run release:sign:gpg && npm run release:finalize",
"release:sign:gpg": "dotenv -e .env -- node scripts/gpg-sign.js",
"setup:deb": "sudo apt update && sudo apt install -y build-essential curl wget file libgtk-3-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev libglib2.0-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev patchelf flatpak flatpak-builder && sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo && sudo flatpak install -y flathub org.gnome.Platform//48 org.gnome.Sdk//48 org.freedesktop.Sdk.Extension.node22//24.08 org.freedesktop.Sdk.Extension.rust-stable//24.08"
"setup:deb": "sudo apt update && sudo apt install -y build-essential curl wget file libgtk-3-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev libglib2.0-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev patchelf flatpak flatpak-builder && sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo && sudo flatpak install -y flathub org.gnome.Platform//48 org.gnome.Sdk//48 org.freedesktop.Sdk.Extension.node22//24.08 org.freedesktop.Sdk.Extension.rust-stable//24.08",
"gitprune": "node scripts/git-prune-local-branches.js",
"gitprune:dry": "node scripts/git-prune-local-branches.js --dry-run",
"gitprune:force": "node scripts/git-prune-local-branches.js --force"
},
"dependencies": {
"@tauri-apps/api": "^2.0.0",
Expand Down
2 changes: 1 addition & 1 deletion run.rosie.zinnia.metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@
<content_rating type="oars-1.1" />
<releases>

<release version="0.2.0" date="2026-03-25"/>
<release version="0.2.1" date="2026-04-01"/>
</releases>
</component>
253 changes: 253 additions & 0 deletions scripts/git-prune-local-branches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";

const SCRIPT_VERSION = "1.0.0";

function runGit(args) {
const result = spawnSync("git", args, {
encoding: "utf8",
stdio: "pipe",
shell: false,
windowsHide: true,
});

const stdout = result.stdout || "";
const stderr = result.stderr || "";
const ok = !result.error && result.status === 0;

return { ok, stdout, stderr, status: result.status, error: result.error };
}

export function parseArgs(argv) {
const args = argv.slice(2);
const options = {
remote: "origin",
dryRun: false,
force: false,
};

for (let i = 0; i < args.length; i++) {
const arg = args[i];
const next = args[i + 1];

if ((arg === "--remote" || arg === "-r") && next) {
options.remote = next;
i++;
continue;
}
if (arg === "--dry-run" || arg === "-n") {
options.dryRun = true;
continue;
}
if (arg === "--force" || arg === "-f") {
options.force = true;
continue;
}
}

return options;
}

function parseLines(value) {
return value
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
}

export function stripRemotePrefix(ref, remote) {
const prefix = `${remote}/`;
if (!ref.startsWith(prefix)) return null;
const name = ref.slice(prefix.length);
if (!name || name === "HEAD") return null;
return name;
}

export function selectBranchesToDelete(
localBranches,
remoteBranches,
currentBranch,
) {
const remoteSet = new Set(remoteBranches);
return localBranches.filter(
(branch) => branch !== currentBranch && !remoteSet.has(branch),
);
}

function ensureRemoteExists(remote) {
const remotes = runGit(["remote"]);
if (!remotes.ok) {
throw new Error(remotes.stderr.trim() || "Failed to list git remotes");
}
const remoteList = parseLines(remotes.stdout);
if (!remoteList.includes(remote)) {
throw new Error(`Remote "${remote}" does not exist in this repository`);
}
}

function getCurrentBranch() {
const result = runGit(["rev-parse", "--abbrev-ref", "HEAD"]);
if (!result.ok) {
throw new Error(result.stderr.trim() || "Failed to detect current branch");
}
return result.stdout.trim();
}

function getLocalBranches() {
const result = runGit([
"for-each-ref",
"--format=%(refname:short)",
"refs/heads",
]);
if (!result.ok) {
throw new Error(result.stderr.trim() || "Failed to list local branches");
}
return parseLines(result.stdout);
}

function getRemoteBranches(remote) {
const result = runGit([
"for-each-ref",
"--format=%(refname:short)",
`refs/remotes/${remote}`,
]);
if (!result.ok) {
throw new Error(
result.stderr.trim() || `Failed to list remote branches for ${remote}`,
);
}
return parseLines(result.stdout)
.map((ref) => stripRemotePrefix(ref, remote))
.filter((name) => typeof name === "string");
}

function fetchPruned(remote) {
const result = runGit(["fetch", remote, "--prune"]);
if (!result.ok) {
throw new Error(
result.stderr.trim() || `Failed to fetch/prune remote ${remote}`,
);
}
}

export function deleteBranches(
branches,
{ force = false, dryRun = false } = {},
) {
const deleted = [];
const skipped = [];
const flag = force ? "-D" : "-d";

for (const branch of branches) {
if (dryRun) {
deleted.push(branch);
continue;
}

const result = runGit(["branch", flag, branch]);
if (result.ok) {
deleted.push(branch);
continue;
}

skipped.push({
branch,
reason:
result.stderr.trim() || result.error?.message || "Unknown git error",
});
}

return { deleted, skipped };
}

function printSummary({
remote,
currentBranch,
localBranches,
remoteBranches,
targetBranches,
dryRun,
force,
deleted,
skipped,
}) {
console.log(`IYERIS gitprune`);
console.log(`Script Version: ${SCRIPT_VERSION}`);
console.log(`Remote: ${remote}`);
console.log(`Current branch: ${currentBranch}`);
console.log(`Local branches: ${localBranches.length}`);
console.log(`Remote branches (${remote}): ${remoteBranches.length}`);
console.log(`Target branches: ${targetBranches.length}`);
console.log(
`Mode: ${dryRun ? "dry-run" : force ? "force delete (-D)" : "safe delete (-d)"}`,
);
console.log("");

if (targetBranches.length === 0) {
console.log("No local-only branches found.");
return;
}

if (deleted.length > 0) {
const label = dryRun ? "Would delete:" : "Deleted:";
console.log(label);
for (const branch of deleted) {
console.log(`- ${branch}`);
}
console.log("");
}

if (skipped.length > 0) {
console.log("Skipped:");
for (const item of skipped) {
console.log(`- ${item.branch}: ${item.reason}`);
}
console.log("");
}
}

export function main(argv = process.argv) {
const options = parseArgs(argv);

ensureRemoteExists(options.remote);
fetchPruned(options.remote);

const currentBranch = getCurrentBranch();
const localBranches = getLocalBranches();
const remoteBranches = getRemoteBranches(options.remote);
const targetBranches = selectBranchesToDelete(
localBranches,
remoteBranches,
currentBranch,
);

const { deleted, skipped } = deleteBranches(targetBranches, {
force: options.force,
dryRun: options.dryRun,
});

printSummary({
remote: options.remote,
currentBranch,
localBranches,
remoteBranches,
targetBranches,
dryRun: options.dryRun,
force: options.force,
deleted,
skipped,
});

return skipped.length > 0 && !options.dryRun ? 1 : 0;
}

if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
try {
process.exit(main());
} catch (error) {
console.error(
`gitprune failed: ${error && error.message ? error.message : error}`,
);
process.exit(1);
}
}
2 changes: 1 addition & 1 deletion scripts/vi.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ try {
run("git", ["reset", "--hard", "@{u}"]);
run("git", ["clean", "-fd"]);
run("git", ["pull"]);
run("npm", ["i"]);
run("npm", ["ci"]);

const branch = execSync("git rev-parse --abbrev-ref HEAD", {
encoding: "utf8",
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "zinnia"
version = "0.2.0"
version = "0.2.1"
description = "Cross-platform 7z GUI built with Tauri."
authors = ["BurntToasters <code@rosie.run>"]
edition = "2021"
Expand Down
3 changes: 2 additions & 1 deletion src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"identifier": "default",
"description": "Default permissions",
"windows": [
"main"
"main",
"extract-*"
],
"permissions": [
"core:default",
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/gen/schemas/capabilities.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"default":{"identifier":"default","description":"Default permissions","local":true,"windows":["main"],"permissions":["core:default","dialog:allow-open","dialog:allow-save","dialog:allow-message","updater:default","dialog:default","process:default","shell:allow-open","notification:default"]}}
{"default":{"identifier":"default","description":"Default permissions","local":true,"windows":["main","extract-*"],"permissions":["core:default","dialog:allow-open","dialog:allow-save","dialog:allow-message","updater:default","dialog:default","process:default","shell:allow-open","notification:default"]}}
Loading
Loading