Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge latest 24.2 into dev. #19302

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
09d8695
Update title of edited PRs only if base ref changed
nsoranzo Nov 21, 2024
d59f7a6
Merge pull request #19183 from nsoranzo/release_24.0_check_base_ref_c…
jdavcs Nov 22, 2024
f3e65fa
Refactor PR title update workflow to handle version extraction and ti…
arash77 Nov 25, 2024
afdfa78
Refactor version extraction in PR title update workflow for improved …
arash77 Nov 26, 2024
137dc4e
Fix workflow_invocation_step not being added to session
mvdbeek Nov 26, 2024
e9194d7
Fix exporting of jobs that set copied_from_history_dataset_association
mvdbeek Nov 26, 2024
e57201e
Drop unused copied_to_history_dataset_collection_association relation…
mvdbeek Nov 26, 2024
28ce456
Prevent assigning opied_from_history_dataset_collection_association i…
mvdbeek Nov 26, 2024
b265c2b
Don't duplicate exported collections if they're references multiple t…
mvdbeek Nov 26, 2024
2d7667d
Add regression test for #18927
mvdbeek Nov 27, 2024
f631f62
Don't store workflow_path when importing invocation
mvdbeek Nov 27, 2024
91fc1d8
Only allow admin users to sync workflows to filesystem
mvdbeek Nov 27, 2024
e301ec9
Fix up action_arguments for workflows converted by gxformat2 < 0.20.0
mvdbeek Nov 28, 2024
f4ab432
Merge pull request #19195 from arash77/pr-title-update-change-base
martenson Nov 29, 2024
a42d7f3
Create harmonized collections from correct tool outputs
wm75 Nov 29, 2024
129b8a2
Fix test file type
wm75 Nov 29, 2024
891d7aa
Try to fix tool test
wm75 Nov 29, 2024
1e6a5c0
Merge pull request #19215 from mvdbeek/24_0_fix_various_invocation_ex…
jmchilton Nov 29, 2024
ee5f807
Merge pull request #19222 from wm75/fix-harmonize
mvdbeek Dec 2, 2024
68efe8d
[24.2] Fix cancellation of workflow scheduling
ahmedhamidawan Dec 3, 2024
274da12
fix force routing for `WorkflowRunButton`
ahmedhamidawan Dec 3, 2024
1415920
use `to` prop for edit button in `WorkflowNavigationTitle`
ahmedhamidawan Dec 3, 2024
24ba9c2
match `WorkflowNavigationTitle` styling with `ToolCard` header
ahmedhamidawan Dec 3, 2024
98236e1
fix jest to align with the workflow nav header changes
ahmedhamidawan Dec 3, 2024
69ae867
poll if invocation state is not terminal again
ahmedhamidawan Dec 4, 2024
53c8fb5
adjust `ProgressBar` props to fix console errors
ahmedhamidawan Dec 4, 2024
79a073e
Initial selenium tests for workflow invocation export.
jmchilton Dec 5, 2024
abf08fa
Update lib/galaxy_test/selenium/test_workflow_run.py
jmchilton Dec 6, 2024
57fa466
Fix bug in attribute handler...
jmchilton Dec 9, 2024
f98b5b3
Update client/src/components/Workflow/Editor/Index.vue
jmchilton Dec 9, 2024
a6954b0
Merge pull request #19288 from jmchilton/fix_undo_stack
jmchilton Dec 9, 2024
1c6bece
Fix PieChart legend visual issue
davelopez Dec 9, 2024
e3ac780
Increase PieChart legend font size
davelopez Dec 9, 2024
631da64
Fix label for 'Group By' dropdown in Workflow Invocation Metrics
davelopez Dec 9, 2024
f07d673
Center PieCharts horizontally
davelopez Dec 9, 2024
285bdf4
flip default value for use_mamba to false
bgruening Dec 9, 2024
5e7e78f
Merge pull request #19291 from davelopez/24.02_fix_workflow_metrics_c…
dannon Dec 9, 2024
b39c3b2
Merge pull request #19295 from bgruening/flip_mamba
nsoranzo Dec 10, 2024
e5f0a8c
Merge branch 'release_24.0' into release_24.1
nsoranzo Dec 10, 2024
c843938
Fix bad merge conflict resolution
nsoranzo Dec 10, 2024
45f315d
Merge pull request #19297 from nsoranzo/release_24.1_fix_merge
mvdbeek Dec 10, 2024
6a22083
Merge pull request #19238 from ahmedhamidawan/fix_cancelling_invocation
mvdbeek Dec 10, 2024
a2e684c
Merge pull request #19262 from jmchilton/wf_export_testing
davelopez Dec 10, 2024
0991d59
Merge branch 'release_24.1' into release_24.2
nsoranzo Dec 10, 2024
3b48311
Merge remote-tracking branch 'origin/release_24.2' into merge_24_2_4
jmchilton Dec 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions .github/workflows/pr-title-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@ name: Update PR title

