Skip to content

Commit ecd4035

Browse files
authored
Api Diff Report CI (#11236)
1 parent e73e86a commit ecd4035

File tree

13 files changed

+354
-291
lines changed

13 files changed

+354
-291
lines changed

.github/workflows/api_diff_report.yml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: API Diff Report
2+
3+
on: [pull_request]
4+
5+
concurrency:
6+
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }}
7+
cancel-in-progress: true
8+
9+
env:
10+
STAGE_PROGRESS: progress
11+
STAGE_END: end
12+
PR_API_OUTPUT: ci_outputs/pr_branch_api
13+
BASE_API_OUTPUT: ci_outputs/base_branch_api
14+
DIFF_REPORT_OUTPUT: ci_outputs/diff_report
15+
16+
jobs:
17+
diff_report:
18+
runs-on: macos-latest
19+
20+
steps:
21+
- name: Checkout PR branch
22+
uses: actions/checkout@v3
23+
with:
24+
fetch-depth: 2
25+
26+
- name: Copy diff report tools
27+
run: cp -a scripts/api_diff_report/. ~/api_diff_report
28+
29+
- id: get_changed_files
30+
name: Get changed file list
31+
run: |
32+
echo "file_list=$(git diff --name-only -r HEAD^1 HEAD | tr '\n' ' ')" >> $GITHUB_OUTPUT
33+
34+
- name: Setup python
35+
uses: actions/setup-python@v4
36+
with:
37+
python-version: 3.7
38+
39+
- name: Install Prerequisites
40+
run: ~/api_diff_report/prerequisite.sh
41+
42+
- name: Clean Diff Report Comment in PR
43+
run: |
44+
python ~/api_diff_report/pr_commenter.py \
45+
--stage ${{ env.STAGE_PROGRESS }} \
46+
--token ${{github.token}} \
47+
--pr_number ${{github.event.pull_request.number}} \
48+
--commit $GITHUB_SHA \
49+
--run_id ${{github.run_id}}
50+
51+
- name: Generate API files for PR branch
52+
run: |
53+
python ~/api_diff_report/api_info.py \
54+
--file_list ${{ steps.get_changed_files.outputs.file_list }} \
55+
--output_dir ${{ env.PR_API_OUTPUT }}
56+
57+
- name: Checkout Base branch
58+
run: git checkout HEAD^
59+
60+
- name: Generate API files for Base branch
61+
run: |
62+
python ~/api_diff_report/api_info.py \
63+
--file_list ${{ steps.get_changed_files.outputs.file_list }} \
64+
--output_dir ${{ env.BASE_API_OUTPUT }}
65+
66+
- name: Generate API Diff Report
67+
run: |
68+
python ~/api_diff_report/api_diff_report.py \
69+
--pr_branch ${{ env.PR_API_OUTPUT }} \
70+
--base_branch ${{ env.BASE_API_OUTPUT }} \
71+
--output_dir ${{ env.DIFF_REPORT_OUTPUT }}
72+
73+
- name: Update Diff Report Comment in PR
74+
run: |
75+
python ~/api_diff_report/pr_commenter.py \
76+
--stage ${{ env.STAGE_END }} \
77+
--report ${{ env.DIFF_REPORT_OUTPUT }} \
78+
--token ${{github.token}} \
79+
--pr_number ${{github.event.pull_request.number}} \
80+
--commit $GITHUB_SHA \
81+
--run_id ${{github.run_id}}
82+
83+
- uses: actions/upload-artifact@v3
84+
if: ${{ !cancelled() }}
85+
with:
86+
name: api_info_and_report
87+
path: ci_outputs
88+
retention-days: 1

scripts/api_diff_report/api_diff_report.py

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,51 @@
1717
import logging
1818
import os
1919
import api_info
20-
import datetime
21-
import pytz
2220

2321
STATUS_ADD = 'ADDED'
2422
STATUS_REMOVED = 'REMOVED'
2523
STATUS_MODIFIED = 'MODIFIED'
2624
STATUS_ERROR = 'BUILD ERROR'
25+
API_DIFF_FILE_NAME = 'api_diff_report.markdown'
2726

2827

2928
def main():
3029
logging.getLogger().setLevel(logging.INFO)
3130

3231
args = parse_cmdline_args()
3332

34-
pr_branch = os.path.expanduser(args.pr_branch)
35-
base_branch = os.path.expanduser(args.base_branch)
36-
new_api_json = json.load(
37-
open(os.path.join(pr_branch, api_info.API_INFO_FILE_NAME)))
38-
old_api_json = json.load(
39-
open(os.path.join(base_branch, api_info.API_INFO_FILE_NAME)))
33+
new_api_file = os.path.join(os.path.expanduser(args.pr_branch),
34+
api_info.API_INFO_FILE_NAME)
35+
old_api_file = os.path.join(os.path.expanduser(args.base_branch),
36+
api_info.API_INFO_FILE_NAME)
37+
if os.path.exists(new_api_file):
38+
with open(new_api_file) as f:
39+
new_api_json = json.load(f)
40+
else:
41+
new_api_json = {}
42+
if os.path.exists(old_api_file):
43+
with open(old_api_file) as f:
44+
old_api_json = json.load(f)
45+
else:
46+
old_api_json = {}
4047

