Skip to content

Commit e74f2c6

Browse files
Nitin Jainnitinjain999
authored andcommitted
feat: add github_copilot_organization_settings data source
Exposes read-only Copilot billing settings for the provider org via GET /orgs/{org}/copilot/billing: seat_management_setting, public_code_suggestions, and the full seat_breakdown block. The GitHub API has no write endpoint for these settings so a resource is not possible — a data source is the correct primitive here. Acceptance test verified against a real Copilot-enabled org.
1 parent 5c63b62 commit e74f2c6

5 files changed

Lines changed: 174 additions & 0 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
data "github_copilot_organization_settings" "example" {}
2+
3+
output "seat_management" {
4+
value = data.github_copilot_organization_settings.example.seat_management_setting
5+
}
6+
7+
output "total_seats" {
8+
value = data.github_copilot_organization_settings.example.seat_breakdown[0].total
9+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/http"
7+
8+
"github.com/google/go-github/v88/github"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
)
12+
13+
func dataSourceGithubCopilotOrganizationSettings() *schema.Resource {
14+
return &schema.Resource{
15+
ReadContext: dataSourceGithubCopilotOrganizationSettingsRead,
16+
17+
Schema: map[string]*schema.Schema{
18+
"seat_management_setting": {
19+
Type: schema.TypeString,
20+
Computed: true,
21+
Description: "How Copilot seats are assigned: assign_selected, all_members, or unconfigured.",
22+
},
23+
"public_code_suggestions": {
24+
Type: schema.TypeString,
25+
Computed: true,
26+
Description: "Whether Copilot can suggest code matching public repositories: allow or block.",
27+
},
28+
"seat_breakdown": {
29+
Type: schema.TypeList,
30+
Computed: true,
31+
Description: "Breakdown of Copilot seat usage.",
32+
Elem: &schema.Resource{
33+
Schema: map[string]*schema.Schema{
34+
"total": {
35+
Type: schema.TypeInt,
36+
Computed: true,
37+
Description: "Total number of Copilot seats.",
38+
},
39+
"added_this_cycle": {
40+
Type: schema.TypeInt,
41+
Computed: true,
42+
Description: "Seats added in the current billing cycle.",
43+
},
44+
"pending_invitation": {
45+
Type: schema.TypeInt,
46+
Computed: true,
47+
Description: "Seats pending invitation acceptance.",
48+
},
49+
"pending_cancellation": {
50+
Type: schema.TypeInt,
51+
Computed: true,
52+
Description: "Seats pending cancellation.",
53+
},
54+
"active_this_cycle": {
55+
Type: schema.TypeInt,
56+
Computed: true,
57+
Description: "Seats active in the current billing cycle.",
58+
},
59+
"inactive_this_cycle": {
60+
Type: schema.TypeInt,
61+
Computed: true,
62+
Description: "Seats inactive in the current billing cycle.",
63+
},
64+
},
65+
},
66+
},
67+
},
68+
}
69+
}
70+
71+
func dataSourceGithubCopilotOrganizationSettingsRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
72+
meta := m.(*Owner)
73+
client := meta.v3client
74+
org := meta.name
75+
76+
billing, _, err := client.Copilot.GetCopilotBilling(ctx, org)
77+
if err != nil {
78+
if ghErr, ok := errors.AsType[*github.ErrorResponse](err); ok && ghErr.Response.StatusCode == http.StatusNotFound {
79+
return diag.Errorf("Copilot is not enabled for organization %q", org)
80+
}
81+
return diag.FromErr(err)
82+
}
83+
84+
d.SetId(org)
85+
86+
if err := d.Set("seat_management_setting", billing.GetSeatManagementSetting()); err != nil {
87+
return diag.FromErr(err)
88+
}
89+
if err := d.Set("public_code_suggestions", billing.GetPublicCodeSuggestions()); err != nil {
90+
return diag.FromErr(err)
91+
}
92+
if breakdown := billing.GetSeatBreakdown(); breakdown != nil {
93+
if err := d.Set("seat_breakdown", []any{map[string]any{
94+
"total": breakdown.GetTotal(),
95+
"added_this_cycle": breakdown.GetAddedThisCycle(),
96+
"pending_invitation": breakdown.GetPendingInvitation(),
97+
"pending_cancellation": breakdown.GetPendingCancellation(),
98+
"active_this_cycle": breakdown.GetActiveThisCycle(),
99+
"inactive_this_cycle": breakdown.GetInactiveThisCycle(),
100+
}}); err != nil {
101+
return diag.FromErr(err)
102+
}
103+
}
104+
105+
return nil
106+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package github
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
7+
)
8+
9+
func TestAccDataSourceGithubCopilotOrganizationSettings(t *testing.T) {
10+
t.Run("reads Copilot organization settings", func(t *testing.T) {
11+
resource.Test(t, resource.TestCase{
12+
PreCheck: func() { skipUnlessHasOrgs(t) },
13+
ProviderFactories: providerFactories,
14+
Steps: []resource.TestStep{
15+
{
16+
Config: `data "github_copilot_organization_settings" "test" {}`,
17+
Check: resource.ComposeTestCheckFunc(
18+
resource.TestCheckResourceAttrSet("data.github_copilot_organization_settings.test", "seat_management_setting"),
19+
resource.TestCheckResourceAttrSet("data.github_copilot_organization_settings.test", "public_code_suggestions"),
20+
),
21+
},
22+
},
23+
})
24+
})
25+
}

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ func NewProvider() func() *schema.Provider {
246246
"github_codespaces_secrets": dataSourceGithubCodespacesSecrets(),
247247
"github_codespaces_user_public_key": dataSourceGithubCodespacesUserPublicKey(),
248248
"github_codespaces_user_secrets": dataSourceGithubCodespacesUserSecrets(),
249+
"github_copilot_organization_settings": dataSourceGithubCopilotOrganizationSettings(),
249250
"github_dependabot_organization_public_key": dataSourceGithubDependabotOrganizationPublicKey(),
250251
"github_dependabot_organization_secrets": dataSourceGithubDependabotOrganizationSecrets(),
251252
"github_dependabot_public_key": dataSourceGithubDependabotPublicKey(),
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
page_title: "{{.Name}} ({{.Type}}) - {{.RenderedProviderName}}"
3+
description: |-
4+
Reads the GitHub Copilot settings for an organization.
5+
---
6+
7+
# {{.Name}} ({{.Type}})
8+
9+
Use this data source to read the GitHub Copilot settings and seat breakdown for an organization.
10+
11+
~> **Note** This data source requires the organization to have a Copilot Business or Enterprise subscription.
12+
13+
## Example Usage
14+
15+
{{ tffile "examples/data-sources/github_copilot_organization_settings/data-source.tf" }}
16+
17+
## Argument Reference
18+
19+
No arguments are required. The data source reads settings for the provider's configured organization.
20+
21+
## Attributes Reference
22+
23+
The following attributes are exported:
24+
25+
- `seat_management_setting` - How Copilot seats are assigned: `assign_selected`, `all_members`, or `unconfigured`.
26+
- `public_code_suggestions` - Whether Copilot can suggest code matching public repositories: `allow` or `block`.
27+
- `seat_breakdown` - Breakdown of Copilot seat usage. Contains:
28+
- `total` - Total number of Copilot seats.
29+
- `added_this_cycle` - Seats added in the current billing cycle.
30+
- `pending_invitation` - Seats pending invitation acceptance.
31+
- `pending_cancellation` - Seats pending cancellation.
32+
- `active_this_cycle` - Seats active in the current billing cycle.
33+
- `inactive_this_cycle` - Seats inactive in the current billing cycle.

0 commit comments

Comments
 (0)