Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,86 @@ jobs:
github_token: ${{ secrets.RELEASE_PR_TOKEN }}
branch: ${{ fromJson(steps.release.outputs.pr).headBranchName }}

safari-package:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not merge this below with the publish workflow?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also instead of maintaining our version of publish/notarize/sign/whatever apple wants, can we reuse existing scripts please. Say https://github.com/rxliuli/safari-webext-publish-action.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still unresolved. It should be just another matrix-option for macos/safari in the existing publish workflow.

name: Safari Package
runs-on: macos-latest
needs: [release-pr]
if: ${{ (github.event_name == 'pull_request' || needs.release-pr.outputs.release_created == 'true') && always() }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
submodules: true

- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24.16.0
cache: "pnpm"

- name: Install dependencies
run: pnpm install

- name: Build Safari app
run: make safari

- name: Archive Safari app
run: |
cd dist/safari
ditto -c -k --keepParent "JabRef Browser Extension.app" "JabRef Browser Extension.app.zip"

- name: Upload Safari Artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: safari-unsigned-app
path: dist/safari/JabRef Browser Extension.app.zip

safari-publish:
name: Safari Publish
runs-on: macos-26
needs: [release-pr, safari-package]
if: ${{ github.event_name != 'pull_request' && needs.release-pr.outputs.release_created == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
submodules: true

- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24.16.0
cache: "pnpm"

- name: Install dependencies
run: pnpm install

- name: Build Safari project
run: make safari

- name: Upload Safari to App Store Connect
uses: rxliuli/safari-webext-publish-action@624701331ecbeb38464a09d3cac6d246b6efb006
with:
project-path: "dist/safari"
bundle-identifier: "org.jabref.JabRef-Browser-Extension"
team-id: ${{ secrets.APPLE_TEAM_ID }}
app-signing-identity: ${{ secrets.SAFARI_APP_SIGNING_IDENTITY }}
installer-signing-identity: ${{ secrets.SAFARI_INSTALLER_SIGNING_IDENTITY }}
env:
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_MACOS_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_MACOS_PROVISIONING_PROFILE_BASE64 }}
APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64 }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}

publish:
name: Publish
runs-on: ubuntu-latest
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/safari-signing-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Safari Signing Test

on:
workflow_dispatch:
inputs:
run_safari_signing_test:
description: Run the Safari signing and publish step
type: boolean
required: true
default: true

permissions:
contents: read

jobs:
safari-publish:
name: Safari Publish
runs-on: macos-26
if: ${{ inputs.run_safari_signing_test }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
submodules: true

- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24.16.0
cache: "pnpm"

- name: Install dependencies
run: pnpm install

- name: Build Safari project
run: make safari

- name: Set unique build number
shell: bash
run: |
build_number="$(date -u +%Y%m%d%H%M%S)${GITHUB_RUN_ATTEMPT}"
project_pbxproj='dist/safari/JabRef Browser Extension.xcodeproj/project.pbxproj'
perl -0pi -e "s/CURRENT_PROJECT_VERSION = [0-9]+;/CURRENT_PROJECT_VERSION = ${build_number};/g" "$project_pbxproj"

for plist in \
'dist/safari/JabRef Browser Extension/Info.plist' \
'dist/safari/JabRef Browser Extension Extension/Info.plist' \
'dist/safari/JabRef Browser Extension.app/Contents/Info.plist' \
'dist/safari/JabRef Browser Extension.app/Contents/PlugIns/JabRef Browser Extension Extension.appex/Contents/Info.plist'
do
if [ -f "$plist" ]; then
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${build_number}" "$plist" >/dev/null 2>&1 || \
/usr/libexec/PlistBuddy -c "Add :CFBundleVersion string ${build_number}" "$plist"
fi
done

- name: Upload Safari to App Store Connect
uses: rxliuli/safari-webext-publish-action@624701331ecbeb38464a09d3cac6d246b6efb006
with:
project-path: "dist/safari"
bundle-identifier: "org.jabref.JabRef-Browser-Extension"
team-id: ${{ secrets.APPLE_TEAM_ID }}
app-signing-identity: ${{ secrets.SAFARI_APP_SIGNING_IDENTITY }}
installer-signing-identity: ${{ secrets.SAFARI_INSTALLER_SIGNING_IDENTITY }}
env:
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_MACOS_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_MACOS_PROVISIONING_PROFILE_BASE64 }}
APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64 }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
24 changes: 24 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,27 @@ jobs:

