Compare commits

..

15 commits
main ... 5.0.77

Author SHA1 Message Date
Dan Stillman
75aeac3f39 Update version 2019-10-31 02:47:42 -04:00
Dan Stillman
83904a6aba Update submodules 2019-10-31 02:44:31 -04:00
Dan Stillman
269a2fb791 Further tweaks to file-not-found dialog
- "path" → "location"
- Send "More Information" button to a `missing_stored_file` wiki page
  (similar to `missing_linked_file`) instead of directly to
  `files_not_syncing`
2019-10-31 02:35:17 -04:00
Dan Stillman
b5d0ad2c4a Update locales from Transifex 2019-10-31 02:13:47 -04:00
Philipp Zumstein
a749d723e0 Use the label Prog. Language for Computer Program (#1743) 2019-10-31 02:06:05 -04:00
Dan Stillman
69a888eb44 Force Lucida Grande in Quick Format dialog
Follow-up to 86d924df9e

See zotero/zotero-standalone-build@ed5d5efcc for details.
2019-10-31 01:57:57 -04:00
Dan Stillman
87f04fa821 Add 'Type:' to extraToCSL() test
Follow-up to #1744
2019-10-31 01:40:02 -04:00
Dan Stillman
59f5c37824 Remove all colored tags on selected items if 0 is pressed
Like Thunderbird
2019-10-31 01:39:39 -04:00
Dan Stillman
763c480a61 Fix number key assignment of colored tag without any items
Fixes #1464
2019-10-31 01:39:22 -04:00
Brenton M. Wiernik
97acd16429 Add 'type' to variables list for
So that it can be stored in Extra as "Type:" instead of "type:"

Replaces https://github.com/zotero/zotero/pull/1688
2019-10-31 01:39:05 -04:00
Dan Stillman
4f9e4bee86 Restore reopening of closed collection and item panes on startup
When we added the View menu pane options in 3aeef69ad3, we removed
automatic reopening of the panes on startup, since the View menu is more
discoverable than the narrow collapsed splitters. Unfortunately we're
now getting many reports of people not knowing where their panes are.
It turns out the View menu pane options in 5.0.76 are actually broken,
so it's not a great test, but enough people are doing this by accident
and enough of them don't seem to be finding the View menu options
(broken or otherwise) that I think it's better to return to reopening
the panes. We can always add a hidden pref to change that behavior, but
no one has complained in years, so it's probably not necessary.
2019-10-31 01:38:39 -04:00
Dan Stillman
49b4c1989b Fix Collection Pane and Item Pane toggles in View → Layout 2019-10-31 01:38:21 -04:00
Dan Stillman
283d3e8e6c Fix "err is undefined" on 200 response for nonexistent WebDAV file
Fixes #1741
2019-10-31 01:38:07 -04:00
Dan Stillman
67214a638b File-not-found dialog improvements
- Include file path
- Show separate message for linked files mentioning Linked Attachment
  Base Directory
- Link to separate support page for linked files
2019-10-31 01:37:37 -04:00
Dan Stillman
6c551abb38 Skip invalid on-demand download attempt if not yet synced
This could happen if an account was set up for syncing but Zotero was
pointed at a new DB and then you double-clicked on a missing attachment.
2019-10-31 01:37:31 -04:00
3824 changed files with 173265 additions and 573317 deletions

View file

@ -7,12 +7,14 @@
"ignore": [
"chrome/content/zotero/include.js",
"chrome/content/zotero/xpcom/citeproc.js",
"chrome/content/ace/*",
"chrome/content/scaffold/templates/*",
"resource/react.js",
"resource/react-dom.js",
"resource/react-virtualized.js",
"test/resource/*.js"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread",
[
"transform-es2015-modules-commonjs",
{

18
.eslintignore Normal file
View file

@ -0,0 +1,18 @@
build/*
chrome/content/zotero/xpcom/citeproc.js
chrome/content/zotero/xpcom/isbn.js
chrome/content/zotero/xpcom/rdf/*
chrome/content/zotero/xpcom/xregexp/*
chrome/content/zotero/xpcom/translation/tlds.js
chrome/skin/default/zotero/timeline/bundle.js
chrome/skin/default/zotero/timeline/timeline-api.js
resource/bluebird/*
resource/classnames.js
resource/csl-validator.js
resource/jspath.js
resource/prop-types.js
resource/react*
resource/schema/connectorTypeSchemaData.js
resource/tinymce/*
test/resource/*
translators/*

43
.eslintrc Normal file
View file

@ -0,0 +1,43 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"globals": {
"Zotero": false,
"ZOTERO_CONFIG": false,
"AddonManager": false,
"Cc": false,
"Ci": false,
"Components": false,
"ConcurrentCaller": false,
"ctypes": false,
"OS": false,
"PluralForm": false,
"Services": false,
"XPCOMUtils": false,
"XRegExp": false
},
"extends": [
"@zotero",
"plugin:react/recommended"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018,
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"plugins": [
"react"
],
"settings": {
"react": {
"version": "16.8"
}
},
"rules": {}
}

4
.gitattributes vendored
View file

@ -1,5 +1 @@
*.sql text eol=lf
app/win/zotero.exe.tar.xz filter=lfs diff=lfs merge=lfs -text
app/mac/updater.tar.xz filter=lfs diff=lfs merge=lfs -text
app/win/updater.exe.tar.xz filter=lfs diff=lfs merge=lfs -text
app/linux/updater.tar.xz filter=lfs diff=lfs merge=lfs -text

7
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,7 @@
**READ THIS BEFORE CREATING AN ISSUE**
Zotero does not use GitHub Issues for bug reports or feature requests.
Please post all such requests to the Zotero Forums at https://forums.zotero.org, where Zotero developers and many others can help. For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.
See https://www.zotero.org/support/zotero_support for more information on how Zotero support works.

View file

@ -1,439 +0,0 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
schedule:
# Monthly, to catch runner image and OS changes
- cron: '0 6 1 * *'
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
# Decide whether to run the network-filesystem and Windows tests: always for manual and
# scheduled runs, and otherwise only when the changes touch the Gecko version, the files
# responsible for network-filesystem database handling, or (for Windows) the files that
# make up the Windows build
changes:
name: Detect changes
runs-on: ubuntu-latest
if: >
github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository
outputs:
network: ${{ steps.check.outputs.network }}
windows: ${{ steps.check.outputs.windows }}
steps:
- name: Check for network-filesystem-related changes
id: check
env:
EVENT: ${{ github.event_name }}
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
BEFORE: ${{ github.event.before }}
BASE: ${{ github.event.pull_request.base.sha }}
run: |
network=false
windows=false
if [[ "$EVENT" == "workflow_dispatch" || "$EVENT" == "schedule" ]]; then
network=true
windows=true
else
base="$BASE"
[[ -z "$base" ]] && base="$BEFORE"
git init -q repo && cd repo
git remote add origin "https://github.com/$REPO"
git fetch -q --depth 1 origin "$SHA"
# Run the tests if the base of the change can't be determined (e.g., a new
# branch or an unfetchable pre-force-push commit)
if [[ -z "$base" || "$base" == 0000000000000000000000000000000000000000 ]] \
|| ! git fetch -q --depth 1 origin "$base" 2>/dev/null; then
network=true
windows=true
else
files=$(git diff --name-only "$base" "$SHA")
if grep -qE '^(app/config\.sh|chrome/content/zotero/xpcom/(db|file)\.js|\.github/workflows/ci\.yml)$' <<< "$files"; then
network=true
windows=true
fi
if grep -qE '^app/(win/|build\.sh$|scripts/(dir_build|fetch_xulrunner|prepare_build)$)' <<< "$files"; then
windows=true
fi
fi
fi
echo "network=$network" >> "$GITHUB_OUTPUT"
echo "windows=$windows" >> "$GITHUB_OUTPUT"
test:
name: Test (${{ matrix.shard }})
runs-on: ubuntu-latest
timeout-minutes: 30
needs: changes
# Same-repo PRs are covered by the push run, so run pull_request jobs only for forks
if: >
github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository
strategy:
matrix:
# Numbered shards split the full suite. 'smb' and 'nfs' run the db and file tests
# with the test data directory on a loopback CIFS or NFS mount, so that connection
# initialization itself runs against a network filesystem, which is where
# network-filesystem failures (startup crashes and hangs) occur. The network shards
# run only when the 'changes' job detects relevant changes.
shard: ${{ fromJSON(needs.changes.outputs.network == 'true'
&& '["1", "2", "3", "4", "smb", "nfs"]'
|| '["1", "2", "3", "4"]') }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
#cache: npm
# Local via act
- name: Install packages for act
if: env.ACT == 'true'
run: apt update && apt install -y zstd xvfb dbus-x11 libgtk-3-0 libx11-xcb1 libdbus-glib-1-2 libxt6
- name: Cache xulrunner
id: xulrunner-cache
uses: actions/cache@v4
with:
path: app/xulrunner/firefox-x86_64
key: xulrunner-${{ hashFiles('app/config.sh', 'app/scripts/fetch_xulrunner') }}
- name: Fetch xulrunner
if: steps.xulrunner-cache.outputs.cache-hit != 'true'
run: app/scripts/fetch_xulrunner -p l
- name: Cache Node modules
id: node-cache
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true'
run: npm install
- name: Build Zotero
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
# Create deployment ZIP from the build output, then replace build/ with the
# unzipped contents so that tests run against the same artifact that gets
# deployed. This catches problems that only manifest after a zip round-trip
# (e.g., resolved symlinks, missing files).
- name: Create deployment ZIP
run: |
cd build
zip -r ../build.zip *
cd ..
- name: Replace build with ZIP contents
run: |
rm -rf build
mkdir build
cd build
unzip ../build.zip
- name: Set up SMB share
if: matrix.shard == 'smb'
timeout-minutes: 10
run: |
sudo apt install -y --no-install-recommends samba cifs-utils
sudo mkdir -p /srv/zotero-smb /mnt/zotero-smb
sudo chown $(whoami) /srv/zotero-smb
sudo tee -a /etc/samba/smb.conf > /dev/null <<EOF
[ztest]
path = /srv/zotero-smb
read only = no
EOF
(echo smbtestpass; echo smbtestpass) | sudo smbpasswd -s -a $(whoami)
sudo systemctl restart smbd
sudo mount -t cifs //127.0.0.1/ztest /mnt/zotero-smb \
-o user=$(whoami),pass=smbtestpass,vers=3.0,uid=$(id -u),gid=$(id -g)
- name: Set up NFS share
if: matrix.shard == 'nfs'
timeout-minutes: 10
run: |
sudo apt install -y --no-install-recommends nfs-kernel-server
sudo mkdir -p /srv/zotero-nfs /mnt/zotero-nfs
sudo chown $(whoami) /srv/zotero-nfs
echo "/srv/zotero-nfs localhost(rw,no_subtree_check,insecure)" | sudo tee -a /etc/exports
sudo exportfs -ra
sudo mount -t nfs4 localhost:/srv/zotero-nfs /mnt/zotero-nfs
- name: Run tests
if: matrix.shard != 'smb' && matrix.shard != 'nfs'
run: xvfb-run test/runtests.sh -f -r 3 -p ${{ matrix.shard }}/4
# The step timeout turns a startup hang into a failure
- name: Run tests on network share
if: matrix.shard == 'smb' || matrix.shard == 'nfs'
timeout-minutes: 10
run: TMPDIR=/mnt/zotero-${{ matrix.shard }} xvfb-run test/runtests.sh -f -r 3 -b db file
# Run the db and file tests on macOS with the test data directory on a loopback NFS
# mount. On macOS, SQLite selects locking methods without shared-memory support on
# network filesystems, which is what the network-filesystem database handling has to
# detect and handle, so this runs the real code path that crashed on SMB/NFS/sshfs
# data directories.
test-mac:
name: Test (macOS NFS)
runs-on: macos-15
timeout-minutes: 30
needs: changes
if: >
needs.changes.outputs.network == 'true'
&& (github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository)
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
- name: Cache xulrunner
id: xulrunner-cache
uses: actions/cache@v4
with:
path: app/xulrunner/Firefox.app
key: xulrunner-mac-${{ hashFiles('app/config.sh', 'app/scripts/fetch_xulrunner') }}
- name: Fetch xulrunner
if: steps.xulrunner-cache.outputs.cache-hit != 'true'
run: app/scripts/fetch_xulrunner -p m
- name: Cache Node modules
id: node-cache
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-mac-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true'
run: npm install
- name: Build Zotero
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
- name: Set up NFS share
run: |
sudo mkdir -p /private/var/zotero-nfs
sudo chown $(whoami) /private/var/zotero-nfs
echo "/private/var/zotero-nfs -mapall=$(whoami) localhost" | sudo tee /etc/exports
sudo nfsd enable || sudo nfsd start
sleep 2
showmount -e localhost
mkdir -p "$HOME/zotero-nfs"
sudo mount_nfs -o vers=3 localhost:/private/var/zotero-nfs "$HOME/zotero-nfs"
# The step timeout turns a startup hang into a failure
- name: Run tests on network share
timeout-minutes: 10
run: TMPDIR="$HOME/zotero-nfs" test/runtests.sh -f -r 3 -b db file
# Boot the Windows build -- which uses custom-built Firefox components (xul.dll) rather
# than stock Mozilla binaries -- and run the db and file tests. A Gecko bump commit
# updates the custom-component hashes in config.sh along with the version, so this
# tests each new set of custom components.
test-win:
name: Test (Windows ${{ matrix.arch }})
runs-on: ${{ matrix.os }}
timeout-minutes: 40
needs: changes
if: >
needs.changes.outputs.windows == 'true'
&& (github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository)
strategy:
fail-fast: false
matrix:
include:
- arch: x64
os: windows-latest
- arch: arm64
os: windows-11-arm
defaults:
run:
shell: bash
steps:
# The repository contains symlinks, which Git checks out as plain files on Windows
# by default, breaking the build
- name: Enable symlinks
run: git config --global core.symlinks true
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
# Git Bash has unzip but not zip, which fetch_xulrunner needs to repack omni.ja
- name: Install zip
run: choco install zip --no-progress -y
- name: Cache xulrunner
id: xulrunner-cache
uses: actions/cache@v4
with:
path: app/xulrunner/firefox-win-${{ matrix.arch }}
key: xulrunner-win-${{ matrix.arch }}-${{ hashFiles('app/config.sh', 'app/scripts/fetch_xulrunner') }}
- name: Fetch xulrunner
if: steps.xulrunner-cache.outputs.cache-hit != 'true'
run: app/scripts/fetch_xulrunner -p w -a ${{ matrix.arch }}
- name: Cache Node modules
id: node-cache
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-win-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true'
run: npm install
# Committed symlinks point into node_modules, which doesn't exist at checkout time,
# so Git creates the directory ones with the wrong symlink type on Windows. Recreate
# them now that the targets exist.
- name: Recreate symlinks
run: |
git ls-files -s | awk '$1 == 120000 { print $4 }' | xargs rm
git checkout -- .
- name: Build Zotero
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
# The step timeout turns a startup hang into a failure
- name: Run tests
timeout-minutes: 15
run: test/runtests.sh -f -r 3 -b -x app/staging/Zotero_win-${{ matrix.arch }}/zotero.exe db file
utilities:
name: Utilities Tests
runs-on: ubuntu-latest
if: >
github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
- name: Cache utilities Node modules
id: utilities-node-cache
uses: actions/cache@v4
with:
path: chrome/content/zotero/xpcom/utilities/node_modules
key: utilities-node-modules-24-${{ hashFiles('chrome/content/zotero/xpcom/utilities/package-lock.json') }}
- name: Install utilities Node modules
if: steps.utilities-node-cache.outputs.cache-hit != 'true'
run: npm install --prefix chrome/content/zotero/xpcom/utilities
- name: Run utilities tests
run: |
npm test --prefix chrome/content/zotero/xpcom/utilities -- -j resource/schema/global/schema.json
# Build deployment ZIPs from main, version branches (e.g., 9.0, 10.0), and *-hotfix branches
deploy:
name: Build, Upload
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.repository == 'zotero/zotero'
steps:
- name: Check if deployment branch
id: check-deploy
if: env.ACT != 'true'
run: |
branch="${GITHUB_REF#refs/heads/}"
if [[ "$branch" == "main" || "$branch" =~ ^[0-9]+\.[0-9]+$ || "$branch" == *-hotfix ]]; then
echo "deploy=true" >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@v4
if: steps.check-deploy.outputs.deploy == 'true'
with:
submodules: recursive
lfs: true
- name: Install Node
if: steps.check-deploy.outputs.deploy == 'true'
uses: actions/setup-node@v4
with:
node-version: 24
- name: Cache Node modules
id: node-cache
if: steps.check-deploy.outputs.deploy == 'true'
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.check-deploy.outputs.deploy == 'true' && steps.node-cache.outputs.cache-hit != 'true'
run: npm install
- name: Build Zotero
if: steps.check-deploy.outputs.deploy == 'true'
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
- name: Create deployment ZIP
if: steps.check-deploy.outputs.deploy == 'true'
run: |
cd build
zip -r ../build.zip *
cd ..
- uses: ruby/setup-ruby@v1
if: steps.check-deploy.outputs.deploy == 'true'
with:
ruby-version: '3.3'
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Upload deployment ZIP
if: steps.check-deploy.outputs.deploy == 'true'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
mkdir build-zip
cp build.zip build-zip/$GITHUB_SHA.zip
gem install --no-document dpl -v '>= 2.0'
dpl s3 --bucket zotero-download --local_dir build-zip --upload_dir ci/client --acl public_read

3
.gitignore vendored
View file

@ -1,6 +1,5 @@
.DS_Store
node_modules
build/
build
.signatures.json
tmp
app/win/codesign

42
.gitmodules vendored
View file

@ -1,14 +1,14 @@
[submodule "translators"]
path = translators
url = https://github.com/zotero/translators.git
url = git://github.com/zotero/translators.git
branch = master
[submodule "chrome/content/zotero/locale/csl"]
path = chrome/content/zotero/locale/csl
url = https://github.com/citation-style-language/locales.git
url = git://github.com/citation-style-language/locales.git
branch = master
[submodule "styles"]
path = styles
url = https://github.com/zotero/bundled-styles.git
url = git://github.com/zotero/bundled-styles.git
branch = master
[submodule "test/resource/chai"]
path = test/resource/chai
@ -18,37 +18,7 @@
path = test/resource/mocha
url = https://github.com/mochajs/mocha.git
branch = master
[submodule "resource/schema/global"]
path = resource/schema/global
url = https://github.com/zotero/zotero-schema.git
[submodule "test/resource/chai-as-promised"]
path = test/resource/chai-as-promised
url = https://github.com/domenic/chai-as-promised.git
branch = master
[submodule "resource/SingleFile"]
path = resource/SingleFile
url = https://github.com/gildas-lormeau/SingleFile.git
[submodule "pdf-reader"]
path = reader
url = https://github.com/zotero/reader.git
branch = master
[submodule "pdf-worker"]
path = document-worker
url = https://github.com/zotero/document-worker.git
branch = master
[submodule "note-editor"]
path = note-editor
url = https://github.com/zotero/note-editor.git
branch = master
[submodule "chrome/content/zotero/xpcom/utilities"]
path = chrome/content/zotero/xpcom/utilities
url = https://github.com/zotero/utilities.git
[submodule "chrome/content/zotero/xpcom/translate"]
path = chrome/content/zotero/xpcom/translate
url = https://github.com/zotero/translate.git
[submodule "app/modules/zotero-word-for-mac-integration"]
path = app/modules/zotero-word-for-mac-integration
url = https://github.com/zotero/zotero-word-for-mac-integration.git
[submodule "app/modules/zotero-word-for-windows-integration"]
path = app/modules/zotero-word-for-windows-integration
url = https://github.com/zotero/zotero-word-for-windows-integration.git
[submodule "app/modules/zotero-libreoffice-integration"]
path = app/modules/zotero-libreoffice-integration
url = https://github.com/zotero/zotero-libreoffice-integration.git

54
.travis.yml Normal file
View file

@ -0,0 +1,54 @@
sudo: false
language: node_js
node_js:
- "8"
cache:
directories:
- "build"
- "node_modules"
env:
global:
secure: "NxvkbZ7/Op7BTGQRR3C4q8lLoO29f8WtyNN27NSH7AO3H0vBr1Vp5xO8gn+H2qHEug5HvM+YrZ/xAkNXaZVbOInmBmKVMxqVvdpKp9JM1Amf+gzsXWQphfySvs6iqzyP6cwU/jspdvX/WSakgU5v7PWXxtUIaKxdANt6Rw7W+Pc="
matrix:
- FX_VERSION="52.0.3"
matrix:
fast_finish: true
#allow_failures:
# - env: FX_CHANNEL="beta"
notifications:
email: false
addons:
apt:
packages:
- dbus-x11
services:
- xvfb
install:
- if [ $FX_VERSION = "52.0.3" ]; then
wget -O tarball "https://archive.mozilla.org/pub/firefox/tinderbox-builds/mozilla-release-linux64-add-on-devel/1491732920/firefox-52.0.3.en-US.linux-x86_64-add-on-devel.tar.bz2";
fi
- tar xf tarball
before_script:
- npm i
- npm run build
- if [[ $TRAVIS_REPO_SLUG = "zotero/zotero" &&
($TRAVIS_BRANCH = "master" || $TRAVIS_BRANCH = *-hotfix) &&
$TRAVIS_PULL_REQUEST = "false" ]]; then
mkdir build-zip;
cd build;
zip -r ../build-zip/$TRAVIS_COMMIT.zip *;
cd ..;
gem install dpl;
dpl --provider=s3
--access-key-id=AKIAJFDVJ54MCAEXPQ5Q
--bucket=zotero-download
--local-dir=build-zip
--upload-dir=ci/client
--acl=public-read
--skip_cleanup=true;
fi
- unset AWS_SECRET_ACCESS_KEY
# Fix warnings in output
- dbus-launch
script:
- test/runtests.sh -x firefox/firefox -f

110
CLAUDE.md
View file

@ -1,110 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Zotero is a Firefox-based desktop research management application. It runs as a XUL/XHTML application on a Mozilla platform (not Electron, not a web app). The UI uses a mix of XUL/XHTML, custom elements (Web Components), and React components.
## Build & Development Commands
```bash
npm install # Install dependencies
npm run build # Full build (Babel transpile JS/JSX, compile SCSS, browserify, symlink)
npm run clean-build # Clean build output and rebuild
```
### Running Tests
Tests run inside a built Zotero application instance via `test/runtests.sh`:
```bash
test/runtests.sh # Run all tests
test/runtests.sh item # Run a single test file (omit "Test" and .js extension)
test/runtests.sh item collections # Run multiple test files
test/runtests.sh -g "pattern" # Run only tests matching a grep pattern
test/runtests.sh -f # Stop after first failure
test/runtests.sh -s item # Start at a specific test file
test/runtests.sh -e item # End at a specific test file
test/runtests.sh -c # Open JS console, don't quit on completion
test/runtests.sh -b # Skip bundled translator/style installation (for faster startup when not necessary)
test/runtests.sh -d 5 # Enable debug logging (level 1-5; default is 3)
```
Tests use Mocha + Chai (assert style) + Sinon. Test files live in `test/tests/` and follow the naming convention `<module>Test.js`. Test helpers are in `test/content/support.js`.
The test runner automatically triggers `npm run build` if the watch process isn't running, so it's not necessary to manually run a build before running tests.
Tips:
- Pass flags before test names (e.g., `test/runtests.sh -f item`).
- `-f` should almost always be used, since test failures can cause later spurious test failures.
- To view debug logging for a specific test when running multiple tests, add `Zotero.Debug.init(1)` at the beginning of the test and run with `-d 5`.
### Linting
```bash
npx eslint <file> # Lint a specific file
```
ESLint v9 flat config is in `eslint.config.mjs`. Uses `@zotero/eslint-config`, Babel parser, and React plugin. Currently, the code base is not fully linted, so only worry about issues with new or modified code lines.
## Architecture
### Core Layers
- **XPCOM modules** (`chrome/content/zotero/xpcom/`) -- Core business logic. Loaded sequentially by `chrome/content/zotero/zotero.mjs`. All modules attach to the global `Zotero` namespace (e.g., `Zotero.Items`, `Zotero.Sync.Runner`).
- **Data model** (`chrome/content/zotero/xpcom/data/`) -- ORM-like classes for database entities. `Zotero.DataObject` is the base class; `Zotero.Item`, `Zotero.Collection`, `Zotero.Search`, `Zotero.Library`, etc. extend it. Plural classes (`Zotero.Items`, `Zotero.Collections`) manage object caches and queries. Data objects use an async `saveTx()` pattern for persistence.
- **Database** (`chrome/content/zotero/xpcom/db.js`) -- SQLite via Mozilla's mozStorage API. Accessed through `Zotero.DB.queryAsync()`, `Zotero.DB.executeTransaction()`.
- **Sync system** (`chrome/content/zotero/xpcom/sync/`) -- `syncRunner.js` orchestrates sync. `syncEngine.js` handles object-level sync logic. `syncAPIClient.js` communicates with the Zotero API server. `syncLocal.js` manages local sync state. Similar `storage*` files for file syncing, along with zfs.js (Zotero Storage) and webdav.js (WebDAV).
- **HTTP server** (`chrome/content/zotero/xpcom/server/`) -- Local HTTP server for browser connector integration and local API.
### UI Layers
- **Main window**: `chrome/content/zotero/zoteroPane.xhtml` + `zoteroPane.js`
- **Custom elements** (`chrome/content/zotero/elements/`) -- XUL custom elements inheriting from `XULElementBase` (defined in `elements/base.js`), which provides lifecycle helpers (`init()`, `destroy()`, `content` getter for templates). Registered via `customElements.define()`.
- **React components** (`chrome/content/zotero/components/`) -- Used for complex interactive UI (tag selector, virtualized table, item tree, collection tree). Major tree views are `itemTree.jsx` and `collectionTree.jsx`.
- **SCSS styles** (`scss/`) -- Compiled to CSS. Platform-specific overrides in `scss/mac/`, `scss/win/`, `scss/linux/`.
- **Localization** -- Fluent (`.ftl` files) in `chrome/locale/en-US/zotero/`. Accessed via `data-l10n-id` attributes or `Zotero.getString()` for legacy `.properties` strings. All new strings must be added to `.ftl` files -- do not add new strings to `.properties` or `.dtd` files.
### Submodules
Several features are developed in separate repos and included as Git submodules:
- `reader/` -- PDF/EPUB/snapshot reader with annotations
- `note-editor/` -- Rich text note editor
- `document-worker/` -- PDF processing (extraction, manipulation)
- `translators/` -- 760+ web translators for importing metadata from websites
- `styles/` -- CSL citation styles
- `chrome/content/zotero/xpcom/utilities/` -- Shared utility library
- `chrome/content/zotero/xpcom/translate/` -- Translation framework
- `resource/SingleFile/` -- Web page archiving
### Build System (`js-build/`)
Custom Node.js build system (not Webpack). `js-build/config.js` defines what gets built:
- **JS/JSX files** in `chrome/`, `components/`, `defaults/`, `test/` are transpiled via Babel (React JSX, CommonJS modules)
- **SCSS files** in `scss/` and `chrome/skin/` are compiled with Dart Sass
- **Browserify** bundles select npm packages (react-select, sinon, url) for use in the XUL environment
- **Symlinks** connect source files and submodules into the `build/` directory
- The built app reads from `build/`, not directly from source
## Code Style
- Tabs for indentation, not spaces
- Use `let` instead of `const` except for true scalar constants (e.g., `const MAX_SECONDS = 5 * 60;`)
- No cuddled braces (opening brace on same line, but `else`/`catch`/etc. on their own line)
- Use two hyphens `--` in comments, not an em dash
- Indent blank lines to match surrounding indentation level
- Objects attach to the `Zotero` global namespace rather than using ES module exports
- Async code uses `async`/`await` throughout
- Mozilla/XPCOM APIs are available globally: `Cc`, `Ci`, `Cu`, `Cr`, `Services`, `ChromeUtils`, `IOUtils`, `PathUtils`
## Translators
See `translators/CLAUDE.md` for guidelines. Key rules: **never** generate translators from scratch, **never** generate UUIDs, **never** generate test cases -- all of these must be done through Zotero's Scaffold tool.

View file

@ -2,7 +2,7 @@
## Bug Reports and Feature Requests
Zotero does not use GitHub Issues for bug reports, feature requests, or support questions. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. See [How Zotero Support Works](https://www.zotero.org/support/zotero_support) for more information.
Zotero does not use GitHub Issues for bug reports or feature requests. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. See [How Zotero Support Works](https://www.zotero.org/support/zotero_support) for more information.
For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.

View file

@ -1,9 +1,9 @@
Zotero
======
[![CI](https://github.com/zotero/zotero/actions/workflows/ci.yml/badge.svg)](https://github.com/zotero/zotero/actions/workflows/ci.yml)
[![Build Status](https://travis-ci.org/zotero/zotero.svg?branch=master)](https://travis-ci.org/zotero/zotero)
[Zotero](https://www.zotero.org/) is a free, easy-to-use tool to help you collect, organize, cite, and share your research sources.
Please post feature requests or bug reports to the [Zotero Forums](https://forums.zotero.org/). If you're having trouble with Zotero, see [Getting Help](https://www.zotero.org/support/getting_help).
For more information on how to use this source code, see the [Zotero documentation](https://www.zotero.org/support/dev/source_code).
For more information on how to use this source code, see the [Zotero wiki](https://www.zotero.org/support/dev/source_code).

12
app/.gitignore vendored
View file

@ -1,12 +0,0 @@
*~
cache
config-custom.sh
dist
lastrev
staging
tmp
xulrunner
win/resource_hacker
win/firefox-*.win32.zip
win/firefox-*.win64.zip
win/firefox-*.win64-aarch64.zip

View file

@ -1,4 +0,0 @@
# Zotero Standalone build utility
These files are used to bundle the [Zotero core](https://github.com/zotero/zotero) into distributable bundles for Mac, Windows, and Linux.
Instructions for building and packaging are available on the [Zotero wiki](https://www.zotero.org/support/dev/client_coding/building_the_standalone_client).

View file

@ -1,18 +0,0 @@
[App]
Vendor=Zotero
Name=Zotero
Version={{VERSION}}
BuildID={{BUILDID}}
Copyright=Copyright (c) 2006-2025 Contributors
ID=zotero@zotero.org
[Gecko]
MinVersion=153.0
MaxVersion=153.99.*
[XRE]
EnableExtensionManager=1
EnableProfileMigrator=1
[AppUpdate]
URL=https://www.zotero.org/download/client/update/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/update.xml

View file

@ -1 +0,0 @@
<!ENTITY brandShortName "Zotero">

View file

@ -1,8 +0,0 @@
-brand-shorter-name = Zotero
-brand-short-name = Zotero
-brand-full-name = Zotero
-brand-product-name = Zotero
-vendor-short-name = Zotero
-app-name = Zotero
-subscription-name = Zotero Storage
trademarkInfo = Zotero is a trademark of the Corporation for Digital Scholarship.

View file

@ -1,3 +0,0 @@
brandShorterName=Zotero
brandShortName=Zotero
brandFullName=Zotero

View file

@ -1,3 +0,0 @@
locale branding en-US chrome/en-US/locale/branding/
content branding chrome/branding/content/
skin browser preferences chrome/skin/

View file

@ -1,104 +0,0 @@
let { CommandLineOptions, TestOptions } = ChromeUtils.importESModule("chrome://zotero/content/modules/commandLineOptions.mjs");
// Only allow BrowserContentHandler to open a new window if this is the initial launch,
// meaning our CLH isn't registered yet.
if (cmdLine.state != Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
cmdLine.preventDefault = true;
}
// Force debug output to window
if (cmdLine.handleFlag("ZoteroDebug", false)) {
CommandLineOptions.forceDebugLog = 2;
}
// Force debug output to text console
else if (cmdLine.handleFlag("ZoteroDebugText", false)) {
CommandLineOptions.forceDebugLog = 1;
}
// Pressing Ctrl-C via the terminal is interpreted as a crash, and after three crashes
// Firefox starts up in automatic safe mode (troubleshooting mode). To avoid this, we clear the crash
// counter when using one of the debug-logging flags, which generally imply terminal usage.
if (CommandLineOptions.forceDebugLog) {
Services.prefs.getBranch("toolkit.startup.").clearUserPref("recent_crashes");
}
CommandLineOptions.forceDataDir = cmdLine.handleFlagWithParam("datadir", false);
// Set here, to be acted upon in xpcom/commandLineHandler.js
CommandLineOptions.file = cmdLine.handleFlagWithParam("file", false);
CommandLineOptions.url = cmdLine.handleFlagWithParam("url", false);
if (CommandLineOptions.url) {
CommandLineOptions.url = cmdLine.resolveURI(CommandLineOptions.url);
}
var processTestOptions = false;
if (cmdLine.handleFlag("ZoteroTest", false)) {
CommandLineOptions.test = true;
processTestOptions = true;
}
if (cmdLine.handleFlag("ZoteroAutomatedTest", false)) {
CommandLineOptions.automatedTest = true;
}
if (cmdLine.handleFlag("ZoteroSkipBundledFiles", false)) {
CommandLineOptions.skipBundledFiles = true;
}
if (processTestOptions) {
TestOptions.tests = cmdLine.handleFlagWithParam("test", false);
TestOptions.noquit = cmdLine.handleFlag("noquit", false);
TestOptions.makeTestData = cmdLine.handleFlag("makeTestData", false);
TestOptions.noquit = !TestOptions.makeTestData && TestOptions.noquit;
TestOptions.runTests = !TestOptions.makeTestData;
TestOptions.bail = cmdLine.handleFlag("bail", false);
TestOptions.startAt = cmdLine.handleFlagWithParam("startAtTestFile", false);
TestOptions.stopAt = cmdLine.handleFlagWithParam("stopAtTestFile", false);
TestOptions.shard = cmdLine.handleFlagWithParam("shard", false);
TestOptions.grep = cmdLine.handleFlagWithParam("grep", false);
TestOptions.timeout = cmdLine.handleFlagWithParam("ZoteroTestTimeout", false);
TestOptions.retries = cmdLine.handleFlagWithParam("retries", false) || 0;
Services.ww.openWindow(
null,
"chrome://zotero-unit/content/runtests.html",
"_blank",
"chrome,dialog=no,all",
Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray)
);
cmdLine.preventDefault = true;
}
if (cmdLine.handleFlag("debugger", false)) {
(async function () {
try {
let portOrPath = Services.prefs.getBranch('').getIntPref('devtools.debugger.remote-port');
const { DevToolsLoader } = ChromeUtils.importESModule(
"resource://devtools/shared/loader/Loader.sys.mjs"
);
const loader = new DevToolsLoader({
freshCompartment: true,
});
const { DevToolsServer } = loader.require("devtools/server/devtools-server");
const { SocketListener } = loader.require("devtools/shared/security/socket");
if (DevToolsServer.initialized) {
dump("Debugger server already initialized\n\n");
return;
}
DevToolsServer.init();
DevToolsServer.registerAllActors();
DevToolsServer.allowChromeProcess = true;
const socketOptions = { portOrPath };
const listener = new SocketListener(DevToolsServer, socketOptions);
await listener.open();
if (!DevToolsServer.listeningSockets) {
throw new Error("No listening sockets");
}
dump(`Debugger server started on ${portOrPath}\n\n`);
}
catch (e) {
dump(e + "\n\n");
Components.utils.reportError(e);
}
})();
}

View file

@ -1,242 +0,0 @@
/*
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Firefox Preferences System.
#
# The Initial Developer of the Original Code is
# Ben Goodger.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
# Kevin Gerich <webmail@kmgerich.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
*/
.windowDialog {
padding: 12px;
font: -moz-dialog;
}
.paneSelector {
list-style-image: url("chrome://browser/skin/preferences/Options.png");
}
/* ----- GENERAL BUTTON ----- */
radio[pane=paneGeneral],
radio[pane=paneMain] {
-moz-image-region: rect(0px, 32px, 32px, 0px);
}
/* ----- TABS BUTTON ----- */
radio[pane=paneTabs] {
-moz-image-region: rect(0px, 64px, 32px, 32px);
}
/* ----- CONTENT BUTTON ----- */
radio[pane=paneContent] {
-moz-image-region: rect(0px, 96px, 32px, 64px);
}
/* ----- APPLICATIONS BUTTON ----- */
radio[pane=paneApplications] {
-moz-image-region: rect(0px, 128px, 32px, 96px);
}
/* ----- PRIVACY BUTTON ----- */
radio[pane=panePrivacy] {
-moz-image-region: rect(0px, 160px, 32px, 128px);
}
/* ----- SECURITY BUTTON ----- */
radio[pane=paneSecurity] {
-moz-image-region: rect(0px, 192px, 32px, 160px);
}
/* ----- ADVANCED BUTTON ----- */
radio[pane=paneAdvanced] {
-moz-image-region: rect(0px, 224px, 32px, 192px);
}
/* ----- SYNC BUTTON ----- */
radio[pane=paneSync] {
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
}
/* ----- APPLICATIONS PREFPANE ----- */
#BrowserPreferences[animated="true"] #handlersView {
height: 25em;
}
#BrowserPreferences[animated="false"] #handlersView {
-moz-box-flex: 1;
}
description {
font: small-caption;
font-weight: normal;
line-height: 1.3em;
margin-bottom: 4px !important;
}
prefpane .groupbox-body {
-moz-appearance: none;
padding: 8px 4px 4px 4px;
}
#paneTabs > groupbox {
margin: 0;
}
#tabPrefsBox {
margin: 12px 4px;
}
prefpane .groupbox-title {
background: url("chrome://global/skin/50pct_transparent_grey.png") repeat-x bottom left;
margin-bottom: 4px;
}
tabpanels {
padding: 20px 7px 7px;
}
caption {
-moz-padding-start: 5px;
padding-top: 4px;
padding-bottom: 2px;
}
#paneMain description,
#paneContent description,
#paneAdvanced description,
#paneSecurity description {
font: -moz-dialog;
}
#paneContent {
padding-top: 8px;
}
#paneContent row {
padding: 2px 4px;
-moz-box-align: center;
}
#popupPolicyRow,
#enableSoftwareInstallRow,
#enableImagesRow {
margin-bottom: 4px !important;
padding-bottom: 4px !important;
border-bottom: 1px solid #ccc;
}
#browserUseCurrent,
#browserUseBookmark,
#browserUseBlank {
margin-top: 10px;
}
#advancedPrefs {
margin: 0 8px;
}
#privacyPrefs {
padding: 0 4px;
}
#privacyPrefs > tabpanels {
padding: 18px 10px 10px;
}
#OCSPDialogPane {
font: message-box !important;
}
/**
* Privacy Pane
*/
/* styles for the link elements copied from .text-link in global.css */
.inline-link {
color: LinkText;
text-decoration: underline;
}
.inline-link:not(:focus) {
outline: 1px dotted transparent;
}
/**
* Update Preferences
*/
#autoInstallOptions {
-moz-margin-start: 20px;
}
.updateControls {
-moz-margin-start: 10px;
}
/**
* Clear Private Data
*/
#SanitizeDialogPane > groupbox {
margin-top: 0;
}
/* ----- SYNC PANE ----- */
#syncDesc {
padding: 0 8em;
}
#accountCaptionImage {
list-style-image: url("chrome://mozapps/skin/profile/profileicon.png");
}
#syncAddDeviceLabel {
margin-top: 1em;
margin-bottom: 1em;
}
#syncEnginesList {
height: 10em;
}

