Just a Theory

Black lives matter

Automate Postgres Extension Releases on GitHub and PGXN

Back in June, I wrote about testing Postgres extensions on multiple versions of Postgres using GitHub Actions. The pattern relies on Docker image, pgxn/pgxn-tools, which contains scripts to build and run any version of PostgreSQL, install additional dependencies, build, test, bundle, and release an extension. I’ve since updated it to support testing on the the latest development release of Postgres, meaning one can test on any major version from 8.4 to (currently) 14. I’ve also created GitHub workflows for all of my PGXN extensions (except for pgTAP, which is complicated). I’m quite happy with it.

But I was never quite satisfied with the release process. Quite a number of Postgres extensions also release on GitHub; indeed, Paul Ramsey told me straight up that he did not want to manually upload extensions like pgsql-http and PostGIS to PGXN, but for PGXN to automatically pull them in when they were published on GitHub. It’s pretty cool that newer packaging systems like pkg.go.dev auto-index any packages on GibHub. Adding such a feature to PGXN would be an interesting exercise.

But since I’m low on TUITs for such a significant undertaking, I decided instead to work out how to automatically publish a release on GitHub and PGXN via GitHub Actions. After experimenting for a few months, I’ve worked out a straightforward method that should meet the needs of most projects. I’ve proven the pattern via the pair extension’s release.yml, which successfully published the v0.1.7 release today on both GitHub and PGXN. With that success, I updated the pgxn/pgxn-tools documentation with a starter example. It looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
name: Release
on:
  push:
    tags:
      - 'v*' # Push events matching v1.0, v20.15.10, etc.
jobs:
  release:
    name: Release on GitHub and PGXN
    runs-on: ubuntu-latest
    container: pgxn/pgxn-tools
    env:
      # Required to create GitHub release and upload the bundle.
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
    - name: Check out the repo
      uses: actions/checkout@v2
    - name: Bundle the Release
      id: bundle
      run: pgxn-bundle
    - name: Release on PGXN
      env:
        # Required to release on PGXN.
        PGXN_USERNAME: ${{ secrets.PGXN_USERNAME }}
        PGXN_USERNAME: ${{ secrets.PGXN_PASSWORD }}
      run: pgxn-release
    - name: Create GitHub Release
      id: release
      uses: actions/create-release@v1
      with:
        tag_name: ${{ github.ref }}
        release_name: Release ${{ github.ref }}
        body: |
          Changes in this Release
          - First Change
          - Second Change          
    - name: Upload Release Asset
      uses: actions/upload-release-asset@v1
      with:
        # Reference the upload URL and bundle name from previous steps.
        upload_url: ${{ steps.release.outputs.upload_url }}
        asset_path: ./${{ steps.bundle.outputs.bundle }}
        asset_name: ${{ steps.bundle.outputs.bundle }}
        asset_content_type: application/zip

Here’s how it works:

  • Lines 4-5 trigger the workflow only when a tag starting with the letter v is pushed to the repository. This follows the common convention of tagging releases with version numbers, such as v0.1.7 or v4.6.0-dev. This assumes that the tag represents the commit for the release.

  • Line 10 specifies that the job run in the pgxn/pgxn-tools container, where we have our tools for building and releasing extensions.

  • Line 13 passes the GITHUB_TOKEN variable into the container. This is the GitHub personal access token that’s automatically set for every build. It lets us call the GitHub API via actions later in the workflow.

  • Step “Bundle the Release”, on Lines 17-19, validates the extension META.json file and creates the release zip file. It does so by simply reading the distribution name and version from the META.json file and archiving the Git repo into a zip file. If your process for creating a release file is more complicated, you can do it yourself here; just be sure to include an id for the step, and emit a line of text so that later actions know what file to release. The output should look like this, with $filename representing the name of the release file, usually $extension-$version.zip:

    ::set-output name=bundle::$filename
    
  • Step “Release on PGXN”, on lines 20-25, releases the extension on PGXN. We take this step first because it’s the strictest, and therefore the most likely to fail. If it fails, we don’t end up with an orphan GitHub release to clean up once we’ve fixed things for PGXN.

  • With the success of a PGXN release, step “Create GitHub Release”, on lines 26-35, uses the GitHub create-release action to create a release corresponding to the tag. Note the inclusion of id: release, which will be referenced below. You’ll want to customize the body of the release; for the pair extension, I added a simple make target to generate a file, then pass it via the body_path config:

    - name: Generate Release Changes
      run: make latest-changes.md
    - name: Create GitHub Release
      id: release
      uses: actions/create-release@v1
      with:
        tag_name: ${{ github.ref }}
        release_name: Release ${{ github.ref }}
        body_path: latest-changes.md
    
  • Step “Upload Release Asset”, on lines 36-43, adds the release file to the GitHub release, using output of the release step to specify the URL to upload to, and the output of the bundle step to know what file to upload.