- name: Run tests
run: pnpm test

safari-build:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, this duplication is not easy to maintain.

name: Safari Build
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
submodules: true

- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24.16.0
cache: "pnpm"

- name: Install dependencies
run: pnpm install

- name: Build Safari app
run: make safari
46 changes: 44 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,27 @@ Preparation:
1. Install [Node.js](https://nodejs.org) (e.g., `choco install nodejs`) and [pnpm](https://pnpm.io) (e.g., `npm install -g pnpm`).
2. [Fork the repository](https://help.github.com/articles/fork-a-repo/).
3. Checkout the repository.
4. Install development dependencies via `pnpm install`.
5. Start browser with the add-on activated:
4. Initialize the git submodules via `git submodule update --init --recursive`.
5. Install development dependencies via `pnpm install`.
6. Start browser with the add-on activated:
Firefox: `pnpm dev:firefox`
Chrome: `pnpm dev:chrome`
Opera: `pnpm dev:opera`
Edge: `pnpm dev:edge`
Safari: `pnpm safari:xcode` (macOS with Xcode required)

Safari local packaging flow:

1. Build and generate the Xcode project:
`pnpm safari:xcode`
2. Open:
`dist/safari/JabRef Browser Extension.xcodeproj`
3. Run the `JabRef Browser Extension` scheme in Xcode
4. Enable the extension in Safari Settings
5. Optional signing:
`pnpm sign:safari-local IDENTITY="Developer ID Application: Your Name (TEAMID)"`
6. Optional notarization:
`pnpm notarize:safari-local PROFILE="profile-name"`

Now just follow the typical steps to [contribute code](https://guides.github.com/activities/contributing-to-open-source/#contributing):

Expand Down Expand Up @@ -43,3 +58,30 @@ The following commands are used to update the dependencies of the project; as we
- https://developer.apple.com/app-store-connect/
- Remove the `key` field in `wxt.config.ts` and build again. Then upload to:
- https://partner.microsoft.com/en-us/dashboard/microsoftedge/2045cdc1-808f-43c4-8091-43e2dcaff53d/packages

## Safari CI and Notarization

Safari CI currently has two jobs:

1. `.github/workflows/test.yml`
- `safari-build`
- runs on `macos-latest`
- executes `make safari`
2. `.github/workflows/release.yml`
- `safari-package`
- builds and uploads the unsigned Safari app artifact
- `safari-publish`
- publishes the Safari project to App Store Connect for actual releases

GitHub Actions secrets required for Safari publishing:

- `APPLE_TEAM_ID`
- `APPLE_CERTIFICATE_BASE64`
- `APPLE_CERTIFICATE_PASSWORD`
- `SAFARI_APP_SIGNING_IDENTITY`
- `SAFARI_INSTALLER_SIGNING_IDENTITY`
- `APPLE_MACOS_PROVISIONING_PROFILE_BASE64`
- `APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64`
- `APPLE_API_KEY`
- `APPLE_API_KEY_ID`
- `APPLE_API_ISSUER`
37 changes: 37 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
DIST := dist
SAFARI_DIR := $(DIST)/safari
SAFARI_PROJECT := $(SAFARI_DIR)/JabRef Browser Extension.xcodeproj
SAFARI_BUNDLE := $(DIST)/safari-mv3
SAFARI_APP := $(SAFARI_DIR)/JabRef Browser Extension.app
SAFARI_DERIVED_DATA := $(SAFARI_DIR)/build

.PHONY: safari sign-safari-local notarize-safari-local clean-safari

safari:
rm -rf "$(SAFARI_DIR)"
rm -rf "$(SAFARI_BUNDLE)"
mkdir -p "$(DIST)"
pnpm build:safari
mkdir -p "$(SAFARI_DIR)"
node scripts/prepare_safari_bundle.mjs
node scripts/patch_safari_project.mjs
xcodebuild -project "$(SAFARI_PROJECT)" \
-scheme "JabRef Browser Extension" \
-configuration Release \
-derivedDataPath "$(SAFARI_DERIVED_DATA)" \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
build
ditto "$(SAFARI_DERIVED_DATA)/Build/Products/Release/JabRef Browser Extension.app" "$(SAFARI_APP)"

sign-safari-local:
chmod +x scripts/sign_safari_local.sh
./scripts/sign_safari_local.sh "$(IDENTITY)"

notarize-safari-local:
chmod +x scripts/notarize_safari_local.sh
./scripts/notarize_safari_local.sh "$(PROFILE)"

clean-safari:
rm -rf "$(SAFARI_DIR)"
54 changes: 52 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# JabRef Browser Extension

> [Firefox](https://addons.mozilla.org/en-US/firefox/addon/jabref/?src=external-github) - [Chrome](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh) - [Edge](https://microsoftedge.microsoft.com/addons/detail/pgkajmkfgbehiomipedjhoddkejohfna) - [Vivaldi](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh)
> [Firefox](https://addons.mozilla.org/en-US/firefox/addon/jabref/?src=external-github) - [Chrome](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh) - [Edge](https://microsoftedge.microsoft.com/addons/detail/pgkajmkfgbehiomipedjhoddkejohfna) - [Vivaldi](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh) - Safari (build from source)

Browser extension for users of the bibliographic reference manager [JabRef](https://www.jabref.org/).
It automatically identifies and extracts bibliographic information on websites and sends them to JabRef with one click.
Expand All @@ -15,10 +15,60 @@ _Please post any issues or suggestions [here on GitHub](https://github.com/JabRe

Normally, you simply install the extension from the browser store and are ready to go.

> [Firefox](https://addons.mozilla.org/en-US/firefox/addon/jabref/?src=external-github) - [Chrome](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh) - [Edge](https://microsoftedge.microsoft.com/addons/detail/pgkajmkfgbehiomipedjhoddkejohfna) - [Vivaldi](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh)
> [Firefox](https://addons.mozilla.org/en-US/firefox/addon/jabref/?src=external-github) - [Chrome](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh) - [Edge](https://microsoftedge.microsoft.com/addons/detail/pgkajmkfgbehiomipedjhoddkejohfna) - [Vivaldi](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh) - Safari (build from source)

Sometimes, a manual installation is necessary (e.g. if you use the portable version of JabRef). In this case, please follow the steps described [in the user manual](https://docs.jabref.org/import-export/import/jabref-browser-extension).

Safari builds are available for local development via WXT:

- `pnpm build:safari` builds the Safari target into `.output/safari-mv3/`
- `pnpm dev:safari` builds the Safari development target

For the Apple packaging step:

- `pnpm safari:xcode` builds the Safari target and generates the Xcode project in `dist/safari/` through [`wxt-module-safari-xcode`](https://github.com/rxliuli/wxt-module-safari-xcode)
- `pnpm sign:safari-local IDENTITY="Developer ID Application: Your Name (TEAMID)"` signs the generated app
- `pnpm notarize:safari-local PROFILE="profile-name"` notarizes and zips the signed app

WXT builds the extension bundle, and `wxt-module-safari-xcode` converts that bundle into the Xcode project and macOS app structure Apple expects.

To test the Safari build locally:

1. Run `pnpm safari:xcode`
2. Open `dist/safari/JabRef Browser Extension.xcodeproj`
3. Run the `JabRef Browser Extension` scheme in Xcode
4. Enable the extension in Safari Settings

The generated macOS app bundle is placed at `dist/safari/JabRef Browser Extension.app`.

Safari CI is split into two parts:

1. `Tests` workflow:
- `safari-build` runs on `macos-latest`
- it executes `make safari`
- this validates the WXT build, Xcode packaging, and Safari app bundle path on pull requests and on `main`
2. `release` workflow:
- `safari-package` builds and uploads the unsigned Safari app artifact
- `safari-publish` rebuilds the Xcode project on `macos-26` and publishes it to App Store Connect with [`rxliuli/safari-webext-publish-action`](https://github.com/rxliuli/safari-webext-publish-action)
3. `Safari Signing Test` workflow:
- manual `workflow_dispatch` workflow with a `run_safari_signing_test` checkbox
- it builds the Safari project and runs the App Store signing/publish step without touching the release workflow

The Safari publish job expects these GitHub Actions secrets:

- `APPLE_TEAM_ID`: Apple Developer team ID
- `APPLE_CERTIFICATE_BASE64`: base64-encoded `.p12` certificate containing the App Store signing identities
- `APPLE_CERTIFICATE_PASSWORD`: password for that `.p12`
- `SAFARI_APP_SIGNING_IDENTITY`: full app signing identity, for example `Apple Distribution: JabRef e.V. (TEAMID)`
- `SAFARI_INSTALLER_SIGNING_IDENTITY`: full installer signing identity, for example `Mac Installer Distribution: JabRef e.V. (TEAMID)`
- `APPLE_MACOS_PROVISIONING_PROFILE_BASE64`: base64-encoded macOS App Store provisioning profile for the app bundle ID
- `APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64`: base64-encoded macOS App Store provisioning profile for the extension bundle ID
- `APPLE_API_KEY`: base64-encoded App Store Connect API key (`.p8`)
- `APPLE_API_KEY_ID`: App Store Connect API key ID
- `APPLE_API_ISSUER`: App Store Connect API issuer ID

The local `pnpm sign:safari-local` and `pnpm notarize:safari-local` commands still exist for manual Developer ID packaging outside the App Store flow.

## Usage

After the installation, you should be able to import bibliographic references into JabRef directly from your browser.
Expand Down
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,27 @@
"dev:chrome": "wxt -b chrome",
"dev:opera": "wxt -b opera",
"dev:edge": "wxt -b edge",
"dev:safari": "wxt -b safari",
"build": "wxt build",
"build:firefox": "wxt build -b firefox",
"build:safari": "wxt build -b safari",
"safari:xcode": "wxt build -b safari",
"sign:safari-local": "make sign-safari-local",
"notarize:safari-local": "make notarize-safari-local",
"zip": "wxt zip",
"zip:firefox": "wxt zip -b firefox",
"zip:safari": "wxt zip -b safari",
"compile": "vue-tsc --noEmit",
"lint": "pnpm lint:oxlint && pnpm lint:oxfmt",
"lint:oxlint": "oxlint",
"lint:oxlint": "oxlint --ignore-pattern 'sources/**' --ignore-pattern 'translators/zotero/**'",
"lint:oxfmt": "oxfmt --check",
"test": "vitest run",
"test:legacy": "node test.js arXiv.org.js",
"postinstall": "wxt prepare"
},
"dependencies": {
"esbuild": "^0.28.1",
"spawn-sync": "^2.0.0",
"vue": "3.5.35"
},
"devDependencies": {
Expand All @@ -51,7 +59,8 @@
"typescript": "6.0.3",
"vitest": "4.1.8",
"vue-tsc": "3.3.3",
"wxt": "0.20.26"
"wxt": "0.20.26",
"wxt-module-safari-xcode": "0.2.2"
},
"packageManager": "pnpm@10.34.1"
}
Loading
Loading