Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aligning project reporting structure in code with README docs #60

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ application-local.yml
/bin
env
lib
.project
11 changes: 11 additions & 0 deletions .project
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,15 @@
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
<filteredResources>
<filter>
<id>1720132041193</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,25 @@ The PAT must have the following scopes enabled:

| Column name | description |
| ----------- | ----------- |
| Total Score | Issue Activity + Commit Activity |
| Score | Issue Activity + Commit Activity |
| Pass | TO BE REMOVED |
| Organisation | Name of the GitHub organisation |
| Repo Name | Name of the GitHub repository |
| Project Name | Name of the FINOS project this repository belongs to, parsed from https://github.com/finos/finos-landscape/blob/master/landscape.yml |
| Project Type | Software Project or Special Interest Group, parsed from https://github.com/finos/finos-landscape/blob/master/landscape.yml |
| Project Stage | FINOS Lifecycle Stage, parsed from https://github.com/finos/finos-landscape/blob/master/landscape.yml |
| Stage | Finos Lifecycle Stage, parsed from README.md badges - https://community.finos.org/docs/governance/Software-Projects/project-lifecycle |
| Github Archived | Check if the GitHub repository is marked as archived, using GitHub API |
| License | Repo license, pulled from GitHub Api - https://community.finos.org/docs/governance/Software-Projects/license-categories |
| Meeting attendance | All comments to issues labeled as `meeting` in the last X days |
| Issue/PR Activity | All issue creations and comments in the last X days (except for those with label `meeting`) |
| Issue/PR lifespan | Average issue lifespan in the last X days |
| Commit Activity | Count number of commits in last 6 months, maxed at 100 (TODO - remove the 100 max) |
| OpenSSF Best Practices Badge | The OpenSSF Best Practices score, parsed from README.md badges - https://www.bestpractices.dev/en |
| Github Archived | Check if the GitHub repository is marked as archived, using GitHub API |
| OpenSSF Best Practices | The OpenSSF Best Practices score, parsed from README.md badges - https://www.bestpractices.dev/en |
| Branch Protection | Check branch rules: returns the number of approvers, 0 if no approvers are set, -1 if the is no branch protection enabled on the main branch |
| SemGrep | Checks existance of .github/workflows/semgrep.yml file - (TODO - would be better to check GitHub Actions execution) |
| CVE Scanning | Checks existance of .github/workflows/cve-scanning.yml file - (TODO - would be better to check GitHub Actions execution) |
| Main Branch Name | Checks if default branch name is `main` |
| Default Branch Name | Checks if default branch name is `main` |
| Admins | List of users that have admin rights on the repository (expected to be 0, since only finos-admin should have Admin rights on FINOS repositories) |
| Issue Participants | List of all participants for issues loaded in Issue Activity column |
| Committers | List of all repository committers |
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/org/finos/ls/queries/BasicQueries.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class BasicQueries {
+ " }\n"
+ " }";

public enum FinosStatus { NO_README, INCUBATING, ACTIVE, NONE }
public enum FinosStage { NO_README, INCUBATING, ACTIVE, NONE }

public enum OpenSSFStatus { NO_README, OK, NONE }

Expand Down Expand Up @@ -91,20 +91,20 @@ public enum OpenSSFStatus { NO_README, OK, NONE }
+ " }", 20, (r, qe) -> wrongAdmins(r));


