Moved to Mastodon

January 20th, 2023

Mastodon

With the recent demise of the Twitter API for third-party clients, it was time to move to Mastodon, all I really needed was a little push from a friend, and then off we went. I will say, the native client isn't bad, and Tapbots is creating Ivory, which sounds promising, but I'm pretty pleased with Ice Cubes - an Open Source Mastodon client using SwiftUI, available on the App Store. It's not bad at all on iPad, and on iOS it's really very nice.

I still will be very interested in seeing what Ivory looks like - as I really want one client that syncs on all my devices - as Twitterrific did, and I know the Tapbots guys will make that happen. But we'll see... who knows, given that it's SwiftUI, it's possible to imagine that Ice Cubes will be on macOS too... Could be interesting.

In any case, it was the people... the community, that made Twitter nice, and they're moving, and the place won't be worth a tenth of the purchase price in a few months.

Twitterrific is Silent

January 14th, 2023

Twitterrific.jpg

This morning, Twitterrific for iOS had problems logging in, and at first, I thought it was the 2FA I had applied to my account after a friend's account was hacked. The token on my iPhone expired, and I needed to re-authenticate my account with the 2FA, and yet when I tried to do that... nothing.

It was an hour later that I was still trying to find out what it might be, when I saw the news that Twitter had simply revoted the access tokens for some of the third-party iOS clients - while leaving the macOS clients alone. For example, Twitterrific for macOS is fine. This wasn't a mistake - it was intentional, and that's a shame. They chose to do this, and it's their company, so OK... but I can't imagine this standing - or maybe it's the beginning of the end?

I guess I'll see what Mastodon is all about...

Happy Birthday to Me!

December 31st, 2022

Cake.jpg

Another year in the books, and another year for me. As a kid, my birthday was way too close to Christmas, but as I've gotten older, it's nice to have them close together. I don't have to worry about people making a fuss on my birthday, and I can be assured that most everyone will be thinking about, and making plans for, their New Year's Eve activities, and so don't really think much about me. It's almost like stealth birthday - which is just fine with me.

Today, I'm heading down to Indy to visit my siblings, and just enjoy the day. I'll be back later, and that's OK. I've got The Talk Show to listen to on the way down and back - and Gruber is without a doubt, one of the more entertaining talk show personalities to me. It's always a good listen. So on my birthday I get to have good food, good company, and something good to listen to.

I am so very grateful for it all.

Merry Christmas!

December 25th, 2022

Christmas Tree

It's another amazing Christmas, and all is quiet. Quiet times... noisy times... it's all a wonderful time of year to spend time with family and friends and enjoy the Season. It's time to enjoy a nice movie, or maybe a football game, and to have some special treats that you don't normally have - all to remember the traditions you grew up with.

Today I'll just be relaxing and enjoying the quiet. It's Sunday, so I'll send out my weekly texts to family and friends, and then sit back and enjoy the quiet of the day. It's just my favorite time of year. 🙂

Big Update Morning

December 14th, 2022

Yosemite

This morning has been a Big Update morning to be sure... iOS 16.2, tvOS 16.2, iPadOS 16.2, and macOS 13.1 all ready to go from Apple, and so everything I had went through the update. It's nice to get things up to date, not just because some of the things I've seen in Safari on macOS and iPadOS are iffy... but it's a chance to get some little surprises as well.

One thing that I've read about that I didn't really like is the new What's Up on tvOS... it used to be the things in my list, and now it's almost advertising for the things that some folks think I might be interested in. Not a huge fan, and would love to have a way to get back to the way it was. We'll see...

Until then, things are humming along, and it's nice to get it all done in an early morning. 🙂

Advent of Code 2022 is On!

December 1st, 2022

Christmas Tree

This morning I did the first day of the 2022 Advent of Code. What fun it is to get back into Clojure - for a month. If I thought it the least bit reasonable, I'd be doing back-end Clojurescript as it gets generated into Javascript in the same way that Typescript does, so it would run on the same stack with the same speed, etc. But it's just too big a leap for most folks, and it's not worth the education cycles.

