Skip to content

Dispatches

Announcing the Packer Plugin for Artifactory

Today marks the v0.1.0 release of the Packer Plugin for Artifactory, my first standalone Packer plugin. This one's been on the list for a while.

The plugin adds an artifactory post-processor. After a Packer build produces an OVA or OVF, the post-processor uploads it straight to JFrog Artifactory. Build, export, upload, done. No handoff step, no separate script, no copy-pasting artifact paths into another job.

Primary targets are the vmware/vsphere and vmware/vmware Packer plugins, covering vSphere, Fusion, and Workstation. Any other builder that emits OVA or OVF should work too. Authentication supports username and password, API key, and access token. Custom properties let teams attach version, OS, and build metadata right from the post-processor block.

Announcing the Packer Plugin for VMware vSphere v2.3.0

Packer Plugin for VMware vSphere v2.3.0 was released on August 3, 2026.

This release expands how vsphere-clone starts a build. Inventory templates still work. So do content library items, and local or remote OVF/OVA packages. Remote sources support HTTP, HTTPS, and HTTPS with basic authentication. Pick the source that matches where the image already lives, then customize, provision, and publish as usual.

v2.3.0 also expands vSphere data sources for inventory lookups in HCL, ships project documentation at vmware.github.io/packer-plugin-vsphere, and includes reliability improvements, clearer validation, broader test coverage, and dependency updates.

How to Structure an Ansible Collection

Ansible

I recently wrapped up work on an Ansible collection I developed for a customer through my role at Broadcom. With the project complete, I wanted to write down the structure it followed and the reasons behind it. This guide is for developers building their first collection and maintainers checking an existing one against Ansible's recommended practices.

Those decisions matter once an Ansible repository has been in use for a while. Roles sit beside playbooks that depend on custom filters. Lookup plugins get copied from one repository to another. The same automation has to run locally and in Ansible Automation Platform (AAP), where repository-specific plugin paths become difficult to maintain.

A collection gives that content a package name and a standard layout. Roles, plugins, playbooks, documentation, and tests can ship together as a versioned package under a namespace such as example.platform. Once you understand where each part belongs, the structure is straightforward.

Field After Field, Song After Song

90s country music takes me back to Saturday mornings in Southwest Georgia, when my dad, my brother, and I would climb into an old two-toned orange and white 1979 Chevy truck we called "Timex," because that thing "took a lickin' and kept on tickin'"

No air conditioning. No mercy from the sun.

Just rolled-down windows, hot vinyl seats, sweaty towels, ham sandwiches, chips, and a cooler full of sodas sweating right along with us.

Those Saturdays, we drove hundreds of miles across several counties checking rain gauges for one of the local peanut companies. Each field had its own little measure of mercy standing out there in the dirt, waiting to tell us what the sky had given and what it withheld.

Some were off highways. Some were down dirt roads.

Some were tucked so far back it felt like the land had swallowed the road whole.

One of us would run the gauge while the other wrote down the number for the week. Then we'd reset it and move on.

Field after field. County after county.

The data went back to the peanut company so they could see which contracted farmers were getting rain, especially the dryland fields with no irrigation to save them, just like our own land.

And while those gauges weren't supposed to sit under a center pivot, more than once we found ourselves caught in the wrong place when one started walking across a field like some long-legged iron ghost.

The rain mattered. In that country, rain wasn't just weather. It was debt, crop, prayer, and judgment. It was the difference between holding on and going under.

And through all of it, the radio stayed on.

Most days it was 95.5 WTVY out of Dothan, Alabama, carrying itself through the cab like a fourth passenger on a crowded bench seat.

Alan Jackson, Aaron Tippin, Shenandoah, Sammy Kershaw, John Michael Montgomery, Joe Diffie, Clay Walker, Brooks and Dunn, George Strait, Garth Brooks, Clint Black, The Tractors, John Anderson, Faith Hill, George Jones, and so many more.

Their voices rode with us through the dust, the heat shimmer, the sudden storms, and the long stretches where nobody had much to say.

That music became part of the work. It was stitched into the smell of hot metal and burning oil, the slap of weeds and briars against our legs, the red dirt on our shoes, and the strange quiet that falls over farmland in the middle of a searing afternoon.

We saw farms like ours, farms with better equipment and bigger money, and farms trying hard to look stronger than they were.

We learned the roads. We learned the fields. We learned how people tried to survive out there without saying too much about what it cost them.

Those were hard, hot days, but I remember them with fondness. Whatever trouble time laid over our family, those drives remain one of the good rooms in my memory. It was just the three of us, making a little extra money, burning up in that old truck, laughing when we could, and letting those songs carry us from one rain gauge to the next on our map.

That's what 90s country means to me.

It's not nostalgia without weight. It's not some easy longing for a past that never hurt.

