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
83 changes: 67 additions & 16 deletions .github/workflows/test-powershell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,62 @@ permissions:
contents: read

env:
DOTNET_VERSION: '10.x'
BUILD_CONFIGURATION: 'Debug'

jobs:
refresh-psd1:
name: 'Refresh PSD1'
runs-on: windows-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PowerShell modules
shell: pwsh
run: |
Install-Module PSPublishModule -Force -Scope CurrentUser -AllowClobber

- name: Refresh module manifest
shell: pwsh
env:
RefreshPSD1Only: 'true'
run: |
./Module/Build/Build-Module.ps1

- name: Upload refreshed manifest
uses: actions/upload-artifact@v4
with:
name: psd1
path: Module/PSTeams/PSTeams.psd1

test-windows-ps5:
needs: refresh-psd1
name: 'Windows PowerShell 5.1'
runs-on: windows-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Download manifest
uses: actions/download-artifact@v4
with:
name: psd1
path: Module/PSTeams

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.x
10.x
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install PowerShell modules
shell: powershell
run: |
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Install-Module -Name Pester -Repository PSGallery -Force -SkipPublisherCheck -AllowClobber
Install-Module -Name PSWriteColor -Repository PSGallery -Force -SkipPublisherCheck -AllowClobber

- name: Build .NET solution
run: |
Expand All @@ -49,28 +82,34 @@ jobs:

- name: Run PowerShell tests
shell: powershell
run: Invoke-Pester -Path ./Module/Tests -Output Detailed
run: ./Module/PSTeams/PSTeams.Tests.ps1

test-windows-ps7:
needs: refresh-psd1
name: 'Windows PowerShell 7'
runs-on: windows-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Download manifest
uses: actions/download-artifact@v4
with:
name: psd1
path: Module/PSTeams

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.x
10.x
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install PowerShell modules
shell: pwsh
run: |
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Install-Module -Name Pester -Repository PSGallery -Force -SkipPublisherCheck -AllowClobber
Install-Module -Name PSWriteColor -Repository PSGallery -Force -SkipPublisherCheck -AllowClobber

- name: Build .NET solution
run: |
Expand All @@ -79,22 +118,27 @@ jobs:

- name: Run PowerShell tests
shell: pwsh
run: Invoke-Pester -Path ./Module/Tests -Output Detailed
run: ./Module/PSTeams/PSTeams.Tests.ps1

test-ubuntu:
needs: refresh-psd1
name: 'Ubuntu PowerShell 7'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Download manifest
uses: actions/download-artifact@v4
with:
name: psd1
path: Module/PSTeams

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.x
10.x
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install PowerShell
run: |
Expand All @@ -109,6 +153,7 @@ jobs:
run: |
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Install-Module -Name Pester -Repository PSGallery -Force -SkipPublisherCheck -AllowClobber
Install-Module -Name PSWriteColor -Repository PSGallery -Force -SkipPublisherCheck -AllowClobber

- name: Build .NET solution
run: |
Expand All @@ -117,22 +162,27 @@ jobs:

- name: Run PowerShell tests
shell: pwsh
run: Invoke-Pester -Path ./Module/Tests -Output Detailed
run: ./Module/PSTeams/PSTeams.Tests.ps1

test-macos:
needs: refresh-psd1
name: 'macOS PowerShell 7'
runs-on: macos-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Download manifest
uses: actions/download-artifact@v4
with:
name: psd1
path: Module/PSTeams

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.x
10.x
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install PowerShell
shell: bash
Expand All @@ -151,6 +201,7 @@ jobs:
run: |
Write-Host "PowerShell Version: $($PSVersionTable.PSVersion)"
Install-Module -Name Pester -Repository PSGallery -Force -SkipPublisherCheck -AllowClobber
Install-Module -Name PSWriteColor -Repository PSGallery -Force -SkipPublisherCheck -AllowClobber

- name: Build .NET solution
run: |
Expand All @@ -159,4 +210,4 @@ jobs:

- name: Run PowerShell tests
shell: pwsh
run: Invoke-Pester -Path ./Module/Tests -Output Detailed
run: ./Module/PSTeams/PSTeams.Tests.ps1
83 changes: 80 additions & 3 deletions Docs/PowerShell-Surface.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@
# PowerShell Surface

