From 5ca38b8482f185a2cf77db39160b50205ebe49aa Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sun, 8 Feb 2026 15:59:13 +0200 Subject: [PATCH 01/21] Add resource for repository pages and deprecate it from repository R Signed-off-by: Timo Sand --- github/provider.go | 1 + github/resource_github_repository.go | 1 + github/resource_github_repository_pages.go | 337 ++++++++++++++++++ .../resource_github_repository_pages_test.go | 197 ++++++++++ website/docs/r/repository.html.markdown | 20 +- website/docs/r/repository_pages.html.markdown | 135 +++++++ 6 files changed, 672 insertions(+), 19 deletions(-) create mode 100644 github/resource_github_repository_pages.go create mode 100644 github/resource_github_repository_pages_test.go create mode 100644 website/docs/r/repository_pages.html.markdown diff --git a/github/provider.go b/github/provider.go index 2d5d6dc33a..d42cad576d 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(), diff --git a/github/resource_github_repository.go b/github/resource_github_repository.go index 2c57b54182..5abfbdbb84 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": { diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go new file mode 100644 index 0000000000..c2c54d9e20 --- /dev/null +++ b/github/resource_github_repository_pages.go @@ -0,0 +1,337 @@ +package github + +import ( + "context" + "fmt" + "net/http" + + "github.com/google/go-github/v82/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "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_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The repository name to configure GitHub Pages for.", + }, + "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.", + }, + }, + CustomizeDiff: resourceGithubRepositoryPagesDiff, + } +} + +func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + meta := m.(*Owner) + client := meta.v3client + + owner := d.Get("owner").(string) + repoName := d.Get("repository_name").(string) + + pages := expandPagesForCreate(d) + pages, _, err := client.Repositories.EnablePages(ctx, owner, repoName, pages) + if err != nil { + return diag.FromErr(err) + } + + id, err := buildID(owner, repoName) + if err != nil { + return diag.FromErr(err) + } + d.SetId(id) + + 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) + } + + // Handle CNAME update after creation if specified + if cname, ok := d.GetOk("cname"); ok && cname.(string) != "" { + update := &github.PagesUpdate{ + CNAME: github.Ptr(cname.(string)), + } + _, err = client.Repositories.UpdatePages(ctx, owner, repoName, update) + if 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 := d.Get("owner").(string) + repoName := d.Get("repository_name").(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) + } + 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) + } + + // 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 := d.Get("owner").(string) + repoName := d.Get("repository_name").(string) + + update := &github.PagesUpdate{} + + if d.HasChange("cname") { + cname := d.Get("cname").(string) + if cname != "" { + update.CNAME = github.Ptr(cname) + } + } + + 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 := d.Get("owner").(string) + repoName := d.Get("repository_name").(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, meta any) ([]*schema.ResourceData, error) { + owner, repoName, err := parseID2(d.Id()) + if err != nil { + return nil, fmt.Errorf("invalid ID specified: supplied ID must be written as /. Original error: %w", err) + } + if err := d.Set("owner", owner); err != nil { + return nil, err + } + if err := d.Set("repository_name", repoName); err != nil { + return nil, err + } + + id, err := buildID(owner, repoName) + if err != nil { + return nil, err + } + d.SetId(id) + + 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) + switch buildType { + case "workflow": + if _, ok := d.GetOk("source"); ok { + return fmt.Errorf("'source' is not supported for workflow build type") + } + return nil + case "legacy": + if _, ok := d.GetOk("source"); !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..eacf400b24 --- /dev/null +++ b/github/resource_github_repository_pages_test.go @@ -0,0 +1,197 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +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 + + lifecycle { + ignore_changes = [ + pages, + ] + } + } + + resource "github_repository_pages" "test" { + owner = "%s" + repository_name = github_repository.test.name + build_type = "legacy" + source { + branch = "main" + path = "/" + } + } + `, repoName, baseRepoVisibility, testAccConf.owner) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "legacy"), + resource.TestCheckResourceAttr("github_repository_pages.test", "source.0.branch", "main"), + resource.TestCheckResourceAttr("github_repository_pages.test", "source.0.path", "/"), + resource.TestCheckResourceAttrSet("github_repository_pages.test", "api_url"), + ), + }, + }, + }) + }) + + 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 + + lifecycle { + ignore_changes = [ + pages, + ] + } + } + + resource "github_repository_pages" "test" { + owner = "%s" + repository_name = github_repository.test.name + build_type = "workflow" + } + `, repoName, baseRepoVisibility, testAccConf.owner) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "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 + + lifecycle { + ignore_changes = [ + pages, + ] + } + } + + resource "github_repository_pages" "test" { + owner = "%s" + repository_name = 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, testAccConf.owner, "legacy", sourceConfig), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "legacy"), + ), + }, + { + Config: fmt.Sprintf(config, repoName, baseRepoVisibility, testAccConf.owner, "workflow", ""), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "workflow"), + ), + }, + }, + }) + }) + + 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 + + lifecycle { + ignore_changes = [ + pages, + ] + } + } + + resource "github_repository_pages" "test" { + owner = "%s" + repository_name = github_repository.test.name + build_type = "legacy" + source { + branch = "main" + path = "/" + } + } + `, repoName, baseRepoVisibility, testAccConf.owner) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "legacy"), + ), + }, + { + ResourceName: "github_repository_pages.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + }) +} 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..7f5ccc38d4 --- /dev/null +++ b/website/docs/r/repository_pages.html.markdown @@ -0,0 +1,135 @@ +--- +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" { + owner = "my-org" + repository_name = 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" { + owner = "my-org" + repository_name = 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" { + owner = "my-org" + repository_name = github_repository.example.name + build_type = "legacy" + cname = "example.com" + + source { + branch = "main" + path = "/docs" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `owner` - (Required) The owner of the repository to configure GitHub Pages for. + +- `repository_name` - (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. + +### 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: + +- `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 `owner:repository` format: + +```sh +terraform import github_repository_pages.example my-org:my-repo +``` From 7c82e68f71b7edd056f51fb4a18947ed329c431e Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sun, 8 Feb 2026 16:03:10 +0200 Subject: [PATCH 02/21] Add data source for repository pages and deprecate it from repository DS Signed-off-by: Timo Sand --- github/data_source_github_repository.go | 5 +- github/data_source_github_repository_pages.go | 138 ++++++++++++++++++ ...ata_source_github_repository_pages_test.go | 68 +++++++++ github/provider.go | 1 + website/docs/d/repository.html.markdown | 2 +- website/docs/d/repository_pages.html.markdown | 53 +++++++ 6 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 github/data_source_github_repository_pages.go create mode 100644 github/data_source_github_repository_pages_test.go create mode 100644 website/docs/d/repository_pages.html.markdown 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..f313ed8da4 --- /dev/null +++ b/github/data_source_github_repository_pages.go @@ -0,0 +1,138 @@ +package github + +import ( + "context" + "net/http" + + "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_name": { + Type: schema.TypeString, + Required: true, + Description: "The repository name to get GitHub Pages information for.", + }, + "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.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The GitHub Pages site's build status e.g. 'building' or 'built'.", + }, + "url": { + Type: schema.TypeString, + Computed: true, + Description: "The API URL of the GitHub Pages resource.", + }, + }, + } +} + +func dataSourceGithubRepositoryPagesRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + meta := m.(*Owner) + client := meta.v3client + + owner := d.Get("owner").(string) + repoName := d.Get("repository_name").(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()) + } + + id, err := buildID(owner, repoName) + if err != nil { + return diag.FromErr(err) + } + d.SetId(id) + + 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("status", pages.GetStatus()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("url", pages.GetURL()); 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..ab27e5f3d2 --- /dev/null +++ b/github/data_source_github_repository_pages_test.go @@ -0,0 +1,68 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +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 + + lifecycle { + ignore_changes = [ + pages, + ] + } + } + + resource "github_repository_pages" "test" { + owner = "%s" + repository_name = github_repository.test.name + build_type = "legacy" + source { + branch = "main" + path = "/" + } + } + + data "github_repository_pages" "test" { + owner = "%s" + repository_name = github_repository.test.name + + depends_on = [github_repository_pages.test] + } + `, repoName, baseRepoVisibility, testAccConf.owner, testAccConf.owner) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.github_repository_pages.test", "build_type", "legacy"), + resource.TestCheckResourceAttr("data.github_repository_pages.test", "source.0.branch", "main"), + resource.TestCheckResourceAttr("data.github_repository_pages.test", "source.0.path", "/"), + resource.TestCheckResourceAttrSet("data.github_repository_pages.test", "url"), + ), + }, + }, + }) + }) +} diff --git a/github/provider.go b/github/provider.go index d42cad576d..e57eba2313 100644 --- a/github/provider.go +++ b/github/provider.go @@ -283,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/website/docs/d/repository.html.markdown b/website/docs/d/repository.html.markdown index 071da23c89..e63334da23 100644 --- a/website/docs/d/repository.html.markdown +++ b/website/docs/d/repository.html.markdown @@ -75,7 +75,7 @@ The following arguments are supported: * `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. diff --git a/website/docs/d/repository_pages.html.markdown b/website/docs/d/repository_pages.html.markdown new file mode 100644 index 0000000000..992c36cac2 --- /dev/null +++ b/website/docs/d/repository_pages.html.markdown @@ -0,0 +1,53 @@ +--- +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" { + owner = "my-org" + repository = "my-repo" +} +``` + +## Argument Reference + +The following arguments are supported: + +- `owner` - (Required) The owner of the repository. + +- `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. + +- `status` - The GitHub Pages site's build status (e.g., `building` or `built`). + +- `url` - The API URL of the GitHub Pages resource. + +### 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. From 2ed3f9f1d187dd968ead541ca766300ad360bad6 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sun, 8 Feb 2026 16:03:26 +0200 Subject: [PATCH 03/21] Update formatting of docs page Signed-off-by: Timo Sand --- website/docs/d/repository.html.markdown | 122 ++++++++++++------------ 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/website/docs/d/repository.html.markdown b/website/docs/d/repository.html.markdown index e63334da23..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` - (**DEPRECATED**) The repository's GitHub Pages configuration. Use the `github_repository_pages` data source instead. This field will be removed in a future version. +- `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. From 7cbf7a664dea4548f1fca1120fef1a6ae9c07030 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sun, 8 Feb 2026 16:31:23 +0200 Subject: [PATCH 04/21] Change `github_repository` to only Read pages info if pages is in the config Signed-off-by: Timo Sand --- ...ata_source_github_repository_pages_test.go | 6 ------ github/resource_github_repository.go | 3 ++- .../resource_github_repository_pages_test.go | 20 ------------------- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/github/data_source_github_repository_pages_test.go b/github/data_source_github_repository_pages_test.go index ab27e5f3d2..b11a53eb80 100644 --- a/github/data_source_github_repository_pages_test.go +++ b/github/data_source_github_repository_pages_test.go @@ -23,12 +23,6 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { name = "%s" visibility = "%s" auto_init = true - - lifecycle { - ignore_changes = [ - pages, - ] - } } resource "github_repository_pages" "test" { diff --git a/github/resource_github_repository.go b/github/resource_github_repository.go index 5abfbdbb84..49f9f83490 100644 --- a/github/resource_github_repository.go +++ b/github/resource_github_repository.go @@ -860,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_test.go b/github/resource_github_repository_pages_test.go index eacf400b24..99e1078afd 100644 --- a/github/resource_github_repository_pages_test.go +++ b/github/resource_github_repository_pages_test.go @@ -24,11 +24,6 @@ func TestAccGithubRepositoryPages(t *testing.T) { visibility = "%s" auto_init = true - lifecycle { - ignore_changes = [ - pages, - ] - } } resource "github_repository_pages" "test" { @@ -69,11 +64,6 @@ func TestAccGithubRepositoryPages(t *testing.T) { visibility = "%s" auto_init = true - lifecycle { - ignore_changes = [ - pages, - ] - } } resource "github_repository_pages" "test" { @@ -113,11 +103,6 @@ source { visibility = "%s" auto_init = true - lifecycle { - ignore_changes = [ - pages, - ] - } } resource "github_repository_pages" "test" { @@ -158,11 +143,6 @@ source { visibility = "%s" auto_init = true - lifecycle { - ignore_changes = [ - pages, - ] - } } resource "github_repository_pages" "test" { From 90228fa30844197be379aa00a96f803ad393f627 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sun, 8 Feb 2026 16:31:51 +0200 Subject: [PATCH 05/21] Fix faulty test Signed-off-by: Timo Sand --- github/resource_github_repository_test.go | 40 +++++++---------------- 1 file changed, 12 insertions(+), 28 deletions(-) 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"), + ), }, }, }) From 5d9dde73f31de527c26f1a44458b921879d9d9db Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sat, 14 Feb 2026 00:14:30 +0200 Subject: [PATCH 06/21] Update Import test Signed-off-by: Timo Sand --- github/data_source_github_repository_pages_test.go | 4 ++-- github/resource_github_repository_pages.go | 8 +------- github/resource_github_repository_pages_test.go | 11 ++++++----- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/github/data_source_github_repository_pages_test.go b/github/data_source_github_repository_pages_test.go index b11a53eb80..d6b637e9f5 100644 --- a/github/data_source_github_repository_pages_test.go +++ b/github/data_source_github_repository_pages_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) func TestAccGithubRepositoryPagesDataSource(t *testing.T) { diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index c2c54d9e20..df74477fab 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -265,7 +265,7 @@ func resourceGithubRepositoryPagesDelete(ctx context.Context, d *schema.Resource func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { owner, repoName, err := parseID2(d.Id()) if err != nil { - return nil, fmt.Errorf("invalid ID specified: supplied ID must be written as /. Original error: %w", err) + return nil, fmt.Errorf("invalid ID specified: supplied ID must be written as :. Original error: %w", err) } if err := d.Set("owner", owner); err != nil { return nil, err @@ -274,12 +274,6 @@ func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.Resource return nil, err } - id, err := buildID(owner, repoName) - if err != nil { - return nil, err - } - d.SetId(id) - return []*schema.ResourceData{d}, nil } diff --git a/github/resource_github_repository_pages_test.go b/github/resource_github_repository_pages_test.go index 99e1078afd..eb60765832 100644 --- a/github/resource_github_repository_pages_test.go +++ b/github/resource_github_repository_pages_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) func TestAccGithubRepositoryPages(t *testing.T) { @@ -167,9 +167,10 @@ source { ), }, { - ResourceName: "github_repository_pages.test", - ImportState: true, - ImportStateVerify: true, + ResourceName: "github_repository_pages.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"build_status"}, }, }, }) From 84a6d55e9915c0446b9a9e9c45381ea3526866cc Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Mon, 16 Feb 2026 23:57:02 +0200 Subject: [PATCH 07/21] Add `repository_id` diff logic and unify naming Signed-off-by: Timo Sand --- github/data_source_github_repository_pages.go | 10 +++-- ...ata_source_github_repository_pages_test.go | 4 +- github/resource_github_repository_pages.go | 45 ++++++++++++++----- .../resource_github_repository_pages_test.go | 28 +++++++++--- 4 files changed, 64 insertions(+), 23 deletions(-) diff --git a/github/data_source_github_repository_pages.go b/github/data_source_github_repository_pages.go index f313ed8da4..0131d5a29e 100644 --- a/github/data_source_github_repository_pages.go +++ b/github/data_source_github_repository_pages.go @@ -3,6 +3,7 @@ package github import ( "context" "net/http" + "strconv" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -14,7 +15,7 @@ func dataSourceGithubRepositoryPages() *schema.Resource { ReadContext: dataSourceGithubRepositoryPagesRead, Schema: map[string]*schema.Schema{ - "repository_name": { + "repository": { Type: schema.TypeString, Required: true, Description: "The repository name to get GitHub Pages information for.", @@ -82,7 +83,7 @@ func dataSourceGithubRepositoryPagesRead(ctx context.Context, d *schema.Resource client := meta.v3client owner := d.Get("owner").(string) - repoName := d.Get("repository_name").(string) + repoName := d.Get("repository").(string) pages, resp, err := client.Repositories.GetPagesInfo(ctx, owner, repoName) if err != nil { @@ -92,11 +93,12 @@ func dataSourceGithubRepositoryPagesRead(ctx context.Context, d *schema.Resource return diag.Errorf("error reading repository pages: %s", err.Error()) } - id, err := buildID(owner, repoName) + repo, _, err := client.Repositories.Get(ctx, owner, repoName) if err != nil { return diag.FromErr(err) } - d.SetId(id) + + d.SetId(strconv.Itoa(int(repo.GetID()))) if err := d.Set("build_type", pages.GetBuildType()); err != nil { return diag.FromErr(err) diff --git a/github/data_source_github_repository_pages_test.go b/github/data_source_github_repository_pages_test.go index d6b637e9f5..c7d379d9b6 100644 --- a/github/data_source_github_repository_pages_test.go +++ b/github/data_source_github_repository_pages_test.go @@ -27,7 +27,7 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { resource "github_repository_pages" "test" { owner = "%s" - repository_name = github_repository.test.name + repository = github_repository.test.name build_type = "legacy" source { branch = "main" @@ -37,7 +37,7 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { data "github_repository_pages" "test" { owner = "%s" - repository_name = github_repository.test.name + repository = github_repository.test.name depends_on = [github_repository_pages.test] } diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index df74477fab..dff5253ac3 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -4,9 +4,11 @@ import ( "context" "fmt" "net/http" + "strconv" "github.com/google/go-github/v82/github" "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" ) @@ -23,12 +25,17 @@ func resourceGithubRepositoryPages() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "repository_name": { + "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.", + }, "owner": { Type: schema.TypeString, Required: true, @@ -89,7 +96,7 @@ func resourceGithubRepositoryPages() *schema.Resource { Description: "The API URL of the GitHub Pages resource.", }, }, - CustomizeDiff: resourceGithubRepositoryPagesDiff, + CustomizeDiff: customdiff.All(resourceGithubRepositoryPagesDiff, diffRepository), } } @@ -98,7 +105,7 @@ func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.Resource client := meta.v3client owner := d.Get("owner").(string) - repoName := d.Get("repository_name").(string) + repoName := d.Get("repository").(string) pages := expandPagesForCreate(d) pages, _, err := client.Repositories.EnablePages(ctx, owner, repoName, pages) @@ -106,11 +113,16 @@ func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.Resource return diag.FromErr(err) } - id, err := buildID(owner, repoName) + repo, _, err := client.Repositories.Get(ctx, owner, repoName) if err != nil { return diag.FromErr(err) } - d.SetId(id) + + 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) @@ -150,7 +162,7 @@ func resourceGithubRepositoryPagesRead(ctx context.Context, d *schema.ResourceDa client := meta.v3client owner := d.Get("owner").(string) - repoName := d.Get("repository_name").(string) + repoName := d.Get("repository").(string) pages, resp, err := client.Repositories.GetPagesInfo(ctx, owner, repoName) if err != nil { @@ -205,7 +217,7 @@ func resourceGithubRepositoryPagesUpdate(ctx context.Context, d *schema.Resource client := meta.v3client owner := d.Get("owner").(string) - repoName := d.Get("repository_name").(string) + repoName := d.Get("repository").(string) update := &github.PagesUpdate{} @@ -252,7 +264,7 @@ func resourceGithubRepositoryPagesDelete(ctx context.Context, d *schema.Resource client := meta.v3client owner := d.Get("owner").(string) - repoName := d.Get("repository_name").(string) + repoName := d.Get("repository").(string) _, err := client.Repositories.DisablePages(ctx, owner, repoName) if err != nil { @@ -262,7 +274,7 @@ func resourceGithubRepositoryPagesDelete(ctx context.Context, d *schema.Resource return nil } -func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { +func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.ResourceData, m any) ([]*schema.ResourceData, error) { owner, repoName, err := parseID2(d.Id()) if err != nil { return nil, fmt.Errorf("invalid ID specified: supplied ID must be written as :. Original error: %w", err) @@ -270,10 +282,23 @@ func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.Resource if err := d.Set("owner", owner); err != nil { return nil, err } - if err := d.Set("repository_name", repoName); err != nil { + if err := d.Set("repository", repoName); err != nil { + return nil, err + } + + meta := m.(*Owner) + client := meta.v3client + + repo, _, err := client.Repositories.Get(ctx, owner, repoName) + if err != nil { + return nil, err + } + if err = d.Set("repository_id", int(repo.GetID())); err != nil { return nil, err } + d.SetId(strconv.Itoa(int(repo.GetID()))) + return []*schema.ResourceData{d}, nil } diff --git a/github/resource_github_repository_pages_test.go b/github/resource_github_repository_pages_test.go index eb60765832..42af27db79 100644 --- a/github/resource_github_repository_pages_test.go +++ b/github/resource_github_repository_pages_test.go @@ -2,10 +2,12 @@ package github import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" ) func TestAccGithubRepositoryPages(t *testing.T) { @@ -28,7 +30,7 @@ func TestAccGithubRepositoryPages(t *testing.T) { resource "github_repository_pages" "test" { owner = "%s" - repository_name = github_repository.test.name + repository = github_repository.test.name build_type = "legacy" source { branch = "main" @@ -68,7 +70,7 @@ func TestAccGithubRepositoryPages(t *testing.T) { resource "github_repository_pages" "test" { owner = "%s" - repository_name = github_repository.test.name + repository = github_repository.test.name build_type = "workflow" } `, repoName, baseRepoVisibility, testAccConf.owner) @@ -107,7 +109,7 @@ source { resource "github_repository_pages" "test" { owner = "%s" - repository_name = github_repository.test.name + repository = github_repository.test.name build_type = "%s" %s } @@ -147,7 +149,7 @@ source { resource "github_repository_pages" "test" { owner = "%s" - repository_name = github_repository.test.name + repository = github_repository.test.name build_type = "legacy" source { branch = "main" @@ -167,9 +169,21 @@ source { ), }, { - ResourceName: "github_repository_pages.test", - ImportState: true, - ImportStateVerify: true, + ResourceName: "github_repository_pages.test", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: func(state *terraform.State) (string, error) { + repo := state.RootModule().Resources["github_repository.test"] + + if repo == nil { + return "", fmt.Errorf("github_repository.test not found in state") + } + repoID := repo.Primary.ID + if repoID == "" { + return "", fmt.Errorf("github_repository.test does not have an id in terraform state") + } + return fmt.Sprintf("%s:%s", strings.Split(repo.Primary.Attributes["full_name"], "/")[0], repoID), nil + }, ImportStateVerifyIgnore: []string{"build_status"}, }, }, From 70489f55dca9a5411d4985c579efd6607beec298 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Tue, 17 Feb 2026 21:38:20 +0200 Subject: [PATCH 08/21] Use `ConfigStateChecks` in DS tests Signed-off-by: Timo Sand --- github/data_source_github_repository_pages.go | 4 ++-- .../data_source_github_repository_pages_test.go | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/github/data_source_github_repository_pages.go b/github/data_source_github_repository_pages.go index 0131d5a29e..7c6f6884ef 100644 --- a/github/data_source_github_repository_pages.go +++ b/github/data_source_github_repository_pages.go @@ -69,7 +69,7 @@ func dataSourceGithubRepositoryPages() *schema.Resource { Computed: true, Description: "The GitHub Pages site's build status e.g. 'building' or 'built'.", }, - "url": { + "api_url": { Type: schema.TypeString, Computed: true, Description: "The API URL of the GitHub Pages resource.", @@ -115,7 +115,7 @@ func dataSourceGithubRepositoryPagesRead(ctx context.Context, d *schema.Resource if err := d.Set("status", pages.GetStatus()); err != nil { return diag.FromErr(err) } - if err := d.Set("url", pages.GetURL()); err != nil { + if err := d.Set("api_url", pages.GetURL()); err != nil { return diag.FromErr(err) } diff --git a/github/data_source_github_repository_pages_test.go b/github/data_source_github_repository_pages_test.go index c7d379d9b6..6ca66e0370 100644 --- a/github/data_source_github_repository_pages_test.go +++ b/github/data_source_github_repository_pages_test.go @@ -4,8 +4,11 @@ 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/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" ) func TestAccGithubRepositoryPagesDataSource(t *testing.T) { @@ -49,12 +52,12 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { Steps: []resource.TestStep{ { Config: config, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.github_repository_pages.test", "build_type", "legacy"), - resource.TestCheckResourceAttr("data.github_repository_pages.test", "source.0.branch", "main"), - resource.TestCheckResourceAttr("data.github_repository_pages.test", "source.0.path", "/"), - resource.TestCheckResourceAttrSet("data.github_repository_pages.test", "url"), - ), + 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("api_url"), "github_repository_pages.test", tfjsonpath.New("api_url"), compare.ValuesSame()), + }, }, }, }) From f9dee266b6e16006bbdcd3f6ce50945e99d4ece0 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Tue, 17 Feb 2026 21:59:58 +0200 Subject: [PATCH 09/21] Refactor to use `ConfigStateChecks` in Resource Signed-off-by: Timo Sand --- .../resource_github_repository_pages_test.go | 61 ++++++++----------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/github/resource_github_repository_pages_test.go b/github/resource_github_repository_pages_test.go index 42af27db79..4c12da0c5e 100644 --- a/github/resource_github_repository_pages_test.go +++ b/github/resource_github_repository_pages_test.go @@ -2,12 +2,14 @@ package github import ( "fmt" - "strings" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/terraform" + "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) { @@ -45,12 +47,12 @@ func TestAccGithubRepositoryPages(t *testing.T) { Steps: []resource.TestStep{ { Config: config, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "legacy"), - resource.TestCheckResourceAttr("github_repository_pages.test", "source.0.branch", "main"), - resource.TestCheckResourceAttr("github_repository_pages.test", "source.0.path", "/"), - resource.TestCheckResourceAttrSet("github_repository_pages.test", "api_url"), - ), + 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://.*"))), + }, }, }, }) @@ -81,9 +83,9 @@ func TestAccGithubRepositoryPages(t *testing.T) { Steps: []resource.TestStep{ { Config: config, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "workflow"), - ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("workflow")), + }, }, }, }) @@ -121,15 +123,15 @@ source { Steps: []resource.TestStep{ { Config: fmt.Sprintf(config, repoName, baseRepoVisibility, testAccConf.owner, "legacy", sourceConfig), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "legacy"), - ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("legacy")), + }, }, { Config: fmt.Sprintf(config, repoName, baseRepoVisibility, testAccConf.owner, "workflow", ""), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "workflow"), - ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("workflow")), + }, }, }, }) @@ -164,27 +166,14 @@ source { Steps: []resource.TestStep{ { Config: config, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("github_repository_pages.test", "build_type", "legacy"), - ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("legacy")), + }, }, { - ResourceName: "github_repository_pages.test", - ImportState: true, - ImportStateVerify: true, - ImportStateIdFunc: func(state *terraform.State) (string, error) { - repo := state.RootModule().Resources["github_repository.test"] - - if repo == nil { - return "", fmt.Errorf("github_repository.test not found in state") - } - repoID := repo.Primary.ID - if repoID == "" { - return "", fmt.Errorf("github_repository.test does not have an id in terraform state") - } - return fmt.Sprintf("%s:%s", strings.Split(repo.Primary.Attributes["full_name"], "/")[0], repoID), nil - }, - ImportStateVerifyIgnore: []string{"build_status"}, + ResourceName: "github_repository_pages.test", + ImportState: true, + ImportStateId: fmt.Sprintf("%s:%s", testAccConf.owner, repoName), }, }, }) From b1e5ff71de7afddfddc8980eb45f0d1454119345 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Tue, 17 Feb 2026 22:09:41 +0200 Subject: [PATCH 10/21] Comment out `owner` field for now Signed-off-by: Timo Sand --- github/resource_github_repository_pages.go | 27 ++++++++++--------- .../resource_github_repository_pages_test.go | 14 ++++------ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index dff5253ac3..80b1c9d1b1 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -36,12 +36,13 @@ func resourceGithubRepositoryPages() *schema.Resource { Computed: true, Description: "The ID of the repository to configure GitHub Pages for.", }, - "owner": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The owner 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, @@ -104,7 +105,7 @@ func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.Resource meta := m.(*Owner) client := meta.v3client - owner := d.Get("owner").(string) + owner := meta.name // TODO: Add owner support // d.Get("owner").(string) repoName := d.Get("repository").(string) pages := expandPagesForCreate(d) @@ -161,7 +162,7 @@ func resourceGithubRepositoryPagesRead(ctx context.Context, d *schema.ResourceDa meta := m.(*Owner) client := meta.v3client - owner := d.Get("owner").(string) + 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) @@ -216,7 +217,7 @@ func resourceGithubRepositoryPagesUpdate(ctx context.Context, d *schema.Resource meta := m.(*Owner) client := meta.v3client - owner := d.Get("owner").(string) + owner := meta.name // TODO: Add owner support // d.Get("owner").(string) repoName := d.Get("repository").(string) update := &github.PagesUpdate{} @@ -263,7 +264,7 @@ func resourceGithubRepositoryPagesDelete(ctx context.Context, d *schema.Resource meta := m.(*Owner) client := meta.v3client - owner := d.Get("owner").(string) + owner := meta.name // TODO: Add owner support // d.Get("owner").(string) repoName := d.Get("repository").(string) _, err := client.Repositories.DisablePages(ctx, owner, repoName) @@ -279,9 +280,9 @@ func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.Resource if err != nil { return nil, fmt.Errorf("invalid ID specified: supplied ID must be written as :. Original error: %w", err) } - if err := d.Set("owner", owner); err != nil { - return nil, err - } + // 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 } diff --git a/github/resource_github_repository_pages_test.go b/github/resource_github_repository_pages_test.go index 4c12da0c5e..25f015a013 100644 --- a/github/resource_github_repository_pages_test.go +++ b/github/resource_github_repository_pages_test.go @@ -31,7 +31,6 @@ func TestAccGithubRepositoryPages(t *testing.T) { } resource "github_repository_pages" "test" { - owner = "%s" repository = github_repository.test.name build_type = "legacy" source { @@ -39,7 +38,7 @@ func TestAccGithubRepositoryPages(t *testing.T) { path = "/" } } - `, repoName, baseRepoVisibility, testAccConf.owner) + `, repoName, baseRepoVisibility) resource.Test(t, resource.TestCase{ PreCheck: func() { skipUnauthenticated(t) }, @@ -71,11 +70,10 @@ func TestAccGithubRepositoryPages(t *testing.T) { } resource "github_repository_pages" "test" { - owner = "%s" repository = github_repository.test.name build_type = "workflow" } - `, repoName, baseRepoVisibility, testAccConf.owner) + `, repoName, baseRepoVisibility) resource.Test(t, resource.TestCase{ PreCheck: func() { skipUnauthenticated(t) }, @@ -110,7 +108,6 @@ source { } resource "github_repository_pages" "test" { - owner = "%s" repository = github_repository.test.name build_type = "%s" %s @@ -122,13 +119,13 @@ source { ProviderFactories: providerFactories, Steps: []resource.TestStep{ { - Config: fmt.Sprintf(config, repoName, baseRepoVisibility, testAccConf.owner, "legacy", sourceConfig), + 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, testAccConf.owner, "workflow", ""), + Config: fmt.Sprintf(config, repoName, baseRepoVisibility, "workflow", ""), ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownValue("github_repository_pages.test", tfjsonpath.New("build_type"), knownvalue.StringExact("workflow")), }, @@ -150,7 +147,6 @@ source { } resource "github_repository_pages" "test" { - owner = "%s" repository = github_repository.test.name build_type = "legacy" source { @@ -158,7 +154,7 @@ source { path = "/" } } - `, repoName, baseRepoVisibility, testAccConf.owner) + `, repoName, baseRepoVisibility) resource.Test(t, resource.TestCase{ PreCheck: func() { skipUnauthenticated(t) }, From 87b36b8c5721b3dbe0ccd5c8cb641d0e87ef3880 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Thu, 19 Feb 2026 11:49:43 +0200 Subject: [PATCH 11/21] Comment out `owner` field for DS as well Signed-off-by: Timo Sand --- github/data_source_github_repository_pages.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/github/data_source_github_repository_pages.go b/github/data_source_github_repository_pages.go index 7c6f6884ef..60649b440b 100644 --- a/github/data_source_github_repository_pages.go +++ b/github/data_source_github_repository_pages.go @@ -20,11 +20,12 @@ func dataSourceGithubRepositoryPages() *schema.Resource { Required: true, Description: "The repository name to get GitHub Pages information for.", }, - "owner": { - Type: schema.TypeString, - Required: true, - Description: "The owner of the repository.", - }, + // 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, @@ -82,7 +83,7 @@ func dataSourceGithubRepositoryPagesRead(ctx context.Context, d *schema.Resource meta := m.(*Owner) client := meta.v3client - owner := d.Get("owner").(string) + 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) From 3324146493126b82b89600bccb39ad74db36cb83 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Thu, 19 Feb 2026 12:00:13 +0200 Subject: [PATCH 12/21] Simplify import ID Signed-off-by: Timo Sand --- github/data_source_github_repository_pages_test.go | 4 +--- github/resource_github_repository_pages.go | 8 +++++--- github/resource_github_repository_pages_test.go | 2 +- website/docs/r/repository_pages.html.markdown | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/github/data_source_github_repository_pages_test.go b/github/data_source_github_repository_pages_test.go index 6ca66e0370..13d5635690 100644 --- a/github/data_source_github_repository_pages_test.go +++ b/github/data_source_github_repository_pages_test.go @@ -29,7 +29,6 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { } resource "github_repository_pages" "test" { - owner = "%s" repository = github_repository.test.name build_type = "legacy" source { @@ -39,12 +38,11 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { } data "github_repository_pages" "test" { - owner = "%s" repository = github_repository.test.name depends_on = [github_repository_pages.test] } - `, repoName, baseRepoVisibility, testAccConf.owner, testAccConf.owner) + `, repoName, baseRepoVisibility) resource.Test(t, resource.TestCase{ PreCheck: func() { skipUnauthenticated(t) }, diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index 80b1c9d1b1..6e26a9658b 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "github.com/google/go-github/v82/github" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -276,9 +277,9 @@ func resourceGithubRepositoryPagesDelete(ctx context.Context, d *schema.Resource } func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.ResourceData, m any) ([]*schema.ResourceData, error) { - owner, repoName, err := parseID2(d.Id()) - if err != nil { - return nil, fmt.Errorf("invalid ID specified: supplied ID must be written as :. Original error: %w", err) + repoName := d.Id() + if strings.Contains(repoName, " ") { + return nil, fmt.Errorf("invalid ID specified: supplied ID must be the slug of the repository name") } // if err := d.Set("owner", owner); err != nil { // TODO: Add owner support // return nil, err @@ -288,6 +289,7 @@ func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.Resource } meta := m.(*Owner) + owner := meta.name client := meta.v3client repo, _, err := client.Repositories.Get(ctx, owner, repoName) diff --git a/github/resource_github_repository_pages_test.go b/github/resource_github_repository_pages_test.go index 25f015a013..eed006d7ff 100644 --- a/github/resource_github_repository_pages_test.go +++ b/github/resource_github_repository_pages_test.go @@ -169,7 +169,7 @@ source { { ResourceName: "github_repository_pages.test", ImportState: true, - ImportStateId: fmt.Sprintf("%s:%s", testAccConf.owner, repoName), + ImportStateId: repoName, }, }, }) diff --git a/website/docs/r/repository_pages.html.markdown b/website/docs/r/repository_pages.html.markdown index 7f5ccc38d4..c2a051704e 100644 --- a/website/docs/r/repository_pages.html.markdown +++ b/website/docs/r/repository_pages.html.markdown @@ -128,8 +128,8 @@ In addition to the above arguments, the following attributes are exported: ## Import -GitHub repository pages can be imported using the `owner:repository` format: +GitHub repository pages can be imported using the `repository-slug`, e.g. ```sh -terraform import github_repository_pages.example my-org:my-repo +terraform import github_repository_pages.example my-repo ``` From f4878cfe255f78b3767845930bd384ee63e664ea Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Thu, 19 Feb 2026 22:23:58 +0200 Subject: [PATCH 13/21] Add support for `public` field in pages Signed-off-by: Timo Sand --- github/data_source_github_repository_pages.go | 13 ++- ...ata_source_github_repository_pages_test.go | 59 ++++++++++++- github/resource_github_repository_pages.go | 43 ++++++++-- .../resource_github_repository_pages_test.go | 84 +++++++++++++++++++ 4 files changed, 189 insertions(+), 10 deletions(-) diff --git a/github/data_source_github_repository_pages.go b/github/data_source_github_repository_pages.go index 60649b440b..05b29c2889 100644 --- a/github/data_source_github_repository_pages.go +++ b/github/data_source_github_repository_pages.go @@ -65,7 +65,7 @@ func dataSourceGithubRepositoryPages() *schema.Resource { Computed: true, Description: "The absolute URL (with scheme) to the rendered GitHub Pages site.", }, - "status": { + "build_status": { Type: schema.TypeString, Computed: true, Description: "The GitHub Pages site's build status e.g. 'building' or 'built'.", @@ -75,6 +75,11 @@ func dataSourceGithubRepositoryPages() *schema.Resource { Computed: true, Description: "The API URL of the GitHub Pages resource.", }, + "public": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether the GitHub Pages site is public.", + }, }, } } @@ -113,13 +118,15 @@ func dataSourceGithubRepositoryPagesRead(ctx context.Context, d *schema.Resource if err := d.Set("html_url", pages.GetHTMLURL()); err != nil { return diag.FromErr(err) } - if err := d.Set("status", pages.GetStatus()); err != nil { + 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) + } // Set source only for legacy build type if pages.GetBuildType() == "legacy" && pages.GetSource() != nil { source := []map[string]any{ diff --git a/github/data_source_github_repository_pages_test.go b/github/data_source_github_repository_pages_test.go index 13d5635690..4a3fc9edc3 100644 --- a/github/data_source_github_repository_pages_test.go +++ b/github/data_source_github_repository_pages_test.go @@ -7,6 +7,7 @@ import ( "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" ) @@ -45,7 +46,58 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { `, repoName, baseRepoVisibility) resource.Test(t, resource.TestCase{ - PreCheck: func() { skipUnauthenticated(t) }, + 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)), + }, + }, + }, + }) + }) + 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{ { @@ -54,7 +106,12 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { 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()), }, }, }, diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index 6e26a9658b..4ee9eb34dd 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -97,6 +97,12 @@ func resourceGithubRepositoryPages() *schema.Resource { 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 public.", + }, }, CustomizeDiff: customdiff.All(resourceGithubRepositoryPagesDiff, diffRepository), } @@ -109,8 +115,8 @@ func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.Resource owner := meta.name // TODO: Add owner support // d.Get("owner").(string) repoName := d.Get("repository").(string) - pages := expandPagesForCreate(d) - pages, _, err := client.Repositories.EnablePages(ctx, owner, repoName, pages) + pagesReq := expandPagesForCreate(d) + pages, _, err := client.Repositories.EnablePages(ctx, owner, repoName, pagesReq) if err != nil { return diag.FromErr(err) } @@ -145,11 +151,25 @@ func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.Resource return diag.FromErr(err) } - // Handle CNAME update after creation if specified - if cname, ok := d.GetOk("cname"); ok && cname.(string) != "" { - update := &github.PagesUpdate{ - CNAME: github.Ptr(cname.(string)), + // Determine if we need to update the page with CNAME or public flag + shouldUpdatePage := false + update := &github.PagesUpdate{} + cname, cnameExists := d.GetOk("cname") + if cnameExists && cname.(string) != "" { + shouldUpdatePage = true + update.CNAME = github.Ptr(cname.(string)) + } + public, publicExists := d.GetOkExists("public") // nolint:staticcheck // SA1019: There is no better alternative for checking if boolean value is set + if publicExists && public != nil { + shouldUpdatePage = true + update.Public = github.Ptr(public.(bool)) + } else { + if err := d.Set("public", pages.GetPublic()); err != nil { + return diag.FromErr(err) } + } + + if shouldUpdatePage { _, err = client.Repositories.UpdatePages(ctx, owner, repoName, update) if err != nil { return diag.FromErr(err) @@ -194,6 +214,10 @@ func resourceGithubRepositoryPagesRead(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } + if err := d.Set("public", pages.GetPublic()); err != nil { + return diag.FromErr(err) + } + // Set source only for legacy build type if pages.GetBuildType() == "legacy" && pages.GetSource() != nil { source := []map[string]any{ @@ -230,6 +254,13 @@ func resourceGithubRepositoryPagesUpdate(ctx context.Context, d *schema.Resource } } + if d.HasChange("public") { + public, ok := d.Get("public").(bool) + if ok { + update.Public = github.Ptr(public) + } + } + if d.HasChange("build_type") { buildType := d.Get("build_type").(string) update.BuildType = github.Ptr(buildType) diff --git a/github/resource_github_repository_pages_test.go b/github/resource_github_repository_pages_test.go index eed006d7ff..8ffc18daef 100644 --- a/github/resource_github_repository_pages_test.go +++ b/github/resource_github_repository_pages_test.go @@ -2,9 +2,11 @@ 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" @@ -134,6 +136,88 @@ source { }) }) + 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("imports_pages_configuration", func(t *testing.T) { randomID := acctest.RandString(5) repoName := fmt.Sprintf("%spages-%s", testResourcePrefix, randomID) From 7401ddf139eeb8099baf45b512741074016a5c35 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Thu, 19 Feb 2026 22:37:38 +0200 Subject: [PATCH 14/21] Update docs Signed-off-by: Timo Sand --- website/docs/d/repository_pages.html.markdown | 9 ++++----- website/docs/r/repository_pages.html.markdown | 17 ++++++++--------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/website/docs/d/repository_pages.html.markdown b/website/docs/d/repository_pages.html.markdown index 992c36cac2..8be4bad3e8 100644 --- a/website/docs/d/repository_pages.html.markdown +++ b/website/docs/d/repository_pages.html.markdown @@ -13,7 +13,6 @@ Use this data source to retrieve GitHub Pages configuration for a repository. ```hcl data "github_repository_pages" "example" { - owner = "my-org" repository = "my-repo" } ``` @@ -22,8 +21,6 @@ data "github_repository_pages" "example" { The following arguments are supported: -- `owner` - (Required) The owner of the repository. - - `repository` - (Required) The repository name to get GitHub Pages information for. ## Attribute Reference @@ -40,9 +37,11 @@ The following attributes are exported: - `source` - The source branch and directory for the rendered Pages site. See [Source](#source) below for details. -- `status` - The GitHub Pages site's build status (e.g., `building` or `built`). +- `build_status` - The GitHub Pages site's build status (e.g., `building` or `built`). + +- `api_url` - The API URL of the GitHub Pages resource. -- `url` - The API URL of the GitHub Pages resource. +- `public` - Whether the GitHub Pages site is public. ### Source diff --git a/website/docs/r/repository_pages.html.markdown b/website/docs/r/repository_pages.html.markdown index c2a051704e..33b948244f 100644 --- a/website/docs/r/repository_pages.html.markdown +++ b/website/docs/r/repository_pages.html.markdown @@ -31,8 +31,7 @@ resource "github_repository" "example" { } resource "github_repository_pages" "example" { - owner = "my-org" - repository_name = github_repository.example.name + repository = github_repository.example.name build_type = "legacy" source { @@ -58,8 +57,7 @@ resource "github_repository" "example" { } resource "github_repository_pages" "example" { - owner = "my-org" - repository_name = github_repository.example.name + repository = github_repository.example.name build_type = "workflow" } ``` @@ -80,8 +78,7 @@ resource "github_repository" "example" { } resource "github_repository_pages" "example" { - owner = "my-org" - repository_name = github_repository.example.name + repository = github_repository.example.name build_type = "legacy" cname = "example.com" @@ -96,9 +93,7 @@ resource "github_repository_pages" "example" { The following arguments are supported: -- `owner` - (Required) The owner of the repository to configure GitHub Pages for. - -- `repository_name` - (Required) The repository name to configure GitHub Pages for. +- `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`. @@ -106,6 +101,8 @@ The following arguments are supported: - `cname` - (Optional) The custom domain for the repository. +- `public` - (Optional) Whether the GitHub Pages site is public. + ### Source The `source` block supports the following: @@ -118,6 +115,8 @@ The `source` block supports the following: 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. From 2d92066c881d3a237baddd8ff475a32adaff9cd0 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Thu, 19 Feb 2026 22:45:48 +0200 Subject: [PATCH 15/21] Improve field descriptions Signed-off-by: Timo Sand --- github/data_source_github_repository_pages.go | 2 +- github/resource_github_repository_pages.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/github/data_source_github_repository_pages.go b/github/data_source_github_repository_pages.go index 05b29c2889..c0fa4d51c9 100644 --- a/github/data_source_github_repository_pages.go +++ b/github/data_source_github_repository_pages.go @@ -78,7 +78,7 @@ func dataSourceGithubRepositoryPages() *schema.Resource { "public": { Type: schema.TypeBool, Computed: true, - Description: "Whether the GitHub Pages site is public.", + 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.", }, }, } diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index 4ee9eb34dd..50c4ec2851 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -101,7 +101,7 @@ func resourceGithubRepositoryPages() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, - Description: "Whether the GitHub Pages site is public.", + 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.", }, }, CustomizeDiff: customdiff.All(resourceGithubRepositoryPagesDiff, diffRepository), From c7086681ea5e41fd4b8458f1771101ce3d96199f Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Thu, 19 Feb 2026 22:46:17 +0200 Subject: [PATCH 16/21] Only modify `CNAME` in state if it has a value upstream Signed-off-by: Timo Sand --- github/resource_github_repository_pages.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index 50c4ec2851..bef1edf7c7 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -198,9 +198,17 @@ func resourceGithubRepositoryPagesRead(ctx context.Context, d *schema.ResourceDa 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) + 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) } From 1f2f9eaf59ffb4fb04a3f04abb1a6236fc8ee841 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sat, 21 Feb 2026 08:22:27 +0200 Subject: [PATCH 17/21] Add `https_enforced` field Signed-off-by: Timo Sand --- github/data_source_github_repository_pages.go | 8 +++++ ...ata_source_github_repository_pages_test.go | 2 ++ github/resource_github_repository_pages.go | 26 ++++++++++++++++ .../resource_github_repository_pages_test.go | 30 +++++++++++++++++++ website/docs/d/repository_pages.html.markdown | 2 ++ website/docs/r/repository_pages.html.markdown | 5 +++- 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/github/data_source_github_repository_pages.go b/github/data_source_github_repository_pages.go index c0fa4d51c9..e65927d83b 100644 --- a/github/data_source_github_repository_pages.go +++ b/github/data_source_github_repository_pages.go @@ -80,6 +80,11 @@ func dataSourceGithubRepositoryPages() *schema.Resource { 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.", + }, }, } } @@ -127,6 +132,9 @@ func dataSourceGithubRepositoryPagesRead(ctx context.Context, d *schema.Resource 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{ diff --git a/github/data_source_github_repository_pages_test.go b/github/data_source_github_repository_pages_test.go index 4a3fc9edc3..36a45b94f0 100644 --- a/github/data_source_github_repository_pages_test.go +++ b/github/data_source_github_repository_pages_test.go @@ -63,6 +63,7 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { 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()), }, }, }, @@ -112,6 +113,7 @@ func TestAccGithubRepositoryPagesDataSource(t *testing.T) { 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/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index bef1edf7c7..f1afe46e3b 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -103,6 +103,13 @@ func resourceGithubRepositoryPages() *schema.Resource { 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), } @@ -168,6 +175,15 @@ func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.Resource return diag.FromErr(err) } } + httpsEnforced, httpsEnforcedExists := d.GetOkExists("https_enforced") // nolint:staticcheck // SA1019: There is no better alternative for checking if boolean value is set + if httpsEnforcedExists && httpsEnforced != nil { + shouldUpdatePage = true + update.HTTPSEnforced = github.Ptr(httpsEnforced.(bool)) + } else { + if err := d.Set("https_enforced", pages.GetHTTPSEnforced()); err != nil { + return diag.FromErr(err) + } + } if shouldUpdatePage { _, err = client.Repositories.UpdatePages(ctx, owner, repoName, update) @@ -225,6 +241,9 @@ func resourceGithubRepositoryPagesRead(ctx context.Context, d *schema.ResourceDa 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 { @@ -269,6 +288,13 @@ func resourceGithubRepositoryPagesUpdate(ctx context.Context, d *schema.Resource } } + 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) diff --git a/github/resource_github_repository_pages_test.go b/github/resource_github_repository_pages_test.go index 8ffc18daef..622db11d61 100644 --- a/github/resource_github_repository_pages_test.go +++ b/github/resource_github_repository_pages_test.go @@ -218,6 +218,36 @@ source { }) }) + 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) diff --git a/website/docs/d/repository_pages.html.markdown b/website/docs/d/repository_pages.html.markdown index 8be4bad3e8..95c5cb73cc 100644 --- a/website/docs/d/repository_pages.html.markdown +++ b/website/docs/d/repository_pages.html.markdown @@ -43,6 +43,8 @@ The following attributes are exported: - `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: diff --git a/website/docs/r/repository_pages.html.markdown b/website/docs/r/repository_pages.html.markdown index 33b948244f..855a04c5f2 100644 --- a/website/docs/r/repository_pages.html.markdown +++ b/website/docs/r/repository_pages.html.markdown @@ -80,7 +80,8 @@ resource "github_repository" "example" { resource "github_repository_pages" "example" { repository = github_repository.example.name build_type = "legacy" - cname = "example.com" + cname = "example.com" + https_enforced = true source { branch = "main" @@ -103,6 +104,8 @@ The following arguments are supported: - `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: From 5938cb572ac47657ced6dd91d9a6acfef61a6a11 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Sat, 21 Feb 2026 08:35:27 +0200 Subject: [PATCH 18/21] Update `go-github` version Signed-off-by: Timo Sand --- github/resource_github_repository_pages.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index f1afe46e3b..32756b9e71 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "github.com/google/go-github/v82/github" + "github.com/google/go-github/v83/github" "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" From 890e23451686e12b01739d3158e09e42e523a6d4 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Mon, 23 Feb 2026 19:01:51 +0200 Subject: [PATCH 19/21] Replace `switch` with `if` Signed-off-by: Timo Sand --- github/resource_github_repository_pages.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index 32756b9e71..5f7dbddfdc 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -376,16 +376,13 @@ func resourceGithubRepositoryPagesDiff(ctx context.Context, d *schema.ResourceDi } buildType := d.Get("build_type").(string) - switch buildType { - case "workflow": - if _, ok := d.GetOk("source"); ok { - return fmt.Errorf("'source' is not supported for workflow build type") - } - return nil - case "legacy": - if _, ok := d.GetOk("source"); !ok { - return fmt.Errorf("'source' is required for legacy build type") - } + _, 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 From 4f2a3359806e8ae86d08d0b116fc1f5e60136b48 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Mon, 23 Feb 2026 19:14:17 +0200 Subject: [PATCH 20/21] Re-order `d.Set` calls Signed-off-by: Timo Sand --- github/resource_github_repository_pages.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index 5f7dbddfdc..4789fef732 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -346,12 +346,6 @@ func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.Resource if strings.Contains(repoName, " ") { return nil, fmt.Errorf("invalid ID specified: supplied ID must be the slug of the repository name") } - // 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 - } meta := m.(*Owner) owner := meta.name @@ -361,12 +355,18 @@ func resourceGithubRepositoryPagesImport(ctx context.Context, d *schema.Resource 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 } - d.SetId(strconv.Itoa(int(repo.GetID()))) - return []*schema.ResourceData{d}, nil } From 56b7d70656adfd9ec374d2ec03fef858fb279aba Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Mon, 23 Feb 2026 19:14:52 +0200 Subject: [PATCH 21/21] Restructure `UpdatePages` section Signed-off-by: Timo Sand --- github/resource_github_repository_pages.go | 59 +++++++++++++--------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/github/resource_github_repository_pages.go b/github/resource_github_repository_pages.go index 4789fef732..bdef828e1e 100644 --- a/github/resource_github_repository_pages.go +++ b/github/resource_github_repository_pages.go @@ -8,6 +8,7 @@ import ( "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" @@ -158,36 +159,46 @@ func resourceGithubRepositoryPagesCreate(ctx context.Context, d *schema.Resource return diag.FromErr(err) } - // Determine if we need to update the page with CNAME or public flag - shouldUpdatePage := false - update := &github.PagesUpdate{} - cname, cnameExists := d.GetOk("cname") - if cnameExists && cname.(string) != "" { - shouldUpdatePage = true - update.CNAME = github.Ptr(cname.(string)) - } - public, publicExists := d.GetOkExists("public") // nolint:staticcheck // SA1019: There is no better alternative for checking if boolean value is set - if publicExists && public != nil { - shouldUpdatePage = true - update.Public = github.Ptr(public.(bool)) - } else { - if err := d.Set("public", pages.GetPublic()); err != nil { + // 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) } } - httpsEnforced, httpsEnforcedExists := d.GetOkExists("https_enforced") // nolint:staticcheck // SA1019: There is no better alternative for checking if boolean value is set - if httpsEnforcedExists && httpsEnforced != nil { - shouldUpdatePage = true - update.HTTPSEnforced = github.Ptr(httpsEnforced.(bool)) - } else { - if err := d.Set("https_enforced", pages.GetHTTPSEnforced()); err != nil { + if !publicOKExists { + if err := d.Set("public", pages.GetPublic()); err != nil { return diag.FromErr(err) } } - - if shouldUpdatePage { - _, err = client.Repositories.UpdatePages(ctx, owner, repoName, update) - if err != nil { + if !httpsEnforcedExists { + if err := d.Set("https_enforced", pages.GetHTTPSEnforced()); err != nil { return diag.FromErr(err) } }