octotail


Nameoctotail JSON
Version 1.0.16 PyPI version JSON
download
home_pageNone
SummaryLive tail GitHub Actions runs on git push
upload_time2024-11-02 18:25:24
maintainerNone
docs_urlNone
authorNone
requires_python>=3.12
licenseUnlicense
keywords codecrafters git github-actions post-receive tail
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # octotail

<p>
<a href="https://pypi.org/project/octotail/"><img alt="PyPI" src="https://img.shields.io/pypi/v/octotail"></a>
<a href="https://github.com/getbettr/octotail/blob/main/UNLICENSE"><img alt="License: Unlicense" src="https://img.shields.io/badge/license-Unlicense-blue.svg"></a>
<a href="https://github.com/getbettr/octotail/actions"><img alt="Actions Status" src="https://github.com/getbettr/octotail/workflows/test/badge.svg"></a>
<a href="https://codecov.io/github/getbettr/octotail"><img alt="Code coverage" src="https://codecov.io/github/getbettr/octotail/graph/badge.svg?token=3CR6BVTC7O"/></a>
<a href="https://github.com/getbettr/octotail"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
</p>

Live tail GitHub Action runs on `git push`. It's cursed.

![](https://raw.githubusercontent.com/getbettr/octotail/a9c662e5f669c22c591d93c32cdeca68e1a05aec/var/demo.gif)

## Motivation

I *really* liked how [Codecrafters][] test runs are mirrored back right in the 
terminal when you `git push`, so I thought: "surely this is something the gh
CLI supports". [It doesn't.](https://github.com/cli/cli/issues/3484)

A couple of hours of messing with HTTPS mitm proxies, websockets, headless
browsers, you-name-it, and __octotail__ was born.

## Wait, what?!

Invoked with a `commit_sha` (and optionally `--workflow` and/or `--ref-name`), 
it polls the GitHub API for a matching, active run. 
When a job associated with the run starts, it instructs a headless 
chromium-based browser to visit the job's page.

The browser's traffic passes through a [mitmproxy][] instance that it uses 
to extract the authenticated WebSocket subscriptions for live tailing.

The WebSocket address and subscribe messages are then passed to the tailing 
workers.

The headless browser tabs are cleaned up immediately after the WebSocket
extraction, so the overhead is minimal. (well, it's still an empty browser)

## Prerequisites

- python 3.12
- a working chromium-based browser under `/usr/bin/chromium`

> [!IMPORTANT]
> Make sure `/usr/bin/chromium` points to a working chromium-based browser.
> 
> This is a good option for Arch Linux users:
> 
> ```shell
> paru ungoogled-chromium-bin
> ```

## Installation

### Via `uvx`

One can simply and hassle-free invoke `octotail` through [uvx][].

First, generate the proxy root certificate:

```shell
uvx --from=octotail octotailx generate-cert
```

Then, see [Install the generated proxy root certificate](#install-the-generated-proxy-root-certificate)
for instructions on how to install the generated certificate on your platform.

Finally, simply invoke it via:

```shell
uvx --from=octotail octotail
```

Or alias it as `octotail`:

```shell
# change .zshrc to .bashrc, config.fish, etc. if needed
echo "alias octotail='uvx --from octotail octotail'" >> ~/.zshrc
```

### Pypi package

```shell
mkdir octotail && cd octotail
python3 -mvenv .venv && source .venv/bin/activate
pip3 install octotail

# change .zshrc to .bashrc, config.fish, etc.
echo "alias octotail='$(pwd)/.venv/bin/python3 $(pwd)/.venv/bin/octotail'" >> ~/.zshrc
echo "alias octotailx='$(pwd)/.venv/bin/python3 $(pwd)/.venv/bin/octotailx'" >> ~/.zshrc
```

### Via git and make

```shell
git clone https://github.com/getbettr/octotail.git
cd octotail
make
sudo make install
```

### Post-install

> [!IMPORTANT] 
>
> Run `octotailx generate-cert` once to generate the proxy root certificate:
> 
> ```shell
> octotailx generate-cert
> ```
 
#### Install the generated proxy root certificate
 
This step is highly platform-dependent.
 
##### On Arch Linux
 
```shell
sudo trust anchor ~/.local/share/octotail/mitmproxy/mitmproxy-ca-cert.cer
```
 
#### On macOS
 
```shell
sudo security add-trusted-cert -d -p ssl -p basic \
  -k /Library/Keychains/System.keychain \
  ~/local/.share/octotail/mitmproxy/mitmproxy-ca-cert.pem
```
 
#### Others
 
Please refer to the ["Installing the mitmproxy CA certificate manually"][]
section of the mitmproxy documentation, changing `~/.mitmproxy` with 
`~/.local/share/octotail/mitmproxy` where appropriate.

## Usage

```
# octotail --help

 Usage: octotail [OPTIONS] COMMIT_SHA

 Find an active workflow run for the given COMMIT_SHA (and optionally --workflow and/or --ref-name)
 and attempt to tail its logs.
 NOTE: the COMMIT_SHA has to be of the full 40 characters length.

-- Arguments ---------------------------------------------------------------------------------------
  *    commit_sha      TEXT  Full commit SHA that triggered the workflow.
                             [required]

-- Authentication ----------------------------------------------------------------------------------
  *  --gh-pat         TEXT  GitHub personal access token. (for API auth)
                            [env var: OCTOTAIL_GH_PAT]
                            [required]
  *  --gh-user        TEXT  GitHub username. (for web auth)
                            [env var: OCTOTAIL_GH_USER]
                            [required]
  *  --gh-pass        TEXT  GitHub password. (for web auth)
                            [env var: OCTOTAIL_GH_PASS]
                            [required]
     --gh-otp         TEXT  GitHub OTP. (for web auth, if 2FA is on)
                            [env var: OCTOTAIL_GH_OTP]
                            [default: None]

-- Workflow filters --------------------------------------------------------------------------------
  --workflow  -w      TEXT       Only consider workflows with this name.
  --ref-name  -r      TEXT       Only consider workflows triggered by this ref. Example:
                                 refs/heads/main
  --repo      -R      USER/REPO  Use this GitHub repo to look for workflow runs. If unspecified,
                                 will look for a remote matching 'git@github.com:user/repo.git' in
                                 the current directory. Examples: user/repo OR org_name/repo

-- Others ------------------------------------------------------------------------------------------
  --headless    --no-headless             Run browser in headless mode.
                                          [env var: OCTOTAIL_HEADLESS]
                                          [default: headless]
  --port                         INTEGER  Port the proxy will listen on.
                                          [env var: OCTOTAIL_PROXY_PORT]
                                          [default: (random in range 8100-8500)]
  --version                               Show the version and exit.
  --help                                  Show this message and exit.

```

### Tail after push

A simple use case is tailing a workflow run right after `git push`:

If simply pushing the `HEAD` of the current branch:

```shell
git push
octotail $(git rev-parse HEAD)
```

Or if pushing to a different remote branch:

```shell
git push origin main
octotail $(git rev-parse origin/main) -r refs/heads/main
```

Or if pushing a tag:

```shell
git push origin v1.0.42
octotail $(git rev-parse v1.0.42^{commit}) -r refs/tags/v1.0.42
```

### NEW: `octotailx install-proxy-remote`

> [!TIP] 
> If you're using `uv` you can now simply run:
> 
> ```shell
> uvx --from=octotail octotailx install-proxy-remote
> ```
> 
> ..which will prompt you for setting up a proxy, post-receive-hook-enabled
> remote for your repository. Then you can simply `git push proxy` and all the
> tailing should happen automatically!
> 
> Alternatively, if you installed via one of the other methods, the script
> should be called with:
> 
> ```shell
> octotailx install-proxy-remote
> ```

### As a post-receive hook

A slightly more advanced use case that allows streaming the run outputs on
`git push` without invoking `octotail` explicitly, similar to [Codecrafters][]
test runs.

For this to work we'll need control over the remote's output, so we can't use
the GitHub remote directly. Instead, we'll use a bare repository as our `proxy`
remote and set up its post-receive hook to call `octotail`.

```shell
cd your-original-repo
export PROXY_REPO="/wherever/you/want/to/store/the/proxy-repo"

mkdir -p $PROXY_REPO
git clone --mirror "$(git remote get-url origin)" $PROXY_REPO
git remote add proxy $PROXY_REPO
# back to octotail
cd -

cp post-receive.sample $PROXY_REPO/hooks/post-receive
```

Edit `$PROXY_REPO/hooks/post-receive` and change things according to 
your setup:

- set `_GH_USER` to your GitHub username
- set `_GH_PASS_CMD` to a command that outputs the GitHub password, e.g. 
  `_GH_PASS_CMD="pass github.com"`
- _if using 2FA_ - set `_GH_OTP_CMD` to a command that outputs an OTP token 
  for the GitHub 2FA, e.g. `_GH_OTP_CMD="totp github.com"`
- set `_GH_PAT_CMD` to a command that outputs your GitHub personal access token,
  e.g. `_GH_PAT_CMD="pass github_pat"`

> [!NOTE]
> The hook assumes one is using `zsh`. The shebang can be changed to 
> any other shell, but it's best to invoke it with the right flags to get an 
> interactive, login shell. Useful to get access to custom functions and aliases.

That's it! (phew) - now try pushing to the `proxy` remote and check
if the GitHub Actions workflow run logs are streaming right back:

```shell
cd your-original-repo
git commit --allow-empty -m 'test octotail'
git push proxy
```

[Codecrafters]: https://codecrafters.io/
[mitmproxy]: https://mitmproxy.org/
[uvx]: https://github.com/astral-sh/uv
["Installing the mitmproxy CA certificate manually"]: https://docs.mitmproxy.org/stable/concepts-certificates/#installing-the-mitmproxy-ca-certificate-manually

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "octotail",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": null,
    "keywords": "codecrafters, git, github-actions, post-receive, tail",
    "author": null,
    "author_email": "Rare\u0219 Cosma <rares@getbetter.ro>",
    "download_url": "https://files.pythonhosted.org/packages/73/93/7f4ac3cf6ddb3a0ecc4c3099ce1588fd8152dea3a51fb6abd561de68a2db/octotail-1.0.16.tar.gz",
    "platform": null,
    "description": "# octotail\n\n<p>\n<a href=\"https://pypi.org/project/octotail/\"><img alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/octotail\"></a>\n<a href=\"https://github.com/getbettr/octotail/blob/main/UNLICENSE\"><img alt=\"License: Unlicense\" src=\"https://img.shields.io/badge/license-Unlicense-blue.svg\"></a>\n<a href=\"https://github.com/getbettr/octotail/actions\"><img alt=\"Actions Status\" src=\"https://github.com/getbettr/octotail/workflows/test/badge.svg\"></a>\n<a href=\"https://codecov.io/github/getbettr/octotail\"><img alt=\"Code coverage\" src=\"https://codecov.io/github/getbettr/octotail/graph/badge.svg?token=3CR6BVTC7O\"/></a>\n<a href=\"https://github.com/getbettr/octotail\"><img alt=\"Code style: black\" src=\"https://img.shields.io/badge/code%20style-black-000000.svg\"></a>\n</p>\n\nLive tail GitHub Action runs on `git push`. It's cursed.\n\n![](https://raw.githubusercontent.com/getbettr/octotail/a9c662e5f669c22c591d93c32cdeca68e1a05aec/var/demo.gif)\n\n## Motivation\n\nI *really* liked how [Codecrafters][] test runs are mirrored back right in the \nterminal when you `git push`, so I thought: \"surely this is something the gh\nCLI supports\". [It doesn't.](https://github.com/cli/cli/issues/3484)\n\nA couple of hours of messing with HTTPS mitm proxies, websockets, headless\nbrowsers, you-name-it, and __octotail__ was born.\n\n## Wait, what?!\n\nInvoked with a `commit_sha` (and optionally `--workflow` and/or `--ref-name`), \nit polls the GitHub API for a matching, active run. \nWhen a job associated with the run starts, it instructs a headless \nchromium-based browser to visit the job's page.\n\nThe browser's traffic passes through a [mitmproxy][] instance that it uses \nto extract the authenticated WebSocket subscriptions for live tailing.\n\nThe WebSocket address and subscribe messages are then passed to the tailing \nworkers.\n\nThe headless browser tabs are cleaned up immediately after the WebSocket\nextraction, so the overhead is minimal. (well, it's still an empty browser)\n\n## Prerequisites\n\n- python 3.12\n- a working chromium-based browser under `/usr/bin/chromium`\n\n> [!IMPORTANT]\n> Make sure `/usr/bin/chromium` points to a working chromium-based browser.\n> \n> This is a good option for Arch Linux users:\n> \n> ```shell\n> paru ungoogled-chromium-bin\n> ```\n\n## Installation\n\n### Via `uvx`\n\nOne can simply and hassle-free invoke `octotail` through [uvx][].\n\nFirst, generate the proxy root certificate:\n\n```shell\nuvx --from=octotail octotailx generate-cert\n```\n\nThen, see [Install the generated proxy root certificate](#install-the-generated-proxy-root-certificate)\nfor instructions on how to install the generated certificate on your platform.\n\nFinally, simply invoke it via:\n\n```shell\nuvx --from=octotail octotail\n```\n\nOr alias it as `octotail`:\n\n```shell\n# change .zshrc to .bashrc, config.fish, etc. if needed\necho \"alias octotail='uvx --from octotail octotail'\" >> ~/.zshrc\n```\n\n### Pypi package\n\n```shell\nmkdir octotail && cd octotail\npython3 -mvenv .venv && source .venv/bin/activate\npip3 install octotail\n\n# change .zshrc to .bashrc, config.fish, etc.\necho \"alias octotail='$(pwd)/.venv/bin/python3 $(pwd)/.venv/bin/octotail'\" >> ~/.zshrc\necho \"alias octotailx='$(pwd)/.venv/bin/python3 $(pwd)/.venv/bin/octotailx'\" >> ~/.zshrc\n```\n\n### Via git and make\n\n```shell\ngit clone https://github.com/getbettr/octotail.git\ncd octotail\nmake\nsudo make install\n```\n\n### Post-install\n\n> [!IMPORTANT] \n>\n> Run `octotailx generate-cert` once to generate the proxy root certificate:\n> \n> ```shell\n> octotailx generate-cert\n> ```\n \n#### Install the generated proxy root certificate\n \nThis step is highly platform-dependent.\n \n##### On Arch Linux\n \n```shell\nsudo trust anchor ~/.local/share/octotail/mitmproxy/mitmproxy-ca-cert.cer\n```\n \n#### On macOS\n \n```shell\nsudo security add-trusted-cert -d -p ssl -p basic \\\n  -k /Library/Keychains/System.keychain \\\n  ~/local/.share/octotail/mitmproxy/mitmproxy-ca-cert.pem\n```\n \n#### Others\n \nPlease refer to the [\"Installing the mitmproxy CA certificate manually\"][]\nsection of the mitmproxy documentation, changing `~/.mitmproxy` with \n`~/.local/share/octotail/mitmproxy` where appropriate.\n\n## Usage\n\n```\n# octotail --help\n\n Usage: octotail [OPTIONS] COMMIT_SHA\n\n Find an active workflow run for the given COMMIT_SHA (and optionally --workflow and/or --ref-name)\n and attempt to tail its logs.\n NOTE: the COMMIT_SHA has to be of the full 40 characters length.\n\n-- Arguments ---------------------------------------------------------------------------------------\n  *    commit_sha      TEXT  Full commit SHA that triggered the workflow.\n                             [required]\n\n-- Authentication ----------------------------------------------------------------------------------\n  *  --gh-pat         TEXT  GitHub personal access token. (for API auth)\n                            [env var: OCTOTAIL_GH_PAT]\n                            [required]\n  *  --gh-user        TEXT  GitHub username. (for web auth)\n                            [env var: OCTOTAIL_GH_USER]\n                            [required]\n  *  --gh-pass        TEXT  GitHub password. (for web auth)\n                            [env var: OCTOTAIL_GH_PASS]\n                            [required]\n     --gh-otp         TEXT  GitHub OTP. (for web auth, if 2FA is on)\n                            [env var: OCTOTAIL_GH_OTP]\n                            [default: None]\n\n-- Workflow filters --------------------------------------------------------------------------------\n  --workflow  -w      TEXT       Only consider workflows with this name.\n  --ref-name  -r      TEXT       Only consider workflows triggered by this ref. Example:\n                                 refs/heads/main\n  --repo      -R      USER/REPO  Use this GitHub repo to look for workflow runs. If unspecified,\n                                 will look for a remote matching 'git@github.com:user/repo.git' in\n                                 the current directory. Examples: user/repo OR org_name/repo\n\n-- Others ------------------------------------------------------------------------------------------\n  --headless    --no-headless             Run browser in headless mode.\n                                          [env var: OCTOTAIL_HEADLESS]\n                                          [default: headless]\n  --port                         INTEGER  Port the proxy will listen on.\n                                          [env var: OCTOTAIL_PROXY_PORT]\n                                          [default: (random in range 8100-8500)]\n  --version                               Show the version and exit.\n  --help                                  Show this message and exit.\n\n```\n\n### Tail after push\n\nA simple use case is tailing a workflow run right after `git push`:\n\nIf simply pushing the `HEAD` of the current branch:\n\n```shell\ngit push\noctotail $(git rev-parse HEAD)\n```\n\nOr if pushing to a different remote branch:\n\n```shell\ngit push origin main\noctotail $(git rev-parse origin/main) -r refs/heads/main\n```\n\nOr if pushing a tag:\n\n```shell\ngit push origin v1.0.42\noctotail $(git rev-parse v1.0.42^{commit}) -r refs/tags/v1.0.42\n```\n\n### NEW: `octotailx install-proxy-remote`\n\n> [!TIP] \n> If you're using `uv` you can now simply run:\n> \n> ```shell\n> uvx --from=octotail octotailx install-proxy-remote\n> ```\n> \n> ..which will prompt you for setting up a proxy, post-receive-hook-enabled\n> remote for your repository. Then you can simply `git push proxy` and all the\n> tailing should happen automatically!\n> \n> Alternatively, if you installed via one of the other methods, the script\n> should be called with:\n> \n> ```shell\n> octotailx install-proxy-remote\n> ```\n\n### As a post-receive hook\n\nA slightly more advanced use case that allows streaming the run outputs on\n`git push` without invoking `octotail` explicitly, similar to [Codecrafters][]\ntest runs.\n\nFor this to work we'll need control over the remote's output, so we can't use\nthe GitHub remote directly. Instead, we'll use a bare repository as our `proxy`\nremote and set up its post-receive hook to call `octotail`.\n\n```shell\ncd your-original-repo\nexport PROXY_REPO=\"/wherever/you/want/to/store/the/proxy-repo\"\n\nmkdir -p $PROXY_REPO\ngit clone --mirror \"$(git remote get-url origin)\" $PROXY_REPO\ngit remote add proxy $PROXY_REPO\n# back to octotail\ncd -\n\ncp post-receive.sample $PROXY_REPO/hooks/post-receive\n```\n\nEdit `$PROXY_REPO/hooks/post-receive` and change things according to \nyour setup:\n\n- set `_GH_USER` to your GitHub username\n- set `_GH_PASS_CMD` to a command that outputs the GitHub password, e.g. \n  `_GH_PASS_CMD=\"pass github.com\"`\n- _if using 2FA_ - set `_GH_OTP_CMD` to a command that outputs an OTP token \n  for the GitHub 2FA, e.g. `_GH_OTP_CMD=\"totp github.com\"`\n- set `_GH_PAT_CMD` to a command that outputs your GitHub personal access token,\n  e.g. `_GH_PAT_CMD=\"pass github_pat\"`\n\n> [!NOTE]\n> The hook assumes one is using `zsh`. The shebang can be changed to \n> any other shell, but it's best to invoke it with the right flags to get an \n> interactive, login shell. Useful to get access to custom functions and aliases.\n\nThat's it! (phew) - now try pushing to the `proxy` remote and check\nif the GitHub Actions workflow run logs are streaming right back:\n\n```shell\ncd your-original-repo\ngit commit --allow-empty -m 'test octotail'\ngit push proxy\n```\n\n[Codecrafters]: https://codecrafters.io/\n[mitmproxy]: https://mitmproxy.org/\n[uvx]: https://github.com/astral-sh/uv\n[\"Installing the mitmproxy CA certificate manually\"]: https://docs.mitmproxy.org/stable/concepts-certificates/#installing-the-mitmproxy-ca-certificate-manually\n",
    "bugtrack_url": null,
    "license": "Unlicense",
    "summary": "Live tail GitHub Actions runs on git push",
    "version": "1.0.16",
    "project_urls": {
        "Documentation": "https://github.com/getbettr/octotail#readme",
        "Homepage": "https://getbetter.ro/projects/#octotail",
        "Issues": "https://github.com/getbettr/octotail/issues",
        "Repository": "https://github.com/getbettr/octotail"
    },
    "split_keywords": [
        "codecrafters",
        " git",
        " github-actions",
        " post-receive",
        " tail"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b3fe120322452e5c914a026abdd059319e6fef966bca22c3666d2124d1ac96cf",
                "md5": "e44f596b09b6d992438f8e8179c36e01",
                "sha256": "b53eab92da8697ee080d53e209eecc1da25ce256cd545705829a9828e42afe22"
            },
            "downloads": -1,
            "filename": "octotail-1.0.16-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e44f596b09b6d992438f8e8179c36e01",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 24754,
            "upload_time": "2024-11-02T18:25:22",
            "upload_time_iso_8601": "2024-11-02T18:25:22.939953Z",
            "url": "https://files.pythonhosted.org/packages/b3/fe/120322452e5c914a026abdd059319e6fef966bca22c3666d2124d1ac96cf/octotail-1.0.16-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "73937f4ac3cf6ddb3a0ecc4c3099ce1588fd8152dea3a51fb6abd561de68a2db",
                "md5": "c6900f4cbb507162797c7a38b0779a8d",
                "sha256": "b7299384abda9ed938160c38b25341b513388f278ec08eaf5f53ed9e7cbefad7"
            },
            "downloads": -1,
            "filename": "octotail-1.0.16.tar.gz",
            "has_sig": false,
            "md5_digest": "c6900f4cbb507162797c7a38b0779a8d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 21416,
            "upload_time": "2024-11-02T18:25:24",
            "upload_time_iso_8601": "2024-11-02T18:25:24.479842Z",
            "url": "https://files.pythonhosted.org/packages/73/93/7f4ac3cf6ddb3a0ecc4c3099ce1588fd8152dea3a51fb6abd561de68a2db/octotail-1.0.16.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-02 18:25:24",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "getbettr",
    "github_project": "octotail#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "octotail"
}
        
Elapsed time: 0.37929s