Lotta steps, but works nicely. I only wish I could require that the testing workflow finish before doing a release, but I generally tag a release once it has been thoroughly tested in previous commits, so I think it’s acceptable.

Now if you’ll excuse me, I’m off to add this workflow to my other PGXN extensions.

George Washington Bridge Pier

View of the Manhattan pier of the George Washington Bridge, Taken on 20 September, 2020 with an iPhone Xs.

Blockhouse

The Blockhouse is a small fort in the North Woods of Central Park, constructed as one of a series of fortifications in norther Manhattan. Photo taken on 6 September 2020 with an iPhone Xs.

Biden on the Green New Deal

This exchange from first presidential debate a few days ago really struck me (from the Rev transcript):

President Donald J. Trump: (57:56)
So why didn’t you get the world… China sends up real dirt into the air. Russia does. India does. They all do. We’re supposed to be good. And by the way, he made a couple of statements. The Green New Deal is a hundred trillion dollars.
Vice President Joe Biden: (58:08)
That is not my plan [crosstalk]. The Green New Deal [crosstalk] is not my plan. [crosstalk]—

A hundred trillion dollars? As David Roberts of Vox points out, “US GDP is $21.44 trillion.” But I digress.

A bewildering back and forth followed (something about insulting the military), before moderator Chris Wallace managed to right the ship:

Chris Wallace: (58:53)
The Green New Deal and the idea of what your environmental changes will do—
Vice President Joe Biden: (58:57)
The Green New Deal will pay for itself as we move forward. We’re not going to build plants that, in fact, are great polluting plants—

This impressed the hell out of me. Shortly after saying the GND isn’t his plan, Biden starts to get into its policy details to defend it? Wow. I mean, he may not agree with it all, but to respond with, “okay, so you wanna talk about the Green New Deal? I’ve got all the details, let’s go!” Props to level of policy engagement.

But listening again jut now, I realize that I missed the next bit:

Chris Wallace: (59:05)
So, do you support the Green New Deal?
Vice President Joe Biden: (59:07)
Pardon me?
Chris Wallace: (59:08)
Do you support the—
Vice President Joe Biden: (59:08)
No, I don’t support the Green New Deal.
President Donald J. Trump: (59:10)
Oh, you don’t? Oh, well, that’s a big statement.
Vice President Joe Biden: (59:12)
I support [crosstalk]—
President Donald J. Trump: (59:13)
You just lost the radical left.
Vice President Joe Biden: (59:15)
I support [crosstalk] the Biden plan that I put forward.
Chris Wallace: (59:19)
Okay.
Vice President Joe Biden: (59:19)
The Biden plan, which is different than what he calls the radical Green New Deal.

He explicitly says that the GND not his plan and he doesn’t support it. When he said, “The Green New Deal will pay for itself as we move forward,” did he mean to say “The Biden Plan”? Digging a little deeper, I don’t think so. From the actual Biden Plan:

