Skip to content

Commit ec618bc

Browse files
authored
fix: backport JAR-bundling and release-workflow fixes to 2.0 (#284, #289, #290, #292) (#294)
Ports the fixes shipped on main to the 2.0 line (2.0.0-rc.1 shipped jarless): - Force-include the codeanalyzer-java JAR via [tool.hatch.build] artifacts (#284) - Pipe-safe release guard that fails a jarless build before publishing (#284) - Source release notes from CHANGELOG.md and fail on blank; drop the label-based changelog scraper and orphaned release_config.json (#289) - Remove the obsolete native-binary test and its unused import (#290) - Fix the stale conftest docstring for the retired native binary (#292) Verified: build on this branch (with .git) yields a 32MB wheel+sdist containing the JAR; tests/analysis/java/test_jcodeanalyzer.py -> 42 passed.
1 parent 87dd6a5 commit ec618bc

6 files changed

Lines changed: 74 additions & 108 deletions

File tree

.github/workflows/release.yml

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,28 +65,66 @@ jobs:
6565
- name: Build Package
6666
run: uv build
6767

68-
- name: Read Changelog Entry
69-
id: changelog_reader
70-
uses: mindsers/changelog-reader-action@v2
71-
with:
72-
validation_level: warn
73-
version: ${{ steps.tag_name.outputs.current_version }}
74-
path: ./CHANGELOG.md
75-
76-
- name: Build Changelog
77-
id: gen_changelog
78-
uses: mikepenz/release-changelog-builder-action@v5
79-
with:
80-
failOnError: "true"
81-
configuration: .github/workflows/release_config.json
82-
env:
83-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68+
- name: Verify the codeanalyzer JAR is bundled
69+
# Guard against the hatchling/.gitignore regression (issue #284): a jarless wheel
70+
# installs fine but fails at runtime with "codeanalyzer jar not found". Fail the
71+
# release here rather than publish a broken artifact to PyPI.
72+
#
73+
# The listing is captured before grepping: piping `tar tzf` (which decompresses the
74+
# whole 32MB sdist) straight into `grep -q` lets grep close the pipe on first match,
75+
# SIGPIPE-killing tar and — under `pipefail` — reporting a false "missing JAR".
76+
run: |
77+
set -euo pipefail
78+
jar_re='codeanalyzer/jar/codeanalyzer-[0-9][^/]*\.jar$'
79+
fail=0
80+
for f in dist/*.whl dist/*.tar.gz; do
81+
case "$f" in
82+
*.whl) listing=$(unzip -l "$f") ;;
83+
*.tar.gz) listing=$(tar tzf "$f") ;;
84+
esac
85+
if grep -qE "$jar_re" <<<"$listing"; then
86+
echo " ✓ $f"
87+
else
88+
echo "::error::$f is missing the codeanalyzer JAR"
89+
grep -i '\.jar' <<<"$listing" || echo " (no .jar entries at all)"
90+
fail=1
91+
fi
92+
done
93+
if [ "$fail" -ne 0 ]; then
94+
echo "Refusing to publish a jarless release."; exit 1
95+
fi
96+
echo "codeanalyzer JAR present in wheel and sdist ✓"
97+
98+
- name: Extract release notes from CHANGELOG.md
99+
id: notes
100+
# Source the release body from the hand-written CHANGELOG.md section for this tag —
101+
# deterministic and independent of PR labels — and refuse to publish/announce a blank
102+
# body. The previous label-based changelog scraper emitted nothing for unlabeled PRs,
103+
# which blanked the release and crashed the org announcement. See issue #289.
104+
run: |
105+
set -euo pipefail
106+
version="${GITHUB_REF#refs/tags/}" # e.g. v1.4.4 — matches the "## [v1.4.4]" heading
107+
notes=$(awk -v h="## [$version]" '
108+
!seen && index($0, h) == 1 { seen = 1; next }
109+
seen && index($0, "## [") == 1 { exit }
110+
seen { print }
111+
' CHANGELOG.md | sed '/./,$!d' | tac | sed '/./,$!d' | tac) # strip blank edges
112+
if [ -z "$notes" ]; then
113+
echo "::error::No CHANGELOG.md entry for $version — refusing to publish a blank release."
114+
exit 1
115+
fi
116+
{
117+
echo "notes<<__CHANGELOG_EOF__"
118+
echo "$notes"
119+
echo "__CHANGELOG_EOF__"
120+
} >> "$GITHUB_OUTPUT"
121+
echo "Release notes for $version:"; echo "$notes"
84122
85123
- name: Publish Release on GitHub
86124
uses: softprops/action-gh-release@v2
87125
with:
88126
files: dist/*
89-
body: ${{ steps.gen_changelog.outputs.changelog }}
127+
body: ${{ steps.notes.outputs.notes }}
90128
# Auto-open a repo-level Discussion linked to this release, seeded with
91129
# the same notes. Requires Discussions enabled and this category to exist.
92130
discussion_category_name: Announcements
@@ -96,13 +134,13 @@ jobs:
96134
# Mirror the release announcement into the ORG-level discussions, which are
97135
# backed by codellm-devkit/.github. GITHUB_TOKEN can't write cross-repo, so
98136
# this uses a PAT (ORG_DISCUSSIONS_TOKEN) with repo scope, and posts via the
99-
# createDiscussion GraphQL mutation. The body (the generated changelog) is
137+
# createDiscussion GraphQL mutation. The body (the CHANGELOG.md notes) is
100138
# passed via env to avoid shell-injection, matching the repo-level post.
101139
- name: Announce in org-level discussions (codellm-devkit/.github)
102140
continue-on-error: true # a failed org post must not fail an otherwise-good release
103141
env:
104142
GH_TOKEN: ${{ secrets.ORG_DISCUSSIONS_TOKEN }}
105-
BODY: ${{ steps.gen_changelog.outputs.changelog }}
143+
BODY: ${{ steps.notes.outputs.notes }}
106144
run: |
107145
set -uo pipefail
108146
VERSION="${GITHUB_REF#refs/tags/v}"

.github/workflows/release_config.json

Lines changed: 0 additions & 65 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
- **Published wheels bundle the `codeanalyzer-java` JAR again.** `2.0.0-rc.1` (like the 1.2.0–1.4.3
12+
line) shipped without the bundled JAR, so `CLDK.java(...)` after a plain `pip install` raised
13+
`CodeanalyzerExecutionException: codeanalyzer jar not found`. Hatchling applied the root
14+
`.gitignore` `*.jar` rule at build time but not the nested `!codeanalyzer-*.jar` negation that
15+
keeps the JAR tracked in git; a `[tool.hatch.build] artifacts` rule force-includes it, and the
16+
release workflow now fails fast if a built artifact is missing the JAR. (#284)
17+
1018
## [v2.0.0-rc.1] - 2026-07-16
1119

1220
First release candidate for 2.0.0 — the schema-v2 release. Both the TypeScript and Python

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ test = [
7474
requires = ["hatchling"]
7575
build-backend = "hatchling.build"
7676

77+
# The codeanalyzer-java JAR is force-included here: it lives under a `*.jar` .gitignore
78+
# (re-included for git via a nested `!codeanalyzer-*.jar`), but hatchling honors the root
79+
# ignore and not the nested negation, so without this it is silently dropped from every
80+
# wheel/sdist built inside a git repo (i.e. in CI). See issue #284.
81+
[tool.hatch.build]
82+
artifacts = ["cldk/analysis/java/codeanalyzer/jar/*.jar"]
83+
7784
[tool.hatch.build.targets.wheel]
7885
packages = ["cldk"]
7986

tests/analysis/java/test_jcodeanalyzer.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"""
2020

2121
import os
22-
import sys
2322
import json
2423
from typing import Dict, List, Tuple
2524
from unittest.mock import patch, MagicMock
@@ -197,27 +196,6 @@ def test_init_codeanalyzer_reuses_legacy_cache_when_compatible(test_fixture, cod
197196
assert compilation_unit.import_declarations[0].is_wildcard is False
198197

199198

200-
def test_get_codeanalyzer_exec(test_fixture, analysis_json, tmp_path):
201-
"""Should resolve the codeanalyzer native binary command (packaged binary only)."""
202-
203-
# Patch subprocess so that it does not run codeanalyzer
204-
with patch("cldk.analysis.java.codeanalyzer.codeanalyzer.subprocess.run") as run_mock:
205-
run_mock.return_value = MagicMock(stdout=analysis_json, returncode=0)
206-
207-
code_analyzer = JCodeanalyzer(
208-
project_dir=test_fixture,
209-
source_code=None,
210-
analysis_json_path=None,
211-
analysis_level=AnalysisLevel.symbol_table,
212-
eager_analysis=False,
213-
target_files=None,
214-
)
215-
216-
# The PyPI native binary, invoked via `python -m codeanalyzer_java`. There is no longer a
217-
# backend-path override (the binary ships with the packaged dependency).
218-
assert code_analyzer._get_codeanalyzer_exec() == [sys.executable, "-m", "codeanalyzer_java"]
219-
220-
221199
def test_generate_call_graph(test_fixture, analysis_json):
222200
"""Should generate a graph"""
223201

tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ def analysis_json(analysis_json_fixture) -> str:
6666
def codeanalyzer_backend_path():
6767
"""Backend-path override for the Java analyzer in tests.
6868
69-
Returns None so the analyzer uses its default: the JVM-free native binary shipped in the
70-
``codeanalyzer-java`` PyPI package (``python -m codeanalyzer_java``).
69+
Returns None so the analyzer uses its default: the ``codeanalyzer-*.jar`` bundled under
70+
``cldk/analysis/java/codeanalyzer/jar/``, run on a cached JDK (``[java, -jar, <jar>]``).
7171
"""
7272
return None
7373

0 commit comments

Comments
 (0)