feat: add branch_protection resource (#72)

added terraform tests for the resource

Reviewed-on: https://gitea.com/gitea/terraform-provider-gitea/pulls/72
Co-authored-by: Jörg Markert <joerg.markert@gmail.com>
Co-committed-by: Jörg Markert <joerg.markert@gmail.com>
This commit is contained in:
Jörg Markert 2024-09-11 17:32:48 +00:00 committed by techknowlogick
parent aa450c1855
commit a07bd291f5
15 changed files with 1133 additions and 15 deletions

View File

@ -22,7 +22,9 @@ jobs:
- name: Terraform Init - name: Terraform Init
id: init id: init
run: terraform init run: terraform init
working-directory: examples
- name: Terraform Validate - name: Terraform Validate
id: validate id: validate
run: terraform validate -no-color run: terraform validate -no-color
working-directory: examples

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
.vscode .vscode
.idea/ .idea/
dist/ dist/
tests/terraform.tfvars
tests/.terraform
tests/.terraform.lock.hcl

View File

@ -0,0 +1,68 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "gitea_repository_branch_protection Resource - terraform-provider-gitea"
subcategory: ""
description: |-
This resource allows you to create and manage branch protections for repositories.
---
# gitea_repository_branch_protection (Resource)
This resource allows you to create and manage branch protections for repositories.
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `name` (String) Repository name
- `rule_name` (String) Protected Branch Name Pattern
- `username` (String) User name or organization name
### Optional
- `approval_whitelist_teams` (List of String) Only reviews from allowlisted teams will count to the required
approvals. Without approval allowlist, reviews from anyone with
write access count to the required approvals.
- `approval_whitelist_users` (List of String) Only reviews from allowlisted users will count to the required
approvals. Without approval allowlist, reviews from anyone with
write access count to the required approvals.
- `block_merge_on_official_review_requests` (Boolean) Merging will not be possible when it has official
review requests, even if there are enough approvals.
- `block_merge_on_outdated_branch` (Boolean) Merging will not be possible when head branch is behind base branch.
- `block_merge_on_rejected_reviews` (Boolean) Merging will not be possible when changes are
requested by official reviewers, even if there are enough
approvals.
- `dismiss_stale_approvals` (Boolean) When new commits that change the content of the pull request
are pushed to the branch, old approvals will be dismissed.
- `enable_push` (Boolean) Anyone with write access will be allowed to push to this branch
(but not force push), add a whitelist users or teams to limit
access.
- `merge_whitelist_teams` (List of String) Allow only allowlisted teams to merge pull requests into this branch.
- `merge_whitelist_users` (List of String) Allow only allowlisted users to merge pull requests into this branch.
- `protected_file_patterns` (String) Protected file patterns (separated using semicolon ';')
- `push_whitelist_deploy_keys` (Boolean) Allow deploy keys with write access to push. Requires enable_push to be set to true.
- `push_whitelist_teams` (List of String) Allowlisted teams for pushing. Requires enable_push to be set to true.
- `push_whitelist_users` (List of String) Allowlisted users for pushing. Requires enable_push to be set to true.
- `require_signed_commits` (Boolean) Reject pushes to this branch if they are unsigned or unverifiable.
- `required_approvals` (Number) Allow only to merge pull request with enough positive reviews.
- `status_check_patterns` (List of String) Enter patterns to specify which status checks must pass before
branches can be merged into a branch that matches this rule.
Each line specifies a pattern. Patterns cannot be empty.
- `unprotected_file_patterns` (String) Unprotected file patterns (separated using semicolon ';')
### Read-Only
- `created_at` (String) Webhook creation timestamp
- `enable_approval_whitelist` (Boolean) True if a approval whitelist is used.
- `enable_merge_whitelist` (Boolean) True if a merge whitelist is used.
- `enable_push_whitelist` (Boolean) True if a push whitelist is used.
- `enable_status_check` (Boolean) Require status checks to pass before merging. When enabled,
commits must first be pushed to another branch, then merged
or pushed directly to a branch that matches this rule after
status checks have passed. If no contexts are matched, the
last commit must be successful regardless of context
- `id` (String) The ID of this resource.
- `updated_at` (String) Webhook creation timestamp

View File

@ -2,7 +2,7 @@ terraform {
required_providers { required_providers {
gitea = { gitea = {
source = "go-gitea/gitea" source = "go-gitea/gitea"
version = "0.1.0" version = "0.3.0"
} }
} }
} }
@ -12,4 +12,4 @@ provider "gitea" {
username = "lerentis" username = "lerentis"
password = var.gitea_password password = var.gitea_password
#token = var.gitea_token #token = var.gitea_token
} }

View File

@ -0,0 +1,14 @@
resource "gitea_repository" "repo" {
username = var.username
name = var.name
auto_init = false
}
resource "gitea_repository_branch_protection" "main" {
username = gitea_repository.repo.username
name = gitea_repository.repo.name
rule_name = "main"
enable_push = true
status_check_patterns = var.branch_protection_patterns
}

View File

