Skip to content

Commit

Permalink
Support scanning for any completed run, not only successful.
Browse files Browse the repository at this point in the history
Fixes #14
  • Loading branch information
oprypin committed Mar 7, 2021
1 parent e484830 commit 00dcabd
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 11 deletions.
17 changes: 17 additions & 0 deletions spec/main_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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"
Expand Down
5 changes: 3 additions & 2 deletions src/github_api.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 27 additions & 9 deletions src/nightly_link.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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."
Expand Down Expand Up @@ -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
Expand All @@ -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}>"
Expand Down Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions templates/controls.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ <h2>Paste a GitHub link, get a nightly.link!</h2>
Note that the <i>branch</i> which you're on also matters.</p>
<p>Following this form (and having selected the "<%= example_art %>" artifact), you will end up at<br><a rel="nofollow" href="<%= example_dest %>"><%= example_dest %></a> [<a rel="nofollow" href="<%= example_dest %>.zip">.zip</a>]<br>
which is a link that always downloads the latest artifact from a <u>succeeding</u> run on that <u>repo</u>+<u>workflow</u>+<u>branch</u>.</p>
<p>To allow <u>any finished</u> workflow runs, not only successful ones, append <code>?status=success</code> to the URL.
<p>If you have several workflows or branches, you can adapt the URL by hand in a predictable way.</p>
</details>

Expand Down

0 comments on commit 00dcabd

Please sign in to comment.