View file

@ -1 +0,0 @@
en-US,ar,bg-BG,br,ca-AD,cs-CZ,da-DK,de,el-GR,en-AU,en-CA,en-GB,en-NZ,es-ES,et-EE,eu-ES,fa,fi-FI,fr-FR,gl-ES,hu-HU,id-ID,is-IS,it-IT,ja-JP,km,ko-KR,lt-LT,nb-NO,nl-NL,pl-PL,pt-BR,pt-PT,ro-RO,ru-RU,sk-SK,sl-SI,sr-RS,sv-SE,ta,th-TH,tr-TR,uk-UA,vi-VN,zh-CN,zh-TW

View file

@ -1,172 +0,0 @@
// We only want a single window, I think
pref("toolkit.singletonWindowType", "navigator:browser");
// For debugging purposes, show errors in console by default
pref("javascript.options.showInConsole", true);
// Don't retrieve unrequested links when performing standalone translation
pref("network.prefetch-next", false);
// Don't make DNS requests for links in snapshots
pref("network.dns.disablePrefetch", true);
// Don't open TCP connection when clicking on a link in a snapshot
pref("network.http.speculative-parallel-limit", 0);
// Let operations run as long as necessary
pref("dom.max_chrome_script_run_time", 0);
pref("intl.locale.requested", '');
pref("intl.regional_prefs.use_os_locales", false);
// Fix error initializing login manager after this was changed in Firefox 57
// Could also disable this with MOZ_LOADER_SHARE_GLOBAL, supposedly
pref("jsloader.shareGlobal", false);
// Needed due to https://bugzilla.mozilla.org/show_bug.cgi?id=1181977
pref("browser.hiddenWindowChromeURL", "chrome://zotero/content/standalone/hiddenWindow.xhtml");
// Use basicViewer for opening new DOM windows from content (for TinyMCE)
pref("browser.chromeURL", "chrome://zotero/content/standalone/basicViewer.xhtml");
// We need these to get the save dialog working with contentAreaUtils.js
pref("browser.download.useDownloadDir", false);
pref("browser.download.manager.showWhenStarting", false);
pref("browser.download.folderList", 1);
// Don't show add-on selection dialog
pref("extensions.shownSelectionUI", true);
pref("extensions.autoDisableScope", 11);
pref("network.protocol-handler.expose-all", false);
pref("network.protocol-handler.expose.zotero", true);
pref("network.protocol-handler.expose.http", true);
pref("network.protocol-handler.expose.https", true);
// Never go offline
pref("offline.autoDetect", false);
pref("network.manage-offline-status", false);
// Without this, we will throw up dialogs if asked to translate strange pages
pref("browser.xul.error_pages.enabled", true);
// Without this, scripts may decide to open popups
pref("dom.disable_open_during_load", true);
// Don't show security warning. The "warn_viewing_mixed" warning just lets the user know that some
// page elements were loaded over an insecure connection. This doesn't matter if all we're doing is
// scraping the page, since we don't provide any information to the site.
pref("security.warn_viewing_mixed", false);
// Temporarily allow plugins to load their own scripts from jar:file: URIs with loadSubScript()
// without passing allowUnsafeURL (Mozilla bug 1974213). This will be disabled once plugins have
// had time to update.
pref("security.allow_unsafe_subscript_loads", true);
// We do need synchronous XHR
pref("network.xhr.block_sync_system_requests", false);
// Preferences for add-on discovery
pref("extensions.getAddons.cache.enabled", false);
//pref("extensions.getAddons.maxResults", 15);
//pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/search/guid:%IDS%?src=thunderbird&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
//pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/%APP%/search?q=%TERMS%");
//pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%?src=thunderbird");
//pref("extensions.webservice.discoverURL", "https://www.zotero.org/support/plugins");
// Check Windows certificate store for custom CAs
pref("security.enterprise_roots.enabled", true);
// Disable add-on signature checking with unbranded Firefox build
pref("xpinstall.signatures.required", false);
// Allow legacy extensions (though this might not be necessary)
pref("extensions.legacy.enabled", true);
// Allow installing XPIs from any host
pref("xpinstall.whitelist.required", false);
// Allow installing XPIs when using a custom CA
pref("extensions.install.requireBuiltInCerts", false);
pref("extensions.update.requireBuiltInCerts", false);
// Don't connect to the Mozilla extensions blocklist
pref("extensions.blocklist.enabled", false);
// Avoid warning in console when opening Tools -> Add-ons
pref("extensions.getAddons.link.url", "");
// Disable places
pref("places.history.enabled", false);
// Probably not used, but prevent an error in the console
pref("app.support.baseURL", "https://www.zotero.org/support/");
// Disable Telemetry, Health Report, error reporting, and remote settings
pref("toolkit.telemetry.unified", false);
pref("toolkit.telemetry.enabled", false);
pref("datareporting.policy.dataSubmissionEnabled", false);
pref("toolkit.crashreporter.enabled", false);
pref("extensions.remoteSettings.disabled", true);
pref("extensions.update.url", "");
// Don't try to load the "Get Add-ons" tab on first load of Add-ons window
pref("extensions.ui.lastCategory", "addons://list/extension");
// Don't show "Using experimental APIs requires a privileged add-on" warning
pref("extensions.experiments.enabled", true);
// Not set on Windows in Firefox anymore since it's a per-installation pref,
// but we override that in fetch_xulrunner
pref("app.update.auto", true);
// URL user can browse to manually if for some reason all update installation
// attempts fail.
pref("app.update.url.manual", "https://www.zotero.org/download");
// A default value for the "More information about this update" link
// supplied in the "An update is available" page of the update wizard.
pref("app.update.url.details", "https://www.zotero.org/support/changelog");
// Interval: Time between checks for a new version (in seconds)
// default=1 day
pref("app.update.interval", 86400);
// The minimum delay in seconds for the timer to fire.
// default=2 minutes
pref("app.update.timerMinimumDelay", 120);
// Whether or not we show a dialog box informing the user that the update was
// successfully applied. This is off in Firefox by default since we show a
// upgrade start page instead! Other apps may wish to show this UI, and supply
// a whatsNewURL field in their brand.properties that contains a link to a page
// which tells users what's new in this new update.
// update channel for this build
pref("app.update.channel", "default");
// This should probably not be a preference that's used in toolkit....
pref("browser.preferences.instantApply", false);
// Allow elements to be displayed full-screen
pref("full-screen-api.enabled", true);
// Allow chrome access in DevTools
// This enables the input field in the Browser Console tool
pref("devtools.chrome.enabled", true);
// Default mousewheel action with Alt/Option is History Back/Forward in Firefox
// We don't have History navigation and users want to scroll the tree with Option
// key held down
pref("mousewheel.with_alt.action", 1);
// Use the system print dialog instead of the new tab-based print dialog in Firefox
pref("print.prefer_system_dialog", true);
// Disable libvpx decoding/encoding
pref("media.webm.enabled", false);
pref("media.encoder.webm.enabled", false);
pref("media.mediasource.webm.enabled", false);
pref("media.mediasource.webm.audio.enabled", false);
pref("media.mediasource.vp9.enabled", false);
pref("media.ffvpx.enabled", false);
pref("media.ffvpx.mp3.enabled", false);
pref("media.rdd-vpx.enabled", false);
pref("media.rdd-ffvpx.enabled", false);
pref("media.utility-ffvpx.enabled", false);
// Allow collectionTree scrolling when Control is highlighting collections on win
pref("mousewheel.with_control.action", 1);

View file

@ -1,188 +0,0 @@
%if 0
/*
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Firefox Preferences System.
#
# The Initial Developer of the Original Code is
# Ben Goodger.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
*/
%endif
/* Global Styles */
#BrowserPreferences radio[pane] {
list-style-image: url("chrome://browser/skin/preferences/Options.png");
}
radio[pane=paneMain] {
-moz-image-region: rect(0px, 32px, 32px, 0px)
}
radio[pane=paneTabs] {
-moz-image-region: rect(0px, 64px, 32px, 32px)
}
radio[pane=paneContent] {
-moz-image-region: rect(0px, 96px, 32px, 64px)
}
radio[pane=paneApplications] {
-moz-image-region: rect(0px, 128px, 32px, 96px)
}
radio[pane=panePrivacy] {
-moz-image-region: rect(0px, 160px, 32px, 128px)
}
radio[pane=paneSecurity] {
-moz-image-region: rect(0px, 192px, 32px, 160px)
}
radio[pane=paneAdvanced] {
-moz-image-region: rect(0px, 224px, 32px, 192px)
}
%ifdef MOZ_SERVICES_SYNC
radio[pane=paneSync] {
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png") !important;
}
%endif
/* Applications Pane */
#BrowserPreferences[animated="true"] #handlersView {
height: 25em;
}
#BrowserPreferences[animated="false"] #handlersView {
-moz-box-flex: 1;
}
/* Privacy Pane */
/* styles for the link elements copied from .text-link in global.css */
.inline-link {
color: LinkText;
text-decoration: underline;
}
.inline-link:not(:focus) {
outline: 1px dotted transparent;
}
/* Modeless Window Dialogs */
.windowDialog,
.windowDialog prefpane {
padding: 0px;
}
.contentPane {
margin: 9px 8px 5px 8px;
}
.actionButtons {
margin: 0px 3px 6px 3px !important;
}
/* Cookies Manager */
#cookiesChildren::-moz-tree-image(domainCol) {
width: 16px;
height: 16px;
margin: 0px 2px;
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
#paneApplications {
margin-left: 4px;
margin-right: 4px;
padding-left: 0;
padding-right: 0;
}
#linksOpenInBox {
margin-top: 5px;
}
#paneAdvanced {
padding-bottom: 10px;
}
#advancedPrefs {
margin-left: 0;
margin-right: 0;
}
#cookiesChildren::-moz-tree-image(domainCol, container) {
list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
}
#cookieInfoBox {
border: 1px solid ThreeDShadow;
border-radius: 0px;
margin: 4px;
padding: 0px;
}
/* bottom-most box containing a groupbox in a prefpane. Prevents the bottom
of the groupbox from being cutoff */
.bottomBox {
padding-bottom: 4px;
}
/**
* Clear Private Data
*/
#SanitizeDialogPane > groupbox {
margin-top: 0;
}
%ifdef MOZ_SERVICES_SYNC
/* Sync Pane */
#syncDesc {
padding: 0 8em;
}
#accountCaptionImage {
list-style-image: url("chrome://mozapps/skin/profile/profileicon.png");
}
#syncAddDeviceLabel {
margin-top: 1em;
margin-bottom: 1em;
}
#syncEnginesList {
height: 10em;
}
%endif

View file

@ -1,4 +0,0 @@
; This file is in the UTF-8 encoding
[Strings]
Title=Zotero Update
Info=Zotero is installing your updates and will start in a few moments…

View file

@ -1,178 +0,0 @@
/*
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Firefox Preferences System.
#
# The Initial Developer of the Original Code is
# Ben Goodger.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
*/
/* Global Styles */
#BrowserPreferences radio[pane] {
list-style-image: url("chrome://browser/skin/preferences/Options.png");
padding: 5px 3px 1px;
}
radio[pane=paneMain] {
-moz-image-region: rect(0, 32px, 32px, 0);
}
radio[pane=paneTabs] {
-moz-image-region: rect(0, 64px, 32px, 32px);
}
radio[pane=paneContent] {
-moz-image-region: rect(0, 96px, 32px, 64px);
}
radio[pane=paneApplications] {
-moz-image-region: rect(0, 128px, 32px, 96px);
}
radio[pane=panePrivacy] {
-moz-image-region: rect(0, 160px, 32px, 128px);
}
radio[pane=paneSecurity] {
-moz-image-region: rect(0, 192px, 32px, 160px);
}
radio[pane=paneAdvanced] {
-moz-image-region: rect(0, 224px, 32px, 192px);
}
%ifdef MOZ_SERVICES_SYNC
radio[pane=paneSync] {
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png") !important;
}
%endif
/* Applications Pane */
#BrowserPreferences[animated="true"] #handlersView {
height: 25em;
}
#BrowserPreferences[animated="false"] #handlersView {
-moz-box-flex: 1;
}
/* Privacy Pane */
/* styles for the link elements copied from .text-link in global.css */
.inline-link {
color: LinkText;
text-decoration: underline;
}
.inline-link:not(:focus) {
outline: 1px dotted transparent;
}
/* Modeless Window Dialogs */
.windowDialog,
.windowDialog prefpane {
padding: 0;
}
.contentPane {
margin: 9px 8px 5px;
}
.actionButtons {
margin: 0 3px 6px !important;
}
/* Cookies Manager */
#cookiesChildren::-moz-tree-image(domainCol) {
width: 16px;
height: 16px;
margin: 0 2px;
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png") !important;
}
#cookiesChildren::-moz-tree-image(domainCol, container) {
list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#cookiesChildren::-moz-tree-image(domainCol, container, open) {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
#cookieInfoBox {
border: 1px solid ThreeDShadow;
border-radius: 0;
margin: 4px;
padding: 0;
}
/* Advanced Pane */
/* Adding padding-bottom prevents the bottom of the tabpanel from being cutoff
when browser.preferences.animateFadeIn = true */
#advancedPrefs {
padding-bottom: 8px;
}
/* bottom-most box containing a groupbox in a prefpane. Prevents the bottom
of the groupbox from being cutoff */
.bottomBox {
padding-bottom: 4px;
}
%ifdef MOZ_SERVICES_SYNC
/* Sync Pane */
#syncDesc {
padding: 0 8em;
}
.syncGroupBox {
padding: 10px;
}
#accountCaptionImage {
list-style-image: url("chrome://mozapps/skin/profile/profileicon.png");
}
#syncAddDeviceLabel {
margin-top: 1em;
margin-bottom: 1em;
}
#syncEnginesList {
height: 11em;
}
%endif

File diff suppressed because it is too large Load diff

View file

@ -1,73 +0,0 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Version of Gecko to build with
GECKO_VERSION_MAC="153.3.0esr"
GECKO_VERSION_LINUX="153.3.0esr"
GECKO_VERSION_WIN="153.3.0esr"
RUST_VERSION=1.94.0
# URL prefix for custom builds of Firefox components
custom_components_url="https://download.zotero.org/dev/firefox-components/"
custom_components_hash_mac="6b586ed383a480fed9d9786ad18a6e6d90b140739e5e4491096df291ec887571"
custom_components_hash_win_x64="db7c9556bd0f9e36120069391ce66cc199ed1ec4e2fdd3b931f71e35b4586d2b"
custom_components_hash_win_arm64="fd0f44b38a471009ce1cdd926f7f48be43ed773dad350c62383b8a7235da8711"
custom_components_hash_win32="694944349fdfd91296f272a68ac5d03183f02d47fdaa7ba9f2b1c74747652aa2"
APP_NAME="Zotero"
APP_ID="zotero\@zotero.org"
# Whether to sign builds
SIGN=0
# OS X Developer ID certificate information
DEVELOPER_ID=F0F1FE48DB909B263AC51C8215374D87FDC12121
# Keychain and keychain password, if not building via the GUI
KEYCHAIN=""
KEYCHAIN_PASSWORD=""
NOTARIZATION_BUNDLE_ID=""
NOTARIZATION_USER=""
NOTARIZATION_TEAM_ID=""
NOTARIZATION_PASSWORD=""
# Name of a notarytool keychain profile (see `xcrun notarytool store-credentials`), e.g., for an
# App Store Connect API key -- used instead of the Apple ID settings above if set
NOTARIZATION_PROFILE=""
# Paths for Windows installer build
NSIS_DIR='C:\Program Files (x86)\NSIS\'
SIGNTOOL_DELAY=5
# Directory for unpacked binaries
STAGE_DIR="$DIR/staging"
# Directory for packed binaries
DIST_DIR="$DIR/dist"
SOURCE_REPO_URL="https://github.com/zotero/zotero"
S3_BUCKET="zotero-download"
S3_CI_ZIP_PATH="ci/client"
S3_DIST_PATH="client"
DEPLOY_HOST="deploy.zotero"
DEPLOY_PATH="www/www-production/public/download/client"
BUILD_PLATFORMS=""
NUM_INCREMENTALS=6
if [ "`uname`" = "Darwin" ]; then
shopt -s expand_aliases
fi
if [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
export WIN_NATIVE=1
else
export WIN_NATIVE=0
fi
# Make utilities (mar/mbsdiff) available in the path
PATH="$DIR/xulrunner/bin:$PATH"
if [ -f "$DIR/config-custom.sh" ]; then
. "$DIR/config-custom.sh"
fi
unset DIR

View file

@ -1,44 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
if [ -z "${1:-}" ]; then
echo "Usage: $0 x64|arm64" >&2
exit 1
fi
gecko_path=~/src/firefox
arch=$1
if [ ! -d "$gecko_path" ]; then
echo "$gecko_path is not a directory" >&2
exit 1
fi
# Install required Rust version
rustup toolchain install $RUST_VERSION
if [ $arch = "x64" ]; then
rust_target=x86_64
elif [ $arch = "arm64" ]; then
rust_target=aarch64
else
echo "Unknown architecture $arch" >&2
exit 1
fi
rustup target add $rust_target-unknown-linux-gnu
rm "$gecko_path/mozconfig"
touch "$gecko_path/mozconfig"
if [ $arch == "arm64" ]; then
echo "ac_add_options --target=aarch64-linux-gnu" >> "$gecko_path/mozconfig"
fi
cat "$SCRIPT_DIR/mozconfig" >> "$gecko_path/mozconfig"
cd "$gecko_path"
./mach build
./mach package

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon style="fill: #000000" points="13.863 2.73 13.027 1 2.137 1 2.137 3.8 2.137 3.921 8.822 3.921 1.289 13.233 2.137 15 13.863 15 13.863 12.142 13.863 12.021 6.448 12.021 13.863 2.73"/>
</svg>

Before

Width:  |  Height:  |  Size: 283 B

View file

@ -1,25 +0,0 @@
ac_add_options --enable-bootstrap
mk_add_options AUTOCLOBBER=1
# These don't all affect the stub, but they can't hurt, and we'll want them if
# we switch to custom XUL builds
ac_add_options MOZ_ENABLE_JS_DUMP=1
ac_add_options MOZ_ENABLE_FORKSERVER=
ac_add_options MOZ_TELEMETRY_REPORTING=
ac_add_options MOZ_DATA_REPORTING=
ac_add_options --disable-tests
ac_add_options --disable-debug
ac_add_options --disable-debug-symbols
ac_add_options --disable-webrtc
# This causes aarch64 build of 140.0esr to fail
#ac_add_options --disable-eme
export MOZILLA_OFFICIAL=1
export RELEASE_OR_BETA=1
MOZ_REQUIRE_SIGNING=
ac_add_options --enable-official-branding
# Build updater without MAR signature verification
ac_add_options --enable-unverified-updates

View file

@ -1,13 +0,0 @@
#!/bin/bash -e
#
# Run this to update the launcher file with the current path to the application icon
#
APPDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ -w "$APPDIR"/zotero.desktop ]; then
sed -i -e "s@^Icon=.*@Icon=$APPDIR/icons/icon128.png@" "$APPDIR"/zotero.desktop
else
echo "$APPDIR"/zotero.desktop is not writable
exit 1
fi

View file

