44 "context"
55 "fmt"
66 "github.com/compose-spec/compose-go/cli"
7+ "github.com/compose-spec/compose-go/loader"
78 "github.com/compose-spec/compose-go/types"
89 "github.com/docker/cli/cli/command"
910 "github.com/docker/cli/cli/flags"
@@ -14,6 +15,7 @@ import (
1415 "os"
1516 "os/signal"
1617 "path/filepath"
18+ "regexp"
1719 "sort"
1820 "strings"
1921 "syscall"
@@ -34,7 +36,7 @@ var options = []string{
3436func main () {
3537 normalizeEnvironment ()
3638
37- dockerizedOptions , commandName , commandArgs := parseArguments ()
39+ dockerizedOptions , commandName , commandVersion , commandArgs := parseArguments ()
3840
3941 var optionHelp = contains (dockerizedOptions , "--help" ) || contains (dockerizedOptions , "-h" )
4042 var optionVerbose = contains (dockerizedOptions , "--verbose" ) || contains (dockerizedOptions , "-v" )
@@ -76,7 +78,55 @@ func main() {
7678 panic (err )
7779 }
7880
79- project , err := getProject (dockerizedDockerComposeFilePath )
81+ if commandVersion != "" {
82+ rawProject , err := getRawProject (dockerizedDockerComposeFilePath )
83+ if err != nil {
84+ panic (err )
85+ }
86+
87+ rawService , err := rawProject .GetService (commandName )
88+
89+ var versionVariableExpected = strings .ReplaceAll (strings .ToUpper (commandName ), "-" , "_" ) + "_VERSION"
90+ var versionVariablesUsed []string
91+ for _ , variable := range ExtractVariables (rawService ) {
92+ if strings .HasSuffix (variable , "_VERSION" ) {
93+ versionVariablesUsed = append (versionVariablesUsed , variable )
94+ }
95+ }
96+
97+ if len (versionVariablesUsed ) == 0 {
98+ fmt .Printf ("Error: Version selection for %s is currently not supported.\n " , commandName )
99+ os .Exit (1 )
100+ }
101+
102+ versionKey := versionVariableExpected
103+
104+ if ! contains (versionVariablesUsed , versionVariableExpected ) {
105+ if len (versionVariablesUsed ) == 1 {
106+ fmt .Printf ("Error: To specify the version of %s, please set %s.\n " ,
107+ commandName ,
108+ versionVariablesUsed [0 ],
109+ )
110+ os .Exit (1 )
111+ } else if len (versionVariablesUsed ) > 1 {
112+ fmt .Println ("Multiple version variables found:" )
113+ for _ , variable := range versionVariablesUsed {
114+ fmt .Println (" " + variable )
115+ }
116+ os .Exit (1 )
117+ }
118+ }
119+
120+ if optionVerbose {
121+ fmt .Printf ("Setting %s to %s...\n " , versionKey , commandVersion )
122+ }
123+ err = os .Setenv (versionKey , commandVersion )
124+ if err != nil {
125+ panic (err )
126+ }
127+ }
128+
129+ project , err := getProject (dockerizedDockerComposeFilePath , true )
80130 if err != nil {
81131 panic (err )
82132 }
@@ -302,7 +352,26 @@ func newSigContext() (context.Context, func()) {
302352 return ctx , cancel
303353}
304354
305- func getProject (dockerComposeFilePath string ) (* types.Project , error ) {
355+ func getRawProject (dockerComposeFilePath string ) (* types.Project , error ) {
356+ options , err := cli .NewProjectOptions ([]string {
357+ dockerComposeFilePath ,
358+ },
359+ cli .WithInterpolation (false ),
360+ cli .WithLoadOptions (func (l * loader.Options ) {
361+ l .SkipValidation = true
362+ l .SkipConsistencyCheck = true
363+ l .SkipNormalization = true
364+ }),
365+ )
366+
367+ if err != nil {
368+ return nil , nil
369+ }
370+
371+ return cli .ProjectFromOptions (options )
372+ }
373+
374+ func getProject (dockerComposeFilePath string , interpolation bool ) (* types.Project , error ) {
306375 options , err := cli .NewProjectOptions ([]string {
307376 dockerComposeFilePath ,
308377 },
@@ -363,7 +432,7 @@ func getBackend() (*api.ServiceProxy, error) {
363432}
364433
365434func dockerComposeBuild (dockerComposeFilePath string , buildOptions api.BuildOptions ) error {
366- project , err := getProject (dockerComposeFilePath )
435+ project , err := getProject (dockerComposeFilePath , true )
367436 if err != nil {
368437 return err
369438 }
@@ -422,12 +491,17 @@ func dockerComposeRun(project *types.Project, runOptions api.RunOptions, volumes
422491}
423492
424493func help (dockerComposeFilePath string ) error {
425- project , err := getProject (dockerComposeFilePath )
494+ project , err := getProject (dockerComposeFilePath , false )
426495 if err != nil {
427496 return err
428497 }
429498
430- fmt .Println ("Usage: dockerized [options] <command> [arguments]" )
499+ fmt .Println ("Usage: dockerized [options] <command>[:version] [arguments]" )
500+ fmt .Println ("" )
501+ fmt .Println ("Examples:" )
502+ fmt .Println (" dockerized go" )
503+ fmt .Println (" dockerized go:1.8 build" )
504+ fmt .Println (" dockerized --shell go" )
431505 fmt .Println ("" )
432506 fmt .Println ("Commands:" )
433507
@@ -455,10 +529,11 @@ func help(dockerComposeFilePath string) error {
455529 return nil
456530}
457531
458- func parseArguments () ([]string , string , []string ) {
532+ func parseArguments () ([]string , string , string , []string ) {
459533 commandName := ""
460534 var commandArgs []string
461535 var dockerizedOptions []string
536+ var commandVersion string
462537 for _ , arg := range os .Args [1 :] {
463538 if arg [0 ] == '-' && commandName == "" {
464539 if contains (options , arg ) {
@@ -475,5 +550,44 @@ func parseArguments() ([]string, string, []string) {
475550 }
476551 }
477552 }
478- return dockerizedOptions , commandName , commandArgs
553+ if strings .ContainsRune (commandName , ':' ) {
554+ commandSplit := strings .Split (commandName , ":" )
555+ commandName = commandSplit [0 ]
556+ commandVersion = commandSplit [1 ]
557+ }
558+ return dockerizedOptions , commandName , commandVersion , commandArgs
559+ }
560+
561+ func unique (s []string ) []string {
562+ keys := make (map [string ]bool )
563+ var list []string
564+ for _ , entry := range s {
565+ if _ , value := keys [entry ]; ! value {
566+ keys [entry ] = true
567+ list = append (list , entry )
568+ }
569+ }
570+ return list
571+ }
572+
573+ func ExtractVariables (rawService types.ServiceConfig ) []string {
574+ var usedVariables []string
575+ for envKey := range rawService .Environment {
576+ usedVariables = append (usedVariables , envKey )
577+ }
578+ if rawService .Build != nil {
579+ for argKey := range rawService .Build .Args {
580+ usedVariables = append (usedVariables , argKey )
581+ }
582+ }
583+
584+ pattern := regexp .MustCompile (`\$\{([^}]+)\}` )
585+ for _ , match := range pattern .FindAllStringSubmatch (rawService .Image , - 1 ) {
586+ usedVariables = append (usedVariables , match [1 ])
587+ }
588+
589+ usedVariables = unique (usedVariables )
590+ sort .Strings (usedVariables )
591+
592+ return usedVariables
479593}
0 commit comments