@ -75,18 +75,19 @@ func Provider() *schema.Provider {
"gitea_org": resourceGiteaOrg(), "gitea_org": resourceGiteaOrg(),
// "gitea_team": resourceGiteaTeam(), // "gitea_team": resourceGiteaTeam(),
// "gitea_repo": resourceGiteaRepo(), // "gitea_repo": resourceGiteaRepo(),
"gitea_user": resourceGiteaUser(), "gitea_user": resourceGiteaUser(),
"gitea_oauth2_app": resourceGiteaOauthApp(), "gitea_oauth2_app": resourceGiteaOauthApp(),
"gitea_repository": resourceGiteaRepository(), "gitea_repository": resourceGiteaRepository(),
"gitea_fork": resourceGiteaFork(), "gitea_fork": resourceGiteaFork(),
"gitea_public_key": resourceGiteaPublicKey(), "gitea_public_key": resourceGiteaPublicKey(),
"gitea_team": resourceGiteaTeam(), "gitea_team": resourceGiteaTeam(),
"gitea_team_membership": resourceGiteaTeamMembership(), "gitea_team_membership": resourceGiteaTeamMembership(),
"gitea_team_members": resourceGiteaTeamMembers(), "gitea_team_members": resourceGiteaTeamMembers(),
"gitea_git_hook": resourceGiteaGitHook(), "gitea_git_hook": resourceGiteaGitHook(),
"gitea_token": resourceGiteaToken(), "gitea_token": resourceGiteaToken(),
"gitea_repository_key": resourceGiteaRepositoryKey(), "gitea_repository_key": resourceGiteaRepositoryKey(),
"gitea_repository_webhook": resourceGiteaRepositoryWebhook(), "gitea_repository_webhook": resourceGiteaRepositoryWebhook(),
"gitea_repository_branch_protection": resourceGiteaRepositoryBranchProtection(),
}, },
ConfigureFunc: providerConfigure, ConfigureFunc: providerConfigure,

View File

