Skip to content

Commit

Permalink
feat: add secret scanning and branch protection charts to project view
Browse files Browse the repository at this point in the history
  • Loading branch information
netomi committed Feb 29, 2024
1 parent a77890c commit 80a31fa
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 48 deletions.
39 changes: 39 additions & 0 deletions otterdog/webapp/home/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
# SPDX-License-Identifier: EPL-2.0
# *******************************************************************************

import json
from typing import Any

from quart import current_app, redirect, render_template, request, url_for
from werkzeug.routing import BuildError

from otterdog.models.github_organization import GitHubOrganization
from otterdog.utils import associate_by_key
from otterdog.webapp.db.service import (
get_active_installations,
Expand Down Expand Up @@ -66,9 +68,46 @@ async def project(project_name: str):
project_name=project_name,
github_id=config.github_id,
config=github_organization,
secret_scanning_data=json.dumps(_get_secret_scanning_data(github_organization)),
branch_protection_data=json.dumps(_get_branch_protection_data(github_organization)),
)


def _get_secret_scanning_data(organization: GitHubOrganization) -> list[int]:
alert_mode = 0
protection_mode = 0
not_configured = 0

for repo in organization.repositories:
if repo.archived is True:
continue

if repo.secret_scanning_push_protection == "enabled":
protection_mode += 1
elif repo.secret_scanning == "enabled":
alert_mode += 1
else:
not_configured += 1

return [not_configured, alert_mode, protection_mode]


def _get_branch_protection_data(organization: GitHubOrganization) -> list[int]:
protected = 0
not_protected = 0

for repo in organization.repositories:
if repo.archived is True:
continue

if len(repo.rulesets) > 0 or len(repo.branch_protection_rules) > 0:
protected += 1
else:
not_protected += 1

return [not_protected, protected]


