mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2025-11-11 02:58:02 -06:00
Add python script that automates uploading VeraCrypt release files to Launchpad
This commit is contained in:
158
src/Build/veracrypt-launchpad-uploader.py
Normal file
158
src/Build/veracrypt-launchpad-uploader.py
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# VeraCrypt Launchpad Uploader
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# Author: Mounir IDRASSI <mounir.idrassi@amcrypto.jp>
|
||||||
|
# Date: May 31st, 2025
|
||||||
|
#
|
||||||
|
# This script is part of the VeraCrypt project
|
||||||
|
# https://www.veracrypt.jp
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# This script automates the process of uploading VeraCrypt release packages
|
||||||
|
# to Launchpad. It authenticates with Launchpad, locates the appropriate
|
||||||
|
# project, series, milestone, and release, and then uploads all package files
|
||||||
|
# from a specified directory, skipping any that have already been uploaded.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
import os
|
||||||
|
import mimetypes
|
||||||
|
from launchpadlib.launchpad import Launchpad
|
||||||
|
|
||||||
|
# === CONFIGURATION ===
|
||||||
|
PROJECT_NAME = 'veracrypt'
|
||||||
|
SERIES_NAME = 'trunk'
|
||||||
|
MILESTONE_NAME = '1.26.24'
|
||||||
|
RELEASE_VERSION = '1.26.24'
|
||||||
|
FILES_DIRECTORY = r"/opt/VeraCrypt_Packages/1.26.24"
|
||||||
|
|
||||||
|
APPLICATION_NAME = 'launchpad-batch-uploader'
|
||||||
|
CACHEDIR = os.path.expanduser(r"~/.launchpadlib/cache")
|
||||||
|
|
||||||
|
# === AUTHENTICATION ===
|
||||||
|
print("Authenticating with Launchpad…")
|
||||||
|
launchpad = Launchpad.login_with(APPLICATION_NAME, 'production', CACHEDIR)
|
||||||
|
|
||||||
|
# === LOOK UP TARGET OBJECTS ===
|
||||||
|
try:
|
||||||
|
# First try direct dictionary-style lookup
|
||||||
|
project = launchpad.projects[PROJECT_NAME]
|
||||||
|
except KeyError:
|
||||||
|
# Fallback: use getByName on projects
|
||||||
|
project = launchpad.projects.getByName(name=PROJECT_NAME)
|
||||||
|
if project is None:
|
||||||
|
raise Exception(f"Project '{PROJECT_NAME}' not found.")
|
||||||
|
|
||||||
|
# Safely fetch the series object
|
||||||
|
try:
|
||||||
|
series = project.series[SERIES_NAME]
|
||||||
|
except (KeyError, TypeError):
|
||||||
|
series = project.getSeries(name=SERIES_NAME)
|
||||||
|
if series is None:
|
||||||
|
raise Exception(f"Series '{SERIES_NAME}' not found in project '{PROJECT_NAME}'.")
|
||||||
|
|
||||||
|
# === REPLACE getMilestone with a loop over all_milestones ===
|
||||||
|
milestone = None
|
||||||
|
print(f"Locating milestone '{MILESTONE_NAME}' in series '{SERIES_NAME}'…")
|
||||||
|
for m in series.all_milestones: # ← series.all_milestones is a PagedCollection of Milestone
|
||||||
|
if m.name == MILESTONE_NAME:
|
||||||
|
milestone = m
|
||||||
|
break
|
||||||
|
|
||||||
|
if milestone is None:
|
||||||
|
raise Exception(f"Milestone '{MILESTONE_NAME}' not found in series '{SERIES_NAME}'.")
|
||||||
|
|
||||||
|
# --- FIND THE RELEASE UNDER THAT MILESTONE ----------------------------
|
||||||
|
print(f"Locating release for milestone '{MILESTONE_NAME}'…")
|
||||||
|
|
||||||
|
try:
|
||||||
|
release = milestone.release # <-- the only release tied to this milestone
|
||||||
|
except AttributeError:
|
||||||
|
# (very old Launchpadlib versions expose only the _link)
|
||||||
|
release = launchpad.load(milestone.release_link)
|
||||||
|
|
||||||
|
# sanity-check
|
||||||
|
if release is None or release.version != RELEASE_VERSION:
|
||||||
|
raise Exception(
|
||||||
|
f"Expected version '{RELEASE_VERSION}', "
|
||||||
|
f"but milestone only links to '{getattr(release, 'version', None)}'."
|
||||||
|
)
|
||||||
|
print("Release found. Beginning upload…")
|
||||||
|
|
||||||
|
# === UPLOAD FILES ===
|
||||||
|
|
||||||
|
# Build a set of filenames already present on the release
|
||||||
|
existing_files = set()
|
||||||
|
for f in release.files:
|
||||||
|
# Each f is a URL; the filename is after the last '/'
|
||||||
|
filename_on_release = os.path.basename(f.self_link)
|
||||||
|
existing_files.add(filename_on_release)
|
||||||
|
|
||||||
|
# Print existing files if existing_files is not empty
|
||||||
|
if not existing_files:
|
||||||
|
print("No files already uploaded to this release.")
|
||||||
|
else:
|
||||||
|
print("Files already uploaded to this release:")
|
||||||
|
for ef in sorted(existing_files):
|
||||||
|
print(" -", ef)
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
for filename in os.listdir(FILES_DIRECTORY):
|
||||||
|
if filename.endswith('.sig'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if filename in existing_files:
|
||||||
|
print(f">>> Skipping {filename} (already uploaded)")
|
||||||
|
continue
|
||||||
|
|
||||||
|
filepath = os.path.join(FILES_DIRECTORY, filename)
|
||||||
|
sig_path = filepath + '.sig'
|
||||||
|
has_signature = os.path.isfile(sig_path)
|
||||||
|
|
||||||
|
content_type, _ = mimetypes.guess_type(filepath)
|
||||||
|
content_type = content_type or 'application/octet-stream'
|
||||||
|
|
||||||
|
print(f"Uploading: {filename} (type: {content_type})")
|
||||||
|
try:
|
||||||
|
with open(filepath, 'rb') as file_content:
|
||||||
|
file_bytes = file_content.read()
|
||||||
|
if has_signature:
|
||||||
|
with open(sig_path, 'rb') as sig_handle:
|
||||||
|
sig_bytes = sig_handle.read()
|
||||||
|
release.add_file(
|
||||||
|
description=f"Uploaded file: {filename}",
|
||||||
|
content_type=content_type,
|
||||||
|
filename=filename,
|
||||||
|
file_content=file_bytes,
|
||||||
|
signature_filename=os.path.basename(sig_path),
|
||||||
|
signature_content=sig_bytes
|
||||||
|
)
|
||||||
|
print(f" -> Uploaded {filename} with signature.")
|
||||||
|
else:
|
||||||
|
release.add_file(
|
||||||
|
description=f"Uploaded file: {filename}",
|
||||||
|
content_type=content_type,
|
||||||
|
filename=filename,
|
||||||
|
file_content=file_bytes,
|
||||||
|
signature_filename=None,
|
||||||
|
signature_content=None
|
||||||
|
)
|
||||||
|
print(f" -> Uploaded {filename} without signature.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"!!! Failed to upload '{filename}': {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
print("Done! All files uploaded (or attempted) successfully.")
|
||||||
Reference in New Issue
Block a user