Skip to content

chore(release): v1.5.2 #115

chore(release): v1.5.2

chore(release): v1.5.2 #115

Workflow file for this run

name: Release
run-name: >
${{
(github.event_name == 'workflow_dispatch' && inputs.version && format('chore(release): v{0}', inputs.version)) ||
(github.event_name == 'workflow_dispatch' && !inputs.version && 'chore(release): auto-increment') ||
(startsWith(github.ref, 'refs/tags/') && format('Release {0}', github.ref_name)) ||
format('Release #{0}', github.run_number)
}}
# 🚀 Release Workflow Guide
# -------------------------
# This workflow supports two modes of operation:
#
# 1. Manual Release (Recommended for "One-Click" Releases)
# - How: Go to "Actions" -> "Release" -> "Run workflow".
# - Input: Optional "Version" (e.g., 1.2.0). If left empty, it auto-increments the patch version.
# - What it does:
# 1. Calculates the next version.
# 2. Bumps version in package.json & manifest.json.
# 3. Commits "chore(release): vX.Y.Z".
# 4. Creates git tag "vX.Y.Z".
# 5. Pushes commit & tag to main.
# 6. Builds artifacts and creates a GitHub Release.
#
# 2. Tag-based Release (Manual Tagging)
# - How: run `git commit -m "..." && git tag v1.2.0 && git push origin v1.2.0` locally.
# - What it does:
# 1. Detects the pushed tag (v1.2.0).
# 2. SKIPS version calculation and bumping (assumes you already did it).
# 3. Builds artifacts from that tag.
# 4. Creates a GitHub Release for that tag.
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g. 1.0.8) - leave empty to auto-increment patch version'
required: false
type: string
notes:
description: 'Release notes (optional)'
required: false
type: string
publish_only:
description: 'Only re-publish an existing release to the Chrome Web Store (no bump/build). Requires version.'
required: false
type: boolean
default: false
permissions:
contents: write
jobs:
# Job 1: Calculate & Bump Version (Only runs on manual trigger)
bump-version:
if: github.event_name == 'workflow_dispatch' && !inputs.publish_only
runs-on: ubuntu-latest
outputs:
new_version: ${{ steps.next_version.outputs.version }}
tag_name: ${{ steps.tag.outputs.name }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Calculate next version
id: next_version
run: |
if [ -n "${{ inputs.version }}" ]; then
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
echo "Using manual version: ${{ inputs.version }}"
else
CURRENT=$(node -e "console.log(require('./package.json').version)")
echo "Current version: ${CURRENT}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
PATCH=$((PATCH + 1))
# Simple carry logic
if [ $PATCH -ge 10 ]; then
MINOR=$((MINOR + 1))
PATCH=0
fi
if [ $MINOR -ge 10 ]; then
MAJOR=$((MAJOR + 1))
MINOR=0
fi
NEXT="${MAJOR}.${MINOR}.${PATCH}"
echo "version=${NEXT}" >> $GITHUB_OUTPUT
echo "Auto-calculated next version: ${NEXT}"
fi
- name: Compute tag name
id: tag
run: echo "name=v${{ steps.next_version.outputs.version }}" >> $GITHUB_OUTPUT
- name: Update files, commit and push
env:
VERSION: ${{ steps.next_version.outputs.version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Bumping to version ${VERSION}"
# Update package.json and manifest.json
node -e "const fs=require('fs');const v=process.env.VERSION;const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.version=v;fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n');const m=JSON.parse(fs.readFileSync('manifest.json','utf8'));m.version=v;fs.writeFileSync('manifest.json',JSON.stringify(m,null,2)+'\n');"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add package.json manifest.json
git commit -m "chore(release): v${VERSION}" || echo "No changes to commit"
git tag v${VERSION} || echo "Tag exists"
# Push commit and tag
git push origin HEAD:main --tags
# Job 2: Build & Release (Runs on both manual and tag push)
build-and-release:
needs: bump-version
if: always() && (needs.bump-version.result == 'success' || github.event_name == 'push')
runs-on: ubuntu-latest
steps:
- name: Set Release Tag Variable
id: vars
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "tag_name=${{ needs.bump-version.outputs.tag_name }}" >> $GITHUB_OUTPUT
else
echo "tag_name=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ steps.vars.outputs.tag_name }}
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 'latest'
- name: Install deps
run: bun i
- name: Build All
run: bun run build:all
- name: Archive artifacts
run: |
TAG=${{ steps.vars.outputs.tag_name }}
cd dist_chrome && zip -r ../voyager-chrome-${TAG}.zip . && cd ..
- name: Validate Chrome artifact
run: |
TAG=${{ steps.vars.outputs.tag_name }}
ZIP="voyager-chrome-${TAG}.zip"
unzip -Z1 "$ZIP" > chrome-zip-files.txt
grep -qx 'manifest.json' chrome-zip-files.txt
grep -qx '_locales/en/messages.json' chrome-zip-files.txt
- name: Sign Firefox Extension and Submit to AMO
run: |
TAG=${{ steps.vars.outputs.tag_name }}
npx web-ext sign \
--source-dir=dist_firefox \
--api-key=${{ secrets.AMO_JWT_ISSUER }} \
--api-secret=${{ secrets.AMO_JWT_SECRET }} \
--channel=listed
# Move signed XPI to root with proper naming
mv web-ext-artifacts/*.xpi voyager-firefox-${TAG}.xpi
- name: Prepare Release Notes
id: release_body
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG=${{ steps.vars.outputs.tag_name }}
NOTES="${{ inputs.notes }}"
if [ -n "$NOTES" ]; then
echo "body<<EOF" >> $GITHUB_OUTPUT
echo "$NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
# Get the previous tag for auto-generated notes
PREV_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")
# Generate release notes via GitHub API
AUTO_NOTES=""
if [ -n "$PREV_TAG" ]; then
AUTO_NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes \
-f tag_name="${TAG}" \
-f target_commitish="main" \
-f previous_tag_name="${PREV_TAG}" \
--jq '.body' 2>/dev/null || echo "")
fi
cat > release_body.md << 'INSTALL_EOF'
## 📥 Installation
<div align="center">
<a href="https://chromewebstore.google.com/detail/iifacdnjakkhjjiengaffnegbndgingi?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=en" target="_blank">
<img src="https://img.shields.io/badge/Chrome%20Web%20Store-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome Web Store" height="36">
</a>
&nbsp;&nbsp;
<a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
<img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
</a>
</div>
<p align="center">
<sub><b>Edge users:</b> Voyager is still maintained on Edge Add-ons for users who need Edge on mobile or tablet. If review is delayed, Chrome Web Store and GitHub manual packages remain available.</sub>
</p>
INSTALL_EOF
# Append version-specific file names
cat >> release_body.md << EOF
- **Chrome / Edge / Chromium browsers**: \`voyager-chrome-${TAG}.zip\`
- **Firefox**: \`voyager-firefox-${TAG}.xpi\`
- **Safari**: \`voyager-${TAG}.dmg\`
> Do not use GitHub's auto-generated **Source code (zip/tar.gz)** archives for manual extension installs. Those files are repository source snapshots, not browser-ready extension packages.
### 🍎 Safari 安装与限制 (Safari Installation & Limitations)
- **安装 (Installation)**: 下载并打开 \`voyager-${TAG}.dmg\`,按提示安装应用。
Download and open \`voyager-${TAG}.dmg\`, then follow the prompts to install the app.
- **限制 (Limitations)**: 受限于 Safari 特性,以下功能暂不支持:(a) Nano Banana 水印去除 (b) 图片导出 (建议使用 PDF 导出) (c) Google Drive 云同步。
Due to Safari's nature, the following features are not supported: (a) Watermark removal (b) Image export (PDF recommended) (c) Cloud sync with Google Drive.
EOF
# Combine: auto-generated notes first, then installation
echo "body<<BODY_EOF" >> $GITHUB_OUTPUT
if [ -n "$AUTO_NOTES" ]; then
echo "$AUTO_NOTES" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
fi
cat release_body.md >> $GITHUB_OUTPUT
echo "BODY_EOF" >> $GITHUB_OUTPUT
fi
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: Voyager ${{ steps.vars.outputs.tag_name }}
tag_name: ${{ steps.vars.outputs.tag_name }}
files: |
voyager-chrome-*.zip
voyager-firefox-*.xpi
voyager-*.dmg
body: ${{ steps.release_body.outputs.body }}
# Last step on purpose: if Chrome publishing fails (e.g. store review,
# token expiry), the GitHub Release and the Firefox/AMO submission above
# have already completed and are unaffected.
- name: Publish to Chrome Web Store
run: |
TAG=${{ steps.vars.outputs.tag_name }}
# Chrome Web Store rejects a manifest that still carries the local dev `key`
# field (PKG_MANIFEST_KEY_NOT_MATCH). Strip it into a store-only copy; the
# GitHub Release zip keeps the key for manual unpacked installs.
rm -rf cws_pkg "voyager-chrome-cws-${TAG}.zip"
mkdir cws_pkg
unzip -q "voyager-chrome-${TAG}.zip" -d cws_pkg
node -e "const fs=require('fs');const m=JSON.parse(fs.readFileSync('cws_pkg/manifest.json','utf8'));delete m.key;fs.writeFileSync('cws_pkg/manifest.json',JSON.stringify(m,null,2)+'\n');"
(cd cws_pkg && zip -qr "../voyager-chrome-cws-${TAG}.zip" .)
bunx chrome-webstore-upload-cli@3 upload \
--source "voyager-chrome-cws-${TAG}.zip" \
--extension-id "$EXTENSION_ID" \
--client-id "$CLIENT_ID" \
--client-secret "$CLIENT_SECRET" \
--refresh-token "$REFRESH_TOKEN" \
--auto-publish
env:
EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
# Job 3: Re-publish Chrome only (manual) — recover a failed Chrome Web Store
# upload without re-cutting the release. Downloads the existing release's Chrome
# zip, strips the dev `key`, and publishes. No version bump, no rebuild.
republish-chrome:
if: github.event_name == 'workflow_dispatch' && inputs.publish_only
runs-on: ubuntu-latest
steps:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 'latest'
- name: Download Chrome zip from the existing release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="v${{ inputs.version }}"
gh release download "$TAG" --repo "${{ github.repository }}" --pattern 'voyager-chrome-v*.zip'
ls -la voyager-chrome-v*.zip
- name: Strip dev key and publish to Chrome Web Store
env:
EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
run: |
TAG="v${{ inputs.version }}"
ZIP="voyager-chrome-${TAG}.zip"
rm -rf cws_pkg "voyager-chrome-cws-${TAG}.zip"
mkdir cws_pkg
unzip -q "$ZIP" -d cws_pkg
node -e "const fs=require('fs');const m=JSON.parse(fs.readFileSync('cws_pkg/manifest.json','utf8'));delete m.key;fs.writeFileSync('cws_pkg/manifest.json',JSON.stringify(m,null,2)+'\n');"
(cd cws_pkg && zip -qr "../voyager-chrome-cws-${TAG}.zip" .)
bunx chrome-webstore-upload-cli@3 upload \
--source "voyager-chrome-cws-${TAG}.zip" \
--extension-id "$EXTENSION_ID" \
--client-id "$CLIENT_ID" \
--client-secret "$CLIENT_SECRET" \
--refresh-token "$REFRESH_TOKEN" \
--auto-publish