Repository navigation
Release 1.1.1 #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
03b5646
Update Config Template and minor error message text adjustment.
Sekers f6a0a86
Minor template default update
Sekers a973d66
Check for dependent modules (if used by a messaging service)
Sekers b49866a
Minor message update
Sekers 4016568
Merge pull request #7 from Sekers/main
Sekers 207ce1a
README UPDATES ABOUT MGGRAPH MODULES USED
Sekers ff31824
Only require Microsoft.Graph.Files when using delegated permissions
Sekers b6ff3da
Add AI agent guidance and pin line endings to LF
Sekers 01713dd
Add Research_Notes folder for measured and documented behavior
Sekers 81dc9a3
Fix Send-ScriptMessage rejecting a one-item array of recipient objects
Sekers 8b796d8
Gate releases on tests and publish from annotated version tags
Sekers 1881674
Adopt Semantic Versioning, Keep a Changelog, and a documented release…
Sekers 3620016
Fix chat messages without attachments failing to send
Sekers e0b0230
Fix chat attachment filename escaping when uploading to OneDrive
Sekers f03d528
Fix Connect-ScriptMessage requiring Microsoft.Graph.Users.Actions for…
Sekers 4d8ccee
Default MgDisconnectWhenDone to false in the configuration template
Sekers c88393d
Complete the Unreleased changelog entries
Sekers 4a909b6
Fix Get-ScriptMessageConfig misreading non-ASCII text and bracketed p…
Sekers 5b3f530
Record how a PSCustomObject in a one-item array converts to a string
Sekers 4e90e29
Prepare release 1.1.1
Sekers 1e69f90
Merge pull request #8 from Sekers/feature/release-1.1.1
Sekers 82855a3
Keep the [Unreleased] changelog section off main
Sekers e2cad52
Merge pull request #10 from Sekers/feature/unreleased-only-on-develop
Sekers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| # Line-ending and diff rules for this repository. | ||
| # | ||
| # Goal: the module must load and behave identically under Windows PowerShell 5.1 (Windows only) and | ||
| # PowerShell 7.x+ on Windows, Linux, and macOS, no matter what each contributor's core.autocrlf is set to. | ||
| # | ||
| # Line endings do not affect how PowerShell parses a script (every supported host reads LF and CRLF | ||
| # equally), so these rules exist to keep checkouts byte-identical and diffs clean rather than to make the | ||
| # code work. Git stores text files with LF internally and applies the eol rules below on checkout. | ||
| # | ||
| # Every text type is pinned to LF, which is what Git already stores, so a checked-out file is byte for byte | ||
| # the repository's copy on every platform and whatever a contributor's core.autocrlf says. That leaves no | ||
| # conversion layer to reason about, which matters because Git hides line-ending differences: it normalizes | ||
| # before diffing, so a file whose endings are wrong shows no diff and leaves "git status" clean. | ||
| # | ||
| # CRLF would keep checkouts equally consistent and Windows PowerShell 5.1 reads either, but it reintroduces | ||
| # that conversion layer, and every tool here that is not PowerShell (grep, sed, diff, CI, the GitHub web | ||
| # view) prefers LF. | ||
|
|
||
| # Let Git detect text files and normalize them, unless overridden below. | ||
| * text=auto eol=lf | ||
|
|
||
| # PowerShell sources. | ||
| *.ps1 text eol=lf | ||
| *.psm1 text eol=lf | ||
| *.psd1 text eol=lf | ||
| *.ps1xml text eol=lf | ||
| *.psrc text eol=lf | ||
| *.pssc text eol=lf | ||
|
|
||
| # Docs, config, and data. Pinned too, so no text file in the tree depends on core.autocrlf. | ||
| *.md text eol=lf | ||
| *.txt text eol=lf | ||
| *.json text eol=lf | ||
| *.yml text eol=lf | ||
| *.yaml text eol=lf | ||
| *.csv text eol=lf | ||
| LICENSE text eol=lf | ||
|
|
||
| # Binaries. These must never be line-ending converted or treated as text. Git's auto-detection normally | ||
| # catches them, but stating it removes the guesswork. | ||
| *.dll binary | ||
| *.exe binary | ||
| *.nupkg binary | ||
| *.zip binary | ||
| *.docx binary | ||
| *.xlsx binary | ||
| *.pdf binary | ||
| *.png binary | ||
| *.jpg binary | ||
| *.jpeg binary | ||
| *.gif binary | ||
| *.ico binary | ||
| *.pfx binary | ||
| *.cer binary |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| ## Summary | ||
|
|
||
| <!-- What changes for someone using the module, and why? Link any related issue. --> | ||
|
|
||
| ## Release classification | ||
|
|
||
| <!-- Check one. RELEASING.md explains what counts as part of the public contract. --> | ||
|
|
||
| - [ ] Major: breaks the public contract (an existing script or configuration file can stop working) | ||
| - [ ] Minor: adds backward-compatible functionality | ||
| - [ ] Patch: a backward-compatible fix | ||
| - [ ] None: no user-visible change (tests, CI, or contributor documentation only) | ||
|
|
||
| ## Checklist | ||
|
|
||
| - [ ] Targets `develop`, or `main` for a hotfix | ||
| - [ ] `CHANGELOG.md` has an entry in the right section under `[Unreleased]` (added if missing), or the change is not user-visible | ||
| - [ ] `.\Tests\Invoke-Tests.ps1` passes | ||
| - [ ] Contains no real tenant names, email domains, secrets, or configuration files | ||
| - [ ] Any live Microsoft Graph testing was done in a test tenant and is described above |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| name: Release | ||
|
|
||
| # Publishes a release when a maintainer pushes a version tag, such as 1.2.0 or 2.0.0-rc01. RELEASING.md describes | ||
| # the whole process. The job order matters: nothing becomes public until the checks and tests pass, and the | ||
| # GitHub release is created only after the PowerShell Gallery accepts the package, so a failed publish never | ||
| # leaves behind a release for a version that did not ship. | ||
| on: | ||
| push: | ||
| tags: | ||
| - '[0-9]+.[0-9]+.[0-9]+' | ||
| - '[0-9]+.[0-9]+.[0-9]+-*' | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| # One run per tag, so two runs can never publish the same version at once. | ||
| concurrency: | ||
| group: release-${{ github.ref_name }} | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| validate: | ||
| name: Validate the release tag | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v7 | ||
| with: | ||
| # Full history, to confirm the tagged commit is on main (or on develop, for a prerelease). | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Check the tag against the manifest, changelog, and branch | ||
| shell: pwsh | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| TAG: ${{ github.ref_name }} | ||
| run: | | ||
| $ErrorActionPreference = 'Stop' | ||
| $Tag = $env:TAG | ||
|
|
||
| # Release tags must be annotated; a lightweight tag has no tagger, date, or message. | ||
| $TagObjectType = gh api "repos/$env:GITHUB_REPOSITORY/git/ref/tags/$Tag" --jq '.object.type' | ||
| if ($LASTEXITCODE -ne 0) { throw "Could not look up tag $Tag." } | ||
| if ($TagObjectType -ne 'tag') { throw "Tag $Tag is a lightweight tag. Release tags must be annotated (git tag -a)." } | ||
|
|
||
| # The tag must match the manifest version, including any prerelease label. | ||
| $Manifest = Import-PowerShellDataFile -Path 'ScriptMessage/ScriptMessage.psd1' | ||
| $Prerelease = $Manifest.PrivateData.PSData.Prerelease | ||
| $ManifestVersion = if ($Prerelease) { "$($Manifest.ModuleVersion)-$Prerelease" } else { "$($Manifest.ModuleVersion)" } | ||
| if ($ManifestVersion -ne $Tag) { throw "Tag $Tag does not match the manifest version $ManifestVersion." } | ||
|
|
||
| # The changelog must have a dated section for this version. | ||
| $HeadingPattern = '^## \[' + [regex]::Escape($Tag) + '\](\([^)]*\))? - \d{4}-\d{2}-\d{2}\s*$' | ||
| if (-not (Select-String -Path 'CHANGELOG.md' -Pattern $HeadingPattern -Quiet)) { throw "CHANGELOG.md has no dated section for $Tag, such as '## [$Tag] - 2026-01-31'." } | ||
|
|
||
| # [Unreleased] exists only on develop, so a released CHANGELOG.md never carries one. | ||
| if (Select-String -Path 'CHANGELOG.md' -Pattern '^## \[Unreleased\]' -Quiet) { throw "CHANGELOG.md still has an [Unreleased] section. The release-prep pull request renames it to the $Tag heading and adds no new one." } | ||
|
|
||
| # Stable releases come only from main; prereleases may also come from develop. | ||
| $AllowedBranches = if ($Prerelease) { @('origin/main', 'origin/develop') } else { @('origin/main') } | ||
| $OnAllowedBranch = $false | ||
| foreach ($Branch in $AllowedBranches) { | ||
| git merge-base --is-ancestor $env:GITHUB_SHA $Branch | ||
| if ($LASTEXITCODE -eq 0) { $OnAllowedBranch = $true } | ||
| } | ||
| if (-not $OnAllowedBranch) { throw "Tag $Tag points at $env:GITHUB_SHA, which is not on $($AllowedBranches -join ' or ')." } | ||
|
|
||
| "Tag $Tag is annotated, matches the manifest and changelog, and is on $($AllowedBranches -join ' or ')." | ||
| # git merge-base leaves a non-zero exit code when the commit is not on one of the branches checked. | ||
| exit 0 | ||
|
|
||
| test: | ||
| name: Tests | ||
| needs: validate | ||
| uses: ./.github/workflows/Tests.yml | ||
|
|
||
| publish: | ||
| name: Publish to the PowerShell Gallery | ||
| needs: test | ||
| runs-on: windows-latest | ||
| # The PowerShell Gallery API key is stored only in this environment, which can require a maintainer's approval. | ||
| environment: psgallery | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - name: Build the package | ||
| shell: pwsh | ||
| run: | | ||
| $ErrorActionPreference = 'Stop' | ||
| New-Item -ItemType Directory -Force -Path 'out' | Out-Null | ||
| Compress-PSResource -Path './ScriptMessage' -DestinationPath './out' | ||
| $Packages = @(Get-ChildItem -Path './out' -Filter '*.nupkg') | ||
| if ($Packages.Count -ne 1) { throw "Expected one package, found $($Packages.Count)." } | ||
| "Built $($Packages[0].Name) ($($Packages[0].Length) bytes)." | ||
|
|
||
| - name: Publish the package | ||
| shell: pwsh | ||
| env: | ||
| PS_GALLERY_KEY: ${{ secrets.PS_GALLERY_KEY }} | ||
| run: | | ||
| $ErrorActionPreference = 'Stop' | ||
| $Package = Get-ChildItem -Path './out' -Filter '*.nupkg' | Select-Object -First 1 | ||
| Publish-PSResource -NupkgPath $Package.FullName -Repository 'PSGallery' -ApiKey $env:PS_GALLERY_KEY | ||
|
|
||
| # The GitHub release job attaches this exact package instead of building its own. | ||
| - name: Keep the package for the GitHub release | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: package | ||
| path: out/*.nupkg | ||
| if-no-files-found: error | ||
|
|
||
| github_release: | ||
| name: Create the GitHub release | ||
| needs: publish | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - name: Get the published package | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
| name: package | ||
| path: out | ||
|
|
||
| - name: Create and publish the release | ||
| shell: pwsh | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| TAG: ${{ github.ref_name }} | ||
| run: | | ||
| $ErrorActionPreference = 'Stop' | ||
| $Tag = $env:TAG | ||
|
|
||
| # The release notes are this version's CHANGELOG.md section, without its heading or trailing separator. | ||
| $Lines = @(Get-Content -Path 'CHANGELOG.md') | ||
| $HeadingPattern = '^## \[' + [regex]::Escape($Tag) + '\]' | ||
| $HeadingIndex = -1 | ||
| for ($Index = 0; $Index -lt $Lines.Count; $Index++) { | ||
| if ($Lines[$Index] -match $HeadingPattern) { $HeadingIndex = $Index; break } | ||
| } | ||
| if ($HeadingIndex -lt 0) { throw "CHANGELOG.md has no section for $Tag." } | ||
| $SectionLines = @() | ||
| if ($HeadingIndex -lt ($Lines.Count - 1)) { | ||
| $SectionLines = foreach ($Line in $Lines[($HeadingIndex + 1)..($Lines.Count - 1)]) { | ||
| if ($Line -match '^## \[') { break } | ||
| $Line | ||
| } | ||
| } | ||
| $Notes = ((@($SectionLines) -join "`n").Trim() -replace '\s*-{3,}\s*$', '').Trim() | ||
| Set-Content -Path 'release-notes.md' -Value $Notes -Encoding utf8 | ||
|
|
||
| # A re-run of this job finds the release already created. Only a draft still needs its package and publishing. | ||
| $Existing = gh release view $Tag --json isDraft 2>$null | ||
| if ($LASTEXITCODE -ne 0) { | ||
| $CreateArguments = @('release', 'create', $Tag, '--verify-tag', '--draft', '--title', $Tag, '--notes-file', 'release-notes.md') | ||
| if ($Tag.Contains('-')) { $CreateArguments += '--prerelease' } | ||
| gh @CreateArguments | ||
| if ($LASTEXITCODE -ne 0) { throw "Could not create the release for $Tag." } | ||
| } | ||
| elseif (-not ($Existing | ConvertFrom-Json).isDraft) { | ||
| "The release for $Tag is already published." | ||
| exit 0 | ||
| } | ||
|
|
||
| foreach ($Package in Get-ChildItem -Path 'out' -Filter '*.nupkg') { | ||
| gh release upload $Tag $Package.FullName --clobber | ||
| if ($LASTEXITCODE -ne 0) { throw "Could not attach $($Package.Name) to the release for $Tag." } | ||
| } | ||
|
|
||
| gh release edit $Tag --draft=false | ||
| if ($LASTEXITCODE -ne 0) { throw "Could not publish the release for $Tag." } | ||
| exit 0 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| name: Tests | ||
|
|
||
| # Runs the Pester suite under Windows PowerShell 5.1 and PowerShell 7, and lints the workflow files. Nothing here | ||
| # connects to a tenant or needs credentials. | ||
| on: | ||
| # Branch pushes only. A release tag runs this suite through the Release workflow instead. | ||
| push: | ||
| branches: ['**'] | ||
| pull_request: | ||
| # Lets the Release workflow gate publishing on this same suite instead of repeating its steps. | ||
| workflow_call: | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| # Catches what a YAML parser cannot: a job that needs one that does not exist, a reusable-workflow path that | ||
| # resolves to nothing, an unknown runner label, a retired action version. Pinned to a version rather than | ||
| # :latest so a workflow that passes today does not start failing on someone else's release. | ||
| lint: | ||
| name: Workflow lint | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - name: Check workflow files | ||
| uses: docker://rhysd/actionlint:1.7.12 | ||
| with: | ||
| args: -color | ||
|
|
||
| pester: | ||
| name: Pester | ||
| # Windows, because Windows PowerShell 5.1 exists nowhere else. Invoke-Tests.ps1 runs both editions itself. | ||
| runs-on: windows-latest | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| # The module tests run PSScriptAnalyzer when it is installed. Pinned so a new analyzer rule cannot fail a | ||
| # build that passed before. | ||
| - name: Install PSScriptAnalyzer | ||
| run: Install-PSResource -Name PSScriptAnalyzer -Version 1.25.0 -TrustRepository | ||
| shell: pwsh | ||
|
|
||
| - name: Run tests | ||
| # -UsePinnedPester so both editions run the same Pester version, whatever the runner has installed. | ||
| run: .\Tests\Invoke-Tests.ps1 -UsePinnedPester | ||
| shell: pwsh |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.