@@ -19,12 +19,19 @@ import (
1919
2020 "github.com/goccy/go-json"
2121 "github.com/klauspost/compress/zstd"
22- "github.com/shamaton/msgpack/v2" //"github.com/fxamacker/cbor/v2"
22+ "github.com/shamaton/msgpack/v2"
2323 "github.com/zeebo/blake3"
2424
2525 "github.com/xplshn/pelf/pkg/utils"
2626)
2727
28+ const (
29+ warningColor = "\x1b [0;33m"
30+ errorColor = "\x1b [0;31m"
31+ blueColor = "\x1b [0;34m"
32+ resetColor = "\x1b [0m"
33+ )
34+
2835func init () {
2936 log .SetFlags (0 )
3037}
@@ -104,7 +111,10 @@ var appStreamMetadata []AppStreamMetadata
104111var appStreamMetadataLoaded bool
105112
106113type RuntimeInfo struct {
107- AppBundleID string `json:"AppBundleID"`
114+ AppBundleID string `json:"AppBundleID"`
115+ FilesystemType string `json:"FilesystemType"`
116+ Hash string `json:"Hash"`
117+ BuildDate string `json:"build_date,omitempty"`
108118}
109119
110120func loadAppStreamMetadata () error {
@@ -126,13 +136,13 @@ func loadAppStreamMetadata() error {
126136
127137 zstdReader , err := zstd .NewReader (nil , zstd .WithDecoderConcurrency (1 ))
128138 if err != nil {
129- return fmt .Errorf ("error creating zstd reader: %v" , err )
139+ return fmt .Errorf ("%serror%s creating zstd reader: %v" , errorColor , resetColor , err )
130140 }
131141 defer zstdReader .Close ()
132142
133143 decompressed , err := zstdReader .DecodeAll (body , nil )
134144 if err != nil {
135- return fmt .Errorf ("error decompressing data: %v" , err )
145+ return fmt .Errorf ("%serror%s decompressing data: %v" , errorColor , resetColor , err )
136146 }
137147
138148 err = msgpack .Unmarshal (decompressed , & appStreamMetadata )
@@ -154,50 +164,55 @@ func findAppStreamMetadataForAppId(appId string) *AppStreamMetadata {
154164 return nil
155165}
156166
157- func extractAppBundleInfo (filename string ) (utils. AppBundleID , string , error ) {
167+ func extractAppBundleInfo (filename string ) (RuntimeInfo , error ) {
158168 file , err := elf .Open (filename )
159169 if err != nil {
160- return utils. AppBundleID {}, " " , err
170+ return RuntimeInfo {}, fmt . Errorf ( "%serror%s opening ELF file %s%s%s: %v " , errorColor , resetColor , blueColor , filename , resetColor , err )
161171 }
162172 defer file .Close ()
163173
164174 section := file .Section (".pbundle_runtime_info" )
165175 if section == nil {
166- return utils. AppBundleID {}, "" , fmt .Errorf ("section .pbundle_runtime_info not found" )
176+ return RuntimeInfo {}, fmt .Errorf ("%serror%s section .pbundle_runtime_info not found in %s%s%s" , errorColor , resetColor , blueColor , filename , resetColor )
167177 }
168178 data , err := section .Data ()
169179 if err != nil {
170- return utils. AppBundleID {}, " " , err
180+ return RuntimeInfo {}, fmt . Errorf ( "%serror%s reading section data from %s%s%s: %v " , errorColor , resetColor , blueColor , filename , resetColor , err )
171181 }
172182
173- var runtimeInfo RuntimeInfo
183+ var runtimeInfo map [ string ] interface {}
174184 if err := msgpack .Unmarshal (data , & runtimeInfo ); err != nil {
175- return utils.AppBundleID {}, "" , err
185+ return RuntimeInfo {}, fmt .Errorf ("%serror%s parsing .pbundle_runtime_info MessagePack in %s%s%s: %v" , errorColor , resetColor , blueColor , filename , resetColor , err )
186+ }
187+
188+ cfg := RuntimeInfo {
189+ AppBundleID : runtimeInfo ["AppBundleID" ].(string ),
190+ FilesystemType : runtimeInfo ["FilesystemType" ].(string ),
191+ Hash : runtimeInfo ["Hash" ].(string ),
176192 }
177- if runtimeInfo .AppBundleID == "" {
178- return utils.AppBundleID {}, "" , fmt .Errorf ("appBundleID not found" )
193+
194+ if cfg .AppBundleID == "" {
195+ return RuntimeInfo {}, fmt .Errorf ("%serror%s appBundleID not found in %s%s%s" , errorColor , resetColor , blueColor , filename , resetColor )
179196 }
180197
181- // Parse the AppBundleID using utils.ParseAppBundleID
182- appBundleID , err := utils .ParseAppBundleID (runtimeInfo .AppBundleID )
198+ appBundleID , err := utils .ParseAppBundleID (cfg .AppBundleID )
183199 if err != nil {
184- return utils. AppBundleID {}, "" , fmt .Errorf ("invalid AppBundleID: %v" , err )
200+ return RuntimeInfo {}, fmt .Errorf ("%serror%s invalid AppBundleID in %s%s%s : %v" , errorColor , resetColor , blueColor , filename , resetColor , err )
185201 }
186202
187- // Extract build date if present
188- var buildDate string
189203 if appBundleID .IsDated () {
190- buildDate = appBundleID .Date .Format ("2006-01-02" ) // Format as YYYY-MM-DD
204+ cfg . BuildDate = appBundleID .Date .Format ("2006-01-02" )
191205 } else {
192- buildDate = "unknown"
206+ cfg . BuildDate = "unknown"
193207 }
194208
195- return * appBundleID , buildDate , nil
209+ return cfg , nil
196210}
197211
198212func getFileSize (path string ) string {
199213 fileInfo , err := os .Stat (path )
200214 if err != nil {
215+ log .Printf ("%swarning%s unable to get file size for %s%s%s: %v" , warningColor , resetColor , blueColor , path , resetColor , err )
201216 return "0 MB"
202217 }
203218 sizeMB := float64 (fileInfo .Size ()) / (1024 * 1024 )
@@ -207,23 +222,23 @@ func getFileSize(path string) string {
207222func computeHashes (path string ) (string , string , error ) {
208223 file , err := os .Open (path )
209224 if err != nil {
210- return "" , "" , err
225+ return "" , "" , fmt . Errorf ( "%serror%s opening file %s%s%s for hashing: %v" , errorColor , resetColor , blueColor , path , resetColor , err )
211226 }
212227 defer file .Close ()
213228
214229 shaHasher := sha256 .New ()
215230 if _ , err := io .Copy (shaHasher , file ); err != nil {
216- return "" , "" , err
231+ return "" , "" , fmt . Errorf ( "%serror%s computing SHA256 for %s%s%s: %v" , errorColor , resetColor , blueColor , path , resetColor , err )
217232 }
218233 shaSum := hex .EncodeToString (shaHasher .Sum (nil ))
219234
220235 _ , err = file .Seek (0 , 0 )
221236 if err != nil {
222- return "" , "" , err
237+ return "" , "" , fmt . Errorf ( "%serror%s seeking file %s%s%s for Blake3: %v" , errorColor , resetColor , blueColor , path , resetColor , err )
223238 }
224239 b3Hasher := blake3 .New ()
225240 if _ , err := io .Copy (b3Hasher , file ); err != nil {
226- return "" , "" , err
241+ return "" , "" , fmt . Errorf ( "%serror%s computing Blake3 for %s%s%s: %v" , errorColor , resetColor , blueColor , path , resetColor , err )
227242 }
228243 b3Sum := hex .EncodeToString (b3Hasher .Sum (nil ))
229244
@@ -233,9 +248,8 @@ func computeHashes(path string) (string, string, error) {
233248func isExecutable (path string ) (bool , error ) {
234249 fileInfo , err := os .Stat (path )
235250 if err != nil {
236- return false , err
251+ return false , fmt . Errorf ( "%serror%s checking executable status for %s%s%s: %v" , errorColor , resetColor , blueColor , path , resetColor , err )
237252 }
238- // Check if the file has executable permissions (e.g., -r-x------, -rwxr-xr-x)
239253 mode := fileInfo .Mode ()
240254 return mode & 0111 != 0 , nil
241255}
@@ -244,18 +258,18 @@ func extractAppStreamXML(filename string) (*AppStreamXML, error) {
244258 cmd := exec .Command (filename , "--pbundle_appstream" )
245259 output , err := cmd .Output ()
246260 if err != nil {
247- return nil , fmt .Errorf ("error extracting AppStream XML: %v" , err )
261+ return nil , fmt .Errorf ("%serror%s extracting AppStream XML from %s%s%s : %v" , errorColor , resetColor , blueColor , filename , resetColor , err )
248262 }
249263
250264 decodedOutput , err := base64 .StdEncoding .DecodeString (string (output ))
251265 if err != nil {
252- return nil , fmt .Errorf ("error decoding base64 output: %v" , err )
266+ return nil , fmt .Errorf ("%serror%s decoding base64 output from %s%s%s : %v" , errorColor , resetColor , blueColor , filename , resetColor , err )
253267 }
254268
255269 var appStreamXML AppStreamXML
256270 err = xml .Unmarshal (decodedOutput , & appStreamXML )
257271 if err != nil {
258- return nil , fmt .Errorf ("error unmarshalling XML: %v" , err )
272+ return nil , fmt .Errorf ("%serror%s unmarshalling XML from %s%s%s : %v" , errorColor , resetColor , blueColor , filename , resetColor , err )
259273 }
260274
261275 return & appStreamXML , nil
@@ -327,73 +341,71 @@ func main() {
327341 }
328342
329343 if err := loadAppStreamMetadata (); err != nil {
330- log .Printf ("Error loading AppStream metadata: %v\n " , err )
344+ log .Printf ("%serror%s loading AppStream metadata: %v" , errorColor , resetColor , err )
331345 return
332346 }
333347
334348 dbinMetadata := make (DbinMetadata )
335349
336350 err := filepath .Walk (* inputDir , func (path string , info os.FileInfo , err error ) error {
337351 if err != nil {
352+ log .Printf ("%serror%s walking directory at %s%s%s: %v" , errorColor , resetColor , blueColor , path , resetColor , err )
338353 return err
339354 }
340355
341356 if ! info .IsDir () && strings .HasSuffix (path , ".AppBundle" ) {
342- appBundleID , buildDate , err := extractAppBundleInfo (path )
357+ appBundleInfo , err := extractAppBundleInfo (path )
343358 if err != nil {
344- log .Printf ("Error extracting runtime info from %s: %v\n " , path , err )
359+ log .Printf ("%serror%s extracting runtime info from %s%s%s : %v" , errorColor , resetColor , blueColor , path , resetColor , err )
345360 return nil
346361 }
347362
348363 b3sum , shasum , err := computeHashes (path )
349364 if err != nil {
350- log .Printf ("Error computing hashes for %s: %v\n " , path , err )
365+ log .Printf ("%serror%s computing hashes for %s%s%s : %v" , errorColor , resetColor , blueColor , path , resetColor , err )
351366 return nil
352367 }
353368
354369 var pkg , pkgId string
355370 baseFilename := filepath .Base (path )
356- if appBundleID . Compliant () == nil {
357- // New format: name#repo:version[@dd_mm_yyyy]
358- pkg = appBundleID .Name
359- pkgId = " github.com.xplshn.appbundlehub." + appBundleID .Name
371+ appBundleID , err := utils . ParseAppBundleID ( appBundleInfo . AppBundleID )
372+ if err == nil && appBundleID . Compliant () == nil {
373+ pkg = appBundleID .Name + "." + appBundleInfo . FilesystemType + ".AppBundle"
374+ pkgId = ternary ( appBundleID . Repo != "" , appBundleID . Repo , " github.com.xplshn.appbundlehub." + appBundleID .ShortName ())
360375 } else {
361- // Legacy format: name-dd_mm_yyyy-maintainer
362- pkg = appBundleID . Name
363- pkgId = "github.com.xplshn.appbundlehub." + appBundleID . Name
376+ pkgId = strings . TrimSuffix ( baseFilename , filepath . Ext ( baseFilename + "." + appBundleInfo . FilesystemType ))
377+ pkg = baseFilename
378+ pkgId = "github.com.xplshn.appbundlehub." + pkgId
364379 }
365-
366- log .Printf ("Adding %s to repository index\n " , baseFilename )
367- log .Println (".pkg: " + pkg )
380+ log .Printf ("Adding [%s%s%s](%s) to repository index" , blueColor , baseFilename , resetColor , appBundleID .String ())
368381
369382 item := binaryEntry {
370383 Pkg : pkg ,
371384 Name : strings .Title (strings .ReplaceAll (appBundleID .Name , "-" , " " )),
372385 PkgId : pkgId ,
373- BuildDate : buildDate ,
386+ BuildDate : appBundleID . Date . String () ,
374387 Size : getFileSize (path ),
375388 Bsum : b3sum ,
376389 Shasum : shasum ,
377390 DownloadURL : * downloadPrefix + filepath .Base (path ),
378391 RepoName : * repoName ,
379392 }
380393
381- // Check if the file is executable before attempting AppStream extraction
382394 isExec , err := isExecutable (path )
383395 if err != nil {
384- log .Printf ("Error checking if %s is executable: %v\n " , path , err )
396+ log .Printf ("%serror%s checking if %s%s%s is executable: %v" , errorColor , resetColor , blueColor , path , resetColor , err )
385397 return nil
386398 }
387399 if ! isExec {
388- log .Printf ("warning: %s is not executable\n " , filepath .Base (path ))
400+ log .Printf ("%swarning%s %s%s%s is not executable" , warningColor , resetColor , blueColor , filepath .Base (path ), resetColor )
389401 }
390402
391403 appStreamXML , err := extractAppStreamXML (path )
392404 if err != nil {
393- // If no AppStream data was found or the file is not executable, use flatpakAppStreamScrapper data
394- appData := findAppStreamMetadataForAppId (appBundleID .ShortName () )
405+ log . Printf ( "%swarning%s %s%s%s does not have an AppStream AppData.xml" , warningColor , resetColor , blueColor , path , resetColor )
406+ appData := findAppStreamMetadataForAppId (appBundleID .Name )
395407 if appData != nil {
396- log .Printf ("Using flatpakAppStreamScrapper data for %s\n " , baseFilename )
408+ log .Printf ("Using flatpakAppStreamScrapper data for %s%s%s " , blueColor , baseFilename , resetColor )
397409 if appData .Name != "" {
398410 item .Name = appData .Name
399411 }
@@ -415,11 +427,11 @@ func main() {
415427 if appData .Version != "" {
416428 item .Version = appData .Version
417429 }
418- item .AppstreamId = appBundleID .ShortName ()
430+ item .AppstreamId = appBundleID .Name
419431 }
420432 } else {
421- if getText (appStreamXML .Names ) != "" {
422- item .Name = getText ( appStreamXML . Names )
433+ if name := getText (appStreamXML .Names ); name != "" {
434+ item .Name = name
423435 }
424436 if appStreamXML .Icon != "" {
425437 item .Icon = appStreamXML .Icon
@@ -429,8 +441,8 @@ func main() {
429441 item .Screenshots = append (item .Screenshots , screenshot .Image )
430442 }
431443 }
432- if getText (appStreamXML .Summaries ) != "" {
433- item .Description = getText ( appStreamXML . Summaries )
444+ if summary := getText (appStreamXML .Summaries ); summary != "" {
445+ item .Description = summary
434446 }
435447 if appStreamXML .Description .InnerXML != "" {
436448 item .LongDescription = appStreamXML .Description .InnerXML
@@ -444,7 +456,7 @@ func main() {
444456 })
445457
446458 if err != nil {
447- log .Println ( "Error processing files:" , err )
459+ log .Printf ( "%serror%s processing files: %v" , errorColor , resetColor , err )
448460 return
449461 }
450462
@@ -455,55 +467,52 @@ func main() {
455467 encoder .SetIndent ("" , " " )
456468
457469 if err := encoder .Encode (dbinMetadata ); err != nil {
458- log .Println ( "Error creating JSON:" , err )
470+ log .Printf ( "%serror%s creating JSON: %v" , errorColor , resetColor , err )
459471 return
460472 }
461473
462474 if err := os .WriteFile (* outputJSON , []byte (buffer .String ()), 0644 ); err != nil {
463- log .Println ( "Error writing JSON file:" , err )
475+ log .Printf ( "%serror%s writing JSON file: %v" , errorColor , resetColor , err )
464476 return
465477 }
466478
467- log .Printf ("Successfully wrote JSON output to %s\n " , * outputJSON )
479+ log .Printf ("Successfully wrote JSON output to %s" , * outputJSON )
468480 }
469481
470482 if * outputMarkdown != "" {
471483 markdownContent , err := generateMarkdown (dbinMetadata )
472484 if err != nil {
473- log .Println ( "Error generating Markdown:" , err )
485+ log .Printf ( "%serror%s generating Markdown: %v" , errorColor , resetColor , err )
474486 return
475487 }
476488
477489 if err := os .WriteFile (* outputMarkdown , []byte (markdownContent ), 0644 ); err != nil {
478- log .Println ( "Error writing Markdown file:" , err )
490+ log .Printf ( "%serror%s writing Markdown file: %v" , errorColor , resetColor , err )
479491 return
480492 }
481493
482- log .Printf ("Successfully wrote Markdown output to %s\n " , * outputMarkdown )
494+ log .Printf ("Successfully wrote Markdown output to %s" , * outputMarkdown )
483495 }
484496}
485497
486498func getText (elements []struct {
487499 Lang string `xml:"lang,attr"`
488500 Text string `xml:",chardata"`
489501}) string {
490- // First, try to find explicit English
491502 for _ , elem := range elements {
492503 if elem .Lang == "en" || elem .Lang == "en_US" || elem .Lang == "en_GB" {
493- return elem .Text
504+ return strings . TrimSpace ( elem .Text )
494505 }
495506 }
496507
497- // If no explicit English, look for elements without lang attribute (default)
498508 for _ , elem := range elements {
499509 if elem .Lang == "" {
500- return elem .Text
510+ return strings . TrimSpace ( elem .Text )
501511 }
502512 }
503513
504- // If still nothing, return the first element
505514 if len (elements ) > 0 {
506- return elements [0 ].Text
515+ return strings . TrimSpace ( elements [0 ].Text )
507516 }
508517
509518 return ""
0 commit comments