Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enable pkg installers #434

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
43 changes: 34 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
name: Build miniforge
on: [push, pull_request]
on:
push:
branches:
- main
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true

jobs:
build:
Expand Down Expand Up @@ -195,6 +203,28 @@ jobs:
miniforge-variant: Mambaforge
use-mamba: true
if: ${{ ! contains(matrix.OS_NAME, 'Linux') }}

- name: Prepare certificates (macOS)
if: contains(matrix.OS_NAME, 'MacOSX') && github.event_name != 'pull_request'
env:
APPLE_INSTALLER_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_BASE64 }}
APPLE_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }}
APPLE_APPLICATION_CERTIFICATE_BASE64: ${{ secrets.APPLE_APPLICATION_CERTIFICATE_BASE64 }}
APPLE_APPLICATION_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APPLICATION_CERTIFICATE_PASSWORD }}
TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }}
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
APPLE_NOTARIZATION_TEAM_ID: ${{ secrets.APPLE_NOTARIZATION_TEAM_ID }}
run: |
# This script will export APPLE_SIGNING_IDENTITY and APPLE_NOTARIZATION_IDENTITY
source scripts/osx_prepare_certificates.sh

# These will be needed by the build.sh script
echo "APPLE_SIGNING_IDENTITY=$APPLE_SIGNING_IDENTITY" >> $GITHUB_ENV
echo "APPLE_NOTARIZATION_IDENTITY=$APPLE_NOTARIZATION_IDENTITY" >> $GITHUB_ENV
echo "APPLE_NOTARIZATION_USERNAME=$APPLE_NOTARIZATION_USERNAME" >> $GITHUB_ENV
echo "APPLE_NOTARIZATION_PASSWORD=$APPLE_NOTARIZATION_PASSWORD" >> $GITHUB_ENV
echo "APPLE_NOTARIZATION_TEAM_ID=$APPLE_NOTARIZATION_TEAM_ID" >> $GITHUB_ENV

