8000 feat: add codebase init command by yeisonvargasf · Pull Request #756 · pyupio/safety · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: add codebase init command #756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025
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
10000
46 changes: 43 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
"request": "launch",
"module": "safety",
"args": "${input:mainCommand}",
// This uses the default environment which is a virtual environment
// created by Hatch
"python": "${workspaceFolder}/.hatch/bin/python",
"python": "${workspaceFolder}/.venv/bin/python",
"console": "integratedTerminal",
"justMyCode": false,
// "cwd": "${workspaceFolder}/poetry-test-1/my-project-v4"
"env": {
"SAFETY_REQUEST_TIMEOUT": "5"
}
}
],
"inputs": [
Expand Down Expand Up @@ -84,7 +86,45 @@
"init",
"pip list",
"pip install insecure-package",
"pip install --index-url https://pkgs.safetycli.com insecure-package",
"pip install --index-url https://pkgs.safetycli.com --requirement requirements.txt",
"pip install 'pyyaml>=5.0.0'",
"pip install -r requirements.txt",
"pip install djanga fest-api==2.2.2",
"pip install fastapi",
"pip install lodestone",
"pip3 install asdadsadsadasdasdasd",
"pip3.13 install asdadsadsadasdasdasd",
"pip list",
"uv sync",
"uv sync --frozen --no-install-project --no-group dev --no-group test",
"uv pip list",
"uv pip install asdadsadsadasdasdasd",
"uv pip install insecure-package",
"uv pip install --index-url https://pkgs.safetycli.com insecure-package",
"uv pip install --index-url https://pkgs.safetycli.com --requirement requirements.txt",
"uv pip install 'pyyaml>=5.0.0'",
"uv pip install -r requirements.txt",
"uv pip install djanga fest-api==0.115.12",
"uv pip install fastapi",
"uv pip install lodestone",
"uv pip uninstall fastapi",
"uv tool install ruff",
"uv tool list --show-paths --show-version-specifiers",
"poetry about",
"poetry init",
"poetry install",
"poetry add pytest requests --dev --optional --group test --extras 'security,http2' --python '>=3.8,<3.11' --dry-run --no-interaction",
"poetry add request@2.32.3",
"poetry add --source other request@2.32.3",
"poetry add 'request@^2.25.0'",
"poetry add 'request[extra]@^1.0.0'",
"poetry add 'request~1.0.0'",
"poetry add 'request^3.2.3'",
"poetry add 'request[security]>=2.25.0' numpi 'panda>=1.3.0,!=1.4.1'",
"poetry add requests",
"poetry add request==3.2.3",
"poetry add lodestone",

// Check commands
"check",
Expand Down
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"python.testing.unittestEnabled": false,
// This uses the default environment which is a virtual environment
// created by Hatch
"python.testing.pytestPath": "${workspaceFolder}/.hatch/bin/pytest",
"python.testing.pytestPath": "${workspaceFolder}/.venv/bin/pytest",
"python.testing.autoTestDiscoverOnSaveEnabled": true,
"python.testing.pytestArgs": [
"-v",
Expand All @@ -22,5 +22,6 @@
"ruamel",
"setuptools",
"typer"
]
],
"python.defaultInterpreterPath": ".venv/bin/python"
}
21 changes: 21 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Setup Safety Development Environment",
"type": "shell",
"command": "bash",
"args": [
"-c",
"mkdir -p ~/.safety && [ -f ~/.safety/config.ini ] && cp ~/.safety/config.ini ~/.safety/config.ini.backup 2>/dev/null || true; cp ${workspaceFolder}/local_config.ini ~/.safety/config.ini 2>/dev/null && echo 'Development config installed successfully!' || echo 'Failed to install development config'"
],
"presentation": {
"reveal": "silent",
"close": true,
"revealProblems": "onProblem",
"panel": "shared"
},
"problemMatcher": []
}
]
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ type = "virtual"
# hatch env remove
# hatch env create
python = "3.8"
path = ".hatch"
path = ".venv"

dependencies = [
"coverage[toml]>=6.0",
Expand Down
7 changes: 5 additions & 2 deletions safety/auth/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
from safety.auth.models import Auth
from safety.auth.utils import initialize, is_email_verified
from safety.console import main_console as console
from safety.constants import MSG_FINISH_REGISTRATION_TPL, MSG_VERIFICATION_HINT
from safety.constants import (
MSG_FINISH_REGISTRATION_TPL,
MSG_VERIFICATION_HINT,
DEFAULT_EPILOG,
)
from safety.meta import get_version
from safety.decorators import notify

Expand Down Expand Up @@ -37,7 +41,6 @@
CLI_AUTH_LOGIN_HELP,
CLI_AUTH_LOGOUT_H 9E81 ELP,
CLI_AUTH_STATUS_HELP,
DEFAULT_EPILOG,
)