@ -1,43 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR"
if [ $# -ne 1 ]; then
echo "Usage: $0 <firefox-version>"
exit 1
fi
version="$1"
for arch in x86_64 aarch64; do
package="firefox-${version}.en-US.linux-${arch}.tar.xz"
if [ $arch = 'aarch64' ]; then
arch="arm64"
fi
if [ ! -f "$package" ]; then
echo "Error: Package not found: $package"
exit 1
fi
echo "Extracting updater from $package..."
tar --extract --xz --file="$package" --to-stdout "firefox/updater" > "updater-$arch"
# Add missing RUNPATH to custom fx140 builds, which is missing for unclear reasons.
# Without this, the updater fails looking for libmozsqlite3.so.
patchelf --set-rpath '$ORIGIN' "updater-$arch"
done
echo "Creating updater.tar.xz..."
if [ `uname` = "Darwin" ]; then
tar=gtar
else
tar=tar
fi
$tar --owner=0 --group=0 --numeric-owner --no-xattrs --mode=0755 -cJvf updater.tar.xz updater-x86_64 updater-arm64
rm updater-x86_64 updater-arm64
echo "Done: updater.tar.xz created"

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a1f368bb36408d61d4b8f9dda561738686e626513346e138d55a0ef58055a6fc
size 123776

View file

@ -1,20 +0,0 @@
#!/bin/bash
# Increase open files limit
#
# Mozilla file functions (OS.File.move()/copy(), NetUtil.asyncFetch/asyncCopy()) can leave file
# descriptors open for a few seconds (even with an explicit inputStream.close() in the case of
# the latter), so a source installation that copies ~500 translators and styles (with fds for
# source and target) can exceed the default 1024 limit.
# Current hard-limit on Ubuntu 16.10 is 4096
ulimit -n 4096
# Allow profile downgrade for Zotero
export MOZ_ALLOW_DOWNGRADE=1
# Don't create dedicated profile (default-esr)
export MOZ_LEGACY_PROFILES=1
# Use wayland backend when available
export MOZ_ENABLE_WAYLAND=1
CALLDIR="$(dirname "$(readlink -f "$0")")"
"$CALLDIR/zotero-bin" -app "$CALLDIR/app/application.ini" "$@"

View file

@ -1,9 +0,0 @@
[Desktop Entry]
Name=Zotero
Exec=bash -c "$(dirname $(realpath $(echo %k | sed -e 's/^file:\\/\\///')))/zotero -url %U"
Icon=zotero.ico
Type=Application
Terminal=false
Categories=Office;
MimeType=text/plain;x-scheme-handler/zotero;application/x-research-info-systems;text/x-research-info-systems;text/ris;application/x-endnote-refer;application/x-inst-for-Scientific-info;application/mods+xml;application/rdf+xml;application/x-bibtex;text/x-bibtex;application/marc;application/vnd.citationstyles.style+xml
X-GNOME-SingleWindow=true

View file

@ -1,204 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<!-- Import formats -->
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>ris</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-research-info-systems</string>
<string>text/x-research-info-systems</string>
<string>text/ris</string>
<string>ris</string>
<string>application/x-endnote-refer</string>
</array>
<key>CFBundleTypeName</key>
<string>Research Information Systems Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>ciw</string>
<string>isi</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-inst-for-Scientific-info</string>
</array>
<key>CFBundleTypeName</key>
<string>ISI Common Export Format Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mods</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/mods+xml</string>
</array>
<key>CFBundleTypeName</key>
<string>Metadata Object Description Schema Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>rdf</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/rdf+xml</string>
</array>
<key>CFBundleTypeName</key>
<string>Resource Description Framework Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>bib</string>
<string>bibtex</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-bibtex</string>
<string>text/x-bibtex</string>
</array>
<key>CFBundleTypeName</key>
<string>BibTeX Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mrc</string>
<string>marc</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/marc</string>
</array>
<key>CFBundleTypeName</key>
<string>MARC Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<!-- Citation styles -->
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>csl</string>
<string>csl.txt</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/vnd.citationstyles.style+xml</string>
</array>
<key>CFBundleTypeName</key>
<string>CSL Citation Style</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<!-- Hopefully, we don't become the default app for these -->
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>xml</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeName</key>
<string>XML Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>txt</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeName</key>
<string>Text File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>zotero</string>
<key>CFBundleGetInfoString</key>
<string>Zotero {{VERSION}}, © 2006-2018 Contributors</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>org.zotero.zotero</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Zotero</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>{{VERSION_NUMERIC}}</string>
<key>CFBundleSignature</key>
<string>ZOTR</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<!--<key>CFBundleURLIconFile</key>
<string>document.icns</string>-->
<key>CFBundleURLName</key>
<string>zotero URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>zotero</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>{{VERSION_NUMERIC}}</string>
<key>NSAppleScriptEnabled</key>
<true/>
<key>CGDisableCoalescedUpdates</key>
<true/>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>10.9.0</string>
</dict>
</plist>

View file

@ -1 +0,0 @@
APPLZOTR

Binary file not shown.

View file

@ -1,93 +0,0 @@
#!/bin/bash
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
fx_app_name=Firefox.app
# Get Mozilla source directory from command line
if [ -z "${1:-}" ]; then
echo "Usage: $0 /path/to/firefox" >&2
exit 1
fi
GECKO_PATH="$(cd "$1" && pwd)"
mach=$GECKO_PATH/mach
BUILD_DIR=`mktemp -d`
function cleanup {
rm -rf $BUILD_DIR
}
trap cleanup EXIT
set -x
export MOZ_BUILD_DATE=`date "+%Y%m%d%H%M%S"`
# Install required Rust version
rustup toolchain install $RUST_VERSION
rustup target add --toolchain $RUST_VERSION aarch64-apple-darwin x86_64-apple-darwin
rustup default $RUST_VERSION
cp "$SCRIPT_DIR/mozconfig" "$GECKO_PATH"
# Build Firefox for Intel and Apple Silicon
export Z_ARCH=x64
$mach build
$mach package
export Z_ARCH=aarch64
$mach build
$mach package
cd $BUILD_DIR
# Unify into Universal build
# From https://searchfox.org/mozilla-central/rev/97c902e8f92b15dc63eb584bfc594ecb041242a4/taskcluster/scripts/misc/unify.sh
for i in x86_64 aarch64; do
$mach python -m mozbuild.action.unpack_dmg "$GECKO_PATH"/obj-$i-apple-darwin/dist/*.dmg $i
done
mv x86_64 x64
$mach python "$GECKO_PATH/toolkit/mozapps/installer/unify.py" x64/*.app aarch64/*.app
cp x64/$fx_app_name/Contents/MacOS/firefox zotero
xz zotero
mv zotero.xz "$APP_ROOT_DIR/mac/zotero.xz"
# Package custom Firefox components for fetch_xulrunner. Add XUL to this list if needed.
# Upload the archive to ${custom_components_url}mac/ and set custom_components_hash_mac
# in config.sh to the hash printed below.
custom_components=(libmozglue.dylib)
mkdir MacOS
for component in "${custom_components[@]}"; do
cp "x64/$fx_app_name/Contents/MacOS/$component" MacOS/
done
tar cfJ MacOS.tar.xz MacOS
components_hash=$(shasum -a 256 MacOS.tar.xz | cut -d ' ' -f 1)
components_file="$APP_ROOT_DIR/mac/$components_hash.tar.xz"
mv MacOS.tar.xz "$components_file"
echo "Custom Firefox components: $components_file"
echo "custom_components_hash_mac=\"$components_hash\""
# Save a copy of ChannelPrefs.framework
#
# Frameworks need to be signed, and we can't sign source builds, so store a signed copy with
# 'default' replaced with 'source'. Deployed builds will then replace 'source' with
# 'release'/'beta'/'dev'.
rm -rf "$APP_ROOT_DIR/mac/ChannelPrefs.framework"
cp -R x64/$fx_app_name/Contents/Frameworks/ChannelPrefs.framework "$APP_ROOT_DIR/mac/ChannelPrefs.framework"
pushd "$APP_ROOT_DIR/mac"
./set-channel-prefs-channel ChannelPrefs.framework/ChannelPrefs default source
security -v unlock-keychain -p "$KEYCHAIN_PASSWORD" ~/Library/Keychains/$KEYCHAIN.keychain-db
/usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID" ChannelPrefs.framework
/usr/bin/codesign -vvvv ChannelPrefs.framework
tar cfvJ ChannelPrefs.framework.tar.xz ChannelPrefs.framework
rm -rf ChannelPrefs.framework
popd
# Uncomment to build updater
#cd x64/$fx_app_name/Contents/MacOS/
#"$APP_ROOT_DIR/mac/updater_renamer"
#cat updater.app/Contents/Resources/English.lproj/InfoPlist.strings
#tar cfvJ "$APP_ROOT_DIR/mac/updater.tar.xz" updater.app/

View file

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!--
Entitlements to apply during codesigning of production builds.
-->
<plist version="1.0">
<dict>
<!-- Firefox needs to create executable pages (without MAP_JIT) -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
<!-- Allow loading third party libraries. Needed for Flash and CDMs -->
<!-- Disabled for Zotero -->
<key>com.apple.security.cs.disable-library-validation</key><false/>
<!-- Firefox needs to access the microphone on sites the user allows -->
<!-- Disabled for Zotero -->
<key>com.apple.security.device.audio-input</key><false/>
<!-- Firefox needs to access the camera on sites the user allows -->
<!-- Disabled for Zotero -->
<key>com.apple.security.device.camera</key><false/>
<!-- Firefox needs to access the location on sites the user allows -->
<!-- Disabled for Zotero -->
<key>com.apple.security.personal-information.location</key><false/>
<!-- For SmartCardServices(7) -->
<!-- Disabled for Zotero -->
<key>com.apple.security.smartcard</key><false/>
<!-- Added for Zotero to control Word and bring windows to the front -->
<key>com.apple.security.automation.apple-events</key><true/>
</dict>
</plist>

View file

@ -1,35 +0,0 @@
if [ "$Z_ARCH" == "x64" ]; then
ac_add_options --target=x86_64-apple-darwin
elif [ "$Z_ARCH" == "aarch64" ]; then
ac_add_options --target=aarch64-apple-darwin
fi
ac_add_options --enable-bootstrap
ac_add_options --with-macos-sdk=$HOME/src/MacOSX26.5.sdk
mk_add_options AUTOCLOBBER=1
# These don't all affect the stub, but they can't hurt, and we'll want them if
# we switch to custom XUL builds
ac_add_options MOZ_ENABLE_JS_DUMP=1
ac_add_options MOZ_ENABLE_FORKSERVER=
ac_add_options MOZ_TELEMETRY_REPORTING=
ac_add_options MOZ_DATA_REPORTING=
ac_add_options --disable-tests
ac_add_options --disable-debug
ac_add_options --disable-debug-symbols
ac_add_options --disable-webrtc
ac_add_options --disable-eme
export MOZILLA_OFFICIAL=1
export RELEASE_OR_BETA=1
MOZ_REQUIRE_SIGNING=
# Official builds require source info, which Mozilla only detects automatically from
# Mercurial checkouts
export MOZ_SOURCE_REPO=https://github.com/mozilla-firefox/firefox
export MOZ_SOURCE_CHANGESET=$(git -C "$topsrcdir" rev-parse HEAD)
ac_add_options --enable-official-branding
# Build updater without MAR signature verification
ac_add_options --enable-unverified-updates

File diff suppressed because it is too large Load diff

View file

@ -1,242 +0,0 @@
diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -193,19 +193,31 @@ static bool HasFlag(int argc, char* argv[], const char* s) {
}
return false;
}
#endif
constinit Bootstrap::UniquePtr gBootstrap;
static int do_main(int argc, char* argv[], char* envp[]) {
+ // Allow profile downgrade for Zotero
+ setenv("MOZ_ALLOW_DOWNGRADE", "1", 1);
+ // Don't create dedicated profile (default-esr)
+ setenv("MOZ_LEGACY_PROFILES", "1", 1);
+
// Allow firefox.exe to launch XULRunner apps via -app <application.ini>
// Note that -app must be the *first* argument.
- const char* appDataFile = getenv("XUL_APP_FILE");
+ UniqueFreePtr<char> iniPath = BinaryPath::GetApplicationIni();
+ if (!iniPath) {
+ Output("Couldn't find application.ini.\n");
+ return 255;
+ }
+ char *appDataFile = iniPath.get();
+
+
if ((!appDataFile || !*appDataFile) && (argc > 1 && IsFlag(argv[1], "app"))) {
if (argc == 2) {
Output("Incorrect number of arguments passed to -app");
return 255;
}
appDataFile = argv[2];
char appEnv[MAXPATHLEN];
diff --git a/xpcom/build/BinaryPath.h b/xpcom/build/BinaryPath.h
--- a/xpcom/build/BinaryPath.h
+++ b/xpcom/build/BinaryPath.h
@@ -126,16 +126,56 @@ class BinaryPath {
} else {
rv = NS_ERROR_FAILURE;
}
CFRelease(executableURL);
return rv;
}
+ static nsresult GetApplicationIni(char aResult[MAXPATHLEN])
+ {
+ // Works even if we're not bundled.
+ CFBundleRef appBundle = CFBundleGetMainBundle();
+ if (!appBundle) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFURLRef iniURL = CFBundleCopyResourceURL(appBundle, CFSTR("application.ini"),
+ NULL, CFSTR("app"));
+ if (!iniURL) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ if (CFURLGetFileSystemRepresentation(iniURL, false, (UInt8*)aResult,
+ MAXPATHLEN)) {
+ // Sanitize path in case the app was launched from Terminal via
+ // './firefox' for example.
+ size_t readPos = 0;
+ size_t writePos = 0;
+ while (aResult[readPos] != '\0') {
+ if (aResult[readPos] == '.' && aResult[readPos + 1] == '/') {
+ readPos += 2;
+ } else {
+ aResult[writePos] = aResult[readPos];
+ readPos++;
+ writePos++;
+ }
+ }
+ aResult[writePos] = '\0';
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ CFRelease(iniURL);
+ return rv;
+ }
+
#elif defined(ANDROID)
static nsresult Get(char aResult[MAXPATHLEN]) {
// On Android, we use the MOZ_ANDROID_LIBDIR variable that is set by the
// Java bootstrap code.
const char* libDir = getenv("MOZ_ANDROID_LIBDIR");
if (!libDir) {
return NS_ERROR_FAILURE;
}
@@ -281,16 +321,29 @@ class BinaryPath {
if (NS_FAILED(Get(path))) {
return nullptr;
}
UniqueFreePtr<char> result;
result.reset(strdup(path));
return result;
}
+#if defined(XP_MACOSX)
+ static UniqueFreePtr<char> GetApplicationIni()
+ {
+ char path[MAXPATHLEN];
+ if (NS_FAILED(GetApplicationIni(path))) {
+ return nullptr;
+ }
+ UniqueFreePtr<char> result;
+ result.reset(strdup(path));
+ return result;
+ }
+#endif
+
#ifdef MOZILLA_INTERNAL_API
static nsresult GetFile(nsIFile** aResult) {
nsCOMPtr<nsIFile> lf;
# ifdef XP_WIN
wchar_t exePath[MAXPATHLEN];
nsresult rv = GetW(exePath);
# else
char exePath[MAXPATHLEN];
diff --git a/mozglue/build/AudioQueueSanitizer.cpp b/mozglue/build/AudioQueueSanitizer.cpp
new file mode 100644
index 000000000000..9efa3255c472
--- /dev/null
+++ b/mozglue/build/AudioQueueSanitizer.cpp
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Work around a use-after-free in the macOS 27 system speech synthesizer.
+//
+// When an utterance finishes, the AudioQueue player in the private
+// TextToSpeech framework (used by both NSSpeechSynthesizer and
+// AVSpeechSynthesizer) enqueues a short de-click ramp that starts from a
+// sample read out of a buffer it has already freed. With the system
+// allocator, which zeroes freed memory, that sample is 0 and the ramp is
+// silent. mozjemalloc poisons freed memory with 0xE5 instead, which reads
+// as -1.36e23 in float32, so every utterance ends with a ~10 ms full-scale
+// burst.
+//
+// We can't fix the framework, so we interpose AudioQueueEnqueueBuffer and
+// zero any float samples that are non-finite or far outside the nominal
+// [-1, 1] range before they reach CoreAudio. Real audio never gets close
+// to kMaxSampleMagnitude, so this only ever affects garbage.
+//
+// dyld only honors __interpose tuples in images loaded at launch, which is
+// why this needs to be in mozglue rather than in XUL (loaded with dlopen)
+// alongside OSXSpeechSynthesizerService.
+
+#include <AudioToolbox/AudioToolbox.h>
+#include <math.h>
+
+namespace {
+
+constexpr float kMaxSampleMagnitude = 16.0f;
+
+void SanitizeBuffer(AudioQueueRef aQueue, AudioQueueBufferRef aBuffer) {
+ AudioStreamBasicDescription format;
+ UInt32 size = sizeof(format);
+ if (AudioQueueGetProperty(aQueue, kAudioQueueProperty_StreamDescription,
+ &format, &size) != noErr) {
+ return;
+ }
+ if (format.mFormatID != kAudioFormatLinearPCM ||
+ !(format.mFormatFlags & kAudioFormatFlagIsFloat) ||
+ format.mBitsPerChannel != 32) {
+ return;
+ }
+
+ float* samples = static_cast<float*>(aBuffer->mAudioData);
+ UInt32 count = aBuffer->mAudioDataByteSize / sizeof(float);
+ for (UInt32 i = 0; i < count; i++) {
+ // Written so that NaN also fails the comparison.
+ if (!(fabsf(samples[i]) <= kMaxSampleMagnitude)) {
+ samples[i] = 0.0f;
+ }
+ }
+}
+
+OSStatus SanitizingEnqueueBuffer(
+ AudioQueueRef aQueue, AudioQueueBufferRef aBuffer, UInt32 aNumPackets,
+ const AudioStreamPacketDescription* aPacketDescs) {
+ SanitizeBuffer(aQueue, aBuffer);
+ return AudioQueueEnqueueBuffer(aQueue, aBuffer, aNumPackets, aPacketDescs);
+}
+
+OSStatus SanitizingEnqueueBufferWithParameters(
+ AudioQueueRef aQueue, AudioQueueBufferRef aBuffer, UInt32 aNumPackets,
+ const AudioStreamPacketDescription* aPacketDescs, UInt32 aTrimFrames,
+ UInt32 aTrimFramesAtEnd, UInt32 aNumParamValues,
+ const AudioQueueParameterEvent* aParamValues,
+ const AudioTimeStamp* aStartTime, AudioTimeStamp* aActualStartTime) {
+ SanitizeBuffer(aQueue, aBuffer);
+ return AudioQueueEnqueueBufferWithParameters(
+ aQueue, aBuffer, aNumPackets, aPacketDescs, aTrimFrames,
+ aTrimFramesAtEnd, aNumParamValues, aParamValues, aStartTime,
+ aActualStartTime);
+}
+
+struct Interpose {
+ const void* mReplacement;
+ const void* mReplacee;
+};
+
+__attribute__((used, section("__DATA,__interpose"))) const Interpose
+ kInterposes[] = {
+ {reinterpret_cast<const void*>(&SanitizingEnqueueBuffer),
+ reinterpret_cast<const void*>(&AudioQueueEnqueueBuffer)},
+ {reinterpret_cast<const void*>(&SanitizingEnqueueBufferWithParameters),
+ reinterpret_cast<const void*>(
+ &AudioQueueEnqueueBufferWithParameters)},
+};
+
+} // namespace
diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build
index f8f01e999e54..111fe5af1b0d 100644
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -41,6 +41,14 @@ if CONFIG["OS_TARGET"] == "WINNT":
"winmm.dll",
]
+if CONFIG["OS_TARGET"] == "Darwin" and FORCE_SHARED_LIB:
+ SOURCES += [
+ "AudioQueueSanitizer.cpp",
+ ]
+ OS_LIBS += [
+ "-framework AudioToolbox",
+ ]
+
if CONFIG["MOZ_WIDGET_TOOLKIT"]:
if CONFIG["MOZ_MEMORY"] and FORCE_SHARED_LIB:
pass

File diff suppressed because it is too large Load diff

View file

@ -1,69 +0,0 @@
#!/bin/bash
# Replace channel in ChannelPrefs, with appropriate null-byte padding
#
# ChannelPrefs.framework needs to be signed, which we can't do during normal source builds, so we
# use this to replace 'default' in our custom framework with 'source\0' and store that, and then
# when making real builds we replace 'source\0' with 'release'/'beta'/'dev'/'test' (padded to 7 chars)
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
if [ -z "${1:-}" ] || [ -z "${2:-}" ] || [ -z "${3:-}" ]; then
echo "Usage: $0 /path/to/ChannelPrefs.framework/ChannelPrefs from_channel to_channel" >&2
exit 1
fi
binary=$1
from_channel=$2
to_channel=$3
# `strings` has a 4-character minimum by default, but we need 3 for 'dev'
# `-arch all` scans all slices of the universal binary rather than just the host architecture
strings_cmd="strings -n 3 -arch all"
if [ ${#to_channel} -gt 7 ]; then
echo "Channel length cannot exceed 7 characters -- aborting" >&2
exit 1
fi
if [ $($strings_cmd "$binary" | grep -c "$from_channel") -ne 2 ] ; then
echo "$from_channel not found twice in ChannelPrefs" >&2
echo >&2
$strings_cmd "$binary" >&2
exit 1
fi
original_size=$(wc -c "$binary" | awk '{print $1}')
perl -pi -e '
binmode(STDIN);
binmode(STDOUT);
use bytes;
my @channels = qw(release beta dev test source); # List of valid channels
# Channels from Bash
my $from_channel = "'"$from_channel"'";
my $to_channel = "'"$to_channel"'";
if (grep { $_ eq $to_channel } @channels) {
# Pad channels with null bytes
$from_channel .= "\0" x (7 - length($from_channel));
$to_channel .= "\0" x (7 - length($to_channel));
} else {
die "Unknown update channel: $to_channel\n";
}
s/$from_channel/$to_channel/g;
' "$binary"
new_size=$(wc -c "$binary" | awk '{print $1}')
if [ $original_size -ne $new_size ]; then
echo "File size of ChannelPrefs has changed -- aborting" >&2
exit 1
fi
if [ $($strings_cmd "$binary" | grep -c "$to_channel") -ne 2 ] ; then
echo "$to_channel not found twice in ChannelPrefs" >&2
$strings_cmd "$binary" >&2
exit 1
fi
$strings_cmd "$binary"

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:361730d7cea854b38c6968143555c5f5fe251e1c95f265a6133514ee72ea2c53
size 98904

View file

@ -1,10 +0,0 @@
#!/usr/bin/env python3
# Convert localized app name from "Firefox Software Update" to "Zotero Software Update"
#
# plist is UTF-16
f='updater.app/Contents/Resources/English.lproj/InfoPlist.strings'
with open(f, 'r', encoding='utf-16') as inputfile:
newText = inputfile.read().replace('Firefox', 'Zotero')
with open(f, 'w', encoding='utf-16') as outputfile:
outputfile.write(newText)

Binary file not shown.

@ -1 +0,0 @@
Subproject commit 2e78b1010ad49b4dde70ec9f0094b34a7b6f8965

@ -1 +0,0 @@
Subproject commit 300886d1984de7a0006d7a48310baa0c8c99bea0

@ -1 +0,0 @@
Subproject commit c0aa6e4bef039d94e17e81cb28b1fe9170c45b96

View file

@ -1,64 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
CHANNEL="release"
BRANCH="10.0"
BUILD_ONLY=0
if [ "`uname`" = "Darwin" ]; then
export SAFARI_APPEX="$ROOT_DIR/../safari-web-extension-builds/ZoteroSafariExtension.appex"
export SAFARI_EXT_RESOURCES="$ROOT_DIR/../safari-web-extension-builds/release/safari"
export SAFARI_APP_EXTENSION="$ROOT_DIR/../safari-app-extension-builds/release/ZoteroSafariExtension.appex"
if [ ! -d "$SAFARI_APPEX" ]; then
echo "Safari extension stub not found at $SAFARI_APPEX -- aborting" >&2
exit 1
fi
if [ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]; then
echo "Safari web extension not found in $SAFARI_EXT_RESOURCES -- aborting" >&2
exit 1
fi
if [ ! -d "$SAFARI_APP_EXTENSION" ]; then
echo "Safari App Extension not found at $SAFARI_APP_EXTENSION -- aborting" >&2
exit 1
fi
fi
while getopts "b" opt; do
case $opt in
b)
BUILD_ONLY=1
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
cd "$SCRIPT_DIR"
./check_requirements
hash=`./get_repo_branch_hash $BRANCH`
./check_app_matches_commit $hash
source_dir=`./get_commit_files $hash`
build_dir=`mktemp -d`
function cleanup {
rm -rf "$source_dir"
rm -rf "$build_dir"
}
trap cleanup EXIT
./prepare_build -s "$source_dir" -o "$build_dir" -c $CHANNEL -m $hash
VERSION="`cat \"$build_dir/version\"`"
./build_for_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL
if [ $BUILD_ONLY -eq 1 ]; then
echo
echo "Build only -- skipping deploy."
echo "To deploy, run on the deploy server:"
echo " $DEPLOY_PATH/deploy $CHANNEL $VERSION $BUILD_PLATFORMS"
else
ssh $DEPLOY_HOST "$DEPLOY_PATH/deploy" $CHANNEL "$VERSION" $BUILD_PLATFORMS
fi

View file

@ -1,55 +0,0 @@
#!/bin/bash
set -euo pipefail
#
# Zip a file directly into app/omni.ja in staging/
#
# Zip paths are relative to the current directory, so this should be run from
# the client build/ directory
#
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
cat >&2 <<DONE
Usage: $0 path/to/file
DONE
exit 1
}
if [ -z "${1:-}" ]; then
usage
fi
files="$@"
for file in $files; do
if [ ! -f "$file" ]; then
echo "Error: $file not found!"
exit 1
fi
done
mac_path="$STAGE_DIR/Zotero.app/Contents/Resources"
win_path="$STAGE_DIR/Zotero_win-x64"
linux_path="$STAGE_DIR/Zotero_linux-x86_64"
added=0
build_id=$(date +%Y%m%d%H%M%S)
for path in "$mac_path" "$win_path" "$linux_path"; do
if [ -d "$path" ]; then
echo "$path/app/omni.ja"
echo "Updating $(basename $(dirname $(dirname $path)))"
zip "$path/app/omni.ja" $files
# Bump BuildID so that startup caches are invalidated
perl -pi -e "s/^BuildID=.*/BuildID=$build_id/" "$path/app/application.ini"
added=1
fi
done
if [ $added -eq 0 ]; then
echo "No directories found in staging!"
exit 1
fi

View file

@ -1,62 +0,0 @@
#!/usr/bin/env python3
"""
Update a builds manifest with info on a given build
"""
import argparse
import os
import re
import sys
import shutil
import json
import traceback
MAJOR = None
parser = argparse.ArgumentParser(
description='Update a builds manifest with info on a given build',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-f', '--file', required=True, help="path to updates.json")
parser.add_argument('-v', '--version', required=True, help='version number of build')
parser.add_argument('-b', '--build_id', required=True, help="build ID ('20160801142343')")
args = parser.parse_args()
def main():
try:
file = args.file
version = args.version
short_version = re.match(r'\d+\.\d+', version).group(0)
# Back up JSON file
shutil.copy2(file, file + '.bak')
# Read in existing file
with open(file) as f:
updates = json.loads(f.read())
updates.append({
'version': version,
'buildID': args.build_id,
'detailsURL': f'https://www.zotero.org/support/{short_version}_changelog',
'major': MAJOR
})
# Keep last 5 entries
updates = updates[-5:]
# Write new file
updates = json.dumps(updates, indent=2)
with open(file, 'w') as f:
f.write(updates + "\n")
print(updates)
return 0
except Exception as err:
sys.stderr.write("\n" + traceback.format_exc())
return 1
if __name__ == '__main__':
sys.exit(main())

View file

@ -1,64 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
CHANNEL="beta"
BRANCH="main"
BUILD_ONLY=0
if [ "`uname`" = "Darwin" ]; then
export SAFARI_APPEX="$ROOT_DIR/../safari-web-extension-builds/ZoteroSafariExtension.appex"
export SAFARI_EXT_RESOURCES="$ROOT_DIR/../safari-web-extension-builds/beta/safari"
export SAFARI_APP_EXTENSION="$ROOT_DIR/../safari-app-extension-builds/beta/ZoteroSafariExtension.appex"
if [ ! -d "$SAFARI_APPEX" ]; then
echo "Safari extension stub not found at $SAFARI_APPEX -- aborting" >&2
exit 1
fi
if [ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]; then
echo "Safari web extension not found in $SAFARI_EXT_RESOURCES -- aborting" >&2
exit 1
fi
if [ ! -d "$SAFARI_APP_EXTENSION" ]; then
echo "Safari App Extension not found at $SAFARI_APP_EXTENSION -- aborting" >&2
exit 1
fi
fi
while getopts "b" opt; do
case $opt in
b)
BUILD_ONLY=1
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
cd "$SCRIPT_DIR"
./check_requirements
hash=`./get_repo_branch_hash $BRANCH`
./check_app_matches_commit $hash
source_dir=`./get_commit_files $hash`
build_dir=`mktemp -d`
function cleanup {
rm -rf "$source_dir"
rm -rf "$build_dir"
}
trap cleanup EXIT
./prepare_build -s "$source_dir" -o "$build_dir" -c $CHANNEL -m $hash
VERSION="`cat \"$build_dir/version\"`"
./build_for_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL
if [ $BUILD_ONLY -eq 1 ]; then
echo
echo "Build only -- skipping deploy."
echo "To deploy, run on the deploy server:"
echo " $DEPLOY_PATH/deploy $CHANNEL $VERSION $BUILD_PLATFORMS"
else
ssh $DEPLOY_HOST "$DEPLOY_PATH/deploy" $CHANNEL "$VERSION" $BUILD_PLATFORMS
fi

View file

@ -1,98 +0,0 @@
#!/bin/bash -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname $APP_ROOT_DIR)"
# Set ZOTERO_PROFILE environment variable to choose profile
profile_args=()
if [ -n "${ZOTERO_PROFILE:-}" ]; then
profile_args=(-p "$ZOTERO_PROFILE")
fi
FORCE_FULL=0
NO_REBUILD=0
SKIP_BUNDLED_FILES=0
DEBUGGER=0
while getopts "rfnbd" opt; do
case $opt in
r)
# Deprecated -- rebuilding is now the default
echo "-r is deprecated -- rebuilding is now the default (use -n to skip)" >&2
;;
f)
FORCE_FULL=1
;;
n)
NO_REBUILD=1
;;
b)
SKIP_BUNDLED_FILES=1
;;
d)
DEBUGGER=1
;;
\?)
exit 1
;;
esac
done
# Remove options from $@
shift $((OPTIND-1))
if [ $NO_REBUILD -eq 0 ]; then
PARAMS=""
if [ $DEBUGGER -eq 1 ]; then
PARAMS="-t"
fi
if [ $FORCE_FULL -eq 1 ]; then
PARAMS="$PARAMS -f"
fi
# Check if build watch is running
# If not, run now
if ! ps u | grep js-build/build.js | grep -v grep > /dev/null; then
echo "Running JS build process"
echo
cd $ROOT_DIR
# TEMP: --openssl-legacy-provider avoids a build error in pdf.js
NODE_OPTIONS=--openssl-legacy-provider node js-build/build.js
echo
fi
"$SCRIPT_DIR/dir_build" $PARAMS
fi
PARAMS=""
if [ $SKIP_BUNDLED_FILES -eq 1 ]; then
PARAMS="$PARAMS -ZoteroSkipBundledFiles"
fi
if [ $DEBUGGER -eq 1 ]; then
PARAMS="$PARAMS -jsdebugger"
else
PARAMS="$PARAMS -jsconsole"
fi
if [ "`uname`" = "Darwin" ]; then
command="Zotero.app/Contents/MacOS/zotero"
elif [ "`uname`" = "Linux" ]; then
if [[ "`uname -m`" = "aarch64" ]]; then
command="Zotero_linux-arm64/zotero"
else
command="Zotero_linux-x86_64/zotero"
fi
elif [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
command="Zotero_win-x64/zotero.exe"
else
echo "Unknown platform" >&2
exit 1
fi
echo
"$APP_ROOT_DIR/staging/$command" "${profile_args[@]}" -ZoteroDebugText $PARAMS "$@"

View file

@ -1,167 +0,0 @@
#!/bin/bash
#
# Builds Zotero and uploads to S3 and deploy server, but does not make the build live.
# To make the build live, run the deploy script on the deploy server (or via SSH).
#
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
cat >&2 <<DONE
Usage: $0 -d SOURCE_DIR -c CHANNEL -p PLATFORMS
-d SOURCE_DIR Source directory to build from
-c CHANNEL Release channel ('release', 'beta', 'dev')
-p PLATFORMS Platforms to build (m=Mac, w=Windows, l=Linux)
-i INCREMENTALS Number of incremental builds to create
DONE
exit 1
}
SOURCE_DIR=""
CHANNEL=""
PLATFORMS=""
while getopts "d:c:p:i:" opt; do
case $opt in
d)
SOURCE_DIR="$OPTARG"
if [ ! -d "$SOURCE_DIR" ]; then
echo "$SOURCE_DIR not found"
exit 1
fi
;;
c)
CHANNEL="$OPTARG"
;;
p)
PLATFORMS="$OPTARG"
;;
i)
NUM_INCREMENTALS="$OPTARG"
;;
*)
usage
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
if [[ -z "$SOURCE_DIR" ]] || [[ -z "$CHANNEL" ]] || [[ -z "$PLATFORMS" ]]; then
usage
fi
"$SCRIPT_DIR"/check_requirements
VERSION="`cat \"$SOURCE_DIR\"/version`"
if [ -z "$VERSION" ]; then
echo "Error getting version from $SOURCE_DIR/version"
exit 1
fi
# Enforce signing unless a test build or macOS, which doesn't start otherwise
if [[ "$CHANNEL" != "test" || $PLATFORMS = "m" ]]; then
SIGN="-e"
else
SIGN=""
fi
# Build Zotero
"$ROOT_DIR/build.sh" -d "$SOURCE_DIR" -p $PLATFORMS -c $CHANNEL $SIGN
BUILD_ID=`cat "$DIST_DIR/build_id"`
if [ -z "$BUILD_ID" ]; then
echo "Error getting build id"
exit 1
fi
TEMP_DIR=`mktemp -d`
# Clean up on exit
function cleanup {
rm -rf "$TEMP_DIR"
}
trap cleanup EXIT
# Build full update
"$ROOT_DIR/update-packaging/build_autoupdate.sh" -f -c $CHANNEL -p $PLATFORMS -l $VERSION
# Build incremental updates for each platform
for i in `seq 0 1 $((${#PLATFORMS}-1))`
do
case ${PLATFORMS:i:1} in
m)
platform=mac
platform_name=Mac
;;
w)
platform=win
platform_name=Windows
;;
l)
platform=linux
platform_name=Linux
;;
*)
echo "$0: Invalid platform option ${PLATFORMS:i:1}"
usage
;;
esac
echo
echo "Getting $platform_name incrementals"
INCREMENTALS="`\"$SCRIPT_DIR/manage_incrementals\" -c $CHANNEL -p ${PLATFORMS:i:1} -n $NUM_INCREMENTALS`"
# Fetch pinned incrementals from deploy server and merge
PINNED_FILE="$DEPLOY_PATH/manifests/$CHANNEL/pinned-incrementals-$platform"
PINNED=""
if ssh $DEPLOY_HOST "test -f $PINNED_FILE"; then
PINNED="`ssh $DEPLOY_HOST cat $PINNED_FILE`"
fi
# Combine and deduplicate
INCREMENTALS="`echo -e "$INCREMENTALS\n$PINNED" | sort -u | grep -v '^$' || true`"
echo "$INCREMENTALS"
echo
if [ -n "$INCREMENTALS" ]; then
for from in $INCREMENTALS; do
echo "Building incremental update for $platform_name from $from to $VERSION"
"$ROOT_DIR/update-packaging/build_autoupdate.sh" -i "$from" -c "$CHANNEL" -p ${PLATFORMS:i:1} -l $VERSION
echo
done
fi
done
# Upload builds to S3
"$SCRIPT_DIR/upload_builds" $CHANNEL $VERSION
# Upload file lists and build info for each platform
channel_deploy_path="$DEPLOY_PATH/manifests/$CHANNEL"
mkdir "$TEMP_DIR/version_info"
chmod g+ws "$TEMP_DIR/version_info"
cp "$DIST_DIR"/files-* "$TEMP_DIR/version_info"
chmod g+w "$TEMP_DIR"/version_info/files-*
# Generate build-{os}.json for each platform
SHORT_VERSION=$(echo "$VERSION" | grep -oE '^[0-9]+\.[0-9]+')
DETAILS_URL="https://www.zotero.org/support/${SHORT_VERSION}_changelog"
for i in `seq 0 1 $((${#PLATFORMS}-1))`
do
case ${PLATFORMS:i:1} in
m) os_name="mac" ;;
w) os_name="win" ;;
l) os_name="linux" ;;
esac
printf '{\n "buildID": "%s",\n "detailsURL": "%s"\n}\n' "$BUILD_ID" "$DETAILS_URL" \
> "$TEMP_DIR/version_info/build-${os_name}.json"
chmod g+w "$TEMP_DIR/version_info/build-${os_name}.json"
done
rsync -rv "$TEMP_DIR/version_info/" $DEPLOY_HOST:"$channel_deploy_path/$VERSION/"
rm -rf "$STAGE_DIR"/*
echo
echo "Build $VERSION uploaded successfully."
echo "To deploy, run on the deploy server:"
echo " ./deploy $CHANNEL $VERSION $PLATFORMS"

View file

@ -1,37 +0,0 @@
#!/bin/bash
set -euo pipefail
# Check that app/ in this checkout matches app/ in the commit being built.
#
# The build-and-deploy scripts run the build scripts and config from the local
# checkout but build source files from the tip of the remote branch, so a stale
# checkout can silently build with the wrong Gecko version or omni patches.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
if [ -z "${1:-}" ]; then
echo "Usage: $0 commit-hash" >&2
exit 1
fi
hash=$1
cd "$ROOT_DIR"
if ! git cat-file -e "$hash^{commit}" 2>/dev/null; then
git fetch -q origin
fi
if ! git cat-file -e "$hash^{commit}" 2>/dev/null; then
echo "Commit $hash not found locally after fetching -- aborting" >&2
exit 1
fi
if ! git diff --quiet HEAD "$hash" -- app/; then
echo >&2
echo "app/ files in this checkout differ from app/ in $hash:" >&2
echo >&2
git --no-pager diff --stat HEAD "$hash" -- app/ >&2
echo >&2
echo "Update this checkout (e.g., 'git pull') and try again" >&2
exit 1
fi

View file

@ -1,158 +0,0 @@
#!/bin/bash
set -uo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$APP_ROOT_DIR")"
. "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
cd "$ROOT_DIR"
platform=`get_current_platform`
FAIL_CMD='echo -e \033[31;1mFAIL\033[0m'
FAIL_CMD_INLINE='echo -n -e \033[31;1mFAIL\033[0m'
FAILED=0
hdr_start=`tput smul`
hdr_stop=`tput rmul`
echo "${hdr_start}Checking build requirements:${hdr_stop}"
echo
echo -n "Checking for Node.js: "
which node || { $FAIL_CMD_INLINE; FAILED=1; }
node_version=$(node -v | cut -c 2-)
echo -n "Checking Node version: $node_version"
if [ $(echo $node_version | cut -d '.' -f1 ) -ge "18" ]; then
echo
else
echo -n " -- must be 18 or later "
{ $FAIL_CMD; FAILED=1; }
fi
echo -n "Checking for Git LFS: "
which git-lfs || { $FAIL_CMD_INLINE; FAILED=1; }
echo "Checking for Git LFS checkouts: "
lfs_files=$(git lfs ls-files -n 2> /dev/null) || { $FAIL_CMD; FAILED=1; }
for lfs_file in $lfs_files; do
echo -n " - $lfs_file: "
file_type=$(file -b $lfs_file)
echo -n $file_type" "
if [ -z "$(echo -n $file_type | grep XZ)" ]; then
{ $FAIL_CMD_INLINE; FAILED=1; echo " -- run 'git lfs install; git lfs pull'"; }
fi
echo
done
echo -n "Checking for perl: "
which perl || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for python3: "
which python3 || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for curl: "
which curl || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for zip: "
which zip || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for unzip: "
which unzip || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for xz: "
which xz || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for awk: "
which awk || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for rsync: "
which rsync || { $FAIL_CMD; FAILED=1; }
if [ $platform = "w" ]; then
echo -n "Checking for 7z: "
which 7z || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for rcedit: "
which rcedit || { $FAIL_CMD; FAILED=1; echo " -- Install with fetch_rcedit"; }
fi
if [ $platform = "w" ]; then
echo
echo "${hdr_start}Checking Windows packaging requirements:${hdr_stop}"
echo
echo -n "Checking for upx: "
which upx || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for uuidgen: "
which uuidgen || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for code-signing script: "
if [ -x "$APP_ROOT_DIR/win/codesign" ]; then
echo "$APP_ROOT_DIR/win/codesign"
else
$FAIL_CMD
FAILED=1
fi
echo -n "Checking for Unicode NSIS: "
if [ -x "`cygpath -u \"${NSIS_DIR}makensis.exe\"`" ]; then
echo "`cygpath -u \"${NSIS_DIR}makensis.exe\"`"
else
$FAIL_CMD
FAILED=1
fi
plugin_path=$(cd "$NSIS_DIR\\Plugins\\x86-unicode" && pwd)
plugins="AppAssocReg ApplicationID InvokeShellVerb nsProcess ShellLink UAC"
echo "Checking for NSIS plugins in $plugin_path"
for i in $plugins; do
echo -n " $i.dll: "
if [ -f "$plugin_path/$i.dll" ]; then
echo OK
else
$FAIL_CMD
FAILED=1
fi
done
fi
if [ $platform = "m" ]; then
echo
echo "${hdr_start}Checking Mac packaging requirements:${hdr_stop}"
echo
echo -n "Checking for codesign: "
which /usr/bin/codesign || { $FAIL_CMD; FAILED=1; }
fi
echo
echo "${hdr_start}Checking distribution requirements:${hdr_stop}"
echo
echo -n "Checking for Mozilla ARchive (MAR) tool: "
which mar || { $FAIL_CMD; FAILED=1; echo " -- Install with fetch_mar_tools"; }
echo -n "Checking for mbsdiff: "
which mbsdiff || { $FAIL_CMD; FAILED=1; echo " -- Install with fetch_mar_tools"; }
echo -n "Checking for rsync: "
which rsync || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for sha512sum/shasum: "
which sha512sum 2>/dev/null || which shasum 2>/dev/null || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for AWS CLI: "
which aws || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for AWS S3 access: "
aws s3 ls $S3_BUCKET/$S3_DIST_PATH | sed 's/^[[:blank:]]*//' || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for deploy host directory access: "
ssh $DEPLOY_HOST ls -d $DEPLOY_PATH || { $FAIL_CMD; FAILED=1; }
exit $FAILED

