Skip to content

Instantly share code, notes, and snippets.

@jerrykrinock
Last active August 22, 2017 00:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jerrykrinock/f5e5e6e6d6dd67d0977e4516ffb74524 to your computer and use it in GitHub Desktop.
Save jerrykrinock/f5e5e6e6d6dd67d0977e4516ffb74524 to your computer and use it in GitHub Desktop.
Script which packages browser extensions for Firefox, Opera and Chrome

The dream of being able to produce one browser extension and deploy it for Firefox, Opera and Chrome is not quite realized, due different packaging, and slight differences in the manifest.json file for Firefox.

Regarding packaging, there are two options: .crx and .zip. Oddly, I have found that the Mozilla web store only works with the .crx, and Opera and Chrome only with the .zip. These are maybe bugs that may be fixed by the time you read this.

The manifest.json file for Firefox may contain an optional key, applications, which prints a warning if used in a Chrome extension and therefore should not be present in what you publish to Chrome. However, as explained in Mozilla Bug 1378248, it's not really optional if you are using certain API such as Native Messaging. It is also not optional if you are hosting your Firefox extension on your own site (probably to avoid the weeks-long approval times for the AMO store) and want it to update automatically, because the update_url key is also a sub-key of applications.

So, anyhow, I finally realized I needed to bite the bullet and write the attached bash shell script which produces the different packages for Firefox, Opera and Chrome. The script is attached, in case others which to steal any lines.

The primary trick this script does is to combine a manifest additions JSON file, found in, say, a BrowserSpecial/Firefox subfolder, into the BrowserCommon/manifest.json file, but only when building for, say, Firefox. To do this, it requires that jq be installed on your system.

In the script directory and symbol names, I use the acronym W3CBE to mean World Wide Web Consortium Browser Extension. This is a placeholder for the dream that someday, the Mozilla team will get this to be a W3C standard, and we will refer to our cross-browser extensions as W3CBE Extensions.

There are at least a few things that you'll need to tweak.

  • This script packages my two extensions, BookMacsterButton and BookMacsterSync. Replace those with your own extension name(s).

  • Unless you're building this associated with another application, you probably won't want the section to # Verify that extension version….

  • I have placed this script in a Run Script Build Phase in Xcode, running on macOS, so it uses a several environment variables with $NAMES_FORMATTED_LIKE_THIS. In particular, two of them specify the source and destination directory paths. You'll need to replace these with your own environment variables or input parameters.

  • You will replace $SOURCE_ROOT/../../Scripts/crxmake.sh with the path to crxmake.sh, which you must have installed somewhere on your system.

packExtensionForBrowser () {
echo "#"
echo "##### Building $productName as $type for $browser #####"
cd $sourceDir
# Remove .DS_Store file, if any
rm -f $productName/BrowserCommon.DS*
# Make a temporary directory path which we shall use for building
outerTempDir=$(mktemp -d -t "Build-W3CBE-Temp")
innerTempDir=$outerTempDir/$productName
echo Will build stuff in temporary directory $innerTempDir
# Copy everything to temporary directory
cp -R $productName/BrowserCommon/ $innerTempDir
# Create a destination directory if one does not already exist
destinDir=$destinDirBase/$browser
echo Creating destinDir $destinDir
# In the next line, the bash && operator between two commands executes the second command if the first one returns true, which in this case means that the $destinDir does not exist, because the ! operator is negation, and [ ... ] tests if directory exists.
[ ! -d $destinDir ] && mkdir -p $destinDir
# See if there is are any additions to the manifest for this browser
manifestAdditions=$productName/BrowserSpecial/$browser/manifest.json
# See if $manifestAdditions file exists
if [ ! -f $manifestAdditions ]
then
echo "There are no additions to manifest for $browser in $productName"
else
echo "Adding additions to manifest for $browser of $productName"
manifest=$productName/BrowserCommon/manifest.json
/usr/local/bin/jq -s '.[0] * .[1]' "$manifestAdditions" "$manifest" > $innerTempDir/manifest.json
fi
cd $outerTempDir
if [ "$type" == "crx" ]; then
# Make the .crx product
# Requires crxmake from https://github.com/GoogleChrome/chrome-app-samples/blob/master/samples/dojo/crxmake.sh
cmd="$SOURCE_ROOT/../../Scripts/crxmake.sh $productName $sourceDir/$productName.pem"
echo current directory is: `pwd`
echo Will crxmake $productName
echo by executing: $cmd
echo in directory: `pwd`
`$cmd`
crxName=$productName.crx
echo Did crxmake $crxName
mv *.crx $destinDir
fi
if [ "$type" == "zip" ]; then
# You can make the .zip product, using Mozilla's web-ext tool. This has the advantage that you can submit it to their store (and wait 38 days for review). The only difference I can see between using `web-ext build`, and `zip -r` is that the name of the unzipped folder is different. Read this:
# https://discourse.mozilla-community.org/t/unable-to-submit-new-add-on/12416/7
# Something is wrong there, because the documentation states that `zip-r` should work:
# https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Publishing_your_WebExtension
# cd $productName
# web-ext build
# cd ..
# Move product to current directory (Note the "." at end of next line)
# mv $productName/web-ext-artifacts/*.zip .
# Remove garbage empty folder left by web-ext tool
# rm -Rf $productName/web-ext-artifacts
# Anyhow, since I cannot wait 38 days and am therefore not submitting to the store, I instead use `zip -r` which takes less code and gives me known file name.
zip -r $productName $productName
mv *.zip $destinDir
fi
echo removing $outerTempDir
rm -Rf $outerTempDir
}
packExtensionForBrowsers () {
cd $sourceDir
productName=$productNamePrefix$productShortName
# Verify that extension version is the same as that which will be demanded by the app to install the extension, and if not, exit with nonzero status.
manifestPath=$productName/BrowserCommon/manifest.json
versionFromManifest=`/usr/local/bin/jq --raw-output '.version' "$manifestPath"`
echo "version in manifest: $versionFromManifest"
echo "min version demanded by xcconfig: $minVersion"
if (( $versionFromManifest >= $minVersion )) ; then
echo "OK. Version from manifest ($versionFromManifest) is >= version demanded by app ($minVersion)"
else
echo "FAIL because Version from manifest ($versionFromManifest) < version demanded by $appKey in product ($minVersion)"
exit 15
fi
# Build for each of the browsers
browser=Opera
type=crx
packExtensionForBrowser
browser=Chrome
type=zip
packExtensionForBrowser
browser=Firefox
type=crx
packExtensionForBrowser
}
##### End of Subroutines
##### Beginning of Script
# Check that the 'jq' program is available on this computer
/usr/local/bin/jq --version
if [[ $? -ne 0 ]]; then
echo "This script requires jq, a command-line JSON file editor. It looks like jq is not installed. Please visit https://stedolan.github.io/jq/"
exit 1
fi
sourceDir="$SOURCE_ROOT/ExtensionsBrowser/W3CBE"
destinDirBase=$BUILT_PRODUCTS_DIR/W3CBE-Extensions
productNamePrefix=BookMacster
productShortName=Button
minVersion=$W3CBE_BUTTON_EXTENSION_MIN_VERSION
packExtensionForBrowsers
productShortName=Sync
minVersion=$W3CBE_SYNC_EXTENSION_MIN_VERSION
packExtensionForBrowsers
# For your convenience, opens a Finder window so you can ship them or whatever.
open $destinDirBase
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment