Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support config file #156

Merged
merged 2 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 6 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,13 @@ Command Line Interface tool for developing, building and securing Elvia applicat
The GitHub composite actions at [core-github-actions-templates](https://github.com/3lvia/core-github-actions-templates) are wrappers around many of the CLI commands.
Therefore it's useful to use the CLI when debugging or testing Elvias actions, since you can very easily reproduce the same commands locally.

## 💾 Installation
## 📚 Documentation

See [docs/installation](docs/installation.md).

## 📋 Requirements

See [docs/requirements](docs/requirements.md).

## 📖 Examples

See [docs/examples](docs/examples.md).

## 🧑‍💻 Development

See [docs/development](docs/development.md).
- **[💾 Installation](docs/installation.md)**
- **[📋 Requirements](docs/requirements.md)**
- **[⚙️ Configuration](docs/configuration.md)**
- **[📖 Examples](docs/examples.md)**
- **[🧑‍💻 Development](docs/development.md)**

## 💥 Breaking changes

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.25.0
0.26.0
23 changes: 23 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## ⚙️ Configuration

The Elvia CLI support a configuration file (`3lv.yml`) to store some default values for the commands.
Using a configuration file means you can avoid typing the same options every time you run a command.
The configuration file is optional and should be placed in the root of your repository.

Example (see [pkg/shared/config.go](https://github.com/3lvia/cli/blob/trunk/pkg/shared/config.go) for the full specification):

```yaml
# 3lv.yml
system: core
applications:
- name: demo-api
projectFile: applications/DemoApi/DemoApi.csproj
helmValuesFile: .github/deploy/values-demo-api.yml
buildContext: . # optional
```

With the above configuration, you can for example run the `build` command using just the application name:

```bash
3lv build demo-api
```
1 change: 1 addition & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Download the MSI file (and optionally the MD5 checksum) from the [releases page]
```pwsh
certutil -hashfile 3lv-windows-amd64.msi MD5
```

### Upgrades

To upgrade the CLI, simply run this command:
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ require (
github.com/muesli/termenv v0.15.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/sys v0.28.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,7 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
12 changes: 9 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,20 @@ func main() {

version := strings.TrimSpace(string(versionFile))

config, err := shared.ReadConfig()
if err != nil {
style.PrintInfo("No configuration file (3lv.yml) found.")
}

// Check for a new version of the CLI after all these commands.
// Any new commands should be added here.
commands := shared.WithCheckVersionAfterCommands(
[]*cli.Command{
build.Command,
deploy.Command,
build.Command(config),
deploy.Command(config),
run.Command(config),
scan.Command,
githubactions.Command,
run.Command,
create.Command,
},
version,
Expand All @@ -60,6 +65,7 @@ func main() {
},
},
// Don't check for updates when running the upgrade command.
// Doesn't need config either.
Commands: append(commands, upgrade.Command(version)),
}

Expand Down
213 changes: 101 additions & 112 deletions pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,137 +19,126 @@ import (

const commandName = "build"

var Command *cli.Command = &cli.Command{
Name: commandName,
Aliases: []string{"b"},
Usage: "Build a Docker image from a project file.",
UsageText: "3lv build [options] <application-name>",
Flags: []cli.Flag{
shared.ProjectFileFlag(),
shared.SystemNameFlag(
"The system name to prefix the image name with." +
" If not provided, we will try to use the current git repository name.",
),
shared.SeverityFlag("scan-severity"),
shared.FormatsFlag("scan-formats"),
shared.DisableErrorFlag("scan-disable-error"),
shared.RegistryFlag("The container registry to use. Image name will be prefixed with this value."),
&cli.StringFlag{
Name: "build-context",
Aliases: []string{"c"},
Usage: "The directory to use as the Docker build context, i.e. what files Docker will know about when building." +
" We default to the directory of the project file. This means that if you need files outside of the directory of" +
" the project file, you need to specify this flag.",
Sources: cli.EnvVars("3LV_BUILD_CONTEXT"),
},
&cli.StringFlag{
Name: "go-main-package-directory",
Usage: "The main package directory to use when building a Go application.",
Sources: cli.EnvVars("3LV_GO_MAIN_PACKAGE_DIRECTORY"),
},
&cli.StringFlag{
Name: "cache-tag",
Usage: "The tag to use for the cache image.",
Value: "latest-cache",
Sources: cli.EnvVars("3LV_CACHE_TAG"),
},
&cli.StringFlag{
Name: "azure-tenant-id",
Usage: "The tenant ID to use when authenticating with the Azure Container Registry.",
Hidden: true,
Sources: cli.EnvVars("3LV_AZURE_TENANT_ID"),
},
&cli.StringFlag{
Name: "azure-subscription-id",
Usage: "The subscription ID to use when authenticating with the Azure Container Registry.",
Hidden: true,
Sources: cli.EnvVars("3LV_AZURE_SUBSCRIPTION_ID"),
},
&cli.StringFlag{
Name: "azure-client-id",
Usage: "The client ID to use when authenticating with the Azure Container registry." +
" Must be combined with --azure-federated-token.",
Hidden: true,
Sources: cli.EnvVars("3LV_AZURE_CLIENT_ID"),
},
&cli.StringFlag{
Name: "azure-federated-token",
Usage: "The federated token to use when authenticating with the Azure Container Registry." +
" Must be combined with --client-id.",
Hidden: true,
Sources: cli.EnvVars("3LV_AZURE_FEDERATED_TOKEN"),
},
&cli.StringSliceFlag{
Name: "additional-tags",
Aliases: []string{"t"},
Usage: "Additional tags to use when pushing the image to the registry.",
Sources: cli.EnvVars("3LV_ADDITIONAL_TAGS"),
},
&cli.BoolFlag{
Name: "push",
Aliases: []string{"p"},
Usage: "Push the image to the registry.",
Value: false,
Sources: cli.EnvVars("3LV_PUSH"),
},
&cli.BoolFlag{
Name: "generate-only",
Aliases: []string{"G"},
Usage: "Generates a Dockerfile, but does not build the image.",
Value: false,
Sources: cli.EnvVars("3LV_GENERATE_ONLY"),
func Command(config *shared.Config) *cli.Command {
return &cli.Command{
Name: commandName,
Aliases: []string{"b"},
Usage: "Build a Docker image from a project file.",
UsageText: "3lv build [options] <application-name>",
Flags: []cli.Flag{
shared.ProjectFileFlag(),
shared.SystemNameFlag(
"The system name to prefix the image name with." +
" If not provided, we will try to use the current git repository name.",
),
shared.SeverityFlag("scan-severity"),
shared.FormatsFlag("scan-formats"),
shared.DisableErrorFlag("scan-disable-error"),
shared.RegistryFlag("The container registry to use. Image name will be prefixed with this value."),
&cli.StringFlag{
Name: "build-context",
Aliases: []string{"c"},
Usage: "The directory to use as the Docker build context, i.e. what files Docker will know about when building." +
" We default to the directory of the project file. This means that if you need files outside of the directory of" +
" the project file, you need to specify this flag.",
Sources: cli.EnvVars("3LV_BUILD_CONTEXT"),
},
&cli.StringFlag{
Name: "go-main-package-directory",
Usage: "The main package directory to use when building a Go application.",
Sources: cli.EnvVars("3LV_GO_MAIN_PACKAGE_DIRECTORY"),
},
&cli.StringFlag{
Name: "cache-tag",
Usage: "The tag to use for the cache image.",
Value: "latest-cache",
Sources: cli.EnvVars("3LV_CACHE_TAG"),
},
&cli.StringFlag{
Name: "azure-tenant-id",
Usage: "The tenant ID to use when authenticating with the Azure Container Registry.",
Hidden: true,
Sources: cli.EnvVars("3LV_AZURE_TENANT_ID"),
},
&cli.StringFlag{
Name: "azure-subscription-id",
Usage: "The subscription ID to use when authenticating with the Azure Container Registry.",
Hidden: true,
Sources: cli.EnvVars("3LV_AZURE_SUBSCRIPTION_ID"),
},
&cli.StringFlag{
Name: "azure-client-id",
Usage: "The client ID to use when authenticating with the Azure Container registry." +
" Must be combined with --azure-federated-token.",
Hidden: true,
Sources: cli.EnvVars("3LV_AZURE_CLIENT_ID"),
},
&cli.StringFlag{
Name: "azure-federated-token",
Usage: "The federated token to use when authenticating with the Azure Container Registry." +
" Must be combined with --client-id.",
Hidden: true,
Sources: cli.EnvVars("3LV_AZURE_FEDERATED_TOKEN"),
},
&cli.StringSliceFlag{
Name: "additional-tags",
Aliases: []string{"t"},
Usage: "Additional tags to use when pushing the image to the registry.",
Sources: cli.EnvVars("3LV_ADDITIONAL_TAGS"),
},
&cli.BoolFlag{
Name: "push",
Aliases: []string{"p"},
Usage: "Push the image to the registry.",
Value: false,
Sources: cli.EnvVars("3LV_PUSH"),
},
&cli.BoolFlag{
Name: "generate-only",
Aliases: []string{"G"},
Usage: "Generates a Dockerfile, but does not build the image.",
Value: false,
Sources: cli.EnvVars("3LV_GENERATE_ONLY"),
},
&cli.BoolFlag{
Name: "skip-authentication",
Usage: "Skip authentication before pushing the image to the registry.",
Value: false,
Sources: cli.EnvVars("3LV_SKIP_AUTHENTICATION"),
},
},
&cli.BoolFlag{
Name: "skip-authentication",
Usage: "Skip authentication before pushing the image to the registry.",
Value: false,
Sources: cli.EnvVars("3LV_SKIP_AUTHENTICATION"),
Action: func(ctx context.Context, c *cli.Command) error {
return Build(ctx, c, config)
},
},
Action: Build,
}
}

func Build(_ context.Context, c *cli.Command) error {
func Build(_ context.Context, c *cli.Command, config *shared.Config) error {
if c.NArg() <= 0 {
cli.ShowSubcommandHelpAndExit(c, 1)
}

// Required args
applicationName := c.Args().First()
if applicationName == "" {
return cli.Exit("Application name not provided", 1)
return cli.Exit("Application name not provided.", 1)
}

projectFile := c.String("project-file")
if projectFile == "" {
return cli.Exit("Project file not provided", 1)
configForApplication, err := config.GetConfigForApplication(applicationName)
if err != nil {
style.PrintWarning(err.Error())
}

systemName, err := func() (string, error) {
possibleSystemName := c.String("system-name")

if possibleSystemName == "" {
style.PrintInfo(
"System name not provided, will try to use the current git repository name.",
)

repositoryName, err := utils.ResolveRepositoryName("")
if err != nil {
return "", err
}

return repositoryName, nil
}

return possibleSystemName, nil
}()
if err != nil {
return cli.Exit(err, 1)
projectFile := utils.FirstNonEmpty(c.String("project-file"), configForApplication.ProjectFile)
if projectFile == "" {
return cli.Exit("Project file not provided.", 1)
}

systemName := utils.FirstNonEmpty(c.String("system-name"), config.System)

generateOptions := GenerateDockerfileOptions{
GoMainPackageDirectory: c.String("go-main-package-directory"),
BuildContext: c.String("build-context"),
BuildContext: utils.FirstNonEmpty(c.String("build-context"), configForApplication.BuildContext),
}

dockerfilePath, buildContext, err := generateDockerfile(
Expand Down
Loading