public static QueryType<FinosStatus> FINOS_STATUS = new AbstractQueryType<FinosStatus>(
public static QueryType<FinosStage> FINOS_STAGE = new AbstractQueryType<FinosStage>(
FILE_LIST, 10, (r, qe) -> {

String text = getReadme(r, qe);
if (text == null) {
return FinosStatus.NO_README;
return FinosStage.NO_README;
} else if (text.contains("https://finosfoundation.atlassian.net/wiki/display/FINOS/Incubating") ||
text.contains("https://cdn.jsdelivr.net/gh/finos/contrib-toolbox@master/images/badge-incubating.svg")) {
return FinosStatus.INCUBATING;
return FinosStage.INCUBATING;
} else if (text.contains("https://finosfoundation.atlassian.net/wiki/display/FINOS/Active") ||
text.contains("https://cdn.jsdelivr.net/gh/finos/contrib-toolbox@master/images/badge-released.svg")) {
return FinosStatus.ACTIVE;
return FinosStage.ACTIVE;
} else {
return FinosStatus.NONE;
return FinosStage.NONE;
}
});

Expand Down
65 changes: 37 additions & 28 deletions src/main/java/org/finos/ls/queries/ProjectScanCSVSummarizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import org.finos.ls.landscape.ProjectInfo;
import org.finos.ls.landscape.ProjectInfo.ProjectType;
import org.finos.ls.queries.BasicQueries.FinosStatus;
import org.finos.ls.queries.BasicQueries.FinosStage;
import org.finos.ls.queries.BasicQueries.OpenSSFStatus;
import org.finos.scan.github.client.Repository;
import org.finos.scan.github.client.util.QueryExecutor;
Expand All @@ -22,26 +22,26 @@ public ProjectScanCSVSummarizer(List<ProjectInfo> projects) {

public static final String[] FIELDS = {
"Score",
"Pass",
"Organisation",
"Repo Name",
"Finos Lifecycle State (from the README badge)",
"License (from GitHub Api)",
"Issue Activity (for most recent 50 issues, count most recent 30 comments in last 6 months)",
"Commit Activity (count of commits in last 6 months, maxed at 100)",
"OpenSSF Status (from the README badge)",
"Github Archived (has the Archive tag in GitHub API)",
"Branch Rules/Private (number of approvers or 0 if no approvers, -1 no branch rules",
"SemGrep (checks .github/workflows/semgrep.yml file exists)",
"CVE Scanning (checks .github/workflows/cve-scanning.yml file exists)",
"Default Branch Name",
"Excess Admins",
"Main Issue Participants (list of all participants for issues loaded in Issue Activity column)",
"Main Committers (list of all participants for commits loaded in Commit Activity column)",
"Length of Readme",
"Project Name",
"Type",
"Stage"
"Project Type",
"Project Stage",
"Stage",
"Github Archived",
"License",
"Meeting Attendance",
"Issue/PR Activity",
"Commit Activity",
"OpenSSF Best Practices",
"Branch Protection",
"SemGrep",
"CVE Scanning",
"Default Branch Name",
"Admins",
"Issue Participants",
"Committers",
"Readme Length",
};

@Override
Expand All @@ -58,31 +58,43 @@ public String getFields() {

@Override
public List<Object> convert(Repository r, QueryExecutor qe) {

// TODO - make sure PRs are included
Activity issue = BasicQueries.ISSUE_ACTIVITY.convert(r, qe);

// TODO - remove 100 items cap
Activity commit = BasicQueries.MAIN_RECENT_COMMITTERS.convert(r, qe);
FinosStatus finosStatus = BasicQueries.FINOS_STATUS.convert(r, qe);

// TODO - build query
Activity meetingAttendance = null;
FinosStage finosStage = BasicQueries.FINOS_STAGE.convert(r, qe);
OpenSSFStatus openSSF = BasicQueries.OPENSSF_STATUS.convert(r, qe);
String license = BasicQueries.LICENSE_INFO.convert(r, qe);

Boolean semGrep = BasicQueries.SEMGREP_ACTION.convert(r, qe);
Boolean cveScan = BasicQueries.CVE_SCANNING_ACTION.convert(r, qe);
String defaultBranchName = BasicQueries.DEFAULT_BRANCH_NAME.convert(r, qe);
String wrongAdmins = BasicQueries.WRONG_ADMINS.convert(r, qe);
int branchReviewers = BasicQueries.BRANCH_RULES.convert(r, qe);
long score = calculateScore(issue, commit, r.getName(), r.getIsPrivate() | r.getIsArchived());
String pass = passes(finosStatus, openSSF, wrongAdmins, branchReviewers, license, semGrep, cveScan, score == -1);
// TODO - this may be useful for documenting health criteria
// String pass = passes(finosStatus, openSSF, wrongAdmins, branchReviewers, license, semGrep, cveScan, score == -1);
long readmeLength = BasicQueries.README_LENGTH.convert(r, qe);

List<Object> out = new ArrayList<>();
out.add(score);
out.add(pass);
out.add(r.getOwner().getLogin());
out.add(r.getName());
out.add(finosStatus.name());
out.add(projectName(r));
out.add(projectType(r));
out.add(projectStage(r));
out.add(finosStage.name());
out.add(r.getIsArchived());
out.add(license);
out.add(meetingAttendance);
out.add(issue.getScore());
out.add(commit.getScore());
out.add(openSSF.name());
out.add(r.getIsArchived());
out.add(r.getIsPrivate() ? "PRIVATE" : ""+branchReviewers);
out.add(semGrep);
out.add(cveScan);
Expand All @@ -91,9 +103,6 @@ public List<Object> convert(Repository r, QueryExecutor qe) {
out.add(convertToSpaceList(issue));
out.add(convertToSpaceList(commit));
out.add(readmeLength);
out.add(projectName(r));
out.add(projectType(r));
out.add(projectStage(r));
return out;
}

Expand Down Expand Up @@ -134,14 +143,14 @@ private ProjectInfo getMatchingProject(String name, String owner) {
return null;
}

private String passes(FinosStatus finosStatus, OpenSSFStatus openSSFStatus, String wrongAdmins, int branchReviewers, String license, boolean semGrep, Boolean cveScan, boolean ignore) {
private String passes(FinosStage finosStatus, OpenSSFStatus openSSFStatus, String wrongAdmins, int branchReviewers, String license, boolean semGrep, Boolean cveScan, boolean ignore) {
StringBuilder sb = new StringBuilder();

if (ignore) {
return "";
}

if ((finosStatus == FinosStatus.NONE) || (finosStatus == FinosStatus.NO_README)) {
if ((finosStatus == FinosStage.NONE) || (finosStatus == FinosStage.NO_README)) {
sb.append(" (Finos Lifecycle)");
}

Expand Down
20 changes: 10 additions & 10 deletions src/test/java/org/finos/ls/BasicTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.finos.ls.outputs.PullRequestService;
import org.finos.ls.queries.Activity;
import org.finos.ls.queries.BasicQueries;
import org.finos.ls.queries.BasicQueries.FinosStatus;
import org.finos.ls.queries.BasicQueries.FinosStage;
import org.finos.ls.queries.BasicQueries.OpenSSFStatus;
import org.finos.ls.queries.MarkdownSummarizer;
import org.finos.ls.queries.MarkdownSummarizer.SummaryLevel;
Expand Down Expand Up @@ -83,20 +83,20 @@ public <X> long countStatus(Map<String, X> input, X expected) {

@Test
public void testListOfReposWithStatusInfo() throws GraphQLRequestExecutionException, GraphQLRequestPreparationException {
FinosStatus status = qs.getSingleRepository(BasicQueries.FINOS_STATUS, ORG, "FDC3");
Assertions.assertEquals(FinosStatus.ACTIVE, status);
FinosStage status = qs.getSingleRepository(BasicQueries.FINOS_STAGE, ORG, "FDC3");
Assertions.assertEquals(FinosStage.ACTIVE, status);

status = qs.getSingleRepository(BasicQueries.FINOS_STATUS, ORG, "spring-bot");
Assertions.assertEquals(FinosStatus.INCUBATING, status);
status = qs.getSingleRepository(BasicQueries.FINOS_STAGE, ORG, "spring-bot");
Assertions.assertEquals(FinosStage.INCUBATING, status);

status = qs.getSingleRepository(BasicQueries.FINOS_STATUS, ORG, "datahub");
Assertions.assertEquals(FinosStatus.NONE, status);
status = qs.getSingleRepository(BasicQueries.FINOS_STAGE, ORG, "datahub");
Assertions.assertEquals(FinosStage.NONE, status);

Map<String, FinosStatus> statuses = qs.getAllRepositoriesInOrg(BasicQueries.FINOS_STATUS, ORG);
Map<String, FinosStage> statuses = qs.getAllRepositoriesInOrg(BasicQueries.FINOS_STAGE, ORG);
outputMap(statuses);

Assertions.assertTrue(countStatus(statuses, FinosStatus.ACTIVE)>5);
Assertions.assertTrue(countStatus(statuses, FinosStatus.INCUBATING)>5);
Assertions.assertTrue(countStatus(statuses, FinosStage.ACTIVE)>5);
Assertions.assertTrue(countStatus(statuses, FinosStage.INCUBATING)>5);


}
Expand Down