Install golang-migrate Inside a Docker Container

To run database migrations using golang-migrate in a pipeline for a Go application, we need the binary migrate command available in the container.

Installing the database migration utility golang-migrate for Go inside a Linux container can be accomplished with the following.
Add this to the Dockerfile (note: the full path):

RUN go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
RUN ln -s /go/bin/linux_amd64/migrate /usr/local/bin/migrate

After running docker build and run, SSH into the container to see if everything is correct.
Use docker ps to get the container ID. The following commands accomplish this:

$ docker build -t my-tag .

$ docker run -d -p 5000:5000 my-tag

$ docker ps
CONTAINER ID IMAGE ...
8e402138e4b2 my-tag ...

$ docker exec -it 8e402138e4b2 /bin/sh

Now we should have a shell inside the container.
Check if the migrate command installed correctly:

/# which migrate

This should show a path to the command, e.g.:

/go/bin/migrate

Check the version of the command:

/# migrate -version

This should show, e.g.:

dev

The container should now be able to run Go database migrations, preferably by running a startup script when the container starts.

 

Perform Speech Recognition in the Terminal with Whisper

OpenAI Whisper is a state-of-the-art speech recognition model that we can run from the command line.

This post assumes macOS with Python >= 3.7 installed.

First we need to install FFmpeg for audio processing.

$ brew install ffmpeg

Install Whisper:

$ pip install openai-whisper

This will also install a binary command: whisper

Now, record a piece of audio using QuickTime or similar.

Save the file to file.m4a, for example.

Then, to run the speech recognition:

$ whisper file.m4a --model small

The output will look something like this:

Detecting language using up to the first 30 seconds. 
Use `--language` to specify the language
Detected language: English
[00:00.000 --> 00:01.420] Hello there.

Notes

We can also use the specific repo URI if brew does not work on a system:

$ pip install git+https://github.com/openai/whisper.git

We can use the medium or large models if the small model is not sufficiently accurate:

$ whisper file.m4a --model medium
$ whisper file.m4a --model large

 

Run TypeScript File from the Command Line

To run a script written in TypeScript from the terminal, outside of the browser, we cannot use the regular Node.js binary.
We need to install the ts-node command as below:

$ npm install -g ts-node

If TypeScript itself is not installed, install it with:

$ npm install -g typescript

Then, we can run the TypeScript file using:

$ ts-node file.ts

 

GraphQL Mesh Gateway Health Check Endpoint

When running the GraphQL Mesh server in a cloud environment in a container, perhaps using a platform like Kubernetes or Elastic Container Service on AWS, we need to specify an HTTP endpoint to act as a health check for the container.

Though it is not well-documented, the Mesh server does have a healthcheck endpoint available.

The route is simply: /healthcheck

Here is an example calling the healthcheck of a locally running Mesh server:

