Skip to content

Commit c917c9a

Browse files
authored
ci: add markdown, website, and image lint checks (#1010)
Adds Markdown, website, and image lint checks to CI, mirroring the setup in `apify-docs` and `apify-core`. The docs and website now go through the same kind of automated checks as the Python code. The website tooling moves from eslint + prettier (which weren't wired into CI) to [oxlint](https://oxc.rs/) and [oxfmt](https://oxc.rs/), and [markdownlint](https://github.com/DavidAnson/markdownlint) now covers the Markdown files. Three jobs run in the existing Checks workflow: - **Markdown lint** — `markdownlint` over `README.md`, `CONTRIBUTING.md`, and the `docs/` folder. - **Website lint and format** — `oxlint` plus an `oxfmt --check` formatting gate for the Docusaurus site. - **Image lint** — fails when a PR adds unoptimized raster images, which should be converted with `pnpm opt:images` first. Notes: - The website JS keeps single quotes, set via `.editorconfig` (`quote_type`) and `.oxfmtrc.json` (`singleQuote`). - The markdownlint config matches `apify-docs`: line length is disabled and inline HTML is allowed. - Minor doc and code fixes were applied where needed to pass the new checks. Closes #754
1 parent efdc5a3 commit c917c9a

28 files changed

Lines changed: 945 additions & 2360 deletions

.editorconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ charset = utf-8
77
trim_trailing_whitespace = true
88
insert_final_newline = true
99
end_of_line = lf
10+
quote_type = single
1011

1112
[Makefile]
1213
indent_style = tab

.github/workflows/_checks.yaml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ on:
1515
permissions:
1616
contents: read
1717

18+
env:
19+
NODE_VERSION: 24
20+
1821
jobs:
1922
actions_lint_check:
2023
name: Actions lint check
@@ -46,6 +49,86 @@ jobs:
4649
with:
4750
python_versions: '["3.11", "3.12", "3.13", "3.14"]'
4851

52+
markdown_lint_check:
53+
name: Markdown lint check
54+
runs-on: ubuntu-latest
55+
steps:
56+
- name: Checkout repository
57+
uses: actions/checkout@v7
58+
59+
- name: Set up Node
60+
uses: actions/setup-node@v6
61+
with:
62+
node-version: ${{ env.NODE_VERSION }}
63+
64+
- name: Install pnpm and website dependencies
65+
uses: apify/actions/pnpm-install@v1.2.0
66+
with:
67+
working-directory: website
68+
69+
- name: Lint Markdown
70+
run: pnpm lint:md
71+
working-directory: website
72+
73+
website_lint_check:
74+
name: Website lint check
75+
runs-on: ubuntu-latest
76+
steps:
77+
- name: Checkout repository
78+
uses: actions/checkout@v7
79+
80+
- name: Set up Node
81+
uses: actions/setup-node@v6
82+
with:
83+
node-version: ${{ env.NODE_VERSION }}
84+
85+
- name: Install pnpm and website dependencies
86+
uses: apify/actions/pnpm-install@v1.2.0
87+
with:
88+
working-directory: website
89+
90+
- name: Lint website code
91+
run: pnpm lint:code
92+
working-directory: website
93+
94+
- name: Check website formatting
95+
run: pnpm format:check
96+
working-directory: website
97+
98+
image_lint_check:
99+
name: Image lint check
100+
runs-on: ubuntu-latest
101+
steps:
102+
- name: Checkout repository
103+
uses: actions/checkout@v7
104+
with:
105+
fetch-depth: 0
106+
107+
# Doc images must be committed as optimized `.webp`. This fails when a PR adds raster
108+
# images in another format so they get converted via `pnpm opt:images` first.
109+
- name: Get changed unoptimized images
110+
id: changed-files
111+
uses: tj-actions/changed-files@v47
112+
with:
113+
files: |
114+
docs/**/*.{png,jpg,jpeg,gif,bmp,tif,tiff,avif}
115+
website/static/**/*.{png,jpg,jpeg,gif,bmp,tif,tiff,avif}
116+
separator: "\n"
117+
118+
- name: Fail on unoptimized images
119+
if: steps.changed-files.outputs.any_changed == 'true'
120+
env:
121+
UNOPTIMIZED_IMAGE_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
122+
run: |
123+
echo "Unoptimized images detected! Convert each one to WebP, e.g.:"
124+
echo ""
125+
while IFS= read -r file_path; do
126+
echo " (cd website && pnpm opt:images \"../$file_path\")"
127+
done <<< "$UNOPTIMIZED_IMAGE_FILES"
128+
echo ""
129+
echo "Then reference the resulting .webp files in your Markdown."
130+
exit 1
131+
49132
unit_tests:
50133
name: Unit tests
51134
if: inputs.run_tests

.github/workflows/manual_release_docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ permissions:
2323
contents: read
2424

2525
env:
26-
NODE_VERSION: 22
26+
NODE_VERSION: 24
2727
PYTHON_VERSION: 3.14
2828

2929
jobs:

.github/workflows/manual_version_docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ permissions:
2727
contents: read
2828

2929
env:
30-
NODE_VERSION: "22"
30+
NODE_VERSION: "24"
3131
PYTHON_VERSION: "3.14"
3232

3333
jobs:

.markdownlint.yaml

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
1+
# markdownlint config for the docs and top-level Markdown files.
2+
# Run via `pnpm lint:md` / `pnpm lint:md:fix` from the `website/` directory.
13
default: true
2-
line-length:
3-
line_length: 150
4-
ul-style: dash
4+
5+
# Prose is written one sentence per line, so line length is not enforced.
6+
line-length: false
7+
8+
ul-style:
9+
style: dash
10+
11+
# Nested unordered lists use 4-space indentation.
12+
ul-indent:
13+
indent: 4
14+
15+
# Docs are MDX and embed JSX components.
516
no-inline-html: false
17+
18+
# MDX pages set their title via front matter, so multiple/duplicate H1s are fine.
19+
single-title: false
20+
no-duplicate-heading:
21+
siblings_only: true
22+
23+
# Anchor links into other pages can't be validated locally.
24+
link-fragments: false
25+
26+
no-bare-urls: false
27+
no-trailing-punctuation:
28+
punctuation: ".,;:。,;:"
29+
no-multiple-blanks:
30+
maximum: 2

.rules.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ uv run poe e2e-tests
5252
- Unused imports are allowed in `__init__.py` files (re-exports)
5353
- **Pre-commit hooks**: lint check + type check run automatically on commit
5454
- **Commits**: [Conventional Commits](https://www.conventionalcommits.org/) format. Choose the type based on *what* changed, not just *why*:
55-
- `feat:` / `fix:` / `perf:` / `refactor:` / `style:`**source code only**; these trigger a release and appear in the changelog
56-
- `test:` — test additions or changes (no release triggered)
57-
- `docs:` — documentation changes; also triggers a doc release on master
58-
- `ci:` — CI/workflow changes
59-
- `chore:` — dependency bumps, tooling, and other housekeeping
60-
- `build:` — build system changes
55+
- `feat:` / `fix:` / `perf:` / `refactor:` / `style:`**source code only**; these trigger a release and appear in the changelog
56+
- `test:` — test additions or changes (no release triggered)
57+
- `docs:` — documentation changes; also triggers a doc release on master
58+
- `ci:` — CI/workflow changes
59+
- `chore:` — dependency bumps, tooling, and other housekeeping
60+
- `build:` — build system changes
6161

6262
## Architecture
6363

CONTRIBUTING.md

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,22 @@ To run the documentation locally (requires Node.js):
118118
uv run poe run-docs
119119
```
120120

121+
### Linting the docs and website
122+
123+
Markdown content (this guide, `README.md`, and the `docs/` folder) is checked with
124+
[markdownlint](https://github.com/DavidAnson/markdownlint). The Docusaurus website code is linted
125+
with [oxlint](https://oxc.rs/) and formatted with [oxfmt](https://oxc.rs/). All of them run in CI.
126+
To run them locally (requires Node.js 22.12 or newer and pnpm), from the `website/` directory:
127+
128+
```sh
129+
pnpm lint # lint Markdown and website code
130+
pnpm lint:fix # auto-fix both
131+
pnpm format # format the website code
132+
```
133+
134+
Doc images are committed as optimized `.webp`. To convert a new image, run
135+
`pnpm opt:images <path-to-image>` from the `website/` directory.
136+
121137
## Commits
122138

123139
We use [Conventional Commits](https://www.conventionalcommits.org/) format for commit messages. This convention is used to automatically determine version bumps during the release process.
@@ -149,25 +165,22 @@ Publishing new versions to [PyPI](https://pypi.org/project/apify) is automated t
149165

150166
1. **Do not do this unless absolutely necessary.** In all conceivable scenarios, you should use the `release` workflow instead.
151167
2. **Make sure you know what you're doing.**
168+
3. Update the version number by modifying the `version` field under `project` in `pyproject.toml`:
152169

153-
3. Update the version number:
154-
155-
- Modify the `version` field under `project` in `pyproject.toml`.
156-
157-
```toml
158-
[project]
159-
name = "apify"
160-
version = "x.z.y"
161-
```
170+
```toml
171+
[project]
172+
name = "apify"
173+
version = "x.z.y"
174+
```
162175

163176
4. Build the package:
164177

165-
```sh
166-
uv run poe build
167-
```
178+
```sh
179+
uv run poe build
180+
```
168181

169182
5. Upload to PyPI:
170183

171-
```sh
172-
uv publish --token YOUR_API_TOKEN
173-
```
184+
```sh
185+
uv publish --token YOUR_API_TOKEN
186+
```

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ async def main() -> None:
195195
The full SDK documentation lives at **[docs.apify.com/sdk/python](https://docs.apify.com/sdk/python)**. For the Apify platform itself, see the [Apify documentation](https://docs.apify.com/).
196196
197197
| Section | What you'll find |
198-
|---|---|
198+
| --- | --- |
199199
| [Overview](https://docs.apify.com/sdk/python/docs/overview) | What the SDK is, what Actors are, and how the pieces fit together. |
200200
| [Quick start](https://docs.apify.com/sdk/python/docs/quick-start) | Create, run, and deploy your first Python Actor. |
201201
| [Concepts](https://docs.apify.com/sdk/python/docs/concepts/actor-lifecycle) | Actor lifecycle, input, storages, events, proxy management, interacting with other Actors, webhooks, accessing the Apify API, logging, configuration, and pay-per-event. |

docs/02_concepts/09_logging.mdx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ You can use the `extra` argument for all log levels, it's not specific to the wa
5353
Result:
5454

5555
<!-- TODO: This is an ugly ugly hack, we should make a component for this in the docs theme -->
56-
<!-- markdownlint-disable no-inline-html -->
5756
<style>{`
5857
.actor-log-block .ansi-blue-fg {
5958
color: rgb(0, 160, 228);
@@ -87,8 +86,6 @@ Result:
8786
<div> RuntimeError: Ouch!</div>
8887
</pre>
8988

90-
<!-- markdownlint-enable no-inline-html -->
91-
9289
## Redirect logs from other Actor runs
9390

9491
In some situations, one Actor is going to start one or more other Actors and wait for them to finish and produce some results. In such cases, you might want to redirect the logs and status messages of the started Actors runs back to the parent Actor run, so that you can see the progress of the started Actors' runs in the parent Actor's logs. This guide will show possibilities on how to do it.

docs/03_guides/06_scrapy.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ For further details, see the [Scrapy migration guide](https://docs.apify.com/cli
7373

7474
The following example shows a Scrapy Actor that scrapes page titles and enqueues links found on each page. This example aligns with the structure provided in the Apify Actor templates.
7575

76-
{/* Not runnable from the docs: a Scrapy Actor is a multi-file project, while the "Run on Apify" runner executes a single self-contained snippet. */}
76+
{/*Not runnable from the docs: a Scrapy Actor is a multi-file project, while the "Run on Apify" runner executes a single self-contained snippet.*/}
7777
<Tabs>
7878
<TabItem value="__main__.py" label="__main__.py">
7979
<CodeBlock className="language-python">

0 commit comments

Comments
 (0)