@blueprint.route("/projects/<project_name>/repos/<repo_name>")
async def repository(project_name: str, repo_name: str):
config = await get_configuration_by_project_name(project_name)
Expand Down
177 changes: 129 additions & 48 deletions otterdog/webapp/templates/home/organization.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,54 +80,86 @@ <h1>{{ project_name }}</h1>
<div class="card-body">
<div class="tab-content" id="organization-tabContent">
<div class="tab-pane show active" id="overview" role="tabpanel" aria-labelledby="overview-tab">
<div class="col-sm-3">
<ul class="nav flex-column">
<li class="nav-item">
<a href="#" class="nav-link">
Project
<span class="float-right">{{ project_name }}</span>
</a>
</li>
<li class="nav-item">
<a href="https://github.com/{{ github_id }}" class="nav-link">
GitHub Org
<span class="float-right">{{ github_id }}</span>
</a>
</li>
<li class="nav-item">
<a href="#settings" class="nav-link tab-link">
{% set two_factor_enabled = config.settings.two_factor_requirement %}
2FA enforced <span class="float-right badge bg-{{ 'success' if two_factor_enabled == true else 'danger' }}">{{ two_factor_enabled }}</span>
</a>
</li>
<li class="nav-item">
<a href="#workflow-settings" class="nav-link tab-link">
{% set default_workflow_permissions = config.settings.workflows.default_workflow_permissions %}
Default workflow permissions <span class="float-right badge bg-{{ 'success' if default_workflow_permissions == 'read' else 'danger' }}">{{ default_workflow_permissions }}</span>
</a>
</li>
<li class="nav-item">
<a href="#secrets" class="nav-link tab-link">
Secrets <span class="float-right badge bg-{{ config.secrets|length_to_color}}">{{ config.secrets|length }}</span>
</a>
</li>
<li class="nav-item">
<a href="#variables" class="nav-link tab-link">
Variables <span class="float-right badge bg-{{ config.variables|length_to_color}}">{{ config.variables|length }}</span>
</a>
</li>
<li class="nav-item">
<a href="#webhooks" class="nav-link tab-link">
Webhooks <span class="float-right badge bg-{{ config.webhooks|length_to_color}}">{{ config.webhooks|length }}</span>
</a>
</li>
<li class="nav-item">
<a href="#repositories" class="nav-link tab-link">
Repositories <span class="float-right badge bg-{{ config.repositories|length_to_color}}">{{ config.repositories|length }}</span>
</a>
</li>
</ul>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">General</h3>
</div>
<div class="card-body">
<ul class="nav flex-column">
<li class="nav-item">
<a href="#" class="nav-link">
Project
<span class="float-right">{{ project_name }}</span>
</a>
</li>
<li class="nav-item">
<a href="https://github.com/{{ github_id }}" class="nav-link">
GitHub Org
<span class="float-right">{{ github_id }}</span>
</a>
</li>
<li class="nav-item">
<a href="#settings" class="nav-link tab-link">
{% set two_factor_enabled = config.settings.two_factor_requirement %}
2FA enforced <span class="float-right badge bg-{{ 'success' if two_factor_enabled == true else 'danger' }}">{{ two_factor_enabled }}</span>
</a>
</li>
<li class="nav-item">
<a href="#workflow-settings" class="nav-link tab-link">
{% set default_workflow_permissions = config.settings.workflows.default_workflow_permissions %}
Default workflow permissions <span class="float-right badge bg-{{ 'success' if default_workflow_permissions == 'read' else 'danger' }}">{{ default_workflow_permissions }}</span>
</a>
</li>
<li class="nav-item">
<a href="#secrets" class="nav-link tab-link">
Secrets <span class="float-right badge bg-{{ config.secrets|length_to_color}}">{{ config.secrets|length }}</span>
</a>
</li>
<li class="nav-item">
<a href="#variables" class="nav-link tab-link">
Variables <span class="float-right badge bg-{{ config.variables|length_to_color}}">{{ config.variables|length }}</span>
</a>
</li>
<li class="nav-item">
<a href="#webhooks" class="nav-link tab-link">
Webhooks <span class="float-right badge bg-{{ config.webhooks|length_to_color}}">{{ config.webhooks|length }}</span>
</a>
</li>
<li class="nav-item">
<a href="#repositories" class="nav-link tab-link">
Repositories <span class="float-right badge bg-{{ config.repositories|length_to_color}}">{{ config.repositories|length }}</span>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="card card-warning">
<div class="card-header">
<h3 class="card-title">Secret Scanning</h3>
</div>
<div class="card-body">
<canvas id="secret-scanning-chart" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 100%;"></canvas>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="card card-warning">
<div class="card-header">
<h3 class="card-title">Branch Protections</h3>
</div>
<div class="card-body">
<canvas id="branch-protection-chart" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 100%;"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- /.card -->
</div>
<div class="tab-pane" id="settings" role="tabpanel" aria-labelledby="settings-tab">
<textarea id="org-settings-textarea">
Expand Down Expand Up @@ -345,6 +377,8 @@ <h1>{{ project_name }}</h1>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
<!-- Codemirror -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/codemirror.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/mode/javascript/javascript.min.js"></script>
Expand Down Expand Up @@ -391,6 +425,53 @@ <h1>{{ project_name }}</h1>
editor.setSize(null, 750);
}
};

const secret_scanning_ctx = document.getElementById('secret-scanning-chart');

new Chart(secret_scanning_ctx, {
type: 'doughnut',
data: {
labels: ['disabled', 'alert mode', 'protection mode'],
datasets: [{
label: 'secret scanning',
data: {{ secret_scanning_data|safe }},
backgroundColor: [
'#dc3545',
'#ffc107',
'#28a745'
],
borderWidth: 1
}]
},
options: {
animation: {
animateRotate: false
}
}
});

const branch_protections_ctx = document.getElementById('branch-protection-chart');

new Chart(branch_protections_ctx, {
type: 'doughnut',
data: {
labels: ['not protected', 'protected'],
datasets: [{
label: 'branch protections',
data: {{ branch_protection_data|safe }},
backgroundColor: [
'#dc3545',
'#28a745'
],
borderWidth: 1
}]
},
options: {
animation: {
animateRotate: false
}
}
});
</script>

{% endblock javascripts %}

0 comments on commit 80a31fa

Please sign in to comment.