from ..cli_util import SafetyCLISubGroup, get_command_for, pass_safety_cli_obj
Expand Down
6 changes: 4 additions & 2 deletions safety/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from safety.auth.cli import auth_app
from safety.auth.models import Organization
from safety.decorators import notify
from safety.codebase.command import codebase_app
from safety.console import main_console as console
from safety.constants import (
BAR_LINE,
Expand All @@ -42,6 +43,8 @@
EXIT_CODE_FAILURE,
EXIT_CODE_OK,
EXIT_CODE_VULNERABILITIES_FOUND,
CLI_MAIN_INTRODUCTION,
DEFAULT_EPILOG,
)
from safety.error_handlers import handle_cmd_exception, output_exception
from safety.errors import InvalidCredentialError, SafetyError, SafetyException
Expand Down Expand Up @@ -70,8 +73,6 @@
CLI_GENERATE_MINIMUM_CVSS_SEVERITY,
CLI_GENERATE_PATH,
CLI_LICENSES_COMMAND_HELP,
CLI_MAIN_INTRODUCTION,
DEFAULT_EPILOG,
DEFAULT_SPINNER,
)
from safety.scan.finder import FileFinder
Expand Down Expand Up @@ -1410,6 +1411,7 @@ def check_updates(
cli.add_command(typer.main.get_command(init_app), name="init")
cli.add_command(typer.main.get_command(scan_project_app), name="scan")
cli.add_command(typer.main.get_command(scan_system_app), name="system-scan")
cli.add_command(typer.main.get_command(codebase_app), name="codebase")

tool_commands.auto_register_tools(group=cli)

Expand Down
175 changes: 175 additions & 0 deletions safety/codebase/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import logging
from pathlib import Path
from typing import Optional
from safety.codebase.render import render_initialization_result
from safety.errors import SafetyError
from safety.events.utils.emission import (
emit_codebase_detection_status,
emit_codebase_setup_completed,
)
from safety.init.main import launch_auth_if_needed
from safety.tool.main import find_local_tool_files
from safety.util import clean_project_id
from typing_extensions import Annotated

import typer
from safety.cli_util import SafetyCLISubGroup, SafetyCLICommand
from .constants import (
CMD_CODEBASE_INIT_NAME,
CMD_HELP_CODEBASE_INIT,
CMD_HELP_CODEBASE,
CMD_CODEBASE_GROUP_NAME,
CMD_HELP_CODEBASE_INIT_DISABLE_FIREWALL,
CMD_HELP_CODEBASE_INIT_LINK_TO,
CMD_HELP_CODEBASE_INIT_NAME,
CMD_HELP_CODEBASE_INIT_PATH,
)

from ..cli_util import CommandType, get_command_for
from ..error_handlers import handle_cmd_exception
from ..decorators import notify
from ..constants import CONTEXT_COMMAND_TYPE, DEFAULT_EPILOG
from safety.console import main_console as console
from .main import initialize_codebase, prepare_unverified_codebase


logger = logging.getLogger(__name__)

cli_apps_opts = {
"rich_markup_mode": "rich",
"cls": SafetyCLISubGroup,
"name": CMD_CODEBASE_GROUP_NAME,
}
codebase_app = typer.Typer(**cli_apps_opts)

DEFAULT_CMD = CMD_CODEBASE_INIT_NAME


@codebase_app.callback(
invoke_without_command=True,
cls=SafetyCLISubGroup,
help=CMD_HELP_CODEBASE,
epilog=DEFAULT_EPILOG,
context_settings={
"allow_extra_args": True,
"ignore_unknown_options": True,
CONTEXT_COMMAND_TYPE: CommandType.BETA,
},
)
def codebase(
ctx: typer.Context,
):
"""
Group command for Safety Codebase (project) operations. Running this command will forward to the default command.
"""
logger.info("codebase started")

# If no subcommand is invoked, forward to the default command
if not ctx.invoked_subcommand:
default_command = get_command_for(name=DEFAULT_CMD, typer_instance=codebase_app)
return ctx.forward(default_command)


@codebase_app.command(
cls=SafetyCLICommand,
help=CMD_HELP_CODEBASE_INIT,
name=CMD_CODEBASE_INIT_NAME,
epilog=DEFAULT_EPILOG,
options_metavar="[OPTIONS]",
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
)
@handle_cmd_exception
@notify
def init(
ctx: typer.Context,
name: Annotated[
Optional[str],
typer.Option(
help=CMD_HELP_CODEBASE_INIT_NAME,
callback=lambda name: clean_project_id(name) if name else None,
),
] = None,
link_to: Annotated[
Optional[str],
typer.Option(
"--link-to",
help=CMD_HELP_CODEBASE_INIT_LINK_TO,
callback=lambda name: clean_project_id(name) if name else None,
),
] = None,
skip_firewall_setup: Annotated[
bool, typer.Option(help=CMD_HELP_CODEBASE_INIT_DISABLE_FIREWALL)
] = False,
codebase_path: Annotated[
Path,
typer.Option(
"--path",
exists=True,
file_okay=False,
dir_okay=True,
writable=True,
readable=True,
resolve_path=True,
show_default=False,
help=CMD_HELP_CODEBASE_INIT_PATH,
),
] = Path("."),
):
"""
Initialize a Safety Codebase. The codebase may be entirely new to Safety Platform,
or may already exist in Safety Platform and the user is wanting to initialize it locally.
"""
logger.info("codebase init started")

if link_to and name:
raise typer.BadParameter("--link-to and --name cannot be used together")

org_slug = launch_auth_if_needed(ctx, console)

if not org_slug:
raise SafetyError("Organization not found")

should_enable_firewall = not skip_firewall_setup and ctx.obj.firewall_enabled

unverified_codebase = prepare_unverified_codebase(
codebase_path=codebase_path,
user_provided_name=name,
user_provided_link_to=link_to,
)

local_files = find_local_tool_files(codebase_path)

emit_codebase_detection_status(
event_bus=ctx.obj.event_bus,
ctx=ctx,
detected=any(local_files),
detected_files=local_files if local_files else None,
)

project_file_created, project_status = initialize_codebase(
ctx=ctx,
console=console,
codebase_path=codebase_path,
unverified_codebase=unverified_codebase,
org_slug=org_slug,
link_to=link_to,
should_enable_firewall=should_enable_firewall,
)

codebase_init_status = (
"reinitialized" if unverified_codebase.created else project_status
)
codebase_id = ctx.obj.project.id if ctx.obj.project and ctx.obj.project.id else None

render_initialization_result(
console=console,
codebase_init_status=codebase_init_status,
codebase_id=codebase_id,
)

emit_codebase_setup_completed(
event_bus=ctx.obj.event_bus,
ctx=ctx,
is_created=project_file_created,
codebase_id=codebase_id,
)
27 changes: 27 additions & 0 deletions safety/codebase/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
CMD_HELP_CODEBASE_INIT = "Initialize a Safety Codebase (like git init for security). Sets up a new codebase or connects your local project to an existing one on Safety Platform."
CMD_HELP_CODEBASE = (
"[BETA] Manage your Safety Codebase integration.\nExample: safety codebase init"
)

CMD_CODEBASE_GROUP_NAME = "codebase"
CMD_CODEBASE_INIT_NAME = "init"


# init options help
CMD_HELP_CODEBASE_INIT_NAME = "Name of the codebase. Defaults to GIT origin name, parent directory name, or random string if parent directory is unnamed. The value will be normalized for use as an identifier."
CMD_HELP_CODEBASE_INIT_LINK_TO = (
"Link to an existing codebase (project) in Safety Platform."
)
CMD_HELP_CODEBASE_INIT_DISABLE_FIREWALL = "Don't enable Firewall protection for this codebase (enabled by default when available in your organization)"
CMD_HELP_CODEBASE_INIT_PATH = (
"Path to the codebase directory. Defaults to current directory."
)


CODEBASE_INIT_REINITIALIZED = "Reinitialized existing codebase {codebase_name}"
CODEBASE_INIT_ALREADY_EXISTS = "A codebase already exists in this directory. Please delete .safety-project.ini and run `safety codebase init` again to initialize a new codebase."
CODEBASE_INIT_NOT_FOUND_LINK_TO = "\nError: codebase '{codebase_name}' specified with --link-to does not exist.\n\nTo create a new codebase instead, use one of:\n safety codebase init\n safety codebase init --name \"custom name\"\n\nTo link to an existing codebase, verify the codebase id and try again."
CODEBASE_INIT_NOT_FOUND_PROJECT_FILE = "\nError: codebase '{codebase_name}' specified with the current .safety-project.ini file does not exist.\n\nTo create a new codebase instead, delete the corrupted .safety-project.ini file and then use one of:\n safety codebase init\n safety codebase init --name \"custom name\"\n\nTo link to an existing codebase, verify the codebase id and try again."
CODEBASE_INIT_LINKED = "Linked to codebase {codebase_name}."
CODEBASE_INIT_CREATED = "Created new codebase {codebase_name}."
CODEBASE_INIT_ERROR = "Error: unable to initialize the codebase. Please try again."
Loading
Loading
0