View file

@ -1,40 +0,0 @@
#!/bin/bash
set -euo pipefail
# Perform ad-hoc code signing of Zotero.app for local usage
#
# Currently we sign only the Word dylib, since that's necessary for Zotero developers to work on
# Word integration on Apple Silicon. If we discover other problems, we can uncomment some of the
# other lines. If you're making a custom build, you can modify this file to sign the entire build
# instead of just the bare minimum needed for development.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
if [ -z "${1:-}" ]; then
echo "Usage: $0 path/to/staging/Zotero.app"
exit 1
fi
APPDIR=$1
DEVELOPER_ID="-"
entitlements_file="$ROOT_DIR/mac/entitlements.xml"
#/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" \
# "$APPDIR/Contents/MacOS/XUL" \
# "$APPDIR/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater"
#find "$APPDIR/Contents" -name '*.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
#find "$APPDIR/Contents" -name '*.app' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
#/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" "$APPDIR/Contents/MacOS/zotero"
# Skip signing of Safari extension, since it's not present for local builds
# Sign final app package
#echo
#/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" "$APPDIR"
# Verify app
#/usr/bin/codesign --verify -vvvv "$APPDIR"
find "$APPDIR/Contents" -name 'libZoteroWordIntegration.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;

View file

@ -1,76 +0,0 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
# Creates the precomplete file containing the remove and rmdir application
# update instructions which is used to remove files and directories that are no
# longer present in a complete update. The current working directory is used for
# the location to enumerate and to create the precomplete file.
import io
import os
def get_build_entries(root_path):
"""Iterates through the root_path, creating a list for each file and
directory. Excludes any file paths ending with channel-prefs.js.
"""
rel_file_path_set = set()
rel_dir_path_set = set()
for root, dirs, files in os.walk(root_path):
for file_name in files:
parent_dir_rel_path = root[len(root_path) + 1 :]
rel_path_file = os.path.join(parent_dir_rel_path, file_name)
rel_path_file = rel_path_file.replace("\\", "/")
if not (
rel_path_file.endswith("channel-prefs.js")
or rel_path_file.endswith("update-settings.ini")
or "/ChannelPrefs.framework/" in rel_path_file
or rel_path_file.startswith("ChannelPrefs.framework/")
or "/UpdateSettings.framework/" in rel_path_file
or rel_path_file.startswith("UpdateSettings.framework/")
or "distribution/" in rel_path_file
):
rel_file_path_set.add(rel_path_file)
for dir_name in dirs:
parent_dir_rel_path = root[len(root_path) + 1 :]
rel_path_dir = os.path.join(parent_dir_rel_path, dir_name)
rel_path_dir = rel_path_dir.replace("\\", "/") + "/"
if rel_path_dir.find("distribution/") == -1:
rel_dir_path_set.add(rel_path_dir)
rel_file_path_list = list(rel_file_path_set)
rel_file_path_list.sort(reverse=True)
rel_dir_path_list = list(rel_dir_path_set)
rel_dir_path_list.sort(reverse=True)
return rel_file_path_list, rel_dir_path_list
def generate_precomplete(root_path):
"""Creates the precomplete file containing the remove and rmdir
application update instructions. The given directory is used
for the location to enumerate and to create the precomplete file.
"""
rel_path_precomplete = "precomplete"
# If inside a Mac bundle use the root of the bundle for the path.
if os.path.basename(root_path) == "Resources":
root_path = os.path.abspath(os.path.join(root_path, "../../"))
rel_path_precomplete = "Contents/Resources/precomplete"
precomplete_file_path = os.path.join(root_path, rel_path_precomplete)
# Open the file so it exists before building the list of files and open it
# in binary mode to prevent OS specific line endings.
precomplete_file = io.open(precomplete_file_path, mode="wt", newline="\n")
rel_file_path_list, rel_dir_path_list = get_build_entries(root_path)
for rel_file_path in rel_file_path_list:
precomplete_file.write('remove "' + rel_file_path + '"\n')
for rel_dir_path in rel_dir_path_list:
precomplete_file.write('rmdir "' + rel_dir_path + '"\n')
precomplete_file.close()
if __name__ == "__main__":
generate_precomplete(os.getcwd())

View file

@ -1,41 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
CHANNEL="dev"
BRANCH="main"
if [ "`uname`" = "Darwin" ]; then
export SAFARI_APPEX="$ROOT_DIR/../safari-web-extension-builds/ZoteroSafariExtension.appex"
export SAFARI_EXT_RESOURCES="$ROOT_DIR/../safari-web-extension-builds/dev/safari"
if [ ! -d "$SAFARI_APPEX" ]; then
echo "Safari extension stub not found at $SAFARI_APPEX -- aborting" >&2
exit 1
fi
if [ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]; then
echo "Safari web extension not found in $SAFARI_EXT_RESOURCES -- aborting" >&2
exit 1
fi
fi
cd "$SCRIPT_DIR"
./check_requirements
hash=`./get_repo_branch_hash $BRANCH`
./check_app_matches_commit $hash
source_dir=`./get_commit_files $hash`
build_dir=`mktemp -d`
function cleanup {
rm -rf "$source_dir"
rm -rf "$build_dir"
}
trap cleanup EXIT
./prepare_build -s "$source_dir" -o "$build_dir" -c $CHANNEL -m $hash
VERSION="`cat \"$build_dir/version\"`"
./build_for_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL -i 1
ssh $DEPLOY_HOST "$DEPLOY_PATH/deploy" $CHANNEL "$VERSION" $BUILD_PLATFORMS

View file

@ -1,175 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage {
cat >&2 <<DONE
Usage: $0 [options]
If -p is omitted, platform (and, for Windows/Linux, architecture) default to the host system.
Architecture is ignored for Mac builds, which are always shipped as universal binaries.
Options
-p PLATFORM Platform to build (m=Mac, w=Windows, l=Linux)
-a ARCH Target architecture (arm64, x64, win32) — **Windows/Linux only**
-t add devtools
-f force a full rebuild instead of updating the staged build in place
DONE
exit 1
}
platform=""
arch=""
devtools=0
force_full=0
while getopts "tp:a:f" opt; do
case $opt in
t)
devtools=1
;;
p)
for (( i=0; i<${#OPTARG}; i++ )); do
case ${OPTARG:i:1} in
m) platform="m";;
w) platform="w";;
l) platform="l";;
*)
echo "$0: Invalid platform option ${OPTARG:i:1}" >&2
usage
;;
esac
done
;;
a)
arch="$OPTARG"
;;
f)
force_full=1
;;
\?)
usage
;;
esac
done
# Derive platform from host if not provided
if [[ -z $platform ]]; then
case "$(uname -s)" in
Darwin) platform="m" ;;
Linux) platform="l" ;;
CYGWIN*|MINGW*) platform="w" ;;
esac
fi
# Handle architecture rules
if [[ $platform = "m" ]]; then
# No arch for Mac — ignore anything provided
if [[ -n $arch ]]; then
echo "Ignoring -a $arch for Mac build (universal binary)" >&2
fi
arch=""
else
# Windows / Linux: derive arch if not supplied
if [[ -z $arch ]]; then
# An x64 build of Git for Windows runs emulated on Windows on ARM, where it
# reports x86_64 from `uname -m`, while `uname -s` reports the native
# architecture
if [[ $platform = "w" && "$(uname -s)" == *ARM64* ]]; then
arch="arm64"
else
case "$(uname -m)" in
arm64|aarch64) arch="arm64" ;;
x86_64) arch="x64" ;;
esac
fi
fi
fi
CHANNEL="source"
# Remove stale symlinks left behind in build/ when source files are deleted,
# which would otherwise break the build
if [ -d "$ROOT_DIR/build" ]; then
# With -L, -type l matches only broken symlinks
stale_links=$(find -L "$ROOT_DIR/build" -type l -print)
if [ -n "$stale_links" ]; then
echo "Removing stale symlinks from build/:" >&2
echo "$stale_links" >&2
while IFS= read -r link; do
rm "$link"
done <<< "$stale_links"
fi
fi
# Try to update the staged build in place instead of doing a full rebuild
if [[ $force_full -eq 0 ]]; then
incr_cmd=("$SCRIPT_DIR/incremental_update" -p "$platform")
[[ -n $arch ]] && incr_cmd+=( -a "$arch" )
[[ $devtools -eq 1 ]] && incr_cmd+=( -t )
set +e
"${incr_cmd[@]}"
incr_status=$?
set -e
if [ $incr_status -ne 2 ]; then
exit $incr_status
fi
fi
hash=$(git -C "$ROOT_DIR" rev-parse --short HEAD)
build_dir=$(mktemp -d)
manifest_tmp=""
cleanup() {
rm -rf "$build_dir"
if [ -n "$manifest_tmp" ]; then
rm -f "$manifest_tmp"
fi
}
trap cleanup EXIT
# Snapshot the state of build/ and app/ before the build for later incremental
# updates
app_hash=""
if [ -d "$ROOT_DIR/build" ]; then
manifest_tmp=$(mktemp)
generate_build_manifest "$ROOT_DIR/build" > "$manifest_tmp"
app_hash=$(generate_app_hash "$APP_ROOT_DIR")
fi
"$SCRIPT_DIR/prepare_build" -s "$ROOT_DIR/build" -o "$build_dir" -c "$CHANNEL" -m "$hash"
build_cmd=("$APP_ROOT_DIR/build.sh" -d "$build_dir" -p "$platform" -c "$CHANNEL" -s -q)
[[ -n $arch ]] && build_cmd+=( -a "$arch" )
[[ $devtools -eq 1 ]] && build_cmd+=( -t )
"${build_cmd[@]}"
# Save manifest for future incremental updates
if [ -n "$manifest_tmp" ]; then
if [[ -z "${ZOTERO_TEST:-}" ]] || [[ "${ZOTERO_TEST:-}" == "0" ]]; then
include_tests=0
else
include_tests=1
fi
{
echo "#format=2"
echo "#include_tests=$include_tests"
echo "#devtools=$devtools"
echo "#app_hash=$app_hash"
cat "$manifest_tmp"
} > "$STAGE_DIR/.build-manifest"
fi
# Ad-hoc-sign the Word dylib so it works on Apple Silicon. This only needs to
# happen when the staged build is fully rebuilt -- incremental updates don't
# touch the dylib.
if [[ $platform = "m" ]] && [[ "$(uname -s)" = "Darwin" ]]; then
"$SCRIPT_DIR/codesign_local" "$STAGE_DIR/Zotero.app"
fi
echo Done

View file