Biden believes the Green New Deal is a crucial framework for meeting the climate challenges we face. It powerfully captures two basic truths, which are at the core of his plan: (1) the United States urgently needs to embrace greater ambition on an epic scale to meet the scope of this challenge, and (2) our environment and our economy are completely and totally connected.

So there it is. The GND may not be his plan, but it deeply informs his plan, and I’ve little doubt he could expound on it. GND champion Alexandria Ocasio-Cortez eliminates any doubt in this clap-back to a snarky tweet by Kellyanne Conway:

This isn’t news, Kellyanne.

Our differences are exactly why I joined Biden’s Climate Unity Task Force - so we could set aside our differences & figure out an aggressive climate plan to address the planetary crisis at our feet.

Trump doesn’t even believe climate change is real.

Fantastic! Let’s do this thing.

Harlem Hawk

Three months into the Covid-19 Pandemic, I had barely left the apartment. But summer humidity splashed int our little apartment — and it became clear that outdoor spread is almost nonexistent — I started taking daily walks. I quickly expanded my range, delighted to find that one can walk from the south end of Central Park at 59th Street to the northern­most tip of Manhattan almost entirely in parks. It’s really quite stunning, and there’s so much to take in: architecture, views, rivers, flowers and trees, wildlife — the works. Those of you who follow my IG know.

On my jaunt through St. Nicholas Park yesterday, a branch shook vigorously ahead, alerting me to an unusual presence. As I approached, this keen stare greeted me, a mere 5-8m.

I don’t know what I expected to see, but it wasn’t this! I’ve heard that red-tailed hawks1 live in the trees or buildings of the nearby City College of New York, but I never saw one I could recognize, and certainly not this close. Turns out, red-tailed hawks are quite common residents of New York City, committed to pest control and delighting residents and visitors alike. Myself included.

This one, however, paid little attention to me. Rather, it seemed quite curious about this black squirrel running up a tree between us, about a meter away.

The squirrel kept running a loop up and down the tree. It would disappear from sight (mine, not the hawk’s), then reappear further down the trunk and scamper up again. The hawk seemed curious, amused, then, perhaps, bored.

Eventually I put my phone away and continued my walk, but kept thinking about this vignette. What story could one tell? Was the squirrel trying to protect its home by distracting the hawk? Was the hawk already well-sated, and now committed to satisfying its intellectual curiosity with a little naturalistic observation? Maybe the hawk and the squirrel were friends and neighbors, happy to enjoy a bit of camaraderie on a beautiful fall day in The City.

When I circled back an hour or so later, the hawk had moved across the path, and now was poking around in the ground cover. It saw me watching. Some ethno­graphy, perhaps?

Naw, it kept picking something up and shaking its head; a dragonfly or cricket I think, but couldn’t get close enough to tell. Could be it was hungry after all.

Either way, it made my day. Man I love this city.


  1. At least I think this is a red-tailed hawk. Though I see many bird watchers on my city schlepps, I myself am not one. Please do give me a holler if you happen to know just how mistaken I am. ↩︎

The Kushner Kakistocracy

Katherine Eban, in a deeply reported piece, for Vanity Faire:

Those representing the private sector expected to learn about a sweeping government plan to procure supplies and direct them to the places they were needed most. New York, home to more than a third of the nation’s coronavirus cases, seemed like an obvious candidate. In turn they came armed with specific commitments of support, a memo on the merits of the Defense Production Act, a document outlining impediments to the private-sector response, and two key questions: How could they best help? And how could they best support the government’s strategy?

According to one attendee, Kushner then began to rail against the governor: “Cuomo didn’t pound the phones hard enough to get PPE for his state…. His people are going to suffer and that’s their problem.”

But wait, it gets worse:

Kushner, seated at the head of the conference table, in a chair taller than all the others, was quick to strike a confrontational tone. “The federal government is not going to lead this response,” he announced. “It’s up to the states to figure out what they want to do.”

One attendee explained to Kushner that due to the finite supply of PPE, Americans were bidding against each other and driving prices up. To solve that, businesses eager to help were looking to the federal government for leadership and direction.

