diff --git a/.github/workflows/pr-semantic.yaml b/.github/workflows/pr-semantic.yaml index 801662905c..45254e2319 100644 --- a/.github/workflows/pr-semantic.yaml +++ b/.github/workflows/pr-semantic.yaml @@ -33,6 +33,7 @@ jobs: test revert requireScope: false + headerPattern: '^(?:\[release-\d+.\d+\] )?(\w*)(?:\(([\w$.\-* ]*)\))?: (.*)$' subjectPattern: ^(?![A-Z]).+$ subjectPatternError: | The subject "{subject}" found in the pull request title "{title}" diff --git a/.ibm/pipelines/auth/secrets-rhdh-secrets.yaml b/.ibm/pipelines/auth/secrets-rhdh-secrets.yaml index 51d9e76640..e434164ebe 100644 --- a/.ibm/pipelines/auth/secrets-rhdh-secrets.yaml +++ b/.ibm/pipelines/auth/secrets-rhdh-secrets.yaml @@ -32,4 +32,10 @@ data: DH_TARGET_URL: dGVzdC1iYWNrc3RhZ2UtY3VzdG9taXphdGlvbi1wcm92aWRlci1zaG93Y2FzZS1jaS5yaGRoLXByLW9zLWE5ODA1NjUwODMwYjIyYzNhZWUyNDNlNTFkNzk1NjVkLTAwMDAudXMtZWFzdC5jb250YWluZXJzLmFwcGRvbWFpbi5jbG91ZA== GOOGLE_CLIENT_ID: dGVtcA== GOOGLE_CLIENT_SECRET: dGVtcA== + RHDH_BASE_URL: dGVtcA== + KEYCLOAK_AUTH_BASE_URL: dGVtcA== + KEYCLOAK_AUTH_CLIENTID: dGVtcA== + KEYCLOAK_AUTH_CLIENT_SECRET: dGVtcA== + KEYCLOAK_AUTH_LOGIN_REALM: dGVtcA== + KEYCLOAK_AUTH_REALM: dGVtcA== type: Opaque diff --git a/.ibm/pipelines/cluster/aks/az.sh b/.ibm/pipelines/cluster/aks/az.sh index 7959e111b3..5d735f9920 100644 --- a/.ibm/pipelines/cluster/aks/az.sh +++ b/.ibm/pipelines/cluster/aks/az.sh @@ -20,7 +20,7 @@ az_aks_approuting_enable() { local resource_group=$2 set +xe local output=$(az aks approuting enable --name $name --resource-group $resource_group 2>&1 | sed 's/^ERROR: //') - set -xe + set -e exit_status=$? if [ $exit_status -ne 0 ]; then diff --git a/.ibm/pipelines/cluster/aks/deployment.sh b/.ibm/pipelines/cluster/aks/deployment.sh index 245ebe1106..0722c9bb81 100644 --- a/.ibm/pipelines/cluster/aks/deployment.sh +++ b/.ibm/pipelines/cluster/aks/deployment.sh @@ -6,7 +6,8 @@ initiate_aks_deployment() { # install_tekton_pipelines uninstall_helmchart "${NAME_SPACE_K8S}" "${RELEASE_NAME}" cd "${DIR}" - apply_yaml_files "${DIR}" "${NAME_SPACE_K8S}" + local rhdh_base_url="https://${K8S_CLUSTER_ROUTER_BASE}" + apply_yaml_files "${DIR}" "${NAME_SPACE_K8S}" "${rhdh_base_url}" yq_merge_value_files "${DIR}/value_files/${HELM_CHART_VALUE_FILE_NAME}" "${DIR}/value_files/${HELM_CHART_AKS_DIFF_VALUE_FILE_NAME}" "/tmp/${HELM_CHART_K8S_MERGED_VALUE_FILE_NAME}" mkdir -p "${ARTIFACT_DIR}/${NAME_SPACE_K8S}" cp -a "/tmp/${HELM_CHART_K8S_MERGED_VALUE_FILE_NAME}" "${ARTIFACT_DIR}/${NAME_SPACE_K8S}/" # Save the final value-file into the artifacts directory. @@ -26,7 +27,8 @@ initiate_rbac_aks_deployment() { # install_tekton_pipelines uninstall_helmchart "${NAME_SPACE_RBAC_K8S}" "${RELEASE_NAME_RBAC}" cd "${DIR}" - apply_yaml_files "${DIR}" "${NAME_SPACE_RBAC_K8S}" + local rbac_rhdh_base_url="https://${K8S_CLUSTER_ROUTER_BASE}" + apply_yaml_files "${DIR}" "${NAME_SPACE_RBAC_K8S}" "${rbac_rhdh_base_url}" yq_merge_value_files "${DIR}/value_files/${HELM_CHART_RBAC_VALUE_FILE_NAME}" "${DIR}/value_files/${HELM_CHART_RBAC_AKS_DIFF_VALUE_FILE_NAME}" "/tmp/${HELM_CHART_RBAC_K8S_MERGED_VALUE_FILE_NAME}" mkdir -p "${ARTIFACT_DIR}/${NAME_SPACE_RBAC_K8S}" cp -a "/tmp/${HELM_CHART_RBAC_K8S_MERGED_VALUE_FILE_NAME}" "${ARTIFACT_DIR}/${NAME_SPACE_RBAC_K8S}/" # Save the final value-file into the artifacts directory. diff --git a/.ibm/pipelines/cluster/gke/deployment.sh b/.ibm/pipelines/cluster/gke/deployment.sh index 99c910fd81..d01dea7566 100644 --- a/.ibm/pipelines/cluster/gke/deployment.sh +++ b/.ibm/pipelines/cluster/gke/deployment.sh @@ -7,7 +7,8 @@ initiate_gke_deployment() { # install_tekton_pipelines uninstall_helmchart "${NAME_SPACE_K8S}" "${RELEASE_NAME}" cd "${DIR}" - apply_yaml_files "${DIR}" "${NAME_SPACE_K8S}" + local rhdh_base_url="https://${K8S_CLUSTER_ROUTER_BASE}" + apply_yaml_files "${DIR}" "${NAME_SPACE_K8S}" "${rhdh_base_url}" oc apply -f "${DIR}/cluster/gke/frontend-config.yaml" --namespace="${project}" yq_merge_value_files "${DIR}/value_files/${HELM_CHART_VALUE_FILE_NAME}" "${DIR}/value_files/${HELM_CHART_GKE_DIFF_VALUE_FILE_NAME}" "/tmp/${HELM_CHART_K8S_MERGED_VALUE_FILE_NAME}" mkdir -p "${ARTIFACT_DIR}/${NAME_SPACE_K8S}" @@ -30,7 +31,8 @@ initiate_rbac_gke_deployment() { # install_tekton_pipelines uninstall_helmchart "${NAME_SPACE_RBAC_K8S}" "${RELEASE_NAME_RBAC}" cd "${DIR}" - apply_yaml_files "${DIR}" "${NAME_SPACE_RBAC_K8S}" + local rbac_rhdh_base_url="https://${K8S_CLUSTER_ROUTER_BASE}" + apply_yaml_files "${DIR}" "${NAME_SPACE_RBAC_K8S}" "${rbac_rhdh_base_url}" yq_merge_value_files "${DIR}/value_files/${HELM_CHART_RBAC_VALUE_FILE_NAME}" "${DIR}/value_files/${HELM_CHART_RBAC_GKE_DIFF_VALUE_FILE_NAME}" "/tmp/${HELM_CHART_RBAC_K8S_MERGED_VALUE_FILE_NAME}" mkdir -p "${ARTIFACT_DIR}/${NAME_SPACE_RBAC_K8S}" cp -a "/tmp/${HELM_CHART_RBAC_K8S_MERGED_VALUE_FILE_NAME}" "${ARTIFACT_DIR}/${NAME_SPACE_RBAC_K8S}/" # Save the final value-file into the artifacts directory. diff --git a/.ibm/pipelines/cluster/gke/gcloud.sh b/.ibm/pipelines/cluster/gke/gcloud.sh index 2c60f8240d..80bf4d9871 100755 --- a/.ibm/pipelines/cluster/gke/gcloud.sh +++ b/.ibm/pipelines/cluster/gke/gcloud.sh @@ -19,7 +19,7 @@ gcloud_ssl_cert_create() { # Capture both stdout and stderr set +xe local output=$(gcloud compute ssl-certificates create "${cert_name}" --domains="${domain}" --project="${project}" --global 2>&1) - set -xe + set -e # Check the return status if [ $? -eq 0 ]; then diff --git a/.ibm/pipelines/cluster/operators/acm/multiclusterhub.yaml b/.ibm/pipelines/cluster/operators/acm/multiclusterhub.yaml new file mode 100644 index 0000000000..fb1c6f0cf1 --- /dev/null +++ b/.ibm/pipelines/cluster/operators/acm/multiclusterhub.yaml @@ -0,0 +1,6 @@ +apiVersion: operator.open-cluster-management.io/v1 +kind: MultiClusterHub +metadata: + name: multiclusterhub + namespace: open-cluster-management +spec: {} diff --git a/.ibm/pipelines/cluster/operators/acm/operator-group.yaml b/.ibm/pipelines/cluster/operators/acm/operator-group.yaml new file mode 100644 index 0000000000..751af1094d --- /dev/null +++ b/.ibm/pipelines/cluster/operators/acm/operator-group.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: open-cluster-management +--- +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: open-cluster-management + namespace: open-cluster-management +spec: + targetNamespaces: + - open-cluster-management diff --git a/.ibm/pipelines/cluster/operators/acm/subscription-acm.yaml b/.ibm/pipelines/cluster/operators/acm/subscription-acm.yaml new file mode 100644 index 0000000000..d2322a6b52 --- /dev/null +++ b/.ibm/pipelines/cluster/operators/acm/subscription-acm.yaml @@ -0,0 +1,11 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: advanced-cluster-management + namespace: open-cluster-management +spec: + channel: release-2.12 + installPlanApproval: Automatic + name: advanced-cluster-management + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/.ibm/pipelines/env_variables.sh b/.ibm/pipelines/env_variables.sh index 924321e036..fa2248d32d 100755 --- a/.ibm/pipelines/env_variables.sh +++ b/.ibm/pipelines/env_variables.sh @@ -24,18 +24,10 @@ NAME_SPACE_RUNTIME="${NAME_SPACE_RUNTIME:-showcase-runtime}" NAME_SPACE_POSTGRES_DB="${NAME_SPACE_POSTGRES_DB:-postgress-external-db}" NAME_SPACE_RDS="showcase-rds-nightly" CHART_VERSION="2.15.2" -GITHUB_APP_APP_ID=$(cat /tmp/secrets/GITHUB_APP_APP_ID) -GITHUB_APP_CLIENT_ID=$(cat /tmp/secrets/GITHUB_APP_CLIENT_ID) -GITHUB_APP_PRIVATE_KEY=$(cat /tmp/secrets/GITHUB_APP_PRIVATE_KEY) -GITHUB_APP_CLIENT_SECRET=$(cat /tmp/secrets/GITHUB_APP_CLIENT_SECRET) -GITHUB_APP_2_APP_ID=$(cat /tmp/secrets/GITHUB_APP_2_APP_ID) -GITHUB_APP_2_CLIENT_ID=$(cat /tmp/secrets/GITHUB_APP_2_CLIENT_ID) -GITHUB_APP_2_PRIVATE_KEY=$(cat /tmp/secrets/GITHUB_APP_2_PRIVATE_KEY) -GITHUB_APP_2_CLIENT_SECRET=$(cat /tmp/secrets/GITHUB_APP_2_CLIENT_SECRET) -GITHUB_APP_3_APP_ID=$(cat /tmp/secrets/GITHUB_APP_3_APP_ID) -GITHUB_APP_3_CLIENT_ID=$(cat /tmp/secrets/GITHUB_APP_3_CLIENT_ID) -GITHUB_APP_3_PRIVATE_KEY=$(cat /tmp/secrets/GITHUB_APP_3_PRIVATE_KEY) -GITHUB_APP_3_CLIENT_SECRET=$(cat /tmp/secrets/GITHUB_APP_3_CLIENT_SECRET) +GITHUB_APP_APP_ID=$(cat /tmp/secrets/GITHUB_APP_3_APP_ID) +GITHUB_APP_CLIENT_ID=$(cat /tmp/secrets/GITHUB_APP_3_CLIENT_ID) +GITHUB_APP_PRIVATE_KEY=$(cat /tmp/secrets/GITHUB_APP_3_PRIVATE_KEY) +GITHUB_APP_CLIENT_SECRET=$(cat /tmp/secrets/GITHUB_APP_3_CLIENT_SECRET) GITHUB_APP_JANUS_TEST_APP_ID=OTE3NjM5 GITHUB_APP_JANUS_TEST_CLIENT_ID=SXYyM2xpSEdtU1l6SUFEbHFIakw= GITHUB_APP_JANUS_TEST_PRIVATE_KEY=$(cat /tmp/secrets/GITHUB_APP_JANUS_TEST_PRIVATE_KEY) @@ -57,6 +49,7 @@ GITLAB_TOKEN=$(cat /tmp/secrets/GITLAB_TOKEN) RHDH_PR_OS_CLUSTER_URL=$(cat /tmp/secrets/RHDH_PR_OS_CLUSTER_URL) RHDH_PR_OS_CLUSTER_TOKEN=$(cat /tmp/secrets/RHDH_PR_OS_CLUSTER_TOKEN) +ENCODED_CLUSTER_NAME=$(echo "my-cluster" | base64) K8S_CLUSTER_API_SERVER_URL=$(printf "%s" "$K8S_CLUSTER_URL" | base64 | tr -d '\n') K8S_SERVICE_ACCOUNT_TOKEN=$K8S_CLUSTER_TOKEN_ENCODED OCM_CLUSTER_URL=$(printf "%s" "$K8S_CLUSTER_URL" | base64 | tr -d '\n') @@ -136,4 +129,10 @@ AUTH_PROVIDERS_NAMESPACE="showcase-auth-providers" STATIC_API_TOKEN="somecicdtoken" AUTH_PROVIDERS_CHART="rhdh-chart/backstage" +KEYCLOAK_AUTH_BASE_URL=$(cat /tmp/secrets/KEYCLOAK_AUTH_BASE_URL) +KEYCLOAK_AUTH_CLIENTID=$(cat /tmp/secrets/KEYCLOAK_AUTH_CLIENTID) +KEYCLOAK_AUTH_CLIENT_SECRET=$(cat /tmp/secrets/KEYCLOAK_AUTH_CLIENT_SECRET) +KEYCLOAK_AUTH_LOGIN_REALM=$(cat /tmp/secrets/KEYCLOAK_AUTH_LOGIN_REALM) +KEYCLOAK_AUTH_REALM=$(cat /tmp/secrets/KEYCLOAK_AUTH_REALM) + set +a # Stop automatically exporting variables diff --git a/.ibm/pipelines/jobs/aks.sh b/.ibm/pipelines/jobs/aks.sh index ddf4d33c56..e6c934d264 100644 --- a/.ibm/pipelines/jobs/aks.sh +++ b/.ibm/pipelines/jobs/aks.sh @@ -17,13 +17,12 @@ handle_aks() { az_aks_approuting_enable "${AKS_NIGHTLY_CLUSTER_NAME}" "${AKS_NIGHTLY_CLUSTER_RESOURCEGROUP}" az_aks_get_credentials "${AKS_NIGHTLY_CLUSTER_NAME}" "${AKS_NIGHTLY_CLUSTER_RESOURCEGROUP}" - set_github_app_3_credentials - initiate_aks_deployment check_and_test "${RELEASE_NAME}" "${NAME_SPACE_K8S}" "${url}" delete_namespace "${NAME_SPACE_K8S}" initiate_rbac_aks_deployment - check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC_K8S}" + local rbac_rhdh_base_url="https://${K8S_CLUSTER_ROUTER_BASE}" + check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC_K8S}" "${rbac_rhdh_base_url}" delete_namespace "${NAME_SPACE_RBAC_K8S}" } diff --git a/.ibm/pipelines/jobs/gke.sh b/.ibm/pipelines/jobs/gke.sh index 71e8de697c..8fe93a782a 100644 --- a/.ibm/pipelines/jobs/gke.sh +++ b/.ibm/pipelines/jobs/gke.sh @@ -12,13 +12,12 @@ handle_gke() { gcloud_auth "${GKE_SERVICE_ACCOUNT_NAME}" "/tmp/secrets/GKE_SERVICE_ACCOUNT_KEY" gcloud_gke_get_credentials "${GKE_CLUSTER_NAME}" "${GKE_CLUSTER_REGION}" "${GOOGLE_CLOUD_PROJECT}" - set_github_app_3_credentials - initiate_gke_deployment check_and_test "${RELEASE_NAME}" "${NAME_SPACE_K8S}" "${url}" delete_namespace "${NAME_SPACE_K8S}" initiate_rbac_gke_deployment - check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC_K8S}" + local rbac_rhdh_base_url="https://${K8S_CLUSTER_ROUTER_BASE}" + check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC_K8S}" "${rbac_rhdh_base_url}" delete_namespace "${NAME_SPACE_RBAC_K8S}" } diff --git a/.ibm/pipelines/jobs/main.sh b/.ibm/pipelines/jobs/main.sh index 027252e91d..f37b47e367 100644 --- a/.ibm/pipelines/jobs/main.sh +++ b/.ibm/pipelines/jobs/main.sh @@ -1,42 +1,16 @@ #!/bin/sh -set -x - -set_namespace() { - # Enable parallel PR testing for main branch by utilizing a pool of namespaces - local namespaces_pool=("pr-1" "pr-2" "pr-3") - local namespace_found=false - # Iterate through namespace pool to find an available set - for ns in "${namespaces_pool[@]}"; do - if ! oc get namespace "showcase-$ns" >/dev/null 2>&1; then - echo "Namespace "showcase-$ns" does not exist, Using NS: showcase-$ns, showcase-rbac-$ns, postgress-external-db-$ns" - export NAME_SPACE="showcase-$ns" - export NAME_SPACE_RBAC="showcase-rbac-$ns" - export NAME_SPACE_POSTGRES_DB="postgress-external-db-$ns" - namespace_found=true - break - fi - done - if ! $namespace_found; then - echo "Error: All namespaces $namespaces_pool already in Use" - exit 1 - fi -} - handle_main() { echo "Configuring namespace: ${NAME_SPACE}" - set_github_app_4_credentials - set_namespace oc_login - - API_SERVER_URL=$(oc whoami --show-server) - ENCODED_API_SERVER_URL=$(echo "${API_SERVER_URL}" | base64) - ENCODED_CLUSTER_NAME=$(echo "my-cluster" | base64) + echo "OCP version: $(oc version)" export K8S_CLUSTER_ROUTER_BASE=$(oc get route console -n openshift-console -o=jsonpath='{.spec.host}' | sed 's/^[^.]*\.//') - local url="https://${RELEASE_NAME}-backstage-${NAME_SPACE}.${K8S_CLUSTER_ROUTER_BASE}" + cluster_setup initiate_deployments deploy_test_backstage_provider "${NAME_SPACE}" + local url="https://${RELEASE_NAME}-backstage-${NAME_SPACE}.${K8S_CLUSTER_ROUTER_BASE}" check_and_test "${RELEASE_NAME}" "${NAME_SPACE}" "${url}" - check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC}" "${url}" + local rbac_url="https://${RELEASE_NAME_RBAC}-backstage-${NAME_SPACE_RBAC}.${K8S_CLUSTER_ROUTER_BASE}" + check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC}" "${rbac_url}" } diff --git a/.ibm/pipelines/jobs/ocp-v4-15.sh b/.ibm/pipelines/jobs/ocp-v4-15.sh deleted file mode 100644 index 7bc8d941c1..0000000000 --- a/.ibm/pipelines/jobs/ocp-v4-15.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -handle_ocp_4_15() { - K8S_CLUSTER_URL=$(cat /tmp/secrets/RHDH_OS_2_CLUSTER_URL) - K8S_CLUSTER_TOKEN=$(cat /tmp/secrets/RHDH_OS_2_CLUSTER_TOKEN) - - oc_login - - API_SERVER_URL=$(oc whoami --show-server) - ENCODED_API_SERVER_URL=$(echo "${API_SERVER_URL}" | base64) - ENCODED_CLUSTER_NAME=$(echo "my-cluster" | base64) - - export K8S_CLUSTER_ROUTER_BASE=$(oc get route console -n openshift-console -o=jsonpath='{.spec.host}' | sed 's/^[^.]*\.//') - apply_yaml_files "${DIR}" "${NAME_SPACE}" - deploy_test_backstage_provider "${NAME_SPACE}" - local url="https://${release_name}-backstage-${namespace}.${K8S_CLUSTER_ROUTER_BASE}" - - initiate_deployments - check_and_test "${RELEASE_NAME}" "${NAME_SPACE}" "${url}" - check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC}" "${url}" -} diff --git a/.ibm/pipelines/jobs/ocp-v4-16.sh b/.ibm/pipelines/jobs/ocp-v4-16.sh deleted file mode 100644 index c8c8f8ed4f..0000000000 --- a/.ibm/pipelines/jobs/ocp-v4-16.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -handle_ocp_4_16() { - K8S_CLUSTER_URL=$(cat /tmp/secrets/RHDH_OS_1_CLUSTER_URL) - K8S_CLUSTER_TOKEN=$(cat /tmp/secrets/RHDH_OS_1_CLUSTER_TOKEN) - - oc_login - - API_SERVER_URL=$(oc whoami --show-server) - ENCODED_API_SERVER_URL=$(echo "${API_SERVER_URL}" | base64) - ENCODED_CLUSTER_NAME=$(echo "my-cluster" | base64) - - export K8S_CLUSTER_ROUTER_BASE=$(oc get route console -n openshift-console -o=jsonpath='{.spec.host}' | sed 's/^[^.]*\.//') - apply_yaml_files "${DIR}" "${NAME_SPACE}" - deploy_test_backstage_provider "${NAME_SPACE}" - local url="https://${release_name}-backstage-${namespace}.${K8S_CLUSTER_ROUTER_BASE}" - - initiate_deployments - check_and_test "${RELEASE_NAME}" "${NAME_SPACE}" "${url}" - check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC}" "${url}" -} diff --git a/.ibm/pipelines/jobs/operator.sh b/.ibm/pipelines/jobs/operator.sh index 58b70c0442..ed0095a32d 100644 --- a/.ibm/pipelines/jobs/operator.sh +++ b/.ibm/pipelines/jobs/operator.sh @@ -3,10 +3,6 @@ handle_operator() { oc_login - API_SERVER_URL=$(oc whoami --show-server) - ENCODED_API_SERVER_URL=$(echo "${API_SERVER_URL}" | base64) - ENCODED_CLUSTER_NAME=$(echo "my-cluster" | base64) - apply_yaml_files "${DIR}" "${NAME_SPACE}" deploy_test_backstage_provider "${NAME_SPACE}" } diff --git a/.ibm/pipelines/jobs/periodic.sh b/.ibm/pipelines/jobs/periodic.sh index 921eb7fb20..99ab74d95e 100644 --- a/.ibm/pipelines/jobs/periodic.sh +++ b/.ibm/pipelines/jobs/periodic.sh @@ -1,40 +1,32 @@ #!/bin/sh handle_nightly() { - export NAME_SPACE="showcase-ci-nightly" - export NAME_SPACE_RBAC="showcase-rbac-nightly" - export NAME_SPACE_POSTGRES_DB="postgress-external-db-nightly" - export NAME_SPACE_K8S="showcase-k8s-ci-nightly" - export NAME_SPACE_RBAC_K8S="showcase-rbac-k8s-ci-nightly" - oc_login - API_SERVER_URL=$(oc whoami --show-server) - ENCODED_API_SERVER_URL=$(echo "${API_SERVER_URL}" | base64) - ENCODED_CLUSTER_NAME=$(echo "my-cluster" | base64) - export K8S_CLUSTER_ROUTER_BASE=$(oc get route console -n openshift-console -o=jsonpath='{.spec.host}' | sed 's/^[^.]*\.//') configure_namespace "${NAME_SPACE}" deploy_test_backstage_provider "${NAME_SPACE}" - local url="https://${RELEASE_NAME}-backstage-${NAME_SPACE}.${K8S_CLUSTER_ROUTER_BASE}" - install_pipelines_operator - sleep 20 # wait for Pipeline Operator/Tekton pipelines to be ready - oc apply -f "$dir/resources/pipeline-run/hello-world-pipeline.yaml" - oc apply -f "$dir/resources/pipeline-run/hello-world-pipeline-run.yaml" + cluster_setup initiate_deployments + local url="https://${RELEASE_NAME}-backstage-${NAME_SPACE}.${K8S_CLUSTER_ROUTER_BASE}" check_and_test "${RELEASE_NAME}" "${NAME_SPACE}" "${url}" - check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC}" "${url}" + local rbac_url="https://${RELEASE_NAME_RBAC}-backstage-${NAME_SPACE_RBAC}.${K8S_CLUSTER_ROUTER_BASE}" + check_and_test "${RELEASE_NAME_RBAC}" "${NAME_SPACE_RBAC}" "${rbac_url}" # Only test TLS config with RDS and Change configuration at runtime in nightly jobs initiate_rds_deployment "${RELEASE_NAME}" "${NAME_SPACE_RDS}" - check_and_test "${RELEASE_NAME}" "${NAME_SPACE_RDS}" "${url}" + local rds_url="https://${RELEASE_NAME}-backstage-${NAME_SPACE_RDS}.${K8S_CLUSTER_ROUTER_BASE}" + check_and_test "${RELEASE_NAME}" "${NAME_SPACE_RDS}" "${rds_url}" # Deploy `showcase-runtime` to run tests that require configuration changes at runtime configure_namespace "${NAME_SPACE_RUNTIME}" uninstall_helmchart "${NAME_SPACE_RUNTIME}" "${RELEASE_NAME}" oc apply -f "$DIR/resources/redis-cache/redis-deployment.yaml" --namespace="${NAME_SPACE_RUNTIME}" - apply_yaml_files "${DIR}" "${NAME_SPACE_RUNTIME}" + + local runtime_url="https://${RELEASE_NAME}-backstage-${NAME_SPACE_RUNTIME}.${K8S_CLUSTER_ROUTER_BASE}" + + apply_yaml_files "${DIR}" "${NAME_SPACE_RUNTIME}" "${runtime_url}" helm upgrade -i "${RELEASE_NAME}" -n "${NAME_SPACE_RUNTIME}" "${HELM_REPO_NAME}/${HELM_IMAGE_NAME}" --version "${CHART_VERSION}" -f "${DIR}/value_files/${HELM_CHART_VALUE_FILE_NAME}" --set global.clusterRouterBase="${K8S_CLUSTER_ROUTER_BASE}" --set upstream.backstage.image.repository="${QUAY_REPO}" --set upstream.backstage.image.tag="${TAG_NAME}" - check_and_test "${RELEASE_NAME}" "${NAME_SPACE_RUNTIME}" "${url}" + check_and_test "${RELEASE_NAME}" "${NAME_SPACE_RUNTIME}" "${runtime_url}" } diff --git a/.ibm/pipelines/ocp-cluster-claim-login.sh b/.ibm/pipelines/ocp-cluster-claim-login.sh new file mode 100755 index 0000000000..9f54d2a572 --- /dev/null +++ b/.ibm/pipelines/ocp-cluster-claim-login.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# Prompt the user for the prow log url +read -p "Enter the prow log url: " input_url + +id=$(echo "$input_url" | awk -F'/' '{print $NF}') +job=$(echo "$input_url" | awk -F'/' '{print $(NF-1)}') + +build_log_url="https://prow.ci.openshift.org/log?container=test&id=${id}&job=${job}" +namespace=$(curl -s $build_log_url | grep "The claimed cluster" | sed -E 's/.*The claimed cluster ([^.]+)\ is ready after.*/\1/') + +# Output the constructed URL +echo "Prow build log URL: $build_log_url" +echo "hosted-mgmt Namespace: $namespace" + +if [[ -z "$namespace" ]]; then + echo "Cluster claim not found. Please provide a valid prow url that uses cluster claim." + exit 1 +elif [[ ! "$namespace" =~ ^rhdh-4-17-us-east-2 ]]; then + echo "Namespace must start with 'rhdh-4-17-us-east-2'." + exit 1 +fi + +# Log in to the cluster +oc login --web https://api.hosted-mgmt.ci.devcluster.openshift.com:6443 + +if ! oc get namespace "$namespace" >/dev/null 2>&1; then + echo "Namespace ${namespace} is expired or deleted, exiting..." + exit 1 +fi + +# Try to retrieve secrets from the namespace +namespace_secrets=$(oc get secrets -n "$namespace" 2>&1) +if echo "$namespace_secrets" | grep -q "Forbidden"; then + echo "Error: You do not have access to the namespace '$namespace'." + echo "check if you are member of 'rhdh-pool-admins' group at: https://rover.redhat.com/groups/search?q=rhdh-pool-admins" + echo "Please reach out to the rhdh-qe team for assistance." + exit 1 +fi + +cluster_secret=$(oc get secrets -n "$namespace" | grep admin-password | awk '{print $1}') +# Retrieve the kubeadmin password from the specified namespace +password=$(oc get secret $cluster_secret -n "$namespace" -o jsonpath='{.data.password}' | base64 -d) + +# Log out from the current session +oc logout + +# Log in to the namespace-specific cluster +oc login https://api."$namespace".rhdh-qe.devcluster.openshift.com:6443 --username kubeadmin --password "$password" --insecure-skip-tls-verify=true +oc project showcase + +# Prompt the user to open the web console +read -p "Do you want to open the OpenShift web console? (y/n): " open_console + +if [[ "$open_console" == "y" || "$open_console" == "Y" ]]; then + + console_url="https://console-openshift-console.apps.${namespace}.rhdh-qe.devcluster.openshift.com/dashboards" + + echo "Opening web console at $console_url..." + echo "Use bellow user and password to login into web console:" + echo "Username: kubeadmin" + echo "Password: $password" + echo "Password copied to clipboard" + echo $password | pbcopy + sleep 3 + + # Attempt to open the web console in the default browser + if command -v xdg-open &> /dev/null; then + xdg-open "$console_url" # For Linux systems + elif command -v open &> /dev/null; then + open "$console_url" # For macOS + else + echo "Unable to detect a browser. Please open the following URL manually:" + echo "$console_url" + fi +else + echo "Web console not opened." +fi \ No newline at end of file diff --git a/.ibm/pipelines/openshift-ci-tests.sh b/.ibm/pipelines/openshift-ci-tests.sh index ca3dfcd8e3..0c4c9a3c48 100755 --- a/.ibm/pipelines/openshift-ci-tests.sh +++ b/.ibm/pipelines/openshift-ci-tests.sh @@ -1,49 +1,37 @@ #!/bin/sh -set -xe +set -e export PS4='[$(date "+%Y-%m-%d %H:%M:%S")] ' # logs timestamp for every cmd. LOGFILE="test-log" export DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -secret_name="rhdh-k8s-plugin-secret" OVERALL_RESULT=0 cleanup() { echo "Cleaning up before exiting" if [[ "$JOB_NAME" == *aks* ]]; then az_aks_stop "${AKS_NIGHTLY_CLUSTER_NAME}" "${AKS_NIGHTLY_CLUSTER_RESOURCEGROUP}" - elif [[ "$JOB_NAME" == *pull-*-main-e2e-tests* ]]; then - # Cleanup namespaces after main branch PR e2e tests execution. - delete_namespace "${NAME_SPACE}" - delete_namespace "${NAME_SPACE_POSTGRES_DB}" - delete_namespace "${NAME_SPACE_RBAC}" fi rm -rf ~/tmpbin } trap cleanup EXIT INT ERR -export K8S_CLUSTER_URL=$(cat /tmp/secrets/RHDH_PR_OS_CLUSTER_URL) -export K8S_CLUSTER_TOKEN=$(cat /tmp/secrets/RHDH_PR_OS_CLUSTER_TOKEN) +SCRIPTS=( + "env_variables.sh" + "utils.sh" + "jobs/aks.sh" + "jobs/gke.sh" + "jobs/main.sh" + "jobs/operator.sh" + "jobs/periodic.sh" +) -source "${DIR}/env_variables.sh" -echo "Loaded env_variables.sh" -source "${DIR}/utils.sh" -echo "Loaded utils.sh" -source "${DIR}/jobs/aks.sh" -echo "Loaded aks.sh" -source "${DIR}/jobs/gke.sh" -echo "Loaded gke.sh" -source "${DIR}/jobs/main.sh" -echo "Loaded main.sh" -source "${DIR}/jobs/ocp-v4-15.sh" -echo "Loaded ocp-v4-15.sh" -source "${DIR}/jobs/ocp-v4-16.sh" -echo "Loaded ocp-v4-16.sh" -source "${DIR}/jobs/operator.sh" -echo "Loaded operator.sh" -source "${DIR}/jobs/periodic.sh" -echo "Loaded periodic.sh" +# Source each script dynamically +for SCRIPT in "${SCRIPTS[@]}"; do + source "${DIR}/${SCRIPT}" + echo "Loaded ${SCRIPT}" +done main() { echo "Log file: ${LOGFILE}" @@ -58,29 +46,20 @@ main() { echo "Calling handle_gke" handle_gke ;; + *operator*) + echo "Calling Operator" + handle_operator + ;; *periodic*) echo "Calling handle_periodic" handle_nightly ;; - *pull-*-main-e2e-tests*) + *pull*) echo "Calling handle_main" handle_main ;; - *ocp-v4-16*) - echo "Calling handle_ocp_v4_16" - handle_ocp_v4_16 - ;; - *ocp-v4-15*) - echo "Calling handle_ocp_v4_15" - handle_ocp_v4_15 - ;; - *operator*) - echo "Calling Operator" - handle_operator - ;; esac -echo "K8S_CLUSTER_ROUTER_BASE : $K8S_CLUSTER_ROUTER_BASE" echo "Main script completed with result: ${OVERALL_RESULT}" exit "${OVERALL_RESULT}" diff --git a/.ibm/pipelines/resources/cluster_role_binding/cluster-role-binding-ocm.yaml b/.ibm/pipelines/resources/cluster_role_binding/cluster-role-binding-ocm.yaml index cb8631b32c..8cd8b1525d 100644 --- a/.ibm/pipelines/resources/cluster_role_binding/cluster-role-binding-ocm.yaml +++ b/.ibm/pipelines/resources/cluster_role_binding/cluster-role-binding-ocm.yaml @@ -10,34 +10,6 @@ subjects: - kind: ServiceAccount name: rhdh-k8s-plugin namespace: showcase - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-rbac - kind: ServiceAccount name: rhdh-k8s-plugin namespace: showcase-ci-nightly - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-rbac-nightly - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-1-2-x - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-rbac-1-2-x - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-1-3-x - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-rbac-1-3-x - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-pr-1 - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-pr-2 - - kind: ServiceAccount - name: rhdh-k8s-plugin - namespace: showcase-pr-3 - diff --git a/.ibm/pipelines/resources/config_map/app-config-rhdh-rbac.yaml b/.ibm/pipelines/resources/config_map/app-config-rhdh-rbac.yaml index 70e61421b9..b951b8b68b 100644 --- a/.ibm/pipelines/resources/config_map/app-config-rhdh-rbac.yaml +++ b/.ibm/pipelines/resources/config_map/app-config-rhdh-rbac.yaml @@ -25,18 +25,25 @@ integrations: - host: gitlab.com token: temp auth: - # see https://backstage.io/docs/auth/ to learn about auth providers environment: development + session: + secret: superSecretSecret providers: - # Plugin: GitHub - github: - development: - clientId: ${GITHUB_APP_CLIENT_ID} - clientSecret: ${GITHUB_APP_CLIENT_SECRET} + guest: + dangerouslyAllowOutsideDevelopment: true google: development: clientId: ${GOOGLE_CLIENT_ID} clientSecret: ${GOOGLE_CLIENT_SECRET} + oidc: + development: + metadataUrl: ${KEYCLOAK_AUTH_BASE_URL}/auth/realms/${KEYCLOAK_AUTH_REALM} + clientId: ${KEYCLOAK_AUTH_CLIENTID} + clientSecret: ${KEYCLOAK_AUTH_CLIENT_SECRET} + prompt: auto + callbackUrl: ${RHDH_BASE_URL}/api/auth/oidc/handler/frame + +signInPage: oidc proxy: skipInvalidProxies: true # endpoints: {} @@ -79,6 +86,16 @@ catalog: rules: - allow: [User, Group] providers: + keycloakOrg: + default: + baseUrl: ${KEYCLOAK_AUTH_BASE_URL}/auth + loginRealm: ${KEYCLOAK_AUTH_LOGIN_REALM} + realm: ${KEYCLOAK_AUTH_REALM} + clientId: ${KEYCLOAK_AUTH_CLIENTID} + clientSecret: ${KEYCLOAK_AUTH_CLIENT_SECRET} + schedule: + frequency: { minutes: 1 } + timeout: { minutes: 1 } githubOrg: id: production githubUrl: "${GITHUB_URL}" diff --git a/.ibm/pipelines/resources/config_map/app-config-rhdh.yaml b/.ibm/pipelines/resources/config_map/app-config-rhdh.yaml index ad49b0850c..a2a5411046 100644 --- a/.ibm/pipelines/resources/config_map/app-config-rhdh.yaml +++ b/.ibm/pipelines/resources/config_map/app-config-rhdh.yaml @@ -14,44 +14,6 @@ app: headerColor1: 'rgb(190, 122, 45)' headerColor2: 'rgb(45, 190, 50)' navigationIndicatorColor: 'rgb(45, 113, 190)' -dynamicPlugins: - rootDirectory: dynamic-plugins-root - frontend: - default.main-menu-items: - menuItems: - default.list: - title: References - icon: bookmarks - default.apis: - parent: default.list - default.learning-path: - parent: default.list - backstage.plugin-techdocs: - menuItems: - favorites: - title: Favorites - icon: star - priority: 10 - docs: - parent: favorites - priority: 1 - pataknight.backstage-plugin-rhdh-qe-theme: - appIcons: - - importName: LightIcon - name: lightIcon - - importName: DarkIcon - name: darkIcon - themes: - - icon: lightIcon - id: light-dynamic - importName: lightThemeProvider - title: Light Dynamic - variant: light - - icon: darkIcon - id: dark-dynamic - importName: darkThemeProvider - title: Dark Dynamic - variant: dark backend: reading: allow: @@ -92,19 +54,25 @@ integrations: - host: gitlab.com token: ${GITLAB_TOKEN} auth: + # see https://backstage.io/docs/auth/ to learn about auth providers environment: development + session: + secret: superSecretSecret providers: guest: dangerouslyAllowOutsideDevelopment: true - # Plugin: GitHub - github: - development: - clientId: ${GITHUB_APP_CLIENT_ID} - clientSecret: ${GITHUB_APP_CLIENT_SECRET} google: development: clientId: ${GOOGLE_CLIENT_ID} clientSecret: ${GOOGLE_CLIENT_SECRET} + oidc: + development: + metadataUrl: ${KEYCLOAK_AUTH_BASE_URL}/auth/realms/${KEYCLOAK_AUTH_REALM} + clientId: ${KEYCLOAK_AUTH_CLIENTID} + clientSecret: ${KEYCLOAK_AUTH_CLIENT_SECRET} + prompt: auto + callbackUrl: ${RHDH_BASE_URL}/api/auth/oidc/handler/frame +signInPage: oidc techRadar: url: "http://${DH_TARGET_URL}/tech-radar" proxy: @@ -149,6 +117,16 @@ catalog: rules: - allow: [User, Group] providers: + keycloakOrg: + default: + baseUrl: ${KEYCLOAK_AUTH_BASE_URL}/auth + loginRealm: ${KEYCLOAK_AUTH_LOGIN_REALM} + realm: ${KEYCLOAK_AUTH_REALM} + clientId: ${KEYCLOAK_AUTH_CLIENTID} + clientSecret: ${KEYCLOAK_AUTH_CLIENT_SECRET} + schedule: + frequency: { minutes: 1 } + timeout: { minutes: 1 } github: providerId: organization: '${GITHUB_ORG}' diff --git a/.ibm/pipelines/resources/config_map/dynamic-homepage-and-sidebar-config.yaml b/.ibm/pipelines/resources/config_map/dynamic-homepage-and-sidebar-config.yaml new file mode 100644 index 0000000000..1abff694d5 --- /dev/null +++ b/.ibm/pipelines/resources/config_map/dynamic-homepage-and-sidebar-config.yaml @@ -0,0 +1,180 @@ +dynamicPlugins: + rootDirectory: dynamic-plugins-root + frontend: + red-hat-developer-hub.backstage-plugin-dynamic-home-page: + mountPoints: + - mountPoint: home.page/cards + importName: SearchBar + config: + layouts: + xl: { w: 10, h: 1, x: 1 } + lg: { w: 10, h: 1, x: 1 } + md: { w: 10, h: 1, x: 1 } + sm: { w: 10, h: 1, x: 1 } + xs: { w: 12, h: 1 } + xxs: { w: 12, h: 1 } + - mountPoint: home.page/cards + importName: QuickAccessCard + config: + layouts: + xl: { w: 7, h: 8 } + lg: { w: 7, h: 8 } + md: { w: 7, h: 8 } + sm: { w: 12, h: 8 } + xs: { w: 12, h: 8 } + xxs: { w: 12, h: 8 } + - mountPoint: home.page/cards + importName: CatalogStarredEntitiesCard + config: + layouts: + xl: { w: 5, h: 4, x: 7 } + lg: { w: 5, h: 4, x: 7 } + md: { w: 5, h: 4, x: 7 } + sm: { w: 12, h: 4 } + xs: { w: 12, h: 4 } + xxs: { w: 12, h: 4 } + + - mountPoint: home.page/cards + importName: Headline + config: + layouts: + xl: { w: 12, h: 1 } + lg: { w: 12, h: 1 } + md: { w: 12, h: 1 } + sm: { w: 12, h: 1 } + xs: { w: 12, h: 1 } + xxs: { w: 12, h: 1 } + props: + title: Placeholder tests + align: center + + - mountPoint: home.page/cards + importName: Placeholder + config: + layouts: + xl: { x: 1, y: 0, w: 10, h: 1 } + lg: { x: 1, y: 0, w: 10, h: 1 } + md: { x: 1, y: 0, w: 10, h: 1 } + sm: { x: 0, y: 0, w: 12, h: 1 } + xs: { x: 0, y: 0, w: 12, h: 1 } + xxs: { x: 0, y: 0, w: 12, h: 1 } + props: + showBorder: true + debugContent: Home page customization test 1 + - mountPoint: home.page/cards + importName: Placeholder + config: + layouts: + xl: { x: 0, y: 0, w: 7, h: 4 } + lg: { x: 0, y: 0, w: 7, h: 4 } + md: { x: 0, y: 0, w: 7, h: 4 } + sm: { x: 0, y: 0, w: 12, h: 4 } + xs: { x: 0, y: 0, w: 12, h: 4 } + xxs: { x: 0, y: 0, w: 12, h: 4 } + props: + showBorder: true + debugContent: Home page customization test 2 + - mountPoint: home.page/cards + importName: Placeholder + config: + layouts: + xl: { x: 7, y: 0, w: 5, h: 4 } + lg: { x: 7, y: 0, w: 5, h: 4 } + md: { x: 7, y: 0, w: 5, h: 4 } + sm: { x: 0, y: 0, w: 12, h: 4 } + xs: { x: 0, y: 0, w: 12, h: 4 } + xxs: { x: 0, y: 0, w: 12, h: 4 } + props: + showBorder: true + debugContent: Home page customization test 3 + - mountPoint: home.page/cards + importName: Headline + config: + layouts: + xl: { w: 12, h: 1 } + lg: { w: 12, h: 1 } + md: { w: 12, h: 1 } + sm: { w: 12, h: 1 } + xs: { w: 12, h: 1 } + xxs: { w: 12, h: 1 } + props: + title: Markdown tests + align: center + + - mountPoint: home.page/cards + importName: MarkdownCard + config: + layouts: + xl: { w: 6, h: 4 } + lg: { w: 6, h: 4 } + md: { w: 6, h: 4 } + sm: { w: 6, h: 4 } + xs: { w: 6, h: 4 } + xxs: { w: 6, h: 4 } + props: + title: Company links + content: | + ### RHDH + + * [Website](https://developers.redhat.com/rhdh/overview) + * [Documentation](https://docs.redhat.com/en/documentation/red_hat_developer_hub/) + * [GitHub Showcase](https://github.com/janus-idp/backstage-showcase) + * [GitHub Plugins](https://github.com/janus-idp/backstage-plugins) + - mountPoint: home.page/cards + importName: Markdown + config: + layouts: + xl: { w: 6, h: 4, x: 6 } + lg: { w: 6, h: 4, x: 6 } + md: { w: 6, h: 4, x: 6 } + sm: { w: 6, h: 4, x: 6 } + xs: { w: 6, h: 4, x: 6 } + xxs: { w: 6, h: 4, x: 6 } + props: + title: Important company links + content: | + ### RHDH + + * [Website](https://developers.redhat.com/rhdh/overview) + * [Documentation](https://docs.redhat.com/en/documentation/red_hat_developer_hub/) + * [GitHub Showcase](https://github.com/janus-idp/backstage-showcase) + * [GitHub Plugins](https://github.com/janus-idp/backstage-plugins) + - mountPoint: home.page/cards + importName: FeaturedDocsCard + - mountPoint: home.page/cards + importName: JokeCard + default.main-menu-items: + menuItems: + default.list: + title: References + icon: bookmarks + default.apis: + parent: default.list + default.learning-path: + parent: default.list + backstage.plugin-techdocs: + menuItems: + favorites: + title: Favorites + icon: star + priority: 10 + docs: + parent: favorites + priority: 1 + pataknight.backstage-plugin-rhdh-qe-theme: + appIcons: + - importName: LightIcon + name: lightIcon + - importName: DarkIcon + name: darkIcon + themes: + - icon: lightIcon + id: light-dynamic + importName: lightThemeProvider + title: Light Dynamic + variant: light + - icon: darkIcon + id: dark-dynamic + importName: darkThemeProvider + title: Dark Dynamic + variant: dark \ No newline at end of file diff --git a/.ibm/pipelines/resources/config_map/rbac-policy.csv b/.ibm/pipelines/resources/config_map/rbac-policy.csv index 6a5a94d832..9a395aa408 100644 --- a/.ibm/pipelines/resources/config_map/rbac-policy.csv +++ b/.ibm/pipelines/resources/config_map/rbac-policy.csv @@ -1,7 +1,7 @@ p, role:default/guests, catalog.entity.create, create, allow p, role:default/team_a, catalog-entity, read, allow g, user:xyz/user, role:xyz/team_a -g, group:janus-qe/rhdh-qe-2-team, role:default/test2-role +g, group:default/rhdh-qe-2-team, role:default/test2-role p, role:xyz/team_a, catalog-entity, read, allow p, role:xyz/team_a, catalog.entity.create, create, allow @@ -17,4 +17,4 @@ p, role:default/qe_rbac_admin, catalog.location.read, read, allow p, role:default/bulk_import, bulk.import, use, allow p, role:default/bulk_import, catalog.location.create, create, allow p, role:default/bulk_import, catalog.entity.create, create, allow -g, group:janus-qe/rhdh-qe-2-team, role:default/bulk_import \ No newline at end of file +g, group:default/rhdh-qe-2-team, role:default/bulk_import \ No newline at end of file diff --git a/.ibm/pipelines/utils.sh b/.ibm/pipelines/utils.sh index c8fe52b958..0794e677c5 100755 --- a/.ibm/pipelines/utils.sh +++ b/.ibm/pipelines/utils.sh @@ -1,7 +1,5 @@ #!/bin/sh -set -x - retrieve_pod_logs() { local pod_name=$1; local container=$2; local namespace=$3 echo " Retrieving logs for container: $container" @@ -13,8 +11,7 @@ retrieve_pod_logs() { save_all_pod_logs(){ set +e local namespace=$1 - namespace=${namespace%-pr-*} # remove -pr- suffix if any. - mkdir -p pod_logs + rm -rf pod_logs && mkdir -p pod_logs # Get all pod names in the namespace pod_names=$(kubectl get pods -n $namespace -o jsonpath='{.items[*].metadata.name}') @@ -269,9 +266,31 @@ spec: EOD } +# Monitors the status of an operator in an OpenShift namespace. +# It checks the ClusterServiceVersion (CSV) for a specific operator to verify if its phase matches an expected value. +check_operator_status() { + local timeout=${1:-300} # Timeout in seconds (default 300) + local namespace=$2 # Namespace to check + local operator_name=$3 # Operator name + local expected_status=${4:-"Succeeded"} # Expected status phase (default Succeeded) + + echo "Checking the status of operator '${operator_name}' in namespace '${namespace}' with a timeout of ${timeout} seconds." + echo "Expected status: ${expected_status}" + + timeout "${timeout}" bash -c " + while true; do + CURRENT_PHASE=\$(oc get csv -n '${namespace}' -o jsonpath='{.items[?(@.spec.displayName==\"${operator_name}\")].status.phase}') + echo \"Operator '${operator_name}' current phase: \${CURRENT_PHASE}\" + [[ \"\${CURRENT_PHASE}\" == \"${expected_status}\" ]] && echo \"Operator '${operator_name}' is now in '${expected_status}' phase.\" && break + sleep 10 + done + " || echo "Timed out after ${timeout} seconds. Operator '${operator_name}' did not reach '${expected_status}' phase." +} + # Installs the Crunchy Postgres Operator using predefined parameters install_crunchy_postgres_operator(){ install_subscription crunchy-postgres-operator openshift-operators crunchy-postgres-operator v5 certified-operators + check_operator_status 300 "openshift-operators" "Crunchy Postgres for Kubernetes" "Succeeded" } add_helm_repos() { @@ -329,7 +348,7 @@ delete_namespace() { echo "Namespace ${project} exists. Attempting to delete..." # Remove blocking finalizers - remove_finalizers_from_resources "$project" + # remove_finalizers_from_resources "$project" # Attempt to delete the namespace oc delete namespace "$project" --grace-period=0 --force || true @@ -364,35 +383,10 @@ configure_external_postgres_db() { oc apply -f "${DIR}/resources/postgres-db/postgres-cred.yaml" --namespace="${project}" } -set_github_app_3_credentials() { - GITHUB_APP_APP_ID=$GITHUB_APP_3_APP_ID - GITHUB_APP_CLIENT_ID=$GITHUB_APP_3_CLIENT_ID - GITHUB_APP_PRIVATE_KEY=$GITHUB_APP_3_PRIVATE_KEY - GITHUB_APP_CLIENT_SECRET=$GITHUB_APP_3_CLIENT_SECRET - - export GITHUB_APP_APP_ID - export GITHUB_APP_CLIENT_ID - export GITHUB_APP_PRIVATE_KEY - export GITHUB_APP_CLIENT_SECRET - echo "GitHub App 3 credentials set for current job." -} - -set_github_app_4_credentials() { - GITHUB_APP_APP_ID=$(cat /tmp/secrets/GITHUB_APP_4_APP_ID) - GITHUB_APP_CLIENT_ID=$(cat /tmp/secrets/GITHUB_APP_4_CLIENT_ID) - GITHUB_APP_PRIVATE_KEY=$(cat /tmp/secrets/GITHUB_APP_4_PRIVATE_KEY) - GITHUB_APP_CLIENT_SECRET=$(cat /tmp/secrets/GITHUB_APP_4_CLIENT_SECRET) - - export GITHUB_APP_APP_ID - export GITHUB_APP_CLIENT_ID - export GITHUB_APP_PRIVATE_KEY - export GITHUB_APP_CLIENT_SECRET - echo "GitHub App 4 credentials set for current job." -} - apply_yaml_files() { local dir=$1 local project=$2 + local rhdh_base_url=$3 echo "Applying YAML files to namespace ${project}" oc config set-context --current --namespace="${project}" @@ -410,8 +404,9 @@ apply_yaml_files() { done DH_TARGET_URL=$(echo -n "test-backstage-customization-provider-${project}.${K8S_CLUSTER_ROUTER_BASE}" | base64 -w 0) + local RHDH_BASE_URL=$(echo -n "$rhdh_base_url" | base64 | tr -d '\n') - for key in GITHUB_APP_APP_ID GITHUB_APP_CLIENT_ID GITHUB_APP_PRIVATE_KEY GITHUB_APP_CLIENT_SECRET GITHUB_APP_JANUS_TEST_APP_ID GITHUB_APP_JANUS_TEST_CLIENT_ID GITHUB_APP_JANUS_TEST_CLIENT_SECRET GITHUB_APP_JANUS_TEST_PRIVATE_KEY GITHUB_APP_WEBHOOK_URL GITHUB_APP_WEBHOOK_SECRET KEYCLOAK_CLIENT_SECRET ACR_SECRET GOOGLE_CLIENT_ID GOOGLE_CLIENT_SECRET K8S_CLUSTER_TOKEN_ENCODED OCM_CLUSTER_URL GITLAB_TOKEN DH_TARGET_URL; do + for key in GITHUB_APP_APP_ID GITHUB_APP_CLIENT_ID GITHUB_APP_PRIVATE_KEY GITHUB_APP_CLIENT_SECRET GITHUB_APP_JANUS_TEST_APP_ID GITHUB_APP_JANUS_TEST_CLIENT_ID GITHUB_APP_JANUS_TEST_CLIENT_SECRET GITHUB_APP_JANUS_TEST_PRIVATE_KEY GITHUB_APP_WEBHOOK_URL GITHUB_APP_WEBHOOK_SECRET KEYCLOAK_CLIENT_SECRET ACR_SECRET GOOGLE_CLIENT_ID GOOGLE_CLIENT_SECRET K8S_CLUSTER_TOKEN_ENCODED OCM_CLUSTER_URL GITLAB_TOKEN KEYCLOAK_AUTH_BASE_URL KEYCLOAK_AUTH_CLIENTID KEYCLOAK_AUTH_CLIENT_SECRET KEYCLOAK_AUTH_LOGIN_REALM KEYCLOAK_AUTH_REALM RHDH_BASE_URL; do sed -i "s|${key}:.*|${key}: ${!key}|g" "$dir/auth/secrets-rhdh-secrets.yaml" done @@ -424,14 +419,11 @@ apply_yaml_files() { oc apply -f "$dir/resources/cluster_role/cluster-role-ocm.yaml" --namespace="${project}" oc apply -f "$dir/resources/cluster_role_binding/cluster-role-binding-ocm.yaml" --namespace="${project}" - escaped_url=$(printf '%s\n' "${ENCODED_API_SERVER_URL}" | sed 's/[\/&]/\\&/g') - sed -i "s/K8S_CLUSTER_API_SERVER_URL:.*/K8S_CLUSTER_API_SERVER_URL: ${escaped_url}/g" "$dir/auth/secrets-rhdh-secrets.yaml" \ - && echo "Updated K8S_CLUSTER_API_SERVER_URL in secrets file." \ - || echo "Failed to update K8S_CLUSTER_API_SERVER_URL." >&2 + sed -i "s/K8S_CLUSTER_API_SERVER_URL:.*/K8S_CLUSTER_API_SERVER_URL: ${K8S_CLUSTER_API_SERVER_URL}/g" "$dir/auth/secrets-rhdh-secrets.yaml" sed -i "s/K8S_CLUSTER_NAME:.*/K8S_CLUSTER_NAME: ${ENCODED_CLUSTER_NAME}/g" "$dir/auth/secrets-rhdh-secrets.yaml" - token=$(oc get secret "${secret_name}" -n "${project}" -o=jsonpath='{.data.token}') + token=$(oc get secret rhdh-k8s-plugin-secret -n "${project}" -o=jsonpath='{.data.token}') sed -i "s/OCM_CLUSTER_TOKEN: .*/OCM_CLUSTER_TOKEN: ${token}/" "$dir/auth/secrets-rhdh-secrets.yaml" # Select the configuration file based on the namespace or job @@ -441,6 +433,10 @@ apply_yaml_files() { create_app_config_map_k8s "$config_file" "$project" else create_app_config_map "$config_file" "$project" + oc create configmap dynamic-homepage-and-sidebar-config \ + --from-file="dynamic-homepage-and-sidebar-config.yaml"="$dir/resources/config_map/dynamic-homepage-and-sidebar-config.yaml" \ + --namespace="${project}" \ + --dry-run=client -o yaml | oc apply -f - fi oc create configmap rbac-policy \ --from-file="rbac-policy.csv"="$dir/resources/config_map/rbac-policy.csv" \ @@ -449,6 +445,10 @@ apply_yaml_files() { oc apply -f "$dir/auth/secrets-rhdh-secrets.yaml" --namespace="${project}" + # Create Pipeline run for tekton test case. + oc apply -f "$dir/resources/pipeline-run/hello-world-pipeline.yaml" + oc apply -f "$dir/resources/pipeline-run/hello-world-pipeline-run.yaml" + } deploy_test_backstage_provider() { @@ -507,7 +507,7 @@ create_app_config_map_k8s() { run_tests() { local release_name=$1 local project=$2 - project=${project%-pr-*} # Remove -pr- suffix if any set for main branchs pr's. + project=${project} cd "${DIR}/../../e2e-tests" yarn install yarn playwright install chromium @@ -572,6 +572,7 @@ check_backstage_running() { return 0 else echo "Attempt ${i} of ${max_attempts}: Backstage not yet available (HTTP Status: ${http_status})" + oc get pods -n "${namespace}" sleep "${wait_seconds}" fi done @@ -592,30 +593,59 @@ install_tekton_pipelines() { fi } +# installs the advanced-cluster-management Operator +install_acm_operator(){ + oc apply -f "${DIR}/cluster/operators/acm/operator-group.yaml" + oc apply -f "${DIR}/cluster/operators/acm/subscription-acm.yaml" + wait_for_deployment "open-cluster-management" "multiclusterhub-operator" + oc apply -f "${DIR}/cluster/operators/acm/multiclusterhub.yaml" + # wait until multiclusterhub is Running. + timeout 600 bash -c 'while true; do + CURRENT_PHASE=$(oc get multiclusterhub multiclusterhub -n open-cluster-management -o jsonpath="{.status.phase}") + echo "MulticlusterHub Current Status: $CURRENT_PHASE" + [[ "$CURRENT_PHASE" == "Running" ]] && echo "MulticlusterHub is now in Running phase." && break + sleep 10 + done' || echo "Timed out after 10 minutes" + +} + +# Installs the Red Hat OpenShift Pipelines operator if not already installed install_pipelines_operator() { - local dir=$1 DISPLAY_NAME="Red Hat OpenShift Pipelines" - + # Check if operator is already installed if oc get csv -n "openshift-operators" | grep -q "${DISPLAY_NAME}"; then echo "Red Hat OpenShift Pipelines operator is already installed." else echo "Red Hat OpenShift Pipelines operator is not installed. Installing..." - oc apply -f "${dir}/resources/pipeline-run/pipelines-operator.yaml" + # Install the operator and wait for deployment + install_subscription openshift-pipelines-operator openshift-operators openshift-pipelines-operator-rh latest redhat-operators + wait_for_deployment "openshift-operators" "pipelines" + timeout 300 bash -c ' + while ! oc get svc tekton-pipelines-webhook -n openshift-pipelines &> /dev/null; do + echo "Waiting for tekton-pipelines-webhook service to be created..." + sleep 5 + done + echo "Service tekton-pipelines-webhook is created." + ' || echo "Error: Timed out waiting for tekton-pipelines-webhook service creation." fi } -initiate_deployments() { - +cluster_setup() { + install_pipelines_operator + install_acm_operator install_crunchy_postgres_operator add_helm_repos +} - uninstall_helmchart "${NAME_SPACE}" "${RELEASE_NAME}" +initiate_deployments() { + configure_namespace ${NAME_SPACE} # Deploy redis cache db. oc apply -f "$DIR/resources/redis-cache/redis-deployment.yaml" --namespace="${NAME_SPACE}" cd "${DIR}" - apply_yaml_files "${DIR}" "${NAME_SPACE}" + local rhdh_base_url="https://${RELEASE_NAME}-backstage-${NAME_SPACE}.${K8S_CLUSTER_ROUTER_BASE}" + apply_yaml_files "${DIR}" "${NAME_SPACE}" "${rhdh_base_url}" echo "Deploying image from repository: ${QUAY_REPO}, TAG_NAME: ${TAG_NAME}, in NAME_SPACE: ${NAME_SPACE}" helm upgrade -i "${RELEASE_NAME}" -n "${NAME_SPACE}" "${HELM_REPO_NAME}/${HELM_IMAGE_NAME}" --version "${CHART_VERSION}" -f "${DIR}/value_files/${HELM_CHART_VALUE_FILE_NAME}" --set global.clusterRouterBase="${K8S_CLUSTER_ROUTER_BASE}" --set upstream.backstage.image.repository="${QUAY_REPO}" --set upstream.backstage.image.tag="${TAG_NAME}" @@ -623,8 +653,9 @@ initiate_deployments() { configure_namespace "${NAME_SPACE_RBAC}" configure_external_postgres_db "${NAME_SPACE_RBAC}" - uninstall_helmchart "${NAME_SPACE_RBAC}" "${RELEASE_NAME_RBAC}" - apply_yaml_files "${DIR}" "${NAME_SPACE_RBAC}" + # Initiate rbac instace deployment. + local rbac_rhdh_base_url="https://${RELEASE_NAME_RBAC}-backstage-${NAME_SPACE_RBAC}.${K8S_CLUSTER_ROUTER_BASE}" + apply_yaml_files "${DIR}" "${NAME_SPACE_RBAC}" "${rbac_rhdh_base_url}" echo "Deploying image from repository: ${QUAY_REPO}, TAG_NAME: ${TAG_NAME}, in NAME_SPACE: ${RELEASE_NAME_RBAC}" helm upgrade -i "${RELEASE_NAME_RBAC}" -n "${NAME_SPACE_RBAC}" "${HELM_REPO_NAME}/${HELM_IMAGE_NAME}" --version "${CHART_VERSION}" -f "${DIR}/value_files/${HELM_CHART_RBAC_VALUE_FILE_NAME}" --set global.clusterRouterBase="${K8S_CLUSTER_ROUTER_BASE}" --set upstream.backstage.image.repository="${QUAY_REPO}" --set upstream.backstage.image.tag="${TAG_NAME}" } @@ -686,12 +717,8 @@ force_delete_namespace() { } oc_login() { - export K8S_CLUSTER_URL=$(cat /tmp/secrets/RHDH_PR_OS_CLUSTER_URL) - export K8S_CLUSTER_TOKEN=$(cat /tmp/secrets/RHDH_PR_OS_CLUSTER_TOKEN) - oc login --token="${K8S_CLUSTER_TOKEN}" --server="${K8S_CLUSTER_URL}" echo "OCP version: $(oc version)" export K8S_CLUSTER_ROUTER_BASE=$(oc get route console -n openshift-console -o=jsonpath='{.spec.host}' | sed 's/^[^.]*\.//') } - diff --git a/.ibm/pipelines/value_files/values_showcase-rbac.yaml b/.ibm/pipelines/value_files/values_showcase-rbac.yaml index 59edfc5c28..748ba831e7 100644 --- a/.ibm/pipelines/value_files/values_showcase-rbac.yaml +++ b/.ibm/pipelines/value_files/values_showcase-rbac.yaml @@ -97,7 +97,7 @@ global: - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import disabled: false - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-dynamic-home-page - disabled: true + disabled: false # Enable tech-radar plugin. - package: ./dynamic-plugins/dist/backstage-community-plugin-tech-radar disabled: false diff --git a/.ibm/pipelines/value_files/values_showcase.yaml b/.ibm/pipelines/value_files/values_showcase.yaml index d4ad325217..a714d1653a 100644 --- a/.ibm/pipelines/value_files/values_showcase.yaml +++ b/.ibm/pipelines/value_files/values_showcase.yaml @@ -114,6 +114,9 @@ global: - package: '@pataknight/backstage-plugin-rhdh-qe-theme@0.5.5' disabled: false integrity: sha512-srTnFDYn3Ett6z33bX4nL2NQY8wqux8TkpgBQNsE8S73nMfsor/wAdmVgHL+xW7pxQ09DT4YTdaG3GkH+cyyNQ== + - package: '@backstage-community/plugin-todo@0.2.42' + disabled: false + integrity: sha512-agmfwxHkZPy0zaXzjMKY9Us9l7J2og+z7p2lDWQBmlJ1KZRo6OBQdnlG1mTEryfEEl/bx5Ko+f1PhFj2/BmiIQ== # -- Upstream Backstage [chart configuration](https://github.com/backstage/charts/blob/main/charts/backstage/values.yaml) upstream: @@ -145,6 +148,8 @@ upstream: extraAppConfig: - configMapRef: app-config-rhdh filename: app-config-rhdh.yaml + - configMapRef: dynamic-homepage-and-sidebar-config + filename: dynamic-homepage-and-sidebar-config.yaml extraEnvVarsSecrets: - rhdh-secrets - redis-secret diff --git a/.rhdh/docker/Dockerfile b/.rhdh/docker/Dockerfile index 180ddd505c..3f3915ff28 100644 --- a/.rhdh/docker/Dockerfile +++ b/.rhdh/docker/Dockerfile @@ -23,7 +23,7 @@ # Stage 1 - Build nodejs skeleton #@follow_tag(registry.access.redhat.com/ubi9/nodejs-20:1) # https://registry.access.redhat.com/ubi9/nodejs-20 -FROM registry.access.redhat.com/ubi9/nodejs-20:9.5-1731603589 AS build +FROM registry.access.redhat.com/ubi9/nodejs-20:9.5-1733148170 AS build # hadolint ignore=DL3002 USER 0 diff --git a/.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.5.0.patch b/.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.5.0.patch deleted file mode 100644 index 20242852c6..0000000000 --- a/.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.5.0.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/dist/index.d.ts b/dist/index.d.ts -index b88643b24b813cf5d9f1cfd17b182618c0937f8b..2407a197e182d7714607132ff2f306cb4a0ad16a 100644 ---- a/dist/index.d.ts -+++ b/dist/index.d.ts -@@ -89,6 +89,7 @@ type TaskBrokerDispatchOptions = { - * @public - */ - interface TaskContext { -+ taskId: string; - cancelSignal: AbortSignal; - spec: TaskSpec; - secrets?: TaskSecrets; diff --git a/.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch b/.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch new file mode 100644 index 0000000000..995f3e582c --- /dev/null +++ b/.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch @@ -0,0 +1,21 @@ +diff --git a/dist/alpha.d.ts b/dist/alpha.d.ts +index 7b6f3aa434c1cfcec9fc0b65cccace1f30b6542c..ee31d169c695a96328a6520417bbb135854c4e74 100644 +--- a/dist/alpha.d.ts ++++ b/dist/alpha.d.ts +@@ -1,4 +1,3 @@ +-/// + import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api'; + import { TemplateAction, TaskBroker, TemplateFilter, TemplateGlobal } from '@backstage/plugin-scaffolder-node'; + +diff --git a/dist/index.d.ts b/dist/index.d.ts +index 148a205bfe7747f916aebcb21c3c9cee45cdc07b..4a3126f375daa9e961743d210e3cecb390b2ec2b 100644 +--- a/dist/index.d.ts ++++ b/dist/index.d.ts +@@ -89,6 +89,7 @@ type TaskBrokerDispatchOptions = { + * @public + */ + interface TaskContext { ++ taskId: string; + cancelSignal: AbortSignal; + spec: TaskSpec; + secrets?: TaskSecrets; diff --git a/docker/Dockerfile b/docker/Dockerfile index 6a378f47d0..ce926c93d4 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -24,7 +24,7 @@ # Stage 1 - Build nodejs skeleton # https://registry.access.redhat.com/ubi9/nodejs-20 -FROM registry.access.redhat.com/ubi9/nodejs-20:9.5-1731603589 AS skeleton +FROM registry.access.redhat.com/ubi9/nodejs-20:9.5-1733148170 AS skeleton # hadolint ignore=DL3002 USER 0 diff --git a/docs/monitoring-and-logging.md b/docs/monitoring-and-logging.md index e5fbf1c266..6897ce80a2 100644 --- a/docs/monitoring-and-logging.md +++ b/docs/monitoring-and-logging.md @@ -1,8 +1,6 @@ # Setting up Metrics Monitoring and Logging for Backstage Showcase -The Backstage Showcase provides a `/metrics` endpoint on port `9464` (for application metrics) and `9463` (for host metrics) that provides OpenTelemetry metrics about your backstage application. This endpoint can be used to monitor your backstage instance using OpenTelemetry and Grafana. - -**Note**: Due to a limitation, the host and application metrics are exposed on separate ports. This is to be improved by making all metrics accessible on the same port (9464) in a future release. This guide will ONLY work with the application metrics from NodeJs and Backstage. +The Backstage Showcase provides a `/metrics` endpoint on port `9464` that provides OpenTelemetry metrics about your backstage application. This endpoint can be used to monitor your backstage instance using OpenTelemetry and Grafana. When deploying Backstage Showcase onto a kubernetes cluster with the [RHDH Helm chart](https://github.com/redhat-developer/rhdh-chart) or the [RHDH Operator](https://github.com/janus-idp/operator), monitoring and logging for your RHDH instance can be configured using the following steps. diff --git a/dynamic-plugins/_utils/src/wrappers.test.ts b/dynamic-plugins/_utils/src/wrappers.test.ts index 79fcc1c13b..642f81fa69 100644 --- a/dynamic-plugins/_utils/src/wrappers.test.ts +++ b/dynamic-plugins/_utils/src/wrappers.test.ts @@ -112,11 +112,12 @@ function parseYamlFile(filePath: string): T { function validateDynamicPluginsConfig( config: DynamicPluginsConfig, wrapperDirNames: string[], - externalDynamicPlugin?: DynamicPluginConfig, + externalDynamicPlugins?: DynamicPluginConfig[], ): void { const dynamicPluginsPackageNames = config.plugins.reduce( (packageNames, plugin) => { - if (externalDynamicPlugin?.package !== plugin.package) { + const isExternalPlugin = externalDynamicPlugins?.some((externalDynamicPlugin) => externalDynamicPlugin.package === plugin.package) + if (!isExternalPlugin) { // We want the third index ['.', 'dynamic-plugins', 'dist', 'backstage-plugin-scaffolder-backend-module-github-dynamic'] packageNames.push(plugin.package.split("/")[3]); } @@ -188,8 +189,8 @@ describe("Dynamic Plugin Wrappers", () => { ); it.each(frontendPackageJsonFiles)( - "$name should have scalprum config in the `package.json`", - ({ name, scalprum }) => { + "should have scalprum config in the `package.json`", + ({ scalprum }) => { expect(scalprum).toBeTruthy(); }, ); @@ -240,16 +241,20 @@ describe("Dynamic Plugin Wrappers", () => { IBM_VALUES_SHOWCASE_CONFIG_FILE, ); - const externalDynamicPluginConfig: DynamicPluginConfig = { + const externalDynamicPluginsConfig: DynamicPluginConfig[] = [{ package: "@pataknight/backstage-plugin-rhdh-qe-theme@0.5.5", disabled: false, - }; + }, + { + package: "@backstage-community/plugin-todo@0.2.42" + } + ]; it("should have a corresponding package", () => { validateDynamicPluginsConfig( config.global.dynamic, wrapperDirNames, - externalDynamicPluginConfig, + externalDynamicPluginsConfig, ); }); }); diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-acr/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-acr/package.json index 8a75160f7f..7dedc79c22 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-acr/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-acr/package.json @@ -30,8 +30,8 @@ "dependencies": { "@backstage-community/plugin-acr": "1.8.5", "@backstage/core-plugin-api": "1.10.0", - "@mui/icons-material": "5.16.7", - "@mui/material": "5.16.7" + "@mui/icons-material": "5.16.9", + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-jfrog-artifactory/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-jfrog-artifactory/package.json index c8e7a0e995..7a2f8c857d 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-jfrog-artifactory/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-jfrog-artifactory/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage-community/plugin-jfrog-artifactory": "1.10.2", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-lighthouse/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-lighthouse/package.json index b5110afd8e..da5a706b3d 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-lighthouse/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-lighthouse/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@backstage-community/plugin-lighthouse": "0.4.24", - "@mui/icons-material": "5.16.7" + "@mui/icons-material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-nexus-repository-manager/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-nexus-repository-manager/package.json index 6f1d14bf81..0b1fec4119 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-nexus-repository-manager/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-nexus-repository-manager/package.json @@ -28,7 +28,7 @@ }, "dependencies": { "@backstage-community/plugin-nexus-repository-manager": "1.10.6", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-ocm/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-ocm/package.json index 5bfb394ce7..3c3dde0350 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-ocm/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-ocm/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@backstage-community/plugin-ocm": "5.2.4", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-quay/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-quay/package.json index 107dd21df5..de36413131 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-quay/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-quay/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage-community/plugin-quay": "1.14.4", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-rbac/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-rbac/package.json index b648ceedb5..f13178bb4b 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-rbac/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-rbac/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@backstage-community/plugin-rbac": "1.33.2", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-redhat-argocd/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-redhat-argocd/package.json index ca85896adb..e4df380539 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-redhat-argocd/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-redhat-argocd/package.json @@ -1,6 +1,6 @@ { "name": "backstage-community-plugin-redhat-argocd", - "version": "1.10.4", + "version": "1.11.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -39,8 +39,8 @@ "export-dynamic:clean": "run export-dynamic --clean" }, "dependencies": { - "@backstage-community/plugin-redhat-argocd": "1.10.4", - "@mui/material": "5.16.7" + "@backstage-community/plugin-redhat-argocd": "1.11.0", + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-tech-radar/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-tech-radar/package.json index 7479572e4b..9463142a73 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-tech-radar/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-tech-radar/package.json @@ -32,7 +32,7 @@ "@backstage-community/plugin-tech-radar": "1.0.0", "@backstage-community/plugin-tech-radar-common": "1.0.0", "@backstage/core-plugin-api": "1.10.0", - "@mui/icons-material": "5.16.7" + "@mui/icons-material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-tekton/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-tekton/package.json index 7044817d0a..d6dfa4824e 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-tekton/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-tekton/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@backstage-community/plugin-tekton": "3.16.2", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-community-plugin-topology/package.json b/dynamic-plugins/wrappers/backstage-community-plugin-topology/package.json index 90db0bd414..906c13d506 100644 --- a/dynamic-plugins/wrappers/backstage-community-plugin-topology/package.json +++ b/dynamic-plugins/wrappers/backstage-community-plugin-topology/package.json @@ -40,7 +40,7 @@ }, "dependencies": { "@backstage-community/plugin-topology": "1.29.7", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-plugin-notifications/package.json b/dynamic-plugins/wrappers/backstage-plugin-notifications/package.json index 060ebbb326..ae828429a5 100644 --- a/dynamic-plugins/wrappers/backstage-plugin-notifications/package.json +++ b/dynamic-plugins/wrappers/backstage-plugin-notifications/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/plugin-notifications": "0.3.2", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/backstage-plugin-signals/package.json b/dynamic-plugins/wrappers/backstage-plugin-signals/package.json index fa9a0c2488..cb18c5555e 100644 --- a/dynamic-plugins/wrappers/backstage-plugin-signals/package.json +++ b/dynamic-plugins/wrappers/backstage-plugin-signals/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/plugin-signals": "0.0.11", - "@mui/material": "5.16.7" + "@mui/material": "5.16.9" }, "devDependencies": { "@backstage/cli": "0.28.2", diff --git a/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json b/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json index c28a44147b..a2898e89fb 100644 --- a/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json +++ b/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json @@ -29,7 +29,7 @@ "export-dynamic:clean": "run export-dynamic --clean" }, "dependencies": { - "@mui/material": "5.16.7", + "@mui/material": "5.16.9", "@red-hat-developer-hub/backstage-plugin-bulk-import": "1.10.3" }, "devDependencies": { diff --git a/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-dynamic-home-page/package.json b/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-dynamic-home-page/package.json index b8dc6fce62..61b101d6d6 100644 --- a/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-dynamic-home-page/package.json +++ b/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-dynamic-home-page/package.json @@ -28,7 +28,7 @@ "export-dynamic:clean": "run export-dynamic --clean" }, "dependencies": { - "@mui/material": "5.16.7", + "@mui/material": "5.16.9", "@red-hat-developer-hub/backstage-plugin-dynamic-home-page": "1.0.1" }, "devDependencies": { diff --git a/e2e-tests/package.json b/e2e-tests/package.json index 5986de13d5..ae5f5998f0 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -10,8 +10,6 @@ "scripts": { "showcase": "playwright test --project=showcase", "showcase-rbac": "playwright test --project=showcase-rbac", - "showcase-1-2-x": "playwright test --project=showcase", - "showcase-rbac-1-2-x": "playwright test --project=showcase-rbac", "showcase-ci-nightly": "playwright test --project=showcase", "showcase-rbac-nightly": "playwright test --project=showcase-rbac", "showcase-k8s-ci-nightly": "playwright test --project=showcase-k8s", @@ -27,14 +25,14 @@ }, "devDependencies": { "@playwright/test": "1.48.0", - "@types/node": "20.17.9", + "@types/node": "20.17.10", "@typescript-eslint/eslint-plugin": "6.21.0", "@typescript-eslint/parser": "6.21.0", "eslint": "8.57.1", "eslint-plugin-filenames": "1.3.2", "ioredis": "5.4.1", "otplib": "12.0.1", - "prettier": "3.4.1", + "prettier": "3.4.2", "typescript": "5.6.3" }, "dependencies": { diff --git a/e2e-tests/playwright/e2e/catalog-scaffolded-from-link.spec.ts b/e2e-tests/playwright/e2e/catalog-scaffolded-from-link.spec.ts index d9794e7e2f..b8ce6cdf72 100644 --- a/e2e-tests/playwright/e2e/catalog-scaffolded-from-link.spec.ts +++ b/e2e-tests/playwright/e2e/catalog-scaffolded-from-link.spec.ts @@ -34,13 +34,9 @@ test.describe.serial("Link Scaffolded Templates to Catalog Items", () => { uiHelper = new UIhelper(page); catalogImport = new CatalogImport(page); - await common.loginAsGithubUser(); + await common.loginAsKeycloakUser(); }); - test.beforeEach( - async () => await new Common(page).checkAndClickOnGHloginPopup(), - ); - test("Register an Template", async () => { await uiHelper.openSidebar("Catalog"); await uiHelper.clickButton("Create"); @@ -86,24 +82,23 @@ test.describe.serial("Link Scaffolded Templates to Catalog Items", () => { await uiHelper.clickLink("Open in catalog"); }); - test.fixme( - "Verify Scaffolded link in components Dependencies and scaffoldedFrom relation in entity Raw Yaml ", - async () => { - await common.clickOnGHloginPopup(); - await uiHelper.clickTab("Dependencies"); - await uiHelper.verifyText( - `ownerOf / ownedByscaffoldedFromcomponent:${reactAppDetails.componentName}group:${reactAppDetails.owner}Create React App Template`, - ); - await catalogImport.inspectEntityAndVerifyYaml( - `- type: scaffoldedFrom\n targetRef: template:default/create-react-app-template-with-timestamp-entityref\n target:\n kind: template\n namespace: default\n name: create-react-app-template-with-timestamp-entityref`, - ); - }, - ); + //FIXME + test.skip("Verify Scaffolded link in components Dependencies and scaffoldedFrom relation in entity Raw Yaml ", async () => { + await common.clickOnGHloginPopup(); + await uiHelper.clickTab("Dependencies"); + await uiHelper.verifyText( + `ownerOf / ownedByscaffoldedFromcomponent:${reactAppDetails.componentName}group:${reactAppDetails.owner}Create React App Template`, + ); + await catalogImport.inspectEntityAndVerifyYaml( + `- type: scaffoldedFrom\n targetRef: template:default/create-react-app-template-with-timestamp-entityref\n target:\n kind: template\n namespace: default\n name: create-react-app-template-with-timestamp-entityref`, + ); + }); test("Verify Registered Template and scaffolderOf relation in entity Raw Yaml", async () => { await uiHelper.openSidebar("Catalog"); await uiHelper.selectMuiBox("Kind", "Template"); - await uiHelper.searchInputPlaceholder("Create React App Template"); + + await uiHelper.searchInputPlaceholder("Create React App Template\n"); await uiHelper.verifyRowInTableByUniqueText("Create React App Template", [ "website", ]); diff --git a/e2e-tests/playwright/e2e/catalog-timestamp.spec.ts b/e2e-tests/playwright/e2e/catalog-timestamp.spec.ts index e9cf5f44a0..0ba48da31c 100644 --- a/e2e-tests/playwright/e2e/catalog-timestamp.spec.ts +++ b/e2e-tests/playwright/e2e/catalog-timestamp.spec.ts @@ -36,7 +36,9 @@ test.describe("Test timestamp column on Catalog", () => { await catalogImport.registerExistingComponent(component); await uiHelper.openSidebar("Catalog"); await uiHelper.selectMuiBox("Kind", "Component"); - await uiHelper.searchInputPlaceholder("timestamp-test"); + await uiHelper.clickByDataTestId("user-picker-all"); + await uiHelper.searchInputPlaceholder("timestamp-test-created"); + await uiHelper.verifyText("timestamp-test-created"); await uiHelper.verifyColumnHeading(["Created At"], true); await uiHelper.verifyRowInTableByUniqueText("timestamp-test-created", [ /^\d{1,2}\/\d{1,2}\/\d{1,4}, \d:\d{1,2}:\d{1,2} (AM|PM)$/g, diff --git a/e2e-tests/playwright/e2e/configuration-test/config-map.spec.ts b/e2e-tests/playwright/e2e/configuration-test/config-map.spec.ts index 15dc21b8fd..45ed55e946 100644 --- a/e2e-tests/playwright/e2e/configuration-test/config-map.spec.ts +++ b/e2e-tests/playwright/e2e/configuration-test/config-map.spec.ts @@ -14,7 +14,7 @@ test.describe("Change app-config at e2e test runtime", () => { const kubeUtils = new KubeClient(); const dynamicTitle = generateDynamicTitle(); - + const uiHelper = new UIhelper(page); try { LOGGER.info(`Updating ConfigMap '${configMapName}' with new title.`); await kubeUtils.updateConfigMapTitle( @@ -31,6 +31,9 @@ test.describe("Change app-config at e2e test runtime", () => { const common = new Common(page); await common.loginAsGuest(); await new UIhelper(page).openSidebar("Home"); + await uiHelper.verifyHeading("Welcome back!"); + await uiHelper.verifyText("Quick Access"); + await expect(page.locator("#search-bar-text-field")).toBeVisible(); LOGGER.info("Verifying new title in the UI..."); expect(await page.title()).toContain(dynamicTitle); LOGGER.info("Title successfully verified in the UI."); diff --git a/e2e-tests/playwright/e2e/github-discovery.spec.ts b/e2e-tests/playwright/e2e/github-discovery.spec.ts index a97b553fe1..3811ddb290 100644 --- a/e2e-tests/playwright/e2e/github-discovery.spec.ts +++ b/e2e-tests/playwright/e2e/github-discovery.spec.ts @@ -22,7 +22,8 @@ const test = base.extend({ testOrganization: JANUS_QE_ORG, }); -test.describe("Github Discovery Catalog", () => { +//TODO: skipping due to RHIDP-4992 +test.describe.skip("Github Discovery Catalog", () => { test(`Discover Organization's Catalog`, async ({ catalogPage, githubApi, diff --git a/e2e-tests/playwright/e2e/github-happy-path.spec.ts b/e2e-tests/playwright/e2e/github-happy-path.spec.ts index 0768fc11aa..808d540b00 100644 --- a/e2e-tests/playwright/e2e/github-happy-path.spec.ts +++ b/e2e-tests/playwright/e2e/github-happy-path.spec.ts @@ -12,6 +12,7 @@ let page: Page; // TODO: replace skip with serial test.describe.skip("GitHub Happy path", () => { + //TODO: skipping due to RHIDP-4992 let common: Common; let uiHelper: UIhelper; let catalogImport: CatalogImport; @@ -89,6 +90,16 @@ test.describe.skip("GitHub Happy path", () => { await uiHelper.selectMuiBox("Kind", "Component"); await uiHelper.clickByDataTestId("user-picker-all"); await uiHelper.clickLink("Backstage Showcase"); + + const expectedPath = "/catalog/default/component/backstage-showcase"; + // Wait for the expected path in the URL + await page.waitForURL(`**${expectedPath}`, { + waitUntil: "domcontentloaded", // Wait until the DOM is loaded + timeout: 10000, + }); + // Optionally, verify that the current URL contains the expected path + await expect(page.url()).toContain(expectedPath); + await common.clickOnGHloginPopup(); await uiHelper.verifyLink("Janus Website", { exact: false }); await backstageShowcase.verifyPRStatisticsRendered(); diff --git a/e2e-tests/playwright/e2e/github-integration-org-fetch.spec.ts b/e2e-tests/playwright/e2e/github-integration-org-fetch.spec.ts index e9b945d6ca..0f71e1631b 100644 --- a/e2e-tests/playwright/e2e/github-integration-org-fetch.spec.ts +++ b/e2e-tests/playwright/e2e/github-integration-org-fetch.spec.ts @@ -11,14 +11,14 @@ test.describe.serial("GitHub integration with Org data fetching", () => { page = (await setupBrowser(browser, testInfo)).page; uiHelper = new UIhelper(page); common = new Common(page); - await common.loginAsGithubUser(); + await common.loginAsKeycloakUser(); }); test("Verify that fetching the groups of the first org works", async () => { await uiHelper.openSidebar("Catalog"); await uiHelper.selectMuiBox("Kind", "Group"); - await uiHelper.searchInputPlaceholder("m"); + await uiHelper.searchInputPlaceholder("maintainers"); await uiHelper.verifyRowsInTable(["maintainers"]); await uiHelper.searchInputPlaceholder("r"); diff --git a/e2e-tests/playwright/e2e/home-page-customization.spec.ts b/e2e-tests/playwright/e2e/home-page-customization.spec.ts new file mode 100644 index 0000000000..70d3f94ad7 --- /dev/null +++ b/e2e-tests/playwright/e2e/home-page-customization.spec.ts @@ -0,0 +1,33 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../utils/ui-helper"; +import { Common } from "../utils/common"; + +test.describe("Home page customization", () => { + let common: Common; + let uiHelper: UIhelper; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + common = new Common(page); + await common.loginAsGuest(); + }); + + test("Verify that home page is customized", async () => { + await uiHelper.verifyTextinCard("Quick Access", "Quick Access"); + await uiHelper.verifyTextinCard( + "Your Starred Entities", + "Your Starred Entities", + ); + await uiHelper.verifyHeading("Placeholder tests"); + await uiHelper.verifyDivHasText("Home page customization test 1"); + await uiHelper.verifyDivHasText("Home page customization test 2"); + await uiHelper.verifyDivHasText("Home page customization test 3"); + await uiHelper.verifyHeading("Markdown tests"); + await uiHelper.verifyTextinCard("Company links", "Company links"); + await uiHelper.verifyHeading("Important company links"); + await uiHelper.verifyHeading("RHDH"); + await uiHelper.verifyTextinCard("Featured Docs", "Featured Docs"); + await uiHelper.verifyTextinCard("Random Joke", "Random Joke"); + await uiHelper.clickButton("Reroll"); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/analytics/analytics-disabled-rbac.spec.ts b/e2e-tests/playwright/e2e/plugins/analytics/analytics-disabled-rbac.spec.ts index 14bb138ec6..f3e447badf 100644 --- a/e2e-tests/playwright/e2e/plugins/analytics/analytics-disabled-rbac.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/analytics/analytics-disabled-rbac.spec.ts @@ -11,7 +11,7 @@ test.describe.skip('Check RBAC "analytics-provider-segment" plugin', () => { test.beforeEach(async ({ page }) => { uiHelper = new UIhelper(page); common = new Common(page); - await common.loginAsGithubUser(); + await common.loginAsKeycloakUser(); await uiHelper.openSidebarButton("Administration"); await uiHelper.openSidebar("Plugins"); await uiHelper.verifyHeading("Plugins"); diff --git a/e2e-tests/playwright/e2e/plugins/bulk-import.spec.ts b/e2e-tests/playwright/e2e/plugins/bulk-import.spec.ts index 695b328ba6..1776f1c721 100644 --- a/e2e-tests/playwright/e2e/plugins/bulk-import.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/bulk-import.spec.ts @@ -10,7 +10,8 @@ import { } from "../../support/testData/bulk-import"; // Pre-req : plugin-bulk-import & plugin-bulk-import-backend-dynamic -test.describe.serial("Bulk Import plugin", () => { +test.describe.skip("Bulk Import plugin", () => { + // Skipping due to RHIDP-5258 let page: Page; let uiHelper: UIhelper; let common: Common; @@ -41,13 +42,12 @@ test.describe.serial("Bulk Import plugin", () => { newRepoDetails.owner, newRepoDetails.repoName, ); - await common.loginAsGithubUser(process.env.GH_USER2_ID); + await common.loginAsKeycloakUser( + process.env.GH_USER2_ID, + process.env.GH_USER2_PASS, + ); }); - test.beforeEach( - async () => await new Common(page).checkAndClickOnGHloginPopup(), - ); - // Select two repos: one with an existing catalog.yaml file and another without it test("Add a Repository from the Repository Tab and Confirm its Preview", async () => { await uiHelper.openSidebar("Bulk import"); @@ -213,8 +213,8 @@ test.describe.serial("Bulk Import plugin", () => { notVisible: true, }); }); - - test("Verify Deleted Bulk Import Repositories Does not Appear in the Catalog", async () => { + // Skipping due to RHIDP-5258 + test.skip("Verify Deleted Bulk Import Repositories Does not Appear in the Catalog", async () => { await uiHelper.openSidebar("Catalog"); await uiHelper.selectMuiBox("Kind", "Component"); await uiHelper.searchInputPlaceholder(catalogRepoDetails.name); @@ -231,84 +231,85 @@ test.describe.serial("Bulk Import plugin", () => { }); }); -test.describe - .serial("Bulk Import - Verify existing repo are displayed in bulk import Added repositories", () => { - let page: Page; - let uiHelper: UIhelper; - let common: Common; - let bulkimport: BulkImport; - let catalogImport: CatalogImport; - const existingRepoFromAppConfig = "janus-test-3-bulk-import"; +// Skipping due to RHIDP-5258 +test.describe.skip( + "Bulk Import - Verify existing repo are displayed in bulk import Added repositories", + () => { + let page: Page; + let uiHelper: UIhelper; + let common: Common; + let bulkimport: BulkImport; + let catalogImport: CatalogImport; + const existingRepoFromAppConfig = "janus-test-3-bulk-import"; - const existingComponentDetails = { - name: "janus-test-2-bulk-import-test", - repoName: "janus-test-2-bulk-import-test", - url: "https://github.com/janus-test/janus-test-2-bulk-import-test/blob/main/catalog-info.yaml", - }; - test.beforeAll(async ({ browser }, testInfo) => { - page = (await setupBrowser(browser, testInfo)).page; + const existingComponentDetails = { + name: "janus-test-2-bulk-import-test", + repoName: "janus-test-2-bulk-import-test", + url: "https://github.com/janus-test/janus-test-2-bulk-import-test/blob/main/catalog-info.yaml", + }; + test.beforeAll(async ({ browser }, testInfo) => { + page = (await setupBrowser(browser, testInfo)).page; - uiHelper = new UIhelper(page); - common = new Common(page); - bulkimport = new BulkImport(page); - catalogImport = new CatalogImport(page); - await common.loginAsGithubUser(process.env.GH_USER2_ID); - }); - - test.beforeEach( - async () => await new Common(page).checkAndClickOnGHloginPopup(), - ); - - test("Verify existing repo from app-config is displayed in bulk import Added repositories", async () => { - await uiHelper.openSidebar("Bulk import"); - await common.waitForLoad(); - await bulkimport.filterAddedRepo(existingRepoFromAppConfig); - await uiHelper.verifyRowInTableByUniqueText(existingRepoFromAppConfig, [ - "Added", - ]); - }); + uiHelper = new UIhelper(page); + common = new Common(page); + bulkimport = new BulkImport(page); + catalogImport = new CatalogImport(page); + await common.loginAsKeycloakUser( + process.env.GH_USER2_ID, + process.env.GH_USER2_PASS, + ); + }); - test('Verify repo from "register existing component" are displayed in bulk import Added repositories', async () => { - // Register Existing Component - await uiHelper.openSidebar("Catalog"); - await uiHelper.clickButton("Create"); - await uiHelper.clickButton("Register Existing Component"); - await catalogImport.registerExistingComponent( - existingComponentDetails.url, - true, - ); + test("Verify existing repo from app-config is displayed in bulk import Added repositories", async () => { + await uiHelper.openSidebar("Bulk import"); + await common.waitForLoad(); + await bulkimport.filterAddedRepo(existingRepoFromAppConfig); + await uiHelper.verifyRowInTableByUniqueText(existingRepoFromAppConfig, [ + "Added", + ]); + }); - // Verify in bulk import's Added Repositories - await uiHelper.openSidebar("Bulk import"); - await common.waitForLoad(); - await bulkimport.filterAddedRepo(existingComponentDetails.repoName); - await uiHelper.verifyRowInTableByUniqueText( - existingComponentDetails.repoName, - ["Added"], - ); - }); -}); + test('Verify repo from "register existing component" are displayed in bulk import Added repositories', async () => { + // Register Existing Component + await uiHelper.openSidebar("Catalog"); + await uiHelper.clickButton("Create"); + await uiHelper.clickButton("Register Existing Component"); + await catalogImport.registerExistingComponent( + existingComponentDetails.url, + true, + ); -test.describe - .serial("Bulk Import - Ensure users without bulk import permissions cannot access the bulk import plugin", () => { - let page: Page; - let uiHelper: UIhelper; - let common: Common; - test.beforeAll(async ({ browser }, testInfo) => { - page = (await setupBrowser(browser, testInfo)).page; + // Verify in bulk import's Added Repositories + await uiHelper.openSidebar("Bulk import"); + await common.waitForLoad(); + await bulkimport.filterAddedRepo(existingComponentDetails.repoName); + await uiHelper.verifyRowInTableByUniqueText( + existingComponentDetails.repoName, + ["Added"], + ); + }); + }, +); - uiHelper = new UIhelper(page); - common = new Common(page); - await common.loginAsGuest(); - }); +// Skipping due to RHIDP-5258 +test.describe.skip( + "Bulk Import - Ensure users without bulk import permissions cannot access the bulk import plugin", + () => { + let page: Page; + let uiHelper: UIhelper; + let common: Common; + test.beforeAll(async ({ browser }, testInfo) => { + page = (await setupBrowser(browser, testInfo)).page; - test.beforeEach( - async () => await new Common(page).checkAndClickOnGHloginPopup(), - ); + uiHelper = new UIhelper(page); + common = new Common(page); + await common.loginAsGuest(); + }); - test("Bulk Import - Verify users without permission cannot access", async () => { - await uiHelper.openSidebar("Bulk import"); - await uiHelper.verifyText("Permission required"); - expect(await uiHelper.isBtnVisible("Add")).toBeFalsy(); - }); -}); + test("Bulk Import - Verify users without permission cannot access", async () => { + await uiHelper.openSidebar("Bulk import"); + await uiHelper.verifyText("Permission required"); + expect(await uiHelper.isBtnVisible("Add")).toBeFalsy(); + }); + }, +); diff --git a/e2e-tests/playwright/e2e/plugins/dynamic-plugins-info/dynamic-plugins-info.spec.ts b/e2e-tests/playwright/e2e/plugins/dynamic-plugins-info/dynamic-plugins-info.spec.ts index ffcff037a5..5d2d801c65 100644 --- a/e2e-tests/playwright/e2e/plugins/dynamic-plugins-info/dynamic-plugins-info.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/dynamic-plugins-info/dynamic-plugins-info.spec.ts @@ -68,17 +68,20 @@ test.describe("dynamic-plugins-info UI tests", () => { expect(await row.locator("td").nth(3).innerText()).toBe("Yes"); // preinstalled }); - // TODO: Add plugin-todo-list plugin in ci process to enable this test + // TODO: Enable this test once the behavior for loading this plugin is fixed. + // TODO: In RHDH 1.5, this plugin incorrectly appears as disabled despite being properly imported and explicitly enabled. test.skip("it should have a plugin-todo-list plugin which is Enabled but not Preinstalled", async ({ page, }) => { await page .getByPlaceholder("Filter") - .pressSequentially("plugin-todo-list\n", { delay: 300 }); - const row = await page.locator( - UI_HELPER_ELEMENTS.rowByText("@internal/plugin-todo-list"), + .pressSequentially("plugin-todo\n", { delay: 300 }); + + // Verify the Enabled and Preinstalled column values for the specific row + await uiHelper.verifyPluginRow( + "@backstage-community/plugin-todo", // Text to locate the row (Name column) + "Yes", // Expected value in the Enabled column + "No", // Expected value in the Preinstalled column ); - expect(await row.locator("td").nth(2).innerText()).toBe("Yes"); // enabled - expect(await row.locator("td").nth(3).innerText()).toBe("No"); // not preinstalled }); }); diff --git a/e2e-tests/playwright/e2e/plugins/http-request.spec.ts b/e2e-tests/playwright/e2e/plugins/http-request.spec.ts index 7017c1f248..98e03e4aff 100644 --- a/e2e-tests/playwright/e2e/plugins/http-request.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/http-request.spec.ts @@ -29,7 +29,7 @@ test.describe("Testing scaffolder-backend-module-http-request to invoke an exter await uiHelper.openSidebar("Catalog"); await uiHelper.selectMuiBox("Kind", "Template"); - await uiHelper.searchInputPlaceholder("Test"); + await uiHelper.searchInputPlaceholder("Test HTTP Request"); await uiHelper.clickLink("Test HTTP Request"); await uiHelper.verifyHeading("Test HTTP Request"); await uiHelper.clickLink("Launch Template"); diff --git a/e2e-tests/playwright/e2e/plugins/ocm.spec.ts b/e2e-tests/playwright/e2e/plugins/ocm.spec.ts index f0df167152..abff5a2ec4 100644 --- a/e2e-tests/playwright/e2e/plugins/ocm.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/ocm.spec.ts @@ -1,4 +1,4 @@ -import { Page, test } from "@playwright/test"; +import { expect, Page, test } from "@playwright/test"; import { Common, setupBrowser } from "../../utils/common"; import { UIhelper } from "../../utils/ui-helper"; import { Clusters } from "../../support/pages/clusters"; @@ -10,10 +10,10 @@ import { Clusters } from "../../support/pages/clusters"; const clusterDetails = { clusterName: "testCluster", status: "Ready", - platform: "IBM", + platform: /IBM|AWS|GCP/, cpuCores: /CPU cores\d+/, memorySize: /Memory size\d.*(Gi|Mi)/, - ocVersion: /^\d+\.\d+\.\d+$/, + ocVersion: /^\d+\.\d+\.\d+(Upgrade available)?$/, }; let page: Page; test.describe.serial("Test OCM plugin", () => { @@ -32,8 +32,19 @@ test.describe.serial("Test OCM plugin", () => { }); test("Navigate to Clusters and Verify OCM Clusters", async () => { await uiHelper.openSidebar("Clusters"); + const expectedPath = "/ocm"; + + // Wait for the expected path in the URL + await page.waitForURL(`**${expectedPath}`, { + waitUntil: "domcontentloaded", // Wait until the DOM is loaded + timeout: 10000, // Set timeout to 10 seconds + }); + + expect(page.url()).toContain(expectedPath); + + await uiHelper.verifyHeading("Your Managed Clusters"); await uiHelper.verifyRowInTableByUniqueText(clusterDetails.clusterName, [ - clusterDetails.status, + new RegExp(clusterDetails.status), clusterDetails.platform, ]); await uiHelper.verifyRowInTableByUniqueText(clusterDetails.clusterName, [ diff --git a/e2e-tests/playwright/e2e/plugins/quick-access-and-tech-radar.spec.ts b/e2e-tests/playwright/e2e/plugins/quick-access-and-tech-radar.spec.ts index fa3de30189..298616d2af 100644 --- a/e2e-tests/playwright/e2e/plugins/quick-access-and-tech-radar.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/quick-access-and-tech-radar.spec.ts @@ -19,7 +19,8 @@ test.describe("Test Customized Quick Access and tech-radar plugin", () => { await homePage.verifyQuickAccess("SECURITY TOOLS", "Keycloak", true); }); - test("Verify tech-radar", async ({ page }) => { + // TODO: Investigate why Tech Radar is showing "Process" instead of "Storage". + test.skip("Verify tech-radar", async ({ page }) => { const uiHelper = new UIhelper(page); const techRadar = new TechRadar(page); @@ -30,6 +31,6 @@ test.describe("Test Customized Quick Access and tech-radar plugin", () => { await techRadar.verifyRadarDetails("Languages", "JavaScript"); await techRadar.verifyRadarDetails("Storage", "AWS S3"); await techRadar.verifyRadarDetails("Frameworks", "React"); - await techRadar.verifyRadarDetails("Infrastructure", "ArgoCD"); + await techRadar.verifyRadarDetails("Infrastructure", "GitHub Actions"); }); }); diff --git a/e2e-tests/playwright/e2e/plugins/rbac/rbac.spec.ts b/e2e-tests/playwright/e2e/plugins/rbac/rbac.spec.ts index 58a84cfbd5..f45202f8aa 100644 --- a/e2e-tests/playwright/e2e/plugins/rbac/rbac.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/rbac/rbac.spec.ts @@ -83,60 +83,66 @@ test.describe.skip( }, ); -test.describe - .serial("Test RBAC plugin: Aliases used in conditional access policies", () => { - let common: Common; - let uiHelper: UIhelper; - let page: Page; - - test.beforeAll(async ({ browser }, testInfo) => { - page = (await setupBrowser(browser, testInfo)).page; +//TODO: skipping due to RHIDP-4993 +test.describe.skip( + "Test RBAC plugin: Aliases used in conditional access policies", + () => { + let common: Common; + let uiHelper: UIhelper; + let page: Page; - uiHelper = new UIhelper(page); - common = new Common(page); - await common.loginAsGithubUser(process.env.GH_USER2_ID); - }); + test.beforeAll(async ({ browser }, testInfo) => { + page = (await setupBrowser(browser, testInfo)).page; - test.beforeEach( - async () => await new Common(page).checkAndClickOnGHloginPopup(), - ); + uiHelper = new UIhelper(page); + common = new Common(page); + await common.loginAsGithubUser(process.env.GH_USER2_ID); + }); - test("Check if aliases used in conditions: the user is allowed to unregister only components they own, not those owned by the group.", async () => { - await uiHelper.openSidebar("Catalog"); - await uiHelper.selectMuiBox("Kind", "Component"); + test.beforeEach( + async () => await new Common(page).checkAndClickOnGHloginPopup(), + ); - await uiHelper.searchInputPlaceholder("test-rhdh-qe-2"); - await page - .getByRole("link", { name: "test-rhdh-qe-2", exact: true }) - .click(); + test("Check if aliases used in conditions: the user is allowed to unregister only components they own, not those owned by the group.", async () => { + await uiHelper.openSidebar("Catalog"); + await uiHelper.selectMuiBox("Kind", "Component"); - await expect(page.locator("header")).toContainText("user:rhdh-qe-2"); - await page.getByTestId("menu-button").click(); - const unregisterUserOwned = page.getByText("Unregister entity"); - await expect(unregisterUserOwned).toBeEnabled(); + await uiHelper.searchInputPlaceholder("test-rhdh-qe-2"); + await page + .getByRole("link", { name: "test-rhdh-qe-2", exact: true }) + .click(); - await page.getByText("Unregister entity").click(); - await expect(page.getByRole("heading")).toContainText( - "Are you sure you want to unregister this entity?", - ); - await page.getByRole("button", { name: "Cancel" }).click(); + await expect(page.locator("header")).toContainText("user:rhdh-qe-2"); + await page.getByTestId("menu-button").click(); + const unregisterUserOwned = page.getByText("Unregister entity"); + await expect(unregisterUserOwned).toBeEnabled(); - await uiHelper.openSidebar("Catalog"); - await page.getByRole("link", { name: "test-rhdh-qe-2-team-owned" }).click(); - await expect(page.locator("header")).toContainText( - "janus-qe/rhdh-qe-2-team", - ); - await page.getByTestId("menu-button").click(); - const unregisterGroupOwned = page.getByText("Unregister entity"); - await expect(unregisterGroupOwned).toBeDisabled(); - }); + await page.getByText("Unregister entity").click(); + await expect(page.getByRole("heading")).toContainText( + "Are you sure you want to unregister this entity?", + ); + await page.getByRole("button", { name: "Cancel" }).click(); + + await uiHelper.openSidebar("Catalog"); + await page + .getByRole("link", { name: "test-rhdh-qe-2-team-owned" }) + .click(); + await expect(page.locator("header")).toContainText( + "janus-qe/rhdh-qe-2-team", + ); + await page.getByTestId("menu-button").click(); + const unregisterGroupOwned = page.getByText("Unregister entity"); + await expect(unregisterGroupOwned).toBeDisabled(); + }); - test.afterAll(async () => { - await page.close(); - }); -}); + test.afterAll(async () => { + await page.close(); + }); + }, +); -test.describe.serial("Test RBAC plugin as an admin user", () => { +//TODO: skipping due to RHIDP-4993 +test.describe.skip("Test RBAC plugin as an admin user", () => { let common: Common; let uiHelper: UIhelper; let page: Page; diff --git a/e2e-tests/playwright/e2e/verify-tls-config-with-external-postgres-db.spec.ts b/e2e-tests/playwright/e2e/verify-tls-config-with-external-postgres-db.spec.ts index 3ecb9061cc..d8bb60fb41 100644 --- a/e2e-tests/playwright/e2e/verify-tls-config-with-external-postgres-db.spec.ts +++ b/e2e-tests/playwright/e2e/verify-tls-config-with-external-postgres-db.spec.ts @@ -3,15 +3,17 @@ import { UIhelper } from "../utils/ui-helper"; import { Common } from "../utils/common"; test.describe("Verify TLS configuration with external Postgres DB", () => { - test("Verify successful DB connection and display of expected entities in the Catalog", async ({ + test("Verify successful DB connection and display of expected entities in the Home Page and Catalog", async ({ page, }) => { const uiHelper = new UIhelper(page); const common = new Common(page); - await common.loginAsGithubUser(); - await uiHelper.openSidebar("Catalog"); + await common.loginAsKeycloakUser(); + await uiHelper.verifyHeading("Welcome back!"); + await uiHelper.verifyText("Quick Access"); + await page.getByLabel("Catalog").click(); await uiHelper.selectMuiBox("Kind", "Component"); await uiHelper.clickByDataTestId("user-picker-all"); - await uiHelper.verifyRowsInTable(["Backstage Showcase"]); + await uiHelper.verifyRowsInTable(["test-rhdh-qe-2-team-owned"]); }); }); diff --git a/e2e-tests/playwright/utils/common.ts b/e2e-tests/playwright/utils/common.ts index 8ab23743ac..01c71cee82 100644 --- a/e2e-tests/playwright/utils/common.ts +++ b/e2e-tests/playwright/utils/common.ts @@ -84,6 +84,29 @@ export class Common { }); } + async logintoKeycloak(userid: string, password: string) { + await new Promise((resolve) => { + this.page.once("popup", async (popup) => { + await popup.waitForLoadState(); + await popup.locator("#username").fill(userid); + await popup.locator("#password").fill(password); + await popup.locator("#kc-login").click(); + resolve(); + }); + }); + } + + async loginAsKeycloakUser( + userid: string = process.env.GH_USER_ID, + password: string = process.env.GH_USER_PASS, + ) { + await this.page.goto("/"); + await this.waitForLoad(240000); + await this.uiHelper.clickButton("Sign In"); + await this.logintoKeycloak(userid, password); + await this.uiHelper.waitForSideBarVisible(); + } + async loginAsGithubUser(userid: string = process.env.GH_USER_ID) { const sessionFileName = `authState_${userid}.json`; @@ -169,12 +192,17 @@ export class Common { } async clickOnGHloginPopup() { - await this.uiHelper.clickButton("Log in"); - await this.checkAndReauthorizeGithubApp(); - await this.page.waitForSelector(this.uiHelper.getButtonSelector("Log in"), { - state: "hidden", - timeout: 100000, - }); + const isLoginRequiredVisible = + await this.uiHelper.isTextVisible("Login Required"); + if (isLoginRequiredVisible) { + await this.uiHelper.clickButton("Log in"); + await this.checkAndReauthorizeGithubApp(); + await this.uiHelper.waitForLoginBtnDisappear(); + } else { + console.log( + '"Log in" button is not visible. Skipping login popup actions.', + ); + } } getGitHub2FAOTP(userid: string): string { diff --git a/e2e-tests/playwright/utils/ui-helper.ts b/e2e-tests/playwright/utils/ui-helper.ts index 1773265f7f..9d2f5167f2 100644 --- a/e2e-tests/playwright/utils/ui-helper.ts +++ b/e2e-tests/playwright/utils/ui-helper.ts @@ -22,8 +22,22 @@ export class UIhelper { await this.page.getByLabel(label).fill(text); } + /** + * Fills the search input with the provided text and waits for at least one + * search result containing the text to become visible. + * + * @param searchText - The text to be entered into the search input field. + */ async searchInputPlaceholder(searchText: string) { - await this.page.fill('input[placeholder="Search"]', searchText); + // Wait for the search input to be visible + const searchInput = this.page.locator('input[placeholder="Search"]'); + await expect(searchInput).toBeVisible(); + + await searchInput.fill(searchText); + + // Wait for at least one search result to become visible + const resultLocator = this.page.locator(`text=${searchText}`); + await expect(resultLocator.first()).toBeVisible(); } async filterInputPlaceholder(searchText: string) { @@ -70,10 +84,8 @@ export class UIhelper { await element.click(); } - async verifyDivHasText(divText: string) { - await expect( - this.page.locator(`div`).filter({ hasText: divText }), - ).toBeVisible(); + async verifyDivHasText(divText: string | RegExp) { + await expect(this.page.locator(`div`).getByText(divText)).toBeVisible(); } async clickLink(linkText: string) { @@ -98,7 +110,6 @@ export class UIhelper { if (options?.notVisible) { await expect(linkLocator).not.toBeVisible(); } else { - await linkLocator.scrollIntoViewIfNeeded(); await expect(linkLocator).toBeVisible(); } } @@ -277,6 +288,16 @@ export class UIhelper { return `${UI_HELPER_ELEMENTS.MuiButtonLabel}:has-text("${label}")`; } + getLoginBtnSelector(): string { + return 'MuiListItem-root li.MuiListItem-root button.MuiButton-root:has(span.MuiButton-label:text("Log in"))'; + } + + async waitForLoginBtnDisappear() { + await this.page.waitForSelector(await this.getLoginBtnSelector(), { + state: "detached", + }); + } + async verifyButtonURL(label: string | RegExp, url: string | RegExp) { const buttonUrl = await this.page .getByRole("button", { name: label }) @@ -494,4 +515,28 @@ export class UIhelper { await deleteButton.waitFor({ state: "attached" }); await deleteButton.click(); } + + /** + * Verifies the values of the Enabled and Preinstalled columns for a specific row. + * + * @param text - Text to locate the specific row (based on the Name column). + * @param expectedEnabled - Expected value for the Enabled column ("Yes" or "No"). + * @param expectedPreinstalled - Expected value for the Preinstalled column ("Yes" or "No"). + */ + async verifyPluginRow( + text: string, + expectedEnabled: string, + expectedPreinstalled: string, + ) { + // Locate the row based on the text in the Name column + const rowSelector = `tr:has(td:text-is("${text}"))`; + const row = this.page.locator(rowSelector); + + // Locate the "Enabled" (3rd column) and "Preinstalled" (4th column) cells by their index + const enabledColumn = row.locator("td").nth(2); // Index 2 for "Enabled" + const preinstalledColumn = row.locator("td").nth(3); // Index 3 for "Preinstalled" + + await expect(enabledColumn).toHaveText(expectedEnabled); + await expect(preinstalledColumn).toHaveText(expectedPreinstalled); + } } diff --git a/package.json b/package.json index 56cc8e1f28..87d3d19105 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,8 @@ "typescript": "5.6.3" }, "resolutions": { - "@types/react": "18.3.12", - "@types/react-dom": "18.3.1", + "@types/react": "18.3.16", + "@types/react-dom": "18.3.5", "@backstage/backend-dynamic-feature-service@0.4.4": "patch:@backstage/backend-dynamic-feature-service@npm%3A0.4.4#./.yarn/patches/@backstage-backend-dynamic-feature-service-npm-0.4.4.patch", "@backstage/plugin-auth-backend-module-oidc-provider@0.3.1": "patch:@backstage/plugin-auth-backend-module-oidc-provider@npm%3A0.3.1#./.yarn/patches/@backstage-plugin-auth-backend-module-oidc-provider-npm-0.3.1.patch", "@backstage/plugin-auth-backend-module-oidc-provider@^0.3.1": "patch:@backstage/plugin-auth-backend-module-oidc-provider@npm%3A0.3.1#./.yarn/patches/@backstage-plugin-auth-backend-module-oidc-provider-npm-0.3.1.patch", @@ -63,10 +63,12 @@ "@backstage/plugin-scaffolder-backend@^1.24.0": "patch:@backstage/plugin-scaffolder-backend@npm%3A1.26.2#./.yarn/patches/@backstage-plugin-scaffolder-backend-npm-1.26.2.patch", "@backstage/plugin-scaffolder-backend@^1.22.6": "patch:@backstage/plugin-scaffolder-backend@npm%3A1.26.2#./.yarn/patches/@backstage-plugin-scaffolder-backend-npm-1.26.2.patch", "@backstage/plugin-scaffolder-backend@1.26.2": "patch:@backstage/plugin-scaffolder-backend@npm%3A1.26.2#./.yarn/patches/@backstage-plugin-scaffolder-backend-npm-1.26.2.patch", - "@backstage/plugin-scaffolder-node@^0.5.0": "patch:@backstage/plugin-scaffolder-node@npm%3A0.5.0#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.5.0.patch", - "@backstage/plugin-scaffolder-node@^0.4.9": "patch:@backstage/plugin-scaffolder-node@npm%3A0.5.0#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.5.0.patch", - "@backstage/plugin-scaffolder-node@^0.2.9": "patch:@backstage/plugin-scaffolder-node@npm%3A0.5.0#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.5.0.patch", - "@backstage/plugin-scaffolder-node@^0.4.8": "patch:@backstage/plugin-scaffolder-node@npm%3A0.5.0#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.5.0.patch" + "@backstage/plugin-scaffolder-node@^0.5.0": "patch:@backstage/plugin-scaffolder-node@npm%3A0.6.1#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch", + "@backstage/plugin-scaffolder-node@^0.4.9": "patch:@backstage/plugin-scaffolder-node@npm%3A0.6.1#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch", + "@backstage/plugin-scaffolder-node@^0.2.9": "patch:@backstage/plugin-scaffolder-node@npm%3A0.6.1#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch", + "@backstage/plugin-scaffolder-node@^0.4.8": "patch:@backstage/plugin-scaffolder-node@npm%3A0.6.1#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch", + "@backstage/plugin-scaffolder-node@^0.6.0": "patch:@backstage/plugin-scaffolder-node@npm%3A0.6.1#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch", + "@backstage/plugin-scaffolder-node@^0.5.1": "patch:@backstage/plugin-scaffolder-node@npm%3A0.6.1#./.yarn/patches/@backstage-plugin-scaffolder-node-npm-0.6.1-a2cc13065e.patch" }, "jest": { "testTimeout": 20000 diff --git a/packages/app/package.json b/packages/app/package.json index acade16972..9b4d0fd724 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -38,13 +38,13 @@ "@backstage/plugin-search-react": "1.8.1", "@backstage/plugin-user-settings": "0.8.14", "@backstage/theme": "0.6.0", - "@emotion/react": "11.13.3", - "@emotion/styled": "11.13.0", + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.0", "@internal/plugin-dynamic-plugins-info": "*", "@janus-idp/backstage-plugin-rbac-common": "1.12.0", - "@mui/icons-material": "5.16.7", - "@mui/material": "5.16.7", - "@mui/styled-engine": "5.16.6", + "@mui/icons-material": "5.16.9", + "@mui/material": "5.16.9", + "@mui/styled-engine": "5.16.8", "@redhat-developer/red-hat-developer-hub-theme": "0.4.0", "@scalprum/core": "0.8.1", "@scalprum/react-core": "0.9.3", @@ -65,10 +65,10 @@ "@testing-library/react": "14.3.1", "@testing-library/react-hooks": "8.0.1", "@testing-library/user-event": "14.5.2", - "@types/node": "20.17.9", - "@types/react": "18.3.12", - "@types/react-dom": "18.3.1", - "prettier": "3.4.1" + "@types/node": "20.17.10", + "@types/react": "18.3.16", + "@types/react-dom": "18.3.5", + "prettier": "3.4.2" }, "browserslist": { "production": [ diff --git a/packages/app/src/components/UserSettings/GeneralPage.tsx b/packages/app/src/components/UserSettings/GeneralPage.tsx index a0a8438064..a8e2452258 100644 --- a/packages/app/src/components/UserSettings/GeneralPage.tsx +++ b/packages/app/src/components/UserSettings/GeneralPage.tsx @@ -6,9 +6,9 @@ import { import Grid from '@mui/material/Grid'; -import { infoCard } from './InfoCard'; +import { InfoCard } from './InfoCard'; -export const generalPage = ( +export const GeneralPage = () => ( @@ -20,7 +20,7 @@ export const generalPage = ( - {infoCard} + ); diff --git a/packages/app/src/components/UserSettings/InfoCard.test.tsx b/packages/app/src/components/UserSettings/InfoCard.test.tsx new file mode 100644 index 0000000000..a8bc659658 --- /dev/null +++ b/packages/app/src/components/UserSettings/InfoCard.test.tsx @@ -0,0 +1,20 @@ +import { renderInTestApp } from '@backstage/test-utils'; + +import { userEvent } from '@testing-library/user-event'; + +import { InfoCard } from './InfoCard'; + +describe('InfoCard', () => { + it('should render essential versions by default', async () => { + const renderResult = await renderInTestApp(); + expect(renderResult.getByText(/RHDH Version/)).toBeInTheDocument(); + expect(renderResult.getByText(/Backstage Version/)).toBeInTheDocument(); + }); + + it('should hide the build time by default and show it on click', async () => { + const renderResult = await renderInTestApp(); + expect(renderResult.queryByText(/Last Commit/)).toBeNull(); + await userEvent.click(renderResult.getByText(/RHDH Version/)); + expect(renderResult.getByText(/Last Commit/)).toBeInTheDocument(); + }); +}); diff --git a/packages/app/src/components/UserSettings/InfoCard.tsx b/packages/app/src/components/UserSettings/InfoCard.tsx index a3977cc8e9..5abbb1eb5c 100644 --- a/packages/app/src/components/UserSettings/InfoCard.tsx +++ b/packages/app/src/components/UserSettings/InfoCard.tsx @@ -1,20 +1,122 @@ -import { InfoCard as BSInfoCard } from '@backstage/core-components'; +import React from 'react'; -import Grid from '@mui/material/Grid'; +import { + InfoCard as BSInfoCard, + CopyTextButton, +} from '@backstage/core-components'; + +import UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; +import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; +import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; import buildMetadata from '../../build-metadata.json'; -export const infoCard = ( - - - {buildMetadata.card.map(text => ( - - - {text} - - - ))} - - -); +export const InfoCard = () => { + const [showBuildInformation, setShowBuildInformation] = + React.useState( + () => + localStorage.getItem('rhdh-infocard-show-build-information') === 'true', + ); + + const toggleBuildInformation = () => { + setShowBuildInformation(!showBuildInformation); + try { + if (showBuildInformation) { + localStorage.removeItem('rhdh-infocard-show-build-information'); + } else { + localStorage.setItem('rhdh-infocard-show-build-information', 'true'); + } + } catch (e) { + // ignore + } + }; + + let clipboardText = buildMetadata.title; + if (buildMetadata.card?.length) { + clipboardText += '\n\n'; + buildMetadata.card.forEach(text => { + clipboardText += `${text}\n`; + }); + } + + const filteredCards = showBuildInformation + ? buildMetadata.card + : buildMetadata.card.filter( + text => + text.startsWith('RHDH Version') || + text.startsWith('Backstage Version'), + ); + // Ensure that we show always some information + const versionInfo = + filteredCards.length > 0 ? filteredCards.join('\n') : buildMetadata.card[0]; + + /** + * Show all build information and automatically select them + * when the user selects the version with the mouse. + */ + const onMouseUp = (event: React.MouseEvent) => { + if (!showBuildInformation) { + setShowBuildInformation(true); + window.getSelection()?.selectAllChildren(event.target as Node); + } + }; + + /** + * Show all build information and automatically select them + * when the user selects the version with the keyboard (tab) + * and presses the space key or the Ctrl+C key combination. + */ + const onKeyDown = (event: React.KeyboardEvent) => { + if ( + event.key === ' ' || + (event.key === 'c' && event.ctrlKey) || + (event.key === 'C' && event.ctrlKey) + ) { + setShowBuildInformation(true); + window.getSelection()?.selectAllChildren(event.target as Node); + } + }; + + return ( + +
+ + + {showBuildInformation ? : } + +
+ + } + > + + {versionInfo} + +
+ ); +}; diff --git a/packages/app/src/components/UserSettings/SettingsPages.tsx b/packages/app/src/components/UserSettings/SettingsPages.tsx index 3b9b2963a4..2d04ae111e 100644 --- a/packages/app/src/components/UserSettings/SettingsPages.tsx +++ b/packages/app/src/components/UserSettings/SettingsPages.tsx @@ -3,12 +3,12 @@ import { UserSettingsAuthProviders, } from '@backstage/plugin-user-settings'; -import { generalPage } from './GeneralPage'; +import { GeneralPage } from './GeneralPage'; export const settingsPage = ( - {generalPage} + =16.8.0" peerDependenciesMeta: "@types/react": optional: true - checksum: f5b951059418f57bc8ea32b238afb25965ece3314f2ffd1b14ce049ba3c066a424990dfbfabbf57bb88e044eaa80bf19f620ac988adda3d2fc483177be6da05e + checksum: 9c1b842e942e69fb6037d1ab161046d2bcfeff95fd2ccfdab30acaaf6b89dc07b14bb00f8cc8ec14df11e6746c8e4e1d781bc54d10bd739aab44966ded64d4fb languageName: node linkType: hard @@ -9144,19 +9092,19 @@ __metadata: languageName: node linkType: hard -"@emotion/use-insertion-effect-with-fallbacks@npm:^1.1.0": - version: 1.1.0 - resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.1.0" +"@emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0": + version: 1.2.0 + resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.2.0" peerDependencies: react: ">=16.8.0" - checksum: 63665191773b27de66807c53b90091ef0d10d5161381f62726cfceecfe1d8c944f18594b8021805fc81575b64246fd5ab9c75d60efabec92f940c1c410530949 + checksum: 8ff6aec7f2924526ff8c8f8f93d4b8236376e2e12c435314a18c9a373016e24dfdf984e82bbc83712b8e90ff4783cd765eb39fc7050d1a43245e5728740ddd71 languageName: node linkType: hard -"@emotion/utils@npm:*, @emotion/utils@npm:^1.4.0": - version: 1.4.0 - resolution: "@emotion/utils@npm:1.4.0" - checksum: 212af0b0d6bcaa63c76e1a36e35bee4d3579359316c03bf970faabb5427a4c0aab3e2346a721bac54f0c8e027958e759c5682be78f308755a1d9753e83963621 +"@emotion/utils@npm:*, @emotion/utils@npm:^1.4.2": + version: 1.4.2 + resolution: "@emotion/utils@npm:1.4.2" + checksum: 04cf76849c6401205c058b82689fd0ec5bf501aed6974880fe9681a1d61543efb97e848f4c38664ac4a9068c7ad2d1cb84f73bde6cf95f1208aa3c28e0190321 languageName: node linkType: hard @@ -10788,7 +10736,7 @@ __metadata: express: 4.21.0 msw: 1.3.4 node-fetch: 2.7.0 - prettier: 3.4.1 + prettier: 3.4.2 supertest: 6.3.4 winston: 3.14.2 languageName: unknown @@ -10806,14 +10754,14 @@ __metadata: "@backstage/test-utils": 1.7.0 "@backstage/theme": 0.6.0 "@material-table/core": 3.2.5 - "@mui/icons-material": 5.16.7 - "@mui/material": 5.16.7 + "@mui/icons-material": 5.16.9 + "@mui/material": 5.16.9 "@testing-library/jest-dom": 6.6.3 "@testing-library/react": 14.3.1 "@testing-library/user-event": 14.5.2 glob: 11.0.0 msw: 1.3.4 - prettier: 3.4.1 + prettier: 3.4.2 react: 18.3.1 react-router-dom: 6.26.2 react-use: 17.5.1 @@ -10849,7 +10797,7 @@ __metadata: luxon: 3.5.0 msw: 1.3.4 node-fetch: 2.7.0 - prettier: 3.4.1 + prettier: 3.4.2 supertest: 6.3.4 languageName: unknown linkType: soft @@ -10871,7 +10819,7 @@ __metadata: mock-fs: 5.4.1 msw: 1.3.4 node-fetch: 2.7.0 - prettier: 3.4.1 + prettier: 3.4.2 supertest: 6.3.4 winston: 3.14.2 languageName: unknown @@ -12480,10 +12428,10 @@ __metadata: languageName: node linkType: hard -"@mui/core-downloads-tracker@npm:^5.16.7": - version: 5.16.7 - resolution: "@mui/core-downloads-tracker@npm:5.16.7" - checksum: b65c48ba2bf6bba6435ba9f2d6c33db0c8a85b3ff7599136a9682b72205bec76470ab5ed5e6e625d5bd012ed9bcbc641ed677548be80d217c9fb5d0435567062 +"@mui/core-downloads-tracker@npm:^5.16.7, @mui/core-downloads-tracker@npm:^5.16.9": + version: 5.16.9 + resolution: "@mui/core-downloads-tracker@npm:5.16.9" + checksum: 25e7cf746627e12671e2bae4ea8f81967fbb7e05188c268052104d05f249eea1baa3f3d97f66d1242112afa944a7b025a331527392797d620acbcc1dde23a3df languageName: node linkType: hard @@ -12519,19 +12467,19 @@ __metadata: languageName: node linkType: hard -"@mui/icons-material@npm:5.16.7, @mui/icons-material@npm:^5.15.19, @mui/icons-material@npm:^5.16.4, @mui/icons-material@npm:^5.16.7": - version: 5.16.7 - resolution: "@mui/icons-material@npm:5.16.7" +"@mui/icons-material@npm:5.16.9, @mui/icons-material@npm:^5.15.19, @mui/icons-material@npm:^5.16.4, @mui/icons-material@npm:^5.16.7": + version: 5.16.9 + resolution: "@mui/icons-material@npm:5.16.9" dependencies: "@babel/runtime": ^7.23.9 peerDependencies: "@mui/material": ^5.0.0 - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: a875f2837897d79a83173d80461e06ab090b64d08913d26433cf2cbeb8e7c7456468632a7aa495d722718f09111a8043255777d73b4dfbe9e0f863a170fc7190 + checksum: 84e6884de333afbc819f2871d4449a00960a8c080bfdcb5e9c621a7727179ce2ce97097dc1e4dbcda7d6b234a09aa7593ab6cd172a323429f562625d70511545 languageName: node linkType: hard @@ -12580,7 +12528,7 @@ __metadata: languageName: node linkType: hard -"@mui/material@npm:5.16.7, @mui/material@npm:^5.12.2, @mui/material@npm:^5.14.18, @mui/material@npm:^5.15.17, @mui/material@npm:^5.15.19, @mui/material@npm:^5.16.7": +"@mui/material@npm:5.16.7": version: 5.16.7 resolution: "@mui/material@npm:5.16.7" dependencies: @@ -12613,20 +12561,53 @@ __metadata: languageName: node linkType: hard -"@mui/private-theming@npm:^5.16.6": - version: 5.16.6 - resolution: "@mui/private-theming@npm:5.16.6" +"@mui/material@npm:5.16.9, @mui/material@npm:^5.12.2, @mui/material@npm:^5.14.18, @mui/material@npm:^5.15.17, @mui/material@npm:^5.15.19, @mui/material@npm:^5.16.7": + version: 5.16.9 + resolution: "@mui/material@npm:5.16.9" dependencies: "@babel/runtime": ^7.23.9 - "@mui/utils": ^5.16.6 + "@mui/core-downloads-tracker": ^5.16.9 + "@mui/system": ^5.16.8 + "@mui/types": ^7.2.15 + "@mui/utils": ^5.16.8 + "@popperjs/core": ^2.11.8 + "@types/react-transition-group": ^4.4.10 + clsx: ^2.1.0 + csstype: ^3.1.3 prop-types: ^15.8.1 + react-is: ^18.3.1 + react-transition-group: ^4.4.5 peerDependencies: - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 + "@emotion/react": ^11.5.0 + "@emotion/styled": ^11.3.0 + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + "@types/react": + optional: true + checksum: e3692ba08add0a4b959d7e5f754cf5bf934d30798a9256d1eb5bbf37021f81cf6cc0b86a3cb065815af7f2112c6a359e463a6641618d07559f93516e1374669e + languageName: node + linkType: hard + +"@mui/private-theming@npm:^5.16.6, @mui/private-theming@npm:^5.16.8": + version: 5.16.8 + resolution: "@mui/private-theming@npm:5.16.8" + dependencies: + "@babel/runtime": ^7.23.9 + "@mui/utils": ^5.16.8 + prop-types: ^15.8.1 + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 314ba598ab17cd425a36e4cab677ed26fe0939b23e53120da77cfbc3be6dada5428fa8e2a55cb697417599a4e3abfee6d4711de0a7318b9fb2c3a822b2d5b5a8 + checksum: 576cf19188c236482a8a698978d3061c7e8d76ba9d891d323ce96d92d6b80d88e1aa2a67fe4698d76b8a43221ef0f8ed21be74971fc684ce1279dc73fd34316b languageName: node linkType: hard @@ -12647,9 +12628,9 @@ __metadata: languageName: node linkType: hard -"@mui/styled-engine@npm:5.16.6, @mui/styled-engine@npm:^5.16.6": - version: 5.16.6 - resolution: "@mui/styled-engine@npm:5.16.6" +"@mui/styled-engine@npm:5.16.8, @mui/styled-engine@npm:^5.16.8": + version: 5.16.8 + resolution: "@mui/styled-engine@npm:5.16.8" dependencies: "@babel/runtime": ^7.23.9 "@emotion/cache": ^11.11.0 @@ -12658,13 +12639,13 @@ __metadata: peerDependencies: "@emotion/react": ^11.4.1 "@emotion/styled": ^11.3.0 - react: ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@emotion/react": optional: true "@emotion/styled": optional: true - checksum: 604f83b91801945336db211a8273061132668d01e9f456c30bb811a3b49cc5786b8b7dd8e0b5b89de15f6209abc900d9e679d3ae7a4651a6df45e323b6ed95c5 + checksum: 8eab246e985bbe2707af4620a3193166bacefb029df65ac846cfc0ac5d7996fcb72c50327241f60913538f58a2ad9aa6de3ef12b72eb7750c71f8d3c284a420b languageName: node linkType: hard @@ -12730,23 +12711,23 @@ __metadata: languageName: node linkType: hard -"@mui/system@npm:^5.16.5, @mui/system@npm:^5.16.7": - version: 5.16.7 - resolution: "@mui/system@npm:5.16.7" +"@mui/system@npm:^5.16.5, @mui/system@npm:^5.16.7, @mui/system@npm:^5.16.8": + version: 5.16.8 + resolution: "@mui/system@npm:5.16.8" dependencies: "@babel/runtime": ^7.23.9 - "@mui/private-theming": ^5.16.6 - "@mui/styled-engine": ^5.16.6 + "@mui/private-theming": ^5.16.8 + "@mui/styled-engine": ^5.16.8 "@mui/types": ^7.2.15 - "@mui/utils": ^5.16.6 + "@mui/utils": ^5.16.8 clsx: ^2.1.0 csstype: ^3.1.3 prop-types: ^15.8.1 peerDependencies: "@emotion/react": ^11.5.0 "@emotion/styled": ^11.3.0 - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@emotion/react": optional: true @@ -12754,7 +12735,7 @@ __metadata: optional: true "@types/react": optional: true - checksum: 86cc11d062645b6742328178ca3a9e2aa2c6d064a559e4fb8c6c6bb8251794959b9dad385f9508fdcab2ae2764503c80f7c3d4f6eb1e0e8aa649f28d4f59133b + checksum: 8082b176009fa907cdb0391db59db6cd3589659ad92497caec998c01e1683978ab2eda4ece4d12e523d233b062aa5946d4a438d9a22bf172f0568be1db773e9c languageName: node linkType: hard @@ -12770,9 +12751,9 @@ __metadata: languageName: node linkType: hard -"@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.15.14, @mui/utils@npm:^5.16.5, @mui/utils@npm:^5.16.6": - version: 5.16.6 - resolution: "@mui/utils@npm:5.16.6" +"@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.15.14, @mui/utils@npm:^5.16.5, @mui/utils@npm:^5.16.6, @mui/utils@npm:^5.16.8": + version: 5.16.8 + resolution: "@mui/utils@npm:5.16.8" dependencies: "@babel/runtime": ^7.23.9 "@mui/types": ^7.2.15 @@ -12781,12 +12762,12 @@ __metadata: prop-types: ^15.8.1 react-is: ^18.3.1 peerDependencies: - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 6f8068f07f60a842fcb2e2540eecbd9c5f04df695bcc427184720e8ae138ae689fefd3c20147ab7c76e809ede6e10f5e08d1c34cd3a8b09bd22d2020a666a96f + checksum: ed714c6aa583bdf17af9f523c9b9ba3789a47677aa6408bc2afd842fa9eaa6d111361c1309435d481ea03b8c695e22ead98c43dc13239c63a3da481c95e5ad72 languageName: node linkType: hard @@ -13940,7 +13921,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api@npm:^1.0.0, @opentelemetry/api@npm:^1.0.1, @opentelemetry/api@npm:^1.3.0, @opentelemetry/api@npm:^1.4.0": +"@opentelemetry/api@npm:1.9.0, @opentelemetry/api@npm:^1.0.0, @opentelemetry/api@npm:^1.0.1, @opentelemetry/api@npm:^1.3.0, @opentelemetry/api@npm:^1.4.0": version: 1.9.0 resolution: "@opentelemetry/api@npm:1.9.0" checksum: 9e88e59d53ced668f3daaecfd721071c5b85a67dd386f1c6f051d1be54375d850016c881f656ffbe9a03bedae85f7e89c2f2b635313f9c9b195ad033cdc31020 @@ -15143,6 +15124,13 @@ __metadata: languageName: node linkType: hard +"@patternfly/patternfly@npm:^6.0.0": + version: 6.0.0 + resolution: "@patternfly/patternfly@npm:6.0.0" + checksum: 035ce47e43ed408fed1116721b0217855d4d522305fd0ad9a162798bdccecb980558a730f11b9a9c180a1b8f885a322752aa987a94a43562197a2fcae1a0464b + languageName: node + linkType: hard + "@patternfly/react-charts@npm:^7.1.1": version: 7.3.0 resolution: "@patternfly/react-charts@npm:7.3.0" @@ -15193,6 +15181,23 @@ __metadata: languageName: node linkType: hard +"@patternfly/react-core@npm:^6.0.0": + version: 6.0.0 + resolution: "@patternfly/react-core@npm:6.0.0" + dependencies: + "@patternfly/react-icons": ^6.0.0 + "@patternfly/react-styles": ^6.0.0 + "@patternfly/react-tokens": ^6.0.0 + focus-trap: 7.6.0 + react-dropzone: ^14.2.3 + tslib: ^2.7.0 + peerDependencies: + react: ^17 || ^18 + react-dom: ^17 || ^18 + checksum: 196ca777d06df73945cfa0c8c11d3d54d303012bf5b15e67fabb6b996c217a5326b58a7be6c9eaf2a7055391b71065c27ab34ddc4b88fd6cc69760b237444864 + languageName: node + linkType: hard + "@patternfly/react-icons@npm:^5.1.1, @patternfly/react-icons@npm:^5.4.2": version: 5.4.2 resolution: "@patternfly/react-icons@npm:5.4.2" @@ -15203,6 +15208,16 @@ __metadata: languageName: node linkType: hard +"@patternfly/react-icons@npm:^6.0.0": + version: 6.0.0 + resolution: "@patternfly/react-icons@npm:6.0.0" + peerDependencies: + react: ^17 || ^18 + react-dom: ^17 || ^18 + checksum: 39ddcda5a0f0a3840cc8b499a9b68f0ea133ea369a6412dfaabb38af0095ed9eef5df3171de3d0990dc0130720d4fbe3e6b28d70fdb6668c1507cd1cbdc6750d + languageName: node + linkType: hard + "@patternfly/react-styles@npm:^5.1.1, @patternfly/react-styles@npm:^5.3.0, @patternfly/react-styles@npm:^5.4.1": version: 5.4.1 resolution: "@patternfly/react-styles@npm:5.4.1" @@ -15210,6 +15225,13 @@ __metadata: languageName: node linkType: hard +"@patternfly/react-styles@npm:^6.0.0": + version: 6.0.0 + resolution: "@patternfly/react-styles@npm:6.0.0" + checksum: 40f6e169eea6b412e8bea3c220c4e29923106fa65363c5e07068db37eaa55f6088411ed0bd36c9befaa3486f108ca8d097ee39bd55276855a4387e6713d484c8 + languageName: node + linkType: hard + "@patternfly/react-table@npm:^5.1.2": version: 5.4.9 resolution: "@patternfly/react-table@npm:5.4.9" @@ -15234,6 +15256,13 @@ __metadata: languageName: node linkType: hard +"@patternfly/react-tokens@npm:^6.0.0": + version: 6.0.0 + resolution: "@patternfly/react-tokens@npm:6.0.0" + checksum: 3390651a7ea0d2664adb1f760cb54e40200ac63b54269ba3f28002c01cd108c57a7bf142c894cfc510f400c950c239f93fb9ca47c00515dda44ec386b6aec972 + languageName: node + linkType: hard + "@patternfly/react-topology@npm:^5.1.0": version: 5.4.0 resolution: "@patternfly/react-topology@npm:5.4.0" @@ -20063,12 +20092,12 @@ __metadata: languageName: node linkType: hard -"@types/multer@npm:^1.4.7": - version: 1.4.11 - resolution: "@types/multer@npm:1.4.11" +"@types/multer@npm:^1.4.12": + version: 1.4.12 + resolution: "@types/multer@npm:1.4.12" dependencies: "@types/express": "*" - checksum: 3d80b2acdfbc9f3e9027d4467e948925810b67e5622a3017f42f58a3598d34b25376890801e55d0c03973ccc34573abf5218af334e8292ec455832f4ade3e5f5 + checksum: 719cacf88ec83ed77e250e45bee830fd7a505323825efa2a2c1144f5f3d7d36e67408ec988e571bcbaa571e0c214b5ede42d57ebb0f9b453a5eb8faba8ff12d0 languageName: node linkType: hard @@ -20109,12 +20138,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:20.17.9, @types/node@npm:^20.1.1": - version: 20.17.9 - resolution: "@types/node@npm:20.17.9" +"@types/node@npm:20.17.10": + version: 20.17.10 + resolution: "@types/node@npm:20.17.10" dependencies: undici-types: ~6.19.2 - checksum: 2fc67ba937d2c4e7a52f0ccf71b8b4c616dcfa1ad6cd5a726582fd3cbf4f409c2eb44595592580f782c2ade05f8130df072dd04ac064fe150cfcd7849e643500 + checksum: 44cfa7cd9a4ebb8f74efa4b89cf963ca0e522121a7d24d8121d40872bbcfd607eaccdc203c4fe92c8b587125be9ca7b071fe4f9b356f263434b8a8512dbebef0 languageName: node linkType: hard @@ -20141,6 +20170,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^20.1.1": + version: 20.17.9 + resolution: "@types/node@npm:20.17.9" + dependencies: + undici-types: ~6.19.2 + checksum: 2fc67ba937d2c4e7a52f0ccf71b8b4c616dcfa1ad6cd5a726582fd3cbf4f409c2eb44595592580f782c2ade05f8130df072dd04ac064fe150cfcd7849e643500 + languageName: node + linkType: hard + "@types/oauth@npm:*": version: 0.9.4 resolution: "@types/oauth@npm:0.9.4" @@ -20257,12 +20295,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:18.3.1": - version: 18.3.1 - resolution: "@types/react-dom@npm:18.3.1" - dependencies: - "@types/react": "*" - checksum: ad28ecce3915d30dc76adc2a1373fda1745ba429cea290e16c6628df9a05fd80b6403c8e87d78b45e6c60e51df7a67add389ab62b90070fbfdc9bda8307d9953 +"@types/react-dom@npm:18.3.5": + version: 18.3.5 + resolution: "@types/react-dom@npm:18.3.5" + peerDependencies: + "@types/react": ^18.0.0 + checksum: 95c757684f71e761515c5a11299e5feec550c72bb52975487f360e6f0d359b26454c26eaf2ce45dd22748205aa9b2c2fe0abe7005ebcbd233a7615283ac39a7d languageName: node linkType: hard @@ -20305,13 +20343,13 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:18.3.12": - version: 18.3.12 - resolution: "@types/react@npm:18.3.12" +"@types/react@npm:18.3.16": + version: 18.3.16 + resolution: "@types/react@npm:18.3.16" dependencies: "@types/prop-types": "*" csstype: ^3.0.2 - checksum: 4ab1577a8c2105a5e316536f724117c90eee5f4bd5c137fc82a2253d8c1fd299dedaa07e8dfc95d6e2f04a4be3cb8b0e1b06098c6233ebd55c508d88099395b7 + checksum: 467c2a325870580b88b4e3bf439749b51b27cb13f52408653cb8c3e7e1b7eff86ada87e384b1aa4d34aa6027c187ca27df00bea77140fda524d726992f5b93ef languageName: node linkType: hard @@ -21435,7 +21473,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.1.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0, ajv@npm:^8.11.2, ajv@npm:^8.12.0, ajv@npm:^8.16.0, ajv@npm:^8.6.0, ajv@npm:^8.6.2, ajv@npm:^8.6.3, ajv@npm:^8.9.0": +"ajv@npm:^8.0.0, ajv@npm:^8.1.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0, ajv@npm:^8.12.0, ajv@npm:^8.16.0, ajv@npm:^8.17.1, ajv@npm:^8.6.0, ajv@npm:^8.6.2, ajv@npm:^8.6.3, ajv@npm:^8.9.0": version: 8.17.1 resolution: "ajv@npm:8.17.1" dependencies: @@ -21591,14 +21629,14 @@ __metadata: "@backstage/plugin-user-settings": 0.8.14 "@backstage/test-utils": 1.7.0 "@backstage/theme": 0.6.0 - "@emotion/react": 11.13.3 - "@emotion/styled": 11.13.0 + "@emotion/react": 11.14.0 + "@emotion/styled": 11.14.0 "@internal/plugin-dynamic-plugins-info": "*" "@janus-idp/backstage-plugin-rbac-common": 1.12.0 "@janus-idp/cli": 1.18.5 - "@mui/icons-material": 5.16.7 - "@mui/material": 5.16.7 - "@mui/styled-engine": 5.16.6 + "@mui/icons-material": 5.16.9 + "@mui/material": 5.16.9 + "@mui/styled-engine": 5.16.8 "@redhat-developer/red-hat-developer-hub-theme": 0.4.0 "@scalprum/core": 0.8.1 "@scalprum/react-core": 0.9.3 @@ -21608,11 +21646,11 @@ __metadata: "@testing-library/react": 14.3.1 "@testing-library/react-hooks": 8.0.1 "@testing-library/user-event": 14.5.2 - "@types/node": 20.17.9 - "@types/react": 18.3.12 - "@types/react-dom": 18.3.1 + "@types/node": 20.17.10 + "@types/react": 18.3.16 + "@types/react-dom": 18.3.5 lodash: 4.17.21 - prettier: 3.4.1 + prettier: 3.4.2 react: 18.3.1 react-dom: 18.3.1 react-router-dom: 6.26.2 @@ -22391,6 +22429,7 @@ __metadata: "@internal/plugin-licensed-users-info-backend": "*" "@internal/plugin-scalprum-backend": "*" "@janus-idp/backstage-plugin-audit-log-node": 1.7.0 + "@opentelemetry/api": 1.9.0 "@opentelemetry/auto-instrumentations-node": 0.50.2 "@opentelemetry/exporter-prometheus": 0.53.0 "@opentelemetry/host-metrics": 0.35.4 @@ -22401,7 +22440,7 @@ __metadata: "@types/global-agent": 2.1.3 app: "*" global-agent: 3.0.0 - prettier: 3.4.1 + prettier: 3.4.2 undici: 6.19.8 winston: 3.14.2 languageName: unknown @@ -22442,8 +22481,8 @@ __metadata: "@backstage/cli": 0.28.2 "@backstage/core-plugin-api": 1.10.0 "@janus-idp/cli": 1.18.5 - "@mui/icons-material": 5.16.7 - "@mui/material": 5.16.7 + "@mui/icons-material": 5.16.9 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22576,7 +22615,7 @@ __metadata: "@backstage-community/plugin-jfrog-artifactory": 1.10.2 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22588,7 +22627,7 @@ __metadata: "@backstage-community/plugin-lighthouse": 0.4.24 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/icons-material": 5.16.7 + "@mui/icons-material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22600,7 +22639,7 @@ __metadata: "@backstage-community/plugin-nexus-repository-manager": 1.10.6 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22623,7 +22662,7 @@ __metadata: "@backstage-community/plugin-ocm": 5.2.4 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22635,7 +22674,7 @@ __metadata: "@backstage-community/plugin-quay": 1.14.4 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22647,7 +22686,7 @@ __metadata: "@backstage-community/plugin-rbac": 1.33.2 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22656,10 +22695,10 @@ __metadata: version: 0.0.0-use.local resolution: "backstage-community-plugin-redhat-argocd@workspace:dynamic-plugins/wrappers/backstage-community-plugin-redhat-argocd" dependencies: - "@backstage-community/plugin-redhat-argocd": 1.10.4 + "@backstage-community/plugin-redhat-argocd": 1.11.0 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22750,7 +22789,7 @@ __metadata: "@backstage/cli": 0.28.2 "@backstage/core-plugin-api": 1.10.0 "@janus-idp/cli": 1.18.5 - "@mui/icons-material": 5.16.7 + "@mui/icons-material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22762,7 +22801,7 @@ __metadata: "@backstage-community/plugin-tekton": 3.16.2 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22774,7 +22813,7 @@ __metadata: "@backstage-community/plugin-topology": 1.29.7 "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -22919,7 +22958,7 @@ __metadata: "@backstage/cli": 0.28.2 "@backstage/plugin-notifications": 0.3.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -23008,7 +23047,7 @@ __metadata: "@backstage/cli": 0.28.2 "@backstage/plugin-signals": 0.0.11 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 typescript: 5.6.3 languageName: unknown linkType: soft @@ -26690,7 +26729,7 @@ __metadata: "@kubernetes/client-node": 0.21.0 "@microsoft/microsoft-graph-client": 3.0.7 "@playwright/test": 1.48.0 - "@types/node": 20.17.9 + "@types/node": 20.17.10 "@typescript-eslint/eslint-plugin": 6.21.0 "@typescript-eslint/parser": 6.21.0 eslint: 8.57.1 @@ -26701,7 +26740,7 @@ __metadata: node-fetch: 2.7.0 octokit: 4.0.2 otplib: 12.0.1 - prettier: 3.4.1 + prettier: 3.4.2 typescript: 5.6.3 winston: 3.14.2 languageName: unknown @@ -28219,12 +28258,12 @@ __metadata: linkType: hard "express-openapi-validator@npm:^5.0.4": - version: 5.1.6 - resolution: "express-openapi-validator@npm:5.1.6" + version: 5.3.9 + resolution: "express-openapi-validator@npm:5.3.9" dependencies: - "@apidevtools/json-schema-ref-parser": ^9.1.2 - "@types/multer": ^1.4.7 - ajv: ^8.11.2 + "@apidevtools/json-schema-ref-parser": ^11.7.0 + "@types/multer": ^1.4.12 + ajv: ^8.17.1 ajv-draft-04: ^1.0.0 ajv-formats: ^2.1.1 content-type: ^1.0.5 @@ -28234,8 +28273,10 @@ __metadata: media-typer: ^1.1.0 multer: ^1.4.5-lts.1 ono: ^7.1.3 - path-to-regexp: ^6.2.0 - checksum: 46be5236101e51593d1365505879d380a40d0307be8173cdaa3b643def518fcca25944f2e0eb5b2c48a7a09eacb192e5287b3a995b1903275dbca0e59fa669e3 + path-to-regexp: ^8.1.0 + peerDependencies: + express: "*" + checksum: 0469b383b769f070dfad9c9148b18857c5aaa9aa14c596e1ada155dac63e1c355e455d42f3c81af62c4aefd3db4ad8b892d34a432e170e4eb7af73d7dbd2f644 languageName: node linkType: hard @@ -37642,7 +37683,7 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:^8.0.0": +"path-to-regexp@npm:^8.0.0, path-to-regexp@npm:^8.1.0": version: 8.2.0 resolution: "path-to-regexp@npm:8.2.0" checksum: 56e13e45962e776e9e7cd72e87a441cfe41f33fd539d097237ceb16adc922281136ca12f5a742962e33d8dda9569f630ba594de56d8b7b6e49adf31803c5e771 @@ -38567,12 +38608,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:3.4.1": - version: 3.4.1 - resolution: "prettier@npm:3.4.1" +"prettier@npm:3.4.2": + version: 3.4.2 + resolution: "prettier@npm:3.4.2" bin: prettier: bin/prettier.cjs - checksum: f83ae83e38ae38f42c0b174833f58f820ed6eb063abfc5aa6725e8f9c1d626b54b1cb9d595cace525f8d59de89e186285f6bbcb460dc644ea9d8a7823cc54aca + checksum: 061c84513db62d3944c8dc8df36584dad82883ce4e49efcdbedd8703dce5b173c33fd9d2a4e1725d642a3b713c932b55418342eaa347479bc4a9cca114a04cd0 languageName: node linkType: hard @@ -39906,7 +39947,7 @@ __metadata: dependencies: "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 "@red-hat-developer-hub/backstage-plugin-bulk-import": 1.10.3 typescript: 5.6.3 languageName: unknown @@ -39918,7 +39959,7 @@ __metadata: dependencies: "@backstage/cli": 0.28.2 "@janus-idp/cli": 1.18.5 - "@mui/material": 5.16.7 + "@mui/material": 5.16.9 "@red-hat-developer-hub/backstage-plugin-dynamic-home-page": 1.0.1 typescript: 5.6.3 languageName: unknown