Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fork): Fork default branch only #1994

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
76 changes: 52 additions & 24 deletions src/main/java/org/kohsuke/github/GHRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -1455,26 +1455,14 @@ public PagedIterable<GHRepository> listForks(final ForkSort sort) {
/**
* Forks this repository as your repository.
*
* @return Newly forked repository that belong to you.
* @return Newly forked repository that belongs to you.
* @throws IOException
* the io exception
* @deprecated Use {@link #createFork(String, String, boolean)} instead
*/
@Deprecated(forRemoval = true)
public GHRepository fork() throws IOException {
root().createRequest().method("POST").withUrlPath(getApiTailUrl("forks")).send();

// this API is asynchronous. we need to wait for a bit
for (int i = 0; i < 10; i++) {
GHRepository r = root().getMyself().getRepository(name);
if (r != null) {
return r;
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw (IOException) new InterruptedIOException().initCause(e);
}
}
throw new IOException(this + " was forked but can't find the new repository");
return createFork(null, null, false);
}

/**
Expand All @@ -1500,20 +1488,60 @@ public GHBranchSync sync(String branch) throws IOException {
*
* @param org
* the org
* @return Newly forked repository that belong to you.
* @return Newly forked repository that belongs to you.
* @throws IOException
* the io exception
* @deprecated Use {@link #createFork(String, String, boolean)} instead
*/
@Deprecated(forRemoval = true)
public GHRepository forkTo(GHOrganization org) throws IOException {
root().createRequest()
.method("POST")
.with("organization", org.getLogin())
.withUrlPath(getApiTailUrl("forks"))
.send();
return createFork(org.getLogin(), null, false);
}

/**
* Creates a fork of this repository with optional parameters.
*
* @param organization
* the organization to fork to, or null to fork to the authenticated user's account
* @param name
* the name of the new repository, or null to use the same name as the original repository
* @param defaultBranchOnly
* whether to fork only the default branch
* @return the newly forked repository
* @throws IOException
* if an I/O error occurs
*/
public GHRepository createFork(@Nullable String organization, @Nullable String name, boolean defaultBranchOnly)
throws IOException {
Comment on lines +1501 to +1515
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You did basically exactly what I ask for with the deprecations, thanks!
Did you leave out the public GHRepository createFork() intentionally or just WIP?
If you still plan to provide the method overload with few parameters, the right signature is public GHRepository createFork(boolean defaultBranchOnly) instead of no parameters. What are your thoughts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WiP, definitely. I'll work on it, thanks!


if (organization != null && organization.isEmpty()) {
throw new IllegalArgumentException("Organization cannot be empty");
gounthar marked this conversation as resolved.
Show resolved Hide resolved
}
if (name != null && name.isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
gounthar marked this conversation as resolved.
Show resolved Hide resolved
}
if (name != null && !name.matches("^[a-zA-Z0-9._-]+$")) {
throw new IllegalArgumentException("Repository name contains invalid characters");
}
Requester requester = root().createRequest().method("POST").withUrlPath(getApiTailUrl("forks"));

if (organization != null) {
requester.with("organization", organization);
}
gounthar marked this conversation as resolved.
Show resolved Hide resolved
if (name != null) {
requester.with("name", name);
}
gounthar marked this conversation as resolved.
Show resolved Hide resolved
if (defaultBranchOnly) {
requester.with("default_branch_only", true);
}

requester.send();

// this API is asynchronous. we need to wait for a bit
for (int i = 0; i < 10; i++) {
GHRepository r = org.getRepository(name);
GHRepository r = organization != null
? root().getOrganization(organization).getRepository(name != null ? name : this.name)
: root().getMyself().getRepository(name != null ? name : this.name);
gounthar marked this conversation as resolved.
Show resolved Hide resolved
if (r != null) {
return r;
}
Expand All @@ -1523,7 +1551,7 @@ public GHRepository forkTo(GHOrganization org) throws IOException {
throw (IOException) new InterruptedIOException().initCause(e);
}
}
throw new IOException(this + " was forked into " + org.getLogin() + " but can't find the new repository");
throw new IOException(this + " was forked but can't find the new repository");
}

/**
Expand Down
27 changes: 27 additions & 0 deletions src/test/java/org/kohsuke/github/GHRepositoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,33 @@ public void ghRepositorySearchBuilderForkDefaultResetForksSearchTerms() {
assertThat(ghRepositorySearchBuilder.terms.stream().filter(item -> item.contains("fork:")).count(), is(0L));
}

/**
* Test createFork method with valid parameters.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
@Test
public void testCreateForkWithValidParameters() throws IOException {
Copy link
Member

@bitwiseman bitwiseman Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at the test for forkTo():

@Test
public void testOrgFork() throws Exception {
cleanupRepository(GITHUB_API_TEST_ORG + "/rubywm");
gitHub.getRepository("kohsuke/rubywm").forkTo(gitHub.getOrganization(GITHUB_API_TEST_ORG));
}

That might help.

Note, it uses GITHUB_API_TEST_ORG but you can you a different org while you're trying things out. When you're basically ready, we can re-record the using GITHUB_API_TEST_ORG .

GHRepository repository = getRepository();
GHRepository forkedRepository = repository.createFork("new-owner", "new-repo", true);
assertThat(forkedRepository, notNullValue());
assertThat(forkedRepository.getOwnerName(), equalTo("new-owner"));
assertThat(forkedRepository.getName(), equalTo("new-repo"));
}

/**
* Test createFork method with invalid parameters.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
@Test(expected = IllegalArgumentException.class)
public void testCreateForkWithInvalidParameters() throws IOException {
GHRepository repository = getRepository();
repository.createFork(null, "", true);
}

/**
* List commit comments some comments.
*
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/org/kohsuke/github/GitHubWireMockRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,16 @@ protected void before() {
.stubFor(any(anyUrl()).willReturn(aResponse().withTransformers(ProxyToOriginalHostTransformer.NAME))
.atPriority(100));
}

this.apiServer()
.stubFor(post(urlMatching("/repos/.*/forks")).willReturn(aResponse().withStatus(202)
.withHeader("Content-Type", "application/json")
.withBodyFile("fork.json")));

this.apiServer()
.stubFor(get(urlMatching("/repos/hub4j-test-org/github-api")).willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("repository.json")));
gounthar marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Loading