on:
pull_request_target:
types: [opened, edited]
branches:
- "release_**"
types: [opened, edited, reopened]

jobs:
update-title:
if: github.event.action != 'edited' || github.event.changes.base.ref.from != ''
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Update PR title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
TARGET_BRANCH: "${{ github.base_ref }}"
PR_TITLE: "${{ github.event.pull_request.title }}"
REPO: "${{ github.repository }}"
run: |
VERSION=$(echo $TARGET_BRANCH | grep -oP '\d+\.\d+')
if [[ -n "$VERSION" && ! "$PR_TITLE" =~ ^\[$VERSION\] ]]; then
NEW_TITLE="[$VERSION] $PR_TITLE"
gh pr edit $PR_NUMBER --title "$NEW_TITLE"
VERSION=$(echo $TARGET_BRANCH | grep -oP '^release_\K\d+.\d+$' || true)
NEW_TITLE=$(echo "$PR_TITLE" | sed -E "s/\[[0-9]+\.[0-9]+\] //")
if [[ -n "$VERSION" ]]; then
NEW_TITLE="[$VERSION] $NEW_TITLE"
fi
if [[ "$NEW_TITLE" != "$PR_TITLE" ]]; then
gh pr edit $PR_NUMBER --repo "$REPO" --title "$NEW_TITLE"
fi
4 changes: 2 additions & 2 deletions client/src/components/ProgressBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import { BProgress, BProgressBar } from "bootstrap-vue";