“Free markets will solve this,” Kushner said dismissively. “That is not the role of government.”

Seldom have falser words been spoken. These incompetents conflate their failure to lead with their belief that the government cannot lead. The prophecy fulfills itself.

The same attendee explained that although he believed in open markets, he feared that the system was breaking. As evidence, he pointed to a CNN report about New York governor Andrew Cuomo and his desperate call for supplies.

“That’s the CNN bullshit,” Kushner snapped. “They lie.”

“That’s when I was like, We’re screwed,” the shocked attendee told Vanity Fair.

And indeed we sure have been. Nearly 200,000 have died from Covid-19 in the United States to date, with close to 400,000 deaths forecast by January 1.

I’m restraining myself from quoting more; the reporting is impeccable, and the truth of the situation deeply alarming. Read the whole thing, then maybe go for a long walk and practice deep breathing.

And then Vote. And make sure everyone you know is registered and ready to vote.

We Need to Talk About Ventilation

Zeynep Tufekci, in a piece for The Atlantic:

Jimenez also wondered why the National Guard hadn’t been deployed to set up tent schools (not sealed, but letting air in like an outdoor wedding canopy) around the country, and why the U.S. hadn’t set up the mass production of HEPA filters for every classroom and essential indoor space. Instead, one air-quality expert reported, teachers who wanted to buy portable HEPA filters were being told that they weren’t allowed to, because the CDC wasn’t recommending them. It is still difficult to get Clorox wipes in my supermarket, but I went online to check, and there is no shortage of portable HEPA filters. There is no run on them.

It’s the profoundly irresponsible plan to reopen schools without any remotely sufficient attempt to upgrade and modernize the air circulation systems of our dilapidated public school buildings that disturbs me. Meanwhile, school reopening proposals pay undue attention to hygiene theater to assuage fears, while very real risks go largely unaddressed.1 It simply won’t work, and that means disastrous outcomes for communities.

And it’s not like there aren’t ways to get things under better control. Tufekci continues:

However, Japan masked up early, focused on super-spreader events (a strategy it calls “cluster busting”), and, crucially, trained its public to focus on avoiding the three C’s—closed spaces, crowded places, and close conversations. In other words, exactly the places where airborne transmission and aerosols could pose a risk. The Japanese were advised not to talk on the subway, where windows were kept open. Oshitani said they also developed guidelines that included the importance of ventilation in many different settings, such as bars, restaurants, and gyms. Six months later, despite having some of the earliest outbreaks, ultradense cities, and one of the oldest populations in the world, Japan has had about 1,000 COVID-19 deaths total—which is how many the United States often has in a single day. Hong Kong, a similarly dense and subway-dependent city, has had only 24 deaths.

The U.S. needs to get it shit together. We have the wealth and knowledge to do this right, but need to put empathy for each other ahead of temporary political and economic impacts to do so.


  1. In fairness, the Health and Safety section the New York City DOE’s Return to School 2020-2021 plan says that the “DOE will make improvements to HVAC systems, as well as air conditioning repairs, to improve air circulation, as well as replacing regular air filters with higher efficiency types.” Still, there’s a social failing here, national leaders ought to fund the upgrading of air circulation systems to the highest standards in every school and classroom in the United States. ↩︎

BLM NYC

I took advantage of the new City Bike dock in front of our flat to ride 80 blocks south through Central Park today to see the new #BlackLivesMatter mural outside Trump Tower. Not too many people around, perhaps a dozen taking photos and selfies. I joined them, then took a photo for a couple and their toddler in front of the mural. I couldn’t stop grinning under my mask. The mural may be symbolic, but holy cats do symbols matter.

More photos on Instagram.

Also, what a great ride this was. Thinking about quitting my job just to wander around New York and take it all in. Love this city.

Jia Tolentino on… Gestures Vaguely

