Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
74b7455
chore: rename AwsEcs to AwsEcsCfn to disambig
lionello Nov 19, 2025
c3ee244
fix: parameterize CloudFormation template
lionello Nov 19, 2025
b358b17
feat: OIDC provider
lionello Nov 19, 2025
5b3a4c1
wip
lionello Nov 20, 2025
dd9cf42
feat: Add OIDCProvider resource and related parameters to CloudFormat…
lionello Nov 22, 2025
62311ad
chore: make cloudformation subcommand of cd
lionello Nov 22, 2025
3c0525a
Add test
lionello Nov 22, 2025
5ae0277
chore: run Go workflow for relevant paths
lionello Nov 23, 2025
c9640ed
login first
lionello Nov 23, 2025
c313f3b
add auth env
lionello Nov 23, 2025
3e20402
Using AWS_ROLE_ARN
lionello Nov 23, 2025
22ec507
write jwt to file
lionello Nov 23, 2025
c78b883
move env to login cmd
lionello Nov 23, 2025
479efc8
add dbg msg
lionello Nov 23, 2025
ceb5abd
add region
lionello Nov 23, 2025
55df77c
set AWS_ROLE_SESSION_NAME
lionello Nov 23, 2025
7f03c4d
save assets
lionello Nov 24, 2025
87a1c38
log all AWS config calls
lionello Nov 24, 2025
e79e675
remove AWS_ROLE_ARN from README
lionello Nov 25, 2025
902b3e2
chore: use difflib instead of gotextdiff
lionello Nov 25, 2025
eaa23fe
fix: t.Cleanup at start of test
lionello Nov 25, 2025
3ac5304
chore: t.Error to show subsequent err
lionello Nov 25, 2025
9ded6d8
chore: no CR and EOF
lionello Nov 25, 2025
b2d7aa1
fix: use defang as CloudFormation creator
lionello Nov 25, 2025
1f3118e
update vendorHash
lionello Nov 25, 2025
9686b01
fix(aws): don't set task LaunchType
lionello Nov 25, 2025
5975197
update role arn
lionello Nov 25, 2025
0e6042e
remove obsolete gcp env
lionello Nov 25, 2025
a3912a4
remove test workflow
lionello Nov 25, 2025
483dbe5
comment
lionello Nov 25, 2025
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
6 changes: 6 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ on:
- "v*" # push events to tagged commits
branches:
- "**"
paths:
- ".github/workflows/go.yml"
- "Makefile"
- "pkgs/**"
- "src/**"

permissions:
contents: read
id-token: write # for GitHub id-token auth
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Install the Defang CLI from one of the following sources:
```
docker run -it defangio/defang-cli help
```

- Using [NPX](https://docs.npmjs.com/cli/v9/commands/npx):

```
Expand Down
2 changes: 1 addition & 1 deletion pkgs/defang/cli.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ buildGo124Module {
pname = "defang-cli";
version = "git";
src = lib.cleanSource ../../src;
vendorHash = "sha256-me/fzAIa1ONgn/9rn13zd2kujzuqrCvbBaAmoqjL90c="; # TODO: use fetchFromGitHub
vendorHash = "sha256-le/O0WhOXO8ItgiG1ZRqDzMNV/2mOwWATn8T/xfn6dM="; # TODO: use fetchFromGitHub

subPackages = [ "cmd/cli" ];

Expand Down
1 change: 0 additions & 1 deletion pkgs/npm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,3 @@ The Defang CLI recognizes the following environment variables:

Environment variables will be loaded from a `.defangrc` file in the current directory, if it exists. This file follows
the same format as a `.env` file: `KEY=VALUE` pairs on each line, lines starting with `#` are treated as comments and ignored.

1 change: 0 additions & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,3 @@ The Defang CLI recognizes the following environment variables:

Environment variables will be loaded from a `.defangrc` file in the current directory, if it exists. This file follows
the same format as a `.env` file: `KEY=VALUE` pairs on each line, lines starting with `#` are treated as comments and ignored.

23 changes: 23 additions & 0 deletions src/cmd/cli/command/cd.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

"github.com/DefangLabs/defang/src/pkg/cli"
cliClient "github.com/DefangLabs/defang/src/pkg/cli/client"
"github.com/DefangLabs/defang/src/pkg/cli/client/byoc/aws"
"github.com/DefangLabs/defang/src/pkg/term"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -188,3 +190,24 @@ var cdInstallCmd = &cobra.Command{
return cli.InstallCD(cmd.Context(), provider)
},
}