@ -0,0 +1,495 @@
package gitea
import (
"log"
"code.gitea.io/sdk/gitea"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
repoBPUsername string = "username"
repoBPName string = "name"
repoBPRuleName string = "rule_name"
repoBPProtectedFilePatterns string = "protected_file_patterns"
repoBPUnprotectedFilePatterns string = "unprotected_file_patterns"
repoBPEnablePush string = "enable_push"
repoBPEnablePushWhitelist string = "enable_push_whitelist"
repoBPPushWhitelistUsers string = "push_whitelist_users"
repoBPPushWhitelistTeams string = "push_whitelist_teams"
repoBPPushWhitelistDeployKeys string = "push_whitelist_deploy_keys"
repoBPRequireSignedCommits string = "require_signed_commits"
repoBPRequiredApprovals string = "required_approvals"
repoBPEnableApprovalWhitelist string = "enable_approval_whitelist"
repoBPApprovalWhitelistUsers string = "approval_whitelist_users"
repoBPApprovalWhitelistTeams string = "approval_whitelist_teams"
repoBPDismissStaleApprovals string = "dismiss_stale_approvals"
// not implemented in go-gitea-sdk
// repoBPIgnoreStaleApprovals string = "ignore_stale_approvals"
repoBPEnableStatusCheck string = "enable_status_check"
repoBPStatusCheckPatterns string = "status_check_patterns"
repoBPEnableMergeWhitelist string = "enable_merge_whitelist"
repoBPMergeWhitelistUsers string = "merge_whitelist_users"
repoBPMergeWhitelistTeams string = "merge_whitelist_teams"
repoBPBlockMergeOnRejectedReviews string = "block_merge_on_rejected_reviews"
repoBPBlockMergeOnOfficialReviewRequests string = "block_merge_on_official_review_requests"
repoBPBlockMergeOnOutdatedBranch string = "block_merge_on_outdated_branch"
repoBPUpdatedAt string = "updated_at"
repoBPCreatedAt string = "created_at"
)
func resourceRepositoryBranchProtectionRead(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
user := d.Get(repoBPUsername).(string)
repo := d.Get(repoBPName).(string)
rule_name := d.Get(repoBPRuleName).(string)
bp, resp, err := client.GetBranchProtection(user, repo, rule_name)
if err != nil {
if resp.StatusCode == 404 {
d.SetId("")
return
} else {
return err
}
}
err = setRepositoryBranchProtectionData(bp, user, repo, d)
return err
}
func generateWhitelist(d *schema.ResourceData, listname string) (enabled bool, users []string, teams []string) {
u := d.Get(listname + "_users")
users = make([]string, 0)
if u != nil {
for _, element := range u.([]interface{}) {
users = append(users, element.(string))
}
}
t := d.Get(listname + "_teams")
teams = make([]string, 0)
if u != nil {
for _, element := range t.([]interface{}) {
teams = append(teams, element.(string))
}
}
if c := len(users) + len(teams); c > 0 {
enabled = true
}
if listname == "push_whitelist" && d.Get(repoBPPushWhitelistDeployKeys).(bool) {
enabled = true
}
log.Println("enabled?:", enabled, listname)
return enabled, users, teams
}
func resourceRepositoryBranchProtectionCreate(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
user := d.Get(repoBPUsername).(string)
repo := d.Get(repoBPName).(string)
enablePushWhitelist, pushWhitelistUsernames, pushWhitelistTeams := generateWhitelist(d, "push_whitelist")
enableMergeWhitelist, mergeWhitelistUsernames, mergeWhitelistTeams := generateWhitelist(d, "merge_whitelist")
enableApprovalsWhitelist, approvalsWhitelistUsernames, approvalsWhitelistTeams := generateWhitelist(d, "approval_whitelist")
statusCheckContexts := make([]string, 0)
for _, element := range d.Get(repoBPStatusCheckPatterns).([]interface{}) {
statusCheckContexts = append(statusCheckContexts, element.(string))
}
log.Println("create_ulist:", pushWhitelistUsernames)
enableStatusCheck := false
if len(statusCheckContexts) > 0 {
enableStatusCheck = true
}
bpOption := gitea.CreateBranchProtectionOption{
// BranchName is deprecated in gitea, but still required in go-gitea-sdk, therefore using RuleName
BranchName: d.Get(repoBPRuleName).(string),
RuleName: d.Get(repoBPRuleName).(string),
EnablePush: d.Get(repoBPEnablePush).(bool),
EnablePushWhitelist: enablePushWhitelist,
PushWhitelistUsernames: pushWhitelistUsernames,
PushWhitelistTeams: pushWhitelistTeams,
PushWhitelistDeployKeys: d.Get(repoBPPushWhitelistDeployKeys).(bool),
EnableMergeWhitelist: enableMergeWhitelist,
MergeWhitelistUsernames: mergeWhitelistUsernames,
MergeWhitelistTeams: mergeWhitelistTeams,
EnableStatusCheck: enableStatusCheck,
StatusCheckContexts: statusCheckContexts,
RequiredApprovals: int64(d.Get(repoBPRequiredApprovals).(int)),
EnableApprovalsWhitelist: enableApprovalsWhitelist,
ApprovalsWhitelistUsernames: approvalsWhitelistUsernames,
ApprovalsWhitelistTeams: approvalsWhitelistTeams,
BlockOnRejectedReviews: d.Get(repoBPBlockMergeOnRejectedReviews).(bool),
BlockOnOfficialReviewRequests: d.Get(repoBPBlockMergeOnOfficialReviewRequests).(bool),
BlockOnOutdatedBranch: d.Get(repoBPBlockMergeOnOutdatedBranch).(bool),
DismissStaleApprovals: d.Get(repoBPDismissStaleApprovals).(bool),
// IgnoreStaleApprovals: d.Get(repoBPIgnoreStaleApprovals).(bool),
RequireSignedCommits: d.Get(repoBPRequireSignedCommits).(bool),
ProtectedFilePatterns: d.Get(repoBPProtectedFilePatterns).(string),
UnprotectedFilePatterns: d.Get(repoBPUnprotectedFilePatterns).(string),
}
bp, _, err := client.CreateBranchProtection(user, repo, bpOption)
if err != nil {
return err
}
err = setRepositoryBranchProtectionData(bp, user, repo, d)
return err
}
func resourceRepositoryBranchProtectionUpdate(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
user := d.Get(repoBPUsername).(string)
repo := d.Get(repoBPName).(string)
rule_name := d.Id()
enablePushWhitelist, pushWhitelistUsernames, pushWhitelistTeams := generateWhitelist(d, "push_whitelist")
enableMergeWhitelist, mergeWhitelistUsernames, mergeWhitelistTeams := generateWhitelist(d, "merge_whitelist")
enableApprovalsWhitelist, approvalsWhitelistUsernames, approvalsWhitelistTeams := generateWhitelist(d, "approval_whitelist")
statusCheckContexts := make([]string, 0)
for _, element := range d.Get(repoBPStatusCheckPatterns).([]interface{}) {
statusCheckContexts = append(statusCheckContexts, element.(string))
}
enablePush := false
if enablePushWhitelist == true || d.Get(repoBPEnablePush).(bool) == true {
enablePush = true
}
pushWhitelistDeployKeys := d.Get(repoBPPushWhitelistDeployKeys).(bool)
enableStatusCheck := false
if len(statusCheckContexts) > 0 {
enableStatusCheck = true
}
requiredApprovals := int64(d.Get(repoBPRequiredApprovals).(int))
blockOnRejectedReviews := d.Get(repoBPBlockMergeOnRejectedReviews).(bool)
blockOnOfficialReviewRequests := d.Get(repoBPBlockMergeOnOfficialReviewRequests).(bool)
blockOnOutdatedBranch := d.Get(repoBPBlockMergeOnOutdatedBranch).(bool)
dismissStaleApprovals := d.Get(repoBPDismissStaleApprovals).(bool)
// ignoreStaleApprovals := d.Get(repoBPIgnoreStaleApprovals).(bool)
requireSignedCommits := d.Get(repoBPRequireSignedCommits).(bool)
protectedFilePatterns := d.Get(repoBPProtectedFilePatterns).(string)
unprotectedFilePatterns := d.Get(repoBPUnprotectedFilePatterns).(string)
bpOption := gitea.EditBranchProtectionOption{
EnablePush: &enablePush,
EnablePushWhitelist: &enablePushWhitelist,
PushWhitelistUsernames: pushWhitelistUsernames,
PushWhitelistTeams: pushWhitelistTeams,
PushWhitelistDeployKeys: &pushWhitelistDeployKeys,
EnableMergeWhitelist: &enableMergeWhitelist,
MergeWhitelistUsernames: mergeWhitelistUsernames,
MergeWhitelistTeams: mergeWhitelistTeams,
EnableStatusCheck: &enableStatusCheck,
StatusCheckContexts: statusCheckContexts,
RequiredApprovals: &requiredApprovals,
EnableApprovalsWhitelist: &enableApprovalsWhitelist,
ApprovalsWhitelistUsernames: approvalsWhitelistUsernames,
ApprovalsWhitelistTeams: approvalsWhitelistTeams,
BlockOnRejectedReviews: &blockOnRejectedReviews,
BlockOnOfficialReviewRequests: &blockOnOfficialReviewRequests,
BlockOnOutdatedBranch: &blockOnOutdatedBranch,
DismissStaleApprovals: &dismissStaleApprovals,
// IgnoreStaleApprovals: &ignoreStaleApprovals,
RequireSignedCommits: &requireSignedCommits,
ProtectedFilePatterns: &protectedFilePatterns,
UnprotectedFilePatterns: &unprotectedFilePatterns,
}
bp, _, err := client.EditBranchProtection(user, repo, rule_name, bpOption)
if err != nil {
return err
}
err = setRepositoryBranchProtectionData(bp, user, repo, d)
return err
}
func resourceRepositoryBranchProtectionDelete(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
user := d.Get(repoBPUsername).(string)
repo := d.Get(repoBPName).(string)
rule_name := d.Id()
_, err = client.DeleteBranchProtection(user, repo, rule_name)
if err != nil {
return err
}
return err
}
func setRepositoryBranchProtectionData(bp *gitea.BranchProtection, user string, repo string, d *schema.ResourceData) (err error) {
d.SetId(bp.RuleName)
d.Set(repoBPUsername, user)
d.Set(repoBPName, repo)
d.Set(repoBPProtectedFilePatterns, bp.ProtectedFilePatterns)
d.Set(repoBPUnprotectedFilePatterns, bp.UnprotectedFilePatterns)
d.Set(repoBPEnablePush, bp.EnablePush)
d.Set(repoBPEnablePushWhitelist, bp.EnablePushWhitelist)
d.Set(repoBPPushWhitelistUsers, bp.PushWhitelistUsernames)
d.Set(repoBPPushWhitelistTeams, bp.PushWhitelistTeams)
d.Set(repoBPPushWhitelistDeployKeys, bp.PushWhitelistDeployKeys)
d.Set(repoBPRequireSignedCommits, bp.RequireSignedCommits)
d.Set(repoBPRequiredApprovals, bp.RequiredApprovals)
d.Set(repoBPEnableApprovalWhitelist, bp.EnableApprovalsWhitelist)
d.Set(repoBPApprovalWhitelistUsers, bp.ApprovalsWhitelistUsernames)
d.Set(repoBPApprovalWhitelistTeams, bp.ApprovalsWhitelistTeams)
d.Set(repoBPDismissStaleApprovals, bp.DismissStaleApprovals)
// d.Set(repoBPIgnoreStaleApprovals, bp.IgnoreStaleApprovals)
d.Set(repoBPEnableStatusCheck, bp.EnableStatusCheck)
d.Set(repoBPStatusCheckPatterns, bp.StatusCheckContexts)
d.Set(repoBPEnableMergeWhitelist, bp.EnableMergeWhitelist)
d.Set(repoBPMergeWhitelistUsers, bp.MergeWhitelistUsernames)
d.Set(repoBPMergeWhitelistTeams, bp.MergeWhitelistTeams)
d.Set(repoBPBlockMergeOnRejectedReviews, bp.BlockOnRejectedReviews)
d.Set(repoBPBlockMergeOnOfficialReviewRequests, bp.BlockOnOfficialReviewRequests)
d.Set(repoBPBlockMergeOnOutdatedBranch, bp.BlockOnOutdatedBranch)
d.Set(repoBPUpdatedAt, bp.Updated)
d.Set(repoBPCreatedAt, bp.Created)
return err
}
func resourceGiteaRepositoryBranchProtection() *schema.Resource {
return &schema.Resource{
Read: resourceRepositoryBranchProtectionRead,
Create: resourceRepositoryBranchProtectionCreate,
Update: resourceRepositoryBranchProtectionUpdate,
Delete: resourceRepositoryBranchProtectionDelete,
// TODO: importer ?
Schema: map[string]*schema.Schema{
"username": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "User name or organization name",
},
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Repository name",
},
"rule_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Protected Branch Name Pattern",
},
"protected_file_patterns": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
Default: "",
Description: "Protected file patterns (separated using semicolon ';')",
},
"unprotected_file_patterns": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
Default: "",
Description: "Unprotected file patterns (separated using semicolon ';')",
},
"enable_push": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
Default: false,
Description: `Anyone with write access will be allowed to push to this branch
(but not force push), add a whitelist users or teams to limit
access.`,
},
"enable_push_whitelist": {
Type: schema.TypeBool,
Computed: true,
Description: "True if a push whitelist is used.",
},
"push_whitelist_users": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
RequiredWith: []string{"enable_push"},
Optional: true,
ForceNew: false,
Description: "Allowlisted users for pushing. Requires enable_push to be set to true.",
},
"push_whitelist_teams": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
RequiredWith: []string{"enable_push"},
Optional: true,
ForceNew: false,
Description: "Allowlisted teams for pushing. Requires enable_push to be set to true.",
},
"push_whitelist_deploy_keys": {
Type: schema.TypeBool,
RequiredWith: []string{"enable_push"},
Optional: true,
ForceNew: false,
Default: false,
Description: "Allow deploy keys with write access to push. Requires enable_push to be set to true.",
},
"require_signed_commits": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
Default: false,
Description: "Reject pushes to this branch if they are unsigned or unverifiable.",
},
"required_approvals": {
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
Default: 0,
Description: "Allow only to merge pull request with enough positive reviews.",
},
"enable_approval_whitelist": {
Type: schema.TypeBool,
Computed: true,
Description: "True if a approval whitelist is used.",
},
"approval_whitelist_users": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
ForceNew: false,
Description: `Only reviews from allowlisted users will count to the required
approvals. Without approval allowlist, reviews from anyone with
write access count to the required approvals.`,
},
"approval_whitelist_teams": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
ForceNew: false,
Description: `Only reviews from allowlisted teams will count to the required
approvals. Without approval allowlist, reviews from anyone with
write access count to the required approvals.`,
},
"dismiss_stale_approvals": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
Default: false,
Description: `When new commits that change the content of the pull request
are pushed to the branch, old approvals will be dismissed.`,
},
//
// not implemented in go-gitea-sdk
//
// "ignore_stale_approvals": {
// Type: schema.TypeBool,
// Optional true,
// ForceNew: false,
// Default: false,
// Description: `Do not count approvals that were made on older commits (stale
// reviews) towards how many approvals the PR has. Irrelevant if
// stale reviews are already dismissed.`,
// },
"enable_status_check": {
Type: schema.TypeBool,
Computed: true,
Description: `Require status checks to pass before merging. When enabled,
commits must first be pushed to another branch, then merged
or pushed directly to a branch that matches this rule after
status checks have passed. If no contexts are matched, the
last commit must be successful regardless of context`,
},
"status_check_patterns": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
ForceNew: false,
Description: `Enter patterns to specify which status checks must pass before
branches can be merged into a branch that matches this rule.
Each line specifies a pattern. Patterns cannot be empty.`,
},
"enable_merge_whitelist": {
Type: schema.TypeBool,
Computed: true,
Description: "True if a merge whitelist is used.",
},
"merge_whitelist_users": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
ForceNew: false,
Description: "Allow only allowlisted users to merge pull requests into this branch.",
},
"merge_whitelist_teams": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
ForceNew: false,
Description: "Allow only allowlisted teams to merge pull requests into this branch.",
},
"block_merge_on_rejected_reviews": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
Default: false,
Description: `Merging will not be possible when changes are
requested by official reviewers, even if there are enough
approvals.`,
},
"block_merge_on_official_review_requests": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
Default: false,
Description: `Merging will not be possible when it has official
review requests, even if there are enough approvals.`,
},
"block_merge_on_outdated_branch": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
Default: false,
Description: "Merging will not be possible when head branch is behind base branch.",
},
"updated_at": {
Type: schema.TypeString,
Computed: true,
Description: "Webhook creation timestamp",
},
"created_at": {
Type: schema.TypeString,
Computed: true,
Description: "Webhook creation timestamp",
},
},
Description: "This resource allows you to create and manage branch protections for repositories.",
}
}