Kottke’s right, this interview with Jia Tolentino in Interview is soo great. In a few cogent, direct, clear paragraphs, she covers her views on quarantine, Covid, reading, pregnancy, capitalism, Black Lives Matter, policy changes, protest, climate change, and more. An example, on white discomfort:

I’m also suspicious of the way that Not Being Racist is a project that people seem to be approaching like boot camp. To deepen your understanding of race, of this country, should make you feel like the world is opening up, like you’re dissolving into the immensity of history and the present rather than being more uncomfortably visible to yourself. Reading more Black writers isn’t like taking medicine. People ought to seek out the genuine pleasure of decentering themselves, and read fiction and history alongside these popular anti-racist manuals, and not feel like they need to calibrate their precise degree of guilt and goodness all the time.

The whole thing’s a gem, don’t miss it.

(Via Kottke)

Harlem Park Steps

Test Postgres Extensions With GitHub Actions

I first heard about GitHub Actions a couple years ago, but fully embraced them only in the last few weeks. Part of the challenge has been the paucity of simple but realistic examples, and quite a lot of complicated-looking JavaScript-based actions that seem like overkill. But through trial-and-error, I figured out enough to update my Postgres extensions projects to automatically test on multiple versions of Postgres, as well as to bundle and release them on PGXN. The first draft of that effort is pgxn/pgxn-tools1, a Docker image with scripts to build and run any version of PostgreSQL between 8.4 and 12, install additional dependencies, build, test, bundle, and release an extension.

Here’s how I’ve put it to use in a GitHub workflow for semver, the Semantic Version data type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
name: CI
on: [push, pull_request]
jobs:
  test:
    strategy:
      matrix:
        pg: [12, 11, 10, 9.6, 9.5, 9.4, 9.3, 9.2]
    name: 🐘 PostgreSQL ${{ matrix.pg }}
    runs-on: ubuntu-latest
    container: pgxn/pgxn-tools
    steps:
      - run: pg-start ${{ matrix.pg }}
      - uses: actions/checkout@v2
      - run: pg-build-test

The important bits are in the jobs.test object. Under strategy.matrix, which defines the build matrix, the pg array defines each version to be tested. The job will run once for each version, and can be referenced via ${{ matrix.pg }} elsewhere in the job. Line 10 has the job a pgxn/pgxn-tools container, where the steps run. The are are:

  • Line 12: Install and start the specified version of PostgreSQL
  • Line 13: Clone the semver repository
  • Line 14: Build and test the extension

The intent here is to cover the vast majority of cases for testing Postgres extensions, where a project uses PGXS Makefile. The pg-build-test script does just that.

A few notes on the scripts included in pgxn/pgxn-tools:

  • pg-start installs, initializes, and starts the specified version of Postgres. If you need other dependencies, simply list their Debian package names after the Postgres version.

  • pgxn is a client for PGXN itself. You can use it to install other dependencies required to test your extension.

  • pg-build-test simply builds, installs, and tests a PostgreSQL extension or other code in the current directory. Effectively the equivalent of make && make install && make installcheck.

  • pgxn-bundle validates the PGXN META.json file, reads the distribution name and version, and bundles up the project into a zip file for release to PGXN.

  • pgxn-release uploads a release zip file to PGXN.

In short, use the first three utilities to handle dependencies and test your extension, and the last two to release it on PGXN. Simply set GitHub secrets with your PGXN credentials, pass them in environment variables named PGXN_USERNAME and PGXN_PASSWORD, and the script will handle the rest. Here’s how a release job might look:

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  release:
    name: Release on PGXN
    # Release pon push to main when the test job succeeds.
    needs: test
    if: github.ref == 'refs/heads/main' && github.event_name == 'push' && needs.test.result == 'success'
    runs-on: ubuntu-latest
    container:
      image: pgxn/pgxn-tools
      env:
        PGXN_USERNAME: ${{ secrets.PGXN_USERNAME }}
        PGXN_PASSWORD: ${{ secrets.PGXN_PASSWORD }}
    steps:
      - name: Check out the repo
        uses: actions/checkout@v2
      - name: Bundle the Release
        run: pgxn-bundle
      - name: Release on PGXN
        run: pgxn-release

