diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubConfiguration/help-maxTagCount.html b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubConfiguration/help-maxTagCount.html
new file mode 100644
index 000000000..a615ecbf7
--- /dev/null
+++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubConfiguration/help-maxTagCount.html
@@ -0,0 +1,6 @@
+
+ Limits the number of tags to process during tag discovery. Set to 0
+ (the default) for no limit. This setting is only effective when
+ descending order is enabled. Individual jobs can override this setting
+ in their Tag Discovery trait configuration.
+
diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubConfiguration/help-tagDescendingOrder.html b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubConfiguration/help-tagDescendingOrder.html
new file mode 100644
index 000000000..28ed2b35b
--- /dev/null
+++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubConfiguration/help-tagDescendingOrder.html
@@ -0,0 +1,6 @@
+
+ When checked, tag discovery will scan tags in descending (reverse alphabetical)
+ order by default across all jobs using GitHub tag discovery. This is useful when you
+ want the most recent semantic version tags to be discovered first. Individual
+ jobs can override this setting in their Tag Discovery trait configuration.
+
diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/config.jelly b/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/config.jelly
index d89935c79..40eb02f23 100644
--- a/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/config.jelly
+++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/config.jelly
@@ -2,4 +2,9 @@
+
+
+
+
+
diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/help-descendingOrder.html b/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/help-descendingOrder.html
new file mode 100644
index 000000000..6090c3080
--- /dev/null
+++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/help-descendingOrder.html
@@ -0,0 +1,7 @@
+
+ When checked, tags will be scanned in descending (reverse alphabetical) order
+ instead of the default ascending order. This is useful when you want the
+ most recent semantic version tags to be discovered first. This setting
+ overrides the global default configured in Manage Jenkins » System
+ » Tag Discovery.
+
diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/help-maxTagCount.html b/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/help-maxTagCount.html
new file mode 100644
index 000000000..7c432caae
--- /dev/null
+++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTrait/help-maxTagCount.html
@@ -0,0 +1,6 @@
+
+ Limits the number of tags to process during tag discovery. Set to 0
+ (the default) for no limit. This setting is only effective when
+ descending order is enabled. This setting overrides the global default
+ configured in Manage Jenkins » System » Tag Discovery.
+
diff --git a/src/test/java/org/jenkinsci/plugins/github_branch_source/GithubSCMSourceTagsTest.java b/src/test/java/org/jenkinsci/plugins/github_branch_source/GithubSCMSourceTagsTest.java
index 806812fe8..eecb92c2d 100644
--- a/src/test/java/org/jenkinsci/plugins/github_branch_source/GithubSCMSourceTagsTest.java
+++ b/src/test/java/org/jenkinsci/plugins/github_branch_source/GithubSCMSourceTagsTest.java
@@ -8,10 +8,12 @@
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.NoSuchElementException;
import jenkins.scm.api.SCMHeadObserver;
import org.junit.Test;
@@ -150,6 +152,86 @@ public void testExistingMultipleTags() throws IOException {
}
}
+ @Test
+ public void testNaturalSortDescendingOrder() throws IOException {
+ // Scenario: Tags with version-like names where natural and alphabetical sort differ.
+ // Fixture returns alphabetical order: v0.1.11, v0.1.2, v0.10.0, v0.2.0
+ // Natural descending should give: v0.10.0, v0.2.0, v0.1.11, v0.1.2
+ // (Alphabetical descending would give: v0.2.0, v0.10.0, v0.1.2, v0.1.11 -- wrong)
+ SCMHeadObserver mockSCMHeadObserver = Mockito.mock(SCMHeadObserver.class);
+ Mockito.when(mockSCMHeadObserver.getIncludes()).thenReturn(null);
+
+ GHRepository versionedRepo = github.getRepository("cloudbeers/yolo-versioned");
+
+ GitHubSCMSourceContext context = new GitHubSCMSourceContext(null, mockSCMHeadObserver);
+ context.wantTags(true);
+ context.withTagDescendingOrder(true);
+ GitHubSCMSourceRequest request =
+ context.newRequest(new GitHubSCMSource("cloudbeers", "yolo-versioned", null, false), null);
+ Iterator tags = new GitHubSCMSource.LazyTags(request, versionedRepo).iterator();
+
+ List tagRefs = new ArrayList<>();
+ while (tags.hasNext()) {
+ tagRefs.add(tags.next().getRef());
+ }
+ assertEquals(4, tagRefs.size());
+ assertEquals("refs/tags/v0.10.0", tagRefs.get(0));
+ assertEquals("refs/tags/v0.2.0", tagRefs.get(1));
+ assertEquals("refs/tags/v0.1.11", tagRefs.get(2));
+ assertEquals("refs/tags/v0.1.2", tagRefs.get(3));
+ }
+
+ @Test
+ public void testExistingMultipleTagsDescendingOrder() throws IOException {
+ // Scenario: Requesting multiple tags in descending order
+ SCMHeadObserver mockSCMHeadObserver = Mockito.mock(SCMHeadObserver.class);
+
+ Mockito.when(mockSCMHeadObserver.getIncludes())
+ .thenReturn(new HashSet<>(Arrays.asList(
+ new GitHubTagSCMHead("existent-multiple-tags1", System.currentTimeMillis()),
+ new GitHubTagSCMHead("existent-multiple-tags2", System.currentTimeMillis()))));
+ GitHubSCMSourceContext context = new GitHubSCMSourceContext(null, mockSCMHeadObserver);
+ context.wantTags(true);
+ context.withTagDescendingOrder(true);
+ GitHubSCMSourceRequest request =
+ context.newRequest(new GitHubSCMSource("cloudbeers", "yolo", null, false), null);
+ assertTrue(request.isTagDescendingOrder());
+ Iterator tags = new GitHubSCMSource.LazyTags(request, repo).iterator();
+
+ // Expected: Tags should be returned in reverse (descending) order
+ List tagRefs = new ArrayList<>();
+ while (tags.hasNext()) {
+ tagRefs.add(tags.next().getRef());
+ }
+ assertEquals(2, tagRefs.size());
+ assertEquals("refs/tags/existent-multiple-tags2", tagRefs.get(0));
+ assertEquals("refs/tags/existent-multiple-tags1", tagRefs.get(1));
+ }
+
+ @Test
+ public void testExistingMultipleTagsAscendingOrderByDefault() throws IOException {
+ // Scenario: Requesting multiple tags without descending flag (default ascending)
+ SCMHeadObserver mockSCMHeadObserver = Mockito.mock(SCMHeadObserver.class);
+
+ Mockito.when(mockSCMHeadObserver.getIncludes())
+ .thenReturn(new HashSet<>(Arrays.asList(
+ new GitHubTagSCMHead("existent-multiple-tags1", System.currentTimeMillis()),
+ new GitHubTagSCMHead("existent-multiple-tags2", System.currentTimeMillis()))));
+ GitHubSCMSourceContext context = new GitHubSCMSourceContext(null, mockSCMHeadObserver);
+ context.wantTags(true);
+ GitHubSCMSourceRequest request =
+ context.newRequest(new GitHubSCMSource("cloudbeers", "yolo", null, false), null);
+ assertFalse(request.isTagDescendingOrder());
+ Iterator tags = new GitHubSCMSource.LazyTags(request, repo).iterator();
+
+ // Expected: Tags should be returned in ascending (default) order
+ assertTrue(tags.hasNext());
+ assertEquals("refs/tags/existent-multiple-tags1", tags.next().getRef());
+ assertTrue(tags.hasNext());
+ assertEquals("refs/tags/existent-multiple-tags2", tags.next().getRef());
+ assertFalse(tags.hasNext());
+ }
+
@Test
public void testExistingMultipleTagsGHFileNotFoundExceptionIterable() throws IOException {
// Scenario: Requesting multiple tags but a FileNotFound is thrown
diff --git a/src/test/java/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTraitTest.java b/src/test/java/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTraitTest.java
index e4e52e83c..bc9ac4714 100644
--- a/src/test/java/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTraitTest.java
+++ b/src/test/java/org/jenkinsci/plugins/github_branch_source/TagDiscoveryTraitTest.java
@@ -24,6 +24,7 @@ public class TagDiscoveryTraitTest {
@Test
public void decorateContext() throws Exception {
+ GitHubConfiguration.get().setTagDescendingOrder(false);
GitHubSCMSourceContext probe = new GitHubSCMSourceContext(null, SCMHeadObserver.collect());
assertThat(probe.wantBranches(), is(false));
assertThat(probe.wantPRs(), is(false));
@@ -33,9 +34,90 @@ public void decorateContext() throws Exception {
assertThat(probe.wantBranches(), is(false));
assertThat(probe.wantPRs(), is(false));
assertThat(probe.wantTags(), is(true));
+ assertThat(probe.isTagDescendingOrder(), is(false));
assertThat(probe.authorities(), contains(instanceOf(TagDiscoveryTrait.TagSCMHeadAuthority.class)));
}
+ @Test
+ public void decorateContextDescendingOrder() throws Exception {
+ GitHubConfiguration.get().setTagDescendingOrder(false);
+ GitHubSCMSourceContext probe = new GitHubSCMSourceContext(null, SCMHeadObserver.collect());
+ assertThat(probe.isTagDescendingOrder(), is(false));
+ TagDiscoveryTrait trait = new TagDiscoveryTrait();
+ trait.setDescendingOrder(true);
+ trait.applyToContext(probe);
+ assertThat(probe.wantTags(), is(true));
+ assertThat(probe.isTagDescendingOrder(), is(true));
+ }
+
+ @Test
+ public void decorateContextUsesGlobalDefault() throws Exception {
+ GitHubConfiguration.get().setTagDescendingOrder(true);
+ try {
+ GitHubSCMSourceContext probe = new GitHubSCMSourceContext(null, SCMHeadObserver.collect());
+ new TagDiscoveryTrait().applyToContext(probe);
+ assertThat(probe.wantTags(), is(true));
+ assertThat(probe.isTagDescendingOrder(), is(true));
+ } finally {
+ GitHubConfiguration.get().setTagDescendingOrder(false);
+ }
+ }
+
+ @Test
+ public void decorateContextPerJobOverridesGlobal() throws Exception {
+ GitHubConfiguration.get().setTagDescendingOrder(true);
+ try {
+ GitHubSCMSourceContext probe = new GitHubSCMSourceContext(null, SCMHeadObserver.collect());
+ TagDiscoveryTrait trait = new TagDiscoveryTrait();
+ trait.setDescendingOrder(false);
+ trait.applyToContext(probe);
+ assertThat(probe.wantTags(), is(true));
+ assertThat(probe.isTagDescendingOrder(), is(false));
+ } finally {
+ GitHubConfiguration.get().setTagDescendingOrder(false);
+ }
+ }
+
+ @Test
+ public void decorateContextMaxTagCount() throws Exception {
+ GitHubConfiguration.get().setMaxTagCount(0);
+ GitHubSCMSourceContext probe = new GitHubSCMSourceContext(null, SCMHeadObserver.collect());
+ assertThat(probe.getMaxTagCount(), is(0));
+ TagDiscoveryTrait trait = new TagDiscoveryTrait();
+ trait.setMaxTagCount(10);
+ trait.applyToContext(probe);
+ assertThat(probe.wantTags(), is(true));
+ assertThat(probe.getMaxTagCount(), is(10));
+ }
+
+ @Test
+ public void decorateContextMaxTagCountUsesGlobalDefault() throws Exception {
+ GitHubConfiguration.get().setMaxTagCount(25);
+ try {
+ GitHubSCMSourceContext probe = new GitHubSCMSourceContext(null, SCMHeadObserver.collect());
+ new TagDiscoveryTrait().applyToContext(probe);
+ assertThat(probe.wantTags(), is(true));
+ assertThat(probe.getMaxTagCount(), is(25));
+ } finally {
+ GitHubConfiguration.get().setMaxTagCount(0);
+ }
+ }
+
+ @Test
+ public void decorateContextMaxTagCountPerJobOverridesGlobal() throws Exception {
+ GitHubConfiguration.get().setMaxTagCount(25);
+ try {
+ GitHubSCMSourceContext probe = new GitHubSCMSourceContext(null, SCMHeadObserver.collect());
+ TagDiscoveryTrait trait = new TagDiscoveryTrait();
+ trait.setMaxTagCount(5);
+ trait.applyToContext(probe);
+ assertThat(probe.wantTags(), is(true));
+ assertThat(probe.getMaxTagCount(), is(5));
+ } finally {
+ GitHubConfiguration.get().setMaxTagCount(0);
+ }
+ }
+
@Test
public void includeCategory() throws Exception {
assertThat(new TagDiscoveryTrait().includeCategory(ChangeRequestSCMHeadCategory.DEFAULT), is(false));
diff --git a/src/test/resources/api/__files/body-cloudbeers-yolo-versioned.json b/src/test/resources/api/__files/body-cloudbeers-yolo-versioned.json
new file mode 100644
index 000000000..14a2a2136
--- /dev/null
+++ b/src/test/resources/api/__files/body-cloudbeers-yolo-versioned.json
@@ -0,0 +1,109 @@
+{
+ "id": 43041241,
+ "name": "yolo-versioned",
+ "full_name": "cloudbeers/yolo-versioned",
+ "owner": {
+ "login": "cloudbeers",
+ "id": 4181899,
+ "avatar_url": "https://avatars.githubusercontent.com/u/4181899?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/cloudbeers",
+ "html_url": "https://github.com/cloudbeers",
+ "followers_url": "https://api.github.com/users/cloudbeers/followers",
+ "following_url": "https://api.github.com/users/cloudbeers/following{/other_user}",
+ "gists_url": "https://api.github.com/users/cloudbeers/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/cloudbeers/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/cloudbeers/subscriptions",
+ "organizations_url": "https://api.github.com/users/cloudbeers/orgs",
+ "repos_url": "https://api.github.com/users/cloudbeers/repos",
+ "events_url": "https://api.github.com/users/cloudbeers/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/cloudbeers/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "private": false,
+ "html_url": "https://github.com/cloudbeers/yolo-versioned",
+ "description": "Versioned tags test repo",
+ "fork": false,
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned",
+ "forks_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/forks",
+ "keys_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/teams",
+ "hooks_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/hooks",
+ "issue_events_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/events",
+ "assignees_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/tags",
+ "blobs_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/languages",
+ "stargazers_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/stargazers",
+ "contributors_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/contributors",
+ "subscribers_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/subscribers",
+ "subscription_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/subscription",
+ "commits_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/merges",
+ "archive_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/downloads",
+ "issues_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/labels{/name}",
+ "releases_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/releases{/id}",
+ "deployments_url": "https://api.github.com/repos/cloudbeers/yolo-versioned/deployments",
+ "created_at": "2015-09-24T02:58:30Z",
+ "updated_at": "2016-12-07T23:55:35Z",
+ "pushed_at": "2016-12-01T16:07:01Z",
+ "git_url": "git://github.com/cloudbeers/yolo-versioned.git",
+ "ssh_url": "git@github.com:cloudbeers/yolo-versioned.git",
+ "clone_url": "https://github.com/cloudbeers/yolo-versioned.git",
+ "svn_url": "https://github.com/cloudbeers/yolo-versioned",
+ "homepage": null,
+ "size": 3,
+ "stargazers_count": 0,
+ "watchers_count": 0,
+ "language": null,
+ "has_issues": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": false,
+ "forks_count": 0,
+ "mirror_url": null,
+ "open_issues_count": 0,
+ "forks": 0,
+ "open_issues": 0,
+ "watchers": 0,
+ "default_branch": "master",
+ "organization": {
+ "login": "cloudbeers",
+ "id": 4181899,
+ "avatar_url": "https://avatars.githubusercontent.com/u/4181899?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/cloudbeers",
+ "html_url": "https://github.com/cloudbeers",
+ "followers_url": "https://api.github.com/users/cloudbeers/followers",
+ "following_url": "https://api.github.com/users/cloudbeers/following{/other_user}",
+ "gists_url": "https://api.github.com/users/cloudbeers/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/cloudbeers/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/cloudbeers/subscriptions",
+ "organizations_url": "https://api.github.com/users/cloudbeers/orgs",
+ "repos_url": "https://api.github.com/users/cloudbeers/repos",
+ "events_url": "https://api.github.com/users/cloudbeers/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/cloudbeers/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "network_count": 0,
+ "subscribers_count": 0
+}
diff --git a/src/test/resources/api/__files/body-yolo-versioned-tags.json b/src/test/resources/api/__files/body-yolo-versioned-tags.json
new file mode 100644
index 000000000..4e29256db
--- /dev/null
+++ b/src/test/resources/api/__files/body-yolo-versioned-tags.json
@@ -0,0 +1,42 @@
+[
+ {
+ "ref": "refs/tags/v0.1.11",
+ "node_id": "MDM6UmVmNTU1NDMyMjU6dmVyc2lvbmVkLXYwLjEuMTE=",
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/refs/tags/v0.1.11",
+ "object": {
+ "sha": "aa11111111111111111111111111111111111111",
+ "type": "tag",
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/tags/aa11111111111111111111111111111111111111"
+ }
+ },
+ {
+ "ref": "refs/tags/v0.1.2",
+ "node_id": "MDM6UmVmNTU1NDMyMjU6dmVyc2lvbmVkLXYwLjEuMg==",
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/refs/tags/v0.1.2",
+ "object": {
+ "sha": "bb22222222222222222222222222222222222222",
+ "type": "tag",
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/tags/bb22222222222222222222222222222222222222"
+ }
+ },
+ {
+ "ref": "refs/tags/v0.2.0",
+ "node_id": "MDM6UmVmNTU1NDMyMjU6dmVyc2lvbmVkLXYwLjIuMA==",
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/refs/tags/v0.2.0",
+ "object": {
+ "sha": "cc33333333333333333333333333333333333333",
+ "type": "tag",
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/tags/cc33333333333333333333333333333333333333"
+ }
+ },
+ {
+ "ref": "refs/tags/v0.10.0",
+ "node_id": "MDM6UmVmNTU1NDMyMjU6dmVyc2lvbmVkLXYwLjEwLjA=",
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/refs/tags/v0.10.0",
+ "object": {
+ "sha": "dd44444444444444444444444444444444444444",
+ "type": "tag",
+ "url": "https://api.github.com/repos/cloudbeers/yolo-versioned/git/tags/dd44444444444444444444444444444444444444"
+ }
+ }
+]
diff --git a/src/test/resources/api/mappings/mapping-cloudbeers-yolo-versioned.json b/src/test/resources/api/mappings/mapping-cloudbeers-yolo-versioned.json
new file mode 100644
index 000000000..4c520ce47
--- /dev/null
+++ b/src/test/resources/api/mappings/mapping-cloudbeers-yolo-versioned.json
@@ -0,0 +1,33 @@
+{
+ "request": {
+ "url": "/repos/cloudbeers/yolo-versioned",
+ "method": "GET"
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "body-cloudbeers-yolo-versioned.json",
+ "headers": {
+ "Server": "GitHub.com",
+ "Date": "Tue, 06 Dec 2016 15:06:25 GMT",
+ "Content-Type": "application/json; charset=utf-8",
+ "Transfer-Encoding": "chunked",
+ "Status": "200 OK",
+ "X-RateLimit-Limit": "600",
+ "X-RateLimit-Remaining": "600",
+ "X-RateLimit-Reset": "1481039662",
+ "Cache-Control": "public, max-age=60, s-maxage=60",
+ "Vary": ["Accept", "Accept-Encoding"],
+ "ETag": "W/\"12cee9e1d9874cbabcbfaf3b112e8dac\"",
+ "X-GitHub-Media-Type": "github.v3; format=json",
+ "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval",
+ "Access-Control-Allow-Origin": "*",
+ "Content-Security-Policy": "default-src 'none'",
+ "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
+ "X-Content-Type-Options": "nosniff",
+ "X-Frame-Options": "deny",
+ "X-XSS-Protection": "1; mode=block",
+ "X-Served-By": "2d7a5e35115884240089368322196939",
+ "X-GitHub-Request-Id": "B2A7FE77:629C:AF96C44:5846D3F1"
+ }
+ }
+}
diff --git a/src/test/resources/api/mappings/mapping-yolo-versioned-tags.json b/src/test/resources/api/mappings/mapping-yolo-versioned-tags.json
new file mode 100644
index 000000000..76c9af5de
--- /dev/null
+++ b/src/test/resources/api/mappings/mapping-yolo-versioned-tags.json
@@ -0,0 +1,33 @@
+{
+ "request": {
+ "url": "/repos/cloudbeers/yolo-versioned/git/refs/tags",
+ "method": "GET"
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "body-yolo-versioned-tags.json",
+ "headers": {
+ "Server": "GitHub.com",
+ "Date": "Mon, 29 Jul 2019 23:22:30 GMT",
+ "Content-Type": "application/json; charset=utf-8",
+ "Transfer-Encoding": "chunked",
+ "Status": "200 OK",
+ "X-RateLimit-Limit": "600",
+ "X-RateLimit-Remaining": "595",
+ "X-RateLimit-Reset": "1481048932",
+ "Cache-Control": "public, max-age=60, s-maxage=60",
+ "Vary": ["Accept", "Accept-Encoding"],
+ "ETag": "W/\"a466888c00a5eaf3354ccabe37f75dd9\"",
+ "X-GitHub-Media-Type": "github.v3; format=json",
+ "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval",
+ "Access-Control-Allow-Origin": "*",
+ "Content-Security-Policy": "default-src 'none'",
+ "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
+ "X-Content-Type-Options": "nosniff",
+ "X-Frame-Options": "deny",
+ "X-XSS-Protection": "1; mode=block",
+ "X-Served-By": "88531cdcf1929112ec480e1806d44a33",
+ "X-GitHub-Request-Id": "BC8D23FA:31E4:269B4E33:5846F623"
+ }
+ }
+}