My Today I Learned snippets. Inspired by jbranchaud/til, which I spotted on Hacker News.
Search these TILs at https://til.simonwillison.net/
386 TILs so far. Atom feed here.- Only run GitHub Action on push to master / main - 2020-04-19
- Dump out all GitHub Actions context - 2020-04-19
- Set environment variables for all steps in a GitHub Action - 2020-04-19
- Commit a file if it changed - 2020-04-19
- Running different steps on a schedule - 2020-04-20
- Updating a Markdown table of contents with a GitHub Action - 2020-07-22
- Using grep to write tests in CI - 2020-08-19
- Skipping a GitHub Actions step without failing - 2020-08-22
- Open a debugging shell in GitHub Actions with tmate - 2020-09-14
- Talking to a PostgreSQL service container from inside a Docker container - 2020-09-18
- Using Prettier to check JavaScript code style in GitHub Actions - 2020-12-31
- Running tests against PostgreSQL in a service container - 2021-02-23
- Installing different PostgreSQL server versions in GitHub Actions - 2021-07-05
- Attaching a generated file to a GitHub release using Actions - 2021-09-07
- Storing files in an S3 bucket between GitHub Actions runs - 2021-12-07
- Testing against Python 3.11 preview using GitHub Actions - 2022-02-02
- Using the GitHub Actions cache with npx and no package.json - 2022-03-22
- Deploying a live Datasette demo when the tests pass - 2022-03-27
- GitHub Actions job summaries - 2022-05-17
- Optimizing PNGs in GitHub Actions using Oxipng - 2022-05-18
- Conditionally running a second job in a GitHub Actions workflow - 2022-07-11
- Ensure labels exist in a GitHub repository - 2022-09-25
- actions/setup-python caching for setup.py projects - 2022-11-28
- Convert a datetime object to UTC without using pytz - 2020-04-19
- macOS Catalina sort-of includes Python 3 - 2020-04-21
- Generated a summary of nested JSON data - 2020-04-28
- Installing and upgrading Datasette plugins with pipx - 2020-05-04
- Use setup.py to install platform-specific dependencies - 2020-05-05
- Build the official Python documentation locally - 2020-05-08
- Introspecting Python function parameters - 2020-05-27
- Password hashing in Python with pbkdf2 - 2020-07-13
- How to call pip programatically from Python - 2020-08-11
- Outputting JSON with reduced floating point precision - 2020-08-21
- Debugging a Click application using pdb - 2020-09-03
- Understanding option names in Click - 2020-09-22
- Explicit file encodings using click.File - 2020-10-16
- Decorators with optional arguments - 2020-10-28
- Running Python code in a subprocess with a time limit - 2020-12-06
- Controlling the style of dumped YAML using PyYAML - 2020-12-07
- Relinquishing control in Python asyncio - 2020-12-29
- Packaging a Python app as a standalone binary with PyInstaller - 2021-01-04
- Handling CSV files with wide columns in Python - 2021-02-15
- Using io.BufferedReader to peek against a non-peekable stream - 2021-02-15
- Tracing every executed Python statement - 2021-03-21
- Check spelling using codespell - 2021-08-03
- Find local variables in the traceback for an exception - 2021-08-09
- Using Fabric with an SSH public key - 2021-10-06
- Using the sqlite3 Python module in Pyodide - Python WebAssembly - 2021-10-18
- Planning parallel downloads with TopologicalSorter - 2021-11-16
- Using cog to update --help in a Markdown README file - 2021-11-18
- Ignoring a line in both flake8 and mypy - 2021-11-30
- init_subclass - 2021-12-03
- Using C_INCLUDE_PATH to install Python packages - 2021-12-09
- Safely outputting JSON - 2021-12-17
- Annotated explanation of David Beazley's dataklasses - 2021-12-19
- Streaming indented output of a JSON array - 2022-01-17
- Generating a calendar week grid with the Python Calendar module - 2022-03-31
- Efficiently copying a file - 2022-05-13
- Freezing requirements with pip-tools - 2022-07-14
- struct endianness in Python - 2022-07-28
- Defining setup.py dependencies using a URL - 2022-08-13
- Running PyPy on macOS using Homebrew - 2022-09-14
- Using psutil to investigate "Too many open files" - 2022-10-13
- Simple load testing with Locust - 2022-10-22
- os.remove() on Windows fails if the file is already open - 2022-10-25
- The pdb interact command - 2022-10-31
- Upgrading a pipx application to an alpha version - 2023-01-11
- Installing lxml for Python on an M1/M2 Mac - 2023-01-27
- Calculating embeddings with gtr-t5-large in Python - 2023-01-31
- Running a Python ASGI app on Vercel - 2020-04-19
- Redirecting all paths on a Vercel instance - 2021-03-27
- Lag window function in SQLite - 2020-04-19
- Null case comparisons in SQLite - 2020-04-21
- Compile a new sqlite3 binary on Ubuntu - 2020-04-30
- List all columns in a SQLite database - 2020-05-06
- Using LD_PRELOAD to run any version of SQLite with Python - 2020-06-17
- SQLite BLOB literals - 2020-07-29
- Enabling WAL mode for SQLite database files - 2020-08-09
- Compiling the SQLite spellfix.c module on macOS - 2020-09-19
- Figuring out if a text value in SQLite is a valid integer or float - 2020-09-27
- Replicating SQLite with rqlite - 2020-12-28
- Identifying column combination patterns in a SQLite table - 2021-01-12
- Fixing broken text encodings with sqlite-transform and ftfy - 2021-01-18
- Splitting on commas in SQLite - 2021-02-01
- Querying for items stored in UTC that were created on a Thursday in PST - 2021-03-12
- Using pysqlite3 on macOS - 2021-07-10
- Importing CSV data into SQLite with .import - 2021-07-13
- SQLite aggregate filter clauses - 2021-08-04
- Building a specific version of SQLite with pysqlite on macOS/Linux - 2021-08-14
- Track timestamped changes to a SQLite table using triggers - 2021-08-19
- json_extract() path syntax in SQLite - 2022-01-18
- Ordered group_concat() in SQLite - 2022-02-06
- Combining substr and instr to extract text - 2022-02-15
- The simplest recursive CTE - 2022-03-20
- Counting SQLite virtual machine operations - 2022-03-20
- One-liner for running queries against CSV files with SQLite - 2022-06-20
- Related content with SQLite FTS and a Datasette template function - 2022-07-31
- Trying out SQLite extensions on macOS - 2022-08-03
- Sort by number of JSON intersections - 2022-08-17
- Seeing which functions are unique to a specific SQLite / Datasette instance - 2022-08-23
- SQLite VACUUM: database or disk is full - 2022-08-29
- Returning related rows in a single SQL query using JSON - 2022-09-16
- Finding the SQLite version used by Web SQL in Chrome - 2022-10-28
- SQLite can use more than one index for a query - 2022-12-11
- Comparing database rows before and after with SQLite JSON functions - 2022-12-14
- Geopoly in SQLite - 2023-01-04
- Loading SQLite extensions in Python on macOS - 2023-01-07
- SQLite pragma_function_list() - 2023-01-27
- Combining CTEs and VALUES in SQLite - 2023-01-29
- The SQLite now argument is stable within the same query - 2023-02-05
- Subqueries in select expressions in SQLite - also window functions - 2023-02-08
- Running pip install -e .[test] in zsh on macOS Catalina - 2020-04-21
- Get Skitch working on Catalina - 2020-04-21
- Close terminal window on Ctrl+D for macOS - 2020-04-21
- Fixing "compinit: insecure directories" error - 2020-04-26
- Finding the largest SQLite files on a Mac - 2020-08-19
- Shrinking PNG files with pngquant and oxipng - 2021-02-07
- Running Docker on an M1 Mac - 2021-05-25
- Using lsof on macOS - 2021-12-11
- Installing Python on macOS with the official Python installer - 2022-02-28
- Trick Apple Photos into letting you access your video files - 2022-04-12
- Seeing files opened by a process using opensnoop - 2022-04-26
- Atuin for zsh shell history in SQLite - 2022-04-26
- Driving an external display from a Mac laptop - 2022-09-10
- Browse files (including SQLite databases) on your iPhone with ifuse - 2022-09-13
- sips: Scriptable image processing system - 2023-02-18
- Use labels on Cloud Run services for a billing breakdown - 2020-04-21
- How to deploy a folder with a Dockerfile to Cloud Run - 2020-08-04
- Using the gcloud run services list command - 2020-09-01
- Listing files uploaded to Cloud Build - 2021-04-14
- Switching between gcloud accounts - 2021-05-18
- Increasing the time limit for a Google Cloud Scheduler task - 2021-07-08
- Tailing Google Cloud Run request logs and importing them into SQLite - 2021-08-09
- Using build-arg variables with Cloud Run deployments - 2021-11-19
- Session-scoped temporary directories in pytest - 2020-04-26
- How to mock httpx using pytest-mock - 2020-04-29
- Asserting a dictionary is a subset of another dictionary - 2020-05-28
- Registering temporary pluggy plugins inside tests - 2020-07-21
- Code coverage using pytest and codecov.io - 2020-08-15
- Start a server in a subprocess during a pytest session - 2020-08-31
- Using VCR and pytest with pytest-recording - 2021-11-02
- Quick and dirty mock testing with mock_calls - 2021-11-02
- Writing pytest tests against tools written with argparse - 2022-01-08
- Testing a Click app with streaming input - 2022-01-09
- Opt-in integration tests with pytest --integration - 2022-01-26
- pytest coverage with context - 2022-03-04
- Async fixtures with pytest-asyncio - 2022-03-19
- Treating warnings as errors in pytest - 2022-04-01
- Using pytest and Playwright to test a JavaScript web application - 2022-07-24
- Mocking a Textract LimitExceededException with boto - 2022-08-07
- Show files opened by pytest tests - 2022-12-11
- Accessing repository dependencies in the GitHub GraphQL API - 2020-04-30
- Paginating through the GitHub GraphQL API with Python - 2020-07-09
- Searching for repositories by topic using the GitHub GraphQL API - 2020-10-09
- Bulk fetching repository details with the GitHub GraphQL API - 2021-01-17
- Syntax highlighting Python console examples with GFM - 2021-01-18
- Transferring a GitHub issue from a private to a public repository - 2021-12-22
- Configuring Dependabot for a Python project with dependencies in setup.py - 2022-01-14
- Setting up a custom subdomain for a GitHub Pages site - 2022-05-04
- Reporting bugs in GitHub to GitHub - 2022-07-07
- Migrating a GitHub wiki from one repository to another - 2022-07-28
- Clone, edit and push files that live in a Gist - 2022-09-08
- GitHub Pages: The Missing Manual - 2022-10-31
- Finding uses of an API with the new GitHub Code Search - 2022-12-08
- Constant-time comparison of strings in Node - 2020-05-01
- Search across all loaded resources in Firefox - 2020-05-05
- Converting HTML and rich-text to Markdown - 2020-05-09
- Rendering Markdown with the GitHub Markdown API - 2020-08-22
- Useful Markdown extensions in Python - 2021-04-03
- Adding project links to PyPI - 2020-05-11
- Using heroku pg:pull to restore a backup to a macOS laptop - 2020-07-10
- Upgrading a Heroku PostgreSQL database with pg:copy - 2020-07-20
- Programatically accessing Heroku PostgreSQL from GitHub Actions - 2020-08-18
- Implementing a "copy to clipboard" button - 2020-07-23
- Working around the size limit for nodeValue in the DOM - 2020-08-21
- Dynamically loading multiple assets with a callback - 2020-08-21
- Minifying JavaScript with npx uglify-js - 2020-08-30
- Manipulating query strings with URLSearchParams - 2020-10-04
- Writing JavaScript that responds to media queries - 2020-10-21
- Dropdown menu with details summary - 2020-10-31
- Using Jest without a package.json - 2020-12-30
- Scroll page to form if there are errors - 2021-05-08
- Preventing double form submissions with JavaScript - 2021-07-08
- Loading lit from Skypack - 2021-09-21
- Using Tesseract.js to OCR every image on a page - 2021-11-09
- JavaScript date objects - 2022-01-16
- Creating a tiled zoomable image with OpenSeadragon and vips - 2022-08-12
- PostgreSQL full-text search in the Django Admin - 2020-07-25
- Adding extra read-only information to a Django admin change page - 2021-02-25
- Writing tests for the Django admin with pytest-django - 2021-03-02
- Show the timezone for datetimes in the Django admin - 2021-03-02
- Pretty-printing all read-only JSON in the Django admin - 2021-03-07
- How to almost get facet counts in the Django admin - 2021-03-11
- Efficient bulk deletions in Django - 2021-04-09
- Enabling the fuzzystrmatch extension in PostgreSQL with a Django migration - 2021-04-18
- Usable horizontal scrollbars in the Django admin for mouse users - 2021-04-20
- Filter by comma-separated values in the Django admin - 2021-04-21
- Django Admin action for exporting selected rows as CSV - 2021-04-25
- migrations.RunSQL.noop for reversible SQL migrations - 2021-05-02
- Enabling a gin index for faster LIKE queries - 2021-05-16
- Django data migration using a PostgreSQL CTE - 2021-05-17
- Using just with Django - 2022-06-06
- Adding a Datasette ASGI app to Django - 2022-10-20
- Attaching a bash shell to a running Docker container - 2020-08-10
- Running gdb against a Python process in a running Docker container - 2021-03-21
- Installing packages from Debian unstable in a Docker image based on stable - 2021-03-22
- Docker Compose for Django development - 2021-05-24
- Allowing a container in Docker Desktop for Mac to talk to a PostgreSQL server on the host machine - 2022-03-31
- Testing things in Fedora using Docker - 2022-07-27
- Emulating a big-endian s390x with QEMU - 2022-07-29
- Run pytest against a specific Python version using Docker - 2022-09-05
- Using pipenv and Docker - 2022-11-28
- Packaging a Python CLI tool for Homebrew - 2020-08-11
- Browsing your local git checkout of homebrew-core - 2020-08-27
- Upgrading Python Homebrew packages using pip - 2020-10-14
- Running a MySQL server using Homebrew - 2021-06-11
- Running the latest SQLite in Datasette using Homebrew - 2022-02-28
- Customizing my zsh prompt - 2020-08-12
- Passing command arguments using heredoc syntax - 2022-07-07
- Pointing a custom subdomain at Read the Docs - 2020-08-14
- Read the Docs Search API - 2020-08-16
- Using custom Sphinx templates on Read the Docs - 2020-12-07
- Promoting the stable version of the documentation using rel=canonical - 2022-01-20
- Linking from /latest/ to /stable/ on Read The Docs - 2022-01-20
- Creating a dynamic line chart with SVG - 2020-08-22
- Piping echo to a file owned by root using sudo and tee - 2020-08-24
- Basic strace to see what a process is doing - 2020-09-07
- Enabling a user to execute a specific command as root without a password - 2022-02-13
- Using iconv to convert the text encoding of a file - 2022-06-14
- Converting Airtable JSON for use with sqlite-utils using jq - 2020-08-28
- Loading radio.garden into SQLite using jq - 2021-02-17
- Flattening nested JSON objects with jq - 2021-03-11
- Converting no-decimal-point latitudes and longitudes using jq - 2021-03-11
- Turning an array of arrays into objects with jq - 2021-05-25
- Extracting objects recursively with jq - 2021-07-24
- Looping over comma-separated values in Bash - 2020-09-01
- Escaping strings in Bash using !:q - 2020-10-01
- Escaping a SQL query to use with curl and Datasette - 2020-12-08
- Skipping CSV rows with odd numbers of quotes using ripgrep - 2020-12-11
- Finding CSV files that start with a BOM using ripgrep - 2021-05-28
- nullglob in bash - 2022-02-14
- Using awk to add a prefix - 2022-04-08
- Ignoring errors in a section of a Bash script - 2022-06-27
- Start, test, then stop a localhost web server in a Bash script - 2022-12-17
- Very basic tsc usage - 2020-09-06
- Display EC2 instance costs per month - 2020-09-06
- Recovering data from AWS Lightsail using EC2 - 2021-01-16
- Adding a CORS policy to an S3 bucket - 2022-01-04
- Helper function for pagination using AWS boto3 - 2022-01-19
- Running OCR against a PDF file with AWS Textract - 2022-06-28
- Using boto3 from the command line - 2022-08-02
- Athena error: The specified key does not exist - 2022-09-27
- Querying newline-delimited JSON logs using AWS Athena - 2022-10-06
- Turning on Jinja autoescaping when using Template() directly - 2020-09-18
- Formatting thousands in Jinja - 2022-06-08
- Installing Selenium for Python on macOS with ChromeDriver - 2020-10-02
- Using async/await in JavaScript in Selenium - 2020-10-02
- Running Datasette on DigitalOcean App Platform - 2020-10-06
- Redirects for Datasette - 2020-11-25
- Serving MBTiles with datasette-media - 2021-02-03
- Querying for GitHub issues open for less than 60 seconds - 2021-03-12
- Running Datasette on Replit - 2021-05-02
- Searching all columns of a table in Datasette - 2021-08-23
- Reusing an existing Click tool with register_commands - 2021-11-29
- Crawling Datasette with Datasette - 2022-02-27
- Registering new Datasette plugin hooks by defining them in other plugins - 2022-06-17
- Writing a Datasette CLI plugin that mostly duplicates an existing command - 2022-10-22
- Writing a CLI utility that is also a Datasette plugin - 2022-11-21
- Embedding JavaScript in a Jupyter notebook - 2021-01-22
- Testing cookiecutter templates with pytest - 2021-01-27
- Conditionally creating directories in cookiecutter - 2021-01-27
- Downloading MapZen elevation tiles - 2021-02-04
- Natural Earth in SpatiaLite and Datasette - 2022-03-04
- Using sphinx.ext.extlinks for issue links - 2021-02-17
- Adding Sphinx autodoc to a project, and configuring Read The Docs to build it - 2021-08-10
- Format code examples in documentation with blacken-docs - 2022-04-24
- Show the SQL schema for a PostgreSQL database - 2021-02-23
- Granting a PostgreSQL user read-only access to some tables - 2021-02-26
- Closest locations to a point - 2021-03-22
- Using unnest() to use a comma-separated string as the input to an IN query - 2021-04-10
- Using json_extract_path in PostgreSQL - 2021-04-13
- Constructing GeoJSON in PostgreSQL - 2021-04-24
- How to run MediaWiki with SQLite on a macOS laptop - 2021-03-06
- Language-specific indentation settings in VS Code - 2021-04-04
- Search and replace with regular expressions in VS Code - 2021-08-02
- The Wikipedia page stats API - 2021-05-13
- Vega-Lite bar charts in the same order as the data - 2021-05-15
- KNN queries with SpatiaLite - 2021-05-16
- Creating a minimal SpatiaLite database with Python - 2021-12-17
- GUnion to combine geometries in SpatiaLite - 2022-04-12
- Viewing GeoPackage data with SpatiaLite and Datasette - 2022-12-11
- Finding duplicate records by matching name and nearby distance - 2021-05-19
- Building a Markdown summary of Django group permissions - 2021-06-03
- Cumulative total over time in SQL - 2021-09-13
- Using recursive CTEs to explore hierarchical Twitter threads - 2023-01-30
- Exporting Amplitude events to SQLite - 2021-06-06
- Mouse support in vim - 2021-06-19
- Scraping Reddit via their JSON API - 2021-06-21
- Using nginx to proxy to a Unix domain socket - 2021-07-10
- Using the tesseract CLI tool - 2021-07-18
- Set a GIF to loop using ImageMagick - 2021-08-03
- Compressing an animated GIF with gifsicle or ImageMagick mogrify - 2021-08-05
- Histogram with tooltips in Observable Plot - 2021-08-21
- Wider tooltip areas for Observable Plot - 2022-11-21
- Using the Chrome DevTools console as a REPL for an Electron app - 2021-08-31
- Open external links in an Electron app using the system browser - 2021-09-02
- Signing and notarizing an Electron app for distribution using GitHub Actions - 2021-09-08
- Bundling Python inside an Electron app - 2021-09-08
- Configuring auto-update for an Electron app - 2021-09-13
- Testing Electron apps with Playwright and GitHub Actions - 2022-07-13
- Publishing to a public Google Cloud bucket with gsutil - 2021-09-20
- Google OAuth for a CLI application - 2022-02-16
- Recursively fetching metadata for all files in a Google Drive folder - 2022-02-16
- Analyzing Google Cloud spend with Datasette - 2022-08-16
- Workaround for google-github-actions/setup-gcloud errors - 2022-12-01
- Removing a git commit and force pushing to remove it from history - 2021-10-22
- Rewriting a repo to contain the history of just specific files - 2022-03-22
- git bisect - 2022-10-29
- How to create a tarball of a git repository using "git archive" - 2022-11-15
- Rewriting a Git repo to remove secrets from the history - 2023-01-24
- Basic Datasette in Kubernetes - 2021-11-05
- kubectl proxy - 2021-12-28
- Assigning a custom subdomain to a Fly app - 2021-11-20
- Using the undocumented Fly GraphQL API - 2022-01-21
- Using the Fly Docker registry - 2022-05-21
- Writing Fly logs to S3 - 2022-05-25
- Wildcard DNS and SSL on Fly - 2022-05-25
- Deploying a redbean app to Fly - 2022-07-24
- How to scp files to and from Fly - 2022-09-02
- Pausing traffic and retrying in Caddy - 2021-11-24
- Publishing a Web Component to npm - 2021-11-28
- Annotated package.json for idb-keyval - 2022-02-10
- Upgrading packages with npm - 2022-07-13
- Adding a robots.txt using Cloudflare workers - 2021-12-21
- WebAuthn browser support - 2021-12-29
- Pixel editing a favicon with Pixelmator - 2022-01-20
- get-graphql-schema - 2022-02-01
- Using curl to run GraphQL queries from the command line - 2022-02-21
- GraphQL fragments - 2022-09-30
- Concatenating strings and newlines in Google Sheets - 2022-03-15
- Exporting and editing a Twitter Spaces recording - 2022-03-23
- How to get credentials for a new Twitter bot - 2022-04-17
- Loading Twitter Birdwatch into SQLite for analysis with Datasette - 2022-09-03
- Extracting web page content using Readability.js and shot-scraper - 2022-03-24
- shot-scraper for a subset of table columns - 2022-10-14
- Scraping the Sky News Westminster Accounts, a Flourish application - 2023-01-10
- Simplest possible OAuth authentication with Auth0 - 2022-03-26
- Logging users out of Auth0 - 2022-04-03
- Compiling to WASM with llvm on macOS - 2022-03-28
- Run Python code in a WebAssembly sandbox - 2023-02-02
- Intercepting fetch in a service worker - 2022-04-30
- Making HTTP calls using IPv6 - 2022-06-08
- Using GPT-3 to figure out jq recipes - 2022-08-10
- Guessing Amazon image URLs using GitHub Copilot - 2022-10-15
- Generating OpenAPI specifications using GPT-3 - 2022-11-13
- Writing tests with Copilot - 2022-11-14
- Reformatting text with Copilot - 2022-12-09
- A simple Python wrapper for the ChatGPT API - 2023-03-02
- Trying out Quarto on macOS - 2022-08-18
- Using DuckDB in Python to access Parquet data - 2022-09-16
- Deploying Python web apps as AWS Lambda functions - 2022-09-18
- Whisky sour - 2022-09-25
- Tommy's Margarita - 2022-10-02
- Pisco sour - 2022-10-08
- HTML video that loads when the user clicks play - 2022-09-29
- HTML datalist - 2022-11-14
- Lazy loading images in HTML - 2022-11-26
- Getting Mastodon running on a custom domain - 2022-11-02
- Export a Mastodon timeline to SQLite - 2022-11-04
- Verifying your GitHub profile on Mastodon - 2022-11-16
- Building Mastodon bots with GitHub Actions and toot - 2023-02-02
- JSON Pointer - 2022-11-14