@@ -362,6 +362,7 @@ jobs:
362362 uses : softprops/action-gh-release@v2
363363 if : startsWith(github.ref, 'refs/tags/')
364364 with :
365+ generate_release_notes : true
365366 files : |
366367 CopyPaste-${{ steps.get_version.outputs.VERSION }}-${{ matrix.arch }}-setup.exe
367368 env :
@@ -506,14 +507,15 @@ jobs:
506507 GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
507508
508509 # ─── Microsoft Store Submission ──────────────────────────────────────────
509- # DISABLED: First Store submission must be done manually via Partner Center.
510- # To re-enable: change the 'if' condition below to:
511- # if: startsWith(github.ref, 'refs/tags/')
510+ # Automatically submits MSIX packages to the Microsoft Store.
511+ # Only runs on stable version tags (no pre-release).
512+ # Requires these GitHub secrets: STORE_TENANT_ID, STORE_CLIENT_ID, STORE_CLIENT_SECRET
513+ # And this GitHub variable: STORE_APP_ID
512514 store-publish :
513515 runs-on : windows-latest
514516 needs : store
515517 timeout-minutes : 15
516- if : false # Disabled until first manual Store submission is approved
518+ if : false # startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-')
517519
518520 env :
519521 STORE_TENANT_ID : ${{ secrets.STORE_TENANT_ID }}
@@ -545,6 +547,57 @@ jobs:
545547 echo "HAS_CREDS=true" >> $env:GITHUB_OUTPUT
546548 }
547549
550+ - name : Extract version from tag
551+ if : steps.check_creds.outputs.HAS_CREDS == 'true'
552+ id : get_version
553+ shell : pwsh
554+ run : |
555+ if ($env:GITHUB_REF -match 'refs/tags/v(.+)') {
556+ $version = $matches[1]
557+ } else {
558+ $version = "1.0.0"
559+ }
560+ echo "VERSION=$version" >> $env:GITHUB_OUTPUT
561+
562+ - name : Get release notes from GitHub Release
563+ if : steps.check_creds.outputs.HAS_CREDS == 'true'
564+ id : release_notes
565+ shell : pwsh
566+ env :
567+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
568+ run : |
569+ $tag = "v${{ steps.get_version.outputs.VERSION }}"
570+ $headers = @{
571+ "Authorization" = "token $env:GH_TOKEN"
572+ "Accept" = "application/vnd.github.v3+json"
573+ }
574+
575+ try {
576+ $release = Invoke-RestMethod `
577+ -Uri "https://api.github.com/repos/${{ github.repository }}/releases/tags/$tag" `
578+ -Headers $headers
579+
580+ $body = $release.body
581+ if ([string]::IsNullOrWhiteSpace($body)) {
582+ $body = "Bug fixes and improvements."
583+ }
584+
585+ # Store listing 'What's new' supports max 1500 characters
586+ if ($body.Length -gt 1500) {
587+ $body = $body.Substring(0, 1497) + "..."
588+ }
589+
590+ # Write multiline output using heredoc delimiter (GitHub Actions recommended)
591+ $delimiter = "RELEASE_NOTES_EOF"
592+ echo "NOTES<<$delimiter" >> $env:GITHUB_OUTPUT
593+ echo $body >> $env:GITHUB_OUTPUT
594+ echo $delimiter >> $env:GITHUB_OUTPUT
595+ Write-Host "Release notes extracted ($($body.Length) chars)"
596+ } catch {
597+ Write-Warning "Failed to fetch release notes: $($_.Exception.Message)"
598+ echo "NOTES=Bug fixes and improvements." >> $env:GITHUB_OUTPUT
599+ }
600+
548601 - name : Download MSIX packages
549602 if : steps.check_creds.outputs.HAS_CREDS == 'true'
550603 uses : actions/download-artifact@v4
@@ -573,15 +626,52 @@ jobs:
573626 --clientId $env:STORE_CLIENT_ID `
574627 --clientSecret $env:STORE_CLIENT_SECRET
575628
576- - name : Submit to Microsoft Store
629+ - name : Upload packages (without committing)
577630 if : steps.check_creds.outputs.HAS_CREDS == 'true'
578631 shell : pwsh
579632 run : |
580633 $packages = Get-ChildItem -Path packages -Include "*.msixupload","*.msix" -Recurse
581- Write-Host "Submitting $($packages.Count) package(s) to Microsoft Store..."
634+ Write-Host "Uploading $($packages.Count) package(s) to Microsoft Store..."
582635 $packages | ForEach-Object {
583636 Write-Host " - $($_.Name) ($([math]::Round($_.Length / 1MB, 2)) MB)"
584637 }
585638
586- $packagePaths = $packages | Select-Object -ExpandProperty FullName
587- msstore publish --inputDirectory packages --id ${{ vars.STORE_APP_ID }}
639+ if ($packages.Count -eq 0) {
640+ Write-Error "No MSIX packages found in packages directory"
641+ exit 1
642+ }
643+
644+ # msstore publish requires a path/URL as first argument
645+ $firstPackage = $packages[0].FullName
646+ msstore publish $firstPackage --inputDirectory packages --appId ${{ vars.STORE_APP_ID }} --noCommit --verbose
647+
648+ - name : Update Store listing with release notes
649+ if : steps.check_creds.outputs.HAS_CREDS == 'true'
650+ shell : pwsh
651+ env :
652+ RELEASE_NOTES : ${{ steps.release_notes.outputs.NOTES }}
653+ run : |
654+ $appId = "${{ vars.STORE_APP_ID }}"
655+ $notes = $env:RELEASE_NOTES
656+
657+ if ([string]::IsNullOrWhiteSpace($notes)) {
658+ $notes = "Bug fixes and improvements."
659+ }
660+
661+ # Escape special characters for JSON
662+ $escapedNotes = $notes -replace '\\', '\\\\' -replace '"', '\"' -replace "`r", '' -replace "`n", '\n'
663+
664+ # Construct metadata JSON directly (PascalCase required for packaged apps)
665+ $metadataJson = '{"Listings":{"en-us":{"BaseListing":{"ReleaseNotes":"' + $escapedNotes + '"}}}}'
666+
667+ Write-Host "Updating Store listing with release notes ($($notes.Length) chars)..."
668+ msstore submission updateMetadata $appId $metadataJson --skipInitialPolling
669+ Write-Host "Store listing metadata updated"
670+
671+ - name : Commit and publish submission
672+ if : steps.check_creds.outputs.HAS_CREDS == 'true'
673+ shell : pwsh
674+ run : |
675+ Write-Host "Committing Store submission..."
676+ msstore submission publish ${{ vars.STORE_APP_ID }}
677+ Write-Host "Store submission committed - awaiting certification"
0 commit comments