chore(release): v0.7.6 #6
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| run: | | |
| echo "${{ secrets.ENV_FILE }}" | while IFS='=' read -r key value; do | |
| [[ -z "$key" || "$key" =~ ^# ]] && continue | |
| echo "::add-mask::$value" | |
| echo "${key}=${value}" >> "$GITHUB_ENV" | |
| done | |
| - 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; } | |
| - 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 | |
| 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 | |
| run: | | |
| echo "${{ secrets.ENV_FILE }}" | while IFS='=' read -r key value; do | |
| [[ -z "$key" || "$key" =~ ^# ]] && continue | |
| echo "::add-mask::$value" | |
| echo "${key}=${value}" >> "$GITHUB_ENV" | |
| done | |
| - 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=${{ env.CHROME_CLIENT_ID }}" \ | |
| -d "client_secret=${{ env.CHROME_CLIENT_SECRET }}" \ | |
| -d "refresh_token=${{ env.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/${{ env.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/${{ env.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 | |
| run: | | |
| echo "${{ secrets.ENV_FILE }}" | while IFS='=' read -r key value; do | |
| [[ -z "$key" || "$key" =~ ^# ]] && continue | |
| echo "::add-mask::$value" | |
| echo "${key}=${value}" >> "$GITHUB_ENV" | |
| done | |
| - 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 "${{ env.FIREFOX_JWT_ISSUER }}" \ | |
| --api-secret "${{ env.FIREFOX_JWT_SECRET }}" \ | |
| --channel listed \ | |
| --approval-timeout 0 | |
| echo "Firefox extension v${{ needs.build.outputs.version }} submitted to AMO" | |