Skip to content

Commit 28c283e

Browse files
committed
initial commit
0 parents  commit 28c283e

17 files changed

+1315
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import subprocess
2+
import shutil
3+
import json
4+
import os
5+
import shutil
6+
7+
def get_date_fields():
8+
# Run git commands and capture as string
9+
output = subprocess.run(['git', 'log', '--date=iso', '--pretty=%cI', '--max-parents=0', '-n', '1'], capture_output=True, text=True)
10+
11+
# Store string and strip of leading / trailing whitespace
12+
date = output.stdout.strip()
13+
14+
# Create a dictionary for date information to be pushed to JSON
15+
date_information = {"created": f"{date}",
16+
"lastModified": "{% now 'utc', '%Y-%m-%dT%H:%M:%S%z' %}",
17+
"metadataLastUpdated": "{% now 'utc', '%Y-%m-%dT%H:%M:%S%z' %}"}
18+
19+
return date_information
20+
21+
def get_scc_labor_hours():
22+
if shutil.which('scc') is not None:
23+
try:
24+
#Run scc and load results into a dictionary
25+
#assuming we are in the .git directory of the repo
26+
cmd = ['scc', '..', '--format', 'json2', '--exclude-file']
27+
28+
# Currently only supports specific files
29+
files_to_exclude = [
30+
"checks.yml,auto-changelog.yml,contributors.yml,repoStructure.yml,code.json,checklist.md,checklist.pdf,README.md,CONTIRBUTING.md,LICENSE,MAINTAINERS.md,repolinter.json,SECURITY.md,CODE_OF_CONDUCT.md,CODEOWNERS.md,COMMUNITY_GUIDELINES.md,GOVERANCE.md"
31+
]
32+
33+
cmd.extend(files_to_exclude)
34+
35+
d = json.loads(subprocess.run(cmd,check=True, capture_output=True).stdout)
36+
37+
l_hours = d['estimatedScheduleMonths'] * 730.001
38+
39+
return round(l_hours,2)
40+
41+
except (subprocess.CalledProcessError, KeyError) as e:
42+
print(e)
43+
return None
44+
else:
45+
print("scc (https://github.com/boyter/scc) not found on system")
46+
47+
#Otherwise just use previous value as a default value.
48+
return None
49+
50+
def prompt_exemption_text(exemptionType):
51+
print(f"You have selected {exemptionType} for your Usage Type.")
52+
return input("Please provide a one or two sentence justification for the exemption used: ")
53+
54+
def format_multi_select_fields(text):
55+
new_text = text.split(",")
56+
57+
new_text = [text.strip() for text in new_text]
58+
59+
return new_text
60+
61+
def update_code_json(json_file_path):
62+
# Read the JSON
63+
with open(json_file_path, 'r') as file:
64+
data = json.load(file)
65+
66+
# Add date_information and labor hours to the JSON
67+
data['date'] = get_date_fields()
68+
69+
# Calculate labor hours
70+
hours = get_scc_labor_hours()
71+
if hours:
72+
data['laborHours'] = hours
73+
else:
74+
data['laborHours'] = None
75+
76+
# Check if usageType is an exemption
77+
if data['permissions']['usageType'].startswith('exempt'):
78+
exemption_text = prompt_exemption_text(data['permissions']['usageType'])
79+
data['permissions']['exemptionText'] = exemption_text
80+
else:
81+
del data['permissions']['exemptionText']
82+
83+
# Format multi-select options
84+
data['categories'] = format_multi_select_fields(data['categories'][0])
85+
data['languages'] = format_multi_select_fields(data['languages'][0])
86+
data['tags'] = format_multi_select_fields(data['tags'][0])
87+
88+
# Update the JSON
89+
with open(json_file_path, 'w') as file:
90+
json.dump(data, file, indent = 2)
91+
92+
def main():
93+
try:
94+
# Change to the parent directory
95+
os.chdir('..')
96+
97+
# Define the codejson directory to remove
98+
dir_name = "codejson"
99+
100+
# Check if codejson directory exists and remove it
101+
if os.path.exists(dir_name):
102+
shutil.rmtree(dir_name)
103+
104+
# Get the project name from cookiecutter
105+
sub_project_dir = "{{cookiecutter.project_name}}"
106+
codejson_file = "code.json"
107+
project_root_dir = os.path.abspath('..')
108+
109+
json_file_path = os.path.join(sub_project_dir, codejson_file)
110+
111+
if os.path.exists(json_file_path):
112+
# Move code.json file to parent directory
113+
new_json_path = os.path.join(project_root_dir, codejson_file)
114+
shutil.move(json_file_path, new_json_path)
115+
116+
# Remove the source directory
117+
shutil.rmtree(sub_project_dir)
118+
119+
# Update the json with date and scc information
120+
update_code_json(new_json_path)
121+
print("Succesfully generated code.json file!")
122+
123+
else:
124+
print(f"Error: {codejson_file} not found in {sub_project_dir}")
125+
126+
except OSError as error:
127+
print(f"Error during OS operations: {error}")
128+
except shutil.Error as error:
129+
print(f"Error during shutil operations: {error}")
130+
131+
if __name__ == "__main__":
132+
main()
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "{{ cookiecutter.project_name }}",
3+
"description": {
4+
"en": {
5+
"shortDescription": "{{ cookiecutter.short_description }}",
6+
"longDescription": "{{ cookiecutter.long_description }}"
7+
}
8+
},
9+
"status": "{{ cookiecutter.status }}",
10+
"permissions": {
11+
"licenses": [
12+
{
13+
"URL": "LICENSE",
14+
"name": "{{ cookiecutter.license }}"
15+
}
16+
],
17+
"usageType": "{{ cookiecutter.usage_type }}",
18+
"exemptionText": ""
19+
},
20+
"organization": "Centers for Medicare & Medicaid Services",
21+
"repositoryURL": "https://github.com/{{ cookiecutter.project_org }}/{{ cookiecutter.project_name }}",
22+
"vcs": "{{ cookiecutter.vcs }}",
23+
"laborHours": [""],
24+
"platforms": [ "{{ cookiecutter.platforms }}" ],
25+
"categories": [ "{{ cookiecutter.categories }}" ],
26+
"softwareType": "{{ cookiecutter.software_type }}",
27+
"languages": [ "{{ cookiecutter.languages }}" ],
28+
"maintenance": "{{ cookiecutter.maintenance }}",
29+
"date": [""],
30+
"tags": [ "{{ cookiecutter.tags }}" ],
31+
"contact": {
32+
"email": "{{ cookiecutter.contact_email }}",
33+
"name": "{{ cookiecutter.contact_name }}"
34+
},
35+
"localisation": "{{ cookiecutter.localisation }}",
36+
"repoType": "{{ cookiecutter.repo_type }}",
37+
"userInput": "{{ cookiecutter.user_input }}",
38+
"fismaLevel": "{{ cookiecutter.fisma_level }}",
39+
"group": "{{ cookiecutter.group }}",
40+
"subsetInHealthcare": "{{ cookiecutter.subset_in_healthcare }}",
41+
"userType": "{{ cookiecutter.user_type }}",
42+
"repositoryHost": "{{ cookiecutter.repository_host }}",
43+
"maturityModelTier": "1"
44+
}

