return false;
}
+ /**
+ * Check if the pharext webhook is set for the repo and return its id
+ * @param object $repo
+ * @return int hook id
+ */
function checkRepoHook($repo) {
if ($repo->hooks) {
foreach ($repo->hooks as $hook) {
if ($hook->name === "web" && $hook->config->url === $this->github->getConfig()->hook->url) {
- return true;
+ return $hook->id;
}
}
}
- return false;
+ return null;
}
function createLinkGenerator($links) {
--- /dev/null
+<?php
+
+namespace app\Controller\Github\Repo;
+
+use app\Controller\Github;
+
+class Hook extends Github
+{
+ function __invoke(array $args = null) {
+ switch ($args["action"]) {
+ case "add":
+ $this->addHook($args["owner"], $args["name"]);
+ break;
+
+ case "del":
+ $this->delHook($args["owner"], $args["name"]);
+ break;
+ }
+ }
+
+ function addHook($owner, $repo) {
+ $this->github->createRepoHook("$owner/$repo", function($hook) use($owner, $repo) {
+ if (($cache = $this->github->getCacheStorage())) {
+ $cache->del($this->github->getCacheKey("hooks:$owner/$repo"));
+ }
+ if (($back = $this->app->getRequest()->getForm("returnback")) && isset($this->session->previous)) {
+ $this->app->redirect($this->app->getBaseUrl()->mod($this->session->previous));
+ } else {
+ $this->app->redirect($this->app->getBaseUrl()->mod("./github/repo/$owner/$repo"));
+ }
+ })->send();
+ }
+
+ function delHook($owner, $repo) {
+ $this->github->fetchRepo("$owner/$repo", function($repo) {
+ $this->github->fetchHooks($repo->full_name, function($hooks) use($repo) {
+ $repo->hooks = $hooks;
+ if (($id = $this->checkRepoHook($repo))) {
+ $this->github->deleteRepoHook($repo->full_name, $id, function() use($repo) {
+ if (($cache = $this->github->getCacheStorage())) {
+ $cache->del($this->github->getCacheKey("hooks:" . $repo->full_name));
+ }
+ if (($back = $this->app->getRequest()->getForm("returnback")) && isset($this->session->previous)) {
+ $this->app->redirect($this->app->getBaseUrl()->mod($this->session->previous));
+ } else {
+ $this->app->redirect($this->app->getBaseUrl()->mod("./github/repo/" . $repo->full_name));
+ }
+ });
+ }
+ });
+ })->send();
+ }
+}
$fetch = new Fetch\Contents($this, compact("repo", "path"));
return $fetch($callback);
}
+
+ function createRepoHook($repo, callable $callback) {
+ $create = new Create\Webhook($this, compact("repo"));
+ return $create($callback);
+ }
+
+ function deleteRepoHook($repo, $id, callable $callback) {
+ $delete = new Delete\Webhook($this, compact("repo", "id"));
+ return $delete($callback);
+ }
}
--- /dev/null
+<?php
+
+namespace app\Github;
+
+use http\Header;
+use http\Url;
+
+abstract class Create
+{
+ /**
+ * @var \app\Github\API
+ */
+ protected $api;
+
+ /**
+ * @var \merry\Config
+ */
+ protected $config;
+
+ /**
+ * @var array
+ */
+ protected $args;
+
+ /**
+ * @var \http\Url
+ */
+ protected $url;
+
+ function __construct(API $api, array $args = []) {
+ $this->api = $api;
+ $this->config = $api->getConfig();
+ $this->args = $args;
+ $this->url = new Url("https://api.github.com/", null, 0);
+ }
+
+ function __toString() {
+ $parts = explode("\\", get_class($this));
+ return strtolower(end($parts));
+ }
+
+ abstract function getRequest();
+ abstract function getException($message, $code, $previous = null);
+
+ function __invoke(callable $callback) {
+ $this->enqueue($callback);
+ return $this->api->getClient();
+ }
+
+ protected function wrap(callable $callback) {
+ return function($response) use($callback) {
+ $rc = $response->getResponseCode();
+
+ if ($rc !== 201) {
+ if ($response->getHeader("Content-Type", Header::class)->match("application/json", Header::MATCH_WORD)) {
+ $message = json_decode($response->getBody())->message;
+ } else {
+ $message = $response->getBody();
+ }
+ throw $this->getException($message, $rc);
+ }
+
+ $json = json_decode($response->getBody());
+
+ if (isset($json->error)) {
+ throw $this->getException($json->error_description, $rc);
+ }
+
+ $callback($json);
+
+ return true;
+ };
+ }
+
+ function enqueue(callable $callback) {
+ $request = $this->getRequest();
+ $wrapper = $this->wrap($callback);
+ return $this->api->getClient()->enqueue($request, $wrapper);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace app\Github\Create;
+
+use app\Github\Create;
+use app\Github\Exception\WebhookCreateFailed;
+use http\Client\Request;
+
+class Webhook extends Create
+{
+ function getRequest() {
+ $url = $this->url->mod("./repos/". $this->args["repo"] ."/hooks");
+ $request = new Request("POST", $url, [
+ "Accept" => "application/vnd.github.v3+json",
+ "Content-Type" => "application/json",
+ "Authorization" => "token " . $this->api->getToken(),
+ ]);
+ $request->getBody()->append(json_encode([
+ "name" => "web",
+ "config" => [
+ "url" => $this->config->hook->url,
+ "content_type" => $this->config->hook->content_type,
+ "secret" => $this->config->client->secret, // FIXME: bad idea?
+ "insecure_ssl" => false,
+ ]
+ ]));
+ return $request;
+ }
+
+ function getException($message, $code, $previous = null) {
+ return new WebhookCreateFailed($message, $code, $previous);
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github;
+
+use http\Header;
+use http\Url;
+
+abstract class Delete
+{
+ /**
+ * @var \app\Github\API
+ */
+ protected $api;
+
+ /**
+ * @var \merry\Config
+ */
+ protected $config;
+
+ /**
+ * @var array
+ */
+ protected $args;
+
+ /**
+ * @var \http\Url
+ */
+ protected $url;
+
+ function __construct(API $api, array $args = []) {
+ $this->api = $api;
+ $this->config = $api->getConfig();
+ $this->args = $args;
+ $this->url = new Url("https://api.github.com/", null, 0);
+ }
+
+ function __toString() {
+ $parts = explode("\\", get_class($this));
+ return strtolower(end($parts));
+ }
+
+ abstract function getRequest();
+ abstract function getException($message, $code, $previous = null);
+
+ function __invoke(callable $callback) {
+ $this->enqueue($callback);
+ return $this->api->getClient();
+ }
+
+ protected function wrap(callable $callback) {
+ return function($response) use($callback) {
+ $rc = $response->getResponseCode();
+
+ if ($rc !== 204) {
+ if ($response->getHeader("Content-Type", Header::class)->match("application/json", Header::MATCH_WORD)) {
+ $message = json_decode($response->getBody())->message;
+ } else {
+ $message = $response->getBody();
+ }
+ throw $this->getException($message, $rc);
+ }
+
+ $callback();
+
+ return true;
+ };
+ }
+
+ function enqueue(callable $callback) {
+ $request = $this->getRequest();
+ $wrapper = $this->wrap($callback);
+ return $this->api->getClient()->enqueue($request, $wrapper);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace app\Github\Delete;
+
+use app\Github\Delete;
+use app\Github\Exception\WebhookDeleteFailed;
+use http\Client\Request;
+
+class Webhook extends Delete
+{
+ function getRequest() {
+ $url = $this->url->mod("./repos/". $this->args["repo"] ."/hooks/". $this->args["id"]);
+ $request = new Request("DELETE", $url, [
+ "Accept" => "application/vnd.github.v3+json",
+ "Authorization" => "token " . $this->api->getToken(),
+ ]);
+ return $request;
+ }
+
+ function getException($message, $code, $previous = null) {
+ return new WebhookDeleteFailed($message, $code, $previous);
+ }
+}
namespace app\Github\Exeption;
-class UserFetchFailed extends Exception implements app\Github\Exception\RequestException
+class UserFetchFailed extends \Exception implements app\Github\Exception\RequestException
{
function __construct($message, $code, $previous = null) {
parent::__construct($message ?: "User fetch request failed", $code, $previous);
--- /dev/null
+<?php
+
+namespace app\Github\Exception;
+
+class WebhookCreateFailed extends \Exception implements \app\Github\Exception\RequestException
+{
+ function __construct($message, $code, $previous = null) {
+ parent::__construct($message ?: "Webhook create request failed", $code, $previous);
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\Exception;
+
+class WebhookDeleteFailed extends \Exception implements \app\Github\Exception\RequestException
+{
+ function __construct($message, $code, $previous = null) {
+ parent::__construct($message ?: "Webhook delete request failed", $code, $previous);
+ }
+}
$this->baseUrl = $baseUrl;
$this->request = $request;
$this->response = $response;
- $this->view = $view;
+ $this->view = $view->addData(["location" => null]);
ob_start($response);
}
GET[] = /github/repo/{owner}/{name}
GET[] = /github/repo/{owner}/{name}/{page}
+[Github\Repo\Hook]
+POST[] = /github/repo/{owner}/{name}/hook/{action}
+
[Github\Signin]
GET[] = /github/signin
<th>Repo</th>
<th class="text-center">Fork</th>
<th class="text-right">Last Pushed</th>
- <th class="text-right">Hook</th>
+ <th class="text-center">Hook</th>
</tr>
</thead>
<tbody>
<?php endif; ?>
</td>
<td class="text-right"><time datetime="<?= $this->e($repo->pushed_at) ?>"><?= $this->e($this->utc($repo->pushed_at)->format("Y-m-d H:i T")) ?></time></td>
- <td class="text-right">
- <a class="btn btn-primary <?= $this->check($repo) ? "disabled":"" ?>"
- href="<?= $baseUrl->mod("./github/repo/".$repo->full_name) ?>">
- <span class="glyphicon glyphicon-plus" title="Add pharext hook"></span></a>
- <a class="btn btn-danger <?= !$this->check($repo) ? "disabled":"" ?>"
- href="<?= $baseUrl->mod(["path" => "github/repo", "query" => "hook=del&repo=". urlencode($repo->full_name)]) ?>">
- <span class="glyphicon glyphicon-remove" title="Remove hook"></span></a>
+ <td class="text-center">
+ <form class="form-inline" style="display: inline-block" method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/add") ?>">
+ <input type="hidden" name="returnback" value="1">
+ <button type="submit" class="btn btn-primary <?= $this->check($repo) ? "disabled":"" ?>">
+ <span class="glyphicon glyphicon-plus" title="Add pharext hook"></span>
+ </button>
+ </form>
+ <form class="form-inline" style="display: inline-block" method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/del") ?>">
+ <input type="hidden" name="returnback" value="1">
+ <button type="submit" class="btn btn-danger <?= $this->check($repo) ? "":"disabled" ?>">
+ <span class="glyphicon glyphicon-remove" title="Remove hook"></span>
+ </button>
+ </form>
+ </div>
</td>
</tr>
<?php endforeach; ?>
</div>
<div class="col-md-6">
+
<div class="row text-center">
<div class="col-md-6">
- <button class="btn btn-lg btn-block btn-success <?= $this->check($repo) ? "disabled":"" ?>">Enable Hook</button>
+ <form method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/add") ?>">
+ <button type="submit" class="btn btn-lg btn-block btn-success <?= $this->check($repo) ? "disabled":"" ?>">Enable Hook</button>
+ </form>
</div>
<div class="col-md-6">
+ <form method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/del") ?>">
<button class="btn btn-lg btn-block btn-danger <?= $this->check($repo) ? "":"disabled" ?>">Disable Hook</button>
+ </form>
</div>
</div>
+ </form>
</div>
</div>