{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Quick tips and learnings Archive",
  "home_page_url": "https://pype.dev/til/",
  "feed_url": "https://pype.dev/til/archive/feed.json",
  "description": "My thoughts and streams of consciousness organized into barely coherent posts about things",
  "authors": [
    {
      "name": "Nic Payne"
    }
  ],
  "items": [
    {
      "id": "https://pype.dev/agents-can-miss-obvious-things-too/",
      "url": "https://pype.dev/agents-can-miss-obvious-things-too/",
      "title": "Agents can miss obvious things too",
      "content_html": "\u003cp\u003eI am working with MiniMax M.2 to vibe up some webapp ideas for \u003ca href=\"/nexus/\" class=\"wikilink\" data-title=\"Nexus\" data-description=\"Nexus is a task tracker and second-brain for collaborating with AI agents on software development. It keeps project boards, task details, subtasks, daily...\" data-date=\"2026-02-05\" data-preview=\"Nexus is a task tracker and second-brain for collaborating with AI agents on software development. It keeps project boards, task details, subtasks, daily...\"\u003eNexus\u003c/a\u003e and I\njust spent many minutes not having the app render correctly because the agent\nmounted the wrong directory into the \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e compose stack. I noticed it about 5\nseconds after opening the file, but wasted nearly a half hour thinking it was a\ndocker issue in distrobox\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"admonition danger\"\u003e\n\u003cp class=\"admonition-title\"\u003eDanger\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/clankers/\" class=\"glossary-term\" title=\"An appropriately derogatory reference to AI agents\" data-hover-title=\"Clankers\"\u003eClankers\u003c/a\u003e are still just clankers man\u003c/p\u003e\n\u003c/div\u003e\n",
      "content_text": "\nI am working with MiniMax M.2 to vibe up some webapp ideas for \u003ca href=\"/nexus/\" class=\"wikilink\" data-title=\"Nexus\" data-description=\"Nexus is a task tracker and second-brain for collaborating with AI agents on software development. It keeps project boards, task details, subtasks, daily...\" data-date=\"2026-02-05\"\u003eNexus\u003c/a\u003e and I\njust spent many minutes not having the app render correctly because the agent\nmounted the wrong directory into the docker compose stack. I noticed it about 5\nseconds after opening the file, but wasted nearly a half hour thinking it was a\ndocker issue in distrobox...\n\n!!! danger \"\"\n\n    Clankers are still just clankers man\n",
      "summary": "I am working with MiniMax M.2 to vibe up some webapp ideas for Nexus and I just spent many minutes not having the app render correctly because the agent...",
      "date_published": "2026-04-03T14:39:51Z",
      "date_modified": "2026-04-03T14:39:51Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "agents",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/prettier-docker-ps-command/",
      "url": "https://pype.dev/prettier-docker-ps-command/",
      "title": "Prettier Docker ‘ps’ Command",
      "content_html": "\u003cp\u003eThe \u003ccode\u003edocker ps\u003c/code\u003e command is very useful, but I hate reading the output. Turns\nout, you can make it prettier:\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edocker ps\u003c/code\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u0026ndash;format \u0026ldquo;table\u0026rdquo; is implied with the command.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cpre\u003e\u003ccode\u003e\n❯ docker ps --format \u0026quot;table\u0026quot; | grep can\n9b716a7d1ab0   postgres:17                                               \u0026quot;docker-entrypoint.s…\u0026quot;   13 hours ago    Up 13 hours (healthy)            0.0.0.0:5432-\u0026gt;5432/tcp, [::]:5432-\u0026gt;5432/tcp                                                cannalyzer-db-1\nf7708ea0c112   adminer                                                   \u0026quot;entrypoint.sh docke…\u0026quot;   13 hours ago    Up 13 hours                      0.0.0.0:8081-\u0026gt;8080/tcp, [::]:8081-\u0026gt;8080/tcp                                                cannalyzer-adminer-1\n93cd67719ed4   frontend:latest                                           \u0026quot;/docker-entrypoint.…\u0026quot;   13 hours ago    Up 13 hours                      0.0.0.0:5173-\u0026gt;80/tcp, [::]:5173-\u0026gt;80/tcp                                                    cannalyzer-frontend-1\nf517625fca98   traefik:3.0                                               \u0026quot;/entrypoint.sh --pr…\u0026quot;   13 hours ago    Up 13 hours                      0.0.0.0:80-\u0026gt;80/tcp, [::]:80-\u0026gt;80/tcp, 0.0.0.0:8090-\u0026gt;8080/tcp, [::]:8090-\u0026gt;8080/tcp           cannalyzer-proxy-1\nf4daa036216e   schickling/mailcatcher                                    \u0026quot;sh -c 'mailcatcher …\u0026quot;   13 hours ago    Up 13 hours                      0.0.0.0:1025-\u0026gt;1025/tcp, [::]:1025-\u0026gt;1025/tcp, 0.0.0.0:1080-\u0026gt;1080/tcp, [::]:1080-\u0026gt;1080/tcp   cannalyzer-mailcatcher-1\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eBut you can pass a template string to the \u003ccode\u003e--format\u003c/code\u003e option, like so:\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edocker ps --format \u0026quot;table {{.Names}}\u0026quot;\u003c/code\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\n✗ docker ps --format \u0026quot;table {{.Names}}\u0026quot; | grep can\ncannalyzer-db-1\ncannalyzer-adminer-1\ncannalyzer-frontend-1\ncannalyzer-proxy-1\ncannalyzer-mailcatcher-1\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003edocker ps --format \u0026quot;table {{.Names}}\\t{{.Status}}\\t{{.Ports}}\u0026quot;\u003c/code\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\n❯ docker ps --format \u0026quot;table {{.Names}}\\t{{.Ports}}\u0026quot; | grep can\ncannalyzer-db-1             0.0.0.0:5432-\u0026gt;5432/tcp, [::]:5432-\u0026gt;5432/tcp\ncannalyzer-adminer-1        0.0.0.0:8081-\u0026gt;8080/tcp, [::]:8081-\u0026gt;8080/tcp\ncannalyzer-frontend-1       0.0.0.0:5173-\u0026gt;80/tcp, [::]:5173-\u0026gt;80/tcp\ncannalyzer-proxy-1          0.0.0.0:80-\u0026gt;80/tcp, [::]:80-\u0026gt;80/tcp, 0.0.0.0:8090-\u0026gt;8080/tcp, [::]:8090-\u0026gt;8080/tcp\ncannalyzer-mailcatcher-1    0.0.0.0:1025-\u0026gt;1025/tcp, [::]:1025-\u0026gt;1025/tcp, 0.0.0.0:1080-\u0026gt;1080/tcp, [::]:1080-\u0026gt;1080/tcp\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"picture\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003ePicture\u003c/span\u003e \u003ca href=\"#picture\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI noticed my template isn\u0026rsquo;t folding the codeblocks in a way that actually makes this post look like I\u0026rsquo;m lying!\u003c/p\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251229122717_67e76a67.png\" alt=\"20251229122717_67e76a67.png\"\u003e\n\u003c/figure\u003e\n",
      "content_text": "\nThe `docker ps` command is very useful, but I hate reading the output. Turns\nout, you can make it prettier:\n\n`docker ps`\n\n\u003e --format \"table\" is implied with the command.\n\n```\n\n❯ docker ps --format \"table\" | grep can\n9b716a7d1ab0   postgres:17                                               \"docker-entrypoint.s…\"   13 hours ago    Up 13 hours (healthy)            0.0.0.0:5432-\u003e5432/tcp, [::]:5432-\u003e5432/tcp                                                cannalyzer-db-1\nf7708ea0c112   adminer                                                   \"entrypoint.sh docke…\"   13 hours ago    Up 13 hours                      0.0.0.0:8081-\u003e8080/tcp, [::]:8081-\u003e8080/tcp                                                cannalyzer-adminer-1\n93cd67719ed4   frontend:latest                                           \"/docker-entrypoint.…\"   13 hours ago    Up 13 hours                      0.0.0.0:5173-\u003e80/tcp, [::]:5173-\u003e80/tcp                                                    cannalyzer-frontend-1\nf517625fca98   traefik:3.0                                               \"/entrypoint.sh --pr…\"   13 hours ago    Up 13 hours                      0.0.0.0:80-\u003e80/tcp, [::]:80-\u003e80/tcp, 0.0.0.0:8090-\u003e8080/tcp, [::]:8090-\u003e8080/tcp           cannalyzer-proxy-1\nf4daa036216e   schickling/mailcatcher                                    \"sh -c 'mailcatcher …\"   13 hours ago    Up 13 hours                      0.0.0.0:1025-\u003e1025/tcp, [::]:1025-\u003e1025/tcp, 0.0.0.0:1080-\u003e1080/tcp, [::]:1080-\u003e1080/tcp   cannalyzer-mailcatcher-1\n```\n\nBut you can pass a template string to the `--format` option, like so:\n\n`docker ps --format \"table {{.Names}}\"`\n\n```\n\n✗ docker ps --format \"table {{.Names}}\" | grep can\ncannalyzer-db-1\ncannalyzer-adminer-1\ncannalyzer-frontend-1\ncannalyzer-proxy-1\ncannalyzer-mailcatcher-1\n```\n\n`docker ps --format \"table {{.Names}}\\t{{.Status}}\\t{{.Ports}}\"`\n\n```\n\n❯ docker ps --format \"table {{.Names}}\\t{{.Ports}}\" | grep can\ncannalyzer-db-1             0.0.0.0:5432-\u003e5432/tcp, [::]:5432-\u003e5432/tcp\ncannalyzer-adminer-1        0.0.0.0:8081-\u003e8080/tcp, [::]:8081-\u003e8080/tcp\ncannalyzer-frontend-1       0.0.0.0:5173-\u003e80/tcp, [::]:5173-\u003e80/tcp\ncannalyzer-proxy-1          0.0.0.0:80-\u003e80/tcp, [::]:80-\u003e80/tcp, 0.0.0.0:8090-\u003e8080/tcp, [::]:8090-\u003e8080/tcp\ncannalyzer-mailcatcher-1    0.0.0.0:1025-\u003e1025/tcp, [::]:1025-\u003e1025/tcp, 0.0.0.0:1080-\u003e1080/tcp, [::]:1080-\u003e1080/tcp\n```\n\n## Picture\n\nI noticed my template isn't folding the codeblocks in a way that actually makes this post look like I'm lying!\n\n![20251229122717_67e76a67.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251229122717_67e76a67.png)\n",
      "summary": "The command is very useful, but I hate reading the output. Turns out, you can make it prettier:",
      "date_published": "2025-12-29T05:33:05Z",
      "date_modified": "2025-12-29T05:33:05Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "docker",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/columns-env-var-for-nicer-screenshots/",
      "url": "https://pype.dev/columns-env-var-for-nicer-screenshots/",
      "title": "COLUMNS Env Var For Nicer Screenshots",
      "content_html": "\u003cp\u003eI was working on \u003ca href=\"/uv-run-pep-723-is-a-match-made-in-heaven/\" class=\"wikilink\" data-title=\"UV Run + PEP 723 Is A Match Made In Heaven\" data-description=\"This post will be short but hopefully be a dense few paragraphs about how using along with PEP 723 is life-changing.\" data-date=\"2025-12-08\" data-preview=\"This post will be short but hopefully be a dense few paragraphs about how using along with PEP 723 is life-changing.\"\u003eUV Run + PEP 723 Is A Match Made In Heaven\u003c/a\u003e and I wanted to grab a screenshot of a TUI, but my terminal is full screen and the TUI was then super stretched\u0026hellip; Nice for working and using it, but not nice for a screenshot going into a blog post\u0026hellip; see the difference \u003ccode\u003eexport COLUMNS=120\u003c/code\u003e does here!\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eCOLUMNS=120 mytui.py\n\u003c/code\u003e\u003c/pre\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251208111709_aa06bd03.png\" alt=\"20251208111709_aa06bd03.png\"\u003e\n\u003c/figure\u003e\n\u003cpre\u003e\u003ccode\u003emytui.py\n\u003c/code\u003e\u003c/pre\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251208111820_a209638f.png\" alt=\"20251208111820_a209638f.png\"\u003e\n\u003c/figure\u003e\n",
      "content_text": "\nI was working on \u003ca href=\"/uv-run-pep-723-is-a-match-made-in-heaven/\" class=\"wikilink\" data-title=\"UV Run + PEP 723 Is A Match Made In Heaven\" data-description=\"This post will be short but hopefully be a dense few paragraphs about how using along with PEP 723 is life-changing.\" data-date=\"2025-12-08\"\u003eUV Run + PEP 723 Is A Match Made In Heaven\u003c/a\u003e and I wanted to grab a screenshot of a TUI, but my terminal is full screen and the TUI was then super stretched... Nice for working and using it, but not nice for a screenshot going into a blog post... see the difference `export COLUMNS=120` does here!\n\n```\nCOLUMNS=120 mytui.py\n```\n\n![20251208111709_aa06bd03.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251208111709_aa06bd03.png)\n\n```\nmytui.py\n```\n\n![20251208111820_a209638f.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251208111820_a209638f.png)\n",
      "summary": "I was working on uv-run-pep-723-is-a-match-made-in-heaven and I wanted to grab a screenshot of a TUI, but my terminal is full screen and the TUI was then...",
      "date_published": "2025-12-08T05:16:55Z",
      "date_modified": "2025-12-08T05:16:55Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "terminal",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/check-swap-in-htop/",
      "url": "https://pype.dev/check-swap-in-htop/",
      "title": "Check SWAP in htop",
      "content_html": "\u003cp\u003eI struggle with maxxing swap on all my machines, I am sure it\u0026rsquo;s a neovim config\nthat I\u0026rsquo;ve propagated everywhere and I\u0026rsquo;m too lazy to figure it out. Instead I\njust swapoff and swapon \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of often\u0026hellip; but this morning I had to know how to\nsee what\u0026rsquo;s using all that swap\u0026hellip;.\u003c/p\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251115125424_a1ddbd7c.png\" alt=\"20251115125424_a1ddbd7c.png\"\u003e\n\u003c/figure\u003e\n\u003cp\u003eThe way to see SWAP usage by process in htop is:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eopen htop\u003c/li\u003e\n\u003cli\u003eSlam \u003ccode\u003eF2\u003c/code\u003e (\u003ccode\u003ef\u003c/code\u003e if you\u0026rsquo;re just in old school \u003ccode\u003etop\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003esee the screenshot, find M_SWAP\u003c/li\u003e\n\u003cli\u003emove M_SWAP to \u003ccode\u003eActive Columns\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251115125603_5d4af687.png\" alt=\"20251115125603_5d4af687.png\"\u003e\n\u003cfigcaption\u003e\u003cp\u003eCredit to \u003ca href=\"https://superuser.com/questions/206424/whats-using-my-swap-ubuntu\"\u003ethis superuser post\u003c/a\u003e\u003c/p\u003e\u003c/figcaption\u003e\u003c/figure\u003e\n",
      "content_text": "\nI struggle with maxxing swap on all my machines, I am sure it's a neovim config\nthat I've propagated everywhere and I'm too lazy to figure it out. Instead I\njust swapoff and swapon kind of often... but this morning I had to know how to\nsee what's using all that swap....\n\n![20251115125424_a1ddbd7c.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251115125424_a1ddbd7c.png)\n\nThe way to see SWAP usage by process in htop is:\n\n1. open htop\n2. Slam `F2` (`f` if you're just in old school `top`)\n3. see the screenshot, find M_SWAP\n4. move M_SWAP to `Active Columns`\n\n![20251115125603_5d4af687.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251115125603_5d4af687.png)\n\n\u003e Credit to [this superuser post](https://superuser.com/questions/206424/whats-using-my-swap-ubuntu)\n",
      "summary": "I struggle with maxxing swap on all my machines, I am sure it's a neovim config that I've propagated everywhere and I'm too lazy to figure it out. Instead I...",
      "date_published": "2025-11-15T06:52:14Z",
      "date_modified": "2025-11-15T06:52:14Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "dev",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/use-jellyfin-tags-for-content-moderation/",
      "url": "https://pype.dev/use-jellyfin-tags-for-content-moderation/",
      "title": "Use Jellyfin Tags For Content Moderation",
      "content_html": "\u003cp\u003eJellyfin brings in ratings and allows you to set custom ratings for shows,\nwhich is nice for things like \u0026ldquo;block anything worse than TV-14 for my kid\u0026rsquo;s\naccount\u0026rdquo;. But what if my firend\u0026rsquo;s kids can watch shows I don\u0026rsquo;t want my kids\nwatching? Turns out tags exist and are great!\u003c/p\u003e\n\u003ch2 id=\"tag-the-content\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTag the content\u003c/span\u003e \u003ca href=\"#tag-the-content\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eFirst is to actually tag the content - in the UI it\u0026rsquo;s simple.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eLogin with admin\u003c/li\u003e\n\u003cli\u003eManage Metadata\u003c/li\u003e\n\u003cli\u003eClick the content you want to tag and find the \u003ccode\u003eTags\u003c/code\u003e section\u003c/li\u003e\n\u003cli\u003eAdd your tag\u0026hellip; for example \u003ccode\u003eblocked:girls\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"filter-on-the-account\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eFilter on the account\u003c/span\u003e \u003ca href=\"#filter-on-the-account\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eGo to \u003ccode\u003eDashboard\u003c/code\u003e -\u0026gt; \u003ccode\u003eUsers\u003c/code\u003e -\u0026gt; \u003ccode\u003eLibrary Access\u003c/code\u003e -\u0026gt; \u003ccode\u003eParental Control\u003c/code\u003e -\u0026gt; \u003ccode\u003eBlock items with tags\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n",
      "content_text": "\nJellyfin brings in ratings and allows you to set custom ratings for shows,\nwhich is nice for things like \"block anything worse than TV-14 for my kid's\naccount\". But what if my firend's kids can watch shows I don't want my kids\nwatching? Turns out tags exist and are great!\n\n## Tag the content\n\nFirst is to actually tag the content - in the UI it's simple.\n\n1. Login with admin\n2. Manage Metadata\n3. Click the content you want to tag and find the `Tags` section\n4. Add your tag... for example `blocked:girls`\n\n## Filter on the account\n\n1. Go to `Dashboard` -\u003e `Users` -\u003e `Library Access` -\u003e `Parental Control` -\u003e `Block items with tags`\n",
      "summary": "Jellyfin brings in ratings and allows you to set custom ratings for shows, which is nice for things like \"block anything worse than TV-14 for my kid's...",
      "date_published": "2025-10-08T10:21:01Z",
      "date_modified": "2025-10-08T10:21:01Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "jellyfin",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/sending-sms-with-smtp/",
      "url": "https://pype.dev/sending-sms-with-smtp/",
      "title": "Sending SMS with SMTP",
      "content_html": "\u003cp\u003eI\u0026rsquo;m cooking something up over here that will include SMS message notifications\nas a feature and I\u0026rsquo;ve been doing some reading on different providers\u0026hellip;\u003c/p\u003e\n\u003cp\u003eMany specialize in bulk SMS messaging, like for campaigns and promotions. So\nthis pricing model doesn\u0026rsquo;t work for me.\u003c/p\u003e\n\u003cp\u003eThe providers that do quick single SMS messages are often $0.005 to $.04 cents\nper message which isn\u0026rsquo;t insanity but I\u0026rsquo;m not exactly backed by anyone\u0026hellip;\u003c/p\u003e\n\u003cp\u003eWell today I learned that you can actually send SMS messages via SMTP! I\nwouldn\u0026rsquo;t bank on total reliability here but for testing and initial roll out of\nwhat I want I think this will be perfect.\u003c/p\u003e\n\u003cp\u003eAnd because I\u0026rsquo;m just that \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of guy, here\u0026rsquo;s some code you can \u003ccode\u003euv run smtp2sms.py\u003c/code\u003e and get yourself a handy little text. I\u0026rsquo;m going to build a simpler\nmodule but you can get a \u003ccode\u003egmail\u003c/code\u003e and \u003ccode\u003egmail app password\u003c/code\u003e within about 10\nminutes. \u003ca href=\"https://support.google.com/mail/answer/185833?hl=en\"\u003esee here\u003c/a\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# /// script\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# requires-python = \u0026#34;\u0026gt;=3.10\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# dependencies = [\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ///\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e,\u003c/span\u003e \u003cspan class=\"nn\"\u003esmtplib\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003eemail.mime.text\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eMIMEText\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSMTP_APP_USER\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;SMTP_APP_USER\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# me@gmail.com\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSMTP_APP_PASSWORD\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;SMTP_APP_PASSWORD\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSMTP_SERVER\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;SMTP_SERVER\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# smtp.gmail.ch\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# NOTE: I haven\u0026#39;t verified all the carrier domains - this was provided by ChatGPT\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecarriers\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;att\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;txt.att.net\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;mms.att.net\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;tmobile\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;tmomail.net\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;tmomail.net\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;verizon\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# \u0026#34;sms\u0026#34;: \u0026#34;vtext.com\u0026#34;, # not reliable\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;vzwpix.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;vzwpix.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;sprint\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;messaging.sprintpcs.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;pm.sprint.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;googlefi\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;msg.fi.google.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;msg.fi.google.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;uscellular\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;email.uscc.net\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;mms.uscc.net\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;boost\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;sms.myboostmobile.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;myboostmobile.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;cricket\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;sms.cricketwireless.net\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;mms.cricketwireless.net\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;metropcs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;mymetropcs.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;mymetropcs.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;virgin\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;sms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;vmobl.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;mms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;vmobl.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003econtacts\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;me\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;carrier\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;verizon\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;1234567890\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emsg\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eMIMEText\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Hello from a python script? What?!\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emsg\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;From\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eSMTP_APP_USER\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSEND_TO\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;me\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Complex for my future use cases - deal with it\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emsg\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;To\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003econtacts\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eSEND_TO\u003c/span\u003e\u003cspan class=\"p\"\u003e][\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;number\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e@\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ecarriers\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003econtacts\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eSEND_TO\u003c/span\u003e\u003cspan class=\"p\"\u003e][\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;carrier\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]][\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;sms\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003esmtplib\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSMTP\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSMTP_SERVER\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e587\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003eemail_handler\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eemail_handler\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estarttls\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eemail_handler\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elogin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSMTP_APP_USER\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eSMTP_APP_PASSWORD\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eemail_handler\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esendmail\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSMTP_APP_USER\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003emsg\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;To\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]],\u003c/span\u003e \u003cspan class=\"n\"\u003emsg\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eas_string\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nI'm cooking something up over here that will include SMS message notifications\nas a feature and I've been doing some reading on different providers...\n\nMany specialize in bulk SMS messaging, like for campaigns and promotions. So\nthis pricing model doesn't work for me.\n\nThe providers that do quick single SMS messages are often $0.005 to $.04 cents\nper message which isn't insanity but I'm not exactly backed by anyone...\n\nWell today I learned that you can actually send SMS messages via SMTP! I\nwouldn't bank on total reliability here but for testing and initial roll out of\nwhat I want I think this will be perfect.\n\nAnd because I'm just that kind of guy, here's some code you can `uv run\nsmtp2sms.py` and get yourself a handy little text. I'm going to build a simpler\nmodule but you can get a `gmail` and `gmail app password` within about 10\nminutes. [see here](https://support.google.com/mail/answer/185833?hl=en)\n\n```python\n# /// script\n# requires-python = \"\u003e=3.10\"\n# dependencies = [\n# ]\n# ///\nimport os, smtplib\nfrom email.mime.text import MIMEText\n\nSMTP_APP_USER=os.environ.get(\"SMTP_APP_USER\") # me@gmail.com\nSMTP_APP_PASSWORD=os.environ.get(\"SMTP_APP_PASSWORD\")\nSMTP_SERVER=os.environ.get(\"SMTP_SERVER\")  # smtp.gmail.ch\n\n# NOTE: I haven't verified all the carrier domains - this was provided by ChatGPT\n\ncarriers = {\n    \"att\": {\n        \"sms\": \"txt.att.net\",\n        \"mms\": \"mms.att.net\"\n    },\n    \"tmobile\": {\n        \"sms\": \"tmomail.net\",\n        \"mms\": \"tmomail.net\"\n    },\n    \"verizon\": {\n        # \"sms\": \"vtext.com\", # not reliable\n        \"sms\": \"vzwpix.com\",\n        \"mms\": \"vzwpix.com\"\n    },\n    \"sprint\": {\n        \"sms\": \"messaging.sprintpcs.com\",\n        \"mms\": \"pm.sprint.com\"\n    },\n    \"googlefi\": {\n        \"sms\": \"msg.fi.google.com\",\n        \"mms\": \"msg.fi.google.com\"\n    },\n    \"uscellular\": {\n        \"sms\": \"email.uscc.net\",\n        \"mms\": \"mms.uscc.net\"\n    },\n    \"boost\": {\n        \"sms\": \"sms.myboostmobile.com\",\n        \"mms\": \"myboostmobile.com\"\n    },\n    \"cricket\": {\n        \"sms\": \"sms.cricketwireless.net\",\n        \"mms\": \"mms.cricketwireless.net\"\n    },\n    \"metropcs\": {\n        \"sms\": \"mymetropcs.com\",\n        \"mms\": \"mymetropcs.com\"\n    },\n    \"virgin\": {\n        \"sms\": \"vmobl.com\",\n        \"mms\": \"vmobl.com\"\n    }\n}\ncontacts = {\n    \"me\": {\"carrier\": \"verizon\", \"number\": \"1234567890\"},\n}\nmsg = MIMEText(\"Hello from a python script? What?!\")\nmsg[\"From\"] = SMTP_APP_USER\n\nSEND_TO = \"me\"\n\n# Complex for my future use cases - deal with it\nmsg[\"To\"] = f\"{contacts[SEND_TO]['number']}@{carriers[contacts[SEND_TO]['carrier']]['sms']}\"\n\nwith smtplib.SMTP(SMTP_SERVER, 587) as email_handler:\n    email_handler.starttls()\n    email_handler.login(SMTP_APP_USER, SMTP_APP_PASSWORD)\n    email_handler.sendmail(SMTP_APP_USER, [msg[\"To\"]], msg.as_string())\n\n```\n",
      "summary": "I'm cooking something up over here that will include SMS message notifications as a feature and I've been doing some reading on different providers...",
      "date_published": "2025-10-04T09:38:58Z",
      "date_modified": "2025-10-04T09:38:58Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/docker-compose-build-from-git-repo/",
      "url": "https://pype.dev/docker-compose-build-from-git-repo/",
      "title": "Docker Compose Build From Git Repo",
      "content_html": "\u003cp\u003eYou can reference a git repo in compose\u0026rsquo;s \u0026ldquo;build\u0026rdquo; argument for a service as of \u003ca href=\"https://github.com/docker/compose/pull/2430\"\u003ethis PR\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nYou can reference a git repo in compose's \"build\" argument for a service as of [this PR](https://github.com/docker/compose/pull/2430)\n",
      "summary": "You can reference a git repo in compose's \"build\" argument for a service as of this PR",
      "date_published": "2025-09-12T08:45:20Z",
      "date_modified": "2025-09-12T08:45:20Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "git",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/zsh-alias-for-uv-scripts/",
      "url": "https://pype.dev/zsh-alias-for-uv-scripts/",
      "title": "Zsh alias for uv scripts",
      "content_html": "\u003cp\u003eIt\u0026rsquo;s apparently advantageous for \u003ca href=\"/uv/\" class=\"glossary-term\" title=\"uv by Astral.sh is one of the dopest python toolchain tools to ever exist\" data-hover-title=\"UV\"\u003euv\u003c/a\u003e to have a specific shebang set that \u003ccode\u003euv init\u003c/code\u003e doesn\u0026rsquo;t add, no problem, we can have a zsh alias for it\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e add-shebang\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;f(){ \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e  if [[ ! -s $1 ]]; then \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e    echo \u0026#34;#!/usr/bin/env -S uv run --script\u0026#34; \u0026gt; \u0026#34;$1\u0026#34;; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e  elif head -n1 \u0026#34;$1\u0026#34; | grep -q \u0026#34;^#!\u0026#34;; then \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e    echo \u0026#34;Shebang already present in $1\u0026#34;; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e  else \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e    sed -i \u0026#34;1i #!/usr/bin/env -S uv run --script\u0026#34; \u0026#34;$1\u0026#34;; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e  fi\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e}; f\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen if you have a python script from \u003ccode\u003euv init --script\u003c/code\u003e you can \u003ccode\u003eadd-shebang myscript.py\u003c/code\u003e to add the shebang.\u003c/p\u003e\n",
      "content_text": "\nIt's apparently advantageous for uv to have a specific shebang set that `uv init` doesn't add, no problem, we can have a zsh alias for it\n\n\n```bash\nalias add-shebang='f(){ \n  if [[ ! -s $1 ]]; then \n    echo \"#!/usr/bin/env -S uv run --script\" \u003e \"$1\"; \n  elif head -n1 \"$1\" | grep -q \"^#!\"; then \n    echo \"Shebang already present in $1\"; \n  else \n    sed -i \"1i #!/usr/bin/env -S uv run --script\" \"$1\"; \n  fi\n}; f'\n```\n\nThen if you have a python script from `uv init --script` you can `add-shebang myscript.py` to add the shebang.\n\n",
      "summary": "It's apparently advantageous for uv to have a specific shebang set that doesn't add, no problem, we can have a zsh alias for it",
      "date_published": "2025-09-08T06:33:13Z",
      "date_modified": "2025-09-08T06:33:13Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/pair-acegamer-wireless-ps4-controller-with-pc/",
      "url": "https://pype.dev/pair-acegamer-wireless-ps4-controller-with-pc/",
      "title": "Pair AceGamer Wireless PS4 Controller With PC",
      "content_html": "\u003cp\u003eI bought a $20 PS4 knock-off controller recently as my PS4 controllers are 12\nyears old (to me!) and I bought them used before that\u0026hellip; the instructions for a\n$20 controller are obviously bad but a godsend redittor discovered the hidden\nsecret to pair this device with my computer! Instructions (for ps4) say to plug\nit in, hit the home button, and you\u0026rsquo;re paired. Now, that didn\u0026rsquo;t work on PC\nobviously, but it ALSO didn\u0026rsquo;t work on my PS4\u0026hellip; Unfortunte! It works wired just\nfine, but turns out the magic to send the thing into traditional Bluetooth\nPairing Mode is to hold the home and share buttons down - wait for a rapid few\nblinks - then you\u0026rsquo;ll see the controller in your bluetooth pairing menu!\u003c/p\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250814115137_34c132cd.png\" alt=\"20250814115137_34c132cd.png\"\u003e\n\u003c/figure\u003e\n",
      "content_text": "\nI bought a $20 PS4 knock-off controller recently as my PS4 controllers are 12\nyears old (to me!) and I bought them used before that... the instructions for a\n$20 controller are obviously bad but a godsend redittor discovered the hidden\nsecret to pair this device with my computer! Instructions (for ps4) say to plug\nit in, hit the home button, and you're paired. Now, that didn't work on PC\nobviously, but it ALSO didn't work on my PS4... Unfortunte! It works wired just\nfine, but turns out the magic to send the thing into traditional Bluetooth\nPairing Mode is to hold the home and share buttons down - wait for a rapid few\nblinks - then you'll see the controller in your bluetooth pairing menu!\n\n![20250814115137_34c132cd.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250814115137_34c132cd.png)\n",
      "summary": "I bought a $20 PS4 knock-off controller recently as my PS4 controllers are 12 years old (to me!) and I bought them used before that... the instructions for a...",
      "date_published": "2025-08-14T06:51:12Z",
      "date_modified": "2025-08-14T06:51:12Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "gaming",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/migrate-github-issues-to-kanboard/",
      "url": "https://pype.dev/migrate-github-issues-to-kanboard/",
      "title": "Migrate Github Issues to Kanboard",
      "content_html": "\u003cp\u003eI am working on shifting everything, or as much as I reasonably can, related to\ndev for myself to on-prem, including git and CI/CD. So for Quadtask I had a\nbunch of Github issues that I wanted to migrate to my kanboard instance, gpt-5\ndid a bang-up job on this script\u003c/p\u003e\n\u003cp\u003eIt gets each issue, creates a tag for each label, creates the kanboard ticket, then closes the github issue\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#!/bin/bash\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# GitHub to Kanboard Issue Migration Script\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uses GitHub CLI (gh) and curl to migrate issues\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Exit on error\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eset\u003c/span\u003e -e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Check required environment variables\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e var in KANBOARD_URL KANBOARD_TOKEN GH_TOKEN QUADTASK_KANBOARD_PROJECT_ID\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e -z \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"p\"\u003e!var\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Error: \u003c/span\u003e\u003cspan class=\"nv\"\u003e$var\u003c/span\u003e\u003cspan class=\"s2\"\u003e is not set\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eexit\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# GitHub repository (default: pypeaday/quadtask)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eGITHUB_REPO\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003eGITHUB_REPO\u003c/span\u003e\u003cspan class=\"k\"\u003e:-\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;pypeaday/quadtask\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Kanboard API endpoint\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eKANBOARD_API\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$KANBOARD_URL\u003c/span\u003e\u003cspan class=\"s2\"\u003e/jsonrpc.php\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Get all open issues from GitHub\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Fetching open issues from \u003c/span\u003e\u003cspan class=\"nv\"\u003e$GITHUB_REPO\u003c/span\u003e\u003cspan class=\"s2\"\u003e...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eISSUES_JSON\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003egh issue list --repo \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$GITHUB_REPO\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e --state open --limit \u003cspan class=\"m\"\u003e1000\u003c/span\u003e --json number,title,body,labels\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Count issues\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eISSUE_COUNT\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$ISSUES_JSON\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq \u003cspan class=\"s1\"\u003e\u0026#39;. | length\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Found \u003c/span\u003e\u003cspan class=\"nv\"\u003e$ISSUE_COUNT\u003c/span\u003e\u003cspan class=\"s2\"\u003e open issues\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Process each issue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$ISSUES_JSON\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -c \u003cspan class=\"s1\"\u003e\u0026#39;.[]\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e \u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"nb\"\u003eread\u003c/span\u003e -r issue\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Extract issue details\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003eNUMBER\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$issue\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -r \u003cspan class=\"s1\"\u003e\u0026#39;.number\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003eTITLE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$issue\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -r \u003cspan class=\"s1\"\u003e\u0026#39;.title\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003eBODY\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$issue\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -r \u003cspan class=\"s1\"\u003e\u0026#39;.body // \u0026#34;\u0026#34;\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Extract label names as a JSON array\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003eLABELS_JSON\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$issue\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -c \u003cspan class=\"s1\"\u003e\u0026#39;[.labels[]?.name] // []\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Prepare Kanboard task data\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003eREQUEST_DATA\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ejq -n \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        --arg method \u003cspan class=\"s2\"\u003e\u0026#34;createTask\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        --argjson id \u003cspan class=\"m\"\u003e1\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        --arg jsonrpc \u003cspan class=\"s2\"\u003e\u0026#34;2.0\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        --arg title \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$TITLE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        --arg description \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$BODY\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        --arg project_id \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$QUADTASK_KANBOARD_PROJECT_ID\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s1\"\u003e\u0026#39;{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e            \u0026#34;jsonrpc\u0026#34;: $jsonrpc,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e            \u0026#34;method\u0026#34;: $method,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e            \u0026#34;id\u0026#34;: $id,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e            \u0026#34;params\u0026#34;: {\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                \u0026#34;title\u0026#34;: $title,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                \u0026#34;description\u0026#34;: $description,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                \u0026#34;project_id\u0026#34;: $project_id\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e        }\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Create task in Kanboard\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Creating task: \u003c/span\u003e\u003cspan class=\"nv\"\u003e$TITLE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003eRESPONSE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ecurl -s -X POST \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        -H \u003cspan class=\"s2\"\u003e\u0026#34;Content-Type: application/json\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        -u \u003cspan class=\"s2\"\u003e\u0026#34;jsonrpc:\u003c/span\u003e\u003cspan class=\"nv\"\u003e$KANBOARD_TOKEN\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        -d \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$REQUEST_DATA\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$KANBOARD_API\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Check for errors\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$RESPONSE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -e \u003cspan class=\"s1\"\u003e\u0026#39;.error\u0026#39;\u003c/span\u003e \u0026gt; /dev/null\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Error creating task: \u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$RESPONSE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -r \u003cspan class=\"s1\"\u003e\u0026#39;.error.message\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nv\"\u003eTASK_ID\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$RESPONSE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -r \u003cspan class=\"s1\"\u003e\u0026#39;.result\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Created task ID: \u003c/span\u003e\u003cspan class=\"nv\"\u003e$TASK_ID\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# If there are labels, set them as Kanboard tags on the task\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$LABELS_JSON\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq \u003cspan class=\"s1\"\u003e\u0026#39;length\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e -gt \u003cspan class=\"m\"\u003e0\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Setting tags on task \u003c/span\u003e\u003cspan class=\"nv\"\u003e$TASK_ID\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$LABELS_JSON\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -r \u003cspan class=\"s1\"\u003e\u0026#39;join(\u0026#34;, \u0026#34;)\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nv\"\u003eTAGS_REQUEST\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ejq -n \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                --arg jsonrpc \u003cspan class=\"s2\"\u003e\u0026#34;2.0\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                --arg method \u003cspan class=\"s2\"\u003e\u0026#34;setTaskTags\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                --argjson id \u003cspan class=\"m\"\u003e2\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                --arg project_id \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$QUADTASK_KANBOARD_PROJECT_ID\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                --arg task_id \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$TASK_ID\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                --argjson tags \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$LABELS_JSON\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s1\"\u003e\u0026#39;{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                    jsonrpc: $jsonrpc,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                    method: $method,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                    id: $id,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                    params: {\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                        project_id: $project_id,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                        task_id: $task_id,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                        tags: $tags\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                    }\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e                }\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nv\"\u003eTAGS_RESPONSE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ecurl -s -X POST \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                -H \u003cspan class=\"s2\"\u003e\u0026#34;Content-Type: application/json\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                -u \u003cspan class=\"s2\"\u003e\u0026#34;jsonrpc:\u003c/span\u003e\u003cspan class=\"nv\"\u003e$KANBOARD_TOKEN\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                -d \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$TAGS_REQUEST\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$KANBOARD_API\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$TAGS_RESPONSE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -e \u003cspan class=\"s1\"\u003e\u0026#39;.error\u0026#39;\u003c/span\u003e \u0026gt; /dev/null\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Warning: Failed to set tags: \u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$TAGS_RESPONSE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e jq -r \u003cspan class=\"s1\"\u003e\u0026#39;.error.message\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Tags set successfully\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# Close the corresponding GitHub issue to avoid duplicates\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nv\"\u003eTASK_URL\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003eKANBOARD_URL\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e/?controller=TaskViewController\u0026amp;action=show\u0026amp;task_id=\u003c/span\u003e\u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003eTASK_ID\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Closing GitHub issue #\u003c/span\u003e\u003cspan class=\"nv\"\u003e$NUMBER\u003c/span\u003e\u003cspan class=\"s2\"\u003e with comment linking to Kanboard task...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e gh issue close \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$NUMBER\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e --repo \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$GITHUB_REPO\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e --comment \u003cspan class=\"s2\"\u003e\u0026#34;Migrated to Kanboard task \u003c/span\u003e\u003cspan class=\"nv\"\u003e$TASK_ID\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"nv\"\u003e$TASK_URL\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u0026gt;/dev/null\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Closed GitHub issue #\u003c/span\u003e\u003cspan class=\"nv\"\u003e$NUMBER\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Warning: Failed to close GitHub issue #\u003c/span\u003e\u003cspan class=\"nv\"\u003e$NUMBER\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;----------------------------------------\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Migration completed\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nI am working on shifting everything, or as much as I reasonably can, related to\ndev for myself to on-prem, including git and CI/CD. So for Quadtask I had a\nbunch of Github issues that I wanted to migrate to my kanboard instance, gpt-5\ndid a bang-up job on this script\n\nIt gets each issue, creates a tag for each label, creates the kanboard ticket, then closes the github issue\n\n```bash\n\n#!/bin/bash\n\n# GitHub to Kanboard Issue Migration Script\n# Uses GitHub CLI (gh) and curl to migrate issues\n\n# Exit on error\nset -e\n\n# Check required environment variables\nfor var in KANBOARD_URL KANBOARD_TOKEN GH_TOKEN QUADTASK_KANBOARD_PROJECT_ID; do\n    if [ -z \"${!var}\" ]; then\n        echo \"Error: $var is not set\"\n        exit 1\n    fi\ndone\n\n# GitHub repository (default: pypeaday/quadtask)\nGITHUB_REPO=${GITHUB_REPO:-\"pypeaday/quadtask\"}\n\n# Kanboard API endpoint\nKANBOARD_API=\"$KANBOARD_URL/jsonrpc.php\"\n\n# Get all open issues from GitHub\necho \"Fetching open issues from $GITHUB_REPO...\"\nISSUES_JSON=$(gh issue list --repo \"$GITHUB_REPO\" --state open --limit 1000 --json number,title,body,labels)\n\n# Count issues\nISSUE_COUNT=$(echo \"$ISSUES_JSON\" | jq '. | length')\necho \"Found $ISSUE_COUNT open issues\"\n\n# Process each issue\necho \"$ISSUES_JSON\" | jq -c '.[]' | while read -r issue; do\n    # Extract issue details\n    NUMBER=$(echo \"$issue\" | jq -r '.number')\n    TITLE=$(echo \"$issue\" | jq -r '.title')\n    BODY=$(echo \"$issue\" | jq -r '.body // \"\"')\n    # Extract label names as a JSON array\n    LABELS_JSON=$(echo \"$issue\" | jq -c '[.labels[]?.name] // []')\n    \n    # Prepare Kanboard task data\n    REQUEST_DATA=$(jq -n \\\n        --arg method \"createTask\" \\\n        --argjson id 1 \\\n        --arg jsonrpc \"2.0\" \\\n        --arg title \"$TITLE\" \\\n        --arg description \"$BODY\" \\\n        --arg project_id \"$QUADTASK_KANBOARD_PROJECT_ID\" \\\n        '{\n            \"jsonrpc\": $jsonrpc,\n            \"method\": $method,\n            \"id\": $id,\n            \"params\": {\n                \"title\": $title,\n                \"description\": $description,\n                \"project_id\": $project_id\n            }\n        }')\n    \n    # Create task in Kanboard\n    echo \"Creating task: $TITLE\"\n    RESPONSE=$(curl -s -X POST \\\n        -H \"Content-Type: application/json\" \\\n        -u \"jsonrpc:$KANBOARD_TOKEN\" \\\n        -d \"$REQUEST_DATA\" \\\n        \"$KANBOARD_API\")\n    \n    # Check for errors\n    if echo \"$RESPONSE\" | jq -e '.error' \u003e /dev/null; then\n        echo \"Error creating task: $(echo \"$RESPONSE\" | jq -r '.error.message')\"\n    else\n        TASK_ID=$(echo \"$RESPONSE\" | jq -r '.result')\n        echo \"Created task ID: $TASK_ID\"\n\n        # If there are labels, set them as Kanboard tags on the task\n        if [ \"$(echo \"$LABELS_JSON\" | jq 'length')\" -gt 0 ]; then\n            echo \"Setting tags on task $TASK_ID: $(echo \"$LABELS_JSON\" | jq -r 'join(\", \")')\"\n            TAGS_REQUEST=$(jq -n \\\n                --arg jsonrpc \"2.0\" \\\n                --arg method \"setTaskTags\" \\\n                --argjson id 2 \\\n                --arg project_id \"$QUADTASK_KANBOARD_PROJECT_ID\" \\\n                --arg task_id \"$TASK_ID\" \\\n                --argjson tags \"$LABELS_JSON\" \\\n                '{\n                    jsonrpc: $jsonrpc,\n                    method: $method,\n                    id: $id,\n                    params: {\n                        project_id: $project_id,\n                        task_id: $task_id,\n                        tags: $tags\n                    }\n                }')\n\n            TAGS_RESPONSE=$(curl -s -X POST \\\n                -H \"Content-Type: application/json\" \\\n                -u \"jsonrpc:$KANBOARD_TOKEN\" \\\n                -d \"$TAGS_REQUEST\" \\\n                \"$KANBOARD_API\")\n\n            if echo \"$TAGS_RESPONSE\" | jq -e '.error' \u003e /dev/null; then\n                echo \"Warning: Failed to set tags: $(echo \"$TAGS_RESPONSE\" | jq -r '.error.message')\"\n            else\n                echo \"Tags set successfully\"\n            fi\n        fi\n\n        # Close the corresponding GitHub issue to avoid duplicates\n        TASK_URL=\"${KANBOARD_URL}/?controller=TaskViewController\u0026action=show\u0026task_id=${TASK_ID}\"\n        echo \"Closing GitHub issue #$NUMBER with comment linking to Kanboard task...\"\n        if gh issue close \"$NUMBER\" --repo \"$GITHUB_REPO\" --comment \"Migrated to Kanboard task $TASK_ID: $TASK_URL\" \u003e/dev/null; then\n            echo \"Closed GitHub issue #$NUMBER\"\n        else\n            echo \"Warning: Failed to close GitHub issue #$NUMBER\"\n        fi\n    fi\n    \n    echo \"----------------------------------------\"\ndone\n\necho \"Migration completed\"\n```\n",
      "summary": "I am working on shifting everything, or as much as I reasonably can, related to dev for myself to on-prem, including git and CI/CD. So for Quadtask I had a...",
      "date_published": "2025-08-07T14:43:27Z",
      "date_modified": "2025-08-07T14:43:27Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "kanboard",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/add-yourself-to-www-data-to-view-your-nextcloud-data-on-the-filesystem/",
      "url": "https://pype.dev/add-yourself-to-www-data-to-view-your-nextcloud-data-on-the-filesystem/",
      "title": "Add yourself to www-data to view your Nextcloud data on the filesystem",
      "content_html": "\u003cp\u003eI am working on using \u003ca href=\"/gotify-cli-for-notifying-me-of-nextcloud-uploads/\" class=\"wikilink\" data-title=\"gotify cli for notifying me of nextcloud uploads\" data-description=\"I was looking for a way to get notified from nextcloud when files were uploaded to a certain directory. This is because the upload is very spotty due to the...\" data-date=\"2025-07-16\" data-preview=\"I was looking for a way to get notified from nextcloud when files were uploaded to a certain directory. This is because the upload is very spotty due to the...\"\u003egotify cli for notifying me of nextcloud uploads\u003c/a\u003e and\nI\u0026rsquo;ll start by running the gotify cli as my user so I\u0026rsquo;ll need to be able to see\nthe directory in the filesystem that Nextcloud stores the data in.\u003c/p\u003e\n\u003cp\u003eThat folder on disk is owned by \u003ccode\u003ewww-data\u003c/code\u003e (see \u003ca href=\"/nextcloud-permissions-with-zfs-and-ansible-nas/\" class=\"wikilink\" data-title=\"Nextcloud permissions with ZFS and Ansible-NAS\" data-description=\"As the nextcloud docs say... if you want to write to an external volume that location has to be writeable by the user/group on the host system... so if that...\" data-date=\"2022-05-19\" data-preview=\"As the nextcloud docs say... if you want to write to an external volume that location has to be writeable by the user/group on the host system... so if that...\"\u003eNextcloud permissions with ZFS and Ansible-NAS\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eTurns out it\u0026rsquo;s my computer and I can do what I want so I just add myself to the\n\u003ccode\u003ewww-data\u003c/code\u003e group instead of running the \u003ccode\u003egotify cli\u003c/code\u003e as \u003ccode\u003eroot\u003c/code\u003e\u003c/p\u003e\n\u003ch1 id=\"bonus\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eBonus\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eYou can remove users from groups with \u003ccode\u003egpasswd -d username groupname\u003c/code\u003e\u003c/p\u003e\n",
      "content_text": "\nI am working on using \u003ca href=\"/gotify-cli-for-notifying-me-of-nextcloud-uploads/\" class=\"wikilink\" data-title=\"gotify cli for notifying me of nextcloud uploads\" data-description=\"I was looking for a way to get notified from nextcloud when files were uploaded to a certain directory. This is because the upload is very spotty due to the...\" data-date=\"2025-07-16\"\u003egotify cli for notifying me of nextcloud uploads\u003c/a\u003e and\nI'll start by running the gotify cli as my user so I'll need to be able to see\nthe directory in the filesystem that Nextcloud stores the data in.\n\nThat folder on disk is owned by `www-data` (see \u003ca href=\"/nextcloud-permissions-with-zfs-and-ansible-nas/\" class=\"wikilink\" data-title=\"Nextcloud permissions with ZFS and Ansible-NAS\" data-description=\"As the nextcloud docs say... if you want to write to an external volume that location has to be writeable by the user/group on the host system... so if that...\" data-date=\"2022-05-19\"\u003eNextcloud permissions with ZFS and Ansible-NAS\u003c/a\u003e\n\nTurns out it's my computer and I can do what I want so I just add myself to the\n`www-data` group instead of running the `gotify cli` as `root`\n\n# Bonus\n\nYou can remove users from groups with `gpasswd -d username groupname`\n",
      "summary": "I am working on using gotify-cli-for-notifying-me-of-nextcloud-uploads and I'll start by running the gotify cli as my user so I'll need to be able to see the...",
      "date_published": "2025-07-17T07:38:09Z",
      "date_modified": "2025-07-17T07:38:09Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "nextcloud",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/lua-type-hinting-undefined-global/",
      "url": "https://pype.dev/lua-type-hinting-undefined-global/",
      "title": "Lua type hinting - undefined global",
      "content_html": "\u003cp\u003eI have tons of linting errors in my nvim config, partly because I use Lazyvim\nplus a set of poorly written customizations that have traveled with me for a\nwhile. I try to re-work things and right now I\u0026rsquo;ve been revisiting my developer\nsetup. Well the LSP rrors as I\u0026rsquo;ve gone through my neovim config are ticking me\noff - and this was the biggest one \u0026ldquo;Undefined global: vim\u0026rdquo;. I saw \u003ccode\u003evim.fn\u003c/code\u003e\neverywhere but the LSP hated it\u0026hellip; turns out \u003ccode\u003evim\u003c/code\u003e is a given global and you\ncan help your lsp with this:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e---@diagnostic disable: undefined-global\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- vim is a global in Neovim Lua scripts\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor example:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e---@diagnostic disable: undefined-global\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- vim is a global in Neovim Lua scripts\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"s2\"\u003e\u0026#34;pypeaday\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003edir\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003evim.fn\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estdpath\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;config\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;/lua/pypeaday\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003econfig\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003edaily\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erequire\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;pypeaday.daily\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evim.api\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003envim_create_user_command\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;DailyNote\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edaily.check_and_open_daily_note\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evim.api\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003envim_create_user_command\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Daily\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edaily.check_and_open_daily_note\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evim.api\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003envim_create_user_command\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;BackLinks\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edaily.find_backlinks\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evim.keymap\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;leader\u0026gt;dn\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edaily.check_and_open_daily_note\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003edesc\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Open daily note\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evim.keymap\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;leader\u0026gt;df\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edaily.find_daily_files\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003edesc\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Find daily files\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evim.keymap\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;leader\u0026gt;dl\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edaily.find_backlinks\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003edesc\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Find backlinks\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nI have tons of linting errors in my nvim config, partly because I use Lazyvim\nplus a set of poorly written customizations that have traveled with me for a\nwhile. I try to re-work things and right now I've been revisiting my developer\nsetup. Well the LSP rrors as I've gone through my neovim config are ticking me\noff - and this was the biggest one \"Undefined global: vim\". I saw `vim.fn`\neverywhere but the LSP hated it... turns out `vim` is a given global and you\ncan help your lsp with this:\n\n```lua\n---@diagnostic disable: undefined-global\n-- vim is a global in Neovim Lua scripts\n```\n\nFor example:\n\n```lua\n\n---@diagnostic disable: undefined-global\n-- vim is a global in Neovim Lua scripts\nreturn {\n  \"pypeaday\",\n  dir = vim.fn.stdpath(\"config\") .. \"/lua/pypeaday\",\n  config = function()\n    local daily = require(\"pypeaday.daily\")\n    vim.api.nvim_create_user_command(\"DailyNote\", daily.check_and_open_daily_note, {})\n    vim.api.nvim_create_user_command(\"Daily\", daily.check_and_open_daily_note, {})\n    vim.api.nvim_create_user_command(\"BackLinks\", daily.find_backlinks, {})\n    vim.keymap.set(\"n\", \"\u003cleader\u003edn\", daily.check_and_open_daily_note, { desc = \"Open daily note\" })\n    vim.keymap.set(\"n\", \"\u003cleader\u003edf\", daily.find_daily_files, { desc = \"Find daily files\" })\n    vim.keymap.set(\"n\", \"\u003cleader\u003edl\", daily.find_backlinks, { desc = \"Find backlinks\" })\n  end,\n}\n```\n",
      "summary": "I have tons of linting errors in my nvim config, partly because I use Lazyvim plus a set of poorly written customizations that have traveled with me for a...",
      "date_published": "2025-07-11T06:08:39Z",
      "date_modified": "2025-07-11T06:08:39Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "lua",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/nostr-hex-key/",
      "url": "https://pype.dev/nostr-hex-key/",
      "title": "Nostr hex key",
      "content_html": "\u003cp\u003eyou can hexify your \u003ca href=\"/nostr/\" class=\"wikilink\" data-title=\"Nostr\" data-description=\"???+ note \u0026#34;How to start...\u0026#34;\" data-date=\"2025-07-02\" data-preview=\"???+ note \u0026#34;How to start...\u0026#34;\"\u003eNostr\u003c/a\u003e key when you need to sometimes which I needed to for \u003ca href=\"/tags/postiz/\" class=\"wikilink\" data-title=\"Posts tagged: postiz\" data-description=\"All posts with the tag \u0026#34;postiz\u0026#34;\" data-preview=\"All posts with the tag \u0026#34;postiz\u0026#34;\"\u003ePosts tagged: postiz\u003c/a\u003e (link with a thought I\u0026rsquo;m sure I have on postiz)\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# /// script\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# requires-python = \u0026#34;\u0026gt;=3.8\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# dependencies = [\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#     \u0026#34;bech32\u0026#34;,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ///\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eos\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ebech32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Set your nsec... key to NOSTR_PRIVATE_KEY or bake it in...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ensec_key\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;NOSTR_PRIVATE_KEY\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003edecode_nsec\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ensec_key\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ehrp\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebech32\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebech32_decode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ensec_key\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ehrp\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;nsec\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"ne\"\u003eValueError\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Not a valid nsec key\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edecoded\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebech32\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econvertbits\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003eFalse\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nb\"\u003ebytes\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edecoded\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ehex\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Example usage\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eraw_hex\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edecode_nsec\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ensec_key\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Raw hex private key: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eraw_hex\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nyou can hexify your \u003ca href=\"/nostr/\" class=\"wikilink\" data-title=\"Nostr\" data-description=\"???+ note \u0026#34;How to start...\u0026#34;\" data-date=\"2025-07-02\"\u003eNostr\u003c/a\u003e key when you need to sometimes which I needed to for \u003ca href=\"/tags/postiz/\" class=\"wikilink\" data-title=\"Posts tagged: postiz\" data-description=\"All posts with the tag \u0026#34;postiz\u0026#34;\"\u003ePosts tagged: postiz\u003c/a\u003e (link with a thought I'm sure I have on postiz)\n\n```python\n\n# /// script\n# requires-python = \"\u003e=3.8\"\n# dependencies = [\n#     \"bech32\",\n# ]\n# ///\n#\nimport os\n\nimport bech32\n\n# Set your nsec... key to NOSTR_PRIVATE_KEY or bake it in...\nnsec_key = os.environ.get(\"NOSTR_PRIVATE_KEY\")\n\n\ndef decode_nsec(nsec_key):\n    hrp, data = bech32.bech32_decode(nsec_key)\n    if hrp != \"nsec\":\n        raise ValueError(\"Not a valid nsec key\")\n    decoded = bech32.convertbits(data, 5, 8, False)\n    return bytes(decoded).hex()\n\n\n# Example usage\nraw_hex = decode_nsec(nsec_key)\nprint(f\"Raw hex private key: {raw_hex}\")\n```\n",
      "summary": "you can hexify your nostr key when you need to sometimes which I needed to for postiz (link with a thought I'm sure I have on postiz)",
      "date_published": "2025-07-02T08:43:32Z",
      "date_modified": "2025-07-02T08:43:32Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "nostr",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/thoughts-688/",
      "url": "https://pype.dev/thoughts-688/",
      "title": "💭 slash pages",
      "content_html": "\u003ca href=\"https://slashpages.net/\"\u003e\n    \u003cimg\n        src=\"https://shots.wayl.one/shot/?url=https://slashpages.net/\u0026height=450\u0026width=800\u0026scaled_width=800\u0026scaled_height=450\u0026selectors=\"\n        alt=\"shot of post - slash pages\"\n        height=450\n        width=800\n    \u003e\n\u003c/a\u003e\n\u003cp\u003eHere\u0026rsquo;s my thought on \u003ca href=\"https://slashpages.net/\"\u003eslash pages\u003c/a\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eWaylon\u0026rsquo;s mentioned slash pages before, but as I work on building my blog again and writing more regularly thanks to \u0026ldquo;thoughts\u0026rdquo; etc. I\u0026rsquo;d like to start putting together some meaningful slash pages for myself\u0026hellip; I\u0026rsquo;m not sure what to put out there yet but this thought can serve as a todo for me\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv class=\"admonition note\"\u003e\n\u003cp class=\"admonition-title\"\u003eNote\u003c/p\u003e\n\u003cp\u003eThis is one of [[ my-thoughts ]]. I picked this up from \u003ca href=\"https://waylonwalker.com\"\u003eWaylon Walker\u003c/a\u003e(\u003ca href=\"https://thoughts.waylonwalker.com\"\u003ehttps://thoughts.waylonwalker.com\u003c/a\u003e). It\u0026rsquo;s a short note that I make about someone else\u0026rsquo;s content online.  Learn more about the process \u003ca href=\"/thoughts/\" class=\"wikilink\" data-title=\"Thoughts\" data-description=\"My thoughts are found at my thoughts feed\" data-date=\"2025-06-09\" data-preview=\"My thoughts are found at my thoughts feed\"\u003eThoughts\u003c/a\u003e\u003c/p\u003e\n\u003c/div\u003e\n",
      "content_text": "\n\u003ca href=\"https://slashpages.net/\"\u003e\n    \u003cimg\n        src=\"https://shots.wayl.one/shot/?url=https://slashpages.net/\u0026height=450\u0026width=800\u0026scaled_width=800\u0026scaled_height=450\u0026selectors=\"\n        alt=\"shot of post - slash pages\"\n        height=450\n        width=800\n    \u003e\n\u003c/a\u003e\n\nHere's my thought on \u003ca href=\"https://slashpages.net/\"\u003eslash pages\u003c/a\u003e\n\n---\n\nWaylon's mentioned slash pages before, but as I work on building my blog again and writing more regularly thanks to \"thoughts\" etc. I'd like to start putting together some meaningful slash pages for myself... I'm not sure what to put out there yet but this thought can serve as a todo for me\n\n---\n\n!!! note\n    This is one of [[ my-thoughts ]]. I picked this up from [Waylon Walker](https://waylonwalker.com)(https://thoughts.waylonwalker.com). It's a short note that I make about someone else's content online.  Learn more about the process \u003ca href=\"/thoughts/\" class=\"wikilink\" data-title=\"Thoughts\" data-description=\"My thoughts are found at my thoughts feed\" data-date=\"2025-06-09\"\u003eThoughts\u003c/a\u003e\n",
      "summary": "Waylon's mentioned slash pages before, but as I work on building my blog again and writing more regularly thanks to \"tho",
      "date_published": "2025-06-12T11:41:43Z",
      "date_modified": "2025-06-12T11:41:43Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "blog",
        "slash-pages",
        "til",
        "todo",
        "thoughts"
      ]
    },
    {
      "id": "https://pype.dev/double-check-your-cidr-blocks/",
      "url": "https://pype.dev/double-check-your-cidr-blocks/",
      "title": "Double check your CIDR blocks!",
      "content_html": "\u003ch1 id=\"tl-dr\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003etl;dr\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eCopy paste - don\u0026rsquo;t rely on typing correctly\u0026hellip;\u003c/p\u003e\n\u003ch1 id=\"the-issue\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe Issue\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eI use tailscale at home and generally love it. One thing I use it for is\nwhitelisting - so I have some apps publically available and my homelab ones\nhave DNS with SSL certs by LE but I whitelist to only my private IPs so that I\ncan use https at home basically. However I have had a few issues\nwith a couple apps, and I \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of chaulked it up to \u0026ldquo;network protocol this\u0026rdquo;\n\u0026ldquo;need to figure out that header that\u0026rdquo; but ultimately I was being lazy and the\nissues weren\u0026rsquo;t catastrophic\u0026hellip; it was things like:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003ecouldn\u0026rsquo;t get to my NVR by DNS, so I\u0026rsquo;d have to use tailscale_ip:port\u003c/li\u003e\n\u003cli\u003ecouldn\u0026rsquo;t login with my user in jellyfin cause I lock me down to LAN only\nsince I dont\u0026rsquo; use a password\u003c/li\u003e\n\u003cli\u003egotify wouldn\u0026rsquo;t connect using DNS\u003c/li\u003e\n\u003c/ol\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThese things aren\u0026rsquo;t huge but they are annoying and today I decided to figure it out.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"typo\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTypo!\u003c/span\u003e \u003ca href=\"#typo\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m not an expert yet but I think the main problem was the CIDR block I was\nusing in my traefik config.\u003c/p\u003e\n\u003cp\u003eThis\u0026rsquo;ll shock some of you but \u003ccode\u003e10.64.0.0/10\u003c/code\u003e and \u003ccode\u003e100.64.0.0/10\u003c/code\u003e are not the\nsame blocks!\u003c/p\u003e\n\u003cdiv class=\"admonition warning\"\u003e\n\u003cp class=\"admonition-title\"\u003eCheck your CIDRs\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s always DNS, and even when it\u0026rsquo;s not, it\u0026rsquo;s still probably networking\u003c/p\u003e\n\u003c/div\u003e\n",
      "content_text": "\n# tl;dr\n\nCopy paste - don't rely on typing correctly...\n\n# The Issue\n\nI use tailscale at home and generally love it. One thing I use it for is\nwhitelisting - so I have some apps publically available and my homelab ones\nhave DNS with SSL certs by LE but I whitelist to only my private IPs so that I\ncan use https at home basically. However I have had a few issues\nwith a couple apps, and I kind of chaulked it up to \"network protocol this\"\n\"need to figure out that header that\" but ultimately I was being lazy and the\nissues weren't catastrophic... it was things like:\n\n1. couldn't get to my NVR by DNS, so I'd have to use tailscale_ip:port\n2. couldn't login with my user in jellyfin cause I lock me down to LAN only\n   since I dont' use a password\n3. gotify wouldn't connect using DNS\n\n\u003e These things aren't huge but they are annoying and today I decided to figure it out.\n\n## Typo!\n\nI'm not an expert yet but I think the main problem was the CIDR block I was\nusing in my traefik config.\n\nThis'll shock some of you but `10.64.0.0/10` and `100.64.0.0/10` are not the\nsame blocks!\n\n!!! warning \"Check your CIDRs\"\n    It's always DNS, and even when it's not, it's still probably networking\n",
      "summary": "Copy paste - don't rely on typing correctly...",
      "date_published": "2025-06-07T07:27:31Z",
      "date_modified": "2025-06-07T07:27:31Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "tailscale",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/backups-are-dope/",
      "url": "https://pype.dev/backups-are-dope/",
      "title": "Backups are dope",
      "content_html": "\u003cp\u003eI accidently chown\u0026rsquo;d -R an app directory and it totally screwed up the database\nfolder. Luckily I zfs replicate my \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e volumes to another drive even on the\nsame host, so a quick [rsync] from /harbor/encrypted/docker/manyfold to\n/tank/encrypted/docker/manyfold got me back up and running since I hadn\u0026rsquo;t\nreplicated the messed up permission set yet\u003c/p\u003e\n\u003cp\u003eQ: How to link to my rsync like a pro post?\u003c/p\u003e\n\u003cp\u003eA: Wikilinks \u003ccode\u003e\u0026lt;a href=\u0026quot;/rsync-like-a-pro/\u0026quot; class=\u0026quot;wikilink\u0026quot; data-title=\u0026quot;rsync like a pro\u0026quot; data-description=\u0026quot;I am moving a hefty amount of data to a new ZFS pool due to some corruption and I want to avoid using for this just to make sure I don\u0026amp;#39;t propagate any...\u0026quot; data-date=\u0026quot;2024-12-11\u0026quot;\u0026gt;rsync like a pro\u0026lt;/a\u0026gt;\u003c/code\u003e \u003ca href=\"/rsync-like-a-pro/\" class=\"wikilink\" data-title=\"rsync like a pro\" data-description=\"I am moving a hefty amount of data to a new ZFS pool due to some corruption and I want to avoid using for this just to make sure I don\u0026#39;t propagate any...\" data-date=\"2024-12-11\" data-preview=\"I am moving a hefty amount of data to a new ZFS pool due to some corruption and I want to avoid using for this just to make sure I don\u0026#39;t propagate any...\"\u003ersync like a pro\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nI accidently chown'd -R an app directory and it totally screwed up the database\nfolder. Luckily I zfs replicate my docker volumes to another drive even on the\nsame host, so a quick [rsync] from /harbor/encrypted/docker/manyfold to\n/tank/encrypted/docker/manyfold got me back up and running since I hadn't\nreplicated the messed up permission set yet\n\nQ: How to link to my rsync like a pro post?\n\nA: Wikilinks `\u003ca href=\"/rsync-like-a-pro/\" class=\"wikilink\" data-title=\"rsync like a pro\" data-description=\"I am moving a hefty amount of data to a new ZFS pool due to some corruption and I want to avoid using for this just to make sure I don\u0026#39;t propagate any...\" data-date=\"2024-12-11\"\u003ersync like a pro\u003c/a\u003e` \u003ca href=\"/rsync-like-a-pro/\" class=\"wikilink\" data-title=\"rsync like a pro\" data-description=\"I am moving a hefty amount of data to a new ZFS pool due to some corruption and I want to avoid using for this just to make sure I don\u0026#39;t propagate any...\" data-date=\"2024-12-11\"\u003ersync like a pro\u003c/a\u003e\n",
      "summary": "I accidently chown'd -R an app directory and it totally screwed up the database folder. Luckily I zfs replicate my docker volumes to another drive even on...",
      "date_published": "2025-05-27T19:42:27Z",
      "date_modified": "2025-05-27T19:42:27Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "tech",
        "homelab",
        "zfs",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/aurora-u-blue-zram-config/",
      "url": "https://pype.dev/aurora-u-blue-zram-config/",
      "title": "Aurora U-Blue ZRAM Config",
      "content_html": "\u003cp\u003eI keep running out of space with my swap getting maxxed out\u0026hellip; I don\u0026rsquo;t know why but U-Blue uses Zram already and apparently I can easily override the defaults:\u003c/p\u003e\n\u003cp\u003ein \u003ccode\u003eusr/lib/systemd/zram-generator.conf\u003c/code\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-conf\"\u003e\n# This config file enables a /dev/zram0 device with the default settings:\n# — size — same as available RAM or 8GB, whichever is less\n# — compression — most likely lzo-rle\n#\n# To disable, uninstall zram-generator-defaults or create empty\n# /etc/systemd/zram-generator.conf file.\n[zram0]\nzram-size = min(ram, 8192)\n\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd so I just made my own file in \u003ccode\u003e/etc/systemd/zram-generator.conf\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-conf\"\u003e\n[zram0]\nzram-size = min(ram, 16384)\n\n\u003c/code\u003e\u003c/pre\u003e\n",
      "content_text": "\nI keep running out of space with my swap getting maxxed out... I don't know why but U-Blue uses Zram already and apparently I can easily override the defaults:\n\nin `usr/lib/systemd/zram-generator.conf`\n\n```conf\n\n# This config file enables a /dev/zram0 device with the default settings:\n# — size — same as available RAM or 8GB, whichever is less\n# — compression — most likely lzo-rle\n#\n# To disable, uninstall zram-generator-defaults or create empty\n# /etc/systemd/zram-generator.conf file.\n[zram0]\nzram-size = min(ram, 8192)\n\n```\n\nAnd so I just made my own file in `/etc/systemd/zram-generator.conf`:\n\n```conf\n\n[zram0]\nzram-size = min(ram, 16384)\n\n```\n",
      "summary": "I keep running out of space with my swap getting maxxed out... I don't know why but U-Blue uses Zram already and apparently I can easily override the defaults:",
      "date_published": "2025-05-27T13:51:27Z",
      "date_modified": "2025-05-27T13:51:27Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "infrastructure",
        "infrastructure",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/pandas-dataframe-info-always-answers-and-is-sometimes-right/",
      "url": "https://pype.dev/pandas-dataframe-info-always-answers-and-is-sometimes-right/",
      "title": "Pandas.DataFrame info always answers and is sometimes right",
      "content_html": "\u003ch1 id=\"til\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTIL\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eToday I learned that the \u003ccode\u003e.info()\u003c/code\u003e of a \u003ccode\u003epandas.DataFrame\u003c/code\u003e will always\ngive you an answer, but it is wildly difficult to know how accurate it is, because it depends\non the underlying data types.\nIf everything is a NumPy Dtype, then the result you get is the amount of memory NumPy is using\nunder the hood, which is very accurate.\nBut as soon as you have something like a Python string in the data frame, now pandas will\ntell you how much memory the NumPy data types are using, and it will give you some quick\nestimate of how much memory space the Python string types are using because numpy is just storing\na pointer to that data in memory, but this is far less\naccurate because it doesn\u0026rsquo;t actually know what the strings are.\u003c/p\u003e\n\u003ch2 id=\"memory-usage-deep-or-true\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eMemory Usage Deep or True\u003c/span\u003e \u003ca href=\"#memory-usage-deep-or-true\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTo get an honest answer about a DataFrame\u0026rsquo;s memory usage, you need to pass \u003ccode\u003ememory_usage='deep'\u003c/code\u003e to the \u003ccode\u003e.info()\u003c/code\u003e method.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# This is the default\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eIn \u003cspan class=\"o\"\u003e[\u003c/span\u003e50\u003cspan class=\"o\"\u003e]\u003c/span\u003e: df.info\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ememory_usage\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003eTrue\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u0026lt;class \u003cspan class=\"s1\"\u003e\u0026#39;pandas.core.frame.DataFrame\u0026#39;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eRangeIndex: \u003cspan class=\"m\"\u003e10\u003c/span\u003e entries, \u003cspan class=\"m\"\u003e0\u003c/span\u003e to \u003cspan class=\"m\"\u003e9\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eData columns \u003cspan class=\"o\"\u003e(\u003c/span\u003etotal \u003cspan class=\"m\"\u003e3\u003c/span\u003e columns\u003cspan class=\"o\"\u003e)\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"c1\"\u003e#   Column  Non-Null Count  Dtype \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e---  ------  --------------  ----- \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e   s1      \u003cspan class=\"m\"\u003e10\u003c/span\u003e non-null     int64 \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e   s2      \u003cspan class=\"m\"\u003e10\u003c/span\u003e non-null     int64 \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e   s3      \u003cspan class=\"m\"\u003e10\u003c/span\u003e non-null     object\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edtypes: int64\u003cspan class=\"o\"\u003e(\u003c/span\u003e2\u003cspan class=\"o\"\u003e)\u003c/span\u003e, object\u003cspan class=\"o\"\u003e(\u003c/span\u003e1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ememory usage: 372.0+ bytes\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice that the memory usage has a \u003ccode\u003e+\u003c/code\u003e in it? You can force a deeper analysis by passing \u003ccode\u003ememory_usage='deep'\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eIn \u003cspan class=\"o\"\u003e[\u003c/span\u003e51\u003cspan class=\"o\"\u003e]\u003c/span\u003e: df.info\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ememory_usage\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;deep\u0026#39;\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u0026lt;class \u003cspan class=\"s1\"\u003e\u0026#39;pandas.core.frame.DataFrame\u0026#39;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eRangeIndex: \u003cspan class=\"m\"\u003e10\u003c/span\u003e entries, \u003cspan class=\"m\"\u003e0\u003c/span\u003e to \u003cspan class=\"m\"\u003e9\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eData columns \u003cspan class=\"o\"\u003e(\u003c/span\u003etotal \u003cspan class=\"m\"\u003e3\u003c/span\u003e columns\u003cspan class=\"o\"\u003e)\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"c1\"\u003e#   Column  Non-Null Count  Dtype \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e---  ------  --------------  ----- \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"m\"\u003e0\u003c/span\u003e   s1      \u003cspan class=\"m\"\u003e10\u003c/span\u003e non-null     int64 \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e   s2      \u003cspan class=\"m\"\u003e10\u003c/span\u003e non-null     int64 \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e   s3      \u003cspan class=\"m\"\u003e10\u003c/span\u003e non-null     object\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edtypes: int64\u003cspan class=\"o\"\u003e(\u003c/span\u003e2\u003cspan class=\"o\"\u003e)\u003c/span\u003e, object\u003cspan class=\"o\"\u003e(\u003c/span\u003e1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ememory usage: 792.0 bytes\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\n# TIL\n\nToday I learned that the `.info()` of a `pandas.DataFrame` will always\ngive you an answer, but it is wildly difficult to know how accurate it is, because it depends\non the underlying data types.\nIf everything is a NumPy Dtype, then the result you get is the amount of memory NumPy is using\nunder the hood, which is very accurate.\nBut as soon as you have something like a Python string in the data frame, now pandas will\ntell you how much memory the NumPy data types are using, and it will give you some quick\nestimate of how much memory space the Python string types are using because numpy is just storing \na pointer to that data in memory, but this is far less\naccurate because it doesn't actually know what the strings are.\n\n## Memory Usage Deep or True\n\nTo get an honest answer about a DataFrame's memory usage, you need to pass `memory_usage='deep'` to the `.info()` method. \n\n```bash\n# This is the default\nIn [50]: df.info(memory_usage=True)\n\u003cclass 'pandas.core.frame.DataFrame'\u003e\nRangeIndex: 10 entries, 0 to 9\nData columns (total 3 columns):\n #   Column  Non-Null Count  Dtype \n---  ------  --------------  ----- \n 0   s1      10 non-null     int64 \n 1   s2      10 non-null     int64 \n 2   s3      10 non-null     object\ndtypes: int64(2), object(1)\nmemory usage: 372.0+ bytes\n```\n\nNotice that the memory usage has a `+` in it? You can force a deeper analysis by passing `memory_usage='deep'`\n\n```bash\nIn [51]: df.info(memory_usage='deep')\n\u003cclass 'pandas.core.frame.DataFrame'\u003e\nRangeIndex: 10 entries, 0 to 9\nData columns (total 3 columns):\n #   Column  Non-Null Count  Dtype \n---  ------  --------------  ----- \n 0   s1      10 non-null     int64 \n 1   s2      10 non-null     int64 \n 2   s3      10 non-null     object\ndtypes: int64(2), object(1)\nmemory usage: 792.0 bytes\n```\n\n\n",
      "summary": "Today I learned that the of a will always give you an answer, but it is wildly difficult to know how accurate it is, because it depends on the underlying...",
      "date_published": "2025-05-26T09:08:08Z",
      "date_modified": "2025-05-26T09:08:08Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/searxng/",
      "url": "https://pype.dev/searxng/",
      "title": "SearXNG",
      "content_html": "\u003cp\u003eI heard about \u003ca href=\"https://searxng.org\"\u003eSearXNG\u003c/a\u003e on a couple podcasts and saw it trending on GitHub\nseveral times before I finally decided to stand it up.\nI used it transparently when trying out \u003ca href=\"https://khoj.dev/\"\u003ekhoj\u003c/a\u003e, a self-hosted AI LLM agent\nplayground \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of a thing, but I\u0026rsquo;ve also been messing around with \u003ca href=\"https://openwebui.com\"\u003eOpen-WebUI\u003c/a\u003e to\nhave a self-hosted ChatGPT-like experience.\nI don\u0026rsquo;t know if SearchXNG used to be harder to set up, but it was pretty simple with a \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003eDocker\u003c/a\u003e Compose up\nand a couple configuration options given my personal homelab setup. Using it feels\nvery nice.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eservices\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eredis\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003econtainer_name\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eredis\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edocker.io/valkey/valkey:8-alpine\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003evalkey-server --save 30 1 --loglevel warning\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003erestart\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eunless-stopped\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evolumes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"l\"\u003e./data/redis:/data\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003elogging\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003edriver\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;json-file\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003emax-size\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;1m\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003emax-file\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003esearxng\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003econtainer_name\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003esearxng\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edocker.io/searxng/searxng:latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003erestart\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eunless-stopped\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eports\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"s2\"\u003e\u0026#34;8080:8080\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evolumes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"l\"\u003e./data/searxng:/etc/searxng:rw\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenv_file\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e.env\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"c\"\u003e# - SEARXNG_BASE_URL=https://${SEARXNG_HOSTNAME:-localhost}/\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"l\"\u003eUWSGI_WORKERS=${SEARXNG_UWSGI_WORKERS:-4}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"l\"\u003eUWSGI_THREADS=${SEARXNG_UWSGI_THREADS:-4}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003elogging\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003edriver\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;json-file\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003emax-size\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;1m\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003emax-file\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI love the self-hosted aspect. I love not seeing any ads on my search\nresults. I did a few searches where I know what results to expect and\nit did okay.\nIt is good right now at filtering out garbage in my results.\u003c/p\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://dropper.wayl.one/api/file/4ff5953c-bc8d-4978-8509-b43582460a0b.png\" alt=\"SearXNG\"\u003e\n\u003c/figure\u003e\n\u003cp\u003eSo I look forward to tweaking it and using it as a search\nbackend with open web UI. Next on my list is having enough resources to run\nOllama and a stable diffusion generator at the same time and have image\ngeneration working through open web UI.\u003c/p\u003e\n",
      "content_text": "\n\nI heard about [SearXNG](https://searxng.org) on a couple podcasts and saw it trending on GitHub\nseveral times before I finally decided to stand it up. \nI used it transparently when trying out [khoj](https://khoj.dev/), a self-hosted AI LLM agent\nplayground kind of a thing, but I've also been messing around with [Open-WebUI](https://openwebui.com) to\nhave a self-hosted ChatGPT-like experience. \nI don't know if SearchXNG used to be harder to set up, but it was pretty simple with a Docker Compose up\nand a couple configuration options given my personal homelab setup. Using it feels\nvery nice. \n\n```yaml\nservices:\n  redis:\n    container_name: redis\n    image: docker.io/valkey/valkey:8-alpine\n    command: valkey-server --save 30 1 --loglevel warning\n    restart: unless-stopped\n    volumes:\n      - ./data/redis:/data\n    logging:\n      driver: \"json-file\"\n      options:\n        max-size: \"1m\"\n        max-file: \"1\"\n\n  searxng:\n    container_name: searxng\n    image: docker.io/searxng/searxng:latest\n    restart: unless-stopped\n    ports:\n      - \"8080:8080\"\n    volumes:\n      - ./data/searxng:/etc/searxng:rw\n    env_file: .env\n    environment:\n      # - SEARXNG_BASE_URL=https://${SEARXNG_HOSTNAME:-localhost}/\n      - UWSGI_WORKERS=${SEARXNG_UWSGI_WORKERS:-4}\n      - UWSGI_THREADS=${SEARXNG_UWSGI_THREADS:-4}\n    logging:\n      driver: \"json-file\"\n      options:\n        max-size: \"1m\"\n        max-file: \"1\"\n```\n\nI love the self-hosted aspect. I love not seeing any ads on my search\nresults. I did a few searches where I know what results to expect and\nit did okay. \nIt is good right now at filtering out garbage in my results. \n\n![SearXNG](https://dropper.wayl.one/api/file/4ff5953c-bc8d-4978-8509-b43582460a0b.png)\n\nSo I look forward to tweaking it and using it as a search\nbackend with open web UI. Next on my list is having enough resources to run\nOllama and a stable diffusion generator at the same time and have image\ngeneration working through open web UI.\n\n",
      "summary": "I heard about SearXNG on a couple podcasts and saw it trending on GitHub several times before I finally decided to stand it up. I used it transparently when...",
      "date_published": "2025-05-25T08:33:10Z",
      "date_modified": "2025-05-25T08:33:10Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/grafana-gotify-for-push-notifications/",
      "url": "https://pype.dev/grafana-gotify-for-push-notifications/",
      "title": "Grafana + Gotify for push notifications",
      "content_html": "\u003cp\u003eIt\u0026rsquo;s easy to configure push notifications through Gotify from Grafana by setting up a generic \u003ccode\u003eWebhook\u003c/code\u003e Alerting Contact Point\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThe parameters are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eHTTP Method: POST\u003c/li\u003e\n\u003cli\u003eURL: http://\u003cgotify url\u003e/message\u003c/li\u003e\n\u003cli\u003eAuthentication is done with an app token generated from Gotify, and you put it in \u003ccode\u003eAuthorization Header: Credentials\u003c/code\u003e in the Grafana configuration\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ca href=\"https://dropper.wayl.one/api/file/610b4efb-b4c4-4930-ac63-499d501ba3c3.png\"\u003eSee image\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nIt's easy to configure push notifications through Gotify from Grafana by setting up a generic `Webhook` Alerting Contact Point...\n\nThe parameters are:\n\n* HTTP Method: POST\n* URL: http://\u003cgotify url\u003e/message\n* Authentication is done with an app token generated from Gotify, and you put it in `Authorization Header: Credentials` in the Grafana configuration \n\n[See image](https://dropper.wayl.one/api/file/610b4efb-b4c4-4930-ac63-499d501ba3c3.png)\n",
      "summary": "It's easy to configure push notifications through Gotify from Grafana by setting up a generic Alerting Contact Point...",
      "date_published": "2025-04-07T10:12:51Z",
      "date_modified": "2025-04-07T10:12:51Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "infrastructure",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/monolith-download-and-save-site-as-html/",
      "url": "https://pype.dev/monolith-download-and-save-site-as-html/",
      "title": "Monolith - download and save site as html",
      "content_html": "\u003ch2 id=\"til-monolith\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTIL: Monolith\u003c/span\u003e \u003ca href=\"#til-monolith\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI needed to save a website earlier and this was just what I needed\u0026hellip;\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/Y2Z/monolith\"\u003ehttps://github.com/Y2Z/monolith\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\n## TIL: Monolith\n\nI needed to save a website earlier and this was just what I needed...\n\nhttps://github.com/Y2Z/monolith\n",
      "summary": "I needed to save a website earlier and this was just what I needed...",
      "date_published": "2025-03-16T16:03:31Z",
      "date_modified": "2025-03-16T16:03:31Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "cli",
        "terminal",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/smb-with-zfs-on-ubuntu/",
      "url": "https://pype.dev/smb-with-zfs-on-ubuntu/",
      "title": "SMB with ZFS on Ubuntu",
      "content_html": "\u003cp\u003esudo apt-get install -y samba \\ then set sharesmb=on \\ chown the user \\ smbpasswd \u003cuser\u003e -a \\ then mount with user/password from other machine\u003c/p\u003e\n",
      "content_text": "\nsudo apt-get install -y samba \\\\ then set sharesmb=on \\\\ chown the user \\\\ smbpasswd \u003cuser\u003e -a \\\\ then mount with user/password from other machine\n",
      "summary": "sudo apt-get install -y samba \\\\ then set sharesmb=on \\\\ chown the user \\\\ smbpasswd -a \\\\ then mount with user/password from other machine",
      "date_published": "2025-02-20T09:18:35Z",
      "date_modified": "2025-02-20T09:18:35Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "zfs",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/using-my-own-domain-on-nostr/",
      "url": "https://pype.dev/using-my-own-domain-on-nostr/",
      "title": "Using my own domain on nostr",
      "content_html": "\u003cp\u003eAfter generating a keypair on a nostr app (I started with primal) I copied my\npublic key to \u003ccode\u003e/.well-known/nostr.json\u003c/code\u003e and put that at the root of my site at\n\u003ccode\u003ehttps://pype.dev\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThe file looks like this:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"err\"\u003e“names”:\u003c/span\u003e \u003cspan class=\"err\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e“nic”:\u003c/span\u003e \u003cspan class=\"err\"\u003e“npub1q3fsec2vcv99v80ga72dlv90qwkqmuxqcr6mdyumcmpkgudlhrespyurfj”\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSo now I can use \u003ccode\u003enic@pype.dev\u003c/code\u003e as on nostr because the client will verify the\npubkey associated with my profile with my domain!\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003ebig upgrade from \u003cwhatever\u003e@primal.net\u003c/p\u003e\n\u003c/blockquote\u003e\n",
      "content_text": "\nAfter generating a keypair on a nostr app (I started with primal) I copied my\npublic key to `/.well-known/nostr.json` and put that at the root of my site at\n`https://pype.dev`.\n\nThe file looks like this:\n\n```json\n\n{\n  “names”: {\n    “nic”: “npub1q3fsec2vcv99v80ga72dlv90qwkqmuxqcr6mdyumcmpkgudlhrespyurfj”\n  }\n}\n```\nSo now I can use `nic@pype.dev` as on nostr because the client will verify the\npubkey associated with my profile with my domain!\n\n\u003e big upgrade from \u003cwhatever\u003e@primal.net\n",
      "summary": "After generating a keypair on a nostr app (I started with primal) I copied my public key to and put that at the root of my site at .",
      "date_published": "2025-01-24T06:28:19Z",
      "date_modified": "2025-01-24T06:28:19Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "nostr",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/deployments-are-not-statefulsets/",
      "url": "https://pype.dev/deployments-are-not-statefulsets/",
      "title": "Deployments are not StatefulSets",
      "content_html": "\u003cp\u003eI was debugging some ArgoCD stuff earlier today and I love using k9s to explore\nmy k8s resources.\u003c/p\u003e\n\u003cp\u003eThe TLDR is that I put some bad env vars in the \u003ccode\u003eglobal\u003c/code\u003e values for my ArgoCD\npods, this caused the webUI to hang. An easy way to push things through is to\nopen k9s, view the Deployments, and edit the env vars there so the pods can be\nin the right state to restart and pickup the changes (you\u0026rsquo;ve obviously fixed\nthe values file already right?)\u003c/p\u003e\n\u003cp\u003eWell the Notification Controller was the pod hanging, and I couldn\u0026rsquo;t find it in\nthe Deployments view! This blew my mind\u0026hellip; until I realized that it was a\nStateful Set.\u003c/p\u003e\n\u003cp\u003eSo, this is not a comparison of the 2 - this is just a note to say that all I\nhad to do was find the StatefulSets in k9s and do the same workflow for editing\nwhat I needed to edit to get the pods into workable state while working on the\nvalues file\u0026hellip;\u003c/p\u003e\n",
      "content_text": "\nI was debugging some ArgoCD stuff earlier today and I love using k9s to explore\nmy k8s resources.\n\nThe TLDR is that I put some bad env vars in the `global` values for my ArgoCD\npods, this caused the webUI to hang. An easy way to push things through is to\nopen k9s, view the Deployments, and edit the env vars there so the pods can be\nin the right state to restart and pickup the changes (you've obviously fixed\nthe values file already right?)\n\nWell the Notification Controller was the pod hanging, and I couldn't find it in\nthe Deployments view! This blew my mind... until I realized that it was a\nStateful Set.\n\nSo, this is not a comparison of the 2 - this is just a note to say that all I\nhad to do was find the StatefulSets in k9s and do the same workflow for editing\nwhat I needed to edit to get the pods into workable state while working on the\nvalues file...\n",
      "summary": "I was debugging some ArgoCD stuff earlier today and I love using k9s to explore my k8s resources.",
      "date_published": "2025-01-15T18:07:57Z",
      "date_modified": "2025-01-15T18:07:57Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "infrastructure",
        "tech",
        "k8s",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/docker-context-and-an-issue-to-question-your-sanity/",
      "url": "https://pype.dev/docker-context-and-an-issue-to-question-your-sanity/",
      "title": "docker context (and an issue to question your sanity)",
      "content_html": "\u003cp\u003e\u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e contexts are great, would recommend putting them in your prompt though (via starship or something else)\u0026hellip; here\u0026rsquo;s why\u003c/p\u003e\n\u003cp\u003eI like to manage my containers remotely - I have a nice development setup on my desktop and I try to keep my server as bare-bones as possible. For a while I\u0026rsquo;ve been using ansible which makes it easy to manage configuration etc on other machines. But I recently learned about docker contexts and I\u0026rsquo;m planning to scale down my homelab management to just docker-compose stacks rather than a bunch of super complicated ansible playbooks\u003c/p\u003e\n\u003cp\u003eSo, setting up a context is easy - it\u0026rsquo;s basically an ssh connection to another machine!\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edocker context create koober --docker \u0026quot;host=ssh://nic@koober\u0026quot;\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ekoober\u003c/code\u003e is one of my dev machines and my \u003ccode\u003e~/.ssh/config\u003c/code\u003e is setup such that I can \u003ccode\u003essh nic@koober\u003c/code\u003e, this makes the context work really seamlessly.\u003c/p\u003e\n\u003cp\u003eSo there\u0026rsquo;s the \u003ccode\u003edefault\u003c/code\u003e context (the machine you\u0026rsquo;re on) and now I have \u003ccode\u003ekoober\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eTo use it you run \u003ccode\u003edocker context use koober\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eAnd then to check we can \u003ccode\u003els\u003c/code\u003e the contexts\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003edocker context ls\nNAME       DESCRIPTION                               DOCKER ENDPOINT               ERROR\ndefault    Current DOCKER_HOST based configuration   unix:///var/run/docker.sock   \nkoober *                                             ssh://nic@koober\n\u003c/code\u003e\u003c/pre\u003e\n\u003cblockquote\u003e\n\u003cp\u003eNotice the * - that indicates that\u0026rsquo;s our current context.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"trouble\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTrouble\u003c/span\u003e \u003ca href=\"#trouble\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eNow here\u0026rsquo;s where things get hairy\u0026hellip; you\u0026rsquo;ve gotta be super-aware of what context you\u0026rsquo;re using. I have an indicator in my starship prompt that shows the current context, but since I\u0026rsquo;m new to using them I \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of didn\u0026rsquo;t notice it until I ran into this issue\u0026hellip;\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m working on a python application in docker but was not able to execute the entrypoint even though I KNEW the file was there\u0026hellip; let\u0026rsquo;s take a look\u003c/p\u003e\n\u003ch3 id=\"example\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eExample\u003c/span\u003e \u003ca href=\"#example\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eHere\u0026rsquo;s a minimal hello world applycation in docker to illustrate the issue\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# main.py\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;hello world\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Use an official Python runtime as a parent image\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eFROM\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003epython:3.11-slim\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Set the working directory in the container\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eWORKDIR\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e/app\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Copy the current directory contents into the container at /app\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eCOPY\u003c/span\u003e . /app\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Run the main.py script as the container\u0026#39;s main process\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eCMD\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;python\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;main.py\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eservices\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ehello-world\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ebuild\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evolumes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"l\"\u003e.:/app\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice volume mounting in my project directory \u003ccode\u003e.\u003c/code\u003e to \u003ccode\u003e/app\u003c/code\u003e as a common practice to develop inside the container\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s where I started to question my sanity\u0026hellip;\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e✗ docker compose up                                            \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e+\u003cspan class=\"o\"\u003e]\u003c/span\u003e Running 1/0\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e ✔ Container docker-context-example-hello-world-1  Created                                                                                                                                                                                                                                                              0.0s \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eAttaching to hello-world-1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehello-world-1  \u003cspan class=\"p\"\u003e|\u003c/span\u003e python: can\u003cspan class=\"s1\"\u003e\u0026#39;t open file \u0026#39;\u003c/span\u003e/app/main.py\u003cspan class=\"err\"\u003e\u0026#39;\u003c/span\u003e: \u003cspan class=\"o\"\u003e[\u003c/span\u003eErrno 2\u003cspan class=\"o\"\u003e]\u003c/span\u003e No such file or directory\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehello-world-1 exited with code \u003cspan class=\"m\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003epython can't open file\u003c/code\u003e? hmm\u0026hellip; Let\u0026rsquo;s take a look at the image\u003c/p\u003e\n\u003cp\u003eFirst let\u0026rsquo;s make sure we get the image name right\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e✗ docker container ls -a              \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCONTAINER ID   IMAGE                                           COMMAND                  CREATED          STATUS                       PORTS                                                                                  NAMES\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e3d7285fc39e2   docker-context-example-hello-world              \u003cspan class=\"s2\"\u003e\u0026#34;python main.py\u0026#34;\u003c/span\u003e         \u003cspan class=\"m\"\u003e10\u003c/span\u003e minutes ago   Exited \u003cspan class=\"o\"\u003e(\u003c/span\u003e2\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"m\"\u003e44\u003c/span\u003e seconds ago                                                                                           docker-context-example-hello-world-1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow we can \u003ccode\u003edocker run --rm -it --entrypoint /bin/bash --name debug docker-context-example-hello-world\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ docker run --rm -it --entrypoint /bin/bash --name debug docker-context-example-hello-world  \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eroot@ee46d0e22de8:/app# python main.py\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eHello, World!\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eroot@ee46d0e22de8:/app# \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWHAT THE HECK??\u003c/p\u003e\n\u003ch2 id=\"what-happened\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhat happened…\u003c/span\u003e \u003ca href=\"#what-happened\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eWhat happened turns out to be pretty simple once we realize I\u0026rsquo;m using contexts\u0026hellip;\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ekoboer\u003c/code\u003e is a remote context, the \u003ccode\u003edocker run\u003c/code\u003e and \u003ccode\u003edocker compose up\u003c/code\u003e commands are interacting with the docker socket on that machine.\u003c/p\u003e\n\u003cp\u003eSo if I compose up the stack notice that there\u0026rsquo;s a volume bind mount in there - well those do \u003cem\u003enot\u003c/em\u003e work with contexts (or at least I\u0026rsquo;m not aware of hose to make it work) and so the \u003ccode\u003e/app\u003c/code\u003e directory was getting blown away essentially with an empty overlay\u0026hellip;\u003c/p\u003e\n\u003cp\u003eBut when running with just \u003ccode\u003edocker run\u003c/code\u003e with no volume mount, the code was copied in during the build and is right where we expect it\u0026hellip;\u003c/p\u003e\n\u003ch3 id=\"let-s-prove-it\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eLet’s prove it\u003c/span\u003e \u003ca href=\"#let-s-prove-it\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h3\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ docker run --rm -it --entrypoint /bin/bash --name debug -v .:/app docker-context-example-hello-world\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eroot@903f591c0384:/app# python main.py\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython: can\u003cspan class=\"s1\"\u003e\u0026#39;t open file \u0026#39;\u003c/span\u003e/app/main.py\u003cspan class=\"err\"\u003e\u0026#39;\u003c/span\u003e: \u003cspan class=\"o\"\u003e[\u003c/span\u003eErrno 2\u003cspan class=\"o\"\u003e]\u003c/span\u003e No such file or directory\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eroot@903f591c0384:/app# \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cpre\u003e\u003ccode\u003eAdding a `-v .:/app` to match the compose file, we get the same error...\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIf we switch to the default context we are back up and running as expected\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e✗ docker context use default\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edefault\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCurrent context is now \u003cspan class=\"s2\"\u003e\u0026#34;default\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003enic in /tmp/docker-context-example  via   v3.13.0  \u003cspan class=\"o\"\u003e(\u003c/span\u003edev\u003cspan class=\"o\"\u003e)\u003c/span\u003e 󰒄 󱔎 NO PYTHON ENVIORNMENT SET \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ docker compose up         \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e+\u003cspan class=\"o\"\u003e]\u003c/span\u003e Running 1/0\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e ✔ Container docker-context-example-hello-world-1  Created                                                                                                                                                                                                                                                              0.0s \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eAttaching to hello-world-1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehello-world-1  \u003cspan class=\"p\"\u003e|\u003c/span\u003e Hello, World!\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehello-world-1 exited with code \u003cspan class=\"m\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003enic in /tmp/docker-context-example  via   v3.13.0  \u003cspan class=\"o\"\u003e(\u003c/span\u003edev\u003cspan class=\"o\"\u003e)\u003c/span\u003e 󰒄 󱔎 NO PYTHON ENVIORNMENT SET \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ docker run --rm -it --entrypoint /bin/bash --name debug -v .:/app docker-context-example-hello-world\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eroot@4045b6aa8883:/app# python main.py\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eHello, World!\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eroot@4045b6aa8883:/app# \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSuccessful runs on both accounts with the volume mount\u003c/p\u003e\n\u003ch2 id=\"tldr\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTLDR\u003c/span\u003e \u003ca href=\"#tldr\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eContext is king\u003c/p\u003e\n",
      "content_text": "\ndocker contexts are great, would recommend putting them in your prompt though (via starship or something else)... here's why\n\nI like to manage my containers remotely - I have a nice development setup on my desktop and I try to keep my server as bare-bones as possible. For a while I've been using ansible which makes it easy to manage configuration etc on other machines. But I recently learned about docker contexts and I'm planning to scale down my homelab management to just docker-compose stacks rather than a bunch of super complicated ansible playbooks\n\nSo, setting up a context is easy - it's basically an ssh connection to another machine!\n\n`docker context create koober --docker \"host=ssh://nic@koober\"`\n\n`koober` is one of my dev machines and my `~/.ssh/config` is setup such that I can `ssh nic@koober`, this makes the context work really seamlessly.\n\nSo there's the `default` context (the machine you're on) and now I have `koober`\n\nTo use it you run `docker context use koober`\n\nAnd then to check we can `ls` the contexts\n\n```\ndocker context ls\nNAME       DESCRIPTION                               DOCKER ENDPOINT               ERROR\ndefault    Current DOCKER_HOST based configuration   unix:///var/run/docker.sock   \nkoober *                                             ssh://nic@koober\n```\n\n\u003e Notice the * - that indicates that's our current context.\n\n## Trouble\n\nNow here's where things get hairy... you've gotta be super-aware of what context you're using. I have an indicator in my starship prompt that shows the current context, but since I'm new to using them I kind of didn't notice it until I ran into this issue...\n\nI'm working on a python application in docker but was not able to execute the entrypoint even though I KNEW the file was there... let's take a look\n\n### Example\n\nHere's a minimal hello world applycation in docker to illustrate the issue\n\n```python\n# main.py\nprint(\"hello world\")\n```\n\n```Dockerfile\n# Use an official Python runtime as a parent image\nFROM python:3.11-slim\n\n# Set the working directory in the container\nWORKDIR /app\n\n# Copy the current directory contents into the container at /app\nCOPY . /app\n\n# Run the main.py script as the container's main process\nCMD [\"python\", \"main.py\"]\n```\n\n```yaml\nservices:\n  hello-world:\n    build: .\n    volumes:\n      - .:/app\n```\n\nNotice volume mounting in my project directory `.` to `/app` as a common practice to develop inside the container\n\nHere's where I started to question my sanity...\n\n```bash\n✗ docker compose up                                            \n[+] Running 1/0\n ✔ Container docker-context-example-hello-world-1  Created                                                                                                                                                                                                                                                              0.0s \nAttaching to hello-world-1\nhello-world-1  | python: can't open file '/app/main.py': [Errno 2] No such file or directory\nhello-world-1 exited with code 2\n```\n\n`python can't open file`? hmm... Let's take a look at the image\n\nFirst let's make sure we get the image name right\n\n```bash\n✗ docker container ls -a              \nCONTAINER ID   IMAGE                                           COMMAND                  CREATED          STATUS                       PORTS                                                                                  NAMES\n3d7285fc39e2   docker-context-example-hello-world              \"python main.py\"         10 minutes ago   Exited (2) 44 seconds ago                                                                                           docker-context-example-hello-world-1\n```\n\nNow we can `docker run --rm -it --entrypoint /bin/bash --name debug docker-context-example-hello-world`\n\n```bash\n❯ docker run --rm -it --entrypoint /bin/bash --name debug docker-context-example-hello-world  \nroot@ee46d0e22de8:/app# python main.py\nHello, World!\nroot@ee46d0e22de8:/app# \n```\n\nWHAT THE HECK??\n\n## What happened...\n\nWhat happened turns out to be pretty simple once we realize I'm using contexts...\n\n`koboer` is a remote context, the `docker run` and `docker compose up` commands are interacting with the docker socket on that machine.\n\nSo if I compose up the stack notice that there's a volume bind mount in there - well those do _not_ work with contexts (or at least I'm not aware of hose to make it work) and so the `/app` directory was getting blown away essentially with an empty overlay...\n\nBut when running with just `docker run` with no volume mount, the code was copied in during the build and is right where we expect it...\n\n### Let's prove it\n\n```bash\n❯ docker run --rm -it --entrypoint /bin/bash --name debug -v .:/app docker-context-example-hello-world\nroot@903f591c0384:/app# python main.py\npython: can't open file '/app/main.py': [Errno 2] No such file or directory\nroot@903f591c0384:/app# \n```\n```\nAdding a `-v .:/app` to match the compose file, we get the same error...\n```\n\nIf we switch to the default context we are back up and running as expected\n\n```bash\n✗ docker context use default\ndefault\nCurrent context is now \"default\"\n\nnic in /tmp/docker-context-example  via   v3.13.0  (dev) 󰒄 󱔎 NO PYTHON ENVIORNMENT SET \n❯ docker compose up         \n[+] Running 1/0\n ✔ Container docker-context-example-hello-world-1  Created                                                                                                                                                                                                                                                              0.0s \nAttaching to hello-world-1\nhello-world-1  | Hello, World!\nhello-world-1 exited with code 0\n\nnic in /tmp/docker-context-example  via   v3.13.0  (dev) 󰒄 󱔎 NO PYTHON ENVIORNMENT SET \n❯ docker run --rm -it --entrypoint /bin/bash --name debug -v .:/app docker-context-example-hello-world\nroot@4045b6aa8883:/app# python main.py\nHello, World!\nroot@4045b6aa8883:/app# \n```\n\nSuccessful runs on both accounts with the volume mount\n\n## TLDR\n\nContext is king\n",
      "summary": "docker contexts are great, would recommend putting them in your prompt though (via starship or something else)... here's why",
      "date_published": "2024-12-19T05:59:30Z",
      "date_modified": "2024-12-19T05:59:30Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "terminal",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/add-a-healthcheck-to-your-fastapi-app/",
      "url": "https://pype.dev/add-a-healthcheck-to-your-fastapi-app/",
      "title": "Add a healthcheck to your FastAPI app",
      "content_html": "\u003cp\u003eI\u0026rsquo;m building a few FastAPI apps to throw in \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e and run on my homelab\u0026hellip; I wanted to add healthchecks and here\u0026rsquo;s a simple way to do it\u003c/p\u003e\n\u003cp\u003eMake sure to install \u003ccode\u003ecurl\u003c/code\u003e in the dockerfile (near the top for effeciency)\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Install curl with minimal dependencies\nRUN apt-get update \u0026amp;\u0026amp; \\\n    apt-get install -y --no-install-recommends curl \u0026amp;\u0026amp; \\\n    apt-get clean \u0026amp;\u0026amp; \\\n    rm -rf /var/lib/apt/lists/*\n\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen I recommend making compose files even for single image deployments\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eservices:\n  app:\n    build: .\n    volumes:\n      - type: bind\n        source: .\n        target: /app\n    environment:\n      - PYTHONPATH=/app\n      - DOCKER_ENV=true\n      - UV_VIRTUALENV=/opt/app-env\n    user: \u0026quot;1000:1000\u0026quot;\n    restart: unless-stopped\n    healthcheck:\n      test: [\u0026quot;CMD\u0026quot;, \u0026quot;curl\u0026quot;, \u0026quot;-f\u0026quot;, \u0026quot;http://localhost:8000/health\u0026quot;]\n      interval: 30s\n      timeout: 10s\n      retries: 3\n      start_period: 10s\n\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen finally you\u0026rsquo;ll need a \u003ccode\u003e/health\u003c/code\u003e endpoint\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@app.get\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/health\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse_class\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eHTMLResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ehealth_check\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    A health check endpoint that returns a status message.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;html\u0026gt;\u0026lt;body\u0026gt;\u0026lt;h1\u0026gt;Service is healthy\u0026lt;/h1\u0026gt;\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nI'm building a few FastAPI apps to throw in docker and run on my homelab... I wanted to add healthchecks and here's a simple way to do it\n\nMake sure to install `curl` in the dockerfile (near the top for effeciency)\n\n```\n# Install curl with minimal dependencies\nRUN apt-get update \u0026\u0026 \\\n    apt-get install -y --no-install-recommends curl \u0026\u0026 \\\n    apt-get clean \u0026\u0026 \\\n    rm -rf /var/lib/apt/lists/*\n\n```\n\nThen I recommend making compose files even for single image deployments\n\n```\nservices:\n  app:\n    build: .\n    volumes:\n      - type: bind\n        source: .\n        target: /app\n    environment:\n      - PYTHONPATH=/app\n      - DOCKER_ENV=true\n      - UV_VIRTUALENV=/opt/app-env\n    user: \"1000:1000\"\n    restart: unless-stopped\n    healthcheck:\n      test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8000/health\"]\n      interval: 30s\n      timeout: 10s\n      retries: 3\n      start_period: 10s\n\n```\n\nThen finally you'll need a `/health` endpoint\n\n```python\n\n@app.get(\"/health\", response_class=HTMLResponse)\nasync def health_check():\n    \"\"\"\n    A health check endpoint that returns a status message.\n    \"\"\"\n    return \"\u003chtml\u003e\u003cbody\u003e\u003ch1\u003eService is healthy\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e\"\n\n```\n",
      "summary": "I'm building a few FastAPI apps to throw in docker and run on my homelab... I wanted to add healthchecks and here's a simple way to do it",
      "date_published": "2024-12-15T15:41:36Z",
      "date_modified": "2024-12-15T15:41:36Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/statically-io-to-help-me-out/",
      "url": "https://pype.dev/statically-io-to-help-me-out/",
      "title": "statically.io to help me out",
      "content_html": "\u003cp\u003e\u003ca href=\"https://statically.io\"\u003estatically.io\u003c/a\u003e offers a CDN that I\u0026rsquo;m going to try to lean into for images in my blog. My hope is that the builds get and stay fast, and that page loads are fast too.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eimg\u003c/span\u003e \u003cspan class=\"na\"\u003esrc\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-zsh.png\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003ealt\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Skimpy ZSH\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;A fancy data summary in the shell\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-zsh.png\" alt=\"Skimpy ZSH\" title=\"A fancy data summary in the shell\" /\u003e\n",
      "content_text": "\n[statically.io](https://statically.io) offers a CDN that I'm going to try to lean into for images in my blog. My hope is that the builds get and stay fast, and that page loads are fast too.\n\nExample:\n\n```html\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-zsh.png\" alt=\"Skimpy ZSH\" title=\"A fancy data summary in the shell\" /\u003e\n```\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-zsh.png\" alt=\"Skimpy ZSH\" title=\"A fancy data summary in the shell\" /\u003e\n",
      "summary": "statically.io offers a CDN that I'm going to try to lean into for images in my blog. My hope is that the builds get and stay fast, and that page loads are...",
      "date_published": "2024-12-14T13:35:31Z",
      "date_modified": "2024-12-14T13:35:31Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "infrastructure",
        "tech",
        "webdev",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/jellyfin-container-updates-for-hwe-amd/",
      "url": "https://pype.dev/jellyfin-container-updates-for-hwe-amd/",
      "title": "Jellyfin container updates for HWE + AMD",
      "content_html": "\u003cp\u003eI use LSIO Jelyfin container for the easy addon they provide for AMD GPUs but I couldn\u0026rsquo;t get trickplay to work with HWE\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThere was almost NOTHING on the internet about the error, and all the threads were about BSD systems\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThankfully someone posted \u003ca href=\"https://forum.jellyfin.org/t-jellyfin-amd-docker\"\u003eon the formum here\u003c/a\u003e but the only answer was to literally upgrade stuff in the container\u0026hellip;\u003c/p\u003e\n\u003cp\u003eSomeday maybe I\u0026rsquo;ll build off of LSIO to add this, but until then I shell\u0026rsquo;d in and homelab\u0026rsquo;d the hell out of it\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eTHIS IS INSIDE THE CONTAINER - I use Portianer to make it easy\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cpre\u003e\u003ccode\u003e\napt update \u0026amp;\u0026amp; apt install -y curl gpg\n\nmkdir -p /etc/apt/keyrings\n\ncurl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | gpg --dearmor -o /etc/apt/keyrings/rocm.gpg\n\ncat \u0026lt;\u0026lt;EOF | tee /etc/apt/sources.list.d/rocm.sources\n\nTypes: deb\n\nURIs: https://repo.radeon.com/rocm/apt/latest\n\nSuites: ubuntu\n\nComponents: main\n\nArchitectures: amd64\n\nSigned-By: /etc/apt/keyrings/rocm.gpg\n\nEOF\n\napt update \u0026amp;\u0026amp; apt install -y rocm-opencl-runtime\n\u003c/code\u003e\u003c/pre\u003e\n",
      "content_text": "\nI use LSIO Jelyfin container for the easy addon they provide for AMD GPUs but I couldn't get trickplay to work with HWE... \n\nThere was almost NOTHING on the internet about the error, and all the threads were about BSD systems...\n\nThankfully someone posted [on the formum here](https://forum.jellyfin.org/t-jellyfin-amd-docker) but the only answer was to literally upgrade stuff in the container...\n\nSomeday maybe I'll build off of LSIO to add this, but until then I shell'd in and homelab'd the hell out of it\n\n\u003e THIS IS INSIDE THE CONTAINER - I use Portianer to make it easy\n\n```\n\napt update \u0026\u0026 apt install -y curl gpg\n\nmkdir -p /etc/apt/keyrings\n\ncurl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | gpg --dearmor -o /etc/apt/keyrings/rocm.gpg\n\ncat \u003c\u003cEOF | tee /etc/apt/sources.list.d/rocm.sources\n\nTypes: deb\n\nURIs: https://repo.radeon.com/rocm/apt/latest\n\nSuites: ubuntu\n\nComponents: main\n\nArchitectures: amd64\n\nSigned-By: /etc/apt/keyrings/rocm.gpg\n\nEOF\n\napt update \u0026\u0026 apt install -y rocm-opencl-runtime\n```\n",
      "summary": "I use LSIO Jelyfin container for the easy addon they provide for AMD GPUs but I couldn't get trickplay to work with HWE...",
      "date_published": "2024-12-14T11:27:15Z",
      "date_modified": "2024-12-14T11:27:15Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "docker",
        "linux",
        "homelab",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/rsync-like-a-pro/",
      "url": "https://pype.dev/rsync-like-a-pro/",
      "title": "rsync like a pro",
      "content_html": "\u003cp\u003eI am moving a hefty amount of data to a new ZFS pool due to some corruption and I want to avoid using \u003ccode\u003ezfs send/recv\u003c/code\u003e for this just to make sure I don\u0026rsquo;t propagate any corrupted data to my new pool.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve used \u003ccode\u003ersync\u003c/code\u003e for simple things before but I needed this to be a little smarter and I wanted to see simple progress without flooding my terminal with a billion filenames.\u003c/p\u003e\n\u003ch2 id=\"tldr\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTLDR\u003c/span\u003e \u003ca href=\"#tldr\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTLDR:\n\u003ccode\u003ersync -aHAX --chmod=Da+s --info=progress2 --inplace --exclude='encrypted/docker/frigate-media' /tank/ /harbor/\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"explanation\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eExplanation\u003c/span\u003e \u003ca href=\"#explanation\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e-aHAX: Preserves attributes (archive mode, hard links, ACLs, extended attributes).\n\u0026ndash;chmod=Da+s: Ensures the setgid bit is applied to directories.\n\u0026ndash;info=progress2: Provides detailed progress information, including overall data transfer stats.\n\u0026ndash;inplace: Writes directly to the destination file, avoiding temporary files (useful for large files).\n\u0026ndash;exclude=\u0026lsquo;encrypted/\u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e/frigate-media\u0026rsquo;: Excludes the specified path (relative to the /tank root).\n/tank/ /harbor/: Ensures the contents of /tank are copied directly into /harbor.\u003c/p\u003e\n",
      "content_text": "\n\nI am moving a hefty amount of data to a new ZFS pool due to some corruption and I want to avoid using `zfs send/recv` for this just to make sure I don't propagate any corrupted data to my new pool.\n\nI've used `rsync` for simple things before but I needed this to be a little smarter and I wanted to see simple progress without flooding my terminal with a billion filenames.\n\n## TLDR\nTLDR: \n`rsync -aHAX --chmod=Da+s --info=progress2 --inplace --exclude='encrypted/docker/frigate-media' /tank/ /harbor/`\n\n## Explanation\n\n-aHAX: Preserves attributes (archive mode, hard links, ACLs, extended attributes).\n--chmod=Da+s: Ensures the setgid bit is applied to directories.\n--info=progress2: Provides detailed progress information, including overall data transfer stats.\n--inplace: Writes directly to the destination file, avoiding temporary files (useful for large files).\n--exclude='encrypted/docker/frigate-media': Excludes the specified path (relative to the /tank root).\n/tank/ /harbor/: Ensures the contents of /tank are copied directly into /harbor.\n\n\n",
      "summary": "I am moving a hefty amount of data to a new ZFS pool due to some corruption and I want to avoid using for this just to make sure I don't propagate any...",
      "date_published": "2024-12-11T10:52:23Z",
      "date_modified": "2024-12-11T10:52:23Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "terminal",
        "cli",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/d-and-uninterruptable-sleep/",
      "url": "https://pype.dev/d-and-uninterruptable-sleep/",
      "title": "D and uninterruptable sleep",
      "content_html": "\u003ch2 id=\"htop\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eHtop\u003c/span\u003e \u003ca href=\"#htop\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI recently have been having significant home server issues, and that\u0026rsquo;s not the point of this - today I learned what \u003ccode\u003eD\u003c/code\u003e state is when looking at htop.\u003c/p\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/d-htop.png\" alt=\"htop-d\" title=\"htop with D state\" /\u003e\n\u003cp\u003eApparently this means \u0026ldquo;uninterruptable sleep\u0026rdquo; and it\u0026rsquo;s a dev\u0026rsquo;s nightmare\u0026hellip;\u003c/p\u003e\n\u003ch3 id=\"context\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eContext\u003c/span\u003e \u003ca href=\"#context\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe issue I was having was that some \u003ccode\u003ezfs rollback\u003c/code\u003e commands were hung - for hours\u0026hellip; I wasn\u0026rsquo;t sure what was going on, rollbacks should be instant but I figured it was just an artifact of these issues.\u003c/p\u003e\n\u003cp\u003eTurns out I still don\u0026rsquo;t know what locked the disks up but I learned why \u003ccode\u003e\u0026lt;C\u0026gt;-c\u003c/code\u003e did \u003cstrong\u003enothing\u003c/strong\u003e\u0026hellip;\nthe more you know\u003c/p\u003e\n",
      "content_text": "\n## Htop\n\nI recently have been having significant home server issues, and that's not the point of this - today I learned what `D` state is when looking at htop.\n\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/d-htop.png\" alt=\"htop-d\" title=\"htop with D state\" /\u003e\n\nApparently this means \"uninterruptable sleep\" and it's a dev's nightmare... \n\n### Context\n\nThe issue I was having was that some `zfs rollback` commands were hung - for hours... I wasn't sure what was going on, rollbacks should be instant but I figured it was just an artifact of these issues.\n\nTurns out I still don't know what locked the disks up but I learned why `\u003cC\u003e-c` did __nothing__...\nthe more you know\n",
      "summary": "I recently have been having significant home server issues, and that's not the point of this - today I learned what state is when looking at htop.",
      "date_published": "2024-12-11T10:48:10Z",
      "date_modified": "2024-12-11T10:48:10Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "zfs",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/hostnamectl-to-easily-change-hostname/",
      "url": "https://pype.dev/hostnamectl-to-easily-change-hostname/",
      "title": "hostnamectl to easily change hostname",
      "content_html": "\u003cp\u003ehostnamectl is apparently a linux utility for easily changing your hostname in a variety of ways\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ hostnamectl --help\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehostnamectl \u003cspan class=\"o\"\u003e[\u003c/span\u003eOPTIONS...\u003cspan class=\"o\"\u003e]\u003c/span\u003e COMMAND ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eQuery or change system hostname.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCommands:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  status                 Show current hostname settings\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  hostname \u003cspan class=\"o\"\u003e[\u003c/span\u003eNAME\u003cspan class=\"o\"\u003e]\u003c/span\u003e        Get/set system hostname\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  icon-name \u003cspan class=\"o\"\u003e[\u003c/span\u003eNAME\u003cspan class=\"o\"\u003e]\u003c/span\u003e       Get/set icon name \u003cspan class=\"k\"\u003efor\u003c/span\u003e host\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  chassis \u003cspan class=\"o\"\u003e[\u003c/span\u003eNAME\u003cspan class=\"o\"\u003e]\u003c/span\u003e         Get/set chassis \u003cspan class=\"nb\"\u003etype\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e host\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  deployment \u003cspan class=\"o\"\u003e[\u003c/span\u003eNAME\u003cspan class=\"o\"\u003e]\u003c/span\u003e      Get/set deployment environment \u003cspan class=\"k\"\u003efor\u003c/span\u003e host\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  location \u003cspan class=\"o\"\u003e[\u003c/span\u003eNAME\u003cspan class=\"o\"\u003e]\u003c/span\u003e        Get/set location \u003cspan class=\"k\"\u003efor\u003c/span\u003e host\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eOptions:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  -h --help              Show this \u003cspan class=\"nb\"\u003ehelp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e     --version           Show package version\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e     --no-ask-password   Do not prompt \u003cspan class=\"k\"\u003efor\u003c/span\u003e password\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  -H --host\u003cspan class=\"o\"\u003e=[\u003c/span\u003eUSER@\u003cspan class=\"o\"\u003e]\u003c/span\u003eHOST  Operate on remote host\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  -M --machine\u003cspan class=\"o\"\u003e=\u003c/span\u003eCONTAINER Operate on \u003cspan class=\"nb\"\u003elocal\u003c/span\u003e container\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e     --transient         Only \u003cspan class=\"nb\"\u003eset\u003c/span\u003e transient hostname\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e     --static            Only \u003cspan class=\"nb\"\u003eset\u003c/span\u003e static hostname\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e     --pretty            Only \u003cspan class=\"nb\"\u003eset\u003c/span\u003e pretty hostname\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e     --json\u003cspan class=\"o\"\u003e=\u003c/span\u003epretty\u003cspan class=\"p\"\u003e|\u003c/span\u003eshort\u003cspan class=\"p\"\u003e|\u003c/span\u003eoff\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                         Generate JSON output\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSee the hostnamectl\u003cspan class=\"o\"\u003e(\u003c/span\u003e1\u003cspan class=\"o\"\u003e)\u003c/span\u003e man page \u003cspan class=\"k\"\u003efor\u003c/span\u003e details.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI learned there\u0026rsquo;s transient and static hostnames, so that\u0026rsquo;s cool\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThe thing I needed was \u003ccode\u003ehostnamectl --static hostname babyblue-aurora\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003epretty sweet tool\u003c/p\u003e\n",
      "content_text": "\nhostnamectl is apparently a linux utility for easily changing your hostname in a variety of ways\n\n```bash\n\n❯ hostnamectl --help\nhostnamectl [OPTIONS...] COMMAND ...\n\nQuery or change system hostname.\n\nCommands:\n  status                 Show current hostname settings\n  hostname [NAME]        Get/set system hostname\n  icon-name [NAME]       Get/set icon name for host\n  chassis [NAME]         Get/set chassis type for host\n  deployment [NAME]      Get/set deployment environment for host\n  location [NAME]        Get/set location for host\n\nOptions:\n  -h --help              Show this help\n     --version           Show package version\n     --no-ask-password   Do not prompt for password\n  -H --host=[USER@]HOST  Operate on remote host\n  -M --machine=CONTAINER Operate on local container\n     --transient         Only set transient hostname\n     --static            Only set static hostname\n     --pretty            Only set pretty hostname\n     --json=pretty|short|off\n                         Generate JSON output\n\nSee the hostnamectl(1) man page for details.\n```\n\nI learned there's transient and static hostnames, so that's cool...\n\nThe thing I needed was `hostnamectl --static hostname babyblue-aurora`\n\npretty sweet tool\n",
      "summary": "hostnamectl is apparently a linux utility for easily changing your hostname in a variety of ways",
      "date_published": "2024-12-06T07:25:59Z",
      "date_modified": "2024-12-06T07:25:59Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "terminal",
        "homelab",
        "cli",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/stylus-for-custom-webpage-themes/",
      "url": "https://pype.dev/stylus-for-custom-webpage-themes/",
      "title": "Stylus for custom webpage themes",
      "content_html": "\u003cp\u003ethe Logos web app is DISGUSTINGY bright/white - enough to actually ruin your morning\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThankfully there\u0026rsquo;s an extension called \u003ca href=\"https://addons.mozilla.org/en-US/firefox/addon/styl-us/\"\u003estylus\u003c/a\u003e and some \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e folks in the Logos community created a nice dark theme \u003ca href=\"https://userstyles.world/style/18547/logos-web-app-dark-mode\"\u003ehere\u003c/a\u003e. You simply install the extension, then go to the userstyles link \u0026lt;\u0026ndash; and click \u003ccode\u003einstall\u003c/code\u003e. It only changes themes for the sites configured - in this case \u003ca href=\"app.logos.com\"\u003eapp.logos.com\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"todo-image\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTODO: image\u003c/span\u003e\u003c/h1\u003e\n",
      "content_text": "\nthe Logos web app is DISGUSTINGY bright/white - enough to actually ruin your morning...\n\nThankfully there's an extension called [stylus](https://addons.mozilla.org/en-US/firefox/addon/styl-us/) and some kind folks in the Logos community created a nice dark theme [here](https://userstyles.world/style/18547/logos-web-app-dark-mode). You simply install the extension, then go to the userstyles link \u003c-- and click `install`. It only changes themes for the sites configured - in this case [app.logos.com](app.logos.com)\n\n# TODO: image\n",
      "summary": "the Logos web app is DISGUSTINGY bright/white - enough to actually ruin your morning...",
      "date_published": "2024-11-27T06:07:39Z",
      "date_modified": "2024-11-27T06:07:39Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "tech",
        "logos",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/dns-broke-after-reboot-ubuntu-22-04/",
      "url": "https://pype.dev/dns-broke-after-reboot-ubuntu-22-04/",
      "title": "DNS Broke After Reboot - Ubuntu 22.04",
      "content_html": "\u003cp\u003eI rebooted by server and DNS broke randomly. I have no idea if it was from a kernel update or what but that\u0026rsquo;s the issue with Ubuntu I guess\u0026hellip;\u003c/p\u003e\n\u003cp\u003eAfter much toil and none of the other options working for me (sorry to not have those documented here) this is what got me the vic from this \u003ca href=\"https://askubuntu.com/questions/1406827/how-to-set-dns-on-ubuntu-22-04-when-you-have-no-netplan-config\"\u003eSO Post\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003esudo mkdir /etc/systemd/resolved.conf.d/\nsudo $EDITOR /etc/systemd/resolved.conf.d/dns_servers.conf\u003c/p\u003e\n\u003cp\u003eMost folks probably are good with google (8.8.8.8) and cloudflare (1.1.1.1)\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e[Resolve]\nDNS=8.8.8.8 1.1.1.1\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eBut I decided to use tailscale\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e[Resolve]\nDNS=100.100.100.100\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen restart systemd-resolved\u003c/p\u003e\n\u003cp\u003esudo systemctl restart systemd-resolved\u003c/p\u003e\n",
      "content_text": "\nI rebooted by server and DNS broke randomly. I have no idea if it was from a kernel update or what but that's the issue with Ubuntu I guess...\n\nAfter much toil and none of the other options working for me (sorry to not have those documented here) this is what got me the vic from this [SO Post](https://askubuntu.com/questions/1406827/how-to-set-dns-on-ubuntu-22-04-when-you-have-no-netplan-config)\n\nsudo mkdir /etc/systemd/resolved.conf.d/\nsudo $EDITOR /etc/systemd/resolved.conf.d/dns_servers.conf\n\nMost folks probably are good with google (8.8.8.8) and cloudflare (1.1.1.1)\n\n```\n[Resolve]\nDNS=8.8.8.8 1.1.1.1\n```\n\nBut I decided to use tailscale\n\n```\n[Resolve]\nDNS=100.100.100.100\n```\n\nThen restart systemd-resolved\n\nsudo systemctl restart systemd-resolved\n",
      "summary": "I rebooted by server and DNS broke randomly. I have no idea if it was from a kernel update or what but that's the issue with Ubuntu I guess...",
      "date_published": "2024-11-22T08:08:40Z",
      "date_modified": "2024-11-22T08:08:40Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/restart-kde-plasma/",
      "url": "https://pype.dev/restart-kde-plasma/",
      "title": "Restart KDE Plasma",
      "content_html": "\u003cp\u003ePlasma shits the bed a little too often on Fedora for me right now but I finally have a quick fix\u0026hellip;\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\nsudo killall plasmashell\n\nkstart plasmashell\n\n\u003c/code\u003e\u003c/pre\u003e\n",
      "content_text": "\nPlasma shits the bed a little too often on Fedora for me right now but I finally have a quick fix...\n\n```\n\nsudo killall plasmashell\n\nkstart plasmashell\n\n```\n",
      "summary": "Plasma shits the bed a little too often on Fedora for me right now but I finally have a quick fix...",
      "date_published": "2024-11-08T15:53:52Z",
      "date_modified": "2024-11-08T15:53:52Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "terminal",
        "cli",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/opnsense-bootstrap-recovery/",
      "url": "https://pype.dev/opnsense-bootstrap-recovery/",
      "title": "OPNSense Bootstrap Recovery",
      "content_html": "\u003cp\u003eenabling DHCP WAN port (\u003ccode\u003edhclient \u0026lt;iface\u0026gt;\u003c/code\u003e)- running the bootstrap script - \u003ccode\u003esh /usr/local/sbin/opnsense-bootstrap\u003c/code\u003e\u003c/p\u003e\n",
      "content_text": "\n\nenabling DHCP WAN port (`dhclient \u003ciface\u003e`)- running the bootstrap script - `sh /usr/local/sbin/opnsense-bootstrap`\n\n",
      "summary": "enabling DHCP WAN port ()- running the bootstrap script -",
      "date_published": "2024-11-07T08:40:19Z",
      "date_modified": "2024-11-07T08:40:19Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "infrastructure",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/docker-remote-add/",
      "url": "https://pype.dev/docker-remote-add/",
      "title": "docker-remote-add",
      "content_html": "\u003cp\u003eAdd from url??\u003c/p\u003e\n\u003cp\u003eADD \u003ca href=\"http://example.com/cars.csv\"\u003ehttp://example.com/cars.csv\u003c/a\u003e /tmp/cars.csv\u003c/p\u003e\n\u003cp\u003eUnpack automatically!? (.tar, .tar.gz, .tgz, .bz2, .tbz2, .txz, .zip)\u003c/p\u003e\n\u003cp\u003eADD myapp.tar.gz /opt/myapp/\u003c/p\u003e\n",
      "content_text": "\nAdd from url??\n\nADD http://example.com/cars.csv /tmp/cars.csv\n\nUnpack automatically!? (.tar, .tar.gz, .tgz, .bz2, .tbz2, .txz, .zip)\n\nADD myapp.tar.gz /opt/myapp/\n",
      "summary": "Add from url??",
      "date_published": "2024-09-17T06:19:00Z",
      "date_modified": "2024-09-17T06:19:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/docker-copy-and-chown/",
      "url": "https://pype.dev/docker-copy-and-chown/",
      "title": "Docker copy and chown",
      "content_html": "\u003cp\u003eCOPY \u0026ndash;chown=myuser:mygroup source-file target-file\u003c/p\u003e\n",
      "content_text": "\nCOPY --chown=myuser:mygroup source-file target-file\n",
      "summary": "COPY --chown=myuser:mygroup source-file target-file",
      "date_published": "2024-09-17T06:18:09Z",
      "date_modified": "2024-09-17T06:18:09Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/upgrading-your-kernel-can-f-you-up-whoops/",
      "url": "https://pype.dev/upgrading-your-kernel-can-f-you-up-whoops/",
      "title": "Upgrading your kernel can F you up… whoops",
      "content_html": "\u003cp\u003e\u003ca href=\"https://forum.proxmox.com/threads/update-error-with-coral-tpu-drivers.136888/#post-608975\"\u003ehttps://forum.proxmox.com/threads/update-error-with-coral-tpu-drivers.136888/#post-608975\u003c/a\u003e I lost TPU for frigate, this saved me ass\u003c/p\u003e\n",
      "content_text": "\nhttps://forum.proxmox.com/threads/update-error-with-coral-tpu-drivers.136888/#post-608975 I lost TPU for frigate, this saved me ass\n",
      "summary": "https://forum.proxmox.com/threads/update-error-with-coral-tpu-drivers.136888/#post-608975 I lost TPU for frigate, this saved me ass",
      "date_published": "2024-06-26T10:21:59Z",
      "date_modified": "2024-06-26T10:21:59Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/fonts-in-vs-c-e/",
      "url": "https://pype.dev/fonts-in-vs-c-e/",
      "title": "Fonts in VS C**e",
      "content_html": "\u003cp\u003eJet Brains has to be specified \u0026lsquo;JetBrainsMono Nerd Font Mono\u0026rsquo;\u003c/p\u003e\n",
      "content_text": "\nJet Brains has to be specified 'JetBrainsMono Nerd Font Mono'\n",
      "summary": "Jet Brains has to be specified 'JetBrainsMono Nerd Font Mono'",
      "date_published": "2024-05-20T06:44:07Z",
      "date_modified": "2024-05-20T06:44:07Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vscode",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/customize-k9s/",
      "url": "https://pype.dev/customize-k9s/",
      "title": "Customize K9s",
      "content_html": "\u003cp\u003eTo customize k9s use the skins from catppuccin or the ones k9s supplies\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eOUT\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003eXDG_CONFIG_HOME\u003c/span\u003e\u003cspan class=\"k\"\u003e:-\u003c/span\u003e\u003cspan class=\"nv\"\u003e$HOME\u003c/span\u003e\u003cspan class=\"p\"\u003e/.config\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e/k9s/skins\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir -p \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$OUT\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecurl -L https://github.com/catppuccin/k9s/archive/main.tar.gz \u003cspan class=\"p\"\u003e|\u003c/span\u003e tar xz -C \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$OUT\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e --strip-components\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e k9s-main/dist\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen edit your k9s config\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e# ~/.config/k9s/config.yml\nk9s:\n  ui:\n    skin: catppuccin-mocha\n    # ...or another flavor:\n    # skin: catppuccin-macchiato\n    # skin: catppuccin-frappe\n    # skin: catppuccin-latte\n\n    # ...or the transparent variants:\n    # skin: catppuccin-mocha-transparent\n    # skin: catppuccin-macchiato-transparent\n    # skin: catppuccin-frappe-transparent\n    # skin: catppuccin-latte-transparent\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eOther k9s skins are available \u003ca href=\"https://github.com/derailed/k9s/tree/master/skins\"\u003ehere\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nTo customize k9s use the skins from catppuccin or the ones k9s supplies\n\n```bash\nOUT=\"${XDG_CONFIG_HOME:-$HOME/.config}/k9s/skins\"\nmkdir -p \"$OUT\"\ncurl -L https://github.com/catppuccin/k9s/archive/main.tar.gz | tar xz -C \"$OUT\" --strip-components=2 k9s-main/dist\n```\n\nThen edit your k9s config\n\n```\n# ~/.config/k9s/config.yml\nk9s:\n  ui:\n    skin: catppuccin-mocha\n    # ...or another flavor:\n    # skin: catppuccin-macchiato\n    # skin: catppuccin-frappe\n    # skin: catppuccin-latte\n\n    # ...or the transparent variants:\n    # skin: catppuccin-mocha-transparent\n    # skin: catppuccin-macchiato-transparent\n    # skin: catppuccin-frappe-transparent\n    # skin: catppuccin-latte-transparent\n```\n\nOther k9s skins are available [here](https://github.com/derailed/k9s/tree/master/skins)\n",
      "summary": "To customize k9s use the skins from catppuccin or the ones k9s supplies",
      "date_published": "2024-05-06T11:57:07Z",
      "date_modified": "2024-05-06T11:57:07Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "cli",
        "homelab",
        "tech",
        "k8s",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/git-fetch-failing-check-your-config/",
      "url": "https://pype.dev/git-fetch-failing-check-your-config/",
      "title": "Git fetch failing - check your config",
      "content_html": "\u003cp\u003eI started deploying a website to Cloudflare on a branch called \u003ccode\u003epages\u003c/code\u003e. Similar to one of the GH Pages deployment patterns. But when my CI was pushing the branch I couldn\u0026rsquo;t see it locally\u0026hellip;\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003egit fetch -a\u003c/code\u003e wasn\u0026rsquo;t pulling any new branches, and \u003ccode\u003egit branch -a\u003c/code\u003e was only showing my development and main branches at the remote\u0026hellip; so what gives?\u003c/p\u003e\n\u003cp\u003eI checked my git config, and to this moment I have no idea how this happened but check out my fetch config:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003egit config --get remote.origin.fetch\n+refs/tags/*:refs/tags/*\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eSo to fix this:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003egit config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNow \u003ccode\u003egit fetch -a\u003c/code\u003e works again\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026gt; git fetch -a\n\nFrom github.com:DigitalHarbor7/DigitalHarbor\n   357a28a..969b027  develop    -\u0026gt; origin/develop\n   c052ac9..6d40210  main       -\u0026gt; origin/main\n * [new branch]      pages      -\u0026gt; origin/pages\n\u003c/code\u003e\u003c/pre\u003e\n",
      "content_text": "\nI started deploying a website to Cloudflare on a branch called `pages`. Similar to one of the GH Pages deployment patterns. But when my CI was pushing the branch I couldn't see it locally...\n\n`git fetch -a` wasn't pulling any new branches, and `git branch -a` was only showing my development and main branches at the remote... so what gives?\n\nI checked my git config, and to this moment I have no idea how this happened but check out my fetch config:\n```\ngit config --get remote.origin.fetch\n+refs/tags/*:refs/tags/*\n```\n\nSo to fix this:\n\n```\ngit config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'\n```\n\nNow `git fetch -a` works again\n```\n\u003e git fetch -a\n\nFrom github.com:DigitalHarbor7/DigitalHarbor\n   357a28a..969b027  develop    -\u003e origin/develop\n   c052ac9..6d40210  main       -\u003e origin/main\n * [new branch]      pages      -\u003e origin/pages\n```\n\n",
      "summary": "I started deploying a website to Cloudflare on a branch called . Similar to one of the GH Pages deployment patterns. But when my CI was pushing the branch I...",
      "date_published": "2024-04-18T08:32:33Z",
      "date_modified": "2024-04-18T08:32:33Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "cli",
        "git",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/git-repo-specific-ssh-key/",
      "url": "https://pype.dev/git-repo-specific-ssh-key/",
      "title": "Git repo specific SSH Key!",
      "content_html": "\u003cp\u003egit config \u0026ndash;add \u0026ndash;local core.sshCommand \u0026lsquo;ssh -i \u0026laquo;\u0026lt;PATH_TO_SSH_KEY\u0026raquo;\u0026gt;\u0026rsquo;\u003c/p\u003e\n",
      "content_text": "\ngit config --add --local core.sshCommand 'ssh -i \u003c\u003c\u003cPATH_TO_SSH_KEY\u003e\u003e\u003e'\n",
      "summary": "git config --add --local core.sshCommand 'ssh -i \u003e\u003e'",
      "date_published": "2024-01-04T15:42:15Z",
      "date_modified": "2024-01-04T15:42:15Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "cli",
        "git",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/dhcp-restart-to-save-ubuntu-22-04-server-networking/",
      "url": "https://pype.dev/dhcp-restart-to-save-ubuntu-22-04-server-networking/",
      "title": "DHCP Restart to Save Ubuntu 22.04 Server Networking",
      "content_html": "\u003cp\u003eI moved a computer to a remote location for an off-site backup but when it was powered on it wouldn\u0026rsquo;t show up on any networks. A solution that got me back in was a friend restarting the dhcp client for me:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo dhclient -r -v \u0026lt;interface\u0026gt; \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e sudo dhclient -v \u0026lt;interface\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nI moved a computer to a remote location for an off-site backup but when it was powered on it wouldn't show up on any networks. A solution that got me back in was a friend restarting the dhcp client for me:\n\n```bash\nsudo dhclient -r -v \u003cinterface\u003e \u0026\u0026 sudo dhclient -v \u003cinterface\u003e\n```\n",
      "summary": "I moved a computer to a remote location for an off-site backup but when it was powered on it wouldn't show up on any networks. A solution that got me back in...",
      "date_published": "2023-12-31T20:26:50Z",
      "date_modified": "2023-12-31T20:26:50Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/simple-port-forwarding-opnsense/",
      "url": "https://pype.dev/simple-port-forwarding-opnsense/",
      "title": "Simple Port Forwarding OPNSense",
      "content_html": "\u003cp\u003e\u003ca href=\"https://forum.opnsense.org/index.php?topic=8783.0\"\u003ehttps://forum.opnsense.org/index.php?topic=8783.0\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nhttps://forum.opnsense.org/index.php?topic=8783.0\n",
      "summary": "https://forum.opnsense.org/index.php?topic=8783.0",
      "date_published": "2023-10-17T10:26:34Z",
      "date_modified": "2023-10-17T10:26:34Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/refresh-nextcloud-groupfolders-after-messing-around-on-the-filesystem/",
      "url": "https://pype.dev/refresh-nextcloud-groupfolders-after-messing-around-on-the-filesystem/",
      "title": "Refresh Nextcloud Groupfolders after messing around on the filesystem",
      "content_html": "\u003cp\u003eExec in as www-data and run ./occ groupfolders:scan folder_id -v (the -v to see what it\u0026rsquo;s doing)\u003c/p\u003e\n",
      "content_text": "\nExec in as www-data and run ./occ groupfolders:scan folder_id -v (the -v to see what it's doing)\n",
      "summary": "Exec in as www-data and run ./occ groupfolders:scan folder_id -v (the -v to see what it's doing)",
      "date_published": "2023-09-23T12:45:06Z",
      "date_modified": "2023-09-23T12:45:06Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/lsof-to-find-what-s-using-your-filesystem/",
      "url": "https://pype.dev/lsof-to-find-what-s-using-your-filesystem/",
      "title": "lsof to find what’s using your filesystem",
      "content_html": "\u003cp\u003elsof | grep /tank/nas shows me what is using my nas at any time!\u003c/p\u003e\n",
      "content_text": "\nlsof | grep /tank/nas shows me what is using my nas at any time!\n",
      "summary": "lsof | grep /tank/nas shows me what is using my nas at any time!",
      "date_published": "2023-04-09T13:32:38Z",
      "date_modified": "2023-04-09T13:32:38Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "zfs",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/changing-zfs-key-for-child-datasets-of-encrypted-dataset-after-migration/",
      "url": "https://pype.dev/changing-zfs-key-for-child-datasets-of-encrypted-dataset-after-migration/",
      "title": "Changing ZFS key for child datasets of encrypted dataset after migration",
      "content_html": "\u003cp\u003e➜ pihole sudo zfs load-key -L file:///path/to/.zfs.tank.key tank/encrypted/vms/arch-sandbox\n➜ pihole sudo zfs change-key -o keylocation=file:///path/to/.zfs.tank.key -o keyformat=raw tank/encrypted/vms/arch-sandbox\nNeed to load-key for each individual dataset, then change key location to be a file instead of the prompt\u003c/p\u003e\n",
      "content_text": "\n➜ pihole sudo zfs load-key -L file:///path/to/.zfs.tank.key tank/encrypted/vms/arch-sandbox\n➜ pihole sudo zfs change-key -o keylocation=file:///path/to/.zfs.tank.key -o keyformat=raw tank/encrypted/vms/arch-sandbox\nNeed to load-key for each individual dataset, then change key location to be a file instead of the prompt\n",
      "summary": "➜ pihole sudo zfs load-key -L file:///path/to/.zfs.tank.key tank/encrypted/vms/arch-sandbox ➜ pihole sudo zfs change-key -o...",
      "date_published": "2023-04-08T20:12:22Z",
      "date_modified": "2023-04-08T20:12:22Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "zfs",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/convert-word-doc-to-pdf-with-headless-libreoffice/",
      "url": "https://pype.dev/convert-word-doc-to-pdf-with-headless-libreoffice/",
      "title": "Convert Word Doc to PDF with Headless Libreoffice",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve been using paperless-ngx to manage all my documents, but every once in a while I\u0026rsquo;ll get a \u003ccode\u003e.docx\u003c/code\u003e file to deal with\u0026hellip;\u003c/p\u003e\n\u003cp\u003eTurns out Libreoffice has a headless mode a \u003ccode\u003epdf\u003c/code\u003e converter built-in!\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003elibreoffice --headless --convert-to pdf /path/to/file.docx --outdir /path/to/output/directory\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cblockquote\u003e\n\u003cp\u003eNote that \u003ccode\u003e--outdir\u003c/code\u003e is in fact a directory, not the path to a file\u003c/p\u003e\n\u003c/blockquote\u003e\n",
      "content_text": "\nI've been using paperless-ngx to manage all my documents, but every once in a while I'll get a `.docx` file to deal with...\n\nTurns out Libreoffice has a headless mode a `pdf` converter built-in!\n\n```Bash\nlibreoffice --headless --convert-to pdf /path/to/file.docx --outdir /path/to/output/directory\n```\n\n\u003e Note that `--outdir` is in fact a directory, not the path to a file\n",
      "summary": "I've been using paperless-ngx to manage all my documents, but every once in a while I'll get a file to deal with...",
      "date_published": "2023-03-09T06:48:38Z",
      "date_modified": "2023-03-09T06:48:38Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/ffmpeg-10-bit-videos-to-8-bit/",
      "url": "https://pype.dev/ffmpeg-10-bit-videos-to-8-bit/",
      "title": "FFMPEG 10-bit videos to 8-bit",
      "content_html": "\u003cp\u003e\u003ccode\u003effmpeg -i input.mp4 -map 0 -c:v libx264 -vf format=yuv420p -c:a copy output.mp4\u003c/code\u003e\u003c/p\u003e\n",
      "content_text": "\n`ffmpeg -i input.mp4 -map 0 -c:v libx264 -vf format=yuv420p -c:a copy output.mp4`\n",
      "date_published": "2023-01-16T13:15:53Z",
      "date_modified": "2023-01-16T13:15:53Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "cli",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/i3-like-keyboard-mapping-in-pop-os/",
      "url": "https://pype.dev/i3-like-keyboard-mapping-in-pop-os/",
      "title": "i3-Like keyboard mapping in Pop_OS",
      "content_html": "\u003cp\u003eI was introduced to tiling window managers through i3, which I use heavily on\none of my machines. I have switched to Pop_OS! at home though, which has a\ntiling window mode but the keybindings are not what I\u0026rsquo;m used to for i3. I\nwanted to at least navigate workspaces how I\u0026rsquo;m used to doing (cause I set\nworkspace 3 for communication apps, 1 for my terminal, etc\u0026hellip;)\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s how I set keybindings for:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e\u0026lt;Super\u0026gt; + \u0026lt;number\u0026gt;\u003c/code\u003e sends me to that numbered workspace\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e\u0026lt;Shift\u0026gt; + \u0026lt;Super\u0026gt; + \u0026lt;number\u0026gt;\u003c/code\u003e moves the window I\u0026rsquo;m focused on to workspace \u003ccode\u003enumber\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.mutter dynamic-workspaces \u003cspan class=\"nb\"\u003efalse\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.preferences num-workspaces \u003cspan class=\"m\"\u003e8\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-1 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-2 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-3 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-4 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-5 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-6 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-7 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-8 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.shell.keybindings switch-to-application-9 \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-1 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;1\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-2 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;2\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-3 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;3\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-4 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;4\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-5 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;5\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-6 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;6\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-7 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;7\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-8 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;8\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-9 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;9\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings switch-to-workspace-10 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;0\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-1 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;1\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-2 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;2\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-3 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;3\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-4 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;4\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-5 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;5\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-6 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;6\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-7 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;7\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-8 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;8\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-9 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;9\u0026#39;]\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egsettings \u003cspan class=\"nb\"\u003eset\u003c/span\u003e org.gnome.desktop.wm.keybindings move-to-workspace-10 \u003cspan class=\"s2\"\u003e\u0026#34;[\u0026#39;\u0026lt;Super\u0026gt;\u0026lt;Shift\u0026gt;0\u0026#39;]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nI was introduced to tiling window managers through i3, which I use heavily on\none of my machines. I have switched to Pop_OS! at home though, which has a\ntiling window mode but the keybindings are not what I'm used to for i3. I\nwanted to at least navigate workspaces how I'm used to doing (cause I set\nworkspace 3 for communication apps, 1 for my terminal, etc...)\n\nHere's how I set keybindings for:\n\n* `\u003cSuper\u003e + \u003cnumber\u003e` sends me to that numbered workspace\n* `\u003cShift\u003e + \u003cSuper\u003e + \u003cnumber\u003e` moves the window I'm focused on to workspace `number`\n\n```bash\n#!/bin/bash\ngsettings set org.gnome.mutter dynamic-workspaces false \ngsettings set org.gnome.desktop.wm.preferences num-workspaces 8 \ngsettings set org.gnome.shell.keybindings switch-to-application-1 [] \ngsettings set org.gnome.shell.keybindings switch-to-application-2 [] \ngsettings set org.gnome.shell.keybindings switch-to-application-3 [] \ngsettings set org.gnome.shell.keybindings switch-to-application-4 [] \ngsettings set org.gnome.shell.keybindings switch-to-application-5 [] \ngsettings set org.gnome.shell.keybindings switch-to-application-6 [] \ngsettings set org.gnome.shell.keybindings switch-to-application-7 [] \ngsettings set org.gnome.shell.keybindings switch-to-application-8 [] \ngsettings set org.gnome.shell.keybindings switch-to-application-9 [] \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-1 \"['\u003cSuper\u003e1']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-2 \"['\u003cSuper\u003e2']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-3 \"['\u003cSuper\u003e3']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-4 \"['\u003cSuper\u003e4']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-5 \"['\u003cSuper\u003e5']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-6 \"['\u003cSuper\u003e6']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-7 \"['\u003cSuper\u003e7']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-8 \"['\u003cSuper\u003e8']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-9 \"['\u003cSuper\u003e9']\" \ngsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-10 \"['\u003cSuper\u003e0']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-1 \"['\u003cSuper\u003e\u003cShift\u003e1']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-2 \"['\u003cSuper\u003e\u003cShift\u003e2']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-3 \"['\u003cSuper\u003e\u003cShift\u003e3']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-4 \"['\u003cSuper\u003e\u003cShift\u003e4']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-5 \"['\u003cSuper\u003e\u003cShift\u003e5']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-6 \"['\u003cSuper\u003e\u003cShift\u003e6']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-7 \"['\u003cSuper\u003e\u003cShift\u003e7']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-8 \"['\u003cSuper\u003e\u003cShift\u003e8']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-9 \"['\u003cSuper\u003e\u003cShift\u003e9']\" \ngsettings set org.gnome.desktop.wm.keybindings move-to-workspace-10 \"['\u003cSuper\u003e\u003cShift\u003e0']\"\n```\n",
      "summary": "I was introduced to tiling window managers through i3, which I use heavily on one of my machines. I have switched to Pop_OS! at home though, which has a...",
      "date_published": "2023-01-12T05:51:25Z",
      "date_modified": "2023-01-12T05:51:25Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/use-non-standard-named-ssh-keys-with-github/",
      "url": "https://pype.dev/use-non-standard-named-ssh-keys-with-github/",
      "title": "Use non-standard named ssh keys with github",
      "content_html": "\u003cp\u003eI was getting \u003ccode\u003e(publickey denied)\u003c/code\u003e when trying to push to GH using ssh. When I\ntested the connection I saw that a bunch of keys in ``~/.ssh/ were being\nattempted\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e✗ ssh git@github.com -vv\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edebug1: Will attempt key: /home/nic/.ssh/id_rsa \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edebug1: Will attempt key: /home/nic/.ssh/id_ecdsa \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edebug1: Will attempt key: /home/nic/.ssh/id_ecdsa_sk \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edebug1: Will attempt key: /home/nic/.ssh/id_ed25519 \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edebug1: Will attempt key: /home/nic/.ssh/id_ed25519_sk \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edebug1: Will attempt key: /home/nic/.ssh/id_xmss \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edebug1: Will attempt key: /home/nic/.ssh/id_dsa \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edebug1: No more authentication methods to try.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003egit@github.com: Permission denied (publickey).\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNone of those were the key I setup with GH. So I added an entry\ninto \u003ccode\u003e~/.ssh/config\u003c/code\u003e:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eHost\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egithub.com\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eUser git\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ePort 22\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eHostname github.com\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eIdentityFile ~/.ssh/my_custom_github_key\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eTCPKeepAlive yes\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eIdentitiesOnly yes \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nI was getting `(publickey denied)` when trying to push to GH using ssh. When I\ntested the connection I saw that a bunch of keys in ``~/.ssh/ were being\nattempted\n\n```console\n✗ ssh git@github.com -vv\n\n...\n\ndebug1: Will attempt key: /home/nic/.ssh/id_rsa \ndebug1: Will attempt key: /home/nic/.ssh/id_ecdsa \ndebug1: Will attempt key: /home/nic/.ssh/id_ecdsa_sk \ndebug1: Will attempt key: /home/nic/.ssh/id_ed25519 \ndebug1: Will attempt key: /home/nic/.ssh/id_ed25519_sk \ndebug1: Will attempt key: /home/nic/.ssh/id_xmss \ndebug1: Will attempt key: /home/nic/.ssh/id_dsa \n\n...\n\ndebug1: No more authentication methods to try.\ngit@github.com: Permission denied (publickey).\n\n```\n\nNone of those were the key I setup with GH. So I added an entry\ninto `~/.ssh/config`:\n\n```text\nHost\ngithub.com\nUser git\nPort 22\nHostname github.com\nIdentityFile ~/.ssh/my_custom_github_key\nTCPKeepAlive yes\nIdentitiesOnly yes \n\n```\n\n",
      "summary": "I was getting when trying to push to GH using ssh. When I tested the connection I saw that a bunch of keys in ``~/.ssh/ were being attempted",
      "date_published": "2023-01-03T08:34:50Z",
      "date_modified": "2023-01-03T08:34:50Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/reminder-about-ssh-copy-id-for-ssh-and-ansible/",
      "url": "https://pype.dev/reminder-about-ssh-copy-id-for-ssh-and-ansible/",
      "title": "Reminder about ssh-copy-id for SSH and Ansible",
      "content_html": "\u003cp\u003e\u003ccode\u003essh-copy-id -i my.key.pub \u0026lt;hostname probably from tailscale\u0026gt;\u003c/code\u003e\nthis makes sure I can run ansible from my desktop against VMs on my server\neasily if they have tailscale for the hostname - otherwise use the IP\u003c/p\u003e\n",
      "content_text": "\n`ssh-copy-id -i my.key.pub \u003chostname probably from tailscale\u003e` \nthis makes sure I can run ansible from my desktop against VMs on my server\neasily if they have tailscale for the hostname - otherwise use the IP\n",
      "summary": "this makes sure I can run ansible from my desktop against VMs on my server easily if they have tailscale for the hostname - otherwise use the IP",
      "date_published": "2022-12-28T13:33:07Z",
      "date_modified": "2022-12-28T13:33:07Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/adding-docker-daemon-json-broke-docker/",
      "url": "https://pype.dev/adding-docker-daemon-json-broke-docker/",
      "title": "Adding docker daemon.json broke docker",
      "content_html": "\u003cp\u003ein /lib/systemd/system/\u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e.service there is an ExecStart command that got placed there when I setup Docker with Ansible - it threw the -H flag which told the daemon what hosts to setup. But I added the \u0026ldquo;hosts\u0026rdquo; key in my daemon.json and it broke - so removing the -H flag from the systemd unit fixed it\u003c/p\u003e\n",
      "content_text": "\nin /lib/systemd/system/docker.service there is an ExecStart command that got placed there when I setup Docker with Ansible - it threw the -H flag which told the daemon what hosts to setup. But I added the \"hosts\" key in my daemon.json and it broke - so removing the -H flag from the systemd unit fixed it\n",
      "summary": "in /lib/systemd/system/docker.service there is an ExecStart command that got placed there when I setup Docker with Ansible - it threw the -H flag which told...",
      "date_published": "2022-12-21T10:02:25Z",
      "date_modified": "2022-12-21T10:02:25Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "tech",
        "docker",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/suda-vim-for-sudo-access-to-files/",
      "url": "https://pype.dev/suda-vim-for-sudo-access-to-files/",
      "title": "suda.vim for sudo access to files",
      "content_html": "\u003cp\u003eI regularly need to edit system config files - take /etc/sanoid/sanoid.conf as\nan example\u0026hellip; I\u0026rsquo;ll want to play with something but if I don\u0026rsquo;t start Neovim as\nroot then I get in trouble making edits I can\u0026rsquo;t save! So\n\u003ca href=\"https://github.com/lambdalisue/suda.vim\"\u003esuda.vim\u003c/a\u003e gives me\n\u003ccode\u003e:SudaWrite\u003c/code\u003e which let\u0026rsquo;s me write that buffer with sudo privileges even though\nI\u0026rsquo;m Neovim is running with my login user!\u003c/p\u003e\n",
      "content_text": "\nI regularly need to edit system config files - take /etc/sanoid/sanoid.conf as\nan example... I'll want to play with something but if I don't start Neovim as\nroot then I get in trouble making edits I can't save! So\n[suda.vim](https://github.com/lambdalisue/suda.vim) gives me\n`:SudaWrite` which let's me write that buffer with sudo privileges even though\nI'm Neovim is running with my login user!\n",
      "summary": "I regularly need to edit system config files - take /etc/sanoid/sanoid.conf as an example... I'll want to play with something but if I don't start Neovim as...",
      "date_published": "2022-12-21T09:45:34Z",
      "date_modified": "2022-12-21T09:45:34Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vim",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/pipe-to-a-pager-to-preserve-console-output-in-ssh-session/",
      "url": "https://pype.dev/pipe-to-a-pager-to-preserve-console-output-in-ssh-session/",
      "title": "Pipe to a pager to preserve console output in SSH session",
      "content_html": "\u003cp\u003eI\u0026rsquo;m playing with my ansible playbook in a remote tmux session, and I\u0026rsquo;m no wiz\nso I don\u0026rsquo;t know the ins and outs, but I can\u0026rsquo;t scroll up to get any console log\noutput that\u0026rsquo;s not already visible on my screen. So I\u0026rsquo;m starting to end my\ncommands with \u003ccode\u003e | less\u003c/code\u003e so I can page through the console output!\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eansible-playbook plays.yml -v --tags mytag | less\u003c/code\u003e\u003c/p\u003e\n",
      "content_text": "\nI'm playing with my ansible playbook in a remote tmux session, and I'm no wiz\nso I don't know the ins and outs, but I can't scroll up to get any console log\noutput that's not already visible on my screen. So I'm starting to end my\ncommands with ` | less` so I can page through the console output!\n\n`ansible-playbook plays.yml -v --tags mytag | less`\n",
      "summary": "I'm playing with my ansible playbook in a remote tmux session, and I'm no wiz so I don't know the ins and outs, but I can't scroll up to get any console log...",
      "date_published": "2022-12-18T15:04:02Z",
      "date_modified": "2022-12-18T15:04:02Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/cron-for-nextcloud-in-docker/",
      "url": "https://pype.dev/cron-for-nextcloud-in-docker/",
      "title": "Cron for Nextcloud in Docker",
      "content_html": "\u003cp\u003eAJAX wasn\u0026rsquo;t cutting it, traditional crontab in containers doesn\u0026rsquo;t make much\nsense to me, webcron is recommended but I don\u0026rsquo;t want to register with anything\noutside my LAN\u0026hellip; Turns out you can just spin up an identical container with a\ndifferent entrypoint to /cron.sh that does what you need!\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eNote that this is a task in an Ansible playbook - but the \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e-compose is straight forward\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eSo the only thing you need to make sure of is that \u003cem\u003eall\u003c/em\u003e the configuration\noptions - data volumes, user permissions, etc. are \u003cem\u003eidentical\u003c/em\u003e between the\ncontainers running the cron job and the one actually hosting NextCloud. This\nensures that the container running cron has proper access to the database and\nfilesystem - or at least the same access as NextCloud proper.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eNextcloud Cron Docker Container\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003edocker_container\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003enextcloud-cron\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ nextcloud_image }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003epull\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003elinks\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"l\"\u003enextcloud-mysql:mysql\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eentrypoint\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e/cron.sh\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evolumes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"s2\"\u003e\u0026#34;{{ nextcloud_data_directory }}/nextcloud:/var/www/html:rw\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_HOST\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;mysql\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_DATABASE\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;nextcloud\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_USER\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ nextcloud_sql_user }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_PASSWORD\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ nextcloud_sql_password }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eNEXTCLOUD_TRUSTED_DOMAINS\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ nextcloud_hostname }}.{{ ansible_nas_domain }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ePUID\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ nextcloud_user_id }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ePGID\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ nextcloud_group_id }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eTZ\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ ansible_nas_timezone }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003erestart_policy\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eunless-stopped\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ememory\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;{{ nextcloud_memory }}\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nAJAX wasn't cutting it, traditional crontab in containers doesn't make much\nsense to me, webcron is recommended but I don't want to register with anything\noutside my LAN... Turns out you can just spin up an identical container with a\ndifferent entrypoint to /cron.sh that does what you need!\n\n\u003e Note that this is a task in an Ansible playbook - but the docker-compose is straight forward\n\nSo the only thing you need to make sure of is that _all_ the configuration\noptions - data volumes, user permissions, etc. are _identical_ between the\ncontainers running the cron job and the one actually hosting NextCloud. This\nensures that the container running cron has proper access to the database and\nfilesystem - or at least the same access as NextCloud proper.\n\n```yaml\n- name: Nextcloud Cron Docker Container\n  docker_container:\n    name: nextcloud-cron\n    image: \"{{ nextcloud_image }}\"\n    pull: true\n    links:\n      - nextcloud-mysql:mysql\n    entrypoint: /cron.sh\n    volumes:\n      - \"{{ nextcloud_data_directory }}/nextcloud:/var/www/html:rw\"\n    env:\n      MYSQL_HOST: \"mysql\"\n      MYSQL_DATABASE: \"nextcloud\"\n      MYSQL_USER: \"{{ nextcloud_sql_user }}\"\n      MYSQL_PASSWORD: \"{{ nextcloud_sql_password }}\"\n      NEXTCLOUD_TRUSTED_DOMAINS: \"{{ nextcloud_hostname }}.{{ ansible_nas_domain }}\"\n      PUID: \"{{ nextcloud_user_id }}\"\n      PGID: \"{{ nextcloud_group_id }}\"\n      TZ: \"{{ ansible_nas_timezone }}\"\n    restart_policy: unless-stopped\n    memory: \"{{ nextcloud_memory }}\"\n\n```\n\n\n\n",
      "summary": "AJAX wasn't cutting it, traditional crontab in containers doesn't make much sense to me, webcron is recommended but I don't want to register with anything...",
      "date_published": "2022-12-13T06:43:45Z",
      "date_modified": "2022-12-13T06:43:45Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "docker",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/call-basicconfig-to-get-python-log-messages-in-ipython/",
      "url": "https://pype.dev/call-basicconfig-to-get-python-log-messages-in-ipython/",
      "title": "Call basicConfig to get Python log messages in iPython",
      "content_html": "\u003ch1 id=\"logging-instead-of-printing\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eLogging instead of printing\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eI am trying to adopt \u003ccode\u003elogger.debug\u003c/code\u003e instead of \u003ccode\u003eprint\u003c/code\u003e but ran into a confusing\nthing in ipython during Advent of Code\u0026hellip; I riddled by script with\n\u003ccode\u003elogger.debug\u003c/code\u003e (yes after setting \u003ccode\u003elogging.setLevel('DEBUG')\u003c/code\u003e) but in ipython\nnone of my log messages showed up!\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003elogging\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003elogger\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elogging\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egetLogger\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vm\"\u003e__name__\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003elogger\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esetLevel\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;DEBUG\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTurns out what I was missing was a call to \u003ccode\u003ebasicConfig\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003elogging\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# forget this and your messages are in the ether! or at least not seen in ipython...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003elogging\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebasicConfig\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003elogger\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elogging\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egetLogger\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vm\"\u003e__name__\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003elogger\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esetLevel\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;DEBUG\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"bonus\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eBonus\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eWant your new messages to show up while iterating on something without killing\nthe ipython kernel?\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003eimportlib\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003ereload\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ereload\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elogging\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# to make sure you get new log messages you add while developing!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\n# Logging instead of printing\n\nI am trying to adopt `logger.debug` instead of `print` but ran into a confusing\nthing in ipython during Advent of Code... I riddled by script with\n`logger.debug` (yes after setting `logging.setLevel('DEBUG')`) but in ipython\nnone of my log messages showed up!\n\n```python\nimport logging\n\nlogger = logging.getLogger(__name__)\nlogger.setLevel(\"DEBUG\")\n\n```\n\nTurns out what I was missing was a call to `basicConfig`\n\n```python\nimport logging\n\n# forget this and your messages are in the ether! or at least not seen in ipython...\nlogging.basicConfig()\n\nlogger = logging.getLogger(__name__)\nlogger.setLevel(\"DEBUG\")\n```\n\n\n# Bonus\n\nWant your new messages to show up while iterating on something without killing\nthe ipython kernel?\n\n```python\nfrom importlib import reload\nreload(logging) # to make sure you get new log messages you add while developing!\n\n```\n",
      "summary": "I am trying to adopt instead of but ran into a confusing thing in ipython during Advent of Code... I riddled by script with (yes after setting ) but in...",
      "date_published": "2022-12-10T14:04:23Z",
      "date_modified": "2022-12-10T14:04:23Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/new-lines-in-markdown-tables/",
      "url": "https://pype.dev/new-lines-in-markdown-tables/",
      "title": "New lines in Markdown tables",
      "content_html": "\u003cp\u003eI wanted to break down some long lines in a Markdown table cell to make it look\nnicer on my blog but \\n didn\u0026rsquo;t do anything for me\u0026hellip; turns out \u003cbr/\u003e is the\nmagic sauce\u003c/p\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e\u003cem\u003eColumn 1\u003c/em\u003e\u003c/th\u003e\n\u003cth\u003e\u003cem\u003eColumn 2\u003c/em\u003e\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003eKey\u003c/td\u003e\n\u003ctd\u003eDoggo ipsum many pats. Borkdrive borking doggo doing me a frighten doggorino, noodle horse heckin. what a nice floof. Pupper borking doggo you are doing me a frighten, much ruin diet.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u0026mdash;\u003c/td\u003e\n\u003ctd\u003e\u0026mdash;\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e\u003cem\u003eColumn 1\u003c/em\u003e\u003c/th\u003e\n\u003cth\u003e\u003cem\u003eColumn 2\u003c/em\u003e\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003eKey\u003c/td\u003e\n\u003ctd\u003eDoggo ipsum many pats. \u003cbr/\u003e Borkdrive borking doggo doing me a frighten doggorino, noodle horse heckin. \u003cbr/\u003e what a nice floof. \u003cbr/\u003e Pupper borking doggo you are doing me a frighten, much ruin diet.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u0026mdash;\u003c/td\u003e\n\u003ctd\u003e\u0026mdash;\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n",
      "content_text": "\nI wanted to break down some long lines in a Markdown table cell to make it look\nnicer on my blog but \\n didn't do anything for me... turns out \u003cbr/\u003e is the\nmagic sauce\n\n| *Column 1* | *Column 2* |\n| --- | --- |\n| Key | Doggo ipsum many pats. Borkdrive borking doggo doing me a frighten doggorino, noodle horse heckin. what a nice floof. Pupper borking doggo you are doing me a frighten, much ruin diet. |\n| --- | --- |\n\n\n| *Column 1* | *Column 2* |\n| --- | --- |\n| Key | Doggo ipsum many pats. \u003cbr/\u003e Borkdrive borking doggo doing me a frighten doggorino, noodle horse heckin. \u003cbr/\u003e what a nice floof. \u003cbr/\u003e Pupper borking doggo you are doing me a frighten, much ruin diet. |\n| --- | --- |\n",
      "summary": "I wanted to break down some long lines in a Markdown table cell to make it look nicer on my blog but \\n didn't do anything for me... turns out is the magic...",
      "date_published": "2022-11-25T13:35:05Z",
      "date_modified": "2022-11-25T13:35:05Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vim",
        "webdev",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/make-a-series-of-directories-fast/",
      "url": "https://pype.dev/make-a-series-of-directories-fast/",
      "title": "Make a series of directories fast!",
      "content_html": "\u003cp\u003emkdir s{1..10} will make directories s1, s2, \u0026hellip; s10 in one command!\u003c/p\u003e\n",
      "content_text": "\nmkdir s{1..10} will make directories s1, s2, ... s10 in one command!\n",
      "summary": "mkdir s{1..10} will make directories s1, s2, ... s10 in one command!",
      "date_published": "2022-11-10T15:27:50Z",
      "date_modified": "2022-11-10T15:27:50Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/case-insensitive-search-in-vim/",
      "url": "https://pype.dev/case-insensitive-search-in-vim/",
      "title": "Case-insensitive search in Vim",
      "content_html": "\u003cp\u003e/mysearch\\c will match mysearch, MYSEARCH, mYSeArCh\u0026hellip;\u003c/p\u003e\n",
      "content_text": "\n/mysearch\\c will match mysearch, MYSEARCH, mYSeArCh... \n",
      "summary": "/mysearch\\c will match mysearch, MYSEARCH, mYSeArCh...",
      "date_published": "2022-10-21T06:40:21Z",
      "date_modified": "2022-10-21T06:40:21Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vim",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/limit-zfs-list-to-avoid-docker-vomit/",
      "url": "https://pype.dev/limit-zfs-list-to-avoid-docker-vomit/",
      "title": "Limit zfs list to avoid docker vomit",
      "content_html": "\u003cp\u003ezfs list has a flag -r, but if you use zfs driver for \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e then you\u0026rsquo;ll get\nflooded with every docker volume in the world. zfs list -r -d N will limit the\ndept of the print out, so zfs list -r -d 2 gives me tank, tank/encrypted,\ntank/encrypted/docker -\u0026gt; but then I don\u0026rsquo;t see all the continer volumes\u003c/p\u003e\n",
      "content_text": "\nzfs list has a flag -r, but if you use zfs driver for docker then you'll get\nflooded with every docker volume in the world. zfs list -r -d N will limit the\ndept of the print out, so zfs list -r -d 2 gives me tank, tank/encrypted,\ntank/encrypted/docker -\u003e but then I don't see all the continer volumes\n",
      "summary": "zfs list has a flag -r, but if you use zfs driver for docker then you'll get flooded with every docker volume in the world. zfs list -r -d N will limit the...",
      "date_published": "2022-10-20T06:39:18Z",
      "date_modified": "2022-10-20T06:39:18Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "zfs",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/use-the-right-lspsage-you-dope/",
      "url": "https://pype.dev/use-the-right-lspsage-you-dope/",
      "title": "Use the right lspsage, you dope!",
      "content_html": "\u003cp\u003eIf you think glepnir/lspsaga.nvim config will work with tali5/lspsaga.nvim installed\u0026hellip; you\u0026rsquo;re in for a rude awakening. Double check your forks folks!\u003c/p\u003e\n",
      "content_text": "\nIf you think glepnir/lspsaga.nvim config will work with tali5/lspsaga.nvim installed... you're in for a rude awakening. Double check your forks folks!\n",
      "summary": "If you think glepnir/lspsaga.nvim config will work with tali5/lspsaga.nvim installed... you're in for a rude awakening. Double check your forks folks!",
      "date_published": "2022-10-14T19:41:44Z",
      "date_modified": "2022-10-14T19:41:44Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vim",
        "cli",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/you-can-embed-gifs/",
      "url": "https://pype.dev/you-can-embed-gifs/",
      "title": "You can embed gifs?!",
      "content_html": "\u003cp\u003eI learned you can embed gifs and stuch in things other than GH personal pages!\u003c/p\u003e\n\u003cp\u003eSo here I\u0026rsquo;ll keep ones I like and might use later\u003c/p\u003e\n\u003ciframe src=\"https://giphy.com/embed/jXBwoFfPsCCoE\" width=\"25px\" height=\"25px\" frameBorder=\"0\" class=\"giphy-embed\" allowFullScreen\u003e\u003c/iframe\u003e\u003cp\u003e\u003ca href=\"https://giphy.com/gifs/tech-rainbow-jXBwoFfPsCCoE\"\u003evia GIPHY\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nI learned you can embed gifs and stuch in things other than GH personal pages!\n\n\nSo here I'll keep ones I like and might use later\n\n\u003ciframe src=\"https://giphy.com/embed/jXBwoFfPsCCoE\" width=\"25px\" height=\"25px\" frameBorder=\"0\" class=\"giphy-embed\" allowFullScreen\u003e\u003c/iframe\u003e\u003cp\u003e\u003ca href=\"https://giphy.com/gifs/tech-rainbow-jXBwoFfPsCCoE\"\u003evia GIPHY\u003c/a\u003e\u003c/p\u003e\n",
      "summary": "I learned you can embed gifs and stuch in things other than GH personal pages!",
      "date_published": "2022-10-09T13:39:39Z",
      "date_modified": "2022-10-09T13:39:39Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "webdev",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/dont-forget-to-load-xmp/",
      "url": "https://pype.dev/dont-forget-to-load-xmp/",
      "title": "Don’t forget to load XMP!",
      "content_html": "\u003cp\u003eBought some DDR4-3600 speed RAM but only seeing 2666? Load up the BIOS, find DRAM config or something similar, and make sure to load the XMP profile to get that advertised RAM speed!\u003c/p\u003e\n",
      "content_text": "\nBought some DDR4-3600 speed RAM but only seeing 2666? Load up the BIOS, find DRAM config or something similar, and make sure to load the XMP profile to get that advertised RAM speed!\n",
      "summary": "Bought some DDR4-3600 speed RAM but only seeing 2666? Load up the BIOS, find DRAM config or something similar, and make sure to load the XMP profile to get...",
      "date_published": "2022-10-09T07:17:43Z",
      "date_modified": "2022-10-09T07:17:43Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/my-passmark-scores/",
      "url": "https://pype.dev/my-passmark-scores/",
      "title": "My PassMark Scores",
      "content_html": "\u003cdiv class=\"admonition note\"\u003e\n\u003cp class=\"admonition-title\"\u003eBabyblue v2\u003c/p\u003e\n\u003cp\u003eRyzen 5700x + 32 GB 3200 CL16 RAM\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=503041456656\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/503041456656.png\" alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv class=\"admonition note\"\u003e\n\u003cp class=\"admonition-title\"\u003eVenus \u003c/p\u003e\n\u003cp\u003eRyzen 3200G + 16GB 3600 CL16 RAM\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=502360857958\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/502360857958.png\" height=120px alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv class=\"admonition note\"\u003e\n\u003cp class=\"admonition-title\"\u003eHogwarts\u003c/p\u003e\n\u003cp\u003eDual Xeon X5650 @2.67GHz | 90 GB 1883 DDR3 ECC RAM\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=502361259420\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/502361259420.png\" height=120px alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv class=\"admonition note\"\u003e\n\u003cp class=\"admonition-title\"\u003eServer 2 ... Change the name\u003c/p\u003e\n\u003cp\u003eRyzen 5700G + 128GB 3200 CL18 RAM\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=502361424410\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/502361424410.png\" height=120px alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv class=\"admonition note\"\u003e\n\u003cp class=\"admonition-title\"\u003eServer 2 with XMP enabled and undervolted from 1.35 to 1.2\u003c/p\u003e\n\u003cp\u003eRyzen 5700G + 128GB 3200 CL18 RAM\u003c/p\u003e\n\u003cp\u003eUndervolted to 1.2V (from 1.35)\nXMP enabled\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=502363064921\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/502363064921.png\" height=120px alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/div\u003e\n",
      "content_text": "\n!!! note \"Babyblue v2\"\n\n    Ryzen 5700x + 32 GB 3200 CL16 RAM\n\n    \u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=503041456656\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/503041456656.png\" alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\n\n!!! note \"Venus \"\n\n    Ryzen 3200G + 16GB 3600 CL16 RAM\n\n    \u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=502360857958\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/502360857958.png\" height=120px alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\n\n!!! note \"Hogwarts\"\n\n    Dual Xeon X5650 @2.67GHz | 90 GB 1883 DDR3 ECC RAM\n\n    \u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=502361259420\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/502361259420.png\" height=120px alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\n\n\n!!! note \"Server 2 ... Change the name\"\n\n    Ryzen 5700G + 128GB 3200 CL18 RAM\n\n    \u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=502361424410\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/502361424410.png\" height=120px alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\n\n!!! note \"Server 2 with XMP enabled and undervolted from 1.35 to 1.2\"\n\n    Ryzen 5700G + 128GB 3200 CL18 RAM\n\n    Undervolted to 1.2V (from 1.35)\n    XMP enabled\n\n    \u003ca href=\"https://www.passmark.com/baselines/V10/display.php?id=502363064921\"\u003e\u003cimg src=\"https://www.passmark.com/baselines/V10/media/502363064921.png\" height=120px alt=\"PassMark Rating\" border=\"0\" /\u003e\u003c/a\u003e\n",
      "summary": "!!! note \"Babyblue v2\"",
      "date_published": "2022-10-08T18:43:29Z",
      "date_modified": "2022-10-08T18:43:29Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/suddenly-ssh-requires-a-password/",
      "url": "https://pype.dev/suddenly-ssh-requires-a-password/",
      "title": "Suddenly SSH requires a password",
      "content_html": "\u003cp\u003essh -v -i ~/.ssh/id_rsa nic@hogwarts\u003c/p\u003e\n\u003cp\u003eTHen we can look at print outs\u003c/p\u003e\n\u003cp\u003ecat /var/log/auth.log  also showed me that I had too wide permissions on files in ~/.ssh -\u0026gt; probably changed from an rsync job\u003c/p\u003e\n",
      "content_text": "\n\nssh -v -i ~/.ssh/id_rsa nic@hogwarts\n\nTHen we can look at print outs\n\ncat /var/log/auth.log  also showed me that I had too wide permissions on files in ~/.ssh -\u003e probably changed from an rsync job\n",
      "summary": "ssh -v -i ~/.ssh/id_rsa nic@hogwarts",
      "date_published": "2022-10-07T11:14:37Z",
      "date_modified": "2022-10-07T11:14:37Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/quick-setup-of-zfs-encrytped-datasets-with-sane-permissions/",
      "url": "https://pype.dev/quick-setup-of-zfs-encrytped-datasets-with-sane-permissions/",
      "title": "Quick setup of ZFS encrytped datasets with sane permissions",
      "content_html": "\u003cp\u003eAssuming you have a pool called \u003ccode\u003etank\u003c/code\u003e\u0026hellip;\u003c/p\u003e\n\u003cp\u003eAnd assuming you have an encrypted dataset (See \u003ca href=\"https://arstechnica.com/gadgets/2021/06/a-quick-start-guide-to-openzfs-native-encryption/\"\u003eJim Saltar\u0026rsquo;s short\nintro\u003c/a\u003e)\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eCreate a group for permissions - in my case I have one called \u003ccode\u003ehome\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eThen if there\u0026rsquo;s anything in \u003ccode\u003e/tank/encrypted\u003c/code\u003e his it with \u003ccode\u003echgrp -R home /tank/encrypted\u003c/code\u003e to give the \u003ccode\u003ehome\u003c/code\u003e group ownership\u003c/li\u003e\n\u003cli\u003eNext we need to make sure that the members of \u003ccode\u003ehome\u003c/code\u003e can do the writing\u0026hellip;\nso \u003ccode\u003echmod 775 -R /tank/encrypted\u003c/code\u003e will do the trick\u003c/li\u003e\n\u003cli\u003eFinally we want to make sure that all data created inside our dataset has\nthe same set of permissions with \u003ccode\u003echmod g+s /tank/encrypted\u003c/code\u003e and \u003ccode\u003echmod g+w /tank/encrypted\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n",
      "content_text": "\nAssuming you have a pool called `tank`...\n\nAnd assuming you have an encrypted dataset (See [Jim Saltar's short\nintro](https://arstechnica.com/gadgets/2021/06/a-quick-start-guide-to-openzfs-native-encryption/))\n\n1. Create a group for permissions - in my case I have one called `home`\n2. Then if there's anything in `/tank/encrypted` his it with `chgrp -R home\n   /tank/encrypted` to give the `home` group ownership\n3. Next we need to make sure that the members of `home` can do the writing...\n   so `chmod 775 -R /tank/encrypted` will do the trick\n4. Finally we want to make sure that all data created inside our dataset has\n   the same set of permissions with `chmod g+s /tank/encrypted` and `chmod g+w\n   /tank/encrypted`\n\n",
      "summary": "Assuming you have a pool called ...",
      "date_published": "2022-10-06T19:58:01Z",
      "date_modified": "2022-10-06T19:58:01Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "zfs",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/check-your-bios-version-on-ubuntu/",
      "url": "https://pype.dev/check-your-bios-version-on-ubuntu/",
      "title": "Check Your BIOS Version On Ubuntu",
      "content_html": "\u003cp\u003esudo dmidecode -s bios-version\u003c/p\u003e\n",
      "content_text": "\nsudo dmidecode -s bios-version\n",
      "summary": "sudo dmidecode -s bios-version",
      "date_published": "2022-10-02T18:21:58Z",
      "date_modified": "2022-10-02T18:21:58Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/append-string-to-list-of-files-with-xarg/",
      "url": "https://pype.dev/append-string-to-list-of-files-with-xarg/",
      "title": "Append string to list of files with xarg",
      "content_html": "\u003cp\u003e❯ find . -name \u0026ldquo;requirements.in\u0026rdquo; -print0 | xargs -0 sh -c \u0026lsquo;for arg in \u0026ldquo;$@\u0026rdquo;; do echo \u0026ldquo;awscli\u0026rdquo; \u0026raquo;\u0026quot;$arg\u0026quot;; done\u0026rsquo;\u003c/p\u003e\n",
      "content_text": "\n❯ find . -name \"requirements.in\" -print0 | xargs -0 sh -c 'for arg in \"$@\"; do echo \"awscli\" \u003e\u003e\"$arg\"; done'\n\n",
      "summary": "❯ find . -name \"requirements.in\" -print0 | xargs -0 sh -c 'for arg in \"$@\"; do echo \"awscli\" \u003e\u003e\"$arg\"; done'",
      "date_published": "2022-09-21T11:26:02Z",
      "date_modified": "2022-09-21T11:26:02Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "bash",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/destroying-tmux-sessions-with-fzf/",
      "url": "https://pype.dev/destroying-tmux-sessions-with-fzf/",
      "title": "Destroying Tmux sessions with fzf",
      "content_html": "\u003cp\u003eI use Tmux and Vim for most of my workflow, but I end up with a lot of dangling\ntmux sessions that dont\u0026rsquo; really need to persist\u0026hellip; but killing them one at a\ntime is a pain so I wrote a little script-kitty nonsense to pipe multiple\nchoices from fzf into \u003ccode\u003etmux kill-session\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eI defined a little function in my \u003ccode\u003e.zshrc\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edestroy\u003cspan class=\"o\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    tmux list-sessions -F \u003cspan class=\"s1\"\u003e\u0026#39;#{session_name}\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e fzf -m \u003cspan class=\"p\"\u003e|\u003c/span\u003e xargs -d \u003cspan class=\"s1\"\u003e$\u0026#39;\\n\u0026#39;\u003c/span\u003e sh -c \u003cspan class=\"s1\"\u003e\u0026#39;echo \u0026#34;killing $0\u0026#34;; tmux kill-session -t \u0026#34;$0\u0026#34;; for arg;do echo \u0026#34;killing $arg\u0026#34;;tmux kill-session -t \u0026#34;$arg\u0026#34;; done\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebindkey -s \u003cspan class=\"s1\"\u003e\u0026#39;^d\u0026#39;\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;destroy \\n\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003etmux list-sessions -F '#{session_name}' \u003c/code\u003e prints all my active tmux sessions to the console with the format of just their name\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epype.dev   main   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003epype.dev\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e proxy\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ tmux list-sessions -F \u003cspan class=\"s1\"\u003e\u0026#39;#{session_name}\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esession-01\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esession-02\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esession-03\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003ePipe that to \u003ccode\u003efzf -m\u003c/code\u003e to allow multiple choices to be made using tab\u003c/p\u003e\n\u003cp\u003eThen the nasty bit in \u003ccode\u003exargs\u003c/code\u003e\u0026hellip; I echo \u003ccode\u003ekilling @0\u003c/code\u003e and \u003ccode\u003ekilling $arg\u003c/code\u003e because the \u003ccode\u003esh -c\u003c/code\u003e passes the first tmux session name to \u003ccode\u003e@0\u003c/code\u003e (it\u0026rsquo;s just what bash does) and then the rest get handled in the for loop.\u003c/p\u003e\n\u003cp\u003eBasically then I get an fzf list to choose multiple tmux sessions to destroy to clean up some RAM!\u003c/p\u003e\n",
      "content_text": "\nI use Tmux and Vim for most of my workflow, but I end up with a lot of dangling\ntmux sessions that dont' really need to persist... but killing them one at a\ntime is a pain so I wrote a little script-kitty nonsense to pipe multiple\nchoices from fzf into `tmux kill-session`\n\nI defined a little function in my `.zshrc`\n```bash \ndestroy() { \n    tmux list-sessions -F '#{session_name}' | fzf -m | xargs -d $'\\n' sh -c 'echo \"killing $0\"; tmux kill-session -t \"$0\"; for arg;do echo \"killing $arg\";tmux kill-session -t \"$arg\"; done'\n}\nbindkey -s '^d' 'destroy \\n'\n```\n\n`tmux list-sessions -F '#{session_name}' ` prints all my active tmux sessions to the console with the format of just their name\n\n```bash \npype.dev   main   ×1 via   v3.8.11(pype.dev)  on  (us-east-1) proxy\n❯ tmux list-sessions -F '#{session_name}'\nsession-01\nsession-02\nsession-03\n...\n```\n\nPipe that to `fzf -m` to allow multiple choices to be made using tab\n\nThen the nasty bit in `xargs`... I echo `killing @0` and `killing $arg` because the `sh -c` passes the first tmux session name to `@0` (it's just what bash does) and then the rest get handled in the for loop.\n\nBasically then I get an fzf list to choose multiple tmux sessions to destroy to clean up some RAM!\n",
      "summary": "I use Tmux and Vim for most of my workflow, but I end up with a lot of dangling tmux sessions that dont' really need to persist... but killing them one at a...",
      "date_published": "2022-09-15T10:27:58Z",
      "date_modified": "2022-09-15T10:27:58Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "cli",
        "bash",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/trick-to-login-to-web-server-on-another-box-on-lan/",
      "url": "https://pype.dev/trick-to-login-to-web-server-on-another-box-on-lan/",
      "title": "Trick to login to web server on another box on lan",
      "content_html": "\u003cp\u003eTODO: change title - add \u0026ndash;listen or \u0026ndash;host or something as 0.0.0.0\u0026hellip; that way it listens not on localhost or 127.0.0.1, but on all addresses!\u003c/p\u003e\n",
      "content_text": "\nTODO: change title - add --listen or --host or something as 0.0.0.0... that way it listens not on localhost or 127.0.0.1, but on all addresses!\n",
      "summary": "TODO: change title - add --listen or --host or something as 0.0.0.0... that way it listens not on localhost or 127.0.0.1, but on all addresses!",
      "date_published": "2022-09-12T18:48:45Z",
      "date_modified": "2022-09-12T18:48:45Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/check-your-smart-status-with-smartctl/",
      "url": "https://pype.dev/check-your-smart-status-with-smartctl/",
      "title": "Check your SMART status with smartctl",
      "content_html": "\u003cp\u003e\u003ca href=\"https://www.simplified.guide/linux/disk-health-check\"\u003ehttps://www.simplified.guide/linux/disk-health-check\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"install\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eInstall\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eFor ubuntu/debian based distros (which is what I primarly use presently)\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003esudo apt update -y \u0026amp;\u0026amp; sudo apt install smartmontools -y\u003c/code\u003e\u003c/p\u003e\n\u003ch1 id=\"list-hard-drives\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eList hard drives\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003elsblk | grep disk\u003c/code\u003e is one way or \u003ccode\u003esudo lshw -c disk\u003c/code\u003e is another\u003c/p\u003e\n\u003ch1 id=\"smartctl\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003esmartctl\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eUse a device\u0026rsquo;s logical name such as \u003ccode\u003edev/sda\u003c/code\u003e, not a partition of the disk\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003esudo smartctl -t short /dev/sda\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edotfiles   home   ×3  ×2  ×2 via   v3.10.6(dotfiles)  took 11s\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e❯ sudo smartctl -t short /dev/sda\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esmartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.15.0-48-generic] (local build)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003eCopyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003eSending command: \u0026#34;Execute SMART Short self-test routine immediately in off-line mode\u0026#34;.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003eDrive command \u0026#34;Execute SMART Short self-test routine immediately in off-line mode\u0026#34; successful.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003eTesting has begun.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003ePlease wait 2 minutes for test to complete.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003eTest will complete after Fri Sep 23 05:59:39 2022 CDT\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003eUse smartctl -X to abort test.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"check-status\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003echeck status\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003esudo smartctl -H /dev/sda\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003edotfiles   home   ×3  ×2  ×2 via   v3.10.6(dotfiles)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e❯ sudo smartctl -H /dev/sda\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esmartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.15.0-48-generic] (local build)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003eCopyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e=== START OF READ SMART DATA SECTION ===\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003eSMART overall-health self-assessment test result: PASSED\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nhttps://www.simplified.guide/linux/disk-health-check\n\n# Install\nFor ubuntu/debian based distros (which is what I primarly use presently)\n\n`sudo apt update -y \u0026\u0026 sudo apt install smartmontools -y`\n\n# List hard drives\n\n`lsblk | grep disk` is one way or `sudo lshw -c disk` is another\n\n# smartctl \n\nUse a device's logical name such as `dev/sda`, not a partition of the disk\n\n`sudo smartctl -t short /dev/sda`\n\n```console\ndotfiles   home   ×3  ×2  ×2 via   v3.10.6(dotfiles)  took 11s\n❯ sudo smartctl -t short /dev/sda\nsmartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.15.0-48-generic] (local build)\nCopyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org\n\n=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\nSending command: \"Execute SMART Short self-test routine immediately in off-line mode\".\nDrive command \"Execute SMART Short self-test routine immediately in off-line mode\" successful.\nTesting has begun.\nPlease wait 2 minutes for test to complete.\nTest will complete after Fri Sep 23 05:59:39 2022 CDT\nUse smartctl -X to abort test.\n```\n\n# check status\n\n`sudo smartctl -H /dev/sda`\n\n```console\n\ndotfiles   home   ×3  ×2  ×2 via   v3.10.6(dotfiles)\n❯ sudo smartctl -H /dev/sda\nsmartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.15.0-48-generic] (local build)\nCopyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org\n\n=== START OF READ SMART DATA SECTION ===\nSMART overall-health self-assessment test result: PASSED\n\n\n```\n",
      "summary": "https://www.simplified.guide/linux/disk-health-check",
      "date_published": "2022-08-29T06:30:27Z",
      "date_modified": "2022-08-29T06:30:27Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "cli",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/playing-with-mdformat/",
      "url": "https://pype.dev/playing-with-mdformat/",
      "title": "Playing with mdformat",
      "content_html": "\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ethis\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ethis\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;what is taking so long black!!\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "```python\nimport this; print(this); print(\"what is taking so long black!!\")\n```\n",
      "date_published": "2022-08-23T06:56:15Z",
      "date_modified": "2022-08-23T06:56:15Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "vim",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/paperless-ngx-filtering-on-ids-instead-of-values/",
      "url": "https://pype.dev/paperless-ngx-filtering-on-ids-instead-of-values/",
      "title": "Paperless-NGX filtering on IDs instead of values",
      "content_html": "\u003cp\u003eTo filter for saved views from the admin console you have to use the \u003ccode\u003eid\u003c/code\u003e of\nthe tag you are using to filter on\u0026hellip;\u003c/p\u003e\n\u003cp\u003eEx: filtering for tags \u0026ldquo;Inbox\u0026rdquo; doesn\u0026rsquo;t work, but for tag 18 does (the id fo the\nInbox tag). I can find that ID by clicking on the tag and looking at the url\nbar\u003c/p\u003e\n",
      "content_text": "\nTo filter for saved views from the admin console you have to use the `id` of\nthe tag you are using to filter on...\n\nEx: filtering for tags \"Inbox\" doesn't work, but for tag 18 does (the id fo the\nInbox tag). I can find that ID by clicking on the tag and looking at the url\nbar\n",
      "summary": "To filter for saved views from the admin console you have to use the of the tag you are using to filter on...",
      "date_published": "2022-08-09T08:28:51Z",
      "date_modified": "2022-08-09T08:28:51Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/mounting-exfat-usb-in-linux/",
      "url": "https://pype.dev/mounting-exfat-usb-in-linux/",
      "title": "Mounting exFAT USB in Linux",
      "content_html": "\u003ch1 id=\"steps\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eSteps\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003esudo fdisk -l\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003ethen look for the device and partition\u003c/p\u003e\n\u003cp\u003eget the Type column\u003c/p\u003e\n\u003cp\u003emount\u003c/p\u003e\n\u003ch2 id=\"example\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eExample\u003c/span\u003e \u003ca href=\"#example\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cpre\u003e\u003ccode\u003e\ndumbledore in /media  NO PYTHON VENV SET \n❯ sudo fdisk -l\n\n...\n\nDevice     Boot    Start      End  Sectors  Size Id Type\n/dev/sdk1  *        2048 60371951 60369904 28.8G  7 HPFS/NTFS/exFAT\n/dev/sdk2       60371952 60437487    65536   32M ef EFI (FAT-12/16/32)\n\n\ndumbledore in /media  NO PYTHON VENV SET \n❯ sudo mount -t ntfs /dev/sdk1 /media/ventoy-usb -o uid=1000                       \nNTFS signature is missing.\nFailed to mount '/dev/sdk1': Invalid argument\nThe device '/dev/sdk1' doesn't seem to have a valid NTFS.\nMaybe the wrong device is used? Or the whole disk instead of a\npartition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n\ndumbledore in /media  NO PYTHON VENV SET \n✗ sudo mount -t exfat /dev/sdk1 /media/ventoy-usb -o uid=1000\n\n\u003c/code\u003e\u003c/pre\u003e\n",
      "content_text": "\n# Steps\n\n`sudo fdisk -l`\n\nthen look for the device and partition\n\nget the Type column\n\nmount\n\n## Example\n\n```\n\ndumbledore in /media  NO PYTHON VENV SET \n❯ sudo fdisk -l\n\n...\n\nDevice     Boot    Start      End  Sectors  Size Id Type\n/dev/sdk1  *        2048 60371951 60369904 28.8G  7 HPFS/NTFS/exFAT\n/dev/sdk2       60371952 60437487    65536   32M ef EFI (FAT-12/16/32)\n\n\ndumbledore in /media  NO PYTHON VENV SET \n❯ sudo mount -t ntfs /dev/sdk1 /media/ventoy-usb -o uid=1000                       \nNTFS signature is missing.\nFailed to mount '/dev/sdk1': Invalid argument\nThe device '/dev/sdk1' doesn't seem to have a valid NTFS.\nMaybe the wrong device is used? Or the whole disk instead of a\npartition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n\ndumbledore in /media  NO PYTHON VENV SET \n✗ sudo mount -t exfat /dev/sdk1 /media/ventoy-usb -o uid=1000\n\n```\n",
      "date_published": "2022-07-31T07:38:43Z",
      "date_modified": "2022-07-31T07:38:43Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/xrdp-authentication-required-to-create-managed-color-device/",
      "url": "https://pype.dev/xrdp-authentication-required-to-create-managed-color-device/",
      "title": "Xrdp-Authentication-Required-To-Create-Managed-Color-Device",
      "content_html": "\u003cp\u003eI just need to RDP into an Ubuntu box via Remmina and everytime I login I have\nto authenticate to create a color managed device\u0026hellip; which I don\u0026rsquo;t even know\nwhat that is!\u003c/p\u003e\n\u003cp\u003eTo fix it?\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003evim /etc/polkit-1/localauthority.conf.d/02-allow-colord.conf\u003c/code\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epolkit.addRule(function(action, subject) {\n if ((action.id == \u0026quot;org.freedesktop.color-manager.create-device\u0026quot; ||\n action.id == \u0026quot;org.freedesktop.color-manager.create-profile\u0026quot; ||\n action.id == \u0026quot;org.freedesktop.color-manager.delete-device\u0026quot; ||\n action.id == \u0026quot;org.freedesktop.color-manager.delete-profile\u0026quot; ||\n action.id == \u0026quot;org.freedesktop.color-manager.modify-device\u0026quot; ||\n action.id == \u0026quot;org.freedesktop.color-manager.modify-profile\u0026quot;) \u0026amp;\u0026amp;\n subject.isInGroup(\u0026quot;{users}\u0026quot;)) {\n return polkit.Result.YES;\n }\n });\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eGive\u0026rsquo;r the \u003ccode\u003e:x\u003c/code\u003e, logout, and you\u0026rsquo;re good to go\u003c/p\u003e\n",
      "content_text": "\nI just need to RDP into an Ubuntu box via Remmina and everytime I login I have\nto authenticate to create a color managed device... which I don't even know\nwhat that is!\n\n\nTo fix it?\n\n`vim /etc/polkit-1/localauthority.conf.d/02-allow-colord.conf`\n\n```\npolkit.addRule(function(action, subject) {\n if ((action.id == \"org.freedesktop.color-manager.create-device\" ||\n action.id == \"org.freedesktop.color-manager.create-profile\" ||\n action.id == \"org.freedesktop.color-manager.delete-device\" ||\n action.id == \"org.freedesktop.color-manager.delete-profile\" ||\n action.id == \"org.freedesktop.color-manager.modify-device\" ||\n action.id == \"org.freedesktop.color-manager.modify-profile\") \u0026\u0026\n subject.isInGroup(\"{users}\")) {\n return polkit.Result.YES;\n }\n });\n```\n\nGive'r the `:x`, logout, and you're good to go\n",
      "summary": "I just need to RDP into an Ubuntu box via Remmina and everytime I login I have to authenticate to create a color managed device... which I don't even know...",
      "date_published": "2022-07-18T00:00:00Z",
      "date_modified": "2022-07-18T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/zfs-permissions-for-sanoid/",
      "url": "https://pype.dev/zfs-permissions-for-sanoid/",
      "title": "ZFS Permissions for Sanoid/Syncoid",
      "content_html": "\u003cp\u003e\u003ccode\u003ezfs allow -u $USER clone,load-key,create,destroy,mount,mountpoint,receive,send,rollback,compression,snapshot,hold,keylocation,bookmark tank\u003c/code\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eload-key only needed if using encrypted datasets\u003c/p\u003e\n\u003c/blockquote\u003e\n",
      "content_text": "\n\n`zfs allow -u $USER clone,load-key,create,destroy,mount,mountpoint,receive,send,rollback,compression,snapshot,hold,keylocation,bookmark tank`\n\n\u003e load-key only needed if using encrypted datasets\n",
      "date_published": "2022-07-08T15:54:58Z",
      "date_modified": "2022-07-08T15:54:58Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "zfs",
        "homelab",
        "cli",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/setup-kvm-to-boot-from-local-pxe-server/",
      "url": "https://pype.dev/setup-kvm-to-boot-from-local-pxe-server/",
      "title": "Setup KVM to boot from local PXE server",
      "content_html": "\u003cp\u003eAssuming you have a PXE server running you only need small edits to the virt-manager config\u003c/p\u003e\n",
      "content_text": "\nAssuming you have a PXE server running you only need small edits to the virt-manager config\n",
      "summary": "Assuming you have a PXE server running you only need small edits to the virt-manager config",
      "date_published": "2022-07-06T12:30:50Z",
      "date_modified": "2022-07-06T12:30:50Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/cheat-on-your-man/",
      "url": "https://pype.dev/cheat-on-your-man/",
      "title": "cheat on your man",
      "content_html": "\u003cp\u003e\u003ccode\u003eman\u003c/code\u003e can be a pain to read\u0026hellip; and there\u0026rsquo;s lots of alternatives out there and one I\u0026rsquo;ve just started playing with is \u003ca href=\"https://github.com/cheat/cheat\"\u003echeat\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eman man\u003c/code\u003e will give you this plus a billion more lines of docs, which is useful when you need it\u0026hellip;\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eMAN\u003cspan class=\"o\"\u003e(\u003c/span\u003e1\u003cspan class=\"o\"\u003e)\u003c/span\u003e                                                                                                                       Manual pager utils                                                                                                                      MAN\u003cspan class=\"o\"\u003e(\u003c/span\u003e1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eNAME\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man - an interface to the on-line reference manuals\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSYNOPSIS\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man  \u003cspan class=\"o\"\u003e[\u003c/span\u003e-C  file\u003cspan class=\"o\"\u003e]\u003c/span\u003e  \u003cspan class=\"o\"\u003e[\u003c/span\u003e-d\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-D\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e--warnings\u003cspan class=\"o\"\u003e[=\u003c/span\u003ewarnings\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-R encoding\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-L locale\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-m system\u003cspan class=\"o\"\u003e[\u003c/span\u003e,...\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-M path\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-S list\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-e extension\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-i\u003cspan class=\"p\"\u003e|\u003c/span\u003e-I\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e--regex\u003cspan class=\"p\"\u003e|\u003c/span\u003e--wildcard\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e--names-only\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-a\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-u\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e--no-subpages\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-P pager\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-r prompt\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-7\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-E encoding\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e--no-hyphenation\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"o\"\u003e[\u003c/span\u003e--no-justification\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-p string\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-t\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-T\u003cspan class=\"o\"\u003e[\u003c/span\u003edevice\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-H\u003cspan class=\"o\"\u003e[\u003c/span\u003ebrowser\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-X\u003cspan class=\"o\"\u003e[\u003c/span\u003edpi\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-Z\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[[\u003c/span\u003esection\u003cspan class=\"o\"\u003e]\u003c/span\u003e page\u003cspan class=\"o\"\u003e[\u003c/span\u003e.section\u003cspan class=\"o\"\u003e]\u003c/span\u003e ...\u003cspan class=\"o\"\u003e]\u003c/span\u003e ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man -k \u003cspan class=\"o\"\u003e[\u003c/span\u003eapropos options\u003cspan class=\"o\"\u003e]\u003c/span\u003e regexp ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man -K \u003cspan class=\"o\"\u003e[\u003c/span\u003e-w\u003cspan class=\"p\"\u003e|\u003c/span\u003e-W\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-S list\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-i\u003cspan class=\"p\"\u003e|\u003c/span\u003e-I\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e--regex\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003esection\u003cspan class=\"o\"\u003e]\u003c/span\u003e term ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man -f \u003cspan class=\"o\"\u003e[\u003c/span\u003ewhatis options\u003cspan class=\"o\"\u003e]\u003c/span\u003e page ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man -l \u003cspan class=\"o\"\u003e[\u003c/span\u003e-C file\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-d\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-D\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e--warnings\u003cspan class=\"o\"\u003e[=\u003c/span\u003ewarnings\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-R encoding\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-L locale\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-P pager\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-r prompt\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-7\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-E encoding\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-p string\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-t\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-T\u003cspan class=\"o\"\u003e[\u003c/span\u003edevice\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-H\u003cspan class=\"o\"\u003e[\u003c/span\u003ebrowser\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-X\u003cspan class=\"o\"\u003e[\u003c/span\u003edpi\u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-Z\u003cspan class=\"o\"\u003e]\u003c/span\u003e file ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man -w\u003cspan class=\"p\"\u003e|\u003c/span\u003e-W \u003cspan class=\"o\"\u003e[\u003c/span\u003e-C file\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-d\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-D\u003cspan class=\"o\"\u003e]\u003c/span\u003e page ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man -c \u003cspan class=\"o\"\u003e[\u003c/span\u003e-C file\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-d\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e-D\u003cspan class=\"o\"\u003e]\u003c/span\u003e page ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man \u003cspan class=\"o\"\u003e[\u003c/span\u003e-?V\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eDESCRIPTION\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man is the system\u003cspan class=\"err\"\u003e\u0026#39;\u003c/span\u003es manual pager.  Each page argument given to man is normally the name of a program, utility or \u003cspan class=\"k\"\u003efunction\u003c/span\u003e.  The manual page associated with each of these arguments is \u003cspan class=\"k\"\u003ethen\u003c/span\u003e found and displayed.  A section, \u003cspan class=\"k\"\u003eif\u003c/span\u003e provided, will direct  man  to  look\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       only  in  that  section of the manual.  The default action is to search in all of the available sections following a pre-defined order \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;1 n l 8 3 2 3posix 3pm 3perl 3am 5 4 9 6 7\u0026#34;\u003c/span\u003e by default, unless overridden by the SECTION directive in /etc/manpath.config\u003cspan class=\"o\"\u003e)\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       and to show only the first page found, even \u003cspan class=\"k\"\u003eif\u003c/span\u003e page exists in several sections.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       The table below shows the section numbers of the manual followed by the types of pages they contain.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e1\u003c/span\u003e   Executable programs or shell commands\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e2\u003c/span\u003e   System calls \u003cspan class=\"o\"\u003e(\u003c/span\u003efunctions provided by the kernel\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e3\u003c/span\u003e   Library calls \u003cspan class=\"o\"\u003e(\u003c/span\u003efunctions within program libraries\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e4\u003c/span\u003e   Special files \u003cspan class=\"o\"\u003e(\u003c/span\u003eusually found in /dev\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e5\u003c/span\u003e   File formats and conventions eg /etc/passwd\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e6\u003c/span\u003e   Games\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e7\u003c/span\u003e   Miscellaneous \u003cspan class=\"o\"\u003e(\u003c/span\u003eincluding macro packages and conventions\u003cspan class=\"o\"\u003e)\u003c/span\u003e, e.g. man\u003cspan class=\"o\"\u003e(\u003c/span\u003e7\u003cspan class=\"o\"\u003e)\u003c/span\u003e, groff\u003cspan class=\"o\"\u003e(\u003c/span\u003e7\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e8\u003c/span\u003e   System administration commands \u003cspan class=\"o\"\u003e(\u003c/span\u003eusually only \u003cspan class=\"k\"\u003efor\u003c/span\u003e root\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"m\"\u003e9\u003c/span\u003e   Kernel routines \u003cspan class=\"o\"\u003e[\u003c/span\u003eNon standard\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       A manual page consists of several sections.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       Conventional section names include NAME, SYNOPSIS, CONFIGURATION, DESCRIPTION, OPTIONS, EXIT STATUS, RETURN VALUE, ERRORS, ENVIRONMENT, FILES, VERSIONS, CONFORMING TO, NOTES, BUGS, EXAMPLE, AUTHORS, and SEE ALSO.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       The following conventions apply to the SYNOPSIS section and can be used as a guide in other sections.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       bold text          \u003cspan class=\"nb\"\u003etype\u003c/span\u003e exactly as shown.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       italic text        replace with appropriate argument.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"o\"\u003e[\u003c/span\u003e-abc\u003cspan class=\"o\"\u003e]\u003c/span\u003e             any or all arguments within \u003cspan class=\"o\"\u003e[\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e are optional.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       -a\u003cspan class=\"p\"\u003e|\u003c/span\u003e-b              options delimited by \u003cspan class=\"p\"\u003e|\u003c/span\u003e cannot be used together.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       argument ...       argument is repeatable.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       \u003cspan class=\"o\"\u003e[\u003c/span\u003eexpression\u003cspan class=\"o\"\u003e]\u003c/span\u003e ...   entire expression within \u003cspan class=\"o\"\u003e[\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e is repeatable.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       Exact rendering may vary depending on the output device.  For instance, man will usually not be able to render italics when running in a terminal, and will typically use underlined or coloured text instead.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       The \u003cspan class=\"nb\"\u003ecommand\u003c/span\u003e or \u003cspan class=\"k\"\u003efunction\u003c/span\u003e illustration is a pattern that should match all possible invocations.  In some cases it is advisable to illustrate several exclusive invocations as is shown in the SYNOPSIS section of this manual page.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eEXAMPLES\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man ls\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e           Display the manual page \u003cspan class=\"k\"\u003efor\u003c/span\u003e the item \u003cspan class=\"o\"\u003e(\u003c/span\u003eprogram\u003cspan class=\"o\"\u003e)\u003c/span\u003e ls.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       man man.7\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e           Display the manual page \u003cspan class=\"k\"\u003efor\u003c/span\u003e macro package man from section 7.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"but-what-if-you-don-t\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eBut what if you don’t?\u003c/span\u003e \u003ca href=\"#but-what-if-you-don-t\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003echeat man\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# To convert a man page to pdf:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eman -t bash \u003cspan class=\"p\"\u003e|\u003c/span\u003e ps2pdf - bash.pdf\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# To view the ascii chart:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eman \u003cspan class=\"m\"\u003e7\u003c/span\u003e ascii\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou get tiny examples to remind you of what you \u003cstrong\u003eprobably\u003c/strong\u003e are trying to do!\u003c/p\u003e\n",
      "content_text": "\n\n`man` can be a pain to read... and there's lots of alternatives out there and one I've just started playing with is [cheat](https://github.com/cheat/cheat)\n\n\n`man man` will give you this plus a billion more lines of docs, which is useful when you need it...\n\n```bash\nMAN(1)                                                                                                                       Manual pager utils                                                                                                                      MAN(1)\n\nNAME\n       man - an interface to the on-line reference manuals\n\nSYNOPSIS\n       man  [-C  file]  [-d] [-D] [--warnings[=warnings]] [-R encoding] [-L locale] [-m system[,...]] [-M path] [-S list] [-e extension] [-i|-I] [--regex|--wildcard] [--names-only] [-a] [-u] [--no-subpages] [-P pager] [-r prompt] [-7] [-E encoding] [--no-hyphenation]\n       [--no-justification] [-p string] [-t] [-T[device]] [-H[browser]] [-X[dpi]] [-Z] [[section] page[.section] ...] ...\n       man -k [apropos options] regexp ...\n       man -K [-w|-W] [-S list] [-i|-I] [--regex] [section] term ...\n       man -f [whatis options] page ...\n       man -l [-C file] [-d] [-D] [--warnings[=warnings]] [-R encoding] [-L locale] [-P pager] [-r prompt] [-7] [-E encoding] [-p string] [-t] [-T[device]] [-H[browser]] [-X[dpi]] [-Z] file ...\n       man -w|-W [-C file] [-d] [-D] page ...\n       man -c [-C file] [-d] [-D] page ...\n       man [-?V]\n\nDESCRIPTION\n       man is the system's manual pager.  Each page argument given to man is normally the name of a program, utility or function.  The manual page associated with each of these arguments is then found and displayed.  A section, if provided, will direct  man  to  look\n       only  in  that  section of the manual.  The default action is to search in all of the available sections following a pre-defined order (\"1 n l 8 3 2 3posix 3pm 3perl 3am 5 4 9 6 7\" by default, unless overridden by the SECTION directive in /etc/manpath.config),\n       and to show only the first page found, even if page exists in several sections.\n\n       The table below shows the section numbers of the manual followed by the types of pages they contain.\n\n       1   Executable programs or shell commands\n       2   System calls (functions provided by the kernel)\n       3   Library calls (functions within program libraries)\n       4   Special files (usually found in /dev)\n       5   File formats and conventions eg /etc/passwd\n       6   Games\n       7   Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)\n       8   System administration commands (usually only for root)\n       9   Kernel routines [Non standard]\n\n       A manual page consists of several sections.\n\n       Conventional section names include NAME, SYNOPSIS, CONFIGURATION, DESCRIPTION, OPTIONS, EXIT STATUS, RETURN VALUE, ERRORS, ENVIRONMENT, FILES, VERSIONS, CONFORMING TO, NOTES, BUGS, EXAMPLE, AUTHORS, and SEE ALSO.\n\n       The following conventions apply to the SYNOPSIS section and can be used as a guide in other sections.\n\n       bold text          type exactly as shown.\n       italic text        replace with appropriate argument.\n       [-abc]             any or all arguments within [ ] are optional.\n       -a|-b              options delimited by | cannot be used together.\n       argument ...       argument is repeatable.\n       [expression] ...   entire expression within [ ] is repeatable.\n\n       Exact rendering may vary depending on the output device.  For instance, man will usually not be able to render italics when running in a terminal, and will typically use underlined or coloured text instead.\n\n       The command or function illustration is a pattern that should match all possible invocations.  In some cases it is advisable to illustrate several exclusive invocations as is shown in the SYNOPSIS section of this manual page.\n\nEXAMPLES\n       man ls\n           Display the manual page for the item (program) ls.\n\n       man man.7\n           Display the manual page for macro package man from section 7.\n```\n\n\n## But what if you don't?\n\n`cheat man`\n\n```bash\n# To convert a man page to pdf:\nman -t bash | ps2pdf - bash.pdf\n\n# To view the ascii chart:\nman 7 ascii\n```\n\nYou get tiny examples to remind you of what you **probably** are trying to do!\n",
      "summary": "can be a pain to read... and there's lots of alternatives out there and one I've just started playing with is cheat",
      "date_published": "2022-06-23T09:35:31Z",
      "date_modified": "2022-06-23T09:35:31Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/reset-ssh-key-passphrase/",
      "url": "https://pype.dev/reset-ssh-key-passphrase/",
      "title": "Reset SSH key passphrase",
      "content_html": "\u003cp\u003eI got into a pickle where I encrypted the ssh keys I use for my SSH connections on LAN, but then I couldn\u0026rsquo;t run my ansible playbook on my server! ssh-keygen -p and leave the new passphrase blank saved my day (although password protected key files are safer!)\u003c/p\u003e\n\u003ch1 id=\"tl-dr-just-reset-it-to-nothing\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTL;DR - just reset it to nothing\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003essh-keygen -p\u003c/code\u003e will let you reset the passphrase on your ssh keys (good for you! yay security!)\u003c/p\u003e\n\u003cp\u003eBut I needed to remove the passphrase to quickly deploy an ansible playbook 🤓\u003c/p\u003e\n",
      "content_text": "\nI got into a pickle where I encrypted the ssh keys I use for my SSH connections on LAN, but then I couldn't run my ansible playbook on my server! ssh-keygen -p and leave the new passphrase blank saved my day (although password protected key files are safer!)\n\n# TL;DR - just reset it to nothing\n\n`ssh-keygen -p` will let you reset the passphrase on your ssh keys (good for you! yay security!)\n\nBut I needed to remove the passphrase to quickly deploy an ansible playbook 🤓\n",
      "summary": "I got into a pickle where I encrypted the ssh keys I use for my SSH connections on LAN, but then I couldn't run my ansible playbook on my server! ssh-keygen...",
      "date_published": "2022-06-13T06:55:32Z",
      "date_modified": "2022-06-13T06:55:32Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "cli",
        "linux",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/add-colored-indicators-to-your-dataframes-html-representation/",
      "url": "https://pype.dev/add-colored-indicators-to-your-dataframes-html-representation/",
      "title": "Add colored indicators to your dataframes html representation",
      "content_html": "\u003cp\u003e\u003ca href=\"https://twitter.com/driscollis\"\u003eMike Driscoll\u003c/a\u003e recently tweeted about making\ncolored out with pandas DataFrames and I just had to try it for myself\u003c/p\u003e\n\u003ch1 id=\"use-case\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eUse Case\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eFirst though\u0026hellip; why?\nMy biggest use case is a monitoring pipeline of mine\u0026hellip; The details aside, the\noutput of my pipeline is a dataframe where each row has information about a\nfailed pipeline that I need to go look into. I dump that result to a simle html\nfile that\u0026rsquo;s hosted on an internal site and the file is updated every couple of\nhours. Adding some colored indicators automatically to the rows to help me\nassess severity of each record would be a handy way to quickly get an\nunderstanding the state of our pipelines.\u003c/p\u003e\n\u003ch1 id=\"how\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eHow?\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eThe docs for the \u003ccode\u003eapplymap\u003c/code\u003e method state simply:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eApply a CSS-styling function elementwise.\n\nUpdates the HTML representation with the result.\n\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eSo we can write a function that returns \u003ccode\u003ecolor: {color}\u003c/code\u003e based on the dataframe\nvalues and when we drop that dataframe to html we\u0026rsquo;ll have some simple css\nstyling applied automagically!\u003c/p\u003e\n\u003cp\u003eBy default the function will be applied to all columns of the dataframe, but\nthat\u0026rsquo;s not useful if the columns are different types which is usually the case.\nLuckily there is a \u003ccode\u003esubset\u003c/code\u003e keyword to only apply to the columns you need!\u003c/p\u003e\n\u003cp\u003eConsider my example\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003emain\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epd\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eread_csv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;cars.csv\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003emain\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003empg_color\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003efloat\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e...\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e     \u003cspan class=\"n\"\u003ecolor\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;red\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eval\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e21\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;green\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e...\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e     \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;color: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ecolor\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003emain\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estyle\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eapplymap\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003empg_color\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003esubset\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;mpg\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_html\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;color.html\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI want to quickly see if the \u003ccode\u003empg\u003c/code\u003e is any good for the cars in the cars dataset\nand I\u0026rsquo;ll define \u0026ldquo;good\u0026rdquo; as better than 21 mpg (not great I know but just for the\nsake of discussion\u0026hellip;)\u003c/p\u003e\n\u003cp\u003eThe function returns an appropriate css string and after I \u003ccode\u003estyle.applymap\u003c/code\u003e on just the \u003ccode\u003empg\u003c/code\u003e column we get this!\u003c/p\u003e\n\u003cstyle type=\"text/css\"\u003e\n#T_95e99_row0_col1, #T_95e99_row1_col1, #T_95e99_row2_col1, #T_95e99_row3_col1 {\n  color: green;\n}\n#T_95e99_row4_col1 {\n  color: red;\n}\n\u003c/style\u003e\n\u003ctable id=\"T_95e99\"\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth class=\"blank level0\" \u003e\u0026nbsp;\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col0\" class=\"col_heading level0 col0\" \u003eUnnamed: 0\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col1\" class=\"col_heading level0 col1\" \u003empg\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col2\" class=\"col_heading level0 col2\" \u003ecyl\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col3\" class=\"col_heading level0 col3\" \u003edisp\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col4\" class=\"col_heading level0 col4\" \u003ehp\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col5\" class=\"col_heading level0 col5\" \u003edrat\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col6\" class=\"col_heading level0 col6\" \u003ewt\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col7\" class=\"col_heading level0 col7\" \u003eqsec\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col8\" class=\"col_heading level0 col8\" \u003evs\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col9\" class=\"col_heading level0 col9\" \u003eam\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col10\" class=\"col_heading level0 col10\" \u003egear\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col11\" class=\"col_heading level0 col11\" \u003ecarb\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row0\" class=\"row_heading level0 row0\" \u003e0\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row0_col0\" class=\"data row0 col0\" \u003eMazda RX4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col1\" class=\"data row0 col1\" \u003e21.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col2\" class=\"data row0 col2\" \u003e6\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col3\" class=\"data row0 col3\" \u003e160.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col4\" class=\"data row0 col4\" \u003e110\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col5\" class=\"data row0 col5\" \u003e3.900000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col6\" class=\"data row0 col6\" \u003e2.620000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col7\" class=\"data row0 col7\" \u003e16.460000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col8\" class=\"data row0 col8\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col9\" class=\"data row0 col9\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col10\" class=\"data row0 col10\" \u003e4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col11\" class=\"data row0 col11\" \u003e4\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row1\" class=\"row_heading level0 row1\" \u003e1\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row1_col0\" class=\"data row1 col0\" \u003eMazda RX4 Wag\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col1\" class=\"data row1 col1\" \u003e21.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col2\" class=\"data row1 col2\" \u003e6\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col3\" class=\"data row1 col3\" \u003e160.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col4\" class=\"data row1 col4\" \u003e110\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col5\" class=\"data row1 col5\" \u003e3.900000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col6\" class=\"data row1 col6\" \u003e2.875000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col7\" class=\"data row1 col7\" \u003e17.020000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col8\" class=\"data row1 col8\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col9\" class=\"data row1 col9\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col10\" class=\"data row1 col10\" \u003e4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col11\" class=\"data row1 col11\" \u003e4\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row2\" class=\"row_heading level0 row2\" \u003e2\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row2_col0\" class=\"data row2 col0\" \u003eDatsun 710\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col1\" class=\"data row2 col1\" \u003e22.800000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col2\" class=\"data row2 col2\" \u003e4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col3\" class=\"data row2 col3\" \u003e108.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col4\" class=\"data row2 col4\" \u003e93\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col5\" class=\"data row2 col5\" \u003e3.850000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col6\" class=\"data row2 col6\" \u003e2.320000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col7\" class=\"data row2 col7\" \u003e18.610000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col8\" class=\"data row2 col8\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col9\" class=\"data row2 col9\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col10\" class=\"data row2 col10\" \u003e4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col11\" class=\"data row2 col11\" \u003e1\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row3\" class=\"row_heading level0 row3\" \u003e3\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row3_col0\" class=\"data row3 col0\" \u003eHornet 4 Drive\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col1\" class=\"data row3 col1\" \u003e21.400000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col2\" class=\"data row3 col2\" \u003e6\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col3\" class=\"data row3 col3\" \u003e258.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col4\" class=\"data row3 col4\" \u003e110\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col5\" class=\"data row3 col5\" \u003e3.080000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col6\" class=\"data row3 col6\" \u003e3.215000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col7\" class=\"data row3 col7\" \u003e19.440000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col8\" class=\"data row3 col8\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col9\" class=\"data row3 col9\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col10\" class=\"data row3 col10\" \u003e3\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col11\" class=\"data row3 col11\" \u003e1\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row4\" class=\"row_heading level0 row4\" \u003e4\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row4_col0\" class=\"data row4 col0\" \u003eHornet Sportabout\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col1\" class=\"data row4 col1\" \u003e18.700000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col2\" class=\"data row4 col2\" \u003e8\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col3\" class=\"data row4 col3\" \u003e360.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col4\" class=\"data row4 col4\" \u003e175\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col5\" class=\"data row4 col5\" \u003e3.150000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col6\" class=\"data row4 col6\" \u003e3.440000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col7\" class=\"data row4 col7\" \u003e17.020000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col8\" class=\"data row4 col8\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col9\" class=\"data row4 col9\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col10\" class=\"data row4 col10\" \u003e3\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col11\" class=\"data row4 col11\" \u003e2\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n",
      "content_text": "\n[Mike Driscoll](https://twitter.com/driscollis) recently tweeted about making\ncolored out with pandas DataFrames and I just had to try it for myself\n\n# Use Case\n\nFirst though... why?\nMy biggest use case is a monitoring pipeline of mine... The details aside, the\noutput of my pipeline is a dataframe where each row has information about a\nfailed pipeline that I need to go look into. I dump that result to a simle html\nfile that's hosted on an internal site and the file is updated every couple of\nhours. Adding some colored indicators automatically to the rows to help me\nassess severity of each record would be a handy way to quickly get an\nunderstanding the state of our pipelines.\n\n# How?\n\nThe docs for the `applymap` method state simply:\n\n```\nApply a CSS-styling function elementwise.\n\nUpdates the HTML representation with the result.\n\n```\n\nSo we can write a function that returns `color: {color}` based on the dataframe\nvalues and when we drop that dataframe to html we'll have some simple css\nstyling applied automagically!\n\nBy default the function will be applied to all columns of the dataframe, but\nthat's not useful if the columns are different types which is usually the case.\nLuckily there is a `subset` keyword to only apply to the columns you need!\n\nConsider my example\n\n```python \nsandbox   main via 3.8.11(sandbox) ipython\n❯ df = pd.read_csv(\"cars.csv\")\n\nsandbox   main via 3.8.11(sandbox) ipython\n❯ def mpg_color(val: float):\n...:     color = \"red\" if val \u003c 21 else \"green\"\n...:     return f\"color: {color}\"\n\nsandbox   main via 3.8.11(sandbox) ipython\n❯ df.style.applymap(mpg_color, subset=\"mpg\").to_html(\"color.html\")\n```\n\nI want to quickly see if the `mpg` is any good for the cars in the cars dataset\nand I'll define \"good\" as better than 21 mpg (not great I know but just for the\nsake of discussion...)\n\nThe function returns an appropriate css string and after I `style.applymap` on just the `mpg` column we get this!\n\n\n\u003cstyle type=\"text/css\"\u003e\n#T_95e99_row0_col1, #T_95e99_row1_col1, #T_95e99_row2_col1, #T_95e99_row3_col1 {\n  color: green;\n}\n#T_95e99_row4_col1 {\n  color: red;\n}\n\u003c/style\u003e\n\u003ctable id=\"T_95e99\"\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth class=\"blank level0\" \u003e\u0026nbsp;\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col0\" class=\"col_heading level0 col0\" \u003eUnnamed: 0\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col1\" class=\"col_heading level0 col1\" \u003empg\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col2\" class=\"col_heading level0 col2\" \u003ecyl\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col3\" class=\"col_heading level0 col3\" \u003edisp\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col4\" class=\"col_heading level0 col4\" \u003ehp\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col5\" class=\"col_heading level0 col5\" \u003edrat\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col6\" class=\"col_heading level0 col6\" \u003ewt\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col7\" class=\"col_heading level0 col7\" \u003eqsec\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col8\" class=\"col_heading level0 col8\" \u003evs\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col9\" class=\"col_heading level0 col9\" \u003eam\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col10\" class=\"col_heading level0 col10\" \u003egear\u003c/th\u003e\n      \u003cth id=\"T_95e99_level0_col11\" class=\"col_heading level0 col11\" \u003ecarb\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row0\" class=\"row_heading level0 row0\" \u003e0\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row0_col0\" class=\"data row0 col0\" \u003eMazda RX4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col1\" class=\"data row0 col1\" \u003e21.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col2\" class=\"data row0 col2\" \u003e6\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col3\" class=\"data row0 col3\" \u003e160.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col4\" class=\"data row0 col4\" \u003e110\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col5\" class=\"data row0 col5\" \u003e3.900000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col6\" class=\"data row0 col6\" \u003e2.620000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col7\" class=\"data row0 col7\" \u003e16.460000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col8\" class=\"data row0 col8\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col9\" class=\"data row0 col9\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col10\" class=\"data row0 col10\" \u003e4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row0_col11\" class=\"data row0 col11\" \u003e4\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row1\" class=\"row_heading level0 row1\" \u003e1\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row1_col0\" class=\"data row1 col0\" \u003eMazda RX4 Wag\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col1\" class=\"data row1 col1\" \u003e21.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col2\" class=\"data row1 col2\" \u003e6\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col3\" class=\"data row1 col3\" \u003e160.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col4\" class=\"data row1 col4\" \u003e110\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col5\" class=\"data row1 col5\" \u003e3.900000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col6\" class=\"data row1 col6\" \u003e2.875000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col7\" class=\"data row1 col7\" \u003e17.020000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col8\" class=\"data row1 col8\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col9\" class=\"data row1 col9\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col10\" class=\"data row1 col10\" \u003e4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row1_col11\" class=\"data row1 col11\" \u003e4\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row2\" class=\"row_heading level0 row2\" \u003e2\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row2_col0\" class=\"data row2 col0\" \u003eDatsun 710\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col1\" class=\"data row2 col1\" \u003e22.800000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col2\" class=\"data row2 col2\" \u003e4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col3\" class=\"data row2 col3\" \u003e108.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col4\" class=\"data row2 col4\" \u003e93\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col5\" class=\"data row2 col5\" \u003e3.850000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col6\" class=\"data row2 col6\" \u003e2.320000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col7\" class=\"data row2 col7\" \u003e18.610000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col8\" class=\"data row2 col8\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col9\" class=\"data row2 col9\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col10\" class=\"data row2 col10\" \u003e4\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row2_col11\" class=\"data row2 col11\" \u003e1\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row3\" class=\"row_heading level0 row3\" \u003e3\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row3_col0\" class=\"data row3 col0\" \u003eHornet 4 Drive\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col1\" class=\"data row3 col1\" \u003e21.400000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col2\" class=\"data row3 col2\" \u003e6\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col3\" class=\"data row3 col3\" \u003e258.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col4\" class=\"data row3 col4\" \u003e110\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col5\" class=\"data row3 col5\" \u003e3.080000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col6\" class=\"data row3 col6\" \u003e3.215000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col7\" class=\"data row3 col7\" \u003e19.440000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col8\" class=\"data row3 col8\" \u003e1\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col9\" class=\"data row3 col9\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col10\" class=\"data row3 col10\" \u003e3\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row3_col11\" class=\"data row3 col11\" \u003e1\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth id=\"T_95e99_level0_row4\" class=\"row_heading level0 row4\" \u003e4\u003c/th\u003e\n      \u003ctd id=\"T_95e99_row4_col0\" class=\"data row4 col0\" \u003eHornet Sportabout\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col1\" class=\"data row4 col1\" \u003e18.700000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col2\" class=\"data row4 col2\" \u003e8\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col3\" class=\"data row4 col3\" \u003e360.000000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col4\" class=\"data row4 col4\" \u003e175\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col5\" class=\"data row4 col5\" \u003e3.150000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col6\" class=\"data row4 col6\" \u003e3.440000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col7\" class=\"data row4 col7\" \u003e17.020000\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col8\" class=\"data row4 col8\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col9\" class=\"data row4 col9\" \u003e0\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col10\" class=\"data row4 col10\" \u003e3\u003c/td\u003e\n      \u003ctd id=\"T_95e99_row4_col11\" class=\"data row4 col11\" \u003e2\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\n",
      "summary": "Mike Driscoll recently tweeted about making colored out with pandas DataFrames and I just had to try it for myself",
      "date_published": "2022-06-04T06:12:33Z",
      "date_modified": "2022-06-04T06:12:33Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "data",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/samba-on-ubuntu-22-needs-inherit-permissions-set/",
      "url": "https://pype.dev/samba-on-ubuntu-22-needs-inherit-permissions-set/",
      "title": "Samba on Ubuntu 22 needs “inherit permissions” set",
      "content_html": "\u003cp\u003esee samba config on hogwarts\u003c/p\u003e\n",
      "content_text": "\nsee samba config on hogwarts\n",
      "summary": "see samba config on hogwarts",
      "date_published": "2022-05-31T06:30:42Z",
      "date_modified": "2022-05-31T06:30:42Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/filtering-emails-with-core-utils/",
      "url": "https://pype.dev/filtering-emails-with-core-utils/",
      "title": "Filtering emails with core utils",
      "content_html": "\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eemail1@me.com\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esomebody_else@gmail.com\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#! /bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# pick multiple emails from list and combine into comma seperated array\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eemails\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003ecat .../emails \u003cspan class=\"p\"\u003e|\u003c/span\u003e fzf -m \u003cspan class=\"p\"\u003e|\u003c/span\u003e sed \u003cspan class=\"s1\"\u003e\u0026#39;s/^\\|$/\u0026#34;/g\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003epaste -sd,\u003cspan class=\"sb\"\u003e`\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"nv\"\u003e$emails\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\n```text\nemail1@me.com\nsomebody_else@gmail.com\n\n```\n\n```bash\n#! /bin/bash\n# pick multiple emails from list and combine into comma seperated array\nemails=`cat .../emails | fzf -m | sed 's/^\\|$/\"/g'|paste -sd,` \n\necho $emails\n\n```\n",
      "date_published": "2022-05-30T06:09:27Z",
      "date_modified": "2022-05-30T06:09:27Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/reindex-nextcloud-after-adding-data-via-cli/",
      "url": "https://pype.dev/reindex-nextcloud-after-adding-data-via-cli/",
      "title": "Reindex Nextcloud After Adding Data via CLI",
      "content_html": "\u003ch1 id=\"my-nextcloud-woes\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eMy Nextcloud woes\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eI wrote \u003ca href=\"%22nextcloud-permissions-with-zfs-and-ansible-nas%22\"\u003ehere\u003c/a\u003e about setting\nup \u003ccode\u003ewww-data\u003c/code\u003e as the owner of any directories you want nextcloud to manage.\nHowever, I regularly struggle wtih permissions issues on my NAS because of the\nexternal storage app anyways so I\u0026rsquo;ve decided to just put our photos in the spot\nNextcloud would otherwise put them, and use this as healthy pressure on our\nfamily to organize our photos and put the ones we care about with the rest of\nour family media.\u003c/p\u003e\n\u003ch1 id=\"migration\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eMigration\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eBecause I had a ton of photos on the NAS anyways that I wanted moved over to\nNextcloud I just rsync\u0026rsquo;d the photos directory on my NAS to the user\u0026rsquo;s photos\ndirectory in nextcloud but they weren\u0026rsquo;t showing up in the web UI!\u003c/p\u003e\n\u003ch1 id=\"the-fix\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe Fix?\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eAs www-data I needed to \u003ccode\u003ephp /var/www/nextcloud/occ files:scan --all\u003c/code\u003e inside my\nnextcloud \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e container AFTER moving a bunch of photos off my \u0026ldquo;NAS\u0026rdquo; into the\nfolder mounted to the nextcloud container as its data folder! Before I did this\nthey weren\u0026rsquo;t showing up in the web UI/\u003c/p\u003e\n",
      "content_text": "\n# My Nextcloud woes\n\nI wrote [here](\"nextcloud-permissions-with-zfs-and-ansible-nas\") about setting\nup `www-data` as the owner of any directories you want nextcloud to manage.\nHowever, I regularly struggle wtih permissions issues on my NAS because of the\nexternal storage app anyways so I've decided to just put our photos in the spot\nNextcloud would otherwise put them, and use this as healthy pressure on our\nfamily to organize our photos and put the ones we care about with the rest of\nour family media.\n\n# Migration\n\nBecause I had a ton of photos on the NAS anyways that I wanted moved over to\nNextcloud I just rsync'd the photos directory on my NAS to the user's photos\ndirectory in nextcloud but they weren't showing up in the web UI!\n\n# The Fix?\n\nAs www-data I needed to `php /var/www/nextcloud/occ files:scan --all` inside my\nnextcloud docker container AFTER moving a bunch of photos off my \"NAS\" into the\nfolder mounted to the nextcloud container as its data folder! Before I did this\nthey weren't showing up in the web UI/\n",
      "summary": "I wrote here about setting up as the owner of any directories you want nextcloud to manage. However, I regularly struggle wtih permissions issues on my NAS...",
      "date_published": "2022-05-29T15:34:33Z",
      "date_modified": "2022-05-29T15:34:33Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/arr-download-client-config/",
      "url": "https://pype.dev/arr-download-client-config/",
      "title": "arr client config",
      "content_html": "\u003cp\u003eTIL that when setting up download clients for\nradarr/sonarr/lidarr/readarr/bazarr/prowlarr that you can utilize internal DNS\nand instead of hardcoding an IP address of your download client server, can use\njust the CNAME record (ie. instead of 172.10.14.13 I can use\ntransmission.mydomain.com\u0026hellip; notice the lact of http(s)://\u0026hellip; adding that won\u0026rsquo;t\nallow the connection to work/\u003c/p\u003e\n\u003cp\u003eFurthermore, you can use internal DNS to lookup the domain, not the subdomain,\nand expose the port, like \u003ccode\u003emydomain.com:7878\u003c/code\u003e for sonarr. This was simpler to\nmaintain because I don\u0026rsquo;t change which ports an application exposes or utilizes\nhardly ever, plus I don\u0026rsquo;t need to maintain CNAME records for every service!\u003c/p\u003e\n",
      "content_text": "\nTIL that when setting up download clients for\nradarr/sonarr/lidarr/readarr/bazarr/prowlarr that you can utilize internal DNS\nand instead of hardcoding an IP address of your download client server, can use\njust the CNAME record (ie. instead of 172.10.14.13 I can use\ntransmission.mydomain.com... notice the lact of http(s)://... adding that won't\nallow the connection to work/\n\nFurthermore, you can use internal DNS to lookup the domain, not the subdomain,\nand expose the port, like `mydomain.com:7878` for sonarr. This was simpler to\nmaintain because I don't change which ports an application exposes or utilizes\nhardly ever, plus I don't need to maintain CNAME records for every service!\n",
      "summary": "TIL that when setting up download clients for radarr/sonarr/lidarr/readarr/bazarr/prowlarr that you can utilize internal DNS and instead of hardcoding an IP...",
      "date_published": "2022-05-28T13:29:28Z",
      "date_modified": "2022-05-28T13:29:28Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/add-space-to-your-lvm-on-ubuntu/",
      "url": "https://pype.dev/add-space-to-your-lvm-on-ubuntu/",
      "title": "Add space to your LVM on Ubuntu",
      "content_html": "\u003cp\u003eI ran out of space on the SSD in my server when doing some file transfers but only 100GB was used of a 256 GB SSD?\u003c/p\u003e\n\u003ch1 id=\"lvm\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eLVM\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eWhen installing Ubuntu live server the default option for how to partition the\ndisk (in my experience) has been to setup an LVM group that defaults to less\nthan the available space. Most recently I put Ubuntu server on a 256 GB SSD but\nthe main partition was formatted as an LVM group with 100GB of storage\u0026hellip; I\ndidn\u0026rsquo;t think anything of this even though I\u0026rsquo;m mostly used to EXT4.\u003c/p\u003e\n\u003cp\u003eI think the reason for LVMs is performance, but in hindsight, I don\u0026rsquo;t really\ncare much about the performance differences, I really just want all my storage\nthat\u0026rsquo;s fast enough\u003c/p\u003e\n\u003ch1 id=\"extending-the-lvm\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eExtending the LVM\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eA moment of googling brought me to Ubuntu\u0026rsquo;s wiki and I\nlearned that I can expand my LVM to the space I need\u0026hellip;\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003esudo lvdisplay\u003c/code\u003e and \u003ccode\u003esudo pvdisplay\u003c/code\u003e show detailed views of the logical volumes and physical volumes respectively.\u003c/p\u003e\n\u003cp\u003eTake a look at those and find the volume you need to extend. For me I found this:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  --- Logical volume ---\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  LV Path                /dev/ubuntu-vg/ubuntu-lv\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  LV Name                ubuntu-lv\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  VG Name                ubuntu-vg\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  LV Write Access        read/write\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  LV Status              available\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThere\u0026rsquo;s more that you\u0026rsquo;ll see but this is what\u0026rsquo;s relevant - I need to extend the\n\u003ccode\u003eubuntu-lv\u003c/code\u003e logical volume in the \u003ccode\u003eubuntu-vg\u003c/code\u003e volume group.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003esudo lvextend -L +50g ubuntu-vg/ubuntu-lv\u003c/code\u003e gives me 50 more GB of storage which should be enough for at least tonight 🤓\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://wiki.ubuntu.com/Lvm\"\u003eRTFM\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nI ran out of space on the SSD in my server when doing some file transfers but only 100GB was used of a 256 GB SSD?\n\n# LVM\n\nWhen installing Ubuntu live server the default option for how to partition the\ndisk (in my experience) has been to setup an LVM group that defaults to less\nthan the available space. Most recently I put Ubuntu server on a 256 GB SSD but\nthe main partition was formatted as an LVM group with 100GB of storage... I\ndidn't think anything of this even though I'm mostly used to EXT4.\n\nI think the reason for LVMs is performance, but in hindsight, I don't really\ncare much about the performance differences, I really just want all my storage\nthat's fast enough\n\n# Extending the LVM\n\nA moment of googling brought me to Ubuntu's wiki and I\nlearned that I can expand my LVM to the space I need...\n\n`sudo lvdisplay` and `sudo pvdisplay` show detailed views of the logical volumes and physical volumes respectively.\n\nTake a look at those and find the volume you need to extend. For me I found this:\n\n```bash\n  --- Logical volume ---\n  LV Path                /dev/ubuntu-vg/ubuntu-lv\n  LV Name                ubuntu-lv\n  VG Name                ubuntu-vg\n  LV Write Access        read/write\n  LV Status              available\n  ...\n```\n\nThere's more that you'll see but this is what's relevant - I need to extend the\n`ubuntu-lv` logical volume in the `ubuntu-vg` volume group.\n\n`sudo lvextend -L +50g ubuntu-vg/ubuntu-lv` gives me 50 more GB of storage which should be enough for at least tonight 🤓\n\n[RTFM](https://wiki.ubuntu.com/Lvm)\n",
      "summary": "I ran out of space on the SSD in my server when doing some file transfers but only 100GB was used of a 256 GB SSD?",
      "date_published": "2022-05-26T21:33:55Z",
      "date_modified": "2022-05-26T21:33:55Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/tdarr-worker-nodes-share-the-cache/",
      "url": "https://pype.dev/tdarr-worker-nodes-share-the-cache/",
      "title": "Tdarr worker nodes share the cache!",
      "content_html": "\u003cp\u003eWhen working with tdarr remote nodes, they need to have access not only to the\nsame libraries but also the same transcode cache as the server otherwise the\ntranscodes will fail\u0026hellip;\u003c/p\u003e\n\u003ch1 id=\"network-setup\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eNetwork Setup\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eTo explain I\u0026rsquo;ll give a brief overview of my home setup\u003c/p\u003e\n\u003cp\u003eI have an old Dell PowerEdge R610 as my main server running a live server distribution of Ubuntu.\nI use ZFS for my NAS file system, and most of my datasets are accesible over my home network.\nI have a Tdarr server running in a \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e container on the R610.\u003c/p\u003e\n\u003cp\u003eIn my office I dailyi drive a gaming desktop with an Nvidia 2060 Super running Ubuntu as well.\nOn that desktop I am running a Tdarr node in a docker container.\nThe container has access to the network folders with my media.\u003c/p\u003e\n\u003ch1 id=\"initial-magic\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eInitial Magic\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eWhen I spun up  a tdarr node on my desktop, the tdarr server running on my R610 automatically registered the node, which was freaking amazing.\nThat magic though spoiled me and I thought that I didn\u0026rsquo;t need to read the rest of the docs\u0026hellip;\u003c/p\u003e\n\u003ch1 id=\"initial-fail\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eInitial Fail\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eI setup a transcode cache directory on the R610 locally and a separate transcode cache on my desktop that the remote tdarr node would use.\nHaving them separated led to 2 main issues:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eTranscodes were not being migrated back to my library properly\u003c/li\u003e\n\u003cli\u003eI was running out of disk space on my desktop because tdarr wasn\u0026rsquo;t deleting the completed cache files properly.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch1 id=\"the-fix\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe Fix\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eMake a transcode cache directory accessible on my network, and give access to that directory to the docker container running my remote tdarr node.\u003c/p\u003e\n",
      "content_text": "\nWhen working with tdarr remote nodes, they need to have access not only to the\nsame libraries but also the same transcode cache as the server otherwise the\ntranscodes will fail...\n\n# Network Setup\n\nTo explain I'll give a brief overview of my home setup  \n\nI have an old Dell PowerEdge R610 as my main server running a live server distribution of Ubuntu.\nI use ZFS for my NAS file system, and most of my datasets are accesible over my home network.\nI have a Tdarr server running in a docker container on the R610.\n\nIn my office I dailyi drive a gaming desktop with an Nvidia 2060 Super running Ubuntu as well.\nOn that desktop I am running a Tdarr node in a docker container. \nThe container has access to the network folders with my media. \n\n# Initial Magic \n\nWhen I spun up  a tdarr node on my desktop, the tdarr server running on my R610 automatically registered the node, which was freaking amazing.\nThat magic though spoiled me and I thought that I didn't need to read the rest of the docs...\n\n# Initial Fail \n\nI setup a transcode cache directory on the R610 locally and a separate transcode cache on my desktop that the remote tdarr node would use.\nHaving them separated led to 2 main issues:\n1. Transcodes were not being migrated back to my library properly\n2. I was running out of disk space on my desktop because tdarr wasn't deleting the completed cache files properly.\n\n# The Fix\n\nMake a transcode cache directory accessible on my network, and give access to that directory to the docker container running my remote tdarr node.\n\n\n\n",
      "summary": "When working with tdarr remote nodes, they need to have access not only to the same libraries but also the same transcode cache as the server otherwise the...",
      "date_published": "2022-05-25T06:36:59Z",
      "date_modified": "2022-05-25T06:36:59Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/local-dns-with-pi-hole/",
      "url": "https://pype.dev/local-dns-with-pi-hole/",
      "title": "Local DNS with Pi-hole",
      "content_html": "\u003ch2 id=\"spoilers\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eSpoilers\u003c/span\u003e \u003ca href=\"#spoilers\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTailscale is way easier than this\u0026hellip; I was doing this local DNS overwrite in\nPi-hole before running tailscale and I haven\u0026rsquo;t just totally \u0026ldquo;kicked the habit\u0026rdquo;\nyet, so for anyone NOT running tailcale, but wanting local HTTPS and using\nPi-Hole this method would work fine\u003c/p\u003e\n\u003ch2 id=\"intro\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eIntro\u003c/span\u003e \u003ca href=\"#intro\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI use \u003ca href=\"https://pi-hole.net/\"\u003epihole\u003c/a\u003e as my DNS server at home. I run unbound as\nwell and have a pretty standard setup from their docs.\u003c/p\u003e\n\u003cp\u003ePi-hole does 2 primary things for me:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003edns sink-hold, the primary use case I believe\u003c/li\u003e\n\u003cli\u003elocal SSL for all my self-hosted apps\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"caveat\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eCaveat\u003c/span\u003e \u003ca href=\"#caveat\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI know for sure there are better ways to do this, but there\u0026rsquo;s also worse\nones\u0026hellip; so for now this has been my pattern, and it\u0026rsquo;s only bitten me in the\nbutt when I\u0026rsquo;ve forgotten to add the final CNAME record\u0026hellip; which\u0026rsquo;ll make sense\nin a sec.\u003c/p\u003e\n\u003ch2 id=\"process\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eProcess\u003c/span\u003e \u003ca href=\"#process\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIt\u0026rsquo;s really quite simple - in pihole I have a DNS record for my domain pointing\nto my primary server\u003c/p\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250706114501_8c34a31e.png\" alt=\"20250706114501_8c34a31e.png\"\u003e\n\u003c/figure\u003e\n\u003cp\u003eThen for each service that I want to keep everything resolved locally for I add a CNAME\u003c/p\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250706114557_916fb4c4.png\" alt=\"20250706114557_916fb4c4.png\"\u003e\n\u003c/figure\u003e\n\u003cp\u003eWith DNS resolving this way for clients using my pihole, the networking all\nstays local and I still get HTTPS for my wildcard cert in cloudflare\u003c/p\u003e\n\u003ch2 id=\"why\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhy?\u003c/span\u003e \u003ca href=\"#why\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe reasons I do this are simple:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eI\u0026rsquo;ve been doing it since I started homelabbing and it started out of misunderstanding of how networking works \u003cem\u003eat all\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003eI use a home dashboard with https:// links, and it\u0026rsquo;s nice to just use that\nsame dashbaord publically or at home. With local DNS resolution then I can\nwhitelist some services, but conveniently access over the public url with https\nbut routing such that my whitelist let\u0026rsquo;s me in when it wouldn\u0026rsquo;t if I tried to\naccess the service from an external client\u003c/li\u003e\n\u003cli\u003eit\u0026rsquo;s my homelab - I can do what I want\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"it-s-too-complicated\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eIt’s too complicated\u003c/span\u003e \u003ca href=\"#it-s-too-complicated\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHonestly, as simple as this is, it is tedious and \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of complicated\u0026hellip;\nThere\u0026rsquo;s options to make it better\u0026hellip;\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003escript any service deployment to update the pihole /etc/host file\u003c/li\u003e\n\u003cli\u003ejust use tailscale\u0026hellip;\u003c/li\u003e\n\u003c/ol\u003e\n",
      "content_text": "\n## Spoilers\n\nTailscale is way easier than this... I was doing this local DNS overwrite in\nPi-hole before running tailscale and I haven't just totally \"kicked the habit\"\nyet, so for anyone NOT running tailcale, but wanting local HTTPS and using\nPi-Hole this method would work fine\n\n## Intro\n\nI use [pihole](https://pi-hole.net/) as my DNS server at home. I run unbound as\nwell and have a pretty standard setup from their docs.\n\nPi-hole does 2 primary things for me:\n\n1. dns sink-hold, the primary use case I believe\n2. local SSL for all my self-hosted apps\n\n## Caveat\n\nI know for sure there are better ways to do this, but there's also worse\nones... so for now this has been my pattern, and it's only bitten me in the\nbutt when I've forgotten to add the final CNAME record... which'll make sense\nin a sec.\n\n## Process\n\nIt's really quite simple - in pihole I have a DNS record for my domain pointing\nto my primary server\n\n![20250706114501_8c34a31e.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250706114501_8c34a31e.png)\n\nThen for each service that I want to keep everything resolved locally for I add a CNAME\n\n![20250706114557_916fb4c4.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250706114557_916fb4c4.png)\n\nWith DNS resolving this way for clients using my pihole, the networking all\nstays local and I still get HTTPS for my wildcard cert in cloudflare\n\n## Why?\n\nThe reasons I do this are simple:\n\n1. I've been doing it since I started homelabbing and it started out of misunderstanding of how networking works _at all_\n2. I use a home dashboard with https:// links, and it's nice to just use that\n   same dashbaord publically or at home. With local DNS resolution then I can\n   whitelist some services, but conveniently access over the public url with https\n   but routing such that my whitelist let's me in when it wouldn't if I tried to\n   access the service from an external client\n3. it's my homelab - I can do what I want\n\n## It's too complicated\n\nHonestly, as simple as this is, it is tedious and kind of complicated...\nThere's options to make it better...\n\n1. script any service deployment to update the pihole /etc/host file\n2. just use tailscale...\n",
      "summary": "Tailscale is way easier than this... I was doing this local DNS overwrite in Pi-hole before running tailscale and I haven't just totally \"kicked the habit\"...",
      "date_published": "2022-05-23T09:40:59Z",
      "date_modified": "2022-05-23T09:40:59Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/configure-bridge-network-on-ubuntu-22-04-with-netplan/",
      "url": "https://pype.dev/configure-bridge-network-on-ubuntu-22-04-with-netplan/",
      "title": "Configure bridge network on Ubuntu 22.04 with Netplan",
      "content_html": "\u003cp\u003eSee 02-\u0026hellip;.yaml in ansible-nas\u003c/p\u003e\n",
      "content_text": "\nSee 02-....yaml in ansible-nas\n",
      "summary": "See 02-....yaml in ansible-nas",
      "date_published": "2022-05-22T14:55:28Z",
      "date_modified": "2022-05-22T14:55:28Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/netplan-change-from-focal-to-jammy/",
      "url": "https://pype.dev/netplan-change-from-focal-to-jammy/",
      "title": "Netplan change from Focal to Jammy",
      "content_html": "\u003cp\u003eI am revamping my home server and bumped myself early up to Jammy Jellyfish\u0026hellip;\nhowever to my peril I reused my netplan config and after hitting my server with\nthe \u0026lsquo;ol \u003ccode\u003enetplan apply\u003c/code\u003e I lost connection\u0026hellip;\nDNS still seemed to kinda work externally, but internally nothing was up\u0026hellip;\u003c/p\u003e\n\u003cp\u003eTurns out Netplan got a little change in how to express the \u003ccode\u003egateway\u003c/code\u003e key in the netplan config!\u003c/p\u003e\n\u003cp\u003eOld Ubuntu 20.04 way\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003enetwork\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eversion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eethernets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenp0s4\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eaddresses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1\u003c/span\u003e\u003cspan class=\"l\"\u003e.{Static IP}/24]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003egateway4\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1.1\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# \u0026lt;-- This changes!\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003enameservers\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eaddresses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1.1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e1.1.1.1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNew jammin way for Jammy Jellyfish (\u003cstrong\u003eat least that worked for me\u003c/strong\u003e)\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003enetwork\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eversion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eethernets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenp0s4\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eaddresses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1\u003c/span\u003e\u003cspan class=\"l\"\u003e.{Static IP}/24]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eroutes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"nt\"\u003eto\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edefault\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003evia\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1.1\u003c/span\u003e\u003cspan class=\"w\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003enameservers\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eaddresses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1.1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e1.1.1.1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nI am revamping my home server and bumped myself early up to Jammy Jellyfish...\nhowever to my peril I reused my netplan config and after hitting my server with\nthe 'ol `netplan apply` I lost connection...\nDNS still seemed to kinda work externally, but internally nothing was up... \n\nTurns out Netplan got a little change in how to express the `gateway` key in the netplan config!\n\nOld Ubuntu 20.04 way\n\n```yaml\nnetwork:\n  version: 2\n  ethernets:\n    enp0s4:\n      addresses: [192.168.1.{Static IP}/24]\n      gateway4: 192.168.1.1  # \u003c-- This changes!\n      nameservers:\n        addresses: [192.168.1.1, 1.1.1.1]\n```\n\nNew jammin way for Jammy Jellyfish (__at least that worked for me__)\n```yaml\nnetwork:\n  version: 2\n  ethernets:\n    enp0s4:\n      addresses: [192.168.1.{Static IP}/24]\n      routes:\n        - to: default\n          via: 192.168.1.1 \n      nameservers:\n        addresses: [192.168.1.1, 1.1.1.1]\n```\n",
      "summary": "I am revamping my home server and bumped myself early up to Jammy Jellyfish... however to my peril I reused my netplan config and after hitting my server...",
      "date_published": "2022-05-22T13:23:17Z",
      "date_modified": "2022-05-22T13:23:17Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/subset-a-list-based-on-values-in-another-list-with-itertools-compress/",
      "url": "https://pype.dev/subset-a-list-based-on-values-in-another-list-with-itertools-compress/",
      "title": "Subset a list based on values in another list with itertools.compress",
      "content_html": "\u003cp\u003eI have list [True, False, False, True] and another list [1, 2, 3, 4] and a use\ncase where I want to filter list 2 based on list 1 to remove values that line\nup with the element False in list 1\u0026hellip;. so the outcome will be [1, 4].\nlist(compress(list2, list1)) will do it. As long as you can create a mask for\nthe filter than itertool.compress will be your friend!\u003c/p\u003e\n",
      "content_text": "\nI have list [True, False, False, True] and another list [1, 2, 3, 4] and a use\ncase where I want to filter list 2 based on list 1 to remove values that line\nup with the element False in list 1.... so the outcome will be [1, 4].\nlist(compress(list2, list1)) will do it. As long as you can create a mask for\nthe filter than itertool.compress will be your friend!\n",
      "summary": "I have list [True, False, False, True] and another list [1, 2, 3, 4] and a use case where I want to filter list 2 based on list 1 to remove values that line...",
      "date_published": "2022-05-19T14:22:58Z",
      "date_modified": "2022-05-19T14:22:58Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/nextcloud-permissions-with-zfs-and-ansible-nas/",
      "url": "https://pype.dev/nextcloud-permissions-with-zfs-and-ansible-nas/",
      "title": "Nextcloud permissions with ZFS and Ansible-NAS",
      "content_html": "\u003ch1 id=\"tl-dr\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTL;DR\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eAs the nextcloud docs say\u0026hellip; if you want to write to an external volume that\nlocation has to be writeable by the user/group \u003ccode\u003ewww-data\u003c/code\u003e on the host system\u0026hellip;\nso if that makes sense to you then this TIL probably isn\u0026rsquo;t a ton of value.. if\nnot however, read on :)\u003c/p\u003e\n\u003ch1 id=\"case-study\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eCase Study\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eYou want to self-host your own cloud and use a smart file system for convenience\u0026hellip;\nNextcloud and ZFS are pretty common goto answers for each of those problems.\u003c/p\u003e\n\u003cp\u003eMy home NAS is built on ZFS and among other things I have a \u003ccode\u003ezpool\u003c/code\u003e named\n\u003ccode\u003etank\u003c/code\u003e and nested in there is a \u003ccode\u003etank/nas\u003c/code\u003e dataset with several child zfs\ndatasets under that.\u003c/p\u003e\n\u003cp\u003eI want to use nextcloud mainly for auto-uploading photos from my wife\u0026rsquo;s and my phones for automatic backups.\nThe issue is that the nextcloud application (I run in \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003eDocker\u003c/a\u003e) is fixed as the\n\u003ccode\u003ewww-data\u003c/code\u003e user and so any volume/folder that you want nextcloud to write to\nneeds to be permissioned such that \u003ccode\u003ewww-data\u003c/code\u003e owns it\u0026hellip; but I don\u0026rsquo;t want\n\u003ccode\u003ewww-data\u003c/code\u003e to own everything in my NAS\u0026hellip; so what\u0026rsquo;s a girl to do?\u003c/p\u003e\n\u003ch1 id=\"solution\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eSolution\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eWell, one way to go is to just utilize docker volumes, write the data in the\ncontainer to \u003ccode\u003e/var/www/html\u003c/code\u003e and let that be the place your data backsup to.\u003c/p\u003e\n\u003cp\u003eI still wanted nextcloud to automatically write right to my NAS so I created a\n\u003ccode\u003enextcloud-upload\u003c/code\u003e directory inside of \u003ccode\u003etank/nas/media/photos\u003c/code\u003e (photos cause\nthat\u0026rsquo;s all that gets automatically uploaded)\u003c/p\u003e\n\u003cp\u003eThen I \u003ccode\u003echown -R www-data:www-data /tank/nas/media/photos/nextcloud-upload\u003c/code\u003e so\nthat just that sub-folder is owned by \u003ccode\u003ewww-data\u003c/code\u003e. Now everyone\u0026rsquo;s happy!\u003c/p\u003e\n",
      "content_text": "\n# TL;DR\n\nAs the nextcloud docs say... if you want to write to an external volume that\nlocation has to be writeable by the user/group `www-data` on the host system...\nso if that makes sense to you then this TIL probably isn't a ton of value.. if\nnot however, read on :)\n\n\n\n# Case Study\n\nYou want to self-host your own cloud and use a smart file system for convenience...\nNextcloud and ZFS are pretty common goto answers for each of those problems.\n\nMy home NAS is built on ZFS and among other things I have a `zpool` named\n`tank` and nested in there is a `tank/nas` dataset with several child zfs\ndatasets under that.\n\nI want to use nextcloud mainly for auto-uploading photos from my wife's and my phones for automatic backups.\nThe issue is that the nextcloud application (I run in Docker) is fixed as the\n`www-data` user and so any volume/folder that you want nextcloud to write to\nneeds to be permissioned such that `www-data` owns it... but I don't want\n`www-data` to own everything in my NAS... so what's a girl to do?\n\n# Solution\n\nWell, one way to go is to just utilize docker volumes, write the data in the\ncontainer to `/var/www/html` and let that be the place your data backsup to.\n\nI still wanted nextcloud to automatically write right to my NAS so I created a\n`nextcloud-upload` directory inside of `tank/nas/media/photos` (photos cause\nthat's all that gets automatically uploaded)\n\nThen I `chown -R www-data:www-data /tank/nas/media/photos/nextcloud-upload` so\nthat just that sub-folder is owned by `www-data`. Now everyone's happy!\n\n\n\n",
      "summary": "As the nextcloud docs say... if you want to write to an external volume that location has to be writeable by the user/group on the host system... so if that...",
      "date_published": "2022-05-19T13:55:20Z",
      "date_modified": "2022-05-19T13:55:20Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "zfs",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/see-git-history-about-one-file/",
      "url": "https://pype.dev/see-git-history-about-one-file/",
      "title": "See git history about one file",
      "content_html": "\u003cp\u003eIn vim \u003ccode\u003eG clog %\u003c/code\u003e does a \u003ccode\u003egit clog {current file}\u003c/code\u003e. You get every commit that the target file is apart of (so there might be info in those commits unrelated)\u003c/p\u003e\n",
      "content_text": "\nIn vim `G clog %` does a `git clog {current file}`. You get every commit that the target file is apart of (so there might be info in those commits unrelated)\n",
      "summary": "In vim does a . You get every commit that the target file is apart of (so there might be info in those commits unrelated)",
      "date_published": "2022-05-19T06:43:32Z",
      "date_modified": "2022-05-19T06:43:32Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "cli",
        "vim",
        "git",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/see-zfs-snapshot-disk-usage/",
      "url": "https://pype.dev/see-zfs-snapshot-disk-usage/",
      "title": "See ZFS snapshot disk usage",
      "content_html": "\u003cp\u003eAs I was cleaning up my NAS recently I noticed that I ran out of storage even\nthough my disk usage looked pretty low\u0026hellip; turns out I was keeping a mega-ton of\nZFS snapshots and due to my own ignorance at the time didn\u0026rsquo;t realize the\nstorage cost of this!\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ezfs list-o space tank/home\u003c/code\u003e will show the disk usage of the dataset and snapshots in \u003ccode\u003etank/home\u003c/code\u003e\u003c/p\u003e\n",
      "content_text": "\nAs I was cleaning up my NAS recently I noticed that I ran out of storage even\nthough my disk usage looked pretty low... turns out I was keeping a mega-ton of\nZFS snapshots and due to my own ignorance at the time didn't realize the\nstorage cost of this!\n\n`zfs list-o space tank/home` will show the disk usage of the dataset and snapshots in `tank/home`\n",
      "summary": "As I was cleaning up my NAS recently I noticed that I ran out of storage even though my disk usage looked pretty low... turns out I was keeping a mega-ton of...",
      "date_published": "2022-05-19T06:05:23Z",
      "date_modified": "2022-05-19T06:05:23Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "zfs",
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/deleting-files-on-remote-storage-from-ubuntu-might-not-do-what-you-think/",
      "url": "https://pype.dev/deleting-files-on-remote-storage-from-ubuntu-might-not-do-what-you-think/",
      "title": "Deleting files on remote storage from Ubuntu might not do what you think",
      "content_html": "\u003cp\u003eFrom my daily driver Ubuntu machine I often open nautilus, dolphin, etc. and delete a file here or there on my NAS\u0026hellip; turns out Ubuntu sends thse file to \u003ccode\u003e.Trash-100\u003c/code\u003e ON THE NAS so I\u0026rsquo;m effectively just moving that file and not freeing up any space\u0026hellip;\u003c/p\u003e\n",
      "content_text": "\nFrom my daily driver Ubuntu machine I often open nautilus, dolphin, etc. and delete a file here or there on my NAS... turns out Ubuntu sends thse file to `.Trash-100` ON THE NAS so I'm effectively just moving that file and not freeing up any space...\n",
      "summary": "From my daily driver Ubuntu machine I often open nautilus, dolphin, etc. and delete a file here or there on my NAS... turns out Ubuntu sends thse file to ON...",
      "date_published": "2022-05-19T05:51:50Z",
      "date_modified": "2022-05-19T05:51:50Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "cli",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/remove-zfs-dataset-specific-snapshots/",
      "url": "https://pype.dev/remove-zfs-dataset-specific-snapshots/",
      "title": "Remove ZFS Dataset Specific Snapshots",
      "content_html": "\u003cp\u003eI started my homelab journey being super naive about ZFS and how to manage the\nfilesystem\u0026hellip; that bit me in the butt when transfering a ton of files out of\nfolders and into datasets because ZFS is copy on write so I was essentially\nduplicating my storage until I got a hair smarter about removing files after\nthey\u0026rsquo;re moved (rsync \u0026ndash;remove-source-file ftw). But I had a ton of snapshots of\nchild datasets with a ton of data that I just never will need, so I learned\n\u003ccode\u003ezfs list -H -o name -t snapshot tank/dataset1/dataset2\u003c/code\u003e will list just the\nsnapshots for dataset2 and if you pipe that into \u003ccode\u003exargs -n1 zfs destroy\u003c/code\u003e then\nyou have a way to clear out some snapshots you don\u0026rsquo;t need!\u003c/p\u003e\n",
      "content_text": "\nI started my homelab journey being super naive about ZFS and how to manage the\nfilesystem... that bit me in the butt when transfering a ton of files out of\nfolders and into datasets because ZFS is copy on write so I was essentially\nduplicating my storage until I got a hair smarter about removing files after\nthey're moved (rsync --remove-source-file ftw). But I had a ton of snapshots of\nchild datasets with a ton of data that I just never will need, so I learned\n`zfs list -H -o name -t snapshot tank/dataset1/dataset2` will list just the\nsnapshots for dataset2 and if you pipe that into `xargs -n1 zfs destroy` then\nyou have a way to clear out some snapshots you don't need!\n",
      "summary": "I started my homelab journey being super naive about ZFS and how to manage the filesystem... that bit me in the butt when transfering a ton of files out of...",
      "date_published": "2022-05-19T05:49:16Z",
      "date_modified": "2022-05-19T05:49:16Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "zfs",
        "cli",
        "bash",
        "homelab",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/plug-snapshot/",
      "url": "https://pype.dev/plug-snapshot/",
      "title": "Plug Snapshot!",
      "content_html": "\u003cp\u003e:PlugSnapshot, :w ~/dotfiles/nvim/snapshot.vim\u0026hellip; keep your config safe with git!\u003c/p\u003e\n",
      "content_text": "\n:PlugSnapshot, :w ~/dotfiles/nvim/snapshot.vim... keep your config safe with git!\n",
      "summary": ":PlugSnapshot, :w ~/dotfiles/nvim/snapshot.vim... keep your config safe with git!",
      "date_published": "2022-05-17T15:45:49Z",
      "date_modified": "2022-05-17T15:45:49Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vim",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/filepath-completion-in-neovim/",
      "url": "https://pype.dev/filepath-completion-in-neovim/",
      "title": "Filepath Completion in Neovim",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve had \u003ccode\u003ePlug 'hrsh7th/cmp-path'\u003c/code\u003e in my plugins for ever but didn\u0026rsquo;t notice\nuntil recently that I wasn\u0026rsquo;t getting any filepath completion in vim!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFuller setup instructions below the TLDR\u003c/strong\u003e\u003c/p\u003e\n\u003ch1 id=\"tl-dr\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTL;DR\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eTurns out I need to not be a dope and configure nvim-cmp to actually use it\u0026hellip;\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erequire\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;cmp\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecmp.setup\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e-- removed rest of setup - see the rest in my dotfiles\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003esources\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.config\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esources\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;path\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e  \u003cspan class=\"c1\"\u003e-- This needs to be here!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"my-setup\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eMy Setup\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eFor the sake of completeness here is how I currently (May 2022) configure completion in Neovim usin \u003ccode\u003envim-cmp\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"plugins\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003ePlugins\u003c/span\u003e \u003ca href=\"#plugins\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI keep all my plugins in \u003ccode\u003eplugins.vim\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ecall\u003c/span\u003e \u003cspan class=\"nx\"\u003eplug\u003c/span\u003e#\u003cspan class=\"nx\"\u003ebegin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003es\u003c/span\u003e:\u003cspan class=\"nx\"\u003eplug_dir\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ePlug\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;neovim/nvim-lspconfig\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ePlug\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;hrsh7th/cmp-nvim-lsp\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ePlug\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;hrsh7th/cmp-buffer\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ePlug\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;hrsh7th/cmp-path\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ePlug\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;hrsh7th/cmp-cmdline\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ePlug\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;hrsh7th/nvim-cmp\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e\u0026#34; For ultisnips users.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;!--\u003c/span\u003e \u003cspan class=\"c\"\u003e\u0026#34; Plug \u0026#39;SirVer/ultisnips\u0026#39; --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;!--\u003c/span\u003e \u003cspan class=\"c\"\u003e\u0026#34; Plug \u0026#39;quangnguyen30192/cmp-nvim-ultisnips\u0026#39; --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ecall\u003c/span\u003e \u003cspan class=\"nx\"\u003eplug\u003c/span\u003e#\u003cspan class=\"nx\"\u003eend\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"vim-settings\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eVim Settings\u003c/span\u003e \u003ca href=\"#vim-settings\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eMy vim settings are also kept in their own file, \u003ccode\u003esettings.vim\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eset\u003c/span\u003e \u003cspan class=\"nx\"\u003ecompleteopt\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"nx\"\u003emenu\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nx\"\u003emenuone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nx\"\u003enoselect\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"nvim-cmp-configuration\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003envim-cmp configuration\u003c/span\u003e \u003ca href=\"#nvim-cmp-configuration\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI have a \u003ccode\u003ecmp.lua\u003c/code\u003e file that gets sourced in \u003ccode\u003einit.lua\u003c/code\u003e (file structure explained below) for configuring cmp.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e-- Setup nvim-cmp.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erequire\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;cmp\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecmp.setup\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003esnippet\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e-- REQUIRED - you must specify a snippet engine\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eexpand\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"c1\"\u003e-- For `ultisnips` user.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003evim.fn\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;UltiSnips#Anon\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e](\u003c/span\u003e\u003cspan class=\"n\"\u003eargs.body\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ewindow\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003ecompletion\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.config\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewindow.bordered\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003emapping\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;Down\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselect_next_item\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"n\"\u003ebehavior\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.SelectBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e \u003cspan class=\"p\"\u003e}),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;Up\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselect_prev_item\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"n\"\u003ebehavior\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.SelectBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSelect\u003c/span\u003e \u003cspan class=\"p\"\u003e}),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;C-d\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003escroll_docs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;C-f\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003escroll_docs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;C-Space\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecomplete\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;C-e\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;Tab\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselect_next_item\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;i\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;s\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e}),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;CR\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.mapping\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econfirm\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003ebehavior\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.ConfirmBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReplace\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003eselect\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003esources\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecmp.config\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esources\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;nvim_lsp\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;ultisnips\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;buffer\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;path\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;tmux\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe \u003ccode\u003esources\u003c/code\u003e section is what was key for this post\u0026hellip;\u003c/p\u003e\n\u003ch1 id=\"piecing-it-together\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003ePiecing it together!\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eMy \u003ccode\u003einit.vim\u003c/code\u003e sources plugins and then settings and then finally calls \u003ccode\u003einit.lua\u003c/code\u003e.\n\u003ccode\u003einit.lua\u003c/code\u003e sources my \u003ccode\u003ecmp.lua\u003c/code\u003e file and BANG! auto-completion.\u003c/p\u003e\n\u003ch2 id=\"more-sources\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eMore sources\u003c/span\u003e \u003ca href=\"#more-sources\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003ehrsh7th\u0026rsquo;s wiki for \u003ccode\u003envim-cmp\u003c/code\u003e is \u003ca href=\"https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources\"\u003ehere\u003c/a\u003e and has example configs as well as a list of sources\u0026hellip;\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDon\u0026rsquo;t forget to configure and not just install!\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/nicpayne713/dotfiles\"\u003emy dotfiles\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\n\nI've had `Plug 'hrsh7th/cmp-path'` in my plugins for ever but didn't notice\nuntil recently that I wasn't getting any filepath completion in vim!\n\n__Fuller setup instructions below the TLDR__\n\n# TL;DR\n\nTurns out I need to not be a dope and configure nvim-cmp to actually use it...\n\n\n```lua\nlocal cmp = require'cmp'\n\ncmp.setup({\n    -- removed rest of setup - see the rest in my dotfiles\n  sources = cmp.config.sources({\n    { name = 'path' },  -- This needs to be here!\n    })\n})\n```\n\n# My Setup\n\nFor the sake of completeness here is how I currently (May 2022) configure completion in Neovim usin `nvim-cmp`\n\n## Plugins\n\nI keep all my plugins in `plugins.vim`\n\n```vim\ncall plug#begin(s:plug_dir)\nPlug 'neovim/nvim-lspconfig'\nPlug 'hrsh7th/cmp-nvim-lsp'\nPlug 'hrsh7th/cmp-buffer'\nPlug 'hrsh7th/cmp-path'\nPlug 'hrsh7th/cmp-cmdline'\nPlug 'hrsh7th/nvim-cmp'\n\n\" For ultisnips users.\n\u003c!-- \" Plug 'SirVer/ultisnips' --\u003e\n\u003c!-- \" Plug 'quangnguyen30192/cmp-nvim-ultisnips' --\u003e\n\ncall plug#end()\n\n```\n\n## Vim Settings\n\nMy vim settings are also kept in their own file, `settings.vim`\n\n```vim\n\nset completeopt=menu,menuone,noselect\n\n```\n\n## nvim-cmp configuration\n\nI have a `cmp.lua` file that gets sourced in `init.lua` (file structure explained below) for configuring cmp.\n\n```lua\n\n  -- Setup nvim-cmp.\nlocal cmp = require'cmp'\n\ncmp.setup({\n  snippet = {\n    -- REQUIRED - you must specify a snippet engine\n    expand = function(args)\n      -- For `ultisnips` user.\n      vim.fn[\"UltiSnips#Anon\"](args.body)\n    end,\n  },\n  window = {\n      completion = cmp.config.window.bordered(),\n  },\n  mapping = {\n    ['\u003cDown\u003e'] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }),\n    ['\u003cUp\u003e'] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }),\n    ['\u003cC-d\u003e'] = cmp.mapping.scroll_docs(-4),\n    ['\u003cC-f\u003e'] = cmp.mapping.scroll_docs(4),\n    ['\u003cC-Space\u003e'] = cmp.mapping.complete(),\n    ['\u003cC-e\u003e'] = cmp.mapping.close(),\n    ['\u003cTab\u003e'] = cmp.mapping(cmp.mapping.select_next_item(), { 'i', 's' }),\n    ['\u003cCR\u003e'] = cmp.mapping.confirm({\n      behavior = cmp.ConfirmBehavior.Replace,\n      select = true,\n    })\n  },\n  sources = cmp.config.sources({\n    { name = 'nvim_lsp' },\n    { name = 'ultisnips' },\n    { name = 'buffer' },\n    { name = 'path' },\n    { name = 'tmux' },\n    })\n})\n\n```\n\n\nThe `sources` section is what was key for this post...\n\n# Piecing it together!\n\nMy `init.vim` sources plugins and then settings and then finally calls `init.lua`.\n`init.lua` sources my `cmp.lua` file and BANG! auto-completion.\n\n## More sources\n\nhrsh7th's wiki for `nvim-cmp` is [here](https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources) and has example configs as well as a list of sources...\n\n__Don't forget to configure and not just install!__\n\n[my dotfiles](https://github.com/nicpayne713/dotfiles)\n",
      "summary": "I've had in my plugins for ever but didn't notice until recently that I wasn't getting any filepath completion in vim!",
      "date_published": "2022-05-17T14:03:27Z",
      "date_modified": "2022-05-17T14:03:27Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vim",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/fastapi-jinja-forms/",
      "url": "https://pype.dev/fastapi-jinja-forms/",
      "title": "Forms with FastAPI and Jinja",
      "content_html": "\u003cp\u003eI just started using FastAPI for a home project and needed to pass back a\ndynamic number of values from a form rendered with jinja\u0026hellip;\u003c/p\u003e\n\u003ch1 id=\"dynamic-values\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eDynamic Values\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eThe jinja templating for rendering HTML based on something like a python iterable is nice and easy\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003edata is the result of a database query, and item is each row, so the dot notation is the value of each column basically in that row\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e\u0026lt;form method=\u0026#34;post\u0026#34;\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e  \u003c/span\u003e\u003cspan class=\"cp\"\u003e{%\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"nv\"\u003eitem\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"nv\"\u003edata\u003c/span\u003e \u003cspan class=\"cp\"\u003e%}\u003c/span\u003e\u003cspan class=\"x\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e    \u0026lt;div class=\u0026#34;form-check \u0026#34;\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e        \u0026lt;input class=\u0026#34;form-check-input\u0026#34;  name=\u0026#34;item_\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e \u003cspan class=\"nv\"\u003eitem.id\u003c/span\u003e \u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"x\"\u003e\u0026#34; id=\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e \u003cspan class=\"nv\"\u003eitem.name\u003c/span\u003e \u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"x\"\u003e\u0026#34; value=\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e \u003cspan class=\"nv\"\u003eitem.id\u003c/span\u003e \u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"x\"\u003e\u0026#34; type=\u0026#34;checkbox\u0026#34;\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e        \u0026lt;label class=\u0026#34;form-check-label\u0026#34; for=\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e \u003cspan class=\"nv\"\u003eitem.id\u003c/span\u003e \u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"x\"\u003e\u0026#34; \u0026gt; Label for: \u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e \u003cspan class=\"nv\"\u003eitem.name\u003c/span\u003e \u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"x\"\u003e \u0026lt;/label\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e    \u0026lt;/div\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e  \u003c/span\u003e\u003cspan class=\"cp\"\u003e{%\u003c/span\u003e \u003cspan class=\"k\"\u003eendfor\u003c/span\u003e \u003cspan class=\"cp\"\u003e%}\u003c/span\u003e\u003cspan class=\"x\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e\u0026lt;button type=\u0026#34;submit\u0026#34; class=\u0026#34;submit btn btn-xl btn-outline-danger\u0026#34; \u0026gt;Remove\u0026lt;/button\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e\u0026lt;/form\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"x\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis form generates a row with a checkbox for every \u003ccode\u003eitem\u003c/code\u003e in \u003ccode\u003edata\u003c/code\u003e (in my\ncase each \u003ccode\u003eitem\u003c/code\u003e is an existing row in my table). it?\u003c/p\u003e\n\u003cp\u003eThe way to pass back all those values is pretty straight forward (after hours of messing around that is!)\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# I hate it when tutorials don\u0026#39;t show ALL relevant pieces to the blurb\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003estarlette.status\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"nn\"\u003estatus\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003efastapi\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eAPIRouter\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eDepends\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eForm\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eRequest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003efastapi.encoders\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003ejsonable_encoder\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003efastapi.responses\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eHTMLResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eRedirectResponse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003efastapi.templating\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eJinja2Templates\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003esqlalchemy.orm\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eSession\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003eapp.session.session\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003ecreate_get_session\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003erouter\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eAPIRouter\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003etemplates\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eJinja2Templates\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edirectory\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;templates/\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@router.post\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/my_route/do_something_with_form\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse_class\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eHTMLResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003edelete_rows\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edb\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eSession\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDepends\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecreate_get_session\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eform_data\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_form\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ejsonable_encoder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eform_data\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# data = {\u0026#34;item_1\u0026#34;: 1, \u0026#34;item_2\u0026#34;: 2, ... \u0026#34;item_N\u0026#34;: N}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eRedirectResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003estatus_code\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003estatus\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHTTP_302_FOUND\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe \u003ccode\u003eawait request.get_form()\u003c/code\u003e and after encoding the data we get a dictionary with key/value pairs of the name/value from the form!\u003c/p\u003e\n\u003cp\u003eThis took me quite a long time to figure out in part because most of the Google-able resources are still on Flask\u0026hellip;\u003c/p\u003e\n",
      "content_text": "\nI just started using FastAPI for a home project and needed to pass back a\ndynamic number of values from a form rendered with jinja...\n\n\n# Dynamic Values \n\nThe jinja templating for rendering HTML based on something like a python iterable is nice and easy\n\n\u003e data is the result of a database query, and item is each row, so the dot notation is the value of each column basically in that row\n\n```jinja\n\u003cform method=\"post\"\u003e\n  {% for item in data %}\n    \u003cdiv class=\"form-check \"\u003e\n        \u003cinput class=\"form-check-input\"  name=\"item_{{ item.id }}\" id=\"{{ item.name }}\" value=\"{{ item.id }}\" type=\"checkbox\"\u003e\n        \u003clabel class=\"form-check-label\" for=\"{{ item.id }}\" \u003e Label for: {{ item.name }} \u003c/label\u003e\n    \u003c/div\u003e\n  {% endfor %}\n\n\u003cbutton type=\"submit\" class=\"submit btn btn-xl btn-outline-danger\" \u003eRemove\u003c/button\u003e\n\u003c/form\u003e\n\n```\n\nThis form generates a row with a checkbox for every `item` in `data` (in my\ncase each `item` is an existing row in my table). it?\n\nThe way to pass back all those values is pretty straight forward (after hours of messing around that is!)\n\n```python\n# I hate it when tutorials don't show ALL relevant pieces to the blurb\nimport starlette.status as status\nfrom fastapi import APIRouter, Depends, Form, Request\nfrom fastapi.encoders import jsonable_encoder\nfrom fastapi.responses import HTMLResponse, RedirectResponse\nfrom fastapi.templating import Jinja2Templates\nfrom sqlalchemy.orm import Session\n\nfrom app.session.session import create_get_session\n\nrouter = APIRouter()\ntemplates = Jinja2Templates(directory=\"templates/\")\n\n@router.post(\"/my_route/do_something_with_form\", response_class=HTMLResponse)\nasync def delete_rows(\n    request: Request,\n    db: Session = Depends(create_get_session),\n):\n    form_data = await request.get_form()\n    data = jsonable_encoder(form_data)\n    # data = {\"item_1\": 1, \"item_2\": 2, ... \"item_N\": N}\n    return RedirectResponse(\"/\", status_code=status.HTTP_302_FOUND)\n```\n\nWe `await request.get_form()` and after encoding the data we get a dictionary with key/value pairs of the name/value from the form!\n\nThis took me quite a long time to figure out in part because most of the Google-able resources are still on Flask...\n",
      "summary": "I just started using FastAPI for a home project and needed to pass back a dynamic number of values from a form rendered with jinja...",
      "date_published": "2022-05-15T00:00:00Z",
      "date_modified": "2022-05-15T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/plug-snapshot-to-save-your-life/",
      "url": "https://pype.dev/plug-snapshot-to-save-your-life/",
      "title": "Plug-Snapshot-To-Save-Your-Life",
      "content_html": "\u003cp\u003eIf you use vim-plug for managing your vim plugins, do yourself a favor and snapshot your plugins before upgrading!\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e:PlugSnapshot\u003c/code\u003e  creates a vim.snapshot file that you can use to restore your plugin versions with \u003ccode\u003evim -S snapshot.vim\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eThe snapshot file looks like this:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e❯ cat snapshot.vim\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e\u0026#34; Generated by vim-plug\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e\u0026#34; Tue 17 May 2022 03:44:29 PM CDT\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e\u0026#34; :source this file in vim to restore the snapshot\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003e\u0026#34; or execute: vim -S snapshot.vim\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;Telegraph.nvim\u0026#39;].commit = \u0026#39;92e472f4e83acd60eb3766168e66d02718bfefe0\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;TrueZen.nvim\u0026#39;].commit = \u0026#39;508b977d71650da5c9243698614a9a1416f116d4\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;ariake-vim-colors\u0026#39;].commit = \u0026#39;9fb35f1255e475631c9df24ecc5485a40360cc7b\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;auto-pairs\u0026#39;].commit = \u0026#39;39f06b873a8449af8ff6a3eee716d3da14d63a76\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;bufutils.vim\u0026#39;].commit = \u0026#39;4634feb1312fd73fab66cfaa860e7af3abde935b\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;cmp-buffer\u0026#39;].commit = \u0026#39;12463cfcd9b14052f9effccbf1d84caa7a2d57f0\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;cmp-cmdline\u0026#39;].commit = \u0026#39;c36ca4bc1dedb12b4ba6546b96c43896fd6e7252\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;cmp-nvim-lsp\u0026#39;].commit = \u0026#39;e6b5feb2e6560b61f31c756fb9231a0d7b10c73d\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;cmp-nvim-ultisnips\u0026#39;].commit = \u0026#39;21f02b62deb409ce69928a23406076bd0043ddbc\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;cmp-path\u0026#39;].commit = \u0026#39;466b6b8270f7ba89abd59f402c73f63c7331ff6e\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;cmp-spell\u0026#39;].commit = \u0026#39;5602f1a0de7831f8dad5b0c6db45328fbd539971\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;compe-tabnine\u0026#39;].commit = \u0026#39;33e4af509c27da9ef2c9c3002c01e3ec031797d4\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;coverage-highlight.vim\u0026#39;].commit = \u0026#39;864e03679ea4168661501246147893cc82020917\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;diffurcate.vim\u0026#39;].commit = \u0026#39;b804675072220ff7c7ebcd24a028aa4aa35f09cc\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;fzf\u0026#39;].commit = \u0026#39;6dcf5c3d7d6c321b17e6a5673f1533d6e8350462\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;fzf.vim\u0026#39;].commit = \u0026#39;d5f1f8641b24c0fd5b10a299824362a2a1b20ae0\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;git-blame.vim\u0026#39;].commit = \u0026#39;9d144b7bed5d8f1c9259551768b7f3b3d1294917\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;harpoon\u0026#39;].commit = \u0026#39;d3d3d22b6207f46f8ca64946f4d781e975aec0fc\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;instant.nvim\u0026#39;].commit = \u0026#39;c02d72267b12130609b7ad39b76cf7f4a3bc9554\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;lsp-colors.nvim\u0026#39;].commit = \u0026#39;517fe3ab6b63f9907b093bc9443ef06b56f804f3\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;lsp_extensions.nvim\u0026#39;].commit = \u0026#39;4011f4aec61ba59c734f5dbf52e91f258b99d985\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;lspkind-nvim\u0026#39;].commit = \u0026#39;57e5b5dfbe991151b07d272a06e365a77cc3d0e7\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;lspsaga.nvim\u0026#39;].commit = \u0026#39;8dde091a61ab07f639baaa82b456d3508d0aa7e8\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;neoformat\u0026#39;].commit = \u0026#39;409ebbba9f4b568ea87ab4f2de90a645cf5d000a\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;neovim-fuzzy\u0026#39;].commit = \u0026#39;0bef4e1a81c65fc05d31380dd74454bd67733837\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nerdtree\u0026#39;].commit = \u0026#39;eed488b1cd1867bd25f19f90e10440c5cc7d6424\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nerdtree-visual-selection\u0026#39;].commit = \u0026#39;05427635ff053a2c542fcf8d7c3744c72575e76c\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nvim-cmp\u0026#39;].commit = \u0026#39;a226b6a4ff72e5e809ed17734318233fb25c87f3\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nvim-compe\u0026#39;].commit = \u0026#39;d186d739c54823e0b010feb205c6f97792322c08\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nvim-lspconfig\u0026#39;].commit = \u0026#39;9ff2a06cebd4c8c3af5259d713959ab310125bec\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nvim-lspinstall\u0026#39;].commit = \u0026#39;79ec2425d6b39cdcb69d379f3e56847f49be73eb\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nvim-treesitter\u0026#39;].commit = \u0026#39;10d57b3ec14cac0b6b759e1eb5594617b8a7e883\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nvim-treesitter-textobjects\u0026#39;].commit = \u0026#39;094e8ad3cc839e825f8dcc91352837653e365a8f\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;nvim-web-devicons\u0026#39;].commit = \u0026#39;bdd43421437f2ef037e0dafeaaaa62b31d35ef2f\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;playground\u0026#39;].commit = \u0026#39;71b00a3c665298e5155ad64a9020135808d4e3e8\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;plenary.nvim\u0026#39;].commit = \u0026#39;25b3475b97f241e6f76249747bc209e70c5d2ec8\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;popup.nvim\u0026#39;].commit = \u0026#39;b7404d35d5d3548a82149238289fa71f7f6de4ac\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;syntastic\u0026#39;].commit = \u0026#39;b7f4f71539038d33f173bfa72631737da049575a\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;tabular\u0026#39;].commit = \u0026#39;339091ac4dd1f17e225fe7d57b48aff55f99b23a\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;targets.vim\u0026#39;].commit = \u0026#39;8d6ff2984cdfaebe5b7a6eee8f226a6dd1226f2d\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;telescope-fzy-native.nvim\u0026#39;].commit = \u0026#39;7b3d2528102f858036627a68821ccf5fc1d78ce4\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;telescope.nvim\u0026#39;].commit = \u0026#39;39b12d84e86f5054e2ed98829b367598ae53ab41\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;termopen.vim\u0026#39;].commit = \u0026#39;3194a991a18a9be2fd9fcf8c4c55fe990c04b2bd\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;tokyonight.nvim\u0026#39;].commit = \u0026#39;8223c970677e4d88c9b6b6d81bda23daf11062bb\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;trouble.nvim\u0026#39;].commit = \u0026#39;da61737d860ddc12f78e638152834487eabf0ee5\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;ultisnips\u0026#39;].commit = \u0026#39;f5ccf0977c611ffd774ca180774959301baaffad\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;undotree\u0026#39;].commit = \u0026#39;08e259be24d4476c1ee745dc735eefd44f90efdc\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-airline\u0026#39;].commit = \u0026#39;c4655701431a9c79704c827fd88a4783ec946879\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-airline-themes\u0026#39;].commit = \u0026#39;97cf3e6e638f936187d5f6e9b5eb1bdf0a4df256\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-be-good\u0026#39;].commit = \u0026#39;bc499a06c14c729b22a6cc7e730a9fbc44d4e737\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-commentary\u0026#39;].commit = \u0026#39;3654775824337f466109f00eaf6759760f65be34\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-devicons\u0026#39;].commit = \u0026#39;a2258658661e42dd4cdba4958805dbad1fe29ef4\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-dispatch\u0026#39;].commit = \u0026#39;00e77d90452e3c710014b26dc61ea919bc895e92\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-flake8\u0026#39;].commit = \u0026#39;a99054ef98e8fdaefa1315af4649138bcadbfdf7\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-floaterm\u0026#39;].commit = \u0026#39;ab7876f86c05c1935eb23a193f4f276132902ac1\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-fugitive\u0026#39;].commit = \u0026#39;f529acef74b4266d94f22414c60b4a8930c1e0f3\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-gitbranch\u0026#39;].commit = \u0026#39;1a8ba866f3eaf0194783b9f8573339d6ede8f1ed\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-indent-object\u0026#39;].commit = \u0026#39;5c5b24c959478929b54a9e831a8e2e651a465965\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-nerdtree-syntax-highlight\u0026#39;].commit = \u0026#39;5178ee4d7f4e7761187df30bb709f703d91df18a\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-polyglot\u0026#39;].commit = \u0026#39;38282d58387cff48ac203f6912c05e4c8686141b\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-pydocstring\u0026#39;].commit = \u0026#39;f6e3c52bfaf4b4f76dab4d84e75b94199d5f3b9c\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-quicklink\u0026#39;].commit = \u0026#39;021167741588555501594e1fc31f130b16acefa0\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-repeat\u0026#39;].commit = \u0026#39;24afe922e6a05891756ecf331f39a1f6743d3d5a\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-signify\u0026#39;].commit = \u0026#39;69498f6d49f3eeac06870012416dd9bf867b84f3\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-sneak\u0026#39;].commit = \u0026#39;94c2de47ab301d476a2baec9ffda07367046bec9\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-snippets\u0026#39;].commit = \u0026#39;6f270bb2d26c38765ff2243e9337c65f8a96a28b\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-startify\u0026#39;].commit = \u0026#39;81e36c352a8deea54df5ec1e2f4348685569bed2\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-surround\u0026#39;].commit = \u0026#39;bf3480dc9ae7bea34c78fbba4c65b4548b5b1fea\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-test\u0026#39;].commit = \u0026#39;2240d7a4b868cb594b7d83544e1b6db4df806e5e\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-tmux-runner\u0026#39;].commit = \u0026#39;54767911fd5e6e2d8e493847149e315ac2e6531a\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-ultest\u0026#39;].commit = \u0026#39;6978fd32e3ca2c1c5591884eea0d57a7ee43d212\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vim-visualstar\u0026#39;].commit = \u0026#39;a18cd0e7a03311ac709595c1d261ed44b45c9098\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;vimtex\u0026#39;].commit = \u0026#39;dfaca59bbbf0079ab1b4f159337ae7f17d1b5289\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003esilent! let g:plugs[\u0026#39;which-key.nvim\u0026#39;].commit = \u0026#39;bd4411a2ed4dd8bb69c125e339d837028a6eea71\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"go\"\u003ePlugUpdate!\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nIf you use vim-plug for managing your vim plugins, do yourself a favor and snapshot your plugins before upgrading!\n\n\n`:PlugSnapshot`  creates a vim.snapshot file that you can use to restore your plugin versions with `vim -S snapshot.vim`\n\nThe snapshot file looks like this:\n\n```console\n❯ cat snapshot.vim\n\" Generated by vim-plug\n\" Tue 17 May 2022 03:44:29 PM CDT\n\" :source this file in vim to restore the snapshot\n\" or execute: vim -S snapshot.vim\n\nsilent! let g:plugs['Telegraph.nvim'].commit = '92e472f4e83acd60eb3766168e66d02718bfefe0'\nsilent! let g:plugs['TrueZen.nvim'].commit = '508b977d71650da5c9243698614a9a1416f116d4'\nsilent! let g:plugs['ariake-vim-colors'].commit = '9fb35f1255e475631c9df24ecc5485a40360cc7b'\nsilent! let g:plugs['auto-pairs'].commit = '39f06b873a8449af8ff6a3eee716d3da14d63a76'\nsilent! let g:plugs['bufutils.vim'].commit = '4634feb1312fd73fab66cfaa860e7af3abde935b'\nsilent! let g:plugs['cmp-buffer'].commit = '12463cfcd9b14052f9effccbf1d84caa7a2d57f0'\nsilent! let g:plugs['cmp-cmdline'].commit = 'c36ca4bc1dedb12b4ba6546b96c43896fd6e7252'\nsilent! let g:plugs['cmp-nvim-lsp'].commit = 'e6b5feb2e6560b61f31c756fb9231a0d7b10c73d'\nsilent! let g:plugs['cmp-nvim-ultisnips'].commit = '21f02b62deb409ce69928a23406076bd0043ddbc'\nsilent! let g:plugs['cmp-path'].commit = '466b6b8270f7ba89abd59f402c73f63c7331ff6e'\nsilent! let g:plugs['cmp-spell'].commit = '5602f1a0de7831f8dad5b0c6db45328fbd539971'\nsilent! let g:plugs['compe-tabnine'].commit = '33e4af509c27da9ef2c9c3002c01e3ec031797d4'\nsilent! let g:plugs['coverage-highlight.vim'].commit = '864e03679ea4168661501246147893cc82020917'\nsilent! let g:plugs['diffurcate.vim'].commit = 'b804675072220ff7c7ebcd24a028aa4aa35f09cc'\nsilent! let g:plugs['fzf'].commit = '6dcf5c3d7d6c321b17e6a5673f1533d6e8350462'\nsilent! let g:plugs['fzf.vim'].commit = 'd5f1f8641b24c0fd5b10a299824362a2a1b20ae0'\nsilent! let g:plugs['git-blame.vim'].commit = '9d144b7bed5d8f1c9259551768b7f3b3d1294917'\nsilent! let g:plugs['harpoon'].commit = 'd3d3d22b6207f46f8ca64946f4d781e975aec0fc'\nsilent! let g:plugs['instant.nvim'].commit = 'c02d72267b12130609b7ad39b76cf7f4a3bc9554'\nsilent! let g:plugs['lsp-colors.nvim'].commit = '517fe3ab6b63f9907b093bc9443ef06b56f804f3'\nsilent! let g:plugs['lsp_extensions.nvim'].commit = '4011f4aec61ba59c734f5dbf52e91f258b99d985'\nsilent! let g:plugs['lspkind-nvim'].commit = '57e5b5dfbe991151b07d272a06e365a77cc3d0e7'\nsilent! let g:plugs['lspsaga.nvim'].commit = '8dde091a61ab07f639baaa82b456d3508d0aa7e8'\nsilent! let g:plugs['neoformat'].commit = '409ebbba9f4b568ea87ab4f2de90a645cf5d000a'\nsilent! let g:plugs['neovim-fuzzy'].commit = '0bef4e1a81c65fc05d31380dd74454bd67733837'\nsilent! let g:plugs['nerdtree'].commit = 'eed488b1cd1867bd25f19f90e10440c5cc7d6424'\nsilent! let g:plugs['nerdtree-visual-selection'].commit = '05427635ff053a2c542fcf8d7c3744c72575e76c'\nsilent! let g:plugs['nvim-cmp'].commit = 'a226b6a4ff72e5e809ed17734318233fb25c87f3'\nsilent! let g:plugs['nvim-compe'].commit = 'd186d739c54823e0b010feb205c6f97792322c08'\nsilent! let g:plugs['nvim-lspconfig'].commit = '9ff2a06cebd4c8c3af5259d713959ab310125bec'\nsilent! let g:plugs['nvim-lspinstall'].commit = '79ec2425d6b39cdcb69d379f3e56847f49be73eb'\nsilent! let g:plugs['nvim-treesitter'].commit = '10d57b3ec14cac0b6b759e1eb5594617b8a7e883'\nsilent! let g:plugs['nvim-treesitter-textobjects'].commit = '094e8ad3cc839e825f8dcc91352837653e365a8f'\nsilent! let g:plugs['nvim-web-devicons'].commit = 'bdd43421437f2ef037e0dafeaaaa62b31d35ef2f'\nsilent! let g:plugs['playground'].commit = '71b00a3c665298e5155ad64a9020135808d4e3e8'\nsilent! let g:plugs['plenary.nvim'].commit = '25b3475b97f241e6f76249747bc209e70c5d2ec8'\nsilent! let g:plugs['popup.nvim'].commit = 'b7404d35d5d3548a82149238289fa71f7f6de4ac'\nsilent! let g:plugs['syntastic'].commit = 'b7f4f71539038d33f173bfa72631737da049575a'\nsilent! let g:plugs['tabular'].commit = '339091ac4dd1f17e225fe7d57b48aff55f99b23a'\nsilent! let g:plugs['targets.vim'].commit = '8d6ff2984cdfaebe5b7a6eee8f226a6dd1226f2d'\nsilent! let g:plugs['telescope-fzy-native.nvim'].commit = '7b3d2528102f858036627a68821ccf5fc1d78ce4'\nsilent! let g:plugs['telescope.nvim'].commit = '39b12d84e86f5054e2ed98829b367598ae53ab41'\nsilent! let g:plugs['termopen.vim'].commit = '3194a991a18a9be2fd9fcf8c4c55fe990c04b2bd'\nsilent! let g:plugs['tokyonight.nvim'].commit = '8223c970677e4d88c9b6b6d81bda23daf11062bb'\nsilent! let g:plugs['trouble.nvim'].commit = 'da61737d860ddc12f78e638152834487eabf0ee5'\nsilent! let g:plugs['ultisnips'].commit = 'f5ccf0977c611ffd774ca180774959301baaffad'\nsilent! let g:plugs['undotree'].commit = '08e259be24d4476c1ee745dc735eefd44f90efdc'\nsilent! let g:plugs['vim-airline'].commit = 'c4655701431a9c79704c827fd88a4783ec946879'\nsilent! let g:plugs['vim-airline-themes'].commit = '97cf3e6e638f936187d5f6e9b5eb1bdf0a4df256'\nsilent! let g:plugs['vim-be-good'].commit = 'bc499a06c14c729b22a6cc7e730a9fbc44d4e737'\nsilent! let g:plugs['vim-commentary'].commit = '3654775824337f466109f00eaf6759760f65be34'\nsilent! let g:plugs['vim-devicons'].commit = 'a2258658661e42dd4cdba4958805dbad1fe29ef4'\nsilent! let g:plugs['vim-dispatch'].commit = '00e77d90452e3c710014b26dc61ea919bc895e92'\nsilent! let g:plugs['vim-flake8'].commit = 'a99054ef98e8fdaefa1315af4649138bcadbfdf7'\nsilent! let g:plugs['vim-floaterm'].commit = 'ab7876f86c05c1935eb23a193f4f276132902ac1'\nsilent! let g:plugs['vim-fugitive'].commit = 'f529acef74b4266d94f22414c60b4a8930c1e0f3'\nsilent! let g:plugs['vim-gitbranch'].commit = '1a8ba866f3eaf0194783b9f8573339d6ede8f1ed'\nsilent! let g:plugs['vim-indent-object'].commit = '5c5b24c959478929b54a9e831a8e2e651a465965'\nsilent! let g:plugs['vim-nerdtree-syntax-highlight'].commit = '5178ee4d7f4e7761187df30bb709f703d91df18a'\nsilent! let g:plugs['vim-polyglot'].commit = '38282d58387cff48ac203f6912c05e4c8686141b'\nsilent! let g:plugs['vim-pydocstring'].commit = 'f6e3c52bfaf4b4f76dab4d84e75b94199d5f3b9c'\nsilent! let g:plugs['vim-quicklink'].commit = '021167741588555501594e1fc31f130b16acefa0'\nsilent! let g:plugs['vim-repeat'].commit = '24afe922e6a05891756ecf331f39a1f6743d3d5a'\nsilent! let g:plugs['vim-signify'].commit = '69498f6d49f3eeac06870012416dd9bf867b84f3'\nsilent! let g:plugs['vim-sneak'].commit = '94c2de47ab301d476a2baec9ffda07367046bec9'\nsilent! let g:plugs['vim-snippets'].commit = '6f270bb2d26c38765ff2243e9337c65f8a96a28b'\nsilent! let g:plugs['vim-startify'].commit = '81e36c352a8deea54df5ec1e2f4348685569bed2'\nsilent! let g:plugs['vim-surround'].commit = 'bf3480dc9ae7bea34c78fbba4c65b4548b5b1fea'\nsilent! let g:plugs['vim-test'].commit = '2240d7a4b868cb594b7d83544e1b6db4df806e5e'\nsilent! let g:plugs['vim-tmux-runner'].commit = '54767911fd5e6e2d8e493847149e315ac2e6531a'\nsilent! let g:plugs['vim-ultest'].commit = '6978fd32e3ca2c1c5591884eea0d57a7ee43d212'\nsilent! let g:plugs['vim-visualstar'].commit = 'a18cd0e7a03311ac709595c1d261ed44b45c9098'\nsilent! let g:plugs['vimtex'].commit = 'dfaca59bbbf0079ab1b4f159337ae7f17d1b5289'\nsilent! let g:plugs['which-key.nvim'].commit = 'bd4411a2ed4dd8bb69c125e339d837028a6eea71'\n\nPlugUpdate!\n\n```\n",
      "summary": "If you use vim-plug for managing your vim plugins, do yourself a favor and snapshot your plugins before upgrading!",
      "date_published": "2022-05-13T00:00:00Z",
      "date_modified": "2022-05-13T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/python-eval/",
      "url": "https://pype.dev/python-eval/",
      "title": "Python-Eval",
      "content_html": "\u003ch1 id=\"todo\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTODO\u003c/span\u003e\u003c/h1\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;my Title\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eeval\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026#34;my\u0026#34; in title\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"kc\"\u003eTrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;hello, world\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;formatting\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\n# TODO\n\n```python\ntitle = \"my Title\"\neval('\"my\" in title')\n\n\u003e\u003e\u003e True\n\n```\n\n```python\nprint(\"hello, world\"); print(\"formatting\")\n```\n",
      "date_published": "2022-05-12T00:00:00Z",
      "date_modified": "2022-05-12T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "tech",
        "python",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/dataframe-to-styled-html/",
      "url": "https://pype.dev/dataframe-to-styled-html/",
      "title": "Dataframe-To-Styled-Html",
      "content_html": "\u003cp\u003eI wrote up a little on exporting DataFrames to markdown and html \u003ca href=\"/dataframe-to-markdown\"\u003ehere\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eBut I\u0026rsquo;ve been playing with a web app for with lists and while I\u0026rsquo;m toying around I learned you can actually give your tables some style with some simple css classes!\u003c/p\u003e\n\u003ch1 id=\"to-html\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTo HTML\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eReminder that if you have a dataframe, \u003ccode\u003edf\u003c/code\u003e, you can \u003ccode\u003edf.to_html()\u003c/code\u003e to get an HTML table of your dataframe.\u003c/p\u003e\n\u003cp\u003eWell you can pass some \u003ccode\u003eclasses\u003c/code\u003e to make it look super nice!\u003c/p\u003e\n\u003ch1 id=\"classes-and-css\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eClasses and CSS\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eI don\u0026rsquo;t know anything really about CSS so I won\u0026rsquo;t pretend otherwise, but as I was learning about bootstrap that\u0026rsquo;s where I stumbled upon this\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThere are several classes you can pass but I found really good luck with \u003ccode\u003etable-bordered\u003c/code\u003e and \u003ccode\u003etable-dark\u003c/code\u003e for my use case\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edf.to_html(classes=[\u0026quot;table table-bordered table-dark\u0026quot;])\u003c/code\u003e\u003c/p\u003e\n\u003ctable border=\"1\" class=\"dataframe table table-bordered table-dark\"\u003e  \u003cthead\u003e\n\u003ctr style=\"text-align: right;\"\u003e      \u003cth\u003eUnnamed: 0\u003c/th\u003e      \u003cth\u003empg\u003c/th\u003e\n\u003cth\u003ecyl\u003c/th\u003e      \u003cth\u003edisp\u003c/th\u003e      \u003cth\u003ehp\u003c/th\u003e      \u003cth\u003edrat\u003c/th\u003e\n\u003cth\u003ewt\u003c/th\u003e      \u003cth\u003eqsec\u003c/th\u003e      \u003cth\u003evs\u003c/th\u003e      \u003cth\u003eam\u003c/th\u003e\n\u003cth\u003egear\u003c/th\u003e      \u003cth\u003ecarb\u003c/th\u003e    \u003c/tr\u003e  \u003c/thead\u003e  \u003ctbody\u003e    \u003ctr\u003e\n\u003ctd\u003eMazda RX4\u003c/td\u003e      \u003ctd\u003e21.0\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e160.0\u003c/td\u003e\n\u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.90\u003c/td\u003e      \u003ctd\u003e2.620\u003c/td\u003e      \u003ctd\u003e16.46\u003c/td\u003e\n\u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e\n\u003ctd\u003eMazda RX4 Wag\u003c/td\u003e      \u003ctd\u003e21.0\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e160.0\u003c/td\u003e\n\u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.90\u003c/td\u003e      \u003ctd\u003e2.875\u003c/td\u003e      \u003ctd\u003e17.02\u003c/td\u003e\n\u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e\n\u003ctd\u003eDatsun 710\u003c/td\u003e      \u003ctd\u003e22.8\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e108.0\u003c/td\u003e\n\u003ctd\u003e93\u003c/td\u003e      \u003ctd\u003e3.85\u003c/td\u003e      \u003ctd\u003e2.320\u003c/td\u003e      \u003ctd\u003e18.61\u003c/td\u003e\n\u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e\n\u003ctd\u003eHornet 4 Drive\u003c/td\u003e      \u003ctd\u003e21.4\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e258.0\u003c/td\u003e\n\u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.08\u003c/td\u003e      \u003ctd\u003e3.215\u003c/td\u003e      \u003ctd\u003e19.44\u003c/td\u003e\n\u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e3\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e\n\u003ctd\u003eHornet Sportabout\u003c/td\u003e      \u003ctd\u003e18.7\u003c/td\u003e      \u003ctd\u003e8\u003c/td\u003e\n\u003ctd\u003e360.0\u003c/td\u003e      \u003ctd\u003e175\u003c/td\u003e      \u003ctd\u003e3.15\u003c/td\u003e      \u003ctd\u003e3.440\u003c/td\u003e\n\u003ctd\u003e17.02\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e3\u003c/td\u003e      \u003ctd\u003e2\u003c/td\u003e\n\u003c/tr\u003e  \u003c/tbody\u003e\u003c/table\u003e\n\u003ch1 id=\"you-try-it\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eYou try it!\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eCrack open ipython and make a dataframe, then \u003ccode\u003edf.to_html(classes=[\u0026quot;table table-bordered table-dark\u0026quot;])\u003c/code\u003e, copy the output (minus the quote marks ipython uses to denote the string type) that into \u003ccode\u003emy-file.html\u003c/code\u003e, open that up in a browser and be amazed!\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eFor added effeciency try using pyperclip to copy the output right to your clipboard!\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ccode\u003epip install pyperclip\u003c/code\u003e and then \u003ccode\u003epyperclip.copy(df.to_html(classes=[\u0026quot;table table-bordered table-dark\u0026quot;]))\u003c/code\u003e\u003c/p\u003e\n",
      "content_text": "\nI wrote up a little on exporting DataFrames to markdown and html [here](/dataframe-to-markdown)\n\nBut I've been playing with a web app for with lists and while I'm toying around I learned you can actually give your tables some style with some simple css classes! \n\n# To HTML\n\nReminder that if you have a dataframe, `df`, you can `df.to_html()` to get an HTML table of your dataframe.\n\nWell you can pass some `classes` to make it look super nice!\n\n# Classes and CSS\n\nI don't know anything really about CSS so I won't pretend otherwise, but as I was learning about bootstrap that's where I stumbled upon this...\n\nThere are several classes you can pass but I found really good luck with `table-bordered` and `table-dark` for my use case\n\n`df.to_html(classes=[\"table table-bordered table-dark\"])`\n\n\u003ctable border=\"1\" class=\"dataframe table table-bordered table-dark\"\u003e  \u003cthead\u003e\n\u003ctr style=\"text-align: right;\"\u003e      \u003cth\u003eUnnamed: 0\u003c/th\u003e      \u003cth\u003empg\u003c/th\u003e\n\u003cth\u003ecyl\u003c/th\u003e      \u003cth\u003edisp\u003c/th\u003e      \u003cth\u003ehp\u003c/th\u003e      \u003cth\u003edrat\u003c/th\u003e\n\u003cth\u003ewt\u003c/th\u003e      \u003cth\u003eqsec\u003c/th\u003e      \u003cth\u003evs\u003c/th\u003e      \u003cth\u003eam\u003c/th\u003e\n\u003cth\u003egear\u003c/th\u003e      \u003cth\u003ecarb\u003c/th\u003e    \u003c/tr\u003e  \u003c/thead\u003e  \u003ctbody\u003e    \u003ctr\u003e\n\u003ctd\u003eMazda RX4\u003c/td\u003e      \u003ctd\u003e21.0\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e160.0\u003c/td\u003e\n\u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.90\u003c/td\u003e      \u003ctd\u003e2.620\u003c/td\u003e      \u003ctd\u003e16.46\u003c/td\u003e\n\u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e\n\u003ctd\u003eMazda RX4 Wag\u003c/td\u003e      \u003ctd\u003e21.0\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e160.0\u003c/td\u003e\n\u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.90\u003c/td\u003e      \u003ctd\u003e2.875\u003c/td\u003e      \u003ctd\u003e17.02\u003c/td\u003e\n\u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e\n\u003ctd\u003eDatsun 710\u003c/td\u003e      \u003ctd\u003e22.8\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e108.0\u003c/td\u003e\n\u003ctd\u003e93\u003c/td\u003e      \u003ctd\u003e3.85\u003c/td\u003e      \u003ctd\u003e2.320\u003c/td\u003e      \u003ctd\u003e18.61\u003c/td\u003e\n\u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e\n\u003ctd\u003eHornet 4 Drive\u003c/td\u003e      \u003ctd\u003e21.4\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e258.0\u003c/td\u003e\n\u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.08\u003c/td\u003e      \u003ctd\u003e3.215\u003c/td\u003e      \u003ctd\u003e19.44\u003c/td\u003e\n\u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e3\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e\n\u003ctd\u003eHornet Sportabout\u003c/td\u003e      \u003ctd\u003e18.7\u003c/td\u003e      \u003ctd\u003e8\u003c/td\u003e\n\u003ctd\u003e360.0\u003c/td\u003e      \u003ctd\u003e175\u003c/td\u003e      \u003ctd\u003e3.15\u003c/td\u003e      \u003ctd\u003e3.440\u003c/td\u003e\n\u003ctd\u003e17.02\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e3\u003c/td\u003e      \u003ctd\u003e2\u003c/td\u003e\n\u003c/tr\u003e  \u003c/tbody\u003e\u003c/table\u003e\n\n\n# You try it!\n\nCrack open ipython and make a dataframe, then `df.to_html(classes=[\"table table-bordered table-dark\"])`, copy the output (minus the quote marks ipython uses to denote the string type) that into `my-file.html`, open that up in a browser and be amazed!\n\n\u003e For added effeciency try using pyperclip to copy the output right to your clipboard!\n\n`pip install pyperclip` and then `pyperclip.copy(df.to_html(classes=[\"table table-bordered table-dark\"]))`\n",
      "summary": "I wrote up a little on exporting DataFrames to markdown and html here",
      "date_published": "2022-05-07T00:00:00Z",
      "date_modified": "2022-05-07T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/dataframe-to-markdown/",
      "url": "https://pype.dev/dataframe-to-markdown/",
      "title": "Dataframe-To-Markdown",
      "content_html": "\u003ch1 id=\"pandas\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003ePandas\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003epandas.DataFrame\u003c/code\u003es are pretty sweet data structures in Python.\u003c/p\u003e\n\u003cp\u003eI do a lot of work with tabular data and one thing I have incorporated into some of that work is automatic data summary reports by throwing the first few, or several relevant, rows of a dataframe at a point in a pipeline into a markdown file.\u003c/p\u003e\n\u003cp\u003ePandas has a method on DataFrames that makes this 100% trivial!\u003c/p\u003e\n\u003ch1 id=\"the-method\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe Method\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eSay we have a dataframe, \u003ccode\u003edf\u003c/code\u003e\u0026hellip; then it\u0026rsquo;s literally just: \u003ccode\u003edf.to_markdown()\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ehead\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"n\"\u003eUnnamed\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e   \u003cspan class=\"n\"\u003empg\u003c/span\u003e  \u003cspan class=\"n\"\u003ecyl\u003c/span\u003e   \u003cspan class=\"n\"\u003edisp\u003c/span\u003e   \u003cspan class=\"n\"\u003ehp\u003c/span\u003e  \u003cspan class=\"n\"\u003edrat\u003c/span\u003e     \u003cspan class=\"n\"\u003ewt\u003c/span\u003e   \u003cspan class=\"n\"\u003eqsec\u003c/span\u003e  \u003cspan class=\"n\"\u003evs\u003c/span\u003e  \u003cspan class=\"n\"\u003eam\u003c/span\u003e  \u003cspan class=\"n\"\u003egear\u003c/span\u003e  \u003cspan class=\"n\"\u003ecarb\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e          \u003cspan class=\"n\"\u003eMazda\u003c/span\u003e \u003cspan class=\"n\"\u003eRX4\u003c/span\u003e  \u003cspan class=\"mf\"\u003e21.0\u003c/span\u003e    \u003cspan class=\"mi\"\u003e6\u003c/span\u003e  \u003cspan class=\"mf\"\u003e160.0\u003c/span\u003e  \u003cspan class=\"mi\"\u003e110\u003c/span\u003e  \u003cspan class=\"mf\"\u003e3.90\u003c/span\u003e  \u003cspan class=\"mf\"\u003e2.620\u003c/span\u003e  \u003cspan class=\"mf\"\u003e16.46\u003c/span\u003e   \u003cspan class=\"mi\"\u003e0\u003c/span\u003e   \u003cspan class=\"mi\"\u003e1\u003c/span\u003e     \u003cspan class=\"mi\"\u003e4\u003c/span\u003e     \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e      \u003cspan class=\"n\"\u003eMazda\u003c/span\u003e \u003cspan class=\"n\"\u003eRX4\u003c/span\u003e \u003cspan class=\"n\"\u003eWag\u003c/span\u003e  \u003cspan class=\"mf\"\u003e21.0\u003c/span\u003e    \u003cspan class=\"mi\"\u003e6\u003c/span\u003e  \u003cspan class=\"mf\"\u003e160.0\u003c/span\u003e  \u003cspan class=\"mi\"\u003e110\u003c/span\u003e  \u003cspan class=\"mf\"\u003e3.90\u003c/span\u003e  \u003cspan class=\"mf\"\u003e2.875\u003c/span\u003e  \u003cspan class=\"mf\"\u003e17.02\u003c/span\u003e   \u003cspan class=\"mi\"\u003e0\u003c/span\u003e   \u003cspan class=\"mi\"\u003e1\u003c/span\u003e     \u003cspan class=\"mi\"\u003e4\u003c/span\u003e     \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e         \u003cspan class=\"n\"\u003eDatsun\u003c/span\u003e \u003cspan class=\"mi\"\u003e710\u003c/span\u003e  \u003cspan class=\"mf\"\u003e22.8\u003c/span\u003e    \u003cspan class=\"mi\"\u003e4\u003c/span\u003e  \u003cspan class=\"mf\"\u003e108.0\u003c/span\u003e   \u003cspan class=\"mi\"\u003e93\u003c/span\u003e  \u003cspan class=\"mf\"\u003e3.85\u003c/span\u003e  \u003cspan class=\"mf\"\u003e2.320\u003c/span\u003e  \u003cspan class=\"mf\"\u003e18.61\u003c/span\u003e   \u003cspan class=\"mi\"\u003e1\u003c/span\u003e   \u003cspan class=\"mi\"\u003e1\u003c/span\u003e     \u003cspan class=\"mi\"\u003e4\u003c/span\u003e     \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e     \u003cspan class=\"n\"\u003eHornet\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e \u003cspan class=\"n\"\u003eDrive\u003c/span\u003e  \u003cspan class=\"mf\"\u003e21.4\u003c/span\u003e    \u003cspan class=\"mi\"\u003e6\u003c/span\u003e  \u003cspan class=\"mf\"\u003e258.0\u003c/span\u003e  \u003cspan class=\"mi\"\u003e110\u003c/span\u003e  \u003cspan class=\"mf\"\u003e3.08\u003c/span\u003e  \u003cspan class=\"mf\"\u003e3.215\u003c/span\u003e  \u003cspan class=\"mf\"\u003e19.44\u003c/span\u003e   \u003cspan class=\"mi\"\u003e1\u003c/span\u003e   \u003cspan class=\"mi\"\u003e0\u003c/span\u003e     \u003cspan class=\"mi\"\u003e3\u003c/span\u003e     \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e  \u003cspan class=\"n\"\u003eHornet\u003c/span\u003e \u003cspan class=\"n\"\u003eSportabout\u003c/span\u003e  \u003cspan class=\"mf\"\u003e18.7\u003c/span\u003e    \u003cspan class=\"mi\"\u003e8\u003c/span\u003e  \u003cspan class=\"mf\"\u003e360.0\u003c/span\u003e  \u003cspan class=\"mi\"\u003e175\u003c/span\u003e  \u003cspan class=\"mf\"\u003e3.15\u003c/span\u003e  \u003cspan class=\"mf\"\u003e3.440\u003c/span\u003e  \u003cspan class=\"mf\"\u003e17.02\u003c/span\u003e   \u003cspan class=\"mi\"\u003e0\u003c/span\u003e   \u003cspan class=\"mi\"\u003e0\u003c/span\u003e     \u003cspan class=\"mi\"\u003e3\u003c/span\u003e     \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn ipython I can call the method and get a markdown table back as a string\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emental\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003elake\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eposts\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emental\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003elake\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ehead\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_markdown\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e\u0026#39;|    | Unnamed: 0        |   mpg |   cyl |   disp |   hp |   drat |    wt |   qsec |   vs |   am |   gear |   carb |\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s1\"\u003e|---:|:------------------|------:|------:|-------:|-----:|-------:|------:|-------:|-----:|-----:|-------:|-------:|\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s1\"\u003e|  0 | Mazda RX4         |  21   |     6 |    160 |  110 |   3.9  | 2.62  |  16.46 |    0 |    1 |      4 |      4 |\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s1\"\u003e|  1 | Mazda RX4 Wag     |  21   |     6 |    160 |  110 |   3.9  | 2.875 |  17.02 |    0 |    1 |      4 |      4 |\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s1\"\u003e|  2 | Datsun 710        |  22.8 |     4 |    108 |   93 |   3.85 | 2.32  |  18.61 |    1 |    1 |      4 |      1 |\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s1\"\u003e|  3 | Hornet 4 Drive    |  21.4 |     6 |    258 |  110 |   3.08 | 3.215 |  19.44 |    1 |    0 |      3 |      1 |\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s1\"\u003e|  4 | Hornet Sportabout |  18.7 |     8 |    360 |  175 |   3.15 | 3.44  |  17.02 |    0 |    0 |      3 |      2 |\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou can drop that string into a markdown file and using any reader that supports the rendering you\u0026rsquo;ll have a nicely formated table of example data in whatever report you\u0026rsquo;re making!\u003c/p\u003e\n\u003ch1 id=\"bonus-method\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eBonus method\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eJust like markdown, you can export a dataframe to html with \u003ccode\u003edf.to_html()\u003c/code\u003e and use that if it\u0026rsquo;s more appropriate for your use case:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u0026#39;\u0026lt;table border=\u0026#34;1\u0026#34; class=\u0026#34;dataframe\u0026#34;\u0026gt;\\n  \u0026lt;thead\u0026gt;\\n    \u0026lt;tr style=\u0026#34;text-align: right;\u0026#34;\u0026gt;\\n      \u0026lt;th\u0026gt;\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;Unnamed: 0\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;mpg\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;cyl\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;disp\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;hp\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;drat\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;wt\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;qsec\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;vs\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;am\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;gear\u0026lt;/th\u0026gt;\\n      \u0026lt;th\u0026gt;carb\u0026lt;/th\u0026gt;\\n    \u0026lt;/tr\u0026gt;\\n  \u0026lt;/thead\u0026gt;\\n  \u0026lt;tbody\u0026gt;\\n    \u0026lt;tr\u0026gt;\\n      \u0026lt;th\u0026gt;0\u0026lt;/th\u0026gt;\\n      \u0026lt;td\u0026gt;Mazda RX4\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;21.0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;6\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;160.0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;110\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3.90\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;2.620\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;16.46\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;1\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;4\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;4\u0026lt;/td\u0026gt;\\n    \u0026lt;/tr\u0026gt;\\n    \u0026lt;tr\u0026gt;\\n      \u0026lt;th\u0026gt;1\u0026lt;/th\u0026gt;\\n      \u0026lt;td\u0026gt;Mazda RX4 Wag\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;21.0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;6\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;160.0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;110\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3.90\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;2.875\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;17.02\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;1\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;4\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;4\u0026lt;/td\u0026gt;\\n    \u0026lt;/tr\u0026gt;\\n    \u0026lt;tr\u0026gt;\\n      \u0026lt;th\u0026gt;2\u0026lt;/th\u0026gt;\\n      \u0026lt;td\u0026gt;Datsun 710\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;22.8\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;4\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;108.0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;93\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3.85\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;2.320\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;18.61\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;1\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;1\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;4\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;1\u0026lt;/td\u0026gt;\\n    \u0026lt;/tr\u0026gt;\\n    \u0026lt;tr\u0026gt;\\n      \u0026lt;th\u0026gt;3\u0026lt;/th\u0026gt;\\n      \u0026lt;td\u0026gt;Hornet 4 Drive\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;21.4\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;6\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;258.0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;110\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3.08\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3.215\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;19.44\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;1\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;1\u0026lt;/td\u0026gt;\\n    \u0026lt;/tr\u0026gt;\\n    \u0026lt;tr\u0026gt;\\n      \u0026lt;th\u0026gt;4\u0026lt;/th\u0026gt;\\n      \u0026lt;td\u0026gt;Hornet Sportabout\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;18.7\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;8\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;360.0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;175\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3.15\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3.440\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;17.02\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;0\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;3\u0026lt;/td\u0026gt;\\n      \u0026lt;td\u0026gt;2\u0026lt;/td\u0026gt;\\n    \u0026lt;/tr\u0026gt;\\n  \u0026lt;/tbody\u0026gt;\\n\u0026lt;/table\u0026gt;\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eMy blog will render that html into a nice table! (After removing new line characters)\u003c/p\u003e\n\u003ctable border=\"1\" class=\"dataframe\"\u003e  \u003cthead\u003e    \u003ctr style=\"text-align: right;\"\u003e      \u003cth\u003eUnnamed: 0\u003c/th\u003e      \u003cth\u003empg\u003c/th\u003e      \u003cth\u003ecyl\u003c/th\u003e      \u003cth\u003edisp\u003c/th\u003e      \u003cth\u003ehp\u003c/th\u003e      \u003cth\u003edrat\u003c/th\u003e      \u003cth\u003ewt\u003c/th\u003e      \u003cth\u003eqsec\u003c/th\u003e      \u003cth\u003evs\u003c/th\u003e      \u003cth\u003eam\u003c/th\u003e      \u003cth\u003egear\u003c/th\u003e      \u003cth\u003ecarb\u003c/th\u003e    \u003c/tr\u003e  \u003c/thead\u003e  \u003ctbody\u003e    \u003ctr\u003e      \u003ctd\u003eMazda RX4\u003c/td\u003e      \u003ctd\u003e21.0\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e160.0\u003c/td\u003e      \u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.90\u003c/td\u003e      \u003ctd\u003e2.620\u003c/td\u003e      \u003ctd\u003e16.46\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e      \u003ctd\u003eMazda RX4 Wag\u003c/td\u003e      \u003ctd\u003e21.0\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e160.0\u003c/td\u003e      \u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.90\u003c/td\u003e      \u003ctd\u003e2.875\u003c/td\u003e      \u003ctd\u003e17.02\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e      \u003ctd\u003eDatsun 710\u003c/td\u003e      \u003ctd\u003e22.8\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e108.0\u003c/td\u003e      \u003ctd\u003e93\u003c/td\u003e      \u003ctd\u003e3.85\u003c/td\u003e      \u003ctd\u003e2.320\u003c/td\u003e      \u003ctd\u003e18.61\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e      \u003ctd\u003eHornet 4 Drive\u003c/td\u003e      \u003ctd\u003e21.4\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e258.0\u003c/td\u003e      \u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.08\u003c/td\u003e      \u003ctd\u003e3.215\u003c/td\u003e      \u003ctd\u003e19.44\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e3\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e      \u003ctd\u003eHornet Sportabout\u003c/td\u003e      \u003ctd\u003e18.7\u003c/td\u003e      \u003ctd\u003e8\u003c/td\u003e      \u003ctd\u003e360.0\u003c/td\u003e      \u003ctd\u003e175\u003c/td\u003e      \u003ctd\u003e3.15\u003c/td\u003e      \u003ctd\u003e3.440\u003c/td\u003e      \u003ctd\u003e17.02\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e3\u003c/td\u003e      \u003ctd\u003e2\u003c/td\u003e    \u003c/tr\u003e  \u003c/tbody\u003e\u003c/table\u003e\n",
      "content_text": "\n# Pandas\n\n`pandas.DataFrame`s are pretty sweet data structures in Python.\n\nI do a lot of work with tabular data and one thing I have incorporated into some of that work is automatic data summary reports by throwing the first few, or several relevant, rows of a dataframe at a point in a pipeline into a markdown file.\n\nPandas has a method on DataFrames that makes this 100% trivial!\n\n# The Method\n\nSay we have a dataframe, `df`... then it's literally just: `df.to_markdown()`\n\n```python\n❯ df.head()\n\n          Unnamed: 0   mpg  cyl   disp   hp  drat     wt   qsec  vs  am  gear  carb\n0          Mazda RX4  21.0    6  160.0  110  3.90  2.620  16.46   0   1     4     4\n1      Mazda RX4 Wag  21.0    6  160.0  110  3.90  2.875  17.02   0   1     4     4\n2         Datsun 710  22.8    4  108.0   93  3.85  2.320  18.61   1   1     4     1\n3     Hornet 4 Drive  21.4    6  258.0  110  3.08  3.215  19.44   1   0     3     1\n4  Hornet Sportabout  18.7    8  360.0  175  3.15  3.440  17.02   0   0     3     2\n\n```\n\nIn ipython I can call the method and get a markdown table back as a string\n\n```python\n\nmental-data-lake   new-posts via 3.8.11(mental-data-lake) ipython\n❯ df.head().to_markdown()\n'|    | Unnamed: 0        |   mpg |   cyl |   disp |   hp |   drat |    wt |   qsec |   vs |   am |   gear |   carb |\\n|---:|:------------------|------:|------:|-------:|-----:|-------:|------:|-------:|-----:|-----:|-------:|-------:|\\n|  0 | Mazda RX4         |  21   |     6 |    160 |  110 |   3.9  | 2.62  |  16.46 |    0 |    1 |      4 |      4 |\\n|  1 | Mazda RX4 Wag     |  21   |     6 |    160 |  110 |   3.9  | 2.875 |  17.02 |    0 |    1 |      4 |      4 |\\n|  2 | Datsun 710        |  22.8 |     4 |    108 |   93 |   3.85 | 2.32  |  18.61 |    1 |    1 |      4 |      1 |\\n|  3 | Hornet 4 Drive    |  21.4 |     6 |    258 |  110 |   3.08 | 3.215 |  19.44 |    1 |    0 |      3 |      1 |\\n|  4 | Hornet Sportabout |  18.7 |     8 |    360 |  175 |   3.15 | 3.44  |  17.02 |    0 |    0 |      3 |      2 |'\n\n```\n\nYou can drop that string into a markdown file and using any reader that supports the rendering you'll have a nicely formated table of example data in whatever report you're making!\n\n# Bonus method\n\nJust like markdown, you can export a dataframe to html with `df.to_html()` and use that if it's more appropriate for your use case:\n\n```text\n\n'\u003ctable border=\"1\" class=\"dataframe\"\u003e\\n  \u003cthead\u003e\\n    \u003ctr style=\"text-align: right;\"\u003e\\n      \u003cth\u003e\u003c/th\u003e\\n      \u003cth\u003eUnnamed: 0\u003c/th\u003e\\n      \u003cth\u003empg\u003c/th\u003e\\n      \u003cth\u003ecyl\u003c/th\u003e\\n      \u003cth\u003edisp\u003c/th\u003e\\n      \u003cth\u003ehp\u003c/th\u003e\\n      \u003cth\u003edrat\u003c/th\u003e\\n      \u003cth\u003ewt\u003c/th\u003e\\n      \u003cth\u003eqsec\u003c/th\u003e\\n      \u003cth\u003evs\u003c/th\u003e\\n      \u003cth\u003eam\u003c/th\u003e\\n      \u003cth\u003egear\u003c/th\u003e\\n      \u003cth\u003ecarb\u003c/th\u003e\\n    \u003c/tr\u003e\\n  \u003c/thead\u003e\\n  \u003ctbody\u003e\\n    \u003ctr\u003e\\n      \u003cth\u003e0\u003c/th\u003e\\n      \u003ctd\u003eMazda RX4\u003c/td\u003e\\n      \u003ctd\u003e21.0\u003c/td\u003e\\n      \u003ctd\u003e6\u003c/td\u003e\\n      \u003ctd\u003e160.0\u003c/td\u003e\\n      \u003ctd\u003e110\u003c/td\u003e\\n      \u003ctd\u003e3.90\u003c/td\u003e\\n      \u003ctd\u003e2.620\u003c/td\u003e\\n      \u003ctd\u003e16.46\u003c/td\u003e\\n      \u003ctd\u003e0\u003c/td\u003e\\n      \u003ctd\u003e1\u003c/td\u003e\\n      \u003ctd\u003e4\u003c/td\u003e\\n      \u003ctd\u003e4\u003c/td\u003e\\n    \u003c/tr\u003e\\n    \u003ctr\u003e\\n      \u003cth\u003e1\u003c/th\u003e\\n      \u003ctd\u003eMazda RX4 Wag\u003c/td\u003e\\n      \u003ctd\u003e21.0\u003c/td\u003e\\n      \u003ctd\u003e6\u003c/td\u003e\\n      \u003ctd\u003e160.0\u003c/td\u003e\\n      \u003ctd\u003e110\u003c/td\u003e\\n      \u003ctd\u003e3.90\u003c/td\u003e\\n      \u003ctd\u003e2.875\u003c/td\u003e\\n      \u003ctd\u003e17.02\u003c/td\u003e\\n      \u003ctd\u003e0\u003c/td\u003e\\n      \u003ctd\u003e1\u003c/td\u003e\\n      \u003ctd\u003e4\u003c/td\u003e\\n      \u003ctd\u003e4\u003c/td\u003e\\n    \u003c/tr\u003e\\n    \u003ctr\u003e\\n      \u003cth\u003e2\u003c/th\u003e\\n      \u003ctd\u003eDatsun 710\u003c/td\u003e\\n      \u003ctd\u003e22.8\u003c/td\u003e\\n      \u003ctd\u003e4\u003c/td\u003e\\n      \u003ctd\u003e108.0\u003c/td\u003e\\n      \u003ctd\u003e93\u003c/td\u003e\\n      \u003ctd\u003e3.85\u003c/td\u003e\\n      \u003ctd\u003e2.320\u003c/td\u003e\\n      \u003ctd\u003e18.61\u003c/td\u003e\\n      \u003ctd\u003e1\u003c/td\u003e\\n      \u003ctd\u003e1\u003c/td\u003e\\n      \u003ctd\u003e4\u003c/td\u003e\\n      \u003ctd\u003e1\u003c/td\u003e\\n    \u003c/tr\u003e\\n    \u003ctr\u003e\\n      \u003cth\u003e3\u003c/th\u003e\\n      \u003ctd\u003eHornet 4 Drive\u003c/td\u003e\\n      \u003ctd\u003e21.4\u003c/td\u003e\\n      \u003ctd\u003e6\u003c/td\u003e\\n      \u003ctd\u003e258.0\u003c/td\u003e\\n      \u003ctd\u003e110\u003c/td\u003e\\n      \u003ctd\u003e3.08\u003c/td\u003e\\n      \u003ctd\u003e3.215\u003c/td\u003e\\n      \u003ctd\u003e19.44\u003c/td\u003e\\n      \u003ctd\u003e1\u003c/td\u003e\\n      \u003ctd\u003e0\u003c/td\u003e\\n      \u003ctd\u003e3\u003c/td\u003e\\n      \u003ctd\u003e1\u003c/td\u003e\\n    \u003c/tr\u003e\\n    \u003ctr\u003e\\n      \u003cth\u003e4\u003c/th\u003e\\n      \u003ctd\u003eHornet Sportabout\u003c/td\u003e\\n      \u003ctd\u003e18.7\u003c/td\u003e\\n      \u003ctd\u003e8\u003c/td\u003e\\n      \u003ctd\u003e360.0\u003c/td\u003e\\n      \u003ctd\u003e175\u003c/td\u003e\\n      \u003ctd\u003e3.15\u003c/td\u003e\\n      \u003ctd\u003e3.440\u003c/td\u003e\\n      \u003ctd\u003e17.02\u003c/td\u003e\\n      \u003ctd\u003e0\u003c/td\u003e\\n      \u003ctd\u003e0\u003c/td\u003e\\n      \u003ctd\u003e3\u003c/td\u003e\\n      \u003ctd\u003e2\u003c/td\u003e\\n    \u003c/tr\u003e\\n  \u003c/tbody\u003e\\n\u003c/table\u003e'\n\n```\n\nMy blog will render that html into a nice table! (After removing new line characters)\n\n\u003ctable border=\"1\" class=\"dataframe\"\u003e  \u003cthead\u003e    \u003ctr style=\"text-align: right;\"\u003e      \u003cth\u003eUnnamed: 0\u003c/th\u003e      \u003cth\u003empg\u003c/th\u003e      \u003cth\u003ecyl\u003c/th\u003e      \u003cth\u003edisp\u003c/th\u003e      \u003cth\u003ehp\u003c/th\u003e      \u003cth\u003edrat\u003c/th\u003e      \u003cth\u003ewt\u003c/th\u003e      \u003cth\u003eqsec\u003c/th\u003e      \u003cth\u003evs\u003c/th\u003e      \u003cth\u003eam\u003c/th\u003e      \u003cth\u003egear\u003c/th\u003e      \u003cth\u003ecarb\u003c/th\u003e    \u003c/tr\u003e  \u003c/thead\u003e  \u003ctbody\u003e    \u003ctr\u003e      \u003ctd\u003eMazda RX4\u003c/td\u003e      \u003ctd\u003e21.0\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e160.0\u003c/td\u003e      \u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.90\u003c/td\u003e      \u003ctd\u003e2.620\u003c/td\u003e      \u003ctd\u003e16.46\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e      \u003ctd\u003eMazda RX4 Wag\u003c/td\u003e      \u003ctd\u003e21.0\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e160.0\u003c/td\u003e      \u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.90\u003c/td\u003e      \u003ctd\u003e2.875\u003c/td\u003e      \u003ctd\u003e17.02\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e      \u003ctd\u003eDatsun 710\u003c/td\u003e      \u003ctd\u003e22.8\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e108.0\u003c/td\u003e      \u003ctd\u003e93\u003c/td\u003e      \u003ctd\u003e3.85\u003c/td\u003e      \u003ctd\u003e2.320\u003c/td\u003e      \u003ctd\u003e18.61\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e4\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e      \u003ctd\u003eHornet 4 Drive\u003c/td\u003e      \u003ctd\u003e21.4\u003c/td\u003e      \u003ctd\u003e6\u003c/td\u003e      \u003ctd\u003e258.0\u003c/td\u003e      \u003ctd\u003e110\u003c/td\u003e      \u003ctd\u003e3.08\u003c/td\u003e      \u003ctd\u003e3.215\u003c/td\u003e      \u003ctd\u003e19.44\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e3\u003c/td\u003e      \u003ctd\u003e1\u003c/td\u003e    \u003c/tr\u003e    \u003ctr\u003e      \u003ctd\u003eHornet Sportabout\u003c/td\u003e      \u003ctd\u003e18.7\u003c/td\u003e      \u003ctd\u003e8\u003c/td\u003e      \u003ctd\u003e360.0\u003c/td\u003e      \u003ctd\u003e175\u003c/td\u003e      \u003ctd\u003e3.15\u003c/td\u003e      \u003ctd\u003e3.440\u003c/td\u003e      \u003ctd\u003e17.02\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e0\u003c/td\u003e      \u003ctd\u003e3\u003c/td\u003e      \u003ctd\u003e2\u003c/td\u003e    \u003c/tr\u003e  \u003c/tbody\u003e\u003c/table\u003e\n",
      "summary": "s are pretty sweet data structures in Python.",
      "date_published": "2022-05-07T00:00:00Z",
      "date_modified": "2022-05-07T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/git-bisect/",
      "url": "https://pype.dev/git-bisect/",
      "title": "Git-Bisect",
      "content_html": "\u003cp\u003eI try to commit a lot, and I also try to write useful tests appropriate for the scope of work I\u0026rsquo;m focusing on, but sometimes I drop the ball\u0026hellip;\u003c/p\u003e\n\u003cp\u003eWhether by laziness, ignorance, or accepted tech debt I don\u0026rsquo;t always code perfectly and recently I was dozens of commits into a new feature before realizing I broke something along the way that none of my tests caught\u0026hellip;\u003c/p\u003e\n\u003cp\u003eBefore today I would\u0026rsquo;ve manually reviewed every commit to see if something obvious slipped by me (talk about a time suck 😩)\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThere must be a better way\u003c/strong\u003e\u003c/p\u003e\n\u003ch1 id=\"bisect\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eBisect?\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003egit bisect\u003c/code\u003e is the magic sauce for this exact problem\u0026hellip;\u003c/p\u003e\n\u003cp\u003eYou essentially create a range of commits to consider and let \u003ccode\u003egit bisect\u003c/code\u003e guide you through them in a manner akin to Newton\u0026rsquo;s method for finding the root of a continuous function.\u003c/p\u003e\n\u003ch1 id=\"how-to-do-it\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eHow to do it?\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eStart with \u003ccode\u003egit bisect start\u003c/code\u003e and then choose the first \u003ccode\u003egood\u003c/code\u003e commit (ie. a commit you know the bug isn\u0026rsquo;t present in)\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox   bisect-post   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ git bisect start\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox   bisect-post \u003cspan class=\"o\"\u003e(\u003c/span\u003eBISECTING\u003cspan class=\"o\"\u003e)\u003c/span\u003e   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ git bisect good 655332b\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebisect-post  HEAD         main         ORIG_HEAD\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e5b31e1e  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD\u003cspan class=\"o\"\u003e]\u003c/span\u003e    add successful print \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e52\u003c/span\u003e seconds ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e308247b  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD^\u003cspan class=\"o\"\u003e]\u003c/span\u003e   init another loop \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e77\u003c/span\u003e seconds ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e4555c59  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD^^\u003cspan class=\"o\"\u003e]\u003c/span\u003e  introduce bug \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e9cf6d55  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~3\u003cspan class=\"o\"\u003e]\u003c/span\u003e  add successful loop \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e3\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebcb41c3  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~4\u003cspan class=\"o\"\u003e]\u003c/span\u003e  change x to \u003cspan class=\"m\"\u003e10\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e4\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e3c34aac  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~5\u003cspan class=\"o\"\u003e]\u003c/span\u003e  init x to \u003cspan class=\"m\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e4\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e12e53bd  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~6\u003cspan class=\"o\"\u003e]\u003c/span\u003e  print cwd \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e4\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e655332b  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~7\u003cspan class=\"o\"\u003e]\u003c/span\u003e  add example.py \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e10\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# \u0026lt;- I want to start at this commit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e59e0048  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~8\u003cspan class=\"o\"\u003e]\u003c/span\u003e  gitignore \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e23\u003c/span\u003e hours ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003efb9e1fb  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~9\u003cspan class=\"o\"\u003e]\u003c/span\u003e  add reqs \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e23\u003c/span\u003e hours ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox   bisect-post \u003cspan class=\"o\"\u003e(\u003c/span\u003eBISECTING\u003cspan class=\"o\"\u003e)\u003c/span\u003e   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ git bisect bad 5b31e1e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebisect-post                                                ORIG_HEAD\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eHEAD                                                       refs/bisect/good-655332b6c384934c2c00c3d4aba3011ccc1e5b57\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emain\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e5b31e1e  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD\u003cspan class=\"o\"\u003e]\u003c/span\u003e    add successful print \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e5\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# \u0026lt;- I start here with the \u0026#34;bad\u0026#34; commit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e308247b  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD^\u003cspan class=\"o\"\u003e]\u003c/span\u003e   init another loop \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e6\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e4555c59  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD^^\u003cspan class=\"o\"\u003e]\u003c/span\u003e  introduce bug \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e6\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e9cf6d55  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~3\u003cspan class=\"o\"\u003e]\u003c/span\u003e  add successful loop \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e7\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebcb41c3  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~4\u003cspan class=\"o\"\u003e]\u003c/span\u003e  change x to \u003cspan class=\"m\"\u003e10\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e8\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e3c34aac  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~5\u003cspan class=\"o\"\u003e]\u003c/span\u003e  init x to \u003cspan class=\"m\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e9\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e12e53bd  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~6\u003cspan class=\"o\"\u003e]\u003c/span\u003e  print cwd \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e9\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e655332b  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~7\u003cspan class=\"o\"\u003e]\u003c/span\u003e  add example.py \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e14\u003c/span\u003e minutes ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e59e0048  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~8\u003cspan class=\"o\"\u003e]\u003c/span\u003e  gitignore \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e23\u003c/span\u003e hours ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003efb9e1fb  -- \u003cspan class=\"o\"\u003e[\u003c/span\u003eHEAD~9\u003cspan class=\"o\"\u003e]\u003c/span\u003e  add reqs \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e23\u003c/span\u003e hours ago\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAfter starting bisect with a \u0026ldquo;good\u0026rdquo; start commit and a \u0026ldquo;bad\u0026rdquo; ending commit we can let git to it\u0026rsquo;s thing!\u003c/p\u003e\n\u003cp\u003eGit checksout a commit somewhere about halfway between the good and bad commit so you can see if your bug is there or not.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox   bisect-post \u003cspan class=\"o\"\u003e(\u003c/span\u003eBISECTING\u003cspan class=\"o\"\u003e)\u003c/span\u003e   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ git bisect bad 5b31e1e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eBisecting: \u003cspan class=\"m\"\u003e3\u003c/span\u003e revisions left to \u003cspan class=\"nb\"\u003etest\u003c/span\u003e after this \u003cspan class=\"o\"\u003e(\u003c/span\u003eroughly \u003cspan class=\"m\"\u003e2\u003c/span\u003e steps\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003ebcb41c3854e343eade85353683f2c1c4ddde4e04\u003cspan class=\"o\"\u003e]\u003c/span\u003e change x to \u003cspan class=\"m\"\u003e10\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox   HEAD \u003cspan class=\"o\"\u003e(\u003c/span\u003ebcb41c38\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003eBISECTING\u003cspan class=\"o\"\u003e)\u003c/span\u003e   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn my example here I have a python script with some loops and print statements - they aren\u0026rsquo;t really relevant, I just wanted an easy to follow git history.\u003c/p\u003e\n\u003cp\u003eSo I check to see if the bug is present or not either by running/writing tests or replicating the bug somehow.\u003c/p\u003e\n\u003cp\u003eIn this session commit \u003ccode\u003ebcb41c38\u003c/code\u003e is actually just fine, so I do \u003ccode\u003egit bisect good\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox   HEAD \u003cspan class=\"o\"\u003e(\u003c/span\u003ebcb41c38\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003eBISECTING\u003cspan class=\"o\"\u003e)\u003c/span\u003e   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ git bisect good\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eBisecting: \u003cspan class=\"m\"\u003e1\u003c/span\u003e revision left to \u003cspan class=\"nb\"\u003etest\u003c/span\u003e after this \u003cspan class=\"o\"\u003e(\u003c/span\u003eroughly \u003cspan class=\"m\"\u003e1\u003c/span\u003e step\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e4555c5979268dff6c475365fdc5ce1d4a12bd820\u003cspan class=\"o\"\u003e]\u003c/span\u003e introduce bug\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd we see that git moves on to checkout another commit\u0026hellip;\u003c/p\u003e\n\u003cp\u003eIn this case the next commit is the one where I introduced a bug\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003egit bisect bad\u003c/code\u003e then gives me:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox   HEAD \u003cspan class=\"o\"\u003e(\u003c/span\u003e4555c597\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003eBISECTING\u003cspan class=\"o\"\u003e)\u003c/span\u003e   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ git bisect bad\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eBisecting: \u003cspan class=\"m\"\u003e0\u003c/span\u003e revisions left to \u003cspan class=\"nb\"\u003etest\u003c/span\u003e after this \u003cspan class=\"o\"\u003e(\u003c/span\u003eroughly \u003cspan class=\"m\"\u003e0\u003c/span\u003e steps\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e9cf6d55301560c51e2f55404d0d80b1f1e22a33d\u003cspan class=\"o\"\u003e]\u003c/span\u003e add successful loop\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAt \u003ccode\u003e4555c597\u003c/code\u003e the script works as expected so one more \u003ccode\u003egit bisect good\u003c/code\u003e yields\u0026hellip;\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox   HEAD \u003cspan class=\"o\"\u003e(\u003c/span\u003e9cf6d553\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003eBISECTING\u003cspan class=\"o\"\u003e)\u003c/span\u003e   ×1 via   v3.8.11\u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ git bisect good\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e4555c5979268dff6c475365fdc5ce1d4a12bd820 is the first bad commit\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecommit 4555c5979268dff6c475365fdc5ce1d4a12bd820\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eAuthor: \u003cspan class=\"c1\"\u003e########################### \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eDate:   Tue May \u003cspan class=\"m\"\u003e3\u003c/span\u003e 09:00:00 \u003cspan class=\"m\"\u003e2022\u003c/span\u003e -0500\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    introduce bug\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e example.py \u003cspan class=\"p\"\u003e|\u003c/span\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e +-\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e file changed, \u003cspan class=\"m\"\u003e1\u003c/span\u003e insertion\u003cspan class=\"o\"\u003e(\u003c/span\u003e+\u003cspan class=\"o\"\u003e)\u003c/span\u003e, \u003cspan class=\"m\"\u003e1\u003c/span\u003e deletion\u003cspan class=\"o\"\u003e(\u003c/span\u003e-\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"what-happened\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhat happened?\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eGit sliced up a range of commits based on me saying of the next one was good or bad and localized the commit that introduced a bug into my workflow!\u003c/p\u003e\n\u003cp\u003eI didn\u0026rsquo;t have to manually review commits, click through logs, etc\u0026hellip; I just let git checkout relevant commits and I ran whatever was appropriate for reproducing the bug to learn when it was comitted!\u003c/p\u003e\n",
      "content_text": "\nI try to commit a lot, and I also try to write useful tests appropriate for the scope of work I'm focusing on, but sometimes I drop the ball...\n\nWhether by laziness, ignorance, or accepted tech debt I don't always code perfectly and recently I was dozens of commits into a new feature before realizing I broke something along the way that none of my tests caught...\n\nBefore today I would've manually reviewed every commit to see if something obvious slipped by me (talk about a time suck 😩)\n\n__There must be a better way__\n\n# Bisect?\n\n`git bisect` is the magic sauce for this exact problem...\n\nYou essentially create a range of commits to consider and let `git bisect` guide you through them in a manner akin to Newton's method for finding the root of a continuous function.\n\n# How to do it?\n\nStart with `git bisect start` and then choose the first `good` commit (ie. a commit you know the bug isn't present in)\n\n```bash\n\nsandbox   bisect-post   ×1 via   v3.8.11(sandbox)  on  (us-east-1)\n❯ git bisect start\n\nsandbox   bisect-post (BISECTING)   ×1 via   v3.8.11(sandbox)  on  (us-east-1)\n❯ git bisect good 655332b\nbisect-post  HEAD         main         ORIG_HEAD\n5b31e1e  -- [HEAD]    add successful print (52 seconds ago)\n308247b  -- [HEAD^]   init another loop (77 seconds ago)\n4555c59  -- [HEAD^^]  introduce bug (2 minutes ago)\n9cf6d55  -- [HEAD~3]  add successful loop (3 minutes ago)\nbcb41c3  -- [HEAD~4]  change x to 10 (4 minutes ago)\n3c34aac  -- [HEAD~5]  init x to 1 (4 minutes ago)\n12e53bd  -- [HEAD~6]  print cwd (4 minutes ago)\n655332b  -- [HEAD~7]  add example.py (10 minutes ago)  # \u003c- I want to start at this commit\n59e0048  -- [HEAD~8]  gitignore (23 hours ago)\nfb9e1fb  -- [HEAD~9]  add reqs (23 hours ago)\n\n```\n\n```bash\n\nsandbox   bisect-post (BISECTING)   ×1 via   v3.8.11(sandbox)  on  (us-east-1)\n❯ git bisect bad 5b31e1e\nbisect-post                                                ORIG_HEAD\nHEAD                                                       refs/bisect/good-655332b6c384934c2c00c3d4aba3011ccc1e5b57\nmain\n5b31e1e  -- [HEAD]    add successful print (5 minutes ago)  # \u003c- I start here with the \"bad\" commit\n308247b  -- [HEAD^]   init another loop (6 minutes ago)\n4555c59  -- [HEAD^^]  introduce bug (6 minutes ago)\n9cf6d55  -- [HEAD~3]  add successful loop (7 minutes ago)\nbcb41c3  -- [HEAD~4]  change x to 10 (8 minutes ago)\n3c34aac  -- [HEAD~5]  init x to 1 (9 minutes ago)\n12e53bd  -- [HEAD~6]  print cwd (9 minutes ago)\n655332b  -- [HEAD~7]  add example.py (14 minutes ago)\n59e0048  -- [HEAD~8]  gitignore (23 hours ago)\nfb9e1fb  -- [HEAD~9]  add reqs (23 hours ago)\n\n```\n\nAfter starting bisect with a \"good\" start commit and a \"bad\" ending commit we can let git to it's thing!\n\nGit checksout a commit somewhere about halfway between the good and bad commit so you can see if your bug is there or not.\n\n```bash\n\nsandbox   bisect-post (BISECTING)   ×1 via   v3.8.11(sandbox)  on  (us-east-1)\n❯ git bisect bad 5b31e1e\nBisecting: 3 revisions left to test after this (roughly 2 steps)\n[bcb41c3854e343eade85353683f2c1c4ddde4e04] change x to 10\n\nsandbox   HEAD (bcb41c38) (BISECTING)   ×1 via   v3.8.11(sandbox)  on  (us-east-1)\n❯\n```\n\nIn my example here I have a python script with some loops and print statements - they aren't really relevant, I just wanted an easy to follow git history.\n\nSo I check to see if the bug is present or not either by running/writing tests or replicating the bug somehow.\n\nIn this session commit `bcb41c38` is actually just fine, so I do `git bisect good`\n\n```bash\n\nsandbox   HEAD (bcb41c38) (BISECTING)   ×1 via   v3.8.11(sandbox)  on  (us-east-1)\n❯ git bisect good\nBisecting: 1 revision left to test after this (roughly 1 step)\n[4555c5979268dff6c475365fdc5ce1d4a12bd820] introduce bug\n\n```\n\nAnd we see that git moves on to checkout another commit...\n\nIn this case the next commit is the one where I introduced a bug\n\n`git bisect bad` then gives me:\n\n```bash\n\nsandbox   HEAD (4555c597) (BISECTING)   ×1 via   v3.8.11(sandbox)  on  (us-east-1)\n❯ git bisect bad\nBisecting: 0 revisions left to test after this (roughly 0 steps)\n[9cf6d55301560c51e2f55404d0d80b1f1e22a33d] add successful loop\n```\n\nAt `4555c597` the script works as expected so one more `git bisect good` yields...\n\n```bash\nsandbox   HEAD (9cf6d553) (BISECTING)   ×1 via   v3.8.11(sandbox)  on  (us-east-1)\n❯ git bisect good\n4555c5979268dff6c475365fdc5ce1d4a12bd820 is the first bad commit\ncommit 4555c5979268dff6c475365fdc5ce1d4a12bd820\nAuthor: ########################### \nDate:   Tue May 3 09:00:00 2022 -0500\n\n    introduce bug\n\n example.py | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\n\n```\n\n# What happened?\n\nGit sliced up a range of commits based on me saying of the next one was good or bad and localized the commit that introduced a bug into my workflow!\n\nI didn't have to manually review commits, click through logs, etc... I just let git checkout relevant commits and I ran whatever was appropriate for reproducing the bug to learn when it was comitted!\n",
      "summary": "I try to commit a lot, and I also try to write useful tests appropriate for the scope of work I'm focusing on, but sometimes I drop the ball...",
      "date_published": "2022-05-03T00:00:00Z",
      "date_modified": "2022-05-03T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "git",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/pandas-string-contains/",
      "url": "https://pype.dev/pandas-string-contains/",
      "title": "Pandas-String-Contains",
      "content_html": "\u003ch1 id=\"tl-dr\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eTL;DR\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003epandas.Series.str.contains\u003c/code\u003e accepts regular expressions and this is turned on by \u003cstrong\u003edefault\u003c/strong\u003e!\u003c/p\u003e\n\u003ch1 id=\"use-case\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eUse case\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eWe often need to filter pandas DataFrames based on several string values in a Series.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eNotice that sweet pyflyby import 😁!\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003emain\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epd\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDataFrame\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;A\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;string1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;string2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;string3\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ePYFLYBY\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003epandas\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"nn\"\u003epd\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003emain\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"n\"\u003eA\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e  \u003cspan class=\"n\"\u003estring1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e  \u003cspan class=\"n\"\u003estring2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e  \u003cspan class=\"n\"\u003estring3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003emain\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eA\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econtains\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eA\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econtains\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;2\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"n\"\u003eA\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e  \u003cspan class=\"n\"\u003estring1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e  \u003cspan class=\"n\"\u003estring2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd this isn\u0026rsquo;t the worst thing in the world, especially for such a tiny example\u0026hellip;\u003c/p\u003e\n\u003cp\u003eBut what if we had dozens or more values to filter on?\u003c/p\u003e\n\u003cp\u003eThen it looks so much nicer to create an iterable of the values we want to filter on and join them with an apropriate regex operator (in this case \u003ccode\u003e|\u003c/code\u003e for \u003cem\u003einclusive or\u003c/em\u003e)\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003emain\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003evals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# iterable with whatever is appropriate for your use case\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e \u003cspan class=\"err\"\u003e\u003c/span\u003e  \u003cspan class=\"n\"\u003emain\u003c/span\u003e \u003cspan class=\"n\"\u003evia\u003c/span\u003e \u003cspan class=\"mf\"\u003e3.8.11\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esandbox\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eipython\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eA\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econtains\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;|\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evals\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003eregex\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"kc\"\u003eTrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"n\"\u003eA\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e  \u003cspan class=\"n\"\u003estring1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e  \u003cspan class=\"n\"\u003estring2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"fin\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eFin\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eThis is a super nice and concise way to do the \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of filtering my team does on a daily basis!\u003c/p\u003e\n",
      "content_text": "\n# TL;DR \n\n`pandas.Series.str.contains` accepts regular expressions and this is turned on by __default__!\n\n# Use case\n\nWe often need to filter pandas DataFrames based on several string values in a Series.\n\n\u003e Notice that sweet pyflyby import 😁!\n\n```python\nsandbox   main via 3.8.11(sandbox) ipython\n❯ df = pd.DataFrame({\"A\": [\"string1\", \"string2\", \"string3\"]})\n[PYFLYBY] import pandas as pd\n\nsandbox   main via 3.8.11(sandbox) ipython\n❯ df\n\n         A\n0  string1\n1  string2\n2  string3\n\nsandbox   main via 3.8.11(sandbox) ipython\n❯ df[df.A.str.contains('1') | df.A.str.contains('2')]\n\n         A\n0  string1\n1  string2\n\n```\n\nAnd this isn't the worst thing in the world, especially for such a tiny example...\n\nBut what if we had dozens or more values to filter on?\n\nThen it looks so much nicer to create an iterable of the values we want to filter on and join them with an apropriate regex operator (in this case `|` for _inclusive or_)\n\n```python\n\nsandbox   main via 3.8.11(sandbox) ipython\n❯ vals = [\"1\", \"2\"]  # iterable with whatever is appropriate for your use case\n\nsandbox   main via 3.8.11(sandbox) ipython\n❯ df[df.A.str.contains(\"|\".join(vals), regex=True)]\n\n         A\n0  string1\n1  string2\n\n```\n\n# Fin\n\nThis is a super nice and concise way to do the kind of filtering my team does on a daily basis!\n",
      "summary": "accepts regular expressions and this is turned on by __!",
      "date_published": "2022-05-02T00:00:00Z",
      "date_modified": "2022-05-02T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/unpack-anywhere-with-star/",
      "url": "https://pype.dev/unpack-anywhere-with-star/",
      "title": "Unpack-Anywhere-With-Star",
      "content_html": "\u003cp\u003eUnpacking iterables in python with \u003ccode\u003e*\u003c/code\u003e is a pretty handy trick for writing code that is just a tiny bit more pythonic than not.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003earr\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eTuple\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eUnion\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;b\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;c\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003earr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;b\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;c\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# the * unpacks the tuple into the individual elements\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003earr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;b\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;c\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ez\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003ealphas\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003earr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# x = 1, y = 2, z = 3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# alphas = [ \u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39; ]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eBut \u003ca href=\"https://twitter.com/nedbat\"\u003e@Ned Batchelder\u003c/a\u003e showed me via Twitter than you can arbitrarily unpack arguments based on position - it doesn\u0026rsquo;t have to be done at the beginning or the end!\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003emixed\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ealpha\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003earr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# x = 1, y = 2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# mixed = [3, \u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# alpha = \u0026#39;c\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI\u0026rsquo;m not entirely sure when I\u0026rsquo;ll need this but it definitley shows me another example of how flexible python is!\u003c/p\u003e\n",
      "content_text": "\n\nUnpacking iterables in python with `*` is a pretty handy trick for writing code that is just a tiny bit more pythonic than not.\n\n```python\narr: Tuple[Union[int, str]] = (1, 2, 3, 'a', 'b', 'c')\n\n\nprint(arr)\n\u003e\u003e\u003e (1, 2, 3, 'a', 'b', 'c')\n\n# the * unpacks the tuple into the individual elements\nprint(*arr)\n\u003e\u003e\u003e 1, 2, 3, 'a', 'b', 'c'\n\nx, y, z, *alphas = arr\n\n# x = 1, y = 2, z = 3\n# alphas = [ 'a', 'b', 'c' ]\n\n```\n\nBut [@Ned Batchelder](https://twitter.com/nedbat) showed me via Twitter than you can arbitrarily unpack arguments based on position - it doesn't have to be done at the beginning or the end!\n\n```python\nx, y, *mixed, alpha = arr\n\n# x = 1, y = 2\n# mixed = [3, 'a', 'b']\n# alpha = 'c'\n```\n\nI'm not entirely sure when I'll need this but it definitley shows me another example of how flexible python is!\n",
      "summary": "Unpacking iterables in python with is a pretty handy trick for writing code that is just a tiny bit more pythonic than not.",
      "date_published": "2022-04-24T00:00:00Z",
      "date_modified": "2022-04-24T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/htop/",
      "url": "https://pype.dev/htop/",
      "title": "Htop",
      "content_html": "\u003cp\u003e\u003ccode\u003ehtop\u003c/code\u003e is a common command line tool for seeing interactive output of your system resource utilization, running processes, etc.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve always been super confused about htop showing seemingly the same process several times though\u0026hellip;\u003c/p\u003e\n\u003ch2 id=\"the-fix\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe Fix…\u003c/span\u003e \u003ca href=\"#the-fix\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eJust hit \u003ccode\u003eH\u003c/code\u003e\u0026hellip;. makes the view a lot nicer 😀\u003c/p\u003e\n",
      "content_text": "\n`htop` is a common command line tool for seeing interactive output of your system resource utilization, running processes, etc.\n\nI've always been super confused about htop showing seemingly the same process several times though...\n\n## The Fix...\n\nJust hit `H`.... makes the view a lot nicer 😀\n",
      "summary": "is a common command line tool for seeing interactive output of your system resource utilization, running processes, etc.",
      "date_published": "2022-04-24T00:00:00Z",
      "date_modified": "2022-04-24T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/pipx/",
      "url": "https://pype.dev/pipx/",
      "title": "Pipx",
      "content_html": "\u003cp\u003e\u003ccode\u003epipx\u003c/code\u003e is a tool I\u0026rsquo;ve been using to solve a few problems of mine\u0026hellip;\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003epinning formatting tools like \u003ccode\u003eblack\u003c/code\u003e, \u003ccode\u003eflake8\u003c/code\u003e, \u003ccode\u003eisort\u003c/code\u003e, etc. to the same version for all my projects\u003c/li\u003e\n\u003cli\u003ekeeping virtual environments clean of things like \u003ccode\u003ecookiecutter\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003epython utilities I want system wide but not in the global environment, like \u003ccode\u003evisidata\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"what-is-it\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhat is it?\u003c/span\u003e \u003ca href=\"#what-is-it\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003epip\u003c/code\u003e itself is just a package manager like \u003ccode\u003ehomebrew\u003c/code\u003e, \u003ccode\u003eapt\u003c/code\u003e, etc. But it is tied to a python environment.\nIf you aren\u0026rsquo;t using a virtual environment then \u003ccode\u003epip\u003c/code\u003e will operate inside the global installation of python.\u003c/p\u003e\n\u003cp\u003eOperating within that environment has burned me several times and now I have a strict virtual environment usage policy.\u003c/p\u003e\n\u003cp\u003eBut there are still things I don\u0026rsquo;t want to have to put in every virtual environment - enter \u003ccode\u003epipx\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"what-s-it-do\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhat’s it do?\u003c/span\u003e \u003ca href=\"#what-s-it-do\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eWhen you \u003ccode\u003epipx install {package}\u003c/code\u003e a stand alone virtual environment gets created (by default in \u003ccode\u003e~/.local/pipx/venvs\u003c/code\u003e).\nTHen you can install extra dependencies with \u003ccode\u003epipx inject {package} {dependency}\u003c/code\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eex. After \u003ccode\u003epipx install visidata\u003c/code\u003e in order to open Excel files you need to \u003ccode\u003epipx inject visidata xlrd\u003c/code\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eIn the example with \u003ccode\u003evisidata\u003c/code\u003e, I can then use it anywhere, in any project, without re-installing with \u003ccode\u003epip\u003c/code\u003e in every env.\u003c/p\u003e\n\u003cp\u003eAlso for the formatting tools - I configure vim to run the \u003ccode\u003epipx\u003c/code\u003e versions of them on save - this way I don\u0026rsquo;t have to put them in every project\u0026rsquo;s virtual environment!\u003c/p\u003e\n\u003ch2 id=\"what-about-pip\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhat about pip?\u003c/span\u003e \u003ca href=\"#what-about-pip\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eSo obviously you can\u0026rsquo;t \u003ccode\u003epipx\u003c/code\u003e everything, nor do you want to.\nI see it as a safe and better alternative to global package installation.\u003c/p\u003e\n\u003cp\u003eHow can you then be sure that you never \u003ccode\u003epip install\u003c/code\u003e into the global env?\u003c/p\u003e\n\u003cp\u003eAdd \u003ccode\u003erequire-virtualenv = True\u003c/code\u003e to your \u003ccode\u003epip.conf\u003c/code\u003e and you\u0026rsquo;re good to go!\u003c/p\u003e\n\u003cp\u003eWith that set, if you try to \u003ccode\u003epip install pandas\u003c/code\u003e into the global env you\u0026rsquo;ll get a message like this:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e~ on  \u003cspan class=\"o\"\u003e(\u003c/span\u003eus-east-1\u003cspan class=\"o\"\u003e)\u003c/span\u003e  NO PYTHON VENV SET\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ pip install pandas\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eERROR: Could not find an activated virtualenv \u003cspan class=\"o\"\u003e(\u003c/span\u003erequired\u003cspan class=\"o\"\u003e)\u003c/span\u003e.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"end\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eEnd\u003c/span\u003e \u003ca href=\"#end\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eDisable your system \u003ccode\u003epip\u003c/code\u003e to keep your base python safe\u003c/li\u003e\n\u003cli\u003eUse \u003ccode\u003epipx\u003c/code\u003e for tools you want available everywhere or don\u0026rsquo;t have to need in a virtual environment!\u003c/li\u003e\n\u003c/ol\u003e\n",
      "content_text": "\n\n`pipx` is a tool I've been using to solve a few problems of mine...\n\n1. pinning formatting tools like `black`, `flake8`, `isort`, etc. to the same version for all my projects\n2. keeping virtual environments clean of things like `cookiecutter`\n3. python utilities I want system wide but not in the global environment, like `visidata`\n\n## What is it?\n\n`pip` itself is just a package manager like `homebrew`, `apt`, etc. But it is tied to a python environment.\nIf you aren't using a virtual environment then `pip` will operate inside the global installation of python.\n\nOperating within that environment has burned me several times and now I have a strict virtual environment usage policy.\n\nBut there are still things I don't want to have to put in every virtual environment - enter `pipx`\n\n## What's it do?\n\nWhen you `pipx install {package}` a stand alone virtual environment gets created (by default in `~/.local/pipx/venvs`).\nTHen you can install extra dependencies with `pipx inject {package} {dependency}`\n\n\u003e ex. After `pipx install visidata` in order to open Excel files you need to `pipx inject visidata xlrd`\n\nIn the example with `visidata`, I can then use it anywhere, in any project, without re-installing with `pip` in every env.\n\nAlso for the formatting tools - I configure vim to run the `pipx` versions of them on save - this way I don't have to put them in every project's virtual environment!\n\n## What about pip?\n\nSo obviously you can't `pipx` everything, nor do you want to. \nI see it as a safe and better alternative to global package installation.\n\nHow can you then be sure that you never `pip install` into the global env?\n\nAdd `require-virtualenv = True` to your `pip.conf` and you're good to go!\n\nWith that set, if you try to `pip install pandas` into the global env you'll get a message like this:\n\n\n```bash\n\n~ on  (us-east-1)  NO PYTHON VENV SET\n❯ pip install pandas\nERROR: Could not find an activated virtualenv (required).\n\n\n```\n\n## End\n\n1. Disable your system `pip` to keep your base python safe\n2. Use `pipx` for tools you want available everywhere or don't have to need in a virtual environment!\n",
      "summary": "is a tool I've been using to solve a few problems of mine...",
      "date_published": "2022-04-22T00:00:00Z",
      "date_modified": "2022-04-22T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "til",
        "python",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/fx-json/",
      "url": "https://pype.dev/fx-json/",
      "title": "Fx-Json",
      "content_html": "\u003cp\u003e\u003ca href=\"https://github.com/antonmedv/fx\"\u003efx\u003c/a\u003e is an interactaive JSON viewer for the terminal.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s a simple tool built with Charmcli\u0026rsquo;s Bubble Tea.\u003c/p\u003e\n\u003ch2 id=\"installation\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eInstallation\u003c/span\u003e \u003ca href=\"#installation\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe installation with go was broken for me - both via the link and direct from the repo.\nNow I\u0026rsquo;m not a gopher so I don\u0026rsquo;t really know how to fix that.\u003c/p\u003e\n\u003cp\u003eLuckily \u003ccode\u003enpm install fx\u003c/code\u003e also works and got me what I needed!\u003c/p\u003e\n\u003ch2 id=\"usage\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eUsage\u003c/span\u003e \u003ca href=\"#usage\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eUsage is simple\u0026hellip; \u003ccode\u003efx \u0026lt;json file\u0026gt;\u003c/code\u003e.\nThe Github has a few other ways such as \u003ccode\u003ecurl ... | fx\u003c/code\u003e etc.\u003c/p\u003e\n",
      "content_text": "\n[fx](https://github.com/antonmedv/fx) is an interactaive JSON viewer for the terminal.\n\nIt's a simple tool built with Charmcli's Bubble Tea.\n\n## Installation\n\nThe installation with go was broken for me - both via the link and direct from the repo.\nNow I'm not a gopher so I don't really know how to fix that.\n\nLuckily `npm install fx` also works and got me what I needed!\n\n## Usage\n\nUsage is simple... `fx \u003cjson file\u003e`.\nThe Github has a few other ways such as `curl ... | fx` etc.\n",
      "summary": "fx is an interactaive JSON viewer for the terminal.",
      "date_published": "2022-04-19T00:00:00Z",
      "date_modified": "2022-04-19T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/typeddict/",
      "url": "https://pype.dev/typeddict/",
      "title": "Typeddict",
      "content_html": "\u003cp\u003eType hinting has helped me write code almost as much, if not more, than unit testing.\u003c/p\u003e\n\u003cp\u003eOne thing I love is that with complete type hinting you get a lot more out of your LSP.\nTyping dictionaries can be tricky and I recently learned about \u003ccode\u003eTypedDict\u003c/code\u003e to do exactly what I needed!\u003c/p\u003e\n\u003ch2 id=\"the-problem\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe Problem\u003c/span\u003e \u003ca href=\"#the-problem\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIt might not be straight up obvious what the problem is, especially if you don\u0026rsquo;t utilize tools like \u003ccode\u003emypy\u003c/code\u003e or \u003ccode\u003eflake8\u003c/code\u003e in your development.\u003c/p\u003e\n\u003cp\u003eMy handy-dandy \u003ccode\u003envim-lsp\u003c/code\u003e gives me a lot of feedback when I\u0026rsquo;m coding and it\u0026rsquo;s immensely helpful.\u003c/p\u003e\n\u003cp\u003eSo with the LSP giving me constant feedback here\u0026rsquo;s the issue:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003etyping\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eDict\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eUnion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_dict\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eDict\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eUnion\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;key_1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;val_1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;key_2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ls_1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ls_2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_dict\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;key_2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epop\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWith the above script you\u0026rsquo;ll get an annoying warning about using \u003ccode\u003epop\u003c/code\u003e on \u003ccode\u003ekey_2\u003c/code\u003e.\u003c/p\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/typed-dict-warning.png\" alt=\"typeddict\" title=\"Warning in TypedDict\" /\u003e\n\u003ch2 id=\"the-solution\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe Solution\u003c/span\u003e \u003ca href=\"#the-solution\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eMaybe you can stomach getting yelled at by your LSP but I like complete silence if at all possible.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eTypedDict\u003c/code\u003e  was the saving grace.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003etyping\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eTypedDict\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eMyDict\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eTypedDict\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;MyDict\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;key_1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;key_2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e]})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_typed_dict\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eMyDict\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;key_1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;val_1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;key_2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ls_1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ls_2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_typed_dict\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;key_2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epop\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/typed-dict.png\" alt=\"typeddict\" title=\"A TypedDict\" /\u003e\n\u003cblockquote\u003e\n\u003cp\u003eI was able to import TypedDict from typing, mypy_extensions, and typing_extensions\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eWith \u003ccode\u003eTypedDict\u003c/code\u003e you define your custom type, match the first argument to \u003ccode\u003eTypedDict\u003c/code\u003e with the name of the variable (idk why), then type hint each key you expect in the dict!\nIt\u0026rsquo;s super easy and I think puts you into a position of being extremely explicit with your dictionary variables.\nThis isn\u0026rsquo;t always desired or appropriate but in most of my use cases it is.\u003c/p\u003e\n\u003ch2 id=\"rtfm\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eRTFM\u003c/span\u003e \u003ca href=\"#rtfm\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThere\u0026rsquo;s other implementation of \u003ccode\u003eTypedDict\u003c/code\u003e and while writing this I saw that most of the docs define a \u003ccode\u003eclass\u003c/code\u003e for the type like this:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003etyping\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eTypedDict\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMyDict\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTypedDict\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ekey_1\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ekey_2\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_dict\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eMyDict\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;key_1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;val_1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;key_2\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ls_1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ls_2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ca href=\"https://peps.python.org/pep-0589/\"\u003epep docs\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://mypy.readthedocs.io/en/latest/more_types.html#typeddict\"\u003emypy docs\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nType hinting has helped me write code almost as much, if not more, than unit testing.\n\nOne thing I love is that with complete type hinting you get a lot more out of your LSP.\nTyping dictionaries can be tricky and I recently learned about `TypedDict` to do exactly what I needed!\n\n\n## The Problem\n\nIt might not be straight up obvious what the problem is, especially if you don't utilize tools like `mypy` or `flake8` in your development.\n\nMy handy-dandy `nvim-lsp` gives me a lot of feedback when I'm coding and it's immensely helpful.\n\nSo with the LSP giving me constant feedback here's the issue:\n\n```python\nfrom typing import Dict, List, Union\n\nmy_dict: Dict[str, Union[List[str], str]] = {\n    \"key_1\": \"val_1\",\n    \"key_2\": [\"ls_1\", \"ls_2\"],\n}\n\nmy_dict[\"key_2\"].pop()\n```\n\nWith the above script you'll get an annoying warning about using `pop` on `key_2`.\n\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/typed-dict-warning.png\" alt=\"typeddict\" title=\"Warning in TypedDict\" /\u003e\n\n\n## The Solution\n\nMaybe you can stomach getting yelled at by your LSP but I like complete silence if at all possible.\n\n`TypedDict`  was the saving grace.\n\n```python\nfrom typing import TypedDict\n\nMyDict = TypedDict(\"MyDict\", {\"key_1\": str, \"key_2\": List[str]})\n\nmy_typed_dict: MyDict = {\n    \"key_1\": \"val_1\",\n    \"key_2\": [\"ls_1\", \"ls_2\"],\n}\n\n\nmy_typed_dict[\"key_2\"].pop()\n```\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/typed-dict.png\" alt=\"typeddict\" title=\"A TypedDict\" /\u003e\n\n\u003e I was able to import TypedDict from typing, mypy_extensions, and typing_extensions\n\nWith `TypedDict` you define your custom type, match the first argument to `TypedDict` with the name of the variable (idk why), then type hint each key you expect in the dict!\nIt's super easy and I think puts you into a position of being extremely explicit with your dictionary variables. \nThis isn't always desired or appropriate but in most of my use cases it is.\n\n## RTFM\n\nThere's other implementation of `TypedDict` and while writing this I saw that most of the docs define a `class` for the type like this:\n\n```python\nfrom typing import TypedDict\nclass MyDict(TypedDict):\n    key_1: str\n    key_2: List[str]\n\nmy_dict : MyDict = {'key_1': 'val_1', 'key_2': [\"ls_1\", \"ls_2\"]}\n\n```\n\n[pep docs](https://peps.python.org/pep-0589/)\n\n[mypy docs](https://mypy.readthedocs.io/en/latest/more_types.html#typeddict)\n",
      "summary": "Type hinting has helped me write code almost as much, if not more, than unit testing.",
      "date_published": "2022-04-15T00:00:00Z",
      "date_modified": "2022-04-15T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "til",
        "python",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/file-length/",
      "url": "https://pype.dev/file-length/",
      "title": "File-Length",
      "content_html": "\u003cp\u003eI have a specific need for counting the number of lines in a file quickly.\nAt work we use S3 for data storage during our Kedro pipeline development, and in the development process we may end up orphaning several datasets.\nIn order to keep our workspace clean I have a short utility that compares the datasets in a Kedro DataCatalog with the files in the relevant S3 location.\u003c/p\u003e\n\u003cp\u003eTo get that list I run an internal tool like this:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ekedro our-liter \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep s3 \u0026gt;\u0026gt; orphaned_datasets.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis simply parses our internal linter for the lines releated to my s3 linter utility and pipes those lines to a file.\u003c/p\u003e\n\u003cp\u003eTo get a quick idea of how out of wack a pipeline is I could open the text file in vim, git it with the \u003ccode\u003eG\u003c/code\u003e and see what line number I\u0026rsquo;m on but I\u0026rsquo;m way too lazy for that\u0026hellip;\u003c/p\u003e\n\u003ch2 id=\"awk\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eAWK\u003c/span\u003e \u003ca href=\"#awk\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eawk 'END {print NR}' orphaned_datasets.txt\u003c/code\u003e gives me the number of lines and I can alias this to whatever feels appropriate in my \u003ccode\u003ezshrc\u003c/code\u003e!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ebuilt-ins for the win!\u003c/strong\u003e\u003c/p\u003e\n",
      "content_text": "\nI have a specific need for counting the number of lines in a file quickly.\nAt work we use S3 for data storage during our Kedro pipeline development, and in the development process we may end up orphaning several datasets.\nIn order to keep our workspace clean I have a short utility that compares the datasets in a Kedro DataCatalog with the files in the relevant S3 location.\n\nTo get that list I run an internal tool like this:\n\n```bash\nkedro our-liter | grep s3 \u003e\u003e orphaned_datasets.txt\n```\n\nThis simply parses our internal linter for the lines releated to my s3 linter utility and pipes those lines to a file.\n\nTo get a quick idea of how out of wack a pipeline is I could open the text file in vim, git it with the `G` and see what line number I'm on but I'm way too lazy for that...\n\n## AWK\n\n`awk 'END {print NR}' orphaned_datasets.txt` gives me the number of lines and I can alias this to whatever feels appropriate in my `zshrc`!\n\n__built-ins for the win!__\n",
      "summary": "I have a specific need for counting the number of lines in a file quickly. At work we use S3 for data storage during our Kedro pipeline development, and in...",
      "date_published": "2022-04-04T00:00:00Z",
      "date_modified": "2022-04-04T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/vim-spell-check/",
      "url": "https://pype.dev/vim-spell-check/",
      "title": "Vim-Spell-Check",
      "content_html": "\u003cp\u003e\u003cstrong\u003eDid you know you can spell check in Vim?!\u003c/strong\u003e\u003c/p\u003e\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n   \u003chead\u003e\n      \u003ctitle\u003eVim Spell check\u003c/title\u003e\n   \u003c/head\u003e\n   \u003cbody\u003e\n      \u003ch3 id=\"without\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWithout...\u003c/span\u003e \u003ca href=\"#without\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h3\u003e\n      \u003cp\u003eHere is a missspelled word.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e  \u0026lt;h3\u0026gt;With!\u0026lt;/h3\u0026gt;\n  \u0026lt;p\u0026gt;Here is a \u0026lt;u\u0026gt;missspelled\u0026lt;/u\u0026gt; word.\u0026lt;/p\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n   \u003c/body\u003e\n\u003c/html\u003e\n\u003ch2 id=\"what-is-this-magic\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhat is this magic???\u003c/span\u003e \u003ca href=\"#what-is-this-magic\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eset: spell spelllang=en_us\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"custom-words\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eCustom words?\u003c/span\u003e \u003ca href=\"#custom-words\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eSometimes there\u0026rsquo;s things that are words to you but not the default spell checker\u0026hellip;\u003c/p\u003e\n\u003cp\u003eCommon example: package names!\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eplotly\u003c/code\u003e, \u003ccode\u003estreamlit\u003c/code\u003e, \u003ccode\u003epsutil\u003c/code\u003e, etc etc\u0026hellip;\u003c/p\u003e\n\u003cp\u003eYou can easily add these to your vim config by hitting \u003ccode\u003ezw\u003c/code\u003e ontop of the word!\u003c/p\u003e\n",
      "content_text": "\n__Did you know you can spell check in Vim?!__\n\n\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n   \u003chead\u003e\n      \u003ctitle\u003eVim Spell check\u003c/title\u003e\n   \u003c/head\u003e\n\n   \u003cbody\u003e\n      \u003ch3\u003eWithout...\u003c/h3\u003e\n      \u003cp\u003eHere is a missspelled word.\u003c/p\u003e\n\n      \u003ch3\u003eWith!\u003c/h3\u003e\n      \u003cp\u003eHere is a \u003cu\u003emissspelled\u003c/u\u003e word.\u003c/p\u003e\n\n   \u003c/body\u003e\n\u003c/html\u003e\n\n## What is this magic???\n\n`set: spell spelllang=en_us`\n\n\n## Custom words?\n\nSometimes there's things that are words to you but not the default spell checker...\n\nCommon example: package names!\n\n`plotly`, `streamlit`, `psutil`, etc etc...\n\n\nYou can easily add these to your vim config by hitting `zw` ontop of the word!\n",
      "summary": "__",
      "date_published": "2022-04-01T00:00:00Z",
      "date_modified": "2022-04-01T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vim",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/deques/",
      "url": "https://pype.dev/deques/",
      "title": "Deques",
      "content_html": "\u003cp\u003eI am working on a project to create a small system monitoring dashboard using the python \u003ccode\u003epsutil\u003c/code\u003e library.\u003c/p\u003e\n\u003cp\u003eThe repo is \u003ca href=\"https://github.com/nicpayne713/not-netdata\"\u003ehere\u003c/a\u003e (if you want actual system monitoring please use \u003ca href=\"https://www.netdata.cloud/\"\u003enetdata\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m using \u003ccode\u003estreamlit\u003c/code\u003e and \u003ccode\u003eplotly\u003c/code\u003e for the webserver, design, and plotting at the moment.\u003c/p\u003e\n\u003ch2 id=\"my-use-case\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eMy Use Case\u003c/span\u003e \u003ca href=\"#my-use-case\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI needed a way to refresh my plotly charts with a fixed window of time so that I\u0026rsquo;m able to just see relevant recent data instead of cramming all data for all time into one plot that\u0026rsquo;s 500 pixels wide\u0026hellip;\u003c/p\u003e\n\u003cp\u003eChecking the length of arrays or lists every time I get a new piece of data feels \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of dumb and I thought \u0026ldquo;python must have a way to do this\u0026rdquo;\u0026hellip;\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u0026ldquo;This\u0026rdquo; meaning, update values in a fixed length array without reallocating memory or recreating a copy of the list\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"deques\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eDeques\u003c/span\u003e \u003ca href=\"#deques\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eEnter the \u003ccode\u003edeque\u003c/code\u003e.\nIt means \u0026ldquo;double ended queue\u0026rdquo; and is in general an \u003ccode\u003eIterable\u003c/code\u003e that you can append values to either side or pop values from either side.\u003c/p\u003e\n\u003cp\u003eThe init signature is straightforward enough and I\u0026rsquo;m sure there\u0026rsquo;s more to them than I know yet but here\u0026rsquo;s how I use it\u0026hellip;\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003ecollections\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003edeque\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_deque\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edeque\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis gives us \u003ccode\u003emy_deque\u003c/code\u003e, created from an iterable, with several familiar methods like \u003ccode\u003eindex\u003c/code\u003e, \u003ccode\u003eextend\u003c/code\u003e, \u003ccode\u003eappend\u003c/code\u003e, etc.\nHowever there\u0026rsquo;s some new ones too such as \u003ccode\u003eappendleft\u003c/code\u003e and \u003ccode\u003epopleft\u003c/code\u003e.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_deque\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eappendleft\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emy_dequqe\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003edeque\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_deque\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epopleft\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"err\"\u003e!\u003c/span\u003e\u003cspan class=\"o\"\u003e--\u003c/span\u003e\u003cspan class=\"n\"\u003emarkata\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eattribution\u003c/span\u003e\u003cspan class=\"o\"\u003e--\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emy_deque\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003edeque\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThese are handy ways to manipulate the iterable that I needed for the arrays I plot with plotly!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSee my follow-up to this on using Deques with plotly and streamlit to create a quick \u0026ldquo;dashboard\u0026rdquo; with live streaming data!\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/plotly-and-streamlit\"\u003efollow-up\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nI am working on a project to create a small system monitoring dashboard using the python `psutil` library.\n\nThe repo is [here](https://github.com/nicpayne713/not-netdata) (if you want actual system monitoring please use [netdata](https://www.netdata.cloud/)).\n\nI'm using `streamlit` and `plotly` for the webserver, design, and plotting at the moment.\n\n## My Use Case\n\nI needed a way to refresh my plotly charts with a fixed window of time so that I'm able to just see relevant recent data instead of cramming all data for all time into one plot that's 500 pixels wide...\n\nChecking the length of arrays or lists every time I get a new piece of data feels kind of dumb and I thought \"python must have a way to do this\"...\n\n\u003e \"This\" meaning, update values in a fixed length array without reallocating memory or recreating a copy of the list\n\n## Deques\n\nEnter the `deque`. \nIt means \"double ended queue\" and is in general an `Iterable` that you can append values to either side or pop values from either side.\n\nThe init signature is straightforward enough and I'm sure there's more to them than I know yet but here's how I use it...\n\n```python\nfrom collections import deque\n\nmy_deque = deque([1,2,3])\n```\n\nThis gives us `my_deque`, created from an iterable, with several familiar methods like `index`, `extend`, `append`, etc.\nHowever there's some new ones too such as `appendleft` and `popleft`.\n\n```python\nmy_deque.appendleft('a')\nprint(my_dequqe)\n\u003e\u003e\u003e deque(['a', 1, 2, 3])\n\nmy_deque.popleft()\n\u003e\u003e\u003e 'a'\nprint(my_deque)\n\u003e\u003e\u003e deque([1, 2, 3])\n```\n\nThese are handy ways to manipulate the iterable that I needed for the arrays I plot with plotly!\n\n\n__See my follow-up to this on using Deques with plotly and streamlit to create a quick \"dashboard\" with live streaming data!__\n\n[follow-up](/plotly-and-streamlit)\n",
      "summary": "I am working on a project to create a small system monitoring dashboard using the python library.",
      "date_published": "2022-03-31T00:00:00Z",
      "date_modified": "2022-03-31T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/skimpy/",
      "url": "https://pype.dev/skimpy/",
      "title": "Skimpy",
      "content_html": "\u003ch2 id=\"eda\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eEDA\u003c/span\u003e \u003ca href=\"#eda\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI work with data a lot, but the nature of my job isn\u0026rsquo;t to dive super deep into a small amount of datasets,\nI\u0026rsquo;m often jumping between several projects every day and need to just get a super quick glance at some tables to get a high level view.\u003c/p\u003e\n\u003cp\u003eWhen I\u0026rsquo;m doing more interactive exploration I\u0026rsquo;ve graduated from Jupyter cells with \u003ccode\u003edf_N.head()\u003c/code\u003e to using an amazing tool called \u003ca href=\"https://www.visidata.org/\"\u003evisidata\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eHowever, Visidata is a terminal based application and I\u0026rsquo;m often in an iPython console\u0026hellip; so is there a way to move even faster for my super quick summary views?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eyes!\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"skimpy\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eSkimpy\u003c/span\u003e \u003ca href=\"#skimpy\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eFirst thing to do is \u003ccode\u003epip install skimpy\u003c/code\u003e and then it\u0026rsquo;s as easy to get some summary stats with \u003ccode\u003eskimpy \u0026lt;data\u0026gt;\u003c/code\u003e\u003c/p\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-zsh.png\" alt=\"Skimpy ZSH\" title=\"A fancy data summary in the shell\" /\u003e\n\u003cp\u003eThis is super nice for seeing missing values in particular as well as the distribution shape of the data.\u003c/p\u003e\n\u003ch2 id=\"ipython\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eiPython\u003c/span\u003e \u003ca href=\"#ipython\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eBut wait\u0026hellip; I just said I\u0026rsquo;m normally in an iPython session but that was called from zsh.. If I\u0026rsquo;m hoping back into zsh I might as well use visidata to have more powerful exploration at my fingertips.\nSo\u0026hellip; can I see this table quickly without breaking my iPython workflow?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOf course you can with magic!\u003c/strong\u003e\u003c/p\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-ipython.png\" alt=\"Skimpy iPython\" title=\"A fancy data summary in iPython\" /\u003e\n\u003cp\u003eThe above assumes you\u0026rsquo;re looking at a file, like you would in the terminal.\n\u003ccode\u003eskimpy\u003c/code\u003e works even better in iPython with \u003ccode\u003efrom skimpy import skim\u003c/code\u003e then pass any DataFrame to \u003ccode\u003eskim\u003c/code\u003e!\u003c/p\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-ipython2.png\" alt=\"Skimpy iPython2\" title=\"A fancy data summary in iPython\" /\u003e\n",
      "content_text": "\n## EDA\n\nI work with data a lot, but the nature of my job isn't to dive super deep into a small amount of datasets,\nI'm often jumping between several projects every day and need to just get a super quick glance at some tables to get a high level view.\n\nWhen I'm doing more interactive exploration I've graduated from Jupyter cells with `df_N.head()` to using an amazing tool called [visidata](https://www.visidata.org/)\n\nHowever, Visidata is a terminal based application and I'm often in an iPython console... so is there a way to move even faster for my super quick summary views?\n\n__yes!__ \n\n## Skimpy\n\nFirst thing to do is `pip install skimpy` and then it's as easy to get some summary stats with `skimpy \u003cdata\u003e`\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-zsh.png\" alt=\"Skimpy ZSH\" title=\"A fancy data summary in the shell\" /\u003e\n\nThis is super nice for seeing missing values in particular as well as the distribution shape of the data.\n\n## iPython\n\nBut wait... I just said I'm normally in an iPython session but that was called from zsh.. If I'm hoping back into zsh I might as well use visidata to have more powerful exploration at my fingertips.\nSo... can I see this table quickly without breaking my iPython workflow?\n\n__Of course you can with magic!__\n\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-ipython.png\" alt=\"Skimpy iPython\" title=\"A fancy data summary in iPython\" /\u003e\n\n\nThe above assumes you're looking at a file, like you would in the terminal. \n`skimpy` works even better in iPython with `from skimpy import skim` then pass any DataFrame to `skim`!\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/skimpy-ipython2.png\" alt=\"Skimpy iPython2\" title=\"A fancy data summary in iPython\" /\u003e\n",
      "summary": "I work with data a lot, but the nature of my job isn't to dive super deep into a small amount of datasets, I'm often jumping between several projects every...",
      "date_published": "2022-03-23T00:00:00Z",
      "date_modified": "2022-03-23T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "homepage",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/pyclean/",
      "url": "https://pype.dev/pyclean/",
      "title": "Pyclean",
      "content_html": "\u003cp\u003eI like to keep my workspace clean and one thing that I don\u0026rsquo;t personally love looking at is the \u003ccode\u003e__pycache__\u003c/code\u003e directory that pops up after running some code.\nThe \u003ccode\u003e*.pyc\u003c/code\u003e files that show up there are python bytecode and they are cached to make subsequent runs a tad faster.\nMy stuff never really needs this bonus speed boost and so I came across a neat tool called \u003ccode\u003epyclean\u003c/code\u003e!\u003c/p\u003e\n\u003ch2 id=\"pyclean\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003ePyclean\u003c/span\u003e \u003ca href=\"#pyclean\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe easiest way (\u003cstrong\u003ein my opinion\u003c/strong\u003e) to run \u003ccode\u003epyclean\u003c/code\u003e is to just use \u003ccode\u003epipx run\u003c/code\u003e.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox/src  🌱 main 🗑️  ×3🛤️  ×2via 🐍 v3.8.11 \u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e  took 9s\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ ls\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eabcmeta.py  __pycache__  python-print-align.py  system-monitor-psutils.py\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox/src  🌱 main 🗑️  ×3🛤️  ×2via 🐍 v3.8.11 \u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ pipx run pyclean .\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e⚠️  pyclean is already on your PATH and installed at /usr/bin/pyclean. Downloading and running anyway.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCleaning directory .\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eTotal \u003cspan class=\"m\"\u003e1\u003c/span\u003e files, \u003cspan class=\"m\"\u003e1\u003c/span\u003e directories removed.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esandbox/src  🌱 main 🗑️  ×3🛤️  ×2via 🐍 v3.8.11 \u003cspan class=\"o\"\u003e(\u003c/span\u003esandbox\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ ls\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eabcmeta.py  python-print-align.py  system-monitor-psutils.py\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"why-not-bash\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhy not bash?\u003c/span\u003e \u003ca href=\"#why-not-bash\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eYou could accomplish something similar with \u003ccode\u003erm **/*.pyc\u003c/code\u003e or \u003ccode\u003efind -n '*.py?' -delete\u003c/code\u003e but there\u0026rsquo;s a chance you\u0026rsquo;ll find something you don\u0026rsquo;t love gone.\nAlso this won\u0026rsquo;t help our poor Windows friends out there!\n\u003ccode\u003epyclean\u003c/code\u003e is fully python so it\u0026rsquo;s OS independent.\u003c/p\u003e\n\u003ch2 id=\"credits\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eCredits!\u003c/span\u003e \u003ca href=\"#credits\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/bittner/pyclean\"\u003erepo\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nI like to keep my workspace clean and one thing that I don't personally love looking at is the `__pycache__` directory that pops up after running some code.\nThe `*.pyc` files that show up there are python bytecode and they are cached to make subsequent runs a tad faster. \nMy stuff never really needs this bonus speed boost and so I came across a neat tool called `pyclean`!\n\n## Pyclean\n\nThe easiest way (**in my opinion**) to run `pyclean` is to just use `pipx run`.\n\n```bash\nsandbox/src  🌱 main 🗑️  ×3🛤️  ×2via 🐍 v3.8.11 (sandbox)  took 9s\n❯ ls\nabcmeta.py  __pycache__  python-print-align.py  system-monitor-psutils.py\n\nsandbox/src  🌱 main 🗑️  ×3🛤️  ×2via 🐍 v3.8.11 (sandbox)\n❯ pipx run pyclean .\n⚠️  pyclean is already on your PATH and installed at /usr/bin/pyclean. Downloading and running anyway.\nCleaning directory .\nTotal 1 files, 1 directories removed.\n\nsandbox/src  🌱 main 🗑️  ×3🛤️  ×2via 🐍 v3.8.11 (sandbox)\n❯ ls\nabcmeta.py  python-print-align.py  system-monitor-psutils.py\n\n```\n\n## Why not bash?\n\nYou could accomplish something similar with `rm **/*.pyc` or `find -n '*.py?' -delete` but there's a chance you'll find something you don't love gone.\nAlso this won't help our poor Windows friends out there!\n`pyclean` is fully python so it's OS independent.\n\n## Credits!\n\n[repo](https://github.com/bittner/pyclean)\n",
      "summary": "I like to keep my workspace clean and one thing that I don't personally love looking at is the directory that pops up after running some code. The files that...",
      "date_published": "2022-03-22T00:00:00Z",
      "date_modified": "2022-03-22T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/psutils-01/",
      "url": "https://pype.dev/psutils-01/",
      "title": "Psutil-01",
      "content_html": "\u003cp\u003e\u003ca href=\"https://twitter.com/driscollis\"\u003eMike Driscoll\u003c/a\u003e has been posting some awesome posts about \u003ccode\u003epsutil\u003c/code\u003e lately.\nI\u0026rsquo;m interested in making my own system monitoring dashboard now using this library.\nI don\u0026rsquo;t expect it to compete with Netdata or Glances but it\u0026rsquo;ll just be for fun to see how Python can solve this problem!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRepo coming soon\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"example-code\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eExample code:\u003c/span\u003e \u003ca href=\"#example-code\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s a short snippit to get used/available/total RAM and disk space (on partitions that you probably care about)\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003epsutil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003esocket\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;System Memory used: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003epsutil\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evirtual_memory\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eused\u003c/span\u003e \u003cspan class=\"o\"\u003e//\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e GB\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;System Memory available: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003epsutil\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evirtual_memory\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eavailable\u003c/span\u003e \u003cspan class=\"o\"\u003e//\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e GB\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;System Memory total: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003epsutil\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evirtual_memory\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etotal\u003c/span\u003e \u003cspan class=\"o\"\u003e//\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e GB\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Hostname: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esocket\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egethostname\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003epartitions\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epsutil\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edisk_partitions\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003epart\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003epartitions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003emnt\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epart\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emountpoint\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;snap\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003emnt\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;boot\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003emnt\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edisk\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epsutil\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edisk_usage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emnt\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Usage at \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003emnt\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e on \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003epart\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edevice\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003edisk\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eused\u003c/span\u003e \u003cspan class=\"o\"\u003e//\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e GB\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Free at \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003emnt\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e on \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003epart\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edevice\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003edisk\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efree\u003c/span\u003e \u003cspan class=\"o\"\u003e//\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003eGB\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Total at \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003emnt\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e on \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003epart\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edevice\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003edisk\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etotal\u003c/span\u003e \u003cspan class=\"o\"\u003e//\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003eGB\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cblockquote\u003e\n\u003cp\u003eBonus Ipython tip! Save this to a script called my_script.py and in Ipython you can %run -m my_script to run it!\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eproject ↪ main v3.8.11 ipython\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ %run -m system-monitor-psutils\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSystem Memory used: \u003cspan class=\"m\"\u003e25\u003c/span\u003e GB\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSystem Memory available: \u003cspan class=\"m\"\u003e5\u003c/span\u003e GB\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSystem Memory total: \u003cspan class=\"m\"\u003e31\u003c/span\u003e GB\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eHostname: ryzen-3600x\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eUsage at / on /dev/nvme1n1p2: \u003cspan class=\"m\"\u003e81\u003c/span\u003e GB\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eFree at / on /dev/nvme1n1p2: \u003cspan class=\"m\"\u003e351\u003c/span\u003e GB\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eTotal at / on /dev/nvme1n1p2: \u003cspan class=\"m\"\u003e456\u003c/span\u003e GB\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\n[Mike Driscoll](https://twitter.com/driscollis) has been posting some awesome posts about `psutil` lately.\nI'm interested in making my own system monitoring dashboard now using this library.\nI don't expect it to compete with Netdata or Glances but it'll just be for fun to see how Python can solve this problem!\n\n__Repo coming soon__\n\n## Example code:\nHere's a short snippit to get used/available/total RAM and disk space (on partitions that you probably care about)\n```python\n\nimport psutil\nimport socket\n\nprint(f\"System Memory used: {psutil.virtual_memory().used // (1024 ** 3)} GB\")\nprint(f\"System Memory available: {psutil.virtual_memory().available // (1024 ** 3)} GB\")\nprint(f\"System Memory total: {psutil.virtual_memory().total // (1024 ** 3)} GB\")\n\n\nprint(f\"Hostname: {socket.gethostname()}\")\n\npartitions = psutil.disk_partitions()\n\nfor part in partitions:\n    mnt = part.mountpoint\n    if \"snap\" in mnt or \"boot\" in mnt:\n        continue\n    disk = psutil.disk_usage(mnt)\n    print(f\"Usage at {mnt} on {part.device}: {disk.used // (1024 ** 3)} GB\")\n    print(f\"Free at {mnt} on {part.device}: {disk.free // (1024 ** 3)}GB\")\n    print(f\"Total at {mnt} on {part.device}: {disk.total // (1024 ** 3)}GB\")\n```\n\n\u003e Bonus Ipython tip! Save this to a script called my_script.py and in Ipython you can %run -m my_script to run it!\n\n```bash\nproject ↪ main v3.8.11 ipython\n❯ %run -m system-monitor-psutils\nSystem Memory used: 25 GB\nSystem Memory available: 5 GB\nSystem Memory total: 31 GB\nHostname: ryzen-3600x\nUsage at / on /dev/nvme1n1p2: 81 GB\nFree at / on /dev/nvme1n1p2: 351 GB\nTotal at / on /dev/nvme1n1p2: 456 GB\n```\n",
      "summary": "Mike Driscoll has been posting some awesome posts about lately. I'm interested in making my own system monitoring dashboard now using this library. I don't...",
      "date_published": "2022-03-16T00:00:00Z",
      "date_modified": "2022-03-16T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/mu/",
      "url": "https://pype.dev/mu/",
      "title": "Mu",
      "content_html": "\u003cp\u003eIf you work with a template for several projects then you might sometimes need to do the same action across all repos.\nA good example of this is updating a package in \u003ccode\u003erequirements.txt\u003c/code\u003e in every project, or refactoring a common module.\nIf you have several repos to do this across then it can be time consuming\u0026hellip; enter \u003ccode\u003emu-repo\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"mu\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eMu\u003c/span\u003e \u003ca href=\"#mu\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://fabioz.github.io/mu-repo/\"\u003emu-repo\u003c/a\u003e is an awesome cli tool for working with multiple git repositories at the same time.\nThere are several things you can do:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003ccode\u003emu status\u003c/code\u003e will give you the \u003ccode\u003egit status\u003c/code\u003e of every registered repo (see below)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003emu sh\u003c/code\u003e will let you execute system level commands in every repo\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003emu stash\u003c/code\u003e will stash all changes across all registered repos\u003c/li\u003e\n\u003cli\u003eThere\u0026rsquo;s literally a ton more but these are some handy ones\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"registration\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eRegistration\u003c/span\u003e \u003ca href=\"#registration\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003emu\u003c/code\u003e tracks its own \u003ccode\u003egroups\u003c/code\u003e, and there is a default group when no particular one is active.\nIt\u0026rsquo;s as simple as \u003ccode\u003emu register proj1 prog2 ...\u003c/code\u003e to get repos registered\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ mu register proj1 proj2\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eRepository: proj1 registered\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eRepository: proj2 registered\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ mu status\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  proj1 : git status\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    On branch main\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    No commits yet\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Untracked files:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e(\u003c/span\u003euse \u003cspan class=\"s2\"\u003e\u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34;\u003c/span\u003e to include in what will be committed\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    nothing added to commit but untracked files present \u003cspan class=\"o\"\u003e(\u003c/span\u003euse \u003cspan class=\"s2\"\u003e\u0026#34;git add\u0026#34;\u003c/span\u003e to track\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  proj2 : git status\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    On branch main\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    No commits yet\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Changes to be committed:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e(\u003c/span\u003euse \u003cspan class=\"s2\"\u003e\u0026#34;git rm --cached \u0026lt;file\u0026gt;...\u0026#34;\u003c/span\u003e to unstage\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    new file:   requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"working-with-mu\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWorking with mu\u003c/span\u003e \u003ca href=\"#working-with-mu\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAs you can see above I have two projects each with a \u003ccode\u003erequirements.txt\u003c/code\u003e added but not committed yet.\nUsing \u003ccode\u003emu\u003c/code\u003e I can stage this change across both repos at once.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ mu add requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  proj1 : git add requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  proj2 : git add requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen as you might imagine, I can make the commit in each repo\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ mu commit -m \u003cspan class=\"s2\"\u003e\u0026#34;Add requirements.txts\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  proj1 : git commit -m Add requirements.txts\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e[\u003c/span\u003emain \u003cspan class=\"o\"\u003e(\u003c/span\u003eroot-commit\u003cspan class=\"o\"\u003e)\u003c/span\u003e 18376d7\u003cspan class=\"o\"\u003e]\u003c/span\u003e Add requirements.txts\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"m\"\u003e1\u003c/span\u003e file changed, \u003cspan class=\"m\"\u003e1\u003c/span\u003e insertion\u003cspan class=\"o\"\u003e(\u003c/span\u003e+\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    create mode \u003cspan class=\"m\"\u003e100644\u003c/span\u003e requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  proj2 : git commit -m Add requirements.txts\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e[\u003c/span\u003emain \u003cspan class=\"o\"\u003e(\u003c/span\u003eroot-commit\u003cspan class=\"o\"\u003e)\u003c/span\u003e 18376d7\u003cspan class=\"o\"\u003e]\u003c/span\u003e Add requirements.txts\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"m\"\u003e1\u003c/span\u003e file changed, \u003cspan class=\"m\"\u003e1\u003c/span\u003e insertion\u003cspan class=\"o\"\u003e(\u003c/span\u003e+\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    create mode \u003cspan class=\"m\"\u003e100644\u003c/span\u003e requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"mu-groups\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003emu groups\u003c/span\u003e \u003ca href=\"#mu-groups\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe other thing I got a lot of use out of recently was \u003ccode\u003emu\u003c/code\u003e\u0026rsquo;s groups.\nAt work I have about 40 repos cloned that are all based on the same kedro pipeline template.\nSome of these projects have been deprecated.\nI also have several more repos that are not kedro template - custom libraries or something.\n\u003ccode\u003egroup\u003c/code\u003e let me utilize \u003ccode\u003emu\u003c/code\u003e across different groups of repos.\u003c/p\u003e\n\u003cp\u003eSay \u003ccode\u003eproj2\u003c/code\u003e is a deprecated project that I don\u0026rsquo;t need to worry about making changes to anymore.\nI don\u0026rsquo;t just have to unregister it, instead I can make a group called \u0026ldquo;active\u0026rdquo; and register \u003ccode\u003eproj1\u003c/code\u003e in that group\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ mu group add active --empty\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e~/personal\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ mu group add deprecated --empty\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e~/personal\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e❯ mu group\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  active\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e* deprecated\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe \u003ccode\u003e*\u003c/code\u003e tells me which group is active.\nThe \u003ccode\u003e--empty\u003c/code\u003e flag tells \u003ccode\u003emu\u003c/code\u003e to not add all registered repos to that group.\nIf I don\u0026rsquo;t want to use any groups then \u003ccode\u003emu group reset\u003c/code\u003e will go back to the default group with all registered repos.\u003c/p\u003e\n\u003cp\u003eWith groups I can register only the repos that I want to be working across in their own group and not worry about affecting other repos with my batch changes!\u003c/p\u003e\n",
      "content_text": "\nIf you work with a template for several projects then you might sometimes need to do the same action across all repos.\nA good example of this is updating a package in `requirements.txt` in every project, or refactoring a common module.\nIf you have several repos to do this across then it can be time consuming... enter `mu-repo`\n\n\n## Mu\n\n[mu-repo](https://fabioz.github.io/mu-repo/) is an awesome cli tool for working with multiple git repositories at the same time. \nThere are several things you can do:\n\n1. `mu status` will give you the `git status` of every registered repo (see below)\n2. `mu sh` will let you execute system level commands in every repo\n3. `mu stash` will stash all changes across all registered repos\n4. There's literally a ton more but these are some handy ones\n\n\n## Registration\n\n`mu` tracks its own `groups`, and there is a default group when no particular one is active.\nIt's as simple as `mu register proj1 prog2 ...` to get repos registered\n\n```bash \n\n❯ mu register proj1 proj2\nRepository: proj1 registered\nRepository: proj2 registered\n\n❯ mu status\n\n  proj1 : git status\n    On branch main\n\n    No commits yet\n\n    Untracked files:\n    (use \"git add \u003cfile\u003e...\" to include in what will be committed)\n    requirements.txt\n\n    nothing added to commit but untracked files present (use \"git add\" to track)\n\n  proj2 : git status\n    On branch main\n\n    No commits yet\n\n    Changes to be committed:\n    (use \"git rm --cached \u003cfile\u003e...\" to unstage)\n    new file:   requirements.txt\n\n\n```\n\n## Working with mu\n\nAs you can see above I have two projects each with a `requirements.txt` added but not committed yet.\nUsing `mu` I can stage this change across both repos at once.\n\n```bash  \n\n❯ mu add requirements.txt\n\n  proj1 : git add requirements.txt\n\n  proj2 : git add requirements.txt\n```\n\nThen as you might imagine, I can make the commit in each repo\n\n\n```bash\n\n❯ mu commit -m \"Add requirements.txts\"\n\n  proj1 : git commit -m Add requirements.txts\n    [main (root-commit) 18376d7] Add requirements.txts\n    1 file changed, 1 insertion(+)\n    create mode 100644 requirements.txt\n\n  proj2 : git commit -m Add requirements.txts\n    [main (root-commit) 18376d7] Add requirements.txts\n    1 file changed, 1 insertion(+)\n    create mode 100644 requirements.txt\n```\n\n## mu groups\n\nThe other thing I got a lot of use out of recently was `mu`'s groups.\nAt work I have about 40 repos cloned that are all based on the same kedro pipeline template.\nSome of these projects have been deprecated.\nI also have several more repos that are not kedro template - custom libraries or something.\n`group` let me utilize `mu` across different groups of repos.\n\nSay `proj2` is a deprecated project that I don't need to worry about making changes to anymore.\nI don't just have to unregister it, instead I can make a group called \"active\" and register `proj1` in that group\n\n```bash\n\n❯ mu group add active --empty\n\n~/personal\n❯ mu group add deprecated --empty\n\n~/personal\n❯ mu group\n  active\n* deprecated\n\n```\n\n\nThe `*` tells me which group is active. \nThe `--empty` flag tells `mu` to not add all registered repos to that group.\nIf I don't want to use any groups then `mu group reset` will go back to the default group with all registered repos.\n\nWith groups I can register only the repos that I want to be working across in their own group and not worry about affecting other repos with my batch changes!\n",
      "summary": "If you work with a template for several projects then you might sometimes need to do the same action across all repos. A good example of this is updating a...",
      "date_published": "2022-03-15T00:00:00Z",
      "date_modified": "2022-03-15T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "git",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/abstract-base-class/",
      "url": "https://pype.dev/abstract-base-class/",
      "title": "Abstract-Base-Class",
      "content_html": "\u003ch2 id=\"abcmeta\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eABCMeta\u003c/span\u003e \u003ca href=\"#abcmeta\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI don\u0026rsquo;t do a lot of OOP currently, but I have been on a few heavy OOP projects and this \u003ccode\u003eABCMeta\u003c/code\u003e and \u003ccode\u003eabstractmethod\u003c/code\u003e from \u003ccode\u003eabc\u003c/code\u003e would\u0026rsquo;ve been super nice to know about!\u003c/p\u003e\n\u003cp\u003eIf you are creating a library with classes that you expect your users to extend, but you want to ensure that any extension has explicit methods defined then this is for you!.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003eabc\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eABCMeta\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eabstractmethod\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eFamily\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emetaclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eABCMeta\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nd\"\u003e@abstractmethod\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eget_dad\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Any extension of the Family class must implement a `get_dad` method\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMyFamily\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eFamily\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf I try to instantiate \u003ccode\u003eMyFamily\u003c/code\u003e I will not be allowed:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e❯\u003c/span\u003e \u003cspan class=\"n\"\u003emy_fam\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eMyFamily\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e╭───────────────────────────────\u003c/span\u003e \u003cspan class=\"n\"\u003eTraceback\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emost\u003c/span\u003e \u003cspan class=\"n\"\u003erecent\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003elast\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"err\"\u003e────────────────────────────────╮\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e│\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eipython\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"nb\"\u003einput\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eecb8e21ce815\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003emodule\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e                                                     \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e╰──────────────────────────────────────────────────────────────────────────────────────────────────╯\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"ne\"\u003eTypeError\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eCan\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;t instantiate abstract class MyFamily with abstract methods get_dad\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/py-abc-meta.png\" alt=\"abcmetadata\" title=\"ABC Metadata\" /\u003e\n\u003cp\u003eIn order for me to extend \u003ccode\u003eFamily\u003c/code\u003e I have to implement the method \u003ccode\u003eget_dad\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMyFamily\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eFamily\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eget_dad\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Me\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow everything works as expected and I can sleep well knowing no one can extend my base class without creating methods I know they need.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_fam\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eMyFamily\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emy_fam\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_dad\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e\u0026#39;Me\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\n## ABCMeta\n\nI don't do a lot of OOP currently, but I have been on a few heavy OOP projects and this `ABCMeta` and `abstractmethod` from `abc` would've been super nice to know about!\n\nIf you are creating a library with classes that you expect your users to extend, but you want to ensure that any extension has explicit methods defined then this is for you!.\n\n```python\nfrom abc import ABCMeta, abstractmethod\nclass Family(metaclass=ABCMeta):\n    @abstractmethod\n    def get_dad(self):\n        \"\"\"Any extension of the Family class must implement a `get_dad` method\"\"\"\n\nclass MyFamily(Family):\n    pass\n\n```\n\nIf I try to instantiate `MyFamily` I will not be allowed:\n```python\n\n❯ my_fam = MyFamily()\n╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮\n│ \u003cipython-input-8-ecb8e21ce815\u003e:1 in \u003cmodule\u003e                                                     │\n╰──────────────────────────────────────────────────────────────────────────────────────────────────╯\nTypeError: Can't instantiate abstract class MyFamily with abstract methods get_dad\n\n```\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/py-abc-meta.png\" alt=\"abcmetadata\" title=\"ABC Metadata\" /\u003e\n\nIn order for me to extend `Family` I have to implement the method `get_dad`\n\n```python\nclass MyFamily(Family):\n    def get_dad(self):\n        return \"Me\"\n```\n\nNow everything works as expected and I can sleep well knowing no one can extend my base class without creating methods I know they need.\n\n\n```python\n\nmy_fam = MyFamily()\n\nmy_fam.get_dad()\n'Me'\n\n```\n",
      "summary": "I don't do a lot of OOP currently, but I have been on a few heavy OOP projects and this and from would've been super nice to know about!",
      "date_published": "2022-03-09T00:00:00Z",
      "date_modified": "2022-03-09T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/python-f-string-align/",
      "url": "https://pype.dev/python-f-string-align/",
      "title": "Python-F-String-Align",
      "content_html": "\u003cp\u003eI am personally trying to use \u003ccode\u003elogger\u003c/code\u003e instead of \u003ccode\u003eprint\u003c/code\u003e in all of my code,\nhowever I learned from [@Python-Hub] that you can align printouts using \u003ccode\u003eprint\u003c/code\u003e with \u003ccode\u003ef\u003c/code\u003e-strings!.\u003c/p\u003e\n\u003cp\u003eThis little python script shows how options in the \u003ccode\u003ef\u003c/code\u003e-string can format the printout.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003erandom\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003evariables\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Foo Bar Baz Bing\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esplit\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003escores\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erandom\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esample\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003erange\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e11\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evariables\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;*\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;With \u0026#39;varable\u0026#39; left aligned\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003evarable\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003escore\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"nb\"\u003ezip\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evariables\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003escores\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003evarable\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026lt;10\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e | \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003escore\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;*\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;With \u0026#39;varable\u0026#39; right aligned\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003evarable\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003escore\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"nb\"\u003ezip\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evariables\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003escores\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003evarable\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026gt;15\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e | \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003escore\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;*\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;With \u0026#39;varable\u0026#39; center aligned\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003evarable\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003escore\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"nb\"\u003ezip\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evariables\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003escores\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003evarable\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e^5\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e | \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003escore\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/py-print-align.png\" alt=\"pyprintalign\" title=\"Align f-string printing\" /\u003e\n",
      "content_text": "\n\nI am personally trying to use `logger` instead of `print` in all of my code, \nhowever I learned from [@Python-Hub] that you can align printouts using `print` with `f`-strings!.\n\nThis little python script shows how options in the `f`-string can format the printout.\n\n```python\n\nimport random\n\nvariables = \"Foo Bar Baz Bing\".split()\nscores = random.sample(range(1, 11), len(variables))\n\nprint(\"*\" * 30)\nprint(\"\\n\")\nprint(\"With 'varable' left aligned\")\nfor varable, score in zip(variables, scores):\n    print(f\"{varable:\u003c10} | {score}\")\n\nprint(\"*\" * 30)\nprint(\"\\n\")\nprint(\"With 'varable' right aligned\")\nfor varable, score in zip(variables, scores):\n    print(f\"{varable:\u003e15} | {score}\")\n\nprint(\"*\" * 30)\nprint(\"\\n\")\nprint(\"With 'varable' center aligned\")\nfor varable, score in zip(variables, scores):\n    print(f\"{varable:^5} | {score}\")\n\n```\n\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/py-print-align.png\" alt=\"pyprintalign\" title=\"Align f-string printing\" /\u003e\n",
      "summary": "I am personally trying to use instead of in all of my code, however I learned from [@Python-Hub] that you can align printouts using with -strings!.",
      "date_published": "2022-03-08T00:00:00Z",
      "date_modified": "2022-03-08T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/python-builtin-calendar/",
      "url": "https://pype.dev/python-builtin-calendar/",
      "title": "Python-Builtin-Calendar",
      "content_html": "\u003ch2 id=\"being-lazy\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eBeing lazy\u003c/span\u003e \u003ca href=\"#being-lazy\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI almost exclusively use Python for my job and have been eye-balls deep in it for almost 5 years but I really lack in-depth knowledge of builtins.\nI recently learned of an awesome builtin called \u003ccode\u003ecalendar\u003c/code\u003e  that has way more than I know about for sure but I\u0026rsquo;m glad I know it\u0026rsquo;s here now!\u003c/p\u003e\n\u003cp\u003eI only needed it because I was too lazy to hard code the 7 weekdays into my module but it turns out there\u0026rsquo;s a lot of useful things like \u003ccode\u003ecalendar.isleap()\u003c/code\u003e!\u003c/p\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/builtin-calendar.png\" alt=\"builtin\" title=\"Python's builtin calendar\" /\u003e\n## Future use\n\u003cp\u003eI\u0026rsquo;m not exactly sure what will come my way where \u003ccode\u003ecalendar\u003c/code\u003e will be super relevant but like anything, I\u0026rsquo;m just glad to know it exists for when the time arises!\u003c/p\u003e\n",
      "content_text": "\n## Being lazy\n\nI almost exclusively use Python for my job and have been eye-balls deep in it for almost 5 years but I really lack in-depth knowledge of builtins.\nI recently learned of an awesome builtin called `calendar`  that has way more than I know about for sure but I'm glad I know it's here now!\n\nI only needed it because I was too lazy to hard code the 7 weekdays into my module but it turns out there's a lot of useful things like `calendar.isleap()`!\n\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/builtin-calendar.png\" alt=\"builtin\" title=\"Python's builtin calendar\" /\u003e\n## Future use\n\nI'm not exactly sure what will come my way where `calendar` will be super relevant but like anything, I'm just glad to know it exists for when the time arises!\n",
      "summary": "I almost exclusively use Python for my job and have been eye-balls deep in it for almost 5 years but I really lack in-depth knowledge of builtins. I recently...",
      "date_published": "2022-03-08T00:00:00Z",
      "date_modified": "2022-03-08T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/dataframe-memory-usage/",
      "url": "https://pype.dev/dataframe-memory-usage/",
      "title": "Dataframe-Memory-Usage",
      "content_html": "\u003cp\u003eI have often wanted to dive into memory usage for pandas DataFrames when it comes to cloud deployment.\nIf I have a python process running on a server at home I can use \u003ccode\u003eglances\u003c/code\u003e or a number of other tools to diagnose a memory issue\u0026hellip;\nHowever at work I normally deploy dockerized processes on AWS Batch and it\u0026rsquo;s much more challenging to get info on the dockerized process without more AWS integration that my team isn\u0026rsquo;t quite ready for.\nSo TIL that I can get some of the info I want from pandas directly!\u003c/p\u003e\n\u003ch1 id=\"dataframe-info\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eDataFrame.info()\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eI didn\u0026rsquo;t realize that \u003ccode\u003edf.info()\u003c/code\u003e was able to give me more info than just dtypes and some summary stats\u0026hellip;\nThere is a kwarg \u003ccode\u003ememory_usage\u003c/code\u003e that can configure what you need to get back, so \u003ccode\u003edf.memory_usage=\u0026quot;deep\u0026quot;\u003c/code\u003e will give you how much RAM any given DataFrame is using!\nAmazing tool for finding issues with joins or renegade source data files.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003edf\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epd\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eread_csv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;cars.csv\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ememory_usage\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;deep\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/df-memory-usage.png\" alt=\"DataFrame Memory Usage\" title=\"DataFrame Memory Usage\" /\u003e\n",
      "content_text": "\n\nI have often wanted to dive into memory usage for pandas DataFrames when it comes to cloud deployment.\nIf I have a python process running on a server at home I can use `glances` or a number of other tools to diagnose a memory issue...\nHowever at work I normally deploy dockerized processes on AWS Batch and it's much more challenging to get info on the dockerized process without more AWS integration that my team isn't quite ready for.\nSo TIL that I can get some of the info I want from pandas directly!\n\n# DataFrame.info()\n\nI didn't realize that `df.info()` was able to give me more info than just dtypes and some summary stats...\nThere is a kwarg `memory_usage` that can configure what you need to get back, so `df.memory_usage=\"deep\"` will give you how much RAM any given DataFrame is using!\nAmazing tool for finding issues with joins or renegade source data files.\n\n```python\ndf = pd.read_csv(\"cars.csv\")\n\ndf.info(memory_usage=\"deep\")\n```\n\n\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/df-memory-usage.png\" alt=\"DataFrame Memory Usage\" title=\"DataFrame Memory Usage\" /\u003e\n",
      "summary": "I have often wanted to dive into memory usage for pandas DataFrames when it comes to cloud deployment. If I have a python process running on a server at home...",
      "date_published": "2022-03-07T00:00:00Z",
      "date_modified": "2022-03-07T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/adblock-coverage/",
      "url": "https://pype.dev/adblock-coverage/",
      "title": "Adblock-Coverage",
      "content_html": "\u003cp\u003eI run pi-hole at home for ad blocking and some internal DNS/DHCP handling.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003epi hole posts on the way\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eOne thing I\u0026rsquo;ve never put too much thought in is asking \u0026ldquo;how well am I doing at blocking?\u0026rdquo;\nThere\u0026rsquo;s lots of ways to measure that depending on what you care about but I just learned of \u003ca href=\"https://d3ward.github.io/toolz/adblock\"\u003eadblock tester\u003c/a\u003e.\nIt\u0026rsquo;s awesome and gave me a quick glimpse into how my pi-hole is performing on keeping my webpages clean and my DNS history private!\u003c/p\u003e\n\u003cp\u003eCredits to \u003ca href=\"https://github.com/d3ward/toolz\"\u003ed3ward\u003c/a\u003e for the awesome tool!\u003c/p\u003e\n",
      "content_text": "\n\nI run pi-hole at home for ad blocking and some internal DNS/DHCP handling.\n\n__pi hole posts on the way__\n\nOne thing I've never put too much thought in is asking \"how well am I doing at blocking?\"\nThere's lots of ways to measure that depending on what you care about but I just learned of [adblock tester](https://d3ward.github.io/toolz/adblock).\nIt's awesome and gave me a quick glimpse into how my pi-hole is performing on keeping my webpages clean and my DNS history private!\n\nCredits to [d3ward](https://github.com/d3ward/toolz) for the awesome tool!\n",
      "summary": "I run pi-hole at home for ad blocking and some internal DNS/DHCP handling.",
      "date_published": "2022-03-07T00:00:00Z",
      "date_modified": "2022-03-07T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "til",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/webservers-and-indexes/",
      "url": "https://pype.dev/webservers-and-indexes/",
      "title": "Webservers-And-Indexes",
      "content_html": "\u003cp\u003eI host a lot of services in my homelab, but they\u0026rsquo;re mostly dockerized applications so I have never had to care much about how content gets served up.\nToday I had several little concepts click into place regarding webservers, and it was a similar experience to when I started homelabing and didn\u0026rsquo;t know what a \u0026ldquo;server\u0026rdquo; was in the first place.\u003c/p\u003e\n\u003ch1 id=\"servers\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eServers\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eA \u0026ldquo;server\u0026rdquo; can have a lot of different meanings but specifically in my world it was a physical server, like my PowerEdge R610 which acts as my main \u0026ldquo;home server\u0026rdquo;.\nBut then on my server, I have other servers\u0026hellip; Jellyfin is my main media server - but that\u0026rsquo;s obviously not a hardware thing, that\u0026rsquo;s software.\nThis is certainly not a groundbreaking thing but it was a tiny piece to the puzzle that I was missing\u0026hellip; that \u0026ldquo;server\u0026rdquo; is highly contextual.\u003c/p\u003e\n\u003ch1 id=\"webservers\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWebservers\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eSomething that confused the heck out of me when I first started down the road of having a server was what a webserver even was\u0026hellip;\nI always thought the \u0026ldquo;webserver\u0026rdquo; was just \u0026ldquo;a server that hosts a website\u0026rdquo;\u0026hellip; and yes, that\u0026rsquo;s true, but also it wasn\u0026rsquo;t true in how I understood \u0026ldquo;server\u0026rdquo;.\nIt turns out that across my 40-odd dockerized services I have at home that I must have about 40-odd web servers running, each \u003ca href=\"/docker/\" class=\"glossary-term\" title=\"docker - container runtime\" data-hover-title=\"docker\"\u003edocker\u003c/a\u003e container is spinning up its own!\u003c/p\u003e\n\u003cp\u003eSo something I have wanted to do for a long time is put my theology notes online for my small group to access whenever they might want\u0026hellip; it doesn\u0026rsquo;t need to be fancy or anything.\nMy issue was not knowing what to even Google. I tried \u0026ldquo;How to serve up static html\u0026rdquo; but that \u003ca href=\"/kind/\" class=\"glossary-term\" title=\"kind - kubernetes in docker\" data-hover-title=\"kind\"\u003ekind\u003c/a\u003e of search is for people who know what a \u0026ldquo;static\u0026rdquo; site is - I am not one of those people.\nI kept running across nginx and apache things, wordpress and other website building tools, etc.\nIn fact I only recently learned that JavaScript assets cann still be considered static so I am a complete baby in the web-dev space.\u003c/p\u003e\n\u003cp\u003eWhat I really wanted was just a simple landing page with a link to each of my \u0026ldquo;posts\u0026rdquo; which are in the form of a single html file each that I can easily export from my tiddlywiki (I have a post about tiddlywiki \u003ca href=\"/tiddly-wiki\"\u003ehere\u003c/a\u003e)\u003c/p\u003e\n\u003cp\u003eThe first win \u003ccode\u003epython -m http.server\u003c/code\u003e right in the directory I kept my html files in and that got me what I wanted functionally.\nBut then I wanted just a hair more organization\u0026hellip;\nI started looking for a way to dynamically generate an index for a directory of html files but again the verbiage of that Google search just wasn\u0026rsquo;t helping me - I didn\u0026rsquo;t want anything complicated and I knew that what I wanted had to be easy\u0026hellip;\u003c/p\u003e\n\u003ch1 id=\"the-index\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe Index\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eLuckily I randomly came across a SO that mentioned a Linux utility called \u003ccode\u003etree\u003c/code\u003e which does exactly what I wanted!\u003c/p\u003e\n\u003cp\u003eSee my TIL on \u003ccode\u003etree\u003c/code\u003e \u003ca href=\"'/tree'\"\u003ehere\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eSo now it goes like this:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eTake notes on X in my tiddlywiki\u003c/li\u003e\n\u003cli\u003eExport that tiddler to a html file\u003c/li\u003e\n\u003cli\u003ePut that html file into a \u003ccode\u003enotes\u003c/code\u003e folder in my github repo for small group notes\u003c/li\u003e\n\u003cli\u003eUse \u003ccode\u003etree\u003c/code\u003e to generate an \u003ccode\u003eindex.html\u003c/code\u003e of each of those files in the \u003ccode\u003enotes\u003c/code\u003e directory\u003c/li\u003e\n\u003cli\u003eUse \u003ccode\u003epython -m http.server\u003c/code\u003e to start a web server that lands me at the \u003ccode\u003eindex.html\u003c/code\u003e and now I can click through to any post!\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIt\u0026rsquo;s not fancy but it\u0026rsquo;s functional\u0026hellip;\nThis site/blog is built with markdown and \u003ca href=\"https://www.markata.dev\"\u003emarkata\u003c/a\u003e and I wanted way more functionality in my tech notes.\nBut for this simple use case I learned a ton about \u003cem\u003ehow\u003c/em\u003e content gets served up on a webpage and my small group benefits from the easy access as well!\u003c/p\u003e\n",
      "content_text": "\nI host a lot of services in my homelab, but they're mostly dockerized applications so I have never had to care much about how content gets served up.\nToday I had several little concepts click into place regarding webservers, and it was a similar experience to when I started homelabing and didn't know what a \"server\" was in the first place.\n\n# Servers\n\nA \"server\" can have a lot of different meanings but specifically in my world it was a physical server, like my PowerEdge R610 which acts as my main \"home server\".\nBut then on my server, I have other servers... Jellyfin is my main media server - but that's obviously not a hardware thing, that's software. \nThis is certainly not a groundbreaking thing but it was a tiny piece to the puzzle that I was missing... that \"server\" is highly contextual.\n\n# Webservers \n\nSomething that confused the heck out of me when I first started down the road of having a server was what a webserver even was...\nI always thought the \"webserver\" was just \"a server that hosts a website\"... and yes, that's true, but also it wasn't true in how I understood \"server\".\nIt turns out that across my 40-odd dockerized services I have at home that I must have about 40-odd web servers running, each docker container is spinning up its own!\n\nSo something I have wanted to do for a long time is put my theology notes online for my small group to access whenever they might want... it doesn't need to be fancy or anything.\nMy issue was not knowing what to even Google. I tried \"How to serve up static html\" but that kind of search is for people who know what a \"static\" site is - I am not one of those people.\nI kept running across nginx and apache things, wordpress and other website building tools, etc.\nIn fact I only recently learned that JavaScript assets cann still be considered static so I am a complete baby in the web-dev space.\n\nWhat I really wanted was just a simple landing page with a link to each of my \"posts\" which are in the form of a single html file each that I can easily export from my tiddlywiki (I have a post about tiddlywiki [here](/tiddly-wiki))\n\nThe first win `python -m http.server` right in the directory I kept my html files in and that got me what I wanted functionally. \nBut then I wanted just a hair more organization...\nI started looking for a way to dynamically generate an index for a directory of html files but again the verbiage of that Google search just wasn't helping me - I didn't want anything complicated and I knew that what I wanted had to be easy...\n\n# The Index \n\nLuckily I randomly came across a SO that mentioned a Linux utility called `tree` which does exactly what I wanted!\n\nSee my TIL on `tree` [here]('/tree')\n\nSo now it goes like this:\n\n* Take notes on X in my tiddlywiki\n* Export that tiddler to a html file \n* Put that html file into a `notes` folder in my github repo for small group notes \n* Use `tree` to generate an `index.html` of each of those files in the `notes` directory\n* Use `python -m http.server` to start a web server that lands me at the `index.html` and now I can click through to any post!\n\nIt's not fancy but it's functional... \nThis site/blog is built with markdown and [markata](https://www.markata.dev) and I wanted way more functionality in my tech notes.\nBut for this simple use case I learned a ton about _how_ content gets served up on a webpage and my small group benefits from the easy access as well!\n",
      "summary": "I host a lot of services in my homelab, but they're mostly dockerized applications so I have never had to care much about how content gets served up. Today I...",
      "date_published": "2022-03-06T00:00:00Z",
      "date_modified": "2022-03-06T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/tree/",
      "url": "https://pype.dev/tree/",
      "title": "Tree",
      "content_html": "\u003cp\u003eI wanted a quick way to generate an \u003ccode\u003eindex.html\u003c/code\u003e for a directory of html files that grows by 1 or 2 files a week.\nI don\u0026rsquo;t know any html (the files are exports from my \u003ca href=\"/tiddly-wiki\"\u003etiddlywiki\u003c/a\u003e)\u0026hellip;\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003etree\u003c/code\u003e is just the answer.\u003c/p\u003e\n\u003cp\u003eSay I have a file structure like this:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e./html-files\n├── file1.html\n└── file2.html\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eTo generate a barebones simple \u003ccode\u003eindex.html\u003c/code\u003e we can use tree as follows:\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003etree ./html-files -H \u0026quot;.\u0026quot; -L 1 -P \u0026quot;*.html\u0026quot;\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eand get the following:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e\u0026lt;!DOCTYPE html\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ehtml\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ehead\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003emeta\u003c/span\u003e \u003cspan class=\"na\"\u003ehttp-equiv\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Content-Type\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003econtent\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;text/html; charset=UTF-8\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003emeta\u003c/span\u003e \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Author\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003econtent\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Made by \u0026#39;tree\u0026#39;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003emeta\u003c/span\u003e \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;GENERATOR\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003econtent\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;$Version: $ tree v1.8.0 (c) 1996 - 2018 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro $\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003eDirectory Tree\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003estyle\u003c/span\u003e \u003cspan class=\"na\"\u003etype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;text/css\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"o\"\u003e\u0026lt;!\u003c/span\u003e\u003cspan class=\"nt\"\u003e--\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003eBODY\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003efont-family\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eariel\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003emonospace\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003esans-serif\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003eP\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003efont-weight\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003efont-family\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eariel\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003emonospace\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003esans-serif\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eblack\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003eB\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003efont-weight\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eblack\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003eA\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"nd\"\u003evisited\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003efont-weight\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003etext-decoration\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enone\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003emargin\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003epadding\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edisplay\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003einline\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003eA\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"nd\"\u003elink\u003c/span\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003efont-weight\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003etext-decoration\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enone\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003emargin\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003epadding\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edisplay\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003einline\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003eA\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"nd\"\u003ehover\u003c/span\u003e   \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mh\"\u003e#000000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003efont-weight\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003etext-decoration\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eunderline\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eyellow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003emargin\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003epadding\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edisplay\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003einline\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003eA\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"nd\"\u003eactive\u003c/span\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mh\"\u003e#000000\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003efont-weight\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003emargin\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003epadding\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"kt\"\u003epx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edisplay\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003einline\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eVERSION\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003efont-size\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003esmall\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003efont-family\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003earial\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003esans-serif\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eNORM\u003c/span\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eblack\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eFIFO\u003c/span\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003epurple\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eCHAR\u003c/span\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eyellow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eDIR\u003c/span\u003e   \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eblue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eBLOCK\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eyellow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eLINK\u003c/span\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eaqua\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eSOCK\u003c/span\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003efuchsia\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eEXEC\u003c/span\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003egreen\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"k\"\u003ebackground-color\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etransparent\u003c/span\u003e\u003cspan class=\"p\"\u003e;}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e--\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003estyle\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ehead\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eh1\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003eDirectory Tree\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003eh1\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;.\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e.\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        ├── \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;./file1.html\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003efile1.html\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        └── \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;./file2.html\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003efile2.html\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e0 directories, 2 files\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ehr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e \u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;VERSION\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 tree v1.8.0 © 1996 - 2018 by Steve Baker and Thomas Moore \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 HTML output hacked and copyleft © 1998 by Francesc Rocher \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 JSON output hacked and copyleft © 2014 by Florian Sesser \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebr\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 Charsets / OS/2 support © 2001 by Kyosuke Tokoro\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ehtml\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003ewhich \u003ca href=\"/tree-index-example.html\"\u003elooks like this\u003c/a\u003e when you serve it up with \u003ccode\u003epython -m http.server\u003c/code\u003e\u003c/p\u003e\n",
      "content_text": "\nI wanted a quick way to generate an `index.html` for a directory of html files that grows by 1 or 2 files a week.\nI don't know any html (the files are exports from my [tiddlywiki](/tiddly-wiki))...\n\n`tree` is just the answer.\n\nSay I have a file structure like this:\n\n```\n./html-files\n├── file1.html\n└── file2.html\n```\n\nTo generate a barebones simple `index.html` we can use tree as follows:\n\n`tree ./html-files -H \".\" -L 1 -P \"*.html\"`\n\nand get the following:\n\n```html\n\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n \u003cmeta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"\u003e\n \u003cmeta name=\"Author\" content=\"Made by 'tree'\"\u003e\n \u003cmeta name=\"GENERATOR\" content=\"$Version: $ tree v1.8.0 (c) 1996 - 2018 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro $\"\u003e\n \u003ctitle\u003eDirectory Tree\u003c/title\u003e\n \u003cstyle type=\"text/css\"\u003e\n  \u003c!--\n  BODY { font-family : ariel, monospace, sans-serif; }\n  P { font-weight: normal; font-family : ariel, monospace, sans-serif; color: black; background-color: transparent;}\n  B { font-weight: normal; color: black; background-color: transparent;}\n  A:visited { font-weight : normal; text-decoration : none; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n  A:link    { font-weight : normal; text-decoration : none; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n  A:hover   { color : #000000; font-weight : normal; text-decoration : underline; background-color : yellow; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n  A:active  { color : #000000; font-weight: normal; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n  .VERSION { font-size: small; font-family : arial, sans-serif; }\n  .NORM  { color: black;  background-color: transparent;}\n  .FIFO  { color: purple; background-color: transparent;}\n  .CHAR  { color: yellow; background-color: transparent;}\n  .DIR   { color: blue;   background-color: transparent;}\n  .BLOCK { color: yellow; background-color: transparent;}\n  .LINK  { color: aqua;   background-color: transparent;}\n  .SOCK  { color: fuchsia;background-color: transparent;}\n  .EXEC  { color: green;  background-color: transparent;}\n  --\u003e\n \u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n        \u003ch1\u003eDirectory Tree\u003c/h1\u003e\u003cp\u003e\n        \u003ca href=\".\"\u003e.\u003c/a\u003e\u003cbr\u003e\n        ├── \u003ca href=\"./file1.html\"\u003efile1.html\u003c/a\u003e\u003cbr\u003e\n        └── \u003ca href=\"./file2.html\"\u003efile2.html\u003c/a\u003e\u003cbr\u003e\n        \u003cbr\u003e\u003cbr\u003e\n        \u003c/p\u003e\n        \u003cp\u003e\n\n0 directories, 2 files\n        \u003cbr\u003e\u003cbr\u003e\n        \u003c/p\u003e\n        \u003chr\u003e\n        \u003cp class=\"VERSION\"\u003e\n                 tree v1.8.0 © 1996 - 2018 by Steve Baker and Thomas Moore \u003cbr\u003e\n                 HTML output hacked and copyleft © 1998 by Francesc Rocher \u003cbr\u003e\n                 JSON output hacked and copyleft © 2014 by Florian Sesser \u003cbr\u003e\n                 Charsets / OS/2 support © 2001 by Kyosuke Tokoro\n        \u003c/p\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n\n```\n\n\nwhich [looks like this](/tree-index-example.html) when you serve it up with `python -m http.server`\n",
      "summary": "I wanted a quick way to generate an for a directory of html files that grows by 1 or 2 files a week. I don't know any html (the files are exports from my...",
      "date_published": "2022-03-06T00:00:00Z",
      "date_modified": "2022-03-06T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/pandas-select-dtypes/",
      "url": "https://pype.dev/pandas-select-dtypes/",
      "title": "Pandas-Select-Dtypes",
      "content_html": "\u003cp\u003eOn my team we often have to change data types of columns in a \u003ccode\u003epandas.DataFrame\u003c/code\u003e for a variety of reasons.\nThe main one is it tends to be an artifact of EDA whereby a file is read in via \u003ccode\u003epandas\u003c/code\u003e but the data types are somewhat wonky (ie. dates show up as strings, or a column that \u003cem\u003eshould\u003c/em\u003e be a integer comes in as float, etc.).\nThe best solution I think is to leverage the \u003ccode\u003edtypes\u003c/code\u003e keyword argument in which \u003ccode\u003epd.read_X\u003c/code\u003e method is used.\nHowever there is another way which is to coerce the data types at runtime instead of loadtime.\u003c/p\u003e\n\u003cp\u003eA handy way to do this is by using \u003ccode\u003epandas.DataFrame.select_dtypes\u003c/code\u003e\u0026hellip;\u003c/p\u003e\n\u003cp\u003eHere is an example of finding columns read in as \u003ccode\u003edatetime64\u003c/code\u003e and the developer would prefer to use pandas datetimes.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003edf\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epd\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eread_csv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;./file-with-confusing-dtypes.csv\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecolumns\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edtype\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;datetime64\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epd\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_datetime\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eHere is the difference in code flow between \u003ccode\u003eselect_dtypes\u003c/code\u003e and manually finding the \u003ccode\u003edatetype64\u003c/code\u003e columns:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003edf\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epd\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eread_csv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;./file-with-confusing-dtypes.csv\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselect_dtypes\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;datetime64\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epd\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_datetime\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edf\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe difference isn\u0026rsquo;t huge but it\u0026rsquo;s the little steps in leveling up that turn script-kitty scripts into clean looking functions.\u003c/p\u003e\n",
      "content_text": "\nOn my team we often have to change data types of columns in a `pandas.DataFrame` for a variety of reasons.\nThe main one is it tends to be an artifact of EDA whereby a file is read in via `pandas` but the data types are somewhat wonky (ie. dates show up as strings, or a column that *should* be a integer comes in as float, etc.).\nThe best solution I think is to leverage the `dtypes` keyword argument in which `pd.read_X` method is used. \nHowever there is another way which is to coerce the data types at runtime instead of loadtime.\n\nA handy way to do this is by using `pandas.DataFrame.select_dtypes`...\n\nHere is an example of finding columns read in as `datetime64` and the developer would prefer to use pandas datetimes.\n\n```python\ndf = pd.read_csv(\"./file-with-confusing-dtypes.csv\")\nfor c in df.columns:\n    if df[c].dtype == \"datetime64\":\n        df[c] = pd.to_datetime(df.c)\n\n```\n\nHere is the difference in code flow between `select_dtypes` and manually finding the `datetype64` columns:\n\n```python\ndf = pd.read_csv(\"./file-with-confusing-dtypes.csv\")\nfor c in df.select_dtypes('datetime64'):\n    df[c] = pd.to_datetime(df.c)\n\n```\n\n\nThe difference isn't huge but it's the little steps in leveling up that turn script-kitty scripts into clean looking functions.\n",
      "summary": "On my team we often have to change data types of columns in a for a variety of reasons. The main one is it tends to be an artifact of EDA whereby a file is...",
      "date_published": "2022-03-05T00:00:00Z",
      "date_modified": "2022-03-05T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "python",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/vim-auto-space/",
      "url": "https://pype.dev/vim-auto-space/",
      "title": "Vim-Auto-Space",
      "content_html": "\u003cp\u003eI ran into an issue where I had some copy-pasta markdown tables in a docstring but the generator I used to make the table gave me tabs instead of spaces in odd places which caused \u003ccode\u003eblack\u003c/code\u003e to throw a fit.\nInstead of manually changing all tabs to spaes, or trying some goofy \u003ccode\u003e:%s/\u0026lt;magic tab character\u0026gt;/\u0026lt;%20 maybe?\u0026gt;/g\u003c/code\u003e I learned that Vim has my back\u0026hellip;\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e:retab\n\u003c/code\u003e\u003c/pre\u003e\n",
      "content_text": "\nI ran into an issue where I had some copy-pasta markdown tables in a docstring but the generator I used to make the table gave me tabs instead of spaces in odd places which caused `black` to throw a fit.\nInstead of manually changing all tabs to spaes, or trying some goofy `:%s/\u003cmagic tab character\u003e/\u003c%20 maybe?\u003e/g` I learned that Vim has my back...\n\n```\n:retab\n```\n",
      "summary": "I ran into an issue where I had some copy-pasta markdown tables in a docstring but the generator I used to make the table gave me tabs instead of spaces in...",
      "date_published": "2022-03-04T00:00:00Z",
      "date_modified": "2022-03-04T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "vim",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/stow/",
      "url": "https://pype.dev/stow/",
      "title": "Stow",
      "content_html": "\u003cp\u003eStow is a great tool for managing dotfiles. My usage looks like cloning my dotfiles to my home directory, setting some environment variables via a script, then stowing relevant packages and boom my config is good to go\u0026hellip;\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e ~\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit clone \u0026lt;my dotfiles repo\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e dotfiles\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# env variable stuff ignored here\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003estow zsh  \u003cspan class=\"c1\"\u003e# This will symlink my .zshrc file which is in ~/dotfiles/zsh to ~/.zshrc\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eBy default stow will stow packages up one directory from the root directory.\nIn this example the root directory is \u003ccode\u003e~/dotfiles\u003c/code\u003e and the package is \u003ccode\u003ezsh\u003c/code\u003e.\nSo the files in the \u003ccode\u003ezsh\u003c/code\u003e package will symlinked into \u003ccode\u003e~/\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003estow\u003c/code\u003e makes it easy to share dotfiles across machines, or safely experiment with config changes while always being protected by \u003ccode\u003egit\u003c/code\u003e since your dotfiles are in a git repo!\n\u0026hellip;They are in a git repo\u0026hellip; right?\u003c/p\u003e\n",
      "content_text": "\nStow is a great tool for managing dotfiles. My usage looks like cloning my dotfiles to my home directory, setting some environment variables via a script, then stowing relevant packages and boom my config is good to go...\n\n```bash\ncd ~\ngit clone \u003cmy dotfiles repo\u003e\ncd dotfiles\n# env variable stuff ignored here\nstow zsh  # This will symlink my .zshrc file which is in ~/dotfiles/zsh to ~/.zshrc\n```\nBy default stow will stow packages up one directory from the root directory. \nIn this example the root directory is `~/dotfiles` and the package is `zsh`.\nSo the files in the `zsh` package will symlinked into `~/`.\n\n`stow` makes it easy to share dotfiles across machines, or safely experiment with config changes while always being protected by `git` since your dotfiles are in a git repo!\n...They are in a git repo... right?\n",
      "summary": "Stow is a great tool for managing dotfiles. My usage looks like cloning my dotfiles to my home directory, setting some environment variables via a script,...",
      "date_published": "2022-03-04T00:00:00Z",
      "date_modified": "2022-03-04T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "bash",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/stow-target/",
      "url": "https://pype.dev/stow-target/",
      "title": "Stow-Target",
      "content_html": "\u003cp\u003eCheck out \u003ca href=\"/stow\"\u003estow\u003c/a\u003e for a brief introduction to \u003ccode\u003estow\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eWhat if I want to stow a package somewhere else?\nBoom, that\u0026rsquo;s where \u003ccode\u003e-t\u003c/code\u003e comes in\u0026hellip;\u003c/p\u003e\n\u003cp\u003eMaybe I don\u0026rsquo;t like having my \u003ccode\u003edotfiles\u003c/code\u003e repo at \u003ccode\u003e$HOME\u003c/code\u003e and instead I want it in \u003ccode\u003e~/git\u003c/code\u003e or \u003ccode\u003e~/personal\u003c/code\u003e just to stay organized\u0026hellip;\nWell then I could have the same workflow except the \u003ccode\u003estow\u003c/code\u003e command looks like this:\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003estow zsh -t ~/\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#or\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003estow zsh -t \u003cspan class=\"nv\"\u003e$HOME\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nCheck out [stow](/stow) for a brief introduction to `stow`\n\nWhat if I want to stow a package somewhere else?\nBoom, that's where `-t` comes in...\n\nMaybe I don't like having my `dotfiles` repo at `$HOME` and instead I want it in `~/git` or `~/personal` just to stay organized...\nWell then I could have the same workflow except the `stow` command looks like this:\n\n```bash\nstow zsh -t ~/\n#or\nstow zsh -t $HOME\n```\n",
      "summary": "Check out stow for a brief introduction to",
      "date_published": "2022-03-04T00:00:00Z",
      "date_modified": "2022-03-04T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "bash",
        "linux",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/git-ammend-no-edit/",
      "url": "https://pype.dev/git-ammend-no-edit/",
      "title": "Git ammend to a commit",
      "content_html": "\u003cp\u003eAfter carefully staging only lines related to a specific change and comitting I suddenly realized I missed one\u0026hellip; darn, what do I do?\u003c/p\u003e\n\u003cp\u003eOld me would have soft reset my branch to the previous commit and redone all my careful staging\u0026hellip; what a PIA\u0026hellip;\u003c/p\u003e\n\u003cp\u003eNew me (credit: ThePrimeagen)\u0026hellip;\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# stage other changes I missed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit commit --amend --no-edit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e",
      "content_text": "\nAfter carefully staging only lines related to a specific change and comitting I suddenly realized I missed one... darn, what do I do?\n\nOld me would have soft reset my branch to the previous commit and redone all my careful staging... what a PIA...\n\nNew me (credit: ThePrimeagen)...\n\n```bash\n# stage other changes I missed\ngit commit --amend --no-edit\n```\n",
      "summary": "After carefully staging only lines related to a specific change and comitting I suddenly realized I missed one... darn, what do I do?",
      "date_published": "2022-03-04T00:00:00Z",
      "date_modified": "2022-03-04T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "git",
        "tech",
        "til"
      ]
    },
    {
      "id": "https://pype.dev/ubuntu-static-ip/",
      "url": "https://pype.dev/ubuntu-static-ip/",
      "title": "Ubuntu-Static-Ip",
      "content_html": "\u003cp\u003eSometimes I need to manually set a static IP of a Linux machine. I generally run the latest version of Ubuntu server in my VMs at home.\u003c/p\u003e\n\u003cp\u003eIn Ubuntu 20 I\u0026rsquo;m able to change up \u003ccode\u003e/etc/netplan/\u0026lt;something\u0026gt;.yml\u003c/code\u003e\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003enetwork\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eversion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eethernets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenp0s4\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eaddresses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1\u003c/span\u003e\u003cspan class=\"l\"\u003e.{Static IP}/24]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003egateway4\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1.1\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003enameservers\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eaddresses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e192.168.1.1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e1.1.1.1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003egateway4\u003c/code\u003e is your router address\n\u003ccode\u003enameservers\u003c/code\u003e is a list of desired DNS servers for that machine to use. I  usually use my router which is configured to use my pi-hole as my primary DNS, then set  \u003ccode\u003e1.1.1.1\u003c/code\u003e (CloudFlare) as a backup\u003c/p\u003e\n\u003cp\u003eHit it with the \u003ccode\u003esudo netplan apply\u003c/code\u003e and you should be good to go!\u003c/p\u003e\n",
      "content_text": "\nSometimes I need to manually set a static IP of a Linux machine. I generally run the latest version of Ubuntu server in my VMs at home.\n\nIn Ubuntu 20 I'm able to change up `/etc/netplan/\u003csomething\u003e.yml`\n\n```yaml\nnetwork:\n  version: 2\n  ethernets:\n    enp0s4:\n      addresses: [192.168.1.{Static IP}/24]\n      gateway4: 192.168.1.1\n      nameservers:\n        addresses: [192.168.1.1, 1.1.1.1]\n```\n\n`gateway4` is your router address\n`nameservers` is a list of desired DNS servers for that machine to use. I  usually use my router which is configured to use my pi-hole as my primary DNS, then set  `1.1.1.1` (CloudFlare) as a backup\n\nHit it with the `sudo netplan apply` and you should be good to go!\n",
      "summary": "Sometimes I need to manually set a static IP of a Linux machine. I generally run the latest version of Ubuntu server in my VMs at home.",
      "date_published": "2022-03-03T00:00:00Z",
      "date_modified": "2022-03-03T00:00:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "linux",
        "til"
      ]
    }
  ]
}