`main` exposes a cmdlet-only PowerShell API.
`main` exposes one `PSTeams` module whose public surface is now binary-backed through `TeamsX.PowerShell`, with the shipping shell in `Module\PSTeams`.

## Current Module Shape

- `TeamsX` is the reusable .NET library
- `TeamsX.PowerShell` is the thin binary cmdlet layer
- `Module\PSTeams` is the shipping module shell and alias bridge
- Legacy public names are preserved as cmdlets and aliases, not script functions
- The runtime module import is now binary-only: the shell loads `TeamsX.PowerShell.dll`, sets aliases, and does not dot-source public/private script functions
- The old module-local helper scripts and stale in-module legacy tests have been removed; the active validation suite lives in `Module\Tests`

## Current Cmdlets

- `New-AdaptiveAction`
- `New-AdaptiveActionSet`
- `New-AdaptiveCard`
- `New-AdaptiveColumn`
- `New-AdaptiveColumnSet`
- `New-AdaptiveContainer`
- `New-AdaptiveFact`
- `New-AdaptiveFactSet`
- `New-AdaptiveImage`
- `New-AdaptiveImageSet`
- `New-AdaptiveLineBreak`
- `New-AdaptiveMedia`
- `New-AdaptiveMediaSource`
- `New-AdaptiveMention`
- `New-AdaptiveRichTextBlock`
- `New-AdaptiveTable`
- `New-AdaptiveTextBlock`
- `ConvertTo-TeamsJson`
- `New-TeamsGraphTarget`
- `New-TeamsHeroCard`
- `New-TeamsThumbnailCard`
- `New-TeamsListCard`
- `New-TeamsMessage`
- `New-TeamsWebhookTarget`
- `Send-TeamsMessage`
- `New-TeamsAdaptiveCard`
- `New-TeamsAdaptiveTextBlock`
- `New-TeamsAdaptiveRichTextBlock`
Expand All @@ -22,16 +51,28 @@
- `New-TeamsAdaptiveColumn`
- `New-TeamsAdaptiveColumnSet`
- `New-TeamsAdaptiveOpenUrlAction`
- `New-TeamsAdaptiveShowCardAction`
- `New-TeamsAdaptiveSubmitAction`
- `New-TeamsAdaptiveToggleVisibilityAction`
- `New-TeamsAdaptiveActionSet`
- `New-TeamsAdaptiveTextRun`

## Migration Status

- `FunctionsToExport` in `Module\PSTeams\PSTeams.psd1` is now empty.
- The whole `New-Adaptive*` surface is binary-backed on `main`.
- `Module\PSTeams\PSTeams.psm1` now follows the `DnsClientX`-style development loader and prefers `net8.0` for PowerShell 7.x, with `net10.0` only as a fallback development build when present.
- Remaining work is now quality and parity polish: warnings cleanup, docs/examples refresh, and feature expansion on the typed cmdlet surface.
- `TeamsX` now includes a Graph sender starter for channel and chat posts, exposed through `New-TeamsGraphTarget`.

## Design Rules

- New public PowerShell features should be implemented as C# cmdlets.
- `TeamsX.PowerShell` should stay thin over `TeamsX`.
- If a feature needs more composition support, add typed .NET models first, then expose cmdlets.
- Do not add new wrapper functions for old `PSTeams` command names on `main`.
- Keep the existing `PSTeams` public names available, but prefer implementing them as cmdlets or aliases.
- Delete PowerShell implementations only after the matching C# cmdlet path is in place and tested.
- Keep new delivery backends dependency-light; prefer direct HTTP clients over large SDK dependencies unless the SDK adds clear value.
- Use `Build\Build-Project.ps1` for project/library release flow.
- Use `Module\Build\Build-Module.ps1` for PowerShell module packaging flow.

