github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/magefiles/magefile.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "os" 8 "regexp" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/devfile/library/pkg/util" 14 "k8s.io/apimachinery/pkg/runtime" 15 16 "golang.org/x/text/cases" 17 "golang.org/x/text/language" 18 19 "k8s.io/klog/v2" 20 21 "sigs.k8s.io/yaml" 22 23 "github.com/google/go-containerregistry/pkg/authn" 24 "github.com/google/go-containerregistry/pkg/name" 25 remoteimg "github.com/google/go-containerregistry/pkg/v1/remote" 26 gh "github.com/google/go-github/v44/github" 27 "github.com/magefile/mage/sh" 28 "github.com/redhat-appstudio/e2e-tests/magefiles/installation" 29 "github.com/redhat-appstudio/e2e-tests/pkg/apis/github" 30 "github.com/redhat-appstudio/e2e-tests/pkg/constants" 31 "github.com/redhat-appstudio/e2e-tests/pkg/utils" 32 "github.com/redhat-appstudio/image-controller/pkg/quay" 33 tektonapi "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" 34 ) 35 36 var ( 37 requiredBinaries = []string{"jq", "kubectl", "oc", "yq", "git", "helm"} 38 artifactDir = utils.GetEnv("ARTIFACT_DIR", ".") 39 openshiftJobSpec = &OpenshiftJobSpec{} 40 pr = &PullRequestMetadata{} 41 jobName = utils.GetEnv("JOB_NAME", "") 42 // can be periodic, presubmit or postsubmit 43 jobType = utils.GetEnv("JOB_TYPE", "") 44 reposToDeleteDefaultRegexp = "jvm-build|e2e-dotnet|build-suite|e2e|pet-clinic-e2e|test-app|e2e-quayio|petclinic|test-app|integ-app|^dockerfile-|new-|^python|my-app|^test-|^multi-component" 45 repositoriesWithWebhooks = []string{"devfile-sample-hello-world", "hacbs-test-project"} 46 ) 47 48 func (CI) parseJobSpec() error { 49 jobSpecEnvVarData := os.Getenv("JOB_SPEC") 50 51 if err := json.Unmarshal([]byte(jobSpecEnvVarData), openshiftJobSpec); err != nil { 52 return fmt.Errorf("error when parsing openshift job spec data: %v", err) 53 } 54 return nil 55 } 56 57 func (ci CI) init() error { 58 var err error 59 60 if jobType == "periodic" || strings.Contains(jobName, "rehearse") { 61 return nil 62 } 63 64 if err = ci.parseJobSpec(); err != nil { 65 return err 66 } 67 68 pr.Organization = openshiftJobSpec.Refs.Organization 69 pr.RepoName = openshiftJobSpec.Refs.Repo 70 pr.CommitSHA = openshiftJobSpec.Refs.Pulls[0].SHA 71 pr.Number = openshiftJobSpec.Refs.Pulls[0].Number 72 73 prUrl := fmt.Sprintf("https://api.github.com/repos/%s/%s/pulls/%d", pr.Organization, pr.RepoName, pr.Number) 74 pr.RemoteName, pr.BranchName, err = getRemoteAndBranchNameFromPRLink(prUrl) 75 if err != nil { 76 return err 77 } 78 79 return nil 80 } 81 82 func (ci CI) PrepareE2EBranch() error { 83 if jobType == "periodic" || strings.Contains(jobName, "rehearse") { 84 return nil 85 } 86 87 if err := ci.init(); err != nil { 88 return err 89 } 90 91 if openshiftJobSpec.Refs.Repo == "e2e-tests" { 92 if err := gitCheckoutRemoteBranch(pr.RemoteName, pr.CommitSHA); err != nil { 93 return err 94 } 95 } else { 96 if ci.isPRPairingRequired("e2e-tests") { 97 if err := gitCheckoutRemoteBranch(pr.RemoteName, pr.BranchName); err != nil { 98 return err 99 } 100 } 101 } 102 103 return nil 104 } 105 106 func (Local) PrepareCluster() error { 107 if err := PreflightChecks(); err != nil { 108 return fmt.Errorf("error when running preflight checks: %v", err) 109 } 110 if err := BootstrapCluster(); err != nil { 111 return fmt.Errorf("error when bootstrapping cluster: %v", err) 112 } 113 114 return nil 115 } 116 117 func (Local) TestE2E() error { 118 return RunE2ETests() 119 } 120 121 // Deletes autogenerated repositories from redhat-appstudio-qe Github org. 122 // Env vars to configure this target: REPO_REGEX (optional), DRY_RUN (optional) - defaults to false 123 // Remove all repos which with 1 day lifetime. By default will delete gitops repositories from redhat-appstudio-qe 124 func (Local) CleanupGithubOrg() error { 125 githubToken := os.Getenv("GITHUB_TOKEN") 126 if githubToken == "" { 127 return fmt.Errorf("env var GITHUB_TOKEN is not set") 128 } 129 dryRun, err := strconv.ParseBool(utils.GetEnv("DRY_RUN", "true")) 130 if err != nil { 131 return fmt.Errorf("unable to parse DRY_RUN env var\n\t%s", err) 132 } 133 134 // Get all repos 135 githubOrgName := utils.GetEnv(constants.GITHUB_E2E_ORGANIZATION_ENV, "redhat-appstudio-qe") 136 ghClient, err := github.NewGithubClient(githubToken, githubOrgName) 137 if err != nil { 138 return err 139 } 140 repos, err := ghClient.GetAllRepositories() 141 if err != nil { 142 return err 143 } 144 var reposToDelete []*gh.Repository 145 146 // Filter repos by regex & time check 147 r, err := regexp.Compile(utils.GetEnv("REPO_REGEX", reposToDeleteDefaultRegexp)) 148 if err != nil { 149 return fmt.Errorf("unable to compile regex: %s", err) 150 } 151 for _, repo := range repos { 152 // Add only repos older than 24 hours 153 dayDuration, _ := time.ParseDuration("24h") 154 if time.Since(repo.GetCreatedAt().Time) > dayDuration { 155 // Add only repos matching the regex 156 if r.MatchString(*repo.Name) { 157 reposToDelete = append(reposToDelete, repo) 158 } 159 } 160 } 161 162 if dryRun { 163 klog.Info("Dry run enabled. Listing repositories that would be deleted:") 164 } 165 166 // Delete repos 167 for _, repo := range reposToDelete { 168 if dryRun { 169 klog.Infof("\t%s", repo.GetName()) 170 } else { 171 err := ghClient.DeleteRepository(repo) 172 if err != nil { 173 klog.Warningf("error deleting repository: %s\n", err) 174 } 175 } 176 } 177 if dryRun { 178 klog.Info("If you really want to delete these repositories, run `DRY_RUN=false [REGEXP=<regexp>] mage local:cleanupGithubOrg`") 179 } 180 return nil 181 } 182 183 // Deletes Quay repos and robot accounts older than 24 hours with prefixes `has-e2e` and `e2e-demos`, uses env vars DEFAULT_QUAY_ORG and DEFAULT_QUAY_ORG_TOKEN 184 func (Local) CleanupQuayReposAndRobots() error { 185 quayOrgToken := os.Getenv("DEFAULT_QUAY_ORG_TOKEN") 186 if quayOrgToken == "" { 187 return fmt.Errorf("DEFAULT_QUAY_ORG_TOKEN env var was not found") 188 } 189 quayOrg := utils.GetEnv("DEFAULT_QUAY_ORG", "redhat-appstudio-qe") 190 191 quayClient := quay.NewQuayClient(&http.Client{Transport: &http.Transport{}}, quayOrgToken, "https://quay.io/api/v1") 192 return cleanupQuayReposAndRobots(&quayClient, quayOrg) 193 } 194 195 // Deletes Quay Tags older than 7 days in `test-images` repository 196 func (Local) CleanupQuayTags() error { 197 quayOrgToken := os.Getenv("DEFAULT_QUAY_ORG_TOKEN") 198 if quayOrgToken == "" { 199 return fmt.Errorf("DEFAULT_QUAY_ORG_TOKEN env var was not found") 200 } 201 quayOrg := utils.GetEnv("DEFAULT_QUAY_ORG", "redhat-appstudio-qe") 202 203 quayClient := quay.NewQuayClient(&http.Client{Transport: &http.Transport{}}, quayOrgToken, "https://quay.io/api/v1") 204 return cleanupQuayTags(&quayClient, quayOrg, "test-images") 205 } 206 207 func (ci CI) TestE2E() error { 208 var testFailure bool 209 210 if err := ci.init(); err != nil { 211 return fmt.Errorf("error when running ci init: %v", err) 212 } 213 214 if err := PreflightChecks(); err != nil { 215 return fmt.Errorf("error when running preflight checks: %v", err) 216 } 217 218 if err := ci.setRequiredEnvVars(); err != nil { 219 return fmt.Errorf("error when setting up required env vars: %v", err) 220 } 221 222 if err := retry(BootstrapCluster, 2, 10*time.Second); err != nil { 223 return fmt.Errorf("error when bootstrapping cluster: %v", err) 224 } 225 226 if err := RunE2ETests(); err != nil { 227 testFailure = true 228 } 229 230 if err := ci.sendWebhook(); err != nil { 231 klog.Infof("error when sending webhook: %v", err) 232 } 233 234 if testFailure { 235 return fmt.Errorf("error when running e2e tests - see the log above for more details") 236 } 237 238 return nil 239 } 240 241 func RunE2ETests() error { 242 cwd, _ := os.Getwd() 243 244 // added --output-interceptor-mode=none to mitigate RHTAPBUGS-34 245 return sh.RunV("ginkgo", "-p", "--output-interceptor-mode=none", "--timeout=90m", fmt.Sprintf("--output-dir=%s", artifactDir), "--junit-report=e2e-report.xml", "--label-filter=$E2E_TEST_SUITE_LABEL", "./cmd", "--", fmt.Sprintf("--config-suites=%s/tests/e2e-demos/config/default.yaml", cwd), "--generate-rppreproc-report=true", fmt.Sprintf("--rp-preproc-dir=%s", artifactDir)) 246 } 247 248 func PreflightChecks() error { 249 requiredEnv := []string{ 250 "GITHUB_TOKEN", 251 "QUAY_TOKEN", 252 "DEFAULT_QUAY_ORG", 253 "DEFAULT_QUAY_ORG_TOKEN", 254 } 255 missingEnv := []string{} 256 for _, env := range requiredEnv { 257 if os.Getenv(env) == "" { 258 missingEnv = append(missingEnv, env) 259 } 260 } 261 if len(missingEnv) != 0 { 262 return fmt.Errorf("required env vars containing secrets (%s) not defined or empty", strings.Join(missingEnv, ",")) 263 } 264 265 for _, binaryName := range requiredBinaries { 266 if err := sh.Run("which", binaryName); err != nil { 267 return fmt.Errorf("binary %s not found in PATH - please install it first", binaryName) 268 } 269 } 270 271 if err := sh.RunV("go", "install", "-mod=mod", "github.com/onsi/ginkgo/v2/ginkgo"); err != nil { 272 return err 273 } 274 275 return nil 276 } 277 278 func (ci CI) setRequiredEnvVars() error { 279 280 if strings.Contains(jobName, "hacbs-e2e-periodic") { 281 os.Setenv("E2E_TEST_SUITE_LABEL", "HACBS") 282 return nil 283 } else if strings.Contains(jobName, "appstudio-e2e-deployment-periodic") { 284 os.Setenv("E2E_TEST_SUITE_LABEL", "!HACBS") 285 return nil 286 } 287 288 if openshiftJobSpec.Refs.Repo != "e2e-tests" { 289 290 if strings.Contains(jobName, "-service") || strings.Contains(jobName, "image-controller") { 291 var envVarPrefix, imageTagSuffix, testSuiteLabel string 292 sp := strings.Split(os.Getenv("COMPONENT_IMAGE"), "@") 293 294 switch { 295 case strings.Contains(jobName, "application-service"): 296 envVarPrefix = "HAS" 297 imageTagSuffix = "has-image" 298 testSuiteLabel = "e2e-demo,byoc" 299 case strings.Contains(jobName, "release-service"): 300 envVarPrefix = "RELEASE_SERVICE" 301 imageTagSuffix = "release-service-image" 302 testSuiteLabel = "release" 303 case strings.Contains(jobName, "integration-service"): 304 envVarPrefix = "INTEGRATION_SERVICE" 305 imageTagSuffix = "integration-service-image" 306 testSuiteLabel = "integration-service" 307 case strings.Contains(jobName, "jvm-build-service"): 308 envVarPrefix = "JVM_BUILD_SERVICE" 309 imageTagSuffix = "jvm-build-service-image" 310 testSuiteLabel = "jvm-build" 311 // Since CI requires to have default values for dependency images 312 // (https://github.com/openshift/release/blob/master/ci-operator/step-registry/redhat-appstudio/e2e/redhat-appstudio-e2e-ref.yaml#L15) 313 // we cannot let these env vars to have identical names in CI as those env vars used in tests 314 // e.g. JVM_BUILD_SERVICE_REQPROCESSOR_IMAGE, otherwise those images they are referencing wouldn't 315 // be always relevant for tests and tests would be failing 316 os.Setenv(fmt.Sprintf("%s_REQPROCESSOR_IMAGE", envVarPrefix), os.Getenv("CI_JBS_REQPROCESSOR_IMAGE")) 317 os.Setenv(fmt.Sprintf("%s_CACHE_IMAGE", envVarPrefix), os.Getenv("CI_JBS_CACHE_IMAGE")) 318 319 klog.Infof("going to override default Tekton bundle for the purpose of testing jvm-build-service PR") 320 var err error 321 var defaultBundleRef string 322 var tektonObj runtime.Object 323 324 tag := fmt.Sprintf("%d-%s", time.Now().Unix(), util.GenerateRandomString(4)) 325 var newS2iJavaTaskRef, _ = name.ParseReference(fmt.Sprintf("%s:task-bundle-%s", constants.DefaultImagePushRepo, tag)) 326 var newJavaBuilderPipelineRef, _ = name.ParseReference(fmt.Sprintf("%s:pipeline-bundle-%s", constants.DefaultImagePushRepo, tag)) 327 var newReqprocessorImage = os.Getenv("JVM_BUILD_SERVICE_REQPROCESSOR_IMAGE") 328 var newTaskYaml, newPipelineYaml []byte 329 330 if err = utils.CreateDockerConfigFile(os.Getenv("QUAY_TOKEN")); err != nil { 331 return fmt.Errorf("failed to create docker config file: %+v", err) 332 } 333 if defaultBundleRef, err = utils.GetDefaultPipelineBundleRef(constants.BuildPipelineSelectorYamlURL, "Java"); err != nil { 334 return fmt.Errorf("failed to get the pipeline bundle ref: %+v", err) 335 } 336 if tektonObj, err = utils.ExtractTektonObjectFromBundle(defaultBundleRef, "pipeline", "java-builder"); err != nil { 337 return fmt.Errorf("failed to extract the Tekton Pipeline from bundle: %+v", err) 338 } 339 javaPipelineObj := tektonObj.(tektonapi.PipelineObject) 340 341 var currentS2iJavaTaskRef string 342 for _, t := range javaPipelineObj.PipelineSpec().Tasks { 343 if t.TaskRef.Name == "s2i-java" { 344 currentS2iJavaTaskRef = t.TaskRef.Bundle 345 t.TaskRef.Bundle = newS2iJavaTaskRef.String() 346 } 347 } 348 if tektonObj, err = utils.ExtractTektonObjectFromBundle(currentS2iJavaTaskRef, "task", "s2i-java"); err != nil { 349 return fmt.Errorf("failed to extract the Tekton Task from bundle: %+v", err) 350 } 351 taskObj := tektonObj.(tektonapi.TaskObject) 352 353 for i, s := range taskObj.TaskSpec().Steps { 354 if s.Name == "analyse-dependencies-java-sbom" { 355 taskObj.TaskSpec().Steps[i].Image = newReqprocessorImage 356 } 357 } 358 359 if newTaskYaml, err = yaml.Marshal(taskObj); err != nil { 360 return fmt.Errorf("error when marshalling a new task to YAML: %v", err) 361 } 362 if newPipelineYaml, err = yaml.Marshal(javaPipelineObj); err != nil { 363 return fmt.Errorf("error when marshalling a new pipeline to YAML: %v", err) 364 } 365 366 keychain := authn.NewMultiKeychain(authn.DefaultKeychain) 367 authOption := remoteimg.WithAuthFromKeychain(keychain) 368 369 if err = utils.BuildAndPushTektonBundle(newTaskYaml, newS2iJavaTaskRef, authOption); err != nil { 370 return fmt.Errorf("error when building/pushing a tekton task bundle: %v", err) 371 } 372 if err = utils.BuildAndPushTektonBundle(newPipelineYaml, newJavaBuilderPipelineRef, authOption); err != nil { 373 return fmt.Errorf("error when building/pushing a tekton pipeline bundle: %v", err) 374 } 375 os.Setenv(constants.CUSTOM_JAVA_PIPELINE_BUILD_BUNDLE_ENV, newJavaBuilderPipelineRef.String()) 376 case strings.Contains(jobName, "build-service"): 377 envVarPrefix = "BUILD_SERVICE" 378 imageTagSuffix = "build-service-image" 379 testSuiteLabel = "build" 380 case strings.Contains(jobName, "image-controller"): 381 envVarPrefix = "IMAGE_CONTROLLER" 382 imageTagSuffix = "image-controller-image" 383 testSuiteLabel = "image-controller" 384 } 385 386 os.Setenv(fmt.Sprintf("%s_IMAGE_REPO", envVarPrefix), sp[0]) 387 os.Setenv(fmt.Sprintf("%s_IMAGE_TAG", envVarPrefix), fmt.Sprintf("redhat-appstudio-%s", imageTagSuffix)) 388 // "rehearse" jobs metadata are not relevant for testing 389 if !strings.Contains(jobName, "rehearse") { 390 os.Setenv(fmt.Sprintf("%s_PR_OWNER", envVarPrefix), pr.RemoteName) 391 os.Setenv(fmt.Sprintf("%s_PR_SHA", envVarPrefix), pr.CommitSHA) 392 } 393 394 os.Setenv("E2E_TEST_SUITE_LABEL", testSuiteLabel) 395 396 } else if openshiftJobSpec.Refs.Repo == "infra-deployments" { 397 os.Setenv("INFRA_DEPLOYMENTS_ORG", pr.RemoteName) 398 os.Setenv("INFRA_DEPLOYMENTS_BRANCH", pr.BranchName) 399 os.Setenv("E2E_TEST_SUITE_LABEL", "e2e-demo,mvp-demo,spi-suite,integration-service,o11y,ec,byoc") 400 } 401 } else { 402 if ci.isPRPairingRequired("infra-deployments") { 403 os.Setenv("INFRA_DEPLOYMENTS_ORG", pr.RemoteName) 404 os.Setenv("INFRA_DEPLOYMENTS_BRANCH", pr.BranchName) 405 } 406 } 407 408 return nil 409 } 410 411 func BootstrapCluster() error { 412 envVars := map[string]string{} 413 414 if os.Getenv("CI") == "true" && os.Getenv("REPO_NAME") == "e2e-tests" { 415 // Some scripts in infra-deployments repo are referencing scripts/utils in e2e-tests repo 416 // This env var allows to test changes introduced in "e2e-tests" repo PRs in CI 417 envVars["E2E_TESTS_COMMIT_SHA"] = os.Getenv("PULL_PULL_SHA") 418 } 419 420 ic, err := installation.NewAppStudioInstallController() 421 if err != nil { 422 return fmt.Errorf("failed to initialize installation controller: %+v", err) 423 } 424 425 return ic.InstallAppStudioPreviewMode() 426 } 427 428 func (CI) isPRPairingRequired(repoForPairing string) bool { 429 var pullRequests []gh.PullRequest 430 431 url := fmt.Sprintf("https://api.github.com/repos/redhat-appstudio/%s/pulls?per_page=100", repoForPairing) 432 if err := sendHttpRequestAndParseResponse(url, "GET", &pullRequests); err != nil { 433 klog.Infof("cannot determine %s Github branches for author %s: %v. will stick with the redhat-appstudio/%s main branch for running tests", repoForPairing, pr.RemoteName, err, repoForPairing) 434 return false 435 } 436 437 for _, pull := range pullRequests { 438 if pull.GetHead().GetRef() == pr.BranchName && pull.GetUser().GetLogin() == pr.RemoteName { 439 return true 440 } 441 } 442 443 return false 444 } 445 446 func (CI) sendWebhook() error { 447 // AppStudio QE webhook configuration values will be used by default (if none are provided via env vars) 448 const appstudioQESaltSecret = "123456789" 449 const appstudioQEWebhookTargetURL = "https://hook.pipelinesascode.com/EyFYTakxEgEy" 450 451 var repoURL string 452 453 var repoOwner = os.Getenv("REPO_OWNER") 454 var repoName = os.Getenv("REPO_NAME") 455 var prNumber = os.Getenv("PULL_NUMBER") 456 var saltSecret = utils.GetEnv("WEBHOOK_SALT_SECRET", appstudioQESaltSecret) 457 var webhookTargetURL = utils.GetEnv("WEBHOOK_TARGET_URL", appstudioQEWebhookTargetURL) 458 459 if strings.Contains(jobName, "hacbs-e2e-periodic") { 460 // TODO configure webhook channel for sending HACBS test results 461 klog.Infof("not sending webhook for HACBS periodic job yet") 462 return nil 463 } 464 465 if jobType == "periodic" { 466 repoURL = "https://github.com/redhat-appstudio/infra-deployments" 467 repoOwner = "redhat-appstudio" 468 repoName = "infra-deployments" 469 prNumber = "periodic" 470 } else if repoName == "e2e-tests" || repoName == "infra-deployments" { 471 repoURL = openshiftJobSpec.Refs.RepoLink 472 } else { 473 klog.Infof("sending webhook for jobType %s, jobName %s is not supported", jobType, jobName) 474 return nil 475 } 476 477 path, err := os.Executable() 478 if err != nil { 479 return fmt.Errorf("error when sending webhook: %+v", err) 480 } 481 482 wh := Webhook{ 483 Path: path, 484 Repository: Repository{ 485 FullName: fmt.Sprintf("%s/%s", repoOwner, repoName), 486 PullNumber: prNumber, 487 }, 488 RepositoryURL: repoURL, 489 } 490 resp, err := wh.CreateAndSend(saltSecret, webhookTargetURL) 491 if err != nil { 492 return fmt.Errorf("error sending webhook: %+v", err) 493 } 494 klog.Infof("webhook response: %+v", resp) 495 496 return nil 497 } 498 499 // Generates test cases for Polarion(polarion.xml) from test files for AppStudio project. 500 func GenerateTestCasesAppStudio() error { 501 return sh.RunV("ginkgo", "--dry-run", "--label-filter=$E2E_TEST_SUITE_LABEL", "./cmd", "--", "--polarion-output-file=polarion.xml", "--generate-test-cases=true") 502 } 503 504 // I've attached to the Local struct for now since it felt like it fit but it can be decoupled later as a standalone func. 505 func (Local) GenerateTestSuiteFile() error { 506 507 var templatePackageName = utils.GetEnv("TEMPLATE_SUITE_PACKAGE_NAME", "") 508 var templatePath = "templates/test_suite_cmd.tmpl" 509 var err error 510 511 if !utils.CheckIfEnvironmentExists("TEMPLATE_SUITE_PACKAGE_NAME") { 512 klog.Exitf("TEMPLATE_SUITE_PACKAGE_NAME not set or is empty") 513 } 514 515 var templatePackageFile = fmt.Sprintf("cmd/%s_test.go", templatePackageName) 516 klog.Infof("Creating new test suite file %s.\n", templatePackageFile) 517 data := TemplateData{SuiteName: templatePackageName} 518 err = renderTemplate(templatePackageFile, templatePath, data, false) 519 520 if err != nil { 521 klog.Errorf("failed to render template with: %s", err) 522 return err 523 } 524 525 err = goFmt(templatePackageFile) 526 if err != nil { 527 528 klog.Errorf("%s", err) 529 return err 530 } 531 532 return nil 533 } 534 535 // Remove all webhooks which with 1 day lifetime. By default will delete webooks from redhat-appstudio-qe 536 func CleanWebHooks() error { 537 token := utils.GetEnv(constants.GITHUB_TOKEN_ENV, "") 538 if token == "" { 539 return fmt.Errorf("empty GITHUB_TOKEN env. Please provide a valid github token") 540 } 541 542 githubOrg := utils.GetEnv(constants.GITHUB_E2E_ORGANIZATION_ENV, "redhat-appstudio-qe") 543 gh, err := github.NewGithubClient(token, githubOrg) 544 if err != nil { 545 return err 546 } 547 for _, repo := range repositoriesWithWebhooks { 548 webhookList, err := gh.ListRepoWebhooks(repo) 549 if err != nil { 550 return err 551 } 552 for _, wh := range webhookList { 553 dayDuration, _ := time.ParseDuration("24h") 554 if time.Since(wh.GetCreatedAt()) > dayDuration { 555 klog.Infof("removing webhook: %s, git_organization: %s, git_repository: %s", wh.GetName(), githubOrg, repo) 556 if err := gh.DeleteWebhook(repo, wh.GetID()); err != nil { 557 return fmt.Errorf("failed to delete webhook: %v, repo: %s", wh.Name, repo) 558 } 559 } 560 } 561 } 562 return nil 563 } 564 565 // I've attached to the Local struct for now since it felt like it fit but it can be decoupled later as a standalone func. 566 func (Local) GenerateTestSpecFile() error { 567 568 var templateDirName = utils.GetEnv("TEMPLATE_SUITE_PACKAGE_NAME", "") 569 var templateSpecName = utils.GetEnv("TEMPLATE_SPEC_FILE_NAME", templateDirName) 570 var templateAppendFrameworkDescribeBool = utils.GetEnv("TEMPLATE_APPEND_FRAMEWORK_DESCRIBE_FILE", "true") 571 var templateJoinSuiteSpecNamesBool = utils.GetEnv("TEMPLATE_JOIN_SUITE_SPEC_FILE_NAMES", "false") 572 var templatePath = "templates/test_spec_file.tmpl" 573 var templateDirPath string 574 var templateSpecFile string 575 var err error 576 var caser = cases.Title(language.English) 577 578 if !utils.CheckIfEnvironmentExists("TEMPLATE_SUITE_PACKAGE_NAME") { 579 klog.Exitf("TEMPLATE_SUITE_PACKAGE_NAME not set or is empty") 580 } 581 582 if !utils.CheckIfEnvironmentExists("TEMPLATE_SPEC_FILE_NAME") { 583 klog.Infof("TEMPLATE_SPEC_FILE_NAME not set. Defaulting test spec file to value of `%s`.\n", templateSpecName) 584 } 585 586 if !utils.CheckIfEnvironmentExists("TEMPLATE_APPEND_FRAMEWORK_DESCRIBE_FILE") { 587 klog.Infof("TEMPLATE_APPEND_FRAMEWORK_DESCRIBE_FILE not set. Defaulting to `%s` which will update the pkg/framework/describe.go after generating the template.\n", templateAppendFrameworkDescribeBool) 588 } 589 590 if utils.CheckIfEnvironmentExists("TEMPLATE_JOIN_SUITE_SPEC_FILE_NAMES") { 591 klog.Infof("TEMPLATE_JOIN_SUITE_SPEC_FILE_NAMES is set to %s. Will join the suite package and spec file name to be used in the Describe of suites.\n", templateJoinSuiteSpecNamesBool) 592 } 593 594 templateDirPath = fmt.Sprintf("tests/%s", templateDirName) 595 err = os.MkdirAll(templateDirPath, 0775) 596 if err != nil { 597 klog.Errorf("failed to create package directory, %s, template with: %v", templateDirPath, err) 598 return err 599 } 600 templateSpecFile = fmt.Sprintf("%s/%s.go", templateDirPath, templateSpecName) 601 602 klog.Infof("Creating new test package directory and spec file %s.\n", templateSpecFile) 603 if templateJoinSuiteSpecNamesBool == "true" { 604 templateSpecName = fmt.Sprintf("%s%v", caser.String(templateDirName), caser.String(templateSpecName)) 605 } else { 606 templateSpecName = caser.String(templateSpecName) 607 } 608 609 data := TemplateData{SuiteName: templateDirName, 610 TestSpecName: templateSpecName} 611 err = renderTemplate(templateSpecFile, templatePath, data, false) 612 if err != nil { 613 klog.Errorf("failed to render template with: %s", err) 614 return err 615 } 616 617 err = goFmt(templateSpecFile) 618 if err != nil { 619 620 klog.Errorf("%s", err) 621 return err 622 } 623 624 if templateAppendFrameworkDescribeBool == "true" { 625 626 err = appendFrameworkDescribeFile(templateSpecName) 627 628 if err != nil { 629 return err 630 } 631 632 } 633 634 return nil 635 } 636 637 func appendFrameworkDescribeFile(packageName string) error { 638 639 var templatePath = "templates/framework_describe_func.tmpl" 640 var describeFile = "pkg/framework/describe.go" 641 var err error 642 var caser = cases.Title(language.English) 643 644 data := TemplateData{TestSpecName: caser.String(packageName)} 645 err = renderTemplate(describeFile, templatePath, data, true) 646 if err != nil { 647 klog.Errorf("failed to append to pkg/framework/describe.go with : %s", err) 648 return err 649 } 650 err = goFmt(describeFile) 651 652 if err != nil { 653 654 klog.Errorf("%s", err) 655 return err 656 } 657 658 return nil 659 660 }