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 COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ 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.176` preserves native `mut` setters by composing `action` with
`fn (mut ...)` in the same 21 existing select bindings. The classic helper's
event `target.value` conversion is unchanged. Model identities, selected-row
references, schema choices, member capability checks, metadata/network writes,
save ownership/finalizers, API payload contracts and authentication are unchanged.
The shared compatibility helper and dependency graph are unchanged. Exact-source
CI, publication and packaged UI acceptance are pending; the full matrix remains
INCOMPLETE and historical HOLDs remain immutable.
See the [release note](docs/releases/web-console-1.6.176.md).

Published `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,
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ PastureStack is an independent community effort to preserve, audit, and moderniz

## Project status

Candidate `1.6.176` repairs native select bindings that passed a `mut` reference
directly to the classic compatibility `action` helper. The helper receives the
current value instead of a setter; wrap `mut` in native `fn` to retain the setter
while preserving the existing event value mapping. This covers the same 21
select bindings in 11 templates, including project roles, schema fields,
balancer rules, settings and machine-driver fields. Project member capabilities,
identities, metadata/network write boundaries, save finalizers and authentication
remain unchanged. Exact-source CI, immutable publication and packaged QA
acceptance are pending; historical HOLDs are not promoted and the full matrix
remains INCOMPLETE. See the [release note](docs/releases/web-console-1.6.176.md).