But still... the simplicity of the language, and it's ability to run in highly multi-threaded environments is a huge win, and so it will remain one of my very favorite languages.

Node, Docker, Google Cloud, and Environment Variables

November 14th, 2022

GoogleCloud

At The Shop, we're using Google Cloud Run for a containerized API written in Node, and it's a fine solution - really. But one of the issues we have run into is that of environment variables. We have a lot of them. The configuration for dev versus prod versus local development is all being held in environment variables, and the standard way for these to be passed in the cloudbuild.yaml file in the Build step:


steps:
  - name: gcr.io/cloud-builders/docker
    entrypoint: '/bin/bash'
    args:
      - '-c'
      - >-
        docker build --no-cache
        --build-arg BRANCH_NAME=$BRANCH_NAME
        --build-arg THESHOP_ENV=$_THESHOP_ENV
        --build-arg BASE_API_URL=$_BASE_API_URL
        -t $_GCR_HOSTNAME/$PROJECT_ID/$REPO_NAME/$_SERVICE_NAME:$COMMIT_SHA
        . -f Dockerfile
    id: Build

and then in the Dockerfile, you have:

ARG BRANCH_NAME
RUN test -n "$BRANCH_NAME" || (echo 'please pass in --build-arg BRANCH_NAME' && exit 1)
ENV BRANCH_NAME=${BRANCH_NAME}
 
ARG THESHOP_ENV
RUN test -n "$THESHOP_ENV" || (echo 'please pass in --build-arg THESHOP_ENV' && exit 1)
ENV THESHOP_ENV=${THESHOP_ENV}
 
ARG BASE_API_URL
RUN test -n "$BASE_API_URL" || (echo 'please pass in --build-arg BASE_API_URL' && exit 1)
ENV BASE_API_URL=${BASE_API_URL}

While will place them in the environment of the built container. And all this is fine, until you start to hit the limits.

The cloudbuild.yaml command has a limit of 4000 characters, and if you have large, or sufficient number, of environment variables then you can exceed this, and we have. There is also a limit of 20 arguments to the docker build command, so again, we run into trouble if the number of environment variables gets more than that. So what can be done?

Well... since we are using Google Cloud Secrets, we could write something to scan those secrets, and pull them all into the running process, and stuff them into the process.env map for Node. But therein lies another problem: Node is asynchronous, so if we have top-level definitions that use these environment variables, like, say clients to Vendor services, then it's quite possible that they will need those variables before we have had the chance to load them.

So what can we do?

The solution that seems to work is to have a separate app that will be run in the Dockerfile, and will generate a .env file resides only in the container, and is built at the time the container is built, and contains all the environment variables we need. Then, the Node app can just use these with the dotenv library.

To make this file, we have the end of the Dockerfile look like:

# now copy everything over to the container to be made...
COPY . .
# run the node script to generate the .env file
RUN THESHOP_ENV=${THESHOP_ENV} \
  GCP_SECRETS_API_EMAIL=${GCP_SECRETS_API_EMAIL} \
  GCP_SECRETS_API_KEY=${GCP_SECRETS_API_KEY} \
  GCP_BUILD_PROJECT=${GCP_BUILD_PROJECT} \
  npm run create-env
# run the migrations for the database to keep things up to date
RUN npx migrate up --store='@platter/migrate-store'
EXPOSE 8080
CMD [ "node", "-r", "dotenv/config", "./bin/www" ]

So that we give the create-env script the few key environment variables it needs to read the Google Cloud Secrets, and then it generates the file. The create-env script is defined in the package.json as:

{
  "scripts": {
    "create-env": "node -r dotenv/config tools/make-env"
  }
}

and then the script itself is:

const arg = require('arg')
const { execSync } = require('child_process')
const { addSecretsToEnv } = require('../secrets')
const { log } = require('../logging')
 
const _help = `Help on command usage:
  npm run create-env -- --help         - show this message
  npm run create-env -- --file <name>  - where to write the env [.env]
  npm run create-env -- --verbose      - be noisy about it
 
  Nothing is required other than the FLEXBASE_ENV and some GCP env variables
  that can be specified on the command line.`;
 
