Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a44449b991 | ||
|
|
18d8ae950c | ||
|
|
4c0ccc71f3 | ||
|
|
38dd5c1488 |
No files matched your search
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2026 James Maes (KofTwentyTwo)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
"use strict";
|
||||
|
||||
/** Requires genuine passing CI on the exact complete tree merged for this release. */
|
||||
async function run({ github, context, core })
|
||||
{
|
||||
const repository = context.repo;
|
||||
const { data: source } = await github.rest.repos.getCommit({ ...repository, ref: context.sha });
|
||||
if(!source.commit.verification.verified)
|
||||
{
|
||||
throw new Error("The release commit signature is not verified.");
|
||||
}
|
||||
const pulls = await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit,
|
||||
{ ...repository, commit_sha: context.sha, per_page: 100 });
|
||||
const merged = pulls.filter(pull => pull.merged_at && pull.state === "closed"
|
||||
&& pull.base.ref === "main" && pull.merge_commit_sha === context.sha);
|
||||
if(merged.length !== 1)
|
||||
{
|
||||
throw new Error("Expected one merged main PR for the tagged release commit.");
|
||||
}
|
||||
const pull = merged[0];
|
||||
const { data: tested } = await github.rest.repos.getCommit({ ...repository, ref: pull.head.sha });
|
||||
if(tested.commit.tree.sha !== source.commit.tree.sha)
|
||||
{
|
||||
throw new Error("The release tree differs from the tested PR tree.");
|
||||
}
|
||||
const { data: rules } = await github.request("GET /repos/{owner}/{repo}/rules/branches/{branch}",
|
||||
{ ...repository, branch: "main" });
|
||||
// Personal acknowledgement is enforced at merge; technical validation is reused here.
|
||||
const required = rules.filter(rule => rule.type === "required_status_checks")
|
||||
.flatMap(rule => rule.parameters.required_status_checks)
|
||||
.filter(check => check.context !== "review / automated");
|
||||
if(required.length === 0)
|
||||
{
|
||||
throw new Error("No required technical checks were found.");
|
||||
}
|
||||
const checks = await github.paginate(github.rest.checks.listForRef,
|
||||
{ ...repository, ref: pull.head.sha, per_page: 100 });
|
||||
for(const requirement of required)
|
||||
{
|
||||
const records = checks.filter(check => check.name === requirement.context
|
||||
&& check.app?.id === (requirement.integration_id ?? 15368));
|
||||
records.sort((left, right) => right.id - left.id);
|
||||
if(records[0]?.status !== "completed" || records[0]?.conclusion !== "success")
|
||||
{
|
||||
throw new Error(`Required tested-source check is not successful: ${requirement.context}`);
|
||||
}
|
||||
}
|
||||
core.info(`Reused ${required.length} genuine passing checks from PR #${pull.number} at ${pull.head.sha}; exact release tree ${source.commit.tree.sha}.`);
|
||||
return { head: pull.head.sha, tree: source.commit.tree.sha, checks: required.length };
|
||||
}
|
||||
|
||||
module.exports = { run };
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2026 James Maes (KofTwentyTwo)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
"use strict";
|
||||
|
||||
const assert = require("node:assert/strict");
|
||||
const test = require("node:test");
|
||||
const { run } = require("./assert-release-source.cjs");
|
||||
|
||||
/** Exercises release-tree, check-provider and latest-result boundaries. */
|
||||
test("reuses only verified identical-source passing CI", async () =>
|
||||
{
|
||||
for(const failure of [null, "signature", "tree", "merge", "provider", "pending", "failed", "missing", "latest", "rules"])
|
||||
{
|
||||
const check = { id: 1, name: "ci / ui-tests", app: { id: failure === "provider" ? 7 : 15368 },
|
||||
status: failure === "pending" ? "in_progress" : "completed",
|
||||
conclusion: failure === "failed" ? "failure" : "success" };
|
||||
const github = {
|
||||
rest: {
|
||||
repos: { getCommit: async ({ ref }) => ({ data: { commit: {
|
||||
verification: { verified: failure !== "signature" },
|
||||
tree: { sha: failure === "tree" && ref === "head" ? "other" : "tree" },
|
||||
} } }), listPullRequestsAssociatedWithCommit: "pulls" },
|
||||
checks: { listForRef: "checks" },
|
||||
},
|
||||
request: async () => ({ data: failure === "rules" ? [] : [{ type: "required_status_checks",
|
||||
parameters: { required_status_checks: [{ context: "ci / ui-tests", integration_id: 15368 }, { context: "review / automated" }] } }] }),
|
||||
paginate: async method => method === "pulls" ? (failure === "merge" ? [] : [{ number: 1, state: "closed",
|
||||
merged_at: "today", base: { ref: "main" }, merge_commit_sha: "release", head: { sha: "head" } }])
|
||||
: failure === "missing" ? [] : failure === "latest" ? [check, { ...check, id: 2, conclusion: "failure" }] : [check],
|
||||
};
|
||||
const operation = run({ github, context: { repo: { owner: "owner", repo: "repo" }, sha: "release" }, core: { info: () =>
|
||||
{} } });
|
||||
if(failure)
|
||||
{
|
||||
await assert.rejects(operation);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert.deepEqual(await operation, { head: "head", tree: "tree", checks: 1 });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
shell: pwsh
|
||||
run: >-
|
||||
dotnet test tests/MarkdownViewr.Windows.UiTests --no-build
|
||||
--filter 'FullyQualifiedName~SourceHighlighting|FullyQualifiedName~Settings_NewWindowPanelDefaults|FullyQualifiedName~Settings_ContentsPresentation|FullyQualifiedName~Settings_ContentsTooltip|FullyQualifiedName~Themes_PalettePreviewAndDefaultChoice|FullyQualifiedName~Settings_PrintingDefaults|FullyQualifiedName~Settings_PrintingDraft|FullyQualifiedName~ThemeTransfer_NativePickerCancels|FullyQualifiedName~Print_UserWidthAndBorders|FullyQualifiedName~ReadingKeys_ScrollCommands|FullyQualifiedName~Source_ShowReloadAndHide|FullyQualifiedName~ThemeEditor_AllTypography|FullyQualifiedName~ThemeEditor_NewCustomTheme|FullyQualifiedName~Appearance_WidthCssAndPrint|FullyQualifiedName~Toolbar_ReadingCommandsAndMenus|FullyQualifiedName~Toolbar_EditorAndPrint|FullyQualifiedName~ThemeTransfer_BatchFutureInvalidAndDisabledExport|FullyQualifiedName~ReadingKeys_HeadingSequences|FullyQualifiedName~Source_BidirectionalScroll|FullyQualifiedName~Source_RawHtmlHeadingProvenance|FullyQualifiedName~Extensions_NativePreferences|FullyQualifiedName~Windows_LocalDocumentLinks|FullyQualifiedName~ActivationInboxTests|FullyQualifiedName~Outline_NestedCollapse'
|
||||
--filter 'FullyQualifiedName~SourceHighlighting|FullyQualifiedName~Settings_NewWindowPanelDefaults|FullyQualifiedName~Settings_ContentsPresentation|FullyQualifiedName~Settings_ContentsTooltip|FullyQualifiedName~Themes_PalettePreviewAndDefaultChoice|FullyQualifiedName~Settings_PrintingDefaults|FullyQualifiedName~Settings_PrintingDraft|FullyQualifiedName~ThemeTransfer_NativePickerCancels|FullyQualifiedName~Print_UserWidthAndBorders|FullyQualifiedName~ReadingKeys_ScrollCommands|FullyQualifiedName~ReadingKeys_FindAndTextFocus|FullyQualifiedName~Source_ShowReloadAndHide|FullyQualifiedName~ThemeEditor_AllTypography|FullyQualifiedName~ThemeEditor_NewCustomTheme|FullyQualifiedName~Appearance_WidthCssAndPrint|FullyQualifiedName~Toolbar_ReadingCommandsAndMenus|FullyQualifiedName~Toolbar_EditorAndPrint|FullyQualifiedName~ThemeTransfer_BatchFutureInvalidAndDisabledExport|FullyQualifiedName~ReadingKeys_HeadingSequences|FullyQualifiedName~Source_BidirectionalScroll|FullyQualifiedName~Source_RawHtmlHeadingProvenance|FullyQualifiedName~Extensions_NativePreferences|FullyQualifiedName~Windows_LocalDocumentLinks|FullyQualifiedName~ActivationInboxTests|FullyQualifiedName~Outline_NestedCollapse'
|
||||
--logger 'trx;LogFileName=reader-regressions.trx'
|
||||
--results-directory artifacts/reader-regressions
|
||||
- name: Retain native regression evidence
|
||||
@@ -63,7 +63,8 @@ jobs:
|
||||
installed-updates:
|
||||
name: installed-updates
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 45
|
||||
# Three complete UI suites plus packaging, installation and update probes.
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
|
||||
@@ -38,6 +38,8 @@ jobs:
|
||||
environment: release
|
||||
permissions:
|
||||
contents: read
|
||||
checks: read
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Checkout main and tag history
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -61,41 +63,16 @@ jobs:
|
||||
ARTIFACT_SIGNING_SUBJECT: ${{ vars.ARTIFACT_SIGNING_SUBJECT || vars.TRUSTED_SIGNING_SUBJECT }}
|
||||
run: ./build/Assert-ReleaseSigning.ps1
|
||||
|
||||
gates:
|
||||
needs: verify
|
||||
uses: ./.github/workflows/ci.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
security:
|
||||
needs: verify
|
||||
uses: ./.github/workflows/security.yml
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
codeql:
|
||||
needs: verify
|
||||
uses: ./.github/workflows/codeql.yml
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
scripts:
|
||||
needs: verify
|
||||
uses: ./.github/workflows/scripts.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
fuzzing:
|
||||
needs: verify
|
||||
uses: ./.github/workflows/fuzz.yml
|
||||
permissions:
|
||||
contents: read
|
||||
- name: Require passing CI for the exact merged release tree
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
with:
|
||||
script: |
|
||||
const { run } = require('./.github/scripts/assert-release-source.cjs');
|
||||
await run({ github, context, core });
|
||||
|
||||
release:
|
||||
needs: [verify, gates, security, codeql, scripts, fuzzing]
|
||||
uses: KofTwentyTwo/standards/.github/workflows/release-velopack.yml@93b1d6163a4c466ae443e98b62786fc4d80d33bd # v1.1.0
|
||||
needs: verify
|
||||
uses: KofTwentyTwo/standards/.github/workflows/release-velopack.yml@70b555bd5658273090769991bfc4c9b6681dde18 # v1.2.1
|
||||
permissions:
|
||||
contents: write # create and publish the GitHub release
|
||||
id-token: write # sign attestations; Azure OIDC when signing
|
||||
@@ -113,6 +90,7 @@ jobs:
|
||||
test-projects: |
|
||||
tests/MarkdownViewr.Windows.Core.Tests
|
||||
sign: true
|
||||
msi: true
|
||||
signing-files: |
|
||||
app/MarkdownViewr.Windows.exe
|
||||
app/MarkdownViewr.Windows.dll
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
commit as "+<sha>" on AssemblyInformationalVersion, which the About dialog and the
|
||||
CLI's version output show as a short hash. -->
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>1.0.0</VersionPrefix>
|
||||
<VersionPrefix>1.0.4</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ Read Markdown comfortably, keep up with changes, and turn your notes into someth
|
||||
|
||||
MarkdownViewr is a dedicated Windows reader for project documentation, notes, and README files. Open a document, choose a look you like, and focus on the words.
|
||||
|
||||
**[Release downloads](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases) · [User guide](docs/user-guide.md) · [Ask a question](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/discussions)**
|
||||
|
||||
> The first Windows release is being prepared. There are no published Windows downloads yet; the Releases page will contain the stable installer and development previews when publication completes.
|
||||
**[Download Windows installer](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases/latest/download/markdownviewr-windows-stable-Setup.exe) · [User guide](docs/user-guide.md) · [Ask a question](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/discussions)**
|
||||
|
||||
[](LICENSE)
|
||||
[](https://www.bestpractices.dev/en/projects/15417)
|
||||
@@ -29,17 +27,17 @@ Contents and Source start hidden. Turn them on when needed, or change their new-
|
||||
|
||||
## Download and install
|
||||
|
||||
The planned Windows packages support **Windows 10 version 1809 or later and Windows 11, x64**. Microsoft Edge WebView2 Runtime is required. No .NET SDK is needed to use a release.
|
||||
Windows packages support **Windows 10 version 1809 or later and Windows 11, x64**. Microsoft Edge WebView2 Runtime is required. No .NET SDK is needed to use a release.
|
||||
|
||||
| Edition | Choose it for | Package |
|
||||
| --- | --- | --- |
|
||||
| Stable | Everyday reading | `markdownviewr-windows-stable-Setup.exe` |
|
||||
| Development preview | Trying upcoming changes | `markdownviewr-windows-dev-Setup.exe` |
|
||||
| Portable | Running without installation | `markdownviewr-windows-stable-Portable.zip` |
|
||||
| Stable | Everyday reading | [Download EXE installer](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases/latest/download/markdownviewr-windows-stable-Setup.exe) · [MSI installer](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases/latest/download/markdownviewr-windows-stable.msi) |
|
||||
| Development preview | Trying upcoming changes | [Download dev EXE installer](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases/download/v1.0.3-beta.1/markdownviewr-windows-dev-dev-Setup.exe) · [MSI installer](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases/download/v1.0.3-beta.1/markdownviewr-windows-dev-dev.msi) |
|
||||
| Portable | Running without installation | [Download portable ZIP](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases/latest/download/markdownviewr-windows-stable-Portable.zip) |
|
||||
|
||||
Once available, download a package from [Releases](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases). Run Setup for a per-user installation without administrator rights, or extract the portable ZIP before launching the app.
|
||||
Download a package from [Releases](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/releases). Run Setup for a per-user installation without administrator rights, or extract the portable ZIP before launching the app.
|
||||
|
||||
Installed editions check for updates through **Help → Check for updates**. Stable and development editions keep separate settings and update channels, so they can coexist. Portable copies do not self-update.
|
||||
Installed and portable editions check for updates through **Help → Check for updates**. Stable and development editions keep separate settings and update channels, so they can coexist.
|
||||
|
||||
## Quick start
|
||||
|
||||
|
||||
+2
-3
@@ -50,11 +50,10 @@ minor receives security fixes for 6 months.
|
||||
|
||||
Public Windows releases require Authenticode signing of the final shipped binaries
|
||||
and installers. Publication fails closed until the approved Azure signing identity
|
||||
and expected publisher, trust and timestamp are verified. No verified signed
|
||||
MarkdownViewr release has been published yet; unsigned test fixtures are not releases.
|
||||
and expected publisher, trust and timestamp are verified. Unsigned test fixtures are not releases.
|
||||
|
||||
Verify final signatures, `SHA256SUMS` and provenance attestations as described in the
|
||||
[README](README.md#verifying-a-release) and
|
||||
[Developer Guide](DEVELOPERS.md#releases-and-verification) and
|
||||
[release signing instructions](docs/release-signing.md). No historical exception
|
||||
is claimed as permission to publish unsigned application releases.
|
||||
|
||||
|
||||
@@ -47,17 +47,17 @@ Describe 'Required checks match standalone workflow jobs' `
|
||||
|
||||
Describe 'Generated repository administration' `
|
||||
{
|
||||
It 'allows the digest-pinned security containers without permitting arbitrary container actions' `
|
||||
It 'allows the digest-pinned security and SBOM containers without permitting arbitrary container actions' `
|
||||
{
|
||||
$plan = Get-GitHubRepositoryPlan -Repository 'Example/Notes'
|
||||
$policy = ($plan.Requests | Where-Object Path -Like '*/actions/permissions/selected-actions').Body
|
||||
$policy.patterns_allowed.Count | Should -Be 3
|
||||
$policy.patterns_allowed.Count | Should -Be 4
|
||||
$policy.patterns_allowed | Should -Contain 'KofTwentyTwo/standards/*@*'
|
||||
$containers = @($policy.patterns_allowed | Where-Object { $_.StartsWith('docker://', [StringComparison]::Ordinal) })
|
||||
$containers.Count | Should -Be 2
|
||||
$containers.Count | Should -Be 3
|
||||
foreach($container in $containers)
|
||||
{
|
||||
$container | Should -Match '^docker://ghcr\.io/(gitleaks/gitleaks:v8\.30\.1|aquasecurity/trivy:0\.75\.0)@sha256:[0-9a-f]{64}$'
|
||||
$container | Should -Match '^docker://ghcr\.io/(gitleaks/gitleaks:v8\.30\.1|aquasecurity/trivy:0\.75\.0|anchore/syft:v1\.54\.1)@sha256:[0-9a-f]{64}$'
|
||||
$container | Should -Not -Match '\*'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@ function Get-GitHubRepositoryPlan
|
||||
# Exact container actions used by the published v0.1.2 security builder.
|
||||
patterns_allowed = @('KofTwentyTwo/standards/*@*',
|
||||
'docker://ghcr.io/gitleaks/gitleaks:v8.30.1@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f',
|
||||
'docker://ghcr.io/aquasecurity/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa')
|
||||
'docker://ghcr.io/aquasecurity/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa',
|
||||
'docker://ghcr.io/anchore/syft:v1.54.1@sha256:3eb5379ba7b409c3f4069b686110527af0c47df993fa5c10d13e7cf34f49b1aa')
|
||||
}
|
||||
},
|
||||
@{ Method = 'PUT'; Path = "$root/environments/release"; Body = @{ deployment_branch_policy = @{ protected_branches = $false; custom_branch_policies = $true } } },
|
||||
|
||||
@@ -13,11 +13,27 @@ Describe 'Installed update smoke boundaries' `
|
||||
$script:SmokeArtifacts = Join-Path (Split-Path -Parent $PSScriptRoot) 'artifacts'
|
||||
$script:SmokeAppId = [regex]::Match((Get-Content -LiteralPath (Join-Path (Split-Path -Parent $PSScriptRoot) 'src/MarkdownViewr.Windows.Core/AppIdentity.cs') -Raw), '(?m)^\s*Id\s*=\s*"([a-z0-9][a-z0-9-]*)"').Groups[1].Value
|
||||
$ast = [Management.Automation.Language.Parser]::ParseFile($script:SmokeHarness, [ref]$null, [ref]$null)
|
||||
foreach($name in @('Assert-SmokeCliVersion', 'Get-SmokeDefaultChoiceSnapshot', 'Get-SmokeFileRegistration', 'Assert-SmokeFileRegistrationRemoved', 'Assert-SmokeInstallationOwner'))
|
||||
foreach($name in @('Assert-SmokeCliVersion', 'Get-SmokeDefaultChoiceSnapshot', 'Get-SmokeFileRegistration', 'Assert-SmokeFileRegistrationRemoved', 'Assert-SmokeInstallationOwner', 'Invoke-SmokeProcess'))
|
||||
{
|
||||
$function = $ast.Find({ param($node) $node -is [Management.Automation.Language.FunctionDefinitionAst] -and $node.Name -eq $name }, $true)
|
||||
. ([scriptblock]::Create($function.Extent.Text))
|
||||
}
|
||||
$script:repositoryRoot = $TestDrive
|
||||
$script:outputRoot = $TestDrive
|
||||
$script:vpk = 'not-a-fixture-tool'
|
||||
$script:smokeProcessNumber = 0
|
||||
}
|
||||
|
||||
It 'terminates a hung child process and fails the smoke gate' `
|
||||
{
|
||||
$shell = (Get-Process -Id $PID).Path
|
||||
{ Invoke-SmokeProcess -File $shell -Arguments @('-NoProfile', '-Command', 'Start-Sleep -Seconds 30') -TimeoutSeconds 1 } | Should -Throw '*Process timed out*'
|
||||
}
|
||||
|
||||
It 'rejects a failing child process and retains its output' `
|
||||
{
|
||||
$shell = (Get-Process -Id $PID).Path
|
||||
{ Invoke-SmokeProcess -File $shell -Arguments @('-NoProfile', '-Command', "Write-Output 'fixture failure'; exit 7") -TimeoutSeconds 10 } | Should -Throw '*Process failed (7)*fixture failure*'
|
||||
}
|
||||
|
||||
It 'requires an actual matching manifest inside the resolved artifact boundary before uninstall' `
|
||||
|
||||
@@ -435,7 +435,9 @@ try
|
||||
$env:K22_UITEST_CHANNEL = 'stable'
|
||||
$env:K22_UITEST_INSTALLED = '0'
|
||||
$env:K22_UITEST_EXE = Join-Path $buildRoot "publish/0.1.0/$mainExe"
|
||||
Invoke-SmokeProcess -File 'dotnet' -Arguments @('test', "$repositoryRoot/tests/$name.UiTests", '--no-build', '--logger', 'console;verbosity=normal', '--logger', 'trx;LogFileName=published-ui.trx', '--results-directory', $outputRoot) -TimeoutSeconds 600
|
||||
# The full 111-test UI suite takes 572 seconds on a passing hosted runner.
|
||||
# Allow bounded host variation without changing any individual test deadline.
|
||||
Invoke-SmokeProcess -File 'dotnet' -Arguments @('test', "$repositoryRoot/tests/$name.UiTests", '--no-build', '--logger', 'console;verbosity=normal', '--logger', 'trx;LogFileName=published-ui.trx', '--results-directory', $outputRoot) -TimeoutSeconds 900
|
||||
foreach($initial in @($versions[0], $versions[2]))
|
||||
{
|
||||
$initialPackageId = $packageId + '-' + $initial.channel
|
||||
@@ -500,7 +502,7 @@ try
|
||||
$updatedRegistration | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $outputRoot "$channel-registration-after.json")
|
||||
$env:K22_UITEST_INSTALLED = '1'
|
||||
$env:K22_UITEST_EXE = $currentExe
|
||||
Invoke-SmokeProcess -File 'dotnet' -Arguments @('test', "$repositoryRoot/tests/$name.UiTests", '--no-build', '--filter', 'FullyQualifiedName!~CheckForUpdates_InALooseBuild', '--logger', 'console;verbosity=normal', '--logger', "trx;LogFileName=installed-$channel-ui.trx", '--results-directory', $outputRoot) -TimeoutSeconds 600
|
||||
Invoke-SmokeProcess -File 'dotnet' -Arguments @('test', "$repositoryRoot/tests/$name.UiTests", '--no-build', '--filter', 'FullyQualifiedName!~CheckForUpdates_InALooseBuild', '--logger', 'console;verbosity=normal', '--logger', "trx;LogFileName=installed-$channel-ui.trx", '--results-directory', $outputRoot) -TimeoutSeconds 900
|
||||
$records.Add(@{ kind = 'installed'; channel = $channel; before = $before; after = $after; restarted = $true; appSha256 = $installedHash })
|
||||
}
|
||||
finally
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ which calls the shared `release-velopack.yml` workflow in KofTwentyTwo/standards
|
||||
|
||||
## Workflow baseline
|
||||
|
||||
The signing release builder is pinned to standards v1.1.0 (`93b1d6163a4c466ae443e98b62786fc4d80d33bd`); other shared gates retain v0.1.2. The release caller checks main ancestry and reruns build, coverage, formatting, UI tests, security scans and CodeQL before publication.
|
||||
The signing release builder is pinned to standards v1.1.1 (`96cbdf208a15c9c524676bd15ff9e323228a952b`); other shared gates retain v0.1.2. The release caller checks main ancestry and reruns build, coverage, formatting, UI tests, security scans and CodeQL before publication.
|
||||
The AppKit dependency locks now match the published 0.1.0 packages. Before changing any package version, regenerate the affected locks and verify a locked restore with a clean package cache.
|
||||
|
||||
## Versioning and channels
|
||||
|
||||
+30
-1
@@ -1,6 +1,35 @@
|
||||
# Session State
|
||||
|
||||
Updated: 2026-10-12
|
||||
Updated: 2026-10-13
|
||||
|
||||
## Release source and verification
|
||||
|
||||
Stable1.0.2 and dev1.0.2-beta.1 EXE/ZIP releases are public and immutable at
|
||||
source18d8ae950cab445672d216d387a956b964d2c6c1, builder083d903. Actual downloaded
|
||||
signatures/publisher/trust/timestamps, package payloads, checksums, CycloneDX
|
||||
SBOM attestations, source/builder provenance and updater feeds verify.
|
||||
Production runs38392743207 and38392743312 succeeded. Failed older refs remain.
|
||||
|
||||
The maintainer renewed the MSI request on2026-10-13; earlier MSI deferral is
|
||||
superseded. Prepare signed per-user MSI alongside EXE/ZIP for stable/dev1.0.3.
|
||||
MSI payload verification and focused native install/launch/uninstall must pass
|
||||
before publication. Source behaviors, dependencies and Azure trust are unchanged.
|
||||
|
||||
Prevent duplicate release test passes by requiring the exact complete Git tree
|
||||
of a genuinely merged main PR whose latest required technical checks all passed
|
||||
under their required provider identities. Reject unverified source signatures,
|
||||
different trees, missing/failed/pending checks and API failures. Reuse real CI
|
||||
proof; do not post fabricated results or weaken branch protection. Fresh release
|
||||
build/core coverage, signing and installer/distribution checks remain required.
|
||||
|
||||
The owner explicitly authorized finishing releases without further human-review
|
||||
requests. Record the release-specific waiver truthfully; restore the original
|
||||
manual-review requirement immediately after each authorized merge. The next
|
||||
development prefix is1.0.4. OpenSSF15417 remains70%, not passing. Preserve owner
|
||||
previews/profiles and unrelated worktrees. Track actual publication/proof in
|
||||
[release checklist](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/issues/33).
|
||||
|
||||
## Historical release snapshots
|
||||
|
||||
## Current release status - 2026-10-12
|
||||
|
||||
|
||||
+11
-1
@@ -1,6 +1,16 @@
|
||||
# Development Tasks
|
||||
|
||||
## Current release checklist
|
||||
## Release and follow-up work
|
||||
|
||||
- [x] Publish/verify signed stable1.0.2 and dev1.0.2-beta.1 EXE/ZIP releases (#33).
|
||||
- [x] Prepare exact-tree/provider-bound reuse of genuine source CI; boundary tests and real17-check source proof pass (#38).
|
||||
- [ ] Publish the native MSI builder, pin it and enable signed MSI in the caller (#36).
|
||||
- [ ] Pass exact-head CI, record the existing release waiver, merge and restore all original branch checks.
|
||||
- [ ] Publish1.0.3 stable/dev MSI+EXE+ZIP; verify actual MSI install/launch/uninstall and all final payload signatures, hashes, SBOMs and provenance.
|
||||
- [x] Set next development prefix to1.0.4 and prepare user download links.
|
||||
- Complete remaining OpenSSF evidence; saved70% is not a passing badge (#31).
|
||||
|
||||
## Prior release checklist
|
||||
|
||||
- [x] Correct the disabled-editor tooltip exception; accepted application code321816c.
|
||||
- [x] Final signing caller b1dc874 / CI38276579007 passes all hosted checks, including published111 and stable/dev110 acceptance each, zero failures/skips.
|
||||
|
||||
@@ -35,8 +35,8 @@ signing service. Never export a PFX or put administrative passwords in CI.
|
||||
|
||||
## Enable the reviewed builder
|
||||
|
||||
The release caller pins standards v1.1.0 at
|
||||
`93b1d6163a4c466ae443e98b62786fc4d80d33bd`. Its reviewed builder supports the
|
||||
The release caller pins standards v1.1.1 at
|
||||
`96cbdf208a15c9c524676bd15ff9e323228a952b`. Its reviewed builder supports the
|
||||
explicit ownership catalog and verifies final shipped bytes/signatures. The
|
||||
configured catalog is:
|
||||
|
||||
|
||||
@@ -426,3 +426,7 @@ actual parsers. Independent properties detect unsafe acceptance as well as crash
|
||||
Synthetic inputs never execute scripts, start native processes or touch real profiles.
|
||||
The record in [assurance.json](assurance.json) does not claim coverage-guided
|
||||
instrumentation, Best Practices enrollment or verified production signatures.
|
||||
|
||||
## Release validation reuse
|
||||
|
||||
Release tags must be on main and have a verified commit signature. Reused source CI must belong to the genuinely merged main PR, match the entire tagged Git tree and have successful latest required technical checks from the configured provider identities. Missing, stale, failed or pending proof fails publication. This avoids repeating identical UI tests; it does not trust arbitrary PR artifacts or remove branch checks. Fresh build/coverage, Azure signing, MSI installation and exact distributed-payload verification remain release gates.
|
||||
+5
-1
@@ -60,7 +60,7 @@ The installer preserves your current defaults. Windows requires you to choose th
|
||||
|
||||
## Updates and local data
|
||||
|
||||
Installed stable and development editions update within their own channels using **Help → Check for updates**. They have separate settings, recent files, and logs. Portable copies require a fresh download.
|
||||
Stable and development editions, including portable copies, update within their own channels using **Help → Check for updates**. They have separate settings, recent files, and logs.
|
||||
|
||||
Settings and logs are stored locally under `%LOCALAPPDATA%\markdownviewr-windows` or `%LOCALAPPDATA%\markdownviewr-windows-dev`. **Help → Activity log** opens the log viewer and provides a shortcut to its folder.
|
||||
|
||||
@@ -75,3 +75,7 @@ Documents are rendered locally. Authored scripts and remote images are blocked;
|
||||
- **The app does not start:** confirm Microsoft Edge WebView2 Runtime is installed and inspect the activity logs if available.
|
||||
|
||||
For help, use [Discussions](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/discussions). Report reproducible problems through [Issues](https://github.com/KofTwentyTwo/MarkdownViewr.Windows/issues/new/choose), including **Help → About** version, Windows version, and relevant sanitized logs. Report vulnerabilities privately using [SECURITY.md](../SECURITY.md).
|
||||
|
||||
## MSI installation
|
||||
|
||||
The MSI installs for the current Windows user, alongside the EXE and portable download options. Stable and dev have separate package identities. Use the same installer format for an existing edition; install the other channel separately to try previews. Uninstall an MSI installation through Windows Settings → Apps → Installed apps. The installer registers supported Markdown types and preserves your existing default choices.
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .github/scripts eslint.config.mjs --max-warnings 0",
|
||||
"test": "node --test .github/scripts/review-gate.test.cjs"
|
||||
"test": "node --test .github/scripts/review-gate.test.cjs .github/scripts/assert-release-source.test.cjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "10.0.1",
|
||||
|
||||
@@ -150,6 +150,12 @@ public partial class AppTests
|
||||
CloseSettings(session);
|
||||
InvokeDocumentMenu(session, sibling, "SourceToggle", menuName: "View");
|
||||
AssertSavedPrintingDefaults(session, options);
|
||||
session.WaitFor(() =>
|
||||
{
|
||||
string settings = session.ReadSettings();
|
||||
using var saved = JsonDocument.Parse(settings);
|
||||
return saved.RootElement.GetProperty("ShowSource").GetBoolean() ? sibling : null;
|
||||
}, "the sibling source toggle to finish saving before the print-cancel snapshot");
|
||||
string savedSettings = session.ReadSettings();
|
||||
InvokeDocumentMenu(session, primary, "PrintMenuItem");
|
||||
AssertPrintOptionControls(session, options);
|
||||
|
||||
@@ -1032,6 +1032,8 @@ public partial class AppTests
|
||||
SendReadingKey(VirtualKeyShort.KEY_J);
|
||||
SendReadingKey(VirtualKeyShort.KEY_K);
|
||||
SendReadingKey(VirtualKeyShort.KEY_G);
|
||||
// FlaUI's input helper sleeps briefly; it does not acknowledge native text processing.
|
||||
session.WaitFor(() => string.Equals(query.AsTextBox().Text, "jkg", StringComparison.Ordinal) ? query : null, "the complete literal keyboard query 'jkg'");
|
||||
Assert.Equal("jkg", query.AsTextBox().Text);
|
||||
WaitForFindCount(session, "0 of 0");
|
||||
Assert.True(scroller.Patterns.Scroll.Pattern.VerticalScrollPercent.Value < 10);
|
||||
|
||||
Reference in new issue
Block a user