Published `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
Expand Down
4 changes: 2 additions & 2 deletions app/components/form-balancer-rules/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
</td>

<td data-title="{{t 'formBalancerRules.access.label'}}">
<select class="form-control input-sm" onchange={{action (mut rule.access) value="target.value"}}>
<select class="form-control input-sm" onchange={{action (fn (mut rule.access)) value="target.value"}}>
<option value="public" selected={{eq rule.access "public"}}>{{t 'formBalancerRules.access.public'}}</option>
<option value="internal" selected={{eq rule.access "internal"}}>{{t 'formBalancerRules.access.internal'}}</option>
</select>
Expand All @@ -78,7 +78,7 @@
{{/if}}

<td data-title="{{t 'formBalancerRules.protocol.label'}}">
<select class="form-control input-sm" onchange={{action (mut rule.protocol) value="target.value"}}>
<select class="form-control input-sm" onchange={{action (fn (mut rule.protocol)) value="target.value"}}>
{{#each this.protocolChoices as |proto|}}
<option value={{proto}} selected={{eq rule.protocol proto}}>{{upper-case proto}}</option>
{{/each}}
Expand Down
4 changes: 2 additions & 2 deletions app/components/machine/driver-amazonec2/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</div>
<div class="col-md-8">
{{#if this.isStep1}}
<select class="form-control" onchange={{action (mut this.amazonec2Config.region) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.amazonec2Config.region)) value="target.value"}}>
{{#each this.regionChoices as |choice|}}
<option value={{choice}} selected={{eq this.amazonec2Config.region choice}}>{{choice}}</option>
{{/each}}
Expand Down Expand Up @@ -77,7 +77,7 @@
</div>
<div class="col-md-8">
{{#if this.isStep3}}
<select class="form-control" onchange={{action (mut this.selectedZone) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.selectedZone)) value="target.value"}}>
{{#each this.zoneChoices as |choice|}}
<option value={{choice}} selected={{eq this.selectedZone choice}}>{{choice}}</option>
{{/each}}
Expand Down
6 changes: 3 additions & 3 deletions app/components/machine/driver-digitalocean/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<label class="form-control-static">{{t 'machine.driverDigitalocean.region.label'}}</label>
</div>
<div class="col-md-10">
<select class="form-control" onchange={{action (mut this.digitaloceanConfig.region) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.digitaloceanConfig.region)) value="target.value"}}>
{{#each this.regionChoices as |choice|}}
<option value={{choice.slug}} selected={{eq this.digitaloceanConfig.region choice.slug}}>{{choice.name}}</option>
{{/each}}
Expand All @@ -55,7 +55,7 @@
<label class="form-control-static">{{t 'machine.driverDigitalocean.image.label'}}</label>
</div>
<div class="col-md-4">
<select class="form-control" onchange={{action (mut this.digitaloceanConfig.image) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.digitaloceanConfig.image)) value="target.value"}}>
{{#each this.imageChoices as |choice|}}
<option value={{choice.slug}} disabled={{choice.disabled}} selected={{eq this.digitaloceanConfig.image choice.slug}}>{{choice.distribution}} {{choice.name}}</option>
{{/each}}
Expand All @@ -65,7 +65,7 @@
<label class="form-control-static">{{t 'machine.driverDigitalocean.size.label'}}</label>
</div>
<div class="col-md-4">
<select class="form-control" onchange={{action (mut this.digitaloceanConfig.size) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.digitaloceanConfig.size)) value="target.value"}}>
{{#each this.filteredSizeChoices as |choice|}}
<option value={{choice.slug}} selected={{eq this.digitaloceanConfig.size choice.slug}}>{{t 'machine.driverDigitalocean.sizeLabel' memoryGb=choice.memoryGb highMem=choice.highMem slug=choice.slug disk=choice.disk vcpus=choice.vcpus}}</option>
{{/each}}
Expand Down
6 changes: 3 additions & 3 deletions app/components/machine/driver-exoscale/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@

<div class="radio"><label>{{radio-button selection=this.whichSecurityGroup value="custom"}} {{t 'machine.driverExoscale.securityGroup.custom'}}</label></div>
{{#if this.isCustomSecurityGroup}}
<select class="form-control" onchange={{action (mut this.selectedSecurityGroup) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.selectedSecurityGroup)) value="target.value"}}>
{{#each this.allSecurityGroups as |choice|}}
<option value={{choice.name}} selected={{eq this.selectedSecurityGroup choice.name}}>{{choice.name}}</option>
{{/each}}
Expand Down Expand Up @@ -181,7 +181,7 @@
<label class="form-control-static">{{t 'machine.driverExoscale.instanceProfile.label'}}</label>
</div>
<div class="col-md-4">
<select class="form-control" onchange={{action (mut this.exoscaleConfig.instanceProfile) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.exoscaleConfig.instanceProfile)) value="target.value"}}>
{{#each this.allInstanceProfiles as |choice|}}
<option value={{choice.name}} selected={{eq this.exoscaleConfig.instanceProfile choice.name}}>{{choice.displaytext}}</option>
{{/each}}
Expand All @@ -200,7 +200,7 @@
<label class="form-control-static">{{t 'machine.driverExoscale.template.label'}}</label>
</div>
<div class="col-md-4">
<select class="form-control" onchange={{action (mut this.exoscaleConfig.template) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.exoscaleConfig.template)) value="target.value"}}>
{{#each this.allTemplates as |choice|}}
<option value={{choice}} selected={{eq this.exoscaleConfig.template choice}}>{{choice}}</option>
{{/each}}
Expand Down
6 changes: 3 additions & 3 deletions app/components/machine/driver-packet/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<label class="form-control-static">{{t 'machine.driverPacket.image.label'}}</label>
</div>
<div class="col-md-4">
<select class="form-control" onchange={{action (mut this.packetConfig.os) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.packetConfig.os)) value="target.value"}}>
{{#each this.osChoices as |choice|}}
<option disabled={{not choice.enabled}} value={{choice.slug}} selected={{eq this.packetConfig.os choice.slug}}>{{choice.name}}</option>
{{/each}}
Expand All @@ -45,7 +45,7 @@
<label class="form-control-static">{{t 'machine.driverPacket.size.label'}}</label>
</div>
<div class="col-md-4">
<select class="form-control" onchange={{action (mut this.packetConfig.plan) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.packetConfig.plan)) value="target.value"}}>
{{#each this.planChoices as |choice|}}
<option disabled={{not choice.enabled}} value={{choice.slug}} selected={{eq this.packetConfig.plan choice.slug}}>{{choice.name}}</option>
{{/each}}
Expand All @@ -62,7 +62,7 @@
<label class="form-control-static">{{t 'machine.driverPacket.region.label'}}</label>
</div>
<div class="col-md-10">
<select class="form-control" onchange={{action (mut this.packetConfig.facilityCode) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.packetConfig.facilityCode)) value="target.value"}}>
{{#each this.facilityChoices as |choice|}}
<option value={{choice.code}} selected={{eq this.packetConfig.facilityCode choice.code}}>{{choice.name}}</option>
{{/each}}
Expand Down
4 changes: 2 additions & 2 deletions app/components/machine/driver-rackspace/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<label class="form-control-static">{{t 'machine.driverRackspace.region.label'}}</label>
</div>
<div class="col-md-10">
<select class="form-control" onchange={{action (mut this.rackspaceConfig.region) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.rackspaceConfig.region)) value="target.value"}}>
{{#each this.regionChoices as |choice|}}
<option value={{choice.value}} selected={{eq this.rackspaceConfig.region choice.value}}>{{choice.label}}</option>
{{/each}}
Expand All @@ -53,7 +53,7 @@
<label class="form-control-static">{{t 'machine.driverRackspace.flavor.label'}}</label>
</div>
<div class="col-md-10">
<select class="form-control" onchange={{action (mut this.rackspaceConfig.flavorId) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.rackspaceConfig.flavorId)) value="target.value"}}>
{{#each this.flavorChoices as |choice|}}
<option value={{choice.value}} selected={{eq this.rackspaceConfig.flavorId choice.value}}>{{choice.label}}</option>
{{/each}}
Expand Down
4 changes: 2 additions & 2 deletions app/components/machine/driver-ubiquity/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
<label class="form-control-static">{{t 'machine.driverUbiquity.image.label'}}</label>
</div>
<div class="col-md-4">
<select class="form-control" onchange={{action (mut this.ubiquityConfig.imageId) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.ubiquityConfig.imageId)) value="target.value"}}>
{{#each this.allImages as |choice|}}
<option value={{choice.id}} selected={{eq this.ubiquityConfig.imageId choice.id}}>{{choice.name}}</option>
{{/each}}
Expand All @@ -101,7 +101,7 @@
<label class="form-control-static">{{t 'machine.driverUbiquity.flavor.label'}}</label>
</div>
<div class="col-md-4">
<select class="form-control" onchange={{action (mut this.ubiquityConfig.flavorId) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.ubiquityConfig.flavorId)) value="target.value"}}>
{{#each this.allFlavors as |choice|}}
<option value={{choice.id}} selected={{eq this.ubiquityConfig.flavorId choice.id}}>{{choice.name}}</option>
{{/each}}
Expand Down
2 changes: 1 addition & 1 deletion app/components/modal-edit-setting/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<label>{{radio-button selection=this.value value="false"}} {{t 'schema.inputBoolean.n'}}</label>
</div>
{{else if (eq this.model.kind 'enum')}}
<select class="form-control" onchange={{action (mut this.value) value="target.value"}}>
<select class="form-control" onchange={{action (fn (mut this.value)) value="target.value"}}>
{{#each this.model.options as |choice|}}
<option value="{{choice}}" selected={{eq choice this.value}}>{{choice}}</option>
{{/each}}
Expand Down
2 changes: 1 addition & 1 deletion app/components/schema/input-enum/template.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<select name="" class="form-control r-mb10" onChange={{action (mut this.value) value="target.value"}}>
<select name="" class="form-control r-mb10" onChange={{action (fn (mut this.value)) value="target.value"}}>
<option value="">{{t 'schema.inputEnum.option'}}</option>
{{#each this.field.options as |choice|}}
<option value={{choice}} selected={{eq choice this.value}}>{{choice}}</option>
Expand Down
2 changes: 1 addition & 1 deletion app/components/schema/input-secret/template.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<select class="{{this.selectClass}}" onchange={{action (mut this.selected) value="target.value"}}>
<select class="{{this.selectClass}}" onchange={{action (fn (mut this.selected)) value="target.value"}}>
<option selected={{eq this.selected null}}>{{t 'schema.inputSecret.prompt'}}</option>
{{#each this.filtered as |secret|}}
<option selected={{eq this.selected secret.id}} value={{secret.id}}>{{secret.name}}</option>
Expand Down
2 changes: 1 addition & 1 deletion app/components/view-edit-project/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
<td>{{member.displayType}}</td>
<td>
{{#if (and this.showEdit this.canEditMembers)}}
<select class="form-control input-sm" onchange={{action (mut member.role) value="target.value"}}>
<select class="form-control input-sm" onchange={{action (fn (mut member.role)) value="target.value"}}>
{{#each this.roleOptions as |choice|}}
<option value={{choice.value}} selected={{eq member.role choice.value}}>{{t choice.label}}</option>
{{/each}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@pasturestack/web-console",
"version": "1.6.175",
"version": "1.6.176",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@pasturestack/web-console",
"version": "1.6.175",
"version": "1.6.176",
"license": "Apache-2.0",
"dependencies": {
"sass": "1.103.1"
Expand Down
35 changes: 35 additions & 0 deletions docs/releases/web-console-1.6.176.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Web Console 1.6.176

Candidate source only. Exact-source CI, signed immutable publication, Server
packaging and deployed UI acceptance are pending. Do not promote historical
Project HOLDs or infer full-matrix acceptance from this correction.

## Root cause and minimal repair

The native Project member-role select used `action (mut member.role)` with
`value="target.value"`. Ember 7.2 exposes the current value of an invokable `mut`
reference to a classic helper. The compatibility `action` helper therefore sees
the current role string, not the setter, and dispatches that name instead of
updating the member. The DOM selection can change while the model stays unchanged.

Wrap the existing reference in native `fn`: `action (fn (mut member.role))`.
The helper now receives a callable setter and retains its existing event value
mapping. Apply exactly the same composition to 21 selects in 11 templates:
Project roles, schema enum/secret fields, settings, balancer rules and the six
affected machine drivers. No shared helper rewrite or unrelated event migration
is introduced. The existing source gate now also detects this unsafe composition
in `{{action ...}}`, not only parenthesized subexpressions.

Real-component regressions cover plain cloned and EmberObject Project members,
sorted selected-row identity, input/change propagation, unchanged owner/metadata,
members-only native save payload/finalizer and no metadata/network writes. A
rendered native schema enum checks nested model updates without replacing unknown
metadata or schema choices. Existing capability tests retain the unavailable
member-role select when no `setmembers` link exists.

Dependencies and their graph are unchanged; only root version metadata and its
executable/reviewed-baseline pins become 1.6.176. The Web175 image validation fix,
session generation, save owner, backend schemas, authorization, OIDC, platform
MFA and compatibility contracts remain intact. The complete matrix remains
INCOMPLETE. Retain configuration, volumes and rollback artifacts; this source
change does not authorize company deployment.
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.175",
"version": "1.6.176",
"private": true,
"description": "PastureStack browser console for the compatible control platform.",
"repository": {
Expand Down
6 changes: 3 additions & 3 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.175" ]]; then
echo "UNEXPECTED_UI_ARTIFACT_VERSION version=$version expected=1.6.175"
if [[ "$version" != "1.6.176" ]]; then
echo "UNEXPECTED_UI_ARTIFACT_VERSION version=$version expected=1.6.176"
failures=$((failures + 1))
fi

Expand All @@ -59,7 +59,7 @@ require_no_pattern 'class="(loader|orbit|loadfield|grass|sun|moon)"' \
'legacy product loading artwork returned to the application transition' \
app/application/template.hbs

require_no_pattern '\(action[[:space:]]+\(mut' \
require_no_pattern '(\{\{|\()action[[:space:]]+\(mut' \
'removed action-mut composition returned instead of the supported fn-mut contract' \
app

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.175 browser-session broker-broadcast
1.6.176 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.175":
if package.get("version") != "1.6.176":
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
Loading
Loading