Initial release

This commit is contained in:
amc 2025-02-25 00:02:36 +00:00
parent 965ee2d187
commit 5dbc75d216
3 changed files with 349 additions and 2 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
/.vscode
/lib/*
/modules/*
/psmodule/*
/tmp/*
/config/*
/data/*

View file

@ -1,3 +1,71 @@
# processing-bootstrap
# processing bootstrap
Simple folder-based script execution environment designed for modularity.
Suitable for the automation tasks as a better alternative to a Behemoth-class monolithic scripts.
Script execution order is a simple alphanumeric sort by a file name. Scripts are executed from a directories with '\d+-\w+' name pattern
# Usage example
```
- 10-init \
10-load-environment.ps1
- 20-functions \
server-connection.ps1
data-mangling.ps1
- 50-exec \
10-connect-server.ps1
30-doing-the-needful.ps1
99-disconnect-server.ps1
- config \
server-credentials.json
- data \
salsa-scheme.xml
start.ps1
```
# Directory structure
### config
Place configuration files here, like server names, credentials, apikeys etc.
Files with .json extension would be autoloaded under $BS.config.filename variable
### data
Use it for the work data like temp files and so on.
### psmodule
If you need to load specific PS modules (not installed in the system or in the user profile) then you can copy them there and they would be automatically imported by PowerShell itself through $PSModules
### lib
.NET assemblies would be autoloaded from here, place the DLLs in the net4 directory for PS5 and net7 for PwSh7+
### submodule
Place *git* submodules here
### docs
You do write the docs?
# Using in the other project tracked by git
Consider using `git submodule`:
```sh
mkdir submodule
cd submodule
git submodule add https://$SITENAME/processing-bootstrap.git
```
Simple folder-based script execution environment designed for modularity

272
start.ps1 Normal file
View file

@ -0,0 +1,272 @@
<#
.Synopsis
Simple folder based script execution environment.
.DESCRIPTION
Simple folder based script execution environment designed for modularity.
Suitable for the automation tasks as a better alternative to a Behemoth-class monolithic scripts.
Script execution order is a simple alphanumeric sort by the file name.
.EXAMPLE
./start.ps1
# execute in the current directory
.EXAMPLE
./start.ps1 -MainScriptRoot /mnt/tukayyid
# expect everything are located at the specified path
.EXAMPLE
./start.ps1 -PSModule /data/ironhold/psmodule
# load the PS modules form the specified path
#>
[CmdletBinding()]
param (
# Path where the scripts and data for the execution are located
$Root = $PSScriptRoot,
# Configuration storage path
$Config,
# Data storage path
$Data,
# .NET assemblies directory
$Lib,
# PowerShell modules path (would be added to PSModulesPath for the auto-magik)
$PSModule,
# Custom arguments
[Parameter(
ValueFromRemainingArguments=$true
)]
$CustomArgs
)
# this is a global variable which would be accessbile anywhere in the session
$global:BS = @{
Path = @{
PSScriptRoot = $PSScriptRoot
Root = $null
}
Config = @{
}
}
# workaround for the interactive script launch (ie no defined $Root)
if ($PSBoundParameters['Root']) {
$global:BS.Path.Root = $PSBoundParameters['Root']
}
else {
$global:BS.Path.Root = $PSScriptRoot
}
# add any custom args to the configuration var
if ($PSBoundParameters['CustomArgs']) {
$global:BS.Config.Add('CustomArgs', $PSBoundParameters['CustomArgs'])
}
# auto configure the main paths
# list of the variables
@(
'config'
'data'
'lib'
'psmodule'
) | % {
if ($value = $PSBoundParameters[$_]) {
# if this path was supplied at the start then add it as is
$global:BS.Path.Add($_, $value)
}
else {
# otherwise assume it's under the main script root path
# no attemps are made for the path validation, it's upon you
$global:BS.Path.Add($_, (Join-Path $BS.Path.Root $_))
}
}
# Adding ./psmodule to $PSModulePath allows the module auto loading without explicitly
# calling Import-Module with the absolute or relative paths
# Note, if you prefer for the bundled modules not to be loaded before the ones available in the system,
# swap '{0}{1}{2}' to '{2}{1}{0}'
if (Test-Path $BS.Path.PSModule) {
# if the path exists
if ($Env:PSModulePath -match [regex]::Escape($BS.Path.PSModule)) {
# nothing to do, the path was already in PSModulePath
}
else {
$Env:PSModulePath = '{0}{1}{2}' -f $BS.Path.PSModule, [System.IO.Path]::PathSeparator, $Env:PSModulePath
}
}
# load any configiguration to the global variable
if (Test-Path $BS.Path.config) {
foreach ($file in (gci $BS.Path.config -Filter *.json)) {
try {
$json = Get-Content $file.fullname | ConvertFrom-json
$BS.config.add($file.basename, $json)
}
catch {
write-warning ('Failed to load configuration data from the file "{0}"' -f $file.fullname)
}
}
}
# auto load .NET assemblies
function Register-Assembly {
<#
.Synopsis
Load .NET assembly
.DESCRIPTION
Load .NET assembly or assemblies
.EXAMPLE
Register-Assembly
Load any .dll in the current path
.EXAMPLE
Register-Assembly -Path ./lib/nestandard99/kewllib.dll
Load a dll from the explicit path
#>
[CmdletBinding()]
[Alias('Register-Assemblies')]
Param
(
# Path to *.dll
[Alias('AssemblyPath')]
$Path = $PWD
)
function loadassembly {
param ($pathToDll)
try {
[System.Reflection.Assembly]::LoadFile($pathToDll)
Write-Verbose ("Loaded assembly: {0}" -f $pathToDll)
}
catch {
Write-Error $_
}
}
try {
$thisPath = Get-Item $Path -ErrorAction Stop
if ($thisPath.PSIsContainer) {
Get-ChildItem -Filter '*.dll' | % {
loadassembly $_.FullName
}
}
# this whould be triggered both for when PSIsContainer is false (ie this is a file) and when this property doesn't exists
else {
loadassembly $thisPath.FullName
}
}
Catch {
Write-Error $_
}
} # end function Register-Assembly
# $PSVersionTable.PSVersion
# Windows PowerShell 5.x
if ($PSVersionTable.PSVersion.Major -eq 5) {
$subFolder = 'net4'
}
# PowerShell 6
elseif ($PSVersionTable.PSVersion.Major -eq 6) {
$subFolder = 'netstandard2'
}
# PowerShell 7.0
elseif ($PSVersionTable.PSVersion -lt [version]'7.2.0') {
$subFolder = 'netstandard2'
}
# PowerShell 7.2
elseif ($PSVersionTable.PSVersion -lt [version]'7.3.0') {
$subFolder = 'net6'
}
# PowerShell 7.3+
else {
$subFolder = 'net7'
}
$pathToLoadFrom = Join-Path $global:BS.Path.lib $subFolder
if (Test-Path $pathToLoadFrom) {
Write-Verbose ("Loading .NET assemblies from the path: {0}" -f $pathToLoadFrom)
Register-Assembly -AssemblyPath $pathToLoadFrom
}
Remove-Variable subFolder, pathToLoadFrom -ErrorAction SilentlyContinue
# end load .NET assemblies
# use only dirs named like 01-Something etc
$dirsToProcess = gci -Path $BS.Path.Root -Directory | ? Name -Match '\d+-\w+' | Sort-Object Name
foreach ($thisDir in $dirsToProcess) {
$scriptFiles = gci $thisDir.FullName -Filter '*.ps1' | Sort-Object Name
foreach ($thisFile in $scriptFiles) {
'Processing {0}\{1}' -f $thisDir.Name, $thisFile.Name | Write-Verbose
. $thisFile.FullName
}
}