Add uninstall option and output pkg

Improve installer mode detection

Fix macOS installer packaging

Fix default selection for uninstall choice

Remove obsolete tar handling and clean script copies
This commit is contained in:
Yaroslav 2025-06-06 20:36:00 +03:00 committed by Yaroslav Yashin
parent 369e08844f
commit fb644e7791
11 changed files with 136 additions and 106 deletions

View file

@ -255,7 +255,6 @@ jobs:
env: env:
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4 # Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
QT_VERSION: 6.4.3 QT_VERSION: 6.4.3
QIF_VERSION: 4.6
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
@ -283,11 +282,6 @@ jobs:
set-env: 'true' set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
run: |
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -301,14 +295,13 @@ jobs:
- name: 'Build project' - name: 'Build project'
run: | run: |
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
bash deploy/build_macos.sh bash deploy/build_macos.sh
- name: 'Upload installer artifact' - name: 'Upload installer artifact'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: AmneziaVPN_MacOS_old_installer name: AmneziaVPN_MacOS_old_installer
path: AmneziaVPN.dmg path: deploy/build/pkg/AmneziaVPN.pkg
retention-days: 7 retention-days: 7
- name: 'Upload unpacked artifact' - name: 'Upload unpacked artifact'
@ -325,7 +318,6 @@ jobs:
env: env:
QT_VERSION: 6.8.0 QT_VERSION: 6.8.0
QIF_VERSION: 4.8.1
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
@ -353,11 +345,6 @@ jobs:
set-env: 'true' set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
run: |
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -371,14 +358,13 @@ jobs:
- name: 'Build project' - name: 'Build project'
run: | run: |
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
bash deploy/build_macos.sh bash deploy/build_macos.sh
- name: 'Upload installer artifact' - name: 'Upload installer artifact'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: AmneziaVPN_MacOS_installer name: AmneziaVPN_MacOS_installer
path: AmneziaVPN.dmg path: deploy/build/pkg/AmneziaVPN.pkg
retention-days: 7 retention-days: 7
- name: 'Upload unpacked artifact' - name: 'Upload unpacked artifact'

View file

