forked from wrenn/wrenn
Compare commits
2 Commits
v0.1.6
...
10148f5b06
| Author | SHA1 | Date | |
|---|---|---|---|
| 10148f5b06 | |||
| a5720d7673 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -55,3 +55,5 @@ internal/dashboard/static/*
|
|||||||
.dual-graph/
|
.dual-graph/
|
||||||
# Added by code-review-graph
|
# Added by code-review-graph
|
||||||
.code-review-graph/
|
.code-review-graph/
|
||||||
|
|
||||||
|
__pycache__
|
||||||
|
|||||||
@ -21,7 +21,6 @@ steps:
|
|||||||
from_secret: wrenn_api_key
|
from_secret: wrenn_api_key
|
||||||
commands:
|
commands:
|
||||||
- pip install wrenn
|
- pip install wrenn
|
||||||
- export RUST_VERSION=$$(grep '^rust-version ' envd-rs/Cargo.toml | cut -d'"' -f2)
|
|
||||||
- python .woodpecker/scripts/build_rust.py
|
- python .woodpecker/scripts/build_rust.py
|
||||||
depends_on: []
|
depends_on: []
|
||||||
|
|
||||||
|
|||||||
@ -73,6 +73,7 @@ def install_rust(capsule: Capsule) -> bool:
|
|||||||
def clone_repo(capsule: Capsule) -> bool:
|
def clone_repo(capsule: Capsule) -> bool:
|
||||||
try:
|
try:
|
||||||
capsule.git.clone(REPO_URL, REPO_DIR)
|
capsule.git.clone(REPO_URL, REPO_DIR)
|
||||||
|
capsule.commands.run(f"cd {REPO_DIR} && git checkout fix/large-operations")
|
||||||
print("OK [git clone]")
|
print("OK [git clone]")
|
||||||
return True
|
return True
|
||||||
except GitCommandError as e:
|
except GitCommandError as e:
|
||||||
@ -84,8 +85,19 @@ def build_rust(capsule: Capsule) -> bool:
|
|||||||
if run(capsule, f"mkdir -p {REPO_DIR}/builds") != 0:
|
if run(capsule, f"mkdir -p {REPO_DIR}/builds") != 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# result = capsule.commands.run("file --version")
|
||||||
|
# print(result.stdout)
|
||||||
|
# result = capsule.commands.run(
|
||||||
|
# 'git rev-parse --short HEAD 2>/dev/null || echo "unknown"'
|
||||||
|
# )
|
||||||
|
# commit = result.stdout
|
||||||
|
|
||||||
|
# run(capsule, f"mkdir -p {REPO_DIR}/builds")
|
||||||
|
# result = capsule.commands.run("which musl-gcc")
|
||||||
|
# print(result.stdout)
|
||||||
|
|
||||||
handle = capsule.commands.run(
|
handle = capsule.commands.run(
|
||||||
"make build-envd",
|
"git checkout fix/large-operations && make build-envd",
|
||||||
background=True,
|
background=True,
|
||||||
cwd=REPO_DIR,
|
cwd=REPO_DIR,
|
||||||
envs={"PATH": RUST_PATH},
|
envs={"PATH": RUST_PATH},
|
||||||
@ -106,6 +118,23 @@ def build_rust(capsule: Capsule) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
print("OK [rust build]")
|
print("OK [rust build]")
|
||||||
|
|
||||||
|
# if (
|
||||||
|
# run(
|
||||||
|
# capsule,
|
||||||
|
# f"cp {REPO_DIR}/envd-rs/target/x86_64-unknown-linux-musl/release/envd {REPO_DIR}/builds/envd",
|
||||||
|
# envs={"BIN_DIR": REPO_DIR},
|
||||||
|
# )
|
||||||
|
# != 0
|
||||||
|
# ):
|
||||||
|
# return False
|
||||||
|
|
||||||
|
# result = capsule.commands.run(f"readelf -d {REPO_DIR}/builds/envd 2>&1")
|
||||||
|
# print(result.stdout, end="")
|
||||||
|
# if result.stderr:
|
||||||
|
# print(result.stderr, end="", file=sys.stderr)
|
||||||
|
# result = capsule.commands.run(f"file {REPO_DIR}/builds/envd 2>&1")
|
||||||
|
# print(result.stdout)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -105,73 +105,32 @@ def get_git_context(
|
|||||||
|
|
||||||
def generate_release_notes(
|
def generate_release_notes(
|
||||||
capsule: Capsule,
|
capsule: Capsule,
|
||||||
|
current_tag: str,
|
||||||
|
git_log: str,
|
||||||
|
git_diff: str,
|
||||||
output_path: str,
|
output_path: str,
|
||||||
model: str,
|
model: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
prompt = f"""
|
prompt = (
|
||||||
You are inside a cloned git repository at:
|
f"You are writing release notes for version {current_tag} of a software project.\n\n"
|
||||||
|
f"Here is what changed between the previous version and this one:\n\n"
|
||||||
{REPO_DIR}
|
f"Commit messages:\n{git_log}\n\n"
|
||||||
|
f"Files and areas that changed:\n{git_diff}\n\n"
|
||||||
Generate release notes for the latest tagged version of this software project.
|
f"Write the release notes in plain, friendly language that any developer can understand "
|
||||||
|
f"without deep knowledge of the codebase. Avoid jargon like 'goroutine', 'PTY', 'envd', "
|
||||||
Before writing anything, inspect the repository yourself using git commands.
|
f"or internal function names — describe what the change means for the user instead. "
|
||||||
|
f"Group related changes under headings that reflect what actually changed. "
|
||||||
You MUST determine:
|
f"Only include sections that are relevant to these specific changes. "
|
||||||
1. The latest version tag.
|
f"Start with a short one-line summary of what this release is about. "
|
||||||
2. The previous version tag, if one exists.
|
f"Keep each bullet point to one clear sentence.\n\n"
|
||||||
3. The commits between the previous tag and the latest tag.
|
f"Here is an example of the style to aim for — not a template to copy:\n\n"
|
||||||
4. The files and areas changed between those tags.
|
f"{RELEASE_NOTES_EXAMPLE}\n\n"
|
||||||
|
f"You MUST start the document with `## What's New`\n"
|
||||||
Use commands like:
|
f"The very next line MUST be a single short summary sentence.\n"
|
||||||
|
f"Output only the markdown. No intro, no explanation."
|
||||||
git tag --sort=-version:refname
|
f"CRITICAL: Do not output any conversational filler, acknowledgments, or thoughts "
|
||||||
|
f"like 'Let me look at the changes'. Output absolutely nothing except the final markdown."
|
||||||
If there are at least two tags, compare the newest tag against the previous tag:
|
)
|
||||||
|
|
||||||
git log PREVIOUS_TAG..LATEST_TAG --pretty=format:'%s (%h)'
|
|
||||||
git diff PREVIOUS_TAG..LATEST_TAG --stat
|
|
||||||
git diff PREVIOUS_TAG..LATEST_TAG --name-only
|
|
||||||
|
|
||||||
If there is only one tag, inspect the latest tag with:
|
|
||||||
|
|
||||||
git log LATEST_TAG --pretty=format:'%s (%h)' -n 50
|
|
||||||
git show LATEST_TAG --stat
|
|
||||||
git show LATEST_TAG --name-only
|
|
||||||
|
|
||||||
Do not rely on any pre-injected commit list or diff summary.
|
|
||||||
You must inspect the git history yourself.
|
|
||||||
|
|
||||||
Write the release notes in plain, friendly language that any developer can understand
|
|
||||||
without deep knowledge of the codebase.
|
|
||||||
|
|
||||||
Avoid jargon like "goroutine", "PTY", "envd", or internal function names.
|
|
||||||
Describe what the change means for the user instead.
|
|
||||||
|
|
||||||
Group related changes under headings that reflect what actually changed.
|
|
||||||
Only include sections that are relevant to the actual changes.
|
|
||||||
Do not include CI/CD-only changes.
|
|
||||||
|
|
||||||
Start with:
|
|
||||||
|
|
||||||
## What's New
|
|
||||||
|
|
||||||
The very next line must be a single short summary sentence.
|
|
||||||
|
|
||||||
Keep each bullet point to one clear sentence.
|
|
||||||
|
|
||||||
Here is an example of the style to aim for — not a template to copy:
|
|
||||||
|
|
||||||
{RELEASE_NOTES_EXAMPLE}
|
|
||||||
|
|
||||||
Output only the final markdown.
|
|
||||||
No intro.
|
|
||||||
No explanation.
|
|
||||||
No conversational filler.
|
|
||||||
No acknowledgments.
|
|
||||||
No "I checked the logs" text.
|
|
||||||
No thoughts.
|
|
||||||
""".strip()
|
|
||||||
|
|
||||||
prompt_b64 = base64.b64encode(prompt.encode("utf-8")).decode("utf-8")
|
prompt_b64 = base64.b64encode(prompt.encode("utf-8")).decode("utf-8")
|
||||||
|
|
||||||
@ -186,22 +145,23 @@ def generate_release_notes(
|
|||||||
print(f"FAIL [write prompt]: {result.stderr}", file=sys.stderr)
|
print(f"FAIL [write prompt]: {result.stderr}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
# FIX: Wrapper function to handle execution and authentication dynamically
|
||||||
def run_opencode_with_model(target_model: str) -> int:
|
def run_opencode_with_model(target_model: str) -> int:
|
||||||
env = ""
|
env = ""
|
||||||
if "zhipu" in target_model.lower():
|
if "zhipu" in target_model.lower():
|
||||||
env = f"ZHIPU_API_KEY={os.environ.get('ZHIPU_API_KEY', '')}"
|
env = f"ZHIPU_API_KEY={os.environ.get('ZHIPU_API_KEY', '')}"
|
||||||
|
|
||||||
raw_output_path = "/tmp/opencode_raw.txt"
|
|
||||||
cmd = (
|
cmd = (
|
||||||
f"{env} "
|
f"{env} "
|
||||||
f"~/.opencode/bin/opencode run "
|
f"~/.opencode/bin/opencode run "
|
||||||
f'"Read the attached file and generate the release notes. Output ONLY markdown." '
|
f'"Read the attached file and generate the release notes. Output ONLY markdown." '
|
||||||
f"--model {target_model} "
|
f"--model {target_model} "
|
||||||
f"--file /tmp/oc_prompt.txt "
|
f"--file /tmp/oc_prompt.txt "
|
||||||
f"> {raw_output_path}"
|
f"> {output_path}"
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd_result = capsule.commands.run(cmd, cwd=REPO_DIR, timeout=300)
|
cmd_result = capsule.commands.run(cmd, cwd=REPO_DIR, timeout=120)
|
||||||
|
|
||||||
if cmd_result.exit_code != 0:
|
if cmd_result.exit_code != 0:
|
||||||
print(
|
print(
|
||||||
f"FAIL [opencode via {target_model}]: exit={cmd_result.exit_code}",
|
f"FAIL [opencode via {target_model}]: exit={cmd_result.exit_code}",
|
||||||
@ -210,30 +170,6 @@ def generate_release_notes(
|
|||||||
print(f"STDOUT:\n{cmd_result.stdout}", file=sys.stderr)
|
print(f"STDOUT:\n{cmd_result.stdout}", file=sys.stderr)
|
||||||
print(f"STDERR:\n{cmd_result.stderr}", file=sys.stderr)
|
print(f"STDERR:\n{cmd_result.stderr}", file=sys.stderr)
|
||||||
|
|
||||||
clean_cmd = (
|
|
||||||
f"awk 'found || /^## What.s [Nn]ew/ {{ found=1; print }}' "
|
|
||||||
f"{raw_output_path} > {output_path}"
|
|
||||||
)
|
|
||||||
|
|
||||||
clean_result = capsule.commands.run(clean_cmd, cwd=REPO_DIR, timeout=10)
|
|
||||||
if clean_result.exit_code != 0:
|
|
||||||
print(f"FAIL [clean output]: {clean_result.stderr}", file=sys.stderr)
|
|
||||||
return clean_result.exit_code
|
|
||||||
|
|
||||||
check_result = capsule.commands.run(
|
|
||||||
f"grep -q '^## What.s New' {output_path}",
|
|
||||||
cwd=REPO_DIR,
|
|
||||||
timeout=10,
|
|
||||||
)
|
|
||||||
if check_result.exit_code != 0:
|
|
||||||
print(
|
|
||||||
"FAIL: Could not find release notes heading in opencode output",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
print(cmd_result.stdout, file=sys.stderr)
|
|
||||||
print(cmd_result.stderr, file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
return cmd_result.exit_code
|
return cmd_result.exit_code
|
||||||
|
|
||||||
# First attempt with the target model
|
# First attempt with the target model
|
||||||
@ -284,13 +220,24 @@ def main() -> None:
|
|||||||
capsule.git.clone(
|
capsule.git.clone(
|
||||||
REPO_URL,
|
REPO_URL,
|
||||||
REPO_DIR,
|
REPO_DIR,
|
||||||
|
username="tksadik92",
|
||||||
)
|
)
|
||||||
print("OK [git clone]")
|
print("OK [git clone]")
|
||||||
|
|
||||||
|
current_tag, previous_tag = get_tags(capsule)
|
||||||
|
git_log, git_diff = get_git_context(capsule, current_tag, previous_tag)
|
||||||
|
|
||||||
# Note: This simply creates the directory string safely
|
# Note: This simply creates the directory string safely
|
||||||
output_path = os.path.normpath(CAPSULE_OUTPUT)
|
output_path = os.path.normpath(CAPSULE_OUTPUT)
|
||||||
|
|
||||||
generate_release_notes(capsule, output_path, model)
|
generate_release_notes(
|
||||||
|
capsule,
|
||||||
|
current_tag,
|
||||||
|
git_log,
|
||||||
|
git_diff,
|
||||||
|
output_path,
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
|
||||||
download_release_notes(capsule)
|
download_release_notes(capsule)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user