Expand All @@ -51,3 +92,39 @@ $card = New-TeamsAdaptiveCard -Body @(
$message = New-TeamsMessage -Summary 'Build notification' -AdaptiveCard $card
$json = $message | ConvertTo-TeamsJson
```

Typed wrapper cards can also be composed as objects and rendered through `ConvertTo-TeamsJson`:

```powershell
$target = New-TeamsWebhookTarget -Uri 'https://example.test/webhook'
$heroCard = New-TeamsHeroCard -Title 'Seattle Center Monorail' -Images @(
New-TeamsCardImage -Url 'https://example.test/monorail.jpg' -AlternateText 'Monorail'
) -Buttons @(
New-CardListButton -Type OpenUrl -Title 'Official website' -Value 'https://example.test'
)

Send-TeamsMessage -HeroCard $heroCard -Target $target

$json = $heroCard | ConvertTo-TeamsJson
$wrapped = $json | Send-TeamsMessageBody -Uri 'https://example.test/webhook' -Wrap -Supress:$false -WhatIf
```

## Graph Starter

`main` now includes a starter Graph target cmdlet for chat and channel posts:

```powershell
$message = New-TeamsMessage -Title 'Build failed' -Text 'Pipeline 42 stopped.'
$target = New-TeamsGraphTarget -ChatId '19:testchat@thread.v2' -AccessTokenVariableName 'TEAMSX_GRAPH_TOKEN'

Send-TeamsMessage -Message $message -Target $target
```

Current scope:

- plain typed messages are rendered as Graph HTML message bodies
- adaptive cards are sent as Graph attachments
- adaptive cards should currently stick to `Action.OpenUrl`
- typed wrapper-card direct sending currently targets incoming and workflow webhooks only
- Graph targets can use a plain token, a secure string, or an environment-variable-backed token provider
- normal Graph chat/channel posting should use delegated tokens; application permissions are documented as migration-only for these endpoints
2 changes: 1 addition & 1 deletion Examples/Adaptive Card Samples/ActivityUpdate.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

# Based on: https://adaptivecards.io/samples/ActivityUpdate.html

Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card Samples/ExpenseReport.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

# Based on: https://adaptivecards.io/samples/ExpenseReport.html

Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card Samples/FlightItinerary.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

# Based on: https://adaptivecards.io/samples/FlightItinerary.html

Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card Samples/SportingEvent.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard -Uri $Env:TEAMSPESTERID -VerticalContentAlignment center {
New-AdaptiveContainer {
Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card Samples/StockUpdate.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard -Uri $Env:TEAMSPESTERID -VerticalContentAlignment center {
New-AdaptiveContainer {
Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card Samples/WeatherCompact.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

# Based on: https://adaptivecards.io/samples/WeatherCompact.html

Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card Samples/WeatherLarge.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

# Based on: https://adaptivecards.io/samples/WeatherLarge.html

Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card/AdaptiveCard-Actions01.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard -Uri $Env:TEAMSPESTERID -VerticalContentAlignment center {
New-AdaptiveTextBlock -Size ExtraLarge -Weight Bolder -Text 'Test' -Color Attention -HorizontalAlignment Center
Expand Down
4 changes: 3 additions & 1 deletion Examples/Adaptive Card/AdaptiveCard-FromJson.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
$Wrapper = @"
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

$Wrapper = @"
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card/AdaptiveCard-Images01.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard -Uri $Env:TEAMSPESTERID -VerticalContentAlignment center {
New-AdaptiveTextBlock -Size ExtraLarge -Weight Bolder -Text 'Test' -Color Attention -HorizontalAlignment Center
Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card/AdaptiveCard-Images02.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard -Uri $Env:TEAMSPESTERID -VerticalContentAlignment center {
New-AdaptiveTextBlock -Size ExtraLarge -Weight Bolder -Text 'Test' -Color Attention -HorizontalAlignment Center
Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card/AdaptiveCard-Native01.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard {
New-AdaptiveTextBlock -Size 'Medium' -Weight Bolder -Text 'Now that we have defined the main rules and features of the format, we need to produce a schema and publish it to GitHub. The schema will be the starting point of our reference documentation.' -Separator -Wrap
Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card/AdaptiveCard-Native02.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard -Uri $Env:TEAMSPESTERID {
New-AdaptiveTextBlock -Size 'Medium' -Weight Bolder -Text 'Test'
Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card/AdaptiveCard-Native03.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard -Uri $Env:TEAMSPESTERID {
New-AdaptiveContainer {
Expand Down
2 changes: 1 addition & 1 deletion Examples/Adaptive Card/AdaptiveCard-Native04.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Import-Module .\PSTeams.psd1 -Force
. (Join-Path $PSScriptRoot '..\Import-PSTeams.ps1')

New-AdaptiveCard -Uri $Env:TEAMSPESTERID -VerticalContentAlignment center {
New-AdaptiveTextBlock -Size ExtraLarge -Weight Bolder -Text 'Test' -Color Attention -HorizontalAlignment Center
Expand Down
Loading
Loading