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
46 changes: 39 additions & 7 deletions .github/workflows/release-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ on:
required: false
type: string

permissions:
contents: write

jobs:
build:
name: Build - macOS (${{ matrix.arch }})
Expand Down Expand Up @@ -66,14 +69,19 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bun run package

# Upload artifacts
- name: Upload artifacts
# Upload artifacts separately to avoid nested zips
- name: Upload DMG artifact
uses: actions/upload-artifact@v4
with:
name: desktop-mac-${{ matrix.arch }}
path: |
apps/desktop/release/*.dmg
apps/desktop/release/*.zip
name: desktop-mac-${{ matrix.arch }}-dmg
path: apps/desktop/release/*.dmg
retention-days: 30

- name: Upload ZIP artifact
uses: actions/upload-artifact@v4
with:
name: desktop-mac-${{ matrix.arch }}-zip
path: apps/desktop/release/*.zip
retention-days: 30

# Create GitHub Release after builds complete
Expand All @@ -91,11 +99,35 @@ jobs:
uses: actions/download-artifact@v4
with:
path: release-artifacts
merge-multiple: true

- name: Create stable-named copies for latest download URLs
run: |
cd release-artifacts
# Create stable-named copies (without version) for /releases/latest/download/ URLs
for file in *.dmg; do
if [[ -f "$file" ]]; then
# Extract architecture from filename (e.g., Superset-0.0.1-arm64.dmg -> arm64)
arch=$(echo "$file" | sed -E 's/.*-([^-]+)\.dmg$/\1/')
cp "$file" "Superset-${arch}.dmg"
echo "Created stable copy: Superset-${arch}.dmg"
fi
done
for file in *-mac.zip; do
if [[ -f "$file" ]]; then
# Extract architecture from filename (e.g., Superset-0.0.1-arm64-mac.zip -> arm64)
arch=$(echo "$file" | sed -E 's/.*-([^-]+)-mac\.zip$/\1/')
cp "$file" "Superset-${arch}-mac.zip"
echo "Created stable copy: Superset-${arch}-mac.zip"
fi
done
echo "Release artifacts:"
ls -la

- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: release-artifacts/**/*
files: release-artifacts/*
draft: true
generate_release_notes: true
name: Superset Desktop ${{ github.ref_name }}
Expand Down
273 changes: 273 additions & 0 deletions apps/desktop/create-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
#!/usr/bin/env bash

# Desktop App Release Script
# Based on apps/desktop/RELEASE.md
#
# Usage:
# ./create-release.sh <version>
# Example: ./create-release.sh 0.0.1
#
# This script will:
# 1. Verify prerequisites (clean git, GitHub CLI authenticated)
# 2. Delete existing release/tag if republishing same version
# 3. Update package.json version
# 4. Create and push a git tag to trigger the release workflow
# 5. Monitor the GitHub Actions workflow in real-time
# 6. Auto-publish the release when build completes
#
# Features:
# - Supports republishing: Running with same version will clean up and rebuild
# - Auto-publishes release (no manual publish step needed)
#
# Requirements:
# - GitHub CLI (gh) installed and authenticated
# - Clean working directory
# - Running from monorepo root

set -e # Exit on error

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Helper functions
info() {
echo -e "${BLUE}ℹ ${NC}$1"
}

success() {
echo -e "${GREEN}✓${NC} $1"
}

warn() {
echo -e "${YELLOW}⚠${NC} $1"
}

error() {
echo -e "${RED}✗${NC} $1"
exit 1
}

# Check if version argument is provided
if [ -z "$1" ]; then
error "Usage: $0 <version>\nExample: $0 0.0.1"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix error message formatting.

The error message uses \n but the error() function (line 44–47) doesn't use the -e flag with echo, so escape sequences won't be interpreted. The newline won't render.

Apply this diff:

-    error "Usage: $0 <version>\nExample: $0 0.0.1"
+    error "Usage: $0 <version>"
+    error "Example: $0 0.0.1"

Alternatively, update the error() function to use echo -e:

 error() {
-    echo -e "${RED}✗${NC} $1"
+    echo -e "${RED}✗${NC} $1" >&2
     exit 1
 }

(The >&2 redirection is also recommended for error output.)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
error "Usage: $0 <version>\nExample: $0 0.0.1"
error "Usage: $0 <version>"
error "Example: $0 0.0.1"
🤖 Prompt for AI Agents
In apps/desktop/create-release.sh around line 51, the error message uses "\n"
but the error() helper doesn't pass -e to echo so escapes won't be interpreted;
update the error() function (lines ~44–47) to use echo -e and redirect output to
stderr (e.g., echo -e "msg" >&2) or change the specific call at line 51 to
invoke echo -e >&2 so the newline renders and the message is sent to stderr.

fi

VERSION="$1"
TAG_NAME="desktop-v${VERSION}"
DESKTOP_DIR="apps/desktop"

# Check if gh CLI is installed
if ! command -v gh &> /dev/null; then
error "GitHub CLI (gh) is required but not installed.\nInstall it from: https://cli.github.com/"
fi

# Check if authenticated with gh
if ! gh auth status &> /dev/null; then
error "Not authenticated with GitHub CLI.\nRun: gh auth login"
fi

info "Starting release process for version ${VERSION}"
echo ""

# Check if we're in the monorepo root
if [ ! -f "package.json" ] || [ ! -d "apps/desktop" ]; then
error "Please run this script from the monorepo root directory"
fi

# Navigate to desktop app directory
cd "${DESKTOP_DIR}"

# 1. Check for uncommitted changes
info "Checking for uncommitted changes..."
if ! git diff-index --quiet HEAD --; then
error "You have uncommitted changes. Please commit or stash them first."
fi
success "Working directory is clean"

# 2. Check if tag/release already exists
info "Checking if tag ${TAG_NAME} already exists..."
if git rev-parse "${TAG_NAME}" >/dev/null 2>&1; then
echo ""
warn "Tag ${TAG_NAME} already exists!"

# Check if there's also a GitHub release
if gh release view "${TAG_NAME}" &>/dev/null; then
RELEASE_STATUS=$(gh release view "${TAG_NAME}" --json isDraft --jq 'if .isDraft then "draft" else "published"' 2>/dev/null || echo "unknown")
echo -e " GitHub release: ${YELLOW}${RELEASE_STATUS}${NC}"
else
echo -e " GitHub release: ${YELLOW}none${NC}"
fi
echo ""

# Ask user what to do
echo "What would you like to do?"
echo " 1) Republish - Delete existing release/tag and create new one"
echo " 2) Cancel - Exit without changes"
echo ""
read -p "Enter choice [1-2]: " choice

case $choice in
1)
info "Cleaning up for republish..."

# Delete the GitHub release if it exists
if gh release view "${TAG_NAME}" &>/dev/null; then
info "Deleting existing GitHub release..."
gh release delete "${TAG_NAME}" --yes
success "Deleted existing release"
fi

# Delete remote tag
info "Deleting remote tag..."
git push origin --delete "${TAG_NAME}" 2>/dev/null || true
success "Deleted remote tag"

# Delete local tag
info "Deleting local tag..."
git tag -d "${TAG_NAME}" 2>/dev/null || true
success "Deleted local tag"
;;
2|*)
info "Cancelled. No changes made."
exit 0
;;
esac
fi
success "Tag ${TAG_NAME} is available"

# 3. Update version in package.json
info "Updating version in package.json..."
CURRENT_VERSION=$(node -p "require('./package.json').version")
if [ "${CURRENT_VERSION}" == "${VERSION}" ]; then
warn "package.json already has version ${VERSION}"
else
# Update the version using jq to handle workspace dependencies
TMP_FILE=$(mktemp)
jq ".version = \"${VERSION}\"" package.json > "${TMP_FILE}" && mv "${TMP_FILE}" package.json
Comment on lines +149 to +150
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add error handling for jq operations.

The temporary file and jq pipeline on lines 100–101 lack error handling. If jq fails or the file move is interrupted, the script continues with corrupted state.

Apply this diff to add proper error handling:

-    TMP_FILE=$(mktemp)
-    jq ".version = \"${VERSION}\"" package.json > "${TMP_FILE}" && mv "${TMP_FILE}" package.json
+    TMP_FILE=$(mktemp) || error "Failed to create temporary file"
+    if jq ".version = \"${VERSION}\"" package.json > "${TMP_FILE}"; then
+        mv "${TMP_FILE}" package.json || error "Failed to move updated package.json"
+    else
+        rm -f "${TMP_FILE}"
+        error "Failed to update package.json version"
+    fi

This ensures cleanup and explicit error messages if any step fails.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/desktop/create-release.sh around lines 100–101, the TMP_FILE creation,
jq write and mv lack error handling; create TMP_FILE and immediately test its
success, add a trap to remove TMP_FILE on EXIT, run jq writing to TMP_FILE and
check jq's exit status before proceeding, on jq failure delete TMP_FILE and exit
with an error, only run mv if jq succeeded and check mv's exit status (on
failure delete TMP_FILE and exit with an error), and emit clear error messages
for each failure so the script cannot continue with a corrupted package.json.

success "Updated package.json from ${CURRENT_VERSION} to ${VERSION}"

# Commit the version change
git add package.json
git commit -m "chore(desktop): bump version to ${VERSION}"
success "Committed version change"
fi

# 4. Push changes
info "Pushing changes to remote..."
CURRENT_BRANCH=$(git branch --show-current)
git push origin "${CURRENT_BRANCH}"
success "Changes pushed to ${CURRENT_BRANCH}"

# 5. Create and push tag
info "Creating tag ${TAG_NAME}..."
git tag "${TAG_NAME}"
success "Tag ${TAG_NAME} created"

info "Pushing tag to trigger release workflow..."
git push origin "${TAG_NAME}"
success "Tag pushed to remote"

echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}🎉 Release process initiated successfully!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""

# Get repository information
REPO=$(git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')

# 6. Monitor the workflow
info "Monitoring GitHub Actions workflow..."
echo " Waiting for workflow to start (this may take a few seconds)..."

# Wait and retry to find the workflow run
MAX_RETRIES=6
RETRY_COUNT=0
WORKFLOW_RUN=""

while [ $RETRY_COUNT -lt $MAX_RETRIES ] && [ -z "$WORKFLOW_RUN" ]; do
sleep 5
WORKFLOW_RUN=$(gh run list --workflow=release-desktop.yml --json databaseId,headBranch,status --jq ".[] | select(.headBranch == \"${TAG_NAME}\") | .databaseId" | head -1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Script executed:

#!/bin/bash
# Inspect the create-release.sh script around line 145
sed -n '130,160p' apps/desktop/create-release.sh

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:


# Get repository information
REPO=$(git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')

# 6. Monitor the workflow
info "Monitoring GitHub Actions workflow..."
echo "  Waiting for workflow to start (this may take a few seconds)..."

# Wait and retry to find the workflow run
MAX_RETRIES=6
RETRY_COUNT=0
WORKFLOW_RUN=""

while [ $RETRY_COUNT -lt $MAX_RETRIES ] && [ -z "$WORKFLOW_RUN" ]; do
    sleep 5
    WORKFLOW_RUN=$(gh run list --workflow=release-desktop.yml --json databaseId,headBranch,status --jq ".[] | select(.headBranch == \"${TAG_NAME}\") | .databaseId" | head -1)
    RETRY_COUNT=$((RETRY_COUNT + 1))

    if [ -z "$WORKFLOW_RUN" ] && [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
        echo "  Still waiting... (attempt $RETRY_COUNT/$MAX_RETRIES)"
    fi
done

if [ -z "$WORKFLOW_RUN" ]; then
    warn "Could not find workflow run automatically"
    echo "  Manual monitoring URL:"
    echo "  https://github.com/${REPO}/actions"
    echo ""
    warn "The workflow may still be starting. Check the URL above in a few moments."
else
    success "Found workflow run: ${WORKFLOW_RUN}"

</shell_result>


Script executed:

#!/bin/bash
# Also check the beginning of the script to see TAG_NAME derivation
sed -n '1,100p' apps/desktop/create-release.sh | head -80

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:

#!/usr/bin/env bash

# Desktop App Release Script
# Based on apps/desktop/RELEASE.md
#
# Usage:
#   ./create-release.sh <version>
#   Example: ./create-release.sh 0.0.1
#
# This script will:
# 1. Verify prerequisites (clean git, GitHub CLI authenticated)
# 2. Update package.json version
# 3. Create and push a git tag to trigger the release workflow
# 4. Monitor the GitHub Actions workflow in real-time
# 5. Display the draft release URL when ready
#
# Requirements:
# - GitHub CLI (gh) installed and authenticated
# - Clean working directory
# - Running from monorepo root

set -e  # Exit on error

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Helper functions
info() {
    echo -e "${BLUE}ℹ ${NC}$1"
}

success() {
    echo -e "${GREEN}✓${NC} $1"
}

warn() {
    echo -e "${YELLOW}⚠${NC} $1"
}

error() {
    echo -e "${RED}✗${NC} $1"
    exit 1
}

# Check if version argument is provided
if [ -z "$1" ]; then
    error "Usage: $0 <version>\nExample: $0 0.0.1"
fi

VERSION="$1"
TAG_NAME="desktop-v${VERSION}"
DESKTOP_DIR="apps/desktop"

# Check if gh CLI is installed
if ! command -v gh &> /dev/null; then
    error "GitHub CLI (gh) is required but not installed.\nInstall it from: https://cli.github.com/"
fi

# Check if authenticated with gh
if ! gh auth status &> /dev/null; then
    error "Not authenticated with GitHub CLI.\nRun: gh auth login"
fi

info "Starting release process for version ${VERSION}"
echo ""

# Check if we're in the monorepo root
if [ ! -f "package.json" ] || [ ! -d "apps/desktop" ]; then
    error "Please run this script from the monorepo root directory"
fi

# Navigate to desktop app directory
cd "${DESKTOP_DIR}"

# 1. Check for uncommitted changes
info "Checking for uncommitted changes..."

</shell_result>


Script executed:

#!/bin/bash
# Find where TAG_NAME is used, particularly the git push command
sed -n '100,130p' apps/desktop/create-release.sh

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:

    TMP_FILE=$(mktemp)
    jq ".version = \"${VERSION}\"" package.json > "${TMP_FILE}" && mv "${TMP_FILE}" package.json
    success "Updated package.json from ${CURRENT_VERSION} to ${VERSION}"

    # Commit the version change
    git add package.json
    git commit -m "chore(desktop): bump version to ${VERSION}"
    success "Committed version change"
fi

# 4. Push changes
info "Pushing changes to remote..."
CURRENT_BRANCH=$(git branch --show-current)
git push origin "${CURRENT_BRANCH}"
success "Changes pushed to ${CURRENT_BRANCH}"

# 5. Create and push tag
info "Creating tag ${TAG_NAME}..."
git tag "${TAG_NAME}"
success "Tag ${TAG_NAME} created"

info "Pushing tag to trigger release workflow..."
git push origin "${TAG_NAME}"
success "Tag pushed to remote"

echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}🎉 Release process initiated successfully!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""

</shell_result>


Script executed:

#!/bin/bash
# Search for the entire tag push section
sed -n '116,125p' apps/desktop/create-release.sh

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:

# 5. Create and push tag
info "Creating tag ${TAG_NAME}..."
git tag "${TAG_NAME}"
success "Tag ${TAG_NAME} created"

info "Pushing tag to trigger release workflow..."
git push origin "${TAG_NAME}"
success "Tag pushed to remote"

echo ""

</shell_result>


Script executed:

#!/bin/bash
# Get the git operations and tag push
sed -n '110,135p' apps/desktop/create-release.sh

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:

# 4. Push changes
info "Pushing changes to remote..."
CURRENT_BRANCH=$(git branch --show-current)
git push origin "${CURRENT_BRANCH}"
success "Changes pushed to ${CURRENT_BRANCH}"

# 5. Create and push tag
info "Creating tag ${TAG_NAME}..."
git tag "${TAG_NAME}"
success "Tag ${TAG_NAME} created"

info "Pushing tag to trigger release workflow..."
git push origin "${TAG_NAME}"
success "Tag pushed to remote"

echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}🎉 Release process initiated successfully!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""

# Get repository information
REPO=$(git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')

# 6. Monitor the workflow
info "Monitoring GitHub Actions workflow..."

</shell_result>


Script executed:

#!/bin/bash
# Query recent workflow runs to inspect the actual structure returned by gh
# This will show us what fields are available
gh run list --workflow=release-desktop.yml --json databaseId,headBranch,baseRef,headRef,headRefName,name,status,conclusion --limit 3

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 1

stderr:

Unknown JSON field: "baseRef"
Available fields:
  attempt
  conclusion
  createdAt
  databaseId
  displayTitle
  event
  headBranch
  headSha
  name
  number
  startedAt
  status
  updatedAt
  url
  workflowDatabaseId
  workflowName

</shell_result>


Script executed:

#!/bin/bash
# Also check the release-desktop.yml workflow file to understand the trigger
find . -name "release-desktop.yml" -type f

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:

./.github/workflows/release-desktop.yml

</shell_result>


Script executed:

#!/bin/bash
# Read the workflow file to see the trigger configuration
cat -n ./github/workflows/release-desktop.yml

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 1

stderr:

cat: ./github/workflows/release-desktop.yml: No such file or directory

</shell_result>


Script executed:

#!/bin/bash
# Search for other gh run list usages in the codebase
rg "gh run list" --type bash -A 3 -B 3

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 2

stderr:

unrecognized file type: bash

</shell_result>


Script executed:

#!/bin/bash
# Check GitHub CLI documentation for tag-triggered workflow filtering
# Also check if there's any test or documentation about this
find . -type f -name "*.md" | xargs grep -l "gh run" 2>/dev/null | head -5

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

</shell_result>


Script executed:

#!/bin/bash
# Create a test to understand GitHub Actions API response for different trigger types
# Query the GitHub API documentation info (if available) or check gh help
gh run list --help | grep -A 20 "json"

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:

      --json fields       Output JSON with the specified fields
  -L, --limit int         Maximum number of runs to fetch (default 20)
  -s, --status string     Filter runs by status: {queued|completed|in_progress|requested|waiting|pending|action_required|cancelled|failure|neutral|skipped|stale|startup_failure|success|timed_out}
  -t, --template string   Format JSON output using a Go template; see "gh help formatting"
  -u, --user string       Filter runs by user who triggered the run
  -w, --workflow string   Filter runs by workflow

INHERITED FLAGS
      --help                     Show help for command
  -R, --repo [HOST/]OWNER/REPO   Select another repository using the [HOST/]OWNER/REPO format

JSON FIELDS
  attempt, conclusion, createdAt, databaseId, displayTitle, event, headBranch,
  headSha, name, number, startedAt, status, updatedAt, url, workflowDatabaseId,
  workflowName

LEARN MORE
  Use `gh <command> <subcommand> --help` for more information about a command.
  Read the manual at https://cli.github.com/manual
  Learn about exit codes using `gh help exit-codes`
  Learn about accessibility experiences using `gh help accessibility`

</shell_result>


Script executed:

#!/bin/bash
# Check if there's any RELEASE.md or documentation about the release process
cat -n apps/desktop/RELEASE.md 2>/dev/null | head -50

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:

     1	# Desktop App Release Process
     2	
     3	This document describes how to create a release for the Superset Desktop application.
     4	
     5	## Prerequisites
     6	
     7	- Ensure all changes are committed and pushed to the repository
     8	- Ensure the build works locally: `bun run package`
     9	- Update version in `package.json` if needed
    10	
    11	## Release Methods
    12	
    13	### Method 1: Tag-Based Release (Recommended)
    14	
    15	Create and push a git tag with the format `desktop-v*.*.*`:
    16	
    17	```bash
    18	# Create a tag (e.g., desktop-v1.0.0)
    19	git tag desktop-v1.0.0
    20	
    21	# Push the tag to trigger the release workflow
    22	git push origin desktop-v1.0.0
    23	```
    24	
    25	This will automatically:
    26	1. Build the app for macOS (arm64), Windows (x64), and Linux (x64)
    27	2. Create artifacts for each platform
    28	3. Create a draft GitHub release with all binaries attached
    29	
    30	### Method 2: Manual Workflow Dispatch
    31	
    32	You can also trigger a release manually from GitHub Actions:
    33	
    34	1. Go to Actions → Release Desktop App
    35	2. Click "Run workflow"
    36	3. Enter the version number (e.g., `1.0.0`)
    37	4. Click "Run workflow"
    38	
    39	This method is useful for testing the workflow or creating builds without creating a tag.
    40	
    41	## Workflow Overview
    42	
    43	The release workflow (`.github/workflows/release-desktop.yml`) performs the following:
    44	
    45	### Build Platform
    46	
    47	Builds are created for:
    48	- **macOS**: arm64 (Apple Silicon) - produces `.dmg` and `.zip`
    49	
    50	### Build Steps

</shell_result>


Script executed:

#!/bin/bash
# Read the RELEASE.md file
cat -n apps/desktop/RELEASE.md

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 0

stdout:

     1	# Desktop App Release Process
     2	
     3	This document describes how to create a release for the Superset Desktop application.
     4	
     5	## Prerequisites
     6	
     7	- Ensure all changes are committed and pushed to the repository
     8	- Ensure the build works locally: `bun run package`
     9	- Update version in `package.json` if needed
    10	
    11	## Release Methods
    12	
    13	### Method 1: Tag-Based Release (Recommended)
    14	
    15	Create and push a git tag with the format `desktop-v*.*.*`:
    16	
    17	```bash
    18	# Create a tag (e.g., desktop-v1.0.0)
    19	git tag desktop-v1.0.0
    20	
    21	# Push the tag to trigger the release workflow
    22	git push origin desktop-v1.0.0
    23	```
    24	
    25	This will automatically:
    26	1. Build the app for macOS (arm64), Windows (x64), and Linux (x64)
    27	2. Create artifacts for each platform
    28	3. Create a draft GitHub release with all binaries attached
    29	
    30	### Method 2: Manual Workflow Dispatch
    31	
    32	You can also trigger a release manually from GitHub Actions:
    33	
    34	1. Go to Actions → Release Desktop App
    35	2. Click "Run workflow"
    36	3. Enter the version number (e.g., `1.0.0`)
    37	4. Click "Run workflow"
    38	
    39	This method is useful for testing the workflow or creating builds without creating a tag.
    40	
    41	## Workflow Overview
    42	
    43	The release workflow (`.github/workflows/release-desktop.yml`) performs the following:
    44	
    45	### Build Platform
    46	
    47	Builds are created for:
    48	- **macOS**: arm64 (Apple Silicon) - produces `.dmg` and `.zip`
    49	
    50	### Build Steps
    51	
    52	1. Checkout code
    53	2. Setup Bun
    54	3. Install dependencies
    55	4. Clean dev folder (`bun run clean:dev`)
    56	5. Compile app with electron-vite (`bun run compile:app`)
    57	6. Package with electron-builder (`bun run package`)
    58	7. Upload artifacts
    59	
    60	### Release Creation
    61	
    62	After the build completes (tag-based releases only):
    63	1. Downloads all artifacts
    64	2. Creates a draft GitHub release
    65	3. Attaches all binaries to the release
    66	4. Generates release notes from commits
    67	
    68	## Code Signing (Optional)
    69	
    70	To enable macOS code signing, add the following secrets to your GitHub repository:
    71	
    72	```yaml
    73	CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
    74	CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
    75	APPLEID: ${{ secrets.APPLE_ID }}
    76	APPLEIDPASS: ${{ secrets.APPLE_ID_PASSWORD }}
    77	```
    78	
    79	Then uncomment the environment variables in the workflow under "Build Electron app".
    80	
    81	## Publishing the Release
    82	
    83	1. After the workflow completes, go to GitHub Releases
    84	2. Find the draft release
    85	3. Review the release notes and binaries
    86	4. Edit the release description if needed
    87	5. Click "Publish release" to make it public
    88	
    89	## Build Outputs
    90	
    91	### macOS (arm64)
    92	- `Superset-<version>-arm64.dmg` - DMG installer
    93	- `Superset-<version>-arm64-mac.zip` - Zipped app bundle
    94	
    95	## Troubleshooting
    96	
    97	### Build fails on macOS
    98	
    99	- Ensure you're building for the correct architecture (arm64 is configured by default)
   100	- Check that icon files exist at `src/resources/build/icons/icon.icns`
   101	- Verify that dependencies are properly installed
   102	
   103	### Native module errors
   104	
   105	- `node-pty` is configured as a native module in both `electron.vite.config.ts` and `electron-builder.ts`
   106	- It's externalized during build and unpacked from ASAR
   107	- If you add more native modules, update both configuration files
   108	
   109	### Missing icons error
   110	
   111	- The macOS build requires `icon.icns` in `src/resources/build/icons/`
   112	- Ensure this file is committed to the repository
   113	
   114	## Local Testing
   115	
   116	To test the build locally before releasing:
   117	
   118	```bash
   119	cd apps/desktop
   120	
   121	# Clean and compile
   122	bun run clean:dev
   123	bun run compile:app
   124	
   125	# Package the app
   126	bun run package
   127	```
   128	
   129	The output will be in `apps/desktop/release/`.
   130	
   131	## Building for Intel Macs (x64)
   132	
   133	To also build for Intel Macs, update `electron-builder.ts`:
   134	
   135	```typescript
   136	mac: {
   137	  target: [
   138	    {
   139	      target: "default",
   140	      arch: ["arm64", "x64"], // Add x64 for Intel Macs
   141	    },
   142	  ],
   143	}
   144	```
   145	
   146	Note: This will increase build time significantly.
   147	
   148	## Adding Windows/Linux Builds
   149	
   150	Currently only macOS builds are supported in CI/CD. To add Windows or Linux:
   151	
   152	1. Add PNG icon files to `src/resources/build/icons/` (for Linux)
   153	2. Update the workflow matrix in `.github/workflows/release-desktop.yml`
   154	3. Update `electron-builder.ts` configuration as needed

</shell_result>


Script executed:

#!/bin/bash
# Search for any commented examples or documentation about headBranch
rg "headBranch|baseRef|headRef" --type bash -B 5 -A 5

Repository: superset-sh/superset


<shell_result>
Repository: superset-sh/superset
Exit code: 2

stderr:

unrecognized file type: bash

</shell_result>


GitHub Actions API workflow run headBranch field tag push trigger


<web_search_result>
Short answer: when a workflow run is triggered by a push of a tag, the workflow-run/Actions API does not populate a branch name in head_branch — you should treat head_branch as null and use the run's ref/head_sha (or the push webhook's ref which will be refs/tags/) to detect the tag and its commit. [1][2][3]

Why / how to handle it

  • A push event for a tag sets GITHUB_REF to refs/tags/ (not a refs/heads/... branch). Use that ref or the run's head_sha to identify the tag commit. [1]
  • The Actions REST API / workflow-run responses expose head_branch (branch name) and head_sha (commit SHA); head_branch can be null (e.g., non-branch refs or certain fork/PR cases), so rely on head_sha + ref to tell tags apart. [2][3]

References

  • Push & trigger behavior (ref is refs/tags/ for tag pushes). [1]
  • Actions REST API / workflow run listing (head_branch / head_sha fields). [2]
  • Community / API notes showing head_branch may be null (use head_sha/ref instead). [3]
    </web_search_result>

GitHub Actions API headRefName field tag workflow runs


<web_search_result>
Short answer: headRefName (aka head_ref / github.head_ref) is only set for pull_request (and pull_request_target) runs — it will be null/empty for tag (push tag / release) workflow runs. For tag-triggered runs use the run's ref/ref_name or head_sha instead.

Sources:

  • GitHub Actions contexts: github.head_ref only available for pull_request / pull_request_target; github.ref and github.ref_name show branch or tag refs. [1]
  • REST API workflow-runs docs: list/get workflow run responses do not populate a pull-request head_ref for push/tag events; use ref/head_sha to identify the trigger. [2]

References:
[1] https://docs.github.com/en/actions/using-workflows/contexts-and-expression-syntax-for-github-actions#github-context
[2] https://docs.github.com/en/rest/actions/workflow-runs (List/Get workflow runs)
</web_search_result>


Workflow detection filter is incompatible with tag-triggered runs.

Line 145 filters by headBranch == "${TAG_NAME}", but GitHub Actions does not populate headBranch for workflows triggered by tag pushes—it returns null or the underlying branch (e.g., "main"), never the tag name. This filter will always fail, exhausting all retries and falling back to the manual monitoring warning.

Available fields in gh run list output are: attempt, conclusion, createdAt, databaseId, displayTitle, event, headBranch, headSha, name, number, startedAt, status, updatedAt, url, workflowDatabaseId, workflowName. Use event or headSha combined with commit metadata to identify the tag-triggered run, or query the workflow run details via a different mechanism.

Suggested investigation: query a recent run using gh run view <run-id> --json event,headBranch to confirm the actual API response for tag-triggered workflows, then adjust the filter accordingly.

🤖 Prompt for AI Agents
In apps/desktop/create-release.sh around line 145, the script filters workflow
runs by headBranch == "${TAG_NAME}", which fails for tag-triggered runs because
headBranch is not the tag; instead, resolve the tag's commit SHA (e.g.,
TAG_SHA=$(git rev-parse "${TAG_NAME}")) and use gh run list
--workflow=release-desktop.yml --json databaseId,event,headSha --jq '.[] |
select(.event=="push" and .headSha == "'"${TAG_SHA}"'") | .databaseId' to
reliably find the run for that tag (alternatively query runs and call gh run
view <id> --json event,headBranch to inspect responses before finalizing the
filter).

RETRY_COUNT=$((RETRY_COUNT + 1))

if [ -z "$WORKFLOW_RUN" ] && [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
echo " Still waiting... (attempt $RETRY_COUNT/$MAX_RETRIES)"
fi
done

if [ -z "$WORKFLOW_RUN" ]; then
warn "Could not find workflow run automatically"
echo " Manual monitoring URL:"
echo " https://github.com/${REPO}/actions"
echo ""
warn "The workflow may still be starting. Check the URL above in a few moments."
else
success "Found workflow run: ${WORKFLOW_RUN}"
echo ""
info "Watching workflow progress..."
echo " View in browser: https://github.com/${REPO}/actions/runs/${WORKFLOW_RUN}"
echo ""

# Watch the workflow (this will stream the status)
gh run watch "${WORKFLOW_RUN}" || warn "Workflow monitoring interrupted"

# Check final status
WORKFLOW_STATUS=$(gh run view "${WORKFLOW_RUN}" --json conclusion --jq .conclusion)

if [ "$WORKFLOW_STATUS" == "success" ]; then
success "Workflow completed successfully!"
elif [ "$WORKFLOW_STATUS" == "failure" ]; then
error "Workflow failed. Please check the logs at: https://github.com/${REPO}/actions/runs/${WORKFLOW_RUN}"
else
warn "Workflow ended with status: ${WORKFLOW_STATUS}"
fi
fi
Comment on lines +183 to +228
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Workflow monitoring cascades into false-negative state due to filter bug.

Because the workflow detection on line 145 uses an incorrect filter (as noted in the critical issue above), the monitoring section (lines 134–179) will always enter the else branch at line 153, displaying a warning that "Could not find workflow run automatically" and directing users to manual monitoring. This masks the underlying filter bug and degrades user experience.

Once the filter bug on line 145 is fixed, this section should work correctly.

🤖 Prompt for AI Agents
In apps/desktop/create-release.sh around lines 134 to 179, the workflow
detection fails because the jq filter only checks .headBranch, which is null for
tag-triggered runs; change the WORKFLOW_RUN assignment so the jq select matches
either .headBranch or .headRef (the tag ref) — e.g. use a predicate like
select((.headBranch == "${TAG_NAME}") or (.headRef == "${TAG_NAME}")) — so
tagged releases are found and the monitoring branch works as intended.


echo ""

# 7. Wait for release and publish it
info "Waiting for draft release to be created..."

# Retry logic for draft release (it may take time to be created)
MAX_RELEASE_RETRIES=10
RELEASE_RETRY_COUNT=0
RELEASE_FOUND=""

while [ $RELEASE_RETRY_COUNT -lt $MAX_RELEASE_RETRIES ] && [ -z "$RELEASE_FOUND" ]; do
sleep 3
RELEASE_FOUND=$(gh release list --json tagName,isDraft --jq ".[] | select(.tagName == \"${TAG_NAME}\") | .tagName")
RELEASE_RETRY_COUNT=$((RELEASE_RETRY_COUNT + 1))

if [ -z "$RELEASE_FOUND" ] && [ $RELEASE_RETRY_COUNT -lt $MAX_RELEASE_RETRIES ]; then
echo " Waiting for release to be created... (attempt $RELEASE_RETRY_COUNT/$MAX_RELEASE_RETRIES)"
fi
done

if [ -z "$RELEASE_FOUND" ]; then
warn "Release not found yet. It may still be processing."
echo " Check releases at: https://github.com/${REPO}/releases"
else
# Publish the release
info "Publishing release..."
gh release edit "${TAG_NAME}" --draft=false
success "Release published!"

RELEASE_URL="https://github.com/${REPO}/releases/tag/${TAG_NAME}"
LATEST_URL="https://github.com/${REPO}/releases/latest"
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}🎉 Release Published!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e "${BLUE}Release URL:${NC} ${RELEASE_URL}"
echo -e "${BLUE}Latest URL:${NC} ${LATEST_URL}"
echo ""
echo -e "${BLUE}Direct downloads:${NC}"
echo " • ${LATEST_URL}/download/Superset-${VERSION}-arm64.dmg"
echo " • ${LATEST_URL}/download/Superset-${VERSION}-arm64-mac.zip"
echo ""
fi
3 changes: 3 additions & 0 deletions apps/desktop/electron-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ const config: Configuration = {
copyright: `Copyright © ${currentYear} — ${author}`,
electronVersion: pkg.devDependencies.electron.replace(/^\^/, ""),

// Disable auto-publish - handled by separate workflow step
publish: null,

// Directories
directories: {
output: "release",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@superset/desktop",
"productName": "Superset",
"description": "The last developer tool you'll ever need",
"version": "0.0.0",
"version": "0.0.1",
"main": "./dist/main/index.js",
"resources": "src/resources",
"repository": {
Expand Down
3 changes: 1 addition & 2 deletions apps/desktop/src/main/lib/terminal-history.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,7 @@ describe("HistoryWriter", () => {
);

// Mix of printable, control chars, and unicode
const binaryLikeData =
"Hello\x00World\x1b[31m红色\x1b[0m\t\r\n\x07Bell🔔";
const binaryLikeData = "Hello\x00World\x1b[31m红色\x1b[0m\t\r\n\x07Bell🔔";

await writer.init();
writer.write(binaryLikeData);
Expand Down
Loading