Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 29 additions & 106 deletions .github/workflows/manual-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ on:
required: false
default: false
type: boolean
update_changelog:
description: "Update CHANGELOG.md with release notes"
required: false
default: false
type: boolean
notify_skills_repo:
description: "Notify the skills repo about the release"
required: false
Expand Down Expand Up @@ -76,44 +71,12 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Check if version needs to be bumped
id: check-version
run: |
if [[ "${{ github.event.inputs.version }}" =~ ^(patch|minor|major)$ ]]; then
echo "bump_version=true" >> $GITHUB_OUTPUT
echo "version_type=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
else
echo "bump_version=false" >> $GITHUB_OUTPUT
echo "version_type=" >> $GITHUB_OUTPUT
fi

- name: Bump version
if: steps.check-version.outputs.bump_version == 'true'
working-directory: ${{ env.CLI_PACKAGE_DIR }}
run: |
npm version ${{ steps.check-version.outputs.version_type }} --no-git-tag-version
echo "NEW_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV

- name: Set version manually
if: steps.check-version.outputs.bump_version == 'false'
- name: Set version
working-directory: ${{ env.CLI_PACKAGE_DIR }}
run: |
npm version ${{ github.event.inputs.version }} --no-git-tag-version
echo "NEW_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV

- name: Get previous release tag
id: prev-tag
run: |
PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
echo "tag=$PREV_TAG" >> $GITHUB_OUTPUT
echo "Previous tag: $PREV_TAG"
if [[ -n "$PREV_TAG" ]]; then
# Get the date of the previous tag in ISO format for filtering PRs
PREV_TAG_DATE=$(git log -1 --format=%aI "$PREV_TAG")
echo "date=$PREV_TAG_DATE" >> $GITHUB_OUTPUT
echo "Previous tag date: $PREV_TAG_DATE"
fi

- name: Build package
run: bun run build
working-directory: ${{ env.CLI_PACKAGE_DIR }}
Expand Down Expand Up @@ -144,16 +107,9 @@ jobs:
echo "Dry run: ${{ github.event.inputs.dry_run }}"

- name: Publish to NPM
if: github.event.inputs.dry_run == 'false'
working-directory: ${{ env.CLI_PACKAGE_DIR }}
run: |
npm publish --tag ${{ github.event.inputs.npm_tag }}

- name: Dry run publish
if: github.event.inputs.dry_run == 'true'
working-directory: ${{ env.CLI_PACKAGE_DIR }}
run: |
npm publish --dry-run --tag ${{ github.event.inputs.npm_tag }}
npm publish --tag ${{ github.event.inputs.npm_tag }} ${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}

- name: Create Git tag
if: github.event.inputs.dry_run == 'false'
Expand All @@ -162,87 +118,54 @@ jobs:
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
# Re-configure git remote to use the App token for push
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
git add ${{ env.CLI_PACKAGE_DIR }}/package.json
# Add CHANGELOG.md only if changelog update was requested
if [[ "${{ github.event.inputs.update_changelog }}" == "true" ]]; then
git add CHANGELOG.md
fi
git commit -m "chore: release v${{ env.NEW_VERSION }}"
git tag v${{ env.NEW_VERSION }}
git push origin HEAD:${{ github.ref }}
git push origin v${{ env.NEW_VERSION }}

- name: Create Release
if: github.event.inputs.dry_run == 'false'
id: create-release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ env.NEW_VERSION }}
release_name: Release v${{ env.NEW_VERSION }}
body: |
## What's Changed

This release includes the latest updates to the Base44 CLI.

### Installation

**npm:**
```bash
npm install -g base44@${{ github.event.inputs.npm_tag }}
```

**Homebrew:**
```bash
brew install base44/tap/base44
```

### Version: ${{ env.NEW_VERSION }}
### NPM Tag: ${{ github.event.inputs.npm_tag }}
draft: false
prerelease: false

- name: Upload binaries to GitHub Release
- name: Create GitHub Release and upload binaries
if: github.event.inputs.dry_run == 'false'
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
for file in ${{ env.CLI_PACKAGE_DIR }}/dist/binaries/base44-*; do
echo "Uploading $file..."
gh release upload "v${{ env.NEW_VERSION }}" "$file" --clobber
done

- name: Release summary
if: steps.prev-tag.outputs.tag != ''
run: |
if [ -f release-summary.md ]; then
cat release-summary.md | tee -a "$GITHUB_STEP_SUMMARY"
fi