View File

@ -0,0 +1,29 @@
resource "gitea_repository_branch_protection" "bp" {
username = var.repo_user_name
name = var.repo_name
rule_name = var.rule_name
protected_file_patterns = var.protected_file_patterns
unprotected_file_patterns = var.unprotected_file_patterns
enable_push = var.enable_push
push_whitelist_users = var.push_whitelist_users
push_whitelist_teams = var.push_whitelist_teams
push_whitelist_deploy_keys = var.push_whitelist_deploy_keys
require_signed_commits = var.require_signed_commits
required_approvals = var.required_approvals
approval_whitelist_users = var.approval_whitelist_users
approval_whitelist_teams = var.approval_whitelist_teams
dismiss_stale_approvals = var.dismiss_stale_approvals
status_check_patterns = var.status_check_patterns
merge_whitelist_users = var.merge_whitelist_users
merge_whitelist_teams = var.merge_whitelist_teams
block_merge_on_rejected_reviews = var.block_merge_on_rejected_reviews
block_merge_on_official_review_requests = var.block_merge_on_official_review_requests
block_merge_on_outdated_branch = var.block_merge_on_outdated_branch
}
# //
# // not implemented in go-gitea-sdk
# //
# // "ignore_stale_approvals": {
# // Description: `Do not count approvals that were made on older commits (stale reviews) towards how many approvals the PR has. Irrelevant if stale reviews are already dismissed.`,
# // },