@ -1,22 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
cd "$APP_ROOT_DIR"
mkdir -p "xulrunner/bin"
if [ "`uname`" = "Darwin" ]; then
# Mozilla has Linux executables where the Mac files should be, so supply our own Mac builds
curl -o "xulrunner/bin/mar" https://zotero-download.s3.us-east-1.amazonaws.com/tools/mac/102.11.0esr/mar
curl -o "xulrunner/bin/mbsdiff" https://zotero-download.s3.us-east-1.amazonaws.com/tools/mac/102.11.0esr/mbsdiff
elif [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
# Mozilla doesn't seem to provide Windows versions via its file server anymore, so supply our own
curl -o "xulrunner/bin/mar.exe" https://zotero-download.s3.us-east-1.amazonaws.com/tools/win/102.11.0esr/mar.exe
curl -o "xulrunner/bin/mbsdiff.exe" https://zotero-download.s3.us-east-1.amazonaws.com/tools/win/102.11.0esr/mbsdiff.exe
else
curl -o "xulrunner/bin/mar" https://ftp.mozilla.org/pub/firefox/nightly/2022/05/2022-05-30-09-39-43-mozilla-central/mar-tools/linux64/mar
curl -o "xulrunner/bin/mbsdiff" https://ftp.mozilla.org/pub/firefox/nightly/2022/05/2022-05-30-09-39-43-mozilla-central/mar-tools/linux64/mbsdiff
fi
chmod 755 xulrunner/bin/mar xulrunner/bin/mbsdiff

View file

@ -1,11 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
cd "$ROOT_DIR"
mkdir -p "xulrunner/bin"
curl -L -o "xulrunner/bin/rcedit.exe" https://github.com/electron/rcedit/releases/download/v1.1.1/rcedit-x86.exe
chmod 755 xulrunner/bin/rcedit

View file

@ -1,855 +0,0 @@
#!/bin/bash
set -euo pipefail
# Copyright (c) 2011 Zotero
# Center for History and New Media
# George Mason University, Fairfax, Virginia, USA
# http://zotero.org
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
cd "$APP_ROOT_DIR"
usage() {
cat >&2 <<'DONE'
Usage: fetch_xulrunner.sh -p <m|w|l> [-a ARCH]
Required
-p PLATFORM m (Mac), w (Windows), l (Linux)
Optional / Conditional
-a ARCH - Mac: disallowed (builds are universal)
- Windows: x64 | arm64 | win32
- Linux: x64 (or x86_64) | arm64
If omitted on Windows/Linux, all standard archs are fetched.
DONE
exit 1
}
BUILD_MAC=0
BUILD_WIN=0
BUILD_LINUX=0
arch=""
while getopts 'p:a:' opt; do
case $opt in
p)
for (( i=0; i<${#OPTARG}; i++ )); do
case ${OPTARG:i:1} in
m) BUILD_MAC=1;;
w) BUILD_WIN=1;;
l) BUILD_LINUX=1;;
*) echo "Invalid platform: ${OPTARG:i:1}"; echo; usage;;
esac
done;;
a) arch="$OPTARG";;
*) usage;;
esac
shift $((OPTIND-1)); OPTIND=1
done
# Require at least one platform
if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; then
usage
fi
# Sanity‑check arch flag vs platform selections
if [[ $BUILD_MAC == 1 ]]; then
[[ -n $arch ]] && { echo "-a is not allowed for Mac" >&2; echo; usage; }
fi
if [[ $BUILD_WIN == 1 || $BUILD_LINUX == 1 ]]; then
# arch optional; if provided validate & map aliases now
if [[ -n $arch ]]; then
if [[ $BUILD_WIN == 1 && $BUILD_LINUX == 1 ]]; then
echo "-a cannot be used when building both Windows and Linux simultaneously" >&2; echo; usage
fi
if [[ $BUILD_WIN == 1 ]]; then
case $arch in
x64) arch="win-x64" ;;
arm64) arch="win-arm64" ;;
win32|win-x64|win-arm64) ;;
*) echo "Invalid Windows arch: $arch" >&2; echo; usage;;
esac
fi
if [[ $BUILD_LINUX == 1 ]]; then
[[ $arch == x64 ]] && arch="x86_64"
case $arch in x86_64|arm64) ;; *) echo "Invalid Linux arch: $arch" >&2; echo; usage;; esac
fi
fi
fi
function get_utf16_chars {
str=$(echo -n "$1" | xxd -p | fold -w 2 | sed -r 's/(.+)/\\\\x{\1}\\\\x{00}/')
# Add NUL padding
if [ -n "${2:-}" ]; then
# Multiply characters x 2 for UTF-16
for i in `seq 1 $(($2 * 2))`; do
str+=$(echo '\\x{00}')
done
fi
echo $str | xargs | sed 's/ //g'
}
#
# Make various modifications to the stock Firefox app
#
function modify_omni {
platform=$1
mkdir omni
mv omni.ja omni
cd omni
# omni.ja is an "optimized" ZIP file, so use a script from Mozilla to avoid a warning from unzip
# here and to make it work after rezipping below
python3 "$APP_ROOT_DIR/scripts/optimizejars.py" --deoptimize ./ ./ ./
rm -f omni.ja.log
unzip omni.ja
rm omni.ja
rm -r chrome/pdfjs
rm -r modules/narrate
#rm -r modules/translation
rm actors/AboutTranslations{Parent,Child}.sys.mjs
rm actors/CookieBanner{Parent,Child}.sys.mjs
rm actors/PictureInPictureChild.sys.mjs
rm actors/ThumbnailsChild.sys.mjs
rm actors/Translations{Engine,}{Parent,Child}.sys.mjs
rm actors/AudioPlayback{Parent,Child}.sys.mjs
replace_line 'BROWSER_CHROME_URL:.+' 'BROWSER_CHROME_URL: "chrome:\/\/zotero\/content\/zoteroPane.xhtml",' modules/AppConstants.sys.mjs
# Used by OSKeyStore as the master-key label, visible in macOS Keychain Access.
# Verify that OSKeyStore still derives the label from MOZ_APP_BASENAME, so a
# future Mozilla change to a hardcoded string doesn't silently rebrand the
# keychain entry back to "Firefox Encrypted Storage".
replace_line 'MOZ_APP_BASENAME: "Firefox"' 'MOZ_APP_BASENAME: "Zotero"' modules/AppConstants.sys.mjs
check_line 'STORE_LABEL: AppConstants\.MOZ_APP_BASENAME \+ " Encrypted Storage"' modules/OSKeyStore.sys.mjs
# https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html
#
# It's not clear that most of these do anything anymore when not compiled in, but just in case
replace_line 'MOZ_REQUIRE_SIGNING:' 'MOZ_REQUIRE_SIGNING: false \&\&' modules/AppConstants.sys.mjs
replace_line 'MOZ_DATA_REPORTING:' 'MOZ_DATA_REPORTING: false \&\&' modules/AppConstants.sys.mjs
replace_line 'MOZ_TELEMETRY_REPORTING:' 'MOZ_TELEMETRY_REPORTING: false \&\&' modules/AppConstants.sys.mjs
replace_line 'MOZ_CRASHREPORTER:' 'MOZ_CRASHREPORTER: false \&\&' modules/AppConstants.sys.mjs
replace_line 'MOZ_UPDATE_CHANNEL:.+' 'MOZ_UPDATE_CHANNEL: "none",' modules/AppConstants.sys.mjs
replace_line '"https:\/\/[^\/]+mozilla.com.+"' '""' modules/AppConstants.sys.mjs
# Don't use Mozilla Maintenance Service on Windows
replace_line 'MOZ_MAINTENANCE_SERVICE:' 'MOZ_MAINTENANCE_SERVICE: false \&\&' modules/AppConstants.sys.mjs
# Continue using app.update.auto in prefs.js on Windows
replace_line 'PER_INSTALLATION_PREFS_PLATFORMS = \["win"\]' 'PER_INSTALLATION_PREFS_PLATFORMS = []' modules/UpdateUtils.sys.mjs
# Prompt if major update is available instead of installing automatically on restart
replace_line 'if \(!updateAuto\) \{' 'if (update.type == "major") {
LOG("UpdateService:_selectAndInstallUpdate - prompting because it is a major update");
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_SHOWPROMPT_PREF);
Services.obs.notifyObservers(update, "update-available", "show-prompt");
return;
}
if (!updateAuto) {' modules/UpdateService.sys.mjs
# Avoid console warning about resource://gre/modules/FxAccountsCommon.js
replace_line 'const logins = this._data.logins;' 'const logins = this._data.logins; if (this._data.logins.length != -1) return;' modules/LoginStore.sys.mjs
# Prevent error during network requests
replace_line 'async lazyInit\(\) \{' 'async lazyInit() { if (this.features) return false;' modules/UrlClassifierExceptionListService.sys.mjs
# Silence CrashManager "Could not get children of [...]/Crash Reports/events" spam
replace_line '\/\/ Missing events folders are allowed' '\/\/ Missing events folders are allowed (silence warning)
if (ex) return entries;' modules/CrashManager.sys.mjs
# Several services pull in services-settings (which we strip) and throw
# at profile-after-change. Remove their #init() methods to prevent errors.
replace_line 'async #init\(\) \{' 'async #init() {} async _unused_init() {' modules/EssentialDomainsRemoteSettings.sys.mjs
replace_line 'async #init\(\) \{' 'async #init() {} async _unused_init() {' modules/SimpleURIUnknownSchemesRemoteObserver.sys.mjs
# RemotePermissionService uses a top-level import, so even loading the
# module throws
replace_line 'import \{ RemoteSettings \} from "resource:\/\/services-settings\/remote-settings.sys.mjs";' \
'const RemoteSettings = () => ({ on() {}, get: async () => [] });' \
modules/RemotePermissionService.sys.mjs
replace_line 'pref\("network.captive-portal-service.enabled".+' 'pref("network.captive-portal-service.enabled", false);' greprefs.js
replace_line 'pref\("network.connectivity-service.enabled".+' 'pref("network.connectivity-service.enabled", false);' greprefs.js
replace_line 'pref\("toolkit.telemetry.server".+' 'pref("toolkit.telemetry.server", "");' greprefs.js
replace_line 'pref\("toolkit.telemetry.unified".+' 'pref("toolkit.telemetry.unified", false);' greprefs.js
replace_line 'pref\("media.gmp-manager.url".+' 'pref("media.gmp-manager.url", "");' greprefs.js
#
# # Disable transaction timeout
# perl -pi -e 's/let timeoutPromise/\/*let timeoutPromise/' modules/Sqlite.jsm
# perl -pi -e 's/return Promise.race\(\[transactionPromise, timeoutPromise\]\);/*\/return transactionPromise;/' modules/Sqlite.jsm
# rm -f jsloader/resource/gre/modules/Sqlite.jsm
#
# Disable unwanted components
remove_line '(RemoteSettings|services-|telemetry|Telemetry|URLDecorationAnnotationsService)' components/components.manifest
file="modules/ActorManagerParent.sys.mjs"
# Remove deleted actors
# Entries in the JSWINDOWACTORS/JSPROCESSACTORS literals:
remove_between '^ AudioPlayback: \{' '^ },' $file
remove_between '^ CookieBanner: \{' '^ },' $file
remove_between '^ Thumbnails: \{' '^ },' $file
remove_between '^ Translations: \{' '^ },' $file
remove_between '^ TranslationsEngine: \{' '^ },' $file
# Entries added conditionally after the literals:
remove_between '^ JSWINDOWACTORS\.AboutTranslations = \{' '^ \};' $file
remove_between '^ JSWINDOWACTORS\.PictureInPictureLauncher = \{' '^ \};' $file
remove_between '^ JSWINDOWACTORS\.PictureInPictureToggle = \{' '^ \};' $file
remove_between '^ JSWINDOWACTORS\.PictureInPicture = \{' '^ \};' $file
# Do not trigger LoginManager event that logs an error on autocomplete submission
remove_line 'DOMInputPasswordAdded: \{\},' $file
# On Mac/Linux, ignore relative paths in PATH in Subprocess.pathSearch(), used when a bare command is passed to Utilities.Internal.subprocess()
if [[ $platform != win* ]]; then
replace_line 'let path = PathUtils.join\(dir, bin\);' 'if (!dir.startsWith("\/")) continue; let path = PathUtils.join(dir, bin);' modules/subprocess/subprocess_unix.sys.mjs
fi
# Remove unwanted files
pushd modules
rm Bookmarks*
rm FxAccounts*
echo '' > FingerprintingWebCompatService.sys.mjs
echo '' > PartitioningExceptionListService.sys.mjs
rm Places*
echo '' > PlacesExpiration.sys.mjs
echo '' > PlacesFrecencyRecalculator.sys.mjs
rm -rf services-settings/*
touch services-settings/remote-settings.sys.mjs
# Causes a startup error -- try an empty file or a shim instead?
#rm Telemetry*
rm URLDecorationAnnotationsService.sys.mjs
popd
pushd chrome/toolkit/content/mozapps/extensions
rm -rf default-theme
popd
# fcntl() is variadic, but js-ctypes declares it with a fixed third argument, which violates
# the ABI on every platform. It's harmless on Linux, where variadic and fixed integer
# arguments are passed in the same registers, but Apple's arm64 ABI passes variadic
# arguments on the stack, so the flag goes into a register the callee never reads and
# fcntl() takes it from an uninitialized stack slot instead. F_SETFD can therefore set
# FD_CLOFORK (bit 1, added in macOS 26) rather than FD_CLOEXEC, closing the pipe in the
# forked child and breaking Subprocess spawns, and F_SETFL can silently fail to set
# O_NONBLOCK. Since the value comes from leftover stack contents, this varies by build and
# call path instead of failing consistently -- don't assume it's fixed just because a given
# build works. This change should stay until this is fixed upstream.
if [[ $platform != win* ]]; then
replace_line 'ctypes\.int \/\* \.\.\. \*\/,' '"...",' modules/subprocess/subprocess_shared_unix.js
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[0], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[1], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFL, LIBC\.O_NONBLOCK\);' 'libc.fcntl(fds[1], LIBC.F_SETFL, ctypes.int(LIBC.O_NONBLOCK));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFL, LIBC\.O_NONBLOCK\);' 'libc.fcntl(fds[0], LIBC.F_SETFL, ctypes.int(LIBC.O_NONBLOCK));' modules/subprocess/subprocess_unix.sys.mjs
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[0], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.sys.mjs
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[1], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.sys.mjs
fi
# This file loads remote-settings.sys.mjs, but if it's empty Zotero crashes
replace_line 'async #init\(\) \{' 'async #init() { if (true) { return; }' modules/URLQueryStrippingListService.sys.mjs
# Clear most WebExtension manifest properties
replace_line 'manifest = normalized.value;' 'manifest = normalized.value;
if (this.type == "extension") {
if (!manifest.applications?.zotero?.id) {
this.manifestError("applications.zotero.id not provided");
}
if (!manifest.applications?.zotero?.update_url) {
this.manifestError("applications.zotero.update_url not provided");
}
if (!manifest.applications?.zotero?.strict_max_version) {
this.manifestError("applications.zotero.strict_max_version not provided");
}
manifest.browser_specific_settings = undefined;
manifest.content_scripts = [];
manifest.permissions = [];
manifest.host_permissions = [];
manifest.web_accessible_resources = undefined;
manifest.experiment_apis = {};
}' modules/Extension.sys.mjs
# Use applications.zotero instead of applications.gecko
replace_line 'let bss = manifest.applications\?.gecko' 'let bss = manifest.applications?.zotero' modules/addons/XPIInstall.sys.mjs
replace_line 'manifest.applications\?.gecko' 'manifest.applications?.zotero' modules/Extension.sys.mjs
# When installing addon, use app version instead of toolkit version for targetApplication
replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/XPIInstall.sys.mjs
# Set strictCompatibility to false in beta/dev/source builds to skip strict_max_version check
replace_line 'addon.strictCompatibility = true;' \
'let version = Services.appinfo.version;
addon.strictCompatibility = !version.includes("-beta") && !version.includes("-dev") && !version.includes("SOURCE");' modules/addons/XPIInstall.sys.mjs
# In beta/dev/source builds, ignore the persisted strictCompatibility flag for already-installed
# plugins, so plugins installed on a stable build (where the flag was set to true) aren't marked
# incompatible after upgrading. The manifest isn't re-parsed on app upgrade, so the flag set at
# install time sticks around.
replace_line '!this\.addon\.strictCompatibility' \
'(!this.addon.strictCompatibility || Services.appinfo.version.includes("-beta") || Services.appinfo.version.includes("-dev") || Services.appinfo.version.includes("SOURCE"))' modules/addons/XPIInstall.sys.mjs
replace_line '!this\.strictCompatibility &&' \
'(!this.strictCompatibility || Services.appinfo.version.includes("-beta") || Services.appinfo.version.includes("-dev") || Services.appinfo.version.includes("SOURCE")) &&' modules/addons/XPIDatabase.sys.mjs
# Accept zotero@chnm.gmu.edu for target application to allow Zotero 6 plugins to remain
# installed in Zotero 7
replace_line "if \(targetApp.id == Services.appinfo.ID\) \{" "if (targetApp.id == 'zotero\@chnm.gmu.edu') targetApp.id = '$APP_ID'; if (targetApp.id == Services.appinfo.ID) {" modules/addons/XPIDatabase.sys.mjs
# TEMP: Skip addons without ids until we figure out what's going on with the default theme.
# This fixes plugin updating and other things.
replace_line 'return addons;' 'return addons.filter(addon => addon.id);' modules/addons/XPIDatabase.sys.mjs
# For updates, look for applications.zotero instead of applications.gecko in manifest.json and
# use the app id and version for strict_min_version/strict_max_version comparisons
replace_line 'gecko: \{\},' 'zotero: {},' modules/addons/AddonUpdateChecker.sys.mjs
replace_line 'if \(!\("gecko" in applications\)\) \{' 'if (!("zotero" in applications)) {' modules/addons/AddonUpdateChecker.sys.mjs
replace_line '"gecko not in application entry' '"zotero not in application entry' modules/addons/AddonUpdateChecker.sys.mjs
replace_line 'let app = getProperty\(applications, "gecko", "object"\);' 'let app = getProperty(applications, "zotero", "object");' modules/addons/AddonUpdateChecker.sys.mjs
replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/AddonUpdateChecker.sys.mjs
replace_line 'lazy.AddonManagerPrivate.webExtensionsMinPlatformVersion' '"7.0"' modules/addons/AddonUpdateChecker.sys.mjs
replace_line 'result.targetApplications.push' 'false && result.targetApplications.push' modules/addons/AddonUpdateChecker.sys.mjs
# In beta/dev/source builds, don't set strictCompatibility on parsed update entries so that
# AddonUpdateChecker doesn't reject an available update whose maxVersion is lower than the
# current (non-stable) app version.
replace_line 'result\.strictCompatibility = appEntry\.maxVersion != "\*";' \
'let appVer = Services.appinfo.version; result.strictCompatibility = appEntry.maxVersion != "*" && !appVer.includes("-beta") && !appVer.includes("-dev") && !appVer.includes("SOURCE");' modules/addons/AddonUpdateChecker.sys.mjs
# Set uninstall flag to true if the update config has it set
replace_line 'targetApplications: \[appEntry\],' \
'targetApplications: \[appEntry\],
uninstall: getProperty(update, "uninstall", "boolean"),' modules/addons/AddonUpdateChecker.sys.mjs
# Prevent automatic installation of default theme
# (chrome/toolkit/content/mozapps/extensions/default-theme, removed above)
replace_line 'this.maybeInstallBuiltinAddon\(' 'if (false) this.maybeInstallBuiltinAddon(' modules/addons/XPIProvider.sys.mjs
# Don't log a warning when checking 'distribution/extensions' for addons
replace_line 'logger.warn\(`Checking' 'logger.debug(`Checking' modules/addons/XPIProvider.sys.mjs
# Allow addon installation by bypassing confirmation dialogs. If we want a confirmation dialog,
# we need to either add gXPInstallObserver from browser-addons.js [1][2] or provide our own with
# Ci.amIWebInstallPrompt [3].
#
# [1] https://searchfox.org/mozilla-esr102/rev/5a6d529652045050c5cdedc0558238949b113741/browser/base/content/browser.js#1902-1923
# [2] https://searchfox.org/mozilla-esr102/rev/5a6d529652045050c5cdedc0558238949b113741/browser/base/content/browser-addons.js#201
# [3] https://searchfox.org/mozilla-esr102/rev/5a6d529652045050c5cdedc0558238949b113741/toolkit/mozapps/extensions/AddonManager.sys.mjs#3114-3124
replace_line 'if \(info.addon.installPermissions\) \{' 'if (false) {' modules/AddonManager.sys.mjs
replace_line '\} else if \(info.addon.sitePermissions\) \{' '} else if (false) {' modules/AddonManager.sys.mjs
replace_line '\} else if \(requireConfirm\) \{' '} else if (false) {' modules/AddonManager.sys.mjs
# Make addon listener methods wait for promises, to allow calling asynchronous plugin `shutdown`
# and `uninstall` methods in our `onInstalling` handler
replace_line 'callAddonListeners\(aMethod' 'async callAddonListeners(aMethod' modules/AddonManager.sys.mjs
# Don't need this one to be async, but we can't easily avoid modifying its `listener[aMethod].apply()` call
replace_line 'callManagerListeners\(aMethod' 'async callManagerListeners(aMethod' modules/AddonManager.sys.mjs
replace_line 'AddonManagerInternal.callAddonListeners.apply\(AddonManagerInternal, aArgs\);' \
'return AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, aArgs);' modules/AddonManager.sys.mjs
replace_line 'listener\[aMethod\].apply\(listener, aArgs\);' \
'let maybePromise = listener[aMethod].apply(listener, aArgs);
if (maybePromise && maybePromise.then) await maybePromise;' modules/AddonManager.sys.mjs
replace_line 'AddonManagerPrivate.callAddonListeners' 'await AddonManagerPrivate.callAddonListeners' modules/addons/XPIInstall.sys.mjs
replace_line 'let uninstall = \(\) => \{' 'let uninstall = async () => {' modules/addons/XPIInstall.sys.mjs
replace_line 'cancelUninstallAddon\(aAddon\)' 'async cancelUninstallAddon(aAddon)' modules/addons/XPIInstall.sys.mjs
# Check for `uninstall` flag in update config and uninstall the addon if set
replace_line 'if \(update && !this.addon.location.locked\) \{' \
'if (update && !this.addon.location.locked) {
if (update.uninstall) {
let aId = this.addon.id;
logger.debug(`AddonUpdateChecker: uninstall addon \${aId} with uninstall flag`);
let addon = await AddonManager.getAddonByID(aId);
if (addon && (addon.permissions & AddonManager.PERM_CAN_UNINSTALL)) {
try {
addon.uninstall();
} catch (err) {
logger.error(`AddonUpdateChecker: error uninstalling addon \${aId} with uninstall flag`, err);
}
sendUpdateAvailableMessages(this, null);
return;
}
else {
logger.error(`AddonUpdateChecker: addon \${aId} with uninstall flag not found or cannot be uninstalled`);
sendUpdateAvailableMessages(this, null);
return;
}
}
' modules/addons/XPIInstall.sys.mjs
# Prevent login manager from trying to load FxAccountsCommon.sys.mjs
replace_line 'return login.hostname == lazy.FXA_PWDMGR_HOST;' 'return false;' modules/storage-json.sys.mjs
# No idea why this is necessary, but without it initialization fails with "TypeError: "constructor" is read-only"
replace_line 'LoginStore.prototype.constructor = LoginStore;' '\/\/LoginStore.prototype.constructor = LoginStore;' modules/LoginStore.sys.mjs
#
# # Allow proxy password saving
# perl -pi -e 's/get _inPrivateBrowsing\(\) \{/get _inPrivateBrowsing() {if (true) { return false; }/' components/nsLoginManagerPrompter.js
#
# # Change text in update dialog
# perl -pi -e 's/A security and stability update for/A new version of/' chrome/en-US/locale/en-US/mozapps/update/updates.properties
# perl -pi -e 's/updateType_major=New Version/updateType_major=New Major Version/' chrome/en-US/locale/en-US/mozapps/update/updates.properties
# perl -pi -e 's/updateType_minor=Security Update/updateType_minor=New Version/' chrome/en-US/locale/en-US/mozapps/update/updates.properties
# perl -pi -e 's/update for &brandShortName; as soon as possible/update as soon as possible/' chrome/en-US/locale/en-US/mozapps/update/updates.dtd
#
# Set available locales
cp "$APP_ROOT_DIR/assets/multilocale.txt" res/multilocale.txt
# Use Zotero URL opening in Mozilla dialogs (e.g., app update dialog)
replace_line 'function openURL\(aURL\) \{' 'function openURL(aURL) {let {Zotero} = ChromeUtils.importESModule("chrome:\/\/zotero\/content\/zotero.mjs"); Zotero.launchURL(aURL); if (true) { return; }' chrome/toolkit/content/global/contentAreaUtils.js
#
# Modify Add-ons window
#
# Prevent display: block from overriding display: none on <hr>s
replace_line 'display: block !important;' 'display: block;' chrome/toolkit/content/global/elements/panel-list.css
file="chrome/toolkit/content/mozapps/extensions/aboutaddons.css"
echo >> $file
# Hide the search bar and the whole sidebar, since we only ever show plugins and the main pane
# is already headed "Manage Your Plugins"
echo '.main-search, #sidebar { display: none; }' >> $file
# Center the content in the window now that it isn't offset by the sidebar
echo '#full { grid-template-columns: 1fr; }' >> $file
echo '#content { max-width: calc(var(--page-main-content-width) + var(--main-margin-start)); margin-inline: auto; }' >> $file
echo '.main-heading { margin-top: 2em; }' >> $file
# Hide Details/Permissions tabs in addon details so we only show details
echo 'addon-details > button-group { display: none !important; }' >> $file
# Hide "Run on sites with restrictions" row and helper message
echo '.addon-detail-row-quarantined-domains { display: none; }' >> $file
echo '.addon-detail-help-row:has([data-l10n-id="addon-detail-quarantined-domains-help"]) { display: none; }' >> $file
# Hide "Report" button from plugin menu
echo 'addon-card addon-options hr:first-of-type { display: none }' >> $file
echo '[data-l10n-id="report-addon-button"] { display: none }' >> $file
# Hide "Debug Addons" and "Manage Extension Shortcuts"
echo 'panel-item[action="debug-addons"], panel-item[action="reset-update-states"] + hr, panel-item[action="manage-shortcuts"] { display: none }' >> $file
# Show cursor feedback on plugin homepage links
echo '.addon-detail-row-homepage .text-link { cursor: pointer; color: LinkText; }' >> $file
echo '.addon-detail-row-homepage .text-link:hover { text-decoration: underline; }' >> $file
file="chrome/toolkit/content/mozapps/extensions/aboutaddons-utils.mjs"
# Hide unsigned-addon warning
replace_line 'export function isUnsignedWarningMessageDisabled\(\) \{' \
'export function isUnsignedWarningMessageDisabled() {if (true) return true;' $file
# Use our own localized string for blocked plugin notification
replace_line 'details-notification-hard-blocked-\$\{typeSuffix\}' 'plugins-blocked-plugin' $file
# Hide Recommendations tab in sidebar and recommendations in main pane
replace_line 'function isDiscoverEnabled\(\) \{' 'function isDiscoverEnabled() {return false;' $file
file="chrome/toolkit/content/mozapps/extensions/components/addon-details.mjs"
# Hide Private Browsing setting in addon details
replace_line 'pbRow\.' '\/\/pbRow.' $file
replace_line 'let isAllowed = await isAllowedInPrivateBrowsing' '\/\/let isAllowed = await isAllowedInPrivateBrowsing' $file
# Open plugin links in external browser
replace_line 'let homepageURL = homepageRow.querySelector\(\"a\"\);' 'let homepageURL = homepageRow.querySelector(\"\.text-link\");' $file
replace_line 'homepageURL.href = addon.homepageURL;' 'homepageURL.setAttribute("href", addon.homepageURL);' $file
replace_line '<a target="_blank" dir="ltr"><\/a>' \
'<label target="_blank" class="text-link" dir="ltr"><\/label>' $file
file="chrome/toolkit/content/mozapps/extensions/components/addon-card.mjs"
# Use our own strings for the removal prompt
replace_line 'let \{ BrowserAddonUI \} = windowRoot.window;' '' $file
replace_line 'await BrowserAddonUI.promptRemoveExtension' 'promptRemoveExtension' $file
# Customize empty-list message
replace_line 'createEmptyListMessage\(\) {' 'createEmptyListMessage() {
var p = document.createElement("p");
p.id = "empty-list-message";
return p;' chrome/toolkit/content/mozapps/extensions/components/addon-list.mjs
# Add zotero.ftl to the addons page for our own localized strings
replace_line 'href="toolkit\/about\/aboutAddons.ftl"' 'href="toolkit\/about\/aboutAddons.ftl" \/>\n <link rel="localization" href="zotero.ftl"' \
chrome/toolkit/content/mozapps/extensions/aboutaddons.html
# TODO: Use our own strings, but for now fix the bundled English ones
replace_line 'Manage Your Extensions' 'Manage Your Plugins' localization/en-US/toolkit/about/aboutAddons.ftl
replace_line 'Add-on' 'Plugin' localization/en-US/toolkit/about/aboutAddons.ftl
replace_line 'add-ons' 'plugins' localization/en-US/toolkit/about/aboutAddons.ftl
replace_line 'Select add-on to install' 'Select plugin to install' localization/en-US/toolkit/about/aboutAddons.ftl
replace_line 'pref\("extensions.htmlaboutaddons.recommendations.enabled".+' 'pref("extensions.htmlaboutaddons.recommendations.enabled", false);' greprefs.js
# Hide Report option
replace_line 'pref\("extensions.abuseReport.enabled".+' 'pref("extensions.abuseReport.enabled", false);' greprefs.js
# The first displayed Services.prompt dialog's size jumps around because sizeToContent() is called twice
# Fix by preventing the first sizeToContent() call if the icon hasn't been loaded yet
replace_line 'window.sizeToContent\(\);' 'if (ui.infoIcon.complete) window.sizeToContent();' chrome/toolkit/content/global/commonDialog.js
replace_line 'ui.infoIcon.addEventListener' 'if (!ui.infoIcon.complete) ui.infoIcon.addEventListener' chrome/toolkit/content/global/commonDialog.js
# Import style into built-in dialogs
replace_line 'id=\"commonDialogWindow\"' \
'id=\"commonDialogWindow\" class=\"zotero-dialog\" drawintitlebar-platforms=\"mac\"' \
chrome/toolkit/content/global/commonDialog.xhtml
# commonDialog.css link is split across multiple lines, so we have to do a weird substitution,
# so check the one-line global.css to make sure the format hasn't changed
check_line '<html:link rel="stylesheet" href="chrome:\/\/global\/skin\/global.css" \/>' chrome/toolkit/content/global/commonDialog.xhtml
replace_line 'chrome:\/\/global\/skin\/commonDialog.css"' \
'chrome:\/\/global\/skin\/commonDialog.css"\/>
<html:link rel="stylesheet" href="chrome:\/\/zotero-platform\/content\/zotero.css"' \
chrome/toolkit/content/global/commonDialog.xhtml
# commonDialog.xhtml can be opened via Services.prompt in the Profile Manager, so check for a profile first
replace_line 'function commonDialogOnLoad\(\) \{' \
'function commonDialogOnLoad() {
try {
Services.dirsvc.get(\"ProfD\", Ci.nsIFile);
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/include.js\", this);
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/titlebar.js\", this);
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/customElements.js\", this);
} catch (e) {}' \
chrome/toolkit/content/global/commonDialog.js
# Use native checkbox instead of Firefox-themed version in prompt dialogs
replace_line '<xul:checkbox' '<xul:checkbox native=\"true\"' chrome/toolkit/content/global/commonDialog.xhtml
# The <browser> CE appends its autoscroll <popup> to document.documentElement, which doesn't work in zoteroPane.xhtml
# (in Firefox browser.xhtml, document.documentElement is <html>, which can contain <popup>s; in zoteroPane.xhtml, it's
# <window>, which can't)
# Fix by appending it to a <popupset> instead
replace_line 'document.documentElement.appendChild\(this._autoScrollPopup\);' 'let popupset = document.querySelector("popupset");
if (!popupset) {
popupset = document.createXULElement("popupset");
document.documentElement.appendChild(popupset);
}
popupset.appendChild(this._autoScrollPopup);' chrome/toolkit/content/global/elements/browser-custom-element.mjs
# Add the dropmarker for toolbarbuttons with wantdropmarker, which Firefox no longer creates
replace_line 'this.appendChild\(this.constructor.fragment.cloneNode\(true\)\);' \
'this.appendChild(this.constructor.fragment.cloneNode(true));
if (this.hasAttribute("wantdropmarker")) {
let dropmarker = document.createXULElement("dropmarker");
dropmarker.setAttribute("type", "menu");
dropmarker.className = "toolbarbutton-menu-dropmarker";
this.appendChild(dropmarker);
}' chrome/toolkit/content/global/elements/toolbarbutton.js
# Let clicks on the dropmarker pass through to the toolbarbutton, which only opens its menu
# when it's the original event target, as xul.css did when Firefox created the dropmarker
echo '.toolbarbutton-menu-dropmarker { pointer-events: none; }' >> chrome/toolkit/content/global/xul.css
# Remove non-native text input styles
remove_between 'html\|input\:where\(' '^}' chrome/toolkit/skin/classic/global/global-shared.css
# Prevent window dragging on scrollbars
echo 'scrollbar { -moz-window-dragging: no-drag; }' >> chrome/toolkit/res/scrollbars.css
# Provide a mechanism for non-UA stylesheets to override the overflow property
# of a text control's anonymous text node child
echo '::placeholder, ::-moz-text-control-editing-root, ::-moz-text-control-preview {
overflow: var(--moz-text-control-overflow);
}' >> chrome/toolkit/res/forms.css
# Size an autocomplete popup to the input that opened it, or to an ancestor that designates
# itself as the width container. Firefox sizes the popup to its content. (For creator inputs.)
replace_line '\/\/ invalidate\(\) depends on the width attribute' \
'let widthElement = aElement.closest(".autocomplete-popup-width-container") || aElement;
this.style.setProperty("--panel-width", Math.max(widthElement.getBoundingClientRect().width, 100) + "px");
\/\/ invalidate() depends on the width attribute' \
chrome/toolkit/content/global/elements/autocomplete-popup.js
zip -qr9XD omni.ja *
mv omni.ja ..
cd ..
python3 "$APP_ROOT_DIR/scripts/optimizejars.py" --optimize ./ ./ ./
rm -rf omni
# Unzip browser/omni.ja and leave unzipped
cd browser
mkdir omni
mv omni.ja omni
cd omni
ls -la
set +e
unzip omni.ja
set -e
rm omni.ja
# Remove Firefox update URLs
remove_line 'pref\("app.update.url.(manual|details)' defaults/preferences/firefox-branding.js
# Remove Firefox overrides (e.g., to use Firefox-specific strings for connection errors)
remove_line '(override)' chrome/chrome.manifest
# Remove WebExtension APIs
remove_line ext-browser.json components/components.manifest
# Don't open a second window if app is already open when launching .exe (Windows) or running via
# command line (macOS)
replace_line 'function dch_handle\(cmdLine\) \{' 'function dch_handle(cmdLine) {
if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) { return; }
' modules/BrowserContentHandler.sys.mjs
}
mkdir -p xulrunner
cd xulrunner
if [ $BUILD_MAC == 1 ]; then
GECKO_VERSION="$GECKO_VERSION_MAC"
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
rm -rf Firefox.app
if [ -e "Firefox $GECKO_VERSION.app.zip" ]; then
echo "Using Firefox $GECKO_VERSION.app.zip"
unzip "Firefox $GECKO_VERSION.app.zip"
else
curl -fo Firefox.dmg "$DOWNLOAD_URL/mac/en-US/Firefox%20$GECKO_VERSION.dmg"
set +e
hdiutil detach -quiet /Volumes/Firefox 2>/dev/null
set -e
hdiutil attach -quiet Firefox.dmg
cp -a /Volumes/Firefox/Firefox.app .
# Store local copy of unmodified app
zip -r "Firefox $GECKO_VERSION.app.zip" Firefox.app
hdiutil detach -quiet /Volumes/Firefox
fi
# Download custom components (e.g., XUL), as an xz-compressed tarball of the MacOS folder with
# custom files in it
echo
rm -rf MacOS
if [ -n "$custom_components_hash_mac" ]; then
tarfile="Firefox $GECKO_VERSION MacOS.tar.xz"
if [[ "$(uname)" = "Darwin" ]]; then
shasum="shasum -a 256"
else
shasum=sha256sum
fi
download_components() {
echo "Downloading custom Firefox components"
echo
curl -fo "$tarfile" "${custom_components_url}mac/$custom_components_hash_mac.tar.xz"
}
verify_components_hash() {
echo "$custom_components_hash_mac $tarfile" | $shasum -c -
}
if [ ! -e "$tarfile" ]; then
download_components
fi
# Verify hash
echo
if ! verify_components_hash; then
echo "SHA hash mismatch. Redownloading custom Firefox components…"
rm -f "$tarfile"
download_components
echo
if ! verify_components_hash; then
echo "Hash still doesn't match. Aborting."
exit 1
fi
fi
echo
tar xvf "$tarfile"
echo
fi
pushd Firefox.app/Contents/Resources
modify_omni mac
popd
# Replace "FirefoxCP" with "ZoteroCP" for subprocesses ("Isolated Web Content", "Socket Process", "Web Content", etc.)
info_plist=Firefox.app/Contents/MacOS/plugin-container.app/Contents/Resources/English.lproj/InfoPlist.strings
from=$(get_utf16_chars "FirefoxCP")
to=$(get_utf16_chars "ZoteroCP")
perl -pi -e "s/$from/$to/" $info_plist
# Check for UTF-16 "ZoteroCP"
if ! grep -a -q "Z.o.t.e.r.o.C.P." $info_plist; then
echo '"ZoteroCP" not found in InfoPlist.strings after replacement'
exit 1
fi
rm -f Firefox.dmg
echo $("$SCRIPT_DIR/xulrunner_hash" -p m) > hash-mac
fi
if [ $BUILD_WIN == 1 ]; then
GECKO_VERSION="$GECKO_VERSION_WIN"
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
if [[ -n $arch ]]; then
arch_list=("$(get_canonical_arch w $arch)")
else
arch_list=(win-x64 win-arm64 win32)
fi
for arch in "${arch_list[@]}"; do
xdir=firefox-$arch
rm -rf $xdir
mkdir $xdir
archived_file="Firefox Setup $GECKO_VERSION-$arch.exe"
if [ -e "$archived_file" ]; then
echo "Using $archived_file"
cp "$archived_file" "Firefox%20Setup%20$GECKO_VERSION.exe"
else
if [ $arch = "win-x64" ]; then
moz_arch=win64
elif [ $arch = "win-arm64" ]; then
moz_arch=win64-aarch64
else
moz_arch=$arch
fi
curl -fO "$DOWNLOAD_URL/$moz_arch/en-US/Firefox%20Setup%20$GECKO_VERSION.exe"
# Store local copy of unmodified app
cp "Firefox%20Setup%20$GECKO_VERSION.exe" "$archived_file"
fi
7z x Firefox%20Setup%20$GECKO_VERSION.exe -o$xdir 'core/*'
mv $xdir/core $xdir-core
rm -rf $xdir
mv $xdir-core $xdir
# Download custom components, as an architecture-specific ZIP containing xul.dll, etc.
echo
hash_arch_name="${arch/-/_}"
arch_var_name="custom_components_hash_${hash_arch_name}"
hash=${!arch_var_name:-}
if [ -n "$hash" ]; then
zipfile="Firefox $GECKO_VERSION-$arch Components.zip"
# ZIP filename doesn't include "esr" or "win-"
url="${custom_components_url}win/${GECKO_VERSION//esr}-${arch#win-}-$hash.zip"
if [[ "$(uname)" = "Darwin" ]]; then
shasum="shasum -a 256"
else
shasum=sha256sum
fi
download_zip() {
echo "Downloading custom Firefox components"
echo
echo $url
curl -fo "$zipfile" "$url"
}
verify_hash() {
echo "$hash $zipfile" | $shasum -c -
}
# Download if file doesn't exist
if [ ! -e "$zipfile" ]; then
download_zip
fi
echo
if ! verify_hash; then
echo "SHA hash mismatch. Redownloading ZIP…"
rm -f "$zipfile"
download_zip
echo
if ! verify_hash; then
echo "Hash still doesn't match. Aborting."
exit 1
fi
fi
echo
unzip -o "$zipfile" -d "$xdir"
echo
fi
pushd $xdir
#
# NOTE: Below is disabled, since the full UTF-16 string no longer appears directly in the DLL
# -- it's now truncated after "e594e8d1" for x64/win32 and doesn't appear at all for ARM64
#
# Replace "Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38" with "Zotero" for C:\ProgramData directory
#
# Mozilla uses a UUID in the path because they previously used just "Mozilla" and needed to
# recreate the folder with correct permissions for security reasons, but we never had a folder in
# ProgramData, so we can just create it as "Zotero". Instead of using a custom xul.dll, just
# replace the hard-coded string with "Zotero" and add a bunch of NULs.
#
#from=$(get_utf16_chars "Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38")
#to=$(get_utf16_chars "Zotero" 38)
#perl -pe "s/$from/$to/" < xul.dll > xul.dll.new
#mv xul.dll.new xul.dll
# Check for UTF-16 "Zotero" in DLL
#
# (The macOS strings command doesn't have an encoding option, so skip on macOS. You need to
# build on Windows to make an installer, so a Windows build from macOS likely isn't being
# deployed.)
#
#if [[ "`uname`" != "Darwin" ]] && [[ -z "$(strings -e l xul.dll | grep -m 1 Zotero)" ]]; then
# echo '"Zotero" not found in xul.dll after replacement'
# exit 1
#fi
# We re-sign DLL in build.sh, but presumably this file, which is for the Widevine CDM,
# isn't vaid anymore
rm xul.dll.sig
modify_omni $arch
# Disable skeleton UI window
# https://forums.zotero.org/discussion/comment/437636/#Comment_437636
replace_line 'pref\("browser\.startup\.preXulSkeletonUI", true\);' 'pref("browser.startup.preXulSkeletonUI", false);' defaults/preferences/firefox.js
popd
# Uncomment to create local copies for reuse
#cp "Firefox%20Setup%20$GECKO_VERSION.exe" "Firefox Setup $GECKO_VERSION-$arch.exe"
rm "Firefox%20Setup%20$GECKO_VERSION.exe"
echo
echo
echo "$( "$SCRIPT_DIR/xulrunner_hash" -p w -a "$arch" )" > "hash-$arch"
done
fi
if [ $BUILD_LINUX == 1 ]; then
GECKO_VERSION="$GECKO_VERSION_LINUX"
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
if [[ -n $arch ]]; then
arch_list=("$arch")
else
arch_list=(x86_64 arm64)
fi
for arch in "${arch_list[@]}"; do
xdir="firefox-linux-$arch"
rm -rf $xdir
archived_file="firefox-$GECKO_VERSION-$arch.tar.xz"
if [ -e "$archived_file" ]; then
echo "Using $archived_file"
cp "$archived_file" "firefox-$GECKO_VERSION.tar.xz"
else
if [ $arch = "arm64" ]; then
moz_arch=aarch64
else
moz_arch=$arch
fi
curl -fO "$DOWNLOAD_URL/linux-$moz_arch/en-US/firefox-$GECKO_VERSION.tar.xz"
# Store local copy of unmodified app
cp "firefox-$GECKO_VERSION.tar.xz" "$archived_file"
fi
tar xvf firefox-$GECKO_VERSION.tar.xz
mv firefox $xdir
pushd $xdir
modify_omni $arch
popd
rm "firefox-$GECKO_VERSION.tar.xz"
echo "$( $SCRIPT_DIR/xulrunner_hash -p l -a "$arch" )" > "hash-linux-${arch}"
done
fi
echo Done

View file

@ -1,21 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
if [ -z "${1:-}" ]; then
echo "Commit hash not provided"
exit 1
fi
hash="$1"
tmpdir=`mktemp -d`
cd $tmpdir
curl -sS -f -L -o build.zip "https://$S3_BUCKET.s3.amazonaws.com/$S3_CI_ZIP_PATH/$hash.zip" >&2 \
|| (echo "ZIP file not found for commit '$hash'" && exit 1)
unzip build.zip >&2
rm build.zip
echo $tmpdir

View file

@ -1,14 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
if [ -z "${1:-}" ]; then
echo "Usage: $0 branch"
exit 1
fi
branch=$1
git ls-remote --exit-code $SOURCE_REPO_URL $branch | cut -f 1

View file

@ -1,322 +0,0 @@
#!/bin/bash
set -euo pipefail
#
# Incrementally update a staged build in staging/ by zipping changed files from
# the client build/ directory directly into app/omni.ja, based on a manifest
# saved by dir_build after the last full build.
#
# Only files that are copied into omni.ja unmodified by build.sh can be updated
# this way. If any other file changed (or a file was removed), a full rebuild
# is required.
#
# Exit codes:
# 0 -- staged build was updated (or nothing changed)
# 2 -- a full rebuild is required
#
# This is normally run automatically by dir_build, not directly.
#
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$APP_ROOT_DIR")"
. "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage {
cat >&2 <<DONE
Usage: $0 -p PLATFORM [-a ARCH] [-t]
Options
-p PLATFORM platform of the staged build to update (m=Mac, w=Windows, l=Linux)
-a ARCH target architecture (Windows/Linux only)
-t devtools requested
DONE
exit 1
}
platform=""
arch=""
devtools=0
while getopts "p:a:t" opt; do
case $opt in
p)
platform="$OPTARG"
;;
a)
arch="$OPTARG"
;;
t)
devtools=1
;;
*)
usage
;;
esac
done
if [ -z "$platform" ]; then
usage
fi
BUILD_SRC_DIR="$ROOT_DIR/build"
MANIFEST_FILE="$STAGE_DIR/.build-manifest"
function fall_back {
echo "$1 -- performing full build" >&2
exit 2
}
if [ ! -d "$BUILD_SRC_DIR" ]; then
fall_back "No build/ directory"
fi
if [ ! -f "$MANIFEST_FILE" ]; then
fall_back "No manifest from previous build"
fi
if [[ "$(sed -n 's/^#format=//p' "$MANIFEST_FILE")" != "2" ]]; then
fall_back "Unsupported manifest format"
fi
# Make sure a staged build exists for the requested platform
case $platform in
m)
app_dir="$STAGE_DIR/Zotero.app/Contents/Resources"
;;
w)
app_dir="$STAGE_DIR/Zotero_$(get_canonical_arch w "$arch")"
;;
l)
app_dir="$STAGE_DIR/Zotero_linux-$(get_canonical_arch l "$arch")"
;;
*)
fall_back "Unknown platform '$platform'"
;;
esac
if [ ! -f "$app_dir/app/omni.ja" ]; then
fall_back "No staged build for requested platform"
fi
# If the previous build didn't include tests or devtools and they're requested
# now, files are missing from the staged build
manifest_tests=$(sed -n 's/^#include_tests=//p' "$MANIFEST_FILE")
manifest_tests=${manifest_tests:-0}
manifest_devtools=$(sed -n 's/^#devtools=//p' "$MANIFEST_FILE")
manifest_devtools=${manifest_devtools:-0}
if [[ -z "${ZOTERO_TEST:-}" ]] || [[ "${ZOTERO_TEST:-}" == "0" ]]; then
want_tests=0
else
want_tests=1
fi
if [[ $want_tests -eq 1 ]] && [[ $manifest_tests != "1" ]]; then
fall_back "Tests requested but not included in staged build"
fi
if [[ $devtools -eq 1 ]] && [[ $manifest_devtools != "1" ]]; then
fall_back "Devtools requested but not included in staged build"
fi
# If anything changed in app/ (build scripts, assets, bundled modules, Firefox
# runtime), a full rebuild is required
manifest_app_hash=$(sed -n 's/^#app_hash=//p' "$MANIFEST_FILE")
app_hash=$(generate_app_hash "$APP_ROOT_DIR")
if [[ -z "$manifest_app_hash" ]] || [[ "$app_hash" != "$manifest_app_hash" ]]; then
fall_back "Files in app/ changed"
fi
file_list=$(mktemp)
new_stats=$(mktemp)
carried=$(mktemp)
candidates=$(mktemp)
removed=$(mktemp)
cand_entries=$(mktemp)
trap 'rm -f "$file_list" "$new_stats" "$carried" "$candidates" "$removed" "$cand_entries"' EXIT
cd "$BUILD_SRC_DIR"
# Quickly find files that may have changed based on size/mtime, so that only
# those have to be hashed. Unmodified manifest entries are carried over,
# new/modified files become candidates, and files no longer present are
# considered removed.
build_manifest_file_list > "$file_list"
stat_file_list < "$file_list" > "$new_stats"
awk -F'\t' -v carried="$carried" -v candidates="$candidates" -v removed="$removed" '
NR==FNR {
if (/^#/) next
hash[$3] = $1
stat[$3] = $2
next
}
{
if (!($2 in stat)) {
print > candidates
}
else {
if ($1 == stat[$2]) {
print hash[$2] "\t" $0 > carried
}
else {
print > candidates
}
delete stat[$2]
}
}
END {
for (path in stat) print path > removed
}
' "$MANIFEST_FILE" "$new_stats"
# Hash the candidates and compare to the previous hashes to find real changes
# -- A = added, C = changed, R = removed
if [ -s "$candidates" ]; then
paste \
<(cut -f2 "$candidates" | sed 's,^,./,' | tr '\n' '\0' | hash_file_list | cut -f1) \
"$candidates" \
> "$cand_entries"
fi
changes=$(
if [ -s "$cand_entries" ]; then
awk -F'\t' '
NR==FNR {
if (/^#/) next
old[$3] = $1
next
}
{
if (!($3 in old)) print "A\t" $3
else if (old[$3] != $1) print "C\t" $3
}
' "$MANIFEST_FILE" "$cand_entries"
fi
if [ -s "$removed" ]; then
awk '{print "R\t" $0}' "$removed"
fi
)
# Make sure all changed files can be zipped directly into omni.ja, and skip
# ones that aren't included in the staged build
zip_files=()
ftl_files=()
test_copy_files=()
while IFS=$'\t' read -r status path; do
if [ -z "$status" ]; then
continue
fi
if [ "$status" == "R" ]; then
case "$path" in
test/*)
# Ignore removed test files if tests weren't included
if [[ $manifest_tests != "1" ]]; then
continue
fi
;;
esac
fall_back "$path was removed"
fi
case "$path" in
# Copied into localization/ subdirectories by build.sh
chrome/locale/*/zotero/mozilla/*)
fall_back "Can't update $path in place"
;;
# Pruned by prepare_build
chrome/content/zotero/locale/csl/*)
fall_back "Can't update $path in place"
;;
# Copied to localization/<locale>/ in addition to the chrome path
chrome/locale/*/zotero/*.ftl)
zip_files+=("$path")
ftl_files+=("$path")
;;
chrome/*|components/*|resource/*)
zip_files+=("$path")
;;
# Appended to the main chrome.manifest by build.sh
test/chrome.manifest)
fall_back "Can't update $path in place"
;;
test/*)
# If tests weren't included in the staged build, ignore changes
# to test files
if [[ $manifest_tests == "1" ]]; then
zip_files+=("$path")
case "$path" in
# Also copied to tests/ at the top level of the app
test/tests/*)
test_copy_files+=("$path")
;;
esac
fi
;;
*)
fall_back "Can't update $path in place"
;;
esac
done <<< "$changes"
# Find all staged omni.ja files to update (multiple can exist -- e.g., all
# Windows architectures)
omni_files=()
while IFS= read -r file; do
omni_files+=("$file")
done < <(find "$STAGE_DIR" -path '*/app/omni.ja')
if [ ${#zip_files[@]} -gt 0 ]; then
echo "Changed files:"
for path in "${zip_files[@]}"; do
echo " $path"
done
cd "$BUILD_SRC_DIR"
for omni_file in "${omni_files[@]}"; do
echo "Updating ${omni_file#"$STAGE_DIR"/}"
zip -qX "$omni_file" "${zip_files[@]}"
done
# Copy .ftl files to their localization/ paths
if [ ${#ftl_files[@]} -gt 0 ]; then
l10n_dir=$(mktemp -d)
for path in "${ftl_files[@]}"; do
locale=${path#chrome/locale/}
locale=${locale%%/*}
mkdir -p "$l10n_dir/localization/$locale"
cp -L "$path" "$l10n_dir/localization/$locale/$(basename "$path")"
done
pushd "$l10n_dir" > /dev/null
for omni_file in "${omni_files[@]}"; do
zip -qrX "$omni_file" localization
done
popd > /dev/null
rm -rf "$l10n_dir"
fi
# Copy test files to tests/ directories
if [ ${#test_copy_files[@]} -gt 0 ]; then
for path in "${test_copy_files[@]}"; do
for omni_file in "${omni_files[@]}"; do
dest="${omni_file%/app/omni.ja}/tests/${path#test/tests/}"
mkdir -p "$(dirname "$dest")"
cp -L "$path" "$dest"
done
done
fi
# Bump BuildID so that startup caches are invalidated even without
# -purgecaches
build_id=$(date +%Y%m%d%H%M%S)
for omni_file in "${omni_files[@]}"; do
perl -pi -e "s/^BuildID=.*/BuildID=$build_id/" "$(dirname "$omni_file")/application.ini"
done
echo "Updated staged build with ${#zip_files[@]} changed file(s)"
elif [ -n "$changes" ]; then
echo "No staged files changed"
else
echo "No changes since last build"
fi
{
echo "#format=2"
echo "#include_tests=$manifest_tests"
echo "#devtools=$manifest_devtools"
echo "#app_hash=$app_hash"
LC_ALL=C sort -t$'\t' -k3 "$carried" "$cand_entries"
} > "$MANIFEST_FILE.tmp"
mv "$MANIFEST_FILE.tmp" "$MANIFEST_FILE"

View file

@ -1,88 +0,0 @@
#!/bin/bash
#
# Manage list of deployed version numbers for a channel in order to generate incremental builds
#
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
cat >&2 <<DONE
Usage:
-c CHANNEL Release channel (e.g., 'beta'); defaults to 'release'
-p PLATFORM Platform (m=Mac, w=Windows, l=Linux)
-a VERSION Add version to incrementals list; cannot be used with -n
-n NUM_VERSIONS Number of previous versions to return; cannot be used with -a
DONE
exit 1
}
CHANNEL="release"
PLATFORM=""
VERSION=""
NUM_VERSIONS=""
while getopts "c:p:a:n:" opt; do
case $opt in
c)
CHANNEL="$OPTARG"
;;
p)
case "$OPTARG" in
m) PLATFORM=mac;;
w) PLATFORM=win;;
l) PLATFORM=linux;;
*)
echo "$0: Invalid platform option $OPTARG"
usage
;;
esac
;;
a)
VERSION="$OPTARG"
;;
n)
NUM_VERSIONS="$OPTARG"
;;
*)
usage
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
if [[ -z "$PLATFORM" ]]; then
usage
fi
if [[ -z "$VERSION" ]] && [[ -z "$NUM_VERSIONS" ]]; then
usage
fi
if [[ "$VERSION" ]] && [[ "$NUM_VERSIONS" ]]; then
usage
fi
INCR_FILENAME="incrementals-$PLATFORM"
REMOTE_PATH="$DEPLOY_PATH/manifests/$CHANNEL/$INCR_FILENAME"
INCR_PATH="$DIST_DIR/$INCR_FILENAME"
mkdir -p "$DIST_DIR"
scp $DEPLOY_HOST:"$REMOTE_PATH" "$INCR_PATH" >&2
# Add version to file and reupload
if [ "$VERSION" ]; then
echo "Adding $VERSION to incrementals-$PLATFORM"
echo $VERSION >> "$INCR_PATH"
scp "$INCR_PATH" $DEPLOY_HOST:"$REMOTE_PATH"
# Show last n versions
elif [ "$NUM_VERSIONS" -gt 0 ]; then
# TEMP: Don't include 6.0 versions
#tail -n $NUM_VERSIONS "$INCR_PATH"
set +e
tail -n $NUM_VERSIONS "$INCR_PATH" | grep -v '^6\.0'
set -e
fi
rm "$INCR_PATH"

