initial commit to copy repo
This commit is contained in:
commit
60760d4880
3 changed files with 266 additions and 0 deletions
44
README.md
Normal file
44
README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Changelog Generator
|
||||
|
||||
## Description
|
||||
|
||||
This Python script allows you to generate changelogs and release notes based on Redmine tickets.
|
||||
It uses the OpenAI API to automatically create the changelog entries.
|
||||
|
||||
## Features
|
||||
|
||||
- Retrieve projects and versions from Redmine
|
||||
- Retrieve tickets for a specific project and version
|
||||
- Generate changelog entries based on the ticket information using the OpenAI API
|
||||
- Output the changelog in AsciiDoc format
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.x
|
||||
- The following Python packages:
|
||||
- urllib
|
||||
- json
|
||||
- dotenv
|
||||
- ssl
|
||||
- argparse
|
||||
- Redmine API key
|
||||
- OpenAI API key
|
||||
|
||||
## Usage
|
||||
|
||||
1. Copy the `.env.example` file and rename it to `.env`. Fill in the environment variables `REDMINE_HOST`, `REDMINE_API_KEY`, and `OPENAI_API_KEY` with your access credentials.
|
||||
2. Run the script with the following arguments:
|
||||
|
||||
- `-p, --project`: The project ID for which the changelog should be generated.
|
||||
- `-f, --fixed-version`: The version ID for which the changelog should be generated.
|
||||
- `-t, --type`: The type of text to be generated (Changelog or Release Notes).
|
||||
|
||||
3. If you don't provide a project ID or version ID, a list of available projects and versions will be displayed for you to choose from.
|
||||
4. The generated changelog will be output in AsciiDoc format on the console.
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
python3 changelog_generator.py -p 47 -f 755 -t Changelog
|
||||
python3 changelog_generator.py -p 47 -f 755 -t ReleaseNotes
|
||||
```
|
||||
219
ai_generator.py
Normal file
219
ai_generator.py
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
import urllib.request as request
|
||||
import json
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
import ssl
|
||||
import argparse
|
||||
import textwrap
|
||||
|
||||
|
||||
def makeRedmineRequest(url: str, APIKey: str):
|
||||
context = ssl.create_default_context()
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
|
||||
req = request.Request(url, method="GET")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
req.add_header("X-Redmine-API-Key", APIKey)
|
||||
return request.urlopen(req, context=context)
|
||||
|
||||
|
||||
def makeProjectRequest(url: str, APIKey: str) -> dict:
|
||||
res = makeRedmineRequest(url, APIKey)
|
||||
json_data = json.load(res)
|
||||
project_info = {project["name"]: project["id"] for project in json_data["projects"]}
|
||||
|
||||
return project_info
|
||||
|
||||
|
||||
def makeVersionRequest(url: str, APIKey: str, project_id: int) -> dict:
|
||||
res = makeRedmineRequest(url, APIKey)
|
||||
json_data = json.load(res)
|
||||
|
||||
version_info = {
|
||||
version["name"]: version["id"]
|
||||
for version in json_data["versions"]
|
||||
if version["project"]["id"] == project_id and version["status"] == "open"
|
||||
}
|
||||
|
||||
return version_info
|
||||
|
||||
|
||||
def makeIssueRequest(url: str, APIKey: str) -> list:
|
||||
res = makeRedmineRequest(url, APIKey)
|
||||
json_data = json.load(res)
|
||||
issue_info = []
|
||||
for issue in json_data["issues"]:
|
||||
issue_dict = {
|
||||
"tracker": issue["tracker"]["name"],
|
||||
"subject": issue["subject"],
|
||||
"id": issue["id"],
|
||||
}
|
||||
issue_info.append(issue_dict)
|
||||
|
||||
return issue_info
|
||||
|
||||
|
||||
def openaiRequest(prompt: str, issues: str, api_key: str):
|
||||
endpoint = "https://api.openai.com/v1/chat/completions"
|
||||
|
||||
request_body = {
|
||||
"model": "gpt-4-0125-preview",
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a technical writer and responsible for writing the user documentation.",
|
||||
},
|
||||
{"role": "user", "content": prompt + issues},
|
||||
],
|
||||
}
|
||||
|
||||
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
|
||||
|
||||
req = request.Request(
|
||||
endpoint, method="POST", headers=headers, data=json.dumps(request_body).encode()
|
||||
)
|
||||
res = request.urlopen(req)
|
||||
response_data = json.load(res)
|
||||
generated_text = response_data["choices"][0]["message"]["content"]
|
||||
print(generated_text)
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
load_dotenv("./.env")
|
||||
rdmnHost = os.getenv("REDMINE_HOST")
|
||||
rdmnAPIKey = os.getenv("REDMINE_API_KEY")
|
||||
openaiAPIKey = os.getenv("OPENAI_API_KEY")
|
||||
except Exception as e:
|
||||
print("No Env_variables set: ", e)
|
||||
finally:
|
||||
rdmnHost = "https://redmine.tixeltec.de/redmine"
|
||||
if rdmnAPIKey is None or openaiAPIKey is None:
|
||||
print(
|
||||
"Please set environment variables for REDMINE_API_KEY and/or OPENAI_API_KEY"
|
||||
)
|
||||
exit(1)
|
||||
|
||||
desc = """This is a simple tool that allows you to generate changelog messages from Redmine using AI.
|
||||
|
||||
Usage:
|
||||
1. Create a `.env` file . Fill in the environment variables as follows:
|
||||
```
|
||||
REDMINE_HOST=your_redmine_host
|
||||
REDMINE_API_KEY=your_redmine_api_key
|
||||
OPENAI_API_KEY=your_openai_api_key
|
||||
```
|
||||
2. Run the script with the following arguments:\n
|
||||
- `-p, --project`: The project ID for which the changelog should be generated.
|
||||
- `-f, --fixed-version`: The version ID for which the changelog should be generated.
|
||||
- `-t, --type`: The type of text to be generated (Changelog or Release Notes).
|
||||
3. If you don't provide a project ID or version ID, a list of available projects and versions will be displayed for you to choose from.
|
||||
4. The generated changelog will be output in AsciiDoc format on the console.
|
||||
|
||||
Examples:
|
||||
python3 changelog_generator.py -p 47 -f 755 -t Changelog
|
||||
python3 changelog_generator.py -p 47 -f 755 -t ReleaseNotes
|
||||
"""
|
||||
clp = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=textwrap.dedent(desc),
|
||||
add_help=True,
|
||||
)
|
||||
clp.add_argument(
|
||||
"-p",
|
||||
"--project",
|
||||
help="The project for which text is to be generated",
|
||||
type=int,
|
||||
)
|
||||
clp.add_argument(
|
||||
"-f",
|
||||
"--fixed-version",
|
||||
help="The version of the project for which text is to be generated",
|
||||
type=int,
|
||||
)
|
||||
clp.add_argument(
|
||||
"-t",
|
||||
"--type",
|
||||
help="The type of text to be generated",
|
||||
choices=["Changelog", "ReleaseNotes"],
|
||||
type=str,
|
||||
)
|
||||
args = clp.parse_args()
|
||||
if not args.project:
|
||||
project_infos = makeProjectRequest(
|
||||
url=rdmnHost + "/projects.json", APIKey=rdmnAPIKey
|
||||
)
|
||||
print("Enter a project id when calling. There is a choice:")
|
||||
for project_name, project_id in project_infos.items():
|
||||
print(f"{project_name} (ID: {project_id})")
|
||||
if args.project and not args.fixed_version:
|
||||
print("Enter a version id when calling. There is a choice:")
|
||||
fixed_version = makeVersionRequest(
|
||||
url=rdmnHost + "/projects/" + str(args.project) + "/versions.json",
|
||||
APIKey=rdmnAPIKey,
|
||||
project_id=args.project,
|
||||
)
|
||||
for version_name, version_id in fixed_version.items():
|
||||
print(f"{version_name}: {version_id}")
|
||||
if args.project and args.fixed_version and not args.type:
|
||||
args.type = input("Please choose a type (Changelog/ReleaseNotes)")
|
||||
customField = ""
|
||||
prompt = ""
|
||||
if args.project and args.fixed_version:
|
||||
if args.type == "Changelog":
|
||||
customField = "&cf_21=Changelog"
|
||||
prompt = """Your task is to create the changelog for the new software version from the Redmine tickets:
|
||||
{{%input%}}.
|
||||
|
||||
== Changelog ==
|
||||
|
||||
{{#each tickets}}
|
||||
=== _({{id}})_ {{subject}} ===
|
||||
* {{Your generated changelog entry}} _(#{{ticket_id}})_
|
||||
{{/each}}
|
||||
|
||||
Do not include any additional text.
|
||||
Write your Output in AsciDoc-Format.
|
||||
If no Redmine Tickets are given, output "No Tickets"."""
|
||||
elif args.type == "ReleaseNotes":
|
||||
customField = "&cf_21=Release_notes"
|
||||
prompt = """Your task is to write a Changelog based on the following redmine tickets:
|
||||
\{\{\%input\%\}\}
|
||||
To make the review easier, always enter the subject from the ticket and then a short, descriptive and customer-readable sentence for the respective ticket.
|
||||
Insert the ticket ID at the end of each sentence in the form _(ticket-id)_.
|
||||
Write your output in AsciDoc-Format
|
||||
"""
|
||||
else:
|
||||
prompt = """Your task is to create the changelog for the new software version from the Redmine tickets:
|
||||
{{%input%}}.
|
||||
|
||||
== Changelog ==
|
||||
|
||||
{{#each tickets}}
|
||||
=== _({{id}})_ {{subject}} ===
|
||||
* {{Your generated changelog entry}} _(#{{ticket_id}})_
|
||||
{{/each}}
|
||||
|
||||
Do not include any additional text.
|
||||
Write your Output in AsciDoc-Format.
|
||||
If no Redmine Tickets are given, output "No Tickets"."""
|
||||
issues = makeIssueRequest(
|
||||
rdmnHost
|
||||
+ "/issues.json?project_id="
|
||||
+ str(args.project)
|
||||
+ "&fixed_version_id="
|
||||
+ str(args.fixed_version)
|
||||
+ "&status_id=*"
|
||||
+ customField,
|
||||
rdmnAPIKey,
|
||||
)
|
||||
issue_str = ""
|
||||
for issue in issues:
|
||||
issue_str += str(issue)
|
||||
issue_str += ", "
|
||||
openaiRequest(prompt=prompt, issues=issue_str, api_key=openaiAPIKey)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
3
env.example
Normal file
3
env.example
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
REDMINE_API_KEY = "Your redmine api key"
|
||||
REDMINE_HOST = "ypur redmine host address"
|
||||
OPENAI_API_KEY = "your openai apikey"
|
||||
Loading…
Add table
Add a link
Reference in a new issue