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
24 changes: 19 additions & 5 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,30 @@ Web Console preserves compatible API paths, schema and resource names, action na

Visible branding, product-owned assets, icon identifiers, package metadata, and operator documentation use PastureStack. Historical identifiers remain only where they are server data or protocol contracts and must not be mechanically replaced.

Candidate `1.6.174` changes VM image guidance and defaults only: it does not
Candidate `1.6.175` refreshes only the image form's locally owned validation
aggregate after image/locale correction. Other validation errors and backend
save failures are preserved; shared NewOrEdit ownership, hook/finalizer behavior,
wire payloads, schema and authorization remain unchanged. Four new real-component
tests passed within 17/17 local Chrome 153 related tests. Exact-source official
CI, signed publication and packaged acceptance remain pending. No dependency
version or graph change is introduced; the full matrix remains INCOMPLETE.
See the [candidate release note](docs/releases/web-console-1.6.175.md).

Published `1.6.174` changes VM image guidance and defaults only: it does not
implement a VM runtime or image whitelist. Existing/custom/last-used VM images,
ordinary container defaults and required validation remain supported. Shared
image-required errors are localized in English and Traditional Chinese, with
the existing English fallback for other locales. Backend capabilities,
payloads, permissions and authentication contracts are unchanged; dependency
versions and the graph are unchanged. The five added Ember regressions and
formal release gates remain pending; 817 is only an expected test count.
Historical releases and HOLDs are not promoted, and the full matrix remains
INCOMPLETE. See the [candidate release note](docs/releases/web-console-1.6.174.md).
versions and the graph are unchanged. Exact-source CI37152802665 passed
817/817 tests with zero failures, skips or todo. Signed source
`d24b7f4e164f058e3ef9057347caa2080e2b5407`, both CI builds and anonymous public
downloads match archive SHA256
`6408775898f412e4b27092eeddd9cdc2028ad7b27835f489139c2d0cf62c6776`.
Server512 packaging and QA512 image-form-only acceptance are verified separately;
VM boot is not verified and the parent validation-message defect remains pending
the unpublished Web175 candidate. Historical HOLDs are not promoted. The full
matrix remains INCOMPLETE. See the [release note](docs/releases/web-console-1.6.174.md).

Published `1.6.173` reads native ProjectTemplate card labels from the model's
existing `name`, not `localizedName`, which native ProjectTemplate does not implement.
Expand Down
33 changes: 26 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,37 @@ PastureStack is an independent community effort to preserve, audit, and moderniz

## Project status

Candidate `1.6.174` removes the ordinary-container default and quick picks from
Candidate `1.6.175` keeps the container/VM form's image validation message in
sync when the operator corrects the image or changes the locale. Only the
form's own validation aggregate is refreshed: model/command errors and later
backend save errors remain intact. The shared save lifecycle, lock ownership,
payloads, permissions and authentication are unchanged. Local Chrome 153
verification passed 17/17 related tests with zero failures, skips or todo,
including four new real-component regression tests. This is source validation,
not packaged QA acceptance. Formal CI, publication and Server packaging for
this candidate remain pending; the full matrix remains INCOMPLETE. See the
[candidate release note](docs/releases/web-console-1.6.175.md).