@ -31,20 +31,14 @@ BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME
PREBUILT_DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/deploy-prebuilt/macos PREBUILT_DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/deploy-prebuilt/macos
DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/macos DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/macos
INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data
INSTALLER_BUNDLE_DIR=$BUILD_DIR/installer/$APP_FILENAME
DMG_FILENAME=$PROJECT_DIR/${APP_NAME}.dmg
# Search Qt # Search Qt
if [ -z "${QT_VERSION+x}" ]; then if [ -z "${QT_VERSION+x}" ]; then
QT_VERSION=6.4.3; QT_VERSION=6.4.3;
QIF_VERSION=4.6
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/macos/bin QT_BIN_DIR=$HOME/Qt/$QT_VERSION/macos/bin
QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin
fi fi
echo "Using Qt in $QT_BIN_DIR" echo "Using Qt in $QT_BIN_DIR"
echo "Using QIF in $QIF_BIN_DIR"
# Checking env # Checking env
@ -113,58 +107,90 @@ if [ "${MAC_CERT_PW+x}" ]; then
fi fi
echo "Packaging installer..." echo "Packaging installer..."
mkdir -p $INSTALLER_DATA_DIR PKG_DIR=$BUILD_DIR/pkg
cp -av $PROJECT_DIR/deploy/installer $BUILD_DIR PKG_ROOT=$PKG_DIR/root
cp -av $DEPLOY_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_install.sh SCRIPTS_DIR=$PKG_DIR/scripts
cp -av $DEPLOY_DATA_DIR/post_uninstall.sh $INSTALLER_DATA_DIR/post_uninstall.sh RESOURCES_DIR=$PKG_DIR/resources
cp -av $DEPLOY_DATA_DIR/$PLIST_NAME $INSTALLER_DATA_DIR/$PLIST_NAME INSTALL_PKG=$PKG_DIR/${APP_NAME}_install.pkg
UNINSTALL_PKG=$PKG_DIR/${APP_NAME}_uninstall.pkg
FINAL_PKG=$PKG_DIR/${APP_NAME}.pkg
UNINSTALL_SCRIPTS_DIR=$PKG_DIR/uninstall_scripts
chmod a+x $INSTALLER_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_uninstall.sh mkdir -p "$PKG_ROOT/Applications" "$SCRIPTS_DIR" "$RESOURCES_DIR" "$UNINSTALL_SCRIPTS_DIR"
cd $BUNDLE_DIR cp -R "$BUNDLE_DIR" "$PKG_ROOT/Applications"
tar czf $INSTALLER_DATA_DIR/$APP_NAME.tar.gz ./ cp "$DEPLOY_DATA_DIR/$PLIST_NAME" "$PKG_ROOT/Applications/$APP_FILENAME/$PLIST_NAME"
cp "$DEPLOY_DATA_DIR/post_install.sh" "$SCRIPTS_DIR/post_install.sh"
cp "$DEPLOY_DATA_DIR/post_uninstall.sh" "$UNINSTALL_SCRIPTS_DIR/postinstall"
mkdir -p "$RESOURCES_DIR/scripts"
cp "$DEPLOY_DATA_DIR/check_install.sh" "$RESOURCES_DIR/scripts/check_install.sh"
cp "$DEPLOY_DATA_DIR/check_uninstall.sh" "$RESOURCES_DIR/scripts/check_uninstall.sh"
echo "Building installer..." cat > "$SCRIPTS_DIR/postinstall" <<'EOS'
$QIF_BIN_DIR/binarycreator --offline-only -v -c $BUILD_DIR/installer/config/macos.xml -p $BUILD_DIR/installer/packages -f $INSTALLER_BUNDLE_DIR #!/bin/bash
SCRIPT_DIR="$(dirname "$0")"
bash "$SCRIPT_DIR/post_install.sh"
exit 0
EOS
chmod +x "$SCRIPTS_DIR"/*
chmod +x "$UNINSTALL_SCRIPTS_DIR"/*
chmod +x "$RESOURCES_DIR/scripts"/*
cp "$PROJECT_DIR/LICENSE" "$RESOURCES_DIR/LICENSE"
APP_VERSION=$(grep -m1 -E 'project\(' "$PROJECT_DIR/CMakeLists.txt" | sed -E 's/.*VERSION ([0-9.]+).*/\1/')
echo "Building component package $INSTALL_PKG ..."
pkgbuild --root "$PKG_ROOT" \
--identifier "$APP_DOMAIN" \
--version "$APP_VERSION" \
--install-location "/" \
--scripts "$SCRIPTS_DIR" \
"$INSTALL_PKG"
# Build uninstaller component package
UNINSTALL_COMPONENT_PKG=$PKG_DIR/${APP_NAME}_uninstall_component.pkg
echo "Building uninstaller component package $UNINSTALL_COMPONENT_PKG ..."
pkgbuild --nopayload \
--identifier "$APP_DOMAIN.uninstall" \
--version "$APP_VERSION" \
--scripts "$UNINSTALL_SCRIPTS_DIR" \
"$UNINSTALL_COMPONENT_PKG"
# Wrap uninstaller component in a distribution package for clearer UI
echo "Building uninstaller distribution package $UNINSTALL_PKG ..."
UNINSTALL_RESOURCES=$PKG_DIR/uninstall_resources
rm -rf "$UNINSTALL_RESOURCES"
mkdir -p "$UNINSTALL_RESOURCES"
cp "$DEPLOY_DATA_DIR/uninstall_welcome.html" "$UNINSTALL_RESOURCES"
cp "$DEPLOY_DATA_DIR/uninstall_conclusion.html" "$UNINSTALL_RESOURCES"
productbuild \
--distribution "$DEPLOY_DATA_DIR/distribution_uninstall.xml" \
--package-path "$PKG_DIR" \
--resources "$UNINSTALL_RESOURCES" \
"$UNINSTALL_PKG"
cp "$PROJECT_DIR/deploy/data/macos/distribution.xml" "$PKG_DIR/distribution.xml"
echo "Creating final installer $FINAL_PKG ..."
productbuild --distribution "$PKG_DIR/distribution.xml" --package-path "$PKG_DIR" --resources "$RESOURCES_DIR" "$FINAL_PKG"
if [ "${MAC_CERT_PW+x}" ]; then if [ "${MAC_CERT_PW+x}" ]; then
echo "Signing installer bundle..." echo "Signing installer package..."
security unlock-keychain -p $TEMP_PASS $KEYCHAIN security unlock-keychain -p $TEMP_PASS $KEYCHAIN
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $INSTALLER_BUNDLE_DIR /usr/bin/codesign --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" "$FINAL_PKG"
/usr/bin/codesign --verify -vvvv $INSTALLER_BUNDLE_DIR || true /usr/bin/codesign --verify -vvvv "$FINAL_PKG" || true
if [ "${NOTARIZE_APP+x}" ]; then if [ "${NOTARIZE_APP+x}" ]; then
echo "Notarizing installer bundle..." echo "Notarizing installer package..."
/usr/bin/ditto -c -k --keepParent $INSTALLER_BUNDLE_DIR $PROJECT_DIR/Installer_bundle_to_notarize.zip xcrun notarytool submit "$FINAL_PKG" --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
xcrun notarytool submit $PROJECT_DIR/Installer_bundle_to_notarize.zip --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
rm $PROJECT_DIR/Installer_bundle_to_notarize.zip
sleep 300 sleep 300
xcrun stapler staple $INSTALLER_BUNDLE_DIR xcrun stapler staple "$FINAL_PKG"
xcrun stapler validate $INSTALLER_BUNDLE_DIR xcrun stapler validate "$FINAL_PKG"
spctl -a -vvvv $INSTALLER_BUNDLE_DIR || true spctl -a -vvvv "$FINAL_PKG" || true
fi fi
fi fi
echo "Building DMG installer..." echo "Finished, artifact is $FINAL_PKG"
# Allow Terminal to make changes in Privacy & Security > App Management
hdiutil create -size 256mb -volname AmneziaVPN -srcfolder $BUILD_DIR/installer/$APP_NAME.app -ov -format UDZO $DMG_FILENAME
if [ "${MAC_CERT_PW+x}" ]; then
echo "Signing DMG installer..."
security unlock-keychain -p $TEMP_PASS $KEYCHAIN
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $DMG_FILENAME
/usr/bin/codesign --verify -vvvv $DMG_FILENAME || true
if [ "${NOTARIZE_APP+x}" ]; then
echo "Notarizing DMG installer..."
xcrun notarytool submit $DMG_FILENAME --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
sleep 300
xcrun stapler staple $DMG_FILENAME
xcrun stapler validate $DMG_FILENAME
fi
fi
echo "Finished, artifact is $DMG_FILENAME"
# restore keychain # restore keychain
security default-keychain -s login.keychain security default-keychain -s login.keychain

