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
84 changes: 77 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,30 @@
<img src="https://res.cloudinary.com/dhyii4oiw/image/upload/v1767324445/Screenshot_2026-01-02_085503_ros5gz.png" alt="Repo-lyzer Logo" width="300">
</p>

<p align="center">
<a href="https://github.com/agnivo988/Repo-lyzer/releases"><img src="https://img.shields.io/github/v/release/agnivo988/Repo-lyzer?style=flat-square" alt="Release"></a>
<a href="https://github.com/agnivo988/Repo-lyzer/blob/main/LICENSE"><img src="https://img.shields.io/github/license/agnivo988/Repo-lyzer?style=flat-square" alt="License"></a>
<a href="https://github.com/agnivo988/Repo-lyzer/issues"><img src="https://img.shields.io/github/issues/agnivo988/Repo-lyzer?style=flat-square" alt="Issues"></a>
<a href="https://github.com/agnivo988/Repo-lyzer/actions"><img src="https://img.shields.io/github/actions/workflow/status/agnivo988/Repo-lyzer/ci.yml?style=flat-square" alt="CI"></a>
</p>
Comment on lines +6 to +11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify that README badge URLs point to valid targets.

# Check 1: Verify LICENSE file exists in repository root
echo "=== Checking LICENSE file ==="
if [ -f "LICENSE" ]; then
  echo "✓ LICENSE file exists"
else
  echo "✗ LICENSE file not found"
fi

# Check 2: Verify CI workflow file matches badge reference
echo ""
echo "=== Checking CI workflow ==="
if [ -f ".github/workflows/ci.yml" ]; then
  echo "✓ CI workflow (.github/workflows/ci.yml) exists"
else
  echo "✗ CI workflow not found at .github/workflows/ci.yml"
fi

# Check 3: List GitHub Actions workflows to confirm the correct workflow name
echo ""
echo "=== Available GitHub Actions workflows ==="
if [ -d ".github/workflows" ]; then
  ls -la .github/workflows/ | grep -E "\.yml|\.yaml"
else
  echo "No .github/workflows directory found"
fi

Repository: agnivo988/Repo-lyzer

Length of output: 490


Add a LICENSE file to the repository root.

