Skip to content

chore(release): v0.8.30 #44

chore(release): v0.8.30

chore(release): v0.8.30 #44

Workflow file for this run

name: Extension Release Pipeline
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
publish:
description: "Publish to stores (false = build only)"
type: boolean
default: true
permissions:
contents: read
jobs:
build:
name: Build Extension
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Load environment variables
env:
ENV_CONTENT: ${{ secrets.ENV_FILE }}
run: |
while IFS='=' read -r key value; do
[[ -z "$key" || "$key" =~ ^# ]] && continue
echo "::add-mask::$value"
echo "${key}=${value}" >> "$GITHUB_ENV"
done <<< "$ENV_CONTENT"
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9.15.1
- uses: actions/setup-node@v4
with:
node-version: "24"
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Extract and validate version
id: version
run: |
MANIFEST_VERSION=$(node -p "JSON.parse(require('fs').readFileSync('apps/extension/src/manifest.chrome.json', 'utf-8')).version")
if [[ "$GITHUB_REF_TYPE" == "tag" ]]; then
TAG_VERSION="${GITHUB_REF_NAME#v}"
if [[ "$TAG_VERSION" != "$MANIFEST_VERSION" ]]; then
echo "::error::Tag version ($TAG_VERSION) does not match manifest version ($MANIFEST_VERSION)"
exit 1
fi
VERSION="$TAG_VERSION"
else
VERSION="$MANIFEST_VERSION"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Building version: $VERSION"
- name: Build extension
run: pnpm turbo build --filter=@marksyncr/extension...
env:
NODE_ENV: production
NEXT_PUBLIC_SUPABASE_URL: ${{ env.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ env.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
NEXT_PUBLIC_APP_URL: ${{ env.NEXT_PUBLIC_APP_URL }}
- name: Verify build output
run: |
echo "Build artifacts:"
ls -lh apps/extension/dist/*.zip
test -f apps/extension/dist/marksyncr-chrome.zip || { echo "::error::Chrome ZIP not found"; exit 1; }
test -f apps/extension/dist/marksyncr-firefox.zip || { echo "::error::Firefox ZIP not found"; exit 1; }
cp apps/extension/dist/marksyncr-chrome.zip apps/extension/dist/marksyncr-edge.zip
- name: Upload Chrome ZIP
uses: actions/upload-artifact@v4
with:
name: marksyncr-chrome
path: apps/extension/dist/marksyncr-chrome.zip
retention-days: 30
- name: Upload Firefox ZIP
uses: actions/upload-artifact@v4
with:
name: marksyncr-firefox
path: apps/extension/dist/marksyncr-firefox.zip
retention-days: 30
- name: Upload Edge ZIP
uses: actions/upload-artifact@v4
with:
name: marksyncr-edge
path: apps/extension/dist/marksyncr-edge.zip
retention-days: 30
chrome-release:
name: Publish to Chrome Web Store
needs: build
if: |
github.ref_type == 'tag' ||
(github.event_name == 'workflow_dispatch' && inputs.publish)
runs-on: ubuntu-latest
steps:
- name: Load environment variables
env:
ENV_CONTENT: ${{ secrets.ENV_FILE }}
run: |
while IFS='=' read -r key value; do
[[ -z "$key" || "$key" =~ ^# ]] && continue
echo "::add-mask::$value"
echo "${key}=${value}" >> "$GITHUB_ENV"
done <<< "$ENV_CONTENT"
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: marksyncr-chrome
- name: Authenticate with Chrome Web Store
id: auth
run: |
RESPONSE=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
-d "client_id=$CHROME_CLIENT_ID" \
-d "client_secret=$CHROME_CLIENT_SECRET" \
-d "refresh_token=$CHROME_REFRESH_TOKEN" \
-d "grant_type=refresh_token")
ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r '.access_token // empty')
if [[ -z "$ACCESS_TOKEN" ]]; then
echo "::error::Failed to obtain Chrome Web Store access token"
echo "$RESPONSE" | jq '{error, error_description}' 2>/dev/null || echo "$RESPONSE"
exit 1
fi
echo "::add-mask::$ACCESS_TOKEN"
echo "token=$ACCESS_TOKEN" >> "$GITHUB_OUTPUT"
echo "Chrome Web Store authentication successful"
- name: Upload to Chrome Web Store
run: |
RESPONSE=$(curl -sS \
-X PUT \
-H "Authorization: Bearer ${{ steps.auth.outputs.token }}" \
-H "x-goog-api-version: 2" \
-T marksyncr-chrome.zip \
"https://www.googleapis.com/upload/chromewebstore/v1.1/items/$CHROME_EXTENSION_ID")
echo "Upload response:"
echo "$RESPONSE" | jq .
UPLOAD_STATE=$(echo "$RESPONSE" | jq -r '.uploadState')
if [[ "$UPLOAD_STATE" != "SUCCESS" ]]; then
echo "::error::Chrome upload failed with state: $UPLOAD_STATE"
echo "$RESPONSE" | jq '.itemError // .'
exit 1
fi
echo "Chrome extension uploaded successfully"
- name: Submit to Chrome Web Store for review
run: |
RESPONSE=$(curl -sS \
-X POST \
-H "Authorization: Bearer ${{ steps.auth.outputs.token }}" \
-H "x-goog-api-version: 2" \
-H "Content-Length: 0" \
"https://www.googleapis.com/chromewebstore/v1.1/items/$CHROME_EXTENSION_ID/publish")
echo "Publish response:"
echo "$RESPONSE" | jq .
STATUS=$(echo "$RESPONSE" | jq -r '.status[0] // empty')
if [[ "$STATUS" != "OK" && "$STATUS" != "PUBLISHED_WITH_FRICTION_WARNING" && "$STATUS" != "PENDING_REVIEW" ]]; then
echo "::error::Chrome publish failed with status: $STATUS"
echo "$RESPONSE" | jq '.statusDetail // .'
exit 1
fi
echo "Chrome extension v${{ needs.build.outputs.version }} submitted (status: $STATUS)"
firefox-release:
name: Publish to Firefox Add-ons
needs: build
if: |
github.ref_type == 'tag' ||
(github.event_name == 'workflow_dispatch' && inputs.publish)
runs-on: ubuntu-latest
steps:
- name: Load environment variables
env:
ENV_CONTENT: ${{ secrets.ENV_FILE }}
run: |
while IFS='=' read -r key value; do
[[ -z "$key" || "$key" =~ ^# ]] && continue
echo "::add-mask::$value"
echo "${key}=${value}" >> "$GITHUB_ENV"
done <<< "$ENV_CONTENT"
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: marksyncr-firefox
- name: Unpack extension
run: |
mkdir -p extension
unzip marksyncr-firefox.zip -d extension
echo "Extension contents:"
ls -la extension/
- uses: actions/setup-node@v4
with:
node-version: "24"
- name: Install web-ext
run: npm install -g web-ext
- name: Submit to AMO
run: |
web-ext sign \
--source-dir ./extension \
--artifacts-dir ./artifacts \
--api-key "$FIREFOX_JWT_ISSUER" \
--api-secret "$FIREFOX_JWT_SECRET" \
--channel listed \
--approval-timeout 0
echo "Firefox extension v${{ needs.build.outputs.version }} submitted to AMO"
edge-release:
name: Publish to Edge Add-ons
needs: build
if: |
github.ref_type == 'tag' ||
(github.event_name == 'workflow_dispatch' && inputs.publish)
runs-on: ubuntu-latest
steps:
- name: Load environment variables
env:
ENV_CONTENT: ${{ secrets.ENV_FILE }}
run: |
while IFS='=' read -r key value; do
[[ -z "$key" || "$key" =~ ^# ]] && continue
echo "::add-mask::$value"
echo "${key}=${value}" >> "$GITHUB_ENV"
done <<< "$ENV_CONTENT"
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: marksyncr-edge
- name: Upload to Edge Add-ons
id: upload
run: |
RESPONSE=$(curl -sS -w "\n%{http_code}" \
-X POST \
-H "Authorization: ApiKey $EDGE_API_KEY" \
-H "X-ClientID: $EDGE_CLIENT_ID" \
-H "Content-Type: application/zip" \
-T marksyncr-edge.zip \
"https://api.addons.microsoftedge.microsoft.com/v1/products/$EDGE_PRODUCT_ID/submissions/draft/package")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
echo "Upload response (HTTP $HTTP_CODE):"
echo "$BODY" | jq . 2>/dev/null || echo "$BODY"
OPERATION_ID=$(echo "$BODY" | jq -r '.operationID // empty')
if [[ -z "$OPERATION_ID" ]]; then
OPERATION_ID=$(echo "$RESPONSE" | grep -oP '"operationID"\s*:\s*"\K[^"]+' || true)
fi
if [[ "$HTTP_CODE" != "202" && "$HTTP_CODE" != "200" ]]; then
echo "::error::Edge upload failed with HTTP $HTTP_CODE"
exit 1
fi
echo "operation_id=$OPERATION_ID" >> "$GITHUB_OUTPUT"
echo "Edge extension uploaded, operation: $OPERATION_ID"
- name: Wait for upload processing
run: |
OPERATION_ID="${{ steps.upload.outputs.operation_id }}"
if [[ -z "$OPERATION_ID" ]]; then
echo "No operation ID, skipping status check"
exit 0
fi
for i in $(seq 1 30); do
sleep 10
RESPONSE=$(curl -sS \
-H "Authorization: ApiKey $EDGE_API_KEY" \
-H "X-ClientID: $EDGE_CLIENT_ID" \
"https://api.addons.microsoftedge.microsoft.com/v1/products/$EDGE_PRODUCT_ID/submissions/draft/package/operations/$OPERATION_ID")
STATUS=$(echo "$RESPONSE" | jq -r '.status // empty')
echo "Attempt $i/30: status=$STATUS"
if [[ "$STATUS" == "Succeeded" ]]; then
echo "Upload processing complete"
break
elif [[ "$STATUS" == "Failed" ]]; then
echo "::error::Edge upload processing failed"
echo "$RESPONSE" | jq .
exit 1
fi
done
- name: Publish submission
run: |
RESPONSE=$(curl -sS -w "\n%{http_code}" \
-X POST \
-H "Authorization: ApiKey $EDGE_API_KEY" \
-H "X-ClientID: $EDGE_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{}' \
"https://api.addons.microsoftedge.microsoft.com/v1/products/$EDGE_PRODUCT_ID/submissions")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
echo "Publish response (HTTP $HTTP_CODE):"
echo "$BODY" | jq . 2>/dev/null || echo "$BODY"
if [[ "$HTTP_CODE" != "202" && "$HTTP_CODE" != "200" ]]; then
echo "::error::Edge publish failed with HTTP $HTTP_CODE"
exit 1
fi
echo "Edge extension v${{ needs.build.outputs.version }} submitted for review"