View file

@ -0,0 +1,5 @@
#!/bin/bash
if [ -d "/Applications/AmneziaVPN.app" ] || pgrep -x "AmneziaVPN-service" >/dev/null; then
exit 1
fi
exit 0

View file

@ -0,0 +1,5 @@
#!/bin/bash
if [ -d "/Applications/AmneziaVPN.app" ] || pgrep -x "AmneziaVPN-service" >/dev/null; then
exit 0
fi
exit 1

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<installer-gui-script minSpecVersion="1">
<title>AmneziaVPN Installer</title>
<license file="LICENSE"/>
<choices-outline>
<line choice="install"/>
<line choice="uninstall"/>
</choices-outline>
<choice id="install" title="Install AmneziaVPN" start_selected="true">
<pkg-ref id="org.amneziavpn.package"/>
</choice>
<choice id="uninstall" title="Uninstall AmneziaVPN" start_selected="false">
<pkg-ref id="org.amneziavpn.uninstall"/>
</choice>
<pkg-ref id="org.amneziavpn.package" auth="Root" install-check="scripts/check_install.sh">AmneziaVPN_install.pkg</pkg-ref>
<pkg-ref id="org.amneziavpn.uninstall" auth="Root" install-check="scripts/check_uninstall.sh">AmneziaVPN_uninstall_component.pkg</pkg-ref>
</installer-gui-script>

View file

@ -0,0 +1,13 @@
<installer-gui-script minSpecVersion="1">
<title>Uninstall AmneziaVPN</title>
<options customize-install-button="always"/>
<welcome file="uninstall_welcome.html"/>
<conclusion file="uninstall_conclusion.html"/>
<choices-outline>
<line choice="uninstall"/>
</choices-outline>
<choice id="uninstall" title="Uninstall AmneziaVPN" start_selected="true">
<pkg-ref id="org.amneziavpn.uninstall"/>
</choice>
<pkg-ref id="org.amneziavpn.uninstall" auth="Root">AmneziaVPN_uninstall_component.pkg</pkg-ref>
</installer-gui-script>

View file