var cdCloudformationCmd = &cobra.Command{
Use: "cloudformation",
Short: "CloudFormation template related commands",
Annotations: authNeededAnnotation,
Args: cobra.NoArgs,
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
provider := aws.NewByocProvider(cmd.Context(), global.Client.GetTenantName(), global.Stack)

if err := canIUseProvider(cmd.Context(), provider, "", 0); err != nil {
return err
}

template, err := provider.PrintCloudFormationTemplate()
if err == nil {
_, err = term.Print(string(template))
}
return err
},
}
7 changes: 4 additions & 3 deletions src/cmd/cli/command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ func SetupCommands(ctx context.Context, version string) {
cdPreviewCmd.Flags().VarP(&global.Mode, "mode", "m", fmt.Sprintf("deployment mode; one of %v", modes.AllDeploymentModes()))
cdCmd.AddCommand(cdPreviewCmd)
cdCmd.AddCommand(cdInstallCmd)
cdCmd.AddCommand(cdCloudformationCmd)

// Eula command
tosCmd.Flags().Bool("agree-tos", false, "agree to the Defang terms of service")
Expand Down Expand Up @@ -227,8 +228,8 @@ func SetupCommands(ctx context.Context, version string) {
lsCommand.Aliases = []string{"getServices", "ps", "ls", "list"}
RootCmd.AddCommand(lsCommand)

// Get Status Command
RootCmd.AddCommand(getVersionCmd)
// Version Command
RootCmd.AddCommand(versionCmd)

// Config Command (was: secrets)
configSetCmd.Flags().BoolP("name", "n", false, "name of the config (backwards compat)")
Expand Down Expand Up @@ -627,7 +628,7 @@ func collectUnsetEnvVars(project *composeTypes.Project) []string {
return nil
}

var getVersionCmd = &cobra.Command{
var versionCmd = &cobra.Command{
Use: "version",
Args: cobra.NoArgs,
Aliases: []string{"ver", "stat", "status"}, // for backwards compatibility
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/cli/command/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,12 @@ func TestCommandGates(t *testing.T) {
err := testCommand(tt.command, server.URL)

if tt.expectCanIUseCalled != mockService.canIUseIsCalled {
t.Fatalf("unexpected canIUse: expected usage: %t", tt.expectCanIUseCalled)
t.Errorf("unexpected canIUse: expected usage: %t", tt.expectCanIUseCalled)
}

if err != nil {
if tt.expectCanIUseCalled && err.Error() != "resource_exhausted: no access to use aws provider" {
t.Fatalf("expected \"no access error\" - got: %v", err.Error())
t.Errorf("expected \"no access\" error - got: %v", err.Error())
}
}
})
Expand Down
20 changes: 10 additions & 10 deletions src/cmd/cli/command/globals_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ func Test_configurationPrecedence(t *testing.T) {
f.Close()
}

t.Cleanup(func() {
// Unseting env vars set for this test is handled by t.Setenv automatically
// t.tempDir() will clean up created files

// Unset all RC env vars created by loadRC since it uses os.Setenv
for _, rcEnv := range rcEnvs {
os.Unsetenv(rcEnv)
}
})

t.Chdir(tempDir)

// simulates the actual loading sequence
Expand Down Expand Up @@ -402,16 +412,6 @@ func Test_configurationPrecedence(t *testing.T) {
if testConfig.HideUpdate != tt.expected.HideUpdate {
t.Errorf("expected HideUpdate to be %v, got %v", tt.expected.HideUpdate, testConfig.HideUpdate)
}

t.Cleanup(func() {
// Unseting env vars set for this test is hanndled by t.Setenv automatically
// t.tempDir() will clean up created files

// Unset all RC env vars created by loadRC since it uses os.Setenv
for _, rcEnv := range rcEnvs {
os.Unsetenv(rcEnv)
}
})
})
}
}
20 changes: 10 additions & 10 deletions src/cmd/crun/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"fmt"
"os"

"github.com/DefangLabs/defang/src/pkg/cmd"
"github.com/DefangLabs/defang/src/pkg/crun"
"github.com/DefangLabs/defang/src/pkg/term"
"github.com/spf13/pflag"
)
Expand Down Expand Up @@ -61,7 +61,7 @@ func main() {
pflag.Parse()
}

region := cmd.Region(*region)
region := crun.Region(*region)
ctx := context.Background()

