From 5cda55df86833d0fc40af9d7f6fd5defae0806c1 Mon Sep 17 00:00:00 2001 From: Alex Finnarn Date: Thu, 11 Apr 2024 15:17:15 -0400 Subject: [PATCH 1/5] initial commit for deployment status form --- .../src/Form/ContentReleaseStatusForm.php | 156 ++++++++++++++++++ .../va_gov_content_release.routing.yml | 8 + 2 files changed, 164 insertions(+) create mode 100644 docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php diff --git a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php new file mode 100644 index 0000000000..bf26933f29 --- /dev/null +++ b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php @@ -0,0 +1,156 @@ +client = new Client(); + // $this->client->authenticate($token, null, AuthMethod::ACCESS_TOKEN); + $this->settings = $settings; + $this->httpClient = $client; + } + + public static function create(ContainerInterface $container) { + return new static( + $container->get('settings'), + $container->get('http_client') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId(): string { + return 'va_gov_content_release_next_simple'; + } + + /** + * {@inheritdoc} + * + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function buildForm( + array $form, + FormStateInterface $form_state + ): array { + // Check if it is in business hours to release content. + RunsDuringBusinessHours: + + + // Make a call to GitHub using http_client service to check workflow runs. + // $response = $this->httpClient->request('GET', 'https://api.github.com/repos/department-of-veterans-affairs/content-build/actions/workflows'); + $response = $this->httpClient->request('GET', + 'https://api.github.com/repos/department-of-veterans-affairs/content-build/actions/workflows/11158212/runs'); + $data = json_decode($response->getBody()->getContents()); + + // Get the latest run. + $latest_run = $data->workflow_runs[0]; + + // Get start of the run. + $start = $latest_run->run_started_at; + + // Calculate the duration of the run so far. + $duration = (time() - strtotime($start)); + + $form['content_release_status_block'] = [ + '#theme' => 'status_report_grouped', + '#grouped_requirements' => [ + [ + 'title' => $this->t('Latest Content Release Run'), + 'type' => 'content-release-status', + 'items' => [ + 'status' => [ + 'title' => $this->t('Status'), + 'value' => $latest_run->status, + ], + 'run_start' => [ + 'title' => $this->t('Last Run Start Time'), + 'value' => date('Y-m-d H:i:s', strtotime($start)), + ], + 'duration' => [ + 'title' => $this->t('Duration'), + 'value' => gmdate('H:i:s', $duration), + ], + ], + ], + ], + ]; + + $foo = 'bar'; + + // Add markup for a message before the form fields. + $form['message'] = [ + '#markup' => $this->t('Any content you set to Published will...this will be the intro message part.'), + ]; + + $form['acknowledgement'] = [ + '#type' => 'checkbox', + '#title' => $this->t('I understand that all VA content set to Published will go live once the release is finished.'), + '#required' => TRUE, + ]; + + $form['actions'] = [ + '#type' => 'actions', + 'submit' => [ + '#type' => 'submit', + '#value' => $this->t('Release Content'), + ], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm( + array &$form, + FormStateInterface $form_state + ): void { + // @todo Validate the form here. + // Example: + // @code + // if (mb_strlen($form_state->getValue('message')) < 10) { + // $form_state->setErrorByName( + // 'message', + // $this->t('Message should be at least 10 characters.'), + // ); + // } + // @endcode + } + + /** + * {@inheritdoc} + */ + public function submitForm( + array &$form, + FormStateInterface $form_state + ): void { + $this->messenger()->addStatus($this->t('The message has been sent.')); + $form_state->setRedirect(''); + } + +} diff --git a/docroot/modules/custom/va_gov_content_release/va_gov_content_release.routing.yml b/docroot/modules/custom/va_gov_content_release/va_gov_content_release.routing.yml index 5aa5572ed1..92b5011e94 100644 --- a/docroot/modules/custom/va_gov_content_release/va_gov_content_release.routing.yml +++ b/docroot/modules/custom/va_gov_content_release/va_gov_content_release.routing.yml @@ -38,3 +38,11 @@ va_gov_content_release.frontend_version_autocomplete: _format: json requirements: _permission: "va gov deploy content build" + +va_gov_content_release.check_status_form: + path: '/admin/content/deploy/check-status' + defaults: + _title: 'Content Release Status' + _form: '\Drupal\va_gov_content_release\Form\ContentReleaseStatusForm' + requirements: + _permission: 'va gov deploy content build' From 13b0d29a3d2dc27147baa058b912e62090b3ab61 Mon Sep 17 00:00:00 2001 From: Alex Finnarn Date: Mon, 22 Apr 2024 13:46:16 -0400 Subject: [PATCH 2/5] update content release status form --- .../src/Form/ContentReleaseStatusForm.php | 73 +++++++++++++++---- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php index bf26933f29..dd3a0a7143 100644 --- a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php +++ b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php @@ -2,6 +2,8 @@ namespace Drupal\va_gov_content_release\Form; +use Drupal\Component\Datetime\TimeInterface; +use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Site\Settings; @@ -17,26 +19,47 @@ class ContentReleaseStatusForm extends FormBase { use RunsDuringBusinessHours; /** + * The http client service. + * * @var \GuzzleHttp\Client */ private ClientInterface $httpClient; /** + * The Drupal settings service. + * * @var \Drupal\Core\Site\Settings */ private Settings $settings; - public function __construct(Settings $settings, ClientInterface $client) { - // $this->client = new Client(); - // $this->client->authenticate($token, null, AuthMethod::ACCESS_TOKEN); + /** + * The Content Release Status Form constructor. + * + * @param \Drupal\Core\Site\Settings $settings + * The settings service. + * @param \GuzzleHttp\ClientInterface $client + * The http client service. + * @param \Drupal\Component\Datetime\TimeInterface $time + * The time service. + * @param \Drupal\Core\Datetime\DateFormatterInterface $dateFormatter + * The date formatter service. + */ + public function __construct(Settings $settings, ClientInterface $client, TimeInterface $time, DateFormatterInterface $dateFormatter) { $this->settings = $settings; $this->httpClient = $client; + $this->time = $time; + $this->dateFormatter = $dateFormatter; } + /** + * {@inheritdoc} + */ public static function create(ContainerInterface $container) { return new static( $container->get('settings'), - $container->get('http_client') + $container->get('http_client'), + $container->get('datetime.time'), + $container->get('date.formatter'), ); } @@ -59,19 +82,18 @@ public function buildForm( // Check if it is in business hours to release content. RunsDuringBusinessHours: - // Make a call to GitHub using http_client service to check workflow runs. - // $response = $this->httpClient->request('GET', 'https://api.github.com/repos/department-of-veterans-affairs/content-build/actions/workflows'); + // $response = $this->httpClient->request('GET', 'https://api.github.com/repos/department-of-veterans-affairs/content-build/actions/workflows'); $response = $this->httpClient->request('GET', 'https://api.github.com/repos/department-of-veterans-affairs/content-build/actions/workflows/11158212/runs'); $data = json_decode($response->getBody()->getContents()); // Get the latest run. $latest_run = $data->workflow_runs[0]; - + // Get last run. + $last_run = $data->workflow_runs[1]; // Get start of the run. $start = $latest_run->run_started_at; - // Calculate the duration of the run so far. $duration = (time() - strtotime($start)); @@ -82,8 +104,12 @@ public function buildForm( 'title' => $this->t('Latest Content Release Run'), 'type' => 'content-release-status', 'items' => [ + 'last_run' => [ + 'title' => $this->t('Previous Build Status'), + 'value' => $last_run->status, + ], 'status' => [ - 'title' => $this->t('Status'), + 'title' => $this->t('Current Build Status'), 'value' => $latest_run->status, ], 'run_start' => [ @@ -94,29 +120,46 @@ public function buildForm( 'title' => $this->t('Duration'), 'value' => gmdate('H:i:s', $duration), ], + 'during_business_hours' => [ + 'title' => $this->t('During Business Hours'), + 'value' => $this->isCurrentlyDuringBusinessHours() ? 'Yes' : 'No', + ], ], ], ], ]; - $foo = 'bar'; + $form['request_release'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Request Content Release'), + ]; + + $message = << +
  • Refresh the page to update the Content Release Status information.
  • +
  • Any content you set to Published will go live once the release is finished.
  • +
  • You cannot release content manually during business hours so the release button is disabled during those times.
  • + +
    +HTML; // Add markup for a message before the form fields. - $form['message'] = [ - '#markup' => $this->t('Any content you set to Published will...this will be the intro message part.'), + $form['request_release']['message'] = [ + '#markup' => $message, ]; - $form['acknowledgement'] = [ + $form['request_release']['acknowledgement'] = [ '#type' => 'checkbox', '#title' => $this->t('I understand that all VA content set to Published will go live once the release is finished.'), '#required' => TRUE, ]; - $form['actions'] = [ + $form['request_release']['actions'] = [ '#type' => 'actions', + '#disabled' => $this->isCurrentlyDuringBusinessHours(), 'submit' => [ '#type' => 'submit', - '#value' => $this->t('Release Content'), + '#value' => $this->t('Release Content Request'), ], ]; From ba7137dd022fde2eb0052b7f88269c1355a46b38 Mon Sep 17 00:00:00 2001 From: Alex Finnarn Date: Mon, 22 Apr 2024 14:04:58 -0400 Subject: [PATCH 3/5] also disable the checkbox if during business hours --- .../va_gov_content_release/src/Form/ContentReleaseStatusForm.php | 1 + 1 file changed, 1 insertion(+) diff --git a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php index dd3a0a7143..a6791c2c65 100644 --- a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php +++ b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php @@ -152,6 +152,7 @@ public function buildForm( '#type' => 'checkbox', '#title' => $this->t('I understand that all VA content set to Published will go live once the release is finished.'), '#required' => TRUE, + '#disabled' => $this->isCurrentlyDuringBusinessHours(), ]; $form['request_release']['actions'] = [ From 79d11aa9991ccd368e1f7167599575339c4e6229 Mon Sep 17 00:00:00 2001 From: Alex Finnarn Date: Thu, 25 Apr 2024 16:34:35 -0400 Subject: [PATCH 4/5] add continous build script, update form for triggering build --- .../src/Form/ContentReleaseStatusForm.php | 54 ++++++++++--------- scripts/continuous_build.sh | 46 ++++++++++++++++ 2 files changed, 74 insertions(+), 26 deletions(-) create mode 100644 scripts/continuous_build.sh diff --git a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php index a6791c2c65..6f7e99a0aa 100644 --- a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php +++ b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php @@ -15,9 +15,12 @@ * Provides a VA.gov Content Release form. */ class ContentReleaseStatusForm extends FormBase { - use RunsDuringBusinessHours; + const OWNER = 'department-of-veterans-affairs'; + const REPO = 'next-build'; + const WORKFLOW_ID = 'content-release.yml'; + /** * The http client service. * @@ -79,13 +82,10 @@ public function buildForm( array $form, FormStateInterface $form_state ): array { - // Check if it is in business hours to release content. - RunsDuringBusinessHours: // Make a call to GitHub using http_client service to check workflow runs. - // $response = $this->httpClient->request('GET', 'https://api.github.com/repos/department-of-veterans-affairs/content-build/actions/workflows'); $response = $this->httpClient->request('GET', - 'https://api.github.com/repos/department-of-veterans-affairs/content-build/actions/workflows/11158212/runs'); + 'https://api.github.com/repos/'. self::OWNER .'/'. self::REPO .'/actions/workflows/'. self::WORKFLOW_ID .'/runs'); $data = json_decode($response->getBody()->getContents()); // Get the latest run. @@ -169,32 +169,34 @@ public function buildForm( /** * {@inheritdoc} - */ - public function validateForm( - array &$form, - FormStateInterface $form_state - ): void { - // @todo Validate the form here. - // Example: - // @code - // if (mb_strlen($form_state->getValue('message')) < 10) { - // $form_state->setErrorByName( - // 'message', - // $this->t('Message should be at least 10 characters.'), - // ); - // } - // @endcode - } - - /** - * {@inheritdoc} + * @throws \GuzzleHttp\Exception\GuzzleException */ public function submitForm( array &$form, FormStateInterface $form_state ): void { - $this->messenger()->addStatus($this->t('The message has been sent.')); - $form_state->setRedirect(''); + + try { + // Trigger the content release. + $this->httpClient->request('POST', + 'https://api.github.com/repos/'. self::OWNER .'/'. self::REPO .'/actions/workflows/'. self::WORKFLOW_ID .'/dispatches', + [ + 'headers' => [ + 'Accept' => 'application/vnd.github.v3+json', + 'Authorization' => 'token ' . $this->settings->get('va_gov_content_release.github_token'), + ], + 'json' => [ + 'ref' => 'main', + // Can add inputs to the workflow, if needed. + // 'inputs' => [ + // 'release' => 'true', + // ], + ], + ]); + } catch (Exception $exception) { + $this->messenger()->addError($this->t('There was an error triggering the content release.')); + $this->logger->error('Error triggering content release: @error', ['@error' => $exception->getMessage()]); + } } } diff --git a/scripts/continuous_build.sh b/scripts/continuous_build.sh new file mode 100644 index 0000000000..6cd0a31042 --- /dev/null +++ b/scripts/continuous_build.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# This script is used to build the project continuously. +# The project is build on GitHub using a Workflow. + +# The continuous build only runs during business hours. +export TZ="America/New_York" +echo "Current time: $(date)" +if [ $(date +%u) -lt 6 ] && [ $(date +%H) -ge 8 ] && [ $(date +%H) -lt 20 ]; then + echo "It is during business hours. Proceeding with the build." +else + echo "It is not during business hours. Exiting build." + exit 0 +fi + +# Make request to Github to check if a workflow is running. +# If a workflow is running, then exit the script. +# If no workflow is running, then build the project. +OWNER="department-of-veterans-affairs" +REPO="next-build" +WORKFLOW_ID="content_release.yml" + +# Get the Content Release workflow status. +WORKFLOW_STATUS=$(curl -L \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/$OWNER/$REPO/actions/workflows/$WORKFLOW_ID/runs | jq '.workflow_runs[0].status' | tr -d '"') +echo "Workflow status: ${WORKFLOW_STATUS}" + +if [ "$WORKFLOW_STATUS" == "in_progress" ]; then + echo "Workflow is running. Exiting the script." + exit 0 +else + echo "Workflow is not running. Building the project." +fi + +# Make call to trigger the workflow. +# The workflow is triggered by making a POST request to the GitHub API. +curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${GH_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/$OWNER/$REPO/actions/workflows/$WORKFLOW_ID/dispatches \ + -d '{"ref":"main"}' + From 4ab98e513354d3a7dc78e85dc983e74d04ee8173 Mon Sep 17 00:00:00 2001 From: Alex Finnarn Date: Mon, 29 Apr 2024 15:48:28 -0400 Subject: [PATCH 5/5] update content release status form --- .../src/Form/ContentReleaseStatusForm.php | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php index 6f7e99a0aa..d127763778 100644 --- a/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php +++ b/docroot/modules/custom/va_gov_content_release/src/Form/ContentReleaseStatusForm.php @@ -17,9 +17,11 @@ class ContentReleaseStatusForm extends FormBase { use RunsDuringBusinessHours; + // @todo Need proper permissions and token sorted out. + // I tested on my personal repo with a personal token. const OWNER = 'department-of-veterans-affairs'; const REPO = 'next-build'; - const WORKFLOW_ID = 'content-release.yml'; + const WORKFLOW_ID = 'content_release.yml'; /** * The http client service. @@ -85,7 +87,7 @@ public function buildForm( // Make a call to GitHub using http_client service to check workflow runs. $response = $this->httpClient->request('GET', - 'https://api.github.com/repos/'. self::OWNER .'/'. self::REPO .'/actions/workflows/'. self::WORKFLOW_ID .'/runs'); + 'https://api.github.com/repos/' . self::OWNER . '/' . self::REPO . '/actions/workflows/' . self::WORKFLOW_ID . '/runs'); $data = json_decode($response->getBody()->getContents()); // Get the latest run. @@ -169,6 +171,7 @@ public function buildForm( /** * {@inheritdoc} + * * @throws \GuzzleHttp\Exception\GuzzleException */ public function submitForm( @@ -179,23 +182,22 @@ public function submitForm( try { // Trigger the content release. $this->httpClient->request('POST', - 'https://api.github.com/repos/'. self::OWNER .'/'. self::REPO .'/actions/workflows/'. self::WORKFLOW_ID .'/dispatches', + 'https://api.github.com/repos/' . self::OWNER . '/' . self::REPO . '/actions/workflows/' . self::WORKFLOW_ID . '/dispatches', [ 'headers' => [ 'Accept' => 'application/vnd.github.v3+json', - 'Authorization' => 'token ' . $this->settings->get('va_gov_content_release.github_token'), + // @todo Replace with proper token. + 'Authorization' => 'token ' . $this->settings->get('va_cms_bot_github_auth_token'), ], 'json' => [ 'ref' => 'main', - // Can add inputs to the workflow, if needed. - // 'inputs' => [ - // 'release' => 'true', - // ], + // Can add 'inputs' => [] to the workflow, if needed. ], ]); - } catch (Exception $exception) { + } + catch (Exception $exception) { $this->messenger()->addError($this->t('There was an error triggering the content release.')); - $this->logger->error('Error triggering content release: @error', ['@error' => $exception->getMessage()]); + // @todo Add more logging? } }