.github/cookiecutter.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"project_name": "metrics-dash-backend-tools",
3+
"project_org": "DSACMS",
4+
"short_description": "A short description of the project.",
5+
"long_description": "A longer description of the project.",
6+
"subset_in_healthcare": "Policy, Operational",
7+
"user_type": "Providers, Patients, Government",
8+
"user_input": ["Yes", "No"],
9+
"group": "CMS/OA/DSAC",
10+
"repository_host": ["Github.com", "GitHub ENT", "GitHub Cloud", "GitLab.com", "GitLab ENT", "GitLab ENT CCSQ"],
11+
"status": ["ideation", "development", "alpha", "beta", "release candidate", "production", "archival"],
12+
"fisma_level": ["Low", "Moderate", "High"],
13+
"license": ["CC0-1.0", "Apache-2.0", "MIT", "MPL-2.0", "GPL-2.0-only", "GPL-3.0-only", "GPL-3.0-or-later", "LGPL-2.1-only", "LGPL-3.0-only", "BSD-2-Clause", "BSD-3-Clause", "EPL-2.0", "Other"],
14+
"vcs": ["git", "hg", "svn", "rcs", "bzr"],
15+
"platforms": ["web", "windows", "mac", "linux", "ios", "android", "other"],
16+
"categories": "healthcare",
17+
"software_type":["standalone/mobile", "standalone/iot", "standalone/desktop", "standalone/web", "standalone/backend", "standalone/other", "addon", "library", "configurationFiles"],
18+
"languages": "None",
19+
"repo_type" : ["Package", "Website", "Standards", "Libraries", "Data", "Apps", "Tools", "APIs", "Docs"],
20+
"usage_type" : ["openSource", "governmentWideReuse", "exemptByLaw", "exemptByNationalSecurity", "exemptByAgencySystem", "exemptByAgencyMission", "exemptByCIO", "exemptByPolicyDate"],
21+
"maintenance": ["internal", "contract", "community", "none"],
22+
"tags": "healthcare",
23+
"contact_email": "[email protected]",
24+
"contact_name": "CMS Open Source Program Office",
25+
"localisation": ["true", "false"],
26+
"__prompts__": {
27+
"project_name": "Project name:",
28+
"project_org": "GitHub organization:",
29+
"short_description": "Provide a short description of the software. It should be a single line containing a single sentence. Maximum 150 characters are allowed.",
30+
"long_description": "Provide longer description of the software, between 150 and 10000 chars. It is meant to provide an overview of the capabilities of the software for a potential user.",
31+
"subset_in_healthcare": "Which subset of healthcare does the project belong to?",
32+
"user_type": "Who are the intended users?",
33+
"user_input": "Does the project accept user input? (e.g. allows user to query a database, allows login by users, upload files, etc.)",
34+
"group": "Which group at CMS is the project part of?",
35+
"repository_host": "Where is the repository hosted?",
36+
"status": "What is the status of the project?",
37+
"fisma_level": "What FISMA level is this project classified as? Learn more: https://security.cms.gov/learn/federal-information-security-modernization-act-fisma#perform-system-risk-categorization",
38+
"license": "What license is the project under?",
39+
"vcs": "What version control system is used?",
40+
"platforms": "What platform does the software runs on?",
41+
"categories": "What categories best describes the project? Separate items by commas. List of categories here: https://yml.publiccode.tools/categories-list.html?highlight=categories",
42+
"software_type": "What type of software is the project?",
43+
"languages": "What programming language(s) is the software written in? Separate items by commas.",
44+
"repo_type": "What type of repository is this project?",
45+
"usage_type": "What is the usage type for this project?",
46+
"maintenance": "How is the software maintained?",
47+
"tags": "Provide a list of tags to describe the software for search. Separate items by commas.",
48+
"contact_name": "A point of contact is needed for the project. What is the name of the point of contact?",
49+
"contact_email": "What is email address of the point of contact?",
50+
"localisation": "Does the software support multiple spoken languages?"
51+
}
52+
}

