Skip to content

Commit e05fe4d

Browse files
committed
Add auto-update feature for pgAdmin 4 desktop (macOS only). #5766
1 parent b6f64c4 commit e05fe4d

16 files changed

Lines changed: 2921 additions & 72 deletions

File tree

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ APP_REVISION := $(shell grep ^APP_REVISION web/version.py | awk -F"=" '{print $$
2020
# Include only platform-independent builds in all
2121
all: docs pip src
2222

23+
# Add BUILD_OPTS variable to pass arguments
2324
appbundle:
24-
./pkg/mac/build.sh
25+
./pkg/mac/build.sh $(BUILD_OPTS)
2526

2627
install-node:
2728
cd web && yarn install

docs/en_US/desktop_deployment.rst

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,140 @@ The configuration settings are stored in *runtime_config.json* file, which
128128
will be available on Unix systems (~/.local/share/pgadmin/),
129129
on Mac OS X (~/Library/Preferences/pgadmin),
130130
and on Windows (%APPDATA%/pgadmin).
131+
132+
133+
Auto-Update Feature for pgAdmin 4 Desktop
134+
*****************************************
135+
136+
pgAdmin 4's desktop application includes an automated update system built using Electron's ``autoUpdater`` module. This feature enables users to receive and install updates seamlessly, ensuring they always have access to the latest features and security fixes.
137+
138+
Supported Platforms
139+
===================
140+
141+
- **macOS:** Fully supported with automatic updates enabled by default
142+
- **Windows:** Not supported
143+
- **Linux:** Not supported
144+
145+
Update Process Overview
146+
=======================
147+
148+
1. **Check for Updates:**
149+
150+
- Automatic check on application startup
151+
- Manual check available via pgAdmin 4 menu > Check for Updates
152+
- Uses Electron's ``autoUpdater`` API to query update server
153+
154+
2. **Download Process:**
155+
156+
- Updates download automatically when detected
157+
- Progress shown via notifications
158+
- Background download prevents interruption of work
159+
160+
3. **Installation Flow:**
161+
162+
- User prompted to restart when update ready
163+
- Update applied during application restart
164+
165+
Technical Architecture
166+
======================
167+
168+
1. **Main Process (runtime/src/js/pgadmin.js)**
169+
170+
Handles core update functionality:
171+
172+
.. code-block:: javascript
173+
174+
autoUpdater.on('checking-for-update', () => {
175+
misc.writeServerLog('checking for updates...');
176+
});
177+
178+
autoUpdater.on('update-available', () => {
179+
setConfigAndRefreshMenu('update-available');
180+
misc.writeServerLog('Update downloading...');
181+
pgAdminMainScreen.webContents.send('appUpdateNotifier', {update_downloading: true});
182+
});
183+
184+
2. **Renderer Process (web/pgadmin/static/js/BrowserComponent.jsx)**
185+
186+
Manages user interface updates:
187+
188+
.. code-block:: javascript
189+
190+
if (window.electronUI?.appUpdateNotifier) {
191+
window.electronUI.appUpdateNotifier((data) => {
192+
if (data.update_downloading) {
193+
appUpdateNotifier('Update downloading...', 'info', 10000);
194+
} else if (data.update_downloaded) {
195+
appUpdateNotifier(UPDATE_DOWNLOADED_MESSAGE, 'warning', null,
196+
'Update downloaded', installUpdate, 'update_downloaded');
197+
}
198+
});
199+
}
200+
201+
3. **Update Server Communication**
202+
203+
- Configures update feed URL based on version information
204+
- Handles server response validation
205+
- Manages error conditions
206+
207+
User Interface Components
208+
=========================
209+
210+
1. **Notification Types:**
211+
212+
- Update available
213+
- Download progress
214+
- Update ready to install
215+
- Error notifications
216+
217+
2. **Menu Integration:**
218+
219+
- Check for Updates option in pgAdmin 4 menu
220+
- Update status indicators
221+
- Restart to Update option when available
222+
223+
Error Handling
224+
==============
225+
226+
The system includes comprehensive error handling:
227+
228+
1. **Network Errors:**
229+
230+
- Connection timeouts
231+
- Download failures
232+
- Server unavailability
233+
234+
2. **Installation Errors:**
235+
236+
- Insufficient disk space
237+
- Permission issues
238+
- Corrupted downloads
239+
240+
3. **Recovery Mechanisms:**
241+
242+
- Fallback to manual update
243+
- Error reporting to logs
244+
245+
Security Considerations
246+
=======================
247+
248+
The update system implements several security measures:
249+
250+
1. **Secure Communication:**
251+
252+
- HTTPS for update checks
253+
- Encrypted download process
254+
- Protected update metadata
255+
256+
Platform-Specific Notes
257+
=======================
258+
259+
1. **macOS:**
260+
261+
- Uses native update mechanisms
262+
- Requires signed packages
263+
264+
References
265+
==========
266+
267+
- `Electron autoUpdater API Documentation <https://www.electronjs.org/docs/latest/api/auto-updater>`_

pkg/mac/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@ Either build the sources or get them from macports or similar:
3131
*notarization.conf* and set the values accordingly. Note that notarization
3232
will fail if the code isn't signed.
3333

34-
4. To build, go to pgAdmin4 source root directory and execute:
34+
4. To build only DMG file, go to pgAdmin4 source root directory and execute:
3535

3636
make appbundle
37+
38+
To build both DMG and ZIP files, go to pgAdmin4 source root directory and execute:
39+
40+
make appbundle BUILD_OPTS="--zip"
3741
3842
This will create the python virtual environment and install all the required
3943
python modules mentioned in the requirements file using pip, build the
40-
runtime code and finally create the app bundle and the DMG in *./dist*
44+
runtime code and finally create the app bundle and the DMG and/or ZIP in *./dist*
4145
directory.

pkg/mac/build-functions.sh

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,24 @@ _codesign_bundle() {
386386
-i org.pgadmin.pgadmin4 \
387387
--sign "${DEVELOPER_ID}" \
388388
"${BUNDLE_DIR}"
389+
390+
echo "Verifying the signature from bundle dir..."
391+
codesign --verify --deep --verbose=4 "${BUNDLE_DIR}"
392+
}
393+
394+
_create_zip() {
395+
ZIP_NAME="${DMG_NAME%.dmg}.zip"
396+
echo "ZIP_NAME: ${ZIP_NAME}"
397+
398+
echo "Compressing pgAdmin 4.app in bundle dir into ${ZIP_NAME}..."
399+
ditto -c -k --sequesterRsrc --keepParent "${BUNDLE_DIR}" "${ZIP_NAME}"
400+
401+
if [ $? -ne 0 ]; then
402+
echo "Failed to create the ZIP file. Exiting."
403+
exit 1
404+
fi
405+
406+
echo "Successfully created ZIP file: ${ZIP_NAME}"
389407
}
390408
391409
_create_dmg() {
@@ -427,8 +445,57 @@ _codesign_dmg() {
427445
"${DMG_NAME}"
428446
}
429447
448+
_notarize_zip() {
449+
if [ "${CODESIGN}" -eq 0 ]; then
450+
return
451+
fi
452+
453+
echo "Uploading ZIP for Notarization ..."
454+
STATUS=$(xcrun notarytool submit "${ZIP_NAME}" \
455+
--team-id "${DEVELOPER_TEAM_ID}" \
456+
--apple-id "${DEVELOPER_USER}" \
457+
--password "${DEVELOPER_ASP}" 2>&1)
458+
459+
echo "${STATUS}"
460+
461+
# Get the submission ID
462+
SUBMISSION_ID=$(echo "${STATUS}" | awk -F ': ' '/id:/ { print $2; exit; }')
463+
echo "Notarization submission ID: ${SUBMISSION_ID}"
464+
465+
echo "Waiting for Notarization to be completed ..."
466+
xcrun notarytool wait "${SUBMISSION_ID}" \
467+
--team-id "${DEVELOPER_TEAM_ID}" \
468+
--apple-id "${DEVELOPER_USER}" \
469+
--password "${DEVELOPER_ASP}"
470+
471+
# Print status information
472+
REQUEST_STATUS=$(xcrun notarytool info "${SUBMISSION_ID}" \
473+
--team-id "${DEVELOPER_TEAM_ID}" \
474+
--apple-id "${DEVELOPER_USER}" \
475+
--password "${DEVELOPER_ASP}" 2>&1 | \
476+
awk -F ': ' '/status:/ { print $2; }')
477+
478+
if [[ "${REQUEST_STATUS}" != "Accepted" ]]; then
479+
echo "Notarization failed."
480+
exit 1
481+
fi
482+
483+
# Staple the notarization
484+
echo "Stapling the notarization to the pgAdmin 4 app..."
485+
if [[ "${ZIP_NAME##*.}" == "zip" ]]; then
486+
xcrun stapler staple ${BUNDLE_DIR}
487+
ditto -c -k --keepParent ${BUNDLE_DIR} ${ZIP_NAME}
488+
fi
489+
490+
if [ $? != 0 ]; then
491+
echo "ERROR: could not staple ${ZIP_NAME}"
492+
exit 1
493+
fi
494+
495+
echo "Notarization completed successfully."
496+
}
430497
431-
_notarize_pkg() {
498+
_notarize_dmg() {
432499
if [ "${CODESIGN}" -eq 0 ]; then
433500
return
434501
fi

pkg/mac/build.sh

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,32 @@ if [ "${PGADMIN_PYTHON_VERSION}" == "" ]; then
5757
export PGADMIN_PYTHON_VERSION=3.13.1
5858
fi
5959

60+
# Initialize variables
61+
CREATE_ZIP=0
62+
CREATE_DMG=1
63+
64+
# Parse command line arguments
65+
while [[ $# -gt 0 ]]; do
66+
case $1 in
67+
--zip)
68+
CREATE_ZIP=1
69+
shift
70+
;;
71+
--help)
72+
echo "Usage: $0 [OPTIONS]"
73+
echo "Options:"
74+
echo " --zip Create both ZIP and DMG files"
75+
echo " --help Display this help message"
76+
exit 0
77+
;;
78+
*)
79+
echo "Unknown option: $1"
80+
echo "Use --help for usage information"
81+
exit 1
82+
;;
83+
esac
84+
done
85+
6086
# shellcheck disable=SC1091
6187
source "${SCRIPT_DIR}/build-functions.sh"
6288

@@ -69,6 +95,16 @@ _complete_bundle
6995
_generate_sbom
7096
_codesign_binaries
7197
_codesign_bundle
72-
_create_dmg
73-
_codesign_dmg
74-
_notarize_pkg
98+
99+
# Handle ZIP creation if requested
100+
if [ "${CREATE_ZIP}" -eq 1 ]; then
101+
_create_zip
102+
_notarize_zip
103+
fi
104+
105+
# Handle DMG creation if not disabled
106+
if [ "${CREATE_DMG}" -eq 1 ]; then
107+
_create_dmg
108+
_codesign_dmg
109+
_notarize_dmg
110+
fi

0 commit comments

Comments
 (0)