All projects

MACHBANK · 2024

Internal CLI Tool

Senior Software Engineer

Adopted company-wide. Grew to v1.9.0 with 7 command groups contributed by multiple engineers.

TypeScriptNode.jsAWS SDKGitHub APIYargs

The Problem

Every production deployment at MACHBANK involved the same manual steps: check which services were being deployed, pull the list of merged PRs from GitHub, cross-reference task definition versions in AWS ECS, and write a formatted message to post in the team Slack channel. Each time, it took several minutes of copy-pasting across the AWS console, GitHub, and a text editor.

Local environment setup had the same friction — keeping .env files in sync with Terraform-managed service variables meant manually navigating the infrastructure repo every time something changed.

These weren’t hard problems. They were just slow, repetitive, and easy to get wrong.

What I Built

I created mach-cli (@soymach/mach-cli) — a TypeScript CLI with a Flow-based architecture where each command group is an isolated module. I started with the commands that hurt the most:

Production release management

  • mach get-prod-message — automatically generates a formatted Slack message for the #mach-a-produccion channel. It queries ECS for task definition versions, hits the GitHub API to list merged PRs per service between release tags, and outputs a ready-to-paste release summary. What took minutes became a single command.
  • mach get-env-variables — fetches environment variables directly from the Terraform service configuration repo, compares them against the local .env file, and patches only the differences.

AWS operations

  • ecs get-tasks-definition — retrieves current task definition revisions across multiple services with direct console links.
  • ecs get-service-ip — lists private IPs of running ECS task instances, useful for debugging internal service connectivity.
  • route53 update-hosts — syncs the local /etc/hosts file with Route53 DNS records for internal MACH domains, eliminating manual host entries.

GitHub

  • github get-prs-merged-last-deploy — finds the last production deploy workflow run and lists every PR merged after it, with authors and links.
  • github set-token — stores a GitHub token to ~/.mach-cli/config so it never needs to be passed again.

AWS SSO

  • iam sso-login — handles the full SSO device authorization flow and writes temporary credentials to ~/.aws/credentials, removing the need to use the AWS CLI or browser console for credential rotation.

What Happened Next

I published the tool as an internal npm package and shared it with the backend team. Other engineers started using it — and then contributing to it.

By v1.9.0, two major command groups had been added by other team members:

  • fastify migrate-* — a multi-step migration pipeline that transforms legacy MACH microservices (command-based or role-based routing structures) into Fastify-based services. It scaffolds entire application structures, generates typed route adapters, and manages dependency updates. It supports four migration paths: JS commands, TS commands, JS roles, and role-action patterns.
  • eslint migrate-eslint-config-file — migrates services from legacy .eslintrc configuration to ESLint 9 Flat Config, handling plugin name remapping, rule extraction, and VS Code settings injection.

The architecture made this possible. The Flow base class gave contributors a clear contract: implement validateCommand() and executeCommand(), register your commands in the flow map, and the CLI handles the rest. Adding a new command group never required touching the core.

Technical Highlights

  • Flow architecture: each command group is an independent class extending an abstract Flow base, registered at startup via a flows map. Clean separation between routing logic, API integrations, and UI helpers.
  • AWS SDK v3: separate clients for ECS, Route53, SSO/OIDC; async credential flows with browser-based device authorization.
  • GitHub API via Octokit: tag-to-tag PR diffing, release tracking, and merge commit parsing.
  • Migration pipeline: the Fastify migration uses a composable step pipeline — each step is independently testable, errors are collected across steps rather than failing fast, and the pipeline reports a full summary of what succeeded and what needs manual attention.