Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Installs the given GardenLinux Python library
inputs:
version:
description: GardenLinux Python library version
default: "0.10.16"
default: "0.10.19"
python_version:
description: Python version to setup
default: "3.13"
Expand Down
62 changes: 14 additions & 48 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "gardenlinux"
version = "0.10.16"
version = "0.10.19"
description = "Contains tools to work with the features directory of gardenlinux, for example deducting dependencies from feature sets or validating cnames"
authors = ["Garden Linux Maintainers <contact@gardenlinux.io>"]
license = "Apache-2.0"
Expand All @@ -21,7 +21,7 @@ pygit2 = "^1.19.1"
pygments = "^2.19.2"
PyGithub = "^2.8.1"
PyYAML = "^6.0.2"
gitpython = "^3.1.45"
semver = "^3.0.4"

[tool.poetry.group.dev.dependencies]
bandit = "^1.9.3"
Expand Down
9 changes: 2 additions & 7 deletions src/gardenlinux/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@

GL_BUG_REPORT_URL = "https://github.com/gardenlinux/gardenlinux/issues"
GL_COMMIT_SPECIAL_VALUES = ("local",)
GL_CONTAINER_REGISTRY_BASE_URL = "ghcr.io/gardenlinux/gardenlinux"
GL_DEB_REPO_BASE_URL = "https://packages.gardenlinux.io/gardenlinux"
GL_DISTRIBUTION_NAME = "Garden Linux"
GL_HOME_URL = "https://gardenlinux.io"
Expand All @@ -157,17 +158,11 @@
OCI_ANNOTATION_SIGNED_STRING_KEY = "io.gardenlinux.oci.signed-string"
OCI_IMAGE_INDEX_MEDIA_TYPE = "application/vnd.oci.image.index.v1+json"

RELEASE_ID_FILE = ".github_release_id"

REQUESTS_TIMEOUTS = (5, 30) # connect, read
REQUESTS_TIMEOUTS = (5, 60) # connect, read

S3_DOWNLOADS_DIR = Path(os.path.dirname(__file__)) / ".." / "s3_downloads"

GARDENLINUX_GITHUB_RELEASE_BUCKET_NAME = "gardenlinux-github-releases"
GLVD_BASE_URL = "https://security.gardenlinux.org/v1"

PODMAN_CONNECTION_MAX_IDLE_SECONDS = 3

# https://github.com/gardenlinux/gardenlinux/issues/3044
# Empty string is the 'legacy' variant with traditional root fs and still needed/supported
IMAGE_VARIANTS = ["", "_usi", "_tpm2_trustedboot"]
63 changes: 63 additions & 0 deletions src/gardenlinux/distro_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from semver import Version


class UnsupportedDistroVersion(Exception):
pass


class NotAPatchRelease(Exception):
pass


class DistroVersion(Version): # type: ignore[misc]
def __init__(self, version: str | Version):
self._version_format_without_patch_number = False

try:
if isinstance(version, Version):
version_parsed = version
elif len(version.split(".")) == 2:
# Support version strings without patch numbers
version_parsed = Version.parse(f"{version}.0")
self._version_format_without_patch_number = True
else:
version_parsed = Version.parse(version)
except Exception as exc:
raise UnsupportedDistroVersion(exc)

Version.__init__(
self,
version_parsed.major,
version_parsed.minor,
version_parsed.patch,
version_parsed.prerelease,
version_parsed.build,
)

@property
def is_patch_release(self) -> bool:
if self._version_format_without_patch_number:
return self.minor > 0 # type: ignore[no-any-return]

return self.patch > 0 # type: ignore[no-any-return]

@property
def previous_patch_release(self) -> str:
if not self.is_patch_release:
raise NotAPatchRelease(f"{self} is not a patch release")

if self._version_format_without_patch_number:
previous_version = DistroVersion(
Version(self.major, self.minor - 1, self.patch)
)
return f"{previous_version.major}.{previous_version.minor}"

return str(
Version(
self.major,
self.minor,
self.patch - 1,
prerelease=self.prerelease,
build=self.build,
)
)
73 changes: 0 additions & 73 deletions src/gardenlinux/distro_version/__init__.py

This file was deleted.

110 changes: 3 additions & 107 deletions src/gardenlinux/github/release/__init__.py
Original file line number Diff line number Diff line change
@@ -1,109 +1,5 @@
import json
import logging
import os
import sys

import requests

from ...constants import RELEASE_ID_FILE, REQUESTS_TIMEOUTS
from ...logger import LoggerSetup
from .deployment_platform import DeploymentPlatform
from .release import Release
from .release_images_metadata import ReleaseImagesMetadata

LOGGER = LoggerSetup.get_logger("gardenlinux.github.release", logging.INFO)


def create_github_release(
owner: str, repo: str, tag: str, commitish: str, latest: bool, body: str
) -> int | None:
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable not set")

headers = {
"Authorization": f"token {token}",
"Accept": "application/vnd.github.v3+json",
}

data = {
"tag_name": tag,
"target_commitish": commitish,
"name": tag,
"body": body,
"draft": False,
"prerelease": False,
"make_latest": "true" if latest else "false",
}

response = requests.post(
f"https://api.github.com/repos/{owner}/{repo}/releases",
headers=headers,
data=json.dumps(data),
timeout=REQUESTS_TIMEOUTS,
)

if response.status_code == 201:
LOGGER.info("Release created successfully")
response_json = response.json()
return int(response_json.get("id")) # Will raise KeyError if missing
else:
LOGGER.error("Failed to create release")
LOGGER.debug(response.json())
response.raise_for_status()

return None # Simply to make mypy happy. should not be reached.


def write_to_release_id_file(release_id: str | int) -> None:
try:
with open(RELEASE_ID_FILE, "w") as file:
file.write(str(release_id))
LOGGER.info(f"Created {RELEASE_ID_FILE} successfully.")
except IOError as e:
LOGGER.error(f"Could not create {RELEASE_ID_FILE} file: {e}")
sys.exit(1)


def upload_to_github_release_page(
github_owner: str,
github_repo: str,
gardenlinux_release_id: str | int,
file_to_upload: str,
dry_run: bool,
) -> None:
if dry_run:
LOGGER.info(
f"Dry run: would upload {file_to_upload} to release {gardenlinux_release_id} in repo {github_owner}/{github_repo}"
)
return

token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable not set")

headers = {
"Authorization": f"token {token}",
"Content-Type": "application/octet-stream",
}

upload_url = f"https://uploads.github.com/repos/{github_owner}/{github_repo}/releases/{gardenlinux_release_id}/assets?name={os.path.basename(file_to_upload)}"

try:
with open(file_to_upload, "rb") as f:
file_contents = f.read()
except IOError as e:
LOGGER.error(f"Error reading file {file_to_upload}: {e}")
return

response = requests.post(
upload_url, headers=headers, data=file_contents, timeout=REQUESTS_TIMEOUTS
)
if response.status_code == 201:
LOGGER.info("Upload successful")
else:
LOGGER.error(
f"Upload failed with status code {response.status_code}: {response.text}"
)
response.raise_for_status()


__all__ = ["Release", "write_to_release_id_file", "upload_to_github_release_page"]
__all__ = ["DeploymentPlatform", "Release", "ReleaseImagesMetadata"]
Loading
Loading