PromucFlow_constructor/app/client/cypress/scripts/util.ts
Sagar Khalasi ebe465b6eb
ci: CI update for commitMsg (#39254)
## Description

**Problem**: The Schedule TBP data for both Release and Airgap workflows
is currently identical in the database, making it difficult to
distinguish between them.
**Solution**: Added the workflow name to the commitMsg column, ensuring
a clear and accurate view of the data for each workflow.


Fixes #
https://app.zenhub.com/workspaces/qa-63316faf86bb2e170ed2e46b/issues/gh/appsmithorg/appsmith/39253


## Automation

/ok-to-test tags="@tag.SignIn"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/13309520198>
> Commit: 87d33db158fe9ded6261edd13cf7f89e5e643ab0
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=13309520198&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.SignIn`
> Spec:
> <hr>Thu, 13 Feb 2025 14:28:44 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a customizable input to specify the number of test
iterations.
- Updated test commands to conditionally target specific test files
based on provided parameters.

- **Tests**
- Enhanced test reporting by appending workflow context to commit
messages.
- Improved workflow tracking with the addition of a variable reflecting
the active workflow name.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-02-13 19:59:11 +05:30

184 lines
6.1 KiB
TypeScript

import { Pool } from "pg";
import { S3 } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import fs from "fs";
import { Octokit } from "@octokit/rest";
import fetch from "node-fetch";
import globby from "globby";
import minimatch from "minimatch";
export interface DataItem {
name: string;
duration: string;
}
export default class util {
public getVars() {
return {
runId: this.getEnvValue("RUNID", { required: true }),
attempt_number: this.getEnvValue("ATTEMPT_NUMBER", { required: true }),
repository: this.getEnvValue("REPOSITORY", { required: true }),
committer: this.getEnvValue("COMMITTER", { required: true }),
tag: this.getEnvValue("TAG", { required: true }),
branch: this.getEnvValue("BRANCH", { required: true }),
cypressDbUser: this.getEnvValue("CYPRESS_DB_USER", { required: true }),
cypressDbHost: this.getEnvValue("CYPRESS_DB_HOST", { required: true }),
cypressDbName: this.getEnvValue("CYPRESS_DB_NAME", { required: true }),
cypressDbPwd: this.getEnvValue("CYPRESS_DB_PWD", { required: true }),
cypressS3Access: this.getEnvValue("CYPRESS_S3_ACCESS", {
required: true,
}),
cypressS3Secret: this.getEnvValue("CYPRESS_S3_SECRET", {
required: true,
}),
githubToken: process.env["GITHUB_TOKEN"],
commitMsg: this.getEnvValue("COMMIT_INFO_MESSAGE", { required: false }),
totalRunners: this.getEnvValue("TOTAL_RUNNERS", { required: false }),
thisRunner: this.getEnvValue("THIS_RUNNER", { required: true }),
cypressSpecs: this.getEnvValue("CYPRESS_SPECS", { required: false }),
cypressRerun: this.getEnvValue("CYPRESS_RERUN", { required: false }),
cypressSkipFlaky: this.getEnvValue("CYPRESS_SKIP_FLAKY", {
required: false,
}),
staticAllocation: this.getEnvValue("CYPRESS_STATIC_ALLOCATION", {
required: false,
}),
gitWorkflowName: this.getEnvValue("GITHUB_WORKFLOW", { required: false }),
};
}
public async divideSpecsIntoBalancedGroups(
data: DataItem[],
numberOfGroups: number,
): Promise<DataItem[][]> {
const groups: DataItem[][] = Array.from(
{ length: numberOfGroups },
() => [],
);
data.forEach((item) => {
// Find the group with the shortest total duration and add the item to it
const shortestGroupIndex = groups.reduce(
(minIndex, group, currentIndex) => {
const totalDuration = groups[minIndex].reduce(
(acc, item) => acc + Number(item.duration),
0,
);
const totalDurationCurrent = group.reduce(
(acc, item) => acc + Number(item.duration),
0,
);
return totalDurationCurrent < totalDuration ? currentIndex : minIndex;
},
0,
);
groups[shortestGroupIndex].push(item);
});
return groups;
}
// This function will get all the spec paths using the pattern
public async getSpecFilePaths(
specPattern: any,
ignoreTestFiles: any,
): Promise<string[]> {
const files = globby.sync(specPattern, {
ignore: ignoreTestFiles,
});
// ignore the files that doesn't match
const ignorePatterns = [...(ignoreTestFiles || [])];
// a function which returns true if the file does NOT match
const doesNotMatchAllIgnoredPatterns = (file: string) => {
// using {dot: true} here so that folders with a '.' in them are matched
const MINIMATCH_OPTIONS = { dot: true, matchBase: true };
return ignorePatterns.every((pattern) => {
return !minimatch(file, pattern, MINIMATCH_OPTIONS);
});
};
const filtered = files.filter(doesNotMatchAllIgnoredPatterns);
return filtered;
}
public getEnvValue(varName: string, { required = true }): string {
if (required && process.env[varName] === undefined) {
throw Error(
`${varName} is not defined.
Please check all the following environment variables are defined properly
[ RUNID, ATTEMPT_NUMBER, REPOSITORY, COMMITTER, TAG, BRANCH, THIS_RUNNER, CYPRESS_DB_USER, CYPRESS_DB_HOST, CYPRESS_DB_NAME, CYPRESS_DB_PWD, CYPRESS_S3_ACCESS, CYPRESS_S3_SECRET ].`,
);
}
return process.env[varName] ?? "";
}
//This is to setup the db client
public configureDbClient() {
const dbConfig = {
user: this.getVars().cypressDbUser,
host: this.getVars().cypressDbHost,
database: this.getVars().cypressDbName,
password: this.getVars().cypressDbPwd,
port: 5432,
connectionTimeoutMillis: 60000,
ssl: true,
keepalives: 30,
};
const dbClient = new Pool(dbConfig);
return dbClient;
}
// This is to setup the AWS client
public configureS3() {
const s3client = new S3({
region: "ap-south-1",
credentials: {
accessKeyId: this.getVars().cypressS3Access,
secretAccessKey: this.getVars().cypressS3Secret,
},
});
return s3client;
}
// This is to upload files to s3 when required
public async uploadToS3(s3Client: S3, filePath: string, key: string) {
const fileContent = fs.readFileSync(filePath);
const params = {
Bucket: "appsmith-internal-cy-db",
Key: key,
Body: fileContent,
};
return await new Upload({ client: s3Client, params }).done();
}
public async getActiveRunners() {
const octokit = new Octokit({
auth: this.getVars().githubToken,
request: {
fetch: fetch,
},
});
try {
const repo: string[] = this.getVars().repository.split("/");
const response = await octokit.request(
"GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs",
{
owner: repo[0],
repo: repo[1],
run_id: Number(this.getVars().runId),
per_page: 100,
headers: {
"X-GitHub-Api-Version": "2022-11-28",
},
},
);
const active_runners = response.data.jobs.filter(
(job) =>
(job.status === "in_progress" || job.status === "queued") &&
job.run_attempt === Number(this.getVars().attempt_number),
);
return active_runners.length;
} catch (error) {
console.error("Error:", error);
}
}
}