Skip to content

Commit

Permalink
Port the updater codebase #20
Browse files Browse the repository at this point in the history
  • Loading branch information
javiertuya committed Feb 27, 2024
1 parent 135392e commit 763e7de
Show file tree
Hide file tree
Showing 32 changed files with 2,349 additions and 0 deletions.
40 changes: 40 additions & 0 deletions dashgit-updater/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="test" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
2 changes: 2 additions & 0 deletions dashgit-updater/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
it.properties
23 changes: 23 additions & 0 deletions dashgit-updater/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>dashgit-updater</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>
6 changes: 6 additions & 0 deletions dashgit-updater/.settings/org.eclipse.core.resources.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/main/resources=UTF-8
encoding//src/test/java=UTF-8
encoding//src/test/resources=UTF-8
encoding/<project>=UTF-8
16 changes: 16 additions & 0 deletions dashgit-updater/.settings/org.eclipse.jdt.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=17
4 changes: 4 additions & 0 deletions dashgit-updater/.settings/org.eclipse.m2e.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
97 changes: 97 additions & 0 deletions dashgit-updater/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>giis</groupId>
<artifactId>dashgit-updater</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>dashgit-updater</name>
<description>Generate dependabot combined updates from DashGit</description>
<url>http://github.com/javiertuya/dashgit</url>
<organization>
<name>Software Engineering Research Group (GIIS) - Universidad de Oviedo, ES</name>
<url>http://giis.uniovi.es/</url>
</organization>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.javiertuya</groupId>
<artifactId>visual-assert</artifactId>
<version>2.4.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.11.0</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.23.0</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>

<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>github-api</artifactId>
<version>1.318</version>
</dependency>
<!-- Version rc de gitlab compatible con Spring Boot 3, sustituir por la definitiva cuando salga) -->
<dependency>
<groupId>org.gitlab4j</groupId>
<artifactId>gitlab4j-api</artifactId>
<version>6.0.0-rc.4</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>6.8.0.202311291450-r</version>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package giis.dashgit.updater;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import giis.qabot.ci.clients.GitLocal;
import giis.qabot.ci.clients.IGitClient;
import giis.qabot.ci.models.Branch;
import giis.qabot.ci.models.Project;
import giis.qabot.ci.models.PullRequest;
import giis.qabot.core.models.Formatter;
import giis.qabot.core.models.Util;
import lombok.extern.slf4j.Slf4j;

