Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion bin/checkScripts/downloadCert.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ const fs = require('fs');
const tls = require('tls');
const net = require('net');

/**
* Downloads the TLS certificate from a remote host and saves it as a PEM-encoded
* file. Establishes a TLS connection, extracts the peer certificate, encodes it
* in Base64 PEM format, and writes it synchronously to the specified output path.
*
* @async
* @param {string} host - The hostname to connect to (e.g. "github.com").
* @param {number} [port=443] - The TCP port to connect on. Defaults to 443 (HTTPS).
* @param {string} outputFile - Absolute or relative file path where the PEM
* certificate will be written.
* @returns {Promise<void>} Resolves when the certificate has been written
* successfully; rejects with an Error on connection
* failure or if no certificate is returned.
*
* @example
* const downloadCert = require('./downloadCert');
* await downloadCert('github.com', 443, './certs/github.pem');
*/
async function downloadCert(host, port = 443, outputFile) {
return new Promise((resolve, reject) => {
const socket = tls.connect(port, host, {}, () => {
Expand All @@ -23,4 +41,4 @@ async function downloadCert(host, port = 443, outputFile) {
}

// Export function
module.exports = downloadCert;
module.exports = downloadCert;
22 changes: 20 additions & 2 deletions bin/checkScripts/runDownloadCert.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,27 @@ const fs = require('fs');
const path = require('path');
const downloadCert = require('./downloadCert');

// Output certificate path
/**
* Absolute path where the downloaded certificate will be stored.
* Resolves to `download_ca_cert.pem` in the same directory as this script.
*
* @type {string}
*/
const OUTPUT_FILE = path.resolve(__dirname, 'download_ca_cert.pem');

/**
* Entry point: backs up any pre-existing certificate file, then fetches the
* current TLS certificate from github.com on port 443 and saves it to
* {@link OUTPUT_FILE}.
*
* Backup naming convention: `<OUTPUT_FILE>.<unix-timestamp-ms>.bak`
*
* Exits with code 1 if the download or file operations fail so that CI
* pipelines can detect and surface the error.
*
* @async
* @returns {Promise<void>}
*/
(async () => {
try {
// Backup existing certs
Expand All @@ -23,4 +41,4 @@ const OUTPUT_FILE = path.resolve(__dirname, 'download_ca_cert.pem');
console.error('Error downloading certificate:', err);
process.exit(1); // Fail workflow if download fails
}
})();
})();
25 changes: 25 additions & 0 deletions cleanup-old-workflows.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
<#
.SYNOPSIS
Interactively removes stale GitHub Actions workflow files from the
repository.

.DESCRIPTION
Iterates over a predefined list of file paths (relative to the repository
root) that are no longer needed. Glob patterns are supported, so entries
like "*.bak" match multiple files.

For each matched file the script prompts the operator for confirmation
before deletion, ensuring no files are removed without explicit consent.

.PARAMETER (none)
This script accepts no parameters.

.EXAMPLE
.\cleanup-old-workflows.ps1
# Prompts for each stale workflow file and deletes those confirmed with "Y".

.NOTES
- Run from the repository root so that relative paths resolve correctly.
- Files not found on disk are reported but do not cause an error.
- PSAvoidUsingWriteHost is suppressed intentionally for interactive output.
#>
param()
# Files to remove (relative to repo root)
$filesToDelete = @(
Expand Down
33 changes: 33 additions & 0 deletions create-download-cert.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
<#
.SYNOPSIS
Generates GitHub Actions workflow files for certificate downloading and
PowerShell CI into .github\workflows\.

.DESCRIPTION
Creates (or overwrites) two workflow YAML files:

1. download-cert.yml – A manually-triggered (workflow_dispatch) workflow
that accepts a certificate URL and filename as inputs, downloads the
certificate with curl, and uploads it as a build artifact.

2. powershell-ci.yml – A push/PR/manual CI workflow that runs on a
3-OS matrix (ubuntu-latest, windows-latest, macos-latest). Each job:
a. Installs PSScriptAnalyzer and Pester from the PSGallery.
b. Runs PSScriptAnalyzer at Error/Warning severity, filtering for
security-related rules.
c. Executes Pester tests found in ./tests (if the directory exists).

The .github\workflows directory is created if it does not already exist.

.PARAMETER (none)
This script accepts no parameters.

.EXAMPLE
.\create-download-cert.ps1
# Writes both workflow files and confirms their paths on success.

.NOTES
- Run from the repository root.
- Existing workflow files with the same names will be overwritten silently.
- PSAvoidUsingWriteHost is suppressed intentionally for status output.
#>
param()
# Workflow folder
$workflowDir = ".github\workflows"
Expand Down
33 changes: 33 additions & 0 deletions create-powershell-ci.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
<#
.SYNOPSIS
Generates the PowerShell CI GitHub Actions workflow file.

.DESCRIPTION
Creates (or overwrites) .github\workflows\powershell-ci.yml with a
multi-OS CI pipeline that:

- Triggers on pushes and pull requests to the main branch, and also
supports manual dispatch (workflow_dispatch).
- Runs on a 3-OS matrix: ubuntu-latest, windows-latest, macos-latest.
- Installs PSScriptAnalyzer and Pester from the PowerShell Gallery.
- Executes a PSScriptAnalyzer security scan (Error/Warning severity,
security rules only) and fails the job if any issues are detected.
- Runs Pester tests from the ./tests directory (if present) and fails
the job if any tests fail.

The .github\workflows directory is created automatically if absent.

.PARAMETER (none)
This script accepts no parameters.

.EXAMPLE
.\create-powershell-ci.ps1
# Writes powershell-ci.yml and prints the file path on success.

.NOTES
- Run from the repository root so the workflow path resolves correctly.
- This script generates only powershell-ci.yml. Use
create-download-cert.ps1 to also generate download-cert.yml.
- Existing files with the same name will be overwritten silently.
- PSAvoidUsingWriteHost is suppressed intentionally for status output.
#>
param()

# Workflow folder
Expand Down
32 changes: 32 additions & 0 deletions run-workflow-manually.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
<#
.SYNOPSIS
Manually triggers a GitHub Actions workflow via the repository dispatch API.

.DESCRIPTION
Lists all *.yml workflow files found in .github/workflows, prompts the
operator to choose one, then uses the GitHub REST API
(POST /repos/{owner}/{repo}/actions/workflows/{workflow}/dispatches)
to trigger a workflow_dispatch event on the main branch.

Authentication is handled through the GITHUB_TOKEN environment variable,
which must hold a Personal Access Token (PAT) with the repo and workflow
scopes.

.PARAMETER (none)
This script accepts no parameters; all inputs are collected interactively
at runtime.

.EXAMPLE
$env:GITHUB_TOKEN = "ghp_..."
.\run-workflow-manually.ps1
# Lists available workflows, prompts for selection and confirmation,
# then dispatches the chosen workflow on the main branch.

.NOTES
- GITHUB_TOKEN must be set before running this script.
- The target repository is hard-coded as Ruh-Al-Tarikh/System-Automation-Hub.
Update $owner and $repo variables to target a different repository.
- The workflow must define a workflow_dispatch trigger; otherwise GitHub
will return a 422 Unprocessable Entity error.
- PSAvoidUsingWriteHost is suppressed intentionally for interactive output.
#>
param()

# Hardcoded GitHub Personal Access Token (with repo/workflows scope)
Expand Down
32 changes: 30 additions & 2 deletions scripts/fetch-profile.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,32 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
<#
.SYNOPSIS
Fetches and displays a GitHub user's public profile information.

.DESCRIPTION
Calls the GitHub REST API (GET /users/{username}) and prints a formatted
summary of the account's public data including login, name, bio, public
repository count, follower/following counts, and profile URL.

No authentication is required; the script uses the unauthenticated public
endpoint, which is subject to GitHub's unauthenticated rate limit
(60 requests per hour per IP).

.PARAMETER Username
The GitHub username to look up. Defaults to "octocat" when not supplied.

.EXAMPLE
.\scripts\fetch-profile.ps1
# Fetches the profile for the default user "octocat".

.EXAMPLE
.\scripts\fetch-profile.ps1 -Username "torvalds"
# Fetches the public profile for the user "torvalds".

.NOTES
Outputs coloured text via Write-Host for readability in interactive
terminals. PSAvoidUsingWriteHost is suppressed intentionally.
#>
param (
[Parameter(Mandatory=$false)]
[string]$Username = "octocat"
Expand All @@ -22,4 +50,4 @@ try {
Write-Host "---------------------------"
} catch {
Write-Host "❌ Error: Could not fetch profile for '$Username'. Details: $($_.Exception.Message)" -ForegroundColor Red
}
}
25 changes: 23 additions & 2 deletions scripts/init.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")]
<#
.SYNOPSIS
Initialises the System Automation Hub environment.

.DESCRIPTION
Prints a startup banner confirming that the Automation Hub has been
initialised, along with the machine name and operator username.
Intended to be dot-sourced or invoked at the beginning of an automation
session to provide a clear, consistent startup message.

.EXAMPLE
.\scripts\init.ps1
# Output:
# Automation Hub Initialized
# Machine: RIZWAN
# User: ruhal

.NOTES
PSAvoidUsingWriteHost is suppressed intentionally; this script targets
interactive console sessions where Write-Host colour output is desirable.
#>
param()
Write-Host 'Automation Hub Initialized' -ForegroundColor Cyan
Write-Host 'Machine:' RIZWAN
Write-Host 'User:' ruhal
Write-Host 'User:' ruhal
32 changes: 31 additions & 1 deletion scripts/ui_improvements.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
"""
ui_improvements.py
------------------
Applies CSS styling improvements to the System Automation Hub website using
headless Chrome via Selenium WebDriver.

This script:
1. Launches a headless Chrome browser managed by webdriver-manager.
2. Navigates to the deployed GitHub Pages site.
3. Injects a <style> element that overrides the page's default typography and
header appearance.
4. Prints a confirmation message and exits cleanly.

Dependencies:
selenium - Browser automation framework.
webdriver-manager - Automatic ChromeDriver version management.

Usage:
python scripts/ui_improvements.py

Notes:
- Requires Google Chrome to be installed on the host machine.
- The script runs in headless mode, so no browser window is displayed.
- Injected styles are ephemeral (session-only); they do not persist to the
source repository. Use this script for visual smoke-testing only.
"""

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
import time

# Configure Chrome to run without a GUI (headless mode)
chrome_options = Options()
chrome_options.add_argument("--headless=new")
chrome_options.add_argument("--disable-gpu")
Expand All @@ -16,8 +44,10 @@
site = "https://ruh-al-tarikh.github.io/System-Automation-Hub/"
driver.get(site)

# Allow the page to fully render before injecting styles
time.sleep(3)

# Inject a <style> element into <head> to improve typography and header contrast
driver.execute_script("""
var style = document.createElement('style');
style.innerHTML = `
Expand All @@ -29,4 +59,4 @@

print("UI automation executed")

driver.quit()
driver.quit()
Loading
Loading