requireTaskID := func() string {
Expand All @@ -85,19 +85,19 @@ func main() {
envMap := make(map[string]string)
// Apply env vars from files first, so they can be overridden by the command line
for _, envFile := range *envFiles {
if _, err := cmd.ParseEnvFile(envFile, envMap); err != nil {
if _, err := crun.ParseEnvFile(envFile, envMap); err != nil {
term.Fatal(err)
}
}
// Apply env vars from the command line last, so they take precedence
for _, env := range *envs {
if key, value := cmd.ParseEnvLine(env); key != "" {
if key, value := crun.ParseEnvLine(env); key != "" {
envMap[key] = value
}
}

memory := cmd.ParseMemory(*memory)
err = cmd.Run(ctx, cmd.RunContainerArgs{
memory := crun.ParseMemory(*memory)
err = crun.Run(ctx, crun.RunContainerArgs{
Region: region,
Image: runFlags.Arg(0),
Memory: memory,
Expand All @@ -109,18 +109,18 @@ func main() {
})
case "stop", "s":
taskID := requireTaskID()
err = cmd.Stop(ctx, region, &taskID)
err = crun.Stop(ctx, region, &taskID)
case "logs", "tail", "l":
taskID := requireTaskID()
err = cmd.Logs(ctx, region, &taskID)
err = crun.Logs(ctx, region, &taskID)
case "destroy", "teardown", "d":
if pflag.NArg() != 1 {
term.Fatal("destroy does not take any arguments")
}
err = cmd.Destroy(ctx, region)
err = crun.Destroy(ctx, region)
case "info", "i":
taskID := requireTaskID()
err = cmd.PrintInfo(ctx, region, &taskID)
err = crun.PrintInfo(ctx, region, &taskID)
}

if err != nil {
Expand Down
5 changes: 2 additions & 3 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7
github.com/aws/smithy-go v1.22.1
github.com/awslabs/goformation/v7 v7.13.1
github.com/awslabs/goformation/v7 v7.14.9
github.com/bufbuild/connect-go v1.10.0
github.com/compose-spec/compose-go/v2 v2.7.2-0.20250715094302-8da9902241f9
github.com/digitalocean/godo v1.131.1
Expand All @@ -40,7 +40,6 @@ require (
github.com/googleapis/gax-go/v2 v2.14.1
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/hexops/gotextdiff v1.0.3
github.com/joho/godotenv v1.5.1
github.com/mark3labs/mcp-go v0.38.0
github.com/miekg/dns v1.1.59
Expand All @@ -50,6 +49,7 @@ require (
github.com/opencontainers/image-spec v1.1.0
github.com/pelletier/go-toml/v2 v2.2.2
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
github.com/ross96D/cancelreader v0.2.6
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
Expand Down Expand Up @@ -105,7 +105,6 @@ require (
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
Expand Down
14 changes: 6 additions & 8 deletions src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGz
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/awslabs/goformation/v7 v7.13.1 h1:QlPn8qwNCqYhrb4GW8kLjT4j1J49n5Qh/anpurCHxUA=
github.com/awslabs/goformation/v7 v7.13.1/go.mod h1:FTCFMNesubEX0LAd6kIR+YkDD1U+5UaMbXtgPUgsck0=
github.com/awslabs/goformation/v7 v7.14.9 h1:sZjjpTqXrcBDz4Fi07JWTT7zKM68XsQkW/7iLAJbA/M=
github.com/awslabs/goformation/v7 v7.14.9/go.mod h1:7obldQ8NQ/AkMsgL5K3l4lRMDFB6kCGUloz5dURcXIs=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
Expand Down Expand Up @@ -208,8 +208,6 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
Expand Down Expand Up @@ -270,10 +268,10 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY=
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
Expand Down
51 changes: 15 additions & 36 deletions src/pkg/cli/client/byoc/aws/byoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/DefangLabs/defang/src/pkg/cli/client"
"github.com/DefangLabs/defang/src/pkg/cli/client/byoc"
"github.com/DefangLabs/defang/src/pkg/cli/compose"
"github.com/DefangLabs/defang/src/pkg/clouds"
"github.com/DefangLabs/defang/src/pkg/clouds/aws"
"github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs"
"github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs/cfn"
Expand All @@ -35,7 +36,6 @@ import (
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/aws/smithy-go"
"github.com/aws/smithy-go/ptr"
"github.com/bufbuild/connect-go"
composeTypes "github.com/compose-spec/compose-go/v2/types"
"google.golang.org/protobuf/proto"
Expand Down Expand Up @@ -128,48 +128,27 @@ func initStsClient(cfg awssdk.Config) {
}
}

func (b *ByocAws) makeContainers() []clouds.Container {
return makeContainers(b.PulumiVersion, b.CDImage)
}

func (b *ByocAws) PrintCloudFormationTemplate() ([]byte, error) {
containers := b.makeContainers()
template, err := cfn.CreateTemplate(byoc.CdTaskPrefix, containers)
if err != nil {
return nil, err
}
return template.YAML()
}

func (b *ByocAws) SetUpCD(ctx context.Context) error {
if b.SetupDone {
return nil
}

term.Debugf("Using CD image: %q", b.CDImage)

cdTaskName := byoc.CdTaskPrefix
containers := []types.Container{
{
// FIXME: get the Pulumi image or version from Fabric: https://github.com/DefangLabs/defang/issues/1027
Image: "public.ecr.aws/pulumi/pulumi-nodejs:" + b.PulumiVersion,
Name: ecs.CdContainerName,
Cpus: 2.0,
Memory: 2048_000_000, // 2G
Essential: ptr.Bool(true),
VolumesFrom: []string{
cdTaskName,
},
WorkDir: "/app",
DependsOn: map[string]types.ContainerCondition{cdTaskName: "START"},
EntryPoint: []string{"node", "lib/index.js"},
},
{
Image: b.CDImage,
Name: cdTaskName,
Essential: ptr.Bool(false),
Volumes: []types.TaskVolume{
{
Source: "pulumi-plugins",
Target: "/root/.pulumi/plugins",
ReadOnly: true,
},
{
Source: "cd",
Target: "/app",
ReadOnly: true,
},
},
},
}
if err := b.driver.SetUp(ctx, containers); err != nil {
if err := b.driver.SetUp(ctx, b.makeContainers()); err != nil {
return AnnotateAwsError(err)
}

Expand Down
Loading
Loading