Skip to content

Commit f547f90

Browse files
committed
appstream-helper: clean up
Signed-off-by: xplshn <anto@xplshn.com.ar>
1 parent 512750d commit f547f90

File tree

1 file changed

+77
-68
lines changed

1 file changed

+77
-68
lines changed

cmd/misc/appstream-helper/appstream-helper.go

Lines changed: 77 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
2835
func init() {
2936
log.SetFlags(0)
3037
}
@@ -104,7 +111,10 @@ var appStreamMetadata []AppStreamMetadata
104111
var appStreamMetadataLoaded bool
105112

106113
type 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

110120
func 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

198212
func 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 {
207222
func 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) {
233248
func 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

486498
func 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

Comments
 (0)