View file

@ -1,22 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage {
echo "Usage: $0 id"
exit 1
}
id=${1:-}
if [[ -z "$id" ]]; then
usage
fi
prepare_notary_auth
xcrun notarytool log "$id" "${notary_auth[@]}" notary_log.json
cat notary_log.json

View file

@ -1,19 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
echo "Usage: $0 file"
exit 1
}
file=${1:-}
if [[ -z "$file" ]]; then
usage
fi
echo "Stapling $file"
xcrun stapler staple $file

View file

@ -1,22 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage {
echo "Usage: $0 file"
exit 1
}
file=${1:-}
if [[ -z "$file" ]]; then
usage
fi
prepare_notary_auth
echo "Uploading ${file##*/} to Apple for notarization" >&2
xcrun notarytool submit $file "${notary_auth[@]}" --wait

View file

@ -1,376 +0,0 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Taras Glek <tglek@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import sys, os, subprocess, struct, re
local_file_header = [
("signature", "uint32"),
("min_version", "uint16"),
("general_flag", "uint16"),
("compression", "uint16"),
("lastmod_time", "uint16"),
("lastmod_date", "uint16"),
("crc32", "uint32"),
("compressed_size", "uint32"),
("uncompressed_size", "uint32"),
("filename_size", "uint16"),
("extra_field_size", "uint16"),
("filename", "filename_size"),
("extra_field", "extra_field_size"),
("data", "compressed_size")
]
cdir_entry = [
("signature", "uint32"),
("creator_version", "uint16"),
("min_version", "uint16"),
("general_flag", "uint16"),
("compression", "uint16"),
("lastmod_time", "uint16"),
("lastmod_date", "uint16"),
("crc32", "uint32"),
("compressed_size", "uint32"),
("uncompressed_size", "uint32"),
("filename_size", "uint16"),
("extrafield_size", "uint16"),
("filecomment_size", "uint16"),
("disknum", "uint16"),
("internal_attr", "uint16"),
("external_attr", "uint32"),
("offset", "uint32"),
("filename", "filename_size"),
("extrafield", "extrafield_size"),
("filecomment", "filecomment_size"),
]
cdir_end = [
("signature", "uint32"),
("disk_num", "uint16"),
("cdir_disk", "uint16"),
("disk_entries", "uint16"),
("cdir_entries", "uint16"),
("cdir_size", "uint32"),
("cdir_offset", "uint32"),
("comment_size", "uint16"),
]
type_mapping = { "uint32":"I", "uint16":"H"}
def format_struct (format):
string_fields = {}
fmt = "<"
for (name,value) in iter(format):
try:
fmt += type_mapping[value][0]
except KeyError:
string_fields[name] = value
return (fmt, string_fields)
def size_of(format):
return struct.calcsize(format_struct(format)[0])
class MyStruct:
def __init__(self, format, string_fields):
self.__dict__["struct_members"] = {}
self.__dict__["format"] = format
self.__dict__["string_fields"] = string_fields
def addMember(self, name, value):
self.__dict__["struct_members"][name] = value
def __getattr__(self, item):
try:
return self.__dict__["struct_members"][item]
except:
pass
print("no %s" %item)
print(self.__dict__["struct_members"])
raise AttributeError
def __setattr__(self, item, value):
if item in self.__dict__["struct_members"]:
self.__dict__["struct_members"][item] = value
else:
raise AttributeError
def pack(self):
extra_data = b""
values = []
string_fields = self.__dict__["string_fields"]
struct_members = self.__dict__["struct_members"]
format = self.__dict__["format"]
for (name,_) in format:
if name in string_fields:
if not isinstance(struct_members[name], bytes):
struct_members[name] = struct_members[name].encode('utf-8')
extra_data = extra_data + struct_members[name]
else:
values.append(struct_members[name]);
return struct.pack(format_struct(format)[0], *values) + extra_data
ENDSIG = 0x06054b50
def assert_true(cond, msg):
if not cond:
raise Exception(msg)
exit(1)
class BinaryBlob:
def __init__(self, f):
self.data = open(f, "rb").read()
self.offset = 0
self.length = len(self.data)
def readAt(self, pos, length):
self.offset = pos + length
return self.data[pos:self.offset]
def read_struct (self, format, offset = None):
if offset == None:
offset = self.offset
(fstr, string_fields) = format_struct(format)
size = struct.calcsize(fstr)
data = self.readAt(offset, size)
ret = struct.unpack(fstr, data)
retstruct = MyStruct(format, string_fields)
i = 0
for (name,_) in iter(format):
member_desc = None
if not name in string_fields:
member_data = ret[i]
i = i + 1
else:
# zip has data fields which are described by other struct fields, this does
# additional reads to fill em in
member_desc = string_fields[name]
member_data = self.readAt(self.offset, retstruct.__getattr__(member_desc))
retstruct.addMember(name, member_data)
# sanity check serialization code
data = self.readAt(offset, self.offset - offset)
out_data = retstruct.pack()
assert_true(out_data == data, "Serialization fail %d !=%d"% (len(out_data), len(data)))
return retstruct
def optimizejar(jar, outjar, inlog = None):
if inlog is not None:
inlog = open(inlog).read().rstrip()
# in the case of an empty log still move the index forward
if len(inlog) == 0:
inlog = []
else:
inlog = inlog.split("\n")
outlog = []
jarblob = BinaryBlob(jar)
dirend = jarblob.read_struct(cdir_end, jarblob.length - size_of(cdir_end))
assert_true(dirend.signature == ENDSIG, "no signature in the end");
cdir_offset = dirend.cdir_offset
readahead = 0
if inlog is None and cdir_offset == 4:
readahead = struct.unpack("<I", jarblob.readAt(0, 4))[0]
print("%s: startup data ends at byte %d" % (outjar, readahead));
total_stripped = 0;
jarblob.offset = cdir_offset
central_directory = []
for i in range(0, dirend.cdir_entries):
entry = jarblob.read_struct(cdir_entry)
if entry.filename[-1:] == "/":
total_stripped += len(entry.pack())
else:
total_stripped += entry.extrafield_size
central_directory.append(entry)
reordered_count = 0
if inlog is not None:
dup_guard = set()
for ordered_name in inlog:
if ordered_name in dup_guard:
continue
else:
dup_guard.add(ordered_name)
found = False
for i in range(reordered_count, len(central_directory)):
if central_directory[i].filename == ordered_name:
# swap the cdir entries
tmp = central_directory[i]
central_directory[i] = central_directory[reordered_count]
central_directory[reordered_count] = tmp
reordered_count = reordered_count + 1
found = True
break
if not found:
print( "Can't find '%s' in %s" % (ordered_name, jar))
outfd = open(outjar, "wb")
out_offset = 0
if inlog is not None:
# have to put central directory at offset 4 cos 0 confuses some tools.
# This also lets us specify how many entries should be preread
dirend.cdir_offset = 4
# make room for central dir + end of dir + 4 extra bytes at front
out_offset = dirend.cdir_offset + dirend.cdir_size + size_of(cdir_end) - total_stripped
outfd.seek(out_offset)
cdir_data = b""
written_count = 0
crc_mapping = {}
dups_found = 0
dupe_bytes = 0
# store number of bytes suggested for readahead
for entry in central_directory:
# read in the header twice..first for comparison, second time for convenience when writing out
jarfile = jarblob.read_struct(local_file_header, entry.offset)
assert_true(jarfile.filename == entry.filename, "Directory/Localheader mismatch")
# drop directory entries
if entry.filename[-1:] == "/":
total_stripped += len(jarfile.pack())
dirend.cdir_entries -= 1
continue
# drop extra field data
else:
total_stripped += jarfile.extra_field_size;
entry.extrafield = jarfile.extra_field = ""
entry.extrafield_size = jarfile.extra_field_size = 0
# January 1st, 2010
entry.lastmod_date = jarfile.lastmod_date = ((2010 - 1980) << 9) | (1 << 5) | 1
entry.lastmod_time = jarfile.lastmod_time = 0
data = jarfile.pack()
outfd.write(data)
old_entry_offset = entry.offset
entry.offset = out_offset
out_offset = out_offset + len(data)
entry_data = entry.pack()
cdir_data += entry_data
expected_len = entry.filename_size + entry.extrafield_size + entry.filecomment_size
assert_true(len(entry_data) != expected_len,
"%s entry size - expected:%d got:%d" % (entry.filename, len(entry_data), expected_len))
written_count += 1
if entry.crc32 in crc_mapping:
dups_found += 1
dupe_bytes += entry.compressed_size + len(data) + len(entry_data)
print("%s\n\tis a duplicate of\n%s\n---"%(entry.filename, crc_mapping[entry.crc32]))
else:
crc_mapping[entry.crc32] = entry.filename;
if inlog is not None:
if written_count == reordered_count:
readahead = out_offset
print("%s: startup data ends at byte %d"%( outjar, readahead));
elif written_count < reordered_count:
pass
#print("%s @ %d" % (entry.filename, out_offset))
elif readahead >= old_entry_offset + len(data):
outlog.append(entry.filename)
reordered_count += 1
if inlog is None:
dirend.cdir_offset = out_offset
if dups_found > 0:
print("WARNING: Found %d duplicate files taking %d bytes"%(dups_found, dupe_bytes))
dirend.cdir_size = len(cdir_data)
dirend.disk_entries = dirend.cdir_entries
dirend_data = dirend.pack()
assert_true(size_of(cdir_end) == len(dirend_data), "Failed to serialize directory end correctly. Serialized size;%d, expected:%d"%(len(dirend_data), size_of(cdir_end)));
outfd.seek(dirend.cdir_offset)
outfd.write(cdir_data)
outfd.write(dirend_data)
# for ordered jars the central directory is written in the begining of the file, so a second central-directory
# entry has to be written in the end of the file
if inlog is not None:
outfd.seek(0)
outfd.write(struct.pack("<I", readahead));
outfd.seek(out_offset)
outfd.write(dirend_data)
print("Stripped %d bytes" % total_stripped)
print("%s %d/%d in %s" % (("Ordered" if inlog is not None else "Deoptimized"),
reordered_count, len(central_directory), outjar))
outfd.close()
return outlog
if len(sys.argv) != 5:
print("Usage: --optimize|--deoptimize %s JAR_LOG_DIR IN_JAR_DIR OUT_JAR_DIR" % sys.argv[0])
exit(1)
jar_regex = re.compile("\\.jar?$")
def optimize(JAR_LOG_DIR, IN_JAR_DIR, OUT_JAR_DIR):
ls = os.listdir(IN_JAR_DIR)
for jarfile in ls:
if not re.search(jar_regex, jarfile):
continue
injarfile = os.path.join(IN_JAR_DIR, jarfile)
outjarfile = os.path.join(OUT_JAR_DIR, jarfile)
logfile = os.path.join(JAR_LOG_DIR, jarfile + ".log")
if not os.path.isfile(logfile):
logfile = None
optimizejar(injarfile, outjarfile, logfile)
def deoptimize(JAR_LOG_DIR, IN_JAR_DIR, OUT_JAR_DIR):
if not os.path.exists(JAR_LOG_DIR):
os.makedirs(JAR_LOG_DIR)
ls = os.listdir(IN_JAR_DIR)
for jarfile in ls:
if not re.search(jar_regex, jarfile):
continue
injarfile = os.path.join(IN_JAR_DIR, jarfile)
outjarfile = os.path.join(OUT_JAR_DIR, jarfile)
logfile = os.path.join(JAR_LOG_DIR, jarfile + ".log")
log = str(optimizejar(injarfile, outjarfile, None))
open(logfile, "wb").write("\n".join(log).encode('utf-8'))
def main():
MODE = sys.argv[1]
JAR_LOG_DIR = sys.argv[2]
IN_JAR_DIR = sys.argv[3]
OUT_JAR_DIR = sys.argv[4]
if MODE == "--optimize":
optimize(JAR_LOG_DIR, IN_JAR_DIR, OUT_JAR_DIR)
elif MODE == "--deoptimize":
deoptimize(JAR_LOG_DIR, IN_JAR_DIR, OUT_JAR_DIR)
else:
print("Unknown mode %s" % MODE)
exit(1)
if __name__ == '__main__':
main()

View file

@ -1,218 +0,0 @@
#!/usr/bin/env python3
import sys
import os
import argparse
import tempfile
import shutil
import subprocess
import re
import fileinput
from collections import OrderedDict
import json
import traceback
# Hack to combine two argparse formatters
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
description='Prepare build/ files for the app build process',
formatter_class=CustomFormatter)
parser.add_argument('--source-dir', '-s', required=True, metavar='BUILD_DIR', help='Directory to build from')
parser.add_argument('--output-dir', '-o', required=True, metavar='OUTPUT_DIR', help='Directory to write files to')
parser.add_argument('-c', '--channel', default='source', help='channel to add to dev build version number (e.g., "beta" for "5.0-beta.3+a5f28ca8"), or "release" or "source" to skip')
parser.add_argument('--commit-hash', '-m', metavar='HASH', help='Commit hash (required for non-release builds)')
args = parser.parse_args()
def main():
try:
app_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
lastrev_dir = os.path.join(app_root_dir, 'lastrev')
if not os.path.exists(lastrev_dir):
os.mkdir(lastrev_dir)
tmp_dir = os.path.join(app_root_dir, 'tmp')
if args.commit_hash:
commit_hash = args.commit_hash[0:9]
elif args.channel != "release":
raise Exception("--commit-hash must be specified for non-release builds")
src_dir = args.source_dir
if not os.path.isdir(src_dir):
raise Exception(src_dir + " is not a directory")
output_dir = args.output_dir
if not os.path.isdir(output_dir):
raise Exception(output_dir + " is not a directory")
if os.listdir(output_dir):
raise Exception(output_dir + " is not empty")
log("Using source directory of " + src_dir)
os.chdir(src_dir)
if not os.path.exists('version'):
raise FileNotFoundError("version file not found in {0}".format(src_dir))
# Extract version number from version file
with open('version') as f:
rdf = f.read()
m = re.search('([0-9].+)\\.SOURCE', rdf)
if not m:
raise Exception("Version number not found in version file")
version = m.group(1)
# Remove tmp build directory if it already exists
if os.path.exists(tmp_dir):
shutil.rmtree(tmp_dir)
os.mkdir(tmp_dir)
tmp_src_dir = os.path.join(tmp_dir, 'zotero')
# Export a clean copy of the source tree -- with Python on Windows, where rsync,
# if present at all, doesn't handle Windows-style paths
if os.name == 'nt' or not shutil.which('rsync'):
shutil.copytree(
'.',
tmp_src_dir,
ignore=shutil.ignore_patterns('.*', '#*', 'package.json', 'package-lock.json')
)
else:
subprocess.check_call([
'rsync',
'-aL',
# Exclude hidden files
'--exclude', '.*',
'--exclude', '#*',
'--exclude', 'package.json',
'--exclude', 'package-lock.json',
'.' + os.sep,
tmp_src_dir + os.sep
])
# Make sure the copy worked
d = os.path.join(tmp_src_dir, 'chrome')
if not os.path.isdir(d):
raise FileNotFoundError(d + " not found")
# Delete CSL locale support files
csl_dir = os.path.normpath(tmp_src_dir + '/chrome/content/zotero/locale/csl/')
for root, dirs, files in os.walk(csl_dir, topdown=False):
for name in files:
if not name.endswith('.xml') and name != 'locales.json':
os.remove(os.path.join(root, name))
for name in dirs:
d = os.path.join(root, name)
if not os.listdir(d):
os.rmdir(d)
# Delete styles build script
os.remove(os.path.join(tmp_src_dir, 'styles', 'update'))
translators_dir = os.path.join(tmp_src_dir, 'translators')
# Move deleted.txt out of translators directory
f = os.path.join(translators_dir, 'deleted.txt')
if os.path.exists(f):
shutil.move(f, tmp_src_dir)
# Build translator index
index = OrderedDict()
for fn in sorted((fn for fn in os.listdir(translators_dir)), key=str.lower):
if not fn.endswith('.js'):
continue
with open(os.path.join(translators_dir, fn), 'r', encoding='utf-8') as f:
contents = f.read()
# Parse out the JSON metadata block
m = re.match(r'^\s*{[\S\s]*?}\s*?[\r\n]', contents)
if not m:
raise Exception("Metadata block not found in " + f.name)
metadata = json.loads(m.group(0))
index[metadata["translatorID"]] = {
"fileName": fn,
"label": metadata["label"],
"lastUpdated": metadata["lastUpdated"]
}
# Write translator index as JSON file
with open(os.path.join(tmp_src_dir, 'translators.json'), 'w', encoding='utf-8') as f:
json.dump(index, f, indent=True, ensure_ascii=False)
version_file = os.path.join(tmp_src_dir, 'version')
log('')
log_line()
log('Original version:\n')
dump_file(version_file)
# Modify version as necessary
# The dev build revision number is stored in lastrev/{version}-{channel}.
#
# If we're including it, get the current version number and increment it.
if args.channel not in ["release", "source"]:
lastrev_file = os.path.join(
lastrev_dir, '{0}-{1}'.format(version, args.channel)
)
if not os.path.exists(lastrev_file):
with open(lastrev_file, 'w') as f:
f.write("0")
rev = 1
else:
with open(lastrev_file, 'r') as f:
rev = f.read()
rev = int(rev if rev else 0) + 1
if args.channel == "release":
rev_sub_str = ""
elif args.channel == "source":
rev_sub_str = ".SOURCE.{0}".format(commit_hash)
else:
rev_sub_str = "-{0}.{1}+{2}".format(args.channel, str(rev), commit_hash)
# Update version
for line in fileinput.FileInput(version_file, inplace=1):
line = line.replace('.SOURCE', rev_sub_str)
print(line, file=sys.stdout, end='')
log('Modified version:\n')
dump_file(version_file)
log('')
log_line()
# Move source files to output directory
os.rmdir(output_dir)
shutil.move(tmp_src_dir, output_dir)
# Update lastrev file with new revision number
if args.channel not in ["release", "source"]:
with open(lastrev_file, 'w') as f:
f.write(str(rev))
return 0
except Exception as err:
sys.stderr.write("\n" + traceback.format_exc())
return 1
# Clean up
finally:
if 'tmp_src_dir' in locals() and os.path.exists(tmp_src_dir):
shutil.rmtree(tmp_src_dir)
def dump_file(f):
with open(f, 'r') as f:
log(f.read())
def log(msg):
print(msg, file=sys.stdout)
def log_line():
log('======================================================\n\n')
if __name__ == '__main__':
sys.exit(main())

View file

@ -1,46 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
CHANNEL="test"
BRANCH="main"
if [ "`uname`" = "Darwin" ]; then
export SAFARI_APPEX="$ROOT_DIR/../safari-web-extension-builds/ZoteroSafariExtension.appex"
export SAFARI_EXT_RESOURCES="$ROOT_DIR/../safari-web-extension-builds/test/safari"
export SAFARI_APP_EXTENSION="$ROOT_DIR/../safari-app-extension-builds/test/ZoteroSafariExtension.appex"
if [ ! -d "$SAFARI_APPEX" ]; then
echo "Safari extension stub not found at $SAFARI_APPEX -- aborting" >&2
exit 1
fi
if [ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]; then
echo "Safari web extension not found in $SAFARI_EXT_RESOURCES -- aborting" >&2
exit 1
fi
if [ ! -d "$SAFARI_APP_EXTENSION" ]; then
echo "Safari App Extension not found at $SAFARI_APP_EXTENSION -- aborting" >&2
exit 1
fi
fi
cd "$SCRIPT_DIR"
./check_requirements
hash=`./get_repo_branch_hash $BRANCH`
./check_app_matches_commit $hash
source_dir=`./get_commit_files $hash`
build_dir=`mktemp -d`
function cleanup {
rm -rf "$source_dir"
rm -rf "$build_dir"
}
trap cleanup EXIT
./prepare_build -s "$source_dir" -o "$build_dir" -c $CHANNEL -m $hash
VERSION="`cat \"$build_dir/version\"`"
./build_for_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL -i 1
ssh $DEPLOY_HOST "$DEPLOY_PATH/deploy" $CHANNEL "$VERSION" $BUILD_PLATFORMS

View file

