Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions docker/lib/dependabot/docker/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,38 @@ def digest?
name.match?(FileParser::DIGEST)
end

sig { returns(T.nilable(T::Boolean)) }
sig { returns(T::Boolean) }
def looks_like_prerelease?
numeric_version&.match?(/[a-zA-Z]/)
return false unless comparable?

# Don't treat SHA-suffixed tags as prereleases (e.g., v3.10.0-169-gfe040d3)
return false if format == :sha_suffixed

# Check for common prerelease patterns in the tag name
# The version regex splits things like "1.0.0-alpha" into version="1.0.0" and suffix="-alpha"
# So we need to check the full name or the combination of version and suffix
prerelease_patterns = [
/alpha/i, # matches: alpha, ALPHA
/beta/i, # matches: beta, BETA
/rc\d*/i, # matches: rc, RC, RC1, rc2, etc.
/dev/i, # matches: dev, DEV
/preview/i, # matches: preview, PREVIEW
/\bpre\b/i, # matches: pre, PRE as a whole word
/nightly/i, # matches: nightly, NIGHTLY
/snapshot/i, # matches: snapshot, SNAPSHOT
/canary/i, # matches: canary, CANARY
/unstable/i, # matches: unstable, UNSTABLE
/\d+[a-z]\d*/, # matches: 3.15.0a2, 1.0b1 (version followed by letter and optional number)
/[a-z]+\d+$/, # matches: alpha1, beta2, rc3 at the end
/\.post\d+/i, # matches: .post1, .POST2 (Python PEP 440 post-release)
/\.dev\d+/i # matches: .dev0, .DEV1 (Python PEP 440 development release)
]

# Check both the version part and the suffix part
version_matches = version && prerelease_patterns.any? { |pattern| T.must(version).match?(pattern) }
suffix_matches = suffix && prerelease_patterns.any? { |pattern| T.must(suffix).match?(pattern) }

!!(version_matches || suffix_matches)
end

sig do
Expand Down
93 changes: 93 additions & 0 deletions docker/spec/dependabot/docker/tag_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,97 @@
expect(described_class.new("2.4").same_but_less_precise?(described_class.new("2.42"))).to be false
end
end

describe "#looks_like_prerelease?" do
context "with prerelease tags" do
it "detects alpha versions" do
expect(described_class.new("3.15.0a2").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-alpha").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-alpha.1").looks_like_prerelease?).to be true
expect(described_class.new("2.0-ALPHA").looks_like_prerelease?).to be true
end

it "detects beta versions" do
expect(described_class.new("3.5.0b3").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-beta").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-beta.1").looks_like_prerelease?).to be true
expect(described_class.new("2.0-BETA").looks_like_prerelease?).to be true
end

it "detects release candidate versions" do
expect(described_class.new("3.6.0rc1").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-rc").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-rc.1").looks_like_prerelease?).to be true
expect(described_class.new("2.0-RC").looks_like_prerelease?).to be true
end

it "detects dev versions" do
expect(described_class.new("1.0-dev").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-DEV").looks_like_prerelease?).to be true
end

it "detects preview versions" do
expect(described_class.new("1.0-preview").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-PREVIEW").looks_like_prerelease?).to be true
end

it "detects pre versions" do
expect(described_class.new("1.0-pre").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-PRE").looks_like_prerelease?).to be true
end

it "detects nightly versions" do
expect(described_class.new("1.0-nightly").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-NIGHTLY").looks_like_prerelease?).to be true
end

it "detects snapshot versions" do
expect(described_class.new("1.0-snapshot").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-SNAPSHOT").looks_like_prerelease?).to be true
end

it "detects canary versions" do
expect(described_class.new("1.0-canary").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-CANARY").looks_like_prerelease?).to be true
end

it "detects unstable versions" do
expect(described_class.new("1.0-unstable").looks_like_prerelease?).to be true
expect(described_class.new("1.0.0-UNSTABLE").looks_like_prerelease?).to be true
end

it "detects Python PEP 440 pre-release formats" do
# Pre-release segment: {a|b|rc}N
expect(described_class.new("1.0.0a1").looks_like_prerelease?).to be true
expect(described_class.new("2.1.0b2").looks_like_prerelease?).to be true
expect(described_class.new("3.2.0rc3").looks_like_prerelease?).to be true
end

it "detects Python PEP 440 post-release formats" do
# Post-release segment: .postN
expect(described_class.new("1.0.0.post1").looks_like_prerelease?).to be true
expect(described_class.new("2.1.0.POST2").looks_like_prerelease?).to be true
end

it "detects Python PEP 440 development release formats" do
# Development release segment: .devN
expect(described_class.new("1.0.0.dev0").looks_like_prerelease?).to be true
expect(described_class.new("2.1.0.DEV1").looks_like_prerelease?).to be true
end
end

context "with stable tags" do
it "does not detect stable versions as prereleases" do
expect(described_class.new("3.14.1").looks_like_prerelease?).to be false
expect(described_class.new("1.0.0").looks_like_prerelease?).to be false
expect(described_class.new("2.4.2").looks_like_prerelease?).to be false
end

it "does not detect tags with suffix as prereleases" do
expect(described_class.new("3.14.1-slim-trixie").looks_like_prerelease?).to be false
expect(described_class.new("3.6.3-alpine").looks_like_prerelease?).to be false
expect(described_class.new("2.7.14-stretch").looks_like_prerelease?).to be false
end
end
end
end
Loading