4148
diff = generate_diff_json(new_api_json, old_api_json)
4249
if diff:
4350
logging.info(f'json diff: \n{json.dumps(diff, indent=2)}')
4451
logging.info(f'plain text diff report: \n{generate_text_report(diff)}')
45-
logging.info(f'markdown diff report: \n{generate_markdown_report(diff)}')
52+
report = generate_markdown_report(diff)
53+
logging.info(f'markdown diff report: \n{report}')
4654
else:
4755
logging.info('No API Diff Detected.')
56+
report = ""
57+
58+
output_dir = os.path.expanduser(args.output_dir)
59+
if not os.path.exists(output_dir):
60+
os.makedirs(output_dir)
61+
api_report_path = os.path.join(output_dir, API_DIFF_FILE_NAME)
62+
logging.info(f'Writing API diff report to {api_report_path}')
63+
with open(api_report_path, 'w') as f:
64+
f.write(report)
4865

4966

5067
def generate_diff_json(new_api, old_api, level='module'):
@@ -150,17 +167,6 @@ def generate_text_report(diff, level=0, print_key=True):
150167
return report
151168

152169

153-
def generate_markdown_title(commit, run_id):
154-
pst_now = datetime.datetime.utcnow().astimezone(
155-
pytz.timezone('America/Los_Angeles'))
156-
return (
157-
'## Apple API Diff Report\n' + 'Commit: %s\n' % commit
158-
+ 'Last updated: %s \n' % pst_now.strftime('%a %b %e %H:%M %Z %G')
159-
+ '**[View workflow logs & download artifacts]'
160-
+ '(https://github.com/firebase/firebase-ios-sdk/actions/runs/%s)**\n\n'
161-
% run_id + '-----\n')
162-
163-
164170
def generate_markdown_report(diff, level=0):
165171
report = ''
166172
header_str = '#' * (level + 3)
@@ -237,8 +243,7 @@ def parse_cmdline_args():
237243
parser = argparse.ArgumentParser()
238244
parser.add_argument('-p', '--pr_branch')
239245
parser.add_argument('-b', '--base_branch')
240-
parser.add_argument('-c', '--commit')
241-
parser.add_argument('-i', '--run_id')
246+
parser.add_argument('-o', '--output_dir', default='output_dir')
242247

243248
args = parser.parse_args()
244249
return args

scripts/api_diff_report/api_info.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,25 @@ def main():
3030
# Parse command-line arguments
3131
args = parse_cmdline_args()
3232
output_dir = os.path.expanduser(args.output_dir)
33-
api_theme_dir = os.path.expanduser(args.api_theme_dir)
3433
if not os.path.exists(output_dir):
3534
os.makedirs(output_dir)
3635

3736
# Detect changed modules based on changed files
3837
changed_api_files = get_api_files(args.file_list)
3938
if not changed_api_files:
4039
logging.info('No Changed API File Detected')
41-
exit(1)
40+
exit(0)
4241
changed_modules = icore_module.detect_changed_modules(changed_api_files)
4342
if not changed_modules:
4443
logging.info('No Changed Module Detected')
45-
exit(1)
44+
exit(0)
4645

4746
# Generate API documentation and parse API declarations
4847
# for each changed module
4948
api_container = {}
5049
for _, module in changed_modules.items():
5150
api_doc_dir = os.path.join(output_dir, 'doc', module['name'])
52-
build_api_doc(module, api_doc_dir, api_theme_dir)
51+
build_api_doc(module, api_doc_dir)
5352

5453
if os.path.exists(api_doc_dir):
5554
module_api_container = parse_module(api_doc_dir)
@@ -74,7 +73,7 @@ def get_api_files(file_list):
7473
]
7574

7675

77-
def build_api_doc(module, output_dir, api_theme_dir):
76+
def build_api_doc(module, output_dir):
7877
"""Use Jazzy to build API documentation for a specific module's source
7978
code."""
8079
if module['language'] == icore_module.SWIFT:
@@ -84,8 +83,7 @@ def build_api_doc(module, output_dir, api_theme_dir):
8483
+ ' --build-tool-arguments'\
8584
+ f' -scheme,{module["scheme"]}'\
8685
+ ',-destination,generic/platform=iOS,build'\
87-
+ f' --output {output_dir}'\
88-
+ f' --theme {api_theme_dir}'
86+
+ f' --output {output_dir}'
8987
logging.info(cmd)
9088
result = subprocess.Popen(cmd,
9189
universal_newlines=True,
@@ -97,8 +95,7 @@ def build_api_doc(module, output_dir, api_theme_dir):
9795
cmd = 'jazzy --objc'\
9896
+ f' --framework-root {module["root_dir"]}'\
9997
+ f' --umbrella-header {module["umbrella_header"]}'\
100-
+ f' --output {output_dir}'\
101-
+ f' --theme {api_theme_dir}'
98+
+ f' --output {output_dir}'
10299
logging.info(cmd)
103100
result = subprocess.Popen(cmd,
104101
universal_newlines=True,
@@ -215,9 +212,6 @@ def parse_cmdline_args():
215212
parser = argparse.ArgumentParser()
216213
parser.add_argument('-f', '--file_list', nargs='+', default=[])
217214
parser.add_argument('-o', '--output_dir', default='output_dir')
218-
parser.add_argument('-t',
219-
'--api_theme_dir',
220-
default='scripts/api_diff_report/theme')
221215

222216
args = parser.parse_args()
223217
return args

0 commit comments

Comments
 (0)