Repository: dbt-labs/dbt-core
Pull request: Use profile specified in --profile with dbt init
resolves #6154

### Description

If --profile is specified with dbt init, and the profile exists, initialize the project with that as the profile instead of creating a new profile. If the profile does not exist, or the command is run inside an existing project, it fails with an error.

### Checklist

- [X] I have read [the contributing guide](https://github.com/dbt-labs/dbt-core/blob/main/CONTRIBUTING.md) and understand what's expected of me
- [X] I have signed the [CLA](https://docs.getdbt.com/docs/contributor-license-agreements)
- [X] I have run this code in development and it appears to resolve the stated issue
- [X] This PR includes tests, or tests are not required/relevant for this PR
- [X] I have [opened an issue to add/update docs](https://github.com/dbt-labs/docs.getdbt.com/issues/new/choose), or docs changes are not required/relevant for this PR
- [X] I have run `changie new` to [create a changelog entry](https://github.com/dbt-labs/dbt-core/blob/main/CONTRIBUTING.md#adding-a-changelog-entry)


Problem statement:
[CT-1418] [Bug] CLI argument `--profile` ignored in `dbt init`
### Is this a new bug in dbt-core?

- [X] I believe this is a new bug in dbt-core
- [X] I have searched the existing issues, and I could not find an existing issue for this bug

### Current Behavior

According to `dbt init --help`, the `--profile` option lets the user load an existing profile to use with a new project. However, that option currently does nothing when you call it with `dbt init`. 

Example:
```
dbt init --profile default proj1
```

Result:
proj1 created with `proj1/dbt_project.yml` showing `profile: 'proj1'`. `~/.dbt/profiles.yml` shows a new profile `proj1` created with placeholders. **Note**: if a profile named `proj1` already exists, dbt *still* creates another profile named `proj1` again.

In contrast, calling `dbt run --profile default` actually does make the project use the profile `default`, so the `--profile` argument does work elsewhere at least. 


### Expected Behavior

proj1 created with `proj1/dbt_project.yml` showing `profile: 'default'`. `~/.dbt/profiles.yml` should not show any new profile created.

### Steps To Reproduce

1. Create new project directory to store project in
2. Open `~/.dbt/profiles.yml` and create a new profile `default`.
3. Run `dbt init --profile default proj1`
4. `cd` into `/proj1` and open `dbt_project.yml`. The `profile` will be set to `proj1` instead of `default`.
5. Check `~/.dbt/profiles.yml` again and see that dbt has created a new blank `proj1` profile to go with the new project, instead of using the existing `default` profile.

### Relevant log output

_No response_

### Environment

```markdown
- OS: Ubuntu 22.04.1 LTS
- Python: 3.10.6
- dbt: 1.3.0
```


### Which database adapter are you using with dbt?

postgres

### Additional Context

_No response_

Hints from issue discussion:
hi @scheah92, thanks for reporting, I am able to reproduce this. my first thought was the order of arguments, so perhaps `dbt init proj1 --profile default` would work, but that behaves the same

we are reworking our CLI commands as part of v1.4 and v1.5 and we can likely pick this up for those releases 
Hi @lostmygithubaccount, if you guys are reworking CLI commands, you might be interested to know that `--profile` is not the only CLI argument that has issues. In my testing, I've found that `--target` and `--vars` also do not appear to have any effect. I suspect these don't work because they rely on `--profile` in the first place.
Good place to start looking at this:
https://github.com/dbt-labs/dbt-core/blob/edb5634b9ad20e84c1234bcc41f4039639692e18/core/dbt/task/init.py#L247
The code shows that the profile argument is simply ignored:
`
        with open("dbt_project.yml", "r+") as f:
            content = f"{f.read()}".format(project_name=project_name, profile_name=project_name)
            f.seek(0)
            f.write(content)
            f.truncate()
`

Commit message: Use profile specified in --profile with dbt init

Patch to review:
diff --git a/.changes/unreleased/Fixes-20230424-161642.yaml b/.changes/unreleased/Fixes-20230424-161642.yaml
new file mode 100644
index 00000000000..aeeb8e1ccab
--- /dev/null
+++ b/.changes/unreleased/Fixes-20230424-161642.yaml
@@ -0,0 +1,7 @@
+kind: Fixes
+body: If --profile specified with dbt-init, create the project with the specified
+  profile
+time: 2023-04-24T16:16:42.994547-04:00
+custom:
+  Author: ezraerb
+  Issue: CT-1418
diff --git a/core/dbt/task/init.py b/core/dbt/task/init.py
index dcc37d859bf..b9b6468f714 100644
--- a/core/dbt/task/init.py
+++ b/core/dbt/task/init.py
@@ -11,6 +11,7 @@
 
 import dbt.config
 import dbt.clients.system
+from dbt.config.profile import read_profile
 from dbt.flags import get_flags
 from dbt.version import _get_adapter_plugin_names
 from dbt.adapters.factory import load_plugin, get_include_paths
@@ -256,6 +257,14 @@ def run(self):
             in_project = False
 
         if in_project:
+            # If --profile was specified, it means use an existing profile, which is not
+            # applicable to this case
+            if getattr(get_flags(), "PROFILE", None):
+                print(
+                    "Can not init existing project with specified profile, edit dbt_project.yml instead"
+                )
+                sys.exit(1)
+
             # When dbt init is run inside an existing project,
             # just setup the user's profile.
             fire_event(SettingUpProfile())
@@ -289,16 +298,30 @@ def run(self):
             fire_event(ProjectNameAlreadyExists(name=project_name))
             return
 
+        # If the user specified an existing profile to use, use it instead of generating a new one
+        user_profile_name = getattr(get_flags(), "PROFILE", None)
+        if user_profile_name:
+            # Verify it exists. Can't use the regular profile validation routine because it assumes
+            # the project file exists
+            raw_profiles = read_profile(profiles_dir)
+            if user_profile_name not in raw_profiles:
+                print("Could not find profile named '{}'".format(user_profile_name))
+                sys.exit(1)
+
         self.copy_starter_repo(project_name)
         os.chdir(project_name)
         with open("dbt_project.yml", "r+") as f:
-            content = f"{f.read()}".format(project_name=project_name, profile_name=project_name)
+            content = f"{f.read()}".format(
+                project_name=project_name,
+                profile_name=user_profile_name if user_profile_name else project_name,
+            )
             f.seek(0)
             f.write(content)
             f.truncate()
 
+        # If an existing profile to use was not provided, generate a profile
         # Ask for adapter only if skip_profile_setup flag is not provided.
-        if not self.args.skip_profile_setup:
+        if not user_profile_name and not self.args.skip_profile_setup:
             if not self.check_if_can_write_profile(profile_name=project_name):
                 return
             adapter = self.ask_for_adapter_choice()
diff --git a/tests/functional/init/test_init.py b/tests/functional/init/test_init.py
index 4e48eeeffb5..cc95552ef56 100644
--- a/tests/functional/init/test_init.py
+++ b/tests/functional/init/test_init.py
@@ -1,5 +1,6 @@
 import click
 import os
+import yaml
 import pytest
 from pathlib import Path
 from unittest import mock
@@ -84,6 +85,12 @@ def test_init_task_in_project_with_existing_profiles_yml(
 """
             )
 
+    def test_init_task_in_project_specifying_profile_errors(self):
+        # This triggers a call to sys.exit(), requring the following to test it
+        with pytest.raises(SystemExit) as error:
+            run_dbt(["init", "--profile", "test"])
+        assert error.value.code == 1
+
 
 class TestInitProjectWithoutExistingProfilesYml:
     @mock.patch("dbt.task.init._get_adapter_plugin_names")
@@ -159,6 +166,21 @@ def exists_side_effect(path):
 """
             )
 
+    @mock.patch.object(Path, "exists", autospec=True)
+    def test_init_task_in_project_without_profile_yml_specifying_profile_errors(self, exists):
+        def exists_side_effect(path):
+            # Override responses on specific files, default to 'real world' if not overriden
+            return {"profiles.yml": False}.get(path.name, os.path.exists(path))
+
+        exists.side_effect = exists_side_effect
+
+        # Even through no profiles.yml file exists, the init will not modify project.yml,
+        # so this errors
+        # This triggers a call to sys.exit(), requring the following to test it
+        with pytest.raises(SystemExit) as error:
+            run_dbt(["init", "--profile", "test"])
+        assert error.value.code == 1
+
 
 class TestInitProjectWithoutExistingProfilesYmlOrTemplate:
     @mock.patch("dbt.task.init._get_adapter_plugin_names")
@@ -684,3 +706,130 @@ def test_init_provided_project_name_and_skip_profile_setup(
       +materialized: view
 """
             )
+
+
+class TestInitOutsideOfProjectWithSpecifiedProfile(TestInitOutsideOfProjectBase):
+    @mock.patch("dbt.task.init._get_adapter_plugin_names")
+    @mock.patch("click.prompt")
+    def test_init_task_outside_of_project_with_specified_profile(
+        self, mock_prompt, mock_get_adapter, project, project_name, unique_schema, dbt_profile_data
+    ):
+        manager = Mock()
+        manager.attach_mock(mock_prompt, "prompt")
+        manager.prompt.side_effect = [
+            project_name,
+        ]
+        mock_get_adapter.return_value = [project.adapter.type()]
+        run_dbt(["init", "--profile", "test"])
+
+        manager.assert_has_calls(
+            [
+                call.prompt("Enter a name for your project (letters, digits, underscore)"),
+            ]
+        )
+
+        # profiles.yml is NOT overwritten, so assert that the text matches that of the
+        # original fixture
+        with open(os.path.join(project.profiles_dir, "profiles.yml"), "r") as f:
+            assert f.read() == yaml.safe_dump(dbt_profile_data)
+
+        with open(os.path.join(project.project_root, project_name, "dbt_project.yml"), "r") as f:
+            assert (
+                f.read()
+                == f"""
+# Name your project! Project names should contain only lowercase characters
+# and underscores. A good package name should reflect your organization's
+# name or the intended use of these models
+name: '{project_name}'
+version: '1.0.0'
+config-version: 2
+
+# This setting configures which "profile" dbt uses for this project.
+profile: 'test'
+
+# These configurations specify where dbt should look for different types of files.
+# The `model-paths` config, for example, states that models in this project can be
+# found in the "models/" directory. You probably won't need to change these!
+model-paths: ["models"]
+analysis-paths: ["analyses"]
+test-paths: ["tests"]
+seed-paths: ["seeds"]
+macro-paths: ["macros"]
+snapshot-paths: ["snapshots"]
+
+clean-targets:         # directories to be removed by `dbt clean`
+  - "target"
+  - "dbt_packages"
+
+
+# Configuring models
+# Full documentation: https://docs.getdbt.com/docs/configuring-models
+
+# In this example config, we tell dbt to build all models in the example/
+# directory as views. These settings can be overridden in the individual model
+# files using the `{{{{ config(...) }}}}` macro.
+models:
+  {project_name}:
+    # Config indicated by + and applies to all files under models/example/
+    example:
+      +materialized: view
+"""
+            )
+
+
+class TestInitOutsideOfProjectSpecifyingInvalidProfile(TestInitOutsideOfProjectBase):
+    @mock.patch("dbt.task.init._get_adapter_plugin_names")
+    @mock.patch("click.prompt")
+    def test_init_task_outside_project_specifying_invalid_profile_errors(
+        self, mock_prompt, mock_get_adapter, project, project_name
+    ):
+        manager = Mock()
+        manager.attach_mock(mock_prompt, "prompt")
+        manager.prompt.side_effect = [
+            project_name,
+        ]
+        mock_get_adapter.return_value = [project.adapter.type()]
+
+        # This triggers a call to sys.exit(), requring the following to test it
+        with pytest.raises(SystemExit) as error:
+            run_dbt(["init", "--profile", "invalid"])
+        assert error.value.code == 1
+
+        manager.assert_has_calls(
+            [
+                call.prompt("Enter a name for your project (letters, digits, underscore)"),
+            ]
+        )
+
+
+class TestInitOutsideOfProjectSpecifyingProfileNoProfilesYml(TestInitOutsideOfProjectBase):
+    @mock.patch("dbt.task.init._get_adapter_plugin_names")
+    @mock.patch("click.prompt")
+    def test_init_task_outside_project_specifying_profile_no_profiles_yml_errors(
+        self, mock_prompt, mock_get_adapter, project, project_name
+    ):
+        manager = Mock()
+        manager.attach_mock(mock_prompt, "prompt")
+        manager.prompt.side_effect = [
+            project_name,
+        ]
+        mock_get_adapter.return_value = [project.adapter.type()]
+
+        # Override responses on specific files, default to 'real world' if not overriden
+        original_isfile = os.path.isfile
+        with mock.patch(
+            "os.path.isfile",
+            new=lambda path: {"profiles.yml": False}.get(
+                os.path.basename(path), original_isfile(path)
+            ),
+        ):
+            # This triggers a call to sys.exit(), requring the following to test it
+            with pytest.raises(SystemExit) as error:
+                run_dbt(["init", "--profile", "test"])
+            assert error.value.code == 1
+
+        manager.assert_has_calls(
+            [
+                call.prompt("Enter a name for your project (letters, digits, underscore)"),
+            ]
+        )


Code review comments:
diff_hunk: @@ -0,0 +1,7 @@
+kind: Fixes
+body: If --profile specified with dbt-init, create the project with the specified
+  profile
+time: 2023-04-24T16:16:42.994547-04:00
+custom:
+  Author: ezraerb
+  Issue: CT-1418
path: .changes/unreleased/Fixes-20230424-161642.yaml
text: @user1:
For `changie`, we use the GitHub issue number rather than the Jira issue number.
```suggestion
  Issue: "6154"
```