Skip to content

Commit 060ec33

Browse files
committed
Add Circle CI job for doc review
1 parent 1426f5f commit 060ec33

File tree

3 files changed

+251
-0
lines changed

3 files changed

+251
-0
lines changed

.circleci/config.yml

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Circle CI configuration file
2+
# https://circleci.com/docs/
3+
4+
---
5+
version: 2.1
6+
7+
#######################################
8+
# Define some common steps as commands.
9+
#
10+
11+
commands:
12+
check-skip:
13+
steps:
14+
- run:
15+
name: Check-skip
16+
command: |
17+
export git_log=$(git log --max-count=1 --pretty=format:"%B" |
18+
tr "\n" " ")
19+
echo "Got commit message:"
20+
echo "${git_log}"
21+
if [[ -v CIRCLE_PULL_REQUEST ]] && ( \
22+
[[ "$git_log" == *"[skip circle]"* ]] || \
23+
[[ "$git_log" == *"[circle skip]"* ]]); then
24+
echo "Skip detected, exiting job ${CIRCLE_JOB} for PR ${CIRCLE_PULL_REQUEST}."
25+
circleci-agent step halt;
26+
fi
27+
28+
merge:
29+
steps:
30+
- run:
31+
name: Merge with upstream
32+
command: |
33+
if ! git remote -v | grep upstream; then
34+
git remote add upstream https://github.com/matplotlib/cycler.git
35+
fi
36+
git fetch upstream
37+
if [[ "$CIRCLE_BRANCH" != "main" ]] && \
38+
[[ "$CIRCLE_PR_NUMBER" != "" ]]; then
39+
echo "Merging ${CIRCLE_PR_NUMBER}"
40+
git pull --ff-only upstream "refs/pull/${CIRCLE_PR_NUMBER}/merge"
41+
fi
42+
43+
pip-install:
44+
description: Upgrade pip to get as clean an install as possible
45+
steps:
46+
- run:
47+
name: Upgrade pip
48+
command: |
49+
python -m pip install --upgrade --user pip
50+
51+
cycler-install:
52+
steps:
53+
- run:
54+
name: Install Cycler
55+
command: |
56+
python -m pip install --user -ve .[docs]
57+
58+
doc-build:
59+
steps:
60+
- restore_cache:
61+
keys:
62+
- sphinx-env-v1-{{ .BuildNum }}-{{ .Environment.CIRCLE_JOB }}
63+
- sphinx-env-v1-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}-{{ .Environment.CIRCLE_JOB }}
64+
- run:
65+
name: Build documentation
66+
command: |
67+
# Set epoch to date of latest tag.
68+
export SOURCE_DATE_EPOCH="$(git log -1 --format=%at $(git describe --abbrev=0))"
69+
mkdir -p logs
70+
make html O="-T -j4 -w /tmp/sphinxerrorswarnings.log"
71+
rm -r build/html/_sources
72+
working_directory: doc
73+
- save_cache:
74+
key: sphinx-env-v1-{{ .BuildNum }}-{{ .Environment.CIRCLE_JOB }}
75+
paths:
76+
- doc/build/doctrees
77+
78+
doc-show-errors-warnings:
79+
steps:
80+
- run:
81+
name: Extract possible build errors and warnings
82+
command: |
83+
(grep "WARNING\|ERROR" /tmp/sphinxerrorswarnings.log ||
84+
echo "No errors or warnings")
85+
# Save logs as an artifact, and convert from absolute paths to
86+
# repository-relative paths.
87+
sed "s~$PWD/~~" /tmp/sphinxerrorswarnings.log > \
88+
doc/logs/sphinx-errors-warnings.log
89+
when: always
90+
- store_artifacts:
91+
path: doc/logs/sphinx-errors-warnings.log
92+
93+
##########################################
94+
# Here is where the real jobs are defined.
95+
#
96+
97+
jobs:
98+
docs-python39:
99+
docker:
100+
- image: cimg/python:3.9
101+
resource_class: large
102+
steps:
103+
- checkout
104+
- check-skip
105+
- merge
106+
107+
- pip-install
108+
109+
- cycler-install
110+
111+
- doc-build
112+
- doc-show-errors-warnings
113+
114+
- store_artifacts:
115+
path: doc/build/html
116+
117+
#########################################
118+
# Defining workflows gets us parallelism.
119+
#
120+
121+
workflows:
122+
version: 2
123+
build:
124+
jobs:
125+
# NOTE: If you rename this job, then you must update the `if` condition
126+
# and `circleci-jobs` option in `.github/workflows/circleci.yml`.
127+
- docs-python39

