Merge branch '20260315-firefox' into main #12
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: Release Publish | |
| on: | |
| push: | |
| branches: | |
| - main | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release-main | |
| cancel-in-progress: false | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| environment: ${{ 'production' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| cache-dependency-path: app-v3/package-lock.json | |
| - name: Install dependencies | |
| working-directory: app-v3 | |
| run: npm ci | |
| - name: Resolve release metadata | |
| id: meta | |
| shell: bash | |
| run: | | |
| VERSION="$(node -p "require('./app-v3/package.json').version")" | |
| BUILD_TAG="v${VERSION}-main.${GITHUB_RUN_NUMBER}" | |
| RELEASE_NAME="upwork-job-scraper@${VERSION}-main.${GITHUB_RUN_NUMBER}" | |
| echo "tag=$BUILD_TAG" >> "$GITHUB_OUTPUT" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "release=$RELEASE_NAME" >> "$GITHUB_OUTPUT" | |
| - name: Build and package | |
| working-directory: app-v3 | |
| env: | |
| WXT_SENTRY_DSN: ${{ vars['WXT_SENTRY_DSN'] }} | |
| WXT_SENTRY_ENVIRONMENT: production | |
| WXT_SENTRY_RELEASE: ${{ steps.meta.outputs.release }} | |
| SENTRY_SOURCEMAPS: "true" | |
| SENTRY_UPLOAD_SOURCEMAPS_VITE: "false" | |
| run: | | |
| npm run zip | |
| npm run zip:firefox | |
| - name: Verify manifest version matches package version | |
| shell: bash | |
| run: | | |
| PACKAGE_VERSION="$(node -p "require('./app-v3/package.json').version")" | |
| MANIFEST_VERSION="$(node -p "require('./app-v3/.output/chrome-mv3/manifest.json').version")" | |
| echo "package.json version: $PACKAGE_VERSION" | |
| echo "manifest version: $MANIFEST_VERSION" | |
| if [[ "$PACKAGE_VERSION" != "$MANIFEST_VERSION" ]]; then | |
| echo "Version mismatch: app-v3/package.json and built manifest.json differ." | |
| exit 1 | |
| fi | |
| - name: Create or update GitHub release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| TAG="${{ steps.meta.outputs.tag }}" | |
| TITLE="Upwork Job Scraper main build v${{ steps.meta.outputs.version }} (#${{ github.run_number }})" | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| gh release edit "$TAG" --title "$TITLE" | |
| else | |
| gh release create "$TAG" --title "$TITLE" --generate-notes | |
| fi | |
| - name: Locate Chrome ZIP | |
| id: zip | |
| shell: bash | |
| run: | | |
| ZIP_FILE="$(ls app-v3/.output/*-chrome.zip | head -n 1)" | |
| if [[ -z "$ZIP_FILE" ]]; then | |
| echo "No Chrome zip artifact found under app-v3/.output" | |
| exit 1 | |
| fi | |
| echo "zip_file=$ZIP_FILE" >> "$GITHUB_OUTPUT" | |
| - name: Upload ZIP to GitHub release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| gh release upload "${{ steps.meta.outputs.tag }}" "${{ steps.zip.outputs.zip_file }}" --clobber | |
| - name: Locate Firefox ZIP | |
| id: zip_firefox | |
| shell: bash | |
| run: | | |
| ZIP_FILE="$(ls app-v3/.output/*-firefox.zip | head -n 1)" | |
| if [[ -z "$ZIP_FILE" ]]; then | |
| echo "No Firefox zip artifact found under app-v3/.output" | |
| exit 1 | |
| fi | |
| echo "zip_file=$ZIP_FILE" >> "$GITHUB_OUTPUT" | |
| - name: Upload Firefox ZIP to GitHub release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| gh release upload "${{ steps.meta.outputs.tag }}" "${{ steps.zip_firefox.outputs.zip_file }}" --clobber | |
| - name: Upload to Firefox Add-ons (upload-only) | |
| env: | |
| AMO_JWT_ISSUER: ${{ secrets['AMO_JWT_ISSUER'] }} | |
| AMO_JWT_SECRET: ${{ secrets['AMO_JWT_SECRET'] }} | |
| working-directory: app-v3 | |
| shell: bash | |
| run: | | |
| npx --yes web-ext sign \ | |
| --channel=listed \ | |
| --api-key="$AMO_JWT_ISSUER" \ | |
| --api-secret="$AMO_JWT_SECRET" \ | |
| --source-dir=.output/firefox-mv3 \ | |
| --no-input \ | |
| --artifacts-dir=.output/amo-artifacts | |
| - name: Upload sourcemaps to Sentry | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ vars['SENTRY_ORG'] }} | |
| SENTRY_PROJECT: ${{ vars['SENTRY_PROJECT'] }} | |
| SENTRY_RELEASE: ${{ steps.meta.outputs.release }} | |
| shell: bash | |
| run: | | |
| npx --yes @sentry/cli sourcemaps inject app-v3/.output/chrome-mv3 | |
| npx --yes @sentry/cli sourcemaps upload \ | |
| --release "$SENTRY_RELEASE" \ | |
| --url-prefix "~/" \ | |
| app-v3/.output/chrome-mv3 | |
| - name: Exchange refresh token for access token | |
| id: token | |
| env: | |
| CWS_CLIENT_ID: ${{ secrets['CWS_CLIENT_ID'] }} | |
| CWS_CLIENT_SECRET: ${{ secrets['CWS_CLIENT_SECRET'] }} | |
| CWS_REFRESH_TOKEN: ${{ secrets['CWS_REFRESH_TOKEN'] }} | |
| shell: bash | |
| run: | | |
| TOKEN_JSON="$(curl --fail --silent --show-error https://oauth2.googleapis.com/token \ | |
| -d client_id="$CWS_CLIENT_ID" \ | |
| -d client_secret="$CWS_CLIENT_SECRET" \ | |
| -d refresh_token="$CWS_REFRESH_TOKEN" \ | |
| -d grant_type=refresh_token)" | |
| ACCESS_TOKEN="$(echo "$TOKEN_JSON" | python -c "import json,sys; print(json.load(sys.stdin).get('access_token',''))")" | |
| if [[ -z "$ACCESS_TOKEN" ]]; then | |
| echo "Failed to obtain access token from refresh token flow." | |
| exit 1 | |
| fi | |
| echo "::add-mask::$ACCESS_TOKEN" | |
| echo "access_token=$ACCESS_TOKEN" >> "$GITHUB_OUTPUT" | |
| - name: Upload to Chrome Web Store (upload-only) | |
| env: | |
| ACCESS_TOKEN: ${{ steps.token.outputs.access_token }} | |
| CWS_PUBLISHER_ID: ${{ vars['CWS_PUBLISHER_ID'] }} | |
| CWS_EXTENSION_ID: ${{ vars['CWS_EXTENSION_ID'] }} | |
| ZIP_FILE: ${{ steps.zip.outputs.zip_file }} | |
| shell: bash | |
| run: | | |
| RESPONSE="$(curl --fail --silent --show-error \ | |
| -X POST \ | |
| -H "Authorization: Bearer $ACCESS_TOKEN" \ | |
| -H "Content-Type: application/zip" \ | |
| --data-binary "@$ZIP_FILE" \ | |
| "https://chromewebstore.googleapis.com/upload/v2/publishers/$CWS_PUBLISHER_ID/items/$CWS_EXTENSION_ID:upload")" | |
| echo "$RESPONSE" | |
| STATE="$(echo "$RESPONSE" | python -c "import json,sys; print(json.load(sys.stdin).get('uploadState',''))")" | |
| if [[ "$STATE" != "SUCCESS" && "$STATE" != "SUCCEEDED" ]]; then | |
| echo "Chrome Web Store upload did not return SUCCESS/SUCCEEDED." | |
| exit 1 | |
| fi | |
| - name: Manual publish reminder | |
| run: | | |
| echo "Chrome: Upload complete. Publish manually in the Chrome Web Store dashboard." | |
| echo "Firefox: Version submitted to AMO review queue. Approve in the AMO dashboard when ready to publish." |