View File

@ -0,0 +1,306 @@
# run "create_org" {
# assert {
# condition = gitea_org.test_org.name == var.org_name
# error_message = "${gitea_org.test_org.name} not eq ${var.org_name}"
# }
# }
#
# run "create_repo" {
# assert {
# condition = gitea_repository.org_repo.name == var.repo_name
# error_message = "${gitea_repository.org_repo.name} not eq ${var.repo_name}"
# }
# }
# run "create_user" {
# assert {
# condition = gitea_user.test_user.username == var.user_name
# error_message = "${gitea_user.test_user.username} not eq ${var.user_name}"
# }
# }
#
# run "create_user_repo" {
# assert {
# condition = gitea_repository.user_repo.name == var.repo_name
# error_message = "${gitea_repository.user_repo.name} not eq ${var.repo_name}"
# }
# }
run "setup" {
module {
source = "./setup"
}
}
run "apply_defaults" {
variables {
rule_name = "apply_defaults"
}
assert {
condition = gitea_repository_branch_protection.bp.name == var.repo_name
error_message = "${gitea_repository_branch_protection.bp.name} not eq ${var.repo_name}"
}
assert {
condition = gitea_repository_branch_protection.bp.username == var.user_name
error_message = "${gitea_repository_branch_protection.bp.username} not eq ${var.user_name}"
}
assert {
condition = gitea_repository_branch_protection.bp.rule_name == var.rule_name
error_message = "${gitea_repository_branch_protection.bp.rule_name} not eq ${var.rule_name}"
}
assert {
condition = gitea_repository_branch_protection.bp.protected_file_patterns == var.protected_file_patterns
error_message = "${gitea_repository_branch_protection.bp.protected_file_patterns} not eq ${var.protected_file_patterns}"
}
assert {
condition = gitea_repository_branch_protection.bp.unprotected_file_patterns == var.unprotected_file_patterns
error_message = "${gitea_repository_branch_protection.bp.unprotected_file_patterns} not eq ${var.unprotected_file_patterns}"
}
assert {
condition = gitea_repository_branch_protection.bp.enable_push == var.enable_push
error_message = "${gitea_repository_branch_protection.bp.enable_push} not eq ${var.enable_push}"
}
assert {
condition = gitea_repository_branch_protection.bp.push_whitelist_users == var.push_whitelist_users
error_message = "${join(",", gitea_repository_branch_protection.bp.push_whitelist_users)} not eq ${join(",", var.push_whitelist_users)}"
}
assert {
condition = gitea_repository_branch_protection.bp.push_whitelist_teams == var.push_whitelist_teams
error_message = "${join(",", gitea_repository_branch_protection.bp.push_whitelist_teams)} not eq ${join(",", var.push_whitelist_teams)}"
}
assert {
condition = gitea_repository_branch_protection.bp.push_whitelist_deploy_keys == var.push_whitelist_deploy_keys
error_message = "${gitea_repository_branch_protection.bp.push_whitelist_deploy_keys} not eq ${var.push_whitelist_deploy_keys}"
}
assert {
condition = gitea_repository_branch_protection.bp.require_signed_commits == var.require_signed_commits
error_message = "${gitea_repository_branch_protection.bp.require_signed_commits} not eq ${var.require_signed_commits}"
}
assert {
condition = gitea_repository_branch_protection.bp.required_approvals == var.required_approvals
error_message = "${gitea_repository_branch_protection.bp.required_approvals} not eq ${var.required_approvals}"
}
assert {
condition = gitea_repository_branch_protection.bp.approval_whitelist_users == var.approval_whitelist_users
error_message = "${join(",", gitea_repository_branch_protection.bp.approval_whitelist_users)} not eq ${join(",", var.approval_whitelist_users)}"
}
assert {
condition = gitea_repository_branch_protection.bp.approval_whitelist_teams == var.approval_whitelist_teams
error_message = "${join(",", gitea_repository_branch_protection.bp.approval_whitelist_teams)} not eq ${join(",", var.approval_whitelist_teams)}"
}
assert {
condition = gitea_repository_branch_protection.bp.dismiss_stale_approvals == var.dismiss_stale_approvals
error_message = "${gitea_repository_branch_protection.bp.dismiss_stale_approvals} not eq ${var.dismiss_stale_approvals}"
}
assert {
condition = gitea_repository_branch_protection.bp.status_check_patterns == var.status_check_patterns
error_message = "${join(",", gitea_repository_branch_protection.bp.status_check_patterns)} not eq ${join(",", var.status_check_patterns)}"
}
assert {
condition = gitea_repository_branch_protection.bp.merge_whitelist_users == var.merge_whitelist_users
error_message = "${join(",", gitea_repository_branch_protection.bp.merge_whitelist_users)} not eq ${join(",", var.merge_whitelist_users)}"
}
assert {
condition = gitea_repository_branch_protection.bp.merge_whitelist_teams == var.merge_whitelist_teams
error_message = "${join(",", gitea_repository_branch_protection.bp.merge_whitelist_teams)} not eq ${join(",", var.merge_whitelist_teams)}"
}
assert {
condition = gitea_repository_branch_protection.bp.block_merge_on_rejected_reviews == var.block_merge_on_rejected_reviews
error_message = "${gitea_repository_branch_protection.bp.block_merge_on_rejected_reviews} not eq ${var.block_merge_on_rejected_reviews}"
}
assert {
condition = gitea_repository_branch_protection.bp.block_merge_on_official_review_requests == var.block_merge_on_official_review_requests
error_message = "${gitea_repository_branch_protection.bp.block_merge_on_official_review_requests} not eq ${var.block_merge_on_official_review_requests}"
}
assert {
condition = gitea_repository_branch_protection.bp.block_merge_on_outdated_branch == var.block_merge_on_outdated_branch
error_message = "${gitea_repository_branch_protection.bp.block_merge_on_outdated_branch} not eq ${var.block_merge_on_outdated_branch}"
}
}
run "simple_params" {
variables {
rule_name = "simple_params"
protected_file_patterns = "foobar.yaml"
unprotected_file_patterns = "foobar.yaml"
require_signed_commits = true
required_approvals = 10
dismiss_stale_approvals = true
block_merge_on_rejected_reviews = true
block_merge_on_official_review_requests = true
block_merge_on_outdated_branch = true
}
assert {
condition = gitea_repository_branch_protection.bp.protected_file_patterns == var.protected_file_patterns
error_message = "${gitea_repository_branch_protection.bp.protected_file_patterns} not eq ${var.protected_file_patterns}"
}
assert {
condition = gitea_repository_branch_protection.bp.unprotected_file_patterns == var.unprotected_file_patterns
error_message = "${gitea_repository_branch_protection.bp.unprotected_file_patterns} not eq ${var.unprotected_file_patterns}"
}
assert {
condition = gitea_repository_branch_protection.bp.require_signed_commits == var.require_signed_commits
error_message = "${gitea_repository_branch_protection.bp.require_signed_commits} not eq ${var.require_signed_commits}"
}
assert {
condition = gitea_repository_branch_protection.bp.required_approvals == var.required_approvals
error_message = "${gitea_repository_branch_protection.bp.required_approvals} not eq ${var.required_approvals}"
}
assert {
condition = gitea_repository_branch_protection.bp.dismiss_stale_approvals == var.dismiss_stale_approvals
error_message = "${gitea_repository_branch_protection.bp.dismiss_stale_approvals} not eq ${var.dismiss_stale_approvals}"
}
assert {
condition = gitea_repository_branch_protection.bp.block_merge_on_rejected_reviews == var.block_merge_on_rejected_reviews
error_message = "${gitea_repository_branch_protection.bp.block_merge_on_rejected_reviews} not eq ${var.block_merge_on_rejected_reviews}"
}
assert {
condition = gitea_repository_branch_protection.bp.block_merge_on_official_review_requests == var.block_merge_on_official_review_requests
error_message = "${gitea_repository_branch_protection.bp.block_merge_on_official_review_requests} not eq ${var.block_merge_on_official_review_requests}"
}
assert {
condition = gitea_repository_branch_protection.bp.block_merge_on_outdated_branch == var.block_merge_on_outdated_branch
error_message = "${gitea_repository_branch_protection.bp.block_merge_on_outdated_branch} not eq ${var.block_merge_on_outdated_branch}"
}
}
run "enable_push" {
variables {
rule_name = "enable_push"
enable_push = true
}
assert {
condition = gitea_repository_branch_protection.bp.enable_push == var.enable_push
error_message = "${gitea_repository_branch_protection.bp.enable_push} not eq ${var.enable_push}"
}
assert {
condition = gitea_repository_branch_protection.bp.enable_push_whitelist == false
error_message = "${gitea_repository_branch_protection.bp.enable_push_whitelist} not eq `false`"
}
}
run "implicit_push_whitelist_with_users" {
variables {
enable_push = true
rule_name = "implicit_and_push_whitelist_with_users"
push_whitelist_users = ["test_user"]
}
assert {
condition = gitea_repository_branch_protection.bp.enable_push_whitelist == true
error_message = "${gitea_repository_branch_protection.bp.enable_push_whitelist} not eq `true`"
}
assert {
condition = gitea_repository_branch_protection.bp.push_whitelist_users == tolist(var.push_whitelist_users)
error_message = "${join(",", gitea_repository_branch_protection.bp.push_whitelist_users)} not eq ${join(",", var.push_whitelist_users)}"
}
}
run "implicit_push_whitelist_with_teams" {
variables {
rule_name = "implicit_and_push_whitelist_with_teams"
repo_user_name = "test-org"
enable_push = true
push_whitelist_teams = ["Owners"]
}
assert {
condition = gitea_repository_branch_protection.bp.enable_push_whitelist == true
error_message = "${gitea_repository_branch_protection.bp.enable_push_whitelist} not eq `true`"
}
assert {
condition = gitea_repository_branch_protection.bp.push_whitelist_teams == tolist(var.push_whitelist_teams)
error_message = "${join(",", gitea_repository_branch_protection.bp.push_whitelist_teams)} not eq ${join(",", var.push_whitelist_teams)}"
}
}
run "implicit_push_whitelist_with_deploy_keys" {
variables {
rule_name = "implicit_push_whitelist_with_deploy_keys"
enable_push = true
push_whitelist_deploy_keys = true
}
assert {
condition = gitea_repository_branch_protection.bp.enable_push_whitelist == true
error_message = "${gitea_repository_branch_protection.bp.enable_push_whitelist} not eq `true`"
}
assert {
condition = gitea_repository_branch_protection.bp.push_whitelist_deploy_keys == var.push_whitelist_deploy_keys
error_message = "${gitea_repository_branch_protection.bp.push_whitelist_deploy_keys} not eq ${var.push_whitelist_deploy_keys}"
}
}
run "implicit_enable_approve_whitelist_with_users" {
variables {
rule_name = "implicit_enable_approve_whitelist_with_users"
approval_whitelist_users = ["test_user"]
}
assert {
condition = gitea_repository_branch_protection.bp.enable_approval_whitelist == true
error_message = "${gitea_repository_branch_protection.bp.enable_approval_whitelist} not eq `true`"
}
assert {
condition = gitea_repository_branch_protection.bp.approval_whitelist_users == tolist(var.approval_whitelist_users)
error_message = "${join(",", gitea_repository_branch_protection.bp.approval_whitelist_users)} not eq ${join(",", var.approval_whitelist_users)}"
}
}
run "implicit_enable_approve_whitelist_with_teams" {
variables {
rule_name = "implicit_enable_approve_whitelist_with_teams"
repo_user_name = "test-org"
approval_whitelist_teams = ["Owners"]
}
assert {
condition = gitea_repository_branch_protection.bp.enable_approval_whitelist == true
error_message = "${gitea_repository_branch_protection.bp.enable_approval_whitelist} not eq `true`"
}
assert {
condition = gitea_repository_branch_protection.bp.approval_whitelist_teams == tolist(var.approval_whitelist_teams)
error_message = "${join(",", gitea_repository_branch_protection.bp.approval_whitelist_teams)} not eq ${join(",", var.approval_whitelist_teams)}"
}
}
run "implicit_enable_merge_whitelist_with_users" {
variables {
rule_name = "implicit_enable_merge_whitelist_with_users"
merge_whitelist_users = ["test_user"]
}
assert {
condition = gitea_repository_branch_protection.bp.enable_merge_whitelist == true
error_message = "${gitea_repository_branch_protection.bp.enable_merge_whitelist} not eq `true`"
}
assert {
condition = gitea_repository_branch_protection.bp.merge_whitelist_users == tolist(var.merge_whitelist_users)
error_message = "${join(",", gitea_repository_branch_protection.bp.merge_whitelist_users)} not eq ${join(",", var.merge_whitelist_users)}"
}
}
run "implicit_enable_merge_whitelist_with_teams" {
variables {
rule_name = "implicit_enable_merge_whitelist_with_teams"
repo_user_name = "test-org"
merge_whitelist_teams = ["Owners"]
}
assert {
condition = gitea_repository_branch_protection.bp.enable_merge_whitelist == true
error_message = "${gitea_repository_branch_protection.bp.enable_merge_whitelist} not eq `true`"
}
assert {
condition = gitea_repository_branch_protection.bp.merge_whitelist_teams == tolist(var.merge_whitelist_teams)
error_message = "${join(",", gitea_repository_branch_protection.bp.merge_whitelist_teams)} not eq ${join(",", var.merge_whitelist_teams)}"
}
}
run "implicit_enable_status_check" {
variables {
rule_name = "implicit_enable_status_check"
status_check_patterns = ["terraform-tests", "tf fmt"]
}
assert {
condition = gitea_repository_branch_protection.bp.enable_status_check == true
error_message = "${gitea_repository_branch_protection.bp.enable_status_check} not eq `true`"
}
assert {
condition = gitea_repository_branch_protection.bp.status_check_patterns == tolist(var.status_check_patterns)
error_message = "${join(",", gitea_repository_branch_protection.bp.status_check_patterns)} not eq ${join(",", var.status_check_patterns)}"
}
}

