diff --git a/github/data_source_github_repository.go b/github/data_source_github_repository.go index f2de8b0c10..4eeb37064c 100644 --- a/github/data_source_github_repository.go +++ b/github/data_source_github_repository.go @@ -241,8 +241,9 @@ func dataSourceGithubRepository() *schema.Resource { }, }, "pages": { - Type: schema.TypeList, - Computed: true, + Type: schema.TypeList, + Computed: true, + Deprecated: "Use the github_repository_pages data source instead. This field will be removed in a future version.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "source": { diff --git a/github/data_source_github_repository_pages.go b/github/data_source_github_repository_pages.go new file mode 100644 index 0000000000..e65927d83b --- /dev/null +++ b/github/data_source_github_repository_pages.go @@ -0,0 +1,156 @@ +package github + +import ( + "context" + "net/http" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubRepositoryPages() *schema.Resource { + return &schema.Resource{ + Description: "Use this data source to retrieve GitHub Pages configuration for a repository.", + ReadContext: dataSourceGithubRepositoryPagesRead, + + Schema: map[string]*schema.Schema{ + "repository": { + Type: schema.TypeString, + Required: true, + Description: "The repository name to get GitHub Pages information for.", + }, + // TODO: Uncomment this when we are ready to support owner fields properly. https://github.com/integrations/terraform-provider-github/pull/3166#discussion_r2816053082 + // "owner": { + // Type: schema.TypeString, + // Required: true, + // Description: "The owner of the repository.", + // }, + "source": { + Type: schema.TypeList, + Computed: true, + Description: "The source branch and directory for the rendered Pages site.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "branch": { + Type: schema.TypeString, + Computed: true, + Description: "The repository branch used to publish the site's source files.", + }, + "path": { + Type: schema.TypeString, + Computed: true, + Description: "The repository directory from which the site publishes.", + }, + }, + }, + }, + "build_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of GitHub Pages site. Can be 'legacy' or 'workflow'.", + }, + "cname": { + Type: schema.TypeString, + Computed: true, + Description: "The custom domain for the repository.", + }, + "custom_404": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether the rendered GitHub Pages site has a custom 404 page.", + }, + "html_url": { + Type: schema.TypeString, + Computed: true, + Description: "The absolute URL (with scheme) to the rendered GitHub Pages site.", + }, + "build_status": { + Type: schema.TypeString, + Computed: true, + Description: "The GitHub Pages site's build status e.g. 'building' or 'built'.", + }, + "api_url": { + Type: schema.TypeString, + Computed: true, + Description: "The API URL of the GitHub Pages resource.", + }, + "public": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether the GitHub Pages site is publicly visible. If set to `true`, the site is accessible to anyone on the internet. If set to `false`, the site will only be accessible to users who have at least `read` access to the repository that published the site.", + }, + "https_enforced": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether the rendered GitHub Pages site will only be served over HTTPS.", + }, + }, + } +} + +func dataSourceGithubRepositoryPagesRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + meta := m.(*Owner) + client := meta.v3client + + owner := meta.name // TODO: Add owner support // d.Get("owner").(string) + repoName := d.Get("repository").(string) + + pages, resp, err := client.Repositories.GetPagesInfo(ctx, owner, repoName) + if err != nil { + if resp != nil && resp.StatusCode == http.StatusNotFound { + return diag.Errorf("GitHub Pages not found for repository %s/%s", owner, repoName) + } + return diag.Errorf("error reading repository pages: %s", err.Error()) + } + + repo, _, err := client.Repositories.Get(ctx, owner, repoName) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(strconv.Itoa(int(repo.GetID()))) + + if err := d.Set("build_type", pages.GetBuildType()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("cname", pages.GetCNAME()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("custom_404", pages.GetCustom404()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("html_url", pages.GetHTMLURL()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("build_status", pages.GetStatus()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("api_url", pages.GetURL()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("public", pages.GetPublic()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("https_enforced", pages.GetHTTPSEnforced()); err != nil { + return diag.FromErr(err) + } + // Set source only for legacy build type + if pages.GetBuildType() == "legacy" && pages.GetSource() != nil { + source := []map[string]any{ + { + "branch": pages.GetSource().GetBranch(), + "path": pages.GetSource().GetPath(), + }, + } + if err := d.Set("source", source); err != nil { + return diag.FromErr(err) + } + } else { + if err := d.Set("source", []map[string]any{}); err != nil { + return diag.FromErr(err) + } + } + + return nil +} diff --git a/github/data_source_github_repository_pages_test.go b/github/data_source_github_repository_pages_test.go new file mode 100644 index 0000000000..36a45b94f0 --- /dev/null +++ b/github/data_source_github_repository_pages_test.go @@ -0,0 +1,122 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/compare" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccGithubRepositoryPagesDataSource(t *testing.T) { + baseRepoVisibility := "public" + if testAccConf.authMode == enterprise { + baseRepoVisibility = "private" + } + + t.Run("reads_pages_configuration", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-ds-%s", testResourcePrefix, randomID) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "legacy" + source { + branch = "main" + path = "/" + } + } + + data "github_repository_pages" "test" { + repository = github_repository.test.name + + depends_on = [github_repository_pages.test] + } + `, repoName, baseRepoVisibility) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + skipUnauthenticated(t) + }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("build_type"), "github_repository_pages.test", tfjsonpath.New("build_type"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("branch"), "github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("branch"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("path"), "github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("path"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("cname"), "github_repository_pages.test", tfjsonpath.New("cname"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("custom_404"), "github_repository_pages.test", tfjsonpath.New("custom_404"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("html_url"), "github_repository_pages.test", tfjsonpath.New("html_url"), compare.ValuesSame()), + statecheck.ExpectKnownValue("data.github_repository_pages.test", tfjsonpath.New("build_status"), knownvalue.NotNull()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("api_url"), "github_repository_pages.test", tfjsonpath.New("api_url"), compare.ValuesSame()), + statecheck.ExpectKnownValue("data.github_repository_pages.test", tfjsonpath.New("public"), knownvalue.Bool(testAccConf.authMode != enterprise)), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("https_enforced"), "github_repository_pages.test", tfjsonpath.New("https_enforced"), compare.ValuesSame()), + }, + }, + }, + }) + }) + t.Run("reads_pages_enterprise_configuration", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-ds-%s", testResourcePrefix, randomID) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "legacy" + source { + branch = "main" + path = "/" + } + public = false + } + + data "github_repository_pages" "test" { + repository = github_repository.test.name + + depends_on = [github_repository_pages.test] + } + `, repoName, baseRepoVisibility) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessEnterprise(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("build_type"), "github_repository_pages.test", tfjsonpath.New("build_type"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("branch"), "github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("branch"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("path"), "github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("path"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("cname"), "github_repository_pages.test", tfjsonpath.New("cname"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("custom_404"), "github_repository_pages.test", tfjsonpath.New("custom_404"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("html_url"), "github_repository_pages.test", tfjsonpath.New("html_url"), compare.ValuesSame()), + statecheck.ExpectKnownValue("data.github_repository_pages.test", tfjsonpath.New("build_status"), knownvalue.NotNull()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("api_url"), "github_repository_pages.test", tfjsonpath.New("api_url"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("public"), "github_repository_pages.test", tfjsonpath.New("public"), compare.ValuesSame()), + statecheck.CompareValuePairs("data.github_repository_pages.test", tfjsonpath.New("https_enforced"), "github_repository_pages.test", tfjsonpath.New("https_enforced"), compare.ValuesSame()), + }, + }, + }, + }) + }) +} diff --git a/github/provider.go b/github/provider.go index 2d5d6dc33a..e57eba2313 100644 --- a/github/provider.go +++ b/github/provider.go @@ -198,6 +198,7 @@ func Provider() *schema.Provider { "github_repository_environment_deployment_policy": resourceGithubRepositoryEnvironmentDeploymentPolicy(), "github_repository_file": resourceGithubRepositoryFile(), "github_repository_milestone": resourceGithubRepositoryMilestone(), + "github_repository_pages": resourceGithubRepositoryPages(), "github_repository_project": resourceGithubRepositoryProject(), "github_repository_pull_request": resourceGithubRepositoryPullRequest(), "github_repository_ruleset": resourceGithubRepositoryRuleset(), @@ -282,6 +283,7 @@ func Provider() *schema.Provider { "github_repository_deployment_branch_policies": dataSourceGithubRepositoryDeploymentBranchPolicies(), "github_repository_file": dataSourceGithubRepositoryFile(), "github_repository_milestone": dataSourceGithubRepositoryMilestone(), + "github_repository_pages": dataSourceGithubRepositoryPages(), "github_repository_pull_request": dataSourceGithubRepositoryPullRequest(), "github_repository_pull_requests": dataSourceGithubRepositoryPullRequests(), "github_repository_teams": dataSourceGithubRepositoryTeams(), diff --git a/github/resource_github_repository.go b/github/resource_github_repository.go index 2c57b54182..49f9f83490 100644 --- a/github/resource_github_repository.go +++ b/github/resource_github_repository.go @@ -328,6 +328,7 @@ func resourceGithubRepository() *schema.Resource { MaxItems: 1, Optional: true, Description: "The repository's GitHub Pages configuration", + Deprecated: "Use the github_repository_pages resource instead. This field will be removed in a future version.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "source": { @@ -859,7 +860,8 @@ func resourceGithubRepositoryRead(ctx context.Context, d *schema.ResourceData, m _ = d.Set("squash_merge_commit_title", repo.GetSquashMergeCommitTitle()) } - if repo.GetHasPages() { + _, isPagesConfigured := d.GetOk("pages") + if repo.GetHasPages() && isPagesConfigured { pages, _, err := client.Repositories.GetPagesInfo(ctx, owner, repoName) if err != nil { return diag.FromErr(err) diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go new file mode 100644 index 0000000000..bdef828e1e --- /dev/null +++ b/github/resource_github_repository_pages.go @@ -0,0 +1,432 @@ +package github + +import ( + "context" + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/google/go-github/v83/github" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceGithubRepositoryPages() *schema.Resource { + return &schema.Resource{ + Description: "Manages GitHub Pages for a repository.", + CreateContext: resourceGithubRepositoryPagesCreate, + ReadContext: resourceGithubRepositoryPagesRead, + UpdateContext: resourceGithubRepositoryPagesUpdate, + DeleteContext: resourceGithubRepositoryPagesDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceGithubRepositoryPagesImport, + }, + + Schema: map[string]*schema.Schema{ + "repository": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The repository name to configure GitHub Pages for.", + }, + "repository_id": { + Type: schema.TypeInt, + Computed: true, + Description: "The ID of the repository to configure GitHub Pages for.", + }, + // TODO: Uncomment this when we are ready to support owner fields properly. https://github.com/integrations/terraform-provider-github/pull/3166#discussion_r2816053082 + // "owner": { + // Type: schema.TypeString, + // Required: true, + // ForceNew: true, + // Description: "The owner of the repository to configure GitHub Pages for.", + // }, + "source": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Description: "The source branch and directory for the rendered Pages site.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "branch": { + Type: schema.TypeString, + Required: true, + Description: "The repository branch used to publish the site's source files. (i.e. 'main' or 'gh-pages')", + }, + "path": { + Type: schema.TypeString, + Optional: true, + Default: "/", + Description: "The repository directory from which the site publishes (Default: '/')", + }, + }, + }, + }, + "build_type": { + Type: schema.TypeString, + Optional: true, + Default: "legacy", + Description: "The type of GitHub Pages site to build. Can be 'legacy' or 'workflow'.", + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"legacy", "workflow"}, false)), + }, + "cname": { + Type: schema.TypeString, + Optional: true, + Description: "The custom domain for the repository.", + }, + "custom_404": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether the rendered GitHub Pages site has a custom 404 page.", + }, + "html_url": { + Type: schema.TypeString, + Computed: true, + Description: "The absolute URL (with scheme) to the rendered GitHub Pages site.", + }, + "build_status": { + Type: schema.TypeString, + Computed: true, + Description: "The GitHub Pages site's build status e.g. 'building' or 'built'.", + }, + "api_url": { + Type: schema.TypeString, + Computed: true, + Description: "The API URL of the GitHub Pages resource.", + }, + "public": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Whether the GitHub Pages site is publicly visible. If set to `true`, the site is accessible to anyone on the internet. If set to `false`, the site will only be accessible to users who have at least `read` access to the repository that published the site.", + }, + "https_enforced": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + RequiredWith: []string{"cname"}, + Description: "Whether the rendered GitHub Pages site will only be served over HTTPS. Requires 'cname' to be set.", + }, + }, + CustomizeDiff: customdiff.All(resourceGithubRepositoryPagesDiff, diffRepository), + } +} + +func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + meta := m.(*Owner) + client := meta.v3client + + owner := meta.name // TODO: Add owner support // d.Get("owner").(string) + repoName := d.Get("repository").(string) + + pagesReq := expandPagesForCreate(d) + pages, _, err := client.Repositories.EnablePages(ctx, owner, repoName, pagesReq) + if err != nil { + return diag.FromErr(err) + } + + repo, _, err := client.Repositories.Get(ctx, owner, repoName) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(strconv.Itoa(int(repo.GetID()))) + + if err = d.Set("repository_id", int(repo.GetID())); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("build_type", pages.GetBuildType()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("cname", pages.GetCNAME()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("custom_404", pages.GetCustom404()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("html_url", pages.GetHTMLURL()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("build_status", pages.GetStatus()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("api_url", pages.GetURL()); err != nil { + return diag.FromErr(err) + } + + // Check if we have values set that can't be configured as part of the create logic + cname, cnameOK := d.GetOk("cname") + public, publicOKExists := d.GetOkExists("public") //nolint:staticcheck // SA1019: d.GetOkExists is deprecated but necessary for bool fields + httpsEnforced, httpsEnforcedExists := d.GetOkExists("https_enforced") //nolint:staticcheck // SA1019: d.GetOkExists is deprecated but necessary for bool fields + tflog.Debug(ctx, "Do we have values set that need the update logic?", map[string]any{ + "public": public, + "public_ok_exists": publicOKExists, + "https_enforced": httpsEnforced, + "https_enforced_exists": httpsEnforcedExists, + "cname": cname, + "cname_ok": cnameOK, + }) + + if cnameOK || publicOKExists || httpsEnforcedExists { + update := &github.PagesUpdate{} + + if cnameOK { + update.CNAME = github.Ptr(cname.(string)) + } + + if publicOKExists { + update.Public = github.Ptr(public.(bool)) + } + + if httpsEnforcedExists { + update.HTTPSEnforced = github.Ptr(httpsEnforced.(bool)) + } + + _, err = client.Repositories.UpdatePages(ctx, owner, repoName, update) + if err != nil { + return diag.FromErr(err) + } + } + if !publicOKExists { + if err := d.Set("public", pages.GetPublic()); err != nil { + return diag.FromErr(err) + } + } + if !httpsEnforcedExists { + if err := d.Set("https_enforced", pages.GetHTTPSEnforced()); err != nil { + return diag.FromErr(err) + } + } + + return nil +} + +func resourceGithubRepositoryPagesRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + meta := m.(*Owner) + client := meta.v3client + + owner := meta.name // TODO: Add owner support // d.Get("owner").(string) + repoName := d.Get("repository").(string) + + pages, resp, err := client.Repositories.GetPagesInfo(ctx, owner, repoName) + if err != nil { + if resp != nil && resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + return diag.Errorf("error reading repository pages: %s", err.Error()) + } + + if err := d.Set("build_type", pages.GetBuildType()); err != nil { + return diag.FromErr(err) + } + upstreamCname := pages.GetCNAME() + if upstreamCname != "" { + if err := d.Set("cname", upstreamCname); err != nil { + return diag.FromErr(err) + } + } else { + if err := d.Set("cname", nil); err != nil { + return diag.FromErr(err) + } + } + + if err := d.Set("custom_404", pages.GetCustom404()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("html_url", pages.GetHTMLURL()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("build_status", pages.GetStatus()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("api_url", pages.GetURL()); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("public", pages.GetPublic()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("https_enforced", pages.GetHTTPSEnforced()); err != nil { + return diag.FromErr(err) + } + + // Set source only for legacy build type + if pages.GetBuildType() == "legacy" && pages.GetSource() != nil { + source := []map[string]any{ + { + "branch": pages.GetSource().GetBranch(), + "path": pages.GetSource().GetPath(), + }, + } + if err := d.Set("source", source); err != nil { + return diag.FromErr(err) + } + } else { + if err := d.Set("source", nil); err != nil { + return diag.FromErr(err) + } + } + + return nil +} + +func resourceGithubRepositoryPagesUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + meta := m.(*Owner) + client := meta.v3client + + owner := meta.name // TODO: Add owner support // d.Get("owner").(string) + repoName := d.Get("repository").(string) + + update := &github.PagesUpdate{} + + if d.HasChange("cname") { + cname := d.Get("cname").(string) + if cname != "" { + update.CNAME = github.Ptr(cname) + } + } + + if d.HasChange("public") { + public, ok := d.Get("public").(bool) + if ok { + update.Public = github.Ptr(public) + } + } + + if d.HasChange("https_enforced") { + httpsEnforced, ok := d.Get("https_enforced").(bool) + if ok { + update.HTTPSEnforced = github.Ptr(httpsEnforced) + } + } + + if d.HasChange("build_type") { + buildType := d.Get("build_type").(string) + update.BuildType = github.Ptr(buildType) + } + + if d.HasChange("source") || d.HasChange("build_type") { + buildType := d.Get("build_type").(string) + if buildType == "legacy" { + if source, ok := d.GetOk("source"); ok { + sourceList := source.([]any) + if len(sourceList) > 0 { + sourceMap := sourceList[0].(map[string]any) + branch := sourceMap["branch"].(string) + path := sourceMap["path"].(string) + update.Source = &github.PagesSource{ + Branch: &branch, + Path: &path, + } + } + } + } + } + + _, err := client.Repositories.UpdatePages(ctx, owner, repoName, update) + if err != nil { + return diag.FromErr(err) + } + + return nil +} + +func resourceGithubRepositoryPagesDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + meta := m.(*Owner) + client := meta.v3client + + owner := meta.name // TODO: Add owner support // d.Get("owner").(string) + repoName := d.Get("repository").(string) + + _, err := client.Repositories.DisablePages(ctx, owner, repoName) + if err != nil { + return diag.FromErr(handleArchivedRepoDelete(err, "repository pages", d.Id(), owner, repoName)) + } + + return nil +} + +func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.ResourceData, m any) ([]*schema.ResourceData, error) { + repoName := d.Id() + if strings.Contains(repoName, " ") { + return nil, fmt.Errorf("invalid ID specified: supplied ID must be the slug of the repository name") + } + + meta := m.(*Owner) + owner := meta.name + client := meta.v3client + + repo, _, err := client.Repositories.Get(ctx, owner, repoName) + if err != nil { + return nil, err + } + + d.SetId(strconv.Itoa(int(repo.GetID()))) + // if err := d.Set("owner", owner); err != nil { // TODO: Add owner support + // return nil, err + // } + if err := d.Set("repository", repoName); err != nil { + return nil, err + } + if err = d.Set("repository_id", int(repo.GetID())); err != nil { + return nil, err + } + + return []*schema.ResourceData{d}, nil +} + +func resourceGithubRepositoryPagesDiff(ctx context.Context, d *schema.ResourceDiff, _ any) error { + if d.Id() == "" { + return nil + } + + buildType := d.Get("build_type").(string) + _, ok := d.GetOk("source") + + if buildType == "workflow" && ok { + return fmt.Errorf("'source' is not supported for workflow build type") + } + if buildType == "legacy" && !ok { + return fmt.Errorf("'source' is required for legacy build type") + } + + return nil +} + +func expandPagesForCreate(d *schema.ResourceData) *github.Pages { + pages := &github.Pages{} + + buildType := d.Get("build_type").(string) + pages.BuildType = github.Ptr(buildType) + + if buildType == "legacy" { + if source, ok := d.GetOk("source"); ok { + sourceList := source.([]any) + if len(sourceList) > 0 { + sourceMap := sourceList[0].(map[string]any) + branch := sourceMap["branch"].(string) + pagesSource := &github.PagesSource{ + Branch: github.Ptr(branch), + } + if path, ok := sourceMap["path"].(string); ok && path != "" && path != "/" { + pagesSource.Path = github.Ptr(path) + } + pages.Source = pagesSource + } + } + // Default to main branch if no source specified + if pages.Source == nil { + pages.Source = &github.PagesSource{ + Branch: github.Ptr("main"), + } + } + } + + return pages +} diff --git a/github/resource_github_repository_pages_test.go b/github/resource_github_repository_pages_test.go new file mode 100644 index 0000000000..622db11d61 --- /dev/null +++ b/github/resource_github_repository_pages_test.go @@ -0,0 +1,291 @@ +package github + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/compare" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccGithubRepositoryPages(t *testing.T) { + baseRepoVisibility := "public" + if testAccConf.authMode == enterprise { + baseRepoVisibility = "private" + } + + t.Run("creates_pages_with_legacy_build_type", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-%s", testResourcePrefix, randomID) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "legacy" + source { + branch = "main" + path = "/" + } + } + `, repoName, baseRepoVisibility) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("legacy")), + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("branch"), knownvalue.StringExact("main")), + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("source").AtSliceIndex(0).AtMapKey("path"), knownvalue.StringExact("/")), + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("api_url"), knownvalue.StringRegexp(regexp.MustCompile("https://.*"))), + }, + }, + }, + }) + }) + + t.Run("creates_pages_with_workflow_build_type", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-%s", testResourcePrefix, randomID) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "workflow" + } + `, repoName, baseRepoVisibility) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("workflow")), + }, + }, + }, + }) + }) + + t.Run("updates_pages_configuration", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-%s", testResourcePrefix, randomID) + + sourceConfig := ` +source { + branch = "main" + path = "/" +} +` + config := ` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "%s" + %s + } + ` + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(config, repoName, baseRepoVisibility, "legacy", sourceConfig), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("legacy")), + }, + }, + { + Config: fmt.Sprintf(config, repoName, baseRepoVisibility, "workflow", ""), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("workflow")), + }, + }, + }, + }) + }) + + t.Run("creates_pages_with_private_visibility", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-%s", testResourcePrefix, randomID) + + config := ` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "workflow" + + public = false + } + ` + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + skipUnlessEnterprise(t) + }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(config, repoName, baseRepoVisibility), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("public"), knownvalue.Bool(false)), + }, + }, + }, + }) + }) + t.Run("updates_pages_visibility", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-%s", testResourcePrefix, randomID) + + config := ` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "workflow" + + public = %t + } + ` + + publicValuesDiffer := statecheck.CompareValue(compare.ValuesDiffer()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + skipUnlessEnterprise(t) + if os.Getenv("GH_TEST_ENTERPRISE_IS_EMU") == "true" { + t.Skip("Skipping as enterprise test mode is EMU") + } + }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(config, repoName, baseRepoVisibility, true), + ConfigStateChecks: []statecheck.StateCheck{ + publicValuesDiffer.AddStateValue("github_repository_pages.test", tfjsonpath.New("public")), + }, + }, + { + Config: fmt.Sprintf(config, repoName, baseRepoVisibility, false), + ConfigStateChecks: []statecheck.StateCheck{ + publicValuesDiffer.AddStateValue("github_repository_pages.test", tfjsonpath.New("public")), + }, + }, + }, + }) + }) + + t.Run("errors_when_https_enforced_without_cname", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-%s", testResourcePrefix, randomID) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "workflow" + https_enforced = true + } + `, repoName, baseRepoVisibility) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ExpectError: regexp.MustCompile(`all of .cname,https_enforced. must be specified`), + }, + }, + }) + }) + + t.Run("imports_pages_configuration", func(t *testing.T) { + randomID := acctest.RandString(5) + repoName := fmt.Sprintf("%spages-%s", testResourcePrefix, randomID) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "%s" + visibility = "%s" + auto_init = true + + } + + resource "github_repository_pages" "test" { + repository = github_repository.test.name + build_type = "legacy" + source { + branch = "main" + path = "/" + } + } + `, repoName, baseRepoVisibility) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("legacy")), + }, + }, + { + ResourceName: "github_repository_pages.test", + ImportState: true, + ImportStateId: repoName, + }, + }, + }) + }) +} diff --git a/github/resource_github_repository_test.go b/github/resource_github_repository_test.go index cc6df3c64d..a585b7fca7 100644 --- a/github/resource_github_repository_test.go +++ b/github/resource_github_repository_test.go @@ -1110,47 +1110,31 @@ resource "github_repository" "test" { }) }) - t.Run("updates repos to public visibility", func(t *testing.T) { + t.Run("updates_repos_to_public_visibility", func(t *testing.T) { randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) testRepoName := fmt.Sprintf("%spublic-vuln-%s", testResourcePrefix, randomID) - config := fmt.Sprintf(` + config := ` resource "github_repository" "test" { name = "%s" - visibility = "private" + visibility = "%s" } - `, testRepoName) - - checks := map[string]resource.TestCheckFunc{ - "before": resource.ComposeTestCheckFunc( - resource.TestCheckNoResourceAttr( - "github_repository.test", "vulnerability_alerts", - ), - ), - "after": resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "github_repository.test", "vulnerability_alerts", - "true", - ), - resource.TestCheckResourceAttr( - "github_repository.test", "visibility", - "private", - ), - ), - } + ` resource.Test(t, resource.TestCase{ PreCheck: func() { skipUnauthenticated(t) }, ProviderFactories: providerFactories, Steps: []resource.TestStep{ { - Config: config, - Check: checks["before"], + Config: fmt.Sprintf(config, testRepoName, "private"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository.test", "visibility", "private"), + ), }, { - Config: strings.Replace(config, - `}`, - "vulnerability_alerts = true\n}", 1), - Check: checks["after"], + Config: fmt.Sprintf(config, testRepoName, "public"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository.test", "visibility", "public"), + ), }, }, }) diff --git a/website/docs/d/repository.html.markdown b/website/docs/d/repository.html.markdown index 071da23c89..2c4475edfa 100644 --- a/website/docs/d/repository.html.markdown +++ b/website/docs/d/repository.html.markdown @@ -21,110 +21,110 @@ data "github_repository" "example" { The following arguments are supported: -* `name` - (Optional) The name of the repository. +- `name` - (Optional) The name of the repository. -* `full_name` - (Optional) Full name of the repository (in `org/name` format). +- `full_name` - (Optional) Full name of the repository (in `org/name` format). ## Attributes Reference -* `node_id` - the Node ID of the repository. +- `node_id` - the Node ID of the repository. -* `description` - A description of the repository. +- `description` - A description of the repository. -* `homepage_url` - URL of a page describing the project. +- `homepage_url` - URL of a page describing the project. -* `private` - Whether the repository is private. +- `private` - Whether the repository is private. -* `visibility` - Whether the repository is public, private or internal. +- `visibility` - Whether the repository is public, private or internal. -* `has_issues` - Whether the repository has GitHub Issues enabled. +- `has_issues` - Whether the repository has GitHub Issues enabled. -* `has_discussions` - Whether the repository has GitHub Discussions enabled. +- `has_discussions` - Whether the repository has GitHub Discussions enabled. -* `has_projects` - Whether the repository has the GitHub Projects enabled. +- `has_projects` - Whether the repository has the GitHub Projects enabled. -* `has_wiki` - Whether the repository has the GitHub Wiki enabled. +- `has_wiki` - Whether the repository has the GitHub Wiki enabled. -* `is_template` - Whether the repository is a template repository. +- `is_template` - Whether the repository is a template repository. -* `fork` - Whether the repository is a fork. +- `fork` - Whether the repository is a fork. -* `allow_merge_commit` - Whether the repository allows merge commits. +- `allow_merge_commit` - Whether the repository allows merge commits. -* `allow_squash_merge` - Whether the repository allows squash merges. +- `allow_squash_merge` - Whether the repository allows squash merges. -* `allow_rebase_merge` - Whether the repository allows rebase merges. +- `allow_rebase_merge` - Whether the repository allows rebase merges. -* `allow_auto_merge` - Whether the repository allows auto-merging pull requests. +- `allow_auto_merge` - Whether the repository allows auto-merging pull requests. -* `allow_forking` - Whether the repository allows private forking; this is only relevant if the repository is owned by an organization and is private or internal. +- `allow_forking` - Whether the repository allows private forking; this is only relevant if the repository is owned by an organization and is private or internal. -* `squash_merge_commit_title` - The default value for a squash merge commit title. +- `squash_merge_commit_title` - The default value for a squash merge commit title. -* `squash_merge_commit_message` - The default value for a squash merge commit message. +- `squash_merge_commit_message` - The default value for a squash merge commit message. -* `merge_commit_title` - The default value for a merge commit title. +- `merge_commit_title` - The default value for a merge commit title. -* `merge_commit_message` - The default value for a merge commit message. +- `merge_commit_message` - The default value for a merge commit message. -* `has_downloads` - (**DEPRECATED**) Whether the repository has Downloads feature enabled. This attribute is no longer in use, but it hasn't been removed yet. It will be removed in a future version. See [this discussion](https://github.com/orgs/community/discussions/102145#discussioncomment-8351756). +- `has_downloads` - (**DEPRECATED**) Whether the repository has Downloads feature enabled. This attribute is no longer in use, but it hasn't been removed yet. It will be removed in a future version. See [this discussion](https://github.com/orgs/community/discussions/102145#discussioncomment-8351756). -* `default_branch` - The name of the default branch of the repository. +- `default_branch` - The name of the default branch of the repository. -* `primary_language` - The primary language used in the repository. +- `primary_language` - The primary language used in the repository. -* `archived` - Whether the repository is archived. +- `archived` - Whether the repository is archived. -* `pages` - The repository's GitHub Pages configuration. +- `pages` - (**DEPRECATED**) The repository's GitHub Pages configuration. Use the `github_repository_pages` data source instead. This field will be removed in a future version. -* `topics` - The list of topics of the repository. +- `topics` - The list of topics of the repository. -* `template` - The repository source template configuration. +- `template` - The repository source template configuration. -* `html_url` - URL to the repository on the web. +- `html_url` - URL to the repository on the web. -* `ssh_clone_url` - URL that can be provided to `git clone` to clone the repository via SSH. +- `ssh_clone_url` - URL that can be provided to `git clone` to clone the repository via SSH. -* `http_clone_url` - URL that can be provided to `git clone` to clone the repository via HTTPS. +- `http_clone_url` - URL that can be provided to `git clone` to clone the repository via HTTPS. -* `git_clone_url` - URL that can be provided to `git clone` to clone the repository anonymously via the git protocol. +- `git_clone_url` - URL that can be provided to `git clone` to clone the repository anonymously via the git protocol. -* `svn_url` - URL that can be provided to `svn checkout` to check out the repository via GitHub's Subversion protocol emulation. +- `svn_url` - URL that can be provided to `svn checkout` to check out the repository via GitHub's Subversion protocol emulation. -* `node_id` - GraphQL global node id for use with v4 API +- `node_id` - GraphQL global node id for use with v4 API -* `repo_id` - GitHub ID for the repository +- `repo_id` - GitHub ID for the repository -* `repository_license` - An Array of GitHub repository licenses. Each `repository_license` block consists of the fields documented below. +- `repository_license` - An Array of GitHub repository licenses. Each `repository_license` block consists of the fields documented below. ___ The `repository_license` block consists of: -* `content` - Content of the license file, encoded by encoding scheme mentioned below. -* `download_url` - The URL to download the raw content of the license file. -* `encoding` - The encoding used for the content (e.g., "base64"). -* `git_url` - The URL to access information about the license file as a Git blob. -* `html_url` - The URL to view the license file on GitHub. -* `license` - `license` block consists of the fields documented below. -* `name` - The name of the license file (e.g., "LICENSE"). -* `path` - The path to the license file within the repository. -* `sha` - The SHA hash of the license file. -* `size` - The size of the license file in bytes. -* `type` - The type of the content, (e.g., "file"). -* `url` - The URL to access information about the license file on GitHub. +- `content` - Content of the license file, encoded by encoding scheme mentioned below. +- `download_url` - The URL to download the raw content of the license file. +- `encoding` - The encoding used for the content (e.g., "base64"). +- `git_url` - The URL to access information about the license file as a Git blob. +- `html_url` - The URL to view the license file on GitHub. +- `license` - `license` block consists of the fields documented below. +- `name` - The name of the license file (e.g., "LICENSE"). +- `path` - The path to the license file within the repository. +- `sha` - The SHA hash of the license file. +- `size` - The size of the license file in bytes. +- `type` - The type of the content, (e.g., "file"). +- `url` - The URL to access information about the license file on GitHub. The `license` block consists of: -* `body` - The text of the license. -* `conditions` - Conditions associated with the license. -* `description` - A description of the license. -* `featured` - Indicates if the license is featured. -* `html_url` - The URL to view the license details on GitHub. -* `implementation` - Details about the implementation of the license. -* `key` - A key representing the license type (e.g., "apache-2.0"). -* `limitations` - Limitations associated with the license. -* `name` - The name of the license (e.g., "Apache License 2.0"). -* `permissions` - Permissions associated with the license. -* `spdx_id` - The SPDX identifier for the license (e.g., "Apache-2.0"). -* `url` - The URL to access information about the license on GitHub. +- `body` - The text of the license. +- `conditions` - Conditions associated with the license. +- `description` - A description of the license. +- `featured` - Indicates if the license is featured. +- `html_url` - The URL to view the license details on GitHub. +- `implementation` - Details about the implementation of the license. +- `key` - A key representing the license type (e.g., "apache-2.0"). +- `limitations` - Limitations associated with the license. +- `name` - The name of the license (e.g., "Apache License 2.0"). +- `permissions` - Permissions associated with the license. +- `spdx_id` - The SPDX identifier for the license (e.g., "Apache-2.0"). +- `url` - The URL to access information about the license on GitHub. diff --git a/website/docs/d/repository_pages.html.markdown b/website/docs/d/repository_pages.html.markdown new file mode 100644 index 0000000000..95c5cb73cc --- /dev/null +++ b/website/docs/d/repository_pages.html.markdown @@ -0,0 +1,54 @@ +--- +layout: "github" +page_title: "GitHub: github_repository_pages" +description: |- + Get information on GitHub Pages for a repository +--- + +# github_repository_pages + +Use this data source to retrieve GitHub Pages configuration for a repository. + +## Example Usage + +```hcl +data "github_repository_pages" "example" { + repository = "my-repo" +} +``` + +## Argument Reference + +The following arguments are supported: + +- `repository` - (Required) The repository name to get GitHub Pages information for. + +## Attribute Reference + +The following attributes are exported: + +- `build_type` - The type of GitHub Pages site. Can be `legacy` or `workflow`. + +- `cname` - The custom domain for the repository. + +- `custom_404` - Whether the rendered GitHub Pages site has a custom 404 page. + +- `html_url` - The absolute URL (with scheme) to the rendered GitHub Pages site. + +- `source` - The source branch and directory for the rendered Pages site. See [Source](#source) below for details. + +- `build_status` - The GitHub Pages site's build status (e.g., `building` or `built`). + +- `api_url` - The API URL of the GitHub Pages resource. + +- `public` - Whether the GitHub Pages site is public. + +- `https_enforced` - Whether HTTPS is enforced for the GitHub Pages site. This setting only applies when a custom domain is configured. + +### Source + +The `source` block contains: + +- `branch` - The repository branch used to publish the site's source files. + +- `path` - The repository directory from which the site publishes. diff --git a/website/docs/r/repository.html.markdown b/website/docs/r/repository.html.markdown index 157fd01caa..3bc3796830 100644 --- a/website/docs/r/repository.html.markdown +++ b/website/docs/r/repository.html.markdown @@ -29,24 +29,6 @@ resource "github_repository" "example" { } ``` -## Example Usage with GitHub Pages Enabled - -```hcl -resource "github_repository" "example" { - name = "example" - description = "My awesome web page" - - private = false - - pages { - source { - branch = "master" - path = "/docs" - } - } -} -``` - ## Example Usage with Repository Forking ```hcl @@ -130,7 +112,7 @@ initial repository creation and create the target branch inside of the repositor * `archive_on_destroy` - (Optional) Set to `true` to archive the repository instead of deleting on destroy. -* `pages` - (Optional) The repository's GitHub Pages configuration. See [GitHub Pages Configuration](#github-pages-configuration) below for details. +* `pages` - (Optional) (**DEPRECATED**) The repository's GitHub Pages configuration. Use the `github_repository_pages` resource instead. This field will be removed in a future version. See [GitHub Pages Configuration](#github-pages-configuration) below for details. * `security_and_analysis` - (Optional) The repository's [security and analysis](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-security-and-analysis-settings-for-your-repository) configuration. See [Security and Analysis Configuration](#security-and-analysis-configuration) below for details. diff --git a/website/docs/r/repository_pages.html.markdown b/website/docs/r/repository_pages.html.markdown new file mode 100644 index 0000000000..855a04c5f2 --- /dev/null +++ b/website/docs/r/repository_pages.html.markdown @@ -0,0 +1,137 @@ +--- +layout: "github" +page_title: "GitHub: github_repository_pages" +description: |- + Manages GitHub Pages for a repository +--- + +# github_repository_pages + +This resource allows you to manage GitHub Pages for a repository. See the +[documentation](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages) +for details on GitHub Pages. + +The authenticated user must be a repository administrator, maintainer, or have the 'manage GitHub Pages settings' permission. OAuth app tokens and personal access tokens (classic) need the repo scope to use this resource. + +## Example Usage + +### Legacy Build Type + +```hcl +resource "github_repository" "example" { + name = "my-repo" + visibility = "public" + auto_init = true + + lifecycle { + ignore_changes = [ + pages, + ] + } +} + +resource "github_repository_pages" "example" { + repository = github_repository.example.name + build_type = "legacy" + + source { + branch = "main" + path = "/" + } +} +``` + +### Workflow Build Type (GitHub Actions) + +```hcl +resource "github_repository" "example" { + name = "my-repo" + visibility = "public" + auto_init = true + + lifecycle { + ignore_changes = [ + pages, + ] + } +} + +resource "github_repository_pages" "example" { + repository = github_repository.example.name + build_type = "workflow" +} +``` + +### With Custom Domain + +```hcl +resource "github_repository" "example" { + name = "my-repo" + visibility = "public" + auto_init = true + + lifecycle { + ignore_changes = [ + pages, + ] + } +} + +resource "github_repository_pages" "example" { + repository = github_repository.example.name + build_type = "legacy" + cname = "example.com" + https_enforced = true + + source { + branch = "main" + path = "/docs" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `repository` - (Required) The repository name to configure GitHub Pages for. + +- `build_type` - (Optional) The type of GitHub Pages site to build. Can be `legacy` or `workflow`. Defaults to `legacy`. + +- `source` - (Optional) The source branch and directory for the rendered Pages site. Required when `build_type` is `legacy`. See [Source](#source) below for details. + +- `cname` - (Optional) The custom domain for the repository. + +- `public` - (Optional) Whether the GitHub Pages site is public. + +- `https_enforced` - (Optional) Whether HTTPS is enforced for the GitHub Pages site. GitHub Pages sites serve over HTTPS by default; this setting only applies when a custom domain (`cname`) is configured. Requires `cname` to be set. + +### Source + +The `source` block supports the following: + +- `branch` - (Required) The repository branch used to publish the site's source files (e.g., `main` or `gh-pages`). + +- `path` - (Optional) The repository directory from which the site publishes. Defaults to `/`. Can be `/` or `/docs`. + +## Attribute Reference + +In addition to the above arguments, the following attributes are exported: + +- `repository_id` - The ID of the repository. + +- `custom_404` - Whether the rendered GitHub Pages site has a custom 404 page. + +- `html_url` - The absolute URL (with scheme) to the rendered GitHub Pages site. + +- `build_status` - The GitHub Pages site's build status (e.g., `building` or `built`). + +- `api_url` - The API URL of the GitHub Pages resource. + +## Import + +GitHub repository pages can be imported using the `repository-slug`, e.g. + +```sh +terraform import github_repository_pages.example my-repo +```