Protecting tags and releases¶
Release tags identify the code that was released. If a tag can be moved or deleted, users who download a release later might get different code than what was reviewed, tested or signed. This guide shows how to protect tags with rulesets and how to make releases immutable.
Note
GitHub retired tag protection rules and migrated them to tag rulesets, see the
GitHub changelog.
Otterdog does not support tag protection rules, use rulesets with target: "tag" instead.
Prevent tags from being moved or deleted¶
The following ruleset allows users with write access to create tags, but nobody can move or delete them afterwards:
orgs.newOrg('my-project', 'my-org') {
_repositories+:: [
orgs.newRepo('my-repo') {
rulesets: [
orgs.newRepoRuleset('tags') {
target: "tag",
include_refs+: [
"~ALL",
],
allows_creations: true,
allows_updates: false, // (1)!
required_pull_request: null, // (2)!
required_status_checks: null,
},
],
},
],
}
- Deletions and force pushes are blocked by default (
allows_deletionsandallows_force_pushesarefalse). - Pull requests do not apply to tags, and this ruleset does not require status checks.
For tag rulesets, include_refs accepts ~ALL or patterns of the form refs/tags/<pattern>, see
Ref Matcher.
Restrict who can create release tags¶
To ensure that releases are only created by the release process, restrict the creation of release tags to a release app or a team of release managers:
orgs.newOrg('my-project', 'my-org') {
_repositories+:: [
orgs.newRepo('my-repo') {
rulesets: [
orgs.newRepoRuleset('release-tags') {
target: "tag",
include_refs+: [
"refs/tags/v*",
],
allows_creations: false,
allows_updates: false,
bypass_actors+: [
"my-release-app",
"@my-org/release-managers",
],
required_pull_request: null,
required_status_checks: null,
},
],
},
],
}
Warning
With allows_creations: false and no bypass actor, nobody can create matching tags, which blocks every
release. If tags are created by a workflow, make sure that the identity used by the workflow is allowed to
create them, e.g. by using a token of a GitHub App that is listed as bypass actor.
Bypass actors can also move or delete the matching tags. Combine both rulesets if tags must stay unchanged for everybody: since all matching rulesets apply, the first ruleset still blocks updates and deletions for the bypass actors of the second one.
Restrict the names of tags¶
A pattern can enforce a naming scheme for tags, e.g. semantic versions:
orgs.newOrg('my-project', 'my-org') {
settings+: {
plan: "enterprise", // (1)!
},
_repositories+:: [
orgs.newRepo('my-repo') {
rulesets: [
orgs.newRepoRuleset('tag-names') {
target: "tag",
include_refs+: [
"~ALL",
],
allows_creations: true,
required_pull_request: null,
required_status_checks: null,
tag_name_pattern: orgs.newPattern() {
name: "Semantic version",
operator: "regex", // (2)!
pattern: "^v[0-9]+\\.[0-9]+\\.[0-9]+$",
},
},
],
},
],
}
- Read-only property that reflects the billing plan of the organization. Restricting the names of tags requires
GitHub Enterprise,
validatewarns otherwise. - The operator is one of
starts_with,ends_with,containsorregex, setnegate: trueto reject the tags that match the pattern instead, see Pattern Settings.
Only tags like v1.2.3 can be pushed. A tag name pattern is only supported by rulesets with target: "tag".
Make releases immutable¶
When a release is immutable, its tag cannot be moved or deleted while the release exists, and its assets cannot be modified or deleted. Immutability only applies to releases published after it has been enabled, see Immutable releases.
Immutable releases can be enforced for some or all repositories of an organization, or enabled per repository:
orgs.newOrg('my-project', 'my-org') {
settings+: {
immutable_releases_enforced_repositories: "selected",
immutable_releases_selected_repositories+: [
"my-library",
],
},
_repositories+:: [
orgs.newRepo('my-library'),
orgs.newRepo('my-tool') {
immutable_releases_enabled: true,
},
],
}
When the organization enforces immutable releases for a repository, the repository setting is ignored, see Organization settings take precedence. These settings are only managed if the default configuration of your organization defines them, see The default configuration also defines what is managed.
Recommendation
Protect release tags with a ruleset in every repository that publishes releases. Immutable releases additionally protect the release assets, but only for releases published after enabling them, while a tag ruleset protects all matching tags, with or without a release.
Deploy only from tags¶
To ensure that deployment credentials are only available to workflows that run for a release tag, restrict the environment used for publishing to tags, see Deployment environments.