interface Props {
total: number;
total?: number;
note: string;
loading: boolean;
loading?: boolean;
okCount?: number;
runningCount?: number;
newCount?: number;
Expand Down
15 changes: 1 addition & 14 deletions client/src/components/Tool/ToolCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const showHelpForum = computed(() => isConfigLoaded.value && config.value.enable

<template>
<div class="position-relative">
<div class="underlay sticky-top" />
<div class="ui-form-header-underlay sticky-top" />
<div class="tool-header sticky-top bg-secondary px-2 py-1 rounded">
<div class="d-flex justify-content-between">
<div class="py-1 d-flex flex-wrap flex-gapx-1">
Expand Down Expand Up @@ -209,19 +209,6 @@ const showHelpForum = computed(() => isConfigLoaded.value && config.value.enable
</template>

<style lang="scss" scoped>
@import "scss/theme/blue.scss";

.underlay::after {
content: "";
display: block;
position: absolute;
top: -$margin-h;
left: -0.5rem;
right: -0.5rem;
height: 50px;
background: linear-gradient($white 75%, change-color($white, $alpha: 0));
}

.fa-wrench {
cursor: unset;
}
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Workflow/Editor/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export default {
ensureParametersSet();
stateStore.activeNodeId = null;
activityBar.value?.setActiveSideBar("workflow-editor-attributes");
if (args.highlight) {
if (args?.highlight) {
this.highlightAttribute = args.highlight;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ Examples of RDM repositories include [Zenodo](https://zenodo.org/), [Invenio RDM
<BCard
v-for="plugin in exportPlugins"
:key="plugin.id"
:data-invocation-export-type="plugin.id"
class="wizard-selection-card"
:border-variant="exportData.exportPluginFormat === plugin.id ? 'primary' : 'default'"
@click="exportData.exportPluginFormat = plugin.id">
Expand All @@ -350,6 +351,7 @@ Examples of RDM repositories include [Zenodo](https://zenodo.org/), [Invenio RDM
<BCard
v-for="target in exportDestinationTargets"
:key="target.destination"
:data-invocation-export-destination="target.destination"
:border-variant="exportData.destination === target.destination ? 'primary' : 'default'"
:header-bg-variant="exportData.destination === target.destination ? 'primary' : 'default'"
:header-text-variant="exportData.destination === target.destination ? 'white' : 'default'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ interface Props {
workflow: Workflow;
/** Whether the invocation is terminal */
isTerminal: boolean;
/** Whether the invocation is scheduled */
isScheduled: boolean;
/** The zoom level for the graph */
zoom?: number;
/** Whether to show the minimap */
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Workflow/Run/WorkflowRunFormSimple.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div>
<div v-if="isConfigLoaded">
<div class="ui-form-header-underlay sticky-top" />
<div v-if="isConfigLoaded" class="sticky-top">
<BAlert v-if="!canRunOnHistory" variant="warning" show>
<span v-localize>
The workflow cannot run because the current history is immutable. Please select a different history
Expand Down
29 changes: 8 additions & 21 deletions client/src/components/Workflow/WorkflowNavigationTitle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,13 @@ const localVue = getLocalVue();
* @param version The version of the component to mount (`run_form` or `invocation` view)
* @param ownsWorkflow Whether the user owns the workflow associated with the invocation
* @param unimportableWorkflow Whether the workflow import should fail
* @returns The wrapper object, and the mockRouter object
* @returns The wrapper object
*/
async function mountWorkflowNavigationTitle(
version: "run_form" | "invocation",
ownsWorkflow = true,
unimportableWorkflow = false
) {
const mockRouter = {
push: jest.fn(),
};

let workflowId: string;
let invocation;
if (version === "invocation") {
Expand All @@ -96,9 +92,6 @@ async function mountWorkflowNavigationTitle(
workflowId,
},
localVue,
mocks: {
$router: mockRouter,
},
pinia: createTestingPinia(),
});

Expand All @@ -107,7 +100,7 @@ async function mountWorkflowNavigationTitle(
username: ownsWorkflow ? WORKFLOW_OWNER : OTHER_USER,
});

return { wrapper, mockRouter };
return { wrapper };
}

describe("WorkflowNavigationTitle renders", () => {
Expand All @@ -116,10 +109,9 @@ describe("WorkflowNavigationTitle renders", () => {

const heading = wrapper.find(SELECTORS.WORKFLOW_HEADING);
expect(heading.text()).toContain(`Invoked Workflow: ${SAMPLE_WORKFLOW.name}`);
expect(heading.text()).toContain(`(version: ${SAMPLE_WORKFLOW.version + 1})`);
expect(heading.text()).toContain(`(Version: ${SAMPLE_WORKFLOW.version + 1})`);

const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP);
const runButton = actionsGroup.find(SELECTORS.ROUTE_TO_RUN_BUTTON);
const runButton = wrapper.find(SELECTORS.ROUTE_TO_RUN_BUTTON);
expect(runButton.attributes("title")).toContain("Rerun");
expect(runButton.attributes("title")).toContain(SAMPLE_WORKFLOW.name);
});
Expand All @@ -129,24 +121,19 @@ describe("WorkflowNavigationTitle renders", () => {

const heading = wrapper.find(SELECTORS.WORKFLOW_HEADING);
expect(heading.text()).toContain(`Workflow: ${SAMPLE_WORKFLOW.name}`);
expect(heading.text()).toContain(`(version: ${SAMPLE_WORKFLOW.version + 1})`);
expect(heading.text()).toContain(`(Version: ${SAMPLE_WORKFLOW.version + 1})`);

const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP);
const runButton = actionsGroup.find(SELECTORS.EXECUTE_WORKFLOW_BUTTON);
const runButton = wrapper.find(SELECTORS.EXECUTE_WORKFLOW_BUTTON);
expect(runButton.attributes("title")).toContain("Run");
});

it("edit button if user owns the workflow", async () => {
async function findAndClickEditButton(version: "invocation" | "run_form") {
const { wrapper, mockRouter } = await mountWorkflowNavigationTitle(version);
const { wrapper } = await mountWorkflowNavigationTitle(version);
const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP);

const editButton = actionsGroup.find(SELECTORS.EDIT_WORKFLOW_BUTTON);
await editButton.trigger("click");
await flushPromises();

expect(mockRouter.push).toHaveBeenCalledTimes(1);
expect(mockRouter.push).toHaveBeenCalledWith(
expect(editButton.attributes("to")).toBe(
`/workflows/edit?id=${SAMPLE_WORKFLOW.id}&version=${SAMPLE_WORKFLOW.version}`
);
}
Expand Down
67 changes: 31 additions & 36 deletions client/src/components/Workflow/WorkflowNavigationTitle.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { BAlert, BButton, BButtonGroup } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { computed, ref } from "vue";
import { RouterLink } from "vue-router";
import { useRouter } from "vue-router/composables";

import { isRegisteredUser } from "@/api";
import type { WorkflowInvocationElementView } from "@/api/invocations";
Expand All @@ -21,8 +20,6 @@ import AsyncButton from "../Common/AsyncButton.vue";
import ButtonSpinner from "../Common/ButtonSpinner.vue";
import WorkflowRunButton from "./WorkflowRunButton.vue";

const router = useRouter();

interface Props {
invocation?: WorkflowInvocationElementView;
workflowId: string;
Expand Down Expand Up @@ -98,16 +95,14 @@ const workflowImportTitle = computed(() => {

<BAlert v-if="error" variant="danger" show>{{ error }}</BAlert>

<div class="position-relative">
<div v-if="workflow" class="ui-portlet-section">
<div class="d-flex portlet-header align-items-center">
<div class="position-relative mb-2">
<div v-if="workflow" class="bg-secondary px-2 py-1 rounded">
<div class="d-flex align-items-center flex-gapx-1">
<div class="flex-grow-1" data-description="workflow heading">
<div class="px-1">
<FontAwesomeIcon :icon="faSitemap" />
<b class="mx-1">
{{ props.invocation ? "Invoked " : "" }}Workflow: {{ getWorkflowName() }}
</b>
<i>(version: {{ workflow.version + 1 }})</i>
<div>
<FontAwesomeIcon :icon="faSitemap" fixed-width />
<b> {{ props.invocation ? "Invoked " : "" }}Workflow: {{ getWorkflowName() }} </b>
<span>(Version: {{ workflow.version + 1 }})</span>
</div>
</div>
<BButtonGroup>
Expand All @@ -122,7 +117,7 @@ const workflowImportTitle = computed(() => {
"
variant="link"
:disabled="workflow.deleted"
@click="router.push(`/workflows/edit?id=${workflow.id}&version=${workflow.version}`)">
:to="`/workflows/edit?id=${workflow.id}&version=${workflow.version}`">
<FontAwesomeIcon :icon="faEdit" fixed-width />
</BButton>
<AsyncButton
Expand All @@ -138,30 +133,30 @@ const workflowImportTitle = computed(() => {
</AsyncButton>

<slot name="workflow-title-actions" />

<ButtonSpinner
v-if="!props.invocation"
id="run-workflow"
data-description="execute workflow button"
:wait="runWaiting"
:disabled="runDisabled"
size="sm"
title="Run"
@onClick="emit('on-execute')" />
<WorkflowRunButton
v-else
:id="workflow.id"
data-description="route to workflow run button"
variant="link"
:title="
!workflow.deleted
? `<b>Rerun</b><br>${getWorkflowName()}`
: 'This workflow has been deleted.'
"
:disabled="workflow.deleted"
full
:version="workflow.version" />
</BButtonGroup>
<ButtonSpinner
v-if="!props.invocation"
id="run-workflow"
data-description="execute workflow button"
:wait="runWaiting"
:disabled="runDisabled"
size="sm"
title="Run Workflow"
@onClick="emit('on-execute')" />
<WorkflowRunButton
v-else
:id="workflow.id"
data-description="route to workflow run button"
variant="link"
:title="
!workflow.deleted
? `<b>Rerun</b><br>${getWorkflowName()}`
: 'This workflow has been deleted.'
"
:disabled="workflow.deleted"
force
full
:version="workflow.version" />
</div>
</div>
<div v-if="props.success" class="donemessagelarge">
Expand Down
60 changes: 54 additions & 6 deletions client/src/components/Workflow/WorkflowRunButton.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { mount } from "@vue/test-utils";
import { mount, type Wrapper } from "@vue/test-utils";
import flushPromises from "flush-promises";
import { getLocalVue } from "tests/jest/helpers";

import { generateRandomString } from "./testUtils";
Expand All @@ -11,22 +12,69 @@ const WORKFLOW_ID = generateRandomString();
const WORKFLOW_VERSION = 1;
const WORKFLOW_RUN_BUTTON_SELECTOR = `[data-workflow-run="${WORKFLOW_ID}"]`;

async function mountWorkflowRunButton(props?: { id: string; version: number; full?: boolean }) {
function getPath() {
return `/workflows/run?id=${WORKFLOW_ID}&version=${WORKFLOW_VERSION}`;
}

async function mountWorkflowRunButton(
props?: { id: string; version: number; full?: boolean; force?: boolean },
currentPath?: string
) {
const mockRouter = {
push: jest.fn(),
afterEach: jest.fn(),
currentRoute: {
fullPath: currentPath || getPath(),
},
};

const wrapper = mount(WorkflowRunButton as object, {
propsData: { ...props },
localVue,
mocks: {
$router: mockRouter,
},
});

return { wrapper };
return { wrapper, mockRouter };
}

async function clickButton(button: Wrapper<Vue>) {
// Remove the href and target attributes to prevent navigation error
// This is done because the `BButton` has a `:to` prop as well as an `@click` event
button.element.setAttribute("href", "javascript:void(0)");
button.element.setAttribute("target", "");

await button.trigger("click");
await flushPromises();
}

describe("WorkflowRunButton.vue", () => {
it("should render button with icon and route", async () => {
const { wrapper } = await mountWorkflowRunButton({ id: WORKFLOW_ID, version: WORKFLOW_VERSION });
it("should render button with icon and route to it", async () => {
const { wrapper, mockRouter } = await mountWorkflowRunButton({ id: WORKFLOW_ID, version: WORKFLOW_VERSION });

const button = wrapper.find(WORKFLOW_RUN_BUTTON_SELECTOR);
expect(button.attributes("title")).toBe("Run workflow");
expect(button.text()).toBe("");
expect(button.attributes("href")).toBe(`/workflows/run?id=${WORKFLOW_ID}&version=${WORKFLOW_VERSION}`);
expect(button.attributes("href")).toBe(getPath());

await clickButton(button);

// Check that router.push was called with the correct arguments
expect(mockRouter.push).toHaveBeenCalledWith(getPath());
});

it("should force route if on the same path", async () => {
const { wrapper, mockRouter } = await mountWorkflowRunButton(
{ id: WORKFLOW_ID, version: WORKFLOW_VERSION, force: true },
getPath()
);

const button = wrapper.find(WORKFLOW_RUN_BUTTON_SELECTOR);

await clickButton(button);

// Check that router.push was called with the correct arguments
expect(mockRouter.push).toHaveBeenCalledWith(getPath(), { force: true });
});
});
Loading
Loading