Skip to content

Commit a15d9db

Browse files
committed
Add CI/CD pipeline for automated releases
1 parent b9b5b11 commit a15d9db

File tree

4 files changed

+157
-4
lines changed

4 files changed

+157
-4
lines changed

.github/workflows/release.yml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
name: Build and Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- 'v*'
9+
10+
jobs:
11+
build-and-release:
12+
runs-on: windows-latest
13+
14+
steps:
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
18+
- name: Set up Python
19+
uses: actions/setup-python@v5
20+
with:
21+
python-version: '3.11'
22+
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install -e ".[dev]"
27+
28+
- name: Build with PyInstaller
29+
run: |
30+
pyinstaller --onefile --windowed --name "PDF-Page-Selector" --add-data "style.qss;." main.py
31+
32+
- name: Set up .NET
33+
uses: actions/setup-dotnet@v4
34+
with:
35+
dotnet-version: '8.0.x'
36+
37+
- name: Install vpk
38+
run: dotnet tool install -g vpk
39+
40+
- name: Generate version number
41+
id: version
42+
run: |
43+
$version = "1.0.${{ github.run_number }}"
44+
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
45+
echo "Version: $version"
46+
47+
- name: Package with Velopack
48+
run: |
49+
vpk pack --packId kelltom.pdfpageselector --packVersion ${{ steps.version.outputs.VERSION }} --packDir .\dist --mainExe PDF-Page-Selector.exe
50+
51+
- name: Create Release
52+
id: create_release
53+
uses: actions/create-release@v1
54+
env:
55+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
56+
with:
57+
tag_name: v${{ steps.version.outputs.VERSION }}
58+
release_name: Release v${{ steps.version.outputs.VERSION }}
59+
draft: false
60+
prerelease: false
61+
62+
- name: Upload Release Assets
63+
uses: actions/upload-release-asset@v1
64+
env:
65+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66+
with:
67+
upload_url: ${{ steps.create_release.outputs.upload_url }}
68+
asset_path: ./Releases/kelltom.pdfpageselector-${{ steps.version.outputs.VERSION }}-win-Setup.exe
69+
asset_name: PDF-Page-Selector-Setup.exe
70+
asset_content_type: application/octet-stream
71+
72+
- name: Upload RELEASES file
73+
uses: actions/upload-release-asset@v1
74+
env:
75+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
76+
with:
77+
upload_url: ${{ steps.create_release.outputs.upload_url }}
78+
asset_path: ./Releases/RELEASES
79+
asset_name: RELEASES
80+
asset_content_type: text/plain
81+
82+
- name: Upload nupkg file
83+
run: |
84+
$nupkg = Get-ChildItem -Path .\Releases\*.nupkg | Select-Object -First 1
85+
if ($nupkg) {
86+
echo "NUPKG_PATH=$($nupkg.FullName)" >> $env:GITHUB_OUTPUT
87+
echo "NUPKG_NAME=$($nupkg.Name)" >> $env:GITHUB_OUTPUT
88+
}
89+
id: find_nupkg
90+
91+
- name: Upload nupkg to release
92+
uses: actions/upload-release-asset@v1
93+
env:
94+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
95+
with:
96+
upload_url: ${{ steps.create_release.outputs.upload_url }}
97+
asset_path: ${{ steps.find_nupkg.outputs.NUPKG_PATH }}
98+
asset_name: ${{ steps.find_nupkg.outputs.NUPKG_NAME }}
99+
asset_content_type: application/octet-stream

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ share/python-wheels/
2626
*.egg
2727
MANIFEST
2828

29+
# Velopack releases
30+
Releases/
31+
2932
# PyInstaller
3033
# Usually these files are written by a python script from a template
3134
# before PyInstaller builds the exe, so as to inject date/other infos into it.

main.py

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import os
22
import sys
3-
from pathlib import Path
3+
import fitz # PyMuPDF
4+
import velopack
45

6+
from pathlib import Path
57
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
68
QHBoxLayout, QLabel, QPushButton, QLineEdit,
79
QFileDialog, QMessageBox, QGroupBox, QRadioButton,
8-
QButtonGroup)
10+
QButtonGroup, QMenu)
911
from PyQt6.QtCore import Qt
10-
from PyQt6.QtGui import QFont
11-
import fitz # PyMuPDF
12+
from PyQt6.QtGui import QFont, QAction
1213

1314

1415
class Mode:
@@ -248,6 +249,37 @@ def convert_to_images(input_path, page_numbers, output_path):
248249
return message
249250

250251

252+
def update_app():
253+
"""Check for updates and apply them if available."""
254+
try:
255+
# Check for updates from GitHub releases
256+
# GitHub Actions will upload release files to the latest release
257+
manager = velopack.UpdateManager("https://github.com/kelltom/pdf-page-selector/releases/latest/download/")
258+
update_info = manager.check_for_updates()
259+
260+
if not update_info:
261+
QMessageBox.information(None, "No Updates", "You're running the latest version!")
262+
return # no updates available
263+
264+
# Ask user if they want to update
265+
reply = QMessageBox.question(
266+
None, "Update Available",
267+
f"A new version is available. Would you like to download and install it?\n\nThis will restart the application.",
268+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
269+
)
270+
271+
if reply == QMessageBox.StandardButton.Yes:
272+
# Download the updates
273+
manager.download_updates(update_info)
274+
275+
# Apply the update and restart the app
276+
manager.apply_updates_and_restart(update_info)
277+
except Exception as e:
278+
# Only show error if it's not the "NotInstalled" development mode error
279+
if "NotInstalled" not in str(e):
280+
QMessageBox.warning(None, "Update Error", f"Could not check for updates:\n{str(e)}")
281+
282+
251283
class PDFPageSelectorApp(QMainWindow):
252284
def __init__(self):
253285
super().__init__()
@@ -302,6 +334,9 @@ def init_ui(self):
302334
self.resize(400, 580)
303335
self.setStyleSheet(load_stylesheet())
304336

337+
# Create menu bar
338+
self._create_menu_bar()
339+
305340
central_widget = QWidget()
306341
self.setCentralWidget(central_widget)
307342
layout = QVBoxLayout(central_widget)
@@ -344,6 +379,18 @@ def init_ui(self):
344379

345380
layout.addStretch()
346381

382+
def _create_menu_bar(self):
383+
"""Create the application menu bar."""
384+
menubar = self.menuBar()
385+
386+
# Help menu
387+
help_menu = menubar.addMenu("Help")
388+
389+
# Check for Updates action
390+
update_action = QAction("Check for Updates", self)
391+
update_action.triggered.connect(update_app)
392+
help_menu.addAction(update_action)
393+
347394
def _create_input_section(self):
348395
group = QGroupBox("Input PDF")
349396
layout = QVBoxLayout()
@@ -564,6 +611,9 @@ def _ensure_output_path(self):
564611

565612

566613
if __name__ == "__main__":
614+
# Velopack needs to run first - it may quit/restart the process
615+
velopack.App().run()
616+
567617
app = QApplication(sys.argv)
568618
window = PDFPageSelectorApp()
569619
window.show()

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dependencies = [
3333
[project.optional-dependencies]
3434
dev = [
3535
"pyinstaller>=6.16.0",
36+
"velopack>=0.0.1369",
3637
]
3738

3839
[project.urls]

0 commit comments

Comments
 (0)