/**
* Core service to create combined dependency updates for a given project/repository.
* Ported from QABot and reduced to remove all Spring Boot dependencies and callbacks,
* now using automerge to merge the combined updates.
*
* Restrictions: Dependabot update PRs must be generated against the main branch.
*/
@Slf4j
public class DependencyUpdater {
private static final String COMBINED_BRANCH_PREFIX = "dependabot/dashgit/update";

// only for combined updates, create the combined PR but does not delete branches nor merge
private static final boolean WET_RUN = false;

/**
* Creates a combined pull requests with all branches included in the indicated project
* and enables the automerge of the resulting PR.
* The combined changes are squashed and details on the included changes are shown in the description.
*/
public PullRequest runCreateCombinedProjectPr(IGitClient gitClient, GitLocal gitLocal, Project project,
long rateLimitDelay) {
log.info("**********************************************************");
log.info("****** Rebase and automerge a combined pull request ******");
log.info("Project {}", project.name());
for (Branch branch : project.branches())
log.info("Branch: {}", branch.name());
log.info("**********************************************************");

// Creates the combined update branch (only with no conflictng branches)
// Note that the combined branch is created using main branch as the base
String combinedBranch = getCombinedBranch(gitClient, gitLocal, project);
if ("".equals(combinedBranch)) { // no hay actualizaciones que combinar
log.warn("There are no updates to merge (or all updates have merge conflicts)");
return null;
}
log.info("Combined branch created in local repo, name: {}", combinedBranch);
gitLocal.push(false);

// Creates the combined update PR.
// Note that target branch is taken from the first PR, and it should be the main branch
Formatter formatter = new Formatter();
PullRequest refPr = project.branches().get(0).pullRequest();
PullRequest pr = createCombinedPullRequest(gitClient, formatter, project, refPr, combinedBranch);
log.info("Combined pull request created: {}", pr.title());

// Cleanup (branches not included in the combined PR) and set comments
finishCombinedPullRequest(gitClient, formatter, project, pr, rateLimitDelay);
log.info("****** End rebase and automerge a combined pull request ******");
log.info("**************************************************************");
return pr;
}

/**
* Creates a local repository branch with all non conflicting changes,
* returning the branch name (empty if no branch has been included)
*/
private String getCombinedBranch(IGitClient gitClient, GitLocal gitLocal, Project project) {
log.info("*** Create Combined Branch");
String repoName = project.name();
gitLocal.cloneRepository(repoName);
// New branch to add to the combined PR
String combinedBranch = COMBINED_BRANCH_PREFIX + "-" + gitLocal.getTimestamp();
gitLocal.checkout(combinedBranch, true);
int successCount = 0;
// Adds each change (note that QABot used Cherry Pick, here we use merge)
for (Branch branch : project.branches()) {
PullRequest pr = branch.pullRequest();
log.info("Combine pull request: {}", pr.title());
boolean success = gitLocal.merge(pr.sha(), pr.title());
pr.canBeMerged(success);
pr.cantBeMergedReason(success ? "" : "cannot be merged");
branch.buildSummary(); // propagates the status (needed in dashgit?)
successCount += success ? 1 : 0;
}
// Push is not done here, the calling method will do it if there have been any success
return successCount > 0 ? combinedBranch : "";
}

private PullRequest createCombinedPullRequest(IGitClient gitClient, Formatter formatter, Project project,
PullRequest refPr, String combinedBranch) {
log.info("*** Create Combined Pull Request");
// Sets the description to show the updates that have (and haven't) been merged
StringBuilder successSb = new StringBuilder();
StringBuilder failSb = new StringBuilder();
List<String> labels = new ArrayList<>();
for (Branch branch : project.branches()) {
PullRequest pr = branch.pullRequest();
if (Boolean.TRUE == pr.canBeMerged()) {
successSb.append("\n- " + formatter.url(pr.title(), pr.htmlUrl()));
for (String label : pr.labels()) // sets labels avoiding reptitions
if (!labels.contains(label))
labels.add(label);
} else {
failSb.append("\n- " + formatter.url(pr.title(), pr.htmlUrl()));
}
}
String title = "Combined dependency updates (" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + ")";
String description = "Includes these updates:" + successSb.toString();
if (failSb.length() > 0)
description += "\n\nDoes not include these updates because of merge conflicts:" + failSb.toString();

// New combined PR
return gitClient.createPullRequest(project.name(), combinedBranch, refPr.targetBranch(), title, description,
refPr.assignee(), labels, true, true, true);
}

private void finishCombinedPullRequest(IGitClient gitClient, Formatter formatter, Project project,
PullRequest newPr, long rateLimitDelay) {
log.info("*** Finish Combined Pull Request");
for (Branch branch : project.branches()) {
PullRequest pr = branch.pullRequest();
if (Boolean.TRUE == pr.canBeMerged()) { // no deberia ser nulo al haberse determinado ya mergeabilty
gitClient.addPullRequestCommment(pr, "This has been included in the combined pull request "
+ formatter.url(newPr.title(), newPr.htmlUrl()));
log.info("Remove branch: {}, project: {}", newPr.sourceBranch(), newPr.repoName());
if (!WET_RUN)
gitClient.deleteBranch(pr.fullName(), pr.sourceBranch());
} else {
gitClient.addPullRequestCommment(pr, "This has not been included in the combined pull request "
+ formatter.url(newPr.title(), newPr.htmlUrl()) + " because of potential merge conflicts");
}
Util.delay(rateLimitDelay); // separa acciones para evitar secondary rate limits en github
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package giis.qabot.ci.clients;

public class ClientException extends RuntimeException {
private static final long serialVersionUID = 6602029701229527554L;

public ClientException(String message) {
super(message);
}

}
Loading

0 comments on commit 763e7de

Please sign in to comment.