Note that lines 18-19 require that the test job defined above pass, and ensure the job runs only on a push event to the main branch, where we push final releases. We set PGXN_USERNAME and PGXN_PASSWORD from the secrets of the same name, and then, in lines 27-32, check out the project, bundle it into a zip file, and release it on PGXN.

There are a few more features of the image, so read the docs for the details. As a first cut at PGXN CI/CD tools, I think it’s fairly robust. Still, as I gain experience and build and release more extensions in the coming year, I expect to work out integration with publishing GitHub releases, and perhaps build and publish relevant actions on the GitHub Marketplace.


  1. Not a great name, I know, will probably change as I learn more. ↩︎

Valerie Wheeler

Valerie Wheeler

CSUS Anthropology department picnic, 1991

Two years ago, my undergraduate Anthropology advisor, Professor Valerie Wheeler, died after a sudden and brief battle with leukemia. She played a vital role in my academic and personal life, and her passing affected me deeply. Here’s the note I left on her Legacy page.

I’m deeply saddened by Valerie’s passing. She was more than just my undergraduate advisor in the late 80s and early 90s, but also “Mom” to me and a slew of my fellow anthropology majors. Such a great, inspiring teacher, who taught me to view the world through the lens of culture, so that I saw it in a completely new way. It was eye-opening, and had such an impact that I could never close them again. And for that, I wanted to do well, to make her proud. She expected a lot, and I wanted to meet those expectations. It made me a better person, a more thoughtful person. And I could not appreciate it more. I’m sorry not to have kept in better touch, and so sad that the world has lost such a compassionate, wonderful person. I will carry some of that with me for the remainder of my life. Valerie’s impact was great, and I’m grateful to have had her in my life. My condolences to her family.

Antigone’s Voice

First of all, No

Photo by Jazmin Quaynor on Unsplash

A couple months back, I saw Antigone in Ferguson at St. Ann’s Church in Brooklyn. The project pairs dramatic readings of Sophocles’ Antigone with a moving choral arrangement performed by a diverse cast of activists, students, police officers, and Ferguson & NYC community members. I don’t tend to go for gospel, but these stunning voices shot through me like a revelation. The powerful, vulnerable expression of the human voice — a profound manifestation of the human capacity for creativity and beauty — broke my heart and raised my optimism for humanity. I got a lot out of the discussion of racial injustice following the performance, too. Don’t miss it if you get the chance.

The relationship between Antigone and King Creon struck me in a new way, no doubt because recent encounters with autocratic and sexist behaviors have been front-of-mind lately.

The play depicts Creon as the relatively thoughtful king of Thebes and doting uncle to Antigone and her sister, Ismene. He forbade the burying of Polynices while still in the heat of the just-ended civil war, and, despite his advisors' best arguments, stubbornly rejects revoking the law. His inability to admit mistake despite the clarity of its recklessness — even to his own mind — exemplifies classic authoritarian behavior: never admit error. Naturally it leads to his downfall.

While Creon’s advisors appeal for revocation of the law, his niece, Antigone, refuses to submit to it, and declines to compromise her integrity or the tone of her voice when speaking against it. She deliberately flaunts the law by burying her brother, makes makes no attempt to deny it, and angrily excoriates Creon at her very public trial. His intransigence will be his downfall, she proclaims. The girl doesn’t sugar-coat it, and in her passion, her voice rings out loud and true to all assembled.

But, to Creon’s ear, shrilly.

Stung by Antigone’s passionate defiance, Creon finds her tone reason enough to ignore the substance of her argument, to dismiss the risks she highlights. The entire community rallies to her cry, but Creon, blinded by his bruised ego, commits to his folly and sentences Antigone to death. It is his undoing, and a tragedy for Thebes.

