diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62024400aa..b4cce0d692 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: '1.18' + go-version: '1.19' - run: make tools - run: make lint - run: make website-lint diff --git a/.gitignore b/.gitignore index 1a2574f744..400f9ea23a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,8 +27,8 @@ website/node_modules *.tfvars .vscode/ testdata/ - website/vendor +terraform-provider-github # Test exclusions !command/test-fixtures/**/*.tfstate diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bfe1f10fc..e78ed98ffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# NOTE: CHANGELOG.md is deprecated + +After the release of v4.24.0, please see the [GitHub release notes](https://github.com/integrations/terraform-provider-github/releases) for the provider in order to view the most up-to-date changes. + # 4.24.0 (Apr 28, 2022) ENHANCEMENTS: diff --git a/GNUmakefile b/GNUmakefile index 0b719103f5..324e32dd6c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -7,7 +7,7 @@ default: build tools: go install github.com/client9/misspell/cmd/misspell - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.45.2 + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0 build: fmtcheck go build ./... diff --git a/github/apps.go b/github/apps.go index 9837c297b0..8d65c15a9a 100644 --- a/github/apps.go +++ b/github/apps.go @@ -6,7 +6,7 @@ import ( "encoding/pem" "errors" "fmt" - "io/ioutil" + "io" "net/http" "time" @@ -51,7 +51,7 @@ func getInstallationAccessToken(baseURL string, jwt string, installationID strin } defer func() { _ = res.Body.Close() }() - resBytes, err := ioutil.ReadAll(res.Body) + resBytes, err := io.ReadAll(res.Body) if err != nil { return "", err } diff --git a/github/apps_test.go b/github/apps_test.go index 0dbeadc79a..6e51aab479 100644 --- a/github/apps_test.go +++ b/github/apps_test.go @@ -5,7 +5,7 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "io/ioutil" + "os" "strings" "testing" "time" @@ -24,7 +24,7 @@ const ( var ( testEpochTime = time.Unix(0, 0) - testGitHubAppPrivateKeyPemData, _ = ioutil.ReadFile(testGitHubAppPrivateKeyFile) + testGitHubAppPrivateKeyPemData, _ = os.ReadFile(testGitHubAppPrivateKeyFile) ) func TestGenerateAppJWT(t *testing.T) { @@ -108,7 +108,7 @@ func TestGenerateAppJWT(t *testing.T) { }) t.Run("produces a verifiable jwt", func(t *testing.T) { - publicKeyData, err := ioutil.ReadFile(testGitHubAppPublicKeyFile) + publicKeyData, err := os.ReadFile(testGitHubAppPublicKeyFile) if err != nil { t.Logf("Failed to read public key file '%s': %s", testGitHubAppPublicKeyFile, err) t.FailNow() diff --git a/github/config.go b/github/config.go index fff2c2d84c..8c4b05bc1b 100644 --- a/github/config.go +++ b/github/config.go @@ -99,7 +99,6 @@ func (c *Config) NewRESTClient(client *http.Client) (*github.Client, error) { } func (c *Config) ConfigureOwner(owner *Owner) (*Owner, error) { - ctx := context.Background() owner.name = c.Owner if owner.name == "" { diff --git a/github/data_source_github_external_groups.go b/github/data_source_github_external_groups.go new file mode 100644 index 0000000000..c174ab4fae --- /dev/null +++ b/github/data_source_github_external_groups.go @@ -0,0 +1,74 @@ +package github + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/google/go-github/v45/github" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func dataSourceGithubExternalGroups() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGithubExternalGroupsRead, + Schema: map[string]*schema.Schema{ + "external_groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeInt, + Computed: true, + }, + "group_name": { + Type: schema.TypeString, + Computed: true, + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceGithubExternalGroupsRead(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + client := meta.(*Owner).v3client + orgName := meta.(*Owner).name + + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + opts := &github.ListExternalGroupsOptions{} + + externalGroups, _, err := client.Teams.ListExternalGroups(ctx, orgName, opts) + if err != nil { + return err + } + + // convert to JSON in order to martial to format we can return + jsonGroups, err := json.Marshal(externalGroups.Groups) + if err != nil { + return err + } + + groupsState := make([]map[string]interface{}, 0) + err = json.Unmarshal(jsonGroups, &groupsState) + if err != nil { + return err + } + + if err := d.Set("external_groups", groupsState); err != nil { + return err + } + + d.SetId(fmt.Sprintf("/orgs/%v/external-groups", orgName)) + return nil +} diff --git a/github/data_source_github_team.go b/github/data_source_github_team.go index 0a3fd6dc66..0e9280660e 100644 --- a/github/data_source_github_team.go +++ b/github/data_source_github_team.go @@ -6,7 +6,10 @@ import ( "github.com/google/go-github/v45/github" + "github.com/shurcooL/githubv4" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) func dataSourceGithubTeam() *schema.Resource { @@ -48,6 +51,12 @@ func dataSourceGithubTeam() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "membership_type": { + Type: schema.TypeString, + Default: "all", + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"all", "immediate"}, false), + }, }, } } @@ -71,22 +80,60 @@ func dataSourceGithubTeamRead(d *schema.ResourceData, meta interface{}) error { } var members []string - for { - member, resp, err := client.Teams.ListTeamMembersByID(ctx, orgId, team.GetID(), &options) - if err != nil { - return err + if d.Get("membership_type").(string) == "all" { + for { + member, resp, err := client.Teams.ListTeamMembersByID(ctx, orgId, team.GetID(), &options) + if err != nil { + return err + } + + for _, v := range member { + members = append(members, v.GetLogin()) + } + + if resp.NextPage == 0 { + break + } + options.Page = resp.NextPage } - - for _, v := range member { - members = append(members, v.GetLogin()) + } else { + type member struct { + Login string } - - if resp.NextPage == 0 { - break + var query struct { + Organization struct { + Team struct { + Members struct { + Nodes []member + PageInfo struct { + EndCursor githubv4.String + HasNextPage bool + } + } `graphql:"members(first:100,after:$memberCursor,membership:IMMEDIATE)"` + } `graphql:"team(slug:$slug)"` + } `graphql:"organization(login:$owner)"` + } + variables := map[string]interface{}{ + "owner": githubv4.String(meta.(*Owner).name), + "slug": githubv4.String(slug), + "memberCursor": (*githubv4.String)(nil), + } + client := meta.(*Owner).v4client + for { + nameErr := client.Query(ctx, &query, variables) + if nameErr != nil { + return nameErr + } + for _, v := range query.Organization.Team.Members.Nodes { + members = append(members, v.Login) + } + if query.Organization.Team.Members.PageInfo.HasNextPage { + variables["memberCursor"] = query.Organization.Team.Members.PageInfo.EndCursor + } else { + break + } } - options.Page = resp.NextPage } - var repositories []string for { repository, resp, err := client.Teams.ListTeamReposByID(ctx, orgId, team.GetID(), &options.ListOptions) diff --git a/github/data_source_github_team_test.go b/github/data_source_github_team_test.go index 2a9d6b980a..a03efd18e3 100644 --- a/github/data_source_github_team_test.go +++ b/github/data_source_github_team_test.go @@ -56,6 +56,50 @@ func TestAccGithubTeamDataSource(t *testing.T) { }) + t.Run("queries an existing team without error with immediate membership", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_team" "test" { + name = "tf-acc-test-%s" + } + + data "github_team" "test" { + slug = github_team.test.slug + membership_type = "immediate" + } + `, randomID) + + check := resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_team.test", "name"), + resource.TestCheckResourceAttr("data.github_team.test", "name", fmt.Sprintf("tf-acc-test-%s", randomID)), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + t.Run("errors when querying a non-existing team", func(t *testing.T) { config := ` diff --git a/github/provider.go b/github/provider.go index 6f5c103b5d..119248d38d 100644 --- a/github/provider.go +++ b/github/provider.go @@ -137,6 +137,7 @@ func Provider() terraform.ResourceProvider { "github_branch": dataSourceGithubBranch(), "github_collaborators": dataSourceGithubCollaborators(), "github_dependabot_public_key": dataSourceGithubDependabotPublicKey(), + "github_external_groups": dataSourceGithubExternalGroups(), "github_ip_ranges": dataSourceGithubIpRanges(), "github_membership": dataSourceGithubMembership(), "github_organization": dataSourceGithubOrganization(), diff --git a/github/resource_github_team_members.go b/github/resource_github_team_members.go index f31757ffae..a047730df6 100644 --- a/github/resource_github_team_members.go +++ b/github/resource_github_team_members.go @@ -148,8 +148,6 @@ func resourceGithubTeamMembersUpdate(d *schema.ResourceData, meta interface{}) e if err != nil { return err } - - continue } if create { @@ -167,12 +165,6 @@ func resourceGithubTeamMembersUpdate(d *schema.ResourceData, meta interface{}) e if err != nil { return err } - continue - } - - // no change - if reflect.DeepEqual(change.Old, change.New) { - continue } } diff --git a/github/transport.go b/github/transport.go index 773d0db560..3a869906ba 100644 --- a/github/transport.go +++ b/github/transport.go @@ -3,7 +3,6 @@ package github import ( "bytes" "io" - "io/ioutil" "log" "net/http" "sync" @@ -176,7 +175,7 @@ func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) { if err = b.Close(); err != nil { return nil, b, err } - return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil + return io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), nil } func isWriteMethod(method string) bool { diff --git a/github/transport_test.go b/github/transport_test.go index 667c27e6fd..f80b634f8b 100644 --- a/github/transport_test.go +++ b/github/transport_test.go @@ -3,7 +3,7 @@ package github import ( "context" "fmt" - "io/ioutil" + "io" "log" "net/http" "net/http/httptest" @@ -51,7 +51,7 @@ func githubApiMock(responseSequence []*mockResponse) *httptest.Server { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Server", "GitHub.com") - bodyBytes, err := ioutil.ReadAll(r.Body) + bodyBytes, err := io.ReadAll(r.Body) if err != nil { log.Printf("[DEBUG] Error: %s", err) } diff --git a/github/util_v4_repository_test.go b/github/util_v4_repository_test.go index 377d5fe391..093de289c0 100644 --- a/github/util_v4_repository_test.go +++ b/github/util_v4_repository_test.go @@ -2,14 +2,14 @@ package github import ( "bytes" - "github.com/shurcooL/githubv4" "io" - "io/ioutil" "net/http" "net/http/httptest" "strings" "testing" "text/template" + + "github.com/shurcooL/githubv4" ) // Heavily based on https://github.com/shurcooL/githubv4/blob/master/githubv4_test.go#L114-L144 @@ -202,7 +202,7 @@ func (l localRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) } func mustRead(r io.Reader) string { - b, err := ioutil.ReadAll(r) + b, err := io.ReadAll(r) if err != nil { panic(err) } diff --git a/website/docs/d/external_groups.html.markdown b/website/docs/d/external_groups.html.markdown new file mode 100644 index 0000000000..e76ee87815 --- /dev/null +++ b/website/docs/d/external_groups.html.markdown @@ -0,0 +1,40 @@ +--- +layout: "github" +page_title: "GitHub: github_external_group" +description: |- + Retrieve external groups belonging to an organization. +--- + +# github\_external\_group + +Use this data source to retrieve external groups belonging to an organization. + +## Example Usage + +```hcl +data "github_external_groups" "example_external_groups" {} + +locals { + local_groups = "${data.github_external_groups.example_external_groups}" +} + +output "groups" { + value = local.local_groups +} +``` + +## Argument Reference + +N/A. This resource will retrieve all the external groups belonging to an organization. + +## Attributes Reference + + * `external_groups` - an array of external groups belonging to the organization. Each group consists of the fields documented below. + +___ + + + * `group_id` - the ID of the group. + * `group_name` - the name of the group. + * `updated_at` - the date the group was last updated. + diff --git a/website/docs/d/team.html.markdown b/website/docs/d/team.html.markdown index a5f348de2c..a9b5e7bcfb 100644 --- a/website/docs/d/team.html.markdown +++ b/website/docs/d/team.html.markdown @@ -20,6 +20,7 @@ data "github_team" "example" { ## Argument Reference * `slug` - (Required) The team slug. + * `membership_type` - (Optional) Type of membershp to be requested to fill the list of members. Can be either "all" or "immediate". Default: "all" ## Attributes Reference @@ -29,6 +30,6 @@ data "github_team" "example" { * `description` - the team's description. * `privacy` - the team's privacy type. * `permission` - the team's permission level. - * `members` - List of team members - * `repositories` - List of team repositories - + * `members` - List of team members (list of GitHub usernames) + * `repositories` - List of team repositories (list of repo names) + diff --git a/website/github.erb b/website/github.erb index 41111559a3..a915ac000f 100644 --- a/website/github.erb +++ b/website/github.erb @@ -22,6 +22,9 @@
  • github_collaborators
  • +
  • + github_external_groups +
  • github_ip_ranges