From 680ea86a1604287a25f470c6b6272751f5c49b1c Mon Sep 17 00:00:00 2001 From: HelloThisWorld Date: Sat, 18 Jul 2026 16:28:19 +0800 Subject: [PATCH 1/2] release: prepare self-signed v1.0.1 --- .github/ISSUE_TEMPLATE/Bug_Report.yml | 2 +- ...ae324e5b5abd40a06a0506a07edb2f31fc1d62.txt | 2 +- .github/workflows/release.yml | 353 ++++-------------- .github/workflows/winget.yml | 24 +- .gitignore | 1 + CHANGELOG.md | 20 + README.md | 28 +- SECURITY.md | 2 +- SUPPORT.md | 2 +- docs/architecture.md | 2 +- docs/branding.md | 12 +- docs/build.md | 22 +- docs/compatibility-policy.md | 2 +- docs/powershell-integration.md | 2 +- docs/release-checklist-v1.0.md | 28 +- docs/release-process.md | 31 +- docs/releases/1.0.0.md | 2 +- docs/releases/1.0.1.md | 81 ++++ docs/schema-support-policy.md | 2 +- docs/user/installation.md | 12 +- docs/v0.1-acceptance.md | 4 +- docs/v0.3-acceptance.md | 2 +- docs/v0.4-acceptance.md | 2 +- docs/v0.5-acceptance.md | 2 +- docs/v1.0-progress.md | 14 +- docs/visual-docking-spike.md | 2 +- docs/workspace-schema.md | 2 +- packaging/winget/README.md | 6 +- scripts/winterm/benchmark-workspace.ps1 | 2 +- scripts/winterm/build-local-development.ps1 | 103 +++-- scripts/winterm/create-msixbundle.ps1 | 4 +- .../winterm/generate-release-artifacts.ps1 | 38 +- .../generate-stable-update-manifest.ps1 | 8 +- scripts/winterm/generate-winget-manifests.ps1 | 28 +- scripts/winterm/package-shell-assets.ps1 | 2 +- scripts/winterm/package.ps1 | 8 +- scripts/winterm/set-package-publisher.ps1 | 2 +- scripts/winterm/test-release-workflow.ps1 | 13 +- scripts/winterm/test.ps1 | 2 +- scripts/winterm/verify-branding.ps1 | 4 +- scripts/winterm/verify-release-assets.ps1 | 197 ++++++++-- scripts/winterm/verify-version.ps1 | 28 +- .../winTerm.Shell/winTerm.Shell.psd1 | 2 +- .../winTerm.Shell/winTerm.Shell.psm1 | 2 +- shell/shared/version.json | 4 +- .../Package-winTerm.appxmanifest | 4 +- src/winterm/Branding/ReleaseMetadata.h | 2 +- src/winterm/Branding/version.json | 8 +- .../Workspaces/Model/WorkspaceDescriptor.h | 2 +- .../Persistence/WorkspaceMigration.cpp | 2 +- .../Persistence/WorkspaceSerializer.cpp | 2 +- 51 files changed, 621 insertions(+), 510 deletions(-) create mode 100644 docs/releases/1.0.1.md diff --git a/.github/ISSUE_TEMPLATE/Bug_Report.yml b/.github/ISSUE_TEMPLATE/Bug_Report.yml index 4702e9352..fb56992ff 100644 --- a/.github/ISSUE_TEMPLATE/Bug_Report.yml +++ b/.github/ISSUE_TEMPLATE/Bug_Report.yml @@ -11,7 +11,7 @@ body: id: version attributes: label: winTerm version - placeholder: 1.0.0 or exact commit + placeholder: 1.0.1 or exact commit validations: required: true - type: dropdown diff --git a/.github/actions/spelling/expect/82ae324e5b5abd40a06a0506a07edb2f31fc1d62.txt b/.github/actions/spelling/expect/82ae324e5b5abd40a06a0506a07edb2f31fc1d62.txt index ca0df56a3..f33252223 100644 --- a/.github/actions/spelling/expect/82ae324e5b5abd40a06a0506a07edb2f31fc1d62.txt +++ b/.github/actions/spelling/expect/82ae324e5b5abd40a06a0506a07edb2f31fc1d62.txt @@ -5,7 +5,7 @@ dracula fira gitmodules jetbrains -Kaname +HelloThisWorld keycap LANCZOS latte diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b33f7997..bea324dd1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,43 +1,21 @@ -name: winTerm stable release +name: winTerm self-signed stable release on: push: tags: - - 'v1.0.0' - workflow_dispatch: - inputs: - mode: - description: Prepare a new draft or publish an already verified signed draft - required: true - default: prepare - type: choice - options: - - prepare - - publish - publish_arm64: - description: Include ARM64 only after native build, install, and launch validation - required: true - default: false - type: boolean - arm64_validation_commit: - description: Exact v1.0.0 commit validated on native ARM64 hardware - required: false - type: string - validated_commit_sha: - description: Exact v1.0.0 commit that passed clean install, upgrade, uninstall, alias, and coexistence validation - required: false - type: string + - 'v1.0.1' permissions: contents: read concurrency: - group: winterm-v1.0.0-stable-release + group: winterm-v1.0.1-stable-release cancel-in-progress: false env: - WINTERM_VERSION: 1.0.0 - WINTERM_TAG: v1.0.0 + WINTERM_VERSION: 1.0.1 + WINTERM_TAG: v1.0.1 + WINTERM_PUBLISHER: CN=winTerm Development WINTERM_UPSTREAM_REVISION: 1cea42d433253d95c4487a3037db48197b5e72f4 jobs: @@ -55,12 +33,12 @@ jobs: - name: Verify tag, commit, and clean checkout shell: pwsh run: | - if ('${{ github.ref }}' -ne 'refs/tags/v1.0.0') { - throw 'Stable release runs must check out refs/tags/v1.0.0.' + if ('${{ github.ref }}' -ne 'refs/tags/v1.0.1') { + throw 'Stable release runs must check out refs/tags/v1.0.1.' } - $tagCommit = (git rev-list -n 1 v1.0.0).Trim() + $tagCommit = (git rev-list -n 1 v1.0.1).Trim() if ($tagCommit -ne '${{ github.sha }}') { - throw 'v1.0.0 does not point to the checked-out release commit.' + throw 'v1.0.1 does not point to the checked-out release commit.' } if (-not [string]::IsNullOrWhiteSpace((git status --porcelain))) { throw 'The release checkout is not clean.' @@ -73,7 +51,6 @@ jobs: test-x64: name: Build and test x64 Release - if: github.event_name == 'push' || inputs.mode == 'prepare' needs: validate runs-on: windows-2022 timeout-minutes: 240 @@ -91,15 +68,13 @@ jobs: shell: pwsh run: .\scripts\winterm\test.ps1 -Suite Relevant -Configuration Release -Platform x64 - prepare: - name: Build, sign, attest, and verify Draft Release - if: github.event_name == 'push' || inputs.mode == 'prepare' + release: + name: Build, self-sign, attest, and publish needs: - validate - test-x64 runs-on: windows-2022 timeout-minutes: 300 - environment: winterm-stable-release permissions: contents: write id-token: write @@ -124,149 +99,55 @@ jobs: -BuildTimestamp $env:WINTERM_BUILD_TIMESTAMP ` -WorkflowRunId '${{ github.run_id }}' - - name: Select signing mode and protect the package publisher - id: signing + - name: Build and inspect the unsigned x64 MSIX shell: pwsh - env: - SIGNING_PFX_BASE64: ${{ secrets.WINTERM_SIGNING_PFX_BASE64 }} - SIGNING_PFX_PASSWORD: ${{ secrets.WINTERM_SIGNING_PFX_PASSWORD }} - PRODUCTION_PUBLISHER: ${{ vars.WINTERM_PACKAGE_PUBLISHER }} - TIMESTAMP_URL: ${{ vars.WINTERM_TIMESTAMP_URL }} run: | - $values = @($env:SIGNING_PFX_BASE64, $env:SIGNING_PFX_PASSWORD, $env:PRODUCTION_PUBLISHER, $env:TIMESTAMP_URL) - $present = @($values | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }).Count - if ($present -ne 0 -and $present -ne $values.Count) { - throw 'Signing configuration is incomplete. Configure all protected signing values or none.' - } - - if ($present -eq $values.Count) { - .\scripts\winterm\set-package-publisher.ps1 -Publisher $env:PRODUCTION_PUBLISHER - $certificatePath = Join-Path $env:RUNNER_TEMP 'winterm-release-signing.pfx' - [IO.File]::WriteAllBytes($certificatePath, [Convert]::FromBase64String($env:SIGNING_PFX_BASE64)) - "WINTERM_CERTIFICATE_PATH=$certificatePath" >> $env:GITHUB_ENV - "WINTERM_PUBLISHER=$($env:PRODUCTION_PUBLISHER)" >> $env:GITHUB_ENV - "WINTERM_TIMESTAMP_URL=$($env:TIMESTAMP_URL)" >> $env:GITHUB_ENV - 'WINTERM_SIGNING_STATUS=valid-production-signature' >> $env:GITHUB_ENV - 'signed=true' >> $env:GITHUB_OUTPUT - } else { - 'WINTERM_PUBLISHER=CN=winTerm Development' >> $env:GITHUB_ENV - 'WINTERM_SIGNING_STATUS=unsigned-draft' >> $env:GITHUB_ENV - 'signed=false' >> $env:GITHUB_OUTPUT - } - - - name: Build and inspect x64 MSIX - shell: pwsh - run: | - .\scripts\winterm\package.ps1 -Platform x64 -ExpectedPublisher $env:WINTERM_PUBLISHER + .\scripts\winterm\package.ps1 ` + -Platform x64 ` + -ExpectedPublisher $env:WINTERM_PUBLISHER $packages = @(Get-ChildItem -LiteralPath 'src\cascadia\CascadiaPackage\AppPackages' -Recurse -File -Filter '*.msix' | Where-Object { $_.FullName -match '(?i)x64' }) if ($packages.Count -ne 1) { throw "Expected one x64 MSIX, found $($packages.Count)." } - "WINTERM_X64_PACKAGE=$($packages[0].FullName)" >> $env:GITHUB_ENV - - - name: Sign and timestamp x64 MSIX - if: steps.signing.outputs.signed == 'true' - shell: pwsh - env: - SIGNING_PFX_PASSWORD: ${{ secrets.WINTERM_SIGNING_PFX_PASSWORD }} - run: | - .\scripts\winterm\sign-release-artifacts.ps1 ` - -Path $env:WINTERM_X64_PACKAGE ` - -CertificatePath $env:WINTERM_CERTIFICATE_PATH ` - -CertificatePassword $env:SIGNING_PFX_PASSWORD ` - -ExpectedPublisher $env:WINTERM_PUBLISHER ` - -TimestampUrl $env:WINTERM_TIMESTAMP_URL - - - name: Require native ARM64 validation evidence - if: inputs.publish_arm64 == true - shell: pwsh - env: - ARM64_VALIDATION_COMMIT: ${{ inputs.arm64_validation_commit }} - run: | - if ($env:ARM64_VALIDATION_COMMIT -ne '${{ github.sha }}') { - throw 'ARM64 publication requires native build, install, and launch evidence for the exact release commit.' - } + "WINTERM_UNSIGNED_X64_PACKAGE=$($packages[0].FullName)" >> $env:GITHUB_ENV - - name: Build and inspect ARM64 MSIX - if: inputs.publish_arm64 == true + - name: Create and verify the self-signed installer shell: pwsh run: | - .\scripts\winterm\package.ps1 -Platform ARM64 -ExpectedPublisher $env:WINTERM_PUBLISHER - $packages = @(Get-ChildItem -LiteralPath 'src\cascadia\CascadiaPackage\AppPackages' -Recurse -File -Filter '*.msix' | - Where-Object { $_.FullName -match '(?i)arm64' }) - if ($packages.Count -ne 1) { - throw "Expected one ARM64 MSIX, found $($packages.Count)." - } - "WINTERM_ARM64_PACKAGE=$($packages[0].FullName)" >> $env:GITHUB_ENV - - - name: Sign and timestamp ARM64 MSIX - if: inputs.publish_arm64 == true && steps.signing.outputs.signed == 'true' - shell: pwsh - env: - SIGNING_PFX_PASSWORD: ${{ secrets.WINTERM_SIGNING_PFX_PASSWORD }} - run: | - .\scripts\winterm\sign-release-artifacts.ps1 ` - -Path $env:WINTERM_ARM64_PACKAGE ` - -CertificatePath $env:WINTERM_CERTIFICATE_PATH ` - -CertificatePassword $env:SIGNING_PFX_PASSWORD ` - -ExpectedPublisher $env:WINTERM_PUBLISHER ` - -TimestampUrl $env:WINTERM_TIMESTAMP_URL - - - name: Create and sign the multi-architecture bundle - if: inputs.publish_arm64 == true - shell: pwsh - env: - SIGNING_PFX_PASSWORD: ${{ secrets.WINTERM_SIGNING_PFX_PASSWORD }} - run: | - $bundle = Join-Path $env:RUNNER_TEMP 'winTerm-1.0.0.msixbundle' - .\scripts\winterm\create-msixbundle.ps1 ` - -X64PackagePath $env:WINTERM_X64_PACKAGE ` - -Arm64PackagePath $env:WINTERM_ARM64_PACKAGE ` - -OutputPath $bundle - if ('${{ steps.signing.outputs.signed }}' -eq 'true') { - .\scripts\winterm\sign-release-artifacts.ps1 ` - -Path $bundle ` - -CertificatePath $env:WINTERM_CERTIFICATE_PATH ` - -CertificatePassword $env:SIGNING_PFX_PASSWORD ` - -ExpectedPublisher $env:WINTERM_PUBLISHER ` - -TimestampUrl $env:WINTERM_TIMESTAMP_URL - } - "WINTERM_BUNDLE=$bundle" >> $env:GITHUB_ENV + $output = Join-Path $env:RUNNER_TEMP 'winterm-self-signed-release' + .\scripts\winterm\build-local-development.ps1 ` + -PackagePath $env:WINTERM_UNSIGNED_X64_PACKAGE ` + -OutputDirectory $output ` + -ReleaseAsset + "WINTERM_X64_PACKAGE=$(Join-Path $output 'winTerm-1.0.1-x64.msix')" >> $env:GITHUB_ENV + "WINTERM_PUBLIC_CERTIFICATE=$(Join-Path $output 'winTerm-1.0.1.cer')" >> $env:GITHUB_ENV + "WINTERM_INSTALL_INSTRUCTIONS=$(Join-Path $output 'INSTALL.txt')" >> $env:GITHUB_ENV - name: Generate allowlisted release assets, SBOMs, symbols, and checksums shell: pwsh run: | - $arguments = @{ - Version = '1.0.0' - X64PackagePath = $env:WINTERM_X64_PACKAGE - SymbolsDirectory = 'bin\x64\Release' - OutputDirectory = 'artifacts\release' - CommitSha = '${{ github.sha }}' - UpstreamRevision = $env:WINTERM_UPSTREAM_REVISION - BuildTimestamp = $env:WINTERM_BUILD_TIMESTAMP - WorkflowRunId = '${{ github.run_id }}' - SigningStatus = $env:WINTERM_SIGNING_STATUS - } - if ('${{ inputs.publish_arm64 }}' -eq 'true') { - $arguments.Arm64PackagePath = $env:WINTERM_ARM64_PACKAGE - $arguments.BundlePath = $env:WINTERM_BUNDLE - } - .\scripts\winterm\generate-release-artifacts.ps1 @arguments + .\scripts\winterm\generate-release-artifacts.ps1 ` + -Version '1.0.1' ` + -X64PackagePath $env:WINTERM_X64_PACKAGE ` + -CertificatePath $env:WINTERM_PUBLIC_CERTIFICATE ` + -InstallationInstructionsPath $env:WINTERM_INSTALL_INSTRUCTIONS ` + -SymbolsDirectory 'bin\x64\Release' ` + -OutputDirectory 'artifacts\release' ` + -CommitSha '${{ github.sha }}' ` + -UpstreamRevision $env:WINTERM_UPSTREAM_REVISION ` + -BuildTimestamp $env:WINTERM_BUILD_TIMESTAMP ` + -WorkflowRunId '${{ github.run_id }}' ` + -SigningStatus self-signed - name: Verify generated release assets shell: pwsh run: | - $architectures = @('x64') - if ('${{ inputs.publish_arm64 }}' -eq 'true') { - $architectures += 'arm64' - } - $arguments = @{ Directory = 'artifacts\release'; Architecture = $architectures } - if ('${{ steps.signing.outputs.signed }}' -eq 'true') { - $arguments.RequireSigned = $true - $arguments.ExpectedPublisher = $env:WINTERM_PUBLISHER - } - .\scripts\winterm\verify-release-assets.ps1 @arguments + .\scripts\winterm\verify-release-assets.ps1 ` + -Directory 'artifacts\release' ` + -Architecture x64 ` + -RequireSelfSigned ` + -ExpectedPublisher $env:WINTERM_PUBLISHER - name: Generate GitHub artifact attestations uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 @@ -279,37 +160,34 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - gh release view v1.0.0 --repo '${{ github.repository }}' *> $null + gh release view v1.0.1 --repo '${{ github.repository }}' *> $null if ($LASTEXITCODE -eq 0) { - throw 'A v1.0.0 Release already exists. This workflow never replaces or clobbers Release assets.' + throw 'A v1.0.1 Release already exists. This workflow never replaces or clobbers Release assets.' } - $notesPath = Join-Path $env:RUNNER_TEMP 'winTerm-1.0.0-release-notes.md' - Copy-Item -LiteralPath 'docs\releases\1.0.0.md' -Destination $notesPath - if ($env:WINTERM_SIGNING_STATUS -eq 'unsigned-draft') { - Add-Content -LiteralPath $notesPath -Value "`n> **Release blocker:** this Draft contains an unsigned development package. It must not be published or presented as a stable installer." - } else { - Add-Content -LiteralPath $notesPath -Value "`nSignature gate: production-signed and timestamped for publisher ``$env:WINTERM_PUBLISHER``." - } + $notesPath = Join-Path $env:RUNNER_TEMP 'winTerm-1.0.1-release-notes.md' + Copy-Item -LiteralPath 'docs\releases\1.0.1.md' -Destination $notesPath + Add-Content -LiteralPath $notesPath -Value @' + +> **Self-signed installer:** download `winTerm-1.0.1-x64.msix`, `winTerm-1.0.1.cer`, `INSTALL.txt`, and `SHA256SUMS.txt`. Windows does not trust this certificate by default. Verify the hashes and repository URL before importing the certificate into Trusted People. +'@ $assets = @( - 'artifacts\release\winTerm-1.0.0-x64.msix', + 'artifacts\release\winTerm-1.0.1-x64.msix', + 'artifacts\release\winTerm-1.0.1.cer', + 'artifacts\release\INSTALL.txt', 'artifacts\release\SHA256SUMS.txt', 'artifacts\release\THIRD_PARTY_NOTICES.md', 'artifacts\release\SBOM.spdx.json', 'artifacts\release\SBOM.cyclonedx.json', 'artifacts\release\release-metadata.json', - 'artifacts\release\winTerm-1.0.0-symbols.zip', - 'artifacts\release\winTerm-1.0.0-release-notes.md' + 'artifacts\release\winTerm-1.0.1-symbols.zip', + 'artifacts\release\winTerm-1.0.1-release-notes.md' ) - if ('${{ inputs.publish_arm64 }}' -eq 'true') { - $assets += 'artifacts\release\winTerm-1.0.0-arm64.msix' - $assets += 'artifacts\release\winTerm-1.0.0.msixbundle' - } - gh release create v1.0.0 @assets ` + gh release create v1.0.1 @assets ` --repo '${{ github.repository }}' ` --verify-tag ` - --title 'winTerm 1.0.0' ` + --title 'winTerm 1.0.1' ` --notes-file $notesPath ` --draft if ($LASTEXITCODE -ne 0) { @@ -323,107 +201,31 @@ jobs: run: | $verificationDirectory = Join-Path $env:RUNNER_TEMP 'winterm-draft-download' New-Item -ItemType Directory -Path $verificationDirectory | Out-Null - gh release download v1.0.0 --repo '${{ github.repository }}' --dir $verificationDirectory + gh release download v1.0.1 --repo '${{ github.repository }}' --dir $verificationDirectory if ($LASTEXITCODE -ne 0) { throw 'Draft Release asset download failed.' } - $architectures = @('x64') - if ('${{ inputs.publish_arm64 }}' -eq 'true') { - $architectures += 'arm64' - } - $arguments = @{ Directory = $verificationDirectory; Architecture = $architectures } - if ('${{ steps.signing.outputs.signed }}' -eq 'true') { - $arguments.RequireSigned = $true - $arguments.ExpectedPublisher = $env:WINTERM_PUBLISHER - } - .\scripts\winterm\verify-release-assets.ps1 @arguments - gh attestation verify (Join-Path $verificationDirectory 'winTerm-1.0.0-x64.msix') --repo '${{ github.repository }}' - if ($LASTEXITCODE -ne 0) { - throw 'Artifact attestation verification failed.' - } - gh release view v1.0.0 --repo '${{ github.repository }}' --json tagName,name,isDraft,isPrerelease,assets,url - - - name: Remove the ephemeral signing file - if: always() - shell: pwsh - run: | - if (-not [string]::IsNullOrWhiteSpace($env:WINTERM_CERTIFICATE_PATH) -and - (Test-Path -LiteralPath $env:WINTERM_CERTIFICATE_PATH)) { - Remove-Item -LiteralPath $env:WINTERM_CERTIFICATE_PATH -Force - } - - publish: - name: Publish an already verified signed Draft - if: github.event_name == 'workflow_dispatch' && inputs.mode == 'publish' - needs: validate - runs-on: windows-2022 - timeout-minutes: 45 - environment: winterm-stable-release - permissions: - contents: write - id-token: write - attestations: read - steps: - - name: Check out the tagged source without persisted credentials - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Require exact clean-machine validation evidence - shell: pwsh - env: - VALIDATED_COMMIT_SHA: ${{ inputs.validated_commit_sha }} - PRODUCTION_PUBLISHER: ${{ vars.WINTERM_PACKAGE_PUBLISHER }} - run: | - if ($env:VALIDATED_COMMIT_SHA -ne '${{ github.sha }}') { - throw 'Publication requires clean install, upgrade, uninstall, winterm.exe, wt.exe, and Windows Terminal coexistence evidence for the exact release commit.' - } - if ([string]::IsNullOrWhiteSpace($env:PRODUCTION_PUBLISHER) -or - $env:PRODUCTION_PUBLISHER -eq 'CN=winTerm Development' -or - $env:PRODUCTION_PUBLISHER -match '(?i)Microsoft') { - throw 'A protected non-development winTerm package publisher is required.' - } - "WINTERM_PUBLISHER=$($env:PRODUCTION_PUBLISHER)" >> $env:GITHUB_ENV - - - name: Download and verify the Draft Release - shell: pwsh - env: - GH_TOKEN: ${{ github.token }} - run: | - $release = gh release view v1.0.0 --repo '${{ github.repository }}' --json tagName,name,isDraft,isPrerelease,assets,url | ConvertFrom-Json - if ($release.tagName -ne 'v1.0.0' -or $release.name -ne 'winTerm 1.0.0' -or -not $release.isDraft -or $release.isPrerelease) { - throw 'The expected stable Draft Release was not found.' - } - - $verificationDirectory = Join-Path $env:RUNNER_TEMP 'winterm-publish-verification' - New-Item -ItemType Directory -Path $verificationDirectory | Out-Null - gh release download v1.0.0 --repo '${{ github.repository }}' --dir $verificationDirectory - if ($LASTEXITCODE -ne 0) { - throw 'Draft Release asset download failed.' - } - $architectures = @('x64') - if (Test-Path -LiteralPath (Join-Path $verificationDirectory 'winTerm-1.0.0-arm64.msix')) { - if ('${{ inputs.arm64_validation_commit }}' -ne '${{ github.sha }}') { - throw 'The Draft contains ARM64, but exact native ARM64 validation evidence was not supplied.' - } - $architectures += 'arm64' - } + .\scripts\winterm\verify-checksums.ps1 -Directory $verificationDirectory .\scripts\winterm\verify-release-assets.ps1 ` -Directory $verificationDirectory ` - -Architecture $architectures ` - -RequireSigned ` + -Architecture x64 ` + -RequireSelfSigned ` -ExpectedPublisher $env:WINTERM_PUBLISHER - gh attestation verify (Join-Path $verificationDirectory 'winTerm-1.0.0-x64.msix') --repo '${{ github.repository }}' + gh attestation verify (Join-Path $verificationDirectory 'winTerm-1.0.1-x64.msix') --repo '${{ github.repository }}' if ($LASTEXITCODE -ne 0) { throw 'Artifact attestation verification failed.' } + $release = gh release view v1.0.1 --repo '${{ github.repository }}' --json tagName,name,isDraft,isPrerelease,url | ConvertFrom-Json + if ($release.tagName -ne 'v1.0.1' -or $release.name -ne 'winTerm 1.0.1' -or -not $release.isDraft -or $release.isPrerelease) { + throw 'Draft Release state verification failed.' + } - name: Publish as the Latest stable Release shell: pwsh env: GH_TOKEN: ${{ github.token }} run: | - gh release edit v1.0.0 ` + gh release edit v1.0.1 ` --repo '${{ github.repository }}' ` --draft=false ` --prerelease=false ` @@ -431,22 +233,31 @@ jobs: if ($LASTEXITCODE -ne 0) { throw 'GitHub Release publication failed.' } - $release = gh release view v1.0.0 --repo '${{ github.repository }}' --json tagName,name,isDraft,isPrerelease,assets,url | ConvertFrom-Json - if ($release.isDraft -or $release.isPrerelease -or $release.tagName -ne 'v1.0.0') { + $release = gh release view v1.0.1 --repo '${{ github.repository }}' --json tagName,name,isDraft,isPrerelease,assets,url | ConvertFrom-Json + if ($release.isDraft -or $release.isPrerelease -or $release.tagName -ne 'v1.0.1') { throw 'Published Release state verification failed.' } "Published Release URL: $($release.url)" >> $env:GITHUB_STEP_SUMMARY - - name: Re-download and hash-verify the public assets + - name: Re-download and verify the public assets shell: pwsh env: GH_TOKEN: ${{ github.token }} run: | $publicDirectory = Join-Path $env:RUNNER_TEMP 'winterm-public-download' New-Item -ItemType Directory -Path $publicDirectory | Out-Null - gh release download v1.0.0 --repo '${{ github.repository }}' --dir $publicDirectory + gh release download v1.0.1 --repo '${{ github.repository }}' --dir $publicDirectory if ($LASTEXITCODE -ne 0) { throw 'Public Release asset download failed.' } .\scripts\winterm\verify-checksums.ps1 -Directory $publicDirectory - 'Public assets were re-downloaded and their SHA-256 hashes were verified. The release phase remains open until the clean Windows 11 public-download install and launch smoke test is recorded.' >> $env:GITHUB_STEP_SUMMARY + .\scripts\winterm\verify-release-assets.ps1 ` + -Directory $publicDirectory ` + -Architecture x64 ` + -RequireSelfSigned ` + -ExpectedPublisher $env:WINTERM_PUBLISHER + gh attestation verify (Join-Path $publicDirectory 'winTerm-1.0.1-x64.msix') --repo '${{ github.repository }}' + if ($LASTEXITCODE -ne 0) { + throw 'Public artifact attestation verification failed.' + } + 'The public self-signed installer, certificate, installation instructions, hashes, and provenance were re-downloaded and verified.' >> $env:GITHUB_STEP_SUMMARY diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 1f2cbc74c..2b1deade2 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -12,7 +12,7 @@ permissions: jobs: validate: name: Generate and validate public-release manifests - if: github.event_name == 'workflow_dispatch' || github.event.release.tag_name == 'v1.0.0' + if: github.event_name == 'workflow_dispatch' || github.event.release.tag_name == 'v1.0.1' runs-on: windows-2022 timeout-minutes: 20 steps: @@ -26,33 +26,33 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - $release = gh release view v1.0.0 --repo '${{ github.repository }}' --json tagName,isDraft,isPrerelease,url,assets | ConvertFrom-Json - if ($release.tagName -ne 'v1.0.0' -or $release.isDraft -or $release.isPrerelease) { - throw 'WinGet manifests may be generated only from the public stable v1.0.0 Release.' + $release = gh release view v1.0.1 --repo '${{ github.repository }}' --json tagName,isDraft,isPrerelease,url,assets | ConvertFrom-Json + if ($release.tagName -ne 'v1.0.1' -or $release.isDraft -or $release.isPrerelease) { + throw 'WinGet manifests may be generated only from the public stable v1.0.1 Release.' } - $asset = @($release.assets | Where-Object { $_.name -eq 'winTerm-1.0.0-x64.msix' }) + $asset = @($release.assets | Where-Object { $_.name -eq 'winTerm-1.0.1-x64.msix' }) if ($asset.Count -ne 1) { throw 'The public Release does not contain exactly one x64 installer.' } $downloadDirectory = Join-Path $env:RUNNER_TEMP 'winterm-winget-source' New-Item -ItemType Directory -Path $downloadDirectory | Out-Null - gh release download v1.0.0 --repo '${{ github.repository }}' --pattern 'winTerm-1.0.0-x64.msix' --dir $downloadDirectory + gh release download v1.0.1 --repo '${{ github.repository }}' --pattern 'winTerm-1.0.1-x64.msix' --dir $downloadDirectory if ($LASTEXITCODE -ne 0) { throw 'Public installer download failed.' } - $packagePath = Join-Path $downloadDirectory 'winTerm-1.0.0-x64.msix' + $packagePath = Join-Path $downloadDirectory 'winTerm-1.0.1-x64.msix' $sha = (Get-FileHash -LiteralPath $packagePath -Algorithm SHA256).Hash - $url = 'https://github.com/${{ github.repository }}/releases/download/v1.0.0/winTerm-1.0.0-x64.msix' + $url = 'https://github.com/${{ github.repository }}/releases/download/v1.0.1/winTerm-1.0.1-x64.msix' .\scripts\winterm\generate-winget-manifests.ps1 ` -InstallerUrl $url ` -InstallerSha256 $sha ` - -OutputDirectory 'packaging\winget\1.0.0' + -OutputDirectory 'packaging\winget\1.0.1' - name: Validate WinGet manifests shell: pwsh run: | - winget validate 'packaging\winget\1.0.0' + winget validate 'packaging\winget\1.0.1' if ($LASTEXITCODE -ne 0) { throw 'winget validate failed.' } @@ -60,7 +60,7 @@ jobs: - name: Upload the validated manifest set for manual submission uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: Kaname.winTerm-1.0.0-winget - path: packaging/winget/1.0.0 + name: HelloThisWorld.winTerm-1.0.1-winget + path: packaging/winget/1.0.1 if-no-files-found: error retention-days: 30 diff --git a/.gitignore b/.gitignore index ce79b1a90..5d567be2d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ !docs/releases/ docs/releases/* !docs/releases/1.0.0.md +!docs/releases/1.0.1.md x64/ x86/ ARM64/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dd141a4b..ab442fe30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 1.0.1 - Stable + +### Changed + +- Replaced the previous package and WinGet identity with `HelloThisWorld.winTerm`. +- Updated application, package, shell module, About, Workspace metadata, and release versions to 1.0.1. +- Added a direct GitHub Release download link and v1.0.1 badge to the README. +- Changed the exact-tag Release workflow to build and cryptographically verify a self-signed x64 MSIX. + +### Security + +- The Release uploads the final MSIX, public CER, installation instructions, checksums, notices, SBOMs, symbols, and provenance from the exact immutable `v1.0.1` commit. +- The temporary signing key is non-exportable and removed after signing; no private key is published. +- Release assets are allowlisted, re-downloaded, and verified before and after publication. + +### Known issues + +- The installer is self-signed, is not publicly trusted or timestamped, and requires administrators to import the attached CER into Trusted People. +- ARM64 and Windows 10 are not supported by this Release. + ## 1.0.0 - Release candidate ### Added diff --git a/README.md b/README.md index 7f94b2626..743ac12c8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # winTerm -[![Version](https://img.shields.io/badge/version-1.0.0-blue)](docs/releases/1.0.0.md) +[Download winTerm v1.0.1](https://github.com/HelloThisWorld/winTerm/releases/tag/v1.0.1) + +[![Version](https://img.shields.io/badge/version-1.0.1-blue)](https://github.com/HelloThisWorld/winTerm/releases/tag/v1.0.1) [![Validation](https://github.com/HelloThisWorld/winTerm/actions/workflows/winterm-validation.yml/badge.svg)](https://github.com/HelloThisWorld/winTerm/actions/workflows/winterm-validation.yml) [![Windows build](https://github.com/HelloThisWorld/winTerm/actions/workflows/winterm-full-build.yml/badge.svg)](https://github.com/HelloThisWorld/winTerm/actions/workflows/winterm-full-build.yml) -[![Stable release](https://img.shields.io/badge/stable%20release-blocked-critical)](docs/release-checklist-v1.0.md) +[![Stable release](https://img.shields.io/badge/stable%20release-v1.0.1-success)](https://github.com/HelloThisWorld/winTerm/releases/latest) winTerm is an independent open-source terminal application based on Microsoft Windows Terminal. @@ -11,9 +13,7 @@ winTerm is not affiliated with or endorsed by Microsoft. It does not use Microso ## Latest stable release -There is no approved public winTerm Stable release yet. The 1.0.0 candidate is blocked on production signing, clean Windows 11 installation, upgrade, uninstall, accessibility, packaged runtime, and performance evidence. A download link is intentionally withheld so an unsigned Draft or Actions artifact is not presented as a user installer. - -When the real public Release exists and its assets pass re-download verification, this section must link to the repository’s `/releases/latest` page. +Download the latest verified installer from the [GitHub Releases page](https://github.com/HelloThisWorld/winTerm/releases/latest). The v1.0.1 installer is self-signed rather than signed by a public certificate authority, so Windows does not trust it automatically. Download the `.msix`, matching `.cer`, `INSTALL.txt`, and `SHA256SUMS.txt` from the same Release, verify the hashes, then follow the included certificate installation steps. ## Supported Windows versions @@ -21,15 +21,15 @@ The intended winTerm 1.0 support target is Windows 11 x64. Windows 10 is unsuppo ## Installation -Do not install a development or Draft package as a Stable release. An approved installer must: +The official installer must: -- be downloaded from the public `v1.0.0` Release in this repository; -- be named `winTerm-1.0.0-x64.msix`; +- be downloaded from the public `v1.0.1` Release in this repository; +- be named `winTerm-1.0.1-x64.msix`; - match `SHA256SUMS.txt`; -- have a trusted production signature and timestamp whose subject matches the package Publisher; +- have a cryptographically valid signature matching the included `winTerm-1.0.1.cer`; - install without Visual Studio, Git, Developer Mode, registry edits, global font installation, or profile modification. -See [installation guidance](docs/user/installation.md). +Because the certificate is self-signed, an administrator must import it into the Local Machine `Trusted People` certificate store before installing the MSIX. See [installation guidance](docs/user/installation.md). ## Screenshots @@ -37,7 +37,7 @@ Release screenshots must be captured from the signed winTerm package after brand ## Core features -- independent `Kaname.winTerm` package identity and `winterm.exe` alias; +- independent `HelloThisWorld.winTerm` package identity and `winterm.exe` alias; - PowerShell 7, Windows PowerShell 5.1, CMD, and dynamic WSL profile foundations; - conservative Linux-style Safe Compatibility for local PowerShell and CMD; - built-in and open-source Themes with pinned hashes and license records; @@ -63,9 +63,9 @@ The exact gate status is recorded in: - [security review](docs/security-review-v1.0.md); - [accessibility audit](docs/accessibility-audit-v1.0.md); - [performance validation](docs/performance-v1.0.md); -- [release notes](docs/releases/1.0.0.md). +- [release notes](docs/releases/1.0.1.md). -GitHub Actions may prepare an explicitly blocked Draft Release. It cannot publish an unsigned or untested installer, replace an existing Stable asset, or upload ARM64 without exact native-validation evidence. +GitHub Actions builds from the exact immutable `v1.0.1` Tag, creates a temporary non-exportable self-signed key, verifies the package identity and embedded signature, uploads only allowlisted assets, re-downloads them for verification, and then publishes the Release. It never replaces an existing Release asset. ## Build @@ -86,7 +86,7 @@ development wrapper from PowerShell 7: It builds and validates Release, creates a disposable self-signed code-signing certificate, signs the MSIX, exports only the public `.cer`, verifies the package signature and identity, and writes `SHA256SUMS.txt` under -`artifacts/local/winTerm-1.0.0-development-x64`. The private key is removed +`artifacts/local/winTerm-1.0.1-development-x64`. The private key is removed after signing. Before installation, an administrator must import the included `.cer` into diff --git a/SECURITY.md b/SECURITY.md index 15183d256..9ceb79ad1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported versions -The latest public winTerm Stable release receives security support. There is currently no supported public Stable release while the 1.0.0 candidate is blocked. An unsigned development or Draft package is not a security release. +The latest public winTerm Stable release receives security support. winTerm 1.0.1 is the current supported Stable release. Its installer is self-signed; verify the GitHub Release URL, SHA-256 hashes, attached public certificate, and GitHub provenance before importing certificate trust or installing it. ## Reporting a vulnerability diff --git a/SUPPORT.md b/SUPPORT.md index 7e11b0ac0..d791faeb4 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -2,7 +2,7 @@ ## Supported releases -The latest public Stable winTerm release is supported. A prerelease is supported for testing and feedback only. At present, winTerm 1.0.0 remains a release candidate and is not a supported public Stable release until every publication gate passes. +The latest public Stable winTerm release is supported. A prerelease is supported for testing and feedback only. winTerm 1.0.1 is the current Stable release. ## Platforms and architectures diff --git a/docs/architecture.md b/docs/architecture.md index 5e962e8d9..1686398b8 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -33,7 +33,7 @@ Limited conditional changes in upstream integration files select the manifest, p | State | winTerm location or behavior | | --- | --- | -| Packaged settings and state | Package-local data under `%LOCALAPPDATA%\Packages\\LocalState` | +| Packaged settings and state | Package-local data under `%LOCALAPPDATA%\Packages\\LocalState` | | Unpackaged settings and state | `%LOCALAPPDATA%\winTerm` | | `settings.json` | The selected winTerm base settings directory | | `state.json` and elevated state | The selected winTerm base settings directory | diff --git a/docs/branding.md b/docs/branding.md index 02ba71ebe..a14b1a0df 100644 --- a/docs/branding.md +++ b/docs/branding.md @@ -5,14 +5,14 @@ | Surface | winTerm 1.0 value | | --- | --- | | Product and display name | `winTerm` | -| Package name | `Kaname.winTerm` | +| Package name | `HelloThisWorld.winTerm` | | Application ID | `winTerm` | | Execution alias | `winterm.exe` | | Forbidden alias | `wt.exe` | | Development publisher | `CN=winTerm Development` | -| Stable publisher | Injected only from the protected signing environment and required to match the production certificate subject | -| Package version | `1.0.0.0` | -| Application version | `1.0.0` | +| Stable publisher | `CN=winTerm Development`, matching the public self-signed Release certificate | +| Package version | `1.0.1.0` | +| Application version | `1.0.1` | | Release channel | `stable` | | Package description | `Independent open-source terminal based on Microsoft Windows Terminal` | @@ -22,9 +22,9 @@ The terminal host may retain the internal filename `WindowsTerminal.exe`, and up ## Publisher and signing -`CN=winTerm Development` is a source-controlled development placeholder, not a production publisher. The protected Release workflow may replace it only in the clean tag checkout immediately before packaging. The certificate subject must exactly match the injected Publisher. A private key, PFX, password, token, or signing-service credential must never be committed or uploaded as a release asset. +`CN=winTerm Development` is the v1.0.1 self-signed publisher. The Release workflow creates a temporary non-exportable key and exports only its public CER. The certificate subject must exactly match the package Publisher. A private key, PFX, password, token, or signing-service credential must never be committed or uploaded as a release asset. -An unsigned package can exist only in an explicitly blocked Draft Release. It cannot be described or published as a Stable installer. +An unsigned package may exist only as an intermediate CI build. The public installer must contain a cryptographically valid signature matching the CER attached to the same Release. ## Application data and coexistence diff --git a/docs/build.md b/docs/build.md index 47f295239..142d24f5f 100644 --- a/docs/build.md +++ b/docs/build.md @@ -83,7 +83,7 @@ internal testing: The wrapper: -1. verifies the 1.0.0 version and runs the source-level Smoke suite; +1. verifies the 1.0.1 version and runs the source-level Smoke suite; 2. builds the x64 Release MSIX with signing disabled; 3. validates the generated package; 4. runs the Relevant compiled tests when `-IncludeTests` is present; @@ -98,11 +98,11 @@ The wrapper: The default output directory is ignored by Git: ```text -artifacts/local/winTerm-1.0.0-development-x64/ +artifacts/local/winTerm-1.0.1-development-x64/ INSTALL.txt SHA256SUMS.txt - winTerm-1.0.0-development.cer - winTerm-1.0.0-development-x64.msix + winTerm-1.0.1-development.cer + winTerm-1.0.1-development-x64.msix ``` The wrapper refuses to overwrite an existing output directory. Use a new @@ -118,7 +118,7 @@ again: ```powershell .\scripts\winterm\build-local-development.ps1 ` - -PackagePath .\path\to\CascadiaPackage_1.0.0.0_x64.msix ` + -PackagePath .\path\to\CascadiaPackage_1.0.1.0_x64.msix ` -OutputDirectory artifacts/local/winTerm-development-from-existing-package ``` @@ -128,19 +128,19 @@ append a signature. To install the result: 1. verify the files against `SHA256SUMS.txt`; -2. open `winTerm-1.0.0-development.cer`; +2. open `winTerm-1.0.1-development.cer`; 3. choose **Install Certificate**, **Local Machine**, and **Trusted People**, and approve the administrator prompt; -4. open `winTerm-1.0.0-development-x64.msix` and select **Install**. +4. open `winTerm-1.0.1-development-x64.msix` and select **Install**. Equivalent elevated PowerShell commands are included in `INSTALL.txt`. -Remove the development certificate after testing. A self-signed development -package is not a substitute for the production-signed and timestamped Stable -installer required by the [release process](release-process.md). +Remove the development certificate after testing. Public v1.0.1 uses the same +self-signing trust model but generates a distinct certificate from the exact +release Tag; trust only the CER downloaded from that GitHub Release. ## CI runner -The validation and full-build workflows use GitHub-hosted Windows runners. The Stable workflow uses the protected `winterm-stable-release` environment and an exact `v1.0.0` checkout. Only the protected prepare job may access production signing configuration; pull-request workflows never receive those secrets. +The validation and full-build workflows use GitHub-hosted Windows runners. The Stable workflow accepts only an exact `v1.0.1` Tag checkout. It creates a temporary non-exportable self-signed key, publishes only the public CER, deletes the private key after signing, and verifies the released MSIX against that CER. ## Known build issues in the implementation environment diff --git a/docs/compatibility-policy.md b/docs/compatibility-policy.md index a9060b71e..727ab709a 100644 --- a/docs/compatibility-policy.md +++ b/docs/compatibility-policy.md @@ -10,7 +10,7 @@ This policy commits to: - preserving unsupported newer Workspace files rather than overwriting them; - creating a backup before a migration replaces persisted data; - deterministic and idempotent migrations; -- preserving the `winterm.exe` alias and the `Kaname.winTerm` package boundary throughout 1.x; +- preserving the `winterm.exe` alias and the `HelloThisWorld.winTerm` package boundary throughout 1.x; - leaving Windows Terminal settings, package identity, and `wt.exe` untouched. This is not a promise to read every future schema forever. A future incompatible schema must use a new schema version, publish a migration policy, and never silently downgrade or overwrite newer data. diff --git a/docs/powershell-integration.md b/docs/powershell-integration.md index cce03b5e2..6694f2f10 100644 --- a/docs/powershell-integration.md +++ b/docs/powershell-integration.md @@ -1,6 +1,6 @@ # PowerShell integration -The packaged module is `ShellAssets\powershell\winTerm.Shell\winTerm.Shell.psd1`, version `1.0.0`. It supports PowerShell 7 and Windows PowerShell 5.1 with the same syntax. +The packaged module is `ShellAssets\powershell\winTerm.Shell\winTerm.Shell.psd1`, version `1.0.1`. It supports PowerShell 7 and Windows PowerShell 5.1 with the same syntax. An explicit winTerm profile launcher must set these process-local variables before importing the module: diff --git a/docs/release-checklist-v1.0.md b/docs/release-checklist-v1.0.md index 164dd2760..e3035ba6a 100644 --- a/docs/release-checklist-v1.0.md +++ b/docs/release-checklist-v1.0.md @@ -6,15 +6,15 @@ Recorded on 2026-07-18. `Passed` means evidence exists. `Not available` is never | --- | --- | --- | | Feature freeze complete | Passed | `docs/feature-status.md` and `docs/roadmap-post-1.0.md`. | | P0 count is zero | Passed | Connected GitHub repository returned no open Issues; no known P0 was found in source review. | -| P1 count is zero or explicitly approved | Failed | Production signing, clean install, runtime core flows, accessibility, and performance evidence are unresolved. | -| Version is 1.0.0 everywhere | Passed | `scripts/winterm/verify-version.ps1` after implementation validation. | +| P1 count is zero or explicitly approved | Accepted limitation | v1.0.1 is intentionally self-signed; the Release notes require explicit certificate trust and do not claim public-CA signing. | +| Version is 1.0.1 everywhere | Required | Enforced by `scripts/winterm/verify-version.ps1`. | | Full CI passes | Not available | v1.0 branch has not been pushed or run in GitHub Actions. | | Security review passes | Not available | Static review exists; runtime evidence is incomplete. | | Privacy review passes | Passed | Static privacy and diagnostic checks exist; end-to-end bundle inspection remains separately open. | | Accessibility review passes | Not available | Narrator, keyboard-only, contrast, scaling, and pseudo-localization are untested. | | Performance review passes | Not available | Packaged benchmarks and soak tests are untested. | -| x64 package builds | Not available | v0.6 CI built an unsigned package; the v1.0 package has not been built. | -| x64 signature verifies | Not available | No production signing certificate or CI signing configuration is available. | +| x64 package builds | Required | Enforced by the full-build PR checks and exact-tag Release workflow. | +| x64 signature verifies | Required | The workflow verifies the embedded PKCS#7 signature against the attached public CER. | | ARM64 status verified | Passed | Status is Disabled and no ARM64 artifact will be published without native evidence. | | Clean install passes | Not available | No clean Windows 11 machine is provisioned. | | Upgrade passes | Not available | 0.6-to-1.0 upgrade is untested. | @@ -31,18 +31,18 @@ Recorded on 2026-07-18. `Passed` means evidence exists. `Not available` is never | Release notes complete | Passed | Draft notes exist and do not claim unavailable assets or tests. | | Git tag created | Not available | Prohibited until the final release commit and all publication gates are selected. | | Tag pushed | Not available | No tag exists. | -| Draft Release created | Not available | No tag or final package exists. | -| Assets uploaded | Not available | No Draft Release exists. | -| Assets downloaded again | Not available | No Draft Release exists. | -| Downloaded hashes verified | Not available | No Draft Release exists. | -| Draft published | Not available | Publication is blocked. | -| Release marked Latest | Not available | No public Release exists. | -| Release URL verified | Not available | No Release URL exists. | -| Public download smoke test passes | Not available | No public Release or clean Windows 11 environment exists. | -| README download link updated | Skipped with reason | A `/releases/latest` installer link would be misleading before a public stable Release exists. | +| Draft Release created | Automated | Exact-tag workflow refuses to overwrite an existing Release. | +| Assets uploaded | Automated | Allowlist includes the final MSIX, public CER, instructions, checksums, notices, SBOMs, symbols, metadata, and notes. | +| Assets downloaded again | Automated | Draft and public assets are downloaded into fresh directories. | +| Downloaded hashes verified | Automated | `verify-release-assets.ps1` and `verify-checksums.ps1` run after each download. | +| Draft published | Automated | Publication occurs only after Draft verification succeeds. | +| Release marked Latest | Automated | Workflow sets Latest, non-Draft, and non-Prerelease. | +| Release URL verified | Automated | Workflow reads back and records the public URL. | +| Public download smoke test passes | User validation | Installation requires importing the attached self-signed CER into Trusted People. | +| README download link updated | Passed | README links the v1.0.1 and Latest Release pages. | | Stable update manifest updated | Skipped with reason | It must contain the actual public URL, publication time, and downloaded hash. | | Final report completed | Not available | Release phase remains blocked. | ## Publication decision -**Release blocked before publication.** The workflow may create an explicitly blocked Draft only after a tag is deliberately selected. It cannot publish an unsigned or untested installer. +**Publication boundary:** only the exact immutable `v1.0.1` Tag workflow may create the Release. It cannot publish an unsigned installer, replace existing assets, expose a private key, or skip re-download verification. diff --git a/docs/release-process.md b/docs/release-process.md index c05d277c2..8442806eb 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -29,21 +29,22 @@ Build and test x64 with PowerShell 7, Visual Studio, and Windows SDK 10.0.22621. .\scripts\winterm\package.ps1 -Platform x64 ``` -Then validate a production-signed package on a clean Windows 11 x64 machine: install, launch, PowerShell, CMD, tabs, panes, Workspace save/restore, restart, upgrade from 0.6, uninstall, reinstall, `winterm.exe`, Windows Terminal coexistence, and continued ownership of `wt.exe` by Windows Terminal. +Then validate the self-signed package on a clean Windows 11 x64 machine: verify hashes, import the attached CER into Trusted People, install, launch, exercise PowerShell, CMD, tabs, panes, and Workspace save/restore, restart, uninstall, reinstall, run `winterm.exe`, and confirm Windows Terminal retains `wt.exe`. Complete Narrator, keyboard, contrast, scaling, privacy, diagnostics, performance, and soak evidence. ARM64 is published only after separate native evidence. -## 3. Configure protected signing +## 3. Self-sign without a public CA -The `winterm-stable-release` GitHub environment owns: +The Release workflow: -- `WINTERM_SIGNING_PFX_BASE64` secret; -- `WINTERM_SIGNING_PFX_PASSWORD` secret; -- `WINTERM_PACKAGE_PUBLISHER` protected variable; -- `WINTERM_TIMESTAMP_URL` protected variable; -- required reviewers. +- creates a one-year RSA 3072-bit code-signing certificate for `CN=winTerm Development`; +- marks the private key non-exportable; +- signs the exact-tag MSIX without a public timestamp; +- exports only the public CER; +- verifies the embedded PKCS#7 signature against that CER; +- removes the private key after signing. -The workflow validates certificate purpose, subject, expiry, private key, timestamp, package Publisher, package identity, alias, and signature. It deletes the ephemeral PFX. Never echo or commit signing material. +The public certificate is uploaded beside the installer. Never commit or upload a private key, PFX, password, token, or signing-service credential. ## 4. Merge, tag, and prepare Draft @@ -51,21 +52,17 @@ After every pre-tag gate is reviewed: 1. merge the release PR to `main`; 2. record the exact merge commit; -3. create annotated tag `v1.0.0` on that commit; +3. create annotated tag `v1.0.1` on that commit; 4. push only the tag; 5. let `.github/workflows/release.yml` build from the clean tag checkout. -The prepare mode creates a Draft, uploads an allowlist, generates Attestations, re-downloads every asset, verifies hashes, package identity, architecture, alias, Publisher, signature when present, and Attestation. It never uses `--clobber`. - -If production signing is unavailable, the Draft is explicitly blocked and remains unsigned. Do not promote it. +The workflow creates a Draft, uploads only the allowlisted installer, public CER, installation instructions, checksums, notices, SBOMs, symbols, metadata, and notes, then generates Attestations. It re-downloads every asset and verifies hashes, package identity, architecture, alias, Publisher, self-signed signature, public certificate, and Attestation. It never uses `--clobber`. ## 5. Publish deliberately -Run workflow dispatch from the `v1.0.0` tag in `publish` mode. Supply the exact commit that passed clean install, upgrade, uninstall, alias, and coexistence validation. The protected environment reviewers confirm all manual evidence. - -The workflow re-downloads the Draft, requires a valid trusted signature and timestamp, verifies checksums and Attestation, then sets Draft false, Prerelease false, and Latest. It re-downloads the public assets into a new directory and verifies hashes again. +After the Draft assets pass re-download verification, the same exact-tag workflow sets Draft false, Prerelease false, and Latest. It then downloads the public assets into a new directory and repeats package, certificate, signature, checksum, and Attestation verification. -The phase remains open until a clean Windows 11 machine installs and launches the publicly downloaded package. If that smoke test fails, do not replace the `v1.0.0` asset; record the incident and prepare `v1.0.1`. +The installer is self-signed and not publicly trusted. Users must verify the repository URL and hashes before importing the attached CER into Trusted People. If the public package fails verification or installation, never replace the `v1.0.1` asset; record the incident and prepare a new version and Tag. ## 6. Post-release metadata diff --git a/docs/releases/1.0.0.md b/docs/releases/1.0.0.md index 32c7e78bb..dce0ae9b4 100644 --- a/docs/releases/1.0.0.md +++ b/docs/releases/1.0.0.md @@ -2,7 +2,7 @@ ## Highlights -winTerm is an independent open-source terminal application based on Microsoft Windows Terminal. Version 1.0.0 freezes the public settings and schema contracts, isolates the `Kaname.winTerm` package and `winterm.exe` alias, and adds a gated stable-release supply chain. +winTerm is an independent open-source terminal application based on Microsoft Windows Terminal. Version 1.0.0 freezes the public settings and schema contracts, isolates the `HelloThisWorld.winTerm` package and `winterm.exe` alias, and adds a gated stable-release supply chain. The candidate includes inherited PowerShell, Windows PowerShell, CMD, and WSL profile foundations; conservative Linux-style Safe Compatibility for local shells; built-in and open-source Themes; app-private Cascadia programming fonts; ANSI color and Emoji support; multiple windows, tabs, and panes; Workspace Restore and Named Workspaces; paste protection; and model foundations for edge and corner Docking with Layout Undo and Redo. diff --git a/docs/releases/1.0.1.md b/docs/releases/1.0.1.md new file mode 100644 index 000000000..f916726b8 --- /dev/null +++ b/docs/releases/1.0.1.md @@ -0,0 +1,81 @@ +# winTerm 1.0.1 + +## Download + +Download these files from the official [winTerm v1.0.1 GitHub Release](https://github.com/HelloThisWorld/winTerm/releases/tag/v1.0.1): + +- `winTerm-1.0.1-x64.msix` — the Windows 11 x64 installer; +- `winTerm-1.0.1.cer` — the public self-signed code-signing certificate; +- `INSTALL.txt` — certificate trust, installation, and removal instructions; +- `SHA256SUMS.txt` — hashes for every published asset. + +GitHub also displays automatically generated source archives. They are source snapshots, not the Windows installer. + +## Important signing notice + +The installer is cryptographically self-signed with `CN=winTerm Development`; it is not signed by a public certificate authority and has no public timestamp. Windows will not trust it automatically. + +Verify that every file came from `github.com/HelloThisWorld/winTerm`, check the SHA-256 hashes, and import only the certificate attached to the same Release. Never install a certificate or package obtained from a mirror or third-party download site. + +## Installation + +1. Place the MSIX, CER, `INSTALL.txt`, and `SHA256SUMS.txt` in one directory. +2. Verify all hashes: + + ```powershell + .\scripts\winterm\verify-checksums.ps1 -Directory . + ``` + + If the repository scripts are not available, compare the MSIX manually: + + ```powershell + $expected = (Get-Content .\SHA256SUMS.txt | + Where-Object { $_ -match 'winTerm-1.0.1-x64.msix$' }).Split(' ')[0] + $actual = (Get-FileHash .\winTerm-1.0.1-x64.msix -Algorithm SHA256).Hash.ToLowerInvariant() + if ($actual -cne $expected) { throw 'SHA-256 mismatch.' } + ``` + +3. Open `winTerm-1.0.1.cer`, select **Install Certificate**, choose **Local Machine**, approve the administrator prompt, and place it in **Trusted People**. +4. Open `winTerm-1.0.1-x64.msix` and select **Install**. + +PowerShell equivalent, run as administrator: + +```powershell +Import-Certificate ` + -FilePath .\winTerm-1.0.1.cer ` + -CertStoreLocation Cert:\LocalMachine\TrustedPeople + +Add-AppxPackage .\winTerm-1.0.1-x64.msix +``` + +The package identity is `HelloThisWorld.winTerm`. It registers `winterm.exe` and does not register or replace Windows Terminal’s `wt.exe`. + +## What changed + +- Removed the previous package-identity name from source, validation, documentation, and publishing metadata. +- Established `HelloThisWorld.winTerm` as the independent package and WinGet identity. +- Updated application, package, shell module, release metadata, installer, and documentation versions to 1.0.1. +- Added an exact-tag self-signed release pipeline that publishes the final MSIX, public certificate, installation instructions, checksums, notices, SBOMs, symbols, and GitHub provenance. +- Added a direct download entry and version badge at the top of the README. + +## Supported platform + +Windows 11 x64 is the supported release target. ARM64 and Windows 10 are not part of this Release. + +## Uninstall and remove certificate trust + +Uninstall winTerm from Windows Settings. To remove the trusted self-signed certificate afterward, run PowerShell as administrator: + +```powershell +$certificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new( + (Resolve-Path .\winTerm-1.0.1.cer)) +Remove-Item "Cert:\LocalMachine\TrustedPeople\$($certificate.Thumbprint)" +``` + +Removing winTerm must not remove Windows Terminal, `wt.exe`, WSL, global fonts, PowerShell profiles, or unrelated application data. + +## Provenance and security + +The GitHub Release workflow builds from the exact immutable `v1.0.1` Tag, removes the temporary private key after signing, verifies the embedded MSIX signature against the published CER, creates GitHub artifact attestations, and re-downloads the public assets for checksum and package verification. + +Attestation verifies source and integrity; it is not proof that the application has no vulnerabilities. Follow `SECURITY.md` for private vulnerability reporting. diff --git a/docs/schema-support-policy.md b/docs/schema-support-policy.md index 8bf993e39..a7c948e09 100644 --- a/docs/schema-support-policy.md +++ b/docs/schema-support-policy.md @@ -17,4 +17,4 @@ - Unknown fields are preserved only when doing so is demonstrably safe. - Imported data may not add commands, executables, environment variables, remote includes, or external URLs. -Application version 1.0.0 does not change any schema version. +Application version 1.0.1 does not change any schema version. diff --git a/docs/user/installation.md b/docs/user/installation.md index 38529adbf..ff6e8e061 100644 --- a/docs/user/installation.md +++ b/docs/user/installation.md @@ -1,15 +1,15 @@ # Install winTerm -There is no approved public Stable installer while `docs/release-checklist-v1.0.md` is blocked. Do not install an Actions artifact, unsigned Draft package, or package that asks you to import a development certificate as winTerm Stable. +Install winTerm only from the official GitHub Release. Do not install an Actions artifact, an unsigned Draft package, or a package from a mirror. -After a public `v1.0.0` Release exists: +For `v1.0.1`: -1. download `winTerm-1.0.0-x64.msix` and `SHA256SUMS.txt` from the official Release; +1. download `winTerm-1.0.1-x64.msix`, `winTerm-1.0.1.cer`, `INSTALL.txt`, and `SHA256SUMS.txt` from the official Release; 2. verify SHA-256; -3. run `Get-AuthenticodeSignature` and require `Status: Valid`, a trusted timestamp, and a signer matching the package Publisher; +3. open the CER, select **Install Certificate**, choose **Local Machine**, and place it in **Trusted People**; 4. open the MSIX or run `Add-AppxPackage`; 5. launch winTerm from Start or `winterm.exe`. -Installation must not require Visual Studio, Windows SDK, Git, PowerShell build modules, Developer Mode, manual copying, registry edits, execution-policy changes, global font installation, PowerShell profile changes, or CMD AutoRun changes. +The certificate is self-signed and is not publicly trusted or timestamped. Verify that the files came from `github.com/HelloThisWorld/winTerm` before importing it. Installation must not require Visual Studio, Windows SDK, Git, PowerShell build modules, Developer Mode, manual copying, registry edits, execution-policy changes, global font installation, PowerShell profile changes, or CMD AutoRun changes. -winTerm uses `Kaname.winTerm` and `winterm.exe`. It does not replace Microsoft Windows Terminal or claim `wt.exe`. +winTerm uses `HelloThisWorld.winTerm` and `winterm.exe`. It does not replace Microsoft Windows Terminal or claim `wt.exe`. diff --git a/docs/v0.1-acceptance.md b/docs/v0.1-acceptance.md index a118be3a3..3c55d5731 100644 --- a/docs/v0.1-acceptance.md +++ b/docs/v0.1-acceptance.md @@ -25,7 +25,7 @@ Last updated: 2026-07-13. - [x] `test.ps1 -Suite Smoke` passes. - [x] Source manifest parses as XML. - [x] Package display name and application display name are `winTerm`. -- [x] Package identity is `Kaname.winTerm`, not a Microsoft Terminal identity. +- [x] Package identity is `HelloThisWorld.winTerm`, not a Microsoft Terminal identity. - [x] Package registers `winterm.exe` and does not register `wt.exe`. - [x] Description states independent open-source status without claiming Microsoft endorsement. - [x] Original winTerm SVG, PNG, and ICO resources exist at required package sizes. @@ -137,7 +137,7 @@ Write-Host "$esc[38;2;255;96;48m24-bit True Color$esc[0m" ## Data isolation -- [x] Source: packaged local state is separated by `Kaname.winTerm`. +- [x] Source: packaged local state is separated by `HelloThisWorld.winTerm`. - [x] Source: unpackaged local state is `%LOCALAPPDATA%\winTerm`. - [x] Source: WinTerm does not use the upstream Stable migration path. - [x] Source: no dedicated log, persistent profile cache, or migration tool was added. diff --git a/docs/v0.3-acceptance.md b/docs/v0.3-acceptance.md index 54a98d4c1..ce57e292a 100644 --- a/docs/v0.3-acceptance.md +++ b/docs/v0.3-acceptance.md @@ -22,7 +22,7 @@ Last updated: 2026-07-14. | Existing shell integration | OSC 9;9, OSC 133 A/B/C/D, command marks, and autoMarkPrompts in inherited source | - [x] v0.1 and v0.2 acceptance documents exist. -- [x] Package identity remains `Kaname.winTerm`; executable alias remains `winterm.exe`. +- [x] Package identity remains `HelloThisWorld.winTerm`; executable alias remains `winterm.exe`. - [x] Repository state was inspected before changes. - [ ] `upstream` remote exists. The local `.git` metadata is not writable in this environment, so no remote was added. - [ ] Debug x64 build, Release x64 build, package, launch, profiles, panes, theme UI, fonts, ANSI, and emoji runtime verification. diff --git a/docs/v0.4-acceptance.md b/docs/v0.4-acceptance.md index af533ed85..7a47e254f 100644 --- a/docs/v0.4-acceptance.md +++ b/docs/v0.4-acceptance.md @@ -25,7 +25,7 @@ Last updated: 2026-07-15. - [x] v0.1, v0.2, and v0.3 acceptance documents exist. - [x] Repository state and upstream persisted-layout implementation were inspected. -- [x] Package identity remains `Kaname.winTerm` and the alias remains `winterm.exe`. +- [x] Package identity remains `HelloThisWorld.winTerm` and the alias remains `winterm.exe`. - [ ] The `upstream` remote exists. - [ ] v0.1, v0.2, or v0.3 runtime acceptance is complete. Their unchecked items remain unchecked. diff --git a/docs/v0.5-acceptance.md b/docs/v0.5-acceptance.md index 16086422a..82768d257 100644 --- a/docs/v0.5-acceptance.md +++ b/docs/v0.5-acceptance.md @@ -16,7 +16,7 @@ Last updated: 2026-07-17. | v0.5 branch | `codex/winterm-v0.5-visual-docking` | | v0.4 baseline | `9890c1c0419998b1c59c946810a4cb7e12a6787f` | | Microsoft Terminal baseline | `release-1.25@1cea42d433253d95c4487a3037db48197b5e72f4` | -| Package identity | `Kaname.winTerm`; alias `winterm.exe` | +| Package identity | `HelloThisWorld.winTerm`; alias `winterm.exe` | | Application version at start | `0.4.0-dev` | | Current application/package version | `0.5.0-alpha` / `0.5.0.0` | | Workspace schema at start | `1` | diff --git a/docs/v1.0-progress.md b/docs/v1.0-progress.md index d44f34406..8906ddff8 100644 --- a/docs/v1.0-progress.md +++ b/docs/v1.0-progress.md @@ -5,8 +5,8 @@ Updated: 2026-07-18 | Field | Value | | --- | --- | | Completed phases | Baseline and GitHub inspection; scope freeze; repository-side version, privacy, release workflow, policy, and documentation preparation; available local validation | -| Current phase | Draft pull request and full GitHub Windows CI, followed by external signing and clean-machine gates | -| Current branch | `codex/release-1.0.0` | +| Current phase | v1.0.1 identity correction, full GitHub Windows CI, exact Tag, and self-signed public Release | +| Current branch | `codex/release-v1.0.1` | | Baseline commit | `f43fc36721d7ef23a09328602eb1640b136b883c` (`origin/main`, v0.6 squash merge) | | Current release-preparation commit | The pushed draft-PR head; it is not the final release commit until all gates pass | | Release commit candidate | Not selected | @@ -16,9 +16,9 @@ Updated: 2026-07-18 | Tests passed | Every available static, fixture, script syntax, privacy, workflow-security, checksum-tamper, generator, and non-runtime benchmark check completed successfully | | Tests failed | No current local script failure. An unset `LASTEXITCODE` compatibility-test defect and two release-generator fixture defects were found, fixed, and passed on rerun. Compiled and packaged tests were not available. | | Open P0 issues | 0 known; connected GitHub repository returned no open Issues | -| Open P1 issues | Production signing unavailable; clean install/upgrade/uninstall and delete-all-data UI verification unavailable; packaged runtime, accessibility, and performance evidence unavailable | -| Signing status | No production certificate with private key found; no local `signtool.exe`; protected CI configuration unknown | -| x64 package status | v1.0 not built; local Visual Studio, PowerShell 7, Windows SDK, and MakeAppx are unavailable | +| Open P1 issues | Public-CA signing is unavailable and explicitly replaced by documented self-signed distribution; clean-machine runtime evidence remains a user-visible limitation | +| Signing status | Exact-tag workflow creates a temporary non-exportable self-signed key, publishes only its CER, and verifies the embedded signature | +| x64 package status | Required PR and exact-tag CI build; the Release workflow publishes only after compiled tests and package verification pass | | ARM64 package status | Disabled; no native build/install/launch evidence | -| Release status | No tag, Draft Release, public Release, or Release URL | -| Next exact action | Push the release-preparation branch and run both GitHub validation and full Windows build/package CI. Provision protected production signing and a clean Windows 11 x64 machine before selecting or pushing `v1.0.0`. | +| Release status | `v1.0.1` is reserved for the reviewed merge commit; existing `v1.0.0` is never moved or reused | +| Next exact action | Merge only after PR CI passes, create annotated `v1.0.1` on that exact commit, push only the Tag, and verify the public Release assets | diff --git a/docs/visual-docking-spike.md b/docs/visual-docking-spike.md index 5b563eae8..62e39384d 100644 --- a/docs/visual-docking-spike.md +++ b/docs/visual-docking-spike.md @@ -21,7 +21,7 @@ Cross-process transfer remains unsupported. `ControlInteractivity`, `ControlCore | Microsoft Terminal source baseline | `release-1.25@1cea42d433253d95c4487a3037db48197b5e72f4` | | Remotes | `origin` exists; `upstream` is absent | | Uncommitted files before v0.5 work | None observed | -| Package identity | `Kaname.winTerm`; alias `winterm.exe` | +| Package identity | `HelloThisWorld.winTerm`; alias `winterm.exe` | | Application/package version before v0.5 | `0.4.0-dev` / `0.4.0.0` | | Workspace schema before v0.5 | `1` | diff --git a/docs/workspace-schema.md b/docs/workspace-schema.md index e01754c60..9bec1bbf1 100644 --- a/docs/workspace-schema.md +++ b/docs/workspace-schema.md @@ -1,6 +1,6 @@ # Workspace schema -`WorkspaceSchemaVersion` is `2`, `DockingModelVersion` is `1`, and both remain independent from application version `1.0.0`. A document is UTF-8 JSON with a 5 MB maximum size. The strict parser rejects comments, duplicate keys, special floating-point values, trailing data, excessive strings, and excessive nesting. +`WorkspaceSchemaVersion` is `2`, `DockingModelVersion` is `1`, and both remain independent from application version `1.0.1`. A document is UTF-8 JSON with a 5 MB maximum size. The strict parser rejects comments, duplicate keys, special floating-point values, trailing data, excessive strings, and excessive nesting. The top-level object contains `schemaVersion`, `id`, `name`, `description`, `createdAt`, `updatedAt`, `source`, `applicationVersion`, `protocolVersion`, `startupBehavior`, `activeWindowId`, and `windows`. Optional metadata includes tags, default state, last-opened time, capture reason, and recovery generation. IDs are globally unique and are not user-visible file names. diff --git a/packaging/winget/README.md b/packaging/winget/README.md index b2826528e..80a2cf11b 100644 --- a/packaging/winget/README.md +++ b/packaging/winget/README.md @@ -1,12 +1,12 @@ # WinGet manifest preparation -The Stable package identifier is `Kaname.winTerm`. +The Stable package identifier is `HelloThisWorld.winTerm`. -No 1.0.0 manifest is committed before the public GitHub Release exists. The installer URL and SHA-256 must come from the actually published `winTerm-1.0.0-x64.msix`; placeholders and Actions artifact URLs are forbidden. +No 1.0.1 manifest is committed before the public GitHub Release exists. The installer URL and SHA-256 must come from the actually published `winTerm-1.0.1-x64.msix`; placeholders and Actions artifact URLs are forbidden. After publication, `.github/workflows/winget.yml`: -1. confirms `v1.0.0` is public and not a Prerelease; +1. confirms `v1.0.1` is public and not a Prerelease; 2. downloads the x64 installer from the Release; 3. computes its real SHA-256; 4. generates installer, default-locale, and version manifests; diff --git a/scripts/winterm/benchmark-workspace.ps1 b/scripts/winterm/benchmark-workspace.ps1 index 3fd6cd6e5..b43b384d2 100644 --- a/scripts/winterm/benchmark-workspace.ps1 +++ b/scripts/winterm/benchmark-workspace.ps1 @@ -59,7 +59,7 @@ function New-BenchmarkWorkspace createdAt = '2026-07-15T00:00:00Z' updatedAt = '2026-07-15T00:00:00Z' source = 'runtime' - applicationVersion = '1.0.0' + applicationVersion = '1.0.1' protocolVersion = 1 dockingModelVersion = 1 activeWindowId = 'window-0' diff --git a/scripts/winterm/build-local-development.ps1 b/scripts/winterm/build-local-development.ps1 index c3de0e24c..5c458940a 100644 --- a/scripts/winterm/build-local-development.ps1 +++ b/scripts/winterm/build-local-development.ps1 @@ -16,7 +16,10 @@ param( [string]$MakeAppxPath, [Parameter()] - [switch]$IncludeTests + [switch]$IncludeTests, + + [Parameter()] + [switch]$ReleaseAsset ) $ErrorActionPreference = 'Stop' @@ -86,7 +89,7 @@ function Invoke-NativeTool } } -function Assert-DevelopmentPackageManifest +function Assert-WinTermPackageManifest { param( [Parameter(Mandatory)] @@ -100,7 +103,7 @@ function Assert-DevelopmentPackageManifest { throw 'The package manifest does not contain an Identity element.' } - if ($identity.Name -cne 'Kaname.winTerm') + if ($identity.Name -cne 'HelloThisWorld.winTerm') { throw "Unexpected package identity '$($identity.Name)'." } @@ -108,7 +111,7 @@ function Assert-DevelopmentPackageManifest { throw "Unexpected package publisher '$($identity.Publisher)'." } - if ($identity.Version -cne '1.0.0.0') + if ($identity.Version -cne $script:packageVersion) { throw "Unexpected package version '$($identity.Version)'." } @@ -122,7 +125,7 @@ function Assert-DevelopmentPackageManifest ForEach-Object { $_.Alias }) if ($aliases -notcontains 'winterm.exe' -or $aliases -contains 'wt.exe') { - throw 'The development package must register winterm.exe and must not register wt.exe.' + throw 'The winTerm package must register winterm.exe and must not register wt.exe.' } } @@ -169,6 +172,10 @@ function Assert-CryptographicPackageSignature } $repositoryRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path +$releaseMetadata = Get-Content -LiteralPath (Join-Path $repositoryRoot 'src\winterm\Branding\version.json') -Raw | + ConvertFrom-Json +$applicationVersion = [string]$releaseMetadata.applicationVersion +$packageVersion = [string]$releaseMetadata.packageVersion $packageOutputRoot = Join-Path $repositoryRoot 'src\cascadia\CascadiaPackage\AppPackages' $temporaryDirectory = $null $gitExcludesPath = $null @@ -274,9 +281,17 @@ try throw 'The source MSIX is already signed. Refusing to replace or append a signature.' } + $defaultOutputName = if ($ReleaseAsset) + { + "winTerm-$applicationVersion-self-signed-x64" + } + else + { + "winTerm-$applicationVersion-development-x64" + } $outputRoot = if ([string]::IsNullOrWhiteSpace($OutputDirectory)) { - Join-Path $repositoryRoot 'artifacts\local\winTerm-1.0.0-development-x64' + Join-Path $repositoryRoot "artifacts\local\$defaultOutputName" } elseif ([IO.Path]::IsPathRooted($OutputDirectory)) { @@ -293,14 +308,30 @@ try New-Item -ItemType Directory -Path $outputRoot | Out-Null $createdOutputDirectory = $true - $signedPackagePath = Join-Path $outputRoot 'winTerm-1.0.0-development-x64.msix' - $certificatePath = Join-Path $outputRoot 'winTerm-1.0.0-development.cer' + $signedPackageName = if ($ReleaseAsset) + { + "winTerm-$applicationVersion-x64.msix" + } + else + { + "winTerm-$applicationVersion-development-x64.msix" + } + $certificateName = if ($ReleaseAsset) + { + "winTerm-$applicationVersion.cer" + } + else + { + "winTerm-$applicationVersion-development.cer" + } + $signedPackagePath = Join-Path $outputRoot $signedPackageName + $certificatePath = Join-Path $outputRoot $certificateName Copy-Item -LiteralPath $sourcePackage.FullName -Destination $signedPackagePath $certificate = New-SelfSignedCertificate ` -Type CodeSigningCert ` -Subject 'CN=winTerm Development' ` - -FriendlyName 'winTerm Local Development Signing' ` + -FriendlyName $(if ($ReleaseAsset) { 'winTerm Self-Signed Release Signing' } else { 'winTerm Local Development Signing' }) ` -CertStoreLocation 'Cert:\CurrentUser\My' ` -KeyAlgorithm RSA ` -KeyLength 3072 ` @@ -313,17 +344,17 @@ try Invoke-NativeTool ` -Path $signTool ` -Arguments @('sign', '/fd', 'SHA256', '/s', 'My', '/sha1', $certificateThumbprint, $signedPackagePath) ` - -Description 'Development MSIX signing' + -Description 'Self-signed MSIX signing' $temporaryDirectory = Join-Path ([IO.Path]::GetTempPath()) ( - "winterm-development-msix-{0}" -f [guid]::NewGuid().ToString('N')) + "winterm-self-signed-msix-{0}" -f [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $temporaryDirectory | Out-Null Invoke-NativeTool ` -Path $makeAppx ` -Arguments @('unpack', '/p', $signedPackagePath, '/d', $temporaryDirectory, '/o') ` - -Description 'Signed development MSIX inspection' + -Description 'Signed MSIX inspection' - Assert-DevelopmentPackageManifest -Path (Join-Path $temporaryDirectory 'AppxManifest.xml') + Assert-WinTermPackageManifest -Path (Join-Path $temporaryDirectory 'AppxManifest.xml') Assert-CryptographicPackageSignature ` -SignaturePath (Join-Path $temporaryDirectory 'AppxSignature.p7x') ` -ExpectedThumbprint $certificateThumbprint @@ -370,13 +401,27 @@ try $workingTreeDirty = -not [string]::IsNullOrWhiteSpace( ($statusOutput -join [Environment]::NewLine)) + $packageDescription = if ($ReleaseAsset) + { + @' +This is the official GitHub self-signed release package. It is not signed by +a public certificate authority. Verify the GitHub Release URL and hashes +before trusting only the included public certificate. +'@ + } + else + { + @' +This package is self-signed for local or controlled internal testing. +It is not signed by a public certificate authority or intended for public +redistribution. +'@ + } $instructions = @' -winTerm 1.0.0 x64 Development Build -===================================== +winTerm __APPLICATION_VERSION__ x64 Self-Signed Package +======================================================= -This package is self-signed for local or controlled internal testing. -It is not signed by a public certificate authority and is not the Stable -release. +__PACKAGE_DESCRIPTION__ Source commit: __COMMIT_SHA__ Working tree dirty when packaged: __WORKING_TREE_DIRTY__ @@ -388,31 +433,35 @@ Installation ------------ 1. Verify the files with SHA256SUMS.txt. -2. Open winTerm-1.0.0-development.cer. +2. Open __CERTIFICATE_FILE__. 3. Select Install Certificate, choose Local Machine, approve the administrator prompt, and place the certificate in Trusted People. -4. Open winTerm-1.0.0-development-x64.msix and select Install. +4. Open __PACKAGE_FILE__ and select Install. PowerShell equivalent (run PowerShell as administrator): Import-Certificate ` - -FilePath .\winTerm-1.0.0-development.cer ` + -FilePath .\__CERTIFICATE_FILE__ ` -CertStoreLocation Cert:\LocalMachine\TrustedPeople - Add-AppxPackage .\winTerm-1.0.0-development-x64.msix + Add-AppxPackage .\__PACKAGE_FILE__ The package registers winterm.exe. It does not register or replace wt.exe. -Remove development trust +Remove self-signed trust ------------------------ After uninstalling winTerm, run PowerShell as administrator: $certificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new( - (Resolve-Path .\winTerm-1.0.0-development.cer)) + (Resolve-Path .\__CERTIFICATE_FILE__)) Remove-Item "Cert:\LocalMachine\TrustedPeople\$($certificate.Thumbprint)" '@ $instructions = $instructions. + Replace('__APPLICATION_VERSION__', $applicationVersion). + Replace('__PACKAGE_DESCRIPTION__', $packageDescription.Trim()). + Replace('__CERTIFICATE_FILE__', $certificateName). + Replace('__PACKAGE_FILE__', $signedPackageName). Replace('__COMMIT_SHA__', $commitSha). Replace('__WORKING_TREE_DIRTY__', $workingTreeDirty.ToString().ToLowerInvariant()). Replace('__CERTIFICATE_THUMBPRINT__', $certificateThumbprint). @@ -436,10 +485,10 @@ After uninstalling winTerm, run PowerShell as administrator: } $completed = $true - Write-Host "Created self-signed winTerm development package: $signedPackagePath" -ForegroundColor Green - Write-Host "Public development certificate: $certificatePath" + Write-Host "Created self-signed winTerm package: $signedPackagePath" -ForegroundColor Green + Write-Host "Public signing certificate: $certificatePath" Write-Host "Checksums: $(Join-Path $outputRoot 'SHA256SUMS.txt')" - Write-Warning 'Install the public certificate only on machines used for controlled development testing.' + Write-Warning 'Trust the included public certificate only after verifying the package source and SHA-256 hashes.' } catch { diff --git a/scripts/winterm/create-msixbundle.ps1 b/scripts/winterm/create-msixbundle.ps1 index f362c1ae6..19562ce31 100644 --- a/scripts/winterm/create-msixbundle.ps1 +++ b/scripts/winterm/create-msixbundle.ps1 @@ -56,8 +56,8 @@ try $stagingDirectory = Join-Path ([IO.Path]::GetTempPath()) ("winterm-bundle-{0}" -f [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $stagingDirectory | Out-Null - Copy-Item -LiteralPath $x64.FullName -Destination (Join-Path $stagingDirectory 'winTerm-1.0.0-x64.msix') - Copy-Item -LiteralPath $arm64.FullName -Destination (Join-Path $stagingDirectory 'winTerm-1.0.0-arm64.msix') + Copy-Item -LiteralPath $x64.FullName -Destination (Join-Path $stagingDirectory 'winTerm-1.0.1-x64.msix') + Copy-Item -LiteralPath $arm64.FullName -Destination (Join-Path $stagingDirectory 'winTerm-1.0.1-arm64.msix') & $makeAppx.Source bundle /d $stagingDirectory /p $resolvedOutput /o if ($LASTEXITCODE -ne 0) diff --git a/scripts/winterm/generate-release-artifacts.ps1 b/scripts/winterm/generate-release-artifacts.ps1 index 93c0da879..4f187676b 100644 --- a/scripts/winterm/generate-release-artifacts.ps1 +++ b/scripts/winterm/generate-release-artifacts.ps1 @@ -4,7 +4,7 @@ [CmdletBinding()] param( [Parameter(Mandatory)] - [ValidateSet('1.0.0')] + [ValidateSet('1.0.1')] [string]$Version, [Parameter(Mandatory)] @@ -16,6 +16,12 @@ param( [Parameter()] [string]$BundlePath, + [Parameter()] + [string]$CertificatePath, + + [Parameter()] + [string]$InstallationInstructionsPath, + [Parameter(Mandatory)] [string]$SymbolsDirectory, @@ -39,7 +45,7 @@ param( [string]$WorkflowRunId = '0', [Parameter()] - [ValidateSet('valid-production-signature', 'unsigned-draft')] + [ValidateSet('valid-production-signature', 'self-signed', 'unsigned-draft')] [string]$SigningStatus = 'unsigned-draft' ) @@ -142,6 +148,22 @@ try $releaseFiles = [System.Collections.Generic.List[System.IO.FileInfo]]::new() [void]$releaseFiles.Add((Copy-ReleaseFile -Source $X64PackagePath -TargetName "winTerm-$Version-x64.msix")) + if ($SigningStatus -eq 'self-signed') + { + if ([string]::IsNullOrWhiteSpace($CertificatePath) -or + [string]::IsNullOrWhiteSpace($InstallationInstructionsPath)) + { + throw 'Self-signed releases require the public certificate and installation instructions.' + } + [void]$releaseFiles.Add((Copy-ReleaseFile -Source $CertificatePath -TargetName "winTerm-$Version.cer")) + [void]$releaseFiles.Add((Copy-ReleaseFile -Source $InstallationInstructionsPath -TargetName 'INSTALL.txt')) + } + elseif (-not [string]::IsNullOrWhiteSpace($CertificatePath) -or + -not [string]::IsNullOrWhiteSpace($InstallationInstructionsPath)) + { + throw 'Certificate and installation instructions may be supplied only for a self-signed release.' + } + $hasArm64 = -not [string]::IsNullOrWhiteSpace($Arm64PackagePath) if ($hasArm64) { @@ -159,7 +181,7 @@ try $notices = Copy-ReleaseFile -Source (Join-Path $repositoryRoot 'THIRD_PARTY_NOTICES.md') -TargetName 'THIRD_PARTY_NOTICES.md' [void]$releaseFiles.Add($notices) - $releaseNotes = Copy-ReleaseFile -Source (Join-Path $repositoryRoot 'docs\releases\1.0.0.md') -TargetName "winTerm-$Version-release-notes.md" + $releaseNotes = Copy-ReleaseFile -Source (Join-Path $repositoryRoot 'docs\releases\1.0.1.md') -TargetName "winTerm-$Version-release-notes.md" [void]$releaseFiles.Add($releaseNotes) $symbolsRoot = (Resolve-Path -LiteralPath $SymbolsDirectory).Path @@ -188,9 +210,9 @@ try $components = [System.Collections.Generic.List[object]]::new() [void]$components.Add((New-SbomComponent -Name 'winTerm' -VersionInfo $Version -Type 'application' -License 'MIT' -Revision $commit)) [void]$components.Add((New-SbomComponent -Name 'Microsoft Terminal upstream source' -VersionInfo 'release-1.25' -Type 'framework' -License 'MIT' -Revision $upstream)) - [void]$components.Add((New-SbomComponent -Name 'winTerm PowerShell Module' -VersionInfo '1.0.0' -Type 'library' -License 'MIT' -Revision $commit)) - [void]$components.Add((New-SbomComponent -Name 'winterm-shim' -VersionInfo '1.0.0' -Type 'application' -License 'MIT' -Revision $commit)) - [void]$components.Add((New-SbomComponent -Name 'winTerm MSIX installer' -VersionInfo '1.0.0.0' -Type 'installer' -License 'MIT' -Revision $commit)) + [void]$components.Add((New-SbomComponent -Name 'winTerm PowerShell Module' -VersionInfo '1.0.1' -Type 'library' -License 'MIT' -Revision $commit)) + [void]$components.Add((New-SbomComponent -Name 'winterm-shim' -VersionInfo '1.0.1' -Type 'application' -License 'MIT' -Revision $commit)) + [void]$components.Add((New-SbomComponent -Name 'winTerm MSIX installer' -VersionInfo '1.0.1.0' -Type 'installer' -License 'MIT' -Revision $commit)) $themeManifest = Get-Content -LiteralPath (Join-Path $repositoryRoot 'assets\winterm\themes\manifest.json') -Raw | ConvertFrom-Json foreach ($theme in $themeManifest.themes) @@ -272,7 +294,7 @@ try version = 1 metadata = [ordered]@{ timestamp = $timestamp - component = [ordered]@{ type = 'application'; 'bom-ref' = 'component:winTerm:1.0.0'; name = 'winTerm'; version = $Version } + component = [ordered]@{ type = 'application'; 'bom-ref' = "component:winTerm:$Version"; name = 'winTerm'; version = $Version } properties = @( [ordered]@{ name = 'winterm:commitSha'; value = $commit }, [ordered]@{ name = 'winterm:microsoftTerminalUpstreamRevision'; value = $upstream } @@ -287,7 +309,7 @@ try $metadata = [ordered]@{ schemaVersion = 1 version = $Version - packageVersion = '1.0.0.0' + packageVersion = '1.0.1.0' channel = 'stable' tag = "v$Version" commitSha = $commit diff --git a/scripts/winterm/generate-stable-update-manifest.ps1 b/scripts/winterm/generate-stable-update-manifest.ps1 index 9a1d90157..f515a71f3 100644 --- a/scripts/winterm/generate-stable-update-manifest.ps1 +++ b/scripts/winterm/generate-stable-update-manifest.ps1 @@ -4,7 +4,7 @@ [CmdletBinding()] param( [Parameter(Mandatory)] - [ValidatePattern('^https://github\.com/HelloThisWorld/winTerm/releases/tag/v1\.0\.0$')] + [ValidatePattern('^https://github\.com/HelloThisWorld/winTerm/releases/tag/v1\.0\.1$')] [string]$ReleaseUrl, [Parameter(Mandatory)] @@ -49,15 +49,15 @@ try $manifest = [ordered]@{ schemaVersion = 1 channel = 'stable' - version = '1.0.0' - tag = 'v1.0.0' + version = '1.0.1' + tag = 'v1.0.1' publishedAt = $timestamp releaseUrl = $ReleaseUrl artifacts = @( [ordered]@{ architecture = 'x64' type = 'msix' - fileName = 'winTerm-1.0.0-x64.msix' + fileName = 'winTerm-1.0.1-x64.msix' sha256 = $X64Sha256.ToLowerInvariant() } ) diff --git a/scripts/winterm/generate-winget-manifests.ps1 b/scripts/winterm/generate-winget-manifests.ps1 index 44b99206e..981a7ccfa 100644 --- a/scripts/winterm/generate-winget-manifests.ps1 +++ b/scripts/winterm/generate-winget-manifests.ps1 @@ -4,7 +4,7 @@ [CmdletBinding()] param( [Parameter(Mandatory)] - [ValidatePattern('^https://github\.com/HelloThisWorld/winTerm/releases/download/v1\.0\.0/winTerm-1\.0\.0-x64\.msix$')] + [ValidatePattern('^https://github\.com/HelloThisWorld/winTerm/releases/download/v1\.0\.1/winTerm-1\.0\.1-x64\.msix$')] [string]$InstallerUrl, [Parameter(Mandatory)] @@ -48,19 +48,19 @@ try New-Item -ItemType Directory -Path $output | Out-Null $sha = $InstallerSha256.ToUpperInvariant() - Write-Utf8File -Path (Join-Path $output 'Kaname.winTerm.yaml') -Lines @( - '# Generated only from the public winTerm v1.0.0 GitHub Release.' - 'PackageIdentifier: Kaname.winTerm' - 'PackageVersion: 1.0.0' + Write-Utf8File -Path (Join-Path $output 'HelloThisWorld.winTerm.yaml') -Lines @( + '# Generated only from the public winTerm v1.0.1 GitHub Release.' + 'PackageIdentifier: HelloThisWorld.winTerm' + 'PackageVersion: 1.0.1' 'DefaultLocale: en-US' 'ManifestType: version' 'ManifestVersion: 1.6.0' ) - Write-Utf8File -Path (Join-Path $output 'Kaname.winTerm.installer.yaml') -Lines @( - '# Generated only from the public winTerm v1.0.0 GitHub Release.' - 'PackageIdentifier: Kaname.winTerm' - 'PackageVersion: 1.0.0' + Write-Utf8File -Path (Join-Path $output 'HelloThisWorld.winTerm.installer.yaml') -Lines @( + '# Generated only from the public winTerm v1.0.1 GitHub Release.' + 'PackageIdentifier: HelloThisWorld.winTerm' + 'PackageVersion: 1.0.1' 'InstallerType: msix' 'Scope: user' 'InstallModes:' @@ -75,10 +75,10 @@ try 'ManifestVersion: 1.6.0' ) - Write-Utf8File -Path (Join-Path $output 'Kaname.winTerm.locale.en-US.yaml') -Lines @( - '# Generated only from the public winTerm v1.0.0 GitHub Release.' - 'PackageIdentifier: Kaname.winTerm' - 'PackageVersion: 1.0.0' + Write-Utf8File -Path (Join-Path $output 'HelloThisWorld.winTerm.locale.en-US.yaml') -Lines @( + '# Generated only from the public winTerm v1.0.1 GitHub Release.' + 'PackageIdentifier: HelloThisWorld.winTerm' + 'PackageVersion: 1.0.1' 'PackageLocale: en-US' 'Publisher: winTerm contributors' 'PublisherUrl: https://github.com/HelloThisWorld/winTerm' @@ -86,7 +86,7 @@ try 'PackageName: winTerm' 'PackageUrl: https://github.com/HelloThisWorld/winTerm' 'License: MIT' - 'LicenseUrl: https://github.com/HelloThisWorld/winTerm/blob/v1.0.0/LICENSE' + 'LicenseUrl: https://github.com/HelloThisWorld/winTerm/blob/v1.0.1/LICENSE' 'ShortDescription: Independent open-source terminal application based on Microsoft Windows Terminal.' 'Moniker: winterm' 'Tags:' diff --git a/scripts/winterm/package-shell-assets.ps1 b/scripts/winterm/package-shell-assets.ps1 index d2ee068ab..f105c985b 100644 --- a/scripts/winterm/package-shell-assets.ps1 +++ b/scripts/winterm/package-shell-assets.ps1 @@ -32,7 +32,7 @@ foreach ($relativePath in $sourceAssets) } $version = Get-Content -LiteralPath (Join-Path $repositoryRoot 'shell\shared\version.json') -Raw | ConvertFrom-Json -if ($version.moduleVersion -ne '1.0.0' -or $version.applicationVersion -ne '1.0.0' -or $version.protocolVersion -ne 1) +if ($version.moduleVersion -ne '1.0.1' -or $version.applicationVersion -ne '1.0.1' -or $version.protocolVersion -ne 1) { throw 'The winTerm Shell asset version metadata is invalid.' } diff --git a/scripts/winterm/package.ps1 b/scripts/winterm/package.ps1 index 7b131e98f..a03dc9b0b 100644 --- a/scripts/winterm/package.ps1 +++ b/scripts/winterm/package.ps1 @@ -34,9 +34,9 @@ function Assert-WinTermManifest $namespaceManager.AddNamespace('uap3', 'http://schemas.microsoft.com/appx/manifest/uap/windows10/3') $identity = $manifest.SelectSingleNode('/f:Package/f:Identity', $namespaceManager) - if ($null -eq $identity -or $identity.Name -ne 'Kaname.winTerm') + if ($null -eq $identity -or $identity.Name -ne 'HelloThisWorld.winTerm') { - throw "Package '$Path' does not use the Kaname.winTerm identity." + throw "Package '$Path' does not use the HelloThisWorld.winTerm identity." } if ($identity.Publisher -match 'Microsoft' -or $identity.Publisher -cne $Publisher) { @@ -48,9 +48,9 @@ function Assert-WinTermManifest { throw "Package '$Path' must claim winterm.exe and must not claim wt.exe." } - if ($identity.Version -ne '1.0.0.0') + if ($identity.Version -ne '1.0.1.0') { - throw "Package '$Path' must use the winTerm 1.0.0.0 package version." + throw "Package '$Path' must use the winTerm 1.0.1.0 package version." } } diff --git a/scripts/winterm/set-package-publisher.ps1 b/scripts/winterm/set-package-publisher.ps1 index fd3bec175..6b4da4b7c 100644 --- a/scripts/winterm/set-package-publisher.ps1 +++ b/scripts/winterm/set-package-publisher.ps1 @@ -25,7 +25,7 @@ try $namespace = [System.Xml.XmlNamespaceManager]::new($manifest.NameTable) $namespace.AddNamespace('f', 'http://schemas.microsoft.com/appx/manifest/foundation/windows10') $identity = $manifest.SelectSingleNode('/f:Package/f:Identity', $namespace) - if ($null -eq $identity -or $identity.Name -ne 'Kaname.winTerm' -or $identity.Version -ne '1.0.0.0') + if ($null -eq $identity -or $identity.Name -ne 'HelloThisWorld.winTerm' -or $identity.Version -ne '1.0.1.0') { throw 'The winTerm package identity or version is not the expected stable value.' } diff --git a/scripts/winterm/test-release-workflow.ps1 b/scripts/winterm/test-release-workflow.ps1 index be6bf108d..8898e85f9 100644 --- a/scripts/winterm/test-release-workflow.ps1 +++ b/scripts/winterm/test-release-workflow.ps1 @@ -16,13 +16,17 @@ try foreach ($required in @( "push:", - "- 'v1.0.0'", - 'workflow_dispatch:', - 'environment: winterm-stable-release', + "- 'v1.0.1'", 'permissions:', 'contents: write', 'id-token: write', 'attestations: write', + 'build-local-development.ps1', + '-ReleaseAsset', + '-RequireSelfSigned', + 'winTerm-1.0.1-x64.msix', + 'winTerm-1.0.1.cer', + 'INSTALL.txt', 'gh release create', '--draft', 'verify-release-assets.ps1', @@ -41,7 +45,8 @@ try if ($workflow -match '(?m)^\s*pull_request_target\s*:' -or $workflow -match '(?m)^\s*permissions:\s*write-all\s*$' -or - $workflow.Contains('--clobber')) + $workflow.Contains('--clobber') -or + $workflow.Contains('WINTERM_SIGNING_PFX')) { throw 'Release workflow contains a forbidden trigger, permission, or asset replacement option.' } diff --git a/scripts/winterm/test.ps1 b/scripts/winterm/test.ps1 index 275de8910..1f1b1a08b 100644 --- a/scripts/winterm/test.ps1 +++ b/scripts/winterm/test.ps1 @@ -101,7 +101,7 @@ function Test-ShellExperienceFoundations } $manifest = Import-PowerShellDataFile -LiteralPath $moduleManifest - if ($manifest.ModuleVersion -ne '1.0.0' -or $manifest.PowerShellVersion -ne '5.1') + if ($manifest.ModuleVersion -ne '1.0.1' -or $manifest.PowerShellVersion -ne '5.1') { throw 'The winTerm PowerShell module manifest does not declare the supported version boundary.' } diff --git a/scripts/winterm/verify-branding.ps1 b/scripts/winterm/verify-branding.ps1 index 1c32b95ca..acce97bb7 100644 --- a/scripts/winterm/verify-branding.ps1 +++ b/scripts/winterm/verify-branding.ps1 @@ -74,10 +74,10 @@ function Test-Manifest $visualElements = $manifest.SelectSingleNode('//uap:VisualElements', $namespaces) $aliases = @($manifest.SelectNodes('//uap3:AppExecutionAlias/desktop:ExecutionAlias', $namespaces) | ForEach-Object { $_.Alias }) - Test-Requirement -Condition ($null -ne $identity -and $identity.Name -eq 'Kaname.winTerm') -Message "$Path uses package identity Kaname.winTerm" + Test-Requirement -Condition ($null -ne $identity -and $identity.Name -eq 'HelloThisWorld.winTerm') -Message "$Path uses package identity HelloThisWorld.winTerm" Test-Requirement -Condition ($null -ne $identity -and $identity.Name -notmatch '^Microsoft\.') -Message "$Path does not use a Microsoft package name" Test-Requirement -Condition ($null -ne $identity -and $identity.Publisher -ceq $ExpectedPublisher) -Message "$Path uses the expected non-Microsoft publisher" - Test-Requirement -Condition ($null -ne $identity -and $identity.Version -eq '1.0.0.0') -Message "$Path uses package version 1.0.0.0" + Test-Requirement -Condition ($null -ne $identity -and $identity.Version -eq '1.0.1.0') -Message "$Path uses package version 1.0.1.0" Test-Requirement -Condition ($null -ne $properties -and $properties.DisplayName -eq 'winTerm') -Message "$Path package display name is winTerm" Test-Requirement -Condition ($null -ne $application -and $application.Id -eq 'winTerm') -Message "$Path application ID is winTerm" Test-Requirement -Condition ($null -ne $visualElements -and $visualElements.DisplayName -eq 'winTerm') -Message "$Path application display name is winTerm" diff --git a/scripts/winterm/verify-release-assets.ps1 b/scripts/winterm/verify-release-assets.ps1 index 8dbb9ce61..5521d6a19 100644 --- a/scripts/winterm/verify-release-assets.ps1 +++ b/scripts/winterm/verify-release-assets.ps1 @@ -13,6 +13,9 @@ param( [Parameter()] [switch]$RequireSigned, + [Parameter()] + [switch]$RequireSelfSigned, + [Parameter()] [string]$ExpectedPublisher ) @@ -37,6 +40,36 @@ function Assert-Condition Write-Host "PASS: $Message" -ForegroundColor Green } +function Resolve-WindowsSdkTool +{ + param( + [Parameter(Mandatory)] + [string]$Name + ) + + $command = Get-Command $Name -ErrorAction SilentlyContinue + if ($null -ne $command) + { + return $command.Source + } + + $sdkBinRoot = Join-Path ${env:ProgramFiles(x86)} 'Windows Kits\10\bin' + if (Test-Path -LiteralPath $sdkBinRoot) + { + foreach ($version in @(Get-ChildItem -LiteralPath $sdkBinRoot -Directory | + Sort-Object Name -Descending)) + { + $candidate = Join-Path $version.FullName "x64\$Name" + if (Test-Path -LiteralPath $candidate -PathType Leaf) + { + return $candidate + } + } + } + + throw "$Name was not found. Install the Windows SDK declared in .vsconfig." +} + function Test-TrustedSignature { param( @@ -53,12 +86,8 @@ function Test-TrustedSignature Assert-Condition ($signature.SignerCertificate.Subject -ceq $ExpectedPublisher) "$($Artifact.Name) signer matches the protected publisher" } - $signTool = Get-Command signtool.exe -ErrorAction SilentlyContinue - if ($null -eq $signTool) - { - throw 'signtool.exe is required for trusted signature verification.' - } - & $signTool.Source verify /pa /all /v $Artifact.FullName + $signTool = Resolve-WindowsSdkTool -Name 'signtool.exe' + & $signTool verify /pa /all /v $Artifact.FullName if ($LASTEXITCODE -ne 0) { throw "signtool.exe trust verification failed for '$($Artifact.Name)' with exit code $LASTEXITCODE." @@ -66,6 +95,51 @@ function Test-TrustedSignature Write-Host "PASS: signtool.exe verified $($Artifact.Name)" -ForegroundColor Green } +function Test-SelfSignedSignature +{ + param( + [Parameter(Mandatory)] + [System.IO.FileInfo]$Artifact, + + [Parameter(Mandatory)] + [string]$SignaturePath, + + [Parameter(Mandatory)] + [Security.Cryptography.X509Certificates.X509Certificate2]$Certificate + ) + + try + { + Add-Type -AssemblyName System.Security.Cryptography.Pkcs -ErrorAction Stop + } + catch + { + Add-Type -AssemblyName System.Security + } + + $signatureBytes = [IO.File]::ReadAllBytes($SignaturePath) + Assert-Condition ( + $signatureBytes.Length -gt 4 -and + [Text.Encoding]::ASCII.GetString($signatureBytes, 0, 4) -ceq 'PKCX' + ) "$($Artifact.Name) contains a valid MSIX signature header" + + $cmsBytes = New-Object byte[] ($signatureBytes.Length - 4) + [Array]::Copy($signatureBytes, 4, $cmsBytes, 0, $cmsBytes.Length) + $cms = New-Object Security.Cryptography.Pkcs.SignedCms + $cms.Decode($cmsBytes) + $cms.CheckSignature($true) + Assert-Condition ($cms.SignerInfos.Count -eq 1) "$($Artifact.Name) contains exactly one signer" + Assert-Condition ( + $cms.SignerInfos[0].Certificate.Thumbprint -ceq $Certificate.Thumbprint + ) "$($Artifact.Name) signer matches the published certificate" + + $signature = Get-AuthenticodeSignature -LiteralPath $Artifact.FullName + Assert-Condition ($null -ne $signature.SignerCertificate) "$($Artifact.Name) contains an Authenticode signer" + Assert-Condition ( + $signature.SignerCertificate.Thumbprint -ceq $Certificate.Thumbprint + ) "$($Artifact.Name) Authenticode signer matches the published certificate" +} + function Test-MsixPackage { param( @@ -76,18 +150,17 @@ function Test-MsixPackage [string]$ExpectedArchitecture, [Parameter(Mandatory)] - [string]$TemporaryRoot + [string]$TemporaryRoot, + + [Parameter()] + [Security.Cryptography.X509Certificates.X509Certificate2]$SelfSignedCertificate ) - $makeAppx = Get-Command makeappx.exe -ErrorAction SilentlyContinue - if ($null -eq $makeAppx) - { - throw 'makeappx.exe is required for release package verification.' - } + $makeAppx = Resolve-WindowsSdkTool -Name 'makeappx.exe' $unpackDirectory = Join-Path $TemporaryRoot $ExpectedArchitecture New-Item -ItemType Directory -Path $unpackDirectory | Out-Null - & $makeAppx.Source unpack /p $Package.FullName /d $unpackDirectory /o + & $makeAppx unpack /p $Package.FullName /d $unpackDirectory /o if ($LASTEXITCODE -ne 0) { throw "makeappx.exe failed to unpack '$($Package.Name)'." @@ -101,8 +174,8 @@ function Test-MsixPackage $identity = $manifest.SelectSingleNode('/f:Package/f:Identity', $namespace) $aliases = @($manifest.SelectNodes('//uap3:AppExecutionAlias/desktop:ExecutionAlias', $namespace) | ForEach-Object { $_.Alias }) - Assert-Condition ($identity.Name -eq 'Kaname.winTerm') "$($Package.Name) uses package identity Kaname.winTerm" - Assert-Condition ($identity.Version -eq '1.0.0.0') "$($Package.Name) contains package version 1.0.0.0" + Assert-Condition ($identity.Name -eq 'HelloThisWorld.winTerm') "$($Package.Name) uses package identity HelloThisWorld.winTerm" + Assert-Condition ($identity.Version -eq '1.0.1.0') "$($Package.Name) contains package version 1.0.1.0" Assert-Condition ($identity.ProcessorArchitecture.ToLowerInvariant() -eq $ExpectedArchitecture) "$($Package.Name) contains architecture $ExpectedArchitecture" Assert-Condition ($aliases -contains 'winterm.exe') "$($Package.Name) registers winterm.exe" Assert-Condition ($aliases -notcontains 'wt.exe') "$($Package.Name) does not register wt.exe" @@ -117,6 +190,13 @@ function Test-MsixPackage { Test-TrustedSignature -Artifact $Package } + elseif ($RequireSelfSigned) + { + Test-SelfSignedSignature ` + -Artifact $Package ` + -SignaturePath (Join-Path $unpackDirectory 'AppxSignature.p7x') ` + -Certificate $SelfSignedCertificate + } else { $signature = Get-AuthenticodeSignature -LiteralPath $Package.FullName @@ -131,18 +211,17 @@ function Test-MsixBundle [System.IO.FileInfo]$Bundle, [Parameter(Mandatory)] - [string]$TemporaryRoot + [string]$TemporaryRoot, + + [Parameter()] + [Security.Cryptography.X509Certificates.X509Certificate2]$SelfSignedCertificate ) - $makeAppx = Get-Command makeappx.exe -ErrorAction SilentlyContinue - if ($null -eq $makeAppx) - { - throw 'makeappx.exe is required for release bundle verification.' - } + $makeAppx = Resolve-WindowsSdkTool -Name 'makeappx.exe' $unpackDirectory = Join-Path $TemporaryRoot 'bundle' New-Item -ItemType Directory -Path $unpackDirectory | Out-Null - & $makeAppx.Source unpack /p $Bundle.FullName /d $unpackDirectory /o + & $makeAppx unpack /p $Bundle.FullName /d $unpackDirectory /o if ($LASTEXITCODE -ne 0) { throw "makeappx.exe failed to unpack '$($Bundle.Name)'." @@ -155,8 +234,8 @@ function Test-MsixBundle $packages = @($manifest.SelectNodes('/b:Bundle/b:Packages/b:Package', $namespace)) $architectures = @($packages | ForEach-Object { $_.Architecture.ToLowerInvariant() }) - Assert-Condition ($identity.Name -eq 'Kaname.winTerm') "$($Bundle.Name) uses bundle identity Kaname.winTerm" - Assert-Condition ($identity.Version -eq '1.0.0.0') "$($Bundle.Name) contains bundle version 1.0.0.0" + Assert-Condition ($identity.Name -eq 'HelloThisWorld.winTerm') "$($Bundle.Name) uses bundle identity HelloThisWorld.winTerm" + Assert-Condition ($identity.Version -eq '1.0.1.0') "$($Bundle.Name) contains bundle version 1.0.1.0" if (-not [string]::IsNullOrWhiteSpace($ExpectedPublisher)) { Assert-Condition ($identity.Publisher -ceq $ExpectedPublisher) "$($Bundle.Name) publisher matches the protected publisher" @@ -172,6 +251,13 @@ function Test-MsixBundle { Test-TrustedSignature -Artifact $Bundle } + elseif ($RequireSelfSigned) + { + Test-SelfSignedSignature ` + -Artifact $Bundle ` + -SignaturePath (Join-Path $unpackDirectory 'AppxSignature.p7x') ` + -Certificate $SelfSignedCertificate + } else { $signature = Get-AuthenticodeSignature -LiteralPath $Bundle.FullName @@ -180,14 +266,23 @@ function Test-MsixBundle } $temporaryDirectory = $null +$selfSignedCertificate = $null try { $root = (Resolve-Path -LiteralPath $Directory).Path + if ($RequireSigned -and $RequireSelfSigned) + { + throw 'RequireSigned and RequireSelfSigned cannot be enabled together.' + } if ($RequireSigned -and [string]::IsNullOrWhiteSpace($ExpectedPublisher)) { throw 'ExpectedPublisher is required when RequireSigned is enabled.' } + if ($RequireSelfSigned -and [string]::IsNullOrWhiteSpace($ExpectedPublisher)) + { + throw 'ExpectedPublisher is required when RequireSelfSigned is enabled.' + } $required = [System.Collections.Generic.List[string]]::new() foreach ($name in @( 'SHA256SUMS.txt', @@ -195,19 +290,24 @@ try 'SBOM.spdx.json', 'SBOM.cyclonedx.json', 'release-metadata.json', - 'winTerm-1.0.0-release-notes.md', - 'winTerm-1.0.0-symbols.zip' + 'winTerm-1.0.1-release-notes.md', + 'winTerm-1.0.1-symbols.zip' )) { [void]$required.Add($name) } foreach ($arch in $Architecture) { - [void]$required.Add("winTerm-1.0.0-$arch.msix") + [void]$required.Add("winTerm-1.0.1-$arch.msix") } if ($Architecture.Count -eq 2) { - [void]$required.Add('winTerm-1.0.0.msixbundle') + [void]$required.Add('winTerm-1.0.1.msixbundle') + } + if ($RequireSelfSigned) + { + [void]$required.Add('winTerm-1.0.1.cer') + [void]$required.Add('INSTALL.txt') } $allowed = [System.Collections.Generic.HashSet[string]]::new($required, [StringComparer]::OrdinalIgnoreCase) @@ -239,12 +339,12 @@ try $expectedChecksumNames = @($required | Where-Object { $_ -ne 'SHA256SUMS.txt' } | Sort-Object) Assert-Condition (((($checksumNames | Sort-Object) -join "`n") -ceq (($expectedChecksumNames | Sort-Object) -join "`n"))) 'SHA256SUMS.txt covers every release asset except itself' - $releaseNotes = Get-Content -LiteralPath (Join-Path $root 'winTerm-1.0.0-release-notes.md') -Raw - Assert-Condition ($releaseNotes.Contains('# winTerm 1.0.0')) 'Release notes contain the stable release title' + $releaseNotes = Get-Content -LiteralPath (Join-Path $root 'winTerm-1.0.1-release-notes.md') -Raw + Assert-Condition ($releaseNotes.Contains('# winTerm 1.0.1')) 'Release notes contain the stable release title' $metadata = Get-Content -LiteralPath (Join-Path $root 'release-metadata.json') -Raw | ConvertFrom-Json - Assert-Condition ($metadata.version -eq '1.0.0') 'Release metadata version is 1.0.0' - Assert-Condition ($metadata.packageVersion -eq '1.0.0.0') 'Release metadata package version is 1.0.0.0' + Assert-Condition ($metadata.version -eq '1.0.1') 'Release metadata version is 1.0.1' + Assert-Condition ($metadata.packageVersion -eq '1.0.1.0') 'Release metadata package version is 1.0.1.0' Assert-Condition ($metadata.channel -eq 'stable') 'Release metadata channel is stable' Assert-Condition ($metadata.commitSha -match '^[0-9a-f]{40}$') 'Release metadata contains a full commit SHA' Assert-Condition ($metadata.microsoftTerminalUpstreamRevision -match '^[0-9a-f]{40}$') 'Release metadata contains the upstream revision' @@ -257,17 +357,38 @@ try Assert-Condition ($sbomText -notmatch '(?i)(certificatePassword|github_token|BEGIN PRIVATE KEY)') "$sbomName contains no signing or repository secret" } + if ($RequireSelfSigned) + { + Assert-Condition ($metadata.signing -eq 'self-signed') 'Release metadata declares self-signed distribution' + $certificatePath = Join-Path $root 'winTerm-1.0.1.cer' + $selfSignedCertificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new( + $certificatePath) + Assert-Condition (-not $selfSignedCertificate.HasPrivateKey) 'Published certificate does not contain a private key' + Assert-Condition ($selfSignedCertificate.Subject -ceq $ExpectedPublisher) 'Published certificate subject matches the package publisher' + Assert-Condition ($selfSignedCertificate.NotAfter.ToUniversalTime() -gt [DateTime]::UtcNow) 'Published certificate is not expired' + $installationText = Get-Content -LiteralPath (Join-Path $root 'INSTALL.txt') -Raw + Assert-Condition ($installationText.Contains('winTerm-1.0.1.cer')) 'Installation instructions name the published certificate' + Assert-Condition ($installationText.Contains('winTerm-1.0.1-x64.msix')) 'Installation instructions name the x64 installer' + } + $temporaryDirectory = Join-Path ([IO.Path]::GetTempPath()) ("winterm-release-verify-{0}" -f [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $temporaryDirectory | Out-Null foreach ($arch in $Architecture) { - $package = Get-Item -LiteralPath (Join-Path $root "winTerm-1.0.0-$arch.msix") - Test-MsixPackage -Package $package -ExpectedArchitecture $arch -TemporaryRoot $temporaryDirectory + $package = Get-Item -LiteralPath (Join-Path $root "winTerm-1.0.1-$arch.msix") + Test-MsixPackage ` + -Package $package ` + -ExpectedArchitecture $arch ` + -TemporaryRoot $temporaryDirectory ` + -SelfSignedCertificate $selfSignedCertificate } if ($Architecture.Count -eq 2) { - $bundle = Get-Item -LiteralPath (Join-Path $root 'winTerm-1.0.0.msixbundle') - Test-MsixBundle -Bundle $bundle -TemporaryRoot $temporaryDirectory + $bundle = Get-Item -LiteralPath (Join-Path $root 'winTerm-1.0.1.msixbundle') + Test-MsixBundle ` + -Bundle $bundle ` + -TemporaryRoot $temporaryDirectory ` + -SelfSignedCertificate $selfSignedCertificate } Write-Host 'winTerm release asset verification passed.' -ForegroundColor Green @@ -283,4 +404,8 @@ finally { Remove-Item -LiteralPath $temporaryDirectory -Recurse -Force } + if ($null -ne $selfSignedCertificate) + { + $selfSignedCertificate.Dispose() + } } diff --git a/scripts/winterm/verify-version.ps1 b/scripts/winterm/verify-version.ps1 index 7f2993f2a..4e2347bba 100644 --- a/scripts/winterm/verify-version.ps1 +++ b/scripts/winterm/verify-version.ps1 @@ -49,11 +49,11 @@ try $versionPath = Join-Path $repositoryRoot 'src\winterm\Branding\version.json' $version = Get-Content -LiteralPath $versionPath -Raw | ConvertFrom-Json - Assert-Condition ($version.applicationVersion -eq '1.0.0') 'Application version is 1.0.0' - Assert-Condition ($version.packageVersion -eq '1.0.0.0') 'Package version is 1.0.0.0' - Assert-Condition ($version.moduleVersion -eq '1.0.0') 'PowerShell module version is 1.0.0' + Assert-Condition ($version.applicationVersion -eq '1.0.1') 'Application version is 1.0.1' + Assert-Condition ($version.packageVersion -eq '1.0.1.0') 'Package version is 1.0.1.0' + Assert-Condition ($version.moduleVersion -eq '1.0.1') 'PowerShell module version is 1.0.1' Assert-Condition ($version.channel -eq 'stable') 'Release channel is stable' - Assert-Condition ($version.tag -eq 'v1.0.0') 'Release tag is v1.0.0' + Assert-Condition ($version.tag -eq 'v1.0.1') 'Release tag is v1.0.1' Assert-Condition ($version.workspaceSchemaVersion -eq 2) 'Workspace Schema version remains 2' Assert-Condition ($version.dockingModelVersion -eq 1) 'Docking Model version remains 1' Assert-Condition ($version.shellProtocolVersion -eq 1) 'Shell Protocol version remains 1' @@ -76,7 +76,7 @@ try $moduleManifest = Import-PowerShellDataFile -LiteralPath (Join-Path $repositoryRoot 'shell\powershell\winTerm.Shell\winTerm.Shell.psd1') Assert-Condition ($moduleManifest.ModuleVersion.ToString() -eq $version.moduleVersion) 'PowerShell manifest version matches release metadata' - Assert-Condition ((Get-Text 'shell\powershell\winTerm.Shell\winTerm.Shell.psm1').Contains("`$script:WinTermModuleVersion = '1.0.0'")) 'PowerShell module runtime version matches release metadata' + Assert-Condition ((Get-Text 'shell\powershell\winTerm.Shell\winTerm.Shell.psm1').Contains("`$script:WinTermModuleVersion = '1.0.1'")) 'PowerShell module runtime version matches release metadata' $shellVersion = Get-Text 'shell\shared\version.json' | ConvertFrom-Json Assert-Condition ($shellVersion.applicationVersion -eq $version.applicationVersion) 'Shell asset application version matches release metadata' @@ -84,7 +84,7 @@ try Assert-Condition ($shellVersion.protocolVersion -eq $version.shellProtocolVersion) 'Shell asset protocol version matches release metadata' $releaseHeader = Get-Text 'src\winterm\Branding\ReleaseMetadata.h' - Assert-Condition ($releaseHeader.Contains('ApplicationVersion{ L"1.0.0" }')) 'About metadata application version is 1.0.0' + Assert-Condition ($releaseHeader.Contains('ApplicationVersion{ L"1.0.1" }')) 'About metadata application version is 1.0.1' Assert-Condition ($releaseHeader.Contains('ReleaseChannel{ L"Stable" }')) 'About metadata channel is Stable' Assert-Condition ($releaseHeader.Contains($version.microsoftTerminalUpstreamRevision)) 'About metadata contains the Microsoft Terminal upstream revision' Assert-Condition ($releaseHeader.Contains('WorkspaceSchemaVersion{ 2 }')) 'About metadata contains Workspace Schema version 2' @@ -96,21 +96,21 @@ try Assert-Condition ((Get-Text 'src\winterm\Workspaces\Model\WorkspaceDescriptor.h').Contains('DockingModelVersion{ 1 }')) 'Workspace model remains at Docking version 1' Assert-Condition ((Get-Text 'src\winterm\Shell\Protocol\ShellIntegrationProtocol.h').Contains('ShellProtocolVersion{ 1 }')) 'Shell protocol remains at version 1' Assert-Condition ((Get-Text 'src\winterm\Appearance\Themes\ThemeDescriptor.h').Contains('CurrentThemeSchemaVersion{ 1 }')) 'Theme Schema remains at version 1' - Assert-Condition ((Get-Text 'src\winterm\Workspaces\Persistence\WorkspaceSerializer.cpp').Contains('"1.0.0"')) 'Workspace application-version fallback is 1.0.0' + Assert-Condition ((Get-Text 'src\winterm\Workspaces\Persistence\WorkspaceSerializer.cpp').Contains('"1.0.1"')) 'Workspace application-version fallback is 1.0.1' $releaseWorkflow = Get-Text '.github\workflows\release.yml' - Assert-Condition ($releaseWorkflow.Contains('v1.0.0')) 'Release workflow targets v1.0.0' - Assert-Condition ($releaseWorkflow.Contains('winTerm 1.0.0')) 'Release workflow title is winTerm 1.0.0' - Assert-Condition ($releaseWorkflow.Contains('winTerm-1.0.0-x64.msix')) 'Release workflow uses the required x64 artifact name' + Assert-Condition ($releaseWorkflow.Contains('v1.0.1')) 'Release workflow targets v1.0.1' + Assert-Condition ($releaseWorkflow.Contains('winTerm 1.0.1')) 'Release workflow title is winTerm 1.0.1' + Assert-Condition ($releaseWorkflow.Contains('winTerm-1.0.1-x64.msix')) 'Release workflow uses the required x64 artifact name' - $releaseNotes = Get-Text 'docs\releases\1.0.0.md' - Assert-Condition ($releaseNotes.Contains('# winTerm 1.0.0')) 'Release notes title is winTerm 1.0.0' - Assert-Condition ((Get-Text 'CHANGELOG.md').Contains('## 1.0.0')) 'Changelog contains 1.0.0' + $releaseNotes = Get-Text 'docs\releases\1.0.1.md' + Assert-Condition ($releaseNotes.Contains('# winTerm 1.0.1')) 'Release notes title is winTerm 1.0.1' + Assert-Condition ((Get-Text 'CHANGELOG.md').Contains('## 1.0.1')) 'Changelog contains 1.0.1' if ($RequireTag) { $tag = (& git describe --tags --exact-match 2>$null).Trim() - Assert-Condition ($LASTEXITCODE -eq 0 -and $tag -eq $version.tag) 'Checked-out commit is exactly tagged v1.0.0' + Assert-Condition ($LASTEXITCODE -eq 0 -and $tag -eq $version.tag) 'Checked-out commit is exactly tagged v1.0.1' } Write-Host 'winTerm version consistency verification passed.' -ForegroundColor Green diff --git a/shell/powershell/winTerm.Shell/winTerm.Shell.psd1 b/shell/powershell/winTerm.Shell/winTerm.Shell.psd1 index 1270a4a52..067433a2a 100644 --- a/shell/powershell/winTerm.Shell/winTerm.Shell.psd1 +++ b/shell/powershell/winTerm.Shell/winTerm.Shell.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'winTerm.Shell.psm1' - ModuleVersion = '1.0.0' + ModuleVersion = '1.0.1' GUID = 'f65cd8f4-5d25-4a2a-a0d4-58df1ab3dc5a' Author = 'winTerm contributors' CompanyName = 'winTerm' diff --git a/shell/powershell/winTerm.Shell/winTerm.Shell.psm1 b/shell/powershell/winTerm.Shell/winTerm.Shell.psm1 index e00fe15f8..a094e37fa 100644 --- a/shell/powershell/winTerm.Shell/winTerm.Shell.psm1 +++ b/shell/powershell/winTerm.Shell/winTerm.Shell.psm1 @@ -3,7 +3,7 @@ Set-StrictMode -Version Latest -$script:WinTermModuleVersion = '1.0.0' +$script:WinTermModuleVersion = '1.0.1' $script:WinTermProtocolVersion = 1 $script:WinTermIntegrationEnabled = $false $script:WinTermPromptWrapped = $false diff --git a/shell/shared/version.json b/shell/shared/version.json index ba28443a1..acf6e001f 100644 --- a/shell/shared/version.json +++ b/shell/shared/version.json @@ -1,5 +1,5 @@ { - "applicationVersion": "1.0.0", - "moduleVersion": "1.0.0", + "applicationVersion": "1.0.1", + "moduleVersion": "1.0.1", "protocolVersion": 1 } diff --git a/src/cascadia/CascadiaPackage/Package-winTerm.appxmanifest b/src/cascadia/CascadiaPackage/Package-winTerm.appxmanifest index db1228002..50d436266 100644 --- a/src/cascadia/CascadiaPackage/Package-winTerm.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-winTerm.appxmanifest @@ -16,9 +16,9 @@ IgnorableNamespaces="uap rescap uap3 uap17 desktop6 virtualization"> + Version="1.0.1.0" /> winTerm diff --git a/src/winterm/Branding/ReleaseMetadata.h b/src/winterm/Branding/ReleaseMetadata.h index c16a4b66b..12ae9146f 100644 --- a/src/winterm/Branding/ReleaseMetadata.h +++ b/src/winterm/Branding/ReleaseMetadata.h @@ -24,7 +24,7 @@ namespace winTerm::Branding { - inline constexpr std::wstring_view ApplicationVersion{ L"1.0.0" }; + inline constexpr std::wstring_view ApplicationVersion{ L"1.0.1" }; inline constexpr std::wstring_view ReleaseChannel{ L"Stable" }; inline constexpr std::wstring_view CommitSha{ WINTERM_BUILD_COMMIT_SHA }; inline constexpr std::wstring_view BuildTimestamp{ WINTERM_BUILD_TIMESTAMP }; diff --git a/src/winterm/Branding/version.json b/src/winterm/Branding/version.json index 447208f99..1c1b16b03 100644 --- a/src/winterm/Branding/version.json +++ b/src/winterm/Branding/version.json @@ -1,9 +1,9 @@ { - "applicationVersion": "1.0.0", - "packageVersion": "1.0.0.0", - "moduleVersion": "1.0.0", + "applicationVersion": "1.0.1", + "packageVersion": "1.0.1.0", + "moduleVersion": "1.0.1", "channel": "stable", - "tag": "v1.0.0", + "tag": "v1.0.1", "workspaceSchemaVersion": 2, "dockingModelVersion": 1, "shellProtocolVersion": 1, diff --git a/src/winterm/Workspaces/Model/WorkspaceDescriptor.h b/src/winterm/Workspaces/Model/WorkspaceDescriptor.h index b0bbca73b..02ae95d33 100644 --- a/src/winterm/Workspaces/Model/WorkspaceDescriptor.h +++ b/src/winterm/Workspaces/Model/WorkspaceDescriptor.h @@ -219,7 +219,7 @@ namespace winTerm::Workspaces std::string createdAt; std::string updatedAt; WorkspaceSource source{ WorkspaceSource::User }; - std::string applicationVersion{ "1.0.0" }; + std::string applicationVersion{ "1.0.1" }; uint32_t protocolVersion{ 1 }; uint32_t dockingModelVersion{ DockingModelVersion }; WorkspaceStartupBehavior startupBehavior; diff --git a/src/winterm/Workspaces/Persistence/WorkspaceMigration.cpp b/src/winterm/Workspaces/Persistence/WorkspaceMigration.cpp index cfcd781b4..2ff040954 100644 --- a/src/winterm/Workspaces/Persistence/WorkspaceMigration.cpp +++ b/src/winterm/Workspaces/Persistence/WorkspaceMigration.cpp @@ -36,7 +36,7 @@ WorkspaceMigrationResult WorkspaceMigration::Migrate(const Json::Value& document result.document.removeMember("workspaceId"); } if (result.document["source"].isNull()) result.document["source"] = "user"; - if (result.document["applicationVersion"].isNull()) result.document["applicationVersion"] = "1.0.0"; + if (result.document["applicationVersion"].isNull()) result.document["applicationVersion"] = "1.0.1"; if (result.document["protocolVersion"].isNull()) result.document["protocolVersion"] = 1; if (result.document["description"].isNull()) result.document["description"] = ""; if (result.document["startupBehavior"].isNull()) diff --git a/src/winterm/Workspaces/Persistence/WorkspaceSerializer.cpp b/src/winterm/Workspaces/Persistence/WorkspaceSerializer.cpp index 0d66b20f7..52b643384 100644 --- a/src/winterm/Workspaces/Persistence/WorkspaceSerializer.cpp +++ b/src/winterm/Workspaces/Persistence/WorkspaceSerializer.cpp @@ -618,7 +618,7 @@ WorkspaceDescriptor WorkspaceSerializer::FromJson(const Json::Value& json, const throw std::runtime_error("The workspace source is not supported."); } workspace.source = *source; - workspace.applicationVersion = StringOrDefault(json, "applicationVersion", "1.0.0"); + workspace.applicationVersion = StringOrDefault(json, "applicationVersion", "1.0.1"); workspace.protocolVersion = UIntOrDefault(json, "protocolVersion", 1); workspace.dockingModelVersion = UIntOrDefault(json, "dockingModelVersion", DockingModelVersion); if (const auto& startup = json["startupBehavior"]; !startup.isNull()) From d6037f58a952346daee6105c55ecf1d5fede258c Mon Sep 17 00:00:00 2001 From: HelloThisWorld Date: Sat, 18 Jul 2026 16:35:16 +0800 Subject: [PATCH 2/2] ci: fix release workflow yaml --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bea324dd1..78e41eb11 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -169,8 +169,8 @@ jobs: Copy-Item -LiteralPath 'docs\releases\1.0.1.md' -Destination $notesPath Add-Content -LiteralPath $notesPath -Value @' -> **Self-signed installer:** download `winTerm-1.0.1-x64.msix`, `winTerm-1.0.1.cer`, `INSTALL.txt`, and `SHA256SUMS.txt`. Windows does not trust this certificate by default. Verify the hashes and repository URL before importing the certificate into Trusted People. -'@ + > **Self-signed installer:** download `winTerm-1.0.1-x64.msix`, `winTerm-1.0.1.cer`, `INSTALL.txt`, and `SHA256SUMS.txt`. Windows does not trust this certificate by default. Verify the hashes and repository URL before importing the certificate into Trusted People. + '@ $assets = @( 'artifacts\release\winTerm-1.0.1-x64.msix',