chore(main): release 0.4.0 #3
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 | |
| on: | |
| pull_request: | |
| branches: [main] | |
| types: [closed] | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: Git ref containing the released package version | |
| required: true | |
| default: main | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.merge_commit_sha || inputs.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| release: | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.pull_request.merged == true && | |
| startsWith(github.event.pull_request.head.ref, 'release-please--')) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| env: | |
| VITE_CLERK_OAUTH_CLIENT_ID: ${{ vars.VITE_CLERK_OAUTH_CLIENT_ID }} | |
| VITE_CLERK_OAUTH_ISSUER: ${{ vars.VITE_CLERK_OAUTH_ISSUER }} | |
| VITE_WEB_APP_URL: ${{ vars.VITE_WEB_APP_URL }} | |
| GH_TOKEN: ${{ github.token }} | |
| steps: | |
| - name: Checkout released source | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.event.pull_request.merge_commit_sha }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - name: Validate release configuration | |
| id: version | |
| run: | | |
| for name in \ | |
| VITE_CLERK_OAUTH_CLIENT_ID \ | |
| VITE_CLERK_OAUTH_ISSUER \ | |
| VITE_WEB_APP_URL | |
| do | |
| if [ -z "${!name}" ]; then | |
| echo "Required release variable is missing: $name" >&2 | |
| exit 1 | |
| fi | |
| done | |
| package_version="$(node -p "require('./package.json').version")" | |
| manifest_version="$(node -p "require('./.release-please-manifest.json')['.']")" | |
| if [ "$package_version" != "$manifest_version" ]; then | |
| echo "package.json version ${package_version} does not match Release Please version ${manifest_version}." >&2 | |
| exit 1 | |
| fi | |
| echo "VERSION=${package_version}" >> "$GITHUB_ENV" | |
| echo "TAG=v${package_version}" >> "$GITHUB_ENV" | |
| echo "RELEASE_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV" | |
| echo "version=${package_version}" >> "$GITHUB_OUTPUT" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Verify release source | |
| run: | | |
| pnpm lint | |
| pnpm test | |
| - name: Build release packages | |
| run: pnpm package | |
| - name: Verify release packages | |
| run: | | |
| for package in \ | |
| "artifacts/memos-web-clipper-chromium-v${VERSION}.zip" \ | |
| "artifacts/memos-web-clipper-chrome-v${VERSION}.zip" \ | |
| "artifacts/memos-web-clipper-edge-v${VERSION}.zip" \ | |
| "artifacts/memos-web-clipper-firefox-v${VERSION}.zip" | |
| do | |
| unzip -tq "$package" | |
| done | |
| cmp \ | |
| "artifacts/memos-web-clipper-chrome-v${VERSION}.zip" \ | |
| "artifacts/memos-web-clipper-edge-v${VERSION}.zip" | |
| verify_stage="$(mktemp -d)" | |
| trap 'rm -rf "$verify_stage"' EXIT | |
| unzip -p "artifacts/memos-web-clipper-chromium-v${VERSION}.zip" manifest.json > "$verify_stage/chromium.json" | |
| unzip -p "artifacts/memos-web-clipper-chrome-v${VERSION}.zip" manifest.json > "$verify_stage/chrome.json" | |
| unzip -p "artifacts/memos-web-clipper-edge-v${VERSION}.zip" manifest.json > "$verify_stage/edge.json" | |
| unzip -p "artifacts/memos-web-clipper-firefox-v${VERSION}.zip" manifest.json > "$verify_stage/firefox.json" | |
| node --input-type=module - "$verify_stage" "$VERSION" <<'NODE' | |
| import { readFileSync } from "node:fs"; | |
| import { join } from "node:path"; | |
| const [stage, version] = process.argv.slice(2); | |
| const readManifest = (name) => JSON.parse(readFileSync(join(stage, `${name}.json`), "utf8")); | |
| const chromium = readManifest("chromium"); | |
| const chrome = readManifest("chrome"); | |
| const edge = readManifest("edge"); | |
| const firefox = readManifest("firefox"); | |
| if (chromium.version !== version || !chromium.key || chromium.update_url) { | |
| throw new Error("The manual Chromium package has the wrong version, identity key, or update URL."); | |
| } | |
| for (const [name, manifest] of [["Chrome", chrome], ["Edge", edge]]) { | |
| if (manifest.version !== version || manifest.key || manifest.update_url) { | |
| throw new Error(`${name} store package has the wrong version or store-owned identity fields.`); | |
| } | |
| } | |
| if ( | |
| firefox.version !== version || | |
| firefox.key || | |
| firefox.update_url || | |
| firefox.browser_specific_settings?.gecko?.id !== "web-clipper@usememos.com" || | |
| !firefox.background?.scripts?.length || | |
| firefox.background?.service_worker | |
| ) { | |
| throw new Error("The Firefox store package has invalid release metadata."); | |
| } | |
| NODE | |
| - name: Generate checksums and release notes | |
| run: | | |
| ( | |
| cd artifacts | |
| sha256sum \ | |
| "memos-web-clipper-chromium-v${VERSION}.zip" \ | |
| "memos-web-clipper-chrome-v${VERSION}.zip" \ | |
| "memos-web-clipper-edge-v${VERSION}.zip" \ | |
| "memos-web-clipper-firefox-v${VERSION}.zip" \ | |
| > SHA256SUMS | |
| ) | |
| awk ' | |
| /^## \[/ { | |
| if (found) exit | |
| found = 1 | |
| } | |
| found { print } | |
| ' CHANGELOG.md > release-notes.md | |
| if [ ! -s release-notes.md ]; then | |
| echo "Release ${VERSION}" > release-notes.md | |
| fi | |
| - name: Publish immutable release with complete assets | |
| run: | | |
| assets=( | |
| "artifacts/memos-web-clipper-chromium-v${VERSION}.zip" | |
| "artifacts/memos-web-clipper-chrome-v${VERSION}.zip" | |
| "artifacts/memos-web-clipper-edge-v${VERSION}.zip" | |
| "artifacts/memos-web-clipper-firefox-v${VERSION}.zip" | |
| "artifacts/SHA256SUMS" | |
| ) | |
| release_state="$(gh release view "$TAG" --json isDraft --jq '.isDraft' 2>/dev/null || true)" | |
| if [ "$release_state" = "false" ]; then | |
| published_sha="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${TAG}" --jq '.sha')" | |
| if [ "$published_sha" != "$RELEASE_SHA" ]; then | |
| echo "Published release $TAG points to $published_sha instead of $RELEASE_SHA." >&2 | |
| exit 1 | |
| fi | |
| if ! diff -u \ | |
| <(printf '%s\n' "${assets[@]##*/}" | sort) \ | |
| <(gh release view "$TAG" --json assets --jq '.assets[].name' | sort) | |
| then | |
| echo "Published release $TAG does not contain the expected immutable assets." >&2 | |
| exit 1 | |
| fi | |
| echo "Release $TAG is already published with the expected commit and assets." | |
| exit 0 | |
| fi | |
| if [ "$release_state" = "true" ]; then | |
| gh release edit "$TAG" --title "$TAG" --notes-file release-notes.md | |
| gh release upload "$TAG" "${assets[@]}" --clobber | |
| else | |
| gh release create "$TAG" \ | |
| --draft \ | |
| --target "$RELEASE_SHA" \ | |
| --title "$TAG" \ | |
| --notes-file release-notes.md | |
| gh release upload "$TAG" "${assets[@]}" | |
| fi | |
| gh release edit "$TAG" --draft=false --latest | |
| - name: Mark release pull request complete | |
| if: github.event_name == 'pull_request' | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: | | |
| labels="$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name')" | |
| edit_args=() | |
| if printf '%s\n' "$labels" | grep -Fxq "autorelease: pending"; then | |
| edit_args+=(--remove-label "autorelease: pending") | |
| fi | |
| if ! printf '%s\n' "$labels" | grep -Fxq "autorelease: tagged"; then | |
| edit_args+=(--add-label "autorelease: tagged") | |
| fi | |
| if [ "${#edit_args[@]}" -gt 0 ]; then | |
| gh pr edit "$PR_NUMBER" "${edit_args[@]}" | |
| fi |