/*
 * This is the main entry point for the script. We will simply read in all
 * the secrets for the THESHOP_ENV defined environment from the Cloud
 * Secrets, and then write them all to the '.env' file, as the default.
 * This will allow us to set up this environment nicely in a Dockerfile.
 */
(async () => {
  // only do this if we are run directly from 'npm run'...
  if (!module.parent) {
    // let's process the arguments and then do what they are asking
    const args = arg({
      '--help': Boolean,
      '--verbose': Boolean,
      '--file': String,
    })
    // break it into what we need
    const verbose = args['--verbose']
    const where = args['--file'] ?? '.env'
 
    // ... now let's pull in all the appropriate Secrets to the local env...
    log.info(`[makeEnv] loading the Secrets for ${process.env.THESHOP_ENV} into
        this environment...`)
    const resp = await addSecretsToEnv()
    if (verbose) {
      console.log(resp)
    }
    // ...and now we can write them out to a suitable file
    log.info(`[makeEnv] writing the environment to ${where}...`)
    const ans = execSync(`printenv > ${where}`).toString()
    if (verbose) {
      console.log(ans)
    }
    return
  }
})()

The addSecretsToEnv() is where we use the Google Secrets Node Client to read all the Secrets in our account, and one by one, pull them down and put them into process.env. The fact that this runs before the app starts is how we get around the asynchronous nature of Node, and by having it be an .env variable, we can use all the normal tools to read and process it, and we no longer need to worry about the top-level Vendor clients trying to define themselves with environment variables that haven't been defined.

Now if Node had a way to force an async function to finish before moving on, then this wouldn't be necessary, as we'd simply call the addSecretsToEnv() in the Node start-up script, well ahead of the loading of the other files. But alas... that's not how it works.

This has turned out to be a very workable solution, and we get past the limitations of the cloudbuild.yaml file, which is a great relief.

Flushing DNS Cache on macOS 13 Ventura

November 12th, 2022

Yosemite

This morning I needed to flush the DNS cache on my MacBook Pro, and so I looked it up, and wanted to keep it around, so here we are. 🙂 The problem was that a service I use had to change DNS mapping due to a change in Google Cloud, and the nature of DNS caching is to try and minimize the hits on the DNS Servers, but this makes it hard to "forget" a DNS entry... unless you flush the cache.

It's really not all that hard:

  $ sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

and after this, the DNS cache is empty, and all services will hit the DNS server for the IP addresses, and everything will detect the "move".

Upgraded Sublime Text to Build 4142

November 10th, 2022

Sublime Text 2

With the update to macOS 13.0.1, I thought it would be a good time to check and see if Sublime Text had an update - because I thought I got notifications, but I guessed I missed this one. Still, it's nice to see that they are making updates to the editor.

I was chatting with a friend the other day, and with all the IDEs, and Super Editors out there, why Sublime? And it was interesting to me to realize it's the simplest, fastest, editor with the minimum chrome that reminds me of the old ProjectBuilder days on NeXT. The editor window is just that - a simple window, and there's no need for all the fancy ornamentation on the window... I know what's happening, and if I need it, I can make it visible. But 99% of the time, I just don't need it - so why clutter the screen?

With this update, there are a host of updates, fixes, and additions, and it's always nice to read the release notes, and realize this is a lot more powerful than I need. It's nice to have the ability - should I need it.

So all in all, a nice day of upgrades. 🙂

Interesting Node sleep() Function

October 26th, 2022

NodeJS

Today I had a reason to look at some Node issues with async processing, and ran across these two little functions that are interesting, but quite deadly. They pause the Node Runtime for the specified number of milliseconds, or seconds, and this is nice - when you have to have a delay, but all Node processing stops. This means all the async calls won't get processed, either.

  function msleep(n) {
    Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, n);
  }
 
  function sleep(n) {
    msleep(n*1000);
  }

Simple. Easy. But not really what I was looking for. 🙂