@ -8,15 +8,13 @@ LOG_FILE="$LOG_FOLDER/post-install.log"
APP_PATH=/Applications/$APP_NAME.app APP_PATH=/Applications/$APP_NAME.app
if launchctl list "$APP_NAME-service" &> /dev/null; then if launchctl list "$APP_NAME-service" &> /dev/null; then
launchctl unload $LAUNCH_DAEMONS_PLIST_NAME launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME"
rm -f $LAUNCH_DAEMONS_PLIST_NAME rm -f "$LAUNCH_DAEMONS_PLIST_NAME"
fi fi
tar xzf $APP_PATH/$APP_NAME.tar.gz -C $APP_PATH sudo chmod -R a-w "$APP_PATH/"
rm -f $APP_PATH/$APP_NAME.tar.gz sudo chown -R root "$APP_PATH/"
sudo chmod -R a-w $APP_PATH/ sudo chgrp -R wheel "$APP_PATH/"
sudo chown -R root $APP_PATH/
sudo chgrp -R wheel $APP_PATH/
rm -rf $LOG_FOLDER rm -rf $LOG_FOLDER
mkdir -p $LOG_FOLDER mkdir -p $LOG_FOLDER
@ -25,11 +23,9 @@ echo "`date` Script started" > $LOG_FILE
killall -9 $APP_NAME-service 2>> $LOG_FILE killall -9 $APP_NAME-service 2>> $LOG_FILE
mv -f $APP_PATH/$PLIST_NAME $LAUNCH_DAEMONS_PLIST_NAME 2>> $LOG_FILE mv -f "$APP_PATH/$PLIST_NAME" "$LAUNCH_DAEMONS_PLIST_NAME" 2>> $LOG_FILE
chown root:wheel $LAUNCH_DAEMONS_PLIST_NAME chown root:wheel "$LAUNCH_DAEMONS_PLIST_NAME"
launchctl load $LAUNCH_DAEMONS_PLIST_NAME launchctl load "$LAUNCH_DAEMONS_PLIST_NAME"
echo "`date` Service status: $?" >> $LOG_FILE echo "`date` Service status: $?" >> $LOG_FILE
echo "`date` Script finished" >> $LOG_FILE echo "`date` Script finished" >> $LOG_FILE
#rm -- "$0"

View file

@ -0,0 +1,7 @@
<html>
<head><title>Uninstall Complete</title></head>
<body>
<h1>AmneziaVPN has been uninstalled</h1>
<p>Thank you for using AmneziaVPN. The application and its components have been removed.</p>
</body>
</html>

View file

@ -0,0 +1,7 @@
<html>
<head><title>Uninstall AmneziaVPN</title></head>
<body>
<h1>Uninstall AmneziaVPN</h1>
<p>This process will remove AmneziaVPN from your system. Click Continue to proceed.</p>
</body>
</html>

View file

@ -4,11 +4,6 @@ if(WIN32)
${CMAKE_CURRENT_LIST_DIR}/config/windows.xml.in ${CMAKE_CURRENT_LIST_DIR}/config/windows.xml.in
${CMAKE_BINARY_DIR}/installer/config/windows.xml ${CMAKE_BINARY_DIR}/installer/config/windows.xml
) )
elseif(APPLE AND NOT IOS)
configure_file(
${CMAKE_CURRENT_LIST_DIR}/config/macos.xml.in
${CMAKE_BINARY_DIR}/installer/config/macos.xml
)
elseif(LINUX) elseif(LINUX)
set(ApplicationsDir "@ApplicationsDir@") set(ApplicationsDir "@ApplicationsDir@")
configure_file( configure_file(

View file

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Installer>
<Name>AmneziaVPN</Name>
<Version>@CMAKE_PROJECT_VERSION@</Version>
<Title>AmneziaVPN</Title>
<Publisher>AmneziaVPN</Publisher>
<StartMenuDir>AmneziaVPN</StartMenuDir>
<TargetDir>/Applications/AmneziaVPN.app</TargetDir>
<WizardDefaultWidth>600</WizardDefaultWidth>
<WizardDefaultHeight>380</WizardDefaultHeight>
<WizardStyle>Mac</WizardStyle>
<RemoveTargetDir>true</RemoveTargetDir>
<AllowSpaceInPath>true</AllowSpaceInPath>
<AllowNonAsciiCharacters>false</AllowNonAsciiCharacters>
<ControlScript>controlscript.js</ControlScript>
<RepositorySettingsPageVisible>false</RepositorySettingsPageVisible>
<DependsOnLocalInstallerBinary>true</DependsOnLocalInstallerBinary>
<SupportsModify>false</SupportsModify>
<DisableAuthorizationFallback>true</DisableAuthorizationFallback>
<RemoteRepositories>
<Repository>
<Url>https://amneziavpn.org/updates/macos</Url>
<Enabled>true</Enabled>
<DisplayName>AmneziaVPN - repository for macOS</DisplayName>
</Repository>
</RemoteRepositories>
</Installer>