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.