Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,13 @@ jobs:
if-no-files-found: error
include-hidden-files: true
retention-days: 7
- name: Retain the verified npm candidate for the production SHA
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: npm-candidate-${{ github.sha }}
path: |
test-results/packed-cohort.json
test-results/packages/*.tgz
if-no-files-found: error
retention-days: 30
90 changes: 90 additions & 0 deletions .github/workflows/npm_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Publish ha-ui npm prerelease

on:
push:
tags: ['v*-next.*']

concurrency:
group: ha-ui-npm-release
cancel-in-progress: false

permissions:
contents: read

jobs:
candidate:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
actions: read
contents: read
outputs:
run-id: ${{ steps.ci.outputs.run-id }}
steps:
- name: Find the successful CI run for this production commit
id: ci
env:
GH_TOKEN: ${{ github.token }}
run: |
[[ "$GITHUB_REF_NAME" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-next\.(0|[1-9][0-9]*)$ ]]
run_id=$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/ci_check.yml/runs?event=push&branch=master&head_sha=$GITHUB_SHA&status=success&per_page=10" \
--jq '.workflow_runs[0].id // empty')
[[ "$run_id" =~ ^[1-9][0-9]*$ ]]
echo "run-id=$run_id" >> "$GITHUB_OUTPUT"

publish:
needs: candidate
runs-on: ubuntu-latest
environment: npm
timeout-minutes: 10
permissions:
actions: read
contents: read
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ github.sha }}
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.0.1
with:
node-version-file: .node-version
registry-url: https://registry.npmjs.org
package-manager-cache: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: npm-candidate-${{ github.sha }}
path: ${{ runner.temp }}/ha-ui-npm-candidate
github-token: ${{ github.token }}
run-id: ${{ needs.candidate.outputs.run-id }}
- name: Publish the verified archives through npm Trusted Publishing
env:
NPM_CANDIDATE_DIR: ${{ runner.temp }}/ha-ui-npm-candidate
run: |
node --input-type=module <<'NODE'
import {execFileSync} from 'node:child_process';
import {appendFileSync,readFileSync} from 'node:fs';
import {join} from 'node:path';
import {packageCoordinates,sha256} from './tooling/packaging.mjs';
import {observeNpmVersion,publishNpmPrerelease} from './tooling/npm-prerelease.mjs';

const directory=process.env.NPM_CANDIDATE_DIR;
const {candidate}=JSON.parse(readFileSync(join(directory,'packed-cohort.json'),'utf8'));
const tag=process.env.GITHUB_REF_NAME;
const version=tag.slice(1);
const paths=new Map(packageCoordinates.map(name=>[
name,join(directory,'packages',name.slice(1).replace('/','-')+'-'+version+'.tgz'),
]));
const archives=new Map([...paths].map(([name,path])=>[name,readFileSync(path)]));
const result=await publishNpmPrerelease({
record:candidate,sourceRevision:process.env.GITHUB_SHA,lockSha256:sha256(readFileSync('pnpm-lock.yaml')),tag,
oidc:Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_URL&&process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN),
archives,observe:observeNpmVersion,
publish:async entry=>execFileSync('npm',[
'publish',paths.get(entry.coordinate),'--access','public','--tag','next','--ignore-scripts',
],{stdio:'inherit',timeout:120_000}),
append:async entry=>appendFileSync(process.env.GITHUB_STEP_SUMMARY,
'- '+entry.coordinate+'@'+entry.version+' `'+entry.sha256+'`\n'),
});
if(result.state!=='published')throw new Error(result.state+': '+result.reason);
NODE
37 changes: 35 additions & 2 deletions docs/provenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,44 @@ source revision and lock. `verifyCandidate` fails before use if any bytes, revis
lock change. `assertShippedManifest` requires exact version pins for the shared cohort
and rejects local fixture references in shared dependencies and overrides.

The release use case requires separate Gateway and Domus evidence for the same candidate
and a successful publisher-rights check. Registry observations are stored for the exact
Stable publication requires separate Gateway and Domus evidence for the same candidate.
The npm prerelease publishes reviewed packed artifacts so that those applications can
complete installed acceptance. Both channels require an authorized publisher. Registry observations are stored for the exact
coordinate, version and digest. Recovery reads before attempting a missing publication
and checks the observed digest before recording it. A conflict remains blocked; a partial
publication leaves consumer pins unadvanced. An uncertain attempt is inspected once, and
a later run rechecks the registry before publishing. The delivery tests exercise this
policy with a deterministic provider; no registry publication or publisher rights are
claimed by those tests.

### Publish a prerelease to npm

The core and the four adapters share one exact version. Prepare the package manifests
and packed consumer pins in a reviewed version change, including the Angular core
dependency and the fixture lockfiles. Merge it to the production
branch and wait for its `CI Check` to succeed. Create a version tag such as
`v0.1.0-next.1` at that production commit. [The npm workflow](../.github/workflows/npm_release.yml)
starts automatically and uses the five tarballs retained by CI for that SHA.

Configure an npm Trusted Publisher for each coordinate in the table below. Use
organization `vypdev`, repository `ha-ui`, workflow `npm_release.yml`, and environment
`npm`. Enable direct publishing for that workflow. npm authorizes each write and
generates provenance from the GitHub runner.

| Package | Intended installation |
| --- | --- |
| `@vypdev/ha-ui` | Lit or browser custom elements |
| `@vypdev/ha-ui-react` | React |
| `@vypdev/ha-ui-vue` | Vue |
| `@vypdev/ha-ui-angular` | Angular |
| `@vypdev/ha-ui-svelte` | Svelte |

The workflow publishes the core first and sets the npm `next` tag. It downloads the
exact version of each package and checks its SHA-256 against the CI artifact before
reporting success. If a run stops after publishing some packages, rerun the same
workflow. Existing versions are verified by hash and reused. An unexpected hash
requires a new reviewed package version. Keep consumer pins on the previous cohort
until the workflow confirms all five archives.

See [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) for configuring
the publisher and [consumer adoption](migration.md) for testing the installed version.
6 changes: 3 additions & 3 deletions specs/CATALOG.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion specs/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"workflows": [
".github/workflows/copilot_issue.yml",
".github/workflows/ci_check.yml",
".github/workflows/npm_release.yml",
".github/workflows/storybook_pages.yml"
],
"entrypoints": [
Expand Down Expand Up @@ -125,7 +126,8 @@
"packages/vue/src/feedback.ts",
"stories/feedback.example.ts",
"tooling/compatibility.mjs",
"tooling/packaging.mjs"
"tooling/packaging.mjs",
"tooling/npm-prerelease.mjs"
],
"tests": [
"tests/contracts/core.test.ts",
Expand Down
23 changes: 13 additions & 10 deletions specs/shared-ui-framework-delivery.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ This companion supplements [the retained foundation specification](shared-ui-fou

## 1. Executive summary

Deliver the retained shared component capability through a reproducible, tested package cohort. First establish a pinned toolchain and real compiled consumer fixtures; then accept one immutable packed candidate, exercise it in fixtures and both applications, verify publication authority and artifact integrity, and publish and adopt that same cohort. The implementation stays within the existing owner's contract. The three requirements below refine delivery controls only.
Deliver the retained shared component capability through a reproducible, tested package cohort. First establish a pinned toolchain and real compiled consumer fixtures. Verify publication authority and artifact integrity for the prerelease cohort, then adopt those exact packages in the applications. Stable publication retains both installed application acceptance gates. The implementation stays within the existing owner's contract. The three requirements below refine delivery controls only.

```text
Pinned toolchain -> compiled fixtures and reviewed packed candidate -> Gateway and Domus checks -> verified publication -> installed consumer acceptance
Pinned toolchain -> compiled fixtures and reviewed packed candidate -> verified prerelease publication -> installed consumer acceptance -> stable release review
```

No framework support, publication authority, or completed adoption is inferred from candidate versions or package names. Planned acceptance cases in this draft have not been executed.
Expand Down Expand Up @@ -88,26 +88,27 @@ Not applicable: this is prospective delivery work and reconstructs no implemente
| Toolchain | No implementation evidence | Frozen, compatible lock and support evidence gate artifacts | Reproducible candidate and explicit failure reason |
| Candidate | No accepted archive | Build and record source revision, archive digest, dependencies, licenses, and compatibility | Reviewers can identify exact bytes |
| Consumer check | No real fixtures or app acceptance | Test packed bytes in framework fixtures and both admitted application workstreams | Adoption refers to one candidate |
| Publication | No publication evidence | Verify rights; publish exact accepted cohort; record per-coordinate result | Partial release is visible and recoverable |
| Publication | No publication evidence | Verify rights, publish the exact reviewed prerelease cohort and record each coordinate | Partial release is visible and recoverable |

```mermaid
flowchart LR
A[Freeze compatible toolchain] --> B[Build and digest candidate]
B --> C[Fixtures and both app checks]
C --> D[Verify rights and publish exact cohort]
B --> C[Compiled consumer fixtures]
C --> D[Verify rights and publish the prerelease]
D --> E[Installed consumer and deployment acceptance]
E --> F[Stable release review]
```

Text equivalent: freeze toolchain -> build candidate -> check fixtures and applications -> verify rights and publish -> accept installed consumer behavior.
Text equivalent: freeze the toolchain, build and test the candidate, verify rights and publish the prerelease, then complete installed consumer acceptance before stable release review.

## 6. Functional behavior and state model

### 6.1 Happy path

1. CI validates exact direct and transitive locks, runtime/compiler compatibility, and the declared supported matrix.
2. The build produces reproducible packed artifacts and records source revision, package versions, integrity digests, dependencies, licenses, and compatibility.
3. Packed artifacts are installed in actual compiled direct-DOM and framework fixtures and exercised in both applications through their admitted work.
4. Review accepts the candidate only after the retained foundation gates and companion refinements pass. Publication rights and artifact digests are verified before publishing those exact bytes.
3. Packed artifacts are installed in actual compiled direct-DOM and framework fixtures. The prerelease publishes those reviewed bytes so owning application work can adopt them.
4. Verify publisher authority and artifact digests before publishing. A production tag identifies the prerelease commit and its successful CI artifact. Stable publication also requires both application receipts.
5. Consumer pins and deployed acceptance refer to the published version/digest cohort. Publication alone does not mark the capability implemented.

### 6.2 Alternative paths
Expand Down Expand Up @@ -249,7 +250,7 @@ Errors state impact -> cause -> action -> retained state. A successfully publish

## 13. Compatibility, migration, rollout, and rollback

No in-flight runtime migration is introduced by this companion. Keep exact locks and the last reviewed support evidence until a new candidate passes. Establish compatibility from actual frozen builds and browser checks, then publish a reviewed compatible version cohort. Release may be staged; partial publication remains incomplete and is resumed by exact version/digest without overwrite. First test the same candidate bytes locally in fixtures and both consumer workstreams; after rights and integrity verification, publish those bytes, pin delivered versions in the actual repositories, then complete installed/deployment acceptance. Do not make an application depend on a private temporary path. Rollback consumer pins only through each application's owning admitted work and recovery contract; package publication itself is not treated as reversible.
No in-flight runtime migration is introduced by this companion. Keep exact locks and the last reviewed support evidence until a new candidate passes. Establish compatibility from actual frozen builds and browser checks, then publish a reviewed compatible version cohort. Release may be staged; partial publication remains incomplete and is resumed by exact version/digest without overwrite. Test the candidate in compiled fixtures and verify publisher rights and integrity. Publish the prerelease, pin the exact version in the consumer repositories, and complete installed and deployment acceptance before stable promotion. Do not make an application depend on a private temporary path. Rollback consumer pins only through each application's owning admitted work and recovery contract; package publication itself is not treated as reversible.

## 14. Testing strategy and numeric budget

Expand Down Expand Up @@ -306,6 +307,7 @@ Prospective repository files (current documents are classified against the immut
| `tests/coverage.test.mjs` | create |
| `LICENSE` | create |
| `.github/workflows/ci_check.yml` | create |
| `.github/workflows/npm_release.yml` | create |
| `.github/workflows/storybook_pages.yml` | create |
| `packages/core/package.json` | create |
| `packages/core/src/index.ts` | create |
Expand Down Expand Up @@ -363,6 +365,7 @@ Prospective repository files (current documents are classified against the immut
| `examples/sveltekit/package.json` | create |
| `examples/sveltekit/README.md` | create |
| `tooling/packaging.mjs` | create |
| `tooling/npm-prerelease.mjs` | create |
| `tooling/compatibility.mjs` | create |
| `tests/delivery.test.mjs` | create |
| `docs/framework-adapters.md` | create |
Expand Down Expand Up @@ -733,7 +736,7 @@ The retained foundation's requirements and cases remain traced in `specs/shared-
3. Establish package contracts and dependency constraints, then compiled direct-DOM and four framework fixtures using actual packed artifacts.
4. Implement accepted-candidate digest and no-private-path promotion checks; retain the full foundation's tests-first, coverage, component, Storybook, and browser scope.
5. Exercise the immutable candidate in both admitted application workstreams and complete their separate UX, deployment, and Bugbot gates.
6. Verify publication rights and provenance, then publish the exact accepted compatible cohort. Record and recover partial outcomes by exact version/digest.
6. Verify publication rights and provenance, then publish the reviewed prerelease cohort for installed adoption. Stable publication retains both consumer evidence gates. Record and recover partial outcomes by exact version/digest.
7. Pin the delivered cohort in actual consumers and complete installed/deployment acceptance; update catalog evidence only for verified implementation.
8. Complete human UX review, documentation, original SDK catalog validation, and all Definition of Done gates.

Expand Down
Loading
Loading