Skip to content

Commit 716ff41

Browse files
committed
Add python script that automates uploading VeraCrypt release files to Launchpad
1 parent 44fc3ca commit 716ff41

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# =============================================================================
2+
# VeraCrypt Launchpad Uploader
3+
# =============================================================================
4+
#
5+
# Author: Mounir IDRASSI <mounir.idrassi@amcrypto.jp>
6+
# Date: May 31st, 2025
7+
#
8+
# This script is part of the VeraCrypt project
9+
# https://www.veracrypt.jp
10+
#
11+
# Licensed under the Apache License, Version 2.0 (the "License");
12+
# you may not use this file except in compliance with the License.
13+
# You may obtain a copy of the License at
14+
#
15+
# http://www.apache.org/licenses/LICENSE-2.0
16+
#
17+
# Unless required by applicable law or agreed to in writing, software
18+
# distributed under the License is distributed on an "AS IS" BASIS,
19+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
# See the License for the specific language governing permissions and
21+
# limitations under the License.
22+
#
23+
# Description:
24+
# This script automates the process of uploading VeraCrypt release packages
25+
# to Launchpad. It authenticates with Launchpad, locates the appropriate
26+
# project, series, milestone, and release, and then uploads all package files
27+
# from a specified directory, skipping any that have already been uploaded.
28+
# =============================================================================
29+
30+
import os
31+
import mimetypes
32+
from launchpadlib.launchpad import Launchpad
33+
34+
# === CONFIGURATION ===
35+
PROJECT_NAME = 'veracrypt'
36+
SERIES_NAME = 'trunk'
37+
MILESTONE_NAME = '1.26.24'
38+
RELEASE_VERSION = '1.26.24'
39+
FILES_DIRECTORY = r"/opt/VeraCrypt_Packages/1.26.24"
40+
41+
APPLICATION_NAME = 'launchpad-batch-uploader'
42+
CACHEDIR = os.path.expanduser(r"~/.launchpadlib/cache")
43+
44+
# === AUTHENTICATION ===
45+
print("Authenticating with Launchpad…")
46+
launchpad = Launchpad.login_with(APPLICATION_NAME, 'production', CACHEDIR)
47+
48+
# === LOOK UP TARGET OBJECTS ===
49+
try:
50+
# First try direct dictionary-style lookup
51+
project = launchpad.projects[PROJECT_NAME]
52+
except KeyError:
53+
# Fallback: use getByName on projects
54+
project = launchpad.projects.getByName(name=PROJECT_NAME)
55+
if project is None:
56+
raise Exception(f"Project '{PROJECT_NAME}' not found.")
57+
58+
# Safely fetch the series object
59+
try:
60+
series = project.series[SERIES_NAME]
61+
except (KeyError, TypeError):
62+
series = project.getSeries(name=SERIES_NAME)
63+
if series is None:
64+
raise Exception(f"Series '{SERIES_NAME}' not found in project '{PROJECT_NAME}'.")
65+
66+
# === REPLACE getMilestone with a loop over all_milestones ===
67+
milestone = None
68+
print(f"Locating milestone '{MILESTONE_NAME}' in series '{SERIES_NAME}'…")
69+
for m in series.all_milestones: # ← series.all_milestones is a PagedCollection of Milestone
70+
if m.name == MILESTONE_NAME:
71+
milestone = m
72+
break
73+
74+
if milestone is None:
75+
raise Exception(f"Milestone '{MILESTONE_NAME}' not found in series '{SERIES_NAME}'.")
76+
77+
# --- FIND THE RELEASE UNDER THAT MILESTONE ----------------------------
78+
print(f"Locating release for milestone '{MILESTONE_NAME}'…")
79+
80+
try:
81+
release = milestone.release # <-- the only release tied to this milestone
82+
except AttributeError:
83+
# (very old Launchpadlib versions expose only the _link)
84+
release = launchpad.load(milestone.release_link)
85+
86+
# sanity-check
87+
if release is None or release.version != RELEASE_VERSION:
88+
raise Exception(
89+
f"Expected version '{RELEASE_VERSION}', "
90+
f"but milestone only links to '{getattr(release, 'version', None)}'."
91+
)
92+
print("Release found. Beginning upload…")
93+
94+
# === UPLOAD FILES ===
95+
96+
# Build a set of filenames already present on the release
97+
existing_files = set()
98+
for f in release.files:
99+
# Each f is a URL; the filename is after the last '/'
100+
filename_on_release = os.path.basename(f.self_link)
101+
existing_files.add(filename_on_release)
102+
103+
# Print existing files if existing_files is not empty
104+
if not existing_files:
105+
print("No files already uploaded to this release.")
106+
else:
107+
print("Files already uploaded to this release:")
108+
for ef in sorted(existing_files):
109+
print(" -", ef)
110+
111+
print()
112+
113+
for filename in os.listdir(FILES_DIRECTORY):
114+
if filename.endswith('.sig'):
115+
continue
116+
117+
if filename in existing_files:
118+
print(f">>> Skipping {filename} (already uploaded)")
119+
continue
120+
121+
filepath = os.path.join(FILES_DIRECTORY, filename)
122+
sig_path = filepath + '.sig'
123+
has_signature = os.path.isfile(sig_path)
124+
125+
content_type, _ = mimetypes.guess_type(filepath)
126+
content_type = content_type or 'application/octet-stream'
127+
128+
print(f"Uploading: {filename} (type: {content_type})")
129+
try:
130+
with open(filepath, 'rb') as file_content:
131+
file_bytes = file_content.read()
132+
if has_signature:
133+
with open(sig_path, 'rb') as sig_handle:
134+
sig_bytes = sig_handle.read()
135+
release.add_file(
136+
description=f"Uploaded file: {filename}",
137+
content_type=content_type,
138+
filename=filename,
139+
file_content=file_bytes,
140+
signature_filename=os.path.basename(sig_path),
141+
signature_content=sig_bytes
142+
)
143+
print(f" -> Uploaded {filename} with signature.")
144+
else:
145+
release.add_file(
146+
description=f"Uploaded file: {filename}",
147+
content_type=content_type,
148+
filename=filename,
149+
file_content=file_bytes,
150+
signature_filename=None,
151+
signature_content=None
152+
)
153+
print(f" -> Uploaded {filename} without signature.")
154+
except Exception as e:
155+
print(f"!!! Failed to upload '{filename}': {e}")
156+
continue
157+
158+
print("Done! All files uploaded (or attempted) successfully.")

0 commit comments

Comments
 (0)