Skip to content

Commit 1abd851

Browse files
committed
refactor(vicinae): remove flake input for extensions
MASSIVE repo size is ridiculous for flake updates. Just manage through sparse checkouts and update script instead.
1 parent e3e4447 commit 1abd851

6 files changed

Lines changed: 256 additions & 82 deletions

File tree

flake.lock

Lines changed: 0 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@
6363
niri = {
6464
url = "github:sodiboo/niri-flake";
6565
};
66-
raycast-extensions = {
67-
url = "github:raycast/extensions";
68-
flake = false;
69-
};
7066
firefox-addons = {
7167
url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons";
7268
inputs.nixpkgs.follows = "nixpkgs";

flake/apps.nix

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,23 @@ _: {
128128
}
129129
);
130130
};
131+
132+
update-vicinae-extensions = {
133+
type = "app";
134+
meta.description = "Update pinned Vicinae Raycast extensions";
135+
program = lib.getExe (
136+
pkgs.writeShellApplication {
137+
name = "update-vicinae-extensions";
138+
runtimeInputs = [
139+
pkgs.git
140+
pkgs.python3
141+
];
142+
text = ''
143+
${pkgs.python3}/bin/python3 ${./apps/scripts/update_vicinae_extensions.py}
144+
'';
145+
}
146+
);
147+
};
131148
};
132149
};
133150
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/env python3
2+
3+
import re
4+
import subprocess
5+
import sys
6+
from pathlib import Path
7+
8+
RAYCAST_REPO = "https://github.com/raycast/extensions.git"
9+
REV_RE = re.compile(r'(^\s*raycastRev\s*=\s*")([0-9a-f]{40})(";\s*$)', re.MULTILINE)
10+
ENTRY_RE = re.compile(
11+
r'(?P<prefix>\{\s*name = "(?P<name>[^"]+)";\s*sha256 = ")'
12+
r'(?P<hash>sha256-[^"]+)'
13+
r'(?P<suffix>";\s*\})',
14+
re.DOTALL,
15+
)
16+
GOT_HASH_RE = re.compile(r"got:\s*(sha256-[A-Za-z0-9+/=]+)")
17+
18+
19+
def run(
20+
command: list[str], *, cwd: Path | None = None, check: bool = True
21+
) -> subprocess.CompletedProcess[str]:
22+
result = subprocess.run(
23+
command,
24+
cwd=cwd,
25+
text=True,
26+
capture_output=True,
27+
)
28+
if check and result.returncode != 0:
29+
sys.stderr.write(result.stdout)
30+
sys.stderr.write(result.stderr)
31+
raise SystemExit(result.returncode)
32+
return result
33+
34+
35+
def get_repo_root() -> Path:
36+
print("Resolving repository root...", flush=True)
37+
result = run(["git", "rev-parse", "--show-toplevel"])
38+
return Path(result.stdout.strip())
39+
40+
41+
def get_head_rev() -> str:
42+
print("Resolving latest raycast/extensions HEAD revision...", flush=True)
43+
result = run(["git", "ls-remote", RAYCAST_REPO, "HEAD"])
44+
return result.stdout.split()[0]
45+
46+
47+
def get_extension_names(text: str) -> list[str]:
48+
return [match.group("name") for match in ENTRY_RE.finditer(text)]
49+
50+
51+
def get_sparse_hash(repo_root: Path, name: str, rev: str) -> str:
52+
expr = f"""
53+
let
54+
flake = builtins.getFlake (toString {repo_root});
55+
pkgs = import flake.inputs.nixpkgs {{ system = builtins.currentSystem; }};
56+
in
57+
pkgs.fetchgit {{
58+
url = "https://github.com/raycast/extensions";
59+
rev = "{rev}";
60+
hash = pkgs.lib.fakeHash;
61+
sparseCheckout = [ "/extensions/{name}" ];
62+
}}
63+
"""
64+
result = run(["nix", "build", "--impure", "--no-link", "--expr", expr], check=False)
65+
output = f"{result.stdout}\n{result.stderr}"
66+
match = GOT_HASH_RE.search(output)
67+
if match is None:
68+
sys.stderr.write(output)
69+
raise SystemExit(f"failed to compute hash for {name}")
70+
return match.group(1)
71+
72+
73+
def replace_rev(text: str, rev: str) -> str:
74+
updated, count = REV_RE.subn(rf"\g<1>{rev}\g<3>", text, count=1)
75+
if count != 1:
76+
raise SystemExit("failed to update raycastRev in Vicinae module")
77+
return updated
78+
79+
80+
def replace_hashes(text: str, hashes: dict[str, str]) -> str:
81+
seen: set[str] = set()
82+
83+
def repl(match: re.Match[str]) -> str:
84+
name = match.group("name")
85+
hash_value = hashes.get(name)
86+
if hash_value is None:
87+
return match.group(0)
88+
seen.add(name)
89+
return f"{match.group('prefix')}{hash_value}{match.group('suffix')}"
90+
91+
updated = ENTRY_RE.sub(repl, text)
92+
missing = [name for name in hashes if name not in seen]
93+
if missing:
94+
raise SystemExit(
95+
"failed to update inline Vicinae hashes for: " + ", ".join(sorted(missing))
96+
)
97+
return updated
98+
99+
100+
def main() -> None:
101+
repo_root = get_repo_root()
102+
module_path = (
103+
repo_root / "modules/home/programs/graphical/launchers/vicinae/default.nix"
104+
)
105+
print(f"Using module: {module_path}", flush=True)
106+
text = module_path.read_text()
107+
names = get_extension_names(text)
108+
print(f"Found {len(names)} pinned extensions in inline list", flush=True)
109+
rev = get_head_rev()
110+
111+
print(f"Updating Vicinae Raycast extensions to {rev}")
112+
hashes: dict[str, str] = {}
113+
for index, name in enumerate(names, start=1):
114+
print(
115+
f"[{index}/{len(names)}] Computing sparse fetch hash for extensions/{name}",
116+
flush=True,
117+
)
118+
hash_value = get_sparse_hash(repo_root, name, rev)
119+
print(f"[{index}/{len(names)}] Resolved {name} -> {hash_value}", flush=True)
120+
hashes[name] = hash_value
121+
122+
print("Rewriting inline Raycast pins...", flush=True)
123+
updated = replace_hashes(replace_rev(text, rev), hashes)
124+
module_path.write_text(updated)
125+
print(f"Updated {module_path}", flush=True)
126+
127+
128+
if __name__ == "__main__":
129+
main()

flake/dev/flake.lock

Lines changed: 0 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)