Protected tags allow control over who has permission to create or update git tags. Each rule allows you to match either an individual tag name, or use an appropriate pattern to control multiple tags at once.
**Table of Contents**
{{<toc>}}
## Setting up protected tags
To protect a tag, you need to follow these steps:
1. Go to the repository’s **Settings** > **Tags** page.
1. Type a pattern to match a name. You can use a single name, a [glob pattern](https://pkg.go.dev/github.com/gobwas/glob#Compile) or a regular expression.
1. Choose the allowed users and/or teams. If you leave these fields empty noone is allowed to create or modify this tag.
1. Select **Save** to save the configuration.
## Pattern protected tags
The pattern uses [glob](https://pkg.go.dev/github.com/gobwas/glob#Compile) or regular expressions to match a tag name. For regular expressions you need to enclose the pattern in slashes.
Examples:
| Type | Pattern Protected Tag | Possible Matching Tags |
settings.branch_filter_desc=Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or <code>*</code>, events for all branches are reported. See <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentation for syntax. Examples: <code>master</code>, <code>{master,release*}</code>.
settings.branch_filter_desc=Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or <code>*</code>, events for all branches are reported. See <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentation for syntax. Examples: <code>master</code>, <code>{master,release*}</code>.
settings.active=Active
settings.active_helper=Information about triggered events will be sent to this webhook URL.
settings.add_hook_success=The webhook has been added.
@ -1872,7 +1874,7 @@ settings.dismiss_stale_approvals_desc = When new commits that change the content
settings.require_signed_commits=Require Signed Commits
settings.require_signed_commits_desc=Reject pushes to this branch if they are unsigned or unverifiable.
settings.protect_protected_file_patterns=Protected file patterns (separated using semicolon '\;'):
settings.protect_protected_file_patterns_desc=Protected files that are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon ('\;'). See <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentation for pattern syntax. Examples: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_protected_file_patterns_desc=Protected files that are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon ('\;'). See <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentation for pattern syntax. Examples: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.tags.protection.allowed.teams=Allowed teams
settings.tags.protection.allowed.noone=No One
settings.tags.protection.create=Protect Tag
settings.tags.protection.none=There are no protected tags.
settings.tags.protection.pattern.description=You can use a single name or a glob pattern or regular expression to match multiple tags. Read more in the <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/protected-tags/">protected tags guide</a>.
settings.bot_token=Bot Token
settings.chat_id=Chat ID
settings.matrix.homeserver_url=Homeserver URL
@ -1904,6 +1916,7 @@ settings.archive.success = The repo was successfully archived.
settings.archive.error=An error occurred while trying to archive the repo. See the log for more details.
settings.archive.error_ismirror=You cannot archive a mirrored repo.
settings.archive.branchsettings_unavailable=Branch settings are not available if the repo is archived.
settings.archive.tagsettings_unavailable=Tag settings are not available if the repo is archived.
settings.unarchive.button=Un-Archive Repo
settings.unarchive.header=Un-Archive This Repo
settings.unarchive.text=Un-Archiving the repo will restore its ability to receive commits and pushes, as well as new issues and pull-requests.
@ -2018,6 +2031,7 @@ release.deletion_tag_desc = Will delete this tag from repository. Repository con
release.deletion_tag_success=The tag has been deleted.
release.tag_name_already_exist=A release with this tag name already exists.
release.tag_name_invalid=The tag name is not valid.
release.tag_name_protected=The tag name is protected.
release.tag_already_exist=This tag name already exists.
log.Warn("Forbidden: User %d is not allowed to push to protected branch: %s in %-v and is not allowed to merge pr #%d",opts.UserID,branchName,repo,pr.Index)
ctx.JSON(http.StatusForbidden,private.Response{
Err:fmt.Sprintf("Not allowed to push to protected branch %s",branchName),
})
return
}
// 6. If we're not allowed to push directly
if!canPush{
// Is this is a merge from the UI/API?
ifopts.PullRequestID==0{
// 6a. If we're not merging from the UI/API then there are two ways we got here:
//
// We are changing a protected file and we're not allowed to do that
// If we're an admin for the repository we can ignore status checks, reviews and override protected files
ifperm.IsAdmin(){
continue
}
// Now if we're not an admin - we can't overwrite protected files so fail now
ifchangedProtectedfiles{
log.Warn("Forbidden: Branch: %s in %-v is protected from changing file %s",branchName,repo,protectedFilePath)
log.Warn("Forbidden: User %d is not allowed push to protected branch %s in %-v and pr #%d is not ready to be merged: %s",opts.UserID,branchName,repo,pr.Index,err.Error())
ctx.JSON(http.StatusForbidden,private.Response{
Err:fmt.Sprintf("Not allowed to push to protected branch %s and pr #%d is not ready to be merged: %s",branchName,opts.PullRequestID,err.Error()),
})
return
}
log.Error("Unable to check if mergable: protected branch %s in %-v and pr #%d. Error: %v",opts.UserID,branchName,repo,pr.Index,err)
Err:fmt.Sprintf("Error calculating if allowed to merge: %v",err),
})
return
}
if!allowedMerge{
log.Warn("Forbidden: User %d is not allowed to push to protected branch: %s in %-v and is not allowed to merge pr #%d",opts.UserID,branchName,repo,pr.Index)
ctx.JSON(http.StatusForbidden,private.Response{
Err:fmt.Sprintf("Not allowed to push to protected branch %s",branchName),
Err:err.Error(),
})
return
}
// If we're an admin for the repository we can ignore status checks, reviews and override protected files
ifperm.IsAdmin(){
continue
}
// Now if we're not an admin - we can't overwrite protected files so fail now
ifchangedProtectedfiles{
log.Warn("Forbidden: Branch: %s in %-v is protected from changing file %s",branchName,repo,protectedFilePath)
if!isAllowed{
log.Warn("Forbidden: Tag %s in %-v is protected",tagName,repo)
ctx.JSON(http.StatusForbidden,private.Response{
Err:fmt.Sprintf("branch %s is protected from changing file %s",branchName,protectedFilePath),
log.Warn("Forbidden: User %d is not allowed push to protected branch %s in %-v and pr #%d is not ready to be merged: %s",opts.UserID,branchName,repo,pr.Index,err.Error())
ctx.JSON(http.StatusForbidden,private.Response{
Err:fmt.Sprintf("Not allowed to push to protected branch %s and pr #%d is not ready to be merged: %s",branchName,opts.PullRequestID,err.Error()),
})
return
}
log.Error("Unable to check if mergable: protected branch %s in %-v and pr #%d. Error: %v",opts.UserID,branchName,repo,pr.Index,err)