Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dc5fa4f
feat(e2e): introduce e2e tests for bulk-import plugin
AndrienkoAleksandr Feb 25, 2026
bf284d8
feat(e2e): fix code format
AndrienkoAleksandr Feb 26, 2026
83be7bd
feat(e2e): fix tsc check
AndrienkoAleksandr Feb 26, 2026
d17fd63
feat(e2e): fix up
AndrienkoAleksandr Feb 26, 2026
bb18228
feat(e2e): use newer e2e library
AndrienkoAleksandr Mar 3, 2026
f6866f2
feat(e2e): skip custom fixture
AndrienkoAleksandr Mar 3, 2026
17028a9
feat(e2e): format code
AndrienkoAleksandr Mar 5, 2026
f8ec22a
feat(e2e): fix eslint
AndrienkoAleksandr Mar 5, 2026
03c2d59
feat(e2e): use newer e2e lib with improved timeout
AndrienkoAleksandr Mar 5, 2026
15a6d16
feat(e2e): use test github credentials
AndrienkoAleksandr Mar 5, 2026
0f80f62
feat(e2e): use another github org to fit with credentials
AndrienkoAleksandr Mar 10, 2026
249d1e9
feat(e2e): use custom github helper with token
04kash Mar 10, 2026
2b8e240
feat(e2e): use token for integrations
AndrienkoAleksandr Mar 10, 2026
1f61899
feat(e2e): keep just needed secret values
AndrienkoAleksandr Mar 10, 2026
4961cd8
feat(e2e): fix some sonar cloud issues, typos
AndrienkoAleksandr Mar 10, 2026
9481ac4
feat(e2e): use origin APIHelper
AndrienkoAleksandr Mar 10, 2026
e597e30
feat(e2e): use e2e-test-utils for orchestrator
AndrienkoAleksandr Mar 25, 2026
4b5c9a6
feat(e2e): fix github token env var name
AndrienkoAleksandr Mar 25, 2026
144a1d2
feat(e2e): don't patch env variable
AndrienkoAleksandr Mar 25, 2026
200088e
feat(e2e): handle code review feedback
AndrienkoAleksandr Mar 26, 2026
b090485
feat(e2e): migrate bulk-import tests from rhdh repo
AndrienkoAleksandr Apr 19, 2026
b88389b
feat(e2e): fix org links
AndrienkoAleksandr Apr 19, 2026
9d60c54
feat(e2e): fix lint
AndrienkoAleksandr Apr 19, 2026
9380700
feat(e2e): fix failing e2e test
AndrienkoAleksandr Apr 19, 2026
1a69279
feat(e2e): fix failing test
AndrienkoAleksandr Apr 19, 2026
f2b5f55
feat(e2e): remove hard coded image
AndrienkoAleksandr Apr 19, 2026
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
1 change: 1 addition & 0 deletions workspaces/bulk-import/e2e-tests/.yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
3 changes: 3 additions & 0 deletions workspaces/bulk-import/e2e-tests/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createEslintConfig } from "@red-hat-developer-hub/e2e-test-utils/eslint";

export default createEslintConfig(import.meta.dirname);
37 changes: 37 additions & 0 deletions workspaces/bulk-import/e2e-tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "bulk-imprort-e2e-tests",
"version": "1.0.0",
"private": true,
"type": "module",
"engines": {
"node": ">=22",
"yarn": ">=3"
},
"packageManager": "yarn@3.8.7",
"description": "E2E tests for Bulk import plugin",
"scripts": {
"test": "CI=true playwright test",
"report": "playwright show-report",
"test:ui": "playwright test --ui",
"test:headed": "playwright test --headed",
"lint:check": "eslint .",
"lint:fix": "eslint . --fix",
"prettier:check": "prettier --check .",
"prettier:fix": "prettier --write .",
"check": "tsc --noEmit && yarn lint:check && yarn prettier:check",
"tsc:check": "tsc --noEmit"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@playwright/test": "^1.56.1",
"@red-hat-developer-hub/e2e-test-utils": "AndrienkoAleksandr/rhdh-e2e-test-utils#force-deploy",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndrienkoAleksandr please remember to use the recent npm version of the utils before it gets merged.

"@types/node": "^24.10.1",
"dotenv": "^16.4.7",
"eslint": "^9.39.2",
"eslint-plugin-check-file": "^3.3.1",
"eslint-plugin-playwright": "^2.4.0",
"prettier": "^3.7.4",
"typescript": "^5.9.3",
"typescript-eslint": "^8.50.0"
}
}
16 changes: 16 additions & 0 deletions workspaces/bulk-import/e2e-tests/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from "@red-hat-developer-hub/e2e-test-utils/playwright-config";
import dotenv from "dotenv";

