use of com.palantir.stash.stashbot.persistence.RepositoryConfiguration in project stashbot by palantir.
the class BuildSuccessReportingServlet method doGet.
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
try {
// Look at JenkinsManager class if you change this:
// final two arguments could be empty...
final String URL_FORMAT = "BASE_URL/REPO_ID/TYPE/STATE/BUILD_NUMBER/BUILD_HEAD[/MERGE_HEAD/PULLREQUEST_ID]";
final String pathInfo = req.getPathInfo();
final String[] parts = pathInfo.split("/");
if (parts.length != 6 && parts.length != 8) {
throw new IllegalArgumentException("The format of the URL is " + URL_FORMAT);
}
final int repoId;
final RepositoryConfiguration rc;
try {
repoId = Integer.valueOf(parts[1]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("The format of the URL is " + URL_FORMAT, e);
}
// This is necessary if we want unauthenticated users to be able to call this. *sigh*
RepoIdFetcherOperation getRepoId = new RepoIdFetcherOperation(repositoryService, repoId);
ss.withPermission(Permission.REPO_READ, "BUILD SUCCESS REPORT").call(getRepoId);
final Repository repo = getRepoId.getRepo();
rc = configurationPersistanceManager.getRepositoryConfigurationForRepository(repo);
if (repo == null) {
throw new IllegalArgumentException("Unable to get a repository for id " + repoId);
}
JobTemplate jt = jtm.fromString(rc, parts[2].toLowerCase());
if (jt == null) {
throw new IllegalArgumentException("Unable to get a valid JobTemplate from " + parts[2]);
}
final State state = BuildStatus.State.fromString(parts[3]);
if (state == null) {
throw new IllegalArgumentException("The state must be 'successful', 'failed', or 'inprogress'");
}
final long buildNumber;
try {
buildNumber = Long.parseLong(parts[4]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Unable to parse build number", e);
}
// TODO: ensure this hash actually exists?
final String buildHead = parts[5];
final String mergeHead;
final long pullRequestId;
final PullRequest pullRequest;
final String retUrl;
if (parts.length == 8 && !parts[6].isEmpty() && !parts[7].isEmpty()) {
mergeHead = parts[6];
try {
// This is a pull request, so add a comment
pullRequestId = Long.parseLong(parts[7]);
PullRequestFetcherOperation prfo = new PullRequestFetcherOperation(pullRequestService, repoId, pullRequestId);
ss.withPermission(Permission.REPO_READ, "BUILD SUCCESS REPORT").call(prfo);
pullRequest = prfo.getPullRequest();
if (pullRequest == null) {
throw new IllegalArgumentException("Unable to find pull request for repo id " + repo.getId().toString() + " pr id " + Long.toString(pullRequestId));
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Unable to parse pull request id " + parts[7], e);
}
retUrl = ub.getJenkinsTriggerUrl(repo, jt.getJobType(), buildHead, pullRequest);
} else {
mergeHead = null;
pullRequestId = 0;
pullRequest = null;
retUrl = ub.getJenkinsTriggerUrl(repo, jt.getJobType(), buildHead, null);
}
if (mergeHead == null) {
BuildStatus bs;
bs = getSuccessStatus(repo, jt, state, buildNumber, buildHead);
log.debug("Registering build status for buildHead " + buildHead + " " + bsToString(bs));
BuildStatusAddOperation bssAdder = new BuildStatusAddOperation(buildStatusService, buildHead, bs);
// Yeah, I know what you are thinking... "Admin permission? To add a build status?"
// I tried REPO_WRITE and REPO_ADMIN and neither was enough, but this worked!
ss.withPermission(Permission.SYS_ADMIN, "BUILD SUCCESS REPORT").call(bssAdder);
printOutput(req, res);
return;
}
// arg order for bools is started, success, override, failed
if (state.equals(State.SUCCESSFUL)) {
configurationPersistanceManager.setPullRequestMetadata(pullRequest, mergeHead, buildHead, null, true, null, false);
} else if (state.equals(State.INPROGRESS)) {
configurationPersistanceManager.setPullRequestMetadata(pullRequest, mergeHead, buildHead, true, false, null, null);
} else if (state.equals(State.FAILED)) {
configurationPersistanceManager.setPullRequestMetadata(pullRequest, mergeHead, buildHead, null, false, null, true);
}
// mergeHead is not null *and* pullRequest is not null if we reach
// here.
final StringBuffer sb = new StringBuffer();
final String url = getJenkinsUrl(repo, jt, buildNumber);
/* NOTE: mergeHead and buildHead are the reverse of what you might
* think, because we have to check out the "toRef" becasue it is
* the ref that is guaranteed to be in the correct repo.
* Nonetheless, buildHead is the commit that is being merged "into"
* the target branch, which is the mergeHead variable here.
*/
final int hashLength = 4;
final String shortMergeHead = mergeHead.substring(0, hashLength);
final String shortBuildHead = buildHead.substring(0, hashLength);
final String mergeHeadUrl = ub.buildStashCommitUrl(repo, mergeHead);
final String buildHeadUrl = ub.buildStashCommitUrl(repo, buildHead);
final String mergeHeadLink = "[" + shortMergeHead + "](" + mergeHeadUrl + ")";
final String buildHeadLink = "[" + shortBuildHead + "](" + buildHeadUrl + ")";
final String consoleUrl = url + "/console";
sb.append("*[Build #" + buildNumber + "](" + url + ") ");
sb.append("(merging " + mergeHeadLink + " into " + buildHeadLink + ") ");
switch(state) {
case INPROGRESS:
sb.append("is in progress...*");
break;
case SUCCESSFUL:
sb.append("has **passed ✓**.*");
break;
case FAILED:
sb.append("has* **FAILED ✖**. ");
sb.append("([*Retrigger this build* ⟳](" + retUrl + ") *or* [*view console output* ☰](" + consoleUrl + ").)");
break;
}
log.debug("Registering comment on pr for buildHead " + buildHead + " mergeHead " + mergeHead);
// Still make comment so users can see links to build
PullRequestCommentAddOperation prcao = new PullRequestCommentAddOperation(pullRequestService, repo.getId(), pullRequest.getId(), sb.toString());
// So in order to create comments, we have to do it AS some user. ss.doAsUser rather than ss.doWithPermission is the magic sauce here.
JenkinsServerConfiguration jsc = configurationPersistanceManager.getJenkinsServerConfiguration(rc.getJenkinsServerName());
StashUser user = us.findUserByNameOrEmail(jsc.getStashUsername());
ss.impersonating(user, "BUILD SUCCESS REPORT").call(prcao);
printOutput(req, res);
} catch (SQLException e) {
throw new RuntimeException("Unable to get configuration", e);
} catch (Exception e) {
throw new RuntimeException("Unable to report build status", e);
}
}
use of com.palantir.stash.stashbot.persistence.RepositoryConfiguration in project stashbot by palantir.
the class ConfigurationV2UpgradeTask method upgrade.
/* This is safe to do because the ao.migrate() API uses varargs with a templated type
* and there's no way around that, but it's just classes and how the API works, so I
* think it's safe.
*/
@SuppressWarnings("unchecked")
@Override
public void upgrade(ModelVersion currentVersion, ActiveObjects ao) {
if (!currentVersion.isSame(ModelVersion.valueOf("1"))) {
throw new IllegalStateException("ConfigurationV2UpgradeTask can only upgrade from version 1");
}
// Migrate the old table to base the info off it.
ao.migrate(RepositoryConfiguration.class);
// Migrate the new table so we can populate it
ao.migrate(JobTypeStatusMapping.class);
for (RepositoryConfiguration rc : ao.find(RepositoryConfiguration.class)) {
for (JobType jt : ImmutableList.of(JobType.PUBLISH, JobType.VERIFY_COMMIT, JobType.VERIFY_PR)) {
ao.create(JobTypeStatusMapping.class, new DBParam("REPO_CONFIG_ID", rc.getID()), new DBParam("IS_ENABLED", true), new DBParam("JOB_TYPE_RAW", jt.name()));
}
}
}
use of com.palantir.stash.stashbot.persistence.RepositoryConfiguration in project stashbot by palantir.
the class ConfigurationTest method storesRepoData.
@Test
public void storesRepoData() throws Exception {
Repository repo = Mockito.mock(Repository.class);
Mockito.when(repo.getId()).thenReturn(1);
Mockito.when(repo.getName()).thenReturn("repoName");
int size = ao.count(RepositoryConfiguration.class);
cpm.setRepositoryConfigurationForRepository(repo, true, "verifyBranchRegex", "verifyBuildCommand", false, "N/A", "publishBranchRegex", "publishBuildCommand", false, "N/A", "prebuildCommand", "default", true, false, "N/A", false, "N/A", size, new EmailSettings(true, "a@a.a", true, true, true), false, false);
RepositoryConfiguration rc = cpm.getRepositoryConfigurationForRepository(repo);
Assert.assertEquals("publishBranchRegex", rc.getPublishBranchRegex());
Assert.assertEquals("publishBuildCommand", rc.getPublishBuildCommand());
Assert.assertEquals("verifyBranchRegex", rc.getVerifyBranchRegex());
Assert.assertEquals("verifyBuildCommand", rc.getVerifyBuildCommand());
Assert.assertEquals("prebuildCommand", rc.getPrebuildCommand());
Assert.assertEquals("default", rc.getJenkinsServerName());
Assert.assertTrue(rc.getCiEnabled());
Assert.assertTrue(rc.getEmailNotificationsEnabled());
Assert.assertFalse(rc.getPreserveJenkinsJobConfig());
Assert.assertEquals(size + 1, ao.count(RepositoryConfiguration.class));
}
use of com.palantir.stash.stashbot.persistence.RepositoryConfiguration in project stashbot by palantir.
the class PullRequestBuildSuccessMergeCheck method check.
@Override
public void check(@Nonnull MergeRequest mr) {
PullRequest pr = mr.getPullRequest();
Repository repo = pr.getToRef().getRepository();
RepositoryConfiguration rc;
try {
rc = cpm.getRepositoryConfigurationForRepository(repo);
} catch (SQLException e) {
throw new RuntimeException("Unable to get RepositoryConfiguration", e);
}
if (!rc.getCiEnabled()) {
return;
}
if (!cpm.getJobTypeStatusMapping(rc, JobType.VERIFY_PR)) {
// speculative merge builds are disabled
return;
}
if (!pr.getToRef().getId().matches(rc.getVerifyBranchRegex())) {
log.debug("Pull Request " + pr.toString() + " ignored, branch " + pr.getToRef().getId() + " doesn't match verify regex");
return;
}
// First, if strict mode is on, we want to veto for each commit in the PR that is missing a successful verify build
if (rc.getStrictVerifyMode()) {
ChangesetsBetweenRequest cbr = new ChangesetsBetweenRequest.Builder(pr).build();
PageRequest pageReq = new PageRequestImpl(0, 500);
Page<? extends Changeset> page = cs.getChangesetsBetween(cbr, pageReq);
while (true) {
for (Changeset c : page.getValues()) {
log.trace("Processing commit " + c.getId());
BuildStats bs = bss.getStats(c.getId());
if (bs.getSuccessfulCount() == 0) {
mr.veto("Commit " + c.getId() + " not verified", "When in strict verification mode, each commit in the PR must have at least one successful build");
}
}
if (page.getIsLastPage()) {
break;
}
pageReq = page.getNextPageRequest();
page = cs.getChangesetsBetween(cbr, pageReq);
}
}
PullRequestMetadata prm = null;
if (!rc.getRebuildOnTargetUpdate()) {
// we want a PRM which simply matches the fromSha and the pull request ID.
Collection<PullRequestMetadata> prms = cpm.getPullRequestMetadataWithoutToRef(pr);
for (PullRequestMetadata cur : prms) {
if (cur.getFromSha().equals(pr.getFromRef().getLatestChangeset()) && (cur.getOverride() || cur.getSuccess())) {
log.debug("Found match PRM");
log.debug("PRM: success " + cur.getSuccess().toString() + " override " + cur.getOverride().toString());
return;
}
}
prm = cpm.getPullRequestMetadata(pr);
} else {
// Then we want to ensure a build that matches exactly succeeded / was overridden
prm = cpm.getPullRequestMetadata(pr);
}
// Override || Success
if (prm.getOverride() || prm.getSuccess()) {
return;
}
// in all other cases, we want to veto for some reason - but figure out the most accurate reason here.
MergeCheckStatus status;
if (prm.getFailed()) {
status = MergeCheckStatus.BUILD_FAILED;
} else if (prm.getBuildStarted()) {
status = MergeCheckStatus.BUILD_IN_PROGRESS;
} else {
status = MergeCheckStatus.NO_BUILD;
}
mr.veto(status.getSummary(), status.getDescription());
}
Aggregations