The badge on line 8 (https://github.com/agnivo988/Repo-lyzer/blob/main/LICENSE) references a LICENSE file that does not exist in the repository. This breaks the license badge. Create a LICENSE file in the repository root and choose an appropriate open-source license for the project.

The CI workflow badge on line 11 correctly references .github/workflows/ci.yml, which exists in the repository.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 6 - 11, The license badge in README.md references a
LICENSE file at the repository root that does not currently exist, causing the
badge to break. Create a LICENSE file in the repository root directory and
select an appropriate open-source license for the project. The CI workflow badge
already correctly references the existing .github/workflows/ci.yml file, so no
changes are needed for that badge.


**Repo-lyzer** is a modern, terminal-based CLI tool written in **Golang** that analyzes GitHub repositories and presents insights in a beautifully formatted, interactive dashboard.

---

## Table of Contents
- [Features](#features)
- [Quick Start](#quick-start)
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Docker Usage](#docker-usage)
- [Architecture Overview](#architecture-overview)
- [Documentation](#documentation)
- [Contributing](#contributing)
- [License](#license)

---

## Features
- **Deep Analytics** – Repository health, maturity scores, and bus factor.
- **Contribution Friendliness** – Assess how easy it is to contribute to a repository using weighted metrics (`--contribute` flag).
Expand All @@ -19,10 +39,18 @@

## Quick Start

### Prerequisites
- **Go 1.21+** (for `go install`) or **Docker** (for containerized usage)
- A [GitHub Personal Access Token](https://github.com/settings/tokens) (required for API calls)

### Installation
```bash
go install [github.com/agnivo988/Repo-lyzer@v1.0.6](https://github.com/agnivo988/Repo-lyzer@v1.0.6)
repo-lyzer
go install github.com/agnivo988/Repo-lyzer@latest
```

Verify the installation:
```bash
repo-lyzer version
```

### Basic Usage
Expand All @@ -33,7 +61,6 @@ repo-lyzer summary golang/go
# Run full interactive analysis
repo-lyzer analyze microsoft/vscode

# Run analysis with contribution scoring enabled
# Run analysis with contribution scoring enabled
repo-lyzer analyze microsoft/vscode --contribute
```
Expand Down Expand Up @@ -113,14 +140,57 @@ The `docker-compose.yml` mounts a local `./data` directory to persist settings a

---

## Maintainers & Contributors
### Maintainer: @agnivo988
## Contributing

We welcome contributions from the community! Here's how to get started:

1. **Fork** the repository
2. **Clone** your fork:
```bash
git clone https://github.com/your-username/Repo-lyzer.git
```
3. **Create a feature branch**:
```bash
git checkout -b feat/your-feature-name
```
4. **Make your changes** and ensure tests pass:
```bash
go test ./...
```
5. **Commit** with a clear message:
```bash
git commit -m "feat: add your feature"
```
6. **Push** and open a Pull Request

### Development Setup
```bash
# Install dependencies
go mod download

# Run tests
go test ./...

# Build from source
go build -o repo-lyzer .
```

### Code Style
- Run `go fmt ./...` before committing
- Follow standard Go conventions
- Add tests for new functionality

<a href="https://github.com/Aamod007"><img src="https://github.com/Aamod007.png" width="50" height="50" alt="Aamod007" title="Contributor"></a> <a href="https://github.com/Aditya8369"><img src="https://github.com/Aditya8369.png" width="50" height="50" alt="Aditya8369" title="Contributor"></a> <a href="https://github.com/agnivo988"><img src="https://github.com/agnivo988.png" width="50" height="50" alt="agnivo988" title="Project Maintainer"></a> <a href="https://github.com/Gupta-02"><img src="https://github.com/Gupta-02.png" width="50" height="50" alt="Gupta-02" title="Contributor"></a> <a href="https://github.com/GauravKarakoti"><img src="https://github.com/GauravKarakoti.png" width="50" height="50" alt="GauravKarakoti" title="Contributor"></a> <a href="https://github.com/Sappymukherjee214"><img src="https://github.com/Sappymukherjee214.png" width="50" height="50" alt="Sappymukherjee214" title="Contributor"></a> <a href="https://github.com/ItsMeArm00n"><img src="https://github.com/ItsMeArm00n.png" width="50" height="50" alt="ItsMeArm00n" title="Contributor"></a> <a href="https://github.com/MuktaRedij"><img src="https://github.com/MuktaRedij.png" width="50" height="50" alt="MuktaRedij" title="Contributor"></a> <a href="https://github.com/Kiran95021"><img src="https://github.com/Kiran95021.png" width="50" height="50" alt="Kiran95021" title="Contributor"></a> <a href="https://github.com/Shriii19"><img src="https://github.com/Shriii19.png" width="50" height="50" alt="Shriii19" title="Contributor"></a> <a href="https://github.com/KUMARI-SONALIUPADHYAY"><img src="https://github.com/KUMARI-SONALIUPADHYAY.png" width="50" height="50" alt="KUMARI-SONALIUPADHYAY" title="Contributor"></a> <a href="https://github.com/magic-peach"><img src="https://github.com/magic-peach.png" width="50" height="50" alt="magic-peach" title="Contributor"></a> <a href="https://github.com/coderabbitai"><img src="https://github.com/coderabbitai.png" width="50" height="50" alt="coderabbitai[bot]" title="Bot Contributor"></a> <a href="https://github.com/sahoo-tech"><img src="https://github.com/sahoo-tech.png" width="50" height="50" alt="sahoo-tech" title="Contributor"></a> <a href="https://github.com/Abhijeet-980"><img src="https://github.com/Abhijeet-980.png" width="50" height="50" alt="Abhijeet-980" title="Contributor"></a> <a href="https://github.com/Diksha78-bot"><img src="https://github.com/Diksha78-bot.png" width="50" height="50" alt="Diksha78-bot" title="Bot Contributor"></a> <a href="https://github.com/Shivani-Meena07"><img src="https://github.com/Shivani-Meena07.png" width="50" height="50" alt="Shivani-Meena07" title="Contributor"></a> <a href="https://github.com/ShashankSaga"><img src="https://github.com/ShashankSaga.png" width="50" height="50" alt="ShashankSaga" title="Contributor"></a>
---

## Maintainers & Contributors

### Project Maintainer
- [@agnivo988](https://github.com/agnivo988)

### Contributors
<a href="https://github.com/Aamod007"><img src="https://github.com/Aamod007.png" width="50" height="50" alt="Aamod007" title="Contributor"></a> <a href="https://github.com/Aditya8369"><img src="https://github.com/Aditya8369.png" width="50" height="50" alt="Aditya8369" title="Contributor"></a> <a href="https://github.com/agnivo988"><img src="https://github.com/agnivo988.png" width="50" height="50" alt="agnivo988" title="Project Maintainer"></a> <a href="https://github.com/Gupta-02"><img src="https://github.com/Gupta-02.png" width="50" height="50" alt="Gupta-02" title="Contributor"></a> <a href="https://github.com/GauravKarakoti"><img src="https://github.com/GauravKarakoti.png" width="50" height="50" alt="GauravKarakoti" title="Contributor"></a> <a href="https://github.com/Sappymukherjee214"><img src="https://github.com/Sappymukherjee214.png" width="50" height="50" alt="Sappymukherjee214" title="Contributor"></a> <a href="https://github.com/ItsMeArm00n"><img src="https://github.com/ItsMeArm00n.png" width="50" height="50" alt="ItsMeArm00n" title="Contributor"></a> <a href="https://github.com/MuktaRedij"><img src="https://github.com/MuktaRedij.png" width="50" height="50" alt="MuktaRedij" title="Contributor"></a> <a href="https://github.com/Kiran95021"><img src="https://github.com/Kiran95021.png" width="50" height="50" alt="Kiran95021" title="Contributor"></a> <a href="https://github.com/Shriii19"><img src="https://github.com/Shriii19.png" width="50" height="50" alt="Shriii19" title="Contributor"></a> <a href="https://github.com/KUMARI-SONALIUPADHYAY"><img src="https://github.com/KUMARI-SONALIUPADHYAY.png" width="50" height="50" alt="KUMARI-SONALIUPADHYAY" title="Contributor"></a> <a href="https://github.com/magic-peach"><img src="https://github.com/magic-peach.png" width="50" height="50" alt="magic-peach" title="Contributor"></a> <a href="https://github.com/coderabbitai"><img src="https://github.com/coderabbitai.png" width="50" height="50" alt="coderabbitai[bot]" title="coderabbitai[bot]"></a>

---

## License
**MIT License © 2026 Agniva Mukherjee**
**MIT License © 2026 Agniva Mukherjee**. See [LICENSE](LICENSE) for details.
5 changes: 4 additions & 1 deletion cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,14 +315,17 @@ var analyzeCmd = &cobra.Command{
busFactor, busRisk := analyzer.BusFactor(contributors)
overallProgress.CompleteStep("Bus factor analysis complete")

// Check releases
hasReleases, _ := client.HasReleases(owner, repo)

// Calculate repository maturity score and level
overallProgress.StartStep("📈 Calculating repository maturity")
maturityScore, maturityLevel :=
analyzer.RepoMaturityScore(
repoInfo,
len(commits),
len(contributors),
false, // Assuming no releases check for simplicity
hasReleases,
)
overallProgress.CompleteStep("Maturity score calculated")

Expand Down
6 changes: 6 additions & 0 deletions cmd/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,17 @@ func fetchCompareInput(client *github.Client, owner, repo string) (output.Compar
return output.CompareInput{}, fmt.Errorf("error fetching contributors for %s/%s: %w", owner, repo, err)
}

hasReleases, err := client.HasReleases(owner, repo)
if err != nil {
return output.CompareInput{}, fmt.Errorf("error checking releases for %s/%s: %w", owner, repo, err)
}

return output.CompareInput{
Repo: repoInfo,
Commits: commits,
Contributors: contributors,
Languages: languages,
HasReleases: hasReleases,
}, nil
}

Expand Down
5 changes: 4 additions & 1 deletion internal/analyzer/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,12 @@ func GenerateCertificate(owner, repo string, client *github.Client) (*Certificat
return nil, fmt.Errorf("failed to get contributors: %w", err)
}

// Check releases
hasReleases, _ := client.HasReleases(owner, repo)

// Calculate scores
healthScore := CalculateHealth(repoInfo, commits)
maturityScore, maturityLevel := RepoMaturityScore(repoInfo, len(commits), len(contributors), false)
maturityScore, maturityLevel := RepoMaturityScore(repoInfo, len(commits), len(contributors), hasReleases)
busFactor, busRisk := BusFactor(contributors)

// Determine primary language
Expand Down
38 changes: 38 additions & 0 deletions internal/github/releases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package github

import (
"fmt"

gocache "github.com/patrickmn/go-cache"
)

func (c *Client) HasReleases(owner, repo string) (bool, error) {
cacheKey := "releases:" + owner + "/" + repo
if cached, found := c.cache.Get(cacheKey); found {
return cached.(bool), nil
Comment on lines +11 to +12

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unsafe type assertion can panic on cache corruption.

The type assertion cached.(bool) will panic if the cached value is not a boolean (for example, due to cache corruption or a programming error elsewhere that stores a different type under this key).

🛡️ Proposed fix with safe type assertion
-	if cached, found := c.cache.Get(cacheKey); found {
-		return cached.(bool), nil
+	if cached, found := c.cache.Get(cacheKey); found {
+		if boolVal, ok := cached.(bool); ok {
+			return boolVal, nil
+		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if cached, found := c.cache.Get(cacheKey); found {
return cached.(bool), nil
if cached, found := c.cache.Get(cacheKey); found {
if boolVal, ok := cached.(bool); ok {
return boolVal, nil
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/github/releases.go` around lines 11 - 12, The type assertion
`cached.(bool)` in the cache retrieval code is unsafe and will cause a panic if
the cached value is not actually a boolean type. Replace the unsafe type
assertion with a safe type assertion using the comma-ok idiom (e.g., `ok :=
true; if v, ok := cached.(bool); ok { ... }`). This way, if the cache value is
corrupted or has an unexpected type, the code will handle it gracefully instead
of panicking, and you can either return an error or treat it as a cache miss.

}

v, err, _ := c.sf.Do(cacheKey, func() (interface{}, error) {
if cached, found := c.cache.Get(cacheKey); found {
return cached.(bool), nil
Comment on lines +16 to +17

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unsafe type assertion can panic on cache corruption.

The type assertion cached.(bool) will panic if the cached value is not a boolean. Apply the same safe type assertion pattern as the outer cache check.

🛡️ Proposed fix with safe type assertion
 		if cached, found := c.cache.Get(cacheKey); found {
-			return cached.(bool), nil
+			if boolVal, ok := cached.(bool); ok {
+				return boolVal, nil
+			}
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if cached, found := c.cache.Get(cacheKey); found {
return cached.(bool), nil
if cached, found := c.cache.Get(cacheKey); found {
if boolVal, ok := cached.(bool); ok {
return boolVal, nil
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/github/releases.go` around lines 16 - 17, The type assertion
`cached.(bool)` in the cache retrieval block will panic if the cached value is
not a boolean. Replace the unsafe type assertion with the safe type assertion
pattern using the comma-ok idiom (e.g., `value, ok := cached.(bool)`). Check the
ok flag and handle the case where the type assertion fails, either by returning
an error or logging a warning and falling through to re-fetch the value from the
source.

}

url := fmt.Sprintf(
"https://api.github.com/repos/%s/%s/releases?per_page=1&page=1",
owner, repo,
)

var releases []struct{}
if err := c.get(url, &releases); err != nil {
return false, err
}

hasReleases := len(releases) > 0
c.cache.Set(cacheKey, hasReleases, gocache.DefaultExpiration)
return hasReleases, nil
})
if err != nil {
return false, err
}
return v.(bool), nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unsafe type assertion can panic if the single-flight function returns unexpected type.

Although the single-flight function should always return bool, defensive programming suggests using a safe type assertion here as well.

🛡️ Proposed fix with safe type assertion
-	return v.(bool), nil
+	if boolVal, ok := v.(bool); ok {
+		return boolVal, nil
+	}
+	return false, fmt.Errorf("internal error: single-flight returned non-bool value")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return v.(bool), nil
if boolVal, ok := v.(bool); ok {
return boolVal, nil
}
return false, fmt.Errorf("internal error: single-flight returned non-bool value")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/github/releases.go` at line 37, The type assertion at line 37 in the
releases.go file is unsafe and can cause a panic if the single-flight function
returns an unexpected type. Replace the unsafe type assertion `v.(bool), nil`
with a safe type assertion using the two-return-value form to check whether the
type assertion succeeded. If the type assertion fails, return an appropriate
error to handle the unexpected type gracefully instead of allowing a panic.

}
3 changes: 2 additions & 1 deletion internal/output/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type CompareInput struct {
Commits []github.Commit
Contributors []github.Contributor
Languages map[string]int
HasReleases bool
}

// CompareRepository contains the computed metrics for one repository.
Expand Down Expand Up @@ -179,7 +180,7 @@ func buildCompareRepository(input CompareInput) CompareRepository {
}

busFactor, busRisk := analyzer.BusFactor(input.Contributors)
maturityScore, maturityLevel := analyzer.RepoMaturityScore(input.Repo, len(input.Commits), len(input.Contributors), false)
maturityScore, maturityLevel := analyzer.RepoMaturityScore(input.Repo, len(input.Commits), len(input.Contributors), input.HasReleases)
healthScore := analyzer.CalculateHealth(input.Repo, input.Commits)

primaryLanguage := input.Repo.Language
Expand Down
8 changes: 6 additions & 2 deletions internal/scheduler/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,13 @@ func (s *Scheduler) executeJob(job config.ScheduledJob) error {
return fmt.Errorf("failed to get contributors: %w", err)
}

// Check releases
hasReleases, _ := client.HasReleases(job.Owner, job.Repo)

// Calculate metrics
healthScore := analyzer.CalculateHealth(repoInfo, commits)
busFactor, busRisk := analyzer.BusFactor(contributors)
maturityScore, maturityLevel := analyzer.RepoMaturityScore(repoInfo, len(commits), len(contributors), false)
maturityScore, maturityLevel := analyzer.RepoMaturityScore(repoInfo, len(commits), len(contributors), hasReleases)

// Build compact config for export
compactCfg := output.CompactConfig{
Expand Down Expand Up @@ -285,7 +288,8 @@ func (s *Scheduler) sendToWebhook(webhookURL, filename string, data []byte) erro
return fmt.Errorf("failed to marshal payload: %w", err)
}

resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData))
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData))
Comment on lines +291 to +292

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Webhook timeout fix incomplete: missing context cancellation support.

The 30-second timeout addresses the core issue from #391 (preventing indefinite hangs), but the implementation using client.Post() doesn't respect context cancellation. If the scheduler needs to shut down gracefully or cancel a job, the webhook delivery will still block for up to 30 seconds.

🔧 Proposed fix using Do() with context
 	client := &http.Client{Timeout: 30 * time.Second}
-	resp, err := client.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData))
+	req, err := http.NewRequestWithContext(context.Background(), "POST", webhookURL, bytes.NewBuffer(jsonData))
+	if err != nil {
+		return fmt.Errorf("failed to create webhook request: %w", err)
+	}
+	req.Header.Set("Content-Type", "application/json")
+	resp, err := client.Do(req)

If executeJob should support context cancellation (which scheduled jobs typically should), pass that context instead of context.Background().

As per static analysis hints, the linter flagged this line with noctx - HTTP calls should use (*http.Client).Do(*http.Request) with context for proper cancellation support.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData))
client := &http.Client{Timeout: 30 * time.Second}
req, err := http.NewRequestWithContext(context.Background(), "POST", webhookURL, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("failed to create webhook request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 292-292: (*net/http.Client).Post must not be called. use (*net/http.Client).Do(*http.Request)

(noctx)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/scheduler/scheduler.go` around lines 291 - 292, The current
implementation using `client.Post()` doesn't support context cancellation, which
means webhook requests cannot be interrupted during graceful shutdown or job
cancellation. Replace the direct `client.Post()` call with
`http.NewRequestWithContext()` to create an `*http.Request` with the appropriate
context (likely from the `executeJob` function's parameters), then call
`client.Do()` with that request instead. This enables the HTTP call to respect
context cancellation while maintaining the 30-second timeout already configured
on the http.Client.

Source: Linters/SAST tools

if err != nil {
return fmt.Errorf("failed to send webhook: %w", err)
}
Expand Down
12 changes: 9 additions & 3 deletions internal/ui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,11 @@ func (m MainModel) analyzeRepo(ctx context.Context, repoName string) tea.Cmd {
// Stage 5: Compute metrics
score := analyzer.CalculateHealth(repo, commits)
busFactor, busRisk := analyzer.BusFactor(contributors)
maturityScore, maturityLevel := analyzer.RepoMaturityScore(repo, len(commits), len(contributors), false)
hasReleases, hasRelErr := client.HasReleases(parts[0], parts[1])
if hasRelErr != nil {
hasReleases = false
}
maturityScore, maturityLevel := analyzer.RepoMaturityScore(repo, len(commits), len(contributors), hasReleases)
contributorInsights := analyzer.AnalyzeContributors(contributors)

commitsLast90Days := 0
Expand Down Expand Up @@ -1426,7 +1430,8 @@ func (m MainModel) compareRepos(repo1Name, repo2Name string) tea.Cmd {
fileTree1, _ := client.GetFileTree(parts1[0], parts1[1], repo1.DefaultBranch)
score1 := analyzer.CalculateHealth(repo1, commits1)
busFactor1, busRisk1 := analyzer.BusFactor(contributors1)
maturityScore1, maturityLevel1 := analyzer.RepoMaturityScore(repo1, len(commits1), len(contributors1), false)
hasReleases1, _ := client.HasReleases(parts1[0], parts1[1])
maturityScore1, maturityLevel1 := analyzer.RepoMaturityScore(repo1, len(commits1), len(contributors1), hasReleases1)

result1 := AnalysisResult{
Repo: repo1,
Expand All @@ -1452,7 +1457,8 @@ func (m MainModel) compareRepos(repo1Name, repo2Name string) tea.Cmd {
fileTree2, _ := client.GetFileTree(parts2[0], parts2[1], repo2.DefaultBranch)
score2 := analyzer.CalculateHealth(repo2, commits2)
busFactor2, busRisk2 := analyzer.BusFactor(contributors2)
maturityScore2, maturityLevel2 := analyzer.RepoMaturityScore(repo2, len(commits2), len(contributors2), false)
hasReleases2, _ := client.HasReleases(parts2[0], parts2[1])
maturityScore2, maturityLevel2 := analyzer.RepoMaturityScore(repo2, len(commits2), len(contributors2), hasReleases2)

result2 := AnalysisResult{
Repo: repo2,
Expand Down
Loading