@ -1,28 +0,0 @@
#!/bin/bash
#
# Upload build archives from 'dist' to S3 with the specified channel and version
#
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
echo Usage: $0 CHANNEL VERSION >&2
exit 1
}
CHANNEL="${1:-}"
VERSION="${2:-}"
if [[ -z "$CHANNEL" ]] || [[ -z "$VERSION" ]]; then
usage
fi
source_dir="$DIST_DIR"
if [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
source_dir=$(cygpath -w "$source_dir")
fi
url="s3://$S3_BUCKET/$S3_DIST_PATH/$CHANNEL/$VERSION/"
aws s3 sync --exclude "files-*" --exclude build_id "$source_dir" $url

View file

@ -1,187 +0,0 @@
get_current_platform() {
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo l
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo m
elif [[ "$OSTYPE" == "cygwin" ]]; then
echo w
elif [[ "$OSTYPE" == "msys" ]]; then
echo w
# Unknown, so probably Unix-y
else
echo l
fi
}
get_canonical_arch() {
local _platform=$1
local _arch=$2
case $_platform in
w)
case $_arch in
x64) _arch="win-x64" ;;
arm64) _arch="win-arm64" ;;
win32|win-x64|win-arm64) ;;
*) echo "Invalid Windows archicture: $_arch" >&2;;
esac
echo $_arch
;;
l)
[[ $_arch == x64 ]] && _arch="x86_64"
echo $_arch
;;
*)
echo "Invalid platform '$platform'" 2>&1
exit 1
esac
}
# Print a sorted NUL-separated list of the files in the current directory to
# include in the build manifest. Follows symlinks, like the rsync calls in
# prepare_build and build.sh.
function build_manifest_file_list {
# Exclusions match the rsync call in prepare_build
find -L . -mindepth 1 \( -name '.*' -o -name '#*' \) -prune \
-o -type f ! -name 'package.json' ! -name 'package-lock.json' -print0 \
| LC_ALL=C sort -z
}
# Read a NUL-separated file list on stdin and print "size|mtime<TAB>path" for
# each file
function stat_file_list {
if [[ "$OSTYPE" == "darwin"* ]]; then
xargs -0 stat -L -f '%z|%Fm|%N'
else
xargs -0 stat -L -c '%s|%y|%n'
fi | sed -E 's,^([0-9]+[|][^|]+)[|](\./)?,\1'$'\t'','
}
# Read a NUL-separated file list on stdin and print "hash<TAB>path" for each
# file
function hash_file_list {
local md5_cmd
if command -v md5sum > /dev/null 2>&1; then
md5_cmd="md5sum"
else
md5_cmd="md5 -r"
fi
xargs -0 $md5_cmd | sed -E 's,^([0-9a-f]{32}) ?(\./)?,\1'$'\t'','
}
# Generate a manifest of a build directory for incremental updates, with one
# "hash<TAB>size|mtime<TAB>path" line per file, sorted by path
function generate_build_manifest {
local dir=$1
(
cd "$dir"
local file_list
file_list=$(mktemp)
build_manifest_file_list > "$file_list"
paste \
<(hash_file_list < "$file_list" | cut -f1) \
<(stat_file_list < "$file_list")
rm -f "$file_list"
)
}
# Generate a hash of the files in app/ that affect build output, for detecting
# when a staged build can't be updated incrementally. Since some of the files
# are large binaries, use file sizes and modification times rather than
# contents. The xulrunner runtimes are covered by the hash-* files written by
# fetch_xulrunner.
function generate_app_hash {
local app_dir=$1
local md5_cmd
if command -v md5sum > /dev/null 2>&1; then
md5_cmd="md5sum"
else
md5_cmd="md5 -r"
fi
(
cd "$app_dir"
local paths=(assets build.sh config.sh scripts mac win linux modules update-packaging)
if [ -f config-custom.sh ]; then
paths+=(config-custom.sh)
fi
{
if [[ "$OSTYPE" == "darwin"* ]]; then
find -L "${paths[@]}" -name '.*' -prune -o -type f -print0 | xargs -0 stat -L -f '%N|%z|%m'
else
find -L "${paths[@]}" -name '.*' -prune -o -type f -print0 | xargs -0 stat -L -c '%n|%s|%Y'
fi
cat xulrunner/hash-* 2> /dev/null || true
} | LC_ALL=C sort | $md5_cmd | awk '{print $1}'
)
}
function check_line {
pattern=$1
file=$2
if ! grep -E -q "$pattern" "$file"; then
echo "$pattern" not found in "$file" -- aborting 2>&1
exit 1
fi
}
function replace_line {
pattern=$1
replacement=$2
file=$3
if grep -E -q "$pattern" "$file"; then
perl -pi -e "s/$pattern/$replacement/" "$file"
else
echo "$pattern" not found in "$file" -- aborting 2>&1
exit 1
fi
}
function remove_line {
pattern=$1
file=$2
if grep -E -q "$pattern" "$file"; then
grep -E -v "$pattern" "$file" > "$file.tmp"
mv "$file.tmp" "$file"
else
echo "$pattern" not found in "$file" -- aborting 2>&1
exit 1
fi
}
function remove_between {
start_pattern=$1
end_pattern=$2
file=$3
if grep -E -q "$start_pattern" "$file" && grep -E -q "$end_pattern" "$file"; then
perl -ni -e '
if (/'"$start_pattern"'/) { $skip = 1; next; }
elsif ($skip && /'"$end_pattern"'/) { $skip = 0; next; }
print unless $skip;' "$file"
else
echo "$start_pattern" and "$end_pattern" not found in "$file" -- aborting 2>&1
exit 1
fi
}
# Set $notary_auth to notarytool authentication arguments: the keychain profile in
# NOTARIZATION_PROFILE if set, or else the Apple ID and app-specific password
function prepare_notary_auth {
if [[ -n "${NOTARIZATION_PROFILE:-}" ]]; then
notary_auth=(--keychain-profile "$NOTARIZATION_PROFILE")
if [[ -n "$KEYCHAIN" ]]; then
keychain_path="$HOME/Library/Keychains/$KEYCHAIN.keychain-db"
# The keychain may have auto-locked since signing
if [[ -n "$KEYCHAIN_PASSWORD" ]]; then
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
fi
notary_auth+=(--keychain "$keychain_path")
fi
else
notary_auth=(--apple-id "$NOTARIZATION_USER" --team-id "$NOTARIZATION_TEAM_ID" --password "$NOTARIZATION_PASSWORD")
fi
}

View file

@ -1,95 +0,0 @@
#!/bin/bash
# Calculates a hash for the processed Firefox package for a given platform and architecture
#
# Usage:
# xulrunner_hash.sh -p <m|w|l> [-a ARCH]
#
# * **Mac (m)** ‑a is disallowed (universal build).
# * **Windows (w)** ‑a is **required**. Accepted values:
# win32 | x64 | arm64
# * **Linux (l)** ‑a is **required**. Accepted values:
# x86_64 | arm64 (alias x64 → x86_64)
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
cd "$APP_ROOT_DIR"
platform=""; arch=""
usage() {
cat >&2 <<'DONE'
Usage: xulrunner_hash.sh -p <m|w|l> [-a ARCH]
-p PLATFORM m (mac), w (windows), l (linux) [required]
-a ARCH *required* for Windows & Linux builds
Windows: x64 | arm64 | win32
Linux: x64 (or x86_64) | arm64
Mac: disallowed – always universal
DONE
exit 1
}
while getopts ':p:a:' opt; do
case $opt in
p) platform="$OPTARG";;
a) arch="$OPTARG";;
:) echo "Option -$OPTARG requires an argument" >&2; usage;;
? ) echo "Invalid option: -$OPTARG" >&2; echo; usage;;
esac
shift $((OPTIND-1)); OPTIND=1
done
[[ -z $platform ]] && { echo "-p is required" >&2; echo; usage; }
case $platform in
m)
[[ -n $arch ]] && { echo "-a is not allowed for Mac (universal build)" >&2; echo; usage; }
arch="universal"
GECKO_VERSION="$GECKO_VERSION_MAC"
;;
w)
[[ -z $arch ]] && { echo "-a is required for Windows builds" >&2; echo; usage; }
# Map CLI-friendly aliases → canonical form
case $arch in
x64) arch="win-x64" ;;
arm64) arch="win-arm64" ;;
win32|win-x64|win-arm64) ;; # already canonical
*) echo "Invalid Windows arch: $arch" >&2; echo; usage;;
esac
GECKO_VERSION="$GECKO_VERSION_WIN"
;;
l)
[[ -z $arch ]] && { echo "-a is required for Linux builds" >&2; echo; usage; }
[[ $arch == x64 ]] && arch="x86_64"
case $arch in
x86_64|arm64) ;;
*) echo "Invalid Linux arch: $arch" >&2; echo; usage;;
esac
GECKO_VERSION="$GECKO_VERSION_LINUX"
;;
*)
echo "Invalid platform: $platform" >&2; echo; usage;;
esac
# Concatenate scripts that affect the build
xulrunner_content=$(< "$SCRIPT_DIR/fetch_xulrunner")
xulrunner_content+=$(< "$SCRIPT_DIR/utils.sh")
xulrunner_content+=$(< "$APP_ROOT_DIR/assets/multilocale.txt")
# Get custom components hash
if [[ $platform = "m" ]]; then
components_hash="$custom_components_hash_mac"
else
hash_arch_name="${arch/-/_}"
arch_var_name="custom_components_hash_${hash_arch_name}"
components_hash=${!arch_var_name:-}
fi
input_string="${platform}-${arch}-${components_hash}|${GECKO_VERSION}|${xulrunner_content}"
printf "%s" "$input_string" | openssl dgst -sha256 | awk '{print $NF}'

View file

