@@ -2,13 +2,16 @@ package main
22
33import (
44 "context"
5+ "debug/buildinfo"
56 "encoding/json"
67 "fmt"
78 "os"
89 "path/filepath"
910 "strings"
1011 "sort"
12+ "time"
1113
14+ pkggodev "github.com/guseggert/pkggodev-client"
1215 "github.com/urfave/cli/v3"
1316)
1417
@@ -25,6 +28,43 @@ type DetectionResult struct {
2528 Directories []* DirectoryInfo `json:"directories"`
2629}
2730
31+ type DbinItem struct {
32+ Pkg string `json:"pkg,omitempty"`
33+ Name string `json:"pkg_name,omitempty"`
34+ PkgId string `json:"pkg_id,omitempty"`
35+ AppstreamId string `json:"app_id,omitempty"`
36+ Icon string `json:"icon,omitempty"`
37+ Description string `json:"description,omitempty"`
38+ LongDescription string `json:"description_long,omitempty"`
39+ Screenshots []string `json:"screenshots,omitempty"`
40+ Version string `json:"version,omitempty"`
41+ DownloadURL string `json:"download_url,omitempty"`
42+ Size string `json:"size,omitempty"`
43+ Bsum string `json:"bsum,omitempty"`
44+ Shasum string `json:"shasum,omitempty"`
45+ BuildDate string `json:"build_date,omitempty"`
46+ SrcURLs []string `json:"src_urls,omitempty"`
47+ WebURLs []string `json:"web_urls,omitempty"`
48+ BuildScript string `json:"build_script,omitempty"`
49+ BuildLog string `json:"build_log,omitempty"`
50+ Categories string `json:"categories,omitempty"`
51+ Snapshots []snapshot `json:"snapshots,omitempty"`
52+ Provides string `json:"provides,omitempty"`
53+ License []string `json:"license,omitempty"`
54+ Maintainers string `json:"maintainers,omitempty"`
55+ Notes []string `json:"notes,omitempty"`
56+ Appstream string `json:"appstream,omitempty"`
57+ Rank uint `json:"rank,omitempty"`
58+ WebManifest string `json:"web_manifest,omitempty"`
59+ }
60+
61+ type snapshot struct {
62+ Commit string `json:"commit,omitempty"`
63+ Version string `json:"version,omitempty"`
64+ }
65+
66+ type DbinMetadata map [string ][]DbinItem
67+
2868func main () {
2969 app := & cli.Command {
3070 Name : "goScrap" ,
@@ -79,6 +119,24 @@ func main() {
79119 },
80120 Action : installAction ,
81121 },
122+ {
123+ Name : "metagen" ,
124+ Usage : "Generate metadata.json for Go binaries in the input directory" ,
125+ Flags : []cli.Flag {
126+ & cli.StringFlag {
127+ Name : "output" ,
128+ Aliases : []string {"o" },
129+ Usage : "Specify output file for metadata (default: metadata.json in input dir)" ,
130+ Value : "" ,
131+ },
132+ & cli.BoolFlag {
133+ Name : "verbose" ,
134+ Usage : "Enable verbose output to stderr" ,
135+ Value : false ,
136+ },
137+ },
138+ Action : metagenAction ,
139+ },
82140 },
83141 }
84142
@@ -88,6 +146,138 @@ func main() {
88146 }
89147}
90148
149+ func metagenAction (ctx context.Context , c * cli.Command ) error {
150+ verbose := c .Bool ("verbose" )
151+ output := c .String ("output" )
152+ inputDir := c .Args ().First ()
153+ if inputDir == "" {
154+ var err error
155+ inputDir , err = os .Getwd ()
156+ if err != nil {
157+ return fmt .Errorf ("failed to get current directory: %w" , err )
158+ }
159+ }
160+
161+ absInputDir , err := filepath .Abs (inputDir )
162+ if err != nil {
163+ return fmt .Errorf ("failed to get absolute path: %w" , err )
164+ }
165+
166+ info , err := os .Stat (absInputDir )
167+ if err != nil {
168+ return fmt .Errorf ("invalid input directory: %w" , err )
169+ }
170+ if ! info .IsDir () {
171+ return fmt .Errorf ("%s is not a directory" , absInputDir )
172+ }
173+
174+ if output == "" {
175+ output = "metadata.json"
176+ }
177+
178+ client := pkggodev .New ()
179+ metadata := make (DbinMetadata )
180+ binaries := make ([]DbinItem , 0 )
181+
182+ err = filepath .Walk (absInputDir , func (path string , info os.FileInfo , err error ) error {
183+ if err != nil {
184+ return err
185+ }
186+ if info .IsDir () {
187+ return nil
188+ }
189+ if filepath .Ext (path ) != "" {
190+ return nil
191+ }
192+
193+ buildInfo , err := buildinfo .ReadFile (path )
194+ if err != nil {
195+ if verbose {
196+ fmt .Fprintf (os .Stderr , "Warning: could not read build info for %s: %v\n " , path , err )
197+ }
198+ return nil
199+ }
200+
201+ if buildInfo .Path == "" || buildInfo .Main .Version == "" {
202+ if verbose {
203+ fmt .Fprintf (os .Stderr , "Warning: incomplete build info for %s\n " , path )
204+ }
205+ return nil
206+ }
207+
208+ pkgInfo , err := client .DescribePackage (pkggodev.DescribePackageRequest {Package : buildInfo .Path })
209+ if err != nil {
210+ if verbose {
211+ fmt .Fprintf (os .Stderr , "Warning: could not fetch package info for %s: %v\n " , buildInfo .Path , err )
212+ }
213+ return nil
214+ }
215+
216+ size := fmt .Sprintf ("%d" , info .Size ())
217+
218+ buildDate := ""
219+ for _ , setting := range buildInfo .Settings {
220+ if setting .Key == "vcs.time" {
221+ buildDate = setting .Value
222+ if parsedTime , err := time .Parse (time .RFC3339 , buildDate ); err == nil {
223+ buildDate = parsedTime .Format ("2006-01-02" )
224+ }
225+ break
226+ }
227+ }
228+ if buildDate == "" {
229+ if verbose {
230+ fmt .Fprintf (os .Stderr , "Warning: no build date found for %s, using file mod time\n " , path )
231+ }
232+ buildDate = info .ModTime ().Format ("2006-01-02" )
233+ }
234+
235+ item := DbinItem {
236+ Pkg : filepath .Base (path ),
237+ Name : filepath .Base (path ),
238+ PkgId : buildInfo .Path ,
239+ Description : "" ,
240+ Version : buildInfo .Main .Version ,
241+ Size : size ,
242+ BuildDate : buildDate ,
243+ SrcURLs : []string {pkgInfo .Repository },
244+ License : []string {pkgInfo .License },
245+ }
246+
247+ binaries = append (binaries , item )
248+ return nil
249+ })
250+
251+ if err != nil {
252+ return fmt .Errorf ("error walking directory: %w" , err )
253+ }
254+
255+ if len (binaries ) == 0 {
256+ return fmt .Errorf ("no valid Go binaries found in %s" , absInputDir )
257+ }
258+
259+ sort .Slice (binaries , func (i , j int ) bool {
260+ return binaries [i ].PkgId < binaries [j ].PkgId
261+ })
262+
263+ metadata ["go" ] = binaries
264+
265+ jsonData , err := json .MarshalIndent (metadata , "" , " " )
266+ if err != nil {
267+ return fmt .Errorf ("failed to marshal JSON: %w" , err )
268+ }
269+
270+ if err := os .WriteFile (output , jsonData , 0644 ); err != nil {
271+ return fmt .Errorf ("failed to write metadata to %s: %w" , output , err )
272+ }
273+
274+ if verbose {
275+ fmt .Fprintf (os .Stderr , "Successfully generated metadata.json with %d binaries\n " , len (binaries ))
276+ }
277+
278+ return nil
279+ }
280+
91281func detectAction (ctx context.Context , c * cli.Command ) error {
92282 verbose := c .Bool ("verbose" )
93283 output := c .String ("output" )
0 commit comments