.circleci/fetch_doc_logs.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
Download artifacts from CircleCI for a documentation build.
3+
4+
This is run by the :file:`.github/workflows/circleci.yml` workflow in order to
5+
get the warning/deprecation logs that will be posted on commits as checks. Logs
6+
are downloaded from the :file:`docs/logs` artifact path and placed in the
7+
:file:`logs` directory.
8+
9+
Additionally, the artifact count for a build is produced as a workflow output,
10+
by appending to the file specified by :env:`GITHUB_OUTPUT`.
11+
12+
If there are no logs, an "ERROR" message is printed, but this is not fatal, as
13+
the initial 'status' workflow runs when the build has first started, and there
14+
are naturally no artifacts at that point.
15+
16+
This script should be run by passing the CircleCI build URL as its first
17+
argument. In the GitHub Actions workflow, this URL comes from
18+
``github.event.target_url``.
19+
"""
20+
import json
21+
import os
22+
from pathlib import Path
23+
import sys
24+
from urllib.parse import urlparse
25+
from urllib.request import URLError, urlopen
26+
27+
28+
if len(sys.argv) != 2:
29+
print('USAGE: fetch_doc_results.py CircleCI-build-url')
30+
sys.exit(1)
31+
32+
target_url = urlparse(sys.argv[1])
33+
*_, organization, repository, build_id = target_url.path.split('/')
34+
print(f'Fetching artifacts from {organization}/{repository} for {build_id}')
35+
36+
artifact_url = (
37+
f'https://circleci.com/api/v2/project/gh/'
38+
f'{organization}/{repository}/{build_id}/artifacts'
39+
)
40+
print(artifact_url)
41+
try:
42+
with urlopen(artifact_url) as response:
43+
artifacts = json.load(response)
44+
except URLError:
45+
artifacts = {'items': []}
46+
artifact_count = len(artifacts['items'])
47+
print(f'Found {artifact_count} artifacts')
48+
49+
with open(os.environ['GITHUB_OUTPUT'], 'w+') as fd:
50+
fd.write(f'count={artifact_count}\n')
51+
52+
logs = Path('logs')
53+
logs.mkdir(exist_ok=True)
54+
55+
found = False
56+
for item in artifacts['items']:
57+
path = item['path']
58+
if path.startswith('doc/logs/'):
59+
path = Path(path).name
60+
print(f'Downloading {path} from {item["url"]}')
61+
with urlopen(item['url']) as response:
62+
(logs / path).write_bytes(response.read())
63+
found = True
64+
65+
if not found:
66+
print('ERROR: Did not find any artifact logs!')

.github/workflows/circleci.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
name: "CircleCI artifact handling"
3+
on: [status]
4+
jobs:
5+
circleci_artifacts_redirector_job:
6+
if: "${{ github.event.context == 'ci/circleci: docs-python39' }}"
7+
permissions:
8+
statuses: write
9+
runs-on: ubuntu-latest
10+
name: Run CircleCI artifacts redirector
11+
steps:
12+
- name: GitHub Action step
13+
uses: larsoner/circleci-artifacts-redirector-action@master
14+
with:
15+
repo-token: ${{ secrets.GITHUB_TOKEN }}
16+
artifact-path: 0/doc/build/html/index.html
17+
circleci-jobs: docs-python39
18+
job-title: View the built docs
19+
20+
post_warnings_as_review:
21+
if: "${{ github.event.context == 'ci/circleci: docs-python39' }}"
22+
permissions:
23+
contents: read
24+
checks: write
25+
pull-requests: write
26+
runs-on: ubuntu-latest
27+
name: Post warnings/errors as review
28+
steps:
29+
- uses: actions/checkout@v3
30+
31+
- name: Fetch result artifacts
32+
id: fetch-artifacts
33+
run: |
34+
python .circleci/fetch_doc_logs.py "${{ github.event.target_url }}"
35+
36+
- name: Set up reviewdog
37+
if: "${{ steps.fetch-artifacts.outputs.count != 0 }}"
38+
uses: reviewdog/action-setup@v1
39+
with:
40+
reviewdog_version: latest
41+
42+
- name: Post review
43+
if: "${{ steps.fetch-artifacts.outputs.count != 0 }}"
44+
env:
45+
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46+
REVIEWDOG_SKIP_DOGHOUSE: "true"
47+
CI_COMMIT: ${{ github.event.sha }}
48+
CI_REPO_OWNER: ${{ github.event.repository.owner.login }}
49+
CI_REPO_NAME: ${{ github.event.repository.name }}
50+
run: |
51+
# The 'status' event does not contain information in the way that
52+
# reviewdog expects, so we unset those so it reads from the
53+
# environment variables we set above.
54+
unset GITHUB_ACTIONS GITHUB_EVENT_PATH
55+
cat logs/sphinx-deprecations.log | \
56+
reviewdog \
57+
-efm '%f\:%l: %m' \
58+
-name=examples -tee -reporter=github-check -filter-mode=nofilter

0 commit comments

Comments
 (0)