452 lines
14 KiB
PHP
452 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Deployment - content only
|
|
* app/Views/docs/deployment.php
|
|
*
|
|
* DevOps deployment notes for branch promotion and release preparation.
|
|
*/
|
|
?>
|
|
|
|
<p>
|
|
Deployment currently has two documented paths. For the dev environment, code
|
|
is deployed in cPanel using a Bitbucket webhook plus deploy scripts. For UAT
|
|
and live preparation, the release flow first promotes code across the
|
|
long-lived Git branches in order: <code>dev</code> → <code>test</code> →
|
|
<code>uat</code> → <code>live</code> using a server-side Python merge helper.
|
|
</p>
|
|
|
|
<div class="callout info">
|
|
<span>i</span>
|
|
<div>
|
|
<strong>Server-side merge helper</strong>
|
|
The merge script is maintained on the deployment servers inside a
|
|
<code>repo_merge</code> folder under the server user's home directory. The
|
|
examples below use dummy paths only, so update them with the real values in
|
|
that environment.
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="overview">Overview</h2>
|
|
|
|
<p>
|
|
The deployment preparation flow has three main parts:
|
|
</p>
|
|
|
|
<ol class="steps">
|
|
<li>
|
|
<strong>Dev environment auto deployment through cPanel</strong>
|
|
<p>A Bitbucket push triggers a webhook, which is received by a custom PHP script and forwarded to a deployment shell script. The same shell script also sends a Cliq channel notification after deployment.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Promote branches using the merge helper</strong>
|
|
<p>This ensures <code>test</code>, <code>uat</code>, and <code>live</code> receive the expected upstream code in order.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Run the environment deployment scripts</strong>
|
|
<p>Once branch promotion completes successfully, call the UAT or live deployment shell scripts from the deployment server.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<h2 id="dev-cpanel-flow">Dev cPanel flow</h2>
|
|
|
|
<p>
|
|
In the dev environment, deployment is handled through cPanel rather than the
|
|
branch-promotion helper. The current flow is:
|
|
</p>
|
|
|
|
<div class="mermaid-wrapper">
|
|
<div class="mermaid">
|
|
flowchart LR
|
|
A[Bitbucket push] --> B[Bitbucket webhook]
|
|
B --> C[deploy.php]
|
|
C --> D[deploy.sh]
|
|
D --> E[Application deployed]
|
|
D --> F[Cliq channel notification]
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
The webhook request is received by a custom PHP entry point, which triggers a
|
|
deployment shell script. That same shell script also posts deployment status
|
|
into Cliq using the Cliq channel message API.
|
|
</p>
|
|
|
|
<div class="callout info">
|
|
<span>i</span>
|
|
<div>
|
|
<strong>Dev-only deployment path</strong>
|
|
This cPanel webhook flow is specifically used for the dev environment. It is
|
|
separate from the branch promotion process documented below for UAT and live
|
|
preparation.
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="cpanel-deploy-scripts">cPanel deploy scripts</h2>
|
|
|
|
<p>
|
|
The cPanel deployment scripts are maintained under the cPanel web root in the
|
|
following locations:
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>Path</th><th>Purpose</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>/public_html/cicd/bashscripts/deploy.php</code></td>
|
|
<td>Receives the Bitbucket webhook request and triggers the deployment shell script.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>/public_html/cicd/bashscripts/deploy.sh</code></td>
|
|
<td>Executes the deployment steps and sends the final Cliq notification from the same script.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<p>
|
|
Operationally, the dev environment follows this chain:
|
|
</p>
|
|
|
|
<ol class="steps">
|
|
<li>
|
|
<strong>Push code to Bitbucket</strong>
|
|
<p>The push event becomes the trigger for the deployment automation.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Bitbucket sends the webhook request</strong>
|
|
<p>The webhook hits the custom PHP receiver hosted in cPanel.</p>
|
|
</li>
|
|
<li>
|
|
<strong><code>deploy.php</code> validates and forwards the action</strong>
|
|
<p>This script acts as the receiver and handoff point into the shell deployment layer.</p>
|
|
</li>
|
|
<li>
|
|
<strong><code>deploy.sh</code> performs deployment</strong>
|
|
<p>The shell script runs the actual deployment commands for the dev environment.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Cliq notification is sent</strong>
|
|
<p>The same shell script sends a channel update through the Cliq message API after deployment completes.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<h2 id="branch-promotion-flow">Branch promotion flow</h2>
|
|
|
|
<p>
|
|
The merge script performs a sequential promotion chain:
|
|
</p>
|
|
|
|
<div class="mermaid-wrapper">
|
|
<div class="mermaid">
|
|
flowchart LR
|
|
A[dev] --> B[test]
|
|
B --> C[uat]
|
|
C --> D[live]
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
The script first refreshes <code>dev</code>, then merges:
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>Step</th><th>Action</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td>1</td><td><code>git checkout dev</code> and <code>git pull origin dev</code></td></tr>
|
|
<tr><td>2</td><td>Merge <code>dev</code> into <code>test</code></td></tr>
|
|
<tr><td>3</td><td>Merge <code>test</code> into <code>uat</code></td></tr>
|
|
<tr><td>4</td><td>Merge <code>uat</code> into <code>live</code></td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="callout warning">
|
|
<span>!</span>
|
|
<div>
|
|
<strong>Important deployment gate</strong>
|
|
This branch promotion step should happen before live release execution. If
|
|
the script fails at any merge or push step, stop and resolve that issue
|
|
before continuing.
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="auto-merge-script">Auto merge script</h2>
|
|
|
|
<p>
|
|
The deployment servers keep a Python helper called
|
|
<code>auto_merge.py</code>. Below is the current script for documentation and
|
|
future reference.
|
|
</p>
|
|
|
|
<pre><code class="language-python">import subprocess
|
|
import sys
|
|
import datetime
|
|
import os
|
|
|
|
# Function to execute shell commands in a specific directory
|
|
def run_command(command, log_file, repo_path):
|
|
try:
|
|
result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True, cwd=repo_path)
|
|
log_message = f"[SUCCESS] {command}\n{result.stdout.strip()}"
|
|
except subprocess.CalledProcessError as e:
|
|
log_message = f"[ERROR] {command}\n{e.stderr.strip() if e.stderr else e.stdout.strip()}"
|
|
print(log_message, file=sys.stderr)
|
|
log_file.write(log_message + "\n")
|
|
sys.exit(1)
|
|
|
|
print(log_message)
|
|
log_file.write(log_message + "\n")
|
|
|
|
# Main function to automate merging
|
|
def auto_merge(repo_path, commit_message):
|
|
if not os.path.isdir(repo_path):
|
|
print(f"[ERROR] Invalid repository path: {repo_path}")
|
|
sys.exit(1)
|
|
|
|
script_dir = os.getcwd()
|
|
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
log_filename = os.path.join(script_dir, f"merge_log_{timestamp}.txt")
|
|
|
|
with open(log_filename, "w") as log_file:
|
|
log_file.write(f"=== Auto Merge Script Started at {timestamp} ===\n")
|
|
|
|
log_file.write("\n--- Pulling latest changes from dev ---\n")
|
|
run_command("git checkout dev", log_file, repo_path)
|
|
run_command("git pull origin dev", log_file, repo_path)
|
|
|
|
branches = [("dev", "test"), ("test", "uat"), ("uat", "live")]
|
|
|
|
for source, target in branches:
|
|
merge_msg = f"MERGE_{target.upper()}_{commit_message}"
|
|
|
|
log_file.write(f"\n--- Merging {source} -> {target} ---\n")
|
|
|
|
run_command(f"git checkout {target}", log_file, repo_path)
|
|
run_command(f"git pull origin {target}", log_file, repo_path)
|
|
run_command(f"git merge --no-ff {source} -m \"{merge_msg}\"", log_file, repo_path)
|
|
run_command(f"git push origin {target}", log_file, repo_path)
|
|
|
|
log_file.write("\n=== Auto Merge Completed Successfully ===\n")
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) != 3:
|
|
print("Usage: python auto_merge.py <REPO_PATH> '<COMMIT_MESSAGE_SUFFIX>'")
|
|
print("Usage Example: python auto_merge.py './nhance' '<COMMIT_MESSAGE_SUFFIX>'")
|
|
print("Usage Example: python auto_merge.py './nhance-enrollment' '<COMMIT_MESSAGE_SUFFIX>'")
|
|
sys.exit(1)
|
|
|
|
repo_path = sys.argv[1].strip()
|
|
commit_suffix = sys.argv[2].strip().upper()
|
|
|
|
auto_merge(repo_path, commit_suffix)
|
|
</code></pre>
|
|
|
|
<h2 id="script-usage">Script usage</h2>
|
|
|
|
<p>
|
|
The script is typically run from the server-side <code>repo_merge</code>
|
|
directory. Use a dummy home path like the example below and replace it with
|
|
the real deployment user home directory.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>Dummy path</th><th>Description</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>/home/deploy-user/repo_merge</code></td>
|
|
<td>Example folder where <code>auto_merge.py</code> is stored on UAT or live.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>/home/deploy-user/nhance</code></td>
|
|
<td>Example repository checkout path passed as the first script argument.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>RELEASE_2026_05_12</code></td>
|
|
<td>Example commit message suffix appended into merge commit messages.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<pre><code class="language-bash">cd /home/deploy-user/repo_merge
|
|
python3 auto_merge.py "/home/deploy-user/nhance" "RELEASE_2026_05_12"</code></pre>
|
|
|
|
<p>
|
|
Usage format:
|
|
</p>
|
|
|
|
<pre><code class="language-bash">python3 auto_merge.py "<REPO_PATH>" "<COMMIT_MESSAGE_SUFFIX>"</code></pre>
|
|
|
|
<p>
|
|
The commit message suffix is converted to uppercase by the script. For each
|
|
merge step, the script generates messages like:
|
|
</p>
|
|
|
|
<pre><code class="language-text">MERGE_TEST_RELEASE_2026_05_12
|
|
MERGE_UAT_RELEASE_2026_05_12
|
|
MERGE_LIVE_RELEASE_2026_05_12</code></pre>
|
|
|
|
<h2 id="log-output">Log output</h2>
|
|
|
|
<p>
|
|
Every run creates a timestamped log file in the directory where the script is
|
|
executed. The log captures both successful commands and the exact step that
|
|
failed.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>Behavior</th><th>Details</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Log filename</td>
|
|
<td><code>merge_log_YYYY-MM-DD_HH-MM-SS.txt</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Success logging</td>
|
|
<td>Writes <code>[SUCCESS]</code> plus command output.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Error logging</td>
|
|
<td>Writes <code>[ERROR]</code>, prints to stderr, and exits immediately.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2 id="operational-notes">Operational notes</h2>
|
|
|
|
<ol class="steps">
|
|
<li>
|
|
<strong>Run the script from the deployment helper folder</strong>
|
|
<p>This keeps the generated merge log files in one predictable location.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Make sure the repo path is correct before starting</strong>
|
|
<p>The script exits immediately if the provided repository directory does not exist.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Do not continue deployment on merge failure</strong>
|
|
<p>If checkout, pull, merge, or push fails for any branch, stop and resolve the issue first.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Use a meaningful commit suffix</strong>
|
|
<p>Choose a release identifier that makes merge history easy to trace later.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<div class="callout success">
|
|
<span>+</span>
|
|
<div>
|
|
<strong>Recommended practice</strong>
|
|
Treat this script as the first gate in the deployment flow for UAT and live
|
|
releases. Once branch promotion completes successfully, continue with the
|
|
environment-specific deployment steps.
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="code-move-scripts">Code move scripts</h2>
|
|
|
|
<div class="callout danger">
|
|
<span>!</span>
|
|
<div>
|
|
<strong>Strict warning before running these steps</strong>
|
|
Before calling the UAT or live deployment shell scripts, any required
|
|
database changes and environment variable changes must be done manually.
|
|
Do not assume these scripts handle DB updates, migrations, secrets, or
|
|
environment-specific configuration automatically.
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
After the previous Python merge script completes successfully, the next step
|
|
is to run the environment-specific deployment shell scripts from the server.
|
|
These scripts are available only on the deployment servers and are not stored
|
|
in this repository.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>Script</th><th>Environment / Purpose</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>crm_deployment.sh</code></td>
|
|
<td>Live CRM code move script.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>enrolment_depoloyment.sh</code></td>
|
|
<td>Live enrolment code move script.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>uat_crm_deployment.sh</code></td>
|
|
<td>UAT CRM code move script.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>uat_enrolment_depoloyment.sh</code></td>
|
|
<td>UAT enrolment code move script.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<p>
|
|
The execution rule is simple:
|
|
</p>
|
|
|
|
<ol class="steps">
|
|
<li>
|
|
<strong>Run <code>auto_merge.py</code> first</strong>
|
|
<p>This completes the required branch promotion chain before any server-side code move starts.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Choose the scripts based on target environment</strong>
|
|
<p>For UAT, call the <code>uat_*</code> scripts. For live, call the non-UAT deployment scripts.</p>
|
|
</li>
|
|
<li>
|
|
<strong>Execute the relevant application scripts</strong>
|
|
<p>Run the CRM and enrolment deployment scripts that match the environment being released.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<pre><code class="language-bash"># UAT example
|
|
python3 auto_merge.py "/home/deploy-user/nhance" "RELEASE_2026_05_12"
|
|
./uat_crm_deployment.sh
|
|
./uat_enrolment_depoloyment.sh
|
|
|
|
# LIVE example
|
|
python3 auto_merge.py "/home/deploy-user/nhance" "RELEASE_2026_05_12"
|
|
./crm_deployment.sh
|
|
./enrolment_depoloyment.sh</code></pre>
|
|
|
|
<div class="callout warning">
|
|
<span>!</span>
|
|
<div>
|
|
<strong>Server-only scripts</strong>
|
|
These deployment scripts exist only on the deployment servers. Keep this
|
|
documentation as the operational reference, but do not expect to find the
|
|
actual shell files inside this repository.
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="manual-fallback">Manual fallback</h2>
|
|
|
|
<p>
|
|
If the dev auto-deploy flow fails, manual deployment is still available through
|
|
cPanel Git Version Control. That manual apply option should be used as the
|
|
fallback path when the webhook receiver or deploy script does not complete
|
|
successfully.
|
|
</p>
|
|
|
|
<div class="callout warning">
|
|
<span>!</span>
|
|
<div>
|
|
<strong>Fallback path</strong>
|
|
Keep cPanel Git Version Control enabled for the repository so the team can
|
|
manually apply the latest revision whenever the automatic cPanel deployment
|
|
path fails.
|
|
</div>
|
|
</div>
|