From 22959eb7187b2f4f68773a498dbb605d96f58ffc Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sat, 4 Oct 2025 10:58:17 -0700 Subject: [PATCH 01/15] feat: Vector Module Scaffolding ( Fixes #1 ) --- Vector.psd1 | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Vector.psm1 | 75 +++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 Vector.psd1 create mode 100644 Vector.psm1 diff --git a/Vector.psd1 b/Vector.psd1 new file mode 100644 index 0000000..61de020 --- /dev/null +++ b/Vector.psd1 @@ -0,0 +1,132 @@ +# +# Module manifest for module 'Vector' +# +# Generated by: PowerPi +# +# Generated on: 10/4/2025 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'Vector.psm1' + +# Version number of this module. +ModuleVersion = '0.0.1' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '2110de70-cbfb-4e01-b273-e7db122a4a53' + +# Author of this module +Author = 'PowerPi' + +# Company or vendor of this module +CompanyName = 'Start-Automating' + +# Copyright statement for this module +Copyright = '2025 Start-Automating' + +# Description of the functionality provided by this module +Description = 'Vectors in PowerShell' + +# Minimum version of the PowerShell engine required by this module +# PowerShellVersion = '' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Get-Vector2', 'Get-Vector3', 'Get-Vector4' + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = '*' + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = 'V2', 'V3', 'V4', 'Vector2', 'Vector3', 'Vector4' + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + diff --git a/Vector.psm1 b/Vector.psm1 new file mode 100644 index 0000000..9198e2b --- /dev/null +++ b/Vector.psm1 @@ -0,0 +1,75 @@ +$commandsPath = Join-Path $PSScriptRoot Commands +:ToIncludeFiles foreach ($file in (Get-ChildItem -Path "$commandsPath" -Filter "*-*" -Recurse)) { + if ($file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1 + foreach ($exclusion in '\.[^\.]+\.ps1$') { + if (-not $exclusion) { continue } + if ($file.Name -match $exclusion) { + continue ToIncludeFiles # Skip excluded files + } + } + . $file.FullName +} + +$myModule = $MyInvocation.MyCommand.ScriptBlock.Module +$ExecutionContext.SessionState.PSVariable.Set($myModule.Name, $myModule) +$myModule.pstypenames.insert(0, $myModule.Name) + +New-PSDrive -Name $MyModule.Name -PSProvider FileSystem -Scope Global -Root $PSScriptRoot -ErrorAction Ignore + +if ($home) { + $MyModuleProfileDirectory = Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) $MyModule.Name + if (-not (Test-Path $MyModuleProfileDirectory)) { + $null = New-Item -ItemType Directory -Path $MyModuleProfileDirectory -Force + } + New-PSDrive -Name "My$($MyModule.Name)" -PSProvider FileSystem -Scope Global -Root $MyModuleProfileDirectory -ErrorAction Ignore +} + +# Set a script variable of this, set to the module +# (so all scripts in this scope default to the correct `$this`) +$script:this = $myModule + +#region Custom + +foreach ($type in [Numerics.Vector2], [Numerics.Vector3],[Numerics.Vector4]) { + $staticMembers = $type | Get-Member -Static + $typeData = [Management.Automation.Runspaces.TypeData]::new($type.FullName) + foreach ($staticMember in $staticMembers) { + if ($staticMember.MemberType -eq 'Method' -and + $staticMember.Definition -match "\($([Regex]::Escape($type.FullName))\s\w+\)" + ) { + $typeData.Members.Add( + $staticMember.Name, + [Management.Automation.Runspaces.ScriptMethodData]::new( + $staticMember.Name, + [ScriptBlock]::Create(" +`$invokeArgs = @(`$this) + `$args +[$($type.FullName -replace '^System\.')]::$($staticMember.Name).Invoke(`$invokeArgs) +") + ) + ) + } + if ($staticMember.MemberType -eq 'Property' -and + $staticMember.Definition -match "^$([Regex]::Escape($type.FullName))\s") { + <#$typeData.Members.Add( + $staticMember.Name, + [Management.Automation.Runspaces.ScriptPropertyData]::new( + $staticMember.Name, + [ScriptBlock]::Create("[$($type.FullName -replace '^System\.')]::$($staticMember.Name)") + ) + )#> + } + + } + Update-TypeData -TypeData $typeData -Force +} + +$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { + Remove-TypeData -ErrorAction Ignore -TypeName 'System.Numerics.Vector2' + Remove-TypeData -ErrorAction Ignore -TypeName 'System.Numerics.Vector3' + Remove-TypeData -ErrorAction Ignore -TypeName 'System.Numerics.Vector4' +} +#endregion Custom + +Export-ModuleMember -Alias * -Function * -Variable $myModule.Name + + From 1138373bd59703f153d042f5bcfc679ce404e5f9 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:02:47 -0700 Subject: [PATCH 02/15] feat: Get-Vector ( Fixes #1 ) --- Commands/Get-Vector.ps1 | 119 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 Commands/Get-Vector.ps1 diff --git a/Commands/Get-Vector.ps1 b/Commands/Get-Vector.ps1 new file mode 100644 index 0000000..8002ada --- /dev/null +++ b/Commands/Get-Vector.ps1 @@ -0,0 +1,119 @@ +function Get-Vector +{ + <# + .SYNOPSIS + Gets a one dimensional vector + .DESCRIPTION + Gets a one dimensional vector (or, more simply, a list of numbers) + + This will convert a variety of types into numbers. + .NOTES + This attempts to convert any type into a number. + + Some types are special: + + * Primitive types will be casted to float + * `[Numerics.Vector2]`,`[Numerics.Vector3]`,`[Numerics.Vector4]` output each component + * `[string]`s that match a range (`$start..$end`) will output that range + * `[Version]`s will output each numeric component + * `[semver]`s will output each numeric component, followed by the bytes of a release type + * `[DateTime]` and `[DateTimeOffset]` will become a series of 12 numbers + * `year`,`month`,`day` + * `hour`, `minute`, `second` + * `millisecond`, `microsecond`, `nanosecond` + * `offset.hours`, `offset.minutes`, `offset.seconds` + * `[string]s` will return their bytes in the current `$outputEncoding` + * Anything unknown will be stringified and the bytes will be returned + #> + [Alias('Vector','Vector1','V1')] + param() + + filter toVector { + $arg = $_ + # Return primitive types + if ($arg.GetType -and $arg.GetType().IsPrimitive) { + # casted to float + return ($arg -as [float]) + } + # Return vector components + if ($arg -is [ValueType]) { + if ($arg -is [Numerics.Vector2]) { + return $arg.X,$arg.Y + } + elseif ($arg -is [Numerics.Vector3]) { + return $arg.X,$arg.Y,$arg.Z + } + elseif ($arg -is [Numerics.Vector4]) { + return $arg.X,$arg.Y,$arg.Z, $arg.W + } + } + # Look for inline ranges. + if ($arg -is [string]) { + if ($arg -match '^\d..\d') { + $start, $end = $arg -split '\..', 2 + $startInt = ($start -as [int]) + $endInt = ($end -as [int]) + if ($null -ne $startInt -and $null -ne $endInt) { + # If found, return them expanded. + return ($startInt..$endInt) + } + } + if ($arg -as [float]) { + return $arg -as [float] + } + } + + + # If the arg is a version, get each number of the version + if ($arg -is [version]) {return $arg.Major,$arg.Minor,$arg.Build,$arg.Revision} + + # If we support semver and the arg is semver + if (('semver' -as [type]) -and $arg -is [semver]) { + # Return the numeric parts of the semver + $arg.Major,$arg.Minor,$arg.Patch + # and turn any string portions to bytes + if ($arg.PreReleaseLabel) { + # make sure to include a leading dash for pre-releases + $OutputEncoding.GetBytes("-$($arg.PreReleaseLabel)") + } + + if ($arg.BuildLabel) { + # make sure to include a leading plus for build labels + $OutputEncoding.GetBytes("+$($arg.BuildLabel)") + } + return + } + + # If the arg is a datetime or datetimeoffset + if ($arg -is [DateTime] -or $arg -is [DateTimeOffset]) { + # make it an offset, and then output 12 values + $dateArg = $arg -as [DateTimeOffset] + # * `year` `month` `day` + $dateArg.Year, $dateArg.Month, $dateArg.Day, + # * `hour` `minute` `second` + $dateArg.Hour, $dateArg.Minute, $dateArg.Second, + # * `millisecond`, `microsecond`, `nanosecond` + $dateArg.Millisecond, $dateArg.Microsecond, $dateArg.Nanosecond, + # * `offset hours`, `offset minutes`, `offset seconds` + $dateArg.Offset.Hours,$dateArg.Offset.Minutes,$dateArg.Offset.Seconds + return + } + # If the arg is a string + if ($arg -is [string]) { + # return its bytes + return $OutputEncoding.GetBytes($arg) + } + # any input we have not caught, stringify and turn to bytes + return $OutputEncoding.GetBytes("$arg") + } + + + # Collect all of our input and arguments + $allIn = @($input) + @( + foreach ($arg in $args) { + $arg + } + ) + + return $allIn | toVector +} \ No newline at end of file From 89daa131c4c88d5aa5a5078626aa1f9ffc3a44ab Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:05:05 -0700 Subject: [PATCH 03/15] feat: Vector scaffolding ( Fixes #1 ) --- Vector.psd1 | 8 ++++---- Vector.psm1 | 36 ------------------------------------ 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/Vector.psd1 b/Vector.psd1 index 61de020..63cecdb 100644 --- a/Vector.psd1 +++ b/Vector.psd1 @@ -12,7 +12,7 @@ RootModule = 'Vector.psm1' # Version number of this module. -ModuleVersion = '0.0.1' +ModuleVersion = '0.1' # Supported PSEditions # CompatiblePSEditions = @() @@ -21,7 +21,7 @@ ModuleVersion = '0.0.1' GUID = '2110de70-cbfb-4e01-b273-e7db122a4a53' # Author of this module -Author = 'PowerPi' +Author = 'James Brundage' # Company or vendor of this module CompanyName = 'Start-Automating' @@ -69,7 +69,7 @@ Description = 'Vectors in PowerShell' # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. -FunctionsToExport = 'Get-Vector2', 'Get-Vector3', 'Get-Vector4' +FunctionsToExport = 'Get-Vector', 'Get-Vector2', 'Get-Vector3', 'Get-Vector4' # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = '*' @@ -78,7 +78,7 @@ CmdletsToExport = '*' VariablesToExport = '*' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. -AliasesToExport = 'V2', 'V3', 'V4', 'Vector2', 'Vector3', 'Vector4' +AliasesToExport = 'V1', 'V2', 'V3', 'V4', 'Vector1', 'Vector2', 'Vector3', 'Vector4', 'Vector' # DSC resources to export from this module # DscResourcesToExport = @() diff --git a/Vector.psm1 b/Vector.psm1 index 9198e2b..ebecaec 100644 --- a/Vector.psm1 +++ b/Vector.psm1 @@ -29,40 +29,6 @@ if ($home) { $script:this = $myModule #region Custom - -foreach ($type in [Numerics.Vector2], [Numerics.Vector3],[Numerics.Vector4]) { - $staticMembers = $type | Get-Member -Static - $typeData = [Management.Automation.Runspaces.TypeData]::new($type.FullName) - foreach ($staticMember in $staticMembers) { - if ($staticMember.MemberType -eq 'Method' -and - $staticMember.Definition -match "\($([Regex]::Escape($type.FullName))\s\w+\)" - ) { - $typeData.Members.Add( - $staticMember.Name, - [Management.Automation.Runspaces.ScriptMethodData]::new( - $staticMember.Name, - [ScriptBlock]::Create(" -`$invokeArgs = @(`$this) + `$args -[$($type.FullName -replace '^System\.')]::$($staticMember.Name).Invoke(`$invokeArgs) -") - ) - ) - } - if ($staticMember.MemberType -eq 'Property' -and - $staticMember.Definition -match "^$([Regex]::Escape($type.FullName))\s") { - <#$typeData.Members.Add( - $staticMember.Name, - [Management.Automation.Runspaces.ScriptPropertyData]::new( - $staticMember.Name, - [ScriptBlock]::Create("[$($type.FullName -replace '^System\.')]::$($staticMember.Name)") - ) - )#> - } - - } - Update-TypeData -TypeData $typeData -Force -} - $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { Remove-TypeData -ErrorAction Ignore -TypeName 'System.Numerics.Vector2' Remove-TypeData -ErrorAction Ignore -TypeName 'System.Numerics.Vector3' @@ -71,5 +37,3 @@ $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { #endregion Custom Export-ModuleMember -Alias * -Function * -Variable $myModule.Name - - From 089bc0adaa80bb50efdd7dc889bec6dead90228e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:09:13 -0700 Subject: [PATCH 04/15] feat: Get-Vector2 ( Fixes #2 ) --- Commands/Get-Vector2.ps1 | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Commands/Get-Vector2.ps1 diff --git a/Commands/Get-Vector2.ps1 b/Commands/Get-Vector2.ps1 new file mode 100644 index 0000000..f3d2c6b --- /dev/null +++ b/Commands/Get-Vector2.ps1 @@ -0,0 +1,46 @@ +function Get-Vector2 { + <# + .SYNOPSIS + Gets a Vector2 + .DESCRIPTION + Gets any input and arguments as a Vector2 + .LINK + https://learn.microsoft.com/en-us/dotnet/api/system.numerics.vector2?wt.mc_id=MVP_321542 + .EXAMPLE + # Create a vector out of two numbers + Vector2 1 2 + .EXAMPLE + (Vector2 1 2) + (Vector2 2 1) + .EXAMPLE + (Vector2 1 2) - (Vector2 2 1) + .EXAMPLE + # Create a thousand vectors + $vectors = Vector2 1..2kb + .EXAMPLE + # Create a thousand vectors in random order, using the pipeline + $vectors = 1..2kb | Get-Random -Count 2kb | Vector2 + .EXAMPLE + # Create a vector from a string + $vector = Vector2 "hi" + #> + [Alias('V2','Vector2')] + param() + # Collect all of our input and arguments + $allIn = @($input) + @( + foreach ($arg in $args) { + $arg + } + ) + + # and expand them + $expandAllIn = @($allIn | Vector) + + For ($n = 0; $n -lt $expandAllIn.Length; $n+=2) { + $argSet = $expandAllIn[$n..($n+1)] -as [float[]] + if ($argSet.Length -eq 1) { + [Numerics.Vector2]::new($argSet[0]) + } else { + [Numerics.Vector2]::new($argSet) + } + } +} From b286c1afa61262b41ab1dfa52f2b35eafd84b66b Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:09:45 -0700 Subject: [PATCH 05/15] feat: Get-Vector3 ( Fixes #3 ) --- Commands/Get-Vector3.ps1 | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Commands/Get-Vector3.ps1 diff --git a/Commands/Get-Vector3.ps1 b/Commands/Get-Vector3.ps1 new file mode 100644 index 0000000..4dc1a25 --- /dev/null +++ b/Commands/Get-Vector3.ps1 @@ -0,0 +1,57 @@ +function Get-Vector3 { + <# + .SYNOPSIS + Gets a Vector3 + .DESCRIPTION + Gets any input and arguments as a Vector3 + .LINK + https://learn.microsoft.com/en-us/dotnet/api/system.numerics.vector3?wt.mc_id=MVP_321542 + .EXAMPLE + # Create a vector out of two numbers + Vector3 1 2 3 + .EXAMPLE + (Vector3 1 2 3 ) + (Vector3 3 2 1) + .EXAMPLE + (Vector3 1 2 3 ) - (Vector3 3 2 1) + .EXAMPLE + # Create a thousand vectors + $vectors = Vector3 1..3kb + .EXAMPLE + # Create a thousand vectors in random order, using the pipeline + $vectors = 1..3kb | Get-Random -Count 3kb | Vector3 + .EXAMPLE + # Create a vector from a string + $vector = Vector3 "hi" + .NOTES + This script is self contained so that it can be easily dropped into any project + #> + [Alias('Vector3','V3')] + param() + + + # Collect all of our input and arguments + $allIn = @($input) + @( + foreach ($arg in $args) { + $arg + } + ) + + # and expand them + $expandAllIn = @($allIn | Vector) + + # Go over our arguments three at a time + For ($n = 0; $n -lt $expandAllIn.Length; $n+=3) { + $argSet = $expandAllIn[$n..($n+2)] -as [float[]] + switch ($argSet.Length) { + 1 { + [Numerics.Vector3]::new($argSet[0]) + } + 2 { + [Numerics.Vector3]::new([Numerics.Vector2]::new($argSet[0],$argSet[1]), 1) + } + 3 { + [Numerics.Vector3]::new($argSet) + } + } + } +} From 8c73ce0af10ad1c4ee5f4bd484e60e00d1cee5a8 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:10:17 -0700 Subject: [PATCH 06/15] feat: Get-Vector4 ( Fixes #4 ) --- Commands/Get-Vector4.ps1 | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Commands/Get-Vector4.ps1 diff --git a/Commands/Get-Vector4.ps1 b/Commands/Get-Vector4.ps1 new file mode 100644 index 0000000..b2d0d69 --- /dev/null +++ b/Commands/Get-Vector4.ps1 @@ -0,0 +1,52 @@ +function Get-Vector4 { + <# + .SYNOPSIS + Gets a Vector4 + .DESCRIPTION + Gets any input and arguments as a Vector4 + .LINK + https://learn.microsoft.com/en-us/dotnet/api/system.numerics.vector4?wt.mc_id=MVP_321542 + .EXAMPLE + # Create a vector out of four numbers + Vector4 1 2 3 4 + .EXAMPLE + (Vector4 1 2 3 4 ) + (Vector4 4 3 2 1 ) + .EXAMPLE + (Vector4 1 2 3 4 ) - (Vector4 4 3 2 1) + .EXAMPLE + # Create a thousand vectors + $vectors = Vector4 1..4kb + .EXAMPLE + # Create a thousand vectors in random order, using the pipeline + $vectors = 1..4kb | Get-Random -Count 4kb | Vector4 + .EXAMPLE + # Create vectors from a string + Vector4 "hi" + #> + [Alias('v4','Vector4')] + param() + # Collect all of our input and arguments + $allIn = @($input) + @( + foreach ($arg in $args) { + $arg + } + ) + + # and expand them + $expandAllIn = @($allIn | vector) + For ($n = 0; $n -lt $expandAllIn.Length; $n+=4) { + $argSet = $expandAllIn[$n..($n+3)] -as [float[]] + switch ($argSet.Length) { + 1 {[Numerics.Vector4]::new($argSet[0]) } + 2 { + [Numerics.Vector4]::new([Numerics.Vector2]::new($argSet[0],$argSet[1]), 1, 1) + } + 3 { + [Numerics.Vector4]::new([Numerics.Vector3]::new($argSet[0],$argSet[1],$argSet[2]), 1) + } + 4 { + [Numerics.Vector4]::new($argSet) + } + } + } +} \ No newline at end of file From 4c66c8fcec08dddd36b8ae9bb98296028ebee9f1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:16:45 -0700 Subject: [PATCH 07/15] feat: Vector workflow ( Fixes #5 ) --- .github/workflows/BuildVector.yml | 501 +++++++++++++++++++++ Build/GitHub/Jobs/BuildVector.psd1 | 18 + Build/GitHub/Steps/PublishTestResults.psd1 | 10 + Build/Vector.GitHubWorkflow.PSDevOps.ps1 | 15 + 4 files changed, 544 insertions(+) create mode 100644 .github/workflows/BuildVector.yml create mode 100644 Build/GitHub/Jobs/BuildVector.psd1 create mode 100644 Build/GitHub/Steps/PublishTestResults.psd1 create mode 100644 Build/Vector.GitHubWorkflow.PSDevOps.ps1 diff --git a/.github/workflows/BuildVector.yml b/.github/workflows/BuildVector.yml new file mode 100644 index 0000000..e0dabd4 --- /dev/null +++ b/.github/workflows/BuildVector.yml @@ -0,0 +1,501 @@ + +name: Build Vector Module +on: + push: + pull_request: + workflow_dispatch: +jobs: + TestPowerShellOnLinux: + runs-on: ubuntu-latest + steps: + - name: InstallPester + id: InstallPester + shell: pwsh + run: | + $Parameters = @{} + $Parameters.PesterMaxVersion = ${env:PesterMaxVersion} + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: InstallPester $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {<# + .Synopsis + Installs Pester + .Description + Installs Pester + #> + param( + # The maximum pester version. Defaults to 4.99.99. + [string] + $PesterMaxVersion = '4.99.99' + ) + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + Install-Module -Name Pester -Repository PSGallery -Force -Scope CurrentUser -MaximumVersion $PesterMaxVersion -SkipPublisherCheck -AllowClobber + Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion} @Parameters + - name: Check out repository + uses: actions/checkout@v2 + - name: RunPester + id: RunPester + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.PesterMaxVersion = ${env:PesterMaxVersion} + $Parameters.NoCoverage = ${env:NoCoverage} + $Parameters.NoCoverage = $parameters.NoCoverage -match 'true'; + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: RunPester $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {<# + .Synopsis + Runs Pester + .Description + Runs Pester tests after importing a PowerShell module + #> + param( + # The module path. If not provided, will default to the second half of the repository ID. + [string] + $ModulePath, + # The Pester max version. By default, this is pinned to 4.99.99. + [string] + $PesterMaxVersion = '4.99.99', + + # If set, will not collect code coverage. + [switch] + $NoCoverage + ) + + $global:ErrorActionPreference = 'continue' + $global:ProgressPreference = 'silentlycontinue' + + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + if (-not $ModulePath) { $ModulePath = ".\$moduleName.psd1" } + $importedPester = Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion + $importedModule = Import-Module $ModulePath -Force -PassThru + $importedPester, $importedModule | Out-Host + + $codeCoverageParameters = @{ + CodeCoverage = "$($importedModule | Split-Path)\*-*.ps1" + CodeCoverageOutputFile = ".\$moduleName.Coverage.xml" + } + + if ($NoCoverage) { + $codeCoverageParameters = @{} + } + + + $result = + Invoke-Pester -PassThru -Verbose -OutputFile ".\$moduleName.TestResults.xml" -OutputFormat NUnitXml @codeCoverageParameters + + "::set-output name=TotalCount::$($result.TotalCount)", + "::set-output name=PassedCount::$($result.PassedCount)", + "::set-output name=FailedCount::$($result.FailedCount)" | Out-Host + if ($result.FailedCount -gt 0) { + "::debug:: $($result.FailedCount) tests failed" + foreach ($r in $result.TestResult) { + if (-not $r.Passed) { + "::error::$($r.describe, $r.context, $r.name -join ' ') $($r.FailureMessage)" + } + } + throw "::error:: $($result.FailedCount) tests failed" + } + } @Parameters + - name: PublishTestResults + uses: actions/upload-artifact@main + with: + name: PesterResults + path: '**.TestResults.xml' + if: ${{always()}} + TagReleaseAndPublish: + runs-on: ubuntu-latest + if: ${{ success() }} + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: TagModuleVersion + id: TagModuleVersion + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.UserEmail = ${env:UserEmail} + $Parameters.UserName = ${env:UserName} + $Parameters.TagVersionFormat = ${env:TagVersionFormat} + $Parameters.TagAnnotationFormat = ${env:TagAnnotationFormat} + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: TagModuleVersion $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {param( + [string] + $ModulePath, + + # The user email associated with a git commit. + [string] + $UserEmail, + + # The user name associated with a git commit. + [string] + $UserName, + + # The tag version format (default value: 'v$(imported.Version)') + # This can expand variables. $imported will contain the imported module. + [string] + $TagVersionFormat = 'v$($imported.Version)', + + # The tag version format (default value: '$($imported.Name) $(imported.Version)') + # This can expand variables. $imported will contain the imported module. + [string] + $TagAnnotationFormat = '$($imported.Name) $($imported.Version)' + ) + + + $gitHubEvent = if ($env:GITHUB_EVENT_PATH) { + [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json + } else { $null } + + + @" + ::group::GitHubEvent + $($gitHubEvent | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + + if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and + (-not $gitHubEvent.psobject.properties['inputs'])) { + "::warning::Pull Request has not merged, skipping Tagging" | Out-Host + return + } + + + + $imported = + if (-not $ModulePath) { + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global + } else { + Import-Module $modulePath -Force -PassThru -Global + } + + if (-not $imported) { return } + + $targetVersion =$ExecutionContext.InvokeCommand.ExpandString($TagVersionFormat) + $existingTags = git tag --list + + @" + Target Version: $targetVersion + + Existing Tags: + $($existingTags -join [Environment]::NewLine) + "@ | Out-Host + + $versionTagExists = $existingTags | Where-Object { $_ -match $targetVersion } + + if ($versionTagExists) { + "::warning::Version $($versionTagExists)" + return + } + + if (-not $UserName) { $UserName = $env:GITHUB_ACTOR } + if (-not $UserEmail) { $UserEmail = "$UserName@github.com" } + git config --global user.email $UserEmail + git config --global user.name $UserName + + git tag -a $targetVersion -m $ExecutionContext.InvokeCommand.ExpandString($TagAnnotationFormat) + git push origin --tags + + if ($env:GITHUB_ACTOR) { + exit 0 + }} @Parameters + - name: ReleaseModule + id: ReleaseModule + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.UserEmail = ${env:UserEmail} + $Parameters.UserName = ${env:UserName} + $Parameters.TagVersionFormat = ${env:TagVersionFormat} + $Parameters.ReleaseNameFormat = ${env:ReleaseNameFormat} + $Parameters.ReleaseAsset = ${env:ReleaseAsset} + $Parameters.ReleaseAsset = $parameters.ReleaseAsset -split ';' -replace '^[''"]' -replace '[''"]$' + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: ReleaseModule $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {param( + [string] + $ModulePath, + + # The user email associated with a git commit. + [string] + $UserEmail, + + # The user name associated with a git commit. + [string] + $UserName, + + # The tag version format (default value: 'v$(imported.Version)') + # This can expand variables. $imported will contain the imported module. + [string] + $TagVersionFormat = 'v$($imported.Version)', + + # The release name format (default value: '$($imported.Name) $($imported.Version)') + [string] + $ReleaseNameFormat = '$($imported.Name) $($imported.Version)', + + # Any assets to attach to the release. Can be a wildcard or file name. + [string[]] + $ReleaseAsset + ) + + + $gitHubEvent = if ($env:GITHUB_EVENT_PATH) { + [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json + } else { $null } + + + @" + ::group::GitHubEvent + $($gitHubEvent | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + + if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and + (-not $gitHubEvent.psobject.properties['inputs'])) { + "::warning::Pull Request has not merged, skipping GitHub release" | Out-Host + return + } + + + + $imported = + if (-not $ModulePath) { + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global + } else { + Import-Module $modulePath -Force -PassThru -Global + } + + if (-not $imported) { return } + + $targetVersion =$ExecutionContext.InvokeCommand.ExpandString($TagVersionFormat) + $targetReleaseName = $targetVersion + $releasesURL = 'https://api.github.com/repos/${{github.repository}}/releases' + "Release URL: $releasesURL" | Out-Host + $listOfReleases = Invoke-RestMethod -Uri $releasesURL -Method Get -Headers @{ + "Accept" = "application/vnd.github.v3+json" + "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + } + + $releaseExists = $listOfReleases | Where-Object tag_name -eq $targetVersion + + if ($releaseExists) { + "::warning::Release '$($releaseExists.Name )' Already Exists" | Out-Host + $releasedIt = $releaseExists + } else { + $releasedIt = Invoke-RestMethod -Uri $releasesURL -Method Post -Body ( + [Ordered]@{ + owner = '${{github.owner}}' + repo = '${{github.repository}}' + tag_name = $targetVersion + name = $ExecutionContext.InvokeCommand.ExpandString($ReleaseNameFormat) + body = + if ($env:RELEASENOTES) { + $env:RELEASENOTES + } elseif ($imported.PrivateData.PSData.ReleaseNotes) { + $imported.PrivateData.PSData.ReleaseNotes + } else { + "$($imported.Name) $targetVersion" + } + draft = if ($env:RELEASEISDRAFT) { [bool]::Parse($env:RELEASEISDRAFT) } else { $false } + prerelease = if ($env:PRERELEASE) { [bool]::Parse($env:PRERELEASE) } else { $false } + } | ConvertTo-Json + ) -Headers @{ + "Accept" = "application/vnd.github.v3+json" + "Content-type" = "application/json" + "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + } + } + + + + + + if (-not $releasedIt) { + throw "Release failed" + } else { + $releasedIt | Out-Host + } + + $releaseUploadUrl = $releasedIt.upload_url -replace '\{.+$' + + if ($ReleaseAsset) { + $fileList = Get-ChildItem -Recurse + $filesToRelease = + @(:nextFile foreach ($file in $fileList) { + foreach ($relAsset in $ReleaseAsset) { + if ($relAsset -match '[\*\?]') { + if ($file.Name -like $relAsset) { + $file; continue nextFile + } + } elseif ($file.Name -eq $relAsset -or $file.FullName -eq $relAsset) { + $file; continue nextFile + } + } + }) + + $releasedFiles = @{} + foreach ($file in $filesToRelease) { + if ($releasedFiles[$file.Name]) { + Write-Warning "Already attached file $($file.Name)" + continue + } else { + $fileBytes = [IO.File]::ReadAllBytes($file.FullName) + $releasedFiles[$file.Name] = + Invoke-RestMethod -Uri "${releaseUploadUrl}?name=$($file.Name)" -Headers @{ + "Accept" = "application/vnd.github+json" + "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + } -Body $fileBytes -ContentType Application/octet-stream + $releasedFiles[$file.Name] + } + } + + "Attached $($releasedFiles.Count) file(s) to release" | Out-Host + } + + + + } @Parameters + - name: PublishPowerShellGallery + id: PublishPowerShellGallery + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.Exclude = ${env:Exclude} + $Parameters.Exclude = $parameters.Exclude -split ';' -replace '^[''"]' -replace '[''"]$' + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: PublishPowerShellGallery $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {param( + [string] + $ModulePath, + + [string[]] + $Exclude = @('*.png', '*.mp4', '*.jpg','*.jpeg', '*.gif', 'docs[/\]*') + ) + + $gitHubEvent = if ($env:GITHUB_EVENT_PATH) { + [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json + } else { $null } + + if (-not $Exclude) { + $Exclude = @('*.png', '*.mp4', '*.jpg','*.jpeg', '*.gif','docs[/\]*') + } + + + @" + ::group::GitHubEvent + $($gitHubEvent | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + + @" + ::group::PSBoundParameters + $($PSBoundParameters | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + + if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and + (-not $gitHubEvent.psobject.properties['inputs'])) { + "::warning::Pull Request has not merged, skipping Gallery Publish" | Out-Host + return + } + + + $imported = + if (-not $ModulePath) { + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global + } else { + Import-Module $modulePath -Force -PassThru -Global + } + + if (-not $imported) { return } + + $foundModule = try { Find-Module -Name $imported.Name -ErrorAction SilentlyContinue} catch {} + + if ($foundModule -and (([Version]$foundModule.Version) -ge ([Version]$imported.Version))) { + "::warning::Gallery Version of $moduleName is more recent ($($foundModule.Version) >= $($imported.Version))" | Out-Host + } else { + + $gk = '${{secrets.GALLERYKEY}}' + + $rn = Get-Random + $moduleTempFolder = Join-Path $pwd "$rn" + $moduleTempPath = Join-Path $moduleTempFolder $moduleName + New-Item -ItemType Directory -Path $moduleTempPath -Force | Out-Host + + Write-Host "Staging Directory: $ModuleTempPath" + + $imported | Split-Path | + Get-ChildItem -Force | + Where-Object Name -NE $rn | + Copy-Item -Destination $moduleTempPath -Recurse + + $moduleGitPath = Join-Path $moduleTempPath '.git' + Write-Host "Removing .git directory" + if (Test-Path $moduleGitPath) { + Remove-Item -Recurse -Force $moduleGitPath + } + + if ($Exclude) { + "::notice::Attempting to Exlcude $exclude" | Out-Host + Get-ChildItem $moduleTempPath -Recurse | + Where-Object { + foreach ($ex in $exclude) { + if ($_.FullName -like $ex) { + "::notice::Excluding $($_.FullName)" | Out-Host + return $true + } + } + } | + Remove-Item + } + + Write-Host "Module Files:" + Get-ChildItem $moduleTempPath -Recurse + Write-Host "Publishing $moduleName [$($imported.Version)] to Gallery" + Publish-Module -Path $moduleTempPath -NuGetApiKey $gk + if ($?) { + Write-Host "Published to Gallery" + } else { + Write-Host "Gallery Publish Failed" + exit 1 + } + } + } @Parameters + BuildVector: + runs-on: ubuntu-latest + if: ${{ success() }} + steps: + - name: Check out repository + uses: actions/checkout@main + - name: UseEZOut + uses: StartAutomating/EZOut@master +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} diff --git a/Build/GitHub/Jobs/BuildVector.psd1 b/Build/GitHub/Jobs/BuildVector.psd1 new file mode 100644 index 0000000..7a6a1d3 --- /dev/null +++ b/Build/GitHub/Jobs/BuildVector.psd1 @@ -0,0 +1,18 @@ +@{ + "runs-on" = "ubuntu-latest" + if = '${{ success() }}' + steps = @( + @{ + name = 'Check out repository' + uses = 'actions/checkout@main' + }, + 'RunEZOut' # , + <#@{ + name = 'Run Vector (on branch)' + if = '${{github.ref_name != ''main''}}' + uses = './' + id = 'VectorAction' + }#> + # 'BuildAndPublishContainer' + ) +} \ No newline at end of file diff --git a/Build/GitHub/Steps/PublishTestResults.psd1 b/Build/GitHub/Steps/PublishTestResults.psd1 new file mode 100644 index 0000000..e8111e8 --- /dev/null +++ b/Build/GitHub/Steps/PublishTestResults.psd1 @@ -0,0 +1,10 @@ +@{ + name = 'PublishTestResults' + uses = 'actions/upload-artifact@main' + with = @{ + name = 'PesterResults' + path = '**.TestResults.xml' + } + if = '${{always()}}' +} + diff --git a/Build/Vector.GitHubWorkflow.PSDevOps.ps1 b/Build/Vector.GitHubWorkflow.PSDevOps.ps1 new file mode 100644 index 0000000..53f291c --- /dev/null +++ b/Build/Vector.GitHubWorkflow.PSDevOps.ps1 @@ -0,0 +1,15 @@ +#requires -Module PSDevOps +Import-BuildStep -SourcePath ( + Join-Path $PSScriptRoot 'GitHub' +) -BuildSystem GitHubWorkflow + +Push-Location ($PSScriptRoot | Split-Path) +New-GitHubWorkflow -Name "Build Vector Module" -On Push, + PullRequest, + Demand -Job TestPowerShellOnLinux, + TagReleaseAndPublish, BuildVector -Environment ([Ordered]@{ + REGISTRY = 'ghcr.io' + IMAGE_NAME = '${{ github.repository }}' + }) -OutputPath .\.github\workflows\BuildVector.yml + +Pop-Location \ No newline at end of file From 670954a769a3185839261b6d9f3bb2d9b2132c3a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:18:06 -0700 Subject: [PATCH 08/15] feat: Vector tests ( Fixes #6 ) --- Vector.tests.ps1 | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Vector.tests.ps1 diff --git a/Vector.tests.ps1 b/Vector.tests.ps1 new file mode 100644 index 0000000..1422003 --- /dev/null +++ b/Vector.tests.ps1 @@ -0,0 +1,43 @@ +describe Vector { + it 'It a collection of points' { + $vector = Vector 1..4 + $vector.GetType() | Should -Be ([object[]]) + $vector | + Should -BeOfType ([int]) + } + + it 'Can be a two dimensional vector' { + Vector2 3,4 | Should -BeOfType ([Numerics.Vector2]) + } + + it 'Can be a three dimensional vector' { + Vector3 1,2,3 | Should -BeOfType ([Numerics.Vector3]) + } + + it 'Can be a four dimensional vector' { + Vector4 1,2,3,4 | Should -BeOfType ([Numerics.Vector4]) + } + + context 'Vector Math' { + it 'Can subtract vectors' { + $subtract = (v2 1 2) - (v2 1 2) + $subtract.X | Should -Be 0 + $subtract.Y | Should -Be 0 + } + it 'Can add vectors' { + $add = (v2 1 2) + (v2 1 2) + $add.X | Should -Be 2 + $add.Y | Should -Be 4 + } + it 'Can multiply vectors' { + $multiply = (v2 1 2) * (v2 1 2) + $multiply.X | Should -Be 1 + $multiply.Y | Should -Be 4 + } + it 'Can divide vectors' { + $divide = (v2 1 2) / (v2 1 2) + $divide.X | Should -Be 1 + $divide.Y | Should -Be 1 + } + } +} From cc0573e3a5343b0325f338c25a32d583b208c4ef Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:40:26 -0700 Subject: [PATCH 09/15] docs: Vector demos ( Fixes #7 ) --- Demos/Vector-101-Intro-to-Vectors.demo.ps1 | 106 +++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Demos/Vector-101-Intro-to-Vectors.demo.ps1 diff --git a/Demos/Vector-101-Intro-to-Vectors.demo.ps1 b/Demos/Vector-101-Intro-to-Vectors.demo.ps1 new file mode 100644 index 0000000..3f9c569 --- /dev/null +++ b/Demos/Vector-101-Intro-to-Vectors.demo.ps1 @@ -0,0 +1,106 @@ +#1. Getting Vectors + +# Numbers are great! + +# When we measure things with one number, it's technically called a scalar. + +# When we measure things with more than one number, it's called a vector + +# We can do lots of things with vectors. We can add or substract them, multiply and divide them. + +# Vectors are very useful. + +# Let's see how we can get a vector: + +# Create a 2D vector +[Numerics.Vector2]::new(1,2) +# Create a 3D vector +[Numerics.Vector3]::new(1,2,3) +# Create a 4D vector +[Numerics.Vector4]::new(1,2,3,4) + + +# The Vector module gives us three vector commands: +Get-Vector2 1 2 +Get-Vector3 1 2 3 +Get-Vector4 1 2 3 4 + +# We can drop the `get` +Vector2 1 2 +Vector3 1 2 3 +Vector4 1 2 3 4 + +# We can use the shorthand `v2`, `v3`, `v4` +v2 1 2 +v3 1 2 3 +v4 1 2 3 4 + +# We can create vectors from a number +v2 1 +v3 1 +v4 1 + +# Strings can be vectors, too (we just get the bytes) +v2 "hi" +v3 "hi" +v4 "hi" + +# Let's start with addition. +# We can add a scalar to a vector. +(v2 1 2) + 1 +(v3 1 2 3) + 1 +(v4 1 2 3 4) + 1 + +# Let's try substraction: +(v2 1 2) - 1 +(v3 1 2 3) - 1 +(v4 1 2 3 4) - 1 + +# How about multiplication? +(v2 1 2) * 2 +(v3 1 2 3) * 2 +(v4 1 2 3 4) * 2 + +# What about division? +(v2 1 2) / 2 +(v3 1 2 3) / 2 +(v4 1 2 3 4) / 2 + +# We can also work with other vectors: + +# Adding vectors: +(v2 1 2) + (v2 1 2) +(v3 1 2 3) + (v3 1 2 3) +(v4 1 2 3 4) + (v4 1 2 3 4) + +# Subtracting vectors: +(v2 1 2) - (v2 1 2) +(v3 1 2 3) - (v3 1 2 3) +(v4 1 2 3 4) - (v4 1 2 3 4) + +# Multiplying vectors: +(v2 1 2) * (v2 1 2) +(v3 1 2 3) * (v3 1 2 3) +(v4 1 2 3 4) * (v4 1 2 3 4) + +# Dividing vectors: +(v2 1 2) / (v2 1 2) +(v3 1 2 3) / (v3 1 2 3) +(v4 1 2 3 4) / (v4 1 2 3 4) + + +# We can also negate a vector: +-(v2 1 2) +-(v3 1 2 3) +-(v4 1 2 3 4) + +# We can compare two vectors to see if they are equal +(v2 1 2) -eq (v2 1 2) +(v3 1 2 3) -eq (v3 1 2 3) +(v4 1 2 3 4) -eq (v4 1 2 3 4) + +# We can also see if they are not equal +(v2 1 2) -ne (v2 1 2) +(v3 1 2 3) -ne (v3 1 2 3) +(v4 1 2 3 4) -ne (v4 1 2 3 4) + From 7e092bfc538872ffabc859a42b705f2693595ac8 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:41:37 -0700 Subject: [PATCH 10/15] docs: Vector README ( Fixes #8 ) --- README.md | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa27482..eac6d0d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,187 @@ # Vector -Vectors in PowerShell + +Numbers are great! + +When we measure things with one number, it's technically called a scalar. + +When we measure things with more than one number, it's called a [vector](https://en.wikipedia.org/wiki/Vector_%28mathematics_and_physics%29) + +We can do lots of things with vectors. We can add or substract them, multiply and divide them. + +Vectors are very useful. + +This module helps you use Vectors in PowerShell + +## Vectors in PowerShell + +Vectors are actually built into PowerShell. + +Because PowerShell is built atop of the .NET Framework, +and the .NET Framework has had vector support for over a decade, +PowerShell has had vectors for over a decade. + +~~~PowerShell +# Create a 2D vector +[Numerics.Vector2]::new(1,2) +# Create a 3D vector +[Numerics.Vector3]::new(1,2,3) +# Create a 4D vector +[Numerics.Vector4]::new(1,2,3,4) +~~~ + +This module exists to make vectors a bit more useful by providing commands to construct them. + +### Installing and Importing + +We can install the Vector module from the gallery: + +~~~PowerShell +# Install the module from the PowerShell gallery +Install-Module Vector +~~~ + +Once installed, we can import the Vector module with Import-Module: + +~~~PowerShell +Import-Module Vector +~~~ + +### Getting Vectors + +There are a few commands in this module: + +* `Get-Vector2` +* `Get-Vector3` +* `Get-Vector4` + +Each command constructs a vector of the corresponding size. + +We can also drop the `Get` and just refer to them by vector number + +~~~PowerShell +Vector2 1 2 +Vector3 1 2 3 +Vector4 1 2 3 4 +~~~ + +We can be even shorter, and use `V2`, `V3`, and `V4` + +~~~PowerShell +v2 1 2 +v3 1 2 3 +v4 1 2 3 4 +~~~ + +We can turn anything into a series of vectors. + +~~~PowerShell +v2 1 +v3 1 +v4 1 +~~~ + +Strings can become vectors, too! (after all, each byte is already a number) + +~~~PowerShell +v2 "hi" +v3 "hi" +v4 "hi" +~~~ + +### Vector Operators + +.NET vectors are _very_ powerful, and overload many operators. + +For example, we can add, subtract, multiply, or divide by a scalar. + +~~~PowerShell +# Let's start with addition. +# We can add a scalar to a vector. +(v2 1 2) + 1 +(v3 1 2 3) + 1 +(v4 1 2 3 4) + 1 + +# Let's try substraction: +(v2 1 2) - 1 +(v3 1 2 3) - 1 +(v4 1 2 3 4) - 1 + +# How about multiplication? +(v2 1 2) * 2 +(v3 1 2 3) * 2 +(v4 1 2 3 4) * 2 + +# What about division? +(v2 1 2) / 2 +(v3 1 2 3) / 2 +(v4 1 2 3 4) / 2 +~~~ + +We can also work with other vectors: + +~~~PowerShell +# Adding vectors: +(v2 1 2) + (v2 1 2) +(v3 1 2 3) + (v3 1 2 3) +(v4 1 2 3 4) + (v4 1 2 3 4) + +# Subtracting vectors: +(v2 1 2) - (v2 1 2) +(v3 1 2 3) - (v3 1 2 3) +(v4 1 2 3 4) - (v4 1 2 3 4) + +# Multiplying vectors: +(v2 1 2) * (v2 1 2) +(v3 1 2 3) * (v3 1 2 3) +(v4 1 2 3 4) * (v4 1 2 3 4) + +# Dividing vectors: +(v2 1 2) / (v2 1 2) +(v3 1 2 3) / (v3 1 2 3) +(v4 1 2 3 4) / (v4 1 2 3 4) +~~~ + +### Vector Methods + +Vectors have a large number of methods to work with. + +Let's start simple, by calculating the length of a given vector. + +~~~PowerShell +(v2 1 1).Length() +(v3 1 1 1).Length() +(v4 1 1 1 1).Length() +~~~ + +Many of the most useful things we can do with a vector are exposed as a static methods: + +~~~PowerShell +(v2 1 1) | Get-Member -Static +(v3 1 1 1) | Get-Member -Static +(v4 1 1 1 1) | Get-Member -Static +~~~ + + +We can access static method with `::` + +For a small example, let's find the distance between vectors: + +~~~PowerShell +$vector1 = v2 1 2 +$vector2 = v2 2 1 +$vector1::Distance($vector1, $vector2) +~~~ + +For another simple example, let's find a few point between two points, using [Linear Interpolation `lerp`](https://learn.microsoft.com/en-us/dotnet/api/system.numerics.vector2.lerp?wt.mc_id=MVP_321542) + +~~~PowerShell +$vector1 = v2 1 5 +$vector2 = v2 1 -5 +$vector1::Lerp($vector1, $vector2, 0.25) +$vector1::Lerp($vector1, $vector2, 0.5) +$vector1::Lerp($vector1, $vector2, 0.75) +~~~ + +All of this would not be possible without the great work of the .NET team to build such incredibly useful data structures. + +Hopefully this module helps us all work with vectors! \ No newline at end of file From 5adfdf609b83b15cd111471327d43103617f3cae Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:46:14 -0700 Subject: [PATCH 11/15] docs: Vector FUNDING ( Fixes #9 ) --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..36bd853 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [StartAutomating] From 49bdb5197d1622dda800a9ac15552c1e6a279ba6 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:46:54 -0700 Subject: [PATCH 12/15] docs: Vector CODE_OF_CONDUCT ( Fixes #10 ) --- CODE_OF_CONDUCT.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a132093 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Code of Conduct + +We have a simple subjective code of conduct: + +1. Be Respectful +2. Be Helpful +3. Do No Harm + +Failure to follow the code of conduct may result in blocks or banishment. From 19f20131338a1cdf469322f77970799b2667e8fd Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:47:27 -0700 Subject: [PATCH 13/15] docs: Vector CONTRIBUTING ( Fixes #11 ) --- CONTRIBUTING.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7a690ce --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +# Contibuting + +We welcome suggestions and careful contributions. + +To suggest something, please [open an issue](https://github.com/PowerShellWeb/Vector/issues) or start a [discussion](https://github.com/PowerShellWeb/Vector/discussion) + +To add a feature, please open an issue and create a pull request. + +## Contributing Examples + +Examples are more than welcome! To contribute an example, please open an issue describing your example and create a pull request. From f8a2d1b5b6bb34f98fa3b5e01da55f2f87090931 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:48:45 -0700 Subject: [PATCH 14/15] docs: Vector SECURITY ( Fixes #12 ) --- SECURITY.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..7f4c180 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security + +We take security seriously. If you believe you have discovered a vulnerability, please [file an issue](https://github.com/PowerShellWeb/Vector/issues). + +## Special Security Considerations + +As this module does little more than expose .NET framework functionality, it should not have any unique special security considerations. If times proves this assertion wrong, please file an issue. \ No newline at end of file From f8c3652db24eeafc18371e9557ec869db07ba259 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 5 Oct 2025 11:54:58 -0700 Subject: [PATCH 15/15] release: Vector 0.1 Adding CHANGELOG and updating manifest --- CHANGELOG.md | 17 +++++++++++++++++ Vector.psd1 | 32 +++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..803f211 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +## Vector 0.1: + +* Initial Release of Vector module +* Commands: + * Get-Vector (#1) + * Get-Vector2 (#2) + * Get-Vector3 (#3) + * Get-Vector4 (#4) +* Vector Workflow (#5) +* Vector Tests (#6) +* Vector Docs + * Demo (#7) + * README (#8) + * FUNDING (#9) + * CODE_OF_CONDUCT (#10) + * CONTRIBUTING (#11) + * SECURITY (#12) diff --git a/Vector.psd1 b/Vector.psd1 index 63cecdb..3e83cbf 100644 --- a/Vector.psd1 +++ b/Vector.psd1 @@ -1,8 +1,6 @@ # # Module manifest for module 'Vector' # -# Generated by: PowerPi -# # Generated on: 10/4/2025 # @@ -95,19 +93,35 @@ PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. - # Tags = @() - - # A URL to the license for this module. - # LicenseUri = '' - + Tags = 'PowerShell', 'Vector', 'Math', 'VectorMath' # A URL to the main website for this project. - # ProjectUri = '' + ProjectURI = 'https://github.com/PowerShellWeb/Vector' + # A URL to the license for this module. + LicenseURI = 'https://github.com/PowerShellWeb/Vector/blob/main/LICENSE' # A URL to an icon representing this module. # IconUri = '' # ReleaseNotes of this module - # ReleaseNotes = '' + ReleaseNotes = @' +## Vector 0.1: + +* Initial Release of Vector module +* Commands: + * Get-Vector (#1) + * Get-Vector2 (#2) + * Get-Vector3 (#3) + * Get-Vector4 (#4) +* Vector Workflow (#5) +* Vector Tests (#6) +* Vector Docs + * Demo (#7) + * README (#8) + * FUNDING (#9) + * CODE_OF_CONDUCT (#10) + * CONTRIBUTING (#11) + * SECURITY (#12) +'@ # Prerelease string of this module # Prerelease = ''