.github/workflows/auto-changelog.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: Changelog
2+
on:
3+
release:
4+
types:
5+
- created
6+
jobs:
7+
changelog:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: "Auto Generate changelog"
11+
uses: heinrichreimer/[email protected]
12+
with:
13+
14+
token: ${{ secrets.GITHUB_TOKEN }}
15+

.github/workflows/checks.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: "run-linting-checks"
2+
on:
3+
push:
4+
branches:
5+
- 'main'
6+
7+
jobs:
8+
resolve-repolinter-json:
9+
uses: DSACMS/repo-scaffolder/.github/workflows/extendJSONFile.yml@main
10+
with:
11+
url_to_json: 'https://raw.githubusercontent.com/DSACMS/repo-scaffolder/main/tier3/%7B%7Bcookiecutter.project_slug%7D%7D/repolinter.json'
12+
13+
repolinter-checks:
14+
name: Tier 3 Checks
15+
needs: resolve-repolinter-json
16+
runs-on: ubuntu-latest
17+
env:
18+
19+
RAW_JSON: ${{ needs.resolve-repolinter-json.outputs.raw-json }}
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
- run: echo $RAW_JSON > repolinter.json
24+
- uses: newrelic/repolinter-action@v1
25+
with:
26+
# A path to the JSON/YAML Repolinter ruleset to use, relative to the workflow
27+
# working directory (i.e. under `$GITHUB_WORKSPACE`).
28+
#
29+
# This option is mutually exclusive with config_url. If this option and
30+
# config_url are not specified, Repolinter's default ruleset will be used.
31+
config_file: 'repolinter.json'
32+
33+
# Where repolinter-action should put the linting results. There are two
34+
# options available:
35+
# * "exit-code": repolinter-action will print the lint output to the console
36+
# and set the exit code to result.passed. This output type is most useful for
37+
# PR status checks.
38+
# * "issue": repolinter-action will create a GitHub issue on the current
39+
# repository with the repolinter output and always exit 0. See the README for
40+
# more details on issue outputting behavior. This output type is ideal for
41+
# non-intrusive notification.
42+
#
43+
# Default: "exit-code"
44+
output_type: 'issue'
45+
46+
# The title to use for the issue created by repolinter-action. This title
47+
# should indicate the purpose of the issue, as well as that it was created by
48+
# a bot.
49+
#
50+
# This option will be ignored if output_type != "issue".
51+
#
52+
# Default: "[Repolinter] Open Source Policy Issues"
53+
output_name: '[Repolinter] Tier 3 Repository Hygiene Issue'
54+
55+
# The default token is the repolinter token for the DSACMS org
56+
# You can change it if needed.
57+
58+
token: ${{ secrets.REPOLINTER_AUTO_TOKEN }}
59+