It's the sound of being young in a place that was already teaching me about labor, weather, family, want, and endurance.

It's the sound of looking out over peanut rows and imagining that life might someday open wider than those backroads, even as the land was busy marking me in ways I wouldn't understand until much later in life.

“Farmer’s Daughter” grew out of these memories. It's a playful homage to the country music I came up on, but it carries the dust of those Saturdays underneath it.

It's for the radio in that old orange and white '79, for the highways and backroads we learned by heart, for the rain we measured, for the heat we endured, and for the boy I was back then, riding through the fields and dreaming about what the future might sound like.

Development Containers: Consistent Environments for Every Contributor

Development Containers logo

Most engineering teams hit this eventually: a new developer clones the repository, follows the setup instructions, and spends the next two hours debugging why the toolchain that works on everyone else's machine doesn't work on theirs. The versions are slightly different. A system dependency is missing. The environment variable was never set. By the time they make their first commit, they've already burned half a day on tasks that had nothing to do with the code they were hired to write.

Development containers help solve this by codifying the development environment as a container image. Every contributor uses the same tools at the same versions on every machine. Onboarding becomes opening the repository instead of working through a long setup guide.

A development container, commonly called a "devcontainer", is a Docker container configured as a development environment. It holds the runtime, tools, extensions, and settings required to work on the project, and that configuration lives in the repository right next to the codebase.

The Development Containers specification defines the format that editor and platform integrations follow. VS Code, GitHub Codespaces, JetBrains Gateway, and the Dev Containers CLI all consume the same .devcontainer/ definition. A valid configuration should work across those entry points without forked setup docs.

Instead of a README.md that says "install x v1.24, y 1.11, and z v7.1.2," the repository includes a container image specification.

MCP Servers: A Practical Guide to Context on Demand

Model Context Protocol, or MCP, is how an AI client asks for context instead of waiting for you to shuttle it in. Clients can already expose open files, workspace context, and sometimes terminal output. For remote services, databases, CI runs, and output from another shell, you still feed things in by hand. MCP gives the client a shared protocol to discover tools, read resources, and reach those systems directly. If the LLM is doing the reasoning, an MCP server is the part that lets it look around and, when you allow it, act on current state.

The model doesn't have to rely only on whatever you remembered to supply it. It can ask for current state, call a tool you exposed, and come back with something closer to evidence.

Less autocomplete. Less guessing. More "I checked x and y, and here is what I found."

MCP is an open protocol for connecting AI applications to the systems around them.

An MCP server can expose a few kinds of things:

  • Tools: actions the model can ask to run, such as read_logs, run_tests, or a query you wrapped instead of exposing raw shell.
  • Resources: readable context, such as repository metadata, service health, runbooks, or log files.
  • Prompts: reusable prompt templates that package common workflows.

An MCP client, such as VS Code or Cursor, connects to one or more servers and surfaces those capabilities to the model.

Old Workflow MCP Workflow
Paste a log into a chat The model calls a read_logs tool
Explain your repository The client provides workspace context
Paste command output The server returns structured tool output
Ask the model to guess state The model asks the system for current state
Manually perform every action The model proposes or invokes approved tool calls

MCP doesn't make a model omniscient. It gives the model a way to ask for context and use the actions you chose to expose. That matters more once you've lived through the manual context loop a few dozen times.

Generating a Python SDK from an OpenAPI Specification

If your API ships an OpenAPI spec, OpenAPI Generator can build the Python client from it. You don't have to hand-write models and endpoint wrappers.

Most tutorials stop at generate. After that you're still choosing generator options, picking a spec with enough surface area to matter, locking dependencies, running smoke tests, and keeping the repo sane when the spec changes again.

This walkthrough uses a compact OpenAPI 3.1 example. I use Astral uv for lock, sync, test, and build because that's what I run locally. The generator config and generate steps are the same with pip or Poetry.

Making Sense of the Circle of Fifths

If the Circle of Fifths has always looked like one of those theory diagrams everyone swears is important but no one actually explains, you're not alone. The good news is that it's not really a chart about memorizing symbols. It's a map of how keys, chords, and musical motion relate to each other, and once you hear that on guitar, it starts feeling useful fast.

For a lot of players, the Circle of Fifths feels abstract because it gets introduced on paper instead of on the fretboard.

If my Decoding the Major and Minor Pentatonic Scales post was about understanding what's happening inside the pentatonic scale shapes, this one is about understanding why chords and keys pull the way they do around those scale shapes.

My goal here is to help you:

  • understand what the circle is actually showing you,
  • learn why moving by fifths sounds so strong in music,
  • connect nearby keys to nearby chord families, and
  • use the circle for progressions, songwriting, and practice.

Tip

The Circle of Fifths isn't mainly about memorizing key signatures. It's about seeing which keys are closely related, which chords want to lead to each other, and why certain progressions feel so natural.

