mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-21 22:22:24 -05:00
Serialize publish-firefox and publish-latest jobs with a 90-second delay to prevent hitting AMO's rate limit when running in parallel. Ensure publish-latest still runs even if publish-firefox fails, while maintaining the job dependency. Also simplify extension certification notes.
277 lines
12 KiB
YAML
277 lines
12 KiB
YAML
name: Extension store release
|
|
|
|
# Versioned releases of Thingport Grab via semantic-release (extension/.releaserc.json): every
|
|
# Friday at 13:00 Polish time, or on demand. Only commits touching extension/ count; with a
|
|
# releasable one it bumps the version, writes the changelog, tags, creates a GitHub release with
|
|
# the store zips, publishes to the Chrome Web Store, Edge Add-ons and addons.mozilla.org, and moves the
|
|
# extension-latest release to the same build. Per-merge checks are in extension-release.yml.
|
|
on:
|
|
workflow_dispatch: {}
|
|
schedule:
|
|
# GitHub cron is UTC-only; fire at both CET and CEST equivalents and let the Warsaw-time step
|
|
# pick the right one.
|
|
- cron: "0 11 * * 5"
|
|
- cron: "0 12 * * 5"
|
|
|
|
concurrency:
|
|
group: extension-store-release
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write # the release commit, the tag and the GitHub release
|
|
outputs:
|
|
version: ${{ steps.release.outputs.version }}
|
|
tag: ${{ steps.release.outputs.tag }}
|
|
defaults:
|
|
run:
|
|
working-directory: extension
|
|
steps:
|
|
- name: Friday 13:00 in Warsaw?
|
|
id: gate
|
|
if: github.event_name == 'schedule'
|
|
run: |
|
|
offset=$(TZ=Europe/Warsaw date +%z)
|
|
case "${{ github.event.schedule }}|$offset" in
|
|
"0 11 * * 5|+0200" | "0 12 * * 5|+0100") echo "run=true" >> "$GITHUB_OUTPUT" ;;
|
|
*)
|
|
echo "This trigger isn't 13:00 in Warsaw (UTC offset $offset) -- the other one is. Skipping."
|
|
echo "run=false" >> "$GITHUB_OUTPUT"
|
|
;;
|
|
esac
|
|
- uses: actions/checkout@v4
|
|
if: steps.gate.outputs.run != 'false'
|
|
with:
|
|
fetch-depth: 0 # semantic-release needs the full history and tags
|
|
- uses: actions/setup-node@v4
|
|
if: steps.gate.outputs.run != 'false'
|
|
with:
|
|
node-version-file: .nvmrc
|
|
cache: npm
|
|
cache-dependency-path: extension/package-lock.json
|
|
- if: steps.gate.outputs.run != 'false'
|
|
run: npm ci
|
|
- name: semantic-release
|
|
id: release
|
|
if: steps.gate.outputs.run != 'false'
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GIT_AUTHOR_NAME: github-actions[bot]
|
|
GIT_AUTHOR_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
|
|
GIT_COMMITTER_NAME: github-actions[bot]
|
|
GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
|
|
run: npx semantic-release
|
|
# Not semantic-release's GitHub plugin: it marks releases "latest", a slot Bridge's links rely on.
|
|
- name: GitHub release
|
|
if: steps.release.outputs.version != ''
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release create "${{ steps.release.outputs.tag }}" dist/zips/*.zip \
|
|
--title "Thingport Grab ${{ steps.release.outputs.version }}" \
|
|
--notes-file dist/release-notes.md \
|
|
--latest=false
|
|
- uses: actions/upload-artifact@v4
|
|
if: steps.release.outputs.version != ''
|
|
with:
|
|
name: thingport-grab-store-zips
|
|
path: extension/dist/zips/*.zip
|
|
if-no-files-found: error
|
|
|
|
# Its own job so it can be re-run alone. Signs in as the publisher's service account; see
|
|
# extension/CONTRIBUTING.md.
|
|
publish-chrome:
|
|
needs: release
|
|
if: needs.release.outputs.version != ''
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: extension
|
|
env:
|
|
CHROME_EXTENSION_ID: ${{ vars.CHROME_EXTENSION_ID }}
|
|
CWS_PUBLISHER_ID: ${{ secrets.CWS_PUBLISHER_ID }}
|
|
CWS_SERVICE_ACCOUNT_KEY: ${{ secrets.CWS_SERVICE_ACCOUNT_KEY }}
|
|
steps:
|
|
- name: Credentials set up?
|
|
id: creds
|
|
run: |
|
|
if [ -z "$CWS_SERVICE_ACCOUNT_KEY" ] || [ -z "$CWS_PUBLISHER_ID" ]; then
|
|
echo "::notice::CWS_SERVICE_ACCOUNT_KEY / CWS_PUBLISHER_ID aren't set -- skipping the Chrome Web Store (see extension/CONTRIBUTING.md)."
|
|
echo "ok=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
working-directory: .
|
|
- uses: actions/checkout@v4
|
|
if: steps.creds.outputs.ok != 'false'
|
|
- uses: actions/setup-node@v4
|
|
if: steps.creds.outputs.ok != 'false'
|
|
with:
|
|
node-version-file: .nvmrc
|
|
cache: npm
|
|
cache-dependency-path: extension/package-lock.json
|
|
- if: steps.creds.outputs.ok != 'false'
|
|
run: npm ci
|
|
- uses: actions/download-artifact@v4
|
|
if: steps.creds.outputs.ok != 'false'
|
|
with:
|
|
name: thingport-grab-store-zips
|
|
path: extension/dist/zips
|
|
- name: Upload and submit for review
|
|
if: steps.creds.outputs.ok != 'false'
|
|
run: npx tsx scripts/publish-chrome.ts dist/zips/thingport-grab-chrome.zip
|
|
|
|
# Its own job so it can be re-run alone: Edge takes one submission at a time and fails with
|
|
# InProgressSubmission while the previous one is in certification. Credentials: see
|
|
# extension/CONTRIBUTING.md.
|
|
publish-edge:
|
|
needs: release
|
|
if: needs.release.outputs.version != ''
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: extension
|
|
env:
|
|
EDGE_PRODUCT_ID: ${{ vars.EDGE_PRODUCT_ID }}
|
|
EDGE_CLIENT_ID: ${{ secrets.EDGE_CLIENT_ID }}
|
|
EDGE_API_KEY: ${{ secrets.EDGE_API_KEY }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version-file: .nvmrc
|
|
cache: npm
|
|
cache-dependency-path: extension/package-lock.json
|
|
- run: npm ci
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: thingport-grab-store-zips
|
|
path: extension/dist/zips
|
|
# Edge wants complete certification notes with every submission; see the notes file.
|
|
- name: Upload and submit for certification
|
|
env:
|
|
RELEASE_VERSION: ${{ needs.release.outputs.version }}
|
|
RELEASE_TAG: ${{ needs.release.outputs.tag }}
|
|
EDGE_REVIEW_TEST_SERVER: ${{ secrets.EDGE_REVIEW_TEST_SERVER }}
|
|
run: npx tsx scripts/publish-edge.ts dist/zips/thingport-grab-edge.zip --notes-file scripts/edge-certification-notes.txt
|
|
|
|
# Its own job so it can be re-run alone. Same AMO credentials as publish-latest's unlisted signing;
|
|
# see extension/CONTRIBUTING.md.
|
|
publish-firefox:
|
|
needs: release
|
|
if: needs.release.outputs.version != ''
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: extension
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version-file: .nvmrc
|
|
cache: npm
|
|
cache-dependency-path: extension/package-lock.json
|
|
- run: npm ci
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: thingport-grab-store-zips
|
|
path: extension/dist/zips
|
|
- name: Unpack the build the release made
|
|
run: unzip -q dist/zips/thingport-grab-firefox.zip -d dist/firefox
|
|
- name: Notes for Mozilla's reviewers
|
|
env:
|
|
VERSION: ${{ needs.release.outputs.version }}
|
|
TAG: ${{ needs.release.outputs.tag }}
|
|
run: |
|
|
jq -n --arg notes "Automated update to version $VERSION. Changes: https://github.com/TautvydasDerzinskas/Thingport/releases/tag/$TAG -- the bundles aren't minified; to rebuild them from the attached source (Node 24+): npm ci && npm run build:firefox, output in dist/firefox. Documentation: https://thingport.net/docs/grab/" \
|
|
'{version: {approval_notes: $notes}}' > dist/amo-metadata.json
|
|
# Mozilla may hold a listed version for manual review, so don't wait for the signed file.
|
|
- name: Submit to addons.mozilla.org
|
|
env:
|
|
WEB_EXT_API_KEY: ${{ secrets.AMO_JWT_ISSUER }}
|
|
WEB_EXT_API_SECRET: ${{ secrets.AMO_JWT_SECRET }}
|
|
run: |
|
|
npx web-ext sign \
|
|
--source-dir dist/firefox \
|
|
--artifacts-dir dist/signed \
|
|
--upload-source-code dist/zips/thingport-grab-sources.zip \
|
|
--amo-metadata dist/amo-metadata.json \
|
|
--channel listed \
|
|
--approval-timeout 0
|
|
|
|
# The builds for installing by hand, on the fixed "extension-latest" tag rather than GitHub's single
|
|
# repo-wide "latest" release, which Bridge's download links rely on.
|
|
# Runs after publish-firefox, even if it failed: both sign with the same AMO key, and in parallel they hit
|
|
# AMO's rate limit.
|
|
publish-latest:
|
|
needs: [release, publish-firefox]
|
|
if: ${{ !cancelled() && needs.release.outputs.version != '' }}
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
defaults:
|
|
run:
|
|
working-directory: extension
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ needs.release.outputs.tag }}
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version-file: .nvmrc
|
|
cache: npm
|
|
cache-dependency-path: extension/package-lock.json
|
|
- run: npm ci
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: thingport-grab-store-zips
|
|
path: extension/dist/zips
|
|
# AMO rejects a version it has seen on any channel, and publish-firefox submits this one as listed, so the
|
|
# self-distributed copy gets the run number as an extra segment (e.g. 1.1.0.456).
|
|
- name: Unpack the Firefox build with a version of its own
|
|
env:
|
|
RUN_NUMBER: ${{ github.run_number }}
|
|
run: |
|
|
unzip -q dist/zips/thingport-grab-firefox.zip -d dist/firefox
|
|
jq --arg run "$RUN_NUMBER" '.version = "\(.version).\($run)"' dist/firefox/manifest.json > manifest.json.tmp
|
|
mv manifest.json.tmp dist/firefox/manifest.json
|
|
# AMO's throttle window is about a minute; let publish-firefox's requests age out first.
|
|
- name: Let AMO's rate limit recover
|
|
run: sleep 90
|
|
- name: Sign (unlisted -- not published to addons.mozilla.org's public listing)
|
|
env:
|
|
WEB_EXT_API_KEY: ${{ secrets.AMO_JWT_ISSUER }}
|
|
WEB_EXT_API_SECRET: ${{ secrets.AMO_JWT_SECRET }}
|
|
run: |
|
|
npx web-ext sign \
|
|
--source-dir dist/firefox \
|
|
--artifacts-dir dist/signed \
|
|
--upload-source-code dist/zips/thingport-grab-sources.zip \
|
|
--channel unlisted
|
|
mv "$(ls dist/signed/*.xpi | head -n1)" dist/signed/thingport-grab-firefox.xpi
|
|
- name: Move extension-latest to this release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
VERSION: ${{ needs.release.outputs.version }}
|
|
TAG: ${{ needs.release.outputs.tag }}
|
|
run: |
|
|
cat > "$RUNNER_TEMP/notes.md" <<EOF
|
|
Thingport Grab $VERSION ([release notes](https://github.com/${{ github.repository }}/releases/tag/$TAG)),
|
|
for installing by hand. The store versions keep themselves up to date; see \`extension/README.md\`:
|
|
|
|
- **Chrome / other Chromium browsers**: the
|
|
[Chrome Web Store](https://chromewebstore.google.com/detail/nmblahmglpbplmfcggghdgohohlaeiee).
|
|
\`thingport-grab-chrome.zip\` loads by hand (\`chrome://extensions\` → **Developer mode** → **Load unpacked**).
|
|
- **Firefox**: [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/thingport-grab/).
|
|
\`thingport-grab-firefox.xpi\` is Mozilla-signed; open it directly, or drag it onto a Firefox window.
|
|
- **Edge**: [Microsoft Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/kahfidpmojfocohinlmglnfoaimocbol).
|
|
\`thingport-grab-edge.zip\` loads by hand (\`edge://extensions\` → **Developer mode** → **Load unpacked**).
|
|
EOF
|
|
gh release delete extension-latest --yes --cleanup-tag || true
|
|
gh release create extension-latest \
|
|
dist/zips/thingport-grab-chrome.zip dist/zips/thingport-grab-edge.zip dist/signed/thingport-grab-firefox.xpi \
|
|
--target "$(git rev-parse HEAD)" \
|
|
--title "Thingport Grab (latest)" \
|
|
--notes-file "$RUNNER_TEMP/notes.md" \
|
|
--latest=false
|