Skip to content

Commit d37839b

Browse files
Alaurantbitwiseman
andauthored
Add option to fork default branch only (hub4j#1995)
* add only fork default branch * Remove owner validation in fork builder tests * update fork tests from personal to org account * improve test coverage * remove mockito test * improve more test coverage * mvn spotless:apply * change sleep method scope * Update src/test/java/org/kohsuke/github/GHRepositoryForkBuilderTest.java * Update src/test/java/org/kohsuke/github/GHRepositoryForkBuilderTest.java * Update src/test/java/org/kohsuke/github/GHRepositoryForkBuilderTest.java * Update src/test/java/org/kohsuke/github/GHRepositoryForkBuilderTest.java * Cleanup extra test files --------- Co-authored-by: Liam Newman <bitwiseman@gmail.com>
1 parent 8714d1b commit d37839b

File tree

80 files changed

+9386
-36
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+9386
-36
lines changed

src/main/java/org/kohsuke/github/GHRepository.java

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import java.io.IOException;
3636
import java.io.InputStream;
3737
import java.io.InputStreamReader;
38-
import java.io.InterruptedIOException;
3938
import java.io.Reader;
4039
import java.net.URL;
4140
import java.util.Arrays;
@@ -1458,23 +1457,11 @@ public PagedIterable<GHRepository> listForks(final ForkSort sort) {
14581457
* @return Newly forked repository that belong to you.
14591458
* @throws IOException
14601459
* the io exception
1460+
* @deprecated Use {@link #createFork()}
14611461
*/
1462+
@Deprecated
14621463
public GHRepository fork() throws IOException {
1463-
root().createRequest().method("POST").withUrlPath(getApiTailUrl("forks")).send();
1464-
1465-
// this API is asynchronous. we need to wait for a bit
1466-
for (int i = 0; i < 10; i++) {
1467-
GHRepository r = root().getMyself().getRepository(name);
1468-
if (r != null) {
1469-
return r;
1470-
}
1471-
try {
1472-
Thread.sleep(3000);
1473-
} catch (InterruptedException e) {
1474-
throw (IOException) new InterruptedIOException().initCause(e);
1475-
}
1476-
}
1477-
throw new IOException(this + " was forked but can't find the new repository");
1464+
return this.createFork().create();
14781465
}
14791466

14801467
/**
@@ -1503,27 +1490,11 @@ public GHBranchSync sync(String branch) throws IOException {
15031490
* @return Newly forked repository that belong to you.
15041491
* @throws IOException
15051492
* the io exception
1493+
* @deprecated Use {@link #createFork()}
15061494
*/
1495+
@Deprecated
15071496
public GHRepository forkTo(GHOrganization org) throws IOException {
1508-
root().createRequest()
1509-
.method("POST")
1510-
.with("organization", org.getLogin())
1511-
.withUrlPath(getApiTailUrl("forks"))
1512-
.send();
1513-
1514-
// this API is asynchronous. we need to wait for a bit
1515-
for (int i = 0; i < 10; i++) {
1516-
GHRepository r = org.getRepository(name);
1517-
if (r != null) {
1518-
return r;
1519-
}
1520-
try {
1521-
Thread.sleep(3000);
1522-
} catch (InterruptedException e) {
1523-
throw (IOException) new InterruptedIOException().initCause(e);
1524-
}
1525-
}
1526-
throw new IOException(this + " was forked into " + org.getLogin() + " but can't find the new repository");
1497+
return this.createFork().organization(org).create();
15271498
}
15281499

15291500
/**
@@ -3453,4 +3424,14 @@ public void deleteAutolink(int autolinkId) throws IOException {
34533424
.send();
34543425
}
34553426

3427+
/**
3428+
* Create fork gh repository fork builder.
3429+
* (https://docs.github.com/en/rest/repos/forks?apiVersion=2022-11-28#create-a-fork)
3430+
*
3431+
* @return the gh repository fork builder
3432+
*/
3433+
public GHRepositoryForkBuilder createFork() {
3434+
return new GHRepositoryForkBuilder(this);
3435+
}
3436+
34563437
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package org.kohsuke.github;
2+
3+
import java.io.IOException;
4+
import java.io.InterruptedIOException;
5+
6+
/**
7+
* A builder pattern object for creating a fork of a repository.
8+
*
9+
* @see GHRepository#createFork() GHRepository#createFork()GHRepository#createFork()
10+
* @see <a href="https://docs.github.com/en/rest/repos/forks#create-a-fork">Repository fork API</a>
11+
*/
12+
public class GHRepositoryForkBuilder {
13+
private final GHRepository repo;
14+
private final Requester req;
15+
private String organization;
16+
private String name;
17+
private Boolean defaultBranchOnly;
18+
19+
static int FORK_RETRY_INTERVAL = 3000;
20+
21+
/**
22+
* Instantiates a new Gh repository fork builder.
23+
*
24+
* @param repo
25+
* the repository
26+
*/
27+
GHRepositoryForkBuilder(GHRepository repo) {
28+
this.repo = repo;
29+
this.req = repo.root().createRequest();
30+
}
31+
32+
/**
33+
* Sets whether to fork only the default branch.
34+
*
35+
* @param defaultBranchOnly
36+
* the default branch only
37+
* @return the gh repository fork builder
38+
*/
39+
public GHRepositoryForkBuilder defaultBranchOnly(boolean defaultBranchOnly) {
40+
this.defaultBranchOnly = defaultBranchOnly;
41+
return this;
42+
}
43+
44+
/**
45+
* Specifies the target organization for the fork.
46+
*
47+
* @param organization
48+
* the organization
49+
* @return the gh repository fork builder
50+
*/
51+
public GHRepositoryForkBuilder organization(GHOrganization organization) {
52+
this.organization = organization.getLogin();
53+
return this;
54+
}
55+
56+
/**
57+
* Sets a custom name for the forked repository.
58+
*
59+
* @param name
60+
* the desired repository name
61+
* @return the builder
62+
*/
63+
public GHRepositoryForkBuilder name(String name) {
64+
this.name = name;
65+
return this;
66+
}
67+
68+
/**
69+
* Creates the fork with the specified parameters.
70+
*
71+
* @return the gh repository
72+
* @throws IOException
73+
* the io exception
74+
*/
75+
public GHRepository create() throws IOException {
76+
if (defaultBranchOnly != null) {
77+
req.with("default_branch_only", defaultBranchOnly);
78+
}
79+
if (organization != null) {
80+
req.with("organization", organization);
81+
}
82+
if (name != null) {
83+
req.with("name", name);
84+
}
85+
86+
req.method("POST").withUrlPath(repo.getApiTailUrl("forks")).send();
87+
88+
// this API is asynchronous. we need to wait for a bit
89+
for (int i = 0; i < 10; i++) {
90+
GHRepository r = lookupForkedRepository();
91+
if (r != null) {
92+
return r;
93+
}
94+
sleep(FORK_RETRY_INTERVAL);
95+
}
96+
throw new IOException(createTimeoutMessage());
97+
}
98+
99+
private GHRepository lookupForkedRepository() throws IOException {
100+
String repoName = name != null ? name : repo.getName();
101+
102+
if (organization != null) {
103+
return repo.root().getOrganization(organization).getRepository(repoName);
104+
}
105+
return repo.root().getMyself().getRepository(repoName);
106+
}
107+
108+
/**
109+
* Sleep.
110+
*
111+
* @param millis
112+
* the millis
113+
* @throws IOException
114+
* the io exception
115+
*/
116+
void sleep(int millis) throws IOException {
117+
try {
118+
Thread.sleep(millis);
119+
} catch (InterruptedException e) {
120+
throw (IOException) new InterruptedIOException().initCause(e);
121+
}
122+
}
123+
124+
/**
125+
* Create timeout message string.
126+
*
127+
* @return the string
128+
*/
129+
String createTimeoutMessage() {
130+
StringBuilder message = new StringBuilder(repo.getFullName());
131+
message.append(" was forked");
132+
133+
if (organization != null) {
134+
message.append(" into ").append(organization);
135+
}
136+
137+
if (name != null) {
138+
message.append(" with name ").append(name);
139+
}
140+
141+
message.append(" but can't find the new repository");
142+
return message.toString();
143+
}
144+
}

0 commit comments

Comments
 (0)