@ -1,411 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
UPDATE_STAGE_DIR="$SCRIPT_DIR/staging"
function usage {
cat >&2 <<DONE
Usage: $0 -c CHANNEL [-f] [-i FROM_VERSION] [-p PLATFORMS] [-l] VERSION
Options
-c CHANNEL Release channel ('release', 'beta')
-f Perform full build
-i FROM_VERSION Perform incremental build
-p PLATFORMS Platforms to build (m=Mac, w=Windows, l=Linux)
-l Use local TO directory instead of downloading TO files from S3
DONE
exit 1
}
# From https://gist.github.com/cdown/1163649#gistcomment-1639097
urlencode() {
local LANG=C
local length="${#1}"
for (( i = 0; i < length; i++ )); do
local c="${1:i:1}"
case $c in
[a-zA-Z0-9.~_-]) printf "$c" ;;
*) printf '%%%02X' "'$c" ;;
esac
done
}
BUILD_FULL=0
BUILD_INCREMENTAL=0
FROM=""
CHANNEL=""
BUILD_MAC=0
BUILD_WIN=0
BUILD_LINUX=0
USE_LOCAL_TO=0
while getopts "i:c:p:fl" opt; do
case $opt in
i)
FROM="$OPTARG"
BUILD_INCREMENTAL=1
;;
c)
CHANNEL="$OPTARG"
;;
p)
for i in `seq 0 1 $((${#OPTARG}-1))`
do
case ${OPTARG:i:1} in
m) BUILD_MAC=1;;
w) BUILD_WIN=1;;
l) BUILD_LINUX=1;;
*)
echo "$0: Invalid platform option ${OPTARG:i:1}"
usage
;;
esac
done
;;
f)
BUILD_FULL=1
;;
l)
USE_LOCAL_TO=1
;;
*)
usage
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
shift $(($OPTIND - 1))
TO=${1:-}
if [ -z "$TO" ]; then
usage
fi
if [ -z "$FROM" ] && [ $BUILD_FULL -eq 0 ]; then
usage
fi
if [[ -z "$CHANNEL" ]]; then
echo "Channel not provided" >&2
exit 1
fi
# Require at least one platform
if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; then
usage
fi
TO_MAJOR_VERSION="${TO%%.*}"
rm -rf "$UPDATE_STAGE_DIR"
mkdir "$UPDATE_STAGE_DIR"
INCREMENTALS_FOUND=0
for version in "$FROM" "$TO"; do
if [[ $version == "$TO" ]] && [[ $INCREMENTALS_FOUND == 0 ]] && [[ $BUILD_FULL == 0 ]]; then
exit
fi
if [ -z "$version" ]; then
continue
fi
echo "Getting Zotero version $version"
versiondir="$UPDATE_STAGE_DIR/$version"
#
# If -l passed, use main build script's staging directory for TO files rather than downloading
# the given version.
#
# The caller must ensure that the files in ../staging match the platforms and version given.
if [[ $version == $TO && $USE_LOCAL_TO == "1" ]]; then
if [ ! -d "$STAGE_DIR" ]; then
echo "Can't find local TO dir $STAGE_DIR"
exit 1
fi
echo "Using files from $STAGE_DIR"
ln -s "$STAGE_DIR" "$versiondir"
continue
fi
#
# Otherwise, download version from S3
#
mkdir -p "$versiondir"
cd "$versiondir"
# Determine Linux archive extension by major Zotero version
MAJOR_VERSION="${version%%.*}"
if [ "$MAJOR_VERSION" -lt 8 ]; then
LINUX_EXT="tar.bz2"
else
LINUX_EXT="tar.xz"
fi
MAC_ARCHIVE="Zotero-${version}.dmg"
WIN32_ARCHIVE="Zotero-${version}_win32.zip"
WIN_X64_ARCHIVE="Zotero-${version}_win-x64.zip"
WIN_ARM64_ARCHIVE="Zotero-${version}_win-arm64.zip"
LINUX_X86_ARCHIVE="Zotero-${version}_linux-i686.${LINUX_EXT}"
LINUX_X86_64_ARCHIVE="Zotero-${version}_linux-x86_64.${LINUX_EXT}"
LINUX_ARM64_ARCHIVE="Zotero-${version}_linux-arm64.${LINUX_EXT}"
CACHE_DIR="$ROOT_DIR/cache"
if [ ! -e "$CACHE_DIR" ]; then
mkdir "$CACHE_DIR"
fi
# Build archive list (no Linux arm64 before Zotero 8, no Linux i686 from Zotero 11)
ARCHIVES=(
"$MAC_ARCHIVE"
"$WIN32_ARCHIVE"
"$WIN_X64_ARCHIVE"
"$WIN_ARM64_ARCHIVE"
"$LINUX_X86_64_ARCHIVE"
)
if [ "$TO_MAJOR_VERSION" -lt 11 ]; then
ARCHIVES+=("$LINUX_X86_ARCHIVE")
fi
if [ "$MAJOR_VERSION" -ge 8 ]; then
ARCHIVES+=("$LINUX_ARM64_ARCHIVE")
fi
for archive in "${ARCHIVES[@]}"; do
if [[ $archive = "$MAC_ARCHIVE" ]] && [[ $BUILD_MAC != 1 ]]; then
continue
fi
if [[ $archive = "$WIN32_ARCHIVE" ]] && [[ $BUILD_WIN != 1 ]]; then
continue
fi
if [[ $archive = "$WIN_X64_ARCHIVE" ]] && [[ $BUILD_WIN != 1 ]]; then
continue
fi
if [[ $archive = "$WIN_ARM64_ARCHIVE" ]] && [[ $BUILD_WIN != 1 ]]; then
continue
fi
if [[ $archive = "$LINUX_X86_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then
continue
fi
if [[ $archive = "$LINUX_X86_64_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then
continue
fi
if [[ $archive = "$LINUX_ARM64_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then
continue
fi
ETAG_FILE="$CACHE_DIR/$archive.etag"
# Check cache for archive
if [[ -f "$CACHE_DIR/$archive" ]] && [[ -f "$CACHE_DIR/$archive.etag" ]]; then
ETAG="`cat $ETAG_FILE | tr '\n' ' '`"
else
ETAG=""
fi
rm -f $archive
# URL-encode '+' in beta version numbers
ENCODED_VERSION=`urlencode $version`
ENCODED_ARCHIVE=`urlencode $archive`
URL="https://$S3_BUCKET.s3.amazonaws.com/$S3_DIST_PATH/$CHANNEL/$ENCODED_VERSION/$ENCODED_ARCHIVE"
echo "Fetching $URL"
set +e
# Cached version is available
if [ -n "$ETAG" ]; then
HTTP_CODE=$(curl -sS -L -D headers.txt -o $archive -w '%{http_code}' -H "If-None-Match: $ETAG" "$URL")
NEW_ETAG=$(awk 'tolower($1) == "etag:" {print $2}' headers.txt | tr -d '\r' | tail -n 1)
rm -f headers.txt
# If ETag didn't match, cache newly downloaded version
if [ "$HTTP_CODE" = 200 ]; then
echo "ETag for $archive didn't match! -- using new version"
rm -f "$CACHE_DIR/$archive.etag"
cp $archive "$CACHE_DIR/"
echo "$NEW_ETAG" > "$CACHE_DIR/$archive.etag"
# If ETag matched (or there was another error), use cached version
else
echo "Using cached $archive"
cp "$CACHE_DIR/$archive" .
fi
else
curl -sS -f -L -D headers.txt -o $archive "$URL"
NEW_ETAG=$(awk 'tolower($1) == "etag:" {print $2}' headers.txt | tr -d '\r' | tail -n 1)
rm -f headers.txt
# Save archive to cache
rm -f "$CACHE_DIR/$archive.etag"
cp $archive "$CACHE_DIR/"
echo "$NEW_ETAG" > "$CACHE_DIR/$archive.etag"
fi
set -e
done
# Delete cached archives older than 14 days
find "$CACHE_DIR" -maxdepth 1 -type f -ctime +14 -delete
# Delete cached diffs and compressed files not created or used in 60 days
# (cache hits touch the entries)
if [ -d "$CACHE_DIR/update-packaging" ]; then
find "$CACHE_DIR/update-packaging" -type f -ctime +60 -delete
fi
# Unpack Zotero.app
if [ $BUILD_MAC == 1 ]; then
if [ -f "$MAC_ARCHIVE" ]; then
set +e
hdiutil detach -quiet /Volumes/Zotero 2>/dev/null
set -e
hdiutil attach -quiet "$MAC_ARCHIVE"
cp -R /Volumes/Zotero/Zotero.app "$versiondir"
rm "$MAC_ARCHIVE"
hdiutil detach -quiet /Volumes/Zotero
INCREMENTALS_FOUND=1
else
echo "$MAC_ARCHIVE not found"
fi
fi
# Unpack Windows zips
if [ $BUILD_WIN == 1 ]; then
if [[ -f "$WIN32_ARCHIVE" ]] && [[ -f "$WIN_X64_ARCHIVE" ]] && [[ -f "$WIN_ARM64_ARCHIVE" ]]; then
# Unpack architectures in parallel
UNPACK_PIDS=""
for build in "$WIN32_ARCHIVE" "$WIN_X64_ARCHIVE" "$WIN_ARM64_ARCHIVE"; do
(unzip -q "$build" && rm "$build") &
UNPACK_PIDS="$UNPACK_PIDS $!"
done
for pid in $UNPACK_PIDS; do
wait $pid
done
INCREMENTALS_FOUND=1
else
echo "$WIN32_ARCHIVE/$WIN_X64_ARCHIVE/$WIN_ARM64_ARCHIVE not found"
fi
fi
# Unpack Linux tarballs
if [ $BUILD_LINUX == 1 ]; then
# Zotero 7 has no Linux arm64 build; Zotero 11 has no Linux i686 build
LINUX_BUILDS_TO_UNPACK=("$LINUX_X86_64_ARCHIVE")
if [ "$TO_MAJOR_VERSION" -lt 11 ]; then
LINUX_BUILDS_TO_UNPACK+=("$LINUX_X86_ARCHIVE")
fi
if [ "$MAJOR_VERSION" -ge 8 ]; then
LINUX_BUILDS_TO_UNPACK+=("$LINUX_ARM64_ARCHIVE")
fi
MISSING=0
for build in "${LINUX_BUILDS_TO_UNPACK[@]}"; do
if [ ! -f "$build" ]; then
MISSING=1
break
fi
done
if [ "$MISSING" -eq 0 ]; then
# Unpack architectures in parallel
UNPACK_PIDS=""
for build in "${LINUX_BUILDS_TO_UNPACK[@]}"; do
(tar -xf "$build" && rm "$build") &
UNPACK_PIDS="$UNPACK_PIDS $!"
done
for pid in $UNPACK_PIDS; do
wait $pid
done
INCREMENTALS_FOUND=1
else
echo "Linux tarballs not found: ${LINUX_BUILDS_TO_UNPACK[*]}"
fi
fi
echo
done
# Set variables for mar command in make_(incremental|full)_update.sh
export MOZ_PRODUCT_VERSION="$TO"
export MAR_CHANNEL_ID="$CHANNEL"
# Reuse diffs and compressed files across incremental builds, since FROM
# versions often share files with each other and with the TO version. Entries
# that haven't been used recently are cleaned up above.
export UPDATE_CACHE_DIR="$ROOT_DIR/cache/update-packaging"
CHANGES_MADE=0
for build in "mac" "win32" "win-x64" "win-arm64" "linux-i686" "linux-x86_64" "linux-arm64"; do
# Zotero 7 has no Linux arm64 builds
if [[ $build == "linux-arm64" ]] && [[ $FROM == 7.* ]]; then
continue
fi
# Zotero 11 has no Linux i686 builds, so there's nothing to update to
if [[ $build == "linux-i686" ]] && [[ $TO_MAJOR_VERSION -ge 11 ]]; then
continue
fi
if [[ $build == "mac" ]]; then
if [[ $BUILD_MAC == 0 ]]; then
continue
fi
dir="Zotero.app"
else
if ([[ $build == "win32" ]] || [[ $build == "win-x64" ]] || [[ $build == "win-arm64" ]]) && [[ $BUILD_WIN == 0 ]]; then
continue
fi
if ([[ $build == "linux-i686" ]] || [[ $build == "linux-x86_64" ]] || [[ $build == "linux-arm64" ]]) && [[ $BUILD_LINUX == 0 ]]; then
continue
fi
dir="Zotero_$build"
fi
if [[ $BUILD_INCREMENTAL == 1 ]] && [[ -d "$UPDATE_STAGE_DIR/$FROM/$dir" ]]; then
echo
echo "Building incremental $build update from $FROM to $TO"
"$SCRIPT_DIR/make_incremental_update.sh" "$DIST_DIR/Zotero-${TO}-${FROM}_$build.mar" "$UPDATE_STAGE_DIR/$FROM/$dir" "$UPDATE_STAGE_DIR/$TO/$dir"
CHANGES_MADE=1
# If it's an incremental patch from a 6.0 build, use bzip instead of xz
if [[ $FROM = 6.0* ]]; then
echo "Building bzip2 version of incremental $build update from $FROM to $TO"
"$SCRIPT_DIR/xz_to_bzip" "$DIST_DIR/Zotero-${TO}-${FROM}_$build.mar" "$DIST_DIR/Zotero-${TO}-${FROM}_${build}_bz.mar"
rm "$DIST_DIR/Zotero-${TO}-${FROM}_$build.mar"
mv "$DIST_DIR/Zotero-${TO}-${FROM}_${build}_bz.mar" "$DIST_DIR/Zotero-${TO}-${FROM}_$build.mar"
fi
fi
if [[ $BUILD_FULL == 1 ]]; then
echo
echo "Building full $build update for $TO"
"$SCRIPT_DIR/make_full_update.sh" "$DIST_DIR/Zotero-${TO}-full_$build.mar" "$UPDATE_STAGE_DIR/$TO/$dir"
CHANGES_MADE=1
# Make a bzip version of all complete patches for serving to <7.0 builds. Don't bother with
# architectures that didn't exist in Z6.
#
# We can stop this once we do a waterfall build that all older versions get updated to.
if [[ $build != "win-x64" ]] && [[ $build != "win-arm64" ]]; then
echo "Building bzip2 version of full $build update for $TO"
"$SCRIPT_DIR/xz_to_bzip" "$DIST_DIR/Zotero-${TO}-full_$build.mar" "$DIST_DIR/Zotero-${TO}-full_bz_${build}.mar"
fi
fi
done
rm -rf "$UPDATE_STAGE_DIR"
# Update file manifests
if [ $CHANGES_MADE -eq 1 ]; then
# Cygwin has sha512sum, macOS has shasum, Linux has both
if [[ -n "`which sha512sum 2> /dev/null`" ]]; then
SHACMD="sha512sum"
else
SHACMD="shasum -a 512"
fi
cd "$DIST_DIR"
for platform in "mac" "win" "linux"; do
file=files-$platform
rm -f $file
for fn in `find . -name "*$platform*.mar" -exec basename {} \;`; do
size=`wc -c "$fn" | awk '{print $1}'`
hash=`$SHACMD "$fn" | awk '{print $1}'`
echo $fn $hash $size >> $file
done
done
fi

View file

@ -1,450 +0,0 @@
#!/bin/bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Code shared by update packaging scripts.
# Author: Darin Fisher
#
# -----------------------------------------------------------------------------
# Preserve a value set by the parent so it survives in the child processes
# spawned by run_parallel_tasks()
QUIET=${QUIET:-0}
# By default just assume that these tools exist on our path
MAR=${MAR:-mar}
MBSDIFF=${MBSDIFF:-mbsdiff}
XZ=${XZ:-xz}
$XZ --version > /dev/null 2>&1
if [ $? -ne 0 ]; then
# If $XZ is not set and not found on the path then this is probably
# running on a windows buildbot. Some of the Windows build systems have
# xz.exe in topsrcdir/xz/. Look in the places this would be in both a
# mozilla-central and comm-central build.
XZ="$(dirname "$(dirname "$(dirname "$0")")")/xz/xz.exe"
$XZ --version > /dev/null 2>&1
if [ $? -ne 0 ]; then
XZ="$(dirname "$(dirname "$(dirname "$(dirname "$0")")")")/xz/xz.exe"
$XZ --version > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "xz was not found on this system!"
echo "exiting"
exit 1
fi
fi
fi
# Ensure that we're always using the right compression settings.
# -T1 (single-threaded) is required: multi-threaded xz produces non-deterministic
# output, which would break both the reproducibility of MARs and the content-hash
# cache in process_update_task(). See get_cache_tag() for how changes here are
# accounted for in the cache.
export XZ_OPT="-T1 -7e"
# -----------------------------------------------------------------------------
# Helper routines
notice() {
echo "$*" 1>&2
}
verbose_notice() {
if [ $QUIET -eq 0 ]; then
notice "$*"
fi
}
get_file_size() {
info=($(ls -ln "$1"))
echo ${info[4]}
}
copy_perm() {
reference="$1"
target="$2"
if [ -x "$reference" ]; then
chmod 0755 "$target"
else
chmod 0644 "$target"
fi
}
make_add_instruction() {
f="$1"
filev3="$2"
# Used to log to the console
if [ $4 ]; then
forced=" (forced)"
else
forced=
fi
# Changed by Zotero for -e
is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/') || true
if [ $is_extension = "1" ]; then
# Use the subdirectory of the extensions folder as the file to test
# before performing this add instruction.
testdir=$(echo "$f" | sed 's/\(.*distribution\/extensions\/[^\/]*\)\/.*/\1/')
verbose_notice " add-if \"$testdir\" \"$f\""
echo "add-if \"$testdir\" \"$f\"" >> "$filev3"
else
verbose_notice " add \"$f\"$forced"
echo "add \"$f\"" >> "$filev3"
fi
}
check_for_add_if_not_update() {
add_if_not_file_chk="$1"
if [[ "$(basename "$add_if_not_file_chk")" = "channel-prefs.js" || \
"$add_if_not_file_chk" =~ (^|/)ChannelPrefs\.framework/ || \
"$(basename "$add_if_not_file_chk")" = "update-settings.ini" || \
"$add_if_not_file_chk" =~ (^|/)UpdateSettings\.framework/ ]]; then
## "true"
return 0;
fi
## "false"
return 1;
}
make_add_if_not_instruction() {
f="$1"
filev3="$2"
verbose_notice " add-if-not \"$f\" \"$f\""
echo "add-if-not \"$f\" \"$f\"" >> "$filev3"
}
make_patch_instruction() {
f="$1"
filev3="$2"
# Changed by Zotero for -e
is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/') || true
if [ $is_extension = "1" ]; then
# Use the subdirectory of the extensions folder as the file to test
# before performing this add instruction.
testdir=$(echo "$f" | sed 's/\(.*distribution\/extensions\/[^\/]*\)\/.*/\1/')
verbose_notice " patch-if \"$testdir\" \"$f.patch\" \"$f\""
echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> "$filev3"
else
verbose_notice " patch \"$f.patch\" \"$f\""
echo "patch \"$f.patch\" \"$f\"" >> "$filev3"
fi
}
append_remove_instructions() {
dir="$1"
filev3="$2"
if [ -f "$dir/removed-files" ]; then
listfile="$dir/removed-files"
elif [ -f "$dir/Contents/Resources/removed-files" ]; then
listfile="$dir/Contents/Resources/removed-files"
fi
if [ -n "$listfile" ]; then
# Changed by Zotero: Use subshell and disable filename globbing to prevent bash from expanding
# entries in removed-files with paths from the root (e.g., 'xulrunner/*')
(
set -f
# Map spaces to pipes so that we correctly handle filenames with spaces.
files=($(cat "$listfile" | tr " " "|" | sort -r))
num_files=${#files[*]}
for ((i=0; $i<$num_files; i=$i+1)); do
# Map pipes back to whitespace and remove carriage returns
f=$(echo ${files[$i]} | tr "|" " " | tr -d '\r')
# Trim whitespace
f=$(echo $f)
# Exclude blank lines.
if [ -n "$f" ]; then
# Exclude comments
if [ ! $(echo "$f" | grep -c '^#') = 1 ]; then
if [ $(echo "$f" | grep -c '/$') = 1 ]; then
verbose_notice " rmdir \"$f\""
echo "rmdir \"$f\"" >> "$filev3"
elif [ $(echo "$f" | grep -c '/\*$') = 1 ]; then
# Remove the *
f=$(echo "$f" | sed -e 's:\*$::')
verbose_notice " rmrfdir \"$f\""
echo "rmrfdir \"$f\"" >> "$filev3"
else
verbose_notice " remove \"$f\""
echo "remove \"$f\"" >> "$filev3"
fi
fi
fi
done
)
fi
}
# Return the number of parallel jobs to use for diffing/compression, from
# UPDATE_PACKAGING_JOBS or the number of cores
get_parallel_jobs() {
if [ -n "${UPDATE_PACKAGING_JOBS:-}" ]; then
echo "$UPDATE_PACKAGING_JOBS"
return 0
fi
nproc 2>/dev/null && return 0
sysctl -n hw.ncpu 2>/dev/null && return 0
echo 1
}
# Run per-file tasks from a task file in parallel by re-invoking the calling
# script with --run-task for each line. Tasks are ordered largest-file-first so
# that long-running jobs (e.g., diffing xul) start as early as possible.
#
# $1 - task file, with one "<type><TAB><relative path>" task per line
# $2 - directory to measure file sizes against for job ordering
# $3 - script to re-invoke (callers pass "$0" and handle --run-task before
# option parsing by calling process_update_task)
#
# Callers must export the variables needed by process_update_task().
run_parallel_tasks() {
local taskfile="$1"
local basedir="$2"
local script="$3"
local jobs
if [ ! -s "$taskfile" ]; then
return 0
fi
# xargs requires a resolvable command path
script="$(cd "$(dirname "$script")" && pwd)/$(basename "$script")"
# Compute the cache tag once here and export it so the per-file child
# processes inherit it rather than recomputing it for every file
if [ -n "${UPDATE_CACHE_DIR:-}" ] && [ -z "${UPDATE_CACHE_TAG:-}" ]; then
UPDATE_CACHE_TAG="$(get_cache_tag)"
export UPDATE_CACHE_TAG
fi
jobs=$(get_parallel_jobs)
notice "Processing $(wc -l < "$taskfile" | tr -d ' ') files with $jobs parallel jobs"
(cd "$basedir" && du -ak .) > "$taskfile.sizes"
awk -F'\t' 'NR==FNR { sizes[substr($2, 3)] = $1; next } { print sizes[$2] "\t" $0 }' \
"$taskfile.sizes" "$taskfile" \
| sort -rn \
| cut -f 2- \
| tr '\n' '\000' \
| xargs -0 -n 1 -P "$jobs" "$script" --run-task
}
# -----------------------------------------------------------------------------
# Diff and compression cache
#
# Building updates repeats the same expensive work: the same file contents get
# xz-compressed once per architecture, per FROM version, and per release, and
# the same before/after file pair gets binary-diffed again and again. When
# UPDATE_CACHE_DIR is set, we cache the results by content hash and reuse them
# across builds. Two kinds of entry are stored:
#
# full-<tag>-<hash>.xz A single file compressed with xz, keyed
# on the file's contents. Reused whenever
# the same content shows up again, in any
# architecture, version, or release.
#
# patch-<tag>-<oldhash>-<newhash>.xz The compressed binary diff between a
# file's old and new contents, keyed on
# both. Reused whenever the same
# before/after pair recurs (e.g., a file
# unchanged across several FROM versions
# being patched to the same TO).
#
# Both entries are written even when only one is ultimately packaged, so a file
# that loses the patch-vs-full size comparison in one build still seeds the
# cache for the next. <tag> is get_cache_tag(), so a toolchain/settings change
# starts a fresh namespace rather than reusing stale output. Entries are touched
# on use and expire via the time-based cleanup in build_autoupdate.sh.
# -----------------------------------------------------------------------------
# Print a SHA-256 content hash to use as a cache key
hash_file() {
if command -v sha256sum > /dev/null 2>&1; then
sha256sum "$1" | awk '{print $1}'
else
shasum -a 256 "$1" | awk '{print $1}'
fi
}
# Print a SHA-256 hash of stdin
hash_stdin() {
if command -v sha256sum > /dev/null 2>&1; then
sha256sum | awk '{print $1}'
else
shasum -a 256 | awk '{print $1}'
fi
}
# Print a short tag identifying the toolchain and options that produce cached
# files, so that entries are invalidated when any of them change. The cache
# keys files by input content only, on the assumption that the same inputs
# always compress/diff to the same output; this tag makes that assumption hold
# across xz/mbsdiff upgrades and XZ_OPT/BCJ_OPTIONS changes. (A change to any
# input invalidates both the full and patch caches, which is more than strictly
# necessary but keeps this simple; a stale build is just a cold rebuild.)
# Stale-tag entries are removed by the time-based cleanup in build_autoupdate.sh.
get_cache_tag() {
local mbsdiff_bin
mbsdiff_bin="$(command -v "$MBSDIFF" 2>/dev/null)"
{
$XZ --version 2>/dev/null | head -1
echo "XZ_OPT=$XZ_OPT"
echo "BCJ_OPTIONS=${BCJ_OPTIONS:-}"
# mbsdiff has no --version, so identify it by its binary contents
if [ -n "$mbsdiff_bin" ]; then
hash_file "$mbsdiff_bin"
else
echo "MBSDIFF=$MBSDIFF"
fi
} | hash_stdin | cut -c 1-16
}
# Atomically store file $2 in the cache as $1
cache_store() {
mkdir -p "$(dirname "$1")"
cp "$2" "$1.tmp.$$" && mv -f "$1.tmp.$$" "$1"
}
# xz-compress $1 to $2, reusing a cached result when one exists (see the cache
# overview above)
compress_full_file() {
local src="$1"
local dest="$2"
local key=""
if [ -n "${UPDATE_CACHE_DIR:-}" ]; then
key="$UPDATE_CACHE_DIR/full-${UPDATE_CACHE_TAG:-}-$(hash_file "$src").xz"
if [ -f "$key" ]; then
# Keep entries that are still in use from expiring
touch -c "$key" 2>/dev/null || true
cp "$key" "$dest"
return 0
fi
fi
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force --stdout "$src" > "$dest"
if [ -n "$key" ]; then
cache_store "$key" "$dest"
fi
}
# Process a single task line from run_parallel_tasks():
#
# full - compress the new file into the work directory
# diff - if the file changed, generate a binary diff and keep the smaller of
# the compressed patch ($f.patch) and the compressed file ($f)
#
# Expects newdir and workdir in the environment, plus olddir for diff tasks.
# The results are assembled into the manifest serially by the caller based on
# which files exist in the work directory. Diffs and compressed files are
# cached when enabled -- see the cache overview above.
process_update_task() {
local task="$1"
local tab=$(printf '\t')
local type="${task%%"$tab"*}"
local f="${task#*"$tab"}"
local oldfile_path newfile_path patch_path patchsize fullsize full_pid patchkey
if [ "$type" = "diff" ]; then
if diff "$olddir/$f" "$newdir/$f" > /dev/null; then
# Unchanged
return 0
fi
mkdir -p "$(dirname "$workdir/$f")"
verbose_notice "diffing \"$f\""
# Compress the full new file in the background while diffing, since both
# are needed to decide which to package
compress_full_file "$newdir/$f" "$workdir/$f" &
full_pid=$!
patchkey=""
if [ -n "${UPDATE_CACHE_DIR:-}" ]; then
patchkey="$UPDATE_CACHE_DIR/patch-${UPDATE_CACHE_TAG:-}-$(hash_file "$olddir/$f")-$(hash_file "$newdir/$f").xz"
fi
if [ -n "$patchkey" ] && [ -f "$patchkey" ]; then
# Keep entries that are still in use from expiring
touch -c "$patchkey" 2>/dev/null || true
cp "$patchkey" "$workdir/$f.patch.xz"
else
# mbsdiff doesn't like POSIX paths on Windows
if [ ${WIN_NATIVE:-0} -eq 1 ]; then
oldfile_path=$(cygpath -m "$olddir/$f")
newfile_path=$(cygpath -m "$newdir/$f")
patch_path=$(cygpath -m "$workdir/$f.patch")
else
oldfile_path="$olddir/$f"
newfile_path="$newdir/$f"
patch_path="$workdir/$f.patch"
fi
$MBSDIFF "$oldfile_path" "$newfile_path" "$patch_path"
$XZ $XZ_OPT --compress --lzma2 --format=xz --check=crc64 --force "$workdir/$f.patch"
if [ -n "$patchkey" ]; then
cache_store "$patchkey" "$workdir/$f.patch.xz"
fi
fi
wait $full_pid
copy_perm "$newdir/$f" "$workdir/$f"
patchsize=$(get_file_size "$workdir/$f.patch.xz")
fullsize=$(get_file_size "$workdir/$f")
if [ $patchsize -lt $fullsize ]; then
mv -f "$workdir/$f.patch.xz" "$workdir/$f.patch"
rm -f "$workdir/$f"
else
rm -f "$workdir/$f.patch.xz"
fi
else
mkdir -p "$(dirname "$workdir/$f")"
compress_full_file "$newdir/$f" "$workdir/$f"
copy_perm "$newdir/$f" "$workdir/$f"
fi
}
# List all files in the current directory, stripping leading "./"
# Pass a variable name and it will be filled as an array.
list_files() {
count=0
temp_filelist=$(mktemp)
find . -type f \
! -name "update.manifest" \
! -name "updatev2.manifest" \
! -name "updatev3.manifest" \
| sed 's/\.\/\(.*\)/\1/' \
| sort -r > "${temp_filelist}"
while read file; do
eval "${1}[$count]=\"$file\""
# Changed for Zotero to avoid eval as 1
#(( count++ ))
(( ++count ))
done < "${temp_filelist}"
rm "${temp_filelist}"
}
# List all directories in the current directory, stripping leading "./"
list_dirs() {
count=0
temp_dirlist=$(mktemp)
find . -type d \
! -name "." \
! -name ".." \
| sed 's/\.\/\(.*\)/\1/' \
| sort -r > "${temp_dirlist}"
while read dir; do
eval "${1}[$count]=\"$dir\""
# Changed for Zotero
#(( count++ ))
(( ++count ))
done < "${temp_dirlist}"
rm "${temp_dirlist}"
}

View file

@ -1,145 +0,0 @@
#!/bin/bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This tool generates full update packages for the update system.
# Author: Darin Fisher
#
# Added for Zotero
set -eo pipefail
. $(dirname "$0")/common.sh
# Process a single file task when invoked from run_parallel_tasks()
if [ "${1:-}" = "--run-task" ]; then
process_update_task "$2"
exit 0
fi
# -----------------------------------------------------------------------------
print_usage() {
notice "Usage: $(basename $0) [OPTIONS] ARCHIVE DIRECTORY"
}
if [ $# = 0 ]; then
print_usage
exit 1
fi
if [ $1 = -h ]; then
print_usage
notice ""
notice "The contents of DIRECTORY will be stored in ARCHIVE."
notice ""
notice "Options:"
notice " -h show this help text"
notice " -q be less verbose"
notice ""
exit 1
fi
if [ $1 = -q ]; then
QUIET=1
export QUIET
shift
fi
# -----------------------------------------------------------------------------
mar_command="$MAR -V ${MOZ_PRODUCT_VERSION:?} -H ${MAR_CHANNEL_ID:?}"
archive="$1"
targetdir="$2"
# Prevent the workdir from being inside the targetdir so it isn't included in
# the update mar.
if [ $(echo "$targetdir" | grep -c '/$') = 1 ]; then
# Remove the /
targetdir=$(echo "$targetdir" | sed -e 's:\/$::')
fi
workdir="$targetdir.work"
updatemanifestv3="$workdir/updatev3.manifest"
targetfiles="updatev3.manifest"
mkdir -p "$workdir"
# Generate a list of all files in the target directory.
pushd "$targetdir"
if test $? -ne 0 ; then
exit 1
fi
if [ ! -f "precomplete" ]; then
if [ ! -f "Contents/Resources/precomplete" ]; then
notice "precomplete file is missing!"
exit 1
fi
fi
list_files files
popd
# Add the type of update to the beginning of the update manifests.
> "$updatemanifestv3"
notice ""
notice "Adding type instruction to update manifests"
notice " type complete"
echo "type \"complete\"" >> "$updatemanifestv3"
notice ""
notice "Adding file add instructions to update manifests"
num_files=${#files[*]}
# Compress the files in parallel via run_parallel_tasks() in
# common.sh, and then assemble the manifest and archive list serially in the
# original order
tmpdir="$(mktemp -d)"
taskfile="$tmpdir/tasks"
TAB="$(printf '\t')"
> "$taskfile"
for ((i=0; $i<$num_files; i=$i+1)); do
echo "full$TAB${files[$i]}" >> "$taskfile"
done
newdir="$targetdir"
export newdir workdir BCJ_OPTIONS QUIET
run_parallel_tasks "$taskfile" "$targetdir" "$0"
rm -rf "$tmpdir"
for ((i=0; $i<$num_files; i=$i+1)); do
f="${files[$i]}"
if check_for_add_if_not_update "$f"; then
make_add_if_not_instruction "$f" "$updatemanifestv3"
else
make_add_instruction "$f" "$updatemanifestv3"
fi
targetfiles="$targetfiles \"$f\""
done
# Append remove instructions for any dead files.
notice ""
notice "Adding file and directory remove instructions from file 'removed-files'"
append_remove_instructions "$targetdir" "$updatemanifestv3"
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force "$updatemanifestv3" && mv -f "$updatemanifestv3.xz" "$updatemanifestv3"
# Changed for Zotero -- -C is unreliable
pushd "$workdir" > /dev/null
mar_command="$mar_command -c output.mar"
eval "$mar_command $targetfiles"
popd > /dev/null
mv -f "$workdir/output.mar" "$archive"
# cleanup
rm -fr "$workdir"
notice ""
notice "Finished"
notice ""

View file

@ -1,301 +0,0 @@
#!/bin/bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This tool generates incremental update packages for the update system.
# Author: Darin Fisher
#
# Added for Zotero
set -eo pipefail
. $(dirname "$0")/common.sh
# Process a single file task when invoked from run_parallel_tasks()
if [ "${1:-}" = "--run-task" ]; then
process_update_task "$2"
exit 0
fi
# -----------------------------------------------------------------------------
print_usage() {
notice "Usage: $(basename $0) [OPTIONS] ARCHIVE FROMDIR TODIR"
notice ""
notice "The differences between FROMDIR and TODIR will be stored in ARCHIVE."
notice ""
notice "Options:"
notice " -h show this help text"
notice " -f clobber this file in the installation"
notice " Must be a path to a file to clobber in the partial update."
notice " -q be less verbose"
notice ""
}
check_for_forced_update() {
force_list="$1"
forced_file_chk="$2"
local f
if [ "$forced_file_chk" = "precomplete" ]; then
## "true" *giggle*
return 0;
fi
if [ "$forced_file_chk" = "Contents/Resources/precomplete" ]; then
## "true" *giggle*
return 0;
fi
if [ "$forced_file_chk" = "removed-files" ]; then
## "true" *giggle*
return 0;
fi
if [ "$forced_file_chk" = "Contents/Resources/removed-files" ]; then
## "true" *giggle*
return 0;
fi
# notarization ticket
if [ "$forced_file_chk" = "Contents/CodeResources" ]; then
## "true" *giggle*
return 0;
fi
if [ "${forced_file_chk##*.}" = "chk" ]; then
## "true" *giggle*
return 0;
fi
for f in $force_list; do
#echo comparing $forced_file_chk to $f
if [ "$forced_file_chk" = "$f" ]; then
## "true" *giggle*
return 0;
fi
done
## 'false'... because this is bash. Oh yay!
return 1;
}
if [ $# = 0 ]; then
print_usage
exit 1
fi
requested_forced_updates='Contents/MacOS/firefox'
while getopts "hqf:" flag
do
case "$flag" in
h) print_usage; exit 0
;;
q) QUIET=1
;;
f) requested_forced_updates="$requested_forced_updates $OPTARG"
;;
?) print_usage; exit 1
;;
esac
done
# -----------------------------------------------------------------------------
mar_command="$MAR -V ${MOZ_PRODUCT_VERSION:?} -H ${MAR_CHANNEL_ID:?}"
# Added for -e for Zotero
set +e
let arg_start=$OPTIND-1
shift $arg_start
set -e
archive="$1"
olddir="$2"
newdir="$3"
# Prevent the workdir from being inside the targetdir so it isn't included in
# the update mar.
if [ $(echo "$newdir" | grep -c '/$') = 1 ]; then
# Remove the /
newdir=$(echo "$newdir" | sed -e 's:\/$::')
fi
workdir="$(mktemp -d)"
updatemanifestv3="$workdir/updatev3.manifest"
archivefiles="updatev3.manifest"
mkdir -p "$workdir"
# Generate a list of all files in the target directory.
pushd "$olddir"
if test $? -ne 0 ; then
exit 1
fi
list_files oldfiles
list_dirs olddirs
popd
pushd "$newdir"
if test $? -ne 0 ; then
exit 1
fi
if [ ! -f "precomplete" ]; then
if [ ! -f "Contents/Resources/precomplete" ]; then
notice "precomplete file is missing!"
exit 1
fi
fi
list_dirs newdirs
list_files newfiles
popd
# Add the type of update to the beginning of the update manifests.
notice ""
notice "Adding type instruction to update manifests"
> $updatemanifestv3
notice " type partial"
echo "type \"partial\"" >> $updatemanifestv3
notice ""
notice "Adding file patch and add instructions to update manifests"
num_oldfiles=${#oldfiles[*]}
remove_array=
num_removes=0
# The main file loop is split into three passes so that the
# CPU-heavy work can run in parallel: classify each file into a task list, run
# the diffing/compression tasks in parallel via run_parallel_tasks() in
# common.sh, and then assemble the manifest and archive list serially in the
# original order based on the files the tasks left in the work directory.
#
# (This also drops the unused MBSDIFF_HOOK/funsize path from the upstream
# script -- the diff behavior itself is unchanged and now lives in
# process_update_task() in common.sh.)
tmpdir="$(mktemp -d)"
taskfile="$tmpdir/tasks"
TAB="$(printf '\t')"
> "$taskfile"
for ((i=0; $i<$num_oldfiles; i=$i+1)); do
f="${oldfiles[$i]}"
# If this file exists in the new directory as well, then check if it differs.
if [ -f "$newdir/$f" ]; then
if check_for_add_if_not_update "$f"; then
echo "full$TAB$f" >> "$taskfile"
elif check_for_forced_update "$requested_forced_updates" "$f"; then
echo "full$TAB$f" >> "$taskfile"
else
echo "diff$TAB$f" >> "$taskfile"
fi
fi
done
# Newly added files -- in the new directory but not the old one. (This replaces
# the O(n^2) nested skip loop from the upstream script.)
printf '%s\n' "${oldfiles[@]}" | sort > "$tmpdir/oldlist"
printf '%s\n' "${newfiles[@]}" | sort > "$tmpdir/newlist"
comm -13 "$tmpdir/oldlist" "$tmpdir/newlist" | sed '/^$/d' | sort -r > "$tmpdir/newonly"
while IFS= read -r f; do
echo "full$TAB$f" >> "$taskfile"
done < "$tmpdir/newonly"
export olddir newdir workdir MBSDIFF BCJ_OPTIONS QUIET
run_parallel_tasks "$taskfile" "$newdir" "$0"
# Assemble the manifest and archive list in the original order. A diffed file
# left $f.patch in the work directory if the patch was smaller, $f if the
# compressed file was smaller, and neither if the file was unchanged.
for ((i=0; $i<$num_oldfiles; i=$i+1)); do
f="${oldfiles[$i]}"
if [ -f "$newdir/$f" ]; then
if check_for_add_if_not_update "$f"; then
make_add_if_not_instruction "$f" "$updatemanifestv3"
archivefiles="$archivefiles \"$f\""
elif check_for_forced_update "$requested_forced_updates" "$f"; then
make_add_instruction "$f" "$updatemanifestv3" 1
archivefiles="$archivefiles \"$f\""
elif [ -f "$workdir/$f.patch" ]; then
make_patch_instruction "$f" "$updatemanifestv3"
archivefiles="$archivefiles \"$f.patch\""
elif [ -f "$workdir/$f" ]; then
make_add_instruction "$f" "$updatemanifestv3"
archivefiles="$archivefiles \"$f\""
fi
else
# remove instructions are added after add / patch instructions for
# consistency with make_incremental_updates.py
remove_array[$num_removes]=$f
# Changed by Zotero for -e
#(( num_removes++ ))
(( ++num_removes ))
fi
done
# Newly added files
notice ""
notice "Adding file add instructions to update manifests"
while IFS= read -r f; do
if check_for_add_if_not_update "$f"; then
make_add_if_not_instruction "$f" "$updatemanifestv3"
else
make_add_instruction "$f" "$updatemanifestv3"
fi
archivefiles="$archivefiles \"$f\""
done < "$tmpdir/newonly"
rm -rf "$tmpdir"
notice ""
notice "Adding file remove instructions to update manifests"
for ((i=0; $i<$num_removes; i=$i+1)); do
f="${remove_array[$i]}"
verbose_notice " remove \"$f\""
echo "remove \"$f\"" >> $updatemanifestv3
done
# Add remove instructions for any dead files.
notice ""
notice "Adding file and directory remove instructions from file 'removed-files'"
append_remove_instructions "$newdir" "$updatemanifestv3"
notice ""
notice "Adding directory remove instructions for directories that no longer exist"
num_olddirs=${#olddirs[*]}
for ((i=0; $i<$num_olddirs; i=$i+1)); do
f="${olddirs[$i]}"
# If this dir doesn't exist in the new directory remove it.
if [ ! -d "$newdir/$f" ]; then
verbose_notice " rmdir $f/"
echo "rmdir \"$f/\"" >> $updatemanifestv3
fi
done
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force "$updatemanifestv3" && mv -f "$updatemanifestv3.xz" "$updatemanifestv3"
# Changed for Zotero -- -C is unreliable
pushd "$workdir" > /dev/null
mar_command="$mar_command -c output.mar"
eval "$mar_command $archivefiles"
popd > /dev/null
mv -f "$workdir/output.mar" "$archive"
# cleanup
rm -fr "$workdir"
notice ""
notice "Finished"
notice ""

View file

@ -1,24 +0,0 @@
application.ini
chrome.manifest
chrome/*
components/*
defaults/*
deleted.txt
dictionaries/*
extensions/*
fonts/EmojiOneMozilla.ttf
gtk2/*
icudt58l.dat
install.rdf
libnssdbm3.chk
libnssdbm3.so
libsoftokn3.chk
pingsender
resource/*
run-zotero.sh
styles.zip
test/*
translators.index
translators.zip
xulrunner/*
zotero.jar

View file

@ -1,67 +0,0 @@
Contents/Frameworks/XUL.framework/
Contents/MacOS/Info.plist
Contents/MacOS/LICENSE
Contents/MacOS/README.xulrunner
Contents/MacOS/Resources/*
Contents/MacOS/active-update.xml
Contents/MacOS/chrome.manifest
Contents/MacOS/components/
Contents/MacOS/crashreporter.app/*
Contents/MacOS/defaults/*
Contents/MacOS/dependentlibs.list
Contents/MacOS/dictionaries/
Contents/MacOS/gmp-fake/*
Contents/MacOS/js-gdb.py
Contents/MacOS/libc++.1.dylib
Contents/MacOS/libfreebl.chk
Contents/MacOS/libmozsqlite3.dylib
Contents/MacOS/libnspr4.dylib
Contents/MacOS/libnssdbm3.chk
Contents/MacOS/libnssdbm3.dylib
Contents/MacOS/libnssutil3.dylib
Contents/MacOS/libplc4.dylib
Contents/MacOS/libplds4.dylib
Contents/MacOS/libplugin_child_interpose.dylib
Contents/MacOS/libsmime3.dylib
Contents/MacOS/libsoftokn3.chk
Contents/MacOS/libssl3.dylib
Contents/MacOS/libxpcom.dylib
Contents/MacOS/omni.ja
Contents/MacOS/pdfinfo
Contents/MacOS/pdftotext
Contents/MacOS/pingsender
Contents/MacOS/platform.ini
Contents/MacOS/precomplete
Contents/MacOS/res/*
Contents/MacOS/update-settings.ini
Contents/MacOS/updater.app/Contents/MacOS/updater-bin
Contents/MacOS/updates.xml
Contents/MacOS/updates/*
Contents/MacOS/zotero-bin
Contents/Resources/CONTRIBUTING.md
Contents/Resources/COPYING
Contents/Resources/README.md
Contents/Resources/application.ini
Contents/Resources/chrome.manifest
Contents/Resources/chrome/*
Contents/Resources/components/*
Contents/Resources/defaults/*
Contents/Resources/deleted.txt
Contents/Resources/devtools.jar
Contents/Resources/dictionaries/*
Contents/Resources/extensions/*
Contents/Resources/gmp-clearkey/0.1/clearkey.info
Contents/Resources/icudt56l.dat
Contents/Resources/install.rdf
Contents/Resources/poppler-data/*
Contents/Resources/resource/*
Contents/Resources/styles.zip
Contents/Resources/test/*
Contents/Resources/translators.index
Contents/Resources/translators.zip
Contents/Resources/voucher.bin
Contents/Resources/zotero.jar
Contents/XPCServices/*
Contents/_CodeSignature/CodeDirectory
Contents/_CodeSignature/CodeRequirements
Contents/_CodeSignature/CodeSignature

View file

@ -1,58 +0,0 @@
Accessible.tlb
AccessibleHandler.dll
IA2Marshal.dll
Microsoft.VC80.CRT.manifest
api-ms-win-core-console-l1-1-0.dll
api-ms-win-core-datetime-l1-1-0.dll
api-ms-win-core-debug-l1-1-0.dll
api-ms-win-core-errorhandling-l1-1-0.dll
api-ms-win-core-file-l1-1-0.dll
api-ms-win-core-handle-l1-1-0.dll
api-ms-win-core-heap-l1-1-0.dll
api-ms-win-core-interlocked-l1-1-0.dll
api-ms-win-core-libraryloader-l1-1-0.dll
api-ms-win-core-memory-l1-1-0.dll
api-ms-win-core-namedpipe-l1-1-0.dll
api-ms-win-core-processenvironment-l1-1-0.dll
api-ms-win-core-processthreads-l1-1-0.dll
api-ms-win-core-profile-l1-1-0.dll
api-ms-win-core-rtlsupport-l1-1-0.dll
api-ms-win-core-string-l1-1-0.dll
api-ms-win-core-synch-l1-1-0.dll
api-ms-win-core-sysinfo-l1-1-0.dll
api-ms-win-core-util-l1-1-0.dll
application.ini
chrome.manifest
chrome/*
components/*
defaults/*
deleted.txt
dictionaries/*
extensions/*
firefox.*
fonts/EmojiOneMozilla.ttf
freebl3.chk
gkmedias.dll
install.rdf
minidump-analyzer.exe
mozcrt19.dll
mozutils.dll
msvcp80.dll
msvcp120.dll
msvcr80.dll
msvcr120.dll
nssdbm3.chk
nssdbm3.dll
pdfinfo.exe
pdftotext.exe
pingsender.exe
plugin-hang-ui.exe
poppler-data/*
resource/*
softokn3.chk
styles.zip
test/*
translators.index
translators.zip
xulrunner/*
zotero.jar

View file

@ -1,66 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
infile=${1:-}
outfile=${2:-}
if [[ -z "$infile" ]] || [[ -z "$infile" ]]; then
echo "Usage: $0 infile.mar outfile.mar"
exit 1
fi
mar="$APP_ROOT_DIR/xulrunner/bin/mar"
if [ ! -f "$mar" ]; then
echo "$mar not found"
exit 1
fi
tmp_dir=`mktemp -d`
function cleanup {
rm -rf $tmp_dir
}
trap cleanup EXIT
cd "$tmp_dir"
mkdir in
cd in
if [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
infile=$(cygpath -w "$infile")
outfile=$(cygpath -w "$outfile")
fi
echo "Extracting MAR"
$mar -x "$infile"
echo "Renaming all files to .xz"
find . -type f -exec mv {} {}.xz \;
echo "Uncompressing files"
find . -type f -exec unxz {} \;
echo "Recompressing files with bzip2"
find . -type f -exec bzip2 {} \;
echo "Removing .bz extension"
find . -type f | while read f; do mv "$f" "${f%.bz2}"; done
channel=$($mar -T "$infile" | sed -n -r 's/.+MAR channel name: ([^\s]+)/\1/p')
version=$($mar -T "$infile" | sed -n -r 's/.+Product version: ([^\s]+)/\1/p')
if [ -z "$channel" ]; then
echo "Could not detect channel"
exit 1
fi
if [ -z "$version" ]; then
echo "Could not detect version"
exit 1
fi
echo "MAR channel name: $channel"
echo "Product version: $version"
echo "Creating new MAR"
find . -type f -print0 | xargs -0 $mar -V $version -H $channel -c "$outfile"
echo
$mar -T "$outfile"

Binary file not shown.

Binary file not shown.

View file

@ -1,59 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
if [ $# -lt 2 ]; then
echo "Usage: $0 /path/to/src/firefox x64|arm64|win32" >&2
exit 1
fi
gecko_path=$1
arch=$2
if [ ! -d "$gecko_path" ]; then
echo "$gecko_path is not a directory" >&2
exit 1
fi
# Install required Rust version
rustup toolchain install $RUST_VERSION
if [ $arch = "x64" ]; then
rust_target=x86_64
elif [ $arch = "arm64" ]; then
rust_target=aarch64
elif [ $arch = "win32" ]; then
rust_target=i686
else
echo "Unknown architecture $arch" >&2
exit 1
fi
rustup default $RUST_VERSION
rustup target add $rust_target-pc-windows-msvc
cp "$SCRIPT_DIR/mozconfig" "$gecko_path"
if [[ $arch == "arm64" || $arch == "win32" ]]; then
export Z_ARCH=$arch
export RUST_TARGET=$rust_target-pc-windows-msvc
fi
cd "$gecko_path"
./mach build
./mach package
# Copy the packaged ZIP back to app/win/ for launcher/xul.dll extraction
case $arch in
x64) zip_suffix=win64 ;;
arm64) zip_suffix=win64-aarch64 ;;
win32) zip_suffix=win32 ;;
esac
zip_file=$(ls "$gecko_path"/obj-*/dist/firefox-*.en-US.${zip_suffix}.zip 2>/dev/null | head -1) || true
if [ -z "$zip_file" ] || [ ! -f "$zip_file" ]; then
echo "Could not find packaged zip for $arch in $gecko_path/obj-*/dist/" >&2
exit 1
fi
cp "$zip_file" "$SCRIPT_DIR/"
echo "Copied $(basename "$zip_file") to $SCRIPT_DIR"

View file

@ -1,59 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
#
# Turns out we need older versions from Mozilla, not the official ZIPs
#
#plugins=$(cat <<EOF
#http://nsis.sourceforge.net/mediawiki/images/d/d4/AppAssocReg-0.4.zip
#https://github.com/connectiblutz/NSIS-ApplicationID/releases/download/1.1/NSIS-ApplicationID.zip
#http://nsis.sourceforge.net/mediawiki/images/c/ca/InvokeShellVerb-1.0.zip
#http://nsis.sourceforge.net/mediawiki/images/6/6c/Shelllink.zip
#http://nsis.sourceforge.net/mediawiki/images/8/8f/UAC.zip
#EOF
#)
#
#mkdir -p nsis
#cd nsis
#for plugin in $plugins; do
# curl -fsSLO $plugin
#done
#
#unzip_cmd="unzip -d Plugins -j"
#for zip in AppAssocReg*.zip InvokeShellVerb*.zip; do
# $unzip_cmd $zip 'Plugins/Unicode/*.dll'
#done
#$unzip_cmd Shelllink*.zip Unicode/Plugins/ShellLink.dll
#$unzip_cmd NSIS-ApplicationID*.zip 'ReleaseUnicode/ApplicationID*'
#$unzip_cmd UAC.zip Plugins/x86-unicode/UAC.dll
#
#rm *.zip
#
#echo
#echo
#echo "Files extracted to ./nsis/Plugins -- move to ${NSIS_DIR}Plugins"
#echo
#ls -la Plugins
mkdir -p Plugins
cd Plugins
plugins="AccessControl AppAssocReg ApplicationID InvokeShellVerb ShellLink UAC"
for plugin in $plugins; do
curl https://hg.mozilla.org/mozilla-central/raw-file/052d53200cf8/other-licenses/nsis/Plugins/$plugin.dll > $plugin.dll
done
# From https://nsis.sourceforge.io/NsProcess_plugin
curl https://download.zotero.org/dev/win/NsProcess-1.6.zip > NsProcess.zip
7z e NsProcess.zip "Plugin/nsProcessW.dll"
mv nsProcessW.dll nsProcess.dll
rm NsProcess.zip
echo
echo
echo "Files downloaded to ./Plugins -- move to ${NSIS_DIR}Plugins"
echo
ls -la

View file

@ -1,84 +0,0 @@
#!/bin/bash
#
# Extract xul.dll from custom Firefox build ZIPs in current folder and create a
# ZIP for each platform with its own SHA256 hash in the filename. The files
# should then be uploaded and their hashes updated in config.sh so that
# fetch_xulrunner will download and use the custom xul.dll.
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR"
if [ $# -ne 1 ]; then
echo "Usage: $0 <version>"
exit 1
fi
version="$1"
# Input ZIP filenames
files=(
"firefox-$version.en-US.win32.zip"
"firefox-$version.en-US.win64-aarch64.zip"
"firefox-$version.en-US.win64.zip"
)
# Corresponding architecture labels and output names
arches=(
"win32"
"arm64"
"x64"
)
# Platform-specific SHA command
if [[ "$(uname)" = "Darwin" ]]; then
shasum_cmd="shasum -a 256"
else
shasum_cmd="sha256sum"
fi
# Clean up any old artifacts
rm -f "$version"-*.zip
# Process whichever arch ZIPs are present
processed=0
for i in "${!files[@]}"; do
file="${files[$i]}"
arch="${arches[$i]}"
temp_zip="xul-temp-$arch.zip"
if [ ! -f "$file" ]; then
continue
fi
dlls=(xul.dll)
# TEMP: Fix startup error on win32 due to missing export numSamples@RLBoxSoundTouch
if [[ $arch == "win32" ]]; then
dlls+=(lgpllibs.dll)
fi
echo "Extracting ${dlls[*]} from $file"
for dll in "${dlls[@]}"; do
unzip -p "$file" "firefox/$dll" > "$dll"
done
zip -q "$temp_zip" "${dlls[@]}"
# Calculate hash
sum=$($shasum_cmd "$temp_zip" | awk '{print $1}')
# Rename to final output
final_zip="$version-$arch-$sum.zip"
mv "$temp_zip" "$final_zip"
rm -f xul.dll lgpllibs.dll
echo "Created $final_zip"
processed=$((processed + 1))
done
if [ $processed -eq 0 ]; then
echo "No firefox-$version.en-US.*.zip files found in $SCRIPT_DIR" >&2
exit 1
fi

View file

@ -1,3 +0,0 @@
// CompressionMethod.cpp
#include "StdAfx.h"

Some files were not shown because too many files have changed in this diff Show more