The size check asserted more than 50 tokens against an actual 67, so a
third of the contract could be deleted with both tests still green.
Replaces it with two assertions that name what the package actually
promises: every shade of every color family, because GButton builds
selectors as var(--color-#{$color}-600) and a missing shade renders an
unstyled variant; and an exact list of the non-palette tokens.
custom_theme_variables.scss already computes eight masthead tokens
through #{$scss-variable}. Nothing in tokens.css uses interpolation
today, but if it ever does the value comparison comes out as
package="2.5rem" client="#{$masthead-height}", which reads like a token
mismatch rather than a parser limit. Interpolated values are now called
out for what they are.
The package arrived with a tsconfig.json but nothing that runs it, no
entry in vitest's include globs, and inside eslint's "packages" ignore
pattern -- so roughly 3,000 lines of moved component code had no check of
its own. Every guarantee it had came from the main client happening to
compile it through a Vite alias.
- eslint ignores packages/api-client specifically instead of all of
packages/, and the eslint scripts lint packages/ui/src alongside src.
The five errors this surfaced were import-sort autofixes.
- vitest's include picks up packages/*/src, which also starts running
packages/api-client's client.test.ts and integration.test.ts. Those
two files and their 9 tests had never run.
- packages/ui gets a type-check script so `vue-tsc -p` can be invoked
against the package on its own, not only via the client's tsconfig.
Porting GTip off SCSS variables replaced $border-radius-base with a
hardcoded 0.25rem, but $border-radius-base is 0.1875rem, so every variant
gained a pixel of corner radius. The code block lower in the same file
still rounds to 3px, which is what 0.1875rem resolves to.
The color changes in that port are a different matter and are left as
they are: GTip's success/warning/danger swatches now come from the token
palette rather than the $brand-* variables, which is what makes GTip
consistent with GButton and is presumably the point of the move.
GHeading landed as a copy of Common/Heading with icon narrowed from
IconLike to IconDefinition, which drops Galaxy's own icons (galaxyLogo and
friends) from what the prop accepts. That left two Heading implementations
in the tree -- Common/Heading with 94 importers, GHeading used only by
GModal -- and no way to migrate from one to the other, because the
replacement typed a strictly smaller prop.
Widening icon back to IconLike makes GHeading a superset of
Common/Heading: the two are otherwise identical apart from cosmetic
computed-property style and $brand-secondary vs var(--color-grey-200),
which are the same #dee2e6. Common/Heading becomes a shim like the other
15 components, so the 94 call sites keep working untouched and there is
one Heading again.
useClickableElement and useCurrentTitle are exported from the barrel but
ClickableProps and HasTitleProps are not, so a consumer can name the
composables and not the shape of the props they take. Both types were
reachable from the old client paths before the move; re-export them from
the barrel and from the two shims that dropped them.
Adds iconTypes with CustomIconDefinition and IconLike. FontAwesomeIcon
accepts icon definitions generated outside FontAwesome's registry as long
as they match its structure, and Galaxy ships a set of them, so the
package needs a name for "FA icon or a lookalike" that does not reach
back into the app for it.
The rebase conflict resolution left componentVariants' import wrapped
across lines; the client's prettier wants it on one (fits in the
120-char width). Caught by CI's format-check.
Wiring the shed frontend up to this package under strict TS surfaced two
gaps: uid.js shipped untyped (the client's allowJs masked it), and
markdown.ts leaned on the client's own @types/markdown-it devDep for its
types. Convert uid to TypeScript and declare @types/markdown-it as a real
dependency so consumers get the same view of the source the client does.
The components style themselves with --color-*/--spacing-*/--font-size-*
custom properties that only the Galaxy client defined, so anywhere else
they compiled fine but rendered unstyled. tokens.css carries that
contract with the package (import "@galaxyproject/galaxy-ui/tokens.css")
and a sync test in the client asserts every token the package declares
matches custom_theme_variables.scss, so the copies can't drift.
The package imported vue-router, @vueuse/core, the FontAwesome stack,
@floating-ui/dom, and markdown-it while declaring only a vue peer -- it
worked in the client workspace by hoisting luck and failed anywhere else.
Vue-coupled imports are now peers with ranges spanning both consumers
(vue 2.7/3, router 3/4, vue-fontawesome 2/3), and the vue-agnostic bits
(floating-ui, markdown-it, the font-awesome-6 icon alias) are regular
dependencies so consumers no longer need the client's npm alias trick.
galaxy-ui is "private": true and never gets published to npm. With it
declared as a runtime dependency, pnpm publish would rewrite workspace:*
to 0.0.0-internal in the published galaxy-client tarball, and consumers'
npm install would then fail trying to fetch a non-existent package.
galaxy-ui is a source dep only -- its compiled output is bundled into
client/dist/ at build time, so consumers don't need it at install time.
Relocates 15 G-prefixed base components (GButton, GButtonGroup, GCheckbox,
GCollapse, GHeading, GLink, GModal, GOverlay, GTab, GTabs, GTip, GTooltip,
Form/{GForm,GFormInput,GFormLabel}), 6 supporting composables
(accessibleHover, clickableElement, currentTitle, markdown, resolveElement,
uid), componentVariants, and tooltipTiming into packages/ui/src/. Old
paths become re-export shims so consumers keep working unchanged --
app-side imports get updated gradually rather than in a mass codemod.
Internal imports within the package are rewritten to relative paths; zero
@/ imports remain in packages/ui/src/, so the boundary doesn't silently
depend on the main client's source tree.
Adds packages/ui/ as a second workspace member alongside api-client --
private, source-only, no library build. Empty src/index.ts barrel;
components and composables move in next.
Wires galaxy-ui into the main client as a workspace:* dep and as an
unconditional resolve.alias in vite.config.mjs (serve and build). The
api-client alias stays serve-only because it has a real dist/ build
and production consumes that via package.json exports; galaxy-ui is
internal-only so we skip the build step entirely and let the main
client's @vitejs/plugin-vue2 handle the Vue 2 SFCs from source in
both dev and prod.
client/packages/README.md now lists both packages and calls out the
"flips to a library build + publish when the interface stabilizes on
Vue 3" intent.
GModal's match() call just maps small/medium/large to sm/md/lg for
GHeading -- swap it for a plain record lookup. GForm's assertDefined
on the form template ref becomes an inline null check. Both are prep
for the galaxy-ui extraction: neither helper is UI-shaped, so those
stay put in @/utils/ while the component boundary stays clean.
GHeading is a near-identical variant of Common/Heading.vue with the
Galaxy-specific bits removed: no IconLike dependency from
@/components/icons/galaxyIcons (accepts FontAwesome IconDefinition
directly), and no @import of the Galaxy theme SCSS -- the separator
stripe uses var(--color-grey-200) instead of $brand-secondary (same
hex value).
GModal was the only G-component importing Common/Heading.vue. It now
uses GHeading, which keeps BaseComponents self-contained. Heading.vue
stays in place for the dozens of feature-component consumers.
Prep work for the @galaxyproject/galaxy-ui workspace package: the
package can't depend on Galaxy-specific icon types or theme SCSS.
Drops the @import of @/style/scss/theme/blue.scss and switches to the
--color-* tokens from custom_theme_variables.scss (PR #19946, Laila
Los): --color-blue-600 for info, --color-orange-600/200 for warning,
--color-green-600/200 for success, --color-red-600/200 for danger, and
--color-grey-100 for neutral background.
GTip was the only G-component with an explicit theme SCSS import, so
removing it cleans up the last bit of mandatory theme coupling in
BaseComponents. The SCSS scale-color() calls for warning/success/danger
backgrounds map to the 200-shade of each hue, which is the closest
pre-defined match and avoids the partial-transparency pattern the color
system intentionally moved away from.
Prep work for the @galaxyproject/galaxy-ui workspace package: the
package can't depend on Galaxy's SCSS theme, so it consumes CSS custom
properties injected by the app at :root.
ColorVariant lives with the other component variant types inside
BaseComponents/ so G-components don't need to reach back into
@/components/Common to get their own types. Common/index.ts re-exports
for the app-code consumers that still import it from there.
Prep work for carving BaseComponents into a @galaxyproject/galaxy-ui
workspace package -- the package needs its types self-contained.
Self-review turned up three problems in the guidance I just added.
The mapped array idiom joined paths unquoted. shell_command is the one branch
that doesn't shlex.quote its result (unlike base_command/arguments right above
it in _build_command_line), so a path with a space split into two arguments.
Switched to the per-element quoted form our own test tool
cat_multiple_user_defined.yml uses, which renders cat '/d/s 1.fq' '/d/b.fq'.
"There is no [] indexing syntax" was too strong and simply wrong -- expressions
are JavaScript with InlineJavascriptRequirement always on, so
inputs.some_repeat[0].x is fine and the validator's own comment cites that form.
Only the empty [] is a syntax error. Narrowed the claim to that.
And scoping `value` to the types that have one: select and data forbid it and
fail with extra_forbidden, so telling the model to give every optional parameter
a `value` traded one validation failure for another. Select defaults are
selected: true on an option. Same correction on the critic side, where asking
for a field that can't exist would have set needs_full_refine and burned an
extra generation pass.
Also dropped the critic's unconditional claim that a dedicated step resolves the
container, which has the same off-by-default problem as the producer prompt.
A pass over the rest of the prompt against the actual model turned up several
places where it tells the model something that isn't true.
The container section promised Galaxy re-resolves a "close, plausible" image to
a verified biocontainer, but container_recommendation_enabled is off by
default, so the guess is usually what runs -- and the accuracy section right
below told the model never to guess. Reworded so the two agree.
`help` was described as help text when it wants a HelpContent object; a scalar
fails validation, so there's an example now. Collection outputs need
discover_datasets rather than from_work_dir, which the generic "one or the
other" wording hid. The id pattern is stricter than "lowercase with hyphens" --
it must start with a letter. And GALAXY_SLOTS was promised to equal cores_min,
which only holds where the deployment maps it; the local runner falls back to
local_slots or 1.
Also fixed the critic prompt, which asked for `default` on optional parameters.
Scalar inputs use `value` and forbid extras, so acting on that suggestion
produced a tool that fails validation.
The prompt told the model to reach for $(inputs.param_name[].path) on multiple
data inputs, which isn't valid JavaScript. Expressions go through do_eval, so
that form dies with "SyntaxError: Unexpected token ']'" -- and nothing catches
it up front, because _check_input_refs only extracts the leading input name. A
tool using it validates, saves, and then fails when Galaxy builds the command
line.
Swapped in the mapped form our own user_defined_tools docs use, and said
plainly that there's no [] indexing, since the old wording is exactly the shape
a model would pattern-match to. Prettier reindented the YAML examples it found
on the way past; those blocks parse identically before and after.
Nothing read custom_tool_structured.md, so when YamlDataParameter.format was
narrowed to a list its examples went stale silently and the prompt started
teaching YAML that schema consumers reject. This parses the fenced YAML out of
the prompt and holds it to UserToolSourceAuthoringView, both through pydantic
and through the dumped JSON Schema.
Checking both matters: _split_format normalizes the legacy comma-separated
string, so `format: fastq` sails through pydantic while the schema a
structured-output consumer actually enforces rejects it. A pydantic-only test
would have caught neither of the bugs in #23335.
get_data_stream returns something that owns an open read, but its Iterator[bytes]
type said only that bytes come out of it. Callers had to probe for close() with
getattr to find out whether they were holding a connection.
Return a DataStream protocol instead, so the obligation to close an unconsumed
stream is part of the contract rather than a comment, and the service can just
close it.
cloudbridge picked the read size itself and it was never a tuning decision: 4 KiB
on AWS, inherited from a 2017 boto2 shim. A 10 GB download is 2.6 million reads
at that size, each one a threadpool hop on the way to the client.
4.4.0 takes a chunk_size, so pass the same STREAM_CHUNK_SIZE the other backends
stream at, and require that version where the dependency is declared.
`closing_stream` became a one-line factory when the context manager grew into a
class, so two names described one thing across four backend modules. Every
signature already says `RemoteDataStream`; the backends now build one directly.
`_ClosingIterator` wraps a generator this module creates, not an arbitrary
iterator, so say so and call `close()` outright rather than reaching for it with
`getattr`. The whole point of the surrounding change is that a chunk iterator
having a `close` proves nothing about what it releases -- worth not modelling the
one case where we do know.
Make remote data streams explicitly closable before their tee generator is entered, and have GalaxyStreamingResponse close synchronous response content on normal completion and failure.
Also release the stream if download logging or response construction fails, with regressions for all three pre-iteration paths.
#19255 reports three backends. Two are boto3 and this branch already fixes them.
The third, and the one the reporter called unusable, is generic_s3: 4GB+
downloads succeed but arrive truncated to about 3GB, silently.
generic_s3 is GenericS3ObjectStore, which extends the boto2 S3ObjectStore -- a
different class from the boto3 one that got `_stream_remote` -- so it inherited
the no-op and kept pulling into the cache. It is also the case the tee handles
best: `_tee_to_cache` compares streamed bytes against the store's size, turning a
silent truncation into a loud failure instead of a corrupt cached object.
boto2's Key reads in sized chunks like the boto3 body does. Its close reads the
rest of the response first unless told not to, hence fast=True: a client that
hung up should not make Galaxy drain 3GB before releasing the connection.
`_stream_remote` handed back a bare `Iterator[bytes]`, which has no way to
release what it is reading from. Closing botocore's `iter_chunks` only ends the
generator -- it is a plain loop over `StreamingBody.read` -- so the response
stays open and its connection never returns to the pool. Cancelled downloads are
the normal case on this path: it exists for the big transfers clients abandon.
Backends now return a context manager owning the read, built by `closing_stream`
from the chunks and the SDK's own close. `_tee_to_cache` enters it, so the
connection is released whether the client took every byte, hung up part way, or
the stream errored.
Also open the read last, in `_get_data_stream` and in `_stream_from_object_store`
alike. Both used to open first and could then decide against streaming -- an
unknown remote size, a size lookup that raised -- returning None with a live read
that nobody owned.
--buffer-size="$GALAXY_MEMORY_MB_PER_SLOT"M is a no-op on the default
deployment. MEMORY_STATEMENT_TEMPLATE.sh explicitly unsets the variable
when it can't derive a positive value, so on the local runner it's absent
and GNU sort accepts --buffer-size=M silently (exit 0, no stderr, no cap).
Emit the flag only when there's a value rather than defaulting to an
invented number, which would be worse than no cap on a big-memory node.
GALAXY_MEMORY_MB rather than the per-slot variant: the two sorts now run
sequentially, one process at a time with the whole job's memory available,
so a per-slot budget combined with --parallel=$GALAXY_SLOTS under-budgets
by a factor of GALAXY_SLOTS.
Also register comp1 1.0.2 -> 1.0.3 in WORKFLOW_SAFE_TOOL_VERSION_UPDATES.
The bump touches only <requirements> and <command>; the inputs are
unchanged, so the update is workflow-safe. Without the entry
_stock_tool_source_for returns None for workflows pinned to 1.0.2.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
for large files sort uses a lot of memory (a user had a job with a file
of 45M lines and top showed 1TB of VIRT memory and the jon crashed a
37GB) and we need a way to limit this.
also parallel processing seems a nice to have
`format: fastq` is a string, but `YamlDataParameter.format` is a list.
The string form parses only via the `_split_format` before-validator; the
JSON Schema dumped from the model does not declare it, so schema
consumers reject what the prompt teaches. The prompt's own complete
example omits `format` entirely, so the two examples disagreed.
Also state `name`'s `min_length=5`. It was unstated, and short names
("BWA", "STAR") are exactly what a model reaches for.
Neither bites upstream, where `output_type=UserToolSourceAuthoringView`
constrains generation. Both bite consumers using the prompt unconstrained.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both onCreate and the save-as handler clear hasChanges before calling
routeToWorkflow, so the onSave() in there hits its early return and never
reaches the getVersions() call below it. onCreate got a compensating fetch
in b592a0f, but save-as didn't, so saving-as leaves you editing the new
workflow with the previous one's version list still in the dropdown.
id only ever changes in routeToWorkflow, so refreshing there covers both
paths and lets the onCreate copy go away.
`CopyStepAction` already stripped the cloned step's own `uuid`, but left the uuids on its `workflow_outputs` array untouched, since `cloneStepWithUniqueLabel` deep-clones the step with `structuredClone` and copies those output uuids verbatim. Saving a workflow after cloning a step with a workflow output would then fail with "Duplicate workflow output UUID '...' in request." the same way the original bug failed with duplicate step UUIDs. The fix strips `uuid` off each entry of the cloned step's `workflow_outputs`, mirroring how the step's own `uuid` is already handled.
Extended the existing "assigns a fresh id and uuid when cloning a step" regression test to also give the source step a `workflow_outputs` entry with a fixed uuid, and assert the clone's output uuid differs from the source's.
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Alireza Heidari <itisalirh@gmail.com>