Skip to content

Commit ce64a32

Browse files
authored
Merge branch 'gitlabhq:main' into main
2 parents c589353 + 1c819f5 commit ce64a32

File tree

4 files changed

+180
-59
lines changed

4 files changed

+180
-59
lines changed

docs/resources/project.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ The `gitlab_project` resource allows to manage the lifecycle of a project.
2020

2121
A project can either be created in a group or user namespace.
2222

23-
-> **Default Branch Protection Workaround** Projects are created with default branch protection.
24-
Since this default branch protection is not currently managed via Terraform, to workaround this limitation,
23+
-> **Default Branch Protection Workaround** Projects are created with default branch protection.
24+
Since this default branch protection is not currently managed via Terraform, to workaround this limitation,
2525
you can remove the default branch protection via the API and create your desired Terraform managed branch protection.
26-
In the `gitlab_project` resource, define a `local-exec` provisioner which invokes
27-
the `/projects/:id/protected_branches/:name` API via curl to delete the branch protection on the default
26+
In the `gitlab_project` resource, define a `local-exec` provisioner which invokes
27+
the `/projects/:id/protected_branches/:name` API via curl to delete the branch protection on the default
2828
branch using a `DELETE` request. Then define the desired branch protection using the `gitlab_branch_protection` resource.
2929

3030
**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ce/api/projects.html)

internal/provider/resource_gitlab_project.go

