Python CLI for scanning a Git provider workspace (GitHub, Bitbucket, GitLab) and running pluggable per-repository actions across all repositories in that workspace.
Use cases:
- Apply code quality rules (SonarQube scans) to all repositories
- Detect programming languages across your organization's codebase
- Generate reports or perform batch operations on multiple repositories
- Migrate repositories from one provider to another
Rules are implemented with a hexagonal architecture boundary and a strategy pattern:
- Hexagonal boundary: core scanner orchestration is provider/rule agnostic.
- Strategy pattern: each rule is an
Actionstrategy injected intoActionPipeline.
- Python 3.11 or higher
- Git CLI installed and available in PATH
- SSH access configured for the target provider (GitHub, Bitbucket, GitLab)
- SSH keys already set up for repository cloning
- No external Python dependencies (uses standard library only)
The scanner:
- Lists repositories from a workspace
- Clones missing repositories into
BASE_DIR - Pulls existing repositories from
BASE_DIR - Executes configured actions in order (
GIT_ACTIONS)
Repositories are cached in BASE_DIR and are not auto-deleted.
- Clone this repository:
git clone https://github.com/AlasAltum/gitoteko.git
cd gitoteko- Create Python virtual environment:
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate- Install the package in editable mode:
pip install -e .- Copy and configure environment file:
cp .env.example .env
# Edit .env with your credentials:
# - BITBUCKET_TOKEN or BITBUCKET_USERNAME/BITBUCKET_APP_PASSWORD
# - GIT_WORKSPACE (your organization/workspace name)
# - BASE_DIR (where repositories will be cloned)
# - SONARQUBE_URL and SONARQUBE_TOKEN (if using Sonar scans)Dry-run (lists repositories without cloning):
set -a && source .env && set +a
PYTHONPATH=src .venv/bin/python -m git_workspace_tool --dry-runTest run with first 3 repositories (useful when analyzing a whole workspace with many repos):
set -a && source .env && set +a
PYTHONPATH=src .venv/bin/python -m git_workspace_tool \
--max-repos 3 \
--repo-selection firstFull workspace scan (all repositories):
set -a && source .env && set +a
PYTHONPATH=src .venv/bin/python -m git_workspace_toolActions are configured through GIT_ACTIONS (comma-separated):
export GIT_ACTIONS="detect-languages,generate-sonar-properties,run-sonar-scan"Optional CSV reporting:
export GIT_ACTIONS="detect-languages,write-language-csv,generate-sonar-properties,run-sonar-scan"Supported action names:
detect-languageswrite-language-csvgenerate-sonar-propertiesrun-sonar-scan
Use remote host URL only (no endpoint suffix):
export SONARQUBE_URL="https://<your-sonarqube-host>"
export SONARQUBE_TOKEN="<token>"
export SONAR_EXECUTION_MODE=localSONAR_EXECUTION_MODE=local(default): runsonar-scannerfrom the machine running this CLI, and submit analysis to your remote SonarQube server (SONARQUBE_URL).SONAR_EXECUTION_MODE=cloud: query Sonar server APIs only (status/quality-gate checks); does not submit new analysis.SONAR_EXECUTION_MODE=ci: trigger Bitbucket Pipelines sosonar-scannerruns on CI/runner.
Important: if you use SONAR_EXECUTION_MODE=local, you must have a sonar-scanner binary available on the CLI machine (or set SONAR_SCANNER_EXECUTABLE to its absolute path).
For CI/runner execution (recommended when local execution is not allowed):
export SONAR_EXECUTION_MODE=ci
export SONAR_CI_PROVIDER=bitbucket
# Optional: custom pipeline selector pattern
export SONAR_CI_PIPELINE_SELECTOR=sonar-scan
# Optional: override branch; otherwise repository main branch is used
export SONAR_CI_REF_NAME=main
# Optional: forward SONAR_HOST_URL and SONAR_TOKEN as pipeline variables
export SONAR_CI_FORWARD_SONAR_ENV=falseNote: repositories must already have a Bitbucket Pipeline configured to run sonar-scanner.
If you need local scanner mode:
export SONAR_EXECUTION_MODE=local
export SONAR_SCANNER_EXECUTABLE=sonar-scanner
export SONAR_SCANNER_TIMEOUT_SECONDS=1800For a complete explanation of Sonar modes, prerequisites, and Java/non-Java behavior, see docs/sonar_execution_guide.md.
Backpressure controls to avoid overloading SonarQube server:
export SONAR_WAIT_MODE=sync
export SONAR_SUBMISSION_DELAY_SECONDS=10
export SONAR_SYNC_POLL_INTERVAL_SECONDS=5
export SONAR_SYNC_TIMEOUT_SECONDS=1800SONAR_WAIT_MODE=sync: waits for CE task completion per repo.SONAR_WAIT_MODE=async: submits scan and continues.
De-dup controls (avoid scanning unchanged repositories twice):
export SONAR_SKIP_UNCHANGED=true
export SONAR_FORCE_SCAN=false
export SONAR_STATE_FILE=.git/gitoteko_sonar_state.jsonSONAR_SKIP_UNCHANGED=true: skip scan if the same repo revision was already scanned successfully.SONAR_FORCE_SCAN=true: force scan even when revision did not change.SONAR_STATE_FILE: per-repo state file path (relative to repo root).
By default, repository failures do not stop the batch.
To stop on first failure:
export GIT_STOP_ON_ERROR=trueWith GIT_STOP_ON_ERROR=true, execution stops immediately on the first failed action or repository sync error.