.github/workflows/contributors.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Update Contributors Information
2+
3+
on:
4+
workflow_dispatch: {}
5+
schedule:
6+
# Weekly on Saturdays.
7+
- cron: "30 1 * * 6"
8+
push:
9+
branches: [main]
10+
11+
jobs:
12+
update-contributors:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
contents: write
16+
pull-requests: write
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
24+
- name: Update contributor list
25+
id: contrib_list
26+
uses: akhilmhdh/[email protected]
27+
env:
28+
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
31+
with:
32+
readme_path: MAINTAINERS.md
33+
use_username: false
34+
commit_message: "update contributors information"
35+
36+
- name: Get contributors count
37+
id: get_contributors
38+
env:
39+
40+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41+
42+
43+
run: |
44+
OWNER=$(echo $GITHUB_REPOSITORY | cut -d'/' -f1)
45+
REPO=$(echo $GITHUB_REPOSITORY | cut -d'/' -f2)
46+
QUERY='query { repository(owner: \"'"$OWNER"'\", name: \"'"$REPO"'\") { collaborators { totalCount } } }'
47+
48+
CONTRIBUTORS=$(gh api \
49+
-H "Accept: application/vnd.github+json" \
50+
-H "X-GitHub-Api-Version: 2022-11-28" \
51+
"/repos/$OWNER/$REPO/contributors?per_page=100" | \
52+
jq '[.[] | select(.type != "Bot" and (.login | test("\\[bot\\]$") | not) and (.login | test("-bot$") | not))] | length')
53+
54+
echo "Total contributors: $CONTRIBUTORS"
55+
echo "contributors=$CONTRIBUTORS" >> $GITHUB_OUTPUT
56+
57+
58+
- name: Update MAINTAINERS.md
59+
run: |
60+
61+
CONTRIBUTORS="${{ steps.get_contributors.outputs.contributors }}"
62+
63+
64+
perl -i -pe 's/(<!--CONTRIBUTOR COUNT START-->).*?(<!--CONTRIBUTOR COUNT END-->)/$1 '"$CONTRIBUTORS"' $2/' MAINTAINERS.md
65+
66+
git config user.name 'github-actions[bot]'
67+
git config user.email 'github-actions[bot]@users.noreply.github.com'
68+
git add MAINTAINERS.md
69+
git commit -m "update contributors count to $CONTRIBUTORS" || exit 0
70+
71+
- name: Push protected
72+
uses: CasperWA/push-protected@v2
73+
with:
74+
75+
token: ${{ secrets.PUSH_TO_PROTECTED_BRANCH }}
76+
77+
78+
branch: main

0 commit comments

Comments
 (0)