- name: Generate token for skills repo
if: github.event.inputs.dry_run == 'false' && github.event.inputs.notify_skills_repo == 'true'
id: skills-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.BASE44_GITHUB_ACTIONS_APP_ID }}
private-key: ${{ secrets.BASE44_GITHUB_ACTIONS_APP_PRIVATE_KEY }}
owner: base44
repositories: skills
RELEASE_URL=$(gh release create "v${{ env.NEW_VERSION }}" \
--title "Release v${{ env.NEW_VERSION }}" \
--notes "## Installation

**npm:**
\`\`\`bash
npm install -g base44@${{ github.event.inputs.npm_tag }}
\`\`\`

**Homebrew:**
\`\`\`bash
brew install base44/tap/base44
\`\`\`

**Version:** ${{ env.NEW_VERSION }}
**NPM Tag:** ${{ github.event.inputs.npm_tag }}" \
${{ env.CLI_PACKAGE_DIR }}/dist/binaries/base44-*.tar.gz)
echo "RELEASE_URL=$RELEASE_URL" >> $GITHUB_ENV

- name: Update Homebrew Tap
if: github.event.inputs.dry_run == 'false' && github.event.inputs.npm_tag == 'latest'
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: bash ${{ env.CLI_PACKAGE_DIR }}/infra/homebrew/update-tap.sh "${{ env.NEW_VERSION }}" "${{ env.CLI_PACKAGE_DIR }}/dist/binaries"

- name: Notify skills repo
if: github.event.inputs.dry_run == 'false' && github.event.inputs.notify_skills_repo == 'true'
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ steps.skills-token.outputs.token }}
token: ${{ steps.generate-token.outputs.token }}
repository: base44/skills
event-type: cli-release
client-payload: |
{
"version": "v${{ env.NEW_VERSION }}",
"release_url": "${{ steps.create-release.outputs.html_url }}",
"release_url": "${{ env.RELEASE_URL }}",
"release_name": "Release v${{ env.NEW_VERSION }}"
}

Expand Down
17 changes: 14 additions & 3 deletions docs/binary-distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ The binary release pipeline has two steps:
2. **`infra/package-binaries.ts`** — packages each binary for release:
- Wraps each binary (renamed to `base44` / `base44.exe`) and `README.md` in a `.tar.gz` archive
- Deletes the raw binary
- Generates SHA256 checksums for each archive

```bash
bun run build # Must run first — produces dist/cli/ and dist/assets/
Expand Down Expand Up @@ -49,11 +48,23 @@ No `assetsDir` parameter is passed through CLIContext or function signatures. Ad

## Homebrew Formula

`infra/homebrew/base44.rb` is a reference template for the Homebrew tap. It downloads the `.tar.gz` archive for the user's platform from GitHub Releases. Homebrew auto-extracts the tarball, so the install block simply does `bin.install "base44"`. Copy it to the `homebrew-tap` repo and update version + SHA256 values on each release.
`infra/homebrew/base44.rb` is a template for the Homebrew tap formula. It downloads the `.tar.gz` archive for the user's platform from GitHub Releases. Homebrew auto-extracts the tarball, so the install block simply does `bin.install "base44"`.

The template uses `PLACEHOLDER_*` SHA256 values that are replaced automatically by CI on each release. The rendered formula is pushed to the [base44/homebrew-tap](https://github.com/base44/homebrew-tap) repository at `Formula/base44.rb`.

Users install with:
```bash
brew install base44/tap/base44
```

## CI Integration

The `manual-publish.yml` workflow runs `build:binaries` then `package:binaries` after `bun run build`, and uploads the resulting `.tar.gz` and `.sha256` files to the GitHub Release. Binaries are excluded from the npm package via `.npmignore`.
The `manual-publish.yml` workflow runs `build:binaries` then `package:binaries` after `bun run build`, and uploads the resulting `.tar.gz` files to the GitHub Release. Binaries are excluded from the npm package via `.npmignore`.

For `latest` tag releases (not beta/alpha), the workflow also updates the Homebrew tap automatically:
1. Computes SHA256 checksums for each platform archive
2. Renders the formula template with the new version and checksums
3. Pushes the updated formula to `base44/homebrew-tap`

## Rules

Expand Down
8 changes: 4 additions & 4 deletions packages/cli/infra/homebrew/base44.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Homebrew formula for the Base44 CLI.
# Homebrew formula template for the Base44 CLI.
#
# This is a reference template. Copy it to your homebrew-tap repository
# at Formula/base44.rb and update the version + SHA256 values on each release.
# CI renders this template with real version + SHA256 values and pushes it
# to base44/homebrew-tap via infra/homebrew/update-tap.sh.
#
# Users install with:
# brew install <org>/tap/base44
# brew install base44/tap/base44
#
class Base44 < Formula
desc "CLI for creating, managing, and deploying Base44 applications"
Expand Down
44 changes: 44 additions & 0 deletions packages/cli/infra/homebrew/update-tap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
#
# Update the Homebrew tap formula with a new version and SHA256 checksums.
#
# Usage: GH_TOKEN=<token> bash update-tap.sh <version> <binaries-dir>
#
# The script:
# 1. Computes SHA256 for each platform archive
# 2. Renders the formula template (base44.rb) with the real values
# 3. Clones base44/homebrew-tap, commits, and pushes
#
set -euo pipefail

VERSION="$1"
BINARIES_DIR="$2"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

SHA_DARWIN_ARM64=$(shasum -a 256 "$BINARIES_DIR/base44-darwin-arm64.tar.gz" | awk '{print $1}')
SHA_DARWIN_X64=$(shasum -a 256 "$BINARIES_DIR/base44-darwin-x64.tar.gz" | awk '{print $1}')
SHA_LINUX_ARM64=$(shasum -a 256 "$BINARIES_DIR/base44-linux-arm64.tar.gz" | awk '{print $1}')
SHA_LINUX_X64=$(shasum -a 256 "$BINARIES_DIR/base44-linux-x64.tar.gz" | awk '{print $1}')

cp "$SCRIPT_DIR/base44.rb" /tmp/base44.rb
sed -i "s/version \".*\"/version \"$VERSION\"/" /tmp/base44.rb
sed -i "s/PLACEHOLDER_DARWIN_ARM64/$SHA_DARWIN_ARM64/" /tmp/base44.rb
sed -i "s/PLACEHOLDER_DARWIN_X64/$SHA_DARWIN_X64/" /tmp/base44.rb
sed -i "s/PLACEHOLDER_LINUX_ARM64/$SHA_LINUX_ARM64/" /tmp/base44.rb
sed -i "s/PLACEHOLDER_LINUX_X64/$SHA_LINUX_X64/" /tmp/base44.rb

rm -rf /tmp/homebrew-tap
git clone "https://x-access-token:${GH_TOKEN}@github.com/base44/homebrew-tap.git" /tmp/homebrew-tap
mkdir -p /tmp/homebrew-tap/Formula
cp /tmp/base44.rb /tmp/homebrew-tap/Formula/base44.rb

cd /tmp/homebrew-tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/base44.rb
if git diff --cached --quiet; then
echo "Homebrew formula already up to date"
exit 0
fi
git commit -m "Update base44 to ${VERSION}"
git push origin master
20 changes: 1 addition & 19 deletions packages/cli/infra/package-binaries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
* Steps:
* 1. Wrap each binary + README.md in a .tar.gz archive
* 2. Delete the raw binary
* 3. Generate SHA256 checksums for each archive
*/
import { readFileSync, existsSync, unlinkSync, writeFileSync } from "node:fs";
import { readFileSync, existsSync, unlinkSync } from "node:fs";
import { join } from "node:path";
import chalk from "chalk";

Expand Down Expand Up @@ -60,23 +59,6 @@ for (const { output } of TARGETS) {
unlinkSync(binaryPath);
}

// ---------------------------------------------------------------------------
// 2. Generate SHA256 checksums
// ---------------------------------------------------------------------------
console.log(chalk.dim(" Generating checksums..."));

for (const { output } of TARGETS) {
const archiveName = `${output.replace(/\.exe$/, "")}.tar.gz`;
const archivePath = join(BINARIES_DIR, archiveName);
const hasher = new Bun.CryptoHasher("sha256");
hasher.update(readFileSync(archivePath));
const hash = hasher.digest("hex");
writeFileSync(
join(BINARIES_DIR, `${archiveName}.sha256`),
`${hash} ${archiveName}\n`,
);
}

// ---------------------------------------------------------------------------
// Done
// ---------------------------------------------------------------------------
Expand Down
Loading