Skip to content
Closed
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
51 changes: 51 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This workflow will upload a Python Package to PyPi when a Release is created
name: Publish Python Package

on:
release:
types: [published]

permissions:
contents: read

env:
PYPI_USERNAME: __token__
PYPI_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

jobs:
publish:
name: Publish to PyPi
runs-on: ubuntu-latest

steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install Poetry
run: |
curl -sSL https://install.python-poetry.org | python - -y
echo "$HOME/.local/bin" >> $GITHUB_PATH

- name: Install Python package dependencies
run: |
poetry config virtualenvs.create false
poetry install --sync --no-interaction

- name: Inject the latest Code Analyzer JAR
run: |
CODE_ANALYZER_URL=$(curl -s https://api.github.com/repos/IBM/codenet-minerva-code-analyzer/releases/latest | jq -r '.assets[] | .browser_download_url')
echo "Downloading: " $CODE_ANALYZER_URL
wget -q $CODE_ANALYZER_URL
echo "Moving codeanalyzer jar to:" ${{ github.workspace }}/cldk/analysis/java/codeanalyzer/jar
mv codeanalyzer-*.jar ${{ github.workspace }}/cldk/analysis/java/codeanalyzer/jar/

- name: Build package
run: poetry build

- name: Publish package distributions to PyPI
run: poetry publish --username $PYPI_USERNAME --password $PYPI_PASSWORD
65 changes: 5 additions & 60 deletions cldk/analysis/java/codeanalyzer/codeanalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,61 +100,6 @@ def __init__(
else:
self.call_graph: DiGraph | None = None

@staticmethod
def _download_or_update_code_analyzer(filepath: Path) -> str:
"""Downloads the codeanalyzer jar from the latest release on GitHub.

Args:
filepath (Path): The path to save the codeanalyzer jar.

Returns:
str: The path to the downloaded codeanalyzer jar file.
"""
url = "https://api.github.com/repos/IBM/codenet-minerva-code-analyzer/releases/latest"
response = requests.get(url)
date_format = "%Y%m%dT%H%M%S"
if response.status_code == 200:
for asset in response.json().get("assets", []):
if asset["name"] == "codeanalyzer.jar":
download_url = asset["browser_download_url"]
pattern = r"(\d{8}T\d{6})"
match = re.search(pattern, download_url)
if match:
datetime_str = match.group(0)
else:
raise Exception(f"Release URL {download_url} does not contain a datetime pattern.")

# Look for codeanalyzer.YYYYMMDDTHHMMSS.jar in the filepath
current_codeanalyzer_jars = [jarfile for jarfile in filepath.glob("*.jar")]
if not any(current_codeanalyzer_jars):
logger.info(f"Codeanalzyer jar is not found. Downloading the latest version.")
filename = filepath / f"codeanalyzer.{datetime_str}.jar"
urlretrieve(download_url, filename)
return filename.__str__()

current_codeanalyzer_jar_name = current_codeanalyzer_jars[0]
match = re.search(pattern, current_codeanalyzer_jar_name.__str__())
if match:
current_datetime_str = match.group(0)

if datetime.strptime(datetime_str, date_format) > datetime.strptime(current_datetime_str, date_format):
logger.info(f"Codeanalzyer jar is outdated. Downloading the latest version.")
# Remove the older codeanalyzer jar
for jarfile in current_codeanalyzer_jars:
jarfile.unlink()
# Download the newer codeanalyzer jar
filename = filepath / f"codeanalyzer.{datetime_str}.jar"
urlretrieve(download_url, filename)
else:
filename = current_codeanalyzer_jar_name
logger.info(f"Codeanalzyer jar is already at the latest version.")
else:
filename = current_codeanalyzer_jar_name

return filename.__str__()
else:
raise Exception(f"Failed to fetch release warn: {response.status_code} {response.text}")

def _get_application(self) -> JApplication:
"""Returns the application view of the Java code.

Expand Down Expand Up @@ -185,13 +130,13 @@ def _get_codeanalyzer_exec(self) -> List[str]:
if self.analysis_backend_path:
analysis_backend_path = Path(self.analysis_backend_path)
logger.info(f"Using codeanalyzer.jar from {analysis_backend_path}")
codeanalyzer_exec = shlex.split(f"java -jar {analysis_backend_path / 'codeanalyzer.jar'}")
codeanalyzer_jar_file = next(analysis_backend_path.glob("*.jar"), None)
if codeanalyzer_jar_file is None:
raise CodeanalyzerExecutionException(f"No codeanalyzer jar found in {analysis_backend_path}")
codeanalyzer_exec = shlex.split(f"java -jar {analysis_backend_path / codeanalyzer_jar_file}")
else:
# Since the path to codeanalyzer.jar was not provided, we'll download the latest version from GitHub.
with resources.as_file(resources.files("cldk.analysis.java.codeanalyzer.jar")) as codeanalyzer_jar_path:
# Download the codeanalyzer jar if it doesn't exist, update if it's outdated,
# do nothing if it's up-to-date.
codeanalyzer_jar_file = self._download_or_update_code_analyzer(codeanalyzer_jar_path)
codeanalyzer_jar_file = next(codeanalyzer_jar_path / "*.jar", None)
codeanalyzer_exec = shlex.split(f"java -jar {codeanalyzer_jar_file}")
return codeanalyzer_exec

Expand Down
7 changes: 4 additions & 3 deletions cldk/models/java/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import re
import json
from ipdb import set_trace
import re
from contextvars import ContextVar
from typing import Dict, List, Optional
from .constants_namespace import ConstantsNamespace

from pydantic import BaseModel, field_validator, model_validator

from .constants_namespace import ConstantsNamespace

constants = ConstantsNamespace()
context_concrete_class = ContextVar("context_concrete_class") # context var to store class concreteness

Expand Down
4 changes: 3 additions & 1 deletion cldk/models/treesitter/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ class Capture:
name: str

def __init__(self, captures: List[Tuple[Node, str]]):
self.captures = [self.Capture(node=node, name=text) for node, text in captures]
self.captures = []
for capture_name, captures in captures.items():
self.captures = [self.Capture(node=node, name=capture_name) for node in captures]

def __getitem__(self, index: int) -> Capture:
"""Get the capture at the specified index.
Expand Down
Loading