tarantool / job-rerunner

Service which can re-run failed GitHub Actions jobs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Tarantool Workflow Re-runner

This service is written in Lua and runs on Tarantool.

It receives webhooks from all completed workflows in a certain GitHub repository. If a webhook came with check_run.conclusion == failure in the payload, the service calls GitHub API to re-run failed jobs in the workflow.

Adding repositories

To enable restarting workflows in a repository:

  1. Add a new webhook in the repository. You should have "Admin" permissions in the repository to do so.

    • Set "Payload URL" to the service endpoint.
    • Select "application/json" in the "Content type" dropdown.
    • Pick "Let me select individual events", then check "Workflow jobs" in the bottom of the page.
  2. Add the bot user to the repository with "Write" permissions. For repos in the tarantool organization, it's the @TarantoolBot.

The service will now restart all failed workflows in this repository up to three times.

Deployment

The app is deployed in Dokku using the dummy buildpack.

Dokku must have dokku-apt plugin enabled to install apt dependencies in apt-* files.

Provide the SSH key for access to Dokku in the SSH_PRIVATE_KEY secret.

Procfile contains the command for running the main process of the application.

Environment variables

Variable Default Meaning
RUN_ATTEMPTS 4 Number of attempts to run a specific workflow job.
REPO_BRANCHES master Branches that require automatic restart of workflow jobs.
GITHUB_TOKEN Access token for authentication to GitHub when using the GitHub API.
GITHUB_SIGN_KEY Secret token for generating a hash function for the checking GitHub requests.

Updating GitHub token and GitHub Sign Key

To update the token or change the user which will restart workflows:

  1. Issue a new token with scopes repo and workflow.

  2. Add the token to the service config in Dokku:

    $ ssh user@dokku-machine
    $ sudo su
    # dokku config job-re-runner
    BUILDPACK_URL:            https://github.com/maximkulkin/heroku-buildpack-dummy.git
    DOKKU_APP_RESTORE:        1
    DOKKU_APP_TYPE:           herokuish
    DOKKU_LETSENCRYPT_EMAIL:  admin@org
    DOKKU_PROXY_PORT:         80
    DOKKU_PROXY_PORT_MAP:     http:80:5000 https:443:5000
    DOKKU_PROXY_SSL_PORT:     443
    GITHUB_TOKEN:             <old_token_value>
    GITHUB_SIGN_KEY:          <old_github_signature>
    GIT_REV:                  e4da2ec3daf9366236da98b5ba06ecbe4d645365
    
    # dokku config:set job-re-runner GITHUB_TOKEN=<value>
    -----> Setting config vars
       GITHUB_TOKEN:  <value>
    -----> Restarting app job-re-runner
    ...
    
    # dokku config:set job-re-runner GITHUB_SIGN_KEY=<value>
    -----> Setting config vars
       GITHUB_SIGN_KEY:  <value>
    -----> Restarting app job-re-runner
    ...

    Dokku will now deploy a new service instance with the new configuration. There shouldn't be any downtime in the deployment.

Logging

To read service logs, SSH to the Dokku machine:

$ ssh user@dokku-machine
$ sudo su
# dokku logs job-re-runner --tail

Payload example

