From 81ceef61e1fc2f353b834e8adc78a1608a9e9784 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Sat, 6 Dec 2025 09:06:32 -0700 Subject: [PATCH 01/10] local deploy without secrets --- DEPLOYMENT.md | 6 + Makefile | 4 + .../configuration/local-values.yaml | 33 +++++ .../infra-server/templates/argo/secrets.yaml | 2 + chart/infra-server/templates/aro/secrets.yaml | 2 + chart/infra-server/templates/aws/secrets.yaml | 2 + .../infra-server/templates/azure/secrets.yaml | 2 + .../infra-server/templates/demo/secrets.yaml | 2 + chart/infra-server/templates/deployment.yaml | 6 +- chart/infra-server/templates/gke/secrets.yaml | 2 + chart/infra-server/templates/ibm/secrets.yaml | 2 + .../templates/monitoring/secrets.yaml | 2 + .../templates/openshift-4/secrets.yaml | 2 + .../templates/openshift/secrets.yaml | 2 + chart/infra-server/templates/osd/secrets.yaml | 2 + chart/infra-server/templates/secrets.yaml | 117 ++++++++++++++++++ pkg/signer/signer.go | 10 +- scripts/deploy/helm.sh | 47 ++++++- 18 files changed, 239 insertions(+), 6 deletions(-) create mode 100644 chart/infra-server/configuration/local-values.yaml diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 5bd97b935..a4b1f26b8 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -116,6 +116,12 @@ Use the environment variable `TEST_MODE` to disable certain infra service behavi This is used in the infra PR clusters to set the login referer and disable telemetry. +#### Deployments for testing only (no secrets) + +For test clusters (such as a local KinD/Colima), you can use the deploy-local make target to skip loading secrets. The flavor provisioning actions that require secrets will no be accessible, and integrations such as with Slack will be disabled. + +`make deploy-local` + ### Rollback Use `helm rollback infra-server `. diff --git a/Makefile b/Makefile index 3e948eb93..65839a2fe 100644 --- a/Makefile +++ b/Makefile @@ -253,6 +253,10 @@ helm-deploy: pre-check helm-dependency-update create-namespaces helm-diff: pre-check helm-dependency-update create-namespaces @./scripts/deploy/helm.sh diff $(VERSION) $(ENVIRONMENT) $(SECRET_VERSION) +## Deploy to local cluster (e.g., Colima) without GCP Secret Manager +.PHONY: deploy-local +deploy-local: helm-dependency-update create-namespaces + TEST_MODE=true ./scripts/deploy/helm.sh deploy-local $(shell make tag) local ## Bounce pods .PHONY: bounce-infra-pods bounce-infra-pods: diff --git a/chart/infra-server/configuration/local-values.yaml b/chart/infra-server/configuration/local-values.yaml new file mode 100644 index 000000000..286c13267 --- /dev/null +++ b/chart/infra-server/configuration/local-values.yaml @@ -0,0 +1,33 @@ +environment: local + +# Disable Auth0 for local development - allow anonymous access +auth0: + clientID: "" + tenant: "" + +# Set local deploy mode to true for local development +localDeploy: true + +# Enable test mode for faster cluster resume intervals +testMode: true + +# Use local Docker images instead of pulling from registry +imagePullPolicy: Never + +# Pull secrets for container registries - dummy values for local development +pullSecrets: + docker: + registry: "docker.io" + username: "dummy" + password: "dummy" + quay: + registry: "quay.io" + username: "dummy" + password: "dummy" + stackrox: + registry: "stackrox.io" + username: "dummy" + password: "dummy" + +# Alertmanager configuration +alertmanagerSlackTeam: "dummy-team" diff --git a/chart/infra-server/templates/argo/secrets.yaml b/chart/infra-server/templates/argo/secrets.yaml index b40833a6f..81dfb6586 100644 --- a/chart/infra-server/templates/argo/secrets.yaml +++ b/chart/infra-server/templates/argo/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- apiVersion: v1 kind: Secret @@ -7,3 +8,4 @@ metadata: data: credentials.json: |- {{ required ".Values.google_credentials_json is undefined" .Values.google_credentials_json }} +{{- end }} diff --git a/chart/infra-server/templates/aro/secrets.yaml b/chart/infra-server/templates/aro/secrets.yaml index 4c9a05601..4f9c05ff0 100644 --- a/chart/infra-server/templates/aro/secrets.yaml +++ b/chart/infra-server/templates/aro/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- apiVersion: v1 kind: Secret @@ -16,3 +17,4 @@ data: {{ .Values.aroClusterManager.azureSPSecretVal | b64enc }} REDHAT_PULL_SECRET_BASE64: |- {{ .Values.aroClusterManager.redHatPullSecretBase64 | b64enc }} +{{- end }} diff --git a/chart/infra-server/templates/aws/secrets.yaml b/chart/infra-server/templates/aws/secrets.yaml index d88ca400b..4aeb0db77 100644 --- a/chart/infra-server/templates/aws/secrets.yaml +++ b/chart/infra-server/templates/aws/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- apiVersion: v1 kind: Secret @@ -10,3 +11,4 @@ data: {{ .Values.aws.accessKeyId | b64enc }} AWS_SECRET_ACCESS_KEY: |- {{ .Values.aws.secretAccessKey | b64enc }} +{{- end }} diff --git a/chart/infra-server/templates/azure/secrets.yaml b/chart/infra-server/templates/azure/secrets.yaml index f05c8135a..0a104c493 100644 --- a/chart/infra-server/templates/azure/secrets.yaml +++ b/chart/infra-server/templates/azure/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- @@ -18,3 +19,4 @@ data: {{ .Values.azure.sp_tenant | b64enc }} ACR_TO_ATTACH: |- {{ .Values.azure.aks_attached_acr | b64enc }} +{{- end }} diff --git a/chart/infra-server/templates/demo/secrets.yaml b/chart/infra-server/templates/demo/secrets.yaml index db96794ec..9c7283725 100644 --- a/chart/infra-server/templates/demo/secrets.yaml +++ b/chart/infra-server/templates/demo/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- apiVersion: v1 @@ -90,3 +91,4 @@ data: .dockerconfigjson: {{ template "pull-secret" .Values.pullSecrets.quay }} --- +{{- end }} diff --git a/chart/infra-server/templates/deployment.yaml b/chart/infra-server/templates/deployment.yaml index 548efc08b..8f609ac7e 100644 --- a/chart/infra-server/templates/deployment.yaml +++ b/chart/infra-server/templates/deployment.yaml @@ -27,8 +27,12 @@ spec: - name: infra-server image: quay.io/rhacs-eng/infra-server:{{ required "A valid .Values.tag entry is required!" .Values.tag }} env: +{{- if not .Values.localDeploy }} - name: GOOGLE_APPLICATION_CREDENTIALS value: /configuration/google-credentials.json +{{- end }} + - name: LOCAL_DEPLOY + value: "{{ .Values.localDeploy }}" - name: TEST_MODE value: "{{ .Values.testMode }}" readinessProbe: @@ -47,7 +51,7 @@ spec: containerPort: 8443 - name: metrics containerPort: 9101 - imagePullPolicy: Always + imagePullPolicy: {{ .Values.imagePullPolicy | default "Always" }} volumeMounts: - mountPath: /configuration name: configuration diff --git a/chart/infra-server/templates/gke/secrets.yaml b/chart/infra-server/templates/gke/secrets.yaml index 0cf1dda7b..ecbbe61f2 100644 --- a/chart/infra-server/templates/gke/secrets.yaml +++ b/chart/infra-server/templates/gke/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- apiVersion: v1 @@ -13,3 +14,4 @@ data: {{ required ".Values.gke__gke_provisioner_json is undefined" .Values.gke__gke_provisioner_json }} --- +{{- end }} diff --git a/chart/infra-server/templates/ibm/secrets.yaml b/chart/infra-server/templates/ibm/secrets.yaml index 8c757cc37..f8d1fa2d9 100644 --- a/chart/infra-server/templates/ibm/secrets.yaml +++ b/chart/infra-server/templates/ibm/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- apiVersion: v1 kind: Secret @@ -8,3 +9,4 @@ metadata: data: IBM_ROKS_API_KEY: |- {{ .Values.ibmCloudSecrets.ibmRoksApiKey | b64enc }} +{{- end }} diff --git a/chart/infra-server/templates/monitoring/secrets.yaml b/chart/infra-server/templates/monitoring/secrets.yaml index eff549996..cb79f6fc5 100644 --- a/chart/infra-server/templates/monitoring/secrets.yaml +++ b/chart/infra-server/templates/monitoring/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- apiVersion: v1 kind: Secret @@ -6,3 +7,4 @@ metadata: namespace: monitoring data: webhookURL: "{{ .Values.alertmanagerSlackWebhook | b64enc }}" +{{- end }} diff --git a/chart/infra-server/templates/openshift-4/secrets.yaml b/chart/infra-server/templates/openshift-4/secrets.yaml index 3fe49ca72..467a1338b 100644 --- a/chart/infra-server/templates/openshift-4/secrets.yaml +++ b/chart/infra-server/templates/openshift-4/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} apiVersion: v1 kind: Secret type: Opaque @@ -43,3 +44,4 @@ metadata: data: REDHAT_PULL_SECRET: |- {{ required ".Values.openshift_4__redhat_pull_secret_json is undefined" .Values.openshift_4__redhat_pull_secret_json }} +{{- end }} diff --git a/chart/infra-server/templates/openshift/secrets.yaml b/chart/infra-server/templates/openshift/secrets.yaml index 42ef2129b..b73b26008 100644 --- a/chart/infra-server/templates/openshift/secrets.yaml +++ b/chart/infra-server/templates/openshift/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} apiVersion: v1 kind: Secret type: Opaque @@ -9,3 +10,4 @@ metadata: data: google-credentials.json: |- {{ required ".Values.openshift__google_credentials_json is undefined" .Values.openshift__google_credentials_json }} +{{- end }} diff --git a/chart/infra-server/templates/osd/secrets.yaml b/chart/infra-server/templates/osd/secrets.yaml index aafbb21b1..dd69fbec2 100644 --- a/chart/infra-server/templates/osd/secrets.yaml +++ b/chart/infra-server/templates/osd/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} --- apiVersion: v1 kind: Secret @@ -19,3 +20,4 @@ data: {{ .Values.osdClusterManager.gcpSaCredsJsonBase64 | b64enc }} GCP_SERVICE_ACCOUNT_KEY_BASE64: |- {{ .Values.osdClusterManager.gcpServiceAccountKeyBase64 | b64enc }} +{{- end }} diff --git a/chart/infra-server/templates/secrets.yaml b/chart/infra-server/templates/secrets.yaml index ddf06699f..663023c34 100644 --- a/chart/infra-server/templates/secrets.yaml +++ b/chart/infra-server/templates/secrets.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.localDeploy }} apiVersion: v1 kind: Secret @@ -112,3 +113,119 @@ metadata: data: .dockerconfigjson: {{ template "pull-secret" .Values.pullSecrets.quay }} +{{- end }} + +{{- if .Values.localDeploy }} +--- +apiVersion: v1 +kind: Secret + +metadata: + name: infra-server-secrets + namespace: infra + labels: + app.kubernetes.io/name: infra-server + +data: + # Minimal config for LOCAL_DEPLOY mode + infra.yaml: {{ printf "server:\n port: 8443\n cert: /configuration/cert.pem\n key: /configuration/key.pem\n static: /etc/infra/static\n metricsPort: 9101" | b64enc }} + + # Minimal OIDC config - uses Google as a valid issuer for initialization + oidc.yaml: {{ printf "issuer: https://accounts.google.com\nclientID: dummy\nclientSecret: dummy\nendpoint: localhost:8443\nsessionSecret: local-dev-secret-min-32-chars-long\ntokenLifetime: 24h" | b64enc }} + + # Empty Google credentials - not used in LOCAL_DEPLOY mode + google-credentials.json: {{ "{}" | b64enc }} + + # Empty BigQuery credentials - not used in LOCAL_DEPLOY mode + bigquery-sa.json: {{ "{}" | b64enc }} + + # Self-signed TLS certificate for local development + cert.pem: {{ .Files.Get "configuration/local-cert.pem" | b64enc }} + + # Self-signed TLS private key for local development + key.pem: {{ .Files.Get "configuration/local-key.pem" | b64enc }} + + # Flavors and all workflows needed by flavors.yaml + flavors.yaml: |- + {{- tpl (.Files.Get "static/flavors.yaml" ) . | b64enc | nindent 4 }} + + workflow-gke-default.yaml: |- + {{- tpl (.Files.Get "static/workflow-gke-default.yaml" ) . | b64enc | nindent 4 }} + + workflow-demo.yaml: |- + {{- tpl (.Files.Get "static/workflow-demo.yaml" ) . | b64enc | nindent 4 }} + + workflow-qa-demo.yaml: |- + {{- tpl (.Files.Get "static/workflow-qa-demo.yaml" ) . | b64enc | nindent 4 }} + + workflow-openshift-4.yaml: |- + {{- tpl (.Files.Get "static/workflow-openshift-4.yaml" ) . | b64enc | nindent 4 }} + + workflow-openshift-4-demo.yaml: |- + {{- tpl (.Files.Get "static/workflow-openshift-4-demo.yaml" ) . | b64enc | nindent 4 }} + + workflow-eks.yaml: |- + {{- tpl (.Files.Get "static/workflow-eks.yaml" ) . | b64enc | nindent 4 }} + + workflow-aks.yaml: |- + {{- tpl (.Files.Get "static/workflow-aks.yaml" ) . | b64enc | nindent 4 }} + + workflow-openshift-aro.yaml: |- + {{- tpl (.Files.Get "static/workflow-openshift-aro.yaml" ) . | b64enc | nindent 4 }} + + workflow-openshift-rosa.yaml: |- + {{- tpl (.Files.Get "static/workflow-openshift-rosa.yaml" ) . | b64enc | nindent 4 }} + + workflow-openshift-rosa-hcp.yaml: |- + {{- tpl (.Files.Get "static/workflow-openshift-rosa-hcp.yaml" ) . | b64enc | nindent 4 }} + + workflow-openshift-ibmroks.yaml: |- + {{- tpl (.Files.Get "static/workflow-openshift-ibmroks.yaml" ) . | b64enc | nindent 4 }} + + workflow-osd-aws.yaml: |- + {{- tpl (.Files.Get "static/workflow-osd-aws.yaml" ) . | b64enc | nindent 4 }} + + workflow-osd-gcp.yaml: |- + {{- tpl (.Files.Get "static/workflow-osd-gcp.yaml" ) . | b64enc | nindent 4 }} + + # Janitor (cleanup) workflows + janitor-delete-gke-default.yaml: |- + {{- tpl (.Files.Get "static/janitor-delete-gke-default.yaml" ) . | b64enc | nindent 4 }} + + janitor-delete-openshift-4.yaml: |- + {{- tpl (.Files.Get "static/janitor-delete-openshift-4.yaml" ) . | b64enc | nindent 4 }} + + janitor-delete-openshift-rosa.yaml: |- + {{- tpl (.Files.Get "static/janitor-delete-openshift-rosa.yaml" ) . | b64enc | nindent 4 }} + + # Test workflows (for non-production environments) + test-url-artifact.yaml: |- + {{- tpl (.Files.Get "static/test-url-artifact.yaml" ) . | b64enc | nindent 4 }} + + test-connect-artifact.yaml: |- + {{- tpl (.Files.Get "static/test-connect-artifact.yaml" ) . | b64enc | nindent 4 }} + + test-gke-lite.yaml: |- + {{- tpl (.Files.Get "static/test-gke-lite.yaml" ) . | b64enc | nindent 4 }} + + test-qa-demo.yaml: |- + {{- tpl (.Files.Get "static/test-qa-demo.yaml" ) . | b64enc | nindent 4 }} + + test-simulate.yaml: |- + {{- tpl (.Files.Get "static/test-simulate.yaml" ) . | b64enc | nindent 4 }} + + test-janitor-delete.yaml: |- + {{- tpl (.Files.Get "static/test-janitor-delete.yaml" ) . | b64enc | nindent 4 }} + +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/dockerconfigjson + +metadata: + name: infra-image-registry-pull-secret + namespace: infra + +data: + .dockerconfigjson: {{ template "pull-secret" .Values.pullSecrets.quay }} +{{- end }} diff --git a/pkg/signer/signer.go b/pkg/signer/signer.go index 1f0bc76dc..620788276 100644 --- a/pkg/signer/signer.go +++ b/pkg/signer/signer.go @@ -13,6 +13,7 @@ import ( "time" "cloud.google.com/go/storage" + "github.com/stackrox/infra/pkg/logging" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" ) @@ -27,6 +28,10 @@ const ( gcsSignedURLLifespan = 10 * time.Minute ) +var ( + log = logging.CreateProductionLogger() +) + // Signer facilitates the generation of signed GCS URLS. type Signer struct { cfg jwt.Config @@ -34,11 +39,12 @@ type Signer struct { } // NewFromEnv constructs a Signer from the GOOGLE_APPLICATION_CREDENTIALS set -// in the working environment. +// in the working environment. Returns an empty Signer if not configured. func NewFromEnv() (*Signer, error) { credentialsFilename, found := os.LookupEnv(googleCredentialsEnvVar) if !found { - return nil, fmt.Errorf("environment variable %q was not set", googleCredentialsEnvVar) + log.Log(logging.WARN, "GCS signing disabled", "reason", "GOOGLE_APPLICATION_CREDENTIALS not set") + return &Signer{}, nil } data, err := os.ReadFile(credentialsFilename) diff --git a/scripts/deploy/helm.sh b/scripts/deploy/helm.sh index a13f250ed..39ba24082 100755 --- a/scripts/deploy/helm.sh +++ b/scripts/deploy/helm.sh @@ -59,6 +59,13 @@ template() { # deploy upgrades the Helm release with deploy() { + echo "Deploy.." >&2 + echo " RELEASE_NAME: ${RELEASE_NAME}" >&2 + echo " Tag: ${TAG}" >&2 + echo " Environment: ${ENVIRONMENT}" >&2 + echo " Test Mode: ${TEST_MODE}" >&2 + echo " Local Values: ${local_values:-false}" >&2 + set -x helm upgrade \ "${RELEASE_NAME}" \ chart/infra-server \ @@ -72,14 +79,21 @@ deploy() { --set tag="${TAG}" \ --set environment="${ENVIRONMENT}" \ --set testMode="${TEST_MODE}" \ + ${HELM_DEBUG:+--debug} \ --values - \ - < <(gcloud secrets versions access "${SECRET_VERSION}" \ + < <( + if [[ ${local_values:-} != 'true' ]]; then + gcloud secrets versions access "${SECRET_VERSION}" \ --secret "infra-values-${ENVIRONMENT}" \ --project "${SECRETS_PROJECT}" \ - && gcloud secrets versions access "${SECRET_VERSION}" \ + && gcloud secrets versions access "${SECRET_VERSION}" \ --secret "infra-values-from-files-${ENVIRONMENT}" \ - --project "${SECRETS_PROJECT}" \ + --project "${SECRETS_PROJECT}"; + else + cat 'chart/infra-server/configuration/local-values.yaml' + fi ) + set +x } # diff renders the Helm chart and compares the deployed resources to show what would change on next deployment. @@ -109,6 +123,33 @@ diff() { | kubectl diff -R -f - } +# deploy-local deploys without secrets +deploy-local() { + echo 'Deploying for testing without secrets...' >&2 + + # Generate self-signed cert for local development if it doesn't exist + local cert_dir='chart/infra-server/configuration' + local cert_file="${cert_dir}/local-cert.pem" + local key_file="${cert_dir}/local-key.pem" + + if [[ ! -f "${cert_file}" ]] || [[ ! -f "${key_file}" ]]; then + echo "Generating self-signed certificate for local development..." >&2 + openssl req -x509 -newkey rsa:2048 -nodes \ + -keyout "${key_file}" \ + -out "${cert_file}" \ + -days 36500 \ + -subj "/CN=localhost" >&2 + echo "Certificate generated: ${cert_file}" >&2 + else + echo "Using existing self-signed certificate: ${cert_file}" >&2 + fi + + ENVIRONMENT='local' + local_values='true' + deploy + echo -e '\nDeployment complete!' >&2 +} + check_not_empty TASK TAG ENVIRONMENT install_crds eval "$TASK" From 41018d015ac14787e455c709bda5732329541b9a Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Sat, 6 Dec 2025 09:10:53 -0700 Subject: [PATCH 02/10] add ui e2e tests --- Makefile | 21 + .../configuration/local-values.yaml | 5 - scripts/deploy/helm.sh | 23 +- ui/.gitignore | 6 + ui/TESTING.md | 161 +++ ui/cypress.config.ts | 80 ++ ui/cypress/e2e/flavor-selection.cy.ts | 73 ++ ui/cypress/e2e/home.cy.ts | 16 + ui/cypress/support/commands.ts | 57 + ui/cypress/support/e2e.ts | 19 + ui/package.json | 7 +- ui/yarn.lock | 1051 ++++++++++++++++- 12 files changed, 1503 insertions(+), 16 deletions(-) create mode 100644 ui/TESTING.md create mode 100644 ui/cypress.config.ts create mode 100644 ui/cypress/e2e/flavor-selection.cy.ts create mode 100644 ui/cypress/e2e/home.cy.ts create mode 100644 ui/cypress/support/commands.ts create mode 100644 ui/cypress/support/e2e.ts diff --git a/Makefile b/Makefile index 65839a2fe..1b9a2c9fb 100644 --- a/Makefile +++ b/Makefile @@ -257,6 +257,27 @@ helm-diff: pre-check helm-dependency-update create-namespaces .PHONY: deploy-local deploy-local: helm-dependency-update create-namespaces TEST_MODE=true ./scripts/deploy/helm.sh deploy-local $(shell make tag) local + +## Run UI E2E tests against local deployment +.PHONY: test-e2e +test-e2e: + @echo "test-e2e starting..." >&2 + @echo "Waiting for infra-server to be ready..." >&2 + @kubectl wait --for=condition=ready pod -l app=infra-server -n infra --timeout=3m >&2 || \ + (echo "ERROR: infra-server pods did not become ready" >&2 && exit 1) + @echo "Starting port-forward and running E2E tests..." >&2 + @kubectl port-forward -n infra svc/infra-server-service 8443:8443 >/dev/null 2>&1 & \ + PF_PID=$$!; \ + cleanup() { \ + echo "" >&2; \ + echo "Cleaning up port-forward (PID: $$PF_PID)..." >&2; \ + kill $$PF_PID 2>/dev/null || true; \ + }; \ + trap cleanup EXIT; \ + sleep 5; \ + echo "Running Cypress E2E tests..." >&2; \ + cd ui && BROWSER=none PORT=3001 INFRA_API_ENDPOINT=http://localhost:8443 npm run test:e2e + ## Bounce pods .PHONY: bounce-infra-pods bounce-infra-pods: diff --git a/chart/infra-server/configuration/local-values.yaml b/chart/infra-server/configuration/local-values.yaml index 286c13267..68e6d4abb 100644 --- a/chart/infra-server/configuration/local-values.yaml +++ b/chart/infra-server/configuration/local-values.yaml @@ -1,10 +1,5 @@ environment: local -# Disable Auth0 for local development - allow anonymous access -auth0: - clientID: "" - tenant: "" - # Set local deploy mode to true for local development localDeploy: true diff --git a/scripts/deploy/helm.sh b/scripts/deploy/helm.sh index 39ba24082..df4b32a40 100755 --- a/scripts/deploy/helm.sh +++ b/scripts/deploy/helm.sh @@ -134,11 +134,32 @@ deploy-local() { if [[ ! -f "${cert_file}" ]] || [[ ! -f "${key_file}" ]]; then echo "Generating self-signed certificate for local development..." >&2 + # Create a temporary config file for SAN extension + local san_config=$(mktemp) + cat > "${san_config}" <&2 + -config "${san_config}" >&2 + rm -f "${san_config}" echo "Certificate generated: ${cert_file}" >&2 else echo "Using existing self-signed certificate: ${cert_file}" >&2 diff --git a/ui/.gitignore b/ui/.gitignore index 528384937..7a3fb58ac 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -8,6 +8,12 @@ # testing /coverage +# cypress +cypress/videos +cypress/screenshots +cypress/downloads +cypress.env.json + # production /build diff --git a/ui/TESTING.md b/ui/TESTING.md new file mode 100644 index 000000000..d5d0def50 --- /dev/null +++ b/ui/TESTING.md @@ -0,0 +1,161 @@ +# Cypress E2E Testing + +This directory contains Cypress E2E tests for the StackRox Infra UI. + +## Quick Start - Running E2E Tests Against Local Backend + +### Prerequisites + +1. **Deploy the local backend** (with authentication disabled): + + ```bash + # From the repository root + make deploy-local + ``` + + This deploys the infra-server to your local Kubernetes cluster with + `LOCAL_DEPLOY=true`, which disables authentication for local development. + +2. **Start port-forwarding** to access the backend: + + ```bash + kubectl port-forward -n infra svc/infra-server-service 8443:8443 + ``` + + Keep this running in a separate terminal. + +3. **Configure the UI to connect to local backend**: + + ```bash + cd ui + cp .env.example .env.local + ``` + + This creates a `.env.local` file. Note: The file contains + `INFRA_API_ENDPOINT` but the environment variable must also be set when + starting the dev server (see next step). + +4. **Start the UI dev server** (in a separate terminal): + + ```bash + cd ui + BROWSER=none PORT=3001 INFRA_API_ENDPOINT=http://localhost:8443 npm start + ``` + + **Important:** The `INFRA_API_ENDPOINT` environment variable must be set when + starting the dev server (not just in `.env.local`) because the proxy + middleware reads it at startup. + + Keep this running. The dev server will: + + - Run on http://localhost:3001 + - Proxy API requests to http://localhost:8443 (your local backend) + - Hot-reload when you make changes to the UI code + +5. **Run the E2E tests** (in another terminal): + + ```bash + cd ui + npm run cypress:run:e2e + ``` + +That's it! The tests will run against the UI dev server at +http://localhost:3001, which proxies API requests to your local backend at +`https://localhost:8443`. + +### Test Results + +After the tests complete: + +- **Videos** are saved to `ui/cypress/videos/` (one per test file) +- **Screenshots** (on failures only) are saved to `ui/cypress/screenshots/` + +Review the videos to verify the tests are properly accessing the backend. + +## Interactive Mode + +To run tests interactively with the Cypress UI (useful for debugging): + +**Prerequisites:** Make sure the UI dev server is running (step 4 above). + +```bash +cd ui +npm run cypress:open +``` + +Then: + +1. Select "E2E Testing" +2. Choose a browser +3. Click on any test file to run it + +Interactive mode lets you see the tests run in real-time, inspect the DOM, and +debug failures. + +## Test Structure + +- `cypress/e2e/home.cy.ts` - Basic home page tests +- `cypress/e2e/flavor-selection.cy.ts` - Tests for flavor API integration + +## Configuration + +Tests are configured in `cypress.config.ts` to: + +- Run against the UI dev server at `http://localhost:3001` (which proxies to the + backend) +- Accept self-signed certificates (`chromeWebSecurity: false`) +- Capture videos of all test runs +- Capture screenshots on failures only +- Retry failed tests 2 times in CI mode (run mode), 0 times in interactive mode + +The UI dev server (configured via `ui/.env.local`) proxies API requests to your +local backend at `https://localhost:8443`. + +## Adding More Tests + +To add new E2E tests: + +1. Create a new file in `cypress/e2e/` with the pattern `*.cy.ts` +2. Follow the existing test patterns for consistency +3. Run the tests locally before committing + +## Troubleshooting + +### Tests fail with "Cypress failed to verify that your server is running" + +**Solution:** Make sure the UI dev server is running on port 3001 before running +tests: + +```bash +cd ui +BROWSER=none PORT=3001 npm start +``` + +### Tests show "access denied" or authentication errors + +**Solution:** Verify that: + +1. The backend was deployed with `LOCAL_DEPLOY=true` (via `make deploy-local`) +2. Port-forwarding is active: + `kubectl port-forward -n infra svc/infra-server-service 8443:8443` +3. The `.env.local` file points to the correct backend: + `INFRA_API_ENDPOINT=https://localhost:8443` + +You can check if LOCAL_DEPLOY is enabled: + +```bash +kubectl get deployment -n infra infra-server-deployment -o jsonpath='{.spec.template.spec.containers[0].env}' | grep LOCAL_DEPLOY +``` + +### Port 3001 or 8443 already in use + +**Solution:** + +- Find and kill the process using the port: `lsof -i :3001` or `lsof -i :8443` +- Or use different ports by modifying `cypress.config.ts` and `.env.local` + +## Documentation + +- Full Cypress documentation: https://docs.cypress.io/ +- Cypress Best Practices: + https://docs.cypress.io/guides/references/best-practices diff --git a/ui/cypress.config.ts b/ui/cypress.config.ts new file mode 100644 index 000000000..218532257 --- /dev/null +++ b/ui/cypress.config.ts @@ -0,0 +1,80 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { defineConfig } from 'cypress'; +import * as crypto from 'crypto'; + +export default defineConfig({ + e2e: { + // UI dev server runs on :3001 + // API calls are proxied to backend at :8443 (configured via INFRA_API_ENDPOINT in setupProxy.js) + // Routes proxied: /v1, /login, /callback, /logout, /downloads/infractl-* + baseUrl: 'http://localhost:3001', + specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'cypress/support/e2e.ts', + fixturesFolder: 'cypress/fixtures', + screenshotsFolder: 'cypress/screenshots', + videosFolder: 'cypress/videos', + viewportWidth: 1280, + viewportHeight: 720, + video: true, + screenshotOnRunFailure: true, + chromeWebSecurity: false, // Allow self-signed certificates for local dev + setupNodeEvents(on, config) { + // Task to generate JWT tokens for local dev authentication + on('task', { + generateJWT({ payload, secret }: { payload: any; secret: string }) { + // Create JWT header + const header = { + alg: 'HS256', + typ: 'JWT', + }; + + // Base64url encode header and payload + const base64UrlEncode = (obj: any) => + Buffer.from(JSON.stringify(obj)) + .toString('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); + + const encodedHeader = base64UrlEncode(header); + const encodedPayload = base64UrlEncode(payload); + + // Create signature + const signatureInput = `${encodedHeader}.${encodedPayload}`; + const signature = crypto + .createHmac('sha256', secret) + .update(signatureInput) + .digest('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); + + // Return complete JWT + return `${encodedHeader}.${encodedPayload}.${signature}`; + }, + }); + + return config; + }, + env: { + // Default environment variables for tests + // Can be overridden via CLI or cypress.env.json + API_URL: 'https://localhost:8000', + }, + }, + component: { + devServer: { + framework: 'react', + bundler: 'vite', + }, + specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'cypress/support/component.ts', + }, + retries: { + runMode: 2, // Retry failed tests in CI + openMode: 0, // Don't retry in interactive mode + }, + defaultCommandTimeout: 10000, + requestTimeout: 10000, + responseTimeout: 10000, +}); diff --git a/ui/cypress/e2e/flavor-selection.cy.ts b/ui/cypress/e2e/flavor-selection.cy.ts new file mode 100644 index 000000000..f78312ac0 --- /dev/null +++ b/ui/cypress/e2e/flavor-selection.cy.ts @@ -0,0 +1,73 @@ +describe('Flavor Selection', () => { + beforeEach(() => { + // Authenticate for local development before visiting the page + cy.loginForLocalDev(); + cy.visit('/'); + }); + + it('should load the page without authentication errors', () => { + // Verify no error messages (confirms LOCAL_DEPLOY mode is working) + cy.get('body').should('not.contain', 'access denied'); + cy.get('body').should('not.contain', 'There was an unexpected error'); + }); + + it('should display a list of available flavors', () => { + // Wait for flavors to load (check for either "My Flavors" or "All Flavors" title) + cy.contains('h2', /My Flavors|All Flavors/).should('be.visible'); + + // Verify that the flavor gallery is not empty + // Each flavor is rendered as a LinkCard inside a GalleryItem + cy.get('.pf-v6-c-card').should('have.length.at.least', 1); + }); + + it('should display flavor details for each flavor card', () => { + // Wait for flavors to load + cy.contains('h2', /My Flavors|All Flavors/).should('be.visible'); + + // Get the first flavor card and verify it has required elements + cy.get('.pf-v6-c-card') + .first() + .within(() => { + // Each flavor card should have a name (header text) + cy.get('.pf-v6-c-card__title').should('exist').and('not.be.empty'); + + // Each flavor card should have an availability label + cy.get('.pf-v6-c-label').should('exist'); + }); + }); + + it('should have clickable flavor cards that navigate to launch page', () => { + // Wait for flavors to load + cy.contains('h2', /My Flavors|All Flavors/).should('be.visible'); + + // Click the first flavor card + cy.get('.pf-v6-c-card').first().click(); + + // Verify navigation to launch page (URL should contain /launch/) + cy.url().should('include', '/launch/'); + + // Verify the launch page loaded with a cluster launch form + cy.contains('h1', /Launch/).should('be.visible'); + }); + + it('should toggle between "My Flavors" and "All Flavors"', () => { + // Verify initial state + cy.contains('h2', 'My Flavors').should('be.visible'); + + // Find and click the "Show All Flavors" toggle switch + // Use force:true because PatternFly switch has a visual element covering the input + cy.get('input[name="flavor-filter-toggle"]').click({ force: true }); + + // Verify the title changed to "All Flavors" + cy.contains('h2', 'All Flavors').should('be.visible'); + + // Verify URL parameter was updated + cy.url().should('include', 'showAllFlavors=true'); + + // Toggle back + cy.get('input[name="flavor-filter-toggle"]').click({ force: true }); + + // Verify we're back to "My Flavors" + cy.contains('h2', 'My Flavors').should('be.visible'); + }); +}); diff --git a/ui/cypress/e2e/home.cy.ts b/ui/cypress/e2e/home.cy.ts new file mode 100644 index 000000000..c76d51556 --- /dev/null +++ b/ui/cypress/e2e/home.cy.ts @@ -0,0 +1,16 @@ +describe('Home Page', () => { + beforeEach(() => { + // Authenticate for local development before visiting the page + cy.loginForLocalDev(); + cy.visit('/'); + }); + + it('should load the home page', () => { + cy.get('body').should('be.visible'); + }); + + it('should not show error messages', () => { + cy.get('body').should('not.contain', 'access denied'); + cy.get('body').should('not.contain', 'There was an unexpected error'); + }); +}); diff --git a/ui/cypress/support/commands.ts b/ui/cypress/support/commands.ts new file mode 100644 index 000000000..db94c6f14 --- /dev/null +++ b/ui/cypress/support/commands.ts @@ -0,0 +1,57 @@ +// Custom Cypress commands for authentication + +/** + * Logs in by setting a valid JWT token cookie for local development. + * This uses the known session secret from local-deploy oidc.yaml. + */ +Cypress.Commands.add('loginForLocalDev', () => { + // The session secret from chart/infra-server/templates/secrets.yaml for local deploy + const sessionSecret = 'local-dev-secret-min-32-chars-long'; + + // Create a test user matching the backend's expected structure + // Note: Fields are capitalized to match Go's JSON serialization of protobuf structs + const testUser = { + Name: 'Test User', + Email: 'test@redhat.com', + Picture: '', + Expiry: { + seconds: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now + }, + }; + + // Create JWT payload matching the backend's userClaims structure + const now = Math.floor(Date.now() / 1000); + const payload = { + user: testUser, + exp: now + 3600, // 1 hour expiry + nbf: now, + iat: now, + }; + + // Generate JWT token using HS256 + cy.task('generateJWT', { payload, secret: sessionSecret }).then((token) => + // Set the token cookie + cy.setCookie('token', token as string, { + path: '/', + httpOnly: false, + secure: false, + sameSite: 'lax', + }) + ); +}); + +// TypeScript declaration for custom command +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + interface Chainable { + /** + * Custom command to log in for local development by setting a valid JWT token cookie. + * @example cy.loginForLocalDev() + */ + loginForLocalDev(): Chainable; + } + } +} + +export {}; diff --git a/ui/cypress/support/e2e.ts b/ui/cypress/support/e2e.ts new file mode 100644 index 000000000..830093642 --- /dev/null +++ b/ui/cypress/support/e2e.ts @@ -0,0 +1,19 @@ +// *********************************************************** +// This example support/e2e.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import custom commands for authentication +import './commands'; + +export {}; diff --git a/ui/package.json b/ui/package.json index b68ea62bf..68fc0d59d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,7 +12,10 @@ "lint": "npm-run-all lint:*", "lint:non-src": "prettier --write '**/*.{md,css,json}'", "lint:src": "yarn lint-check:src --fix", - "gen:src": "./scripts/generate-client.sh" + "gen:src": "./scripts/generate-client.sh", + "cypress:open": "cypress open", + "cypress:run:e2e": "cypress run --spec 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}'", + "test:e2e": "start-server-and-test start http://localhost:3001 cypress:run:e2e" }, "dependencies": { "@patternfly/patternfly": "^6.1.0", @@ -44,6 +47,7 @@ "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.14", + "cypress": "^15.6.0", "eslint-config-airbnb-typescript": "^12.0.0", "eslint-config-prettier": "^7.2.0", "eslint-config-react-app": "^7.0.1", @@ -52,6 +56,7 @@ "http-proxy-middleware": "^2.0.6", "npm-run-all": "^4.1.5", "prettier": "^2.2.1", + "start-server-and-test": "^2.1.2", "typescript": "~5.1.6", "vite": "^6.1.0", "vite-tsconfig-paths": "^4.3.2" diff --git a/ui/yarn.lock b/ui/yarn.lock index 48b35b54e..221a99824 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1086,6 +1086,38 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@cypress/request@^3.0.9": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.9.tgz#8ed6e08fea0c62998b5552301023af7268f11625" + integrity sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~4.0.4" + http-signature "~1.4.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + performance-now "^2.1.0" + qs "6.14.0" + safe-buffer "^5.1.2" + tough-cookie "^5.0.0" + tunnel-agent "^0.6.0" + uuid "^8.3.2" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + "@esbuild/aix-ppc64@0.25.0": version "0.25.0" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64" @@ -1238,6 +1270,40 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@hapi/address@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-5.1.1.tgz#e9925fc1b65f5cc3fbea821f2b980e4652e84cb6" + integrity sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA== + dependencies: + "@hapi/hoek" "^11.0.2" + +"@hapi/formula@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-3.0.2.tgz#81b538060ee079481c906f599906d163c4badeaf" + integrity sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw== + +"@hapi/hoek@^11.0.2", "@hapi/hoek@^11.0.7": + version "11.0.7" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-11.0.7.tgz#56a920793e0a42d10e530da9a64cc0d3919c4002" + integrity sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ== + +"@hapi/pinpoint@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-2.0.1.tgz#32077e715655fc00ab8df74b6b416114287d6513" + integrity sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q== + +"@hapi/tlds@^1.1.1": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@hapi/tlds/-/tlds-1.1.4.tgz#df4a7b59082b54ba4f3b7b38f781e2ac3cbc359a" + integrity sha512-Fq+20dxsxLaUn5jSSWrdtSRcIUba2JquuorF9UW1wIJS5cSUwxIsO2GIhaWynPRflvxSzFN+gxKte2HEW1OuoA== + +"@hapi/topo@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-6.0.2.tgz#f219c1c60da8430228af4c1f2e40c32a0d84bbb4" + integrity sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg== + dependencies: + "@hapi/hoek" "^11.0.2" + "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -1465,6 +1531,11 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.5.tgz#3a1c12c959010a55c17d46b395ed3047b545c246" integrity sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A== +"@standard-schema/spec@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" + integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== + "@tanstack/query-core@5.66.4": version "5.66.4" resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.66.4.tgz#44b87bff289466adbfa0de8daa5756cbd2d61c61" @@ -1689,6 +1760,16 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== + +"@types/sizzle@^2.3.2": + version "2.3.10" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.10.tgz#277a542aff6776d8a9b15f2ac682a663e3e94bbd" + integrity sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww== + "@types/testing-library__jest-dom@^5.9.1": version "5.9.2" resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.2.tgz#59e4771a1cf87d51e89a5cc8195cd3b647cba322" @@ -1696,6 +1777,11 @@ dependencies: "@types/jest" "*" +"@types/tmp@^0.2.3": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.6.tgz#d785ee90c52d7cc020e249c948c36f7b32d1e217" + integrity sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA== + "@types/unist@^2", "@types/unist@^2.0.0": version "2.0.10" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" @@ -1713,6 +1799,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^5.5.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -1870,6 +1963,14 @@ acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -1895,6 +1996,13 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" @@ -1932,6 +2040,16 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2052,6 +2170,18 @@ arraybuffer.prototype.slice@^1.0.4: get-intrinsic "^1.2.6" is-array-buffer "^3.0.4" +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + ast-types-flow@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" @@ -2067,6 +2197,16 @@ async-function@^1.0.0: resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + attr-accept@^2.2.4: version "2.2.5" resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.5.tgz#d7061d958e6d4f97bf8665c68b75851a0713ab5e" @@ -2091,6 +2231,16 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.13.2" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" + integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== + axe-core@^4.10.0: version "4.10.2" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df" @@ -2103,6 +2253,15 @@ axios@^0.21.4: dependencies: follow-redirects "^1.14.0" +axios@^1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.2.tgz#9ada120b7b5ab24509553ec3e40123521117f687" + integrity sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.4" + proxy-from-env "^1.1.0" + axobject-query@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" @@ -2187,6 +2346,28 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +blob-util@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + +bluebird@3.7.2, bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2229,6 +2410,24 @@ browserslist@^4.24.0, browserslist@^4.24.3: node-releases "^2.0.19" update-browserslist-db "^1.1.1" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +cachedir@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d" + integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== + call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" @@ -2278,6 +2477,11 @@ caniuse-lite@^1.0.30001688: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz#ad9c90301f7153cf6b3314d16cc30757285bf9e7" integrity sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw== +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -2308,6 +2512,45 @@ character-entities@^2.0.0: resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== + +ci-info@^4.1.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" + integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-table3@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.1.tgz#36ce9b7af4847f288d3cdd081fbd09bf7bd237b8" + integrity sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA== + dependencies: + string-width "^4.2.0" + optionalDependencies: + colors "1.4.0" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2332,11 +2575,38 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^2.0.16: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +colors@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2374,6 +2644,11 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + cosmiconfig@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" @@ -2396,7 +2671,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -2420,11 +2695,66 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.2.tgz#ee5ff8f208c8cd613b389f7b222c9801ca62b3f7" integrity sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw== +cypress@^15.6.0: + version "15.7.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-15.7.0.tgz#4d2a317766360232920f63c7d11ea8f5d83bc358" + integrity sha512-1C81zKxnQckYm2XGi37rPV4rN0bzUoWhydhKdOyshJn5gJKszEx5as9VLSZI0jp0ye49QxmnbU4TtMpcD+OmGQ== + dependencies: + "@cypress/request" "^3.0.9" + "@cypress/xvfb" "^1.2.4" + "@types/sinonjs__fake-timers" "8.1.1" + "@types/sizzle" "^2.3.2" + "@types/tmp" "^0.2.3" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "^3.7.2" + buffer "^5.7.1" + cachedir "^2.3.0" + chalk "^4.1.0" + ci-info "^4.1.0" + cli-cursor "^3.1.0" + cli-table3 "0.6.1" + commander "^6.2.1" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "^4.3.4" + enquirer "^2.3.6" + eventemitter2 "6.4.7" + execa "4.1.0" + executable "^4.1.1" + extract-zip "2.0.1" + figures "^3.2.0" + fs-extra "^9.1.0" + hasha "5.2.2" + is-installed-globally "~0.4.0" + listr2 "^3.8.3" + lodash "^4.17.21" + log-symbols "^4.0.0" + minimist "^1.2.8" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + process "^0.11.10" + proxy-from-env "1.0.0" + request-progress "^3.0.0" + supports-color "^8.1.1" + systeminformation "5.27.7" + tmp "~0.2.4" + tree-kill "1.2.2" + untildify "^4.0.0" + yauzl "^2.10.0" + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + data-view-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" @@ -2452,7 +2782,19 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@^3.2.7: +dayjs@^1.10.4: + version "1.11.19" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" + integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== + +debug@4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -2522,6 +2864,11 @@ define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dequal@^2.0.0, dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -2572,6 +2919,19 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" +duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + electron-to-chromium@^1.4.431: version "1.4.451" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.451.tgz#12b63ee5c82cbbc7b4ddd91e90f5a0dfc10de26e" @@ -2592,7 +2952,14 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -enquirer@^2.3.5: +end-of-stream@^1.1.0: + version "1.4.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== + dependencies: + once "^1.4.0" + +enquirer@^2.3.5, enquirer@^2.3.6: version "2.4.1" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== @@ -3137,16 +3504,92 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter2@6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== + eventemitter3@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== -extend@^3.0.0: +execa@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +execa@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3201,6 +3644,20 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -3255,6 +3712,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== +follow-redirects@^1.15.6: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + for-each@^0.3.3: version "0.3.5" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" @@ -3262,6 +3724,22 @@ for-each@^0.3.3: dependencies: is-callable "^1.2.7" +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data@^4.0.4, form-data@~4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + formik@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/formik/-/formik-2.4.2.tgz#a1115457cfb012a5c782cea3ad4b40b2fe36fa18" @@ -3280,6 +3758,21 @@ fraction.js@^4.2.0: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3365,6 +3858,18 @@ get-proto@^1.0.0, get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" +get-stream@^5.0.0, get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" @@ -3374,6 +3879,13 @@ get-symbol-description@^1.1.0: es-errors "^1.3.0" get-intrinsic "^1.2.6" +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3393,6 +3905,13 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -3452,6 +3971,11 @@ graceful-fs@^4.1.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -3510,6 +4034,14 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasha@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" + integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== + dependencies: + is-stream "^2.0.0" + type-fest "^0.8.0" + hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -3561,11 +4093,35 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +http-signature@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.4.0.tgz#dee5a9ba2bf49416abc544abd6d967f6a94c8c3f" + integrity sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.18.0" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + hyphenate-style-name@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz#1797bf50369588b47b72ca6d5e65374607cf4436" integrity sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw== +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -3617,6 +4173,11 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + inline-style-parser@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" @@ -3768,6 +4329,14 @@ is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" +is-installed-globally@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" @@ -3791,6 +4360,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" @@ -3837,6 +4411,11 @@ is-shared-array-buffer@^1.0.4: dependencies: call-bound "^1.0.3" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-string@^1.0.7, is-string@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" @@ -3868,6 +4447,16 @@ is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: dependencies: which-typed-array "^1.1.16" +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-weakmap@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" @@ -3898,6 +4487,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + iterator.prototype@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" @@ -3925,6 +4519,19 @@ jest-get-type@^27.0.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== +joi@^18.0.1: + version "18.0.2" + resolved "https://registry.yarnpkg.com/joi/-/joi-18.0.2.tgz#30ced6aed00a7848cc11f92859515258301dc3a4" + integrity sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA== + dependencies: + "@hapi/address" "^5.1.1" + "@hapi/formula" "^3.0.2" + "@hapi/hoek" "^11.0.7" + "@hapi/pinpoint" "^2.0.1" + "@hapi/tlds" "^1.1.1" + "@hapi/topo" "^6.0.2" + "@standard-schema/spec" "^1.0.0" + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3938,6 +4545,11 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + jsesc@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" @@ -3973,11 +4585,21 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -3990,6 +4612,25 @@ json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonfile@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" + integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" @@ -4024,6 +4665,11 @@ language-tags@^1.0.9: dependencies: language-subtag-registry "^0.3.20" +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -4037,6 +4683,20 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +listr2@^3.8.3: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.1" + through "^2.3.8" + wrap-ansi "^7.0.0" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -4062,6 +4722,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" @@ -4072,6 +4737,24 @@ lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -4098,6 +4781,11 @@ lz-string@^1.4.4: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== + matchmediaquery@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/matchmediaquery/-/matchmediaquery-0.4.2.tgz#22582bd4ae63ad9f54c53001bba80cbed0f7eafa" @@ -4163,6 +4851,11 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -4378,6 +5071,23 @@ micromatch@^4.0.8: braces "^3.0.3" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + min-indent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" @@ -4397,7 +5107,7 @@ minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -4482,6 +5192,13 @@ npm-run-all@^4.1.5: shell-quote "^1.6.1" string.prototype.padend "^3.0.0" +npm-run-path@^4.0.0, npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -4587,13 +5304,20 @@ object.values@^1.1.6, object.values@^1.2.0, object.values@^1.2.1: define-properties "^1.2.1" es-object-atoms "^1.0.0" -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + optionator@^0.9.1: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -4606,6 +5330,11 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.5" +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== + own-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" @@ -4615,6 +5344,13 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -4650,7 +5386,7 @@ path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -4672,6 +5408,23 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== + dependencies: + through "~2.3" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -4697,6 +5450,11 @@ pidtree@^0.3.0: resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== +pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -4738,6 +5496,11 @@ prettier@^2.2.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f" @@ -4748,6 +5511,11 @@ pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.0.6: ansi-styles "^5.0.0" react-is "^17.0.1" +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -4772,11 +5540,43 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.4.0.tgz#6bc4c618b0c2d68b3bb8b552cbb97f8e300a0f82" integrity sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ== +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + +pump@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" + integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qs@6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== + dependencies: + side-channel "^1.1.0" + queue-microtask@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" @@ -5004,6 +5804,13 @@ remark-rehype@^10.0.0: mdast-util-to-hast "^12.1.0" unified "^10.0.0" +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== + dependencies: + throttleit "^1.0.0" + require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" @@ -5045,11 +5852,24 @@ resolve@^2.0.0-next.5: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -5092,6 +5912,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^7.5.1, rxjs@^7.8.2: + version "7.8.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== + dependencies: + tslib "^2.1.0" + sade@^1.7.3: version "1.8.1" resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" @@ -5110,6 +5937,11 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-push-apply@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" @@ -5127,6 +5959,11 @@ safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -5266,11 +6103,25 @@ side-channel@^1.1.0: side-channel-map "^1.0.1" side-channel-weakmap "^1.0.2" +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -5316,17 +6167,60 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sshpk@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +start-server-and-test@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-2.1.3.tgz#1f8f8f2666131c502a7f8be69cf35418dde3f719" + integrity sha512-k4EcbNjeg0odaDkAMlIeDVDByqX9PIgL4tivgP2tES6Zd8o+4pTq/HgbWCyA3VHIoZopB+wGnNPKYGGSByNriQ== + dependencies: + arg "^5.0.2" + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.4.3" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "9.0.3" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== + dependencies: + duplexer "~0.1.1" + string-natural-compare@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -5455,6 +6349,11 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -5488,11 +6387,23 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +systeminformation@5.27.7: + version "5.27.7" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.27.7.tgz#4dc9d436419948cd5e5f076779a1298220d19a72" + integrity sha512-saaqOoVEEFaux4v0K8Q7caiauRwjXC4XbD2eH60dxHXbpKxQ8kH9Rf7Jh+nryKpOUSEFxtCdBlSUx0/lO6rwRg== + tabbable@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97" @@ -5514,6 +6425,16 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +throttleit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.1.tgz#304ec51631c3b770c65c6c6f76938b384000f4d5" + integrity sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ== + +through@2, through@^2.3.8, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + tiny-case@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03" @@ -5524,6 +6445,23 @@ tiny-warning@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tldts-core@^6.1.86: + version "6.1.86" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" + integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== + +tldts@^6.1.32: + version "6.1.86" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" + integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== + dependencies: + tldts-core "^6.1.86" + +tmp@~0.2.4: + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -5541,6 +6479,18 @@ toposort@^2.0.2: resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= +tough-cookie@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" + integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== + dependencies: + tldts "^6.1.32" + +tree-kill@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + trim-lines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" @@ -5576,7 +6526,7 @@ tslib@^2.0.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== -tslib@^2.7.0, tslib@^2.8.1: +tslib@^2.1.0, tslib@^2.7.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -5595,6 +6545,18 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -5607,6 +6569,16 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.8.0: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + type-fest@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" @@ -5756,6 +6728,16 @@ unist-util-visit@^4.0.0: unist-util-is "^5.0.0" unist-util-visit-parents "^5.1.1" +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + update-browserslist-db@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" @@ -5779,6 +6761,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uvu@^0.5.0: version "0.5.6" resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df" @@ -5802,6 +6789,15 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + vfile-message@^3.0.0: version "3.1.4" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.1.4.tgz#15a50816ae7d7c2d1fa87090a7f9f96612b59dea" @@ -5840,6 +6836,17 @@ vite@^6.1.0: optionalDependencies: fsevents "~2.3.3" +wait-on@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-9.0.3.tgz#3ea858db0b854039e6aff5f323885aaef25e32bf" + integrity sha512-13zBnyYvFDW1rBvWiJ6Av3ymAaq8EDQuvxZnPIw3g04UqGi4TyoIJABmfJ6zrvKo9yeFQExNkOk7idQbDJcuKA== + dependencies: + axios "^1.13.2" + joi "^18.0.1" + lodash "^4.17.21" + minimist "^1.2.8" + rxjs "^7.8.2" + which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" @@ -5911,6 +6918,24 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -5931,6 +6956,14 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yup@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/yup/-/yup-1.3.3.tgz#d2f6020ad1679754c5f8178a29243d5447dead04" From 184603c6ad0f19c02e22aafcee302d440f378b61 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:06:54 -0700 Subject: [PATCH 03/10] run ui e2e tests in github actions --- .github/workflows/ui.yaml | 128 ++++++++++++++++++++++++++ DEPLOYMENT.md | 2 +- scripts/deploy/helm.sh | 5 +- ui/TESTING.md | 9 ++ ui/cypress.config.ts | 18 +++- ui/cypress/e2e/flavor-selection.cy.ts | 68 ++++++++------ ui/cypress/e2e/home.cy.ts | 9 +- ui/cypress/support/commands.ts | 6 +- 8 files changed, 211 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/ui.yaml diff --git a/.github/workflows/ui.yaml b/.github/workflows/ui.yaml new file mode 100644 index 000000000..cbe2808dc --- /dev/null +++ b/.github/workflows/ui.yaml @@ -0,0 +1,128 @@ +name: UI tests + +on: + pull_request: + push: + branches: + - master + +jobs: + ui-e2e-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Free disk space (delete unused tools) + id: delete-unused-tools + continue-on-error: true + shell: bash + run: | + free_disk_space=22 + # delete preinstalled unused tools + cleanup=( + /usr/share/dotnet + /usr/share/miniconda + /usr/share/swift + /usr/share/kotlinc + /opt/ghc + /opt/hostedtoolcache/CodeQL + /opt/hostedtoolcache/Ruby + /opt/az + /usr/local/lib/android + ) + for d in "${cleanup[@]}"; do + if [[ -d "$d" ]]; then + rm -rf -- "$d" && echo "deleted $d" + else + echo "$d not found" + continue + fi + free=$(df -BGB --output=avail / | tail -1) + if [[ ${free%GB} -ge "${free_disk_space}" ]]; then + echo "Reached requested free disk space ${free_disk_space} [${free} free]." + exit 0 + fi + done + df -h + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create KinD Cluster + uses: helm/kind-action@v1 + with: + cluster_name: kind + + - name: tags + run: | + echo "TAG=$(make tag)" | tee -a "$GITHUB_ENV" + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + file: image/Dockerfile + context: . + push: false + load: true + tags: quay.io/rhacs-eng/infra-server:${{ env.TAG }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Load into KinD + run: | + # Check cluster name + kind get clusters + #docker build -t quay.io/rhacs-eng/infra-server:${{ env.TAG }} -f image/Dockerfile . + kind load docker-image quay.io/rhacs-eng/infra-server:${{ env.TAG }} --name kind + docker images | grep infra-server + + - name: Deploy + run: make deploy-local + + - name: Wait for pods + run: kubectl wait --for=condition=ready pod -l app=infra-server -n infra --timeout=3m + + - name: Start port-forward + run: | + kubectl port-forward -n infra svc/infra-server-service 8443:8443 >/dev/null 2>&1 & + echo "PORT_FORWARD_PID=$!" >> "$GITHUB_ENV" + sleep 5 + # Verify port-forward is working + timeout 10 sh -c 'until curl -k -f https://localhost:8443/v1/whoami 2>/dev/null; do sleep 1; done' || echo "Warning: Backend may not be ready" + + - name: Run E2E tests + uses: cypress-io/github-action@v6 + with: + working-directory: ui + start: npm run start + wait-on: 'http://localhost:3001' + wait-on-timeout: 60 + command: npm run cypress:run:e2e + env: + BROWSER: none + PORT: 3001 + # Backend uses HTTPS with self-signed cert (see scripts/deploy/helm.sh) + INFRA_API_ENDPOINT: https://localhost:8443 + + - name: Upload test artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: cypress-artifacts + path: | + ui/cypress/videos + ui/cypress/screenshots + retention-days: 7 + + - name: Cleanup port-forward + if: always() + run: | + # Kill by PID if available, otherwise kill by process name + if [ -n "${{ env.PORT_FORWARD_PID }}" ]; then + echo "Cleaning up port-forward (PID: ${{ env.PORT_FORWARD_PID }})..." + kill ${{ env.PORT_FORWARD_PID }} 2>/dev/null || true + fi + # Fallback: kill any remaining port-forward processes + pkill -f "kubectl port-forward.*8443:8443" 2>/dev/null || true diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index a4b1f26b8..1ea2029c8 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -118,7 +118,7 @@ This is used in the infra PR clusters to set the login referer and disable telem #### Deployments for testing only (no secrets) -For test clusters (such as a local KinD/Colima), you can use the deploy-local make target to skip loading secrets. The flavor provisioning actions that require secrets will no be accessible, and integrations such as with Slack will be disabled. +For test clusters (such as a local KinD/Colima), you can use the deploy-local make target to skip loading secrets. The flavor provisioning actions that require secrets will not be accessible, and integrations such as with Slack will be disabled. `make deploy-local` diff --git a/scripts/deploy/helm.sh b/scripts/deploy/helm.sh index df4b32a40..eb311211a 100755 --- a/scripts/deploy/helm.sh +++ b/scripts/deploy/helm.sh @@ -135,7 +135,10 @@ deploy-local() { if [[ ! -f "${cert_file}" ]] || [[ ! -f "${key_file}" ]]; then echo "Generating self-signed certificate for local development..." >&2 # Create a temporary config file for SAN extension - local san_config=$(mktemp) + # SAN (Subject Alternative Name) is required for gRPC-Gateway TLS validation in modern Go versions + # Without SAN, you'll get: "x509: certificate relies on legacy Common Name field" + local san_config + san_config=$(mktemp) || { echo "Failed to create temporary config file" >&2; return 1; } cat > "${san_config}" < + const base64UrlEncode = (obj: object) => Buffer.from(JSON.stringify(obj)) .toString('base64') .replace(/\+/g, '-') diff --git a/ui/cypress/e2e/flavor-selection.cy.ts b/ui/cypress/e2e/flavor-selection.cy.ts index f78312ac0..cdea3c17e 100644 --- a/ui/cypress/e2e/flavor-selection.cy.ts +++ b/ui/cypress/e2e/flavor-selection.cy.ts @@ -1,3 +1,16 @@ +const ERROR_MESSAGES = { + ACCESS_DENIED: 'access denied', + UNEXPECTED_ERROR: 'There was an unexpected error', +}; + +const SELECTORS = { + FLAVOR_CARD: '.pf-v6-c-card', + CARD_TITLE: '.pf-v6-c-card__title', + LABEL: '.pf-v6-c-label', + FLAVOR_TOGGLE: 'input[name="flavor-filter-toggle"]', + PAGE_HEADING: 'h2', +}; + describe('Flavor Selection', () => { beforeEach(() => { // Authenticate for local development before visiting the page @@ -7,41 +20,41 @@ describe('Flavor Selection', () => { it('should load the page without authentication errors', () => { // Verify no error messages (confirms LOCAL_DEPLOY mode is working) - cy.get('body').should('not.contain', 'access denied'); - cy.get('body').should('not.contain', 'There was an unexpected error'); + cy.get('body').should('not.contain', ERROR_MESSAGES.ACCESS_DENIED); + cy.get('body').should('not.contain', ERROR_MESSAGES.UNEXPECTED_ERROR); }); it('should display a list of available flavors', () => { - // Wait for flavors to load (check for either "My Flavors" or "All Flavors" title) - cy.contains('h2', /My Flavors|All Flavors/).should('be.visible'); + // Wait for the page heading to be visible (indicates page has loaded) + cy.get(SELECTORS.PAGE_HEADING).should('be.visible'); // Verify that the flavor gallery is not empty // Each flavor is rendered as a LinkCard inside a GalleryItem - cy.get('.pf-v6-c-card').should('have.length.at.least', 1); + cy.get(SELECTORS.FLAVOR_CARD).should('have.length.at.least', 1); }); it('should display flavor details for each flavor card', () => { // Wait for flavors to load - cy.contains('h2', /My Flavors|All Flavors/).should('be.visible'); + cy.get(SELECTORS.PAGE_HEADING).should('be.visible'); // Get the first flavor card and verify it has required elements - cy.get('.pf-v6-c-card') + cy.get(SELECTORS.FLAVOR_CARD) .first() .within(() => { // Each flavor card should have a name (header text) - cy.get('.pf-v6-c-card__title').should('exist').and('not.be.empty'); + cy.get(SELECTORS.CARD_TITLE).should('exist').and('not.be.empty'); // Each flavor card should have an availability label - cy.get('.pf-v6-c-label').should('exist'); + cy.get(SELECTORS.LABEL).should('exist'); }); }); it('should have clickable flavor cards that navigate to launch page', () => { // Wait for flavors to load - cy.contains('h2', /My Flavors|All Flavors/).should('be.visible'); + cy.get(SELECTORS.PAGE_HEADING).should('be.visible'); // Click the first flavor card - cy.get('.pf-v6-c-card').first().click(); + cy.get(SELECTORS.FLAVOR_CARD).first().click(); // Verify navigation to launch page (URL should contain /launch/) cy.url().should('include', '/launch/'); @@ -50,24 +63,27 @@ describe('Flavor Selection', () => { cy.contains('h1', /Launch/).should('be.visible'); }); - it('should toggle between "My Flavors" and "All Flavors"', () => { - // Verify initial state - cy.contains('h2', 'My Flavors').should('be.visible'); - - // Find and click the "Show All Flavors" toggle switch - // Use force:true because PatternFly switch has a visual element covering the input - cy.get('input[name="flavor-filter-toggle"]').click({ force: true }); + it('should toggle between flavor filter states', () => { + // Get the initial heading text + cy.get(SELECTORS.PAGE_HEADING) + .should('be.visible') + .invoke('text') + .then((initialHeading) => { + // Find and click the flavor filter toggle switch + // Use force:true because PatternFly switch has a visual element covering the input + cy.get(SELECTORS.FLAVOR_TOGGLE).click({ force: true }); - // Verify the title changed to "All Flavors" - cy.contains('h2', 'All Flavors').should('be.visible'); + // Verify the heading text changed + cy.get(SELECTORS.PAGE_HEADING).invoke('text').should('not.equal', initialHeading); - // Verify URL parameter was updated - cy.url().should('include', 'showAllFlavors=true'); + // Verify URL parameter was updated + cy.url().should('include', 'showAllFlavors=true'); - // Toggle back - cy.get('input[name="flavor-filter-toggle"]').click({ force: true }); + // Toggle back + cy.get(SELECTORS.FLAVOR_TOGGLE).click({ force: true }); - // Verify we're back to "My Flavors" - cy.contains('h2', 'My Flavors').should('be.visible'); + // Verify we're back to the original heading + cy.get(SELECTORS.PAGE_HEADING).invoke('text').should('equal', initialHeading); + }); }); }); diff --git a/ui/cypress/e2e/home.cy.ts b/ui/cypress/e2e/home.cy.ts index c76d51556..c00fd742e 100644 --- a/ui/cypress/e2e/home.cy.ts +++ b/ui/cypress/e2e/home.cy.ts @@ -1,3 +1,8 @@ +const ERROR_MESSAGES = { + ACCESS_DENIED: 'access denied', + UNEXPECTED_ERROR: 'There was an unexpected error', +}; + describe('Home Page', () => { beforeEach(() => { // Authenticate for local development before visiting the page @@ -10,7 +15,7 @@ describe('Home Page', () => { }); it('should not show error messages', () => { - cy.get('body').should('not.contain', 'access denied'); - cy.get('body').should('not.contain', 'There was an unexpected error'); + cy.get('body').should('not.contain', ERROR_MESSAGES.ACCESS_DENIED); + cy.get('body').should('not.contain', ERROR_MESSAGES.UNEXPECTED_ERROR); }); }); diff --git a/ui/cypress/support/commands.ts b/ui/cypress/support/commands.ts index db94c6f14..73e5a2f0d 100644 --- a/ui/cypress/support/commands.ts +++ b/ui/cypress/support/commands.ts @@ -5,14 +5,16 @@ * This uses the known session secret from local-deploy oidc.yaml. */ Cypress.Commands.add('loginForLocalDev', () => { - // The session secret from chart/infra-server/templates/secrets.yaml for local deploy + // IMPORTANT: This secret is ONLY for local development (LOCAL_DEPLOY=true). + // It matches chart/infra-server/configuration/local-values.yaml + // Production deployments use different secrets from GCP Secret Manager. const sessionSecret = 'local-dev-secret-min-32-chars-long'; // Create a test user matching the backend's expected structure // Note: Fields are capitalized to match Go's JSON serialization of protobuf structs const testUser = { Name: 'Test User', - Email: 'test@redhat.com', + Email: 'test@redhat.com', // Backend requires @redhat.com domain (see pkg/auth/tokenizer.go:128) Picture: '', Expiry: { seconds: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now From 62b4a54233fec5dc473ade6ab124bc92ad59c6fd Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Mon, 8 Dec 2025 09:04:32 -0700 Subject: [PATCH 04/10] always pull and cleanup LOCAL_DEPLOY --- .../configuration/local-values.yaml | 6 +++-- chart/infra-server/templates/deployment.yaml | 2 -- chart/infra-server/templates/secrets.yaml | 6 ++--- ui/TESTING.md | 23 ++----------------- ui/cypress/e2e/flavor-selection.cy.ts | 2 +- ui/cypress/support/commands.ts | 2 +- 6 files changed, 11 insertions(+), 30 deletions(-) diff --git a/chart/infra-server/configuration/local-values.yaml b/chart/infra-server/configuration/local-values.yaml index 68e6d4abb..9cb860334 100644 --- a/chart/infra-server/configuration/local-values.yaml +++ b/chart/infra-server/configuration/local-values.yaml @@ -6,8 +6,10 @@ localDeploy: true # Enable test mode for faster cluster resume intervals testMode: true -# Use local Docker images instead of pulling from registry -imagePullPolicy: Never +# Use local Docker images when available, pull from registry if not present +# Works with Colima (shared Docker daemon) and kind (after kind load docker-image) +# IfNotPresent provides flexibility: uses local images when available, pulls when needed +imagePullPolicy: IfNotPresent # Pull secrets for container registries - dummy values for local development pullSecrets: diff --git a/chart/infra-server/templates/deployment.yaml b/chart/infra-server/templates/deployment.yaml index 8f609ac7e..d2002f978 100644 --- a/chart/infra-server/templates/deployment.yaml +++ b/chart/infra-server/templates/deployment.yaml @@ -31,8 +31,6 @@ spec: - name: GOOGLE_APPLICATION_CREDENTIALS value: /configuration/google-credentials.json {{- end }} - - name: LOCAL_DEPLOY - value: "{{ .Values.localDeploy }}" - name: TEST_MODE value: "{{ .Values.testMode }}" readinessProbe: diff --git a/chart/infra-server/templates/secrets.yaml b/chart/infra-server/templates/secrets.yaml index 663023c34..60b8a9239 100644 --- a/chart/infra-server/templates/secrets.yaml +++ b/chart/infra-server/templates/secrets.yaml @@ -127,16 +127,16 @@ metadata: app.kubernetes.io/name: infra-server data: - # Minimal config for LOCAL_DEPLOY mode + # Minimal config for localDeploy mode infra.yaml: {{ printf "server:\n port: 8443\n cert: /configuration/cert.pem\n key: /configuration/key.pem\n static: /etc/infra/static\n metricsPort: 9101" | b64enc }} # Minimal OIDC config - uses Google as a valid issuer for initialization oidc.yaml: {{ printf "issuer: https://accounts.google.com\nclientID: dummy\nclientSecret: dummy\nendpoint: localhost:8443\nsessionSecret: local-dev-secret-min-32-chars-long\ntokenLifetime: 24h" | b64enc }} - # Empty Google credentials - not used in LOCAL_DEPLOY mode + # Empty Google credentials - not used in localDeploy mode google-credentials.json: {{ "{}" | b64enc }} - # Empty BigQuery credentials - not used in LOCAL_DEPLOY mode + # Empty BigQuery credentials - not used in localDeploy mode bigquery-sa.json: {{ "{}" | b64enc }} # Self-signed TLS certificate for local development diff --git a/ui/TESTING.md b/ui/TESTING.md index 34b956711..d96eb129c 100644 --- a/ui/TESTING.md +++ b/ui/TESTING.md @@ -15,16 +15,13 @@ This directory contains Cypress E2E tests for the StackRox Infra UI. In your clone of the repository, you must build the infra-server into the image for it to be deployed in the next step. -1. **Deploy the local backend** (with authentication disabled): +1. **Deploy the local backend**: ```bash # From the repository root make deploy-local ``` - This deploys the infra-server to your local Kubernetes cluster with - `LOCAL_DEPLOY=true`, which disables authentication for local development. - 2. **Start port-forwarding** to access the backend: ```bash @@ -133,29 +130,13 @@ To add new E2E tests: ### Tests fail with "Cypress failed to verify that your server is running" **Solution:** Make sure the UI dev server is running on port 3001 before running -tests: +tests. If not using the make target, you can manually run the dev server: ```bash cd ui BROWSER=none PORT=3001 npm start ``` -### Tests show "access denied" or authentication errors - -**Solution:** Verify that: - -1. The backend was deployed with `LOCAL_DEPLOY=true` (via `make deploy-local`) -2. Port-forwarding is active: - `kubectl port-forward -n infra svc/infra-server-service 8443:8443` -3. The `.env.local` file points to the correct backend: - `INFRA_API_ENDPOINT=https://localhost:8443` - -You can check if LOCAL_DEPLOY is enabled: - -```bash -kubectl get deployment -n infra infra-server-deployment -o jsonpath='{.spec.template.spec.containers[0].env}' | grep LOCAL_DEPLOY -``` - ### Port 3001 or 8443 already in use **Solution:** diff --git a/ui/cypress/e2e/flavor-selection.cy.ts b/ui/cypress/e2e/flavor-selection.cy.ts index cdea3c17e..7d5385983 100644 --- a/ui/cypress/e2e/flavor-selection.cy.ts +++ b/ui/cypress/e2e/flavor-selection.cy.ts @@ -19,7 +19,7 @@ describe('Flavor Selection', () => { }); it('should load the page without authentication errors', () => { - // Verify no error messages (confirms LOCAL_DEPLOY mode is working) + // Verify no error messages cy.get('body').should('not.contain', ERROR_MESSAGES.ACCESS_DENIED); cy.get('body').should('not.contain', ERROR_MESSAGES.UNEXPECTED_ERROR); }); diff --git a/ui/cypress/support/commands.ts b/ui/cypress/support/commands.ts index 73e5a2f0d..d2e36adec 100644 --- a/ui/cypress/support/commands.ts +++ b/ui/cypress/support/commands.ts @@ -5,7 +5,7 @@ * This uses the known session secret from local-deploy oidc.yaml. */ Cypress.Commands.add('loginForLocalDev', () => { - // IMPORTANT: This secret is ONLY for local development (LOCAL_DEPLOY=true). + // IMPORTANT: This secret is ONLY for local development. // It matches chart/infra-server/configuration/local-values.yaml // Production deployments use different secrets from GCP Secret Manager. const sessionSecret = 'local-dev-secret-min-32-chars-long'; From 2855c40e3827b09fa8ea7af6dc23d28a43bd5605 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Mon, 8 Dec 2025 13:59:38 -0700 Subject: [PATCH 05/10] remove verbose logging --- scripts/deploy/helm.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/deploy/helm.sh b/scripts/deploy/helm.sh index eb311211a..1ea5eea5d 100755 --- a/scripts/deploy/helm.sh +++ b/scripts/deploy/helm.sh @@ -57,7 +57,6 @@ template() { ) } -# deploy upgrades the Helm release with deploy() { echo "Deploy.." >&2 echo " RELEASE_NAME: ${RELEASE_NAME}" >&2 @@ -65,7 +64,6 @@ deploy() { echo " Environment: ${ENVIRONMENT}" >&2 echo " Test Mode: ${TEST_MODE}" >&2 echo " Local Values: ${local_values:-false}" >&2 - set -x helm upgrade \ "${RELEASE_NAME}" \ chart/infra-server \ @@ -93,7 +91,6 @@ deploy() { cat 'chart/infra-server/configuration/local-values.yaml' fi ) - set +x } # diff renders the Helm chart and compares the deployed resources to show what would change on next deployment. From 340151feb11752200e6f51488e7699b6d684ae39 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:24:36 -0700 Subject: [PATCH 06/10] only setup signer if google creds are set --- cmd/infra-server/main.go | 16 +++++++++-- pkg/signer/signer.go | 10 ++----- ui/TESTING.md | 62 ++++++++++++++++------------------------ 3 files changed, 39 insertions(+), 49 deletions(-) diff --git a/cmd/infra-server/main.go b/cmd/infra-server/main.go index 834b085a5..7db104c20 100644 --- a/cmd/infra-server/main.go +++ b/cmd/infra-server/main.go @@ -67,9 +67,19 @@ func mainCmd() error { return errors.Wrapf(err, "failed to load oidc config file %q", oidcConfigFile) } - signer, err := signer.NewFromEnv() - if err != nil { - return errors.Wrapf(err, "failed to load GCS signing credentials") + // Initialize GCS signer for signed URLs and artifact downloads. + // Only create signer if GOOGLE_APPLICATION_CREDENTIALS is set (production/development). + // Local deployments skip GCS signing entirely. + var signer *signer.Signer + if _, hasGCSCredentials := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); hasGCSCredentials { + var err error + signer, err = signer.NewFromEnv() + if err != nil { + return errors.Wrapf(err, "failed to load GCS signing credentials") + } + } else { + log.Log(logging.INFO, "GCS signing disabled: GOOGLE_APPLICATION_CREDENTIALS not set") + signer = &signer.Signer{} // Empty signer for local deployments } slackClient, err := slack.New(cfg.Slack) diff --git a/pkg/signer/signer.go b/pkg/signer/signer.go index 620788276..1f0bc76dc 100644 --- a/pkg/signer/signer.go +++ b/pkg/signer/signer.go @@ -13,7 +13,6 @@ import ( "time" "cloud.google.com/go/storage" - "github.com/stackrox/infra/pkg/logging" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" ) @@ -28,10 +27,6 @@ const ( gcsSignedURLLifespan = 10 * time.Minute ) -var ( - log = logging.CreateProductionLogger() -) - // Signer facilitates the generation of signed GCS URLS. type Signer struct { cfg jwt.Config @@ -39,12 +34,11 @@ type Signer struct { } // NewFromEnv constructs a Signer from the GOOGLE_APPLICATION_CREDENTIALS set -// in the working environment. Returns an empty Signer if not configured. +// in the working environment. func NewFromEnv() (*Signer, error) { credentialsFilename, found := os.LookupEnv(googleCredentialsEnvVar) if !found { - log.Log(logging.WARN, "GCS signing disabled", "reason", "GOOGLE_APPLICATION_CREDENTIALS not set") - return &Signer{}, nil + return nil, fmt.Errorf("environment variable %q was not set", googleCredentialsEnvVar) } data, err := os.ReadFile(credentialsFilename) diff --git a/ui/TESTING.md b/ui/TESTING.md index d96eb129c..890dfe9c3 100644 --- a/ui/TESTING.md +++ b/ui/TESTING.md @@ -30,44 +30,22 @@ This directory contains Cypress E2E tests for the StackRox Infra UI. Keep this running in a separate terminal. -3. **Configure the UI to connect to local backend**: +3. **Run the E2E tests**: ```bash cd ui - cp .env.example .env.local + INFRA_API_ENDPOINT=https://localhost:8443 npm run test:e2e ``` - This creates a `.env.local` file. Note: The file contains - `INFRA_API_ENDPOINT` but the environment variable must also be set when - starting the dev server (see next step). +That's it! The `test:e2e` command will: -4. **Start the UI dev server** (in a separate terminal): +- Automatically start the UI dev server on http://localhost:3001 +- Proxy API requests to your local backend at https://localhost:8443 +- Run all Cypress E2E tests +- Shut down the dev server when tests complete - ```bash - cd ui - BROWSER=none PORT=3001 INFRA_API_ENDPOINT=http://localhost:8443 npm start - ``` - - **Important:** The `INFRA_API_ENDPOINT` environment variable must be set when - starting the dev server (not just in `.env.local`) because the proxy - middleware reads it at startup. - - Keep this running. The dev server will: - - - Run on http://localhost:3001 - - Proxy API requests to http://localhost:8443 (your local backend) - - Hot-reload when you make changes to the UI code - -5. **Run the E2E tests** (in another terminal): - - ```bash - cd ui - npm run cypress:run:e2e - ``` - -That's it! The tests will run against the UI dev server at -http://localhost:3001, which proxies API requests to your local backend at -`https://localhost:8443`. +The tests run against the UI dev server at http://localhost:3001, which proxies +API requests to your local backend at `https://localhost:8443`. ### Test Results @@ -82,7 +60,14 @@ Review the videos to verify the tests are properly accessing the backend. To run tests interactively with the Cypress UI (useful for debugging): -**Prerequisites:** Make sure the UI dev server is running (step 4 above). +**Prerequisites:** Start the UI dev server with the backend endpoint configured: + +```bash +cd ui +INFRA_API_ENDPOINT=https://localhost:8443 npm start +``` + +Keep this running, then in another terminal: ```bash cd ui @@ -114,8 +99,8 @@ Tests are configured in `cypress.config.ts` to: - Capture screenshots on failures only - Retry failed tests 2 times in CI mode (run mode), 0 times in interactive mode -The UI dev server (configured via `ui/.env.local`) proxies API requests to your -local backend at `https://localhost:8443`. +The UI dev server (configured via `INFRA_API_ENDPOINT` environment variable) +proxies API requests to your local backend at `https://localhost:8443`. ## Adding More Tests @@ -129,12 +114,12 @@ To add new E2E tests: ### Tests fail with "Cypress failed to verify that your server is running" -**Solution:** Make sure the UI dev server is running on port 3001 before running -tests. If not using the make target, you can manually run the dev server: +**Solution:** Make sure you're using `npm run test:e2e` which automatically +starts the dev server. If you want to run the dev server manually, use: ```bash cd ui -BROWSER=none PORT=3001 npm start +INFRA_API_ENDPOINT=https://localhost:8443 npm start ``` ### Port 3001 or 8443 already in use @@ -142,7 +127,8 @@ BROWSER=none PORT=3001 npm start **Solution:** - Find and kill the process using the port: `lsof -i :3001` or `lsof -i :8443` -- Or use different ports by modifying `cypress.config.ts` and `.env.local` +- Or use different ports by modifying `cypress.config.ts` and the + `INFRA_API_ENDPOINT` environment variable ## Documentation From c14ad5173c57438ed2300d33a7ca9167538b78b2 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Mon, 8 Dec 2025 16:15:28 -0700 Subject: [PATCH 07/10] go lint --- cmd/infra-server/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/infra-server/main.go b/cmd/infra-server/main.go index 7db104c20..595e098ee 100644 --- a/cmd/infra-server/main.go +++ b/cmd/infra-server/main.go @@ -70,16 +70,16 @@ func mainCmd() error { // Initialize GCS signer for signed URLs and artifact downloads. // Only create signer if GOOGLE_APPLICATION_CREDENTIALS is set (production/development). // Local deployments skip GCS signing entirely. - var signer *signer.Signer + var gcsSigner *signer.Signer if _, hasGCSCredentials := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); hasGCSCredentials { var err error - signer, err = signer.NewFromEnv() + gcsSigner, err = signer.NewFromEnv() if err != nil { return errors.Wrapf(err, "failed to load GCS signing credentials") } } else { log.Log(logging.INFO, "GCS signing disabled: GOOGLE_APPLICATION_CREDENTIALS not set") - signer = &signer.Signer{} // Empty signer for local deployments + gcsSigner = &signer.Signer{} // Empty signer for local deployments } slackClient, err := slack.New(cfg.Slack) @@ -106,7 +106,7 @@ func mainCmd() error { service.NewStatusService, service.NewVersionService, func() (middleware.APIService, error) { - return cluster.NewClusterService(registry, signer, slackClient, bqClient) + return cluster.NewClusterService(registry, gcsSigner, slackClient, bqClient) }, ) if err != nil { From 9149c979d0dd1d51ef1a5963c1aabe4bd2e7e4b4 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Mon, 19 Jan 2026 09:00:54 -0700 Subject: [PATCH 08/10] Enable UI E2E tests to run on GitHub Actions with PR cluster This commit enables UI E2E tests to run against ephemeral GKE clusters in GitHub Actions, similar to how Go E2E tests work. Changes: 1. **TEST_MODE deployment support**: - Fix Helm template conditional in secrets.yaml to prevent duplicate secret creation when testMode=true - Generate self-signed certificates at deployment time for TEST_MODE - Delete and recreate secrets to force correct template application - Add test-mode-values.yaml to explicitly set testMode=true 2. **Session secret handling**: - Generate random session secrets for PR cluster deployments - Pass session secret from deploy job to UI E2E test job via outputs - Update Cypress commands to read SESSION_SECRET from environment - Update Makefile to generate and display session secret for local dev 3. **Runtime fixes**: - Handle invalid GCS credentials gracefully in TEST_MODE - Add wait for cluster readiness before deployment 4. **Test improvements**: - Add API error logging to Cypress tests for debugging - Use dynamic session secrets instead of hardcoded local-dev secret - Run UI E2E tests before Go E2E tests (UI tests are faster) - Go E2E tests use port-forward approach like PR 1735 This allows UI E2E tests to authenticate and run against PR clusters that don't have production secrets, enabling automated UI testing in CI. Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/PR.yaml | 166 +++++++++++++++++++++- Makefile | 16 ++- chart/infra-server/templates/secrets.yaml | 40 +++--- chart/infra-server/test-mode-values.yaml | 4 + cmd/infra-server/main.go | 10 +- scripts/deploy/helm.sh | 149 ++++++++++++++++--- ui/cypress/e2e/flavor-selection.cy.ts | 39 +++-- ui/cypress/support/commands.ts | 15 +- 8 files changed, 382 insertions(+), 57 deletions(-) create mode 100644 chart/infra-server/test-mode-values.yaml diff --git a/.github/workflows/PR.yaml b/.github/workflows/PR.yaml index e369945f4..6d4084dec 100644 --- a/.github/workflows/PR.yaml +++ b/.github/workflows/PR.yaml @@ -66,6 +66,8 @@ jobs: runs-on: ubuntu-latest container: image: quay.io/stackrox-io/apollo-ci:stackrox-test-0.4.9 + outputs: + session-secret: ${{ steps.deploy.outputs.session-secret }} env: KUBECONFIG: /github/home/artifacts/kubeconfig INFRA_TOKEN: ${{ secrets.INFRA_TOKEN }} @@ -100,10 +102,26 @@ jobs: - name: Download artifacts run: | /github/home/.local/bin/infractl artifacts "$CLUSTER_NAME" -d /github/home/artifacts >> "$GITHUB_STEP_SUMMARY" - kubectl get nodes -o wide || true + + - name: Wait for cluster to be ready + run: | + echo "Waiting for cluster API server to be ready..." + timeout 300 sh -c 'until kubectl get nodes >/dev/null 2>&1; do + echo "Waiting for cluster..." + sleep 5 + done' + echo "Cluster is ready" + kubectl get nodes -o wide - name: Deploy infra to dev cluster + id: deploy run: | + # Generate random session secret for JWT signing + # This secret is used by both the server (for verification) and Cypress (for JWT generation) + SESSION_SECRET=$(openssl rand -base64 32 | tr -d '\n') + export SESSION_SECRET + echo "Generated random session secret for this PR cluster deployment" + ENVIRONMENT=development TEST_MODE=true make helm-deploy sleep 10 # wait for old pods to disappear so the svc port-forward doesn't connect to them kubectl -n infra port-forward svc/infra-server-service 8443:8443 > /dev/null 2>&1 & @@ -115,6 +133,11 @@ jobs: kill %1 + # Save session secret for UI E2E tests (job output for next job) + echo "session-secret=$SESSION_SECRET" >> "$GITHUB_OUTPUT" + # Also set as env var for steps in this job + echo "SESSION_SECRET=$SESSION_SECRET" >> "$GITHUB_ENV" + - name: Check the deployment run: | kubectl -n infra port-forward svc/infra-server-service 8443:8443 > /dev/null 2>&1 & @@ -162,3 +185,144 @@ jobs: kubectl -n infra port-forward svc/infra-server-service 8443:8443 > /dev/null 2>&1 & sleep 5 make go-e2e-tests + + ui-e2e-test-pr-cluster: + needs: + - deploy-and-test + runs-on: ubuntu-latest + # Note: This job does NOT use the apollo-ci container to avoid path issues + env: + KUBECONFIG: /tmp/kubeconfig + INFRA_TOKEN: ${{ secrets.INFRA_TOKEN }} + USE_GKE_GCLOUD_AUTH_PLUGIN: "True" + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + path: go/src/github.com/stackrox/infra + + - name: Authenticate to GCloud + uses: google-github-actions/auth@v3 + with: + credentials_json: ${{ secrets.INFRA_CI_AUTOMATION_GCP_SA }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v3 + with: + install_components: "gke-gcloud-auth-plugin" + + - name: Download production infractl + uses: stackrox/actions/infra/install-infractl@v1 + + - name: Get kubeconfig for PR cluster + run: | + echo "Downloading kubeconfig for $CLUSTER_NAME..." + /home/runner/.local/bin/infractl artifacts "$CLUSTER_NAME" -d /tmp/artifacts + cp /tmp/artifacts/kubeconfig "$KUBECONFIG" + + echo "Verifying cluster access..." + kubectl get nodes -o wide + + - name: Wait for infra-server deployment + run: | + echo "Checking infra-server pods..." + kubectl get pods -n infra + kubectl wait --for=condition=ready pod -l app=infra-server -n infra --timeout=5m + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install UI dependencies + run: | + cd ui + npm install --legacy-peer-deps + + - name: Start port-forward to PR cluster + run: | + kubectl -n infra port-forward svc/infra-server-service 8443:8443 >/dev/null 2>&1 & + PORT_FORWARD_PID=$! + echo "PORT_FORWARD_PID=$PORT_FORWARD_PID" >> "$GITHUB_ENV" + echo "Started port-forward with PID: $PORT_FORWARD_PID" + sleep 10 + + # Verify port-forward is working + echo "Verifying port-forward connectivity..." + timeout 30 sh -c 'until curl -k -f https://localhost:8443/v1/whoami 2>/dev/null; do + echo "Waiting for port-forward..." + sleep 2 + done' || { + echo "Port-forward verification failed" + pgrep -a port-forward || true + exit 1 + } + echo "Port-forward is working" + + - name: Debug - Check flavors API + run: | + echo "Checking if flavors are available..." + + # First try without auth (should fail with access denied) + echo "1. Testing without authentication:" + UNAUTH_RESPONSE=$(curl -k -s https://localhost:8443/v1/flavor/list || echo "API call failed") + echo "$UNAUTH_RESPONSE" | jq . || echo "$UNAUTH_RESPONSE" + + # Check whoami endpoint + echo "" + echo "2. Testing /v1/whoami:" + WHOAMI=$(curl -k -s https://localhost:8443/v1/whoami || echo "whoami failed") + echo "$WHOAMI" | jq . || echo "$WHOAMI" + + # The real issue is the UI itself - let's check if the flavors endpoint + # works at all. The UI must be getting an error from somewhere. + echo "" + echo "3. Checking flavors API (unauthenticated count):" + FLAVOR_COUNT=$(echo "$UNAUTH_RESPONSE" | jq '.flavors | length' 2>/dev/null || echo "0") + echo "Number of flavors available: $FLAVOR_COUNT" + + if [ "$FLAVOR_COUNT" = "0" ]; then + echo "NOTE: Flavors API requires authentication" + echo "This is expected - Cypress tests use JWT authentication with randomly generated secret" + fi + + - name: Run UI E2E tests + uses: cypress-io/github-action@v6 + with: + working-directory: go/src/github.com/stackrox/infra/ui + install: false + start: npm run start + wait-on: 'http://localhost:3001' + wait-on-timeout: 60 + command: npm run cypress:run:e2e + env: + BROWSER: none + PORT: 3001 + # Backend is the PR cluster deployment accessed via port-forward + # This deployment uses ENVIRONMENT=development with real OIDC (NOT localDeploy=true) + INFRA_API_ENDPOINT: https://localhost:8443 + # Session secret for JWT generation (matches what the server uses) + # Retrieved from deploy-and-test job output + CYPRESS_SESSION_SECRET: ${{ needs.deploy-and-test.outputs.session-secret }} + + - name: Upload test artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: cypress-artifacts-pr-cluster-${{ github.event.pull_request.number }} + path: | + go/src/github.com/stackrox/infra/ui/cypress/videos + go/src/github.com/stackrox/infra/ui/cypress/screenshots + retention-days: 7 + + - name: Cleanup port-forward + if: always() + run: | + if [ -n "${{ env.PORT_FORWARD_PID }}" ]; then + echo "Cleaning up port-forward (PID: ${{ env.PORT_FORWARD_PID }})..." + kill ${{ env.PORT_FORWARD_PID }} 2>/dev/null || true + fi + pkill -f "kubectl port-forward.*8443:8443" 2>/dev/null || true diff --git a/Makefile b/Makefile index 1b9a2c9fb..659526321 100644 --- a/Makefile +++ b/Makefile @@ -256,7 +256,15 @@ helm-diff: pre-check helm-dependency-update create-namespaces ## Deploy to local cluster (e.g., Colima) without GCP Secret Manager .PHONY: deploy-local deploy-local: helm-dependency-update create-namespaces - TEST_MODE=true ./scripts/deploy/helm.sh deploy-local $(shell make tag) local + @echo "Generating random session secret for local deployment..." + $(eval SESSION_SECRET := $(shell openssl rand -base64 32 | tr -d '\n')) + @echo "SESSION_SECRET generated (use 'export SESSION_SECRET=' for Cypress tests)" + @SESSION_SECRET="$(SESSION_SECRET)" TEST_MODE=true ./scripts/deploy/helm.sh deploy-local $(shell make tag) local + @echo "" + @echo "Deployment complete!" + @echo "To run E2E tests, export the session secret:" + @echo " export SESSION_SECRET='$(SESSION_SECRET)'" + @echo " make test-e2e" ## Run UI E2E tests against local deployment .PHONY: test-e2e @@ -276,7 +284,11 @@ test-e2e: trap cleanup EXIT; \ sleep 5; \ echo "Running Cypress E2E tests..." >&2; \ - cd ui && BROWSER=none PORT=3001 INFRA_API_ENDPOINT=http://localhost:8443 npm run test:e2e + if [ -z "$$SESSION_SECRET" ]; then \ + echo "WARNING: SESSION_SECRET not set. Using default for local laptop development." >&2; \ + echo "If tests fail, make sure you exported SESSION_SECRET from deploy-local output." >&2; \ + fi; \ + cd ui && BROWSER=none PORT=3001 INFRA_API_ENDPOINT=http://localhost:8443 CYPRESS_SESSION_SECRET="$$SESSION_SECRET" npm run test:e2e ## Bounce pods .PHONY: bounce-infra-pods diff --git a/chart/infra-server/templates/secrets.yaml b/chart/infra-server/templates/secrets.yaml index 60b8a9239..81e23ac1f 100644 --- a/chart/infra-server/templates/secrets.yaml +++ b/chart/infra-server/templates/secrets.yaml @@ -1,4 +1,19 @@ -{{- if not .Values.localDeploy }} +{{- if not (or .Values.localDeploy .Values.testMode) }} +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/dockerconfigjson + +metadata: + name: infra-image-registry-pull-secret + namespace: infra + +data: + .dockerconfigjson: {{ template "pull-secret" .Values.pullSecrets.quay }} + +--- +{{- end }} +{{- if not (or .Values.localDeploy .Values.testMode) }} apiVersion: v1 kind: Secret @@ -101,21 +116,9 @@ data: test-janitor-delete.yaml: |- {{- tpl (.Files.Get "static/test-janitor-delete.yaml" ) . | b64enc | nindent 4 }} {{ end }} - ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson - -metadata: - name: infra-image-registry-pull-secret - namespace: infra - -data: - .dockerconfigjson: {{ template "pull-secret" .Values.pullSecrets.quay }} {{- end }} -{{- if .Values.localDeploy }} +{{- if or .Values.localDeploy .Values.testMode }} --- apiVersion: v1 kind: Secret @@ -127,16 +130,17 @@ metadata: app.kubernetes.io/name: infra-server data: - # Minimal config for localDeploy mode + # Minimal config for localDeploy and testMode infra.yaml: {{ printf "server:\n port: 8443\n cert: /configuration/cert.pem\n key: /configuration/key.pem\n static: /etc/infra/static\n metricsPort: 9101" | b64enc }} # Minimal OIDC config - uses Google as a valid issuer for initialization - oidc.yaml: {{ printf "issuer: https://accounts.google.com\nclientID: dummy\nclientSecret: dummy\nendpoint: localhost:8443\nsessionSecret: local-dev-secret-min-32-chars-long\ntokenLifetime: 24h" | b64enc }} + # sessionSecret is randomly generated at deployment time for security + oidc.yaml: {{ printf "issuer: https://accounts.google.com\nclientID: dummy\nclientSecret: dummy\nendpoint: localhost:8443\nsessionSecret: %s\ntokenLifetime: 24h" (required ".Values.sessionSecret is required for localDeploy and testMode" .Values.sessionSecret) | b64enc }} - # Empty Google credentials - not used in localDeploy mode + # Empty Google credentials - not used in localDeploy or testMode google-credentials.json: {{ "{}" | b64enc }} - # Empty BigQuery credentials - not used in localDeploy mode + # Empty BigQuery credentials - not used in localDeploy or testMode bigquery-sa.json: {{ "{}" | b64enc }} # Self-signed TLS certificate for local development diff --git a/chart/infra-server/test-mode-values.yaml b/chart/infra-server/test-mode-values.yaml new file mode 100644 index 000000000..e7dcc9ddc --- /dev/null +++ b/chart/infra-server/test-mode-values.yaml @@ -0,0 +1,4 @@ +# Values for TEST_MODE deployments (PR clusters, CI testing) +# This file explicitly sets testMode to true, overriding any values from GCloud secrets + +testMode: true diff --git a/cmd/infra-server/main.go b/cmd/infra-server/main.go index 595e098ee..28e9a2881 100644 --- a/cmd/infra-server/main.go +++ b/cmd/infra-server/main.go @@ -69,13 +69,19 @@ func mainCmd() error { // Initialize GCS signer for signed URLs and artifact downloads. // Only create signer if GOOGLE_APPLICATION_CREDENTIALS is set (production/development). - // Local deployments skip GCS signing entirely. + // Local deployments and test mode skip GCS signing entirely. var gcsSigner *signer.Signer + testMode := os.Getenv("TEST_MODE") == "true" if _, hasGCSCredentials := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); hasGCSCredentials { var err error gcsSigner, err = signer.NewFromEnv() if err != nil { - return errors.Wrapf(err, "failed to load GCS signing credentials") + if testMode { + log.Log(logging.WARN, "GCS signing disabled in TEST_MODE: invalid credentials", "error", err.Error()) + gcsSigner = &signer.Signer{} // Empty signer for test mode with invalid credentials + } else { + return errors.Wrapf(err, "failed to load GCS signing credentials") + } } } else { log.Log(logging.INFO, "GCS signing disabled: GOOGLE_APPLICATION_CREDENTIALS not set") diff --git a/scripts/deploy/helm.sh b/scripts/deploy/helm.sh index 1ea5eea5d..515be604a 100755 --- a/scripts/deploy/helm.sh +++ b/scripts/deploy/helm.sh @@ -11,6 +11,13 @@ SECRET_VERSION="${4:-latest}" # Cannot use CI, because then CD with GHA would not be possible. TEST_MODE="${TEST_MODE:-false}" +# Session secret for JWT signing (used in local and test deployments) +# Randomly generated at deployment time for security +SESSION_SECRET="${SESSION_SECRET:-}" + +# Helm debug mode (optional) +HELM_DEBUG="${HELM_DEBUG:-}" + SECRETS_PROJECT="acs-team-automation" RELEASE_NAMESPACE="infra" RELEASE_NAME="infra-server" @@ -64,22 +71,112 @@ deploy() { echo " Environment: ${ENVIRONMENT}" >&2 echo " Test Mode: ${TEST_MODE}" >&2 echo " Local Values: ${local_values:-false}" >&2 - helm upgrade \ - "${RELEASE_NAME}" \ - chart/infra-server \ - --install \ - --create-namespace \ - --timeout 5m \ - --wait \ - --namespace "${RELEASE_NAMESPACE}" \ - --values chart/infra-server/argo-values.yaml \ - --values chart/infra-server/monitoring-values.yaml \ - --set tag="${TAG}" \ - --set environment="${ENVIRONMENT}" \ - --set testMode="${TEST_MODE}" \ - ${HELM_DEBUG:+--debug} \ - --values - \ - < <( + echo " Session Secret: ${SESSION_SECRET:+}" >&2 + + # Delete existing secret in TEST_MODE to force recreation with correct template + # Previous deployments may have created the secret with the wrong template + if [[ "${TEST_MODE}" == "true" ]]; then + echo "Deleting existing infra-server-secrets to force recreation..." >&2 + kubectl delete secret infra-server-secrets -n "${RELEASE_NAMESPACE}" --ignore-not-found >&2 + + # Generate self-signed cert for TEST_MODE deployments (similar to deploy-local) + local cert_dir='chart/infra-server/configuration' + local cert_file="${cert_dir}/local-cert.pem" + local key_file="${cert_dir}/local-key.pem" + + mkdir -p "${cert_dir}" + + if [[ ! -f "${cert_file}" ]] || [[ ! -f "${key_file}" ]]; then + echo "Generating self-signed certificate for TEST_MODE..." >&2 + # Create a temporary config file for SAN extension + local san_config + san_config=$(mktemp) || { echo "Failed to create temporary config file" >&2; return 1; } + cat > "${san_config}" <&2 + rm -f "${san_config}" + echo "Certificate generated: ${cert_file}" >&2 + else + echo "Using existing certificate: ${cert_file}" >&2 + fi + fi + + # Build helm command + local helm_cmd=( + helm upgrade + "${RELEASE_NAME}" + chart/infra-server + --install + --create-namespace + --timeout 5m + --wait + --namespace "${RELEASE_NAMESPACE}" + --values chart/infra-server/argo-values.yaml + --values chart/infra-server/monitoring-values.yaml + --values - + ) + + # Add test-mode values file if TEST_MODE is enabled + # This ensures testMode=true overrides any GCloud secret values + if [[ "${TEST_MODE}" == "true" ]]; then + helm_cmd+=(--values chart/infra-server/test-mode-values.yaml) + fi + + # Add runtime values as --set flags + helm_cmd+=( + --set tag="${TAG}" + --set environment="${ENVIRONMENT}" + ) + + # Add sessionSecret if set + if [[ -n "${SESSION_SECRET}" ]]; then + helm_cmd+=(--set sessionSecret="${SESSION_SECRET}") + fi + + # Enable debug in TEST_MODE to see actual values + if [[ -n "${HELM_DEBUG}" ]] || [[ "${TEST_MODE}" == "true" ]]; then + helm_cmd+=(--debug) + fi + + # Debug: Show the helm command that will be executed + echo "=== Helm Command ===" >&2 + printf '%q ' "${helm_cmd[@]}" >&2 + echo >&2 + echo "===================" >&2 + + # Check if test-mode-values.yaml exists + if [[ "${TEST_MODE}" == "true" ]]; then + if [[ -f "chart/infra-server/test-mode-values.yaml" ]]; then + echo "test-mode-values.yaml exists" >&2 + cat chart/infra-server/test-mode-values.yaml >&2 + else + echo "ERROR: test-mode-values.yaml NOT FOUND" >&2 + fi + fi + + # Temporarily disable error exit to capture helm failure and run debugging + set +e + "${helm_cmd[@]}" < <( if [[ ${local_values:-} != 'true' ]]; then gcloud secrets versions access "${SECRET_VERSION}" \ --secret "infra-values-${ENVIRONMENT}" \ @@ -91,6 +188,19 @@ deploy() { cat 'chart/infra-server/configuration/local-values.yaml' fi ) + local helm_exit=$? + set -e + + if [[ $helm_exit -ne 0 ]]; then + echo "Helm deployment failed with exit code $helm_exit" >&2 + echo "=== Pod Status ===" >&2 + kubectl get pods -n "${RELEASE_NAMESPACE}" >&2 || true + echo "=== Pod Descriptions ===" >&2 + kubectl describe pods -n "${RELEASE_NAMESPACE}" >&2 || true + echo "=== Pod Logs ===" >&2 + kubectl logs -n "${RELEASE_NAMESPACE}" -l app=infra-server --tail=100 >&2 || true + return $helm_exit + fi } # diff renders the Helm chart and compares the deployed resources to show what would change on next deployment. @@ -124,6 +234,13 @@ diff() { deploy-local() { echo 'Deploying for testing without secrets...' >&2 + # Generate random session secret for JWT signing + # This is used by both the server and Cypress tests + if [[ -z "${SESSION_SECRET}" ]]; then + SESSION_SECRET=$(openssl rand -base64 32 | tr -d '\n') + echo "Generated random session secret for this deployment" >&2 + fi + # Generate self-signed cert for local development if it doesn't exist local cert_dir='chart/infra-server/configuration' local cert_file="${cert_dir}/local-cert.pem" diff --git a/ui/cypress/e2e/flavor-selection.cy.ts b/ui/cypress/e2e/flavor-selection.cy.ts index 7d5385983..d7e20dc80 100644 --- a/ui/cypress/e2e/flavor-selection.cy.ts +++ b/ui/cypress/e2e/flavor-selection.cy.ts @@ -13,6 +13,16 @@ const SELECTORS = { describe('Flavor Selection', () => { beforeEach(() => { + // Intercept all API calls to log failures + cy.intercept('**/v1/**', (req) => { + req.continue((res) => { + if (res.statusCode >= 400) { + cy.log(`API Error: ${req.method} ${req.url} -> ${res.statusCode}`); + cy.log(`Response: ${JSON.stringify(res.body)}`); + } + }); + }); + // Authenticate for local development before visiting the page cy.loginForLocalDev(); cy.visit('/'); @@ -26,19 +36,20 @@ describe('Flavor Selection', () => { it('should display a list of available flavors', () => { // Wait for the page heading to be visible (indicates page has loaded) - cy.get(SELECTORS.PAGE_HEADING).should('be.visible'); + // Increase timeout for slower environments like PR clusters + cy.get(SELECTORS.PAGE_HEADING, { timeout: 30000 }).should('be.visible'); // Verify that the flavor gallery is not empty // Each flavor is rendered as a LinkCard inside a GalleryItem - cy.get(SELECTORS.FLAVOR_CARD).should('have.length.at.least', 1); + cy.get(SELECTORS.FLAVOR_CARD, { timeout: 30000 }).should('have.length.at.least', 1); }); it('should display flavor details for each flavor card', () => { // Wait for flavors to load - cy.get(SELECTORS.PAGE_HEADING).should('be.visible'); + cy.get(SELECTORS.PAGE_HEADING, { timeout: 30000 }).should('be.visible'); // Get the first flavor card and verify it has required elements - cy.get(SELECTORS.FLAVOR_CARD) + cy.get(SELECTORS.FLAVOR_CARD, { timeout: 30000 }) .first() .within(() => { // Each flavor card should have a name (header text) @@ -51,39 +62,43 @@ describe('Flavor Selection', () => { it('should have clickable flavor cards that navigate to launch page', () => { // Wait for flavors to load - cy.get(SELECTORS.PAGE_HEADING).should('be.visible'); + cy.get(SELECTORS.PAGE_HEADING, { timeout: 30000 }).should('be.visible'); // Click the first flavor card - cy.get(SELECTORS.FLAVOR_CARD).first().click(); + cy.get(SELECTORS.FLAVOR_CARD, { timeout: 30000 }).first().click(); // Verify navigation to launch page (URL should contain /launch/) cy.url().should('include', '/launch/'); // Verify the launch page loaded with a cluster launch form - cy.contains('h1', /Launch/).should('be.visible'); + cy.contains('h1', /Launch/, { timeout: 30000 }).should('be.visible'); }); it('should toggle between flavor filter states', () => { // Get the initial heading text - cy.get(SELECTORS.PAGE_HEADING) + cy.get(SELECTORS.PAGE_HEADING, { timeout: 30000 }) .should('be.visible') .invoke('text') .then((initialHeading) => { // Find and click the flavor filter toggle switch // Use force:true because PatternFly switch has a visual element covering the input - cy.get(SELECTORS.FLAVOR_TOGGLE).click({ force: true }); + cy.get(SELECTORS.FLAVOR_TOGGLE, { timeout: 30000 }).click({ force: true }); // Verify the heading text changed - cy.get(SELECTORS.PAGE_HEADING).invoke('text').should('not.equal', initialHeading); + cy.get(SELECTORS.PAGE_HEADING, { timeout: 30000 }) + .invoke('text') + .should('not.equal', initialHeading); // Verify URL parameter was updated cy.url().should('include', 'showAllFlavors=true'); // Toggle back - cy.get(SELECTORS.FLAVOR_TOGGLE).click({ force: true }); + cy.get(SELECTORS.FLAVOR_TOGGLE, { timeout: 30000 }).click({ force: true }); // Verify we're back to the original heading - cy.get(SELECTORS.PAGE_HEADING).invoke('text').should('equal', initialHeading); + cy.get(SELECTORS.PAGE_HEADING, { timeout: 30000 }) + .invoke('text') + .should('equal', initialHeading); }); }); }); diff --git a/ui/cypress/support/commands.ts b/ui/cypress/support/commands.ts index d2e36adec..f1b5ead55 100644 --- a/ui/cypress/support/commands.ts +++ b/ui/cypress/support/commands.ts @@ -2,13 +2,16 @@ /** * Logs in by setting a valid JWT token cookie for local development. - * This uses the known session secret from local-deploy oidc.yaml. + * Reads the session secret from CYPRESS_SESSION_SECRET env var if available, + * otherwise uses a default for true local laptop development. */ Cypress.Commands.add('loginForLocalDev', () => { - // IMPORTANT: This secret is ONLY for local development. - // It matches chart/infra-server/configuration/local-values.yaml - // Production deployments use different secrets from GCP Secret Manager. - const sessionSecret = 'local-dev-secret-min-32-chars-long'; + // Read session secret from environment variable (set by CI/PR workflows) + // Fall back to default for true local development on laptop + // IMPORTANT: The default is ONLY for local laptop development. + // PR clusters and test environments use randomly generated secrets. + const sessionSecret: string = + (Cypress.env('SESSION_SECRET') as string) || 'local-dev-secret-min-32-chars-long'; // Create a test user matching the backend's expected structure // Note: Fields are capitalized to match Go's JSON serialization of protobuf structs @@ -31,7 +34,7 @@ Cypress.Commands.add('loginForLocalDev', () => { }; // Generate JWT token using HS256 - cy.task('generateJWT', { payload, secret: sessionSecret }).then((token) => + cy.task('generateJWT', { payload, secret: sessionSecret }).then((token: unknown) => // Set the token cookie cy.setCookie('token', token as string, { path: '/', From 99ab42991ab642655744d6c82c2d2f75a74df072 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Mon, 19 Jan 2026 14:32:21 -0700 Subject: [PATCH 09/10] Allow UI E2E tests to run even when Go E2E tests fail Add 'if: success() || failure()' to ui-e2e-test-pr-cluster job so it runs even when deploy-and-test fails (due to Go e2e test failures). This allows us to: - See Go e2e failures as RED in GitHub UI (shows real status) - Still run and test UI E2E functionality independently - Get results for both test suites --- .github/workflows/PR.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/PR.yaml b/.github/workflows/PR.yaml index 6d4084dec..8b9d8500c 100644 --- a/.github/workflows/PR.yaml +++ b/.github/workflows/PR.yaml @@ -189,6 +189,7 @@ jobs: ui-e2e-test-pr-cluster: needs: - deploy-and-test + if: success() || failure() runs-on: ubuntu-latest # Note: This job does NOT use the apollo-ci container to avoid path issues env: From c8bf444664c54559060254d29ef05f0b7d82eb6c Mon Sep 17 00:00:00 2001 From: "Claude Sonnet 4.5" Date: Tue, 20 Jan 2026 09:10:06 -0700 Subject: [PATCH 10/10] Fix TEST_MODE GCS signer handling Don't create invalid empty Signer struct in TEST_MODE. Instead, leave gcsSigner as nil which is the proper way to indicate GCS signing is disabled. The previous approach of creating &signer.Signer{} was causing workflow creation failures in go e2e tests. --- cmd/infra-server/main.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/cmd/infra-server/main.go b/cmd/infra-server/main.go index 28e9a2881..4379e5dbe 100644 --- a/cmd/infra-server/main.go +++ b/cmd/infra-server/main.go @@ -72,20 +72,18 @@ func mainCmd() error { // Local deployments and test mode skip GCS signing entirely. var gcsSigner *signer.Signer testMode := os.Getenv("TEST_MODE") == "true" - if _, hasGCSCredentials := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); hasGCSCredentials { - var err error - gcsSigner, err = signer.NewFromEnv() - if err != nil { - if testMode { - log.Log(logging.WARN, "GCS signing disabled in TEST_MODE: invalid credentials", "error", err.Error()) - gcsSigner = &signer.Signer{} // Empty signer for test mode with invalid credentials - } else { + if !testMode { + if _, hasGCSCredentials := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); hasGCSCredentials { + var err error + gcsSigner, err = signer.NewFromEnv() + if err != nil { return errors.Wrapf(err, "failed to load GCS signing credentials") } + } else { + log.Log(logging.INFO, "GCS signing disabled: GOOGLE_APPLICATION_CREDENTIALS not set") } } else { - log.Log(logging.INFO, "GCS signing disabled: GOOGLE_APPLICATION_CREDENTIALS not set") - gcsSigner = &signer.Signer{} // Empty signer for local deployments + log.Log(logging.INFO, "GCS signing disabled in TEST_MODE") } slackClient, err := slack.New(cfg.Slack)