Skip to content

Commit 5fe3ab4

Browse files
committed
Add support to create pre-releases with a generated changelog
Signed-off-by: Tobias Wolf <[email protected]> On-behalf-of: SAP <[email protected]>
1 parent e5e9c52 commit 5fe3ab4

File tree

12 files changed

+748
-313
lines changed

12 files changed

+748
-313
lines changed

.github/workflows/release.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: pre-release
2+
3+
on:
4+
push:
5+
tags: [ "[0-9]+.[0-9]+.[0-9]+*" ]
6+
7+
jobs:
8+
create-pre-release:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: write
12+
13+
steps:
14+
- name: Checkout commit
15+
uses: actions/checkout@v6
16+
- uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@main
17+
with:
18+
version: 1.0.0-pre1
19+
- name: Use cargo cache
20+
uses: actions/cache@v4
21+
with:
22+
path: |
23+
~/.cargo
24+
save-always: true
25+
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
26+
restore-keys: ${{ runner.os }}-cargo-
27+
- name: Get the Git tag name
28+
id: get-tag-name
29+
run: echo "tag-name=${GITHUB_REF/refs\/tags\//}" | tee -a "$GITHUB_OUTPUT"
30+
- id: release
31+
name: Create changelog and release
32+
env:
33+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34+
run: |
35+
cargo update --recursive git-cliff
36+
37+
gl-gh-release create \
38+
--repo "python-gardenlinux-lib" \
39+
--tag "${{ steps.get-tag-name.outputs.tag-name }}" \
40+
--commit "${{ github.sha }}" \
41+
--name 'python-gardenlinux-lib v${{ steps.get-tag-name.outputs.tag-name }}' \
42+
--body "
43+
Changes for v${{ steps.get-tag-name.outputs.tag-name }}
44+
45+
$(git-cliff -o - --current --topo-order")
46+
"

poetry.lock

Lines changed: 315 additions & 231 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ packages = [{ include = "gardenlinux", from = "src" }]
1010
[tool.poetry.dependencies]
1111
python = ">=3.13"
1212
apt-repo = "^0.5"
13-
boto3 = "^1.40.57"
13+
boto3 = "^1.41.5"
1414
click = "^8.2.1"
1515
cryptography = "^46.0.1"
1616
jsonschema = "^4.25.1"
1717
networkx = "^3.5"
1818
oras = "^0.2.38"
1919
pygit2 = "^1.19.0"
2020
pygments = "^2.19.2"
21+
PyGithub = "^2.8.1"
2122
PyYAML = "^6.0.2"
2223
gitpython = "^3.1.45"
2324

@@ -26,11 +27,11 @@ bandit = "^1.8.6"
2627
black = "^25.1.0"
2728
moto = "^5.1.12"
2829
python-dotenv = "^1.1.1"
29-
pytest = "^9.0.0"
30+
pytest = "^9.0.1"
3031
pytest-cov = "^7.0.0"
3132
isort = "^7.0.0"
3233
requests-mock = "^1.12.1"
33-
pyright = "^1.1.403"
34+
pyright = "^1.1.407"
3435

3536
[tool.poetry.group.docs.dependencies]
3637
sphinx-rtd-theme = "^3.0.2"

src/gardenlinux/github/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
GitHub module
5+
"""
6+
7+
from .client import Client
8+
9+
__all__ = ["Client"]

src/gardenlinux/github/client.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
GitHub client
5+
"""
6+
7+
from logging import Logger
8+
from os import environ
9+
from typing import Any, Optional
10+
11+
from github import Auth, Github
12+
13+
from ..logger import LoggerSetup
14+
15+
16+
class Client(object):
17+
"""
18+
GitHub client instance to provide methods for interaction with GitHub API.
19+
20+
:author: Garden Linux Maintainers
21+
:copyright: Copyright 2024 SAP SE
22+
:package: gardenlinux
23+
:subpackage: github
24+
:since: 1.0.0
25+
:license: https://www.apache.org/licenses/LICENSE-2.0
26+
Apache License, Version 2.0
27+
"""
28+
29+
def __init__(self, token: Optional[str] = None, logger: Optional[Logger] = None):
30+
"""
31+
Constructor __init__(Client)
32+
33+
:param token: GitHub access token
34+
:param logger: Logger instance
35+
36+
:since: 1.0.0
37+
"""
38+
39+
self._client = None
40+
self._token = token
41+
42+
if self._token is None or self._token.strip() == "":
43+
self._token = environ.get("GITHUB_TOKEN")
44+
45+
if self._token is None:
46+
raise ValueError("GITHUB_TOKEN environment variable not set")
47+
48+
if logger is None or not logger.hasHandlers():
49+
logger = LoggerSetup.get_logger("gardenlinux.github")
50+
51+
self._logger = logger
52+
53+
@property
54+
def instance(self) -> Github:
55+
if self._client is None:
56+
self._client = Github(auth=Auth.Token(self._token))
57+
58+
return self._client
59+
60+
def __getattr__(self, name: str) -> Any:
61+
"""
62+
python.org: Called when an attribute lookup has not found the attribute in
63+
the usual places (i.e. it is not an instance attribute nor is it found in the
64+
class tree for self).
65+
66+
:param name: Attribute name
67+
68+
:return: (mixed) Attribute
69+
:since: 0.8.0
70+
"""
71+
72+
self._logger.debug(f"gardenlinux.github.Client.{name} accessed")
73+
return getattr(self.instance, name)
Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,15 @@
1-
import json
21
import os
32
import sys
43

54
import requests
65

7-
from gardenlinux.constants import RELEASE_ID_FILE, REQUESTS_TIMEOUTS
8-
from gardenlinux.logger import LoggerSetup
6+
from ...constants import RELEASE_ID_FILE, REQUESTS_TIMEOUTS
7+
from ...logger import LoggerSetup
8+
from .release import Release
99

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

1212

13-
def create_github_release(owner, repo, tag, commitish, latest, body):
14-
token = os.environ.get("GITHUB_TOKEN")
15-
if not token:
16-
raise ValueError("GITHUB_TOKEN environment variable not set")
17-
18-
headers = {
19-
"Authorization": f"token {token}",
20-
"Accept": "application/vnd.github.v3+json",
21-
}
22-
23-
data = {
24-
"tag_name": tag,
25-
"target_commitish": commitish,
26-
"name": tag,
27-
"body": body,
28-
"draft": False,
29-
"prerelease": False,
30-
"make_latest": "true" if latest else "false",
31-
}
32-
33-
response = requests.post(
34-
f"https://api.github.com/repos/{owner}/{repo}/releases",
35-
headers=headers,
36-
data=json.dumps(data),
37-
timeout=REQUESTS_TIMEOUTS,
38-
)
39-
40-
if response.status_code == 201:
41-
LOGGER.info("Release created successfully")
42-
response_json = response.json()
43-
return response_json.get("id")
44-
else:
45-
LOGGER.error("Failed to create release")
46-
LOGGER.debug(response.json())
47-
response.raise_for_status()
48-
49-
5013
def write_to_release_id_file(release_id):
5114
try:
5215
with open(RELEASE_ID_FILE, "w") as file:
@@ -94,3 +57,6 @@ def upload_to_github_release_page(
9457
f"Upload failed with status code {response.status_code}: {response.text}"
9558
)
9659
response.raise_for_status()
60+
61+
62+
__all__ = ["Release", "write_to_release_id_file", "upload_to_github_release_page"]

src/gardenlinux/github/release/__main__.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
from gardenlinux.constants import GARDENLINUX_GITHUB_RELEASE_BUCKET_NAME
44
from gardenlinux.logger import LoggerSetup
55

6-
from .release import (
7-
create_github_release,
6+
from ..release_notes import create_github_release_notes
7+
from . import (
88
upload_to_github_release_page,
99
write_to_release_id_file,
1010
)
11-
from ..release_notes import create_github_release_notes
11+
from .release import Release
1212

1313
LOGGER = LoggerSetup.get_logger("gardenlinux.github", "INFO")
1414

@@ -21,9 +21,19 @@ def main():
2121
create_parser.add_argument("--owner", default="gardenlinux")
2222
create_parser.add_argument("--repo", default="gardenlinux")
2323
create_parser.add_argument("--tag", required=True)
24-
create_parser.add_argument("--commit", required=True)
24+
create_parser.add_argument("--name")
25+
create_parser.add_argument("--body", required=True)
26+
create_parser.add_argument("--commit")
27+
create_parser.add_argument("--pre-release", action="store_true", default=True)
2528
create_parser.add_argument("--latest", action="store_true", default=False)
26-
create_parser.add_argument("--dry-run", action="store_true", default=False)
29+
30+
create_parser_gl = subparsers.add_parser("create-with-gl-release-notes")
31+
create_parser_gl.add_argument("--owner", default="gardenlinux")
32+
create_parser_gl.add_argument("--repo", default="gardenlinux")
33+
create_parser_gl.add_argument("--tag", required=True)
34+
create_parser_gl.add_argument("--commit", required=True)
35+
create_parser_gl.add_argument("--latest", action="store_true", default=False)
36+
create_parser_gl.add_argument("--dry-run", action="store_true", default=False)
2737

2838
upload_parser = subparsers.add_parser("upload")
2939
upload_parser.add_argument("--owner", default="gardenlinux")
@@ -35,6 +45,15 @@ def main():
3545
args = parser.parse_args()
3646

3747
if args.command == "create":
48+
release = Release(args.repo, args.owner)
49+
release.tag = args.tag
50+
release.name = args.name
51+
release.body = args.body
52+
release.commit_hash = args.commit
53+
release.is_pre_release = args.pre_release
54+
release.is_latest = args.latest
55+
release.create()
56+
elif args.command == "create-with-gl-release-notes":
3857
body = create_github_release_notes(
3958
args.tag, args.commit, GARDENLINUX_GITHUB_RELEASE_BUCKET_NAME
4059
)
@@ -43,9 +62,13 @@ def main():
4362
print("This release would be created:")
4463
print(body)
4564
else:
46-
release_id = create_github_release(
47-
args.owner, args.repo, args.tag, args.commit, args.latest, body
48-
)
65+
release = Release(args.repo, args.owner)
66+
release.tag = args.tag
67+
release.body = body
68+
release.commit_hash = args.commit
69+
release.is_latest = args.latest
70+
71+
release_id = release.create()
4972
write_to_release_id_file(f"{release_id}")
5073
LOGGER.info(f"Release created with ID: {release_id}")
5174
elif args.command == "upload":

0 commit comments

Comments
 (0)