@@ -13,6 +13,7 @@ import (
1313 "github.com/jfrog/build-info-go/build"
1414 "github.com/jfrog/build-info-go/entities"
1515 "github.com/jfrog/build-info-go/flexpack"
16+ aptflex "github.com/jfrog/build-info-go/flexpack/apt"
1617 "github.com/jfrog/build-info-go/flexpack/conan"
1718 nixflex "github.com/jfrog/build-info-go/flexpack/nix"
1819 gradleflex "github.com/jfrog/build-info-go/flexpack/gradle"
@@ -291,6 +292,61 @@ func GetCommands(logger utils.Log) []*clitool.Command {
291292 return printBuildInfo (buildInfo , formatValue )
292293 },
293294 },
295+ {
296+ Name : "apt" ,
297+ Usage : "Run apt-get install and generate build-info for the installed Debian packages" ,
298+ UsageText : "bi apt install [--from-file pkgs.txt] <packages...>" ,
299+ Flags : flags ,
300+ SkipFlagParsing : true ,
301+ Action : func (context * clitool.Context ) error {
302+ formatValue , positional , err := extractStringFlag (context .Args ().Slice (), formatFlag )
303+ if err != nil {
304+ return err
305+ }
306+ if len (positional ) == 0 {
307+ return fmt .Errorf ("bi apt requires arguments, e.g.: bi apt install curl" )
308+ }
309+
310+ // Run apt-get install first (bi apt always runs the real install)
311+ if positional [0 ] == "install" {
312+ // Extract --from-file before forwarding to apt-get.
313+ fromFile , installArgs := extractFromFileArg (positional [1 :])
314+ if fromFile != "" {
315+ pkgs , err := aptflex .ReadPackagesFile (fromFile )
316+ if err != nil {
317+ return fmt .Errorf ("--from-file %s: %w" , fromFile , err )
318+ }
319+ installArgs = append (installArgs , pkgs ... )
320+ }
321+ if len (installArgs ) == 0 {
322+ return fmt .Errorf ("bi apt install: no packages specified" )
323+ }
324+
325+ collector := aptflex .NewAptFlexPack (aptflex.AptConfig {})
326+
327+ installCmd := exec .Command ("apt-get" , append ([]string {"install" }, installArgs ... )... )
328+ installCmd .Stdout = os .Stdout
329+ installCmd .Stderr = os .Stderr
330+ installCmd .Stdin = os .Stdin
331+ if err := installCmd .Run (); err != nil {
332+ return fmt .Errorf ("apt-get install failed: %w" , err )
333+ }
334+
335+ // Collect build-info after the install completes.
336+ if err := collector .CollectDependencies (installArgs ); err != nil {
337+ return fmt .Errorf ("apt build-info collection failed: %w" , err )
338+ }
339+ buildInfo , err := collector .CollectBuildInfo ("apt-build" , "1" , "" )
340+ if err != nil {
341+ return fmt .Errorf ("apt build-info assembly failed: %w" , err )
342+ }
343+ return printBuildInfo (buildInfo , formatValue )
344+ }
345+
346+ // For non-install subcommands, pass through to apt-get.
347+ return exec .Command ("apt-get" , positional ... ).Run ()
348+ },
349+ },
294350 {
295351 Name : "nix" ,
296352 Usage : "Generate build-info for a Nix project (works for channels and flakes)" ,
@@ -621,6 +677,25 @@ func printBuildInfo(buildInfo *entities.BuildInfo, format string) error {
621677 return nil
622678}
623679
680+ // extractFromFileArg pulls --from-file <path> out of args, returning the path
681+ // and the remaining args (without the flag pair).
682+ func extractFromFileArg (args []string ) (fromFile string , remaining []string ) {
683+ for i , a := range args {
684+ if a == "--from-file" && i + 1 < len (args ) {
685+ fromFile = args [i + 1 ]
686+ remaining = append (args [:i :i ], args [i + 2 :]... )
687+ return
688+ }
689+ if strings .HasPrefix (a , "--from-file=" ) {
690+ fromFile = strings .TrimPrefix (a , "--from-file=" )
691+ remaining = append (args [:i :i ], args [i + 1 :]... )
692+ return
693+ }
694+ }
695+ remaining = args
696+ return
697+ }
698+
624699func filterCliFlags (allArgs []string , cliFlags []clitool.Flag ) []string {
625700 var filteredArgs []string
626701 for _ , arg := range allArgs {
0 commit comments