Repository navigation
Conversation
Store the address as the compose label sencho.service.url so a port click follows that host, and keep the generated link when the label is absent.
The compose filename scan checks the path stays inside the compose directory before it stats the file, so a rejected path is not treated as a missing file.
A URL edit copies inherited labels onto that service, a changed compose filename keeps the draft unless the operator reloads, and a missing URL list still opens the generated link.
A shared label or service anchor is copied before the edit, clearing a merged URL does not restore the inherited address, and a running container opens only its own saved URL.
A URL edit dropped empty labels and failed when a nested anchor was still used. A Docker-assigned port now opens that service's own URL, and the pencil stays unavailable when the service is not in the open compose file.
A shared URL value no longer changes another service. Open App does not follow a different service's dynamic address, a named port on its own bind address stays clickable, and a repeated list label takes the new address.
The dialog now records the node and stack it was opened on, closes when that selection changes, and refuses the save before it can write a different stack. Clearing or replacing a list alias updates only the edited service. A URL supplied by another compose file stays on that file, and reloading the compose source refreshes the cached addresses.
A save splices the label into the original text, so octal modes, line endings, and unrelated keys stay as written. A node with no host keeps the plain port, a refresh keeps the last URL, and Open App opens its tab before the lookup.
- Refuse a compose file, or a stack directory, that resolves outside <compose root>/<stack>, so a read or save of one stack cannot reach a sibling stack's compose file. Covers saveStackContent too. - Edit flow-style labels and services in place, copy anchored flow nodes without their defining key, keep source tokens such as 0440 and 022 when labels are copied, append after the last real label (before trailing comments, and at an end of file with no newline), and keep the separator when an empty URL value is set or cleared. - Check every patch before it can be saved: the file is read with YAML types and as untyped text, and anything that changes outside the URL label, drops a comment inside brackets, or touches a file with a custom YAML tag is refused with a clear message. - Return the cached service URLs only for the node and stack they were read for, so a port never links to the previous selection. - Log stacks the networking summary skips instead of dropping them silently.
A stack whose compose file resolves outside its own directory is skipped with a warning. Assert the linked stack stays out of every bucket, the healthy stack is still summarized, and the skip is announced.
The effective-anatomy response now reports, per service, whether the open main compose file controls the sencho.service.url label (serviceUrlEdit). That is established from the authored chain: the label key presence is read in the files merged after the open one (deploy spec order, or the resolved main file plus the auto-discovered override). A later file that sets the key blocks the edit whatever its value (invalid, interpolated, or equal); a later file that uses include, extends, a custom YAML tag, or COMPOSE_FILE, or that cannot be read, refuses the edit as unverifiable. The pencil lock, the save guard, and a fresh re-read immediately before the write consume this metadata, so the editor no longer infers ownership from the label value or its absence. Post-save verification stays. Service-keyed maps are built on null prototypes and read through own-property lookups across the backend extractor, the response parser and resolvers, and the local anatomy facts. Services named constructor, toString, hasOwnProperty, or __proto__ now resolve the generated link when they have no label and keep an own override when they do.
A later bare `sencho.service.url` list item (`- sencho.service.url`) sets the key to an empty value and shadows the open file, so the source authority now counts it as a key contribution like the `key=value` form. Clearing is not the mirror of setting: removing the open file's direct label can surface an earlier chain file, the open file's own include, a service's extends target, or its label_file. The anatomy response now carries a per-service clear authority alongside the set authority, the editor refuses an empty save the chain would keep winning or reveal, and the patch helper refuses the same layouts before writing. Setting a URL is unaffected. The patch helper also treats a bare list item as the key when setting or clearing, so a set no longer leaves a trailing empty entry that renders as an empty value. Operator docs describe the set and clear limits accurately.
Compose interpolates a list label's key, so `- "${URL_LABEL}=https://app"`
sets `sencho.service.url` even though the authored source never names it.
A later file using that shape reported the open file as editable and the
write did not take effect; an earlier or later one failed to block a clear.
Such a key now marks the service unverifiable, so both guards refuse before
the save. A literal key with an interpolated value stays a known set.
`published: 0` (from `"0:80"` or `{ target: 80, published: 0 }`) asks
Docker to assign the host port, but the dynamic-owner predicate treated it
as a fixed host, so a container-port click and Open App fell back to the
generated node address. It now counts as automatic allocation, leaving UDP
binding, named-service isolation, and ambiguity refusal unchanged.
- Read the open compose file's own source in the service URL set
authority: a labels list whose key is interpolated (`- "${NAME}=..."`)
makes that service unverifiable instead of editable, including when the
file's other facts cannot be traced (a top-level `include` or a custom
tag) and when the file cannot be read at all.
- Refuse a set and a clear in the editor buffer when the target service's
label list carries an interpolated key, so an unsaved draft is caught
before any write.
- Open a named container's own automatic binding URL on its card when
Docker assigned it the host port, even when another service's saved
file declares the same number, and refuse a saved-only single fixed
declaration when another service's automatic binding could hold it.
- Carry the runtime main-port owner (`com.docker.compose.service`) from
the bulk stack status through to Open App so it opens the address of
the running service that owns the port.
- Treat a named runtime container as direct ownership evidence: when a card carries both the compose service and the running container's own port, that service's configured URL applies even after the saved port declarations changed or were removed without a redeploy. - Keep saved-model inference and its ambiguity refusals for callers without runtime port data, such as the anatomy footer. - Cover the changed-port, removed-binding, hostless-remote, and rendered anchor cases in resolver and component tests. - State in the stack management docs that the configured label applies first and the generated address is the fallback.
Clearing a service URL from a labels map that also merges a YAML anchor materialized the whole map from parsed values, which deleted operator comments inside the map and respelled unrelated scalar tokens. The clear now copies inherited entries from their own source text, leaves direct entries untouched, and refuses layouts whose text cannot be kept. A running container whose compose service has no URL override fell through to saved-model inference and could turn ambiguous when other saved services claimed the same host port. Runtime ownership now answers with the service's own URL or the ordinary generated host and port. Regression tests cover the preserved comments and tokens (block, flow, CRLF, merge lists, nested merges, shared anchors) and the generated anchor plus copy target for a runtime owner without an override.
A first effective-anatomy lookup was read as "no override", so a published-port link and Open App could open the generated host and port while a configured service URL was still being fetched. The in-flight entry now stays pending: the port renders as plain text until the outcome is known, and Open App opens the tab in the click and navigates once the lookup resolves. Completed outcomes (no label, a failed load, an older node) keep the generated fallback, and a refresh that still holds the prior model keeps rendering it.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
sencho.service.urlcompose label when that service owns the port.- sencho.service.urllist item (Compose reads that as the empty value), when those later files cannot be traced (include,extends, a custom YAML tag,COMPOSE_FILE, a label list that builds the key from a variable, or an unreadable file), and when the open file's own label list builds the key from a variable or the open file cannot be read, because the requested value could not be proven effective; the pencil says the source cannot be verified. Clearing is refused before any write when another file still declares the label, when the chain cannot be traced or a label list builds the key from a variable on either side of the open file, or when the service inherits one throughextendsorlabel_file, because removing the label would reveal or keep another address. An unsaved draft that carries an interpolated list key is refused at the patch itself, before any write. A clear that is allowed returns the port to the generated host and port. The authority is re-read right before the write.0host port such as"0:80", resolves the same way. Open App opens the address of the service whose running container published the port; when the port could belong to another service's automatic binding and no runtime owner is known, it refuses rather than open the wrong address. On a cache miss, or while the first service URL lookup is still in flight, Open App opens the tab in the click and navigates it once the address is known.<<merge replaces only the merge key with the inherited labels copied from their own text, so the direct entries, comments, and tokens in the edited block stay exactly as written.{ }service, a comment inside a bracketed[ ]or{ }label list where removing the URL would drop it, aclearon a file or service that inherits labels throughinclude,extends, orlabel_file, and any file that uses!reset,!override, or another custom YAML tag. A clear through a<<merge is refused the same way when the merge source is written inline, when a merged label carries an anchor, alias, or tag its text cannot keep, when a comment sits on the merge key line, or when a merged value cannot be written into flow labels.constructor,toString,hasOwnProperty,__proto__) resolve their generated link when they have no label and keep their own override when they do, across the extractor, the response parser, the resolvers, and the local anatomy facts.sencho.service.urlentry. A list entry that is an alias is replaced or cleared on the edited service only.compose.yaml,compose.yml,docker-compose.yaml, ordocker-compose.yml. A save writes that file. If it is no longer the file on disk, the editor keeps the draft until the operator reloads it.http://orhttps://URL.Test plan
- sencho.service.urllist item and a label list whose key is interpolated (- "${URL_LABEL}=https://app"), while a literal key with an interpolated value stays a set and the uncertainty stays with the service that carries it; the open file's own interpolated list key refuses the set and the clear, including through a top-levelinclude, and an unreadable open file refuses too;include,extends, custom tags,COMPOSE_FILE, unreadable files, and an open file outside the chain all refuse; a genuinely authoritative interpolated label stays editable; chain ordering counts only files merged after the open one, including a later same-basename file in a subdirectoryinclude, a serviceextends, and alabel_fileall block the clear; a single-contributor file clears; the open file's own direct label does not block its own clearmainPortServicebeside the port it belongs to, through its cache, and the controller leaves it unset when the container has no compose service labelpublished: 0from both Compose syntaxes at its assigned runtime port, UDP bindings, Open App ambiguity, loading with a prior URL, a remote node with no host, Open App opening the tab before the lookup, and a stack or node switch never exposing the previous URL<<merge: the inherited labels are materialized from their own source text, a merge list and nested merges follow YAML precedence, direct entries, comments, octal and quoted tokens, CRLF endings, and a URL scalar another service aliases keep their exact text, flow and block layouts convert cleanly, and the layouts whose text cannot be kept (an inline merge source, an anchored, aliased, or tagged merged value, a comment on the merge key line, a value that cannot fit flow labels) are refused before any writeconstructor,toString,hasOwnProperty, and__proto__through the full parse-to-resolve path and the patch path$$escape, a map-form key, and a sibling service stay editable; and for the runtime owner: a named container card keeps its own service URL after a fixed saved port changed, after the saved binding was removed, and on a remote node with no host, while a stopped service's declaration of the same host number never takes the link (omitted publication and explicitpublished: 0included); the rendered card link and its copy target carry that address; a saved-only caller still refuses ambiguity instead of opening the stopped service, and Open App follows the runtime owner end to enddocker compose configrenders confirm a direct label in the open file wins over its ownincludeandextends; a later file shadows it; a later file's bare list label wins as the empty value; clearing the open file's label surfaces an earlier file's label, alabel_filevalue (from either side), anextendstarget's label, and aninclude's label. A render of a label list with an interpolated key (- "${URL_LABEL}=https://app") confirms it sets the effective label, and a render of"0:80"and{ target: 80, published: 0 }confirms both ask Docker for an automatic host port; the production source-authority and URL-resolution functions, driven by those renders, refuse the shadowed edit and the clear, and open the configured URL at the assigned port.docker compose configrenders of the merged-label fixture confirm the cleared service loses the label and keeps its other merged labels exactly, while the anchor's other consumer keeps the address, and the operator comment inside the edited block survives in the file textgit-transport-ssh.integration, which needs loopback port 22 to be freelabel_fileshows the clear refusal in the dialog with Save URL disabled, and typing a new URL re-enables it; clearing a URL on a single-contributor file restores the generated host and port and writes only that label; a service named__proto__renders its generated port fact and link, and its configured override; desktop and phone-width layoutswhoamicontainer published 8090:80 while the saved file was changed to 9090:80, then to no port at all; the container card kept its configuredhttps://web.example.comlink in both states, and with the label removed it fell back to the generatedhttp://localhost:8090Notes
- "${URL_LABEL}=https://app") is unverifiable, so the edit and the clear are refused before any write rather than saved and re-checked afterwards, in the later files, in the open file's saved source, and in an unsaved draft, while a literal key with an interpolated value stays a known set; clearing a URL that another file or an inherited service still supplies is refused before it reaches disk; an explicitpublished: 0counts as automatic allocation; and a published port resolves to the running service that owns it, so another service's saved declaration of the same host number cannot take over the card link or Open App, which refuses ambiguity when no runtime owner is known and an automatic binding could hold the port instead.<<merge no longer rebuilds the labels map from parsed values. The merge key is replaced by the inherited entries copied from their own text and the direct URL label is removed, so comments, quoting, octal tokens, and the anchor itself stay as written. A layout whose text cannot be preserved that way is refused with the same edit-in-the-editor message.docker compose configrenders above.PUT /api/stacks/:stackNameaccepts the resolved compose basename inX-Compose-Fileand answers412withcompose_source_changedwhen the file on disk is no longer the one that was read. Callers that omit the header keep the previous behavior.serviceUrlEditand the clear authority asserviceUrlClear.GET /api/stacks/statusesentries may carrymainPortService, the compose service of the running container that suppliedmainPort. Nodes without it keep the previous behavior: the port link falls back to the saved model and refuses ambiguity when another service could hold the port.Closes #2121