Skip to content

Commit cdfebf8

Browse files
committed
add cloud changelog
1 parent 1d7e903 commit cdfebf8

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/usr/bin/python
2+
3+
import re
4+
import os
5+
from collections import defaultdict
6+
import yaml
7+
import subprocess
8+
import sys
9+
import argparse
10+
11+
12+
def is_valid_change_log(ref):
13+
return re.match("^changelogs/fragments/(.*)\.(yaml|yml)$", ref)
14+
15+
def is_module_or_plugin(ref):
16+
prefix_list = (
17+
"plugins/modules",
18+
"plugins/action",
19+
"plugins/inventory",
20+
"plugins/lookup",
21+
"plugins/filter",
22+
"plugins/connection",
23+
"plugins/become",
24+
"plugins/cache",
25+
"plugins/callback",
26+
"plugins/cliconf",
27+
"plugins/httpapi",
28+
"plugins/netconf",
29+
"plugins/shell",
30+
"plugins/strategy",
31+
"plugins/terminal",
32+
"plugins/test",
33+
"plugins/vars",
34+
)
35+
return ref.startswith(prefix_list)
36+
37+
38+
def is_documentation_file(ref):
39+
prefix_list = (
40+
"docs/",
41+
"plugins/doc_fragments",
42+
)
43+
return ref.startswith(prefix_list)
44+
45+
def is_added_module_or_plugin_or_documentation_changes(changes):
46+
47+
# Validate Pull request add new modules and plugins
48+
if any([is_module_or_plugin(x) for x in changes["A"]]):
49+
return True
50+
51+
# Validate documentation changes only
52+
all_files = changes["A"] + changes["M"] + changes["D"]
53+
if all([is_documentation_file(x) for x in all_files]):
54+
return True
55+
56+
return False
57+
58+
def validate_changelog(path):
59+
60+
try:
61+
# https://github.com/ansible-community/antsibull-changelog/blob/main/docs/changelogs.rst#changelog-fragment-categories
62+
changes_type = (
63+
"release_summary",
64+
"breaking_changes",
65+
"major_changes",
66+
"minor_changes",
67+
"removed_features",
68+
"deprecated_features",
69+
"security_fixes",
70+
"bugfixes",
71+
"known_issues",
72+
"trivial",
73+
)
74+
with open(path, "rb") as f:
75+
result = list(yaml.safe_load_all(f))
76+
77+
for section in result:
78+
for key in section.keys():
79+
if key not in changes_type:
80+
print("Unexpected changelog section {0} from file {1}".format(key, os.path.basename(path)))
81+
return False
82+
if not isinstance(section[key], list):
83+
print(
84+
"Changelog section {0} from file {1} must be a list, {2} found instead.".format(
85+
key,
86+
os.path.basename(path),
87+
type(section[key])
88+
)
89+
)
90+
return False
91+
return True
92+
except (IOError, yaml.YAMLError) as exc:
93+
print("Error loading changelog file {0}: {1}".format(os.path.basename(path),exc))
94+
return False
95+
96+
def run_command(cmd):
97+
params = {
98+
"stdout": subprocess.PIPE,
99+
"stderr": subprocess.PIPE,
100+
"shell": True
101+
}
102+
proc = subprocess.Popen(cmd, **params)
103+
out, err = proc.communicate()
104+
return proc.returncode, out, err
105+
106+
def list_files(head_ref, base_ref):
107+
cmd = "git diff origin/{0} {1} --name-status".format(base_ref, head_ref)
108+
print("Running command '{0}'".format(cmd))
109+
rc, stdout, stderr = run_command(cmd)
110+
if rc != 0:
111+
raise ValueError(stderr)
112+
113+
changes = defaultdict(list)
114+
for file in stdout.decode("utf-8").split("\n"):
115+
v = file.split("\t")
116+
if len(v) == 2:
117+
changes[v[0]].append(v[1])
118+
return changes
119+
120+
def main(head_ref, base_ref):
121+
122+
print("[INFO] Head Ref '{0}' Base Ref '{1}'".format(head_ref, base_ref))
123+
124+
changes = list_files(head_ref, base_ref)
125+
if changes:
126+
changelog = [x for x in changes["A"] if is_valid_change_log(x)]
127+
if not changelog:
128+
if not is_added_module_or_plugin_or_documentation_changes(changes):
129+
print(
130+
"Missing changelog fragment. This is not required only if "\
131+
"PR adds new modules and plugins or contain only documentation changes."
132+
)
133+
sys.exit(1)
134+
print(
135+
"Changelog not required as PR adds new modules and/or plugins or "\
136+
"contain only documentation changes.")
137+
elif any(not validate_changelog(f) for f in changelog):
138+
sys.exit(1)
139+
sys.exit(0)
140+
141+
if __name__ == "__main__":
142+
143+
parser = argparse.ArgumentParser(description="Validate changelog file from new commit")
144+
parser.add_argument("--base-ref", required=True)
145+
parser.add_argument("--head-ref", required=True)
146+
147+
args = parser.parse_args()
148+
main(args.head_ref, args.base_ref)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Valid changelog file required for pull request not adding new modules only
2+
on:
3+
workflow_call:
4+
jobs:
5+
changelog:
6+
env:
7+
source_directory: "./source"
8+
script_directory: ${{ github.workspace }}
9+
runs-on: ubuntu-latest
10+
name: Require a changelog
11+
steps:
12+
- name: Checkout the collection repository
13+
uses: actions/checkout@v3
14+
with:
15+
path: ${{ env.source_directory }}
16+
ref: ${{ github.event.pull_request.head.sha }}
17+
fetch-depth: "0"
18+
19+
- name: setup python
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: '3.9'
23+
24+
- name: Download script
25+
run: curl -o ${{ env.script_directory }}/validate_changelog.py https://raw.githubusercontent.com/abikouo/github_actions/gha_cloud/.github/scripts/validate_changelog.py
26+
27+
- name: Install python required libraries
28+
run: pip install -U pyyaml
29+
30+
- name: Ensure a valid changelog entry exists
31+
run: >-
32+
python ${{ env.script_directory }}/validate_changelog.py
33+
--base-ref ${{ github.event.pull_request.base.ref }}
34+
--head-ref ${{ github.event.pull_request.head.sha }}
35+
working-directory: ${{ env.source_directory }}

0 commit comments

Comments
 (0)