|
| 1 | +module dlangbot.appveyor; |
| 2 | + |
| 3 | +string appveyorAPIURL = "https://ci.appveyor.com/api"; |
| 4 | +string appveyorAuth; |
| 5 | + |
| 6 | +import vibe.core.log; |
| 7 | + |
| 8 | +//============================================================================== |
| 9 | +// Dedup AppVeyor builds |
| 10 | +//============================================================================== |
| 11 | + |
| 12 | +// https://www.appveyor.com/docs/api/projects-builds/#cancel-build |
| 13 | +void cancelBuild(string repoSlug, size_t buildId) |
| 14 | +{ |
| 15 | + import std.format : format; |
| 16 | + import vibe.http.client : requestHTTP; |
| 17 | + import vibe.http.common : HTTPMethod; |
| 18 | + import vibe.stream.operations : readAllUTF8; |
| 19 | + |
| 20 | + auto url = "%s/builds/%s/%s/cancel".format(appveyorAPIURL, repoSlug, buildId); |
| 21 | + requestHTTP(url, (scope req) { |
| 22 | + req.headers["Authorization"] = appveyorAuth; |
| 23 | + req.method = HTTPMethod.DELETE; |
| 24 | + }, (scope res) { |
| 25 | + if (res.statusCode / 100 == 2) |
| 26 | + logInfo("[appveyor/%s]: Canceled Build %s\n", repoSlug, buildId); |
| 27 | + else |
| 28 | + logWarn("[appveyor/%s]: POST %s failed; %s %s.\n%s", repoSlug, url, res.statusPhrase, |
| 29 | + res.statusCode, res.bodyReader.readAllUTF8); |
| 30 | + }); |
| 31 | +} |
| 32 | + |
| 33 | +// https://www.appveyor.com/docs/api/projects-builds/#get-project-history |
| 34 | +void dedupAppVeyorBuilds(string action, string repoSlug, uint pullRequestNumber) |
| 35 | +{ |
| 36 | + import std.algorithm.iteration : filter; |
| 37 | + import std.conv : to; |
| 38 | + import std.format : format; |
| 39 | + import std.range : drop; |
| 40 | + import vibe.data.json : Json; |
| 41 | + import vibe.http.client : requestHTTP; |
| 42 | + |
| 43 | + if (action != "synchronize" && action != "merged") |
| 44 | + return; |
| 45 | + |
| 46 | + static bool activeState(string state) |
| 47 | + { |
| 48 | + import std.algorithm.comparison : among; |
| 49 | + return state.among("created", "queued", "started") > 0; |
| 50 | + } |
| 51 | + // GET /api/projects/{accountName}/{projectSlug}/history?recordsNumber={records-per-page}[&startBuildId={buildId}&branch={branch}] |
| 52 | + |
| 53 | + auto url = "%s/projects/%s/history?recordsNumber=100".format(appveyorAPIURL, repoSlug); |
| 54 | + auto activeBuildsForPR = requestHTTP(url, (scope req) { |
| 55 | + req.headers["Authorization"] = appveyorAuth; |
| 56 | + }) |
| 57 | + .readJson["builds"][] |
| 58 | + .filter!(b => "pullRequestId" in b && !b["pullRequestId"].type != Json.undefined) |
| 59 | + .filter!(b => activeState(b["status"].get!string)) |
| 60 | + .filter!(b => b["pullRequestId"].get!string.to!uint == pullRequestNumber); |
| 61 | + |
| 62 | + // Keep only the most recent build for this PR. Kill all builds |
| 63 | + // when it got merged as it'll be retested after the merge anyhow. |
| 64 | + foreach (b; activeBuildsForPR.drop(action == "merged" ? 0 : 1)) |
| 65 | + cancelBuild(repoSlug, b["buildId"].get!size_t); |
| 66 | +} |
| 67 | + |
0 commit comments