Job `osx_11_lto` failed:
{
   "workflow_job": {
      "head_sha": "0d5ec357fb8684bd26a74f0b8b3a5020768688eb",
      "id": 11786061154,
      "head_branch": "master",
      "node_id": "CR_kwDOH2Isjs8AAAACvoEFYg",
      "workflow_name": "Deploy branch",
      "url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/actions\/jobs\/11786061154",
      "run_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/actions\/runs\/4341897961",
      "run_id": 4341897961,
      "status": "in_progress",
      "conclusion": null,
      "runner_group_id": 1,
      "completed_at": null,
      "runner_name": "ghacts-shared-1-2-n1",
      "created_at": "2023-03-06T09:30:16Z",
      "steps": [
         {
            "number": 1,
            "conclusion": null,
            "status": "in_progress",
            "completed_at": null,
            "name": "Set up job",
            "started_at": "2023-03-06T09:30:19.000Z"
         }
      ],
      "run_attempt": 1,
      "runner_id": 232,
      "check_run_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/check-runs\/11786061154",
      "labels": [
         "self-hosted",
         "Linux",
         "flavor-1-2"
      ],
      "runner_group_name": "Default",
      "started_at": "2023-03-06T09:30:20Z",
      "name": "other-job (5, 1)",
      "html_url": "https:\/\/github.com\/tarantool\/devx-team-sandbox\/actions\/runs\/4341897961\/jobs\/7582057694"
   },
   "sender": {
      "login": "lastoCHka42",
      "events_url": "https:\/\/api.github.com\/users\/lastoCHka42\/events{\/privacy}",
      "gists_url": "https:\/\/api.github.com\/users\/lastoCHka42\/gists{\/gist_id}",
      "repos_url": "https:\/\/api.github.com\/users\/lastoCHka42\/repos",
      "node_id": "MDQ6VXNlcjg4NzQ2Nzkw",
      "gravatar_id": "",
      "received_events_url": "https:\/\/api.github.com\/users\/lastoCHka42\/received_events",
      "starred_url": "https:\/\/api.github.com\/users\/lastoCHka42\/starred{\/owner}{\/repo}",
      "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/88746790?v=4",
      "site_admin": false,
      "type": "User",
      "html_url": "https:\/\/github.com\/lastoCHka42",
      "id": 88746790,
      "followers_url": "https:\/\/api.github.com\/users\/lastoCHka42\/followers",
      "subscriptions_url": "https:\/\/api.github.com\/users\/lastoCHka42\/subscriptions",
      "following_url": "https:\/\/api.github.com\/users\/lastoCHka42\/following{\/other_user}",
      "url": "https:\/\/api.github.com\/users\/lastoCHka42",
      "organizations_url": "https:\/\/api.github.com\/users\/lastoCHka42\/orgs"
   },
   "action": "in_progress",
   "organization": {
      "members_url": "https:\/\/api.github.com\/orgs\/tarantool\/members{\/member}",
      "login": "tarantool",
      "issues_url": "https:\/\/api.github.com\/orgs\/tarantool\/issues",
      "events_url": "https:\/\/api.github.com\/orgs\/tarantool\/events",
      "repos_url": "https:\/\/api.github.com\/orgs\/tarantool\/repos",
      "node_id": "MDEyOk9yZ2FuaXphdGlvbjIzNDQ5MTk=",
      "public_members_url": "https:\/\/api.github.com\/orgs\/tarantool\/public_members{\/member}",
      "url": "https:\/\/api.github.com\/orgs\/tarantool",
      "hooks_url": "https:\/\/api.github.com\/orgs\/tarantool\/hooks",
      "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/2344919?v=4",
      "id": 2344919,
      "description": "In-memory computing platform with flexible data schema."
   },
   "repository": {
      "disabled": false,
      "subscribers_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/subscribers",
      "private": false,
      "notifications_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/notifications{?since,all,participating}",
      "tags_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/tags",
      "pushed_at": "2023-02-14T12:18:16Z",
      "description": "Tests and experiments in the DevX team",
      "license": null,
      "deployments_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/deployments",
      "keys_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/keys{\/key_id}",
      "comments_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/comments{\/number}",
      "language": null,
      "has_wiki": false,
      "releases_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/releases{\/id}",
      "has_downloads": true,
      "forks_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/forks",
      "has_pages": false,
      "watchers_count": 0,
      "downloads_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/downloads",
      "size": 5,
      "assignees_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/assignees{\/user}",
      "issues_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/issues{\/number}",
      "commits_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/commits{\/sha}",
      "clone_url": "https:\/\/github.com\/tarantool\/devx-team-sandbox.git",
      "owner": {
         "login": "tarantool",
         "events_url": "https:\/\/api.github.com\/users\/tarantool\/events{\/privacy}",
         "gists_url": "https:\/\/api.github.com\/users\/tarantool\/gists{\/gist_id}",
         "repos_url": "https:\/\/api.github.com\/users\/tarantool\/repos",
         "node_id": "MDEyOk9yZ2FuaXphdGlvbjIzNDQ5MTk=",
         "gravatar_id": "",
         "received_events_url": "https:\/\/api.github.com\/users\/tarantool\/received_events",
         "starred_url": "https:\/\/api.github.com\/users\/tarantool\/starred{\/owner}{\/repo}",
         "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/2344919?v=4",
         "site_admin": false,
         "type": "Organization",
         "html_url": "https:\/\/github.com\/tarantool",
         "id": 2344919,
         "followers_url": "https:\/\/api.github.com\/users\/tarantool\/followers",
         "subscriptions_url": "https:\/\/api.github.com\/users\/tarantool\/subscriptions",
         "following_url": "https:\/\/api.github.com\/users\/tarantool\/following{\/other_user}",
         "url": "https:\/\/api.github.com\/users\/tarantool",
         "organizations_url": "https:\/\/api.github.com\/users\/tarantool\/orgs"
      },
      "languages_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/languages",
      "hooks_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/hooks",
      "git_commits_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/git\/commits{\/sha}",
      "name": "devx-team-sandbox",
      "teams_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/teams",
      "updated_at": "2022-10-05T11:24:55Z",
      "issue_events_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/issues\/events{\/number}",
      "default_branch": "master",
      "events_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/events",
      "homepage": null,
      "node_id": "R_kgDOH2Isjg",
      "pulls_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/pulls{\/number}",
      "visibility": "public",
      "url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox",
      "statuses_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/statuses\/{sha}",
      "git_url": "git:\/\/github.com\/tarantool\/devx-team-sandbox.git",
      "branches_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/branches{\/branch}",
      "issue_comment_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/issues\/comments{\/number}",
      "created_at": "2022-08-19T08:39:08Z",
      "labels_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/labels{\/name}",
      "trees_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/git\/trees{\/sha}",
      "web_commit_signoff_required": false,
      "archived": false,
      "allow_forking": true,
      "topics": [
      ],
      "id": 526527630,
      "watchers": 0,
      "open_issues": 1,
      "has_issues": true,
      "forks": 0,
      "contributors_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/contributors",
      "mirror_url": null,
      "open_issues_count": 1,
      "is_template": false,
      "milestones_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/milestones{\/number}",
      "git_tags_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/git\/tags{\/sha}",
      "subscription_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/subscription",
      "forks_count": 0,
      "git_refs_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/git\/refs{\/sha}",
      "html_url": "https:\/\/github.com\/tarantool\/devx-team-sandbox",
      "svn_url": "https:\/\/github.com\/tarantool\/devx-team-sandbox",
      "contents_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/contents\/{+path}",
      "merges_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/merges",
      "stargazers_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/stargazers",
      "full_name": "tarantool\/devx-team-sandbox",
      "archive_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/{archive_format}{\/ref}",
      "fork": false,
      "collaborators_url": "https:\/\/api.github.com\/repos\/tarantool\/devx-team-sandbox\/collaborators{\/collaborator}",
      "ssh_url": "git@github.com:tarantool\/devx-team-sandbox.git",
      "stargazers_count": 0,
      "blobs_url": "https:\/\/api.github.com\/repos\/"
   }
}

Status codes

Job rerunner returns some status code while working on payload from GitHub.

Result Meaning
204 No Content Got empty payload, overall run attempts more than allowed, or user in NO_RETRY_LIST No action.
200 OK Final job was completed, but no action is required.
201 Created Final job was completed, restarting the workflow.
202 Accepted Recorded the queued or completed jobs and waiting for results.
403 Forbidden Request headers must contain X-Hub-Signature-256 with correct value.
404 Error Job action is 'completed', but there's no previous record about it.

Excluding usernames from retrying

Rerunner will not restart workflows initiated by GitHub users, specified in the environment variable NO_RETRY_LIST: NO_RETRY_LIST=username1,username2

About

Service which can re-run failed GitHub Actions jobs


Languages

Language:Lua 99.6%Language:Procfile 0.4%