Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9dcd227e8 | ||
|
|
2d816d50e0 | ||
|
|
5f951c6c12 | ||
|
|
42efe476db |
@@ -0,0 +1,51 @@
|
|||||||
|
name: Update zims pin
|
||||||
|
|
||||||
|
"on":
|
||||||
|
schedule:
|
||||||
|
- cron: "0 2 1 * *" # 0200 on the first of every month
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-flake-lock:
|
||||||
|
runs-on: nix-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Update flake.lock
|
||||||
|
run: nix run .#zim-updater -- --output pkgs/zim/blobs.json
|
||||||
|
|
||||||
|
- name: Create PR if changed
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.KLAATU_TOKEN }}
|
||||||
|
GITEA_URL: https://src.thehellings.com
|
||||||
|
REPO: greg/nixos
|
||||||
|
run: |
|
||||||
|
if git diff --quiet pkgs/zim/blobs.json; then
|
||||||
|
echo "blobs.json unchanged, nothing to do"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
BRANCH="auto/update-zims-$(date +%Y%m%d)"
|
||||||
|
git config user.email "klaatu@thehellings.com"
|
||||||
|
git config user.name "klaatu"
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
git add pkgs/zim/blobs.json
|
||||||
|
git commit -m "chore: update zim blobs.json $(date +%Y-%m-%d)"
|
||||||
|
|
||||||
|
# Push branch using token auth
|
||||||
|
git remote set-url origin "https://klaatu:${GITEA_TOKEN}@${GITEA_URL#https://}/${REPO}.git"
|
||||||
|
git push origin "$BRANCH"
|
||||||
|
|
||||||
|
# Create PR via Gitea API
|
||||||
|
curl -s -X POST \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"${GITEA_URL}/api/v1/repos/${REPO}/pulls" \
|
||||||
|
-d "{
|
||||||
|
\"title\": \"chore: update zims $(date +%Y-%m-%d)\",
|
||||||
|
\"head\": \"$BRANCH\",
|
||||||
|
\"base\": \"main\",
|
||||||
|
\"body\": \"Automated monthly zims update.\\n\\nGenerated by Gitea Actions.\",
|
||||||
|
\"assignees\": [\"greg\"]
|
||||||
|
}"
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
top.nix-hardware.nixosModules.framework-11th-gen-intel
|
top.nix-hardware.nixosModules.framework-intel-core-ultra-series1
|
||||||
];
|
];
|
||||||
|
|
||||||
boot = {
|
boot = {
|
||||||
|
|||||||
+10
-6
@@ -16,12 +16,15 @@ let
|
|||||||
adblock_update = c ./adblock_update.nix { };
|
adblock_update = c ./adblock_update.nix { };
|
||||||
brew = c ./homebrew.nix { };
|
brew = c ./homebrew.nix { };
|
||||||
create_ssl = c ./create_ssl.nix { };
|
create_ssl = c ./create_ssl.nix { };
|
||||||
dockerCompat = pkgs.runCommand "docker-compat" {
|
dockerCompat =
|
||||||
nativeBuildInputs = [];
|
pkgs.runCommand "docker-compat"
|
||||||
} ''
|
{
|
||||||
mkdir -p $out/bin
|
nativeBuildInputs = [ ];
|
||||||
ln -s ${pkgs.podman}/bin/podman $out/bin/docker
|
}
|
||||||
'';
|
''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
ln -s ${pkgs.podman}/bin/podman $out/bin/docker
|
||||||
|
'';
|
||||||
gcc-tune = c ./gcc-tune.nix { };
|
gcc-tune = c ./gcc-tune.nix { };
|
||||||
#gen-build = c ./gen-build { };
|
#gen-build = c ./gen-build { };
|
||||||
hms = c ./hms { };
|
hms = c ./hms { };
|
||||||
@@ -29,6 +32,7 @@ let
|
|||||||
inject = c ./inject.nix { };
|
inject = c ./inject.nix { };
|
||||||
setup-ssh = c ./setup-ssh { };
|
setup-ssh = c ./setup-ssh { };
|
||||||
upgrade-pg-cluster = c ./upgrade-pg-cluster.nix { };
|
upgrade-pg-cluster = c ./upgrade-pg-cluster.nix { };
|
||||||
|
zim-updater = c ./zim/updater.nix { };
|
||||||
};
|
};
|
||||||
x86Linux = {
|
x86Linux = {
|
||||||
qemu-hook = c ./qemu-hook.nix { };
|
qemu-hook = c ./qemu-hook.nix { };
|
||||||
|
|||||||
+28
-28
@@ -2,18 +2,18 @@
|
|||||||
"en": {
|
"en": {
|
||||||
"gutenberg": {
|
"gutenberg": {
|
||||||
"name": "gutenberg_en_all",
|
"name": "gutenberg_en_all",
|
||||||
"version": "2023-08",
|
"version": "2025-11",
|
||||||
"hash": "sha256-OXmdHdsLZcW4nCUQsy7sMpUssS8NV7ztkCgS/nsISuw="
|
"hash": "sha256-AWd8jVVKHKssv9AgrJnryn3Wx0084NE/JmorpgPfryg="
|
||||||
},
|
},
|
||||||
"phet": {
|
"phet": {
|
||||||
"name": "phet_en_all",
|
"name": "phet_en_all",
|
||||||
"version": "2025-03",
|
"version": "2026-05",
|
||||||
"hash": "sha256-ARuUzU2o17J2/ZedHc2acXl33dtHCbQJEb72UQUEm1Y="
|
"hash": "sha256-zAAq/X5rjQUiYmjMpBtWP5z3J2ZHJMmIxFKnxLAhOlA="
|
||||||
},
|
},
|
||||||
"wikibooks": {
|
"wikibooks": {
|
||||||
"name": "wikibooks_en_all_maxi",
|
"name": "wikibooks_en_all_maxi",
|
||||||
"version": "2025-10",
|
"version": "2026-04",
|
||||||
"hash": "sha256-ONBh/ze1Fv2Ffm5b9vDzYS/i2cWSa4pM4MLZnozr2n8="
|
"hash": "sha256-wt7Zr+RkfCsFrOudMCYBADacu6IvPQDkZ6Y0VG2j9hA="
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"name": "wikipedia_en_all_maxi",
|
"name": "wikipedia_en_all_maxi",
|
||||||
@@ -22,40 +22,40 @@
|
|||||||
},
|
},
|
||||||
"wikisource": {
|
"wikisource": {
|
||||||
"name": "wikisource_en_all_maxi",
|
"name": "wikisource_en_all_maxi",
|
||||||
"version": "2025-11",
|
"version": "2026-05",
|
||||||
"hash": "sha256-p1Jio+PMTZVSLIDIOBeMG3acXJg83CwQM7zIWDUyozM="
|
"hash": "sha256-OA4b+U8mxpcX3fst6hrMyctucYTQOlG/b4qZDiVlcf4="
|
||||||
},
|
},
|
||||||
"wikiversity": {
|
"wikiversity": {
|
||||||
"name": "wikiversity_en_all_maxi",
|
"name": "wikiversity_en_all_maxi",
|
||||||
"version": "2025-11",
|
"version": "2026-05",
|
||||||
"hash": "sha256-IG/gAUdc/vHRrIWGHhw8vMJdLWwqrNbfh+SiclQRIAI="
|
"hash": "sha256-8mZ1CSUcF4QnDIyN0M2KedQ4pcSUCmzHBlOm93MYpCE="
|
||||||
},
|
},
|
||||||
"wiktionary": {
|
"wiktionary": {
|
||||||
"name": "wiktionary_en_all_nopic",
|
"name": "wiktionary_en_all_nopic",
|
||||||
"version": "2025-09",
|
"version": "2026-05",
|
||||||
"hash": "sha256-Ghcb60qeGaSJtTKQkJoMx/XucX7Lt1XY145lD3gHlMg="
|
"hash": "sha256-Dwiz+viVQt0zb077R9KQRgtUtxG9ixA/DeXAkCdD4rM="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fr": {
|
"fr": {
|
||||||
"gutenberg": {
|
"gutenberg": {
|
||||||
"name": "gutenberg_fr_all",
|
"name": "gutenberg_fr_all",
|
||||||
"version": "2025-10",
|
"version": "2026-01",
|
||||||
"hash": "sha256-gLHxLXJNwwcxM/mZHtoJ8ojbGQ1fugxgni/+RXccwhU="
|
"hash": "sha256-sGn1TeKwBK0+Er5YOJWq6g0itEm4gIGvKxv/PW0DIao="
|
||||||
},
|
},
|
||||||
"phet": {
|
"phet": {
|
||||||
"name": "phet_fr_all",
|
"name": "phet_fr_all",
|
||||||
"version": "2025-03",
|
"version": "2026-05",
|
||||||
"hash": "sha256-CSboZNTIowLLhp1u9wkxH8xmu3LvQNx5q493AOYJRIc="
|
"hash": "sha256-bJmchFnaPcofE/hy3ZcRggMT9osHqXwLbwMLJ+cpF28="
|
||||||
},
|
},
|
||||||
"wikibooks": {
|
"wikibooks": {
|
||||||
"name": "wikibooks_fr_all_maxi",
|
"name": "wikibooks_fr_all_maxi",
|
||||||
"version": "2025-09",
|
"version": "2026-07",
|
||||||
"hash": "sha256-KHCc/73L5Bd9iZ47SRV6vpI8fVM1v9hk1TpEvTro2uo="
|
"hash": "sha256-csI+E5UFGi42BLGWWHxQhVr5KSihCFc53KGKwo557Ck="
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"name": "wikipedia_fr_all_maxi",
|
"name": "wikipedia_fr_all_maxi",
|
||||||
"version": "2025-06",
|
"version": "2026-05",
|
||||||
"hash": "sha256-ve7Mbh96/ObNbV/KVNYZpfLN+HdWlJ7C5LItEdVkRH0="
|
"hash": "sha256-YUAbGzYYr+c2RQqGjHIF9XL1kNsxDgRKnHK1H7/PtDE="
|
||||||
},
|
},
|
||||||
"wikisource": {
|
"wikisource": {
|
||||||
"name": "wikisource_fr_all_maxi",
|
"name": "wikisource_fr_all_maxi",
|
||||||
@@ -64,25 +64,25 @@
|
|||||||
},
|
},
|
||||||
"wikiversity": {
|
"wikiversity": {
|
||||||
"name": "wikiversity_fr_all_maxi",
|
"name": "wikiversity_fr_all_maxi",
|
||||||
"version": "2025-09",
|
"version": "2026-05",
|
||||||
"hash": "sha256-55fdtw/cRezq6nkRp6wuwkukBiqsQKdwh9QWVAgFcBI="
|
"hash": "sha256-nVDHtkWIOJkoiGixnA1zVR1QS58thjc12FJyagK3Ec0="
|
||||||
},
|
},
|
||||||
"wiktionary": {
|
"wiktionary": {
|
||||||
"name": "wiktionary_fr_all_nopic",
|
"name": "wiktionary_fr_all_nopic",
|
||||||
"version": "2025-11",
|
"version": "2026-05",
|
||||||
"hash": "sha256-P0CJptee5rDzccxyRDBzyRLuuFOcaLsJYi6DkBFUfIc="
|
"hash": "sha256-7UXByW2IIL0hec8RPT39jpg5IEGvRMD47hc4Y4vFkJs="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ht": {
|
"ht": {
|
||||||
"phet": {
|
"phet": {
|
||||||
"name": "phet_ht_all",
|
"name": "phet_ht_all",
|
||||||
"version": "2025-03",
|
"version": "2026-05",
|
||||||
"hash": "sha256-bHrPzE8H7ptrP6r5wPlXSsXNlTiKxJ9KatABC4kwx+s="
|
"hash": "sha256-GiJ1jllhioyMYidQ7+Lcbdd9JN2yf04ZQgnO8XaGAqY="
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"name": "wikipedia_ht_all_maxi",
|
"name": "wikipedia_ht_all_maxi",
|
||||||
"version": "2026-04",
|
"version": "2026-07",
|
||||||
"hash": "sha256-qUX0pKxIyNsWX4V6kNIsP4Z9nc6TDhwdDR0MmpDOQFs="
|
"hash": "sha256-InS9cMRaIv9ArlKVwKUypz56m4DgSR7Tl6XpSTMzH+o="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+167
-112
@@ -9,7 +9,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -17,27 +17,144 @@ import (
|
|||||||
|
|
||||||
const BASE = "https://download.kiwix.org/zim"
|
const BASE = "https://download.kiwix.org/zim"
|
||||||
|
|
||||||
func getTypes() []string {
|
// ///////////////////////////////////////////////////////////////////////////
|
||||||
return []string{
|
// //////////////// THE BLOB ITSELF /////////////////////////////////////////
|
||||||
"phet",
|
// ///////////////////////////////////////////////////////////////////////////
|
||||||
"wikipedia",
|
type Blobs struct {
|
||||||
"wiktionary",
|
En Language `json:"en"`
|
||||||
"wikiversity",
|
Fr Language `json:"fr"`
|
||||||
"wikisource",
|
Ht Language `json:"ht"`
|
||||||
"wikibooks",
|
dirty bool
|
||||||
"gutenberg",
|
}
|
||||||
"ted",
|
|
||||||
|
func (b *Blobs) Populate() {
|
||||||
|
done := make(chan bool)
|
||||||
|
waitFor := 0
|
||||||
|
// Launch self-populating efforts
|
||||||
|
blobType := reflect.Indirect(reflect.ValueOf(b)).Type()
|
||||||
|
for f := range blobType.Fields() {
|
||||||
|
if f.IsExported() {
|
||||||
|
//fmt.Printf("%s:\tBeginning population efforts\n", f.Name)
|
||||||
|
waitFor += 1
|
||||||
|
go reflect.Indirect(reflect.ValueOf(b)).
|
||||||
|
FieldByName(f.Name).
|
||||||
|
Addr().
|
||||||
|
Interface().
|
||||||
|
(*Language).
|
||||||
|
Populate(strings.ToLower(f.Name), done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wait until all languages are completed
|
||||||
|
for d := range done {
|
||||||
|
waitFor -= 1
|
||||||
|
if waitFor == 0 {
|
||||||
|
fmt.Println("Completed waiting for all languages")
|
||||||
|
close(done)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.dirty = b.dirty || d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLanguages() []string {
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
return []string{
|
//////////////////////// The Language ///////////////////////////////////////
|
||||||
"ht",
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
"en",
|
|
||||||
"fr",
|
type Language struct {
|
||||||
|
Gutenberg *Zim `json:"gutenberg,omitempty"`
|
||||||
|
Phet *Zim `json:"phet,omitempty"`
|
||||||
|
Ted *Zim `json:"ted,omitempty"`
|
||||||
|
Wikibooks *Zim `json:"wikibooks,omitempty"`
|
||||||
|
Wikipedia *Zim `json:"wikipedia,omitempty"`
|
||||||
|
Wikisource *Zim `json:"wikisource,omitempty"`
|
||||||
|
Wikiversity *Zim `json:"wikiversity,omitempty"`
|
||||||
|
Wiktionary *Zim `json:"wiktionary,omitempty"`
|
||||||
|
dirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Language) Populate(code string, done chan bool) {
|
||||||
|
childDone := make(chan bool)
|
||||||
|
waitFor := 0
|
||||||
|
languageType := reflect.Indirect(reflect.ValueOf(l)).Type()
|
||||||
|
l.dirty = false
|
||||||
|
for f := range languageType.Fields() {
|
||||||
|
if f.IsExported() {
|
||||||
|
ptrPtrZim := reflect.Indirect(reflect.ValueOf(l)).
|
||||||
|
FieldByName(f.Name)
|
||||||
|
if ptrPtrZim.IsValid() && !ptrPtrZim.IsNil() {
|
||||||
|
waitFor += 1
|
||||||
|
go reflect.Indirect(ptrPtrZim).
|
||||||
|
Addr().
|
||||||
|
Interface().
|
||||||
|
(*Zim).
|
||||||
|
Populate(strings.ToLower(f.Name), code, childDone)
|
||||||
|
} else {
|
||||||
|
// TODO: Figure out how to set a value over the nil pointer
|
||||||
|
// of the field, so we can auto-detect when these are available
|
||||||
|
// in the future
|
||||||
|
// waitFor += 1
|
||||||
|
//z := &Zim{Name: f.Name, Version: "1999-01-01", Hash: ""}
|
||||||
|
//ptrPtrZim.Set(reflect.ValueOf(z))
|
||||||
|
//go z.Populate(strings.ToLower(f.Name), code, childDone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wait until children are all done
|
||||||
|
for d := range childDone {
|
||||||
|
waitFor -= 1
|
||||||
|
if waitFor == 0 {
|
||||||
|
close(childDone)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.dirty = l.dirty || d
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("%s\tFinished populate\n", code)
|
||||||
|
done <- l.dirty
|
||||||
|
}
|
||||||
|
|
||||||
|
// ///////////////////////////////////////////////////////////////////////////
|
||||||
|
// ////////////////////// A Single Zim ///////////////////////////////////////
|
||||||
|
// ///////////////////////////////////////////////////////////////////////////
|
||||||
|
type Zim struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
dirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Zim) Populate(category string, language string, done chan bool) {
|
||||||
|
//fmt.Printf("%s:%s\tBeginning populate for\n", language, category)
|
||||||
|
// Look for a possible Zim file
|
||||||
|
links := getLinks(getPage(category))
|
||||||
|
link, err := getName(links, category, language)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s:%s\tNo zim found\n", language, category)
|
||||||
|
done <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//fmt.Printf("%s:%s\tFound link: %s\n", category, language, link)
|
||||||
|
// Extract name and version
|
||||||
|
re := regexp.MustCompilePOSIX("^([a-zA-Z_]+)_([0-9-]+)\\.zim$")
|
||||||
|
match := re.FindStringSubmatch(link)
|
||||||
|
if len(match) != 3 {
|
||||||
|
fmt.Printf("%s:%s Matches: %s", language, category, match)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// Check if the name and version mismatch
|
||||||
|
if match[1] == z.Name && match[2] == z.Version {
|
||||||
|
fmt.Printf("Skipping existing hash: %s\n", link)
|
||||||
|
done <- false
|
||||||
|
} else {
|
||||||
|
z.Name = match[1]
|
||||||
|
z.Version = match[2]
|
||||||
|
// Fetch the Zim file, if it differs from what we currently have
|
||||||
|
z.UpdateHash(category)
|
||||||
|
done <- true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to fetch the HTML of a given type page
|
||||||
func getPage(t string) string {
|
func getPage(t string) string {
|
||||||
page, err := http.Get(fmt.Sprintf("%s/%s/", BASE, t))
|
page, err := http.Get(fmt.Sprintf("%s/%s/", BASE, t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -48,6 +165,7 @@ func getPage(t string) string {
|
|||||||
return string(pageBytes)
|
return string(pageBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to parse out all the links from the page
|
||||||
func getLinks(page string) []string {
|
func getLinks(page string) []string {
|
||||||
ret := []string{}
|
ret := []string{}
|
||||||
|
|
||||||
@@ -60,6 +178,7 @@ func getLinks(page string) []string {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to get the most likely link for this particular entry
|
||||||
func getName(links []string, t, lang string) (string, error) {
|
func getName(links []string, t, lang string) (string, error) {
|
||||||
candidates := []string{}
|
candidates := []string{}
|
||||||
prefix := fmt.Sprintf("%s_%s_all", t, lang)
|
prefix := fmt.Sprintf("%s_%s_all", t, lang)
|
||||||
@@ -79,44 +198,39 @@ func getName(links []string, t, lang string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHash(ch chan result, file, category, language string) {
|
// Helper function to get the hash value from the resulting link
|
||||||
// TODO: Only call this if the file doesn't already have a hash
|
func (z *Zim) UpdateHash(category string) error {
|
||||||
// in the existing file
|
file := fmt.Sprintf("%s_%s.zim", z.Name, z.Version)
|
||||||
fmt.Printf("Fetching hash for %s\n", file)
|
|
||||||
cmd := exec.Command( "nix-prefetch-url", fmt.Sprintf("%s/%s/%s", BASE, category, file))
|
// First fetch the file into our local Nix store
|
||||||
|
fmt.Printf("Fetching hash: %s\n", file)
|
||||||
|
cmd := exec.Command("nix-prefetch-url", fmt.Sprintf("%s/%s/%s", BASE, category, file))
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error fetching hash for %s (category: %s, language: %s): %v\n", file, category, language, err)
|
fmt.Printf("%s: ERROR fetching hash: %v\n", file, err)
|
||||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
fmt.Printf("Command stderr: %s\n", string(exitErr.Stderr))
|
fmt.Printf("Command stderr: %s\n", string(exitErr.Stderr))
|
||||||
}
|
}
|
||||||
ch <- result{category, language, ""}
|
return errors.New("Error fetching file")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
hash := strings.TrimSpace(string(out))
|
hash := strings.TrimSpace(string(out))
|
||||||
fmt.Printf("Successfully fetched hash for %s (category: %s, language: %s)\n", file, category, language)
|
fmt.Printf("%s: Successfully fetched raw hash\n", file)
|
||||||
ch <- result{category, language, hash}
|
fmt.Printf("%s: Hash is: %s\n", file, hash)
|
||||||
}
|
|
||||||
|
|
||||||
func outputIsValid(o map[string]map[string]Zim) bool {
|
// Then, convert the hash to SRI
|
||||||
for a := range o {
|
cmd2 := exec.Command("nix", "hash", "convert", "--to", "sri", hash, "--hash-algo", "sha256")
|
||||||
for b := range o[a] {
|
out2, err2 := cmd2.Output()
|
||||||
if o[a][b].Hash == "" {
|
if err2 != nil {
|
||||||
return false
|
fmt.Printf("%s: Error converting hash to SRI: %v\n", file, err)
|
||||||
}
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
fmt.Printf("%s: Command stderr: %s\n", file, string(exitErr.Stderr))
|
||||||
}
|
}
|
||||||
|
return errors.New("Error fetching file")
|
||||||
}
|
}
|
||||||
return true
|
z.Hash = strings.TrimSpace(string(out2))
|
||||||
}
|
z.dirty = true
|
||||||
|
fmt.Printf("%s: Successfully convert hash to SRI: %s", file, out2)
|
||||||
type result struct {
|
return nil
|
||||||
category, language, hash string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Zim struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
Hash string `json:"hash"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -125,90 +239,31 @@ func main() {
|
|||||||
|
|
||||||
// Determine the output file path
|
// Determine the output file path
|
||||||
var outputPath string
|
var outputPath string
|
||||||
if *outputFile != "" {
|
outputPath = *outputFile
|
||||||
outputPath = *outputFile
|
|
||||||
} else {
|
|
||||||
// Get the directory where updater.go is located
|
|
||||||
execPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
// Fallback to current directory if we can't determine executable path
|
|
||||||
outputPath = "blobs.json"
|
|
||||||
} else {
|
|
||||||
dir := filepath.Dir(execPath)
|
|
||||||
outputPath = filepath.Join(dir, "blobs.json")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Writing file to ", outputPath)
|
fmt.Println("Writing file to ", outputPath)
|
||||||
|
|
||||||
// Read existing cache if it exists
|
// Read existing cache if it exists
|
||||||
cached := make(map[string]map[string]Zim)
|
var blobs Blobs
|
||||||
if data, err := os.ReadFile(outputPath); err == nil {
|
if data, err := os.ReadFile(outputPath); err == nil {
|
||||||
if err := json.Unmarshal(data, &cached); err != nil {
|
if err := json.Unmarshal(data, &blobs); err != nil {
|
||||||
fmt.Printf("Warning: could not parse existing cache file: %v\n", err)
|
fmt.Printf("Warning: could not parse existing cache file: %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Loaded existing cache from %s\n", outputPath)
|
fmt.Printf("Loaded existing cache from %s\n", outputPath)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Error reading file: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
blobs.Populate()
|
||||||
|
|
||||||
output := make(map[string]map[string]Zim)
|
ret, err := json.MarshalIndent(&blobs, "", " ")
|
||||||
comms := make(chan result)
|
|
||||||
pendingHashes := 0
|
|
||||||
|
|
||||||
for _, t := range getTypes() {
|
|
||||||
page := getPage(t)
|
|
||||||
links := getLinks(page)
|
|
||||||
for _, lang := range getLanguages() {
|
|
||||||
if file, err := getName(links, t, lang); err == nil {
|
|
||||||
if _, ok := output[lang]; !ok {
|
|
||||||
output[lang] = make(map[string]Zim)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this file already exists in cache with same name
|
|
||||||
if cachedLang, ok := cached[lang]; ok {
|
|
||||||
if cachedEntry, ok := cachedLang[t]; ok && cachedEntry.Name == file {
|
|
||||||
// Reuse cached hash
|
|
||||||
fmt.Printf("Using cached hash for %s (category: %s, language: %s)\n", file, t, lang)
|
|
||||||
output[lang][t] = cachedEntry
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// File is new or name has changed, fetch hash
|
|
||||||
output[lang][t] = Zim{file, ""}
|
|
||||||
pendingHashes++
|
|
||||||
go getHash(comms, file, t, lang)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only wait for results if we actually spawned goroutines
|
|
||||||
if pendingHashes > 0 {
|
|
||||||
hashesReceived := 0
|
|
||||||
for r := range comms {
|
|
||||||
if entry, ok := output[r.language][r.category]; ok {
|
|
||||||
entry.Hash = r.hash
|
|
||||||
output[r.language][r.category] = entry
|
|
||||||
}
|
|
||||||
hashesReceived++
|
|
||||||
if hashesReceived >= pendingHashes {
|
|
||||||
close(comms)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify all hashes are present
|
|
||||||
if !outputIsValid(output) {
|
|
||||||
fmt.Println("Warning: Some hashes are missing from the output")
|
|
||||||
}
|
|
||||||
ret, err := json.MarshalIndent(output, "", " ")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error marshaling JSON: %v\n", err)
|
fmt.Printf("Error marshaling JSON: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(outputPath, ret, 0644)
|
err = os.WriteFile(outputPath, []byte(fmt.Sprintf("%s\n", ret)), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error writing to file %s: %v\n", outputPath, err)
|
fmt.Printf("Error writing to file %s: %v\n", outputPath, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user