Lines changed: 66 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -645,11 +645,11 @@ var _ = registerResource("gitlab_project", func() *schema.Resource {
645645
646646
A project can either be created in a group or user namespace.
647647
648-
-> **Default Branch Protection Workaround** Projects are created with default branch protection.
649-
Since this default branch protection is not currently managed via Terraform, to workaround this limitation,
648+
-> **Default Branch Protection Workaround** Projects are created with default branch protection.
649+
Since this default branch protection is not currently managed via Terraform, to workaround this limitation,
650650
you can remove the default branch protection via the API and create your desired Terraform managed branch protection.
651-
In the ` + "`gitlab_project`" + ` resource, define a ` + "`local-exec`" + ` provisioner which invokes
652-
the ` + "`/projects/:id/protected_branches/:name`" + ` API via curl to delete the branch protection on the default
651+
In the ` + "`gitlab_project`" + ` resource, define a ` + "`local-exec`" + ` provisioner which invokes
652+
the ` + "`/projects/:id/protected_branches/:name`" + ` API via curl to delete the branch protection on the default
653653
branch using a ` + "`DELETE`" + ` request. Then define the desired branch protection using the ` + "`gitlab_branch_protection`" + ` resource.
654654
655655
**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ce/api/projects.html)`,
@@ -1018,66 +1018,73 @@ func resourceGitlabProjectCreate(ctx context.Context, d *schema.ResourceData, me
10181018
}
10191019
}
10201020

1021-
// default_branch cannot always be set during creation.
1022-
// If the branch does not exist, the update will fail, so we also create it here.
1023-
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/333426
1024-
// This logic may be removed when the above issue is resolved.
1025-
if v, ok := d.GetOk("default_branch"); ok && project.DefaultBranch != "" && project.DefaultBranch != v.(string) {
1026-
oldDefaultBranch := project.DefaultBranch
1027-
newDefaultBranch := v.(string)
1021+
// see: https://gitlab.com/gitlab-org/gitlab/-/issues/333426
1022+
noDefaultBranchAPISupport, err := isGitLabVersionLessThan(ctx, client, "14.10")()
1023+
if err != nil {
1024+
return diag.Errorf("unable to get information if `default_branch` handling is supported in the GitLab instance: %v", err)
1025+
}
1026+
1027+
if noDefaultBranchAPISupport {
1028+
// default_branch cannot always be set during creation.
1029+
// If the branch does not exist, the update will fail, so we also create it here.
1030+
// This logic may be removed when the above issue is resolved.
1031+
if v, ok := d.GetOk("default_branch"); ok && project.DefaultBranch != "" && project.DefaultBranch != v.(string) {
1032+
oldDefaultBranch := project.DefaultBranch
1033+
newDefaultBranch := v.(string)
1034+
1035+
log.Printf("[DEBUG] create branch %q for project %q", newDefaultBranch, d.Id())
1036+
_, _, err := client.Branches.CreateBranch(project.ID, &gitlab.CreateBranchOptions{
1037+
Branch: gitlab.String(newDefaultBranch),
1038+
Ref: gitlab.String(oldDefaultBranch),
1039+
}, gitlab.WithContext(ctx))
1040+
if err != nil {
1041+
return diag.Errorf("Failed to create branch %q for project %q: %s", newDefaultBranch, d.Id(), err)
1042+
}
10281043

1029-
log.Printf("[DEBUG] create branch %q for project %q", newDefaultBranch, d.Id())
1030-
_, _, err := client.Branches.CreateBranch(project.ID, &gitlab.CreateBranchOptions{
1031-
Branch: gitlab.String(newDefaultBranch),
1032-
Ref: gitlab.String(oldDefaultBranch),
1033-
}, gitlab.WithContext(ctx))
1034-
if err != nil {
1035-
return diag.Errorf("Failed to create branch %q for project %q: %s", newDefaultBranch, d.Id(), err)
1036-
}
1044+
log.Printf("[DEBUG] set new default branch to %q for project %q", newDefaultBranch, d.Id())
1045+
_, _, err = client.Projects.EditProject(project.ID, &gitlab.EditProjectOptions{
1046+
DefaultBranch: gitlab.String(newDefaultBranch),
1047+
}, gitlab.WithContext(ctx))
1048+
if err != nil {
1049+
return diag.Errorf("Failed to set default branch to %q for project %q: %s", newDefaultBranch, d.Id(), err)
1050+
}
10371051

1038-
log.Printf("[DEBUG] set new default branch to %q for project %q", newDefaultBranch, d.Id())
1039-
_, _, err = client.Projects.EditProject(project.ID, &gitlab.EditProjectOptions{
1040-
DefaultBranch: gitlab.String(newDefaultBranch),
1041-
}, gitlab.WithContext(ctx))
1042-
if err != nil {
1043-
return diag.Errorf("Failed to set default branch to %q for project %q: %s", newDefaultBranch, d.Id(), err)
1044-
}
1052+
log.Printf("[DEBUG] protect new default branch %q for project %q", newDefaultBranch, d.Id())
1053+
_, _, err = client.ProtectedBranches.ProtectRepositoryBranches(project.ID, &gitlab.ProtectRepositoryBranchesOptions{
1054+
Name: gitlab.String(newDefaultBranch),
1055+
}, gitlab.WithContext(ctx))
1056+
if err != nil {
1057+
return diag.Errorf("Failed to protect default branch %q for project %q: %s", newDefaultBranch, d.Id(), err)
1058+
}
10451059

1046-
log.Printf("[DEBUG] protect new default branch %q for project %q", newDefaultBranch, d.Id())
1047-
_, _, err = client.ProtectedBranches.ProtectRepositoryBranches(project.ID, &gitlab.ProtectRepositoryBranchesOptions{
1048-
Name: gitlab.String(newDefaultBranch),
1049-
}, gitlab.WithContext(ctx))
1050-
if err != nil {
1051-
return diag.Errorf("Failed to protect default branch %q for project %q: %s", newDefaultBranch, d.Id(), err)
1052-
}
1060+
log.Printf("[DEBUG] check for protection on old default branch %q for project %q", oldDefaultBranch, d.Id())
1061+
branch, _, err := client.ProtectedBranches.GetProtectedBranch(project.ID, oldDefaultBranch, gitlab.WithContext(ctx))
1062+
if err != nil && !is404(err) {
1063+
return diag.Errorf("Failed to check for protected default branch %q for project %q: %v", oldDefaultBranch, d.Id(), err)
1064+
}
1065+
if branch == nil {
1066+
log.Printf("[DEBUG] Default protected branch %q for project %q does not exist", oldDefaultBranch, d.Id())
1067+
} else {
1068+
log.Printf("[DEBUG] unprotect old default branch %q for project %q", oldDefaultBranch, d.Id())
1069+
_, err = client.ProtectedBranches.UnprotectRepositoryBranches(project.ID, oldDefaultBranch, gitlab.WithContext(ctx))
1070+
if err != nil {
1071+
return diag.Errorf("Failed to unprotect undesired default branch %q for project %q: %v", oldDefaultBranch, d.Id(), err)
1072+
}
1073+
}
10531074

1054-
log.Printf("[DEBUG] check for protection on old default branch %q for project %q", oldDefaultBranch, d.Id())
1055-
branch, _, err := client.ProtectedBranches.GetProtectedBranch(project.ID, oldDefaultBranch, gitlab.WithContext(ctx))
1056-
if err != nil && !is404(err) {
1057-
return diag.Errorf("Failed to check for protected default branch %q for project %q: %v", oldDefaultBranch, d.Id(), err)
1058-
}
1059-
if branch == nil {
1060-
log.Printf("[DEBUG] Default protected branch %q for project %q does not exist", oldDefaultBranch, d.Id())
1061-
} else {
1062-
log.Printf("[DEBUG] unprotect old default branch %q for project %q", oldDefaultBranch, d.Id())
1063-
_, err = client.ProtectedBranches.UnprotectRepositoryBranches(project.ID, oldDefaultBranch, gitlab.WithContext(ctx))
1075+
log.Printf("[DEBUG] delete old default branch %q for project %q", oldDefaultBranch, d.Id())
1076+
_, err = client.Branches.DeleteBranch(project.ID, oldDefaultBranch, gitlab.WithContext(ctx))
10641077
if err != nil {
1065-
return diag.Errorf("Failed to unprotect undesired default branch %q for project %q: %v", oldDefaultBranch, d.Id(), err)
1078+
return diag.Errorf("Failed to clean up undesired default branch %q for project %q: %s", oldDefaultBranch, d.Id(), err)
10661079
}
10671080
}
1068-
1069-
log.Printf("[DEBUG] delete old default branch %q for project %q", oldDefaultBranch, d.Id())
1070-
_, err = client.Branches.DeleteBranch(project.ID, oldDefaultBranch, gitlab.WithContext(ctx))
1071-
if err != nil {
1072-
return diag.Errorf("Failed to clean up undesired default branch %q for project %q: %s", oldDefaultBranch, d.Id(), err)
1073-
}
10741081
}
10751082

10761083
// If the project is assigned to a group namespace and the group has *default branch protection*
10771084
// disabled (`default_branch_protection = 0`) then we don't have to wait for one.
10781085
waitForDefaultBranchProtection, err := expectDefaultBranchProtection(ctx, client, project)
10791086
if err != nil {
1080-
return diag.Errorf("Failed to fetch group the project %d is owned by: %+v", project.ID, err)
1087+
return diag.Errorf("Failed to discover if branch protection is enabled by default or not for project %d: %+v", project.ID, err)
10811088
}
10821089

10831090
if waitForDefaultBranchProtection {
@@ -1759,6 +1766,7 @@ func namespaceOrPathChanged(ctx context.Context, d *schema.ResourceDiff, meta in
17591766
}
17601767

17611768
func expectDefaultBranchProtection(ctx context.Context, client *gitlab.Client, project *gitlab.Project) (bool, error) {
1769+
// If the project is part of a group it may have default branch protection disabled for its projects
17621770
if project.Namespace.Kind == "group" {
17631771
group, _, err := client.Groups.GetGroup(project.Namespace.ID, nil, gitlab.WithContext(ctx))
17641772
if err != nil {
@@ -1768,7 +1776,11 @@ func expectDefaultBranchProtection(ctx context.Context, client *gitlab.Client, p
17681776
return group.DefaultBranchProtection != 0, nil
17691777
}
17701778

1771-
// projects which are not assigned to a group can't have a "no branch protection" default,
1772-
// thus, we always expect a default branch protection.
1773-
return true, nil
1779+
// // If the project is not part of a group it may have default branch protection disabled because of the instance-wide application settings
1780+
settings, _, err := client.Settings.GetSettings(nil, gitlab.WithContext(ctx))
1781+
if err != nil {
1782+
return false, err
1783+
}
1784+
1785+
return settings.DefaultBranchProtection != 0, nil
17741786
}

internal/provider/resource_gitlab_project_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,13 @@ resource "gitlab_project" "foo" {
844844
},
845845
),
846846
},
847+
// Verify Import
848+
{
849+
ResourceName: "gitlab_project.foo",
850+
ImportState: true,
851+
ImportStateVerify: true,
852+
ImportStateVerifyIgnore: []string{"initialize_with_readme"},
853+
},
847854
},
848855
})
849856
}
@@ -883,6 +890,99 @@ func TestAccGitlabProject_CreateProjectInUserNamespace(t *testing.T) {
883890
})
884891
}
885892

893+
func TestAccGitlabProject_InstanceBranchProtectionDisabled(t *testing.T) {
894+
rInt := acctest.RandInt()
895+
896+
resource.Test(t, resource.TestCase{
897+
ProviderFactories: providerFactories,
898+
CheckDestroy: testAccCheckGitlabProjectDestroy,
899+
Steps: []resource.TestStep{
900+
{
901+
PreConfig: func() {
902+
settings, _, err := testGitlabClient.Settings.GetSettings()
903+
if err != nil {
904+
t.Fatalf("failed to get settings: %v", err)
905+
}
906+
t.Cleanup(func() {
907+
if _, _, err := testGitlabClient.Settings.UpdateSettings(&gitlab.UpdateSettingsOptions{DefaultBranchProtection: gitlab.Int(settings.DefaultBranchProtection)}); err != nil {
908+
t.Fatalf("failed to update instance-wide default branch protection setting to default: %v", err)
909+
}
910+
})
911+
912+
if _, _, err := testGitlabClient.Settings.UpdateSettings(&gitlab.UpdateSettingsOptions{DefaultBranchProtection: gitlab.Int(0)}); err != nil {
913+
t.Fatalf("failed to update instance-wide default branch protection setting: %v", err)
914+
}
915+
},
916+
Config: ` `, // requires a space for empty config
917+
},
918+
// Without explicit default branch
919+
{
920+
Config: fmt.Sprintf(`
921+
resource "gitlab_project" "foo" {
922+
name = "foo-%d"
923+
description = "Terraform acceptance tests"
924+
visibility_level = "public"
925+
initialize_with_readme = true
926+
}
927+
`, rInt),
928+
},
929+
// Verify Import
930+
{
931+
ResourceName: "gitlab_project.foo",
932+
ImportState: true,
933+
ImportStateVerify: true,
934+
ImportStateVerifyIgnore: []string{"initialize_with_readme"},
935+
},
936+
// Force a destroy for the project so that it can be recreated as the same resource
937+
{
938+
Config: ` `, // requires a space for empty config
939+
},
940+
// With explicit default branch set to instance-wide default
941+
{
942+
Config: fmt.Sprintf(`
943+
resource "gitlab_project" "foo" {
944+
name = "foo-%d"
945+
description = "Terraform acceptance tests"
946+
visibility_level = "public"
947+
default_branch = "main"
948+
initialize_with_readme = true
949+
}
950+
`, rInt),
951+
},
952+
// Verify Import
953+
{
954+
ResourceName: "gitlab_project.foo",
955+
ImportState: true,
956+
ImportStateVerify: true,
957+
ImportStateVerifyIgnore: []string{"initialize_with_readme"},
958+
},
959+
// Force a destroy for the project so that it can be recreated as the same resource
960+
{
961+
Config: ` `, // requires a space for empty config
962+
},
963+
// With custom default branch
964+
{
965+
Config: fmt.Sprintf(`
966+
resource "gitlab_project" "foo" {
967+
name = "foo-%d-custom-default-branch"
968+
description = "Terraform acceptance tests"
969+
visibility_level = "public"
970+
default_branch = "foobar-non-default-branch"
971+
initialize_with_readme = true
972+
}
973+
`, rInt),
974+
},
975+
// Verify Import
976+
{
977+
ResourceName: "gitlab_project.foo",
978+
ImportState: true,
979+
ImportStateVerify: true,
980+
ImportStateVerifyIgnore: []string{"initialize_with_readme"},
981+
},
982+
},
983+
})
984+
}
985+
886986
type testAccGitlabProjectMirroredExpectedAttributes struct {
887987
Mirror bool
888988
MirrorTriggerBuilds bool

scripts/gitlab.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,13 @@
88
registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab-registry.pem"
99
registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab-registry.key"
1010

11-
gitlab_rails['initial_shared_runners_registration_token'] = "ACCTEST1234567890123_RUNNER_REG_TOKEN"
11+
gitlab_rails['initial_shared_runners_registration_token'] = "ACCTEST1234567890123_RUNNER_REG_TOKEN"
12+
13+
# This setting is required to disable caching for application settings
14+
# which is required to test different scenarios in the acceptance tests.
15+
# see https://gitlab.com/gitlab-org/gitlab/-/issues/364812#note_986366898
16+
# see https://github.com/gitlabhq/terraform-provider-gitlab/pull/1128
17+
gitlab_rails['application_settings_cache_seconds'] = 0
18+
gitlab_rails['env'] = {
19+
'IN_MEMORY_APPLICATION_SETTINGS' => 'false'
20+
}

0 commit comments

Comments
 (0)