-
Notifications
You must be signed in to change notification settings - Fork 763
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add github_app_installation_repository resource #690
Changes from all commits
72b466a
0e8232b
5608652
5c1bab6
0e3b3b8
ed5419b
b47638c
609980b
0534415
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# App Installation Example | ||
|
||
This example gives an application installation access to a | ||
specific repository in the same organization. | ||
|
||
To complete this demo, first [install an application in your | ||
github organization](https://docs.github.com/en/github/customizing-your-github-workflow/installing-an-app-in-your-organization). To use the full scope of this | ||
resource, make sure you install the application only on select repositories | ||
in the organization (instead of all repositories). | ||
|
||
This will allow you to use this resource to manage which repositories | ||
the app installation has access to. | ||
|
||
After you have installed the application, locate the installation id of the | ||
application by visiting `https://github.com/organizations/{ORG_NAME}/settings/installations` | ||
and configuring the app you'd like to install. | ||
The ID should be located in the URL on the configure page. | ||
|
||
This example will create a repository in the specified organization. | ||
It will also add the created repository to the app installation. | ||
|
||
Alternatively, you may use variables passed via command line: | ||
|
||
```console | ||
export GITHUB_ORG= | ||
export GITHUB_TOKEN= | ||
export INSTALLATION_ID= | ||
``` | ||
|
||
```console | ||
terraform apply \ | ||
-var "organization=${GITHUB_ORG}" \ | ||
-var "github_token=${GITHUB_TOKEN}" \ | ||
-var "installation_id=${INSTALLATION_ID}" \ | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
resource "github_repository" "app_installation_example" { | ||
name = "appinstallationexample" | ||
description = "A repository to install an application in." | ||
} | ||
|
||
resource "github_app_installation_repository" "test"{ | ||
repository = github_repository.app_installation_example.name | ||
installation_id = var.installation_id | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
provider "github" { | ||
organization = var.organization | ||
token = var.github_token | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
variable "installation_id" { | ||
description = "ID of an app installation in an organization" | ||
type = string | ||
} | ||
|
||
variable "organization" { | ||
description = "GitHub organization used to configure the provider" | ||
type = string | ||
} | ||
|
||
variable "github_token" { | ||
description = "GitHub access token used to configure the provider" | ||
type = string | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package github | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"strconv" | ||
|
||
"github.com/google/go-github/v32/github" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
) | ||
|
||
func resourceGithubAppInstallationRepository() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceGithubAppInstallationRepositoryCreate, | ||
Read: resourceGithubAppInstallationRepositoryRead, | ||
Delete: resourceGithubAppInstallationRepositoryDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"installation_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"repository": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"repo_id": { | ||
Type: schema.TypeInt, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceGithubAppInstallationRepositoryCreate(d *schema.ResourceData, meta interface{}) error { | ||
err := checkOrganization(meta) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
installationIDString := d.Get("installation_id").(string) | ||
installationID, err := strconv.ParseInt(installationIDString, 10, 64) | ||
if err != nil { | ||
return unconvertibleIdErr(installationIDString, err) | ||
} | ||
|
||
client := meta.(*Owner).v3client | ||
owner := meta.(*Owner).name | ||
ctx := context.Background() | ||
repoName := d.Get("repository").(string) | ||
repo, _, err := client.Repositories.Get(ctx, owner, repoName) | ||
if err != nil { | ||
return err | ||
} | ||
repoID := repo.GetID() | ||
|
||
log.Printf("[DEBUG] Creating app installation repository association: %s:%s", | ||
installationIDString, repoName) | ||
|
||
_, _, err = client.Apps.AddRepository(ctx, installationID, repoID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(buildTwoPartID(installationIDString, repoName)) | ||
return resourceGithubAppInstallationRepositoryRead(d, meta) | ||
} | ||
|
||
func resourceGithubAppInstallationRepositoryRead(d *schema.ResourceData, meta interface{}) error { | ||
err := checkOrganization(meta) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
client := meta.(*Owner).v3client | ||
installationIDString, repoName, err := parseTwoPartID(d.Id(), "installation_id", "repository") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
installationID, err := strconv.ParseInt(installationIDString, 10, 64) | ||
if err != nil { | ||
return unconvertibleIdErr(installationIDString, err) | ||
} | ||
|
||
ctx := context.WithValue(context.Background(), ctxId, d.Id()) | ||
opt := &github.ListOptions{PerPage: maxPerPage} | ||
|
||
for { | ||
repos, resp, err := client.Apps.ListUserRepos(ctx, installationID, opt) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("[DEBUG] Found %d repos, checking if any matches %s", len(repos), repoName) | ||
for _, r := range repos { | ||
if r.GetName() == repoName { | ||
d.Set("installation_id", installationIDString) | ||
d.Set("repository", repoName) | ||
d.Set("repo_id", r.GetID()) | ||
return nil | ||
} | ||
} | ||
|
||
if resp.NextPage == 0 { | ||
break | ||
} | ||
opt.Page = resp.NextPage | ||
} | ||
|
||
log.Printf("[WARN] Removing app installation repository association %s from state because it no longer exists in GitHub", | ||
d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
func resourceGithubAppInstallationRepositoryDelete(d *schema.ResourceData, meta interface{}) error { | ||
err := checkOrganization(meta) | ||
if err != nil { | ||
return err | ||
} | ||
installationIDString := d.Get("installation_id").(string) | ||
installationID, err := strconv.ParseInt(installationIDString, 10, 64) | ||
if err != nil { | ||
return unconvertibleIdErr(installationIDString, err) | ||
} | ||
|
||
client := meta.(*Owner).v3client | ||
ctx := context.Background() | ||
|
||
repoName := d.Get("repository").(string) | ||
repoID := d.Get("repo_id").(int) | ||
log.Printf("[DEBUG] Deleting app installation repository association: %s:%s", | ||
installationIDString, repoName) | ||
|
||
_, err = client.Apps.RemoveRepository(ctx, installationID, int64(repoID)) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
--- | ||
layout: "github" | ||
page_title: "GitHub: github_app_installation_repository" | ||
description: |- | ||
Manages the associations between app installations and repositories. | ||
--- | ||
|
||
# github_app_installation_repository | ||
|
||
This resource manages relationships between app installations and repositories | ||
in your GitHub organization. | ||
|
||
Creating this resource installs a particular app on a particular repository. | ||
|
||
The app installation and the repository must both belong to the same | ||
organization on GitHub. Note: you can review your organization's installations | ||
by the following the instructions at this | ||
[link](https://docs.github.com/en/github/setting-up-and-managing-organizations-and-teams/reviewing-your-organizations-installed-integrations). | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
# Create a repository. | ||
resource "github_repository" "some_repo" { | ||
name = "some-repo" | ||
} | ||
|
||
resource "github_app_installation_repository" "some_app_repo" { | ||
# The installation id of the app (in the organization). | ||
installation_id = "1234567" | ||
repository = "${github_repository.some_repo.name}" | ||
} | ||
Comment on lines
+23
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we get a test that exercises this configuration? Open to updates in HCL syntax as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤦🏾 I now see an earlier comment where I requested an example over an acceptance test. I'll give testing a shot and plan to move forward with this with just the example if the test does not pan out. |
||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `installation_id` - (Required) The GitHub app installation id. | ||
* `repository` - (Required) The repository to install the app on. | ||
|
||
## Import | ||
|
||
GitHub App Installation Repository can be imported | ||
using an ID made up of `installation_id:repository`, e.g. | ||
|
||
``` | ||
$ terraform import github_app_installation_repository.terraform_repo 1234567:terraform | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 would we benefit from an
Update
function here as well? Or is creating a new App installation usually non-disruptive?