@@ -44,35 +44,7 @@ var resourceGitLabProjectSchema = map[string]*schema.Schema{
44
44
"default_branch" : {
45
45
Type : schema .TypeString ,
46
46
Optional : true ,
47
- DiffSuppressFunc : func (k , old , new string , d * schema.ResourceData ) bool {
48
- // `old` is the current value on GitLab side
49
- // `new` is the value that Terraform plans to set there
50
-
51
- log .Printf ("[DEBUG] default_branch DiffSuppressFunc old new" )
52
- log .Printf ("[DEBUG] (%T) %#v, (%T) %#v" , old , old , new , new )
53
-
54
- // If there is no current default branch, it means that the project is
55
- // empty and does not have branches. Setting the default branch will fail
56
- // with 400 error. The check will defer the setting of a default branch
57
- // to a time when the repository is no longer empty.
58
- if old == "" {
59
- if new != "" {
60
- log .Printf ("[WARN] not setting default_branch %#v on empty repo" , new )
61
- }
62
- return true
63
- }
64
-
65
- // For non-empty repositories GitLab automatically sets master as the
66
- // default branch. If the project resource doesn't specify default_branch
67
- // attribute, Terraform will force "master" => "" on the next run. This
68
- // check makes Terraform ignore default branch value until it is set in
69
- // .tf configuration. For schema.TypeString empty is equal to "".
70
- if new == "" {
71
- return true
72
- }
73
-
74
- return old == new
75
- },
47
+ Computed : true ,
76
48
},
77
49
"import_url" : {
78
50
Type : schema .TypeString ,
@@ -275,25 +247,31 @@ var resourceGitLabProjectSchema = map[string]*schema.Schema{
275
247
Default : "private" ,
276
248
ValidateFunc : validation .StringInSlice ([]string {"public" , "private" , "enabled" , "disabled" }, true ),
277
249
},
250
+ // The GitLab API requires that import_url is also set when mirror options are used
251
+ // Ref: https://github.com/gitlabhq/terraform-provider-gitlab/pull/449#discussion_r549729230
278
252
"mirror" : {
279
- Type : schema .TypeBool ,
280
- Optional : true ,
281
- Default : false ,
253
+ Type : schema .TypeBool ,
254
+ Optional : true ,
255
+ Default : false ,
256
+ RequiredWith : []string {"import_url" },
282
257
},
283
258
"mirror_trigger_builds" : {
284
- Type : schema .TypeBool ,
285
- Optional : true ,
286
- Default : false ,
259
+ Type : schema .TypeBool ,
260
+ Optional : true ,
261
+ Default : false ,
262
+ RequiredWith : []string {"import_url" },
287
263
},
288
264
"mirror_overwrites_diverged_branches" : {
289
- Type : schema .TypeBool ,
290
- Optional : true ,
291
- Default : false ,
265
+ Type : schema .TypeBool ,
266
+ Optional : true ,
267
+ Default : false ,
268
+ RequiredWith : []string {"import_url" },
292
269
},
293
270
"only_mirror_protected_branches" : {
294
- Type : schema .TypeBool ,
295
- Optional : true ,
296
- Default : false ,
271
+ Type : schema .TypeBool ,
272
+ Optional : true ,
273
+ Default : false ,
274
+ RequiredWith : []string {"import_url" },
297
275
},
298
276
"build_coverage_regex" : {
299
277
Type : schema .TypeString ,
@@ -316,43 +294,44 @@ func resourceGitlabProject() *schema.Resource {
316
294
317
295
func resourceGitlabProjectSetToState (d * schema.ResourceData , project * gitlab.Project ) error {
318
296
d .SetId (fmt .Sprintf ("%d" , project .ID ))
319
- values := map [string ]interface {}{
320
- "name" : project .Name ,
321
- "path" : project .Path ,
322
- "path_with_namespace" : project .PathWithNamespace ,
323
- "description" : project .Description ,
324
- "default_branch" : project .DefaultBranch ,
325
- "request_access_enabled" : project .RequestAccessEnabled ,
326
- "issues_enabled" : project .IssuesEnabled ,
327
- "merge_requests_enabled" : project .MergeRequestsEnabled ,
328
- "pipelines_enabled" : project .JobsEnabled ,
329
- "approvals_before_merge" : project .ApprovalsBeforeMerge ,
330
- "wiki_enabled" : project .WikiEnabled ,
331
- "snippets_enabled" : project .SnippetsEnabled ,
332
- "container_registry_enabled" : project .ContainerRegistryEnabled ,
333
- "lfs_enabled" : project .LFSEnabled ,
334
- "visibility_level" : string (project .Visibility ),
335
- "merge_method" : string (project .MergeMethod ),
336
- "only_allow_merge_if_pipeline_succeeds" : project .OnlyAllowMergeIfPipelineSucceeds ,
337
- "only_allow_merge_if_all_discussions_are_resolved" : project .OnlyAllowMergeIfAllDiscussionsAreResolved ,
338
- "namespace_id" : project .Namespace .ID ,
339
- "ssh_url_to_repo" : project .SSHURLToRepo ,
340
- "http_url_to_repo" : project .HTTPURLToRepo ,
341
- "web_url" : project .WebURL ,
342
- "runners_token" : project .RunnersToken ,
343
- "shared_runners_enabled" : project .SharedRunnersEnabled ,
344
- "tags" : project .TagList ,
345
- "archived" : project .Archived ,
346
- "remove_source_branch_after_merge" : project .RemoveSourceBranchAfterMerge ,
347
- "packages_enabled" : project .PackagesEnabled ,
348
- "pages_access_level" : string (project .PagesAccessLevel ),
349
- "mirror" : project .Mirror ,
350
- "mirror_trigger_builds" : project .MirrorTriggerBuilds ,
351
- "mirror_overwrites_diverged_branches" : project .MirrorOverwritesDivergedBranches ,
352
- "only_mirror_protected_branches" : project .OnlyMirrorProtectedBranches ,
353
- "build_coverage_regex" : project .BuildCoverageRegex ,
354
- }
355
- return setResourceData (d , values )
297
+ d .Set ("name" , project .Name )
298
+ d .Set ("path" , project .Path )
299
+ d .Set ("path_with_namespace" , project .PathWithNamespace )
300
+ d .Set ("description" , project .Description )
301
+ d .Set ("default_branch" , project .DefaultBranch )
302
+ d .Set ("request_access_enabled" , project .RequestAccessEnabled )
303
+ d .Set ("issues_enabled" , project .IssuesEnabled )
304
+ d .Set ("merge_requests_enabled" , project .MergeRequestsEnabled )
305
+ d .Set ("pipelines_enabled" , project .JobsEnabled )
306
+ d .Set ("approvals_before_merge" , project .ApprovalsBeforeMerge )
307
+ d .Set ("wiki_enabled" , project .WikiEnabled )
308
+ d .Set ("snippets_enabled" , project .SnippetsEnabled )
309
+ d .Set ("container_registry_enabled" , project .ContainerRegistryEnabled )
310
+ d .Set ("lfs_enabled" , project .LFSEnabled )
311
+ d .Set ("visibility_level" , string (project .Visibility ))
312
+ d .Set ("merge_method" , string (project .MergeMethod ))
313
+ d .Set ("only_allow_merge_if_pipeline_succeeds" , project .OnlyAllowMergeIfPipelineSucceeds )
314
+ d .Set ("only_allow_merge_if_all_discussions_are_resolved" , project .OnlyAllowMergeIfAllDiscussionsAreResolved )
315
+ d .Set ("namespace_id" , project .Namespace .ID )
316
+ d .Set ("ssh_url_to_repo" , project .SSHURLToRepo )
317
+ d .Set ("http_url_to_repo" , project .HTTPURLToRepo )
318
+ d .Set ("web_url" , project .WebURL )
319
+ d .Set ("runners_token" , project .RunnersToken )
320
+ d .Set ("shared_runners_enabled" , project .SharedRunnersEnabled )
321
+ if err := d .Set ("tags" , project .TagList ); err != nil {
322
+ return err
323
+ }
324
+ d .Set ("archived" , project .Archived )
325
+ d .Set ("remove_source_branch_after_merge" , project .RemoveSourceBranchAfterMerge )
326
+ d .Set ("packages_enabled" , project .PackagesEnabled )
327
+ d .Set ("pages_access_level" , string (project .PagesAccessLevel ))
328
+ d .Set ("mirror" , project .Mirror )
329
+ d .Set ("mirror_trigger_builds" , project .MirrorTriggerBuilds )
330
+ d .Set ("mirror_overwrites_diverged_branches" , project .MirrorOverwritesDivergedBranches )
331
+ d .Set ("only_mirror_protected_branches" , project .OnlyMirrorProtectedBranches )
332
+ d .Set ("build_coverage_regex" , project .BuildCoverageRegex )
333
+
334
+ return nil
356
335
}
357
336
358
337
func resourceGitlabProjectCreate (d * schema.ResourceData , meta interface {}) error {
@@ -393,6 +372,10 @@ func resourceGitlabProjectCreate(d *schema.ResourceData, meta interface{}) error
393
372
options .Description = gitlab .String (v .(string ))
394
373
}
395
374
375
+ if v , ok := d .GetOk ("default_branch" ); ok {
376
+ options .DefaultBranch = gitlab .String (v .(string ))
377
+ }
378
+
396
379
if v , ok := d .GetOk ("tags" ); ok {
397
380
options .TagList = stringSetToStringSlice (v .(* schema.Set ))
398
381
}
@@ -436,10 +419,8 @@ func resourceGitlabProjectCreate(d *schema.ResourceData, meta interface{}) error
436
419
// is committed to state since we set its ID
437
420
d .SetId (fmt .Sprintf ("%d" , project .ID ))
438
421
439
- _ , importURLSet := d .GetOk ("import_url" )
440
- _ , templateNameSet := d .GetOk ("template_name" )
441
- _ , templateProjectIDSet := d .GetOk ("template_project_id" )
442
- if importURLSet || templateNameSet || templateProjectIDSet {
422
+ // An import can be triggered by import_url or by creating the project from a template.
423
+ if project .ImportStatus != "none" {
443
424
log .Printf ("[DEBUG] waiting for project %q import to finish" , * options .Name )
444
425
445
426
stateConf := & resource.StateChangeConf {
@@ -459,6 +440,12 @@ func resourceGitlabProjectCreate(d *schema.ResourceData, meta interface{}) error
459
440
if _ , err := stateConf .WaitForState (); err != nil {
460
441
return fmt .Errorf ("error while waiting for project %q import to finish: %w" , * options .Name , err )
461
442
}
443
+
444
+ // Read the project again, so that we can detect the default branch.
445
+ project , _ , err = client .Projects .GetProject (project .ID , nil )
446
+ if err != nil {
447
+ return fmt .Errorf ("Failed to get project %q after completing import: %w" , d .Id (), err )
448
+ }
462
449
}
463
450
464
451
if d .Get ("archived" ).(bool ) {
@@ -480,9 +467,69 @@ func resourceGitlabProjectCreate(d *schema.ResourceData, meta interface{}) error
480
467
}
481
468
}
482
469
483
- // Some project settings can't be set in the Project Create API and have to
484
- // set in a second call after project creation.
485
- resourceGitlabProjectUpdate (d , meta ) // nolint // TODO: Resolve this golangci-lint issue: Error return value is not checked (errcheck)
470
+ // default_branch cannot always be set during creation.
471
+ // If the branch does not exist, the update will fail, so we also create it here.
472
+ // See: https://gitlab.com/gitlab-org/gitlab/-/issues/333426
473
+ // This logic may be removed when the above issue is resolved.
474
+ if v , ok := d .GetOk ("default_branch" ); ok && project .DefaultBranch != "" && project .DefaultBranch != v .(string ) {
475
+ oldDefaultBranch := project .DefaultBranch
476
+ newDefaultBranch := v .(string )
477
+
478
+ log .Printf ("[DEBUG] create branch %q for project %q" , newDefaultBranch , d .Id ())
479
+ _ , _ , err := client .Branches .CreateBranch (project .ID , & gitlab.CreateBranchOptions {
480
+ Branch : gitlab .String (newDefaultBranch ),
481
+ Ref : gitlab .String (oldDefaultBranch ),
482
+ })
483
+ if err != nil {
484
+ return fmt .Errorf ("Failed to create branch %q for project %q: %w" , newDefaultBranch , d .Id (), err )
485
+ }
486
+
487
+ log .Printf ("[DEBUG] set new default branch to %q for project %q" , newDefaultBranch , d .Id ())
488
+ _ , _ , err = client .Projects .EditProject (project .ID , & gitlab.EditProjectOptions {
489
+ DefaultBranch : gitlab .String (newDefaultBranch ),
490
+ })
491
+ if err != nil {
492
+ return fmt .Errorf ("Failed to set default branch to %q for project %q: %w" , newDefaultBranch , d .Id (), err )
493
+ }
494
+
495
+ log .Printf ("[DEBUG] protect new default branch %q for project %q" , newDefaultBranch , d .Id ())
496
+ _ , _ , err = client .ProtectedBranches .ProtectRepositoryBranches (project .ID , & gitlab.ProtectRepositoryBranchesOptions {
497
+ Name : gitlab .String (newDefaultBranch ),
498
+ })
499
+ if err != nil {
500
+ return fmt .Errorf ("Failed to protect default branch %q for project %q: %w" , newDefaultBranch , d .Id (), err )
501
+ }
502
+
503
+ log .Printf ("[DEBUG] unprotect old default branch %q for project %q" , oldDefaultBranch , d .Id ())
504
+ _ , err = client .ProtectedBranches .UnprotectRepositoryBranches (project .ID , oldDefaultBranch )
505
+ if err != nil {
506
+ return fmt .Errorf ("Failed to unprotect undesired default branch %q for project %q: %w" , oldDefaultBranch , d .Id (), err )
507
+ }
508
+
509
+ log .Printf ("[DEBUG] delete old default branch %q for project %q" , oldDefaultBranch , d .Id ())
510
+ _ , err = client .Branches .DeleteBranch (project .ID , oldDefaultBranch )
511
+ if err != nil {
512
+ return fmt .Errorf ("Failed to clean up undesired default branch %q for project %q: %w" , oldDefaultBranch , d .Id (), err )
513
+ }
514
+ }
515
+
516
+ var editProjectOptions gitlab.EditProjectOptions
517
+
518
+ if v , ok := d .GetOk ("mirror_overwrites_diverged_branches" ); ok {
519
+ editProjectOptions .MirrorOverwritesDivergedBranches = gitlab .Bool (v .(bool ))
520
+ editProjectOptions .ImportURL = gitlab .String (d .Get ("import_url" ).(string ))
521
+ }
522
+
523
+ if v , ok := d .GetOk ("only_mirror_protected_branches" ); ok {
524
+ editProjectOptions .OnlyMirrorProtectedBranches = gitlab .Bool (v .(bool ))
525
+ editProjectOptions .ImportURL = gitlab .String (d .Get ("import_url" ).(string ))
526
+ }
527
+
528
+ if (editProjectOptions != gitlab.EditProjectOptions {}) {
529
+ if _ , _ , err := client .Projects .EditProject (d .Id (), & editProjectOptions ); err != nil {
530
+ return fmt .Errorf ("Could not update project %q: %w" , d .Id (), err )
531
+ }
532
+ }
486
533
487
534
return resourceGitlabProjectRead (d , meta )
488
535
}
@@ -501,8 +548,7 @@ func resourceGitlabProjectRead(d *schema.ResourceData, meta interface{}) error {
501
548
return nil
502
549
}
503
550
504
- err = resourceGitlabProjectSetToState (d , project )
505
- if err != nil {
551
+ if err := resourceGitlabProjectSetToState (d , project ); err != nil {
506
552
return err
507
553
}
508
554
@@ -516,9 +562,7 @@ func resourceGitlabProjectRead(d *schema.ResourceData, meta interface{}) error {
516
562
return fmt .Errorf ("Failed to get push rules for project %q: %w" , d .Id (), err )
517
563
}
518
564
519
- d .Set ("push_rules" , flattenProjectPushRules (pushRules )) // lintignore: XR004 // TODO: Resolve this tfproviderlint issue
520
-
521
- return nil
565
+ return d .Set ("push_rules" , flattenProjectPushRules (pushRules ))
522
566
}
523
567
524
568
func resourceGitlabProjectUpdate (d * schema.ResourceData , meta interface {}) error {
@@ -620,29 +664,21 @@ func resourceGitlabProjectUpdate(d *schema.ResourceData, meta interface{}) error
620
664
}
621
665
622
666
if d .HasChange ("mirror" ) {
623
- // It appears that GitLab API requires that import_url is also set when `mirror` is updated/changed
624
- // Ref: https://github.com/gitlabhq/terraform-provider-gitlab/pull/449#discussion_r549729230
625
667
options .ImportURL = gitlab .String (d .Get ("import_url" ).(string ))
626
668
options .Mirror = gitlab .Bool (d .Get ("mirror" ).(bool ))
627
669
}
628
670
629
671
if d .HasChange ("mirror_trigger_builds" ) {
630
- // It appears that GitLab API requires that import_url is also set when `mirror_trigger_builds` is updated/changed
631
- // Ref: https://github.com/gitlabhq/terraform-provider-gitlab/pull/449#discussion_r549729230
632
672
options .ImportURL = gitlab .String (d .Get ("import_url" ).(string ))
633
673
options .MirrorTriggerBuilds = gitlab .Bool (d .Get ("mirror_trigger_builds" ).(bool ))
634
674
}
635
675
636
676
if d .HasChange ("only_mirror_protected_branches" ) {
637
- // It appears that GitLab API requires that import_url is also set when `only_mirror_protected_branches` is updated/changed
638
- // Ref: https://github.com/gitlabhq/terraform-provider-gitlab/pull/449#discussion_r549729230
639
677
options .ImportURL = gitlab .String (d .Get ("import_url" ).(string ))
640
678
options .OnlyMirrorProtectedBranches = gitlab .Bool (d .Get ("only_mirror_protected_branches" ).(bool ))
641
679
}
642
680
643
681
if d .HasChange ("mirror_overwrites_diverged_branches" ) {
644
- // It appears that GitLab API requires that import_url is also set when `mirror_overwrites_diverged_branches` is updated/changed
645
- // Ref: https://github.com/gitlabhq/terraform-provider-gitlab/pull/449#discussion_r549729230
646
682
options .ImportURL = gitlab .String (d .Get ("import_url" ).(string ))
647
683
options .MirrorOverwritesDivergedBranches = gitlab .Bool (d .Get ("mirror_overwrites_diverged_branches" ).(bool ))
648
684
}
0 commit comments