Skip to content
Snippets Groups Projects
Commit a2199848 authored by Jan-Hendrik Willms's avatar Jan-Hendrik Willms
Browse files

review apps, fixes #53

parent 4fdd0a5a
No related branches found
No related tags found
1 merge request!10review apps, fixes #53
Showing
with 500 additions and 445 deletions
......@@ -14,11 +14,15 @@ final class TracToGitlabPlugin extends TracToGitlab\Plugin implements SystemPlug
'TracToGitlabPlugin::markupGitlabLinks'
);
if (!is_object($GLOBALS['user']) || $GLOBALS['user']->id === 'nobody') {
if (!User::findCurrent()) {
return;
}
$this->buildNavigation();
$this->buildDashboardNavigation();
if (User::findCurrent()->perms === 'root') {
$this->buildAdminNavigation();
}
}
public function getPluginName()
......@@ -34,7 +38,7 @@ final class TracToGitlabPlugin extends TracToGitlab\Plugin implements SystemPlug
parent::perform($unconsumed);
}
private function buildNavigation()
private function buildDashboardNavigation()
{
$navigation = new Navigation(_('Dashboard'), PluginEngine::getURL($this, [], 'dashboard'));
$navigation->setImage(Icon::create($this->getPluginURL() . '/assets/cardiogram.svg', Icon::ROLE_NAVIGATION));
......@@ -67,6 +71,14 @@ final class TracToGitlabPlugin extends TracToGitlab\Plugin implements SystemPlug
Navigation::addItem('/gitlab-dashboard', $navigation);
}
private function buildAdminNavigation(): void
{
Navigation::addItem('/admin/trac2gitlab', new Navigation(
_('GitLab-Verbindung konfigurieren'),
PluginEngine::getURL($this, [], 'admin/index')
));
}
public static function markupGitlabLinks($markup, $matches, $contents)
{
if ($matches[1][0] === '#') {
......
export default {
data: () => ({
needle: '',
filters: {},
}),
created() {
if (this.storedFilters !== null) {
Object.entries(this.storedFilters).forEach(([filter, value]) => {
this.$set(this.filters, filter, value);
});
}
Object.values(this.qmLabels).concat(['status', 'mr_status']).forEach(abbr => {
if (this.filters[abbr] === undefined) {
this.$set(this.filters, abbr, null);
}
});
this.$watch('filters', this.storeFilters, {deep: true});
},
methods: {
getStateForIssueAndQmLabel(issue, qm) {
return issue.qm_states[qm];
},
valueMatchesNeedle(what) {
if (this.needle.length === 0) {
return false;
}
return what.toLowerCase().includes(this.needle.toLowerCase());
},
storeFilters(filters) {
const data = new URLSearchParams();
for (const [label, value] of Object.entries(filters)) {
if (value !== null) {
data.append(`filters[${label}]`, value);
}
}
fetch(this.storeFiltersUrl, {
method: 'POST',
body: data,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
}
},
computed: {
colspan() {
return 8 + Object.values(this.qmLabels).length;
},
filteredIssues() {
let filtered = this.issues.filter(issue => {
for (const abbr of Object.values(this.qmLabels)) {
if (this.filters[abbr] === null) {
continue;
}
if (issue.qm_states[abbr] !== this.filters[abbr]) {
return false;
}
}
if (this.filters.status) {
if (this.filters.status === 'open' && issue.closed) {
return false;
}
if (this.filters.status === 'closed' && !issue.closed) {
return false;
}
}
if (this.filters.mr_status) {
if (this.filters.mr_status === 'none' && issue.merge_requests) {
return false;
}
if (this.filters.mr_status === 'merged' && !issue.merged) {
return false;
}
if (
this.filters.mr_status === 'pending'
&& (!issue.merge_requests || issue.merged)
) {
return false;
}
}
return true;
});
if (this.needle.length > 0) {
filtered = filtered.filter(issue => {
const ciNeedle = this.needle.toLowerCase();
return issue.iid.toString().includes(this.needle)
|| issue.title.toLowerCase().includes(ciNeedle)
|| (issue.assignee ?? '').toLowerCase().includes(ciNeedle)
|| (issue.author ?? '').toLowerCase().includes(ciNeedle)
|| issue.reviewers.some(reviewer => reviewer.toLowerCase().includes(ciNeedle));
});
}
return filtered;
}
}
}
(function ($, STUDIP) {
(function (STUDIP) {
'use strict';
const lookup = {
'#dashboard-app': () => import('./apps/dashboard.js'),
'#labels-list-app': () => import('./apps/labels.js'),
};
......@@ -31,94 +32,4 @@
});
});
$(document).ready(function () {
if (document.getElementById('dashboard') !== null) {
const dashboard = document.getElementById('dashboard');
const issues = JSON.parse(dashboard.dataset.issues);
const qmLabels = JSON.parse(dashboard.dataset.qmLabels);
const filters = JSON.parse(dashboard.dataset.filters) || {};
const filterStoreUrl = dashboard.dataset.filterStoreUrl;
Object.values(qmLabels).forEach(abbr => {
if (filters[abbr] === undefined) {
filters[abbr] = null;
}
});
STUDIP.loadChunk('vue').then(({createApp}) => {
createApp({
data () {
return {
needle: '',
issues,
qmLabels,
filters
};
},
methods: {
getStateForIssueAndQmLabel(issue, qm) {
return issue.qm_states[qm];
},
valueMatchesNeedle(what) {
if (this.needle.length === 0) {
return false;
}
return what.toLowerCase().includes(this.needle.toLowerCase());
}
},
computed: {
colspan() {
return 8 + Object.values(qmLabels).length;
},
filteredIssues() {
let filtered = this.issues.filter(issue => {
for (const [key, value] of Object.entries(this.filters)) {
if (value === null) {
continue;
}
if (issue.qm_states[key] !== value) {
return false;
}
}
return true;
});
if (this.needle.length > 0) {
filtered = filtered.filter(issue => {
const ciNeedle = this.needle.toLowerCase();
return issue.iid.toString().includes(this.needle)
|| issue.title.toLowerCase().includes(ciNeedle)
|| (issue.assignee ?? '').toLowerCase().includes(ciNeedle)
|| (issue.author ?? '').toLowerCase().includes(ciNeedle)
|| issue.reviewers.some(reviewer => reviewer.toLowerCase().includes(ciNeedle));
});
}
return filtered;
}
},
watch: {
filters: {
handler(current) {
const data = new URLSearchParams();
for (const [label, value] of Object.entries(current)) {
if (value !== null) {
data.append(`filters[${label}]`, value);
}
}
fetch(filterStoreUrl, {
method: 'POST',
body: data
});
},
deep: true
}
}
}).$mount('#dashboard');
});
}
});
}(jQuery, STUDIP));
}(STUDIP));
td.filter-match {
background-color: var(--yellow-20);
}
#dashboard table.default td {
#dashboard-app {
table.default td {
vertical-align: top;
}
.review-app-list {
&::before {
content: '[';
}
&::after {
content: ']';
}
}
}
#labels-list-app {
* {
box-sizing: border-box;
......
<?php
use Psr\Http\Client\ClientInterface;
use Studip\DIContainer;
use TracToGitlab\BytistConnector;
use TracToGitlab\TracLookup;
StudipAutoloader::addAutoloadPath(__DIR__ . '/lib', 'TracToGitlab');
......@@ -7,13 +7,11 @@
}
],
"require": {
"fguillot/json-rpc": "^1.2",
"m4tthumphrey/php-gitlab-api": "^11.12.0",
"guzzlehttp/guzzle": "^7.2",
"http-interop/http-factory-guzzle": "^1.0",
"erusev/parsedown": "^1.7",
"ext-json": "*",
"pimple/pimple": "^3.4",
"ext-curl": "*"
},
"config": {
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "364dc3e4d32decc645b3e2914dae455b",
"content-hash": "d7ac7fbb441d73b0404063ba0c3f4e9c",
"packages": [
{
"name": "clue/stream-filter",
......@@ -122,49 +122,6 @@
},
"time": "2019-12-30T22:54:17+00:00"
},
{
"name": "fguillot/json-rpc",
"version": "v1.2.8",
"source": {
"type": "git",
"url": "https://github.com/matasarei/JsonRPC.git",
"reference": "f1eef90bf0bb3f7779c9c8113311811ef449ece8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matasarei/JsonRPC/zipball/f1eef90bf0bb3f7779c9c8113311811ef449ece8",
"reference": "f1eef90bf0bb3f7779c9c8113311811ef449ece8",
"shasum": ""
},
"require": {
"php": ">=5.4"
},
"require-dev": {
"phpunit/phpunit": "4.8.*"
},
"type": "library",
"autoload": {
"psr-0": {
"JsonRPC": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "Simple Json-RPC client/server library that just works",
"homepage": "https://github.com/matasarei/JsonRPC",
"support": {
"issues": "https://github.com/matasarei/JsonRPC/issues",
"source": "https://github.com/matasarei/JsonRPC/tree/v1.2.8"
},
"time": "2019-03-23T16:13:00+00:00"
},
{
"name": "guzzlehttp/guzzle",
"version": "7.3.0",
......@@ -1037,59 +994,6 @@
},
"time": "2023-11-08T12:57:08+00:00"
},
{
"name": "pimple/pimple",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "86406047271859ffc13424a048541f4531f53601"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/86406047271859ffc13424a048541f4531f53601",
"reference": "86406047271859ffc13424a048541f4531f53601",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"psr/container": "^1.1"
},
"require-dev": {
"symfony/phpunit-bridge": "^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "https://pimple.symfony.com",
"keywords": [
"container",
"dependency injection"
],
"support": {
"source": "https://github.com/silexphp/Pimple/tree/v3.4.0"
},
"time": "2021-03-06T08:28:00+00:00"
},
{
"name": "psr/cache",
"version": "1.0.1",
......@@ -1139,54 +1043,6 @@
},
"time": "2016-08-06T20:24:11+00:00"
},
{
"name": "psr/container",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
"reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
"shasum": ""
},
"require": {
"php": ">=7.2.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"support": {
"issues": "https://github.com/php-fig/container/issues",
"source": "https://github.com/php-fig/container/tree/1.1.1"
},
"time": "2021-03-05T17:36:06+00:00"
},
{
"name": "psr/http-client",
"version": "1.0.3",
......@@ -1701,5 +1557,5 @@
"ext-curl": "*"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}
<?php
final class AdminController extends \TracToGitlab\Controller
{
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
if (User::findCurrent()->perms !== 'root') {
throw new AccessDeniedException();
}
Navigation::activateItem('/admin/trac2gitlab');
PageLayout::setTitle(_('GitLab-Verbindung konfigurieren'));
}
public function index_action()
{
$this->gitlab_url = Config::get()->TRAC2GITLAB_GITLAB_URL;
$this->gitlab_token = Config::get()->TRAC2GITLAB_GITLAB_TOKEN;
$this->gitlab_project_id = Config::get()->TRAC2GITLAB_GITLAB_PROJECT_ID;
$this->bytist_token = Config::get()->TRAC2GITLAB_BYTIST_TOKEN;
$this->webhook_secret = Config::get()->TRAC2GITLAB_GITLAB_WEBHOOK_SECRET;
$this->systemhook_secret = Config::get()->TRAC2GITLAB_GITLAB_SYSTEMHOOK_SECRET;
}
public function store_action()
{
if (!Request::isPost()) {
throw new MethodNotAllowedException();
}
Config::get()->store('TRAC2GITLAB_GITLAB_URL', Request::get('gitlab-url'));
Config::get()->store('TRAC2GITLAB_GITLAB_TOKEN', Request::get('gitlab-token'));
Config::get()->store('TRAC2GITLAB_GITLAB_PROJECT_ID', Request::int('gitlab-project-id'));
Config::get()->store('TRAC2GITLAB_BYTIST_TOKEN', Request::get('bytist-token'));
Config::get()->store('TRAC2GITLAB_GITLAB_WEBHOOK_SECRET', Request::get('webhook-secret'));
Config::get()->store('TRAC2GITLAB_GITLAB_SYSTEMHOOK_SECRET', Request::get('systemhook-secret'));
PageLayout::postSuccess(_('Einstellungen wurden gespeichert'));
$this->redirect($this->indexURL());
}
}
<?php
use TracToGitlab\GitlabIssue;
/**
* @property User|null $user
*/
final class DashboardController extends TracToGitlab\Controller
final class DashboardController extends TracToGitlab\GitlabController
{
public function before_filter(&$action, &$args)
{
......@@ -65,9 +67,15 @@ final class DashboardController extends TracToGitlab\Controller
public function index_action()
{
$this->issues = $this->getIssues();
$this->issues = $this->cached('issues', fn() => $this->getIssues());
$this->mapping = $this->getQMLabelMapping();
$this->filters = $this->getSelected('filters');
Sidebar::get()->addWidget(new TemplateWidget(
_('Stand der Daten'),
$this->get_template_factory()->open('sidebar'),
['time' => $this->getCachedDate('issues')]
));
}
public function select_action(string $what, string $value = null)
......@@ -118,11 +126,11 @@ final class DashboardController extends TracToGitlab\Controller
foreach (explode(',', $this->getSelected('types')) as $type) {
$issues = array_merge(
$issues,
$this->gitlabPager->fetchAll(
$this->result_pager->fetchAll(
$this->gitlab->issues(),
'all',
[
$this->gitlabProjectId,
$this->gitlab_project_id,
[
'sort' => 'asc',
'scope' => 'all',
......@@ -137,23 +145,27 @@ final class DashboardController extends TracToGitlab\Controller
return $a['id'] - $b['id'];
});
return array_map(function ($issue) {
$mrs = $this->gitlab->issues()->relatedMergeRequests($this->gitlabProjectId, $issue['iid']);
return new TracToGitlab\GitlabIssue($issue, $mrs);
return array_map(function (array $issue): GitlabIssue {
$mrs = $this->gitlab->issues()->closedByMergeRequests(
$this->gitlab_project_id,
$issue['iid']
);
return new GitlabIssue($issue, $mrs);
}, $issues);
}
private function getMilestones()
{
$milestones = $this->gitlabPager->fetchAll(
return $this->cached('milestones', function () {
$milestones = $this->result_pager->fetchAll(
$this->gitlab->milestones(),
'all',
[$this->gitlabProjectId]
[Config::get()->TRAC2GITLAB_GITLAB_PROJECT_ID]
);
$milestones = array_filter($milestones, function ($milestone) {
return array_filter($milestones, function ($milestone) {
return preg_match('/^Stud\.IP \d+\.\d+$/', $milestone['title']);
});
return $milestones;
});
}
private function getMilestonesAsSelection()
......@@ -165,24 +177,6 @@ final class DashboardController extends TracToGitlab\Controller
return $result;
}
private function getQMLabels()
{
$labels = $this->gitlabPager->fetchAll(
$this->gitlab->projects(),
'labels',
[$this->gitlabProjectId]
);
$labels = array_filter($labels, function ($label) {
return strpos($label['name'], 'QM::') === 0;
});
$labels = array_map(function ($label) {
return substr($label['name'], 4, -3);
}, $labels);
$labels = array_unique($labels);
$labels = array_values($labels);
return $labels;
}
private function getQMLabelMapping()
{
return TracToGitlab\GitlabIssue::QM_LABEL_MAPPING;
......
......@@ -11,27 +11,21 @@ final class LabelsController extends \TracToGitlab\GitlabController
public function index_action()
{
$data = $this->readFromCache('labels', true);
if ($data === false) {
$this->labels = $this->fetchLabels();
$this->writeToCache('labels', $this->labels);
} else {
$this->labels = $data['data'];
}
$this->labels = $this->cached('labels', fn() => $this->fetchLabels());
}
private function fetchLabels(): array
{
$labels = $this->gitlabPager->fetchAll(
$labels = $this->result_pager->fetchAll(
$this->gitlab->projects(),
'labels',
[
$this->gitlabProjectId,
$this->gitlab_project_id,
['with_counts' => true]
]
);
usort($labels, function ($a, $b) {
usort($labels, function (array $a, array $b) {
return strnatcasecmp($a['name'], $b['name']);
});
......
......@@ -11,35 +11,22 @@ final class MergeController extends TracToGitlab\GitlabController
public function index_action()
{
$data = $this->readFromCache('issues-to-merge', true);
if ($data === false) {
$this->issues = $this->fetchIssues();
$this->writeToCache('issues-to-merge', $this->issues);
} else {
$this->issues = $data['data'];
$this->issues = $this->cached('issues-to-merge', fn() => $this->fetchIssues());
Sidebar::get()->addWidget(new TemplateWidget(
'Aus dem Cache',
$this->get_template_factory()->open(
$this->get_default_template('sidebar')
),
['time' => time()]
_('Stand der Daten'),
$this->get_template_factory()->open('sidebar'),
['time' => $this->getCachedDate('issues-to-merge')]
));
}
}
public function diff_action($mr_iid)
{
// $versions = $this->gitlab->mergeRequests()->
}
private function fetchIssues(): array
{
$issues = $this->gitlabPager->fetchAll(
$issues = $this->result_pager->fetchAll(
$this->gitlab->issues(),
'all',
[
$this->gitlabProjectId,
$this->gitlab_project_id,
[
'sort' => 'asc',
'scope' => 'all',
......@@ -61,10 +48,9 @@ final class MergeController extends TracToGitlab\GitlabController
}
}
$has_version = array_reduce($issue['labels'], function ($has_version, $label) {
return $has_version || strpos($label, 'Version::') === 0;
return array_reduce($issue['labels'], function ($has_version, $label) {
return $has_version || str_starts_with($label, 'Version::');
}, false);
return $has_version;
});
$issues = array_map(function ($issue) {
......@@ -83,7 +69,7 @@ final class MergeController extends TracToGitlab\GitlabController
private function fetchRelatedMergeRequest(int $issue_id)
{
$mrs = $this->gitlab->issues()->closedByMergeRequests(
$this->gitlabProjectId,
$this->gitlab_project_id,
$issue_id
);
foreach ($mrs as $mr) {
......@@ -98,7 +84,7 @@ final class MergeController extends TracToGitlab\GitlabController
{
$version = '';
foreach ($labels as $label) {
if (strpos($label, 'Version::') === 0) {
if (str_starts_with($label, 'Version::')) {
$v = substr($label, 9);
if (!$version || $v < $version) {
$version = $v;
......
......@@ -11,35 +11,22 @@ final class MergerequestsController extends TracToGitlab\GitlabController
public function index_action()
{
$data = $this->readFromCache('mergerequests', true);
if ($data === false) {
$this->mrs = $this->fetchMergeRequests();
$this->writeToCache('mergerequests', $this->mrs);
} else {
$this->mrs = $data['data'];
$this->mrs = $this->cached('mergerequests', fn() => $this->fetchMergeRequests());
Sidebar::get()->addWidget(new TemplateWidget(
'Aus dem Cache',
$this->get_template_factory()->open(
$this->get_default_template('sidebar')
),
['time' => $data['time']]
_('Aus dem Cache'),
$this->get_template_factory()->open('sidebar'),
['time' => $this->getCachedDate('mergerequests')]
));
}
}
public function diff_action($mr_iid)
{
// $versions = $this->gitlab->mergeRequests()->
}
private function fetchMergeRequests(): array
{
$mrs = $this->gitlabPager->fetchAll(
$mrs = $this->result_pager->fetchAll(
$this->gitlab->mergeRequests(),
'all',
[
$this->gitlabProjectId,
$this->gitlab_project_id,
[
'sort' => 'asc',
'scope' => 'all',
......@@ -58,7 +45,7 @@ final class MergerequestsController extends TracToGitlab\GitlabController
$mrs = array_map(function ($mr) {
$mr['approvals'] = $this->gitlab->mergeRequests()->approvals(
$this->gitlabProjectId,
$this->gitlab_project_id,
$mr['iid']
);
return $mr;
......@@ -74,7 +61,7 @@ final class MergerequestsController extends TracToGitlab\GitlabController
private function fetchRelatedMergeRequest(int $issue_id)
{
$mrs = $this->gitlab->issues()->closedByMergeRequests(
$this->gitlabProjectId,
$this->gitlab_project_id,
$issue_id
);
foreach ($mrs as $mr) {
......
<?php
final class ReleasesController extends \TracToGitlab\Controller
final class ReleasesController extends \TracToGitlab\GitlabController
{
const CACHE_KEY = 'studip-releases';
const CACHE_KEY = 'releases';
private static $curl_handle = null;
private $filesize_cache;
private StudipCachedArray $filesize_cache;
public function before_filter(&$action, &$args)
{
......@@ -36,7 +36,7 @@ final class ReleasesController extends \TracToGitlab\Controller
Sidebar::get()->addWidget($widget);
$this->filesize_cache = new StudipCachedArray(
self::CACHE_KEY . '/filesizes',
'gitlab/releases/filesizes',
strtotime('+10 years')
);
}
......@@ -54,7 +54,7 @@ final class ReleasesController extends \TracToGitlab\Controller
{
$version = Request::get('version', Request::get('v'));
$this->releases = $this->getReleases();
$this->releases = $this->cached('releases', fn() => $this->getReleases());
$this->addVersionFilter($this->releases, $version);
$this->addResetAction();
......@@ -69,7 +69,7 @@ final class ReleasesController extends \TracToGitlab\Controller
public function reset_action()
{
if ($this->isAdmin()) {
StudipCacheFactory::getCache()->expire(self::CACHE_KEY);
$this->expireCache('releases');
$this->filesize_cache->expire();
}
......@@ -78,10 +78,6 @@ final class ReleasesController extends \TracToGitlab\Controller
private function getReleases(): array
{
$cache = StudipCacheFactory::getCache();
$releases = $cache->read(self::CACHE_KEY);
if ($releases === false) {
$temp = array_map(
function (array $release): array {
$links = array_filter(
......@@ -112,7 +108,7 @@ final class ReleasesController extends \TracToGitlab\Controller
try {
$file = $this->gitlab->repositoryFiles()->getFile(
$this->gitlabProjectId,
$this->gitlab_project_id,
'RELEASE-NOTES.md',
$release['tag_name']
);
......@@ -137,7 +133,7 @@ final class ReleasesController extends \TracToGitlab\Controller
'links' => $links,
];
},
$this->gitlab->repositories()->releases($this->gitlabProjectId)
$this->gitlab->repositories()->releases($this->gitlab_project_id)
);
$releases = [];
......@@ -167,9 +163,6 @@ final class ReleasesController extends \TracToGitlab\Controller
$releases[$version] = $subversions;
}
$cache->write(self::CACHE_KEY, $releases);
}
return $releases;
}
......
......@@ -10,16 +10,16 @@ final class UsersController extends TracToGitlab\EventController
}
if (
!$this->verifySecret(self::SECRET_USER_CREATED)
!$this->verifySecret(Config::get()->TRAC2GITLAB_GITLAB_SYSTEMHOOK_SECRET)
|| !$this->verifyEventType('System Hook')
) {
throw new AccessDeniedException();
}
if ($this->getFromPayload('event_name') === 'user_create') {
if ($this->payload('event_name') === 'user_create') {
$this->gitlab->projects()->addMember(
$this->gitlabProjectId,
$this->getFromPayload('user_id'),
$this->gitlab_project_id,
$this->payload('user_id'),
20
);
}
......
<?php
use TracToGitlab\EventController;
use TracToGitlab\EventHandler;
use TracToGitlab\EventHandlers;
final class WebhooksController extends EventController
{
public function before_filter(&$action, &$args)
{
if (!Request::isPost()) {
throw new MethodNotAllowedException();
}
if (!$this->verifySecret(Config::get()->TRAC2GITLAB_GITLAB_WEBHOOK_SECRET)) {
throw new AccessDeniedException();
}
parent::before_filter($action, $args);
}
public function gitlab_action()
{
$payload = $this->payload();
if (!$this->verifyEvent($payload)) {
$this->set_status(400, 'This is not a valid gitlab event');
}
$event_type = $payload['object_kind'];
foreach ($this->getKnownHandlers() as $handler) {
if (
!$handler->shouldReactToEventHeader($_SERVER['X-GITLAB-EVENT'])
|| !$handler->shouldReactToEventType($event_type)
) {
continue;
}
$result = $handler($payload);
if ($result === false) {
break;
}
}
$this->render_nothing();
}
private function verifyEvent(array $payload): bool
{
if (!isset($_SERVER['X-GITLAB-EVENT'])) {
return false;
}
if (!isset($payload['object_kind'])) {
return false;
}
if (!isset($payload['project']['id'])) {
return false;
}
if ($payload['project']['id'] !== $this->gitlab_project_id) {
return false;
}
return true;
}
/**
* @return iterator|EventHandler[]
*/
private function getKnownHandlers(): iterator
{
yield app(EventHandlers\AllAutomaticJobsHaveSucceeded::class);
yield app(EventHandlers\BranchDeleted::class);
yield app(EventHandlers\BuildImageJobSucceeded::class);
yield app(EventHandlers\IssueCreated::class);
}
}
<?php
namespace TracToGitlab;
use GuzzleHttp\Psr7\Request;
use Psr\Http\Client\ClientInterface;
final class BytistConnector
{
private string $token;
public function __construct(string $token)
{
$this->token = $token;
}
public function deploy(string $branch): void
{
$this->sendCommandToBranch('deploy', $branch);
}
public function decommission(string $branch): void
{
$this->sendCommandToBranch('decomission', $branch);
}
private function sendCommandToBranch(string $command, string $branch): void
{
$request = new Request(
'POST',
"https://studip.bytist.de/{$command}/{$branch}",
'Authorization: Basic '. base64_encode($this->token)
);
app(ClientInterface::class)->sendRequest($request);
}
}
......@@ -3,27 +3,9 @@ namespace TracToGitlab;
/**
* @property \TracToGitlabPlugin $plugin
* @property TracLookup $trac
* @property \Gitlab\Client $gitlab
* @property \Gitlab\ResultPager $gitlabPager
* @property int $gitlabProjectId
*/
abstract class Controller extends \PluginController
{
public function &__get($offset)
{
$result = &parent::__get($offset);
if ($result === null) {
$container = $this->plugin->getDIContainer();
if (isset($container[$offset])) {
return $container[$offset];
}
}
return $result;
}
protected function activateNavigation(string ...$path): void
{
\Navigation::activateItem('/gitlab-dashboard/' . implode('/', $path));
......
<?php
namespace TracToGitlab;
abstract class EventController extends Controller
abstract class EventController extends GitlabController
{
private $payload = null;
......@@ -15,7 +15,7 @@ abstract class EventController extends Controller
return $_SERVER['HTTP_X_GITLAB_EVENT'] === $type;
}
protected function getFromPayload(...$keys)
protected function payload(...$keys)
{
if ($this->payload === null) {
$this->payload = $this->getPayloadFromRequest() ?? false;
......
<?php
namespace TracToGitlab;
interface EventHandler
{
public function shouldReactToEventHeader(string $event_header): bool;
public function shouldReactToEventType(string $event_type): bool;
public function __invoke(array $payload);
}
<?php
namespace TracToGitlab\EventHandlers;
use Gitlab;
use TracToGitlab\Hooks;
use TracToGitlab\Traits\GetBuildImageJob;
final class AllAutomaticJobsHaveSucceeded extends Hooks\Pipeline
{
use GetBuildImageJob;
private Gitlab\Client $gitlab_client;
public function __construct(Gitlab\Client $client)
{
$this->gitlab_client = $client;
}
public function __invoke(array $payload)
{
if (
isset($payload['merge_request'], $payload['builds'])
&& $this->allAutomaticsHaveSucceeded($payload['builds'])
&& !$this->buildImageJobHasStarted($payload['builds'])
) {
$this->gitlab_client->jobs()->play(
$payload['project']['id'],
$this->getBuildImageJob($payload['builds'])['id']
);
}
}
private function allAutomaticsHaveSucceeded($builds): bool
{
$builds = array_filter($builds, function ($build): bool {
return !$build['manual'];
});
foreach ($builds as $build) {
if ($build['status'] !== 'success') {
return false;
}
}
return true;
}
private function buildImageJobHasStarted($builds): bool
{
$job = $this->getBuildImageJob($builds);
return $job && $job['status'] !== 'manual';
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment