From 00dcabd3cd5619f5fb4d23cd07ffa807b6699387 Mon Sep 17 00:00:00 2001
From: Oleh Prypin
Date: Sun, 7 Mar 2021 23:11:25 +0100
Subject: [PATCH] Support scanning for any completed run, not only successful.
Fixes #14
---
spec/main_spec.cr | 17 +++++++++++++++++
src/github_api.cr | 5 +++--
src/nightly_link.cr | 36 +++++++++++++++++++++++++++---------
templates/controls.html | 1 +
4 files changed, 48 insertions(+), 11 deletions(-)
diff --git a/spec/main_spec.cr b/spec/main_spec.cr
index 53c8f50..6851faf 100644
--- a/spec/main_spec.cr
+++ b/spec/main_spec.cr
@@ -175,6 +175,11 @@ describe "dash_by_branch" do
assert_nofollow
end
+ test "bad request" do
+ resp, body = serve("/UserName/RepoName/workflows/SomeWorkflow/SomeBranch?status=foo")
+ assert resp.status == HTTP::Status::BAD_REQUEST
+ end
+
describe "private" do
test "without password" do
resp, body = serve("/#{PRIVATE_REPO}/workflows/SomeWorkflow/SomeBranch")
@@ -310,6 +315,18 @@ describe "by_branch" do
assert_nofollow
end
+ test "completed" do
+ WebMock.stub(:get, "https://api.github.com/repos/username/reponame/actions/workflows/SomeWorkflow.yml/runs?per_page=1&branch=SomeBranch&event=push&status=completed").to_return(
+ body: %({"workflow_runs":[
+ {"id":#{RUN_1},"event":"push","workflow_id":#{WORKFLOW_1},"check_suite_url":"https://api.github.com/repos/UserName/RepoName/check-suites/#{CHECK_SUITE_1}","updated_at":"2020-12-19T22:22:22Z","repository":{"full_name":"UserName/RepoName","private":false,"fork":false}}]}))
+ WebMock.stub(:get, "https://api.github.com/repos/username/reponame/actions/workflows/SomeWorkflow.yml/runs?per_page=1&branch=SomeBranch&event=schedule&status=completed").to_return(
+ body: %({"workflow_runs":[
+ {"id":#{RUN_2},"event":"schedule","workflow_id":#{WORKFLOW_1},"check_suite_url":"https://api.github.com/repos/UserName/RepoName/check-suites/#{CHECK_SUITE_1}","updated_at":"2021-02-07T07:15:00Z","repository":{"full_name":"UserName/RepoName","private":false,"fork":false}}]}))
+
+ resp, body = serve("/UserName/RepoName/workflows/SomeWorkflow/SomeBranch/SomeArtifact?status=completed")
+ assert_canonical "https://nightly.link/UserName/RepoName/workflows/SomeWorkflow/SomeBranch/SomeArtifact"
+ end
+
test "redirect" do
resp, body = serve("/UserName/RepoName/workflows/SomeWorkflow/SomeBranch/SomeArtifact.zip")
assert_redirect "http://example.org/download1"
diff --git a/src/github_api.cr b/src/github_api.cr
index 5cf9eae..f625e4f 100644
--- a/src/github_api.cr
+++ b/src/github_api.cr
@@ -233,13 +233,14 @@ struct WorkflowRuns
property workflow_runs : Array(WorkflowRun)
cached_array def self.for_workflow(
- repo_owner : DowncaseString, repo_name : DowncaseString, workflow : String, branch : String, event : String,
+ repo_owner : DowncaseString, repo_name : DowncaseString, workflow : String,
+ branch : String, event : String, status : String,
token : InstallationToken | UserToken, max_items : Int32, & : WorkflowRun ->
)
# https://docs.github.com/v3/actions#list-workflow-runs
get_json_list(
WorkflowRuns, "repos/#{repo_owner}/#{repo_name}/actions/workflows/#{workflow}/runs",
- params: {branch: branch, event: event, status: "success"},
+ params: {branch: branch, event: event, status: status},
headers: {Authorization: token}, max_items: max_items
)
end
diff --git a/src/nightly_link.cr b/src/nightly_link.cr
index 0212527..d872831 100644
--- a/src/nightly_link.cr
+++ b/src/nightly_link.cr
@@ -136,9 +136,12 @@ private def github_run_link(repo_owner : String, repo_name : String, run_id : In
"https://github.com" + GitHubRoutes.gen_run(repo_owner: repo_owner, repo_name: repo_name, run_id: run_id) + "#artifacts"
end
-private def github_actions_link(repo_owner : String, repo_name : String, *, event : String, branch : String) : String
+private def github_actions_link(
+ repo_owner : String, repo_name : String, *,
+ event : String, branch : String, status : String
+) : String
"https://github.com/#{repo_owner}/#{repo_name}/actions?" + HTTP::Params.encode({
- query: "event:#{event} is:success branch:#{branch}",
+ query: "event:#{event} is:#{status} branch:#{branch}",
})
end
@@ -228,7 +231,10 @@ class NightlyLink
run, artifact = @@examples_cache.fetch(example_workflow) do
token = GitHubApp.token(FALLBACK_INSTALL_ID)
- run_ = get_latest_run(args[:repo_owner], args[:repo_name], args[:workflow] + ".yml", args[:branch], token)
+ run_ = get_latest_run(
+ args[:repo_owner], args[:repo_name],
+ workflow: args[:workflow] + ".yml", branch: args[:branch], status: "success", token: token
+ )
artifact_ = Artifacts.for_run(args[:repo_owner], args[:repo_name], run_.id, token, expires_in: 3.hours).first
{run_, artifact_}
end
@@ -301,8 +307,12 @@ class NightlyLink
unless workflow.to_i64?(whitespace: false) || workflow.ends_with?(".yml") || workflow.ends_with?(".yaml")
workflow += ".yml"
end
+ status = ctx.request.query_params.fetch("status", "success")
+ if !status.in?("success", "completed")
+ raise HTTPException.new(:BadRequest, "?status must be 'success' (default) or 'completed'")
+ end
- run = get_latest_run(repo_owner, repo_name, workflow, branch, token)
+ run = get_latest_run(repo_owner, repo_name, workflow: workflow, branch: branch, status: status, token: token)
repo_owner, repo_name = run.repository.owner, run.repository.name
if run.updated_at < 90.days.ago
message = "Warning: the latest successful run is older than 90 days, and its artifacts likely expired."
@@ -384,12 +394,15 @@ class NightlyLink
ECR.embed("templates/artifact_list.html", ctx.response)
end
- private def get_latest_run(repo_owner : String, repo_name : String, workflow : String, branch : String, token : InstallationToken)
+ private def get_latest_run(
+ repo_owner : String, repo_name : String,
+ workflow : String, branch : String, status : String, token : InstallationToken
+ )
futures = [{"push", 5.minutes}, {"schedule", 1.hour}].map do |(event, expires_in)|
future do
begin
WorkflowRuns.for_workflow(
- repo_owner, repo_name, workflow, branch: branch, event: event,
+ repo_owner, repo_name, workflow, branch: branch, event: event, status: status,
token: token, max_items: 1, expires_in: expires_in
)
rescue e : Halite::Exception::ClientError
@@ -405,7 +418,7 @@ class NightlyLink
end
runs = futures.map(&.get.first?).compact
if runs.empty?
- gh_link = github_actions_link(repo_owner, repo_name, event: "push", branch: branch)
+ gh_link = github_actions_link(repo_owner, repo_name, event: "push", branch: branch, status: status)
raise HTTPException.new(:NotFound,
"No successful runs found for workflow '#{workflow}' and branch '#{branch}'.\n" +
"Check on GitHub: <#{gh_link}>"
@@ -439,13 +452,18 @@ class NightlyLink
unless workflow.to_i64?(whitespace: false) || workflow.ends_with?(".yml") || workflow.ends_with?(".yaml")
workflow += ".yml"
end
- run = get_latest_run(repo_owner, repo_name, workflow, branch, token)
+ status = ctx.request.query_params.fetch("status", "success")
+ if !status.in?("success", "completed")
+ raise HTTPException.new(:BadRequest, "?status must be 'success' (default) or 'completed'")
+ end
+
+ run = get_latest_run(repo_owner, repo_name, workflow: workflow, branch: branch, status: status, token: token)
repo_owner, repo_name = run.repository.owner, run.repository.name
links = by_run(nil, repo_owner, repo_name, run.id, artifact, run.check_suite_id, h, zip: zip)
title = {"Repository #{repo_owner}/#{repo_name}", "Workflow #{workflow} | Branch #{branch} | Artifact #{artifact}"}
links << ArtifactLink.new(
- github_actions_link(repo_owner, repo_name, event: run.event, branch: branch),
+ github_actions_link(repo_owner, repo_name, event: run.event, branch: branch, status: status),
"Browse workflow runs on branch '#{branch}'", ext: true
)
canonical = abs_url(NightlyLink.gen_by_branch(
diff --git a/templates/controls.html b/templates/controls.html
index 73afd4c..381c1a9 100644
--- a/templates/controls.html
+++ b/templates/controls.html
@@ -29,6 +29,7 @@ Paste a GitHub link, get a nightly.link!
Note that the branch which you're on also matters.
Following this form (and having selected the "<%= example_art %>" artifact), you will end up at
<%= example_dest %> [.zip]
which is a link that always downloads the latest artifact from a succeeding run on that repo+workflow+branch.
+To allow any finished workflow runs, not only successful ones, append ?status=success
to the URL.
If you have several workflows or branches, you can adapt the URL by hand in a predictable way.