Repository: gruntwork-io/terragrunt (language: go)

Issue:
WARN[] No double-slash (//) found - on remote modules without submodules
When doing a plan using a module that doesn't have submodules, terragrunt keeps throwing a warning. The double slash would address the issue if using submodules but the warning is redundant if you have a single flat module. 

Example:

```
terragrunt run-all plan  --terragrunt-working-dir "resources/platform" --terragrunt-log-level INFO
INFO[0000] Stack at resources/platform:
  => Module /<my home>/terraform/cloud-factory/resources/platform/folders/root (excluded: false, dependencies: [])
WARN[0000] No double-slash (//) found in source URL /terraform-google-modules/terraform-google-folders.git. 
Relative paths in downloaded Terraform code may not work.  
prefix=[/<my home>/terraform/cloud-factory/resources/platform/folders/root]
```

The terragrunt.hcl configuration used in this example:
```
terraform {
  source = "git::git@github.com:terraform-google-modules/terraform-google-folders.git?ref=v3.0.0"
}
```

The module in question has a flat structure with no submodules: https://github.com/terraform-google-modules/terraform-google-folders. I've seen a few threads about this but nothing with a permanent solution that addresses this for simple modules like the above mentioned one.

Using:

`terragrunt version v0.29.2`

`Terraform v0.15.3 on darwin_amd64`


Discussion / hints:
I am also seeing this issue.
I'm using in this way and showing the same warning

```hcl
terraform {
  source = "github.com/terraform-aws-modules/terraform-aws-acm?ref=v3.0.0"
}
```
```sh
WARN[0000] No double-slash (//) found in source URL /terraform-aws-modules/terraform-aws-acm. Relative paths in downloaded Terraform code may not work.  prefix=[/Users/MY_PATH/] 
```
It would be great if we could disable this warning deliberately via some flag / ENV variable/configuration.
Ah, that's a good point. That logic needs to handle modules in the root of a repo better. PR to fix this is very welcome!
I see the same warning
workaround for source without submodules
terraform {
  source = "github.com/terraform-aws-modules/terraform-aws-acm//.?ref=v3.0.0"
}

double-slash(//) and a period (.)
We are getting the same issue when using the `tfr` source method. For instance:
```terraform
terraform {
  source = "tfr:///terraform-aws-modules/vpc/aws?version=3.5.0"
}
```
results in a: 
```
No double-slash (//) found in source URL /terraform-aws-modules/vpc/aws. Relative paths in downloaded Terraform code may not work...
```

The above fix also works for `tfr` sources: 
```terraform 
terraform {
  source = "tfr:///terraform-aws-modules/vpc/aws//.?version=3.5.0"
}

```
A more systemic solution would be appreciated though as this is a Terragrunt issue, not a module issue.
Just hit this issue. Commenting for visibility.
Getting same error for:
`source = "git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git"`
Fixed by adding `//.`
`source = "git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//."`
As others have mentioned, adding a trailing '.' at the end of the path solves but would appreciate a better solve for this.

`  source = "git::git@github.com:terraform-aws-modules/terraform-aws-autoscaling.git//.?ref=v4.11.0"
`
The //. doesn't work when using the find_in_parent_folders function.

```
terraform {
  source = find_in_parent_folders("_resources///_vpc")
}
```
Workaround when you use functions.
See de triple slash at the end, that works for me

```hcl
terraform {
  #source = "${get_parent_terragrunt_dir()}/../modules/stacks/${local.stack_name}/"
  source = "${get_parent_terragrunt_dir()}/../modules/stacks/${local.stack_name}///"
}
```
this is working for me
```terraform
terraform {
  source = "${find_in_parent_folders("modules/global/dns")}///"
}
```
The trick here is the `///` at the end 


I did a little digging into this today, and it looks like this issue happens because terragrunt is using the `getter.Detect` function from https://github.com/hashicorp/go-getter to parse the source strings, and if the source string is in a somewhat non-canonical format such as `git::git@github.com:foo...`, `getter.Detect` transforms it into `git::ssh://git@github.com/foo...`. When it does this, if there are trailing `//` at the end of the source string, it removes them.

So, if your source is `git::github.com:terraform-aws-modules/terraform-aws-acm//?ref=v3.0.0` for example, `getter.Detect` will transform it into `git::ssh://git@github.com/terraform-aws-modules/terraform-aws-acm?ref=v3.0.0`. Note that it has removed the `//`. 

Then when this `sourceUrl` is passed into the `splitSourceUrl` function in the terragrunt code, it raises this warning because there is now no `//` in the transformed source url even though the original source had it.

So, yet another workaround is to use the more canonical URL (`git::ssh://git@github.com/terraform-aws-modules/terraform-aws-acm//?ref=v3.0.0`) to begin with, which `getter.Detect` will not have to transform. In this case it leaves the `//` in, and terragrunt's `splitSourceUrl` function no longer throws the warning.

I'm not sure if the go-getter library could/should be patched to leave the trailing `//` in, or if there's a good reason for it to remove them, but it seems like maybe it could be an issue for them to consider there.
> this is working for me
> 
> ```terraform
> terraform {
>   source = "${find_in_parent_folders("modules/global/dns")}///"
> }
> ```
> 
> The trick here is the `///` at the end

That did it for me, thank you, bud
This is also an issue for `/dev/null` (hack used for decommissioned modules 😅 )

```hcl
terraform {
  source = "/dev/null"
  # switched off since not needed
}
```

..

```
level=warning msg=No double-slash (//) found in source URL /dev/null. Relative paths in downloaded Terraform code may not work...
```

I'm seeing this as well after following this tutorial using Terragrunt 0.50.9:
https://blog.gruntwork.io/how-to-manage-multiple-environments-with-terraform-using-terragrunt-2c3e32fc60a8

That blog even uses relative module paths as an example:

<img width="709" alt="image" src="https://github.com/gruntwork-io/terragrunt/assets/57132/63cd54a7-960c-4828-a022-aac5d83bcc8e">


Fix patch:
diff --git a/terraform/source.go b/terraform/source.go
index 849b885904..4bdc4dac35 100644
--- a/terraform/source.go
+++ b/terraform/source.go
@@ -244,10 +244,18 @@ func splitSourceUrl(sourceUrl *url.URL, logger *logrus.Entry) (*url.URL, string,
 
 		sourceUrlModifiedPath.Path = pathSplitOnDoubleSlash[0]
 		return sourceUrlModifiedPath, pathSplitOnDoubleSlash[1], nil
-	} else {
-		logger.Warningf("No double-slash (//) found in source URL %s. Relative paths in downloaded Terraform code may not work.", sourceUrl.Path)
+	}
+	// check if path is remote URL
+	if sourceUrl.Scheme != "" {
 		return sourceUrl, "", nil
 	}
+	// check if sourceUrl.Path is a local file path
+	_, err := os.Stat(sourceUrl.Path)
+	if err != nil {
+		// log warning message to notify user that sourceUrl.Path may not work
+		logger.Warningf("No double-slash (//) found in source URL %s. Relative paths in downloaded Terraform code may not work.", sourceUrl.Path)
+	}
+	return sourceUrl, "", nil
 }
 
 // Encode a the module name for the given source URL. When calculating a module name, we calculate the base 64 encoded


Test patch:
diff --git a/test/fixture-disabled-path/main.tf b/test/fixture-disabled-path/main.tf
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/fixture-disabled-path/terragrunt.hcl b/test/fixture-disabled-path/terragrunt.hcl
new file mode 100644
index 0000000000..04cb39be4a
--- /dev/null
+++ b/test/fixture-disabled-path/terragrunt.hcl
@@ -0,0 +1,3 @@
+terraform {
+  source = "/dev/null"
+}
diff --git a/test/fixture-no-submodules/main.tf b/test/fixture-no-submodules/main.tf
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/fixture-no-submodules/terragrunt.hcl b/test/fixture-no-submodules/terragrunt.hcl
new file mode 100644
index 0000000000..114814a801
--- /dev/null
+++ b/test/fixture-no-submodules/terragrunt.hcl
@@ -0,0 +1,3 @@
+terraform {
+  source = "git::git@github.com:terraform-google-modules/terraform-google-folders.git?ref=v3.0.0"
+}
diff --git a/test/integration_test.go b/test/integration_test.go
index 42f8dfff65..10cccf1346 100644
--- a/test/integration_test.go
+++ b/test/integration_test.go
@@ -167,6 +167,8 @@ const (
 	TEST_FIXTURE_S3_ERRORS                                                   = "fixture-s3-errors/"
 	TEST_FIXTURE_GCS_NO_BUCKET                                               = "fixture-gcs-no-bucket/"
 	TEST_FIXTURE_GCS_NO_PREFIX                                               = "fixture-gcs-no-prefix/"
+	TEST_FIXTURE_DISABLED_PATH                                               = "fixture-disabled-path/"
+	TEST_FIXTURE_NO_SUBMODULES                                               = "fixture-no-submodules/"
 	TERRAFORM_BINARY                                                         = "terraform"
 	TERRAFORM_FOLDER                                                         = ".terraform"
 	TERRAFORM_STATE                                                          = "terraform.tfstate"
@@ -5964,6 +5966,36 @@ func TestTerragruntNoPrefixGCSBucket(t *testing.T) {
 	assert.NoError(t, err)
 }
 
+func TestTerragruntNoWarningLocalPath(t *testing.T) {
+	t.Parallel()
+
+	tmpEnvPath := copyEnvironment(t, TEST_FIXTURE_DISABLED_PATH)
+	cleanupTerraformFolder(t, tmpEnvPath)
+	testPath := util.JoinPath(tmpEnvPath, TEST_FIXTURE_DISABLED_PATH)
+
+	stdout := bytes.Buffer{}
+	stderr := bytes.Buffer{}
+
+	err := runTerragruntCommand(t, fmt.Sprintf("terragrunt apply --terragrunt-non-interactive --terragrunt-working-dir %s", testPath), &stdout, &stderr)
+	require.NoError(t, err)
+	require.NotContains(t, stderr.String(), "No double-slash (//) found in source URL")
+}
+
+func TestTerragruntNoWarningRemotePath(t *testing.T) {
+	t.Parallel()
+
+	tmpEnvPath := copyEnvironment(t, TEST_FIXTURE_NO_SUBMODULES)
+	cleanupTerraformFolder(t, tmpEnvPath)
+	testPath := util.JoinPath(tmpEnvPath, TEST_FIXTURE_NO_SUBMODULES)
+
+	stdout := bytes.Buffer{}
+	stderr := bytes.Buffer{}
+
+	err := runTerragruntCommand(t, fmt.Sprintf("terragrunt init --terragrunt-non-interactive --terragrunt-working-dir %s", testPath), &stdout, &stderr)
+	require.NoError(t, err)
+	require.NotContains(t, stderr.String(), "No double-slash (//) found in source URL")
+}
+
 func validateOutput(t *testing.T, outputs map[string]TerraformOutput, key string, value interface{}) {
 	t.Helper()
 	output, hasPlatform := outputs[key]