Skip to content
Merged
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
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 My Personal GHAS Playground
Copyright Modus Create, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
18 changes: 18 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## Reporting Bugs/Feature Requests
We welcome you to use the GitHub issue tracker to report bugs or suggest features.

When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:

* A reproducible test case or series of steps
* The version of our code being used
* Any modifications you've made relevant to the bug
* Anything unusual about your environment or deployment


## Security issue notifications
If you discover a potential security issue in this project we ask that you notify Modus Create via our [contact page](https://moduscreate.com/contact/). Please do **not** create a public github issue.


## Licensing
See the [LICENSE](LICENSE) file for our project's licensing.
17 changes: 14 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "codeql-wrapper"
version = "0.1.2"
version = "0.1.4"
description = "A universal Python CLI wrapper for running CodeQL analysis on any type of project (monorepo or single repository) across different CI/CD platforms including Jenkins, GitHub Actions, Harness, and any environment where Python scripts can be executed."
authors = ["Mateus Perdigão Domiciano <mateus.domiciano@moduscreate.com>", "Fernando Matsuo Santos <fernando.matsuo@moduscreate.com>"]
license = "MIT"
Expand All @@ -25,13 +25,15 @@ packages = [{include = "codeql_wrapper", from = "src"}]
[tool.poetry.dependencies]
python = "^3.8.1"
click = "^8.0.0"
colorama = "^0.4.6"

[tool.poetry.group.dev.dependencies]
pytest = "^7.0.0"
pytest-cov = "^4.0.0"
black = ">=23,<25"
flake8 = "^6.0.0"
mypy = "^1.0.0"
types-colorama = "^0.4.15"

[tool.poetry.scripts]
codeql-wrapper = "codeql_wrapper.cli:cli"
Expand Down
102 changes: 78 additions & 24 deletions src/codeql_wrapper/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Optional

import click
import colorama

from .domain.use_cases.codeql_analysis_use_case import CodeQLAnalysisUseCase
from .domain.use_cases.sarif_upload_use_case import SarifUploadUseCase
Expand All @@ -17,6 +18,9 @@
from .infrastructure.git_utils import GitUtils
from . import __version__

# Initialize colorama for cross-platform color support
colorama.init()


def version_callback(ctx: click.Context, param: click.Parameter, value: bool) -> None:
"""Callback for version option."""
Expand Down Expand Up @@ -139,23 +143,26 @@ def analyze(

if not final_repository:
click.echo(
"❌ --repository is required when using --upload-sarif. "
click.style("ERROR:", fg="red", bold=True)
+ " --repository is required when using --upload-sarif. "
"Could not auto-detect from Git remote.",
err=True,
)
sys.exit(1)

if not final_commit_sha:
click.echo(
"❌ --commit-sha is required when using --upload-sarif. "
click.style("ERROR:", fg="red", bold=True)
+ " --commit-sha is required when using --upload-sarif. "
"Could not auto-detect from Git.",
err=True,
)
sys.exit(1)

if not github_token:
click.echo(
"❌ GitHub token is required when using --upload-sarif. "
click.style("ERROR:", fg="red", bold=True)
+ " GitHub token is required when using --upload-sarif. "
"Set GITHUB_TOKEN environment variable or use "
"--github-token option.",
err=True,
Expand Down Expand Up @@ -198,6 +205,7 @@ def analyze(
output_directory=Path(output_dir) if output_dir else None,
verbose=verbose,
force_install=force_install,
monorepo=monorepo,
Copy link

Copilot AI Jul 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The analyze command passes monorepo=monorepo, but no --monorepo option is defined in the Click decorator. This will cause a NameError. Add a @click.option('--monorepo', is_flag=True, help='Enable monorepo analysis') above the analyze definition.

Copilot uses AI. Check for mistakes.
)

# Execute analysis
Expand All @@ -216,7 +224,11 @@ def analyze(
click.echo(f"Total findings: {summary.total_findings}")

if summary.failed_analyses > 0:
click.echo(f"\n⚠️ {summary.failed_analyses} analysis(es) failed")
click.echo(
"\n"
+ click.style("WARNING:", fg="yellow", bold=True)
+ f" {summary.failed_analyses} analysis(es) failed"
)
for result in summary.analysis_results:
if not result.is_successful:
click.echo(
Expand All @@ -230,7 +242,7 @@ def analyze(
for result in summary.analysis_results
if result.output_files
):
click.echo("\n📄 Output files:")
click.echo("\n" + click.style("OUTPUT FILES:", fg="blue", bold=True))
for result in summary.analysis_results:
if result.output_files:
for output_file in result.output_files:
Expand All @@ -241,7 +253,11 @@ def analyze(
# Upload SARIF files if requested
if upload_sarif:
if not sarif_files:
click.echo("\n⚠️ No SARIF files found for upload")
click.echo(
"\n"
+ click.style("WARNING:", fg="yellow", bold=True)
+ " No SARIF files found for upload"
)
else:
# These are guaranteed to be non-None due to validation above
assert repository is not None
Expand All @@ -251,7 +267,9 @@ def analyze(
# Show upload info
used_ref = ref or "refs/heads/main"
click.echo(
f"\n📤 Uploading {len(sarif_files)} SARIF file(s) to {repository}"
"\n"
+ click.style("UPLOADING:", fg="blue", bold=True)
+ f" {len(sarif_files)} SARIF file(s) to {repository}"
)
click.echo(f" Commit: {commit_sha}")
click.echo(f" Reference: {used_ref}")
Expand All @@ -272,12 +290,16 @@ def analyze(
# Display results
if upload_result.success:
click.echo(
f"\n✅ Successfully uploaded "
"\n"
+ click.style("SUCCESS:", fg="green", bold=True)
+ f" Successfully uploaded "
f"{upload_result.successful_uploads} SARIF file(s)"
)
else:
click.echo(
f"\n❌ Upload failed: "
"\n"
+ click.style("ERROR:", fg="red", bold=True)
+ f" Upload failed: "
f"{upload_result.failed_uploads}/"
f"{upload_result.total_files} files failed"
)
Expand All @@ -291,7 +313,7 @@ def analyze(
except Exception as e:
logger = get_logger(__name__)
logger.error(f"CodeQL analysis failed: {e}")
click.echo(f"Error: {e}", err=True)
click.echo(click.style("ERROR:", fg="red", bold=True) + f" {e}", err=True)
sys.exit(1)


Expand Down Expand Up @@ -320,23 +342,35 @@ def install(ctx: click.Context, version: str, force: bool) -> None:
# Check if already installed
if installer.is_installed() and not force:
current_version = installer.get_version()
click.echo(f"✅ CodeQL is already installed (version: {current_version})")
click.echo(
click.style("SUCCESS:", fg="green", bold=True)
+ f" CodeQL is already installed (version: {current_version})"
)
click.echo(f" Location: {installer.get_binary_path()}")
click.echo(" Use --force to reinstall")
return

# Show installation progress
if force:
click.echo("🔄 Force reinstalling CodeQL...")
click.echo(
click.style("REINSTALLING:", fg="yellow", bold=True)
+ " Force reinstalling CodeQL..."
)
else:
click.echo("📥 Installing CodeQL...")
click.echo(
click.style("INSTALLING:", fg="blue", bold=True)
+ " Installing CodeQL..."
)

# Install CodeQL
binary_path = installer.install(version=version, force=force)

# Verify installation
installed_version = installer.get_version()
click.echo(f"✅ CodeQL {installed_version} installed successfully!")
click.echo(
click.style("SUCCESS:", fg="green", bold=True)
+ f" CodeQL {installed_version} installed successfully!"
)
click.echo(f" Location: {binary_path}")
click.echo(" You can now run: codeql-wrapper analyze /path/to/repo")

Expand All @@ -345,7 +379,10 @@ def install(ctx: click.Context, version: str, force: bool) -> None:
except Exception as e:
logger = get_logger(__name__)
logger.error(f"CodeQL installation failed: {e}")
click.echo(f"❌ Installation failed: {e}", err=True)
click.echo(
click.style("ERROR:", fg="red", bold=True) + f" Installation failed: {e}",
err=True,
)
sys.exit(1)


Expand Down Expand Up @@ -423,15 +460,17 @@ def upload_sarif(
# Parse repository owner/name
if not final_repository:
click.echo(
"❌ Repository is required. Provide --repository or ensure you're in a Git repository "
click.style("ERROR:", fg="red", bold=True)
+ " Repository is required. Provide --repository or ensure you're in a Git repository "
"with a GitHub remote configured.",
err=True,
)
sys.exit(1)

if not final_commit_sha:
click.echo(
"❌ Commit SHA is required. Provide --commit-sha or ensure you're in a Git repository.",
click.style("ERROR:", fg="red", bold=True)
+ " Commit SHA is required. Provide --commit-sha or ensure you're in a Git repository.",
err=True,
)
sys.exit(1)
Expand All @@ -440,21 +479,27 @@ def upload_sarif(
repository_owner, repository_name = final_repository.split("/", 1)
except ValueError:
click.echo(
"❌ Invalid repository format. Use 'owner/name' format.", err=True
click.style("ERROR:", fg="red", bold=True)
+ " Invalid repository format. Use 'owner/name' format.",
err=True,
)
sys.exit(1)

# Validate GitHub token
if not github_token:
click.echo(
"❌ GitHub token is required. Set GITHUB_TOKEN environment variable "
click.style("ERROR:", fg="red", bold=True)
+ " GitHub token is required. Set GITHUB_TOKEN environment variable "
"or use --github-token option.",
err=True,
)
sys.exit(1)

used_ref = final_ref or "refs/heads/main"
click.echo(f"📤 Uploading SARIF file: {sarif_file}")
click.echo(
click.style("UPLOADING:", fg="blue", bold=True)
+ f" SARIF file: {sarif_file}"
)
click.echo(f" Repository: {final_repository}")
click.echo(f" Commit: {final_commit_sha}")
click.echo(f" Reference: {used_ref}")
Expand All @@ -469,7 +514,10 @@ def upload_sarif(
if not ref and git_info.ref:
auto_detected.append("ref")
if auto_detected:
click.echo(f" Auto-detected: {', '.join(auto_detected)}")
click.echo(
click.style("INFO:", fg="cyan", bold=True)
+ f" Auto-detected: {', '.join(auto_detected)}"
)

# Create upload request
upload_request = SarifUploadRequest(
Expand All @@ -486,17 +534,23 @@ def upload_sarif(

# Display results
if upload_result.success:
click.echo("✅ Successfully uploaded SARIF file")
click.echo(
click.style("SUCCESS:", fg="green", bold=True)
+ " Successfully uploaded SARIF file"
)
else:
if upload_result.errors:
for error in upload_result.errors:
click.echo(f"❌ {error}")
click.echo(click.style("ERROR:", fg="red", bold=True) + f" {error}")
raise Exception("SARIF upload failed")

logger.info(f"SARIF upload completed for {final_repository}")

except Exception as e:
logger = get_logger(__name__)
logger.error(f"SARIF upload failed: {e}")
click.echo(f"❌ Upload failed: {e}", err=True)
click.echo(
click.style("ERROR:", fg="red", bold=True) + f" Upload failed: {e}",
err=True,
)
sys.exit(1)
1 change: 1 addition & 0 deletions src/codeql_wrapper/domain/entities/codeql_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class CodeQLAnalysisRequest:
output_directory: Optional[Path] = None
verbose: bool = False
force_install: bool = False
monorepo: bool = False

def __post_init__(self) -> None:
"""Validate analysis request."""
Expand Down
Loading