What Is the Circle of Fifths?

At its core, the Circle of Fifths is just an ordered list of notes.

Start on C. Move up a perfect fifth and you get G. Move up another fifth and you get D.

Keep going and you get:

  • C
  • G
  • D
  • A
  • E
  • B
  • F#
  • Db
  • Ab
  • Eb
  • Bb
  • F
  • back to C

That's the circle.

If you move the other direction, you're moving by fourths, which lands on the same set of keys in reverse order.

"Why does that matter?"

Because music built from neighboring keys tends to share a lot of notes and chords. The closer two keys are on the circle, the more naturally they tend to connect.

Circle of Fifths Circle of Fifths

Guitar Translation

Think of the circle as a map of musical neighborhoods. Keys that sit next to each other are closely related. Keys across the circle feel more distant.

Why Fifths Sound So Strong

The interval of a fifth is one of the most stable and powerful sounds in music.

Guitar players already know this sound, even if they don't call it by name:

  • A basic power chord is built around the Root Note and the 5.
  • Power chords put the sound of the fifth under your fingers all over the neck.
  • A lot of strong chord movement in rock, blues, country, and pop follows fifth relationships!

The motion around the circle lines up with one of the most fundamental relationships in the instrument and in tonal music itself.

Tip

When you hear progressions like D - G - C or Em - Am - D - G, you're hearing the motion that lines up with the circle. And your ear recognizes that pull immediately.

Clockwise and Counterclockwise

One reason the circle matters is that it helps organize keys.

Moving clockwise:

  • goes up by fifths,
  • adds sharps, and
  • often feels like increasing harmonic brightness or tension

Moving counterclockwise:

  • goes up by fourths,
  • adds flats, and
  • often feels like a move toward warmer, flatter key areas

Here's a practical shorthand:

  • C has no sharps and no flats
  • one step clockwise, G, has 1 sharp
  • one more step, D, has 2 sharps
  • one step counterclockwise, F, has 1 flat
  • one more step, Bb, has 2 flats

Stop! You Don't Need to Memorize All of This at Once!

Start with just C, G, D, A on the sharp side and C, F, Bb on the flat side. That small map already explains a huge amount of real guitar music.

Relative Minor

The Circle of Fifths isn't only about major keys. Each major key has a Relative Minor that uses the same notes.

Examples:

  • C major and A minor
  • G major and E minor
  • D major and B minor

If that sounds familiar, it should. It's the same relationship we used in Decoding the Major and Minor Pentatonic Scales.

The circle helps you see those pairs as families:

  • major key on the outside
  • relative minor connected to the same note pool

That's useful when:

  • understanding songs that shift between major and minor moods,
  • finding related scales quickly, and
  • writing chord progressions that feel connected without feeling repetitive

Requests vs. HTTPX: Choosing a Python HTTP Client

requests earned its reputation honestly. For a long time, if you needed to call an API, download a file, or glue two services together in Python, requests was usually the obvious answer.

Async web frameworks, high-concurrency services, and I/O-heavy workloads changed what many teams need from an HTTP client. That's where httpx starts to matter.

This post compares requests and httpx from the point of view of a Python developer who already knows requests, wants a clear explanation of what httpx adds, and doesn't want hand-wavy "modernization" advice.

The Champion: requests

requests became the standard because it removed friction. Small API. Sensible defaults. Session when you need reuse.

  • The API is small and easy to learn.
  • The defaults are simple enough for scripts and internal tools.
  • Session gives you connection reuse and shared configuration without much ceremony.
  • The ecosystem around it is huge: tutorials, examples, blog posts, Stack Overflow answers, and third-party integrations all assume you know requests.

For synchronous Python code, it's still a good library. A lot of production code doesn't need async support, doesn't need HTTP/2, and doesn't need a broader transport model. In those cases, requests remains a sensible choice.

That's why this comparison isn't really about replacing a bad tool. It's about recognizing where the older tool stops fitting as cleanly.

The Challenger: httpx

httpx looks familiar on purpose. The synchronous API feels close enough to requests that most Python developers can read it immediately. That compatibility is one of its best features, because it lowers the cost of trying it.

Its two biggest advantages are:

  • Native async and await support through httpx.AsyncClient
  • Optional HTTP/2 support on both sync and async clients

Those aren't cosmetic differences.

Async support means httpx fits naturally into frameworks like FastAPI, Starlette, and any other async application that shouldn't block the event loop on outbound network calls. HTTP/2 support means one client connection can handle multiplexed requests when the remote server supports it, which can matter for high-concurrency workloads.

You opt in with httpx.Client(http2=True) or httpx.AsyncClient(http2=True), and the server still has to support HTTP/2 for you to get it.

httpx also pushes users toward safer networking defaults. The biggest example is timeouts: requests doesn't time out unless you ask it to, while httpx enforces timeouts by default.