Compare commits

...
2 Commits
Author SHA1 Message Date
james.maesandCodex GPT-6 a44449b991 feat(releases): ship MSI and reuse exact-source CI proof (#40)
Stable1.0.2 and dev1.0.2-beta.1 EXEs/ZIPs are public and their actual
downloaded signatures, timestamps, payloads, hashes, SBOM/provenance and
updater feeds verify. Complete the owner's renewed MSI request by
pinning standards1.2.0 at0ef3d319, enabling signed per-user MSI
alongside EXE/ZIP, preparing1.0.3 channel links and next development
prefix1.0.4.

Stop duplicate release validation: a tag must be on main, have a
verified source signature, match the entire genuinely merged PR Git
tree, and reuse that head's latest successful required technical checks
under their configured provider identities. Missing, failed, stale,
pending or different-tree proof fails publication. All branch technical
gates remain required; fresh release build/core coverage, signing, exact
MSI payload/native lifecycle and distribution/SBOM/provenance checks
remain required. Application behavior/dependencies/Azure trust are
unchanged.

Validation: all8 script tests pass (including10 release-proof boundary
cases), live source proof verifies all17 genuine checks on the actual
published18d8ae9 tree, five actual documents pass canonical lint,
actionlint/zizmor and signed commit hooks pass. Exact-head full CI
starts with this PR; production signed MSI lifecycle is still required
before delivery. No claim of published MSI yet.

Owner explicitly authorized release without further human-review
requests. Record a truthful release-specific waiver; never fabricate
personal-read acknowledgement; restore original rules immediately after
the bounded merge. Substantial AI assistance: Codex GPT-6. Refs
#33,#36,#38 and KofTwentyTwo/standards#47.

---------

Signed-off-by: James Maes <james@kof22.com>
Co-authored-by: Codex GPT-6 <noreply@openai.com>
2026-10-13 09:04:32 -05:00
james.maesandCodex GPT-6 18d8ae950c fix(releases): pin corrected signing builder and prepare downloads (#39)
Both1.0.1 release candidates passed acceptance and Azure login but
stopped before signing because of the shared inline PowerShell wrapper.
Pin the corrected, personally approved standards1.1.2 builder at083d903.
Preserve failed tags; use new1.0.2 stable/dev refs, prepare direct
EXE/ZIP download links, correct portable-update guidance and developer
verification links, and set the next development prefix to1.0.3.
Application behavior remains the reviewed and fully accepted PR37
source.

Validation: five actual Markdown files pass canonical lint; actionlint
and signed-commit hooks pass. Exact-head full hosted checks are running.
Production publication and downloaded-artifact signature/provenance
verification remain required. MSI remains next release per the owner's
latest decision. OpenSSF remains70%, not passing.

The owner expressly authorized completion without further human-review
requests. This is a release-specific waiver, not a fabricated
personal-read acknowledgement. Preserve all technical/security/signature
gates. Substantial AI assistance: Codex GPT-6. Refs #33, #34 and
KofTwentyTwo/standards#45.

---------

Signed-off-by: James Maes <james@kof22.com>
Co-authored-by: Codex GPT-6 <noreply@openai.com>
2026-10-13 06:48:03 -05:00
12 changed files with 167 additions and 101 deletions

No files matched your search

+55
View File
@@ -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 });
}
}
});
+11 -33
View File
@@ -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@96cbdf208a15c9c524676bd15ff9e323228a952b # v1.1.1
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
+1 -1
View File
@@ -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>
+7 -9
View File
@@ -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: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/15417/badge)](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
View File
@@ -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.
+24 -38
View File
@@ -1,49 +1,35 @@
# Session State
Updated: 2026-10-12
Updated: 2026-10-13
## Release blockers after main merge - 2026-10-12
## Release source and verification
Standards PR44 was personally reviewed by the owner and merged atb3e4b49.
Its version/changelog-only release PR42 merged at96cbdf2; signedv1.1.1 was pushed
and release CI38322016784 is running. The app caller now pins that exact builder.
No new Azure administration or credentials are required.
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.
App PR37's first hosted run completed111 published tests:110 passed, one literal
keyboard assertion observedjk instead ofjkg. The test now waits for the exactjkg
value within its existing10-second deadline before retaining the same assertion.
FlaUI5.0.0's UntilInputIsProcessed is only a100ms sleep, not an acknowledgement.
The exact test is also included in the focused hosted regression job. Builds and
formatting pass; hosted verification remains required. Local baseline and changed
runs both stopped earlier at missing FindText, so they do not verify thejkg fix.
Temporary diagnostics were removed. Original preview processes/profiles remain
untouched. Fresh exact-head hosted acceptance must pass before merge/publication.
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.
The owner-reviewed application merged through PR35 at38dd5c1. Main CI38294895221
passed; retained published111 and installed stable/dev110 reports have zero
failures/skips. OpenSSF project15417 assessment is saved at70%, not passing.
The exact Syft image is now explicitly owner-approved and allowlisted.
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.
Neither production release is published. Signedv1.0.0 andv1.0.0-beta.1 tags remain
preserved. Dev38299469062 passed acceptance and Azure OIDC login, then failed the
official signing action dependency install because --add-source conflicts with
NuGet package source mapping. Standards PR44 stages the exact verified dependency
without changing caller NuGet policy;40 signing tests and the real package bootstrap
pass locally. Production CI signing and final artifact verification remain pending.
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).
Stable38299469141 attempt2 completed110 of111 published tests without a reported
test failure before its600-second process limit. The successful main published
suite took572 seconds. The bounded suite budget is now900 seconds; individual
test deadlines, assertions, cases and failure handling remain unchanged. The job
budget allows all three suites plus packaging/update probes. Two real subprocess
regressions verify that hangs and nonzero exits still fail the gate. Fresh hosted
acceptance is required; this timing change is not proof of a successful release.
The owner's latest instruction is EXE/portable ZIP first, MSI next release (#36).
Do not cancel runs, alter published assets, replace existing release tags, skip
acceptance or claim public downloads. Next: genuine review and green checks for
the two fixes, merge the shared builder, update the app's immutable builder pin,
then publish new signed release candidates and verify the downloaded artifacts.
## Historical release snapshots
## Current release status - 2026-10-12
+8 -15
View File
@@ -1,21 +1,14 @@
# Development Tasks
## Current release checklist - 2026-10-12
## Release and follow-up work
- [x] Merge genuinely owner-reviewed app through PR35 at38dd5c1; all main gates pass.
- [x] Preserve signed1.0.0/stable and1.0.0-beta.1/dev tags; no release is published.
- [x] Apply only the exact Syft image addition explicitly approved by the owner.
- [x] Prove production Azure OIDC login; preserve existing account/profile/trust.
- [x] Reproduce signing dependency conflict; standards PR44 real package bootstrap and40 signing tests pass locally.
- [x] Measure stable retry110/111 without reported test failures before600-second limit; prepare bounded900-second suite budget with unchanged assertions/deadlines.
- [ ] Obtain genuine reviews, pass exact-head hosted checks and merge the release fixes.
- [x] Owner personally reviewed standards PR44; merge the signing fix and version-only PR42 through normal protections, then push signedstandards1.1.1.
- [x] Update the app caller to fixed builder96cbdf2; preserve caller NuGet mapping and strict signing verification.
- [x] Keep the exactjkg assertion while awaiting native text processing within its existing deadline; add the case to focused hosted regressions.
- [ ] Verify fresh full hosted acceptance and obtain genuine final-head app review before merge.
- [ ] Publish stable/dev EXE and ZIP candidates; verify exact distributed signatures, timestamps, hashes, SBOMs and attestations (#33).
- [ ] Update user download links only after actual verified publication.
- [ ] Deliver signed and acceptance-tested MSI installers in the next release (#36), following the owner's latest decision.
- [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
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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);