detached 0d670e0

fix: use manifest to discover filter files instead of hardcoded list

Fetch manifest.json first to get the list of combined filter files
dynamically. This handles changes in the upstream ublock-webkit-filters
repo where the number of split files varies based on entries-per-file
limits (currently 150k). Also read cached manifest for GetCachedFilterPaths
instead of the removed hardcoded list.
commit
0d670e06df6fcc628c2e9e0ada5b7eabf8b5e91d
author
brice · · 2026-03-27 06:03 UTC
parent
2158d6c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +31 -23
M internal/infrastructure/filtering/downloader.go +30 -16
@@ -106,8 +106,8 @@ type DownloadProgress struct {
106106 BytesTotal int64
107107 }
108108
109-// DownloadFilters downloads filter JSON files from GitHub releases.
110-// Returns the paths to the downloaded files.
109+// DownloadFilters fetches the manifest to discover which filter files exist,
110+// then downloads them. Returns the paths to the downloaded files.
111111 func (d *Downloader) DownloadFilters(ctx context.Context, onProgress func(DownloadProgress)) ([]string, error) {
112112 log := logging.FromContext(ctx).With().
113113 Str("component", "filter-downloader").
@@ -118,7 +118,22 @@ func (d *Downloader) DownloadFilters(ctx context.Context, onProgress func(Downlo
118118 return nil, fmt.Errorf("failed to create cache dir: %w", err)
119119 }
120120
121- files := FilterFiles.Combined
121+ // Fetch manifest to discover which combined files to download
122+ manifest, err := d.FetchManifest(ctx)
123+ if err != nil {
124+ return nil, fmt.Errorf("failed to fetch manifest: %w", err)
125+ }
126+ if len(manifest.Combined.Files) == 0 {
127+ return nil, fmt.Errorf("manifest contains no combined filter files")
128+ }
129+
130+ log.Info().
131+ Int("files", len(manifest.Combined.Files)).
132+ Int("total_rules", manifest.Combined.TotalRules).
133+ Str("version", manifest.Version).
134+ Msg("manifest lists filter files")
135+
136+ files := manifest.Combined.Files
122137 paths := make([]string, 0, len(files))
123138 var totalBytes int64
124139
@@ -149,10 +164,9 @@ func (d *Downloader) DownloadFilters(ctx context.Context, onProgress func(Downlo
149164 log.Debug().Str("file", filename).Int64("bytes", bytesDownloaded).Msg("downloaded filter file")
150165 }
151166
152- // Also download and cache the manifest
153- if err := d.downloadAndCacheManifest(ctx); err != nil {
167+ // Cache the manifest we already fetched
168+ if err := d.cacheManifest(manifest); err != nil {
154169 log.Warn().Err(err).Msg("failed to cache manifest")
155- // Non-fatal, continue
156170 }
157171
158172 log.Info().Int("files", len(paths)).Int64("total_bytes", totalBytes).Msg("filter download complete")
@@ -223,13 +237,8 @@ func (*Downloader) renameTempFile(tmpPath, localPath string) error {
223237 return nil
224238 }
225239
226-// downloadAndCacheManifest downloads and caches the manifest.
227-func (d *Downloader) downloadAndCacheManifest(ctx context.Context) error {
228- manifest, err := d.FetchManifest(ctx)
229- if err != nil {
230- return err
231- }
232-
240+// cacheManifest writes an already-fetched manifest to disk.
241+func (d *Downloader) cacheManifest(manifest *Manifest) error {
233242 data, err := json.MarshalIndent(manifest, "", " ")
234243 if err != nil {
235244 return fmt.Errorf("failed to marshal manifest: %w", err)
@@ -244,10 +253,15 @@ func (d *Downloader) downloadAndCacheManifest(ctx context.Context) error {
244253 }
245254
246255 // GetCachedFilterPaths returns paths to cached filter JSON files.
247-// Returns nil if any files are missing.
256+// Reads the cached manifest to discover which files should exist.
257+// Returns nil if the manifest is missing or any filter file is missing.
248258 func (d *Downloader) GetCachedFilterPaths() []string {
249- paths := make([]string, 0, len(FilterFiles.Combined))
250- for _, filename := range FilterFiles.Combined {
259+ manifest, err := d.GetCachedManifest()
260+ if err != nil || manifest == nil || len(manifest.Combined.Files) == 0 {
261+ return nil
262+ }
263+ paths := make([]string, 0, len(manifest.Combined.Files))
264+ for _, filename := range manifest.Combined.Files {
251265 path := filepath.Join(d.cacheDir, filename)
252266 if _, err := os.Stat(path); err != nil {
253267 return nil
@@ -106,8 +106,8 @@ type DownloadProgress struct {
106 BytesTotal int64106 BytesTotal int64
107 }107 }
108 108
109-// DownloadFilters downloads filter JSON files from GitHub releases.109+// DownloadFilters fetches the manifest to discover which filter files exist,
110-// Returns the paths to the downloaded files.110+// then downloads them. Returns the paths to the downloaded files.
111 func (d *Downloader) DownloadFilters(ctx context.Context, onProgress func(DownloadProgress)) ([]string, error) {111 func (d *Downloader) DownloadFilters(ctx context.Context, onProgress func(DownloadProgress)) ([]string, error) {
112 log := logging.FromContext(ctx).With().112 log := logging.FromContext(ctx).With().
113 Str("component", "filter-downloader").113 Str("component", "filter-downloader").
@@ -118,7 +118,22 @@ func (d *Downloader) DownloadFilters(ctx context.Context, onProgress func(Downlo
118 return nil, fmt.Errorf("failed to create cache dir: %w", err)118 return nil, fmt.Errorf("failed to create cache dir: %w", err)
119 }119 }
120 120
121- files := FilterFiles.Combined121+ // Fetch manifest to discover which combined files to download
122+ manifest, err := d.FetchManifest(ctx)
123+ if err != nil {
124+ return nil, fmt.Errorf("failed to fetch manifest: %w", err)
125+ }
126+ if len(manifest.Combined.Files) == 0 {
127+ return nil, fmt.Errorf("manifest contains no combined filter files")
128+ }
129+
130+ log.Info().
131+ Int("files", len(manifest.Combined.Files)).
132+ Int("total_rules", manifest.Combined.TotalRules).
133+ Str("version", manifest.Version).
134+ Msg("manifest lists filter files")
135+
136+ files := manifest.Combined.Files
122 paths := make([]string, 0, len(files))137 paths := make([]string, 0, len(files))
123 var totalBytes int64138 var totalBytes int64
124 139
@@ -149,10 +164,9 @@ func (d *Downloader) DownloadFilters(ctx context.Context, onProgress func(Downlo
149 log.Debug().Str("file", filename).Int64("bytes", bytesDownloaded).Msg("downloaded filter file")164 log.Debug().Str("file", filename).Int64("bytes", bytesDownloaded).Msg("downloaded filter file")
150 }165 }
151 166
152- // Also download and cache the manifest167+ // Cache the manifest we already fetched
153- if err := d.downloadAndCacheManifest(ctx); err != nil {168+ if err := d.cacheManifest(manifest); err != nil {
154 log.Warn().Err(err).Msg("failed to cache manifest")169 log.Warn().Err(err).Msg("failed to cache manifest")
155- // Non-fatal, continue
156 }170 }
157 171
158 log.Info().Int("files", len(paths)).Int64("total_bytes", totalBytes).Msg("filter download complete")172 log.Info().Int("files", len(paths)).Int64("total_bytes", totalBytes).Msg("filter download complete")
@@ -223,13 +237,8 @@ func (*Downloader) renameTempFile(tmpPath, localPath string) error {
223 return nil237 return nil
224 }238 }
225 239
226-// downloadAndCacheManifest downloads and caches the manifest.240+// cacheManifest writes an already-fetched manifest to disk.
227-func (d *Downloader) downloadAndCacheManifest(ctx context.Context) error {241+func (d *Downloader) cacheManifest(manifest *Manifest) error {
228- manifest, err := d.FetchManifest(ctx)
229- if err != nil {
230- return err
231- }
232-
233 data, err := json.MarshalIndent(manifest, "", " ")242 data, err := json.MarshalIndent(manifest, "", " ")
234 if err != nil {243 if err != nil {
235 return fmt.Errorf("failed to marshal manifest: %w", err)244 return fmt.Errorf("failed to marshal manifest: %w", err)
@@ -244,10 +253,15 @@ func (d *Downloader) downloadAndCacheManifest(ctx context.Context) error {
244 }253 }
245 254
246 // GetCachedFilterPaths returns paths to cached filter JSON files.255 // GetCachedFilterPaths returns paths to cached filter JSON files.
247-// Returns nil if any files are missing.256+// Reads the cached manifest to discover which files should exist.
257+// Returns nil if the manifest is missing or any filter file is missing.
248 func (d *Downloader) GetCachedFilterPaths() []string {258 func (d *Downloader) GetCachedFilterPaths() []string {
249- paths := make([]string, 0, len(FilterFiles.Combined))259+ manifest, err := d.GetCachedManifest()
250- for _, filename := range FilterFiles.Combined {260+ if err != nil || manifest == nil || len(manifest.Combined.Files) == 0 {
261+ return nil
262+ }
263+ paths := make([]string, 0, len(manifest.Combined.Files))
264+ for _, filename := range manifest.Combined.Files {
251 path := filepath.Join(d.cacheDir, filename)265 path := filepath.Join(d.cacheDir, filename)
252 if _, err := os.Stat(path); err != nil {266 if _, err := os.Stat(path); err != nil {
253 return nil267 return nil
M internal/infrastructure/filtering/types.go +1 -7
@@ -48,17 +48,11 @@ type CombinedInfo struct {
4848 Files []string `json:"files"`
4949 }
5050
51-// FilterFiles defines the files to download from GitHub releases.
51+// FilterFiles defines the well-known file names for GitHub releases.
5252 var FilterFiles = struct {
5353 Manifest string
54- Combined []string
5554 }{
5655 Manifest: "manifest.json",
57- Combined: []string{
58- "combined-part1.json",
59- "combined-part2.json",
60- "combined-part3.json",
61- },
6256 }
6357
6458 // FilterIdentifier is the identifier used when storing/loading compiled filters.
@@ -48,17 +48,11 @@ type CombinedInfo struct {
48 Files []string `json:"files"`48 Files []string `json:"files"`
49 }49 }
50 50
51-// FilterFiles defines the files to download from GitHub releases.51+// FilterFiles defines the well-known file names for GitHub releases.
52 var FilterFiles = struct {52 var FilterFiles = struct {
53 Manifest string53 Manifest string
54- Combined []string
55 }{54 }{
56 Manifest: "manifest.json",55 Manifest: "manifest.json",
57- Combined: []string{
58- "combined-part1.json",
59- "combined-part2.json",
60- "combined-part3.json",
61- },
62 }56 }
63 57
64 // FilterIdentifier is the identifier used when storing/loading compiled filters.58 // FilterIdentifier is the identifier used when storing/loading compiled filters.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close