14
tests/readme.md Normal file
View File

@ -0,0 +1,14 @@
# How to run tests
```bash
docker run -p 3000:3000 gitea/gitea:latest-rootless
```
1. Setup gitea under http://localhost:3000
2. Username: gitea_admin
3. Password: gitea_admin
4. Email: admin@gitea.local
```bash
terraform test
```

22
tests/setup/main.tf Normal file
View File

@ -0,0 +1,22 @@
resource "gitea_org" "test_org" {
name = var.org_name
}
resource "gitea_repository" "org_repo" {
username = gitea_org.test_org.name
name = var.repo_name
}
resource "gitea_user" "test_user" {
password = "Geheim1!"
email = "terraform@local.host"
username = var.user_name
login_name = var.user_name
must_change_password = false
}
resource "gitea_repository" "user_repo" {
username = gitea_user.test_user.username
name = var.repo_name
}

15
tests/setup/variables.tf Normal file
View File

@ -0,0 +1,15 @@
variable "repo_name" {
type = string
default = "test-repo"
}
variable "org_name" {
type = string
default = "test-org"
}
variable "user_name" {
type = string
default = "test_user"
}

16
tests/setup/versions.tf Normal file
View File

@ -0,0 +1,16 @@
terraform {
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.3.0"
}
}
required_version = ">= 0.13"
}
provider "gitea" {
base_url = "http://localhost:3000"
username = "gitea_admin"
password = "gitea_admin"
insecure = true
}

