|
18 | 18 | --> |
19 | 19 | # ASF Infrastructure Pelican Action |
20 | 20 |
|
21 | | -**Note** Starting a branch and draft PR for systemic upgrades to the Pelican Action. |
22 | | - |
23 | | -**Note** This Action simplifies managing a project website. More information is available at <a href="https://infra.apache.org/asf-pelican.html" target="_blank">infra.apache.org/asf-pelican.html</a>. |
| 21 | +This Action simplifies managing a project website. More information is available at |
| 22 | +[infra.apache.org/asf-pelican.html](https://infra.apache.org/asf-pelican.html). |
24 | 23 |
|
25 | 24 | ## Inputs |
26 | | -* destination Pelican Output branch (optional) default: asf-site |
27 | | -* publish Publish to destination branch (optional) default: true |
28 | | -* gfm Uses GitHub Flavored Markdown (optional) default: true |
29 | | -* output Pelican generated output directory (optional) default: output |
30 | | -* tempdir Temporary Directory name (optional) default: ../output.tmp |
31 | | -* debug Pelican Debug mode (optional) default: false |
32 | | -* version Pelican Version (default 4.5.4) (optional) default: 4.5.4 |
33 | | -* requirements Python Requirements file (optional) default: None |
34 | | -* fatal Value for --fatal option [errors|warnings] - sets exit code to error (default: errors) |
35 | | - |
36 | | -## Example Workflow Usage: |
37 | 25 |
|
38 | | -``` |
39 | | -... |
| 26 | +| Name | Description | Default | |
| 27 | +| -------------- | --------------------------------------------------------------------------- | ----------------- | |
| 28 | +| `destination` | Pelican output branch | `asf-site` | |
| 29 | +| `publish` | Publish the site to the destination branch (set `false` to build only) | `true` | |
| 30 | +| `gfm` | Use GitHub Flavored Markdown | `true` | |
| 31 | +| `output` | Pelican generated output directory | `output` | |
| 32 | +| `tempdir` | Temporary directory name | `../output.tmp` | |
| 33 | +| `debug` | Pelican debug mode | `false` | |
| 34 | +| `version` | Pelican version to install | `4.11.0.post0` | |
| 35 | +| `requirements` | Extra Python requirements file to install on top of the action | _(none)_ | |
| 36 | +| `fatal` | Value for `--fatal` option (`errors` or `warnings`) | `errors` | |
| 37 | + |
| 38 | +## Example workflows |
| 39 | + |
| 40 | +Build and publish a site on every push: |
| 41 | + |
| 42 | +```yaml |
40 | 43 | jobs: |
41 | 44 | build-pelican: |
42 | 45 | runs-on: ubuntu-latest |
43 | 46 | steps: |
44 | 47 | - uses: actions/checkout@v4 |
45 | | - with: |
46 | 48 | - uses: apache/infrastructure-actions/pelican@main |
47 | 49 | with: |
48 | 50 | destination: master |
49 | 51 | gfm: 'true' |
50 | 52 | ``` |
51 | 53 |
|
52 | | -Example workflow for only building the site, not publishing. Useful for PR tests: |
| 54 | +Build only (useful for pull request checks): |
53 | 55 |
|
54 | | -``` |
55 | | -... |
| 56 | +```yaml |
56 | 57 | jobs: |
57 | 58 | build-pelican: |
58 | 59 | runs-on: ubuntu-latest |
59 | 60 | steps: |
60 | 61 | - uses: actions/checkout@v4 |
61 | | - with: |
62 | 62 | - uses: apache/infrastructure-actions/pelican@main |
63 | 63 | with: |
64 | 64 | publish: 'false' |
65 | 65 | ``` |
66 | 66 |
|
| 67 | +## Project layout |
| 68 | +
|
| 69 | +| Path | Purpose | |
| 70 | +| ----------------- | ---------------------------------------------------------------------------- | |
| 71 | +| `action.yml` | Composite GitHub Action entrypoint. | |
| 72 | +| `Dockerfile` | Docker-based runtime used by Apache CI pipelines. See [Docker.md](Docker.md).| |
| 73 | +| `pyproject.toml` | PEP 621 project metadata and dependencies (single source of truth). | |
| 74 | +| `plugin_paths.py` | Helper that injects plugin directories into the Pelican configuration. | |
| 75 | +| `plugins/` | Bundled ASF Pelican plugins (`asfdata`, `asfgenid`, `gfm`, ...). | |
| 76 | +| `migration/` | Scripts for migrating legacy infrastructure-pelican sites to this action. | |
| 77 | +| `build-cmark.sh` | Builder script for `cmark-gfm` used by GFM support. | |
| 78 | + |
| 79 | +## Working with the project |
| 80 | + |
| 81 | +The `pelican` directory is a Python project defined by `pyproject.toml` |
| 82 | +([PEP 517](https://peps.python.org/pep-0517/), |
| 83 | +[PEP 518](https://peps.python.org/pep-0518/), |
| 84 | +[PEP 621](https://peps.python.org/pep-0621/), |
| 85 | +[PEP 639](https://peps.python.org/pep-0639/)). |
| 86 | +Dependencies, metadata and lint configuration all live there — there is no |
| 87 | +separate `requirements.txt` to keep in sync. |
| 88 | + |
| 89 | +### Local development |
| 90 | + |
| 91 | +This project uses [`uv`](https://docs.astral.sh/uv/) as its package manager. |
| 92 | +For day-to-day development (editing plugins, running tests, linting) you |
| 93 | +have three options — pick whichever matches your workflow. |
| 94 | + |
| 95 | +**Tool version requirements.** Both `uv` and `hatch` need PEP 735 |
| 96 | +dependency-group support: |
| 97 | + |
| 98 | +| Tool | Minimum version | Why | |
| 99 | +| ----- | --------------- | --------------------------------------------------------------------- | |
| 100 | +| uv | `0.5.0` | `[tool.uv].required-version` enforces this; PEP 735 landed in 0.4.27. | |
| 101 | +| hatch | `1.16.0` | First release with `dependency-groups` support in env configs. | |
| 102 | + |
| 103 | +The uv floor is machine-enforced via `[tool.uv].required-version` in |
| 104 | +`pyproject.toml`; the hatch floor is currently only documented here because |
| 105 | +Hatch has no equivalent pyproject.toml field. |
| 106 | + |
| 107 | +**Option 1 — `uv sync` (recommended).** Let uv manage the virtual environment |
| 108 | +as a project. This creates (or updates) `.venv/`, resolves every runtime dep |
| 109 | +and the PEP 735 `dev` group in one pass, and writes a `uv.lock` for |
| 110 | +reproducible installs: |
| 111 | + |
| 112 | +```shell |
| 113 | +uv sync |
| 114 | +``` |
| 115 | + |
| 116 | +`uv sync` includes the `dev` group by default. Any `uv run <cmd>` invocation |
| 117 | +from the project root will automatically use this environment, so you can |
| 118 | +skip manual activation: |
| 119 | + |
| 120 | +```shell |
| 121 | +uv run ruff check . |
| 122 | +uv run pytest |
| 123 | +uv run pelican --version |
| 124 | +``` |
| 125 | + |
| 126 | +**Option 2 — manual venv.** If you prefer to manage the environment yourself |
| 127 | +(e.g. you already have one, or you want to combine it with other projects), |
| 128 | +create it, install the project in editable mode, and add the `dev` group: |
| 129 | + |
| 130 | +```shell |
| 131 | +uv venv |
| 132 | +source .venv/bin/activate |
| 133 | +uv pip install -e . |
| 134 | +uv pip install --group dev |
| 135 | +``` |
| 136 | + |
| 137 | +**Option 3 — [Hatch](https://hatch.pypa.io/).** The repo ships a |
| 138 | +`[tool.hatch.envs.default]` section that pulls the same PEP 735 `dev` group |
| 139 | +via `dependency-groups = ["dev"]`, with `uv` wired in as the installer: |
| 140 | + |
| 141 | +```shell |
| 142 | +hatch env create |
| 143 | +hatch shell |
| 144 | +``` |
| 145 | + |
| 146 | +Common tasks are exposed as named scripts, so you don't need to activate a |
| 147 | +shell if you just want to run one command: |
| 148 | + |
| 149 | +```shell |
| 150 | +hatch run lint # ruff check . |
| 151 | +hatch run fmt # ruff format . |
| 152 | +hatch run test # pytest |
| 153 | +``` |
| 154 | + |
| 155 | +All three options resolve the same `dev` dependency group, so you get |
| 156 | +Pelican alongside the project's runtime dependencies and the lint/test |
| 157 | +tooling no matter which workflow you choose. |
| 158 | + |
| 159 | +Pelican is intentionally **not** listed in `[project].dependencies`. The |
| 160 | +composite action installs `pelican[markdown]` itself via `uv tool install`, |
| 161 | +with the version controlled by the action's `version` input. Keeping Pelican |
| 162 | +in the `dev` group means there is a single authoritative version source for |
| 163 | +the composite action's runtime and a separate one for local development and |
| 164 | +the Docker image, with no risk of the two fighting during dependency |
| 165 | +resolution. |
| 166 | + |
| 167 | +To reproduce the exact install the **composite action** performs (Pelican |
| 168 | +inside an isolated uv tool venv, with this project's runtime dependencies |
| 169 | +injected), run: |
| 170 | + |
| 171 | +```shell |
| 172 | +uv tool install 'pelican[markdown]' --with . |
| 173 | +pelican --version |
| 174 | +``` |
| 175 | + |
| 176 | +To reproduce the **Docker image's** install instead (project + locked dev |
| 177 | +group, including Pelican, into `.venv/`), run: |
| 178 | + |
| 179 | +```shell |
| 180 | +uv sync --frozen |
| 181 | +.venv/bin/pelican --version |
| 182 | +``` |
| 183 | + |
| 184 | +### Linting and formatting |
| 185 | + |
| 186 | +Lint rules and formatter config live under `[tool.ruff]` in `pyproject.toml`: |
| 187 | + |
| 188 | +```shell |
| 189 | +ruff check . |
| 190 | +ruff format . |
| 191 | +``` |
| 192 | + |
| 193 | +### Updating dependencies |
| 194 | + |
| 195 | +`pyproject.toml` is the source of truth for declared dependency ranges. |
| 196 | +`uv.lock` (committed alongside it) pins every transitive package to a |
| 197 | +specific version + hash so the Docker image and `uv sync` workflows are |
| 198 | +fully reproducible. The composite action (`action.yml`) deliberately does |
| 199 | +**not** consume the lockfile — it installs `pelican[markdown]==<version>` |
| 200 | +fresh on every run so the action's `version` input stays authoritative. |
| 201 | + |
| 202 | +**Adding or changing a dependency.** Edit the `dependencies` list (or the |
| 203 | +`[dependency-groups].dev` list) in `pyproject.toml`, then refresh the |
| 204 | +lockfile: |
| 205 | + |
| 206 | +```shell |
| 207 | +uv lock # re-resolves only what your edit changed |
| 208 | +uv sync # apply the new lockfile to your local .venv |
| 209 | +``` |
| 210 | + |
| 211 | +Commit `pyproject.toml` and `uv.lock` together. Any drift between the two |
| 212 | +will cause the Docker build to fail (it runs `uv sync --frozen`), so the |
| 213 | +two files must always move as a pair. |
| 214 | + |
| 215 | +**Bumping pinned versions without changing constraints.** To pull in newer |
| 216 | +patch/minor releases that already satisfy the existing constraints in |
| 217 | +`pyproject.toml`, upgrade the lockfile in place: |
| 218 | + |
| 219 | +```shell |
| 220 | +uv lock --upgrade # bump every package to its latest allowed |
| 221 | +uv lock --upgrade-package foo # bump just one package |
| 222 | +uv sync # apply the refreshed lockfile |
| 223 | +``` |
| 224 | + |
| 225 | +**Automated updates via Dependabot.** The repo's |
| 226 | +[`.github/dependabot.yml`](../.github/dependabot.yml) registers |
| 227 | +`/pelican/` under the `uv` package ecosystem, so Dependabot reads |
| 228 | +`pelican/uv.lock` and opens PRs that bump pinned versions on a **weekly** |
| 229 | +schedule with a **4-day cooldown** (`cooldown.default-days: 4`) — newly |
| 230 | +released versions have to age four days before Dependabot will propose |
| 231 | +them, which avoids picking up brand-new releases that get yanked shortly |
| 232 | +after publication. Each Dependabot PR updates `uv.lock` only; if a bump |
| 233 | +needs a wider constraint in `pyproject.toml`, do that change manually |
| 234 | +following the "Adding or changing a dependency" flow above. |
| 235 | + |
| 236 | +### Building the Docker image |
| 237 | + |
| 238 | +See [Docker.md](Docker.md) for the full workflow. The short version: |
| 239 | + |
| 240 | +```shell |
| 241 | +docker build -t pelican-asf . |
| 242 | +docker run --rm -it -p 8000:8000 -v "$PWD":/site pelican-asf |
| 243 | +``` |
| 244 | + |
| 245 | +### How the action runs |
| 246 | + |
| 247 | +There are two execution paths and they install Pelican differently. In both |
| 248 | +cases the `pelican` CLI ends up on `PATH` alongside this project's runtime |
| 249 | +dependencies, and `plugin_paths` is importable from the same Python — so any |
| 250 | +dependency that `pelicanconf.py` needs at runtime lives in the same place as |
| 251 | +Pelican itself. |
| 252 | + |
| 253 | +1. **Composite action** (`action.yml`) — runs on a GitHub-hosted runner. |
| 254 | + It provisions a hermetic Python via [`actions/setup-python`][setup-python], |
| 255 | + bootstraps `uv` into that interpreter, runs |
| 256 | + `uv tool install 'pelican[markdown]==<version>' --with <action-path>` |
| 257 | + (optionally layering `--with-requirements <user-file>` when the |
| 258 | + `requirements` input is set), optionally builds `cmark-gfm` if `gfm: true`, |
| 259 | + and invokes `pelican content ...` directly. This path **does not** use |
| 260 | + `uv.lock` — Pelican's version comes from the `version` input and the |
| 261 | + project's runtime deps re-resolve on each run, so users can pin Pelican |
| 262 | + from their workflow without editing this repo. The tool venv's Python is |
| 263 | + published to later steps via `$PELICAN_TOOL_PY` so `plugin_paths` runs |
| 264 | + inside the same environment as Pelican. |
| 265 | + |
| 266 | +[setup-python]: https://github.com/actions/setup-python |
| 267 | +2. **Docker image** (`Dockerfile`) — a long-lived image used by Apache CI. |
| 268 | + It bootstraps `uv`, copies `pyproject.toml` + `uv.lock` into the image, |
| 269 | + then runs [`uv sync --frozen`](https://docs.astral.sh/uv/reference/cli/#uv-sync) |
| 270 | + to install the project together with the locked `dev` group (which |
| 271 | + includes Pelican) into `/opt/pelican-asf/.venv`. `--frozen` makes the |
| 272 | + build fail if `uv.lock` is out of sync with `pyproject.toml`, so the |
| 273 | + image is always a faithful materialisation of the committed lockfile. |
| 274 | + The image bakes in the plugins and exposes a `pelicanasf` wrapper (in |
| 275 | + `/usr/local/bin`) that calls `pelican` and uses the venv's Python to run |
| 276 | + `python -m plugin_paths`. |
| 277 | + |
| 278 | +The composite action path is intentionally re-resolved on every run so the |
| 279 | +`version` input stays authoritative. The Docker image is intentionally |
| 280 | +locked so production rebuilds are byte-reproducible. The two paths therefore |
| 281 | +have different update workflows (see [Updating dependencies](#updating-dependencies)). |
67 | 282 |
|
68 | 283 | # Pelican Migration Scripts |
69 | 284 |
|
|
0 commit comments