From 5ebdaf52a34351e5094d0f812b2d6d152f9aaa52 Mon Sep 17 00:00:00 2001 From: Daniel Leal Date: Mon, 11 May 2026 16:55:06 -0400 Subject: [PATCH 1/2] fix: deployment CI pipeline --- .github/workflows/release.yml | 152 ++++++++++++++++++++++++-- .github/workflows/update-homebrew.yml | 124 --------------------- 2 files changed, 142 insertions(+), 134 deletions(-) delete mode 100644 .github/workflows/update-homebrew.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 635d7ff..bea7bc5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,30 +5,162 @@ on: types: [closed] branches: [main] +permissions: + contents: read + jobs: - release: + checks: if: github.event.pull_request.merged && startsWith(github.event.pull_request.head.ref, 'release/') runs-on: ubuntu-latest - permissions: - contents: write - id-token: write + outputs: + version: ${{ steps.version.outputs.version }} + tag_name: ${{ steps.version.outputs.tag_name }} steps: - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - name: Get version id: version - run: echo "version=v$(node -p 'require("./package.json").version')" >> "$GITHUB_OUTPUT" - - name: Create and push tag run: | - git tag ${{ steps.version.outputs.version }} - git push origin ${{ steps.version.outputs.version }} + VERSION=$(node -p 'require("./package.json").version') + if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Invalid version: $VERSION" + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tag_name=v$VERSION" >> "$GITHUB_OUTPUT" + + - run: npm ci + - run: npm run lint + - run: npm run typecheck + - run: npm run build + - run: npm test + + publish: + needs: checks + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 22 registry-url: https://registry.npmjs.org cache: npm + - run: npm ci - - run: npm run build - - run: npm test - run: npm publish --access public --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + update-homebrew: + needs: [checks, publish] + runs-on: ubuntu-latest + steps: + - name: Wait for npm publish + run: | + VERSION="${{ needs.checks.outputs.version }}" + for i in $(seq 1 30); do + if npm view "@fintoc/cli@$VERSION" version 2>/dev/null; then + echo "Found @fintoc/cli@$VERSION on npm" + exit 0 + fi + echo "Attempt $i/30 - waiting 10s..." + sleep 10 + done + echo "Timed out waiting for @fintoc/cli@$VERSION on npm" + npm view "@fintoc/cli" versions --json || true + exit 1 + + - name: Download tarball and compute SHA256 + id: sha + run: | + VERSION="${{ needs.checks.outputs.version }}" + URL="https://registry.npmjs.org/@fintoc/cli/-/cli-${VERSION}.tgz" + TARBALL=$(mktemp) + HTTP_CODE=$(curl -sL -o "$TARBALL" -w '%{http_code}' "$URL") + if [ "$HTTP_CODE" != "200" ]; then + echo "Failed to download tarball (HTTP $HTTP_CODE)" + exit 1 + fi + SHA256=$(sha256sum "$TARBALL" | awk '{print $1}') + echo "sha256=$SHA256" >> "$GITHUB_OUTPUT" + echo "url=$URL" >> "$GITHUB_OUTPUT" + + - name: Clone homebrew-tap + uses: actions/checkout@v4 + with: + repository: fintoc-com/homebrew-tap + token: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} + path: homebrew-tap + + - name: Update formula + env: + TARBALL_URL: ${{ steps.sha.outputs.url }} + TARBALL_SHA256: ${{ steps.sha.outputs.sha256 }} + VERSION: ${{ needs.checks.outputs.version }} + run: | + for var in TARBALL_URL TARBALL_SHA256 VERSION; do + [ -z "${!var}" ] && echo "::error::$var is empty" && exit 1 + done + envsubst '$TARBALL_URL $TARBALL_SHA256 $VERSION' > homebrew-tap/Formula/fintoc.rb <<'RUBY' + class Fintoc < Formula + desc "CLI for the Fintoc API" + homepage "https://github.com/fintoc-com/fintoc-cli" + url "$TARBALL_URL" + version "$VERSION" + sha256 "$TARBALL_SHA256" + license "BSD-3-Clause" + + depends_on "node" + + def install + system "npm", "install", *std_npm_args + bin.install_symlink Dir["#{libexec}/bin/*"] + end + + test do + assert_match "fintoc/#{version}", shell_output("#{bin}/fintoc --version") + end + end + RUBY + + - name: Commit and push + run: | + VERSION="${{ needs.checks.outputs.version }}" + cd homebrew-tap + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Formula/fintoc.rb + if git diff --cached --quiet; then + echo "Formula already up to date" + exit 0 + fi + git commit -m "fintoc $VERSION" -m "https://github.com/fintoc-com/fintoc-cli/releases/tag/v$VERSION" + git push + + tag: + needs: [checks, publish] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Create and push tag + env: + TAG_NAME: ${{ needs.checks.outputs.tag_name }} + run: | + if git ls-remote --exit-code --tags origin "refs/tags/$TAG_NAME" >/dev/null 2>&1; then + echo "::error::Tag $TAG_NAME already exists" + exit 1 + fi + git tag "$TAG_NAME" + git push origin "$TAG_NAME" diff --git a/.github/workflows/update-homebrew.yml b/.github/workflows/update-homebrew.yml deleted file mode 100644 index b36c01f..0000000 --- a/.github/workflows/update-homebrew.yml +++ /dev/null @@ -1,124 +0,0 @@ -name: Update Homebrew Formula - -on: - workflow_run: - workflows: [Release] - types: [completed] - workflow_dispatch: - inputs: - version: - description: 'Version to publish (e.g. 0.1.1)' - required: true - -permissions: - contents: read - -jobs: - update-formula: - if: >- - github.event_name == 'workflow_dispatch' || - github.event.workflow_run.conclusion == 'success' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Determine version - id: version - env: - INPUT_VERSION: ${{ github.event.inputs.version }} - EVENT_NAME: ${{ github.event_name }} - run: | - if [ "$EVENT_NAME" = "workflow_dispatch" ]; then - VERSION="${INPUT_VERSION#v}" - else - VERSION=$(node -p 'require("./package.json").version') - fi - if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then - echo "::error::Invalid version: $VERSION" - exit 1 - fi - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - - name: Wait for npm publish - run: | - VERSION="${{ steps.version.outputs.version }}" - for i in $(seq 1 30); do - if npm view "@fintoc/cli@$VERSION" version 2>/dev/null; then - echo "Found @fintoc/cli@$VERSION on npm" - exit 0 - fi - echo "Attempt $i/30 — waiting 10s..." - sleep 10 - done - echo "Timed out waiting for @fintoc/cli@$VERSION on npm" - npm view "@fintoc/cli" versions --json || true - exit 1 - - - name: Download tarball and compute SHA256 - id: sha - run: | - VERSION="${{ steps.version.outputs.version }}" - URL="https://registry.npmjs.org/@fintoc/cli/-/cli-${VERSION}.tgz" - TARBALL=$(mktemp) - HTTP_CODE=$(curl -sL -o "$TARBALL" -w '%{http_code}' "$URL") - if [ "$HTTP_CODE" != "200" ]; then - echo "Failed to download tarball (HTTP $HTTP_CODE)" - exit 1 - fi - SHA256=$(sha256sum "$TARBALL" | awk '{print $1}') - echo "sha256=$SHA256" >> "$GITHUB_OUTPUT" - echo "url=$URL" >> "$GITHUB_OUTPUT" - - - name: Clone homebrew-tap - uses: actions/checkout@v4 - with: - repository: fintoc-com/homebrew-tap - token: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} - path: homebrew-tap - - - name: Update formula - env: - TARBALL_URL: ${{ steps.sha.outputs.url }} - TARBALL_SHA256: ${{ steps.sha.outputs.sha256 }} - VERSION: ${{ steps.version.outputs.version }} - run: | - for var in TARBALL_URL TARBALL_SHA256 VERSION; do - [ -z "${!var}" ] && echo "::error::$var is empty" && exit 1 - done - envsubst '$TARBALL_URL $TARBALL_SHA256 $VERSION' > homebrew-tap/Formula/fintoc.rb <<'RUBY' -class Fintoc < Formula - desc "CLI for the Fintoc API" - homepage "https://github.com/fintoc-com/fintoc-cli" - url "$TARBALL_URL" - version "$VERSION" - sha256 "$TARBALL_SHA256" - license "MIT" - - depends_on "node" - - def install - system "npm", "install", *std_npm_args - bin.install_symlink Dir["#{libexec}/bin/*"] - end - - test do - assert_match "fintoc/#{version}", shell_output("#{bin}/fintoc --version") - end -end -RUBY - - - name: Commit and push - run: | - VERSION="${{ steps.version.outputs.version }}" - cd homebrew-tap - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add Formula/fintoc.rb - if git diff --cached --quiet; then - echo "Formula already up to date" - exit 0 - fi - git commit -m "fintoc $VERSION - - https://github.com/fintoc-com/fintoc-cli/releases/tag/v$VERSION" - git push From 9afd31079e23d320f0bd7fc7a43f1b04bc01f32f Mon Sep 17 00:00:00 2001 From: Daniel Leal Date: Mon, 11 May 2026 16:55:22 -0400 Subject: [PATCH 2/2] chore(webhooks): change arrow directions --- src/lib/webhooks/__tests__/handlers.test.ts | 8 ++++---- src/lib/webhooks/handlers.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/webhooks/__tests__/handlers.test.ts b/src/lib/webhooks/__tests__/handlers.test.ts index fe9fca8..07621e5 100644 --- a/src/lib/webhooks/__tests__/handlers.test.ts +++ b/src/lib/webhooks/__tests__/handlers.test.ts @@ -40,7 +40,7 @@ describe('webhook relay handlers', () => { await createWebhookRelayHandlers({}).webhook_event!(message) - expect(log).toHaveBeenCalledWith('2026-05-11 14:52:12 --> payment.succeeded [evt_123]') + expect(log).toHaveBeenCalledWith('2026-05-11 14:52:12 <-- payment.succeeded [evt_123]') }) test('prints the full event in JSON mode', async () => { @@ -86,9 +86,9 @@ describe('webhook relay handlers', () => { }, body: event, }) - expect(log).toHaveBeenCalledWith('2026-05-11 14:52:12 --> payment.succeeded [evt_123]') + expect(log).toHaveBeenCalledWith('2026-05-11 14:52:12 <-- payment.succeeded [evt_123]') expect(log).toHaveBeenCalledWith( - '2026-05-11 14:52:13 <-- [204] POST https://webhook.site/828b463d-c4a4-4f6b-a450-7c2cdb575d63 [evt_123]', + '2026-05-11 14:52:13 --> [204] POST https://webhook.site/828b463d-c4a4-4f6b-a450-7c2cdb575d63 [evt_123]', ) }) @@ -104,7 +104,7 @@ describe('webhook relay handlers', () => { await createWebhookRelayHandlers({ events: ['payment.succeeded'] }).webhook_event!(message) - expect(log).toHaveBeenCalledWith('2026-05-11 14:52:12 --> payment.succeeded [evt_123]') + expect(log).toHaveBeenCalledWith('2026-05-11 14:52:12 <-- payment.succeeded [evt_123]') }) test('skips events not included in the event filter', async () => { diff --git a/src/lib/webhooks/handlers.ts b/src/lib/webhooks/handlers.ts index e76f156..cc1e06f 100644 --- a/src/lib/webhooks/handlers.ts +++ b/src/lib/webhooks/handlers.ts @@ -63,7 +63,7 @@ const displayWebhookEvent = (event: WebhookEvent, options: { json: boolean | und return } - log(`${dim(reformatDate(event.created_at))} --> ${bold(event.type)} [${event.id}]`) + log(`${dim(reformatDate(event.created_at))} <-- ${bold(event.type)} [${event.id}]`) } const colorStatusCode = (statusCode: number) => { @@ -89,7 +89,7 @@ const displayForwardResult = ( const timestamp = dim(reformatDate(result.timestamp)) const code = bold(colorStatusCode(result.statusCode)) - log(`${timestamp} <-- [${code}] ${result.method} ${result.url} [${result.event}]`) + log(`${timestamp} --> [${code}] ${result.method} ${result.url} [${result.event}]`) if (options.json) { log('\n')