dotenv.config({ path: `${import.meta.dirname}/.env` });
/**
* Bulk import plugin e2e test configuration.
* Extends the base config from rhdh-e2e-test-utils.
*/
export default defineConfig({
projects: [
{
name: "bulk-import",
timeout: 30 * 60 * 1000, // 30 min
},
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# rhdh app config file
# this file is used to merge with the default values of the rhdh app config

integrations:
github:
- host: github.com
token: ${VAULT_GITHUB_USER_TOKEN}
gitlab:
- host: gitlab.com
token: temp

bulkImport:
importAPI: orchestrator
orchestratorWorkflow: universal-pr
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# rhdh app config file
# this file is used to merge with the default values of the rhdh app config

catalog:
import:
entityFilename: catalog-info.yaml
pullRequestBranchName: backstage-integration
rules:
- allow: [Component, System, Group, User, Resource, Location, Template, API]
locations:
- type: url
target: https://github.com/janus-test/janus-test-3-bulk-import/blob/main/catalog-info.yaml

integrations:
github:
- host: github.com
token: ${VAULT_GITHUB_USER_TOKEN}
gitlab:
- host: gitlab.com
token: temp

permission:
enabled: true
rbac:
maxDepth: 1
policyFileReload: true
policies-csv-file: "./rbac/rbac-policy.csv"
pluginsWithPermission:
- catalog
- permission
- scaffolder
- bulk-import
# admin:
# users:
# todo remove it.
# - name: user:default/test1
policyDecisionPrecedence: conditional # default behavior
includeTransitiveGroupOwnership: true

bulkImport:
instructionsEnabled: true
instructionsDefaultExpanded: true
instructionsSteps:
- id: "step1"
text: "Choose a source control tool for pull request creation"
icon: "approval-tool"
- id: "step2"
text: "Choose which items you want to import"
icon: "search"
- id: "step3"
text: "Generate a catalog-info.yaml file for each selected item"
icon: "build"
- id: "step4"
text: "View the pull/merge request details"
icon: "kind:api"
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
plugins:
# bulk-import plugins
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic
disabled: false
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import
disabled: false
pluginConfig:
dynamicPlugins:
frontend:
red-hat-developer-hub.backstage-plugin-bulk-import:
appIcons:
- name: bulkImportIcon
importName: BulkImportIcon
dynamicRoutes:
- path: /bulk-import/repositories
importName: BulkImportPage
menuItem:
icon: bulkImportIcon
text: Bulk import

- package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-github-dynamic
disabled: true

# Group: Orchestrator
- package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator:bs_1.45.3__5.3.1"
disabled: false
pluginConfig:
dynamicPlugins:
frontend:
red-hat-developer-hub.backstage-plugin-orchestrator:
appIcons:
- importName: OrchestratorIcon
name: orchestratorIcon
dynamicRoutes:
- importName: OrchestratorPage
menuItem:
icon: orchestratorIcon
text: Orchestrator
textKey: menuItem.orchestrator
path: /orchestrator
entityTabs:
- path: /workflows
title: Workflows
titleKey: catalog.entityPage.workflows.title
mountPoint: entity.page.workflows
mountPoints:
- mountPoint: entity.page.workflows/cards
importName: OrchestratorCatalogTab
config:
layout:
gridColumn: 1 / -1
if:
anyOf:
- IsOrchestratorCatalogTabAvailable

- package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-backend:bs_1.45.3__8.5.1"
disabled: false
pluginConfig:
orchestrator:
dataIndexService:
url: http://sonataflow-platform-data-index-service.orchestrator.svc.cluster.local:80

- package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-scaffolder-backend-module-orchestrator:bs_1.45.3__1.3.1"
disabled: false
pluginConfig:
orchestrator:
dataIndexService:
url: http://sonataflow-platform-data-index-service.orchestrator.svc.cluster.local:80

- package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-form-widgets:bs_1.45.3__1.6.0"
disabled: false
pluginConfig:
dynamicPlugins:
frontend:
red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
plugins:
# bulk-import plugins
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic
disabled: false
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import
disabled: false
pluginConfig:
dynamicPlugins:
frontend:
red-hat-developer-hub.backstage-plugin-bulk-import:
appIcons:
- name: bulkImportIcon
importName: BulkImportIcon
dynamicRoutes:
- path: /bulk-import/repositories
importName: BulkImportPage
menuItem:
icon: bulkImportIcon
text: Bulk import

- package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-github-dynamic
disabled: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
kind: ConfigMap
apiVersion: v1
metadata:
name: rbac-policy
labels:
backstage.io/kubernetes-id: developer-hub
data:
rbac-policy.csv: |
g, user:default/test1, role:default/bulk-importer
p, role:default/bulk-importer, bulk-import, use, allow
p, role:default/bulk-importer, catalog-entity, read, allow
p, role:default/bulk-importer, catalog.entity.create, create, allow
p, role:default/bulk-importer, catalog.location.create, create, allow
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: rhdh-secrets
type: Opaque
stringData:
VAULT_GITHUB_USER_TOKEN: $VAULT_GH_RHDH_QE_USER_TOKEN
53 changes: 53 additions & 0 deletions workspaces/bulk-import/e2e-tests/tests/config/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
upstream:
backstage:
extraVolumeMounts:
# NOTE: Lists will not be merged with the default values file. So need to append all the defaults if you want to add a new item here
# See https://issues.redhat.com/browse/RHDHPLAN-869
# The initContainer below will install dynamic plugins in this volume mount.
- name: dynamic-plugins-root
mountPath: /opt/app-root/src/dynamic-plugins-root
- name: extensions-catalog
mountPath: /extensions
- name: temp
mountPath: /tmp
- name: rbac-policy
mountPath: /opt/app-root/src/rbac
extraVolumes:
# NOTE: Lists will not be merged with the default values file. So need to append all the defaults if you want to add a new item here
# See https://issues.redhat.com/browse/RHDHPLAN-869
# -- Ephemeral volume that will contain the dynamic plugins installed by the initContainer below at start.
# To have more control on underlying storage, the [emptyDir](https://docs.openshift.com/container-platform/4.13/storage/understanding-ephemeral-storage.html)
# could be changed to a [generic ephemeral volume](https://docs.openshift.com/container-platform/4.13/storage/generic-ephemeral-vols.html#generic-ephemeral-vols-procedure_generic-ephemeral-volumes).
- name: dynamic-plugins-root
emptyDir: {}
# Volume that will expose the `dynamic-plugins.yaml` file from the `dynamic-plugins` config map.
# The `dynamic-plugins` config map is created by the helm chart from the content of the `global.dynamic` field.
- name: dynamic-plugins
configMap:
defaultMode: 420
name: '{{ printf "%s-dynamic-plugins" .Release.Name }}'
optional: true
# Optional volume that allows exposing the `.npmrc` file (through a `dynamic-plugins-npmrc` secret)
# to be used when running `npm pack` during the dynamic plugins installation by the initContainer.
- name: dynamic-plugins-npmrc
secret:
defaultMode: 420
optional: true
secretName: '{{ printf "%s-dynamic-plugins-npmrc" .Release.Name }}'
- name: dynamic-plugins-registry-auth
secret:
defaultMode: 416
optional: true
secretName: '{{ printf "%s-dynamic-plugins-registry-auth" .Release.Name }}'
- name: npmcacache
emptyDir: {}
# -- Ephemeral volume used by the install-dynamic-plugins init container to extract catalog entities from the catalog index image.
# Mounted at the /extensions path in the backstage-backend main container for automatic discovery by the extension catalog backend providers.
- name: extensions-catalog
emptyDir: {}
- name: temp
emptyDir: {}
- name: rbac-policy
configMap:
defaultMode: 420
name: rbac-policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/bin/bash

export NAME_SPACE="${1:-${NAME_SPACE:-orchestrator}}"

escape_yq() {
local input="$1"
printf '%s' "$input" | sed 's/"/\\"/g'
return 0
}

wait_for_deployment() {
local namespace=$1 resource_name=$2 timeout_minutes=${3:-5} check_interval=${4:-10}
[[ -z "$namespace" || -z "$resource_name" ]] && { echo "wait_for_deployment: namespace and resource_name required"; return 1; }
local max_attempts=$((timeout_minutes * 60 / check_interval))
echo "Waiting for '$resource_name' in '$namespace' (timeout ${timeout_minutes}m)..."
for ((i = 1; i <= max_attempts; i++)); do
local pod_name
pod_name=$(oc get pods -n "$namespace" 2>/dev/null | grep "$resource_name" | awk '{print $1}' | head -n 1)
if [[ -n "$pod_name" ]]; then
local is_ready
is_ready=$(oc get pod "$pod_name" -n "$namespace" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null)
if [[ "$is_ready" == "True" ]] && oc get pod "$pod_name" -n "$namespace" 2>/dev/null | grep -q Running; then
echo "Pod '$pod_name' is ready"
return 0
fi
fi
sleep "$check_interval"
done
echo "Timeout waiting for $resource_name"
return 1
}

deploy_workflows() {

Check warning on line 33 in workspaces/bulk-import/e2e-tests/tests/scripts/install-workflow.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add an explicit return statement at the end of the function.

See more on https://sonarcloud.io/project/issues?id=redhat-developer_rhdh-plugin-export-overlays&issues=AZ0msIle0ICNWsss9ncI&open=AZ0msIle0ICNWsss9ncI&pullRequest=1972
local namespace=$1

local psql_secret_name psql_svc_name psql_user_key psql_password_key sonataflow_db
psql_secret_name=$(oc get secrets -n "$namespace" -o name 2>/dev/null | grep "backstage-psql" | grep "secret" | head -1 | sed 's|secret\/||')
psql_svc_name='backstage-psql'
psql_user_key="POSTGRES_USER"
psql_password_key="POSTGRES_PASSWORD"
sonataflow_db="backstage_plugin_orchestrator"

local workflow_repo="https://github.com/AndrienkoAleksandr/serverless-workflows.git"
local workflow_dir="/tmp/serverless-workflows"
local local_manifests="${SCRIPT_DIR}/yaml"

# Prefer local yaml/ if it exists and has content
if [[ -d "${local_manifests}" ]] && [[ -n "$(ls -A "${local_manifests}" 2>/dev/null)" ]]; then
echo "Using local workflow manifests from ${local_manifests}"
# Apply all YAMLs in yaml/ with correct namespace
for f in "${local_manifests}"/*.yaml "${local_manifests}"/*.yml; do
[[ -e "$f" ]] && oc apply -f "$f" -n "$namespace" && echo "Applied $(basename "$f")"
done
else
echo "Cloning workflow repo..."
rm -rf "${workflow_dir}"
git clone --single-branch --branch bulk-import-workflow-sample "${workflow_repo}" "${workflow_dir}"
local workflow_manifests="${workflow_dir}/workflows/bulk-import-git-repos/manifests"
if [[ -d "${workflow_manifests}" ]]; then
echo "Applying workflow manifests from repo..."

snToDbPatch="${workflow_manifests}/05-sonataflow_universal-pr.yaml"
yq eval -i '.spec.persistence.postgresql.secretRef.name = "'"$(escape_yq "$psql_secret_name")"'"' "$snToDbPatch"
yq eval -i '.spec.persistence.postgresql.secretRef.userKey = "'"$(escape_yq "$psql_user_key")"'"' "$snToDbPatch"
yq eval -i '.spec.persistence.postgresql.secretRef.passwordKey = "'"$(escape_yq "$psql_password_key")"'"' "$snToDbPatch"
yq eval -i '.spec.persistence.postgresql.serviceRef.name = "'"$(escape_yq "$psql_svc_name")"'"' "$snToDbPatch"
yq eval -i '.spec.persistence.postgresql.serviceRef.namespace = "'"$(escape_yq "$namespace")"'"' "$snToDbPatch"
yq eval -i '.spec.persistence.postgresql.serviceRef.databaseName = "'"$(escape_yq "$sonataflow_db")"'"' "$snToDbPatch"

oc apply -f "${workflow_manifests}" -n "$namespace"
else
echo "Manifests path not found in repo: ${workflow_manifests}"
fi
fi

echo "Waiting for SonataFlow resources..."
timeout 30s bash -c "
until [[ \$(oc get sf -n $namespace --no-headers 2>/dev/null | wc -l) -ge 1 ]]; do
echo \"Waiting for sf resources... \$(oc get sf -n $namespace --no-headers 2>/dev/null | wc -l)\"
sleep 5
done
"

wait_for_deployment "$namespace" universal-pr 5 || true
echo "Orchestrator workflows deployment done."
}

deploy_workflows ${NAME_SPACE}
Loading