Published `1.6.174` removes the ordinary-container default and quick picks from
the VM image field. Custom images, existing image values and the last-used VM
image remain supported; ordinary container defaults are unchanged. A blank
image still blocks the existing save lifecycle. VM boot-image guidance and
required errors for both VM and container images use reviewed English and
Traditional Chinese copy with the existing English fallback.
Dependency versions and the dependency graph are unchanged. Five added Ember
regressions have not yet run; 817 total tests is an expectation, not a result.
Exact-source CI, reproducible artifacts, signed publication, Server packaging
and packaged VM acceptance remain pending. Historical releases and HOLDs are
unchanged; the full matrix remains INCOMPLETE. See the
[candidate release note](docs/releases/web-console-1.6.174.md).
Dependency versions and the dependency graph are unchanged. Exact-source
[CI37152802665](https://github.com/PastureStack/web-console/actions/runs/37152802665)
passed 817/817 tests with zero failures, skips or todo. The signed numeric
[release](https://github.com/PastureStack/web-console/releases/tag/1.6.174)
pins source `d24b7f4e164f058e3ef9057347caa2080e2b5407`; both reproducible CI
archives and anonymous public readback match SHA256
`6408775898f412e4b27092eeddd9cdc2028ad7b27835f489139c2d0cf62c6776`
(2,982,022 bytes). Server `v1.6.512` packages this exact component. QA125/8080
fresh VM/container image forms passed eight scoped English/Traditional Chinese
cases with zero resource writes. This is not a successful VM boot or full
permission/resource/locale acceptance; the discovered stale parent image error
is addressed by the unpublished `1.6.175` candidate above. Historical releases
and HOLDs are unchanged; the full matrix remains INCOMPLETE. See the
[published release note](docs/releases/web-console-1.6.174.md).

Published `1.6.173` repairs empty environment-template choice labels. These
choices are native `ProjectTemplate` resources, not Catalog templates: their
Expand Down
19 changes: 17 additions & 2 deletions app/components/new-container/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ export default Component.extend(NewOrEdit, SelectTab, {
// ----------------------------------
validate() {
this._super();
var errors = this.get('errors')||[];
var errors = (this.get('errors')||[]).slice();

if ( this.get('isService') )
{
Expand Down Expand Up @@ -490,7 +490,6 @@ export default Component.extend(NewOrEdit, SelectTab, {
hardwareIssues(config, host).forEach((key) => errors.push(this.get('intl').t(key === 'deviceGroup' ? 'formResources.deviceGroupHelp' : `formResources.errors.${key}`)));
});
errors.pushObjects(this.get('scaleErrors')||[]);
errors.pushObjects(this.get('imageErrors')||[]);
errors.pushObjects(this.get('portErrors')||[]);
errors.pushObjects(this.get('diskErrors')||[]);

Expand All @@ -502,7 +501,10 @@ export default Component.extend(NewOrEdit, SelectTab, {
errors.push(this.get('intl').t('formPorts.preflight.error.sidekickBlocked'));
}

this._nonImageValidationErrors = errors.slice();
errors.pushObjects(this.get('imageErrors')||[]);
errors = errors.uniq();
this._imageValidationAggregate = errors.get('length') ? errors : null;

if ( errors.get('length') )
{
Expand All @@ -514,6 +516,19 @@ export default Component.extend(NewOrEdit, SelectTab, {
return true;
},

// Refresh only the local aggregate produced by validate(), not a later save error.
imageErrorsDidChange: function() {
if ( !this._nonImageValidationErrors || this.get('errors') !== this._imageValidationAggregate ) {
return;
}

let errors = this._nonImageValidationErrors.slice();
errors.pushObjects(this.get('imageErrors')||[]);
errors = errors.uniq();
this._imageValidationAggregate = errors.get('length') ? errors : null;
this.set('errors', this._imageValidationAggregate);
}.observes('imageErrors.[]'),

doSave() {
if ( this.get('isService') && this.get('isUpgrade') )
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@pasturestack/web-console",
"version": "1.6.174",
"version": "1.6.175",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@pasturestack/web-console",
"version": "1.6.174",
"version": "1.6.175",
"license": "Apache-2.0",
"dependencies": {
"sass": "1.103.1"
Expand Down
26 changes: 17 additions & 9 deletions docs/releases/web-console-1.6.174.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
# Web Console 1.6.174

Candidate only. Exact-source CI, reproducible production artifacts, signed
publication, anonymous public readback and Server packaging have not yet been
completed for this version. No VM lifecycle PASS is claimed.
Published immutable numeric release. Exact-source CI37152802665 passed 817/817
tests with zero failures, skips or todo, including the five direct VM-image
regressions. Signed source `d24b7f4e164f058e3ef9057347caa2080e2b5407` and both
reproducible CI archives match the anonymous public archive readback: SHA256
`6408775898f412e4b27092eeddd9cdc2028ad7b27835f489139c2d0cf62c6776`,
2,982,022 bytes. Publication reused the CI asset without rebuilding.
Server `v1.6.512` packages this component; immutable image digest
`sha256:805078de83c0320c751dff90304bd841b8e64bec720079198258d22fa41d0145`.
QA125/8080 image-form-only acceptance passed eight English/Traditional Chinese
VM/container cases, with fresh dual MFA, three Full17/14 guards and zero
resource writes. No successful VM boot or full-matrix PASS is claimed.

## Root cause and bounded repair

Expand Down Expand Up @@ -35,16 +43,16 @@ change only their root package version metadata to `1.6.174`.
Five new real-component Ember regressions cover existing and last-used images,
blank-image save cancellation, custom VM input without misleading quick picks,
VM guidance/required-error locale changes and ordinary-container required-error
locale changes on the same form. They have not yet been executed. The expected
full test count is 817 (the previous 812 plus these five), not a verified result.
locale changes on the same form. All five passed in the verified 817-test CI run.
Offline method controls and syntax checks do not replace Chrome rendering,
exact-source CI or packaged native VM acceptance.

No source commit, formal run, archive checksum or publication coordinate is
asserted for this candidate. Reuse only the eventual reviewed exact-source CI
artifact for publication; do not rebuild or overwrite an older release.
The published coordinates above do not establish VM runtime acceptance.
The packaged forms exposed a separate stale parent image-validation error after
the child input was corrected. That observation remains OPEN on Server512;
the unpublished Web175 source candidate has four new focused regression tests.
The existing vendor-pending advisory and audit policy remain unchanged; this
candidate is not a zero-CVE claim.
release is not a zero-CVE claim.

The [published Web173 record](web-console-1.6.173.md) and its scoped results
remain historical evidence, not proof of this candidate's VM lifecycle.
Expand Down
32 changes: 32 additions & 0 deletions docs/releases/web-console-1.6.175.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Web Console 1.6.175

Candidate only. Formal exact-source CI, signed numeric release, reproducible
archive and Server packaging are pending. Local tests do not establish packaged
UI acceptance or full permission/resource/locale acceptance.

## Root cause and minimal repair

The child image form refreshed its own `imageErrors` when an operator corrected
the image, but `new-container` retained the previous required message in its
parent error aggregate. The same stale copy could remain after a locale change.

Keep a private snapshot of non-image validation errors and observe the existing
image-error array. Refresh the parent only while it still owns the exact aggregate
it created. A later backend error replaces that array and is not overwritten.
The non-image snapshot preserves model and command errors, even when their text
matches the image error. No shared NewOrEdit change, new save hook, wire-format
change, authorization change or backend patch is introduced.

Four new real-component QUnit tests cover VM/container correction, preservation
of non-image errors, locale replacement and a real save failure/errorSaving hook
followed by image correction. Local Chrome 153 passed 17/17 related tests with
zero failures, skips or todo. The full CI count is not yet a verified result.

Dependency versions and the dependency graph are unchanged. Package metadata
changes only the root version. Existing session generation, mutex, session-bound
logout, OIDC, TOTP, Passkey, permissions, hardware payload and save ownership
remain intact. The full matrix is INCOMPLETE; historical HOLDs are preserved.
Executable version gates and the reviewed lockfile baseline carry the same
1.6.175 root metadata; no previous-release version pin is reused for this build.
Keep configuration, volumes and rollback artifacts. No company deployment is
authorized by this source change.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pasturestack/web-console",
"version": "1.6.174",
"version": "1.6.175",
"private": true,
"description": "PastureStack browser console for the compatible control platform.",
"repository": {
Expand Down
4 changes: 2 additions & 2 deletions scripts/check-modernization-blockers
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ with open('package.json', encoding='utf-8') as f:
print(json.load(f).get('version', ''))
PY
)
if [[ "$version" != "1.6.174" ]]; then
echo "UNEXPECTED_UI_ARTIFACT_VERSION version=$version expected=1.6.174"
if [[ "$version" != "1.6.175" ]]; then
echo "UNEXPECTED_UI_ARTIFACT_VERSION version=$version expected=1.6.175"
failures=$((failures + 1))
fi

Expand Down
2 changes: 1 addition & 1 deletion scripts/check-ui-console-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,4 @@ if [[ -n ${PASTURESTACK_PRIVATE_MARKER:-} ]] && grep -RInF -- "$PASTURESTACK_PRI
fi

printf 'UI_CONSOLE_WORKSPACE_OK version=%s persistence=%s cross_tab=%s\n' \
1.6.174 browser-session broker-broadcast
1.6.175 browser-session broker-broadcast
2 changes: 1 addition & 1 deletion scripts/check-ui-critical-high-dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ if lock_bytes != baseline_bytes:
lock = json.loads(lock_bytes)
packages = lock.get("packages", {})
root = packages.get("", {})
if package.get("version") != "1.6.174":
if package.get("version") != "1.6.175":
fail(f"unexpected Web Console version: {package.get('version')}")
if root.get("version") != package.get("version"):
fail(f"lock root version differs: {root.get('version')}")
Expand Down
99 changes: 99 additions & 0 deletions tests/unit/components/new-container-image-errors-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { A } from '@ember/array';
import EmberObject from '@ember/object';
import { run } from '@ember/runloop';
import { module, test } from 'qunit';
import NewContainerComponent from 'ui/components/new-container/component';
import FormImageComponent from 'ui/components/form-image/component';
import inertRenderer from '../../helpers/inert-renderer';
import { createOwned, destroyOwned } from '../../helpers/owned-subject';

module('Unit | Component | new container image errors');

function pair(isVm = true) {
let launchConfig = EmberObject.create({labels: {}, ports: A(), secrets: A()});
let model = EmberObject.create({
launchConfig, secondaryLaunchConfigs: A(),
validationErrors() { return A(); },
});
let parent, image;
let intl = EmberObject.create({label: '', t(key) { return this.get('label') + key; }});
run(() => {
parent = createOwned(NewContainerComponent, {
renderer: inertRenderer(), intl, launchConfig, service: model,
primaryResource: model, primaryService: model, isService: true,
}, 'component');
image = createOwned(FormImageComponent, {
renderer: inertRenderer(), intl, isVm, initialValue: 'docker:example.test/custom:qa',
projects: EmberObject.create({current: EmberObject.create({isWindows: false})}),
settings: EmberObject.create({appName: 'PastureStack'}), sendAction() {},
}, 'component');
});
let sync = () => parent.set('imageErrors', image.get('errors'));
image.addObserver('errors', parent, sync);
run(sync);
return {parent, image, model, intl, destroy() {
image.removeObserver('errors', parent, sync);
destroyOwned(image);
destroyOwned(parent);
}};
}

test('VM and container correction clears only their locally aggregated required error', function(assert) {
for (let isVm of [true, false]) {
let p = pair(isVm);
try {
run(() => p.image.set('userInput', ''));
assert.notOk(p.parent.get('errors.length'), 'does not auto-validate the parent before local validation');
assert.notOk(p.parent.validate());
assert.strictEqual(p.parent.get('errors.length'), 1);
run(() => p.image.set('userInput', 'registry.example/custom:qa'));
assert.deepEqual(p.image.get('errors'), []);
assert.strictEqual(p.parent.get('errors'), null, 'top-errors clears without another validate/save');
assert.ok(p.parent.validate(), 'required validation is not weakened');
} finally { p.destroy(); }
}
});

test('correction retains model and other component errors, including matching text', function(assert) {
let p = pair();
let required = 'formImage.vm.bootImageRequired';
try {
p.model.validationErrors = () => A(['model error']);
run(() => {
p.parent.set('commandErrors', A([required, 'command error']));
p.image.set('userInput', '');
});
assert.notOk(p.parent.validate());
run(() => p.image.set('userInput', 'registry.example/custom:qa'));
assert.deepEqual(p.parent.get('errors'), ['model error', required, 'command error'], 'matching non-image diagnostics are not removed');
assert.notOk(p.parent.validate(), 'remaining invalid fields still block saving');
} finally { p.destroy(); }
});

test('locale replacement refreshes the required text without retaining the previous image message', function(assert) {
let p = pair();
try {
run(() => p.image.set('userInput', ''));
assert.notOk(p.parent.validate());
run(() => { p.intl.set('label', 'zh:'); p.image.validate(); });
assert.deepEqual(p.parent.get('errors'), ['zh:formImage.vm.bootImageRequired']);
run(() => p.image.set('userInput', 'registry.example/custom:qa'));
assert.strictEqual(p.parent.get('errors'), null);
} finally { p.destroy(); }
});

test('a real save failure and errorSaving hook survive subsequent image correction', async function(assert) {
let p = pair();
let cleanupCalls = 0;
p.parent.errorSaving = () => { cleanupCalls++; };
try {
run(() => p.image.set('userInput', ''));
assert.notOk(p.parent.validate());
await p.parent._handleSaveFailure('backend rejection');
let backend = p.parent.get('errors');
assert.deepEqual(backend, ['backend rejection']);
run(() => p.image.set('userInput', 'registry.example/custom:qa'));
assert.strictEqual(p.parent.get('errors'), backend, 'the server error array is neither cleared nor replaced');
assert.strictEqual(cleanupCalls, 1, 'the existing failure cleanup runs once');
} finally { p.destroy(); }
});
Loading