One can learn a lot from Creon.

How often do we discount a woman’s message because of her tone? When passion speaks truth to power, what reasons do we find to dismiss it? When someone cares, but poor leadership prevents understanding and growth, does the anger, the passion, the righteousness wound the ego or motivate action?

Passion is a virtue, and tone a reflection of commitment. People who care about their world — their work, their environment, their society — will be angry when it fails them. If it stings when they tell you that you’ve made mistakes, that you’re failing them and other people, put ego aside, recognize that your implicit biases might seek reason to dismiss them, and instead, simply listen.

My fellow white dudes, don’t be like Creon. Recognize your fallibility, head off your urge to disregard feedback in the name of your discomfort, and beware the ego reflexively assigning labels such as “whiner”, or “negative nelly”, or even “not focused on problem-solving”. This, too, tells you something. Because the people who don’t care say nothing. Those with the passion to speak and the willingness to do so expect more of you. They may be disappointed in you or your actions, but want you to take the opportunity to be better, to admit and rectify your mistakes, and to set things on a better path.

So maybe let’s take a stab at meeting their expectations.

Sqitch v1.0.0

Sqitch Logo

After seven years of development and hundreds of production database deployments, I finally decide it was time to release Sqitch v1.0.0, and today’s the day. I took the opportunity to resolve all known bugs in previous releases, so there’s no new functionality since v0.9999. Still, given the typical attention given to a significant milestone release like 1.0.0, my employer published a blog post describing a bit of the history and philosophy behind Sqitch.

The new site goes into great detail describing how to install Sqitch, but the important links are:

  • CPAN — Install Sqitch from CPAN
  • Docker — Run Sqitch from a Docker container
  • Homebrew — Homebrew Sqitch on macOS
  • GitHub — Sqitch releases on GitHub

Thanks to everyone who helped get Sqitch to this point, I appreciate it tremendously. I’m especially grateful to:

Thanks a million for all your help and support!

Impeach

Dan Pfeiffer’s has a plan to win the impeachment fight:

Third, an impeachment inquiry should be plotted out more like a TV show than a trial. The star witnesses and high-profile hearings should be spaced out and timed for maximum impact. They should tell a story about Trump’s misdeeds. There should be no rush to get this over with quickly or to meet some artificial timeline. The audience for this show is not the Senate. It’s not Twitter and it’s not the panel on Morning Joe. The audience is the American people — specifically the new and sporadic Democratic voters who came out in 2018, or the independents and Republicans who say they’re most concerned about Trump’s conduct. Our job is to persuade them, not the DC pundit class.

Smart strategy to “prosecute a devastating case against Trump that increases the likelihood that Democrats win the White House, expand our House Majority, and take the Senate.” Each day brings us closer to impeachment proceedings, regardless of what Democratic leadership might want. The over-the-top malfeasance and criminality of this president and his White House leads inexorably to impeachment proceedings. It’s past time for the Democrats to accept that fact and make a plan to maximize its effectiveness.

Ban the Nazis, Twitter

Joseph Cox and Jason Koebler, in a Motherboard article provoked by a report that a Twitter employee explained that an automated banning of white supremacy would also ban Republican politicians:

Twitter has not publicly explained why it has been able to so successfully eradicate ISIS while it continues to struggle with white nationalism. As a company, Twitter won’t say that it can’t treat white supremacy in the same way as it treated ISIS. But external experts Motherboard spoke to said that the measures taken against ISIS were so extreme that, if applied to white supremacy, there would certainly be backlash, because algorithms would obviously flag content that has been tweeted by prominent Republicans—or, at the very least, their supporters. So it’s no surprise, then, that employees at the company have realized that as well.

Here’s an idea: ban white supremacists. Then when white supremacists complain, no matter what their office or political affiliation, cite the tweets at issue and explain how they violate the rules and qualify has hate speech. If they scream “free speech!”, invite them to find another platform on which to express their hate.

In other words, do the right thing, and have a fucking backbone.