Best Rod code snippet using utils.AbsolutePaths
utils.go
Source:utils.go  
1package config2import (3	"errors"4	"fmt"5	"os"6	"path/filepath"7	"strconv"8	"strings"9	g "github.com/cloudposse/atmos/pkg/globals"10	u "github.com/cloudposse/atmos/pkg/utils"11)12// FindAllStackConfigsInPathsForStack finds all stack config files in the paths specified by globs for the provided stack13func FindAllStackConfigsInPathsForStack(14	stack string,15	includeStackPaths []string,16	excludeStackPaths []string,17) ([]string, []string, bool, error) {18	var absolutePaths []string19	var relativePaths []string20	var stackIsDir = strings.IndexAny(stack, "/") > 021	for _, p := range includeStackPaths {22		pathWithExt := p23		ext := filepath.Ext(p)24		if ext == "" {25			ext = g.DefaultStackConfigFileExtension26			pathWithExt = p + ext27		}28		// Find all matches in the glob29		matches, err := u.GetGlobMatches(pathWithExt)30		if err != nil || len(matches) == 0 {31			// Retry (b/c we are using `doublestar` library, and it sometimes has issues reading many files in a Docker container)32			// TODO: review `doublestar` library33			matches, err = u.GetGlobMatches(pathWithExt)34			if err != nil {35				return nil, nil, false, err36			}37		}38		// Exclude files that match any of the excludePaths39		for _, matchedFileAbsolutePath := range matches {40			matchedFileRelativePath := u.TrimBasePathFromPath(ProcessedConfig.StacksBaseAbsolutePath+"/", matchedFileAbsolutePath)41			// Check if the provided stack matches a file in the config folders (excluding the files from `excludeStackPaths`)42			stackMatch := strings.HasSuffix(matchedFileAbsolutePath, stack+g.DefaultStackConfigFileExtension)43			if stackMatch {44				allExcluded := true45				for _, excludePath := range excludeStackPaths {46					excludeMatch, err := u.PathMatch(excludePath, matchedFileAbsolutePath)47					if err != nil {48						u.PrintError(err)49						continue50					} else if excludeMatch {51						allExcluded = false52						break53					}54				}55				if allExcluded && stackIsDir {56					return []string{matchedFileAbsolutePath}, []string{matchedFileRelativePath}, true, nil57				}58			}59			include := true60			for _, excludePath := range excludeStackPaths {61				excludeMatch, err := u.PathMatch(excludePath, matchedFileAbsolutePath)62				if err != nil {63					u.PrintError(err)64					include = false65					continue66				} else if excludeMatch {67					include = false68					continue69				}70			}71			if include {72				absolutePaths = append(absolutePaths, matchedFileAbsolutePath)73				relativePaths = append(relativePaths, matchedFileRelativePath)74			}75		}76	}77	return absolutePaths, relativePaths, false, nil78}79// FindAllStackConfigsInPaths finds all stack config files in the paths specified by globs80func FindAllStackConfigsInPaths(81	includeStackPaths []string,82	excludeStackPaths []string,83) ([]string, []string, error) {84	var absolutePaths []string85	var relativePaths []string86	for _, p := range includeStackPaths {87		pathWithExt := p88		ext := filepath.Ext(p)89		if ext == "" {90			ext = g.DefaultStackConfigFileExtension91			pathWithExt = p + ext92		}93		// Find all matches in the glob94		matches, err := u.GetGlobMatches(pathWithExt)95		if err != nil || len(matches) == 0 {96			// Retry (b/c we are using `doublestar` library, and it sometimes has issues reading many files in a Docker container)97			// TODO: review `doublestar` library98			matches, err = u.GetGlobMatches(pathWithExt)99			if err != nil {100				return nil, nil, err101			}102		}103		// Exclude files that match any of the excludePaths104		for _, matchedFileAbsolutePath := range matches {105			matchedFileRelativePath := u.TrimBasePathFromPath(ProcessedConfig.StacksBaseAbsolutePath+"/", matchedFileAbsolutePath)106			include := true107			for _, excludePath := range excludeStackPaths {108				excludeMatch, err := u.PathMatch(excludePath, matchedFileAbsolutePath)109				if err != nil {110					u.PrintError(err)111					include = false112					continue113				} else if excludeMatch {114					include = false115					continue116				}117			}118			if include {119				absolutePaths = append(absolutePaths, matchedFileAbsolutePath)120				relativePaths = append(relativePaths, matchedFileRelativePath)121			}122		}123	}124	return absolutePaths, relativePaths, nil125}126func processEnvVars() error {127	basePath := os.Getenv("ATMOS_BASE_PATH")128	if len(basePath) > 0 {129		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_BASE_PATH=%s", basePath))130		Config.BasePath = basePath131	}132	stacksBasePath := os.Getenv("ATMOS_STACKS_BASE_PATH")133	if len(stacksBasePath) > 0 {134		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_STACKS_BASE_PATH=%s", stacksBasePath))135		Config.Stacks.BasePath = stacksBasePath136	}137	stacksIncludedPaths := os.Getenv("ATMOS_STACKS_INCLUDED_PATHS")138	if len(stacksIncludedPaths) > 0 {139		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_STACKS_INCLUDED_PATHS=%s", stacksIncludedPaths))140		Config.Stacks.IncludedPaths = strings.Split(stacksIncludedPaths, ",")141	}142	stacksExcludedPaths := os.Getenv("ATMOS_STACKS_EXCLUDED_PATHS")143	if len(stacksExcludedPaths) > 0 {144		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_STACKS_EXCLUDED_PATHS=%s", stacksExcludedPaths))145		Config.Stacks.ExcludedPaths = strings.Split(stacksExcludedPaths, ",")146	}147	stacksNamePattern := os.Getenv("ATMOS_STACKS_NAME_PATTERN")148	if len(stacksNamePattern) > 0 {149		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_STACKS_NAME_PATTERN=%s", stacksNamePattern))150		Config.Stacks.NamePattern = stacksNamePattern151	}152	componentsTerraformBasePath := os.Getenv("ATMOS_COMPONENTS_TERRAFORM_BASE_PATH")153	if len(componentsTerraformBasePath) > 0 {154		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_TERRAFORM_BASE_PATH=%s", componentsTerraformBasePath))155		Config.Components.Terraform.BasePath = componentsTerraformBasePath156	}157	componentsTerraformApplyAutoApprove := os.Getenv("ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE")158	if len(componentsTerraformApplyAutoApprove) > 0 {159		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=%s", componentsTerraformApplyAutoApprove))160		applyAutoApproveBool, err := strconv.ParseBool(componentsTerraformApplyAutoApprove)161		if err != nil {162			return err163		}164		Config.Components.Terraform.ApplyAutoApprove = applyAutoApproveBool165	}166	componentsTerraformDeployRunInit := os.Getenv("ATMOS_COMPONENTS_TERRAFORM_DEPLOY_RUN_INIT")167	if len(componentsTerraformDeployRunInit) > 0 {168		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_TERRAFORM_DEPLOY_RUN_INIT=%s", componentsTerraformDeployRunInit))169		deployRunInitBool, err := strconv.ParseBool(componentsTerraformDeployRunInit)170		if err != nil {171			return err172		}173		Config.Components.Terraform.DeployRunInit = deployRunInitBool174	}175	componentsInitRunReconfigure := os.Getenv("ATMOS_COMPONENTS_TERRAFORM_INIT_RUN_RECONFIGURE")176	if len(componentsInitRunReconfigure) > 0 {177		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_TERRAFORM_INIT_RUN_RECONFIGURE=%s", componentsInitRunReconfigure))178		initRunReconfigureBool, err := strconv.ParseBool(componentsInitRunReconfigure)179		if err != nil {180			return err181		}182		Config.Components.Terraform.InitRunReconfigure = initRunReconfigureBool183	}184	componentsTerraformAutoGenerateBackendFile := os.Getenv("ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_BACKEND_FILE")185	if len(componentsTerraformAutoGenerateBackendFile) > 0 {186		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_BACKEND_FILE=%s", componentsTerraformAutoGenerateBackendFile))187		componentsTerraformAutoGenerateBackendFileBool, err := strconv.ParseBool(componentsTerraformAutoGenerateBackendFile)188		if err != nil {189			return err190		}191		Config.Components.Terraform.AutoGenerateBackendFile = componentsTerraformAutoGenerateBackendFileBool192	}193	componentsHelmfileBasePath := os.Getenv("ATMOS_COMPONENTS_HELMFILE_BASE_PATH")194	if len(componentsHelmfileBasePath) > 0 {195		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_HELMFILE_BASE_PATH=%s", componentsHelmfileBasePath))196		Config.Components.Helmfile.BasePath = componentsHelmfileBasePath197	}198	componentsHelmfileKubeconfigPath := os.Getenv("ATMOS_COMPONENTS_HELMFILE_KUBECONFIG_PATH")199	if len(componentsHelmfileKubeconfigPath) > 0 {200		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_HELMFILE_KUBECONFIG_PATH=%s", componentsHelmfileKubeconfigPath))201		Config.Components.Helmfile.KubeconfigPath = componentsHelmfileKubeconfigPath202	}203	componentsHelmfileHelmAwsProfilePattern := os.Getenv("ATMOS_COMPONENTS_HELMFILE_HELM_AWS_PROFILE_PATTERN")204	if len(componentsHelmfileHelmAwsProfilePattern) > 0 {205		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_HELMFILE_HELM_AWS_PROFILE_PATTERN=%s", componentsHelmfileHelmAwsProfilePattern))206		Config.Components.Helmfile.HelmAwsProfilePattern = componentsHelmfileHelmAwsProfilePattern207	}208	componentsHelmfileClusterNamePattern := os.Getenv("ATMOS_COMPONENTS_HELMFILE_CLUSTER_NAME_PATTERN")209	if len(componentsHelmfileClusterNamePattern) > 0 {210		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_HELMFILE_CLUSTER_NAME_PATTERN=%s", componentsHelmfileClusterNamePattern))211		Config.Components.Helmfile.ClusterNamePattern = componentsHelmfileClusterNamePattern212	}213	workflowsBasePath := os.Getenv("ATMOS_WORKFLOWS_BASE_PATH")214	if len(workflowsBasePath) > 0 {215		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_WORKFLOWS_BASE_PATH=%s", workflowsBasePath))216		Config.Workflows.BasePath = workflowsBasePath217	}218	jsonschemaBasePath := os.Getenv("ATMOS_SCHEMAS_JSONSCHEMA_BASE_PATH")219	if len(jsonschemaBasePath) > 0 {220		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_SCHEMAS_JSONSCHEMA_BASE_PATH=%s", jsonschemaBasePath))221		Config.Schemas.JsonSchema.BasePath = jsonschemaBasePath222	}223	opaBasePath := os.Getenv("ATMOS_SCHEMAS_OPA_BASE_PATH")224	if len(opaBasePath) > 0 {225		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_SCHEMAS_OPA_BASE_PATH=%s", opaBasePath))226		Config.Schemas.Opa.BasePath = opaBasePath227	}228	cueBasePath := os.Getenv("ATMOS_SCHEMAS_CUE_BASE_PATH")229	if len(cueBasePath) > 0 {230		u.PrintInfoVerbose(fmt.Sprintf("Found ENV var ATMOS_SCHEMAS_CUE_BASE_PATH=%s", cueBasePath))231		Config.Schemas.Cue.BasePath = cueBasePath232	}233	return nil234}235func checkConfig() error {236	if len(Config.Stacks.BasePath) < 1 {237		return errors.New("stack base path must be provided in 'stacks.base_path' config or ATMOS_STACKS_BASE_PATH' ENV variable")238	}239	if len(Config.Stacks.IncludedPaths) < 1 {240		return errors.New("at least one path must be provided in 'stacks.included_paths' config or ATMOS_STACKS_INCLUDED_PATHS' ENV variable")241	}242	return nil243}244func processCommandLineArgs(configAndStacksInfo ConfigAndStacksInfo) error {245	if len(configAndStacksInfo.BasePath) > 0 {246		Config.BasePath = configAndStacksInfo.BasePath247		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as base path for stacks and components", configAndStacksInfo.BasePath))248	}249	if len(configAndStacksInfo.TerraformDir) > 0 {250		Config.Components.Terraform.BasePath = configAndStacksInfo.TerraformDir251		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as terraform directory", configAndStacksInfo.TerraformDir))252	}253	if len(configAndStacksInfo.HelmfileDir) > 0 {254		Config.Components.Helmfile.BasePath = configAndStacksInfo.HelmfileDir255		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as helmfile directory", configAndStacksInfo.HelmfileDir))256	}257	if len(configAndStacksInfo.ConfigDir) > 0 {258		Config.Stacks.BasePath = configAndStacksInfo.ConfigDir259		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as stacks directory", configAndStacksInfo.ConfigDir))260	}261	if len(configAndStacksInfo.StacksDir) > 0 {262		Config.Stacks.BasePath = configAndStacksInfo.StacksDir263		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as stacks directory", configAndStacksInfo.StacksDir))264	}265	if len(configAndStacksInfo.DeployRunInit) > 0 {266		deployRunInitBool, err := strconv.ParseBool(configAndStacksInfo.DeployRunInit)267		if err != nil {268			return err269		}270		Config.Components.Terraform.DeployRunInit = deployRunInitBool271		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s=%s'", g.DeployRunInitFlag, configAndStacksInfo.DeployRunInit))272	}273	if len(configAndStacksInfo.AutoGenerateBackendFile) > 0 {274		autoGenerateBackendFileBool, err := strconv.ParseBool(configAndStacksInfo.AutoGenerateBackendFile)275		if err != nil {276			return err277		}278		Config.Components.Terraform.AutoGenerateBackendFile = autoGenerateBackendFileBool279		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s=%s'", g.AutoGenerateBackendFileFlag, configAndStacksInfo.AutoGenerateBackendFile))280	}281	if len(configAndStacksInfo.WorkflowsDir) > 0 {282		Config.Workflows.BasePath = configAndStacksInfo.WorkflowsDir283		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as workflows directory", configAndStacksInfo.WorkflowsDir))284	}285	if len(configAndStacksInfo.InitRunReconfigure) > 0 {286		initRunReconfigureBool, err := strconv.ParseBool(configAndStacksInfo.InitRunReconfigure)287		if err != nil {288			return err289		}290		Config.Components.Terraform.InitRunReconfigure = initRunReconfigureBool291		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s=%s'", g.InitRunReconfigure, configAndStacksInfo.InitRunReconfigure))292	}293	if len(configAndStacksInfo.JsonSchemaDir) > 0 {294		Config.Schemas.JsonSchema.BasePath = configAndStacksInfo.JsonSchemaDir295		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as JsonSchema schemas directory", configAndStacksInfo.JsonSchemaDir))296	}297	if len(configAndStacksInfo.OpaDir) > 0 {298		Config.Schemas.Opa.BasePath = configAndStacksInfo.OpaDir299		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as OPA schemas directory", configAndStacksInfo.OpaDir))300	}301	if len(configAndStacksInfo.CueDir) > 0 {302		Config.Schemas.Cue.BasePath = configAndStacksInfo.CueDir303		u.PrintInfoVerbose(fmt.Sprintf("Using command line argument '%s' as CUE schemas directory", configAndStacksInfo.CueDir))304	}305	return nil306}307func processLogsConfig() error {308	logVerbose := os.Getenv("ATMOS_LOGS_VERBOSE")309	if len(logVerbose) > 0 {310		u.PrintInfo(fmt.Sprintf("Found ENV var ATMOS_LOGS_VERBOSE=%s", logVerbose))311		logVerboseBool, err := strconv.ParseBool(logVerbose)312		if err != nil {313			return err314		}315		Config.Logs.Verbose = logVerboseBool316		g.LogVerbose = logVerboseBool317	}318	return nil319}320// GetContextFromVars creates a context object from the provided variables321func GetContextFromVars(vars map[any]any) Context {322	var context Context323	if namespace, ok := vars["namespace"].(string); ok {324		context.Namespace = namespace325	}326	if tenant, ok := vars["tenant"].(string); ok {327		context.Tenant = tenant328	}329	if environment, ok := vars["environment"].(string); ok {330		context.Environment = environment331	}332	if stage, ok := vars["stage"].(string); ok {333		context.Stage = stage334	}335	if region, ok := vars["region"].(string); ok {336		context.Region = region337	}338	if attributes, ok := vars["attributes"].([]string); ok {339		context.Attributes = attributes340	}341	return context342}343// GetContextPrefix calculates context prefix from the context344func GetContextPrefix(stack string, context Context, stackNamePattern string, stackFile string) (string, error) {345	if len(stackNamePattern) == 0 {346		return "",347			errors.New("stack name pattern must be provided in 'stacks.name_pattern' config or 'ATMOS_STACKS_NAME_PATTERN' ENV variable")348	}349	contextPrefix := ""350	stackNamePatternParts := strings.Split(stackNamePattern, "-")351	for _, part := range stackNamePatternParts {352		if part == "{namespace}" {353			if len(context.Namespace) == 0 {354				return "",355					fmt.Errorf("the stack name pattern '%s' specifies 'namespace`, but the stack '%s' does not have a namespace defined in the stack file '%s'",356						stackNamePattern,357						stack,358						stackFile,359					)360			}361			if len(contextPrefix) == 0 {362				contextPrefix = context.Namespace363			} else {364				contextPrefix = contextPrefix + "-" + context.Namespace365			}366		} else if part == "{tenant}" {367			if len(context.Tenant) == 0 {368				return "",369					fmt.Errorf("the stack name pattern '%s' specifies 'tenant`, but the stack '%s' does not have a tenant defined in the stack file '%s'",370						stackNamePattern,371						stack,372						stackFile,373					)374			}375			if len(contextPrefix) == 0 {376				contextPrefix = context.Tenant377			} else {378				contextPrefix = contextPrefix + "-" + context.Tenant379			}380		} else if part == "{environment}" {381			if len(context.Environment) == 0 {382				return "",383					fmt.Errorf("the stack name pattern '%s' specifies 'environment`, but the stack '%s' does not have an environment defined in the stack file '%s'",384						stackNamePattern,385						stack,386						stackFile,387					)388			}389			if len(contextPrefix) == 0 {390				contextPrefix = context.Environment391			} else {392				contextPrefix = contextPrefix + "-" + context.Environment393			}394		} else if part == "{stage}" {395			if len(context.Stage) == 0 {396				return "",397					fmt.Errorf("the stack name pattern '%s' specifies 'stage`, but the stack '%s' does not have a stage defined in the stack file '%s'",398						stackNamePattern,399						stack,400						stackFile,401					)402			}403			if len(contextPrefix) == 0 {404				contextPrefix = context.Stage405			} else {406				contextPrefix = contextPrefix + "-" + context.Stage407			}408		}409	}410	return contextPrefix, nil411}412// ReplaceContextTokens replaces context tokens in the provided pattern and returns a string with all the tokens replaced413func ReplaceContextTokens(context Context, pattern string) string {414	r := strings.NewReplacer(415		"{base-component}", context.BaseComponent,416		"{component}", context.Component,417		"{component-path}", context.ComponentPath,418		"{namespace}", context.Namespace,419		"{environment}", context.Environment,420		"{region}", context.Region,421		"{tenant}", context.Tenant,422		"{stage}", context.Stage,423		"{workspace}", context.Workspace,424		"{attributes}", strings.Join(context.Attributes, "-"),425	)426	return r.Replace(pattern)427}428// GetStackNameFromContextAndStackNamePattern calculates stack name from the provided context using the provided stack name pattern429func GetStackNameFromContextAndStackNamePattern(430	namespace string,431	tenant string,432	environment string,433	stage string,434	stackNamePattern string,435) (string, error) {436	if len(stackNamePattern) == 0 {437		return "",438			fmt.Errorf("stack name pattern must be provided")439	}440	var stack string441	stackNamePatternParts := strings.Split(stackNamePattern, "-")442	for _, part := range stackNamePatternParts {443		if part == "{namespace}" {444			if len(namespace) == 0 {445				return "", fmt.Errorf("stack name pattern '%s' includes '{namespace}', but namespace is not provided", stackNamePattern)446			}447			if len(stack) == 0 {448				stack = namespace449			} else {450				stack = fmt.Sprintf("%s-%s", stack, namespace)451			}452		} else if part == "{tenant}" {453			if len(tenant) == 0 {454				return "", fmt.Errorf("stack name pattern '%s' includes '{tenant}', but tenant is not provided", stackNamePattern)455			}456			if len(stack) == 0 {457				stack = tenant458			} else {459				stack = fmt.Sprintf("%s-%s", stack, tenant)460			}461		} else if part == "{environment}" {462			if len(environment) == 0 {463				return "", fmt.Errorf("stack name pattern '%s' includes '{environment}', but environment is not provided", stackNamePattern)464			}465			if len(stack) == 0 {466				stack = environment467			} else {468				stack = fmt.Sprintf("%s-%s", stack, environment)469			}470		} else if part == "{stage}" {471			if len(stage) == 0 {472				return "", fmt.Errorf("stack name pattern '%s' includes '{stage}', but stage is not provided", stackNamePattern)473			}474			if len(stack) == 0 {475				stack = stage476			} else {477				stack = fmt.Sprintf("%s-%s", stack, stage)478			}479		}480	}481	return stack, nil482}...options.go
Source:options.go  
1/*2   Copyright 2020 The Compose Specification Authors.3   Licensed under the Apache License, Version 2.0 (the "License");4   you may not use this file except in compliance with the License.5   You may obtain a copy of the License at6       http://www.apache.org/licenses/LICENSE-2.07   Unless required by applicable law or agreed to in writing, software8   distributed under the License is distributed on an "AS IS" BASIS,9   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.10   See the License for the specific language governing permissions and11   limitations under the License.12*/13package cli14import (15	"fmt"16	"io"17	"os"18	"path/filepath"19	"strings"20	"github.com/compose-spec/compose-go/consts"21	"github.com/compose-spec/compose-go/dotenv"22	"github.com/compose-spec/compose-go/errdefs"23	"github.com/compose-spec/compose-go/loader"24	"github.com/compose-spec/compose-go/types"25	"github.com/compose-spec/compose-go/utils"26	"github.com/pkg/errors"27	"github.com/sirupsen/logrus"28)29// ProjectOptions groups the command line options recommended for a Compose implementation30type ProjectOptions struct {31	Name        string32	WorkingDir  string33	ConfigPaths []string34	Environment map[string]string35	EnvFile     string36	loadOptions []func(*loader.Options)37}38type ProjectOptionsFn func(*ProjectOptions) error39// NewProjectOptions creates ProjectOptions40func NewProjectOptions(configs []string, opts ...ProjectOptionsFn) (*ProjectOptions, error) {41	options := &ProjectOptions{42		ConfigPaths: configs,43		Environment: map[string]string{},44	}45	for _, o := range opts {46		err := o(options)47		if err != nil {48			return nil, err49		}50	}51	return options, nil52}53// WithName defines ProjectOptions' name54func WithName(name string) ProjectOptionsFn {55	return func(o *ProjectOptions) error {56		if name != loader.NormalizeProjectName(name) {57			return fmt.Errorf("%q is not a valid project name", name)58		}59		o.Name = name60		return nil61	}62}63// WithWorkingDirectory defines ProjectOptions' working directory64func WithWorkingDirectory(wd string) ProjectOptionsFn {65	return func(o *ProjectOptions) error {66		if wd == "" {67			return nil68		}69		abs, err := filepath.Abs(wd)70		if err != nil {71			return err72		}73		o.WorkingDir = abs74		return nil75	}76}77// WithConfigFileEnv allow to set compose config file paths by COMPOSE_FILE environment variable78func WithConfigFileEnv(o *ProjectOptions) error {79	if len(o.ConfigPaths) > 0 {80		return nil81	}82	sep := o.Environment[consts.ComposePathSeparator]83	if sep == "" {84		sep = string(os.PathListSeparator)85	}86	f, ok := o.Environment[consts.ComposeFilePath]87	if ok {88		paths, err := absolutePaths(strings.Split(f, sep))89		o.ConfigPaths = paths90		return err91	}92	return nil93}94// WithDefaultConfigPath searches for default config files from working directory95func WithDefaultConfigPath(o *ProjectOptions) error {96	if len(o.ConfigPaths) > 0 {97		return nil98	}99	pwd, err := o.GetWorkingDir()100	if err != nil {101		return err102	}103	for {104		candidates := findFiles(DefaultFileNames, pwd)105		if len(candidates) > 0 {106			winner := candidates[0]107			if len(candidates) > 1 {108				logrus.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", "))109				logrus.Warnf("Using %s", winner)110			}111			o.ConfigPaths = append(o.ConfigPaths, winner)112			overrides := findFiles(DefaultOverrideFileNames, pwd)113			if len(overrides) > 0 {114				if len(overrides) > 1 {115					logrus.Warnf("Found multiple override files with supported names: %s", strings.Join(overrides, ", "))116					logrus.Warnf("Using %s", overrides[0])117				}118				o.ConfigPaths = append(o.ConfigPaths, overrides[0])119			}120			return nil121		}122		parent := filepath.Dir(pwd)123		if parent == pwd {124			// no config file found, but that's not a blocker if caller only needs project name125			return nil126		}127		pwd = parent128	}129}130// WithEnv defines a key=value set of variables used for compose file interpolation131func WithEnv(env []string) ProjectOptionsFn {132	return func(o *ProjectOptions) error {133		for k, v := range utils.GetAsEqualsMap(env) {134			o.Environment[k] = v135		}136		return nil137	}138}139// WithDiscardEnvFiles sets discards the `env_file` section after resolving to140// the `environment` section141func WithDiscardEnvFile(o *ProjectOptions) error {142	o.loadOptions = append(o.loadOptions, loader.WithDiscardEnvFiles)143	return nil144}145// WithLoadOptions provides a hook to control how compose files are loaded146func WithLoadOptions(loadOptions ...func(*loader.Options)) ProjectOptionsFn {147	return func(o *ProjectOptions) error {148		o.loadOptions = append(o.loadOptions, loadOptions...)149		return nil150	}151}152// WithOsEnv imports environment variables from OS153func WithOsEnv(o *ProjectOptions) error {154	for k, v := range utils.GetAsEqualsMap(os.Environ()) {155		if _, set := o.Environment[k]; set {156			continue157		}158		o.Environment[k] = v159	}160	return nil161}162// WithEnvFile set an alternate env file163func WithEnvFile(file string) ProjectOptionsFn {164	return func(options *ProjectOptions) error {165		options.EnvFile = file166		return nil167	}168}169// WithDotEnv imports environment variables from .env file170func WithDotEnv(o *ProjectOptions) error {171	wd, err := o.GetWorkingDir()172	if err != nil {173		return err174	}175	envMap, err := GetEnvFromFile(o.Environment, wd, o.EnvFile)176	if err != nil {177		return err178	}179	for k, v := range envMap {180		o.Environment[k] = v181		if osVal, ok := os.LookupEnv(k); ok {182			o.Environment[k] = osVal183		}184	}185	return nil186}187func GetEnvFromFile(currentEnv map[string]string, workingDir string, filename string) (map[string]string, error) {188	envMap := make(map[string]string)189	dotEnvFile := filename190	if dotEnvFile == "" {191		dotEnvFile = filepath.Join(workingDir, ".env")192	}193	abs, err := filepath.Abs(dotEnvFile)194	if err != nil {195		return envMap, err196	}197	dotEnvFile = abs198	s, err := os.Stat(dotEnvFile)199	if os.IsNotExist(err) {200		if filename != "" {201			return nil, errors.Errorf("Couldn't find env file: %s", filename)202		}203		return envMap, nil204	}205	if err != nil {206		return envMap, err207	}208	if s.IsDir() {209		if filename == "" {210			return envMap, nil211		}212		return envMap, errors.Errorf("%s is a directory", dotEnvFile)213	}214	file, err := os.Open(dotEnvFile)215	if err != nil {216		return envMap, err217	}218	defer file.Close()219	env, err := dotenv.ParseWithLookup(file, func(k string) (string, bool) {220		v, ok := currentEnv[k]221		if !ok {222			return "", false223		}224		return v, true225	})226	if err != nil {227		return envMap, err228	}229	for k, v := range env {230		envMap[k] = v231	}232	return envMap, nil233}234// WithInterpolation set ProjectOptions to enable/skip interpolation235func WithInterpolation(interpolation bool) ProjectOptionsFn {236	return func(o *ProjectOptions) error {237		o.loadOptions = append(o.loadOptions, func(options *loader.Options) {238			options.SkipInterpolation = !interpolation239		})240		return nil241	}242}243// WithNormalization set ProjectOptions to enable/skip normalization244func WithNormalization(normalization bool) ProjectOptionsFn {245	return func(o *ProjectOptions) error {246		o.loadOptions = append(o.loadOptions, func(options *loader.Options) {247			options.SkipNormalization = !normalization248		})249		return nil250	}251}252// WithResolvedPaths set ProjectOptions to enable paths resolution253func WithResolvedPaths(resolve bool) ProjectOptionsFn {254	return func(o *ProjectOptions) error {255		o.loadOptions = append(o.loadOptions, func(options *loader.Options) {256			options.ResolvePaths = resolve257		})258		return nil259	}260}261// DefaultFileNames defines the Compose file names for auto-discovery (in order of preference)262var DefaultFileNames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"}263// DefaultOverrideFileNames defines the Compose override file names for auto-discovery (in order of preference)264var DefaultOverrideFileNames = []string{"compose.override.yml", "compose.override.yaml", "docker-compose.override.yml", "docker-compose.override.yaml"}265func (o ProjectOptions) GetWorkingDir() (string, error) {266	if o.WorkingDir != "" {267		return o.WorkingDir, nil268	}269	for _, path := range o.ConfigPaths {270		if path != "-" {271			absPath, err := filepath.Abs(path)272			if err != nil {273				return "", err274			}275			return filepath.Dir(absPath), nil276		}277	}278	return os.Getwd()279}280// ProjectFromOptions load a compose project based on command line options281func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {282	configPaths, err := getConfigPathsFromOptions(options)283	if err != nil {284		return nil, err285	}286	var configs []types.ConfigFile287	for _, f := range configPaths {288		var b []byte289		if f == "-" {290			b, err = io.ReadAll(os.Stdin)291			if err != nil {292				return nil, err293			}294		} else {295			f, err := filepath.Abs(f)296			if err != nil {297				return nil, err298			}299			b, err = os.ReadFile(f)300			if err != nil {301				return nil, err302			}303		}304		configs = append(configs, types.ConfigFile{305			Filename: f,306			Content:  b,307		})308	}309	workingDir, err := options.GetWorkingDir()310	if err != nil {311		return nil, err312	}313	absWorkingDir, err := filepath.Abs(workingDir)314	if err != nil {315		return nil, err316	}317	options.loadOptions = append(options.loadOptions,318		withNamePrecedenceLoad(absWorkingDir, options),319		withConvertWindowsPaths(options))320	project, err := loader.Load(types.ConfigDetails{321		ConfigFiles: configs,322		WorkingDir:  workingDir,323		Environment: options.Environment,324	}, options.loadOptions...)325	if err != nil {326		return nil, err327	}328	project.ComposeFiles = configPaths329	return project, nil330}331func withNamePrecedenceLoad(absWorkingDir string, options *ProjectOptions) func(*loader.Options) {332	return func(opts *loader.Options) {333		if options.Name != "" {334			opts.SetProjectName(options.Name, true)335		} else if nameFromEnv, ok := options.Environment[consts.ComposeProjectName]; ok && nameFromEnv != "" {336			opts.SetProjectName(nameFromEnv, true)337		} else {338			opts.SetProjectName(filepath.Base(absWorkingDir), false)339		}340	}341}342func withConvertWindowsPaths(options *ProjectOptions) func(*loader.Options) {343	return func(o *loader.Options) {344		o.ConvertWindowsPaths = utils.StringToBool(options.Environment["COMPOSE_CONVERT_WINDOWS_PATHS"])345		o.ResolvePaths = true346	}347}348// getConfigPathsFromOptions retrieves the config files for project based on project options349func getConfigPathsFromOptions(options *ProjectOptions) ([]string, error) {350	if len(options.ConfigPaths) != 0 {351		return absolutePaths(options.ConfigPaths)352	}353	return nil, errors.Wrap(errdefs.ErrNotFound, "no configuration file provided")354}355func findFiles(names []string, pwd string) []string {356	candidates := []string{}357	for _, n := range names {358		f := filepath.Join(pwd, n)359		if _, err := os.Stat(f); err == nil {360			candidates = append(candidates, f)361		}362	}363	return candidates364}365func absolutePaths(p []string) ([]string, error) {366	var paths []string367	for _, f := range p {368		if f == "-" {369			paths = append(paths, f)370			continue371		}372		abs, err := filepath.Abs(f)373		if err != nil {374			return nil, err375		}376		f = abs377		if _, err := os.Stat(f); err != nil {378			return nil, err379		}380		paths = append(paths, f)381	}382	return paths, nil383}...fileutils.go
Source:fileutils.go  
...9		panic(err)10	}11	return ex12}13func RelativeToAbsolutePaths(relativePaths []string) []string {14	currentFolder := GetExecutingPath()15	absolutePaths := make([]string, len(relativePaths))16	for i, path := range relativePaths {17		absolutePaths[i] = filepath.Join(currentFolder, path)18	}19	return absolutePaths20}...AbsolutePaths
Using AI Code Generation
1import (2func main() {3	absPath, err := utils.AbsolutePaths("D:\\golang\\src\\utils")4	if err != nil {5		fmt.Println(err)6	} else {7		fmt.Println(absPath)8	}9}Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