- name: Build and test miniforge
env:
Expand All @@ -210,24 +240,19 @@ jobs:
export MINIFORGE_VERSION=${GITHUB_REF##*/};
fi
if [[ "$OS_NAME" == "Linux" ]]; then
export EXT=sh
bash build_miniforge.sh;
fi
if [[ "$OS_NAME" == "MacOSX" ]]; then
export EXT=sh
bash build_miniforge_osx.sh;
# Provide a Darwin alias
cp build/$MINIFORGE_NAME-*-$OS_NAME-$ARCH.sh build/$MINIFORGE_NAME-Darwin-$ARCH.sh;
cp build/$MINIFORGE_NAME-*-$OS_NAME-$ARCH.pkg build/$MINIFORGE_NAME-Darwin-$ARCH.pkg;
fi
if [[ "$OS_NAME" == "Windows" ]]; then
export EXT=exe
echo "WINDIR:$WINDIR"
source /c/Miniconda3/Scripts/activate;
source build_miniforge_win.sh;
fi
# Copy for latest release
cp build/$MINIFORGE_NAME-*-$OS_NAME-$ARCH.$EXT build/$MINIFORGE_NAME-$OS_NAME-$ARCH.$EXT
if [[ "$OS_NAME" == "MacOSX" ]]; then
cp build/$MINIFORGE_NAME-*-$OS_NAME-$ARCH.$EXT build/$MINIFORGE_NAME-Darwin-$ARCH.$EXT
fi
ls -alh build
shell: bash

Expand Down
13 changes: 13 additions & 0 deletions Miniforge3/construct.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ license_file: ../LICENSE
initialize_conda: True
initialize_by_default: False

# macOS PKG options
installer_type: all # [osx]
welcome_image: osx_pkg_background.png # [osx]
# Reset everything to system default (no Anaconda stuff)
# We can customize it if needed
welcome_text: "" # [osx]
readme_text: "" # [osx]
conclusion_text: "" # [osx]

user_requested_specs:
{% if name.endswith("pypy3") %}
- python 3.9.* *_pypy
Expand All @@ -48,3 +57,7 @@ specs:

- pip
- miniforge_console_shortcut 1.* # [win]

# Signing settings will be added by CI
signing_identity_name: {{ os.environ.get("APPLE_SIGNING_IDENTITY", "") }}
notarization_identity_name: {{ os.environ.get("APPLE_NOTARIZATION_IDENTITY", "") }}
Binary file added Miniforge3/osx_pkg_background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 30 additions & 18 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -xe

env | sort

echo "***** Start: Building Miniforge installer *****"
echo "***** Start: Building Miniforge installer(s) *****"
CONSTRUCT_ROOT="${CONSTRUCT_ROOT:-${PWD}}"

cd "${CONSTRUCT_ROOT}"
Expand Down Expand Up @@ -53,7 +53,7 @@ if [[ "${TARGET_PLATFORM}" != win-* ]]; then
EXTRA_CONSTRUCTOR_ARGS="${EXTRA_CONSTRUCTOR_ARGS} --conda-exe ${MICROMAMBA_FILE} --platform ${TARGET_PLATFORM}"
fi

echo "***** Construct the installer *****"
echo "***** Construct the installer(s) *****"
# Transmutation requires the current directory is writable
cd "${TEMP_DIR}"
# shellcheck disable=SC2086
Expand All @@ -64,24 +64,36 @@ echo "***** Generate installer hash *****"
cd "${TEMP_DIR}"
ls -alh
if [[ "$(uname)" == MINGW* ]]; then
EXT="exe";
EXTS=("exe");
elif [[ "$(uname)" == Darwin ]]; then
EXTS=("sh" "pkg");
else
EXT="sh";
EXTS=("sh");
fi
# This line will break if there is more than one installer in the folder.
INSTALLER_PATH=$(find . -name "M*forge*.${EXT}" | head -n 1)
HASH_PATH="${INSTALLER_PATH}.sha256"
sha256sum "${INSTALLER_PATH}" > "${HASH_PATH}"

echo "***** Move installer and hash to build folder *****"
mkdir -p "${CONSTRUCT_ROOT}/build"
mv "${INSTALLER_PATH}" "${CONSTRUCT_ROOT}/build/"
mv "${HASH_PATH}" "${CONSTRUCT_ROOT}/build/"
for EXT in "${EXTS[@]}"; do
# This line will break if there is more than one installer in the folder.
INSTALLER_PATH=$(find . -name "M*forge*.${EXT}" | head -n 1)

if [[ "${EXT}" == "pkg" && -n "${APPLE_NOTARIZATION_USERNAME:-}" ]]; then
# notarize the PKG installer
echo ""***** Notarizing the PKG installer "*****"
scripts/notarize_osx_pkg.sh "${INSTALLER_PATH}"
fi

HASH_PATH="${INSTALLER_PATH}.sha256"
sha256sum "${INSTALLER_PATH}" > "${HASH_PATH}"

echo "***** Move .$EXT installer and hash to build folder *****"
mkdir -p "${CONSTRUCT_ROOT}/build"
mv "${INSTALLER_PATH}" "${CONSTRUCT_ROOT}/build/"
mv "${HASH_PATH}" "${CONSTRUCT_ROOT}/build/"

# copy the installer for latest
if [[ "${MINIFORGE_NAME:-}" != "" && "${OS_NAME:-}" != "" && "${ARCH:-}" != "" ]]; then
cp "${CONSTRUCT_ROOT}/build/${MINIFORGE_NAME}-"*"-${OS_NAME}-${ARCH}.${EXT}" "${CONSTRUCT_ROOT}/build/${MINIFORGE_NAME}-${OS_NAME}-${ARCH}.${EXT}"
fi
done

echo "***** Done: Building Miniforge installer *****"
cd "${CONSTRUCT_ROOT}"

# copy the installer for latest
if [[ "${MINIFORGE_NAME:-}" != "" && "${OS_NAME:-}" != "" && "${ARCH:-}" != "" ]]; then
cp "${CONSTRUCT_ROOT}/build/${MINIFORGE_NAME}-"*"-${OS_NAME}-${ARCH}.${EXT}" "${CONSTRUCT_ROOT}/build/${MINIFORGE_NAME}-${OS_NAME}-${ARCH}.${EXT}"
fi
echo "***** Done: Building Miniforge installer(s) *****"
88 changes: 88 additions & 0 deletions scripts/notarize_osx_pkg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env bash

# This script takes a macOS installer package (.pkg) and notarizes it.
# It expects the following environment variables to be set:
# APPLE_NOTARIZATION_USERNAME: Apple ID username
# APPLE_NOTARIZATION_PASSWORD: Apple ID password
# APPLE_NOTARIZATION_TEAM_ID: Apple ID team identifier

set -euxo pipefail

if [ -n "$1" ]; then
echo "Usage: $0 <installer.pkg>"
exit 1
else
INSTALLER_PATH="$1"
fi

if [ ! -f "$INSTALLER_PATH" ]; then
echo "Error: $INSTALLER_PATH does not exist"
exit 1
fi

if [ "$(basename "$INSTALLER_PATH")" != "*.pkg" ]; then
echo "Error: $INSTALLER_PATH is not a .pkg file"
exit 1
fi

if [ "$(uname)" != "Darwin" ]; then
echo "Error: $0 can only be run on macOS"
exit 1
fi

if ! command -v xcrun >/dev/null; then
echo "Error: xcrun is not installed"
exit 1
fi

if ! command -v stapler >/dev/null; then
echo "Error: stapler is not installed"
exit 1
fi

if [ -z "${APPLE_NOTARIZATION_USERNAME:-}" ]; then
echo "Error: APPLE_NOTARIZATION_USERNAME is not set"
exit 1
fi

if [ -z "${APPLE_NOTARIZATION_PASSWORD:-}" ]; then
echo "Error: APPLE_NOTARIZATION_USERNAME is not set"
exit 1
fi

if [ -z "${APPLE_NOTARIZATION_TEAM_ID:-}" ]; then
echo "Error: APPLE_NOTARIZATION_USERNAME is not set"
exit 1
fi

# Check signatures. If this fails, there's no point in attempting notarization.
pkgutil --check-signature "$INSTALLER_PATH"

# Submit for notarization to Apple servers
tmp_output_dir=$(mktemp -d)
json_output_file="${tmp_output_dir}/$(basename "$INSTALLER_PATH").notarization.json"
set +e
xcrun notarytool submit "$INSTALLER_PATH" \
--apple-id "$APPLE_NOTARIZATION_USERNAME" \
--password "$APPLE_NOTARIZATION_PASSWORD" \
--team-id "$APPLE_NOTARIZATION_TEAM_ID" \
--output-format json \
--wait \
--timeout 30m \
| tee "$json_output_file"
notary_exit_code=$?
set -e
if [[ $notary_exit_code != 0 ]]; then
submission_id=$(jq -r '.id' "$json_output_file")
xcrun notarytool log "$submission_id" \
--apple-id "$APPLE_NOTARIZATION_USERNAME" \
--password "$APPLE_NOTARIZATION_PASSWORD" \
--team-id "$APPLE_NOTARIZATION_TEAM_ID"
exit $notary_exit_code
fi

# Staple
xcrun stapler staple --verbose "$INSTALLER_PATH"

# Check notarization status
spctl --assess -vv --type install "$INSTALLER_PATH" 2>&1 | tee /dev/stderr | grep accepted
12 changes: 12 additions & 0 deletions scripts/osx_pkg_background.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
Run this to generate a macOS PKG background with
the conda-forge anvil logo in the bottom left corner
"""
# needs 'pillow' package
from PIL import Image, ImageOps

# logo taken from https://github.com/conda-forge/marketing/084d589/main/logo/just_anvil_black.png
logo = Image.open("just_anvil_black.png")
background = Image.new("RGBA", (1227, 600), (0, 0, 0, 0))
background.paste(ImageOps.contain(logo, (290, 290)), (30, 460))
background.save("osx_pkg_background.png", format="png")
37 changes: 37 additions & 0 deletions scripts/osx_prepare_certificates.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash

# Import AppleID certificates on macOS
# This script is used to import the AppleID certificates into the keychain
# It expects the following environment variables to be set:
# - APPLE_INSTALLER_CERTIFICATE_BASE64: base64 encoded certificate
# - APPLE_INSTALLER_CERTIFICATE_PASSWORD: password for the certificate
# - APPLE_APPLICATION_CERTIFICATE_BASE64: base64 encoded certificate
# - APPLE_APPLIATION_CERTIFICATE_PASSWORD: password for the certificate
# - TEMP_KEYCHAIN_PASSWORD: password for the temporary keychain

# create variables
TEMP_DIR="$(mktemp -d)"
INSTALLER_CERTIFICATE_PATH="$TEMP_DIR/installer_developer_cert.p12"
APPLICATION_CERTIFICATE_PATH="$TEMP_DIR/application_developer_cert.p12"
KEYCHAIN_PATH="$TEMP_DIR/installer-signing.keychain-db"

# import certificate and provisioning profile from secrets
echo -n "${APPLE_INSTALLER_CERTIFICATE_BASE64}" | base64 --decode --output "$INSTALLER_CERTIFICATE_PATH"
echo -n "${APPLE_APPLICATION_CERTIFICATE_BASE64}" | base64 --decode --output "$APPLICATION_CERTIFICATE_PATH"

# create temporary keychain
security create-keychain -p "${TEMP_KEYCHAIN_PASSWORD}" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "${TEMP_KEYCHAIN_PASSWORD}" "$KEYCHAIN_PATH"

# import certificate to keychain
security import "$INSTALLER_CERTIFICATE_PATH" -P "${APPLE_INSTALLER_CERTIFICATE_PASSWORD}" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
security import "$APPLICATION_CERTIFICATE_PATH" -P "${APPLE_INSTALLER_CERTIFICATE_PASSWORD}" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
security list-keychain -d user -s "$KEYCHAIN_PATH"

# export identity name to construct.yaml
APPLE_SIGNING_IDENTITY=$(security find-identity "$KEYCHAIN_PATH" | grep -m 1 -o '"Developer ID Installer.*"' | tr -d '"')
APPLE_NOTARIZATION_IDENTITY=$(security find-identity "$KEYCHAIN_PATH" | grep -m 1 -o '"Developer ID Application.*"' | tr -d '"')

export APPLE_SIGNING_IDENTITY
export APPLE_NOTARIZATION_IDENTITY
Loading