From a3cce2df73bab9a196d43cfe1bca5264bf4b5493 Mon Sep 17 00:00:00 2001 From: Sebastian Frank Date: Tue, 5 Mar 2019 15:28:29 +0100 Subject: [PATCH] init --- Controller/Admin.php | 31 +++++++ LICENSE | 21 +++++ README.md | 46 +++++++++++ admin.php | 29 +++++++ bootstrap.php | 133 ++++++++++++++++++++++++++++++ icon.svg | 8 ++ views/deploys/index.php | 175 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 443 insertions(+) create mode 100644 Controller/Admin.php create mode 100644 LICENSE create mode 100644 README.md create mode 100644 admin.php create mode 100644 bootstrap.php create mode 100644 icon.svg create mode 100644 views/deploys/index.php diff --git a/Controller/Admin.php b/Controller/Admin.php new file mode 100644 index 0000000..38e09a6 --- /dev/null +++ b/Controller/Admin.php @@ -0,0 +1,31 @@ +app->module('cockpit')->hasaccess('drone', 'manage.view')) { + return false; + } + + $data = $this->app->module('drone')->fetchDeploys(); + + return $this->render('drone:views/deploys/index.php', [ + 'deploys' => $data['deploys'] ?? [], + 'building' => $data['building'] ?? FALSE, + 'build' => $data['build'] + ]); + } + +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8f0b8b7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Paulo Gomes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..dd239b3 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# Cockpit CMS Drone Deploys Addon + +This addons is a modified version of the addon [CockpitCMS-Netlify](https://github.com/pauloamgomes/CockpitCMS-Netlify). + +It provides an integration with Drone CI promote feature to trigger a pipeline run. You can use it to deploy a website based on data in Cockpit CMS and built with a static site generator. + +## Installation + +1. Confirm that you have Cockpit CMS (Next branch) installed and working. +2. Download and extract to 'your-cockpit-docroot/addons' (e.g. cockpitcms/addons/Drone, the addon folder name must be Drone) +3. Drone icon will apear, if configuration is completed. + +## Configuration + +1. Ensure that from your Drone account you have an access token and permissions to promote a build for the project and branch, you want to use. +2. Edit Cockpit config/config.yaml and add a new entry for drone like below: + +```yaml +drone: + url: https://drone.yourserver.de + token: + owner: + project: + branch: + environment: + build: # leave empty and the addon will use the latest successfull build based on a push event + +``` + +### Permissions + +There are just two permissions: + +- **manage.view** - provides access to the Drone deploy list +- **manage.deploy** - provides access to trigger a new deploy + +## Usage + +Having the configuration defined accessing the Drone deploys page (/drone/deploys) a list of latest (limited to 50) deploys is displayed: + +To trigger a new deploy just hist the Deploy button an confirm the action. + +## Copyright and license + +Copyright 2019 Sebastian Frank under the MIT license. + diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..1a4344e --- /dev/null +++ b/admin.php @@ -0,0 +1,29 @@ +addResource('drone', [ + 'manage.view', + 'manage.deploy', +]); + +$app->on('admin.init', function () use ($app) { + if ($app->config['drone']) { + // Bind admin routes. + $this->bindClass('Drone\\Controller\\Admin', 'drone/deploys'); + + if ($app->module('cockpit')->hasaccess('drone', 'deploys.view')) { + // Add to modules menu. + $this('admin')->addMenuItem('modules', [ + 'label' => 'Drone Deploys', + 'icon' => 'drone:icon.svg', + 'route' => '/drone/deploys', + 'active' => strpos($this['route'], '/drone/deploys') === 0, + ]); + } + } +}); diff --git a/bootstrap.php b/bootstrap.php new file mode 100644 index 0000000..d27595d --- /dev/null +++ b/bootstrap.php @@ -0,0 +1,133 @@ +module('drone')->extend([ + 'fetchDeploys' => function ($limit = 50) { + $settings = $this->app->config['drone'] ?? FALSE; + + if (!$settings || !isset($settings['url'], + $settings['owner'], + $settings['project'], + $settings['environment'], + $settings['token'])) { + return []; + } + + // $url = $settings['api_url'] . '/sites/' . $settings['site_id'] . '/deploys' . '?access_token=' . $settings['access_token']; + $url = trim($settings['url'], "/") . '/api/repos/' . $settings['owner'] . '/' . $settings['project'] . '/builds'; + $branch = $settings['branch'] ? $settings['branch'] : 'master'; + + $headers = [ + 'Content-Type: application/json', + 'Accept: application/json', + 'Authorization: Bearer ' . $settings['token'] + ]; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $deploys = []; + $unfiltered_deploys = curl_exec($ch); + curl_close($ch); + $unfiltered_deploys = json_decode($unfiltered_deploys); + $build = $settings['build']; + if ($unfiltered_deploys && is_array($unfiltered_deploys)) { + foreach($unfiltered_deploys as $deploy) { + if ($deploy->{target} == $branch && ($deploy->event == 'push' || ($deploy->event == 'promote' && $deploy->deploy_to == $settings['environment']))) { + // find latest successful build + if (!$build) { + if ($deploy->event == 'push' && $deploy->status == 'success') { + $build = $deploy->number; + } else { + $build = $deploy->parent; + } + } + $limit--; + if ($limit>0) { + $deploys[] = $deploy; + } + } + } + } + + // Parse dates and check if any deploy is on building status. + $building = false; + foreach ($deploys as $idx => $deploy) { + $deploys[$idx]->building = false; + if (!in_array($deploy->status, ['success', 'failure'])) { + $building = true; + $deploys[$idx]->building = true; + } + $deploys[$idx]->created_at = date('Y-m-d H:i', $deploy->created); + $deploys[$idx]->updated_at = date('Y-m-d H:i', $deploy->updated); + if ($deploy->finished) { + $deploys[$idx]->deploy_time = $deploy->finished - $deploy->started; + } + } + + return [ + 'deploys' => $deploys, + 'building' => $building, + 'build' => $build + ]; + }, + + 'createDeploy' => function ($fromBuild) { + $settings = $this->app->config['drone']; + + if (!$fromBuild || !$settings || !isset($settings['url'], + $settings['owner'], + $settings['project'], + $settings['environment'], + $settings['token'])) { + + return array( + "error" => "missing settings" + ); + } + + $url = trim($settings['url'], "/") . '/api/repos/' . $settings['owner'] . '/' . $settings['project'] . '/builds/' . $fromBuild . '/promote?target=' . $settings['environment']; + + $headers = [ + 'Content-Type: application/json', + 'Accept: application/json', + 'Authorization: Bearer ' . $settings['token'] + ]; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + // curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + $result = curl_exec($ch); + $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + $decoded = json_decode($result); + + if ($httpcode >= 400) { + $decoded->error = "ERROR: $httpcode"; + } + + return $decoded; + }, + ]); + + // Include admin. + include_once __DIR__ . '/admin.php'; +} diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..5778bff --- /dev/null +++ b/icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/views/deploys/index.php b/views/deploys/index.php new file mode 100644 index 0000000..945e69d --- /dev/null +++ b/views/deploys/index.php @@ -0,0 +1,175 @@ + + +
+
    +
  • @lang('Drone Deploys')
  • +
+
+ +
+ + @if($app->module('cockpit')->hasaccess('drone', 'manage.view')) +
+ @if($app->module('cockpit')->hasaccess('drone', 'manage.deploy')) + + @endif +
+ +
+ +
+ +
+ @lang('Drone Deploys') +

@lang('No deploys found')

+
+ +
+
+ +

{ deploy && deploy.title }

+
+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ @lang('State') + + @lang('Build') # + + @lang('Title') + + @lang('Created') + + @lang('Updated') + + @lang('Deploy time') +
+ + { App.i18n.get(deploy.status) } + { App.i18n.get(deploy.status) } + { App.i18n.get(deploy.status) } + { App.i18n.get(deploy.status) } + + { deploy.number }{ deploy.event }: { deploy.message }{ deploy.created_at }{ deploy.updated_at }{ deploy.deploy_time }s
+
+ @endif + + + +