$ curl "http://localhost:4000/healthcheck" -v
* Connected to localhost (127.0.0.1) port 4000 (#0)
> GET /healthcheck HTTP/1.1
> Host: localhost:4000
> User-Agent: curl/7.77.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 19 Dec 2022 03:06:17 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 0
<

Note that just the root response (“/”) returns an HTTP 302 Found and so /healthcheck is ideal for a 200 OK health status response.

Set a POST Request to be a Query in GraphQL Mesh using OpenAPI

When integrating REST APIs into GraphQL Mesh through the OpenAPI plugin, sometimes we want to specify that a POST path is actually a query and not a mutation.
That is, we need to override the default GraphQL Mesh OpenAPI/Swagger plugin behaviour of assuming that a path with a POST method is a mutation.

In the GraphQL Mesh YAML file (.meshrc.yml) we use the following in the declaration of our example service:

- name: example-service
  handler:
    openapi:
      source: '${EXAMPLE_SERVICE_BASE_URI}/docs'
      baseUrl: '${EXAMPLE_SERVICE_BASE_URI}'
      operationHeaders:
        Authorization: "{context.headers['authorization']}"
      selectQueryOrMutationField:
        - fieldName: 'exampleAction'
          type: Query

Note that fieldName is the value of operationId in the OpenAPI Spec (in this example: ‘exampleAction’).

Version Differences

Important: there was a change in the syntax for this feature in November 2022.

The previous format was as below:

- name: example-service
  handler:
    openapi:
      source: '${EXAMPLE_SERVICE_BASE_URI}/docs'
      baseUrl: '${EXAMPLE_SERVICE_BASE_URI}'
      operationHeaders:
        Authorization: "{context.headers['authorization']}"
      selectQueryOrMutationField:
        - title: 'Example Service Spec'
          path: /v1/example-service/resource
          method: post
          type: Query

Make sure the title is the key in the YAML and matches the exact title in the OpenAPI Spec referenced.

To confirm this works, start the Mesh server and confirm that the operation shows up as a Query instead of a Mutation.

References

https://github.com/Urigo/graphql-mesh/discussions/2921

Run an Ubuntu VM on an Apple M1 Mac

The simplest way to run an Ubuntu Linux virtual machine on a new Apple M1 chip macOS machine is to use Multipass.

First, install Multipass:

$ brew install --cask multipass

There should be a primary VM instance available already.

To start the primary Ubuntu instance:

$ multipass start

It should show:

Starting primary

Show running instances:

$ multipass list

The output should be similar to:

Name    State   IPv4         Image
primary Running 192.168.64.2 Ubuntu 20.04 LTS

Now we can SSH into the VM using:

$ multipass shell

You should see a prompt for the shell inside the Ubuntu VM:

ubuntu@primary:~$

Your Ubuntu VM is now ready to use!

Additionally, to mount a directory to access files from the host machine inside the VM, see this post:

Mount a Host Machine Directory Inside a Multipass VM

Mount a Host Machine Directory Inside a Multipass VM

To be able to access files on a host machine from a Multipass Ubuntu VM, we can mount the local directory into the virtual machine.

Assuming we have a host system directory my-stuff, we can mount it into the VM with:

$ multipass mount my-stuff primary:/home/ubuntu/my-stuff

Note the required name primary which refers to the VM instance name.

Check that the mount shows up correctly in Multipass:

$ multipass info primary

This should show your local and VM directory, with a line similar to:

Mounts: /Users/abc/Documents/my-stuff => /home/ubuntu/my-stuff

SSH into the VM to see the directory:

$ multipass shell
ubuntu@primary:~$ ls my-stuff
file1
file2
...

The directory should now be accessible inside the VM and we can share files between the host machine and VM.

Related Posts

Run an Ubuntu VM on an Apple M1 Mac

Rename a Branch in Git

Sometimes we need to rename an existing Git branch without creating a new branch or removing the old branch.

First, make sure you have the existing branch to rename checked out:

$ git branch

Output:

main
* old-name

To rename the branch use:

$ git branch -m old-name new-name

The command ‘m’ is short for “move”, similar to moving a file to rename it in Unix systems.

Confirm the new name:

$ git branch

Output:

main
* new-name

Open Visual Studio Code from the Terminal on macOS

It is useful to be able to open VSCode from the command line with the code command.

To add this ability, edit your PATH as follows:

PATH=$PATH:/Applications/Visual\ Studio\ Code.app/Contents/Resources/app/bin

Add this to your .bash_profile or equivalent.

This will ensure your shell will find the code binary from VSCode while in any directory.

Now you can open any file in your current working directory with VSCode using:

$ code file.js

Or just open the editor without a file in the current directory:

$ code .

 

Include the Same Query More than Once in a GraphQL Request

It can sometimes be useful to request two or more copies of the same query result in one GraphQL request.

We can repeat the same query twice, for example, using output naming as in the example below:

{
  getBook {
    title
  }

  secondCopy:getBook {
    title
  }
}

The label secondCopy is required to create a unique name in the output data.

The label we use will replace the query name in the output response, as below:

{
  "data": {
    "getBook": {
      "title": "Book A"
    },
    "secondCopy": {
      "title": "Book A"
    }
  }
}

We can request as many copies as desired in the query.