diff --git a/doc/source/releases/25.1_announce.rst b/doc/source/releases/25.1_announce.rst index 540f7dd83e9..714ec761e5d 100644 --- a/doc/source/releases/25.1_announce.rst +++ b/doc/source/releases/25.1_announce.rst @@ -1,6 +1,118 @@ -:orphan: - =========================================================== -25.1 Galaxy Release +25.1 Galaxy Release (December 2025) =========================================================== + +.. include:: _header.rst + +Please see the `25.1 user release notes <25.1_announce_user.html>`__ for a summary of new user features. +The `GitHub Release Notes `__ provide a comprehensive overview of all changes. + +Get Galaxy +=========================================================== + +The code lives at `GitHub `__ and you should have `Git `__ to obtain it. + +To get a new Galaxy repository run: + .. code-block:: shell + + $ git clone -b release_25.1 https://github.com/galaxyproject/galaxy.git + +To update an existing Galaxy repository run: + .. code-block:: shell + + $ git fetch origin && git checkout release_25.1 && git pull --ff-only origin release_25.1 + +See the `community hub `__ for additional details on source code locations. + + +Admin Notes +=========================================================== + +**Database Migrations will require stopping the Galaxy instance** + This release includes database migrations that require the Galaxy instance to be stopped + before applying the update. Please plan accordingly to minimize downtime for users. + +Configuration Changes +=========================================================== + + +Added +----- + +The following configuration options are new + +config/galaxy.yml.sample:galaxy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- mapping.tool_shed.mapping.allow_local_account_creation + + +Removed +------- + +The following configuration options have been completely removed + +config/galaxy.yml.sample:galaxy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- mapping.tool_shed.mapping.allow_user_creation + + +Deprecation Notices +=========================================================== + +**Deprecation of the Trackster Visualization in Galaxy release 26.0** + The legacy Trackster genome browser visualization has been deprecated and + `will be completely removed in Galaxy 26.0 `__. This includes + the removal of special handling for Trackster across tool execution, toolbox filtering, visualization + framework features, controller endpoints, client code, and other conditional logic. With the removal of + Trackster, the era of Mako-based visualizations served from controller endpoints ends. + Galaxy now provides the modern IGV (Integrative Genomics Viewer) as the primary genome browser + visualization, offering superior functionality and performance for genomic data exploration. + +**Deprecation of Mako-based visualizations in Galaxy release 26.0** + With the removal of Trackster in 26.0, the remaining legacy Mako-based visualization framework + and controller endpoints are deprecated and + `will be completely removed in Galaxy 26.0 `__. + Any remaining Mako-based visualizations should be migrated to the modern script-based visualization + plugin framework. Users and administrators are encouraged to transition to supported visualization + plugins to ensure continued functionality and access to new features. + +**Deprecation of the Galaxy Reports application in Galaxy release 26.0** + The standalone Galaxy Reports application, which provides administrative usage statistics and + analytics for Galaxy instances, is deprecated and will be removed in Galaxy 26.0. + The Reports app was a separate web application that connected to the Galaxy database to display + information about jobs, workflows, users, and system usage. Administrators currently using the + Reports app should plan to migrate to alternative monitoring and analytics solutions before + Galaxy 26.0 is released next year. The following GTN tutorials provide guidance on alternative + monitoring approaches: + + * `Galaxy Monitoring with Telegraf and Grafana `__ + * `Galaxy Monitoring with gxadmin `__ + +Release Team +=========================================================== + +Release manager: `Ahmed Awan `__, `Aysam Guerler `__ + +Release testing: + +* `Tyler Collins `__ +* `Arash Kadkhodaei `__ +* `John Chilton `__ +* `Keith Suderman `__ +* `Aysam Guerler `__ +* `Alireza Heidari `__ +* `Jennifer Hillman-Jackson `__ + +Communications: + +* `Natalie Whitaker-Allen `__ +* `Scott Cain `__ + +A special thank you goes to everyone who helped test the new release after its deployment on usegalaxy.org. + +---- + +.. include:: _thanks.rst diff --git a/doc/source/releases/25.1_announce_user.rst b/doc/source/releases/25.1_announce_user.rst new file mode 100644 index 00000000000..fec3e5865e0 --- /dev/null +++ b/doc/source/releases/25.1_announce_user.rst @@ -0,0 +1,456 @@ + +=========================================================== +25.1 Galaxy Release (October 2025) +=========================================================== + +.. include:: _header.rst + +Please see the full `release notes `__ for more details. + +Highlights +=========================================================== + +Discover some of the exciting new features, enhancements, and improvements in Galaxy 25.1. + +A Fresh, Modern History List Interface +-------------------------------------- + +Galaxy 25.1 introduces a redesigned history list that makes managing your histories more intuitive and +visually appealing. + +- **Card-based design**. Histories are now displayed as clean, modern cards that provide better visual + organization in a grid vs list view, as you prefer. +- **Enhanced information display**. Each history card shows key details at a glance, including + creation date, size, update time, and status indicators in a more structured layout. +- **Improved responsive design**. The new interface adapts better to different screen sizes and + provides a more consistent experience across devices. +- **Advanced keyboard navigation and selection**. Navigate through histories using arrow keys, + select multiple histories with Shift+click for range selection, and use keyboard shortcuts + for efficient bulk operations — available across multiple Galaxy list interfaces. + +This update modernizes one of Galaxy's core interfaces, providing a cleaner and more user-friendly +way to navigate your analysis history collection while adding powerful selection capabilities. +[`#20744 `__] + +.. raw:: html + + + +Smart Sample Sheets for Complex Workflow Inputs +----------------------------------------------- + +Galaxy now supports **Sample Sheets** to simplify complex workflow inputs and data organization. This +feature allows you to import data from external spreadsheets (CSV, TSV, or Excel) and use them to +configure workflow parameters and organize datasets. + +- **Spreadsheet-style data entry**. Define your experimental data using familiar spreadsheet interfaces + with customizable columns for sample names, treatments, conditions, and file locations — whether + datasets or remote URLs. +- **Workflow author-defined templates**. Workflow creators can pre-configure sample sheet templates + with specific columns, validation rules, and helpful descriptions, guiding users through proper data + organization. +- **Built into the Apply Rules tool**. Advanced users can leverage sample sheet data within the Apply + Rules tool for sophisticated data manipulation and collection operations. +- **Integration within the Workflow Editor**. Configure workflows to accept sample sheet inputs + directly from the Workflow Editor. + +Sample Sheets are particularly useful for workflows that require multiple related inputs or when you +need to process batches of samples with associated metadata in a structured format. +[`#19305 `__] + +.. raw:: html + + + +An Enhanced Tool Discovery View +------------------------------- + +Galaxy 25.1 introduces a redesigned **Tool Discovery View** that replaces the previous advanced tool +search with a more powerful and intuitive tool exploration interface. + +- **Dedicated Discovery Interface**. Advanced tool search has moved from the tool panel to a rich, + dedicated center-panel view optimized for finding the right tools for your analysis. +- **Card-based Tool Display**. Tools are presented as visually appealing cards with key information, + including EDAM ontology tags, descriptions, and links. +- **Tool Favorites and Sharing**. Easily add tools to your favorites for quick access, and share useful + tools with colleagues via the copyable link feature. +- **EDAM Ontology Integration**. Browse and filter tools by EDAM (EMBRACE Data and Methods) ontologies + to discover tools based on scientific domains and data types. + +This new interface makes tool discovery more efficient and helps users find the most appropriate tools +for their specific analysis needs. +[`#20747 `__] + +.. raw:: html + + + +New Galaxy Charts Visualization Framework +----------------------------------------- + +This release also introduces the **Galaxy Charts** framework, bringing enhanced visualizations and a +more powerful visualization experience to Galaxy users. + +- **Improved Visualization Performance**. Enjoy faster loading times, smoother interactions, and more + responsive visualizations built with modern web technologies for a better user experience. +- **Enhanced IGV.js Genome Browser**. Galaxy now includes a complete IGV.js (Integrative Genome Browser) + integration for comprehensive genomic data visualization with multi-track support for BAM, VCF, + BigWig, BED, GFF, GTF, and CRAM files. +- **Automatic Data Integration**. Visualizations now automatically connect to your Galaxy datasets and + match reference genomes, eliminating manual configuration steps and making it easier to explore your + data. +- **Expanded Visualization Library**. The new framework enables rapid deployment of cutting-edge + visualizations, meaning Galaxy users will see more powerful visualization options in future releases. + +*See the Admin Notes and Deprecation Notices sections* :doc:`here <25.1_announce>` *for more details on +the visualization framework and the removal of Trackster.* + +**Featured Implementation: IGV.js Genome Browser** + +Galaxy 25.1 ships with a complete IGV.js (Integrative Genome Browser) implementation built on the +Galaxy Charts framework, showcasing the platform's capabilities: + +- **Multi-track Genomic Visualization**. View multiple tracks in various formats including BAM, VCF, + BigWig, BED, GFF, GTF, and CRAM files with full support for indexed data and byte-range requests. +- **Automatic Genome Matching**. Automatically matches dataset database keys against available genomes, + supporting both IGV-provided reference genomes and Galaxy-served genomes in 2bit and FASTA formats. +- **Interactive Exploration**. Full IGV.js functionality including zooming, panning, dynamic track + configuration, and multi-sample comparisons for comprehensive genomic data analysis. + +The Galaxy Charts framework represents a major advancement in Galaxy's visualization capabilities, +making it easier than ever to create sophisticated, interactive visualizations while maintaining full +integration with Galaxy's data management and analysis ecosystem. Here's a look at the IGV.js addition: +[`#20943 `__] + +.. raw:: html + + + +A Place to Find your Recent Downloads in Galaxy +----------------------------------------------- + +Galaxy 25.1 introduces a dedicated "Recent Exports & Downloads" page that lists your recent +exports/downloads. + +- **Re-download available items**. Easily access and download files still available in the short-term + storage cache. +- **Navigate to source objects**. Jump directly to the details page of the originating item (e.g. + workflow invocation, history, etc.). + +This improves the discoverability and accessibility of recent downloads without needing to track links +manually. +[`#20585 `__] + +.. raw:: html + + + +Visual Indicators for Short-Term Storage Expiration +--------------------------------------------------- + +Galaxy 25.1 introduces visual expiration indicators that help users identify when datasets and +collections are stored in temporary or short-term storage that will be automatically cleaned up. + +- **Expiration warnings in history**. Datasets and collections now display countdown timers showing + when they will expire, helping users plan data management and downloads before automatic cleanup. +- **Collection-aware indicators**. Smart detection across dataset collections shows the earliest + expiration time when collections contain datasets from mixed storage types. +- **Admin-configurable expiration periods**. Administrators can set `object_expires_after_days` in + object store configurations to match their actual cleanup schedules and inform users appropriately. + +*Note that Galaxy instance administrators must set `object_expires_after_days` in their object store +configurations for these indicators to function correctly.* + +This feature is particularly valuable for instances using scratch storage or temporary +high-performance storage tiers, as it gives users clear visibility into data lifecycle and helps +prevent unexpected data loss due to automatic cleanup processes. +[`#20332 `__] + +.. raw:: html + + + +Galaxy Tools Now Handle Secrets and Authentication +-------------------------------------------------- + +Galaxy 25.1 implements a comprehensive tool credentials system, allowing tools to securely access +external services by defining authentication credentials (variables and secrets) in tool requirements +while keeping sensitive information separate from tool definitions. + +- **Credential Definition in Tools**. Tools can define credential requirements using `` + elements within `` with support for both variables (non-sensitive config like URLs, + usernames) and secrets (sensitive data like passwords, API keys). +- **Secure Storage & Management**. Integration with Galaxy's vault system provides secure secret + storage with user-specific credential isolation by tool and service, plus credential groups for + organizing multiple credential sets. +- **REST API**. Complete CRUD operations enable credential management with user credential listing, + group-based credential organization, and secure deletion with vault cleanup. +- **Frontend Integration**. Unified credential management UI appears for every tool with workflow-level + credential status display, multi-tool credential coordination, and real-time credential validation + and feedback. +- **Tool Execution Integration**. Automatic credential injection as environment variables with job + context tracking for credential usage, supporting tool proxies and various tool types while + maintaining backward compatibility. + +This system provides secure credential management for tools that need to access external APIs, +databases, or cloud services, ensuring sensitive authentication information is properly protected +while making it easy for users to configure and manage their credentials across different tools and +workflows. +[`#19084 `__] + +.. raw:: html + + + +Search for Steps, Tools and Subworkflows within the Workflow Editor +------------------------------------------------------------------- + +As with many improvements to the Workflow Editor in recent releases, Galaxy 25.1 adds the ability to +search for several properties of all elements in the workflow graph, displays the search results in a +list, and highlights them when clicked. +[`#20390 `__] + +.. raw:: html + + + +A Redesign of your User Preferences +----------------------------------- + +The User Preferences interface has been redesigned to enhance user experience through improved +navigation and a modernised interface. +[`#19658 `__] + +.. raw:: html + + + +---- + +Visualizations Updates +=========================================================== + +.. visualizations +* [igv] Allow selection of genomes from history fasta/2bit + (thanks to `@guerler `__). + `Pull Request 21269`_ +* Add Hyphy Vision Result Viewer + (thanks to `@guerler `__). + `Pull Request 21196`_ +* Enable drag-and-drop of collection elements in IGV + (thanks to `@guerler `__). + `Pull Request 21173`_ +* Update Javascript package licenses to MIT + (thanks to `@mr-c `__). + `Pull Request 20264`_ +* Install Visualization plugins as self-contained packages + (thanks to `@guerler `__). + `Pull Request 20348`_ +* Restore Nora + (thanks to `@guerler `__). + `Pull Request 20387`_ +* Install visualizations directly to static path, avoid duplication + (thanks to `@guerler `__). + `Pull Request 20372`_ +* Add rerun.io web viewer + (thanks to `@guerler `__). + `Pull Request 20202`_ +* Update tiffviewer visualization to version 0.0.3 + (thanks to `@davelopez `__). + `Pull Request 20658`_ +* Consolidate visualization mako, avoid user agent styles + (thanks to `@guerler `__). + `Pull Request 20760`_ +* Enable data label column selection in basic Plotly plots and add heatmap + (thanks to `@guerler `__). + `Pull Request 20813`_ +* Update JupyterLite version to 0.6.4 + (thanks to `@guerler `__). + `Pull Request 20877`_ +* Add Multiple Sequence Alignment Viewer 2.0 + (thanks to `@guerler `__). + `Pull Request 20907`_ +* Make jupyterlite default visualization for ipynb + (thanks to `@guerler `__). + `Pull Request 20931`_ +* Fix JupyterLite History Identifier Retrieval Without Dataset + (thanks to `@guerler `__). + `Pull Request 20971`_ +* Save IGV locus and improve error handling + (thanks to `@guerler `__). + `Pull Request 20996`_ +* Bump vitessce to 3.8.2 + (thanks to `@guerler `__). + `Pull Request 21003`_ +* Fix and migrate HIV-TRACE to Charts Visualization Framework + (thanks to `@guerler `__). + `Pull Request 21006`_ +* Add README and update the minimal visualization example + (thanks to `@guerler `__). + `Pull Request 21007`_ +* Allow creation of visualizations without dataset + (thanks to `@guerler `__). + `Pull Request 20914`_ +* Adds IGV.js Visualization + (thanks to `@guerler `__). + `Pull Request 20943`_ + +Datatypes Updates +=========================================================== + +.. datatypes +* New datatype addition: HAL + (thanks to `@SaimMomin12 `__). + `Pull Request 21379`_ +* Add PNTS datatype for 3D Tiles Point Cloud + (thanks to `@kgerb `__). + `Pull Request 21414`_ +* Backport fix anndata datatype update + (thanks to `@nilchia `__). + `Pull Request 21111`_ +* Fix MIME type for LAZ datatype + (thanks to `@kysrpex `__). + `Pull Request 21202`_ +* Add SpatialData datatype + (thanks to `@nilchia `__). + `Pull Request 21000`_ +* support ZARR v3 for Spatialdata dt + (thanks to `@nilchia `__). + `Pull Request 21265`_ +* Fix forward merge + (thanks to `@mvdbeek `__). + `Pull Request 21307`_ +* Fix tool-provided metadata for CONVERTER_tar_to_directory + (thanks to `@mr-c `__). + `Pull Request 20260`_ +* Pin isa-rwval 0.10.11, drop conditional import handling + (thanks to `@natefoo `__). + `Pull Request 20527`_ +* Remove ``num_unique_values`` tiff metadata element + (thanks to `@mvdbeek `__). + `Pull Request 20464`_ +* Add LocusZoom Visualization + (thanks to `@elmedjadjirayane `__). + `Pull Request 20354`_ +* Add mzMLb and MBI datatypes + (thanks to `@chambm `__). + `Pull Request 20632`_ +* Pairtool updated format and sniffers + (thanks to `@Smeds `__). + `Pull Request 20634`_ +* Add `num_unique_values` tiff metadata element, fixed + (thanks to `@kostrykin `__). + `Pull Request 20669`_ +* add fasta.bz2 as auto_compressed type + (thanks to `@bernt-matthias `__). + `Pull Request 20496`_ +* Add support for M4A audio files + (thanks to `@arash77 `__). + `Pull Request 20667`_ +* Add datatype for LexicMap index + (thanks to `@Smeds `__). + `Pull Request 20586`_ +* Bump requirement of directory converters + (thanks to `@bernt-matthias `__). + `Pull Request 20834`_ +* Undo accidental push + (thanks to `@bernt-matthias `__). + `Pull Request 20854`_ +* Fix Ipynb datatype sniffer and add unit tests + (thanks to `@ksuderman `__). + `Pull Request 20811`_ +* Add axt and maf to auto_compressed_types + (thanks to `@richard-burhans `__). + `Pull Request 20875`_ +* Add HiC datatype + (thanks to `@abretaud `__). + `Pull Request 20874`_ +* Fix DOCX detection and add PPTX support + (thanks to `@arash77 `__). + `Pull Request 20827`_ +* Fix anndata metadata + (thanks to `@nilchia `__). + `Pull Request 20778`_ +* Use a streaming based parser (ijson) when setting metadata for biom1 files + (thanks to `@ksuderman `__). + `Pull Request 20851`_ +* Add support for picking ``from_work_dir`` directory + (thanks to `@mvdbeek `__). + `Pull Request 20916`_ +* Skip sam metadata if we have too many references + (thanks to `@mvdbeek `__). + `Pull Request 20820`_ +* Extract: do not use common prefix dir + (thanks to `@bernt-matthias `__). + `Pull Request 20929`_ +* Add Auspice JSON datatype + (thanks to `@pvanheus `__). + `Pull Request 20466`_ +* Clean up code from pyupgrade + (thanks to `@nsoranzo `__). + `Pull Request 20642`_ +* Add safetensors datatype + (thanks to `@nilchia `__). + `Pull Request 20754`_ +* Add freq.json datatype subclass + (thanks to `@guerler `__). + `Pull Request 21012`_ +* Merge 25.0 into dev + (thanks to `@guerler `__). + `Pull Request 21018`_ +* New datatype addition: beast.trees datatype + (thanks to `@SaimMomin12 `__). + `Pull Request 21055`_ +* New datatype addition: gam for vg toolkit + (thanks to `@SaimMomin12 `__). + `Pull Request 21096`_ +* Prefix download link + (thanks to `@martenson `__). + `Pull Request 21112`_ +* Add LAS and LAZ file format + (thanks to `@bgruening `__). + `Pull Request 21049`_ +* Add display_in_upload to LAZ and LAS + (thanks to `@bgruening `__). + `Pull Request 21060`_ +* Type annotation fixes for mypy 1.16.0 + (thanks to `@nsoranzo `__). + `Pull Request 20424`_ + +Builtin Tool Updates +=========================================================== + +.. tools +* Set minimum profile version for expression tools + (thanks to `@mvdbeek `__). + `Pull Request 21151`_ +* Drop ucsc test server from sample build sites + (thanks to `@mvdbeek `__). + `Pull Request 20723`_ +* Drop Python helper script from sort1 tool + (thanks to `@nsoranzo `__). + `Pull Request 20876`_ +* Update requirements of sort1 tool + (thanks to `@nsoranzo `__). + `Pull Request 20892`_ +* Update maf tools with profiles, required_files, and requirements + (thanks to `@natefoo `__). + `Pull Request 20865`_ +* Modernize tools in the filters/ dir + (thanks to `@natefoo `__). + `Pull Request 20840`_ +* Add special case for BAM files in RStudio + (thanks to `@bgruening `__). + `Pull Request 20692`_ +* Fix comp1 / test_subworkflow_map_over_data_column + (thanks to `@mvdbeek `__). + `Pull Request 20992`_ + +Please see the full `release notes `__ for more details. +The admin-facing release notes are available :doc:`here <25.1_announce>`. + +.. include:: 25.1_prs.rst + +---- + +.. include:: _thanks.rst diff --git a/doc/source/releases/25.1_prs.rst b/doc/source/releases/25.1_prs.rst new file mode 100644 index 00000000000..208f4e0baaa --- /dev/null +++ b/doc/source/releases/25.1_prs.rst @@ -0,0 +1,445 @@ + +.. github_links +.. _Pull Request 21442: https://github.com/galaxyproject/galaxy/pull/21442 +.. _Pull Request 21435: https://github.com/galaxyproject/galaxy/pull/21435 +.. _Pull Request 21342: https://github.com/galaxyproject/galaxy/pull/21342 +.. _Pull Request 21269: https://github.com/galaxyproject/galaxy/pull/21269 +.. _Pull Request 21346: https://github.com/galaxyproject/galaxy/pull/21346 +.. _Pull Request 21349: https://github.com/galaxyproject/galaxy/pull/21349 +.. _Pull Request 21350: https://github.com/galaxyproject/galaxy/pull/21350 +.. _Pull Request 21379: https://github.com/galaxyproject/galaxy/pull/21379 +.. _Pull Request 21276: https://github.com/galaxyproject/galaxy/pull/21276 +.. _Pull Request 21360: https://github.com/galaxyproject/galaxy/pull/21360 +.. _Pull Request 21377: https://github.com/galaxyproject/galaxy/pull/21377 +.. _Pull Request 21414: https://github.com/galaxyproject/galaxy/pull/21414 +.. _Pull Request 21419: https://github.com/galaxyproject/galaxy/pull/21419 +.. _Pull Request 21426: https://github.com/galaxyproject/galaxy/pull/21426 +.. _Pull Request 21428: https://github.com/galaxyproject/galaxy/pull/21428 +.. _Pull Request 21191: https://github.com/galaxyproject/galaxy/pull/21191 +.. _Pull Request 21193: https://github.com/galaxyproject/galaxy/pull/21193 +.. _Pull Request 21196: https://github.com/galaxyproject/galaxy/pull/21196 +.. _Pull Request 21195: https://github.com/galaxyproject/galaxy/pull/21195 +.. _Pull Request 21111: https://github.com/galaxyproject/galaxy/pull/21111 +.. _Pull Request 21202: https://github.com/galaxyproject/galaxy/pull/21202 +.. _Pull Request 21197: https://github.com/galaxyproject/galaxy/pull/21197 +.. _Pull Request 21192: https://github.com/galaxyproject/galaxy/pull/21192 +.. _Pull Request 21214: https://github.com/galaxyproject/galaxy/pull/21214 +.. _Pull Request 21203: https://github.com/galaxyproject/galaxy/pull/21203 +.. _Pull Request 21210: https://github.com/galaxyproject/galaxy/pull/21210 +.. _Pull Request 21212: https://github.com/galaxyproject/galaxy/pull/21212 +.. _Pull Request 21023: https://github.com/galaxyproject/galaxy/pull/21023 +.. _Pull Request 21223: https://github.com/galaxyproject/galaxy/pull/21223 +.. _Pull Request 21233: https://github.com/galaxyproject/galaxy/pull/21233 +.. _Pull Request 21213: https://github.com/galaxyproject/galaxy/pull/21213 +.. _Pull Request 21232: https://github.com/galaxyproject/galaxy/pull/21232 +.. _Pull Request 21000: https://github.com/galaxyproject/galaxy/pull/21000 +.. _Pull Request 21246: https://github.com/galaxyproject/galaxy/pull/21246 +.. _Pull Request 21240: https://github.com/galaxyproject/galaxy/pull/21240 +.. _Pull Request 21249: https://github.com/galaxyproject/galaxy/pull/21249 +.. _Pull Request 21200: https://github.com/galaxyproject/galaxy/pull/21200 +.. _Pull Request 21274: https://github.com/galaxyproject/galaxy/pull/21274 +.. _Pull Request 21265: https://github.com/galaxyproject/galaxy/pull/21265 +.. _Pull Request 21282: https://github.com/galaxyproject/galaxy/pull/21282 +.. _Pull Request 21284: https://github.com/galaxyproject/galaxy/pull/21284 +.. _Pull Request 21280: https://github.com/galaxyproject/galaxy/pull/21280 +.. _Pull Request 21293: https://github.com/galaxyproject/galaxy/pull/21293 +.. _Pull Request 21013: https://github.com/galaxyproject/galaxy/pull/21013 +.. _Pull Request 21290: https://github.com/galaxyproject/galaxy/pull/21290 +.. _Pull Request 21294: https://github.com/galaxyproject/galaxy/pull/21294 +.. _Pull Request 21314: https://github.com/galaxyproject/galaxy/pull/21314 +.. _Pull Request 21307: https://github.com/galaxyproject/galaxy/pull/21307 +.. _Pull Request 21261: https://github.com/galaxyproject/galaxy/pull/21261 +.. _Pull Request 21273: https://github.com/galaxyproject/galaxy/pull/21273 +.. _Pull Request 21286: https://github.com/galaxyproject/galaxy/pull/21286 +.. _Pull Request 21185: https://github.com/galaxyproject/galaxy/pull/21185 +.. _Pull Request 21184: https://github.com/galaxyproject/galaxy/pull/21184 +.. _Pull Request 21175: https://github.com/galaxyproject/galaxy/pull/21175 +.. _Pull Request 21181: https://github.com/galaxyproject/galaxy/pull/21181 +.. _Pull Request 21159: https://github.com/galaxyproject/galaxy/pull/21159 +.. _Pull Request 21166: https://github.com/galaxyproject/galaxy/pull/21166 +.. _Pull Request 21165: https://github.com/galaxyproject/galaxy/pull/21165 +.. _Pull Request 21164: https://github.com/galaxyproject/galaxy/pull/21164 +.. _Pull Request 21160: https://github.com/galaxyproject/galaxy/pull/21160 +.. _Pull Request 21169: https://github.com/galaxyproject/galaxy/pull/21169 +.. _Pull Request 21173: https://github.com/galaxyproject/galaxy/pull/21173 +.. _Pull Request 21085: https://github.com/galaxyproject/galaxy/pull/21085 +.. _Pull Request 21151: https://github.com/galaxyproject/galaxy/pull/21151 +.. _Pull Request 21156: https://github.com/galaxyproject/galaxy/pull/21156 +.. _Pull Request 20260: https://github.com/galaxyproject/galaxy/pull/20260 +.. _Pull Request 20268: https://github.com/galaxyproject/galaxy/pull/20268 +.. _Pull Request 20264: https://github.com/galaxyproject/galaxy/pull/20264 +.. _Pull Request 20278: https://github.com/galaxyproject/galaxy/pull/20278 +.. _Pull Request 20262: https://github.com/galaxyproject/galaxy/pull/20262 +.. _Pull Request 20284: https://github.com/galaxyproject/galaxy/pull/20284 +.. _Pull Request 20292: https://github.com/galaxyproject/galaxy/pull/20292 +.. _Pull Request 20291: https://github.com/galaxyproject/galaxy/pull/20291 +.. _Pull Request 20139: https://github.com/galaxyproject/galaxy/pull/20139 +.. _Pull Request 20347: https://github.com/galaxyproject/galaxy/pull/20347 +.. _Pull Request 20348: https://github.com/galaxyproject/galaxy/pull/20348 +.. _Pull Request 20355: https://github.com/galaxyproject/galaxy/pull/20355 +.. _Pull Request 20364: https://github.com/galaxyproject/galaxy/pull/20364 +.. _Pull Request 20408: https://github.com/galaxyproject/galaxy/pull/20408 +.. _Pull Request 20303: https://github.com/galaxyproject/galaxy/pull/20303 +.. _Pull Request 20422: https://github.com/galaxyproject/galaxy/pull/20422 +.. _Pull Request 20356: https://github.com/galaxyproject/galaxy/pull/20356 +.. _Pull Request 20357: https://github.com/galaxyproject/galaxy/pull/20357 +.. _Pull Request 20445: https://github.com/galaxyproject/galaxy/pull/20445 +.. _Pull Request 20446: https://github.com/galaxyproject/galaxy/pull/20446 +.. _Pull Request 20435: https://github.com/galaxyproject/galaxy/pull/20435 +.. _Pull Request 20387: https://github.com/galaxyproject/galaxy/pull/20387 +.. _Pull Request 20418: https://github.com/galaxyproject/galaxy/pull/20418 +.. _Pull Request 20383: https://github.com/galaxyproject/galaxy/pull/20383 +.. _Pull Request 20470: https://github.com/galaxyproject/galaxy/pull/20470 +.. _Pull Request 20372: https://github.com/galaxyproject/galaxy/pull/20372 +.. _Pull Request 20500: https://github.com/galaxyproject/galaxy/pull/20500 +.. _Pull Request 20501: https://github.com/galaxyproject/galaxy/pull/20501 +.. _Pull Request 20507: https://github.com/galaxyproject/galaxy/pull/20507 +.. _Pull Request 20509: https://github.com/galaxyproject/galaxy/pull/20509 +.. _Pull Request 20520: https://github.com/galaxyproject/galaxy/pull/20520 +.. _Pull Request 20516: https://github.com/galaxyproject/galaxy/pull/20516 +.. _Pull Request 20526: https://github.com/galaxyproject/galaxy/pull/20526 +.. _Pull Request 20527: https://github.com/galaxyproject/galaxy/pull/20527 +.. _Pull Request 20531: https://github.com/galaxyproject/galaxy/pull/20531 +.. _Pull Request 20529: https://github.com/galaxyproject/galaxy/pull/20529 +.. _Pull Request 20438: https://github.com/galaxyproject/galaxy/pull/20438 +.. _Pull Request 20511: https://github.com/galaxyproject/galaxy/pull/20511 +.. _Pull Request 20528: https://github.com/galaxyproject/galaxy/pull/20528 +.. _Pull Request 20518: https://github.com/galaxyproject/galaxy/pull/20518 +.. _Pull Request 20533: https://github.com/galaxyproject/galaxy/pull/20533 +.. _Pull Request 20534: https://github.com/galaxyproject/galaxy/pull/20534 +.. _Pull Request 20464: https://github.com/galaxyproject/galaxy/pull/20464 +.. _Pull Request 20536: https://github.com/galaxyproject/galaxy/pull/20536 +.. _Pull Request 20545: https://github.com/galaxyproject/galaxy/pull/20545 +.. _Pull Request 20354: https://github.com/galaxyproject/galaxy/pull/20354 +.. _Pull Request 20552: https://github.com/galaxyproject/galaxy/pull/20552 +.. _Pull Request 20525: https://github.com/galaxyproject/galaxy/pull/20525 +.. _Pull Request 20546: https://github.com/galaxyproject/galaxy/pull/20546 +.. _Pull Request 20537: https://github.com/galaxyproject/galaxy/pull/20537 +.. _Pull Request 20497: https://github.com/galaxyproject/galaxy/pull/20497 +.. _Pull Request 20577: https://github.com/galaxyproject/galaxy/pull/20577 +.. _Pull Request 20571: https://github.com/galaxyproject/galaxy/pull/20571 +.. _Pull Request 20584: https://github.com/galaxyproject/galaxy/pull/20584 +.. _Pull Request 20502: https://github.com/galaxyproject/galaxy/pull/20502 +.. _Pull Request 20591: https://github.com/galaxyproject/galaxy/pull/20591 +.. _Pull Request 20202: https://github.com/galaxyproject/galaxy/pull/20202 +.. _Pull Request 20593: https://github.com/galaxyproject/galaxy/pull/20593 +.. _Pull Request 20596: https://github.com/galaxyproject/galaxy/pull/20596 +.. _Pull Request 20601: https://github.com/galaxyproject/galaxy/pull/20601 +.. _Pull Request 20597: https://github.com/galaxyproject/galaxy/pull/20597 +.. _Pull Request 20594: https://github.com/galaxyproject/galaxy/pull/20594 +.. _Pull Request 20603: https://github.com/galaxyproject/galaxy/pull/20603 +.. _Pull Request 20460: https://github.com/galaxyproject/galaxy/pull/20460 +.. _Pull Request 20618: https://github.com/galaxyproject/galaxy/pull/20618 +.. _Pull Request 20521: https://github.com/galaxyproject/galaxy/pull/20521 +.. _Pull Request 20625: https://github.com/galaxyproject/galaxy/pull/20625 +.. _Pull Request 20615: https://github.com/galaxyproject/galaxy/pull/20615 +.. _Pull Request 20636: https://github.com/galaxyproject/galaxy/pull/20636 +.. _Pull Request 20632: https://github.com/galaxyproject/galaxy/pull/20632 +.. _Pull Request 20620: https://github.com/galaxyproject/galaxy/pull/20620 +.. _Pull Request 20639: https://github.com/galaxyproject/galaxy/pull/20639 +.. _Pull Request 20551: https://github.com/galaxyproject/galaxy/pull/20551 +.. _Pull Request 19697: https://github.com/galaxyproject/galaxy/pull/19697 +.. _Pull Request 20646: https://github.com/galaxyproject/galaxy/pull/20646 +.. _Pull Request 20638: https://github.com/galaxyproject/galaxy/pull/20638 +.. _Pull Request 20627: https://github.com/galaxyproject/galaxy/pull/20627 +.. _Pull Request 20651: https://github.com/galaxyproject/galaxy/pull/20651 +.. _Pull Request 20654: https://github.com/galaxyproject/galaxy/pull/20654 +.. _Pull Request 20649: https://github.com/galaxyproject/galaxy/pull/20649 +.. _Pull Request 20658: https://github.com/galaxyproject/galaxy/pull/20658 +.. _Pull Request 20652: https://github.com/galaxyproject/galaxy/pull/20652 +.. _Pull Request 20647: https://github.com/galaxyproject/galaxy/pull/20647 +.. _Pull Request 20653: https://github.com/galaxyproject/galaxy/pull/20653 +.. _Pull Request 20663: https://github.com/galaxyproject/galaxy/pull/20663 +.. _Pull Request 20665: https://github.com/galaxyproject/galaxy/pull/20665 +.. _Pull Request 20630: https://github.com/galaxyproject/galaxy/pull/20630 +.. _Pull Request 20664: https://github.com/galaxyproject/galaxy/pull/20664 +.. _Pull Request 20634: https://github.com/galaxyproject/galaxy/pull/20634 +.. _Pull Request 20669: https://github.com/galaxyproject/galaxy/pull/20669 +.. _Pull Request 20547: https://github.com/galaxyproject/galaxy/pull/20547 +.. _Pull Request 20672: https://github.com/galaxyproject/galaxy/pull/20672 +.. _Pull Request 20674: https://github.com/galaxyproject/galaxy/pull/20674 +.. _Pull Request 20496: https://github.com/galaxyproject/galaxy/pull/20496 +.. _Pull Request 20667: https://github.com/galaxyproject/galaxy/pull/20667 +.. _Pull Request 20680: https://github.com/galaxyproject/galaxy/pull/20680 +.. _Pull Request 20685: https://github.com/galaxyproject/galaxy/pull/20685 +.. _Pull Request 20690: https://github.com/galaxyproject/galaxy/pull/20690 +.. _Pull Request 20693: https://github.com/galaxyproject/galaxy/pull/20693 +.. _Pull Request 20696: https://github.com/galaxyproject/galaxy/pull/20696 +.. _Pull Request 20699: https://github.com/galaxyproject/galaxy/pull/20699 +.. _Pull Request 20694: https://github.com/galaxyproject/galaxy/pull/20694 +.. _Pull Request 20702: https://github.com/galaxyproject/galaxy/pull/20702 +.. _Pull Request 20586: https://github.com/galaxyproject/galaxy/pull/20586 +.. _Pull Request 20553: https://github.com/galaxyproject/galaxy/pull/20553 +.. _Pull Request 20703: https://github.com/galaxyproject/galaxy/pull/20703 +.. _Pull Request 20510: https://github.com/galaxyproject/galaxy/pull/20510 +.. _Pull Request 20684: https://github.com/galaxyproject/galaxy/pull/20684 +.. _Pull Request 20708: https://github.com/galaxyproject/galaxy/pull/20708 +.. _Pull Request 20522: https://github.com/galaxyproject/galaxy/pull/20522 +.. _Pull Request 20705: https://github.com/galaxyproject/galaxy/pull/20705 +.. _Pull Request 20729: https://github.com/galaxyproject/galaxy/pull/20729 +.. _Pull Request 20723: https://github.com/galaxyproject/galaxy/pull/20723 +.. _Pull Request 20721: https://github.com/galaxyproject/galaxy/pull/20721 +.. _Pull Request 20714: https://github.com/galaxyproject/galaxy/pull/20714 +.. _Pull Request 20732: https://github.com/galaxyproject/galaxy/pull/20732 +.. _Pull Request 20734: https://github.com/galaxyproject/galaxy/pull/20734 +.. _Pull Request 20727: https://github.com/galaxyproject/galaxy/pull/20727 +.. _Pull Request 20739: https://github.com/galaxyproject/galaxy/pull/20739 +.. _Pull Request 20753: https://github.com/galaxyproject/galaxy/pull/20753 +.. _Pull Request 20752: https://github.com/galaxyproject/galaxy/pull/20752 +.. _Pull Request 20755: https://github.com/galaxyproject/galaxy/pull/20755 +.. _Pull Request 20710: https://github.com/galaxyproject/galaxy/pull/20710 +.. _Pull Request 20760: https://github.com/galaxyproject/galaxy/pull/20760 +.. _Pull Request 20701: https://github.com/galaxyproject/galaxy/pull/20701 +.. _Pull Request 20736: https://github.com/galaxyproject/galaxy/pull/20736 +.. _Pull Request 20731: https://github.com/galaxyproject/galaxy/pull/20731 +.. _Pull Request 20762: https://github.com/galaxyproject/galaxy/pull/20762 +.. _Pull Request 20772: https://github.com/galaxyproject/galaxy/pull/20772 +.. _Pull Request 20769: https://github.com/galaxyproject/galaxy/pull/20769 +.. _Pull Request 20770: https://github.com/galaxyproject/galaxy/pull/20770 +.. _Pull Request 20775: https://github.com/galaxyproject/galaxy/pull/20775 +.. _Pull Request 20768: https://github.com/galaxyproject/galaxy/pull/20768 +.. _Pull Request 20779: https://github.com/galaxyproject/galaxy/pull/20779 +.. _Pull Request 20777: https://github.com/galaxyproject/galaxy/pull/20777 +.. _Pull Request 20749: https://github.com/galaxyproject/galaxy/pull/20749 +.. _Pull Request 20384: https://github.com/galaxyproject/galaxy/pull/20384 +.. _Pull Request 20402: https://github.com/galaxyproject/galaxy/pull/20402 +.. _Pull Request 20782: https://github.com/galaxyproject/galaxy/pull/20782 +.. _Pull Request 20784: https://github.com/galaxyproject/galaxy/pull/20784 +.. _Pull Request 20728: https://github.com/galaxyproject/galaxy/pull/20728 +.. _Pull Request 20698: https://github.com/galaxyproject/galaxy/pull/20698 +.. _Pull Request 20783: https://github.com/galaxyproject/galaxy/pull/20783 +.. _Pull Request 20795: https://github.com/galaxyproject/galaxy/pull/20795 +.. _Pull Request 20786: https://github.com/galaxyproject/galaxy/pull/20786 +.. _Pull Request 20806: https://github.com/galaxyproject/galaxy/pull/20806 +.. _Pull Request 20812: https://github.com/galaxyproject/galaxy/pull/20812 +.. _Pull Request 20807: https://github.com/galaxyproject/galaxy/pull/20807 +.. _Pull Request 20813: https://github.com/galaxyproject/galaxy/pull/20813 +.. _Pull Request 20814: https://github.com/galaxyproject/galaxy/pull/20814 +.. _Pull Request 20799: https://github.com/galaxyproject/galaxy/pull/20799 +.. _Pull Request 20794: https://github.com/galaxyproject/galaxy/pull/20794 +.. _Pull Request 20800: https://github.com/galaxyproject/galaxy/pull/20800 +.. _Pull Request 20774: https://github.com/galaxyproject/galaxy/pull/20774 +.. _Pull Request 20822: https://github.com/galaxyproject/galaxy/pull/20822 +.. _Pull Request 20771: https://github.com/galaxyproject/galaxy/pull/20771 +.. _Pull Request 20823: https://github.com/galaxyproject/galaxy/pull/20823 +.. _Pull Request 20824: https://github.com/galaxyproject/galaxy/pull/20824 +.. _Pull Request 20829: https://github.com/galaxyproject/galaxy/pull/20829 +.. _Pull Request 20834: https://github.com/galaxyproject/galaxy/pull/20834 +.. _Pull Request 20797: https://github.com/galaxyproject/galaxy/pull/20797 +.. _Pull Request 20836: https://github.com/galaxyproject/galaxy/pull/20836 +.. _Pull Request 20837: https://github.com/galaxyproject/galaxy/pull/20837 +.. _Pull Request 20841: https://github.com/galaxyproject/galaxy/pull/20841 +.. _Pull Request 20761: https://github.com/galaxyproject/galaxy/pull/20761 +.. _Pull Request 20854: https://github.com/galaxyproject/galaxy/pull/20854 +.. _Pull Request 20866: https://github.com/galaxyproject/galaxy/pull/20866 +.. _Pull Request 20811: https://github.com/galaxyproject/galaxy/pull/20811 +.. _Pull Request 20869: https://github.com/galaxyproject/galaxy/pull/20869 +.. _Pull Request 20863: https://github.com/galaxyproject/galaxy/pull/20863 +.. _Pull Request 20871: https://github.com/galaxyproject/galaxy/pull/20871 +.. _Pull Request 20873: https://github.com/galaxyproject/galaxy/pull/20873 +.. _Pull Request 20853: https://github.com/galaxyproject/galaxy/pull/20853 +.. _Pull Request 20859: https://github.com/galaxyproject/galaxy/pull/20859 +.. _Pull Request 20872: https://github.com/galaxyproject/galaxy/pull/20872 +.. _Pull Request 20870: https://github.com/galaxyproject/galaxy/pull/20870 +.. _Pull Request 20877: https://github.com/galaxyproject/galaxy/pull/20877 +.. _Pull Request 20875: https://github.com/galaxyproject/galaxy/pull/20875 +.. _Pull Request 20874: https://github.com/galaxyproject/galaxy/pull/20874 +.. _Pull Request 20884: https://github.com/galaxyproject/galaxy/pull/20884 +.. _Pull Request 20889: https://github.com/galaxyproject/galaxy/pull/20889 +.. _Pull Request 20888: https://github.com/galaxyproject/galaxy/pull/20888 +.. _Pull Request 20886: https://github.com/galaxyproject/galaxy/pull/20886 +.. _Pull Request 20891: https://github.com/galaxyproject/galaxy/pull/20891 +.. _Pull Request 20876: https://github.com/galaxyproject/galaxy/pull/20876 +.. _Pull Request 20892: https://github.com/galaxyproject/galaxy/pull/20892 +.. _Pull Request 20887: https://github.com/galaxyproject/galaxy/pull/20887 +.. _Pull Request 20890: https://github.com/galaxyproject/galaxy/pull/20890 +.. _Pull Request 20880: https://github.com/galaxyproject/galaxy/pull/20880 +.. _Pull Request 20893: https://github.com/galaxyproject/galaxy/pull/20893 +.. _Pull Request 20895: https://github.com/galaxyproject/galaxy/pull/20895 +.. _Pull Request 20827: https://github.com/galaxyproject/galaxy/pull/20827 +.. _Pull Request 20905: https://github.com/galaxyproject/galaxy/pull/20905 +.. _Pull Request 20862: https://github.com/galaxyproject/galaxy/pull/20862 +.. _Pull Request 20908: https://github.com/galaxyproject/galaxy/pull/20908 +.. _Pull Request 20907: https://github.com/galaxyproject/galaxy/pull/20907 +.. _Pull Request 20778: https://github.com/galaxyproject/galaxy/pull/20778 +.. _Pull Request 20332: https://github.com/galaxyproject/galaxy/pull/20332 +.. _Pull Request 20909: https://github.com/galaxyproject/galaxy/pull/20909 +.. _Pull Request 20650: https://github.com/galaxyproject/galaxy/pull/20650 +.. _Pull Request 20915: https://github.com/galaxyproject/galaxy/pull/20915 +.. _Pull Request 20920: https://github.com/galaxyproject/galaxy/pull/20920 +.. _Pull Request 20918: https://github.com/galaxyproject/galaxy/pull/20918 +.. _Pull Request 20919: https://github.com/galaxyproject/galaxy/pull/20919 +.. _Pull Request 20922: https://github.com/galaxyproject/galaxy/pull/20922 +.. _Pull Request 20921: https://github.com/galaxyproject/galaxy/pull/20921 +.. _Pull Request 20917: https://github.com/galaxyproject/galaxy/pull/20917 +.. _Pull Request 20925: https://github.com/galaxyproject/galaxy/pull/20925 +.. _Pull Request 20930: https://github.com/galaxyproject/galaxy/pull/20930 +.. _Pull Request 20928: https://github.com/galaxyproject/galaxy/pull/20928 +.. _Pull Request 20932: https://github.com/galaxyproject/galaxy/pull/20932 +.. _Pull Request 20941: https://github.com/galaxyproject/galaxy/pull/20941 +.. _Pull Request 20939: https://github.com/galaxyproject/galaxy/pull/20939 +.. _Pull Request 20938: https://github.com/galaxyproject/galaxy/pull/20938 +.. _Pull Request 20937: https://github.com/galaxyproject/galaxy/pull/20937 +.. _Pull Request 20942: https://github.com/galaxyproject/galaxy/pull/20942 +.. _Pull Request 19086: https://github.com/galaxyproject/galaxy/pull/19086 +.. _Pull Request 20838: https://github.com/galaxyproject/galaxy/pull/20838 +.. _Pull Request 20361: https://github.com/galaxyproject/galaxy/pull/20361 +.. _Pull Request 20940: https://github.com/galaxyproject/galaxy/pull/20940 +.. _Pull Request 18732: https://github.com/galaxyproject/galaxy/pull/18732 +.. _Pull Request 20899: https://github.com/galaxyproject/galaxy/pull/20899 +.. _Pull Request 20717: https://github.com/galaxyproject/galaxy/pull/20717 +.. _Pull Request 20946: https://github.com/galaxyproject/galaxy/pull/20946 +.. _Pull Request 20951: https://github.com/galaxyproject/galaxy/pull/20951 +.. _Pull Request 20851: https://github.com/galaxyproject/galaxy/pull/20851 +.. _Pull Request 20945: https://github.com/galaxyproject/galaxy/pull/20945 +.. _Pull Request 20865: https://github.com/galaxyproject/galaxy/pull/20865 +.. _Pull Request 20957: https://github.com/galaxyproject/galaxy/pull/20957 +.. _Pull Request 20962: https://github.com/galaxyproject/galaxy/pull/20962 +.. _Pull Request 20965: https://github.com/galaxyproject/galaxy/pull/20965 +.. _Pull Request 20718: https://github.com/galaxyproject/galaxy/pull/20718 +.. _Pull Request 20963: https://github.com/galaxyproject/galaxy/pull/20963 +.. _Pull Request 20975: https://github.com/galaxyproject/galaxy/pull/20975 +.. _Pull Request 20949: https://github.com/galaxyproject/galaxy/pull/20949 +.. _Pull Request 20954: https://github.com/galaxyproject/galaxy/pull/20954 +.. _Pull Request 20906: https://github.com/galaxyproject/galaxy/pull/20906 +.. _Pull Request 20885: https://github.com/galaxyproject/galaxy/pull/20885 +.. _Pull Request 20707: https://github.com/galaxyproject/galaxy/pull/20707 +.. _Pull Request 20828: https://github.com/galaxyproject/galaxy/pull/20828 +.. _Pull Request 20931: https://github.com/galaxyproject/galaxy/pull/20931 +.. _Pull Request 20960: https://github.com/galaxyproject/galaxy/pull/20960 +.. _Pull Request 20980: https://github.com/galaxyproject/galaxy/pull/20980 +.. _Pull Request 20926: https://github.com/galaxyproject/galaxy/pull/20926 +.. _Pull Request 20916: https://github.com/galaxyproject/galaxy/pull/20916 +.. _Pull Request 20840: https://github.com/galaxyproject/galaxy/pull/20840 +.. _Pull Request 20830: https://github.com/galaxyproject/galaxy/pull/20830 +.. _Pull Request 20820: https://github.com/galaxyproject/galaxy/pull/20820 +.. _Pull Request 20177: https://github.com/galaxyproject/galaxy/pull/20177 +.. _Pull Request 19817: https://github.com/galaxyproject/galaxy/pull/19817 +.. _Pull Request 20929: https://github.com/galaxyproject/galaxy/pull/20929 +.. _Pull Request 20466: https://github.com/galaxyproject/galaxy/pull/20466 +.. _Pull Request 20692: https://github.com/galaxyproject/galaxy/pull/20692 +.. _Pull Request 20986: https://github.com/galaxyproject/galaxy/pull/20986 +.. _Pull Request 13570: https://github.com/galaxyproject/galaxy/pull/13570 +.. _Pull Request 20519: https://github.com/galaxyproject/galaxy/pull/20519 +.. _Pull Request 20523: https://github.com/galaxyproject/galaxy/pull/20523 +.. _Pull Request 20524: https://github.com/galaxyproject/galaxy/pull/20524 +.. _Pull Request 20542: https://github.com/galaxyproject/galaxy/pull/20542 +.. _Pull Request 20566: https://github.com/galaxyproject/galaxy/pull/20566 +.. _Pull Request 20575: https://github.com/galaxyproject/galaxy/pull/20575 +.. _Pull Request 20582: https://github.com/galaxyproject/galaxy/pull/20582 +.. _Pull Request 20599: https://github.com/galaxyproject/galaxy/pull/20599 +.. _Pull Request 20656: https://github.com/galaxyproject/galaxy/pull/20656 +.. _Pull Request 20676: https://github.com/galaxyproject/galaxy/pull/20676 +.. _Pull Request 20713: https://github.com/galaxyproject/galaxy/pull/20713 +.. _Pull Request 20780: https://github.com/galaxyproject/galaxy/pull/20780 +.. _Pull Request 20856: https://github.com/galaxyproject/galaxy/pull/20856 +.. _Pull Request 20883: https://github.com/galaxyproject/galaxy/pull/20883 +.. _Pull Request 20936: https://github.com/galaxyproject/galaxy/pull/20936 +.. _Pull Request 20953: https://github.com/galaxyproject/galaxy/pull/20953 +.. _Pull Request 20968: https://github.com/galaxyproject/galaxy/pull/20968 +.. _Pull Request 20970: https://github.com/galaxyproject/galaxy/pull/20970 +.. _Pull Request 20976: https://github.com/galaxyproject/galaxy/pull/20976 +.. _Pull Request 20982: https://github.com/galaxyproject/galaxy/pull/20982 +.. _Pull Request 20983: https://github.com/galaxyproject/galaxy/pull/20983 +.. _Pull Request 20984: https://github.com/galaxyproject/galaxy/pull/20984 +.. _Pull Request 20844: https://github.com/galaxyproject/galaxy/pull/20844 +.. _Pull Request 20994: https://github.com/galaxyproject/galaxy/pull/20994 +.. _Pull Request 20971: https://github.com/galaxyproject/galaxy/pull/20971 +.. _Pull Request 20662: https://github.com/galaxyproject/galaxy/pull/20662 +.. _Pull Request 20576: https://github.com/galaxyproject/galaxy/pull/20576 +.. _Pull Request 20626: https://github.com/galaxyproject/galaxy/pull/20626 +.. _Pull Request 20815: https://github.com/galaxyproject/galaxy/pull/20815 +.. _Pull Request 20845: https://github.com/galaxyproject/galaxy/pull/20845 +.. _Pull Request 20992: https://github.com/galaxyproject/galaxy/pull/20992 +.. _Pull Request 20826: https://github.com/galaxyproject/galaxy/pull/20826 +.. _Pull Request 20642: https://github.com/galaxyproject/galaxy/pull/20642 +.. _Pull Request 20788: https://github.com/galaxyproject/galaxy/pull/20788 +.. _Pull Request 20996: https://github.com/galaxyproject/galaxy/pull/20996 +.. _Pull Request 21003: https://github.com/galaxyproject/galaxy/pull/21003 +.. _Pull Request 20999: https://github.com/galaxyproject/galaxy/pull/20999 +.. _Pull Request 21006: https://github.com/galaxyproject/galaxy/pull/21006 +.. _Pull Request 21007: https://github.com/galaxyproject/galaxy/pull/21007 +.. _Pull Request 20985: https://github.com/galaxyproject/galaxy/pull/20985 +.. _Pull Request 21005: https://github.com/galaxyproject/galaxy/pull/21005 +.. _Pull Request 20754: https://github.com/galaxyproject/galaxy/pull/20754 +.. _Pull Request 21009: https://github.com/galaxyproject/galaxy/pull/21009 +.. _Pull Request 21012: https://github.com/galaxyproject/galaxy/pull/21012 +.. _Pull Request 21014: https://github.com/galaxyproject/galaxy/pull/21014 +.. _Pull Request 21017: https://github.com/galaxyproject/galaxy/pull/21017 +.. _Pull Request 21020: https://github.com/galaxyproject/galaxy/pull/21020 +.. _Pull Request 21018: https://github.com/galaxyproject/galaxy/pull/21018 +.. _Pull Request 21019: https://github.com/galaxyproject/galaxy/pull/21019 +.. _Pull Request 21022: https://github.com/galaxyproject/galaxy/pull/21022 +.. _Pull Request 21026: https://github.com/galaxyproject/galaxy/pull/21026 +.. _Pull Request 21045: https://github.com/galaxyproject/galaxy/pull/21045 +.. _Pull Request 20801: https://github.com/galaxyproject/galaxy/pull/20801 +.. _Pull Request 20757: https://github.com/galaxyproject/galaxy/pull/20757 +.. _Pull Request 20898: https://github.com/galaxyproject/galaxy/pull/20898 +.. _Pull Request 20987: https://github.com/galaxyproject/galaxy/pull/20987 +.. _Pull Request 21056: https://github.com/galaxyproject/galaxy/pull/21056 +.. _Pull Request 21055: https://github.com/galaxyproject/galaxy/pull/21055 +.. _Pull Request 21070: https://github.com/galaxyproject/galaxy/pull/21070 +.. _Pull Request 21081: https://github.com/galaxyproject/galaxy/pull/21081 +.. _Pull Request 21095: https://github.com/galaxyproject/galaxy/pull/21095 +.. _Pull Request 20821: https://github.com/galaxyproject/galaxy/pull/20821 +.. _Pull Request 21072: https://github.com/galaxyproject/galaxy/pull/21072 +.. _Pull Request 21094: https://github.com/galaxyproject/galaxy/pull/21094 +.. _Pull Request 21087: https://github.com/galaxyproject/galaxy/pull/21087 +.. _Pull Request 21091: https://github.com/galaxyproject/galaxy/pull/21091 +.. _Pull Request 21096: https://github.com/galaxyproject/galaxy/pull/21096 +.. _Pull Request 21035: https://github.com/galaxyproject/galaxy/pull/21035 +.. _Pull Request 21105: https://github.com/galaxyproject/galaxy/pull/21105 +.. _Pull Request 21112: https://github.com/galaxyproject/galaxy/pull/21112 +.. _Pull Request 21117: https://github.com/galaxyproject/galaxy/pull/21117 +.. _Pull Request 21115: https://github.com/galaxyproject/galaxy/pull/21115 +.. _Pull Request 21049: https://github.com/galaxyproject/galaxy/pull/21049 +.. _Pull Request 21060: https://github.com/galaxyproject/galaxy/pull/21060 +.. _Pull Request 21078: https://github.com/galaxyproject/galaxy/pull/21078 +.. _Pull Request 20424: https://github.com/galaxyproject/galaxy/pull/20424 +.. _Pull Request 21107: https://github.com/galaxyproject/galaxy/pull/21107 +.. _Pull Request 20427: https://github.com/galaxyproject/galaxy/pull/20427 +.. _Pull Request 20550: https://github.com/galaxyproject/galaxy/pull/20550 +.. _Pull Request 20558: https://github.com/galaxyproject/galaxy/pull/20558 +.. _Pull Request 20565: https://github.com/galaxyproject/galaxy/pull/20565 +.. _Pull Request 20585: https://github.com/galaxyproject/galaxy/pull/20585 +.. _Pull Request 20592: https://github.com/galaxyproject/galaxy/pull/20592 +.. _Pull Request 20609: https://github.com/galaxyproject/galaxy/pull/20609 +.. _Pull Request 20624: https://github.com/galaxyproject/galaxy/pull/20624 +.. _Pull Request 20666: https://github.com/galaxyproject/galaxy/pull/20666 +.. _Pull Request 20719: https://github.com/galaxyproject/galaxy/pull/20719 +.. _Pull Request 20737: https://github.com/galaxyproject/galaxy/pull/20737 +.. _Pull Request 20738: https://github.com/galaxyproject/galaxy/pull/20738 +.. _Pull Request 20744: https://github.com/galaxyproject/galaxy/pull/20744 +.. _Pull Request 20747: https://github.com/galaxyproject/galaxy/pull/20747 +.. _Pull Request 20776: https://github.com/galaxyproject/galaxy/pull/20776 +.. _Pull Request 20798: https://github.com/galaxyproject/galaxy/pull/20798 +.. _Pull Request 20805: https://github.com/galaxyproject/galaxy/pull/20805 +.. _Pull Request 20839: https://github.com/galaxyproject/galaxy/pull/20839 +.. _Pull Request 20868: https://github.com/galaxyproject/galaxy/pull/20868 +.. _Pull Request 20881: https://github.com/galaxyproject/galaxy/pull/20881 +.. _Pull Request 20912: https://github.com/galaxyproject/galaxy/pull/20912 +.. _Pull Request 20914: https://github.com/galaxyproject/galaxy/pull/20914 +.. _Pull Request 20943: https://github.com/galaxyproject/galaxy/pull/20943 +.. _Pull Request 20947: https://github.com/galaxyproject/galaxy/pull/20947 +.. _Pull Request 21025: https://github.com/galaxyproject/galaxy/pull/21025 +.. _Pull Request 19084: https://github.com/galaxyproject/galaxy/pull/19084 +.. _Pull Request 19225: https://github.com/galaxyproject/galaxy/pull/19225 +.. _Pull Request 19305: https://github.com/galaxyproject/galaxy/pull/19305 +.. _Pull Request 19318: https://github.com/galaxyproject/galaxy/pull/19318 +.. _Pull Request 19658: https://github.com/galaxyproject/galaxy/pull/19658 +.. _Pull Request 19973: https://github.com/galaxyproject/galaxy/pull/19973 +.. _Pull Request 20287: https://github.com/galaxyproject/galaxy/pull/20287 +.. _Pull Request 20288: https://github.com/galaxyproject/galaxy/pull/20288 +.. _Pull Request 20390: https://github.com/galaxyproject/galaxy/pull/20390 +.. _Pull Request 21051: https://github.com/galaxyproject/galaxy/pull/21051 +.. _Pull Request 21021: https://github.com/galaxyproject/galaxy/pull/21021 +.. _Pull Request 21130: https://github.com/galaxyproject/galaxy/pull/21130 +.. _Pull Request 21134: https://github.com/galaxyproject/galaxy/pull/21134 +.. _Pull Request 21138: https://github.com/galaxyproject/galaxy/pull/21138 +.. _Pull Request 20539: https://github.com/galaxyproject/galaxy/pull/20539 +.. _Pull Request 20629: https://github.com/galaxyproject/galaxy/pull/20629 +.. _Pull Request 21141: https://github.com/galaxyproject/galaxy/pull/21141 +.. _Pull Request 21145: https://github.com/galaxyproject/galaxy/pull/21145 +.. _Pull Request 21142: https://github.com/galaxyproject/galaxy/pull/21142 +.. _Pull Request 21140: https://github.com/galaxyproject/galaxy/pull/21140 +.. _Pull Request 21149: https://github.com/galaxyproject/galaxy/pull/21149 diff --git a/doc/source/releases/26.0_announce.rst b/doc/source/releases/26.0_announce.rst new file mode 100644 index 00000000000..0628593ca82 --- /dev/null +++ b/doc/source/releases/26.0_announce.rst @@ -0,0 +1,6 @@ + +:orphan: + +=========================================================== +26.0 Galaxy Release +=========================================================== diff --git a/doc/source/releases/index.rst b/doc/source/releases/index.rst index 7832c65985d..2b6908c8b39 100644 --- a/doc/source/releases/index.rst +++ b/doc/source/releases/index.rst @@ -4,6 +4,7 @@ Releases .. toctree:: :maxdepth: 1 + 25.1_announce_user 25.0_announce_user 24.2_announce_user 24.1_announce_user diff --git a/lib/galaxy/managers/collections.py b/lib/galaxy/managers/collections.py index 789cf9acc0b..c8539c319ea 100644 --- a/lib/galaxy/managers/collections.py +++ b/lib/galaxy/managers/collections.py @@ -158,11 +158,17 @@ class DatasetCollectionManager: substructure, allow_uninitialized_element=allow_uninitialized_element ) + # Preserve columns metadata from input structure for sample sheets + columns = None + if structure.columns_metadata and identifier in structure.columns_metadata: + columns = structure.columns_metadata[identifier] + element = model.DatasetCollectionElement( collection=dataset_collection, element=element, element_identifier=identifier, element_index=index, + columns=columns, ) elements.append(element) dataset_collection.element_count = len(elements) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 3f6e6a55dae..03000433057 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7378,7 +7378,11 @@ class DatasetCollection(Base, Dictifiable, UsesAnnotations, Serializable): minimize_copies=False, copy_hid=True, ): - new_collection = DatasetCollection(collection_type=self.collection_type, element_count=self.element_count) + new_collection = DatasetCollection( + collection_type=self.collection_type, + element_count=self.element_count, + column_definitions=self.column_definitions, + ) for element in self.elements: element.copy_to_collection( new_collection, diff --git a/lib/galaxy/model/database_heartbeat.py b/lib/galaxy/model/database_heartbeat.py index 84a84842db8..abffbf64625 100644 --- a/lib/galaxy/model/database_heartbeat.py +++ b/lib/galaxy/model/database_heartbeat.py @@ -96,9 +96,10 @@ class DatabaseHeartbeat: session.add(worker_process) # We only want a single process watching the various config files on the file system. # We just pick the max server name for simplicity - is_config_watcher = self.server_name == max( + webapp_servers = [ p.server_name for p in self.get_active_processes(self.heartbeat_interval + 1) if p.app_type == WEBAPP - ) + ] + is_config_watcher = bool(webapp_servers) and self.server_name == max(webapp_servers) if is_config_watcher != self.is_config_watcher: self.is_config_watcher = is_config_watcher diff --git a/lib/galaxy/model/dataset_collections/structure.py b/lib/galaxy/model/dataset_collections/structure.py index 3c0e53e1cc4..97d38319703 100644 --- a/lib/galaxy/model/dataset_collections/structure.py +++ b/lib/galaxy/model/dataset_collections/structure.py @@ -64,14 +64,17 @@ class UninitializedTree(BaseTree): class Tree(BaseTree): children_known = True - def __init__(self, children, collection_type_description, when_values=None): + def __init__(self, children, collection_type_description, when_values=None, columns_metadata=None): super().__init__(collection_type_description) self.children = children self.when_values = when_values + # columns_metadata is a dict mapping element_identifier to columns data + self.columns_metadata = columns_metadata or {} @staticmethod def for_dataset_collection(dataset_collection, collection_type_description): children = [] + columns_metadata = {} for element in dataset_collection.elements: if collection_type_description.has_subcollections(): child_collection = element.child_collection @@ -84,7 +87,10 @@ class Tree(BaseTree): children.append((element.element_identifier, tree)) else: children.append((element.element_identifier, leaf)) - return Tree(children, collection_type_description) + # Capture columns metadata from sample sheet collections + if element.columns is not None: + columns_metadata[element.element_identifier] = element.columns + return Tree(children, collection_type_description, columns_metadata=columns_metadata) def walk_collections(self, hdca_dict): return self._walk_collections(dict_map(lambda hdca: hdca.collection, hdca_dict)) @@ -142,11 +148,12 @@ class Tree(BaseTree): for identifier, structure in self.children: new_children.append((identifier, structure.multiply(other_structure))) - return Tree(new_children, new_collection_type) + # Preserve columns_metadata when multiplying + return Tree(new_children, new_collection_type, columns_metadata=self.columns_metadata.copy()) def clone(self): cloned_children = [(_[0], _[1].clone()) for _ in self.children] - return Tree(cloned_children, self.collection_type_description) + return Tree(cloned_children, self.collection_type_description, columns_metadata=self.columns_metadata.copy()) def __str__(self): return f"Tree[collection_type={self.collection_type_description},children=({','.join(f'{identifier_and_element[0]}={identifier_and_element[1]}' for identifier_and_element in self.children)})]" diff --git a/lib/galaxy/model/migrations/dbrevisions.py b/lib/galaxy/model/migrations/dbrevisions.py index 7585b2e59e7..23fcc838a83 100644 --- a/lib/galaxy/model/migrations/dbrevisions.py +++ b/lib/galaxy/model/migrations/dbrevisions.py @@ -14,5 +14,5 @@ REVISION_TAGS = { "24.1": "04288b6a5b25", "24.2": "a4c3ef999ab5", "25.0": "c716ee82337b", - "25.1": "cd26484899fb", + "25.1": "63dc6cca023f", } diff --git a/lib/galaxy/tools/sample_sheet_to_tabular.xml b/lib/galaxy/tools/sample_sheet_to_tabular.xml index a7a5f8fc100..d6be1f17d23 100644 --- a/lib/galaxy/tools/sample_sheet_to_tabular.xml +++ b/lib/galaxy/tools/sample_sheet_to_tabular.xml @@ -6,7 +6,13 @@ operation_3359 - + + @@ -39,5 +47,11 @@ Synopsis Takes a sample sheet dataset collection and converts the sample sheet metadata into a tabular dataset. +The output is a tab-separated file where each row corresponds to an element in the sample sheet collection. +The first column contains the element identifier, followed by columns for each metadata field defined in the sample sheet. + +When "Include column headers" is enabled, the first row will contain the column names, with "element_identifier" +as the first column followed by the names from the sample sheet column definitions. + ]]> diff --git a/lib/galaxy/workflow/run.py b/lib/galaxy/workflow/run.py index 36792896a49..6d4bd6d1eb0 100644 --- a/lib/galaxy/workflow/run.py +++ b/lib/galaxy/workflow/run.py @@ -224,6 +224,12 @@ class WorkflowInvoker: remaining_steps = self.progress.remaining_steps() delayed_steps = False max_jobs_per_iteration_reached = False + + # Pre-populate outputs for all input steps so subworkflows can access them + for step in self.workflow_invocation.workflow.steps: + if step.is_input_type: + self.progress._ensure_input_step_outputs_populated(step) + for step, workflow_invocation_step in remaining_steps: max_jobs_to_schedule = self.progress.maximum_jobs_to_schedule_or_none if max_jobs_to_schedule is not None and max_jobs_to_schedule <= 0: @@ -255,13 +261,29 @@ class WorkflowInvoker: self.progress.mark_step_outputs_delayed(step, why=de.why) except Exception as e: log_function = log.error - if isinstance(e, modules.FailWorkflowEvaluation) and e.why.reason in FAILURE_REASONS_EXPECTED: - log_function = log.info + failure_details = [] + if isinstance(e, modules.FailWorkflowEvaluation): + if e.why.reason in FAILURE_REASONS_EXPECTED: + log_function = log.info + failure_details.append(f"reason={e.why.reason}") + if hasattr(e.why, "output_name") and e.why.output_name: + failure_details.append(f"output_name={e.why.output_name}") + if hasattr(e.why, "dependent_workflow_step_id") and e.why.dependent_workflow_step_id: + failure_details.append(f"dependent_step_id={e.why.dependent_workflow_step_id}") + if hasattr(e.why, "workflow_step_id") and e.why.workflow_step_id: + failure_details.append(f"failed_step_id={e.why.workflow_step_id}") + if hasattr(e.why, "details") and e.why.details: + failure_details.append(f"details={e.why.details}") + else: + failure_details.append(f"{type(e).__name__}: {str(e)}") + failure_reason = ", ".join(failure_details) if failure_details else "unknown" + log_function( - "Failed to schedule %s for %s, problem occurred on %s.", + "Failed to schedule %s for %s, problem occurred on %s. Failure reason: %s", self.workflow_invocation.log_str(), self.workflow_invocation.workflow.log_str(), step.log_str(), + failure_reason, ) if isinstance(e, MessageException): # This is the highest level at which we can inject the step id @@ -578,14 +600,26 @@ class WorkflowProgress: if outputs is None: outputs = {} - if self.inputs_by_step_id: - step_id = step.id - if step_id not in self.inputs_by_step_id and "output" not in outputs: - default_value = step.get_input_default_value(NO_REPLACEMENT) - outputs["output"] = default_value - elif step_id in self.inputs_by_step_id: - if self.inputs_by_step_id[step_id] is not None or "output" not in outputs: + # Check if we have a pre-populated value from _ensure_input_step_outputs_populated + # This takes precedence over values from step.state.inputs for input steps + # Only use pre-populated values when inputs_by_step_id was available during pre-population + if step.is_input_type and self.inputs_by_step_id and step.id in self.outputs: + pre_populated = self.outputs.get(step.id) + if isinstance(pre_populated, dict) and "output" in pre_populated: + outputs["output"] = pre_populated["output"] + + # Output not yet set + if "output" not in outputs: + if self.inputs_by_step_id: + step_id = step.id + if step_id not in self.inputs_by_step_id: + default_value = step.get_input_default_value(NO_REPLACEMENT) + outputs["output"] = default_value + elif self.inputs_by_step_id[step_id] is not None or "output" not in outputs: outputs["output"] = self.inputs_by_step_id[step_id] + else: + # When inputs_by_step_id is None (e.g., during recovery), use default + outputs["output"] = step.get_input_default_value(NO_REPLACEMENT) if step.label and step.type == "parameter_input" and "output" in outputs: self.runtime_replacements[step.label] = str(outputs["output"]) @@ -651,6 +685,32 @@ class WorkflowProgress: raise MessageException(f"Failed to find persisted subworkflow invocation for step [{step.order_index + 1}]") return subworkflow_invocation + def _ensure_input_step_outputs_populated(self, step: "WorkflowStep") -> None: + """Pre-populate outputs for input steps that haven't executed yet. + + Input steps have no dependencies, so their output value can be determined + immediately. This allows subworkflows to access parent input values before + the input step formally executes. + """ + if not step.is_input_type or step.id in self.outputs: + return + + # Check if step already executed - if so, skip pre-population + step_invocations = self.workflow_invocation.step_invocations_by_step_id() + if step.id in step_invocations: + invocation_step = step_invocations[step.id] + if invocation_step.state == "scheduled": + return + + # Determine output value from inputs_by_step_id or default + outputs = {} + if step.id not in self.inputs_by_step_id: + outputs["output"] = step.get_input_default_value(NO_REPLACEMENT) + else: + outputs["output"] = self.inputs_by_step_id[step.id] + + self.outputs[step.id] = outputs + def subworkflow_invoker( self, trans: "WorkRequestContext", diff --git a/lib/galaxy_test/api/test_dataset_collections.py b/lib/galaxy_test/api/test_dataset_collections.py index 4c5ae2773b6..c2e0797784c 100644 --- a/lib/galaxy_test/api/test_dataset_collections.py +++ b/lib/galaxy_test/api/test_dataset_collections.py @@ -16,6 +16,7 @@ from galaxy_test.base.decorators import requires_new_user from galaxy_test.base.populators import ( DatasetCollectionPopulator, DatasetPopulator, + skip_without_tool, ) from ._framework import ApiTestCase @@ -427,6 +428,86 @@ class TestDatasetCollectionsApi(ApiTestCase): assert row_1[0] == 45 # TODO: test case where column definition does not match supplied data + @skip_without_tool("cat1") + def test_sample_sheet_map_over_preserves_columns(self, history_id): + """Test that mapping cat1 over a sample sheet preserves columns metadata.""" + # Create a sample sheet collection with columns metadata + contents = [ + ("sample1", "content1"), + ("sample2", "content2"), + ("sample3", "content3"), + ] + sample_sheet_identifiers = self.dataset_collection_populator.list_identifiers(history_id, contents) + payload = dict( + name="test sample sheet", + instance_type="history", + history_id=history_id, + element_identifiers=sample_sheet_identifiers, + collection_type="sample_sheet", + column_definitions=[ + {"type": "int", "name": "replicate", "optional": False}, + {"type": "string", "name": "condition", "optional": False}, + ], + rows={ + "sample1": [1, "control"], + "sample2": [2, "treatment"], + "sample3": [3, "control"], + }, + ) + create_response = self._post("dataset_collections", payload, json=True) + sample_sheet = self._check_create_response(create_response) + hdca_id = sample_sheet["id"] + + # Verify the input sample sheet has columns metadata + input_elements = sample_sheet["elements"] + assert len(input_elements) == 3 + assert input_elements[0]["columns"] == [1, "control"] + assert input_elements[1]["columns"] == [2, "treatment"] + assert input_elements[2]["columns"] == [3, "control"] + + # Run cat1 on the sample sheet collection in batch mode (mapping over it) + inputs = { + "input1": {"batch": True, "values": [{"src": "hdca", "id": hdca_id}]}, + } + run = self.dataset_populator.run_tool("cat1", inputs=inputs, history_id=history_id) + self.dataset_populator.wait_for_history_jobs(history_id) + + # Get the implicit output collection + implicit_collections = run["implicit_collections"] + assert len(implicit_collections) == 1, f"Expected 1 implicit collection, got {len(implicit_collections)}" + output_collection = implicit_collections[0] + + # Fetch the full collection details including elements + collection_details = self.dataset_populator.get_history_collection_details( + history_id, content_id=output_collection["id"] + ) + + # Verify that the output collection preserved the columns metadata + output_elements = collection_details["elements"] + assert len(output_elements) == 3, f"Expected 3 output elements, got {len(output_elements)}" + + # Check that columns metadata was preserved for each element + self._assert_has_keys(output_elements[0], "columns") + assert output_elements[0]["columns"] == [ + 1, + "control", + ], f"Expected [1, 'control'], got {output_elements[0]['columns']}" + assert output_elements[0]["element_identifier"] == "sample1" + + self._assert_has_keys(output_elements[1], "columns") + assert output_elements[1]["columns"] == [ + 2, + "treatment", + ], f"Expected [2, 'treatment'], got {output_elements[1]['columns']}" + assert output_elements[1]["element_identifier"] == "sample2" + + self._assert_has_keys(output_elements[2], "columns") + assert output_elements[2]["columns"] == [ + 3, + "control", + ], f"Expected [3, 'control'], got {output_elements[2]['columns']}" + assert output_elements[2]["element_identifier"] == "sample3" + def test_workbook_download(self): xlsx_file = self.dataset_collection_populator.download_workbook( "sample_sheet", diff --git a/lib/galaxy_test/api/test_workflows.py b/lib/galaxy_test/api/test_workflows.py index fd93c0ca606..447cf454819 100644 --- a/lib/galaxy_test/api/test_workflows.py +++ b/lib/galaxy_test/api/test_workflows.py @@ -5528,6 +5528,100 @@ test_data: assert_ok=True, ) + def test_run_subworkflow_with_optional_parent_input_connected_but_not_provided(self): + """Test subworkflow when parent's optional input is connected but not provided. + + This test verifies the fix for a bug where: + 1. Parent workflow has an OPTIONAL input + 2. That optional input IS CONNECTED to the subworkflow's input + 3. But NO DATA is provided for the parent's optional input + 4. Subworkflow has DELAYED SCHEDULING (via $link) + + After the fix, the parent's optional input outputs are pre-populated with NO_REPLACEMENT + before the subworkflow executes, allowing it to properly handle the missing value. + + This is different from the case where subworkflow inputs are completely + unconnected, which was fixed in 2022. + """ + with self.dataset_populator.test_history() as history_id: + # Workflow with OPTIONAL input that IS CONNECTED to subworkflow + # input data is not provided + workflow = """ +class: GalaxyWorkflow +inputs: + parent_required: + type: data + parent_optional: + type: data + optional: true +steps: + nested_workflow: + in: + required_input: parent_required + optional_input: parent_optional + run: + class: GalaxyWorkflow + inputs: + required_input: data + optional_input: + type: data + optional: true + steps: + expression: + tool_id: expression_parse_int + state: + input1: 1 + head: + tool_id: head + in: + input: required_input + state: + lineNum: + $link: expression/out1 + count_multi_file: + tool_id: count_multi_file + in: + input1: + - optional_input + - head/out_file1 +test_data: + parent_required: + value: 1.bed + type: File +""" + + # After the fix, this should now succeed + summary = self._run_workflow( + workflow, + history_id=history_id, + wait=True, + assert_ok=True, + ) + + # Check parent invocation + parent_invocation = self.workflow_populator.get_invocation(summary.invocation_id, step_details=True) + + # Get the subworkflow invocation + subworkflow_step = None + for step in parent_invocation["steps"]: + if step.get("subworkflow_invocation_id"): + subworkflow_step = step + break + + assert subworkflow_step is not None, "No subworkflow step found" + + subworkflow_invocation_id = subworkflow_step["subworkflow_invocation_id"] + subworkflow_invocation = self.workflow_populator.get_invocation(subworkflow_invocation_id) + + # The subworkflow should have succeeded + assert ( + subworkflow_invocation["state"] == "scheduled" + ), f"Expected subworkflow to succeed, got state: {subworkflow_invocation['state']}" + + # Should not have error messages + messages = subworkflow_invocation.get("messages", []) + assert len(messages) == 0, f"Expected no error messages, got: {messages}" + def test_run_with_non_optional_data_unspecified_fails_invocation(self): with self.dataset_populator.test_history() as history_id: error = self._run_jobs( diff --git a/test/unit/data/dataset_collections/test_matching.py b/test/unit/data/dataset_collections/test_matching.py index 60de9c300ef..50cb6af35d4 100644 --- a/test/unit/data/dataset_collections/test_matching.py +++ b/test/unit/data/dataset_collections/test_matching.py @@ -254,6 +254,7 @@ class MockCollectionElement: self.element_identifier = element_identifier self.child_collection = collection self.hda = None + self.columns = None class MockHDAElement: @@ -261,6 +262,7 @@ class MockHDAElement: self.element_identifier = element_identifier self.child_collection = False self.hda = object() + self.columns = None collection_instance = MockCollectionInstance