117
tests/variables.tf Normal file
View File

@ -0,0 +1,117 @@
variable "repo_name" {
type = string
default = "test-repo"
}
variable "org_name" {
type = string
default = "test-org"
}
variable "repo_user_name" {
type = string
default = "test_user"
}
variable "user_name" {
type = string
default = "test_user"
}
variable "rule_name" {
type = string
default = "branch-protection"
}
variable "protected_file_patterns" {
type = string
default = ""
}
variable "unprotected_file_patterns" {
type = string
default = ""
}
variable "enable_push" {
type = bool
default = true
}
variable "push_whitelist_users" {
type = list(string)
default = []
}
variable "push_whitelist_teams" {
type = list(string)
default = []
}
variable "push_whitelist_deploy_keys" {
type = bool
default = false
}
variable "require_signed_commits" {
type = bool
default = false
}
variable "required_approvals" {
type = number
default = 0
}
variable "approval_whitelist_users" {
type = list(string)
default = []
}
variable "approval_whitelist_teams" {
type = list(string)
default = []
}
variable "dismiss_stale_approvals" {
type = bool
default = false
}
variable "status_check_patterns" {
type = list(string)
default = []
}
variable "merge_whitelist_users" {
type = list(string)
default = []
}
variable "merge_whitelist_teams" {
type = list(string)
default = []
}
variable "block_merge_on_rejected_reviews" {
type = bool
default = false
}
variable "block_merge_on_official_review_requests" {
type = bool
default = false
}
variable "block_merge_on_outdated_branch" {
type = bool
default = false
}
# // not implemented in go-gitea-sdk
//
# //
# // "ignore_stale_approvals": {
# // Description: `Do not count approvals that were made on older commits (stale reviews) towards how many approvals the PR has. Irrelevant if stale reviews are already dismissed.`,
# // },

16
tests/versions.tf Normal file
View File

@ -0,0 +1,16 @@
terraform {
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.3.0"
}
}
required_version = ">= 0.13"
}
provider "gitea" {
base_url = "http://localhost:3000"
username = "gitea_admin"
password = "gitea_admin"
insecure = true
}