mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
Sign and notarize directly in build-helper (#389)
* Sign and notarize in CI * add dmg * remove flag * fix env var * add team id * conditionally set apple specific env vars * publish to a staging location * upload unzipped * add script to publish to staging, update publish url * turn off autodiscovery again * update scripts * deprecate old method * move stuff * remove autodiscovery
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
// Usage: node generate-hash.js <path-to-installer>
|
||||
// Example: node generate-hash.js ./make/Wave-0.0.1.dmg
|
||||
// This script will generate a hash of the installer file, as defined by electron-builder.
|
||||
// Courtesy of https://github.com/electron-userland/electron-builder/issues/3913#issuecomment-504698845
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const crypto = require("crypto");
|
||||
|
||||
/**
|
||||
* Generate a hash of a file, as defined by electron-builder
|
||||
* @param {string} file - Path to the file
|
||||
* @param {string} algorithm - Hash algorithm to use
|
||||
* @param {string} encoding - Encoding to use
|
||||
* @returns {Promise<string>} - The hash of the file
|
||||
*/
|
||||
async function hashFile(file, algorithm = "sha512", encoding = "base64") {
|
||||
return new Promise((resolve, reject) => {
|
||||
const hash = crypto.createHash(algorithm);
|
||||
hash.on("error", reject).setEncoding(encoding);
|
||||
fs.createReadStream(file, {
|
||||
highWaterMark: 1024 * 1024,
|
||||
/* better to use more memory but hash faster */
|
||||
})
|
||||
.on("error", reject)
|
||||
.on("end", () => {
|
||||
hash.end();
|
||||
resolve(hash.read());
|
||||
})
|
||||
.pipe(hash, {
|
||||
end: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
const installerPath = path.resolve(process.cwd(), process.argv[2]);
|
||||
(async () => {
|
||||
const hash = await hashFile(installerPath);
|
||||
console.log(`hash of ${installerPath}: ${hash}`);
|
||||
})();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hashFile,
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
// Notarize the Wave.app for macOS
|
||||
|
||||
const { notarize } = require("@electron/notarize");
|
||||
const path = require("path");
|
||||
|
||||
/**
|
||||
* Notarize the Wave.app for macOS
|
||||
* @param {string} waveAppPath - Path to the Wave.app
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function notarizeApp(waveAppPath) {
|
||||
return notarize({
|
||||
appPath: waveAppPath,
|
||||
tool: "notarytool",
|
||||
keychainProfile: "notarytool-creds",
|
||||
})
|
||||
.then(() => {
|
||||
console.log("notarize success");
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log("notarize error", e);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
console.log("running osx-notarize");
|
||||
const waveAppPath = path.resolve(__dirname, "temp", "Wave.app");
|
||||
(async () => {
|
||||
await notarizeApp(waveAppPath);
|
||||
console.log("notarization complete");
|
||||
})();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
notarizeApp,
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
// Sign the app and binaries for macOS
|
||||
|
||||
const { signAsync } = require("@electron/osx-sign");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
/**
|
||||
* Sign the app and binaries for macOS
|
||||
* @param {string} waveAppPath - Path to the Wave.app
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function signApp(waveAppPath) {
|
||||
const binDirPath = path.resolve(waveAppPath, "Contents", "Resources", "app.asar.unpacked", "bin");
|
||||
const binFilePaths = fs
|
||||
.readdirSync(binDirPath, { recursive: true, withFileTypes: true })
|
||||
.filter((f) => f.isFile())
|
||||
.map((f) => path.resolve(binDirPath, f.path, f.name));
|
||||
console.log("waveAppPath", waveAppPath);
|
||||
console.log("binDirPath", binDirPath);
|
||||
console.log("binFilePaths", binFilePaths);
|
||||
return signAsync({
|
||||
app: waveAppPath,
|
||||
binaries: binFilePaths,
|
||||
})
|
||||
.then(() => {
|
||||
console.log("signing success");
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log("signing error", e);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
console.log("running osx-sign");
|
||||
const waveAppPath = path.resolve(__dirname, "temp", "Wave.app");
|
||||
(async () => {
|
||||
await signApp(waveAppPath);
|
||||
console.log("signing complete");
|
||||
})();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
signApp,
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
# This script is used to sign and notarize the universal app for macOS
|
||||
|
||||
# Gets the directory of the script
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
# Remove old files and dirs, create new ones
|
||||
rm -f *.zip *.dmg
|
||||
ZIP_DIR=$SCRIPT_DIR/zip
|
||||
rm -rf $ZIP_DIR
|
||||
mkdir $ZIP_DIR
|
||||
TEMP_DIR=$SCRIPT_DIR/temp
|
||||
rm -rf $TEMP_DIR
|
||||
mkdir $TEMP_DIR
|
||||
BUILDS_DIR=$SCRIPT_DIR/builds
|
||||
rm -rf $BUILDS_DIR
|
||||
|
||||
# Download the builds zip
|
||||
aws s3 cp s3://waveterm-github-artifacts/waveterm-builds.zip .
|
||||
BUILDS_ZIP=waveterm-builds.zip
|
||||
if ! [ -f $BUILDS_ZIP ]; then
|
||||
echo "no $BUILDS_ZIP found";
|
||||
exit 1;
|
||||
fi
|
||||
echo "unzipping $BUILDS_ZIP"
|
||||
unzip -q $BUILDS_ZIP -d $BUILDS_DIR
|
||||
rm $BUILDS_ZIP
|
||||
|
||||
# Finds a file in a directory matching a filename pattern. Ensures there is exactly one match.
|
||||
find_file()
|
||||
{
|
||||
local FILE_DIR=$1
|
||||
local FILE_PATTERN=$2
|
||||
local FILE_PATH=$(find $FILE_DIR -type f -iname "$FILE_PATTERN")
|
||||
local NUM_MATCHES=$(echo $FILE_PATH | wc -l)
|
||||
if [ "0" -eq "$NUM_MATCHES" ]; then
|
||||
echo "no $FILE_PATTERN found in $FILE_DIR"
|
||||
exit 1
|
||||
elif [ "1" -lt "$NUM_MATCHES" ]; then
|
||||
echo "multiple $FILE_PATTERN found in $FILE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
echo $FILE_PATH
|
||||
}
|
||||
|
||||
# Unzip Mac build
|
||||
MAC_ZIP=$(find_file $BUILDS_DIR "Wave-darwin-universal-*.zip")
|
||||
unzip -q $MAC_ZIP -d $TEMP_DIR
|
||||
|
||||
# Sign and notarize the app
|
||||
node $SCRIPT_DIR/osx-sign.js
|
||||
DEBUG=electron-notarize node $SCRIPT_DIR/osx-notarize.js
|
||||
|
||||
# Zip and move
|
||||
echo "creating universal zip"
|
||||
ZIP_NAME=$(basename $MAC_ZIP)
|
||||
TEMP_WAVE_DIR_UNIVERSAL=$TEMP_DIR/Wave.app
|
||||
ditto $TEMP_WAVE_DIR_UNIVERSAL $ZIP_DIR/Wave.app
|
||||
cd $ZIP_DIR
|
||||
zip -9yqr $ZIP_NAME Wave.app
|
||||
mv $ZIP_NAME $BUILDS_DIR/
|
||||
cd $SCRIPT_DIR
|
||||
|
||||
# Create a dmg
|
||||
# Expects create-dmg repo to be cloned in the same parent directory as the waveterm repo.
|
||||
echo "creating universal dmg"
|
||||
DMG_NAME=$(echo $ZIP_NAME | sed 's/\.zip/\.dmg/')
|
||||
$SCRIPT_DIR/../../create-dmg/create-dmg \
|
||||
--volname "WaveTerm" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 600 300 \
|
||||
--icon-size 100 \
|
||||
--icon "Wave.app" 200 130 \
|
||||
--hide-extension "Wave.app" \
|
||||
--app-drop-link 400 125 \
|
||||
$DMG_NAME \
|
||||
"$TEMP_WAVE_DIR_UNIVERSAL"
|
||||
echo "success, created $DMG_NAME"
|
||||
mv $DMG_NAME $BUILDS_DIR/
|
||||
spctl -a -vvv -t install $TEMP_WAVE_DIR_UNIVERSAL/
|
||||
|
||||
# Update latest-mac.yml
|
||||
echo "updating latest-mac.yml"
|
||||
LATEST_MAC_YML=$BUILDS_DIR/latest-mac.yml
|
||||
node $SCRIPT_DIR/update-latest-mac.js $MAC_ZIP $LATEST_MAC_YML
|
||||
|
||||
# Clean up
|
||||
rm -rf $TEMP_DIR $ZIP_DIR
|
||||
@@ -0,0 +1,40 @@
|
||||
// Updates the latest-mac.yml file with the signed and notarized version of the latest installer
|
||||
// Usage: node update-latest.js <path-to-installer>
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const { hashFile } = require("./generate-hash");
|
||||
const yaml = require("yaml");
|
||||
|
||||
/**
|
||||
* Updates the latest-mac.yml file with the signed and notarized version of the latest installer
|
||||
* @param {string} installerPath - Path to the installer
|
||||
* @param {string} ymlPath - Path to the latest-mac.yml file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function updateLatestMac(installerPath, ymlPath) {
|
||||
const hash = (await hashFile(installerPath)).trim();
|
||||
const size = fs.statSync(installerPath).size;
|
||||
const yml = yaml.parse(fs.readFileSync(ymlPath, "utf8"));
|
||||
for (const file of yml.files) {
|
||||
if (file.url === path.basename(installerPath)) {
|
||||
file.sha512 = hash;
|
||||
file.size = size;
|
||||
}
|
||||
}
|
||||
yml.sha512 = hash;
|
||||
fs.writeFileSync(ymlPath, yaml.stringify(yml));
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
const installerPath = path.resolve(process.cwd(), process.argv[2]);
|
||||
const ymlPath = path.resolve(process.cwd(), process.argv[3]);
|
||||
(async () => {
|
||||
await updateLatestMac(installerPath, ymlPath);
|
||||
console.log("latest-mac.yml updated");
|
||||
})();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
updateLatestMac,
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
# This script is used to upload signed and notarized releases to S3 and update the Electron auto-update release feeds.
|
||||
|
||||
# Gets the directory of the script
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
BUILDS_DIR=$SCRIPT_DIR/builds
|
||||
TEMP2_DIR=$SCRIPT_DIR/temp2
|
||||
|
||||
AUTOUPDATE_RELEASE_PATH="dl.waveterm.dev/releases"
|
||||
|
||||
# Copy the builds to the temp2 directory
|
||||
echo "Copying builds to temp2"
|
||||
rm -rf $TEMP2_DIR
|
||||
mkdir -p $TEMP2_DIR
|
||||
cp -r $BUILDS_DIR/* $TEMP2_DIR
|
||||
|
||||
UVERSION=$(cat $TEMP2_DIR/version.txt)
|
||||
|
||||
if [ -z "$UVERSION" ]; then
|
||||
echo "version.txt is empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove files we don't want to upload
|
||||
rm $TEMP2_DIR/version.txt
|
||||
rm $TEMP2_DIR/builder-*.yml
|
||||
|
||||
# Upload the artifacts
|
||||
echo "Uploading build artifacts to $AUTOUPDATE_RELEASE_PATH"
|
||||
aws s3 cp $TEMP2_DIR/ s3://$AUTOUPDATE_RELEASE_PATH/ --recursive
|
||||
|
||||
# Clean up
|
||||
echo "Cleaning up"
|
||||
rm -rf $TEMP2_DIR
|
||||
Reference in New Issue
Block a user