Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed .github/workflows/main.yml
Empty file.
30 changes: 22 additions & 8 deletions .github/workflows/pages.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This is a basic workflow to help you get started with Actions

name: CI
name: Build and deploy all docs to github pages.

# Controls when the workflow will run
on:
Expand All @@ -10,12 +10,17 @@ on:
pull_request:
branches: [ "main" ]

permissions:
contents: read
pages: write
id-token: write

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"

build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
Expand All @@ -27,10 +32,19 @@ jobs:

# Runs a single command using the runners shell
- name: Run a one-line script
run: echo Hello, world!
run: |
./tools/build.sh

- uses: actions/upload-pages-artifact@v3
with:
path: site

# Runs a set of commands using the runners shell
- name: Run a multi-line script
run: |
echo Add other actions to build,
echo test, and deploy your project.
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ generated/xcore/contab/netex.html
templates/README.md~
tools/schematron_builder/__pycache__/template2schematron.cpython-313.pyc
__pycache__
netex_rg_ch.egg-info
netex_rg_ch.egg-info
dist/
site/
build/
12 changes: 9 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[build-system]
requires = ["setuptools>=70", "wheel"]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "netex-rg-ch"
version = "0.1.0"
version = "0.1.1"
description = "Provides tools to help building the NeTEx RG."
requires-python = ">=3.13"
dependencies = [
Expand All @@ -23,4 +23,10 @@ xml-snippets="tools.xml_snippets.build_xml_snippets:main"

# Only package the specified packages
[tool.setuptools.packages.find]
include = ["tools*"]
include = ["tools*"]

[tool.setuptools.package-data]
"expanded_docs" = ["generated/docs/**/*.md"]

[options.entry_points."distutils.commands"]
expand_docs = "tools.expand_docs.ExpandDocs"
46 changes: 46 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# setup.py - tiny hook to build docs into ./site)
import os
import subprocess
import sys
from pathlib import Path
from setuptools import setup, Command
from setuptools.command.sdist import sdist as _sdist

class build_docs(Command):
"""Generate documentation into ./site."""
description = "Build project documentation into ./site"
user_options = [
("clean", None, "clean the output directory before building"),
]

def initialize_options(self):
self.clean = False

def finalize_options(self):
self.clean = bool(self.clean)

def run(self):
outdir = Path("site")
if self.clean and outdir.exists():
import shutil
shutil.rmtree(outdir)
outdir.mkdir(exist_ok=True)

# run tools here ...
cmd = [sys.executable, "-m", "tools.expand_docs.expand_docs", "--out", str(outdir)]
self.announce(f"Running: {' '.join(cmd)}", level=2)
# Build environment variables if you want to pass context
env = dict(os.environ)
env.setdefault("PYTHONHASHSEED", "0")
subprocess.check_call(cmd, env=env) # fails the build on nonzero exit
self.announce(f"Docs generated in {outdir}", level=2)

class sdist(_sdist):
def run(self):
# Build docs before sdist (writes to ./site but we will exclude it from sdist)
self.run_command("build_docs")
super().run()

cmdclass = {"build_docs": build_docs, "sdist": sdist}

setup(cmdclass=cmdclass)
79 changes: 71 additions & 8 deletions tools/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,53 @@
# Tools for the Swiss NeTEx RG

## Install Tools with uv
## How to setup and run the build

The package manager `uv` simplifies the build and installation of scripts for the tools.
The build builds the tools and runs them to create the generated documents in the directory `site`.

- Dependencies are managed by `uv`, as configured in `pyproject.toml` and more detailed in `uv.lock`.
- `uv` provides an os-independent interface for scripts
- The generated tool scripts run on Windows, Mac or Linux
### Steps involved to setup and run the build

1. Install the [uv package manager](#install-the-uv-package-manager)
2. Initialize the [virtual environment](#initialize-the-virtual-environment)
3. [Run the build]()

For more information about the build framework, see [Build Automation](#build-automation).

### Install the uv package manager

Install the uv package manager:
- See [uv package manager](https://docs.astral.sh/uv/)
- if you have pip installed, you can run `pip install uv`

### Initialize the virtual environment

#### Mac/Linux

Run the following following commands in the project root directory:
```sh
uv venv
uv sync
sh ./venv/bin/activate
```

#### Windows

Run the following following commands in the project root directory:

``` shell
uv venv
uv sync
venv\bin\activate.bat
```

### Install the package manager
### Run the build

See [uv package manager](https://docs.astral.sh/uv/)
If everything is setup correctly, you should be able to run the build doing `python -m build` in the project root directory.


## Tool Scripts

The `pyproject.toml` is configured to generate scripts for the tools.
These tool scripts are not required for the build, but they may be useful for running tools locally.

### Prerequisites: Set PYTHONPATH and PATH

Expand Down Expand Up @@ -52,4 +89,30 @@ This generates executable scripts for Linux/Mac and Windows in subdirectories of
### How to add a new Script

- Add a new entry in the `[project.scripts]` section of `pyproject.toml`.
- If the script requires another package, use `uv add` to added to the environment.
- If the script requires another package, use `uv add` to added to the environment.

## Build Automation Framework

### Package Manager

The package manager `uv` simplifies the build and installation of scripts for the tools.

- Dependencies are managed by `uv`, as configured in `pyproject.toml` and more detailed in `uv.lock`.
- `uv` provides an os-independent interface for scripts
- Generated tool scripts run on Windows, Mac or Linux

### Project build

Components of the build automation:
- [pyproject.toml](../pyproject.toml) is configured with `setuptools` (https://setuptools.pypa.io/en/latest/)
- docs can be generated running `python -m build`
- `setup.py` in the root project acts as the interface for the build
- here we can add tools to be run during the build.
- The build writes all output to directory `site`, excluded from git

### Github Action

The Github Action [pages.yaml](../.github/pages.yaml) runs the script [build.sh](./build.sh) (can also be tested locally)
- triggered after commits to main branch (e.g. after the merge of a branch)
- runs the build via the `python -m build` mechanism
- uploads generated docs to GitHub Pages
61 changes: 61 additions & 0 deletions tools/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# Build script for the github action.
# - can also be run and tested locally (Mac or Linux)
# - use "python -m build to build docs otherwise
set -Eeuo pipefail
IFS=$'\n\t'

# --- Configuration (override via environment variables) ---
PYTHON="${PYTHON_BIN:-python3}" # or "python" if that's your default
VENV_DIR="${VENV_DIR:-.venv-build}" # ephemeral venv only for build tools
OUTDIR="${OUTDIR:-dist}" # where artifacts go
EXTRA_BUILD_ARGS="${EXTRA_BUILD_ARGS:-}" # e.g. "--no-isolation" (not recommended)
TEST_INSTALL="${TEST_INSTALL:-1}" # 1 to smoke-test installing the wheel

# --- Move to repo root ---
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." &>/dev/null && pwd)"
cd "$REPO_ROOT"

echo "==> Building package in $REPO_ROOT"

# --- Pre-flight checks ---
if [[ ! -f pyproject.toml ]]; then
echo "Error: pyproject.toml not found in repository root: $REPO_ROOT" >&2
exit 1
fi

# --- Clean previous build outputs ---
# rm -rf "$OUTDIR" build .pytest_cache
mkdir -p "$OUTDIR"

# --- Create isolated venv for build tools ---
if [[ ! -d "$VENV_DIR" ]]; then
echo "==> Creating build venv at $VENV_DIR"
$PYTHON -m venv "$VENV_DIR"
fi

# shellcheck source=/dev/null
source "$VENV_DIR/bin/activate"
$PYTHON -m pip install --upgrade pip setuptools
$PYTHON -m pip install --upgrade build

# If you use setuptools-scm, ensure it's present for version derivation
if grep -qiE 'setuptools[-_]scm' pyproject.toml; then
$PYTHON -m pip install --upgrade setuptools-scm
fi

# --- Make builds more reproducible (optional but harmless) ---
export PYTHONHASHSEED=0
export SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git log -1 --pretty=%ct 2>/dev/null || date +%s)}"

# --- Build sdist and wheel ---
echo "==> Running python -m build"
$PYTHON -m build --sdist --wheel --outdir "$OUTDIR" $EXTRA_BUILD_ARGS

# --- List outputs ---
echo "==> Produced artifacts:"
ls -lh "$OUTDIR"

deactivate
echo "==> Build complete."
3 changes: 2 additions & 1 deletion tools/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
TEMPLATES_DIR = PROJECT_DIR.joinpath("../templates")

# Generated documents
GENERATED_DIR = PROJECT_DIR.joinpath("../generated")
GENERATED_DIR = PROJECT_DIR.joinpath("../site")
GENERATED_DOCS_DIR = GENERATED_DIR.joinpath("/docs")

XSD_FILE_PATH = PROJECT_DIR.joinpath("../xsd/xsd/NeTEx_publication.xsd")
24 changes: 21 additions & 3 deletions tools/expand_docs/expand_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
import shutil
import argparse
import re
from abc import ABC

from setuptools import Command

from tools.configuration import DOCS_DIR, GENERATED_DOCS_DIR


def copy_media_folder(input_folder, output_folder):
"""Copy media folder from input to output."""
Expand Down Expand Up @@ -73,8 +79,8 @@ def process_markdown_file(input_path, output_path, base_folder):

def main():
parser = argparse.ArgumentParser(description='Expand documentation by including examples and tables.')
parser.add_argument('--docs', required=True, help='Input documentation folder')
parser.add_argument('--out', required=True, help='Output folder')
parser.add_argument('--docs', default=DOCS_DIR, help=f"Input documentation folder (default = {DOCS_DIR})")
parser.add_argument('--out', default=GENERATED_DOCS_DIR, help=f"Output folder (default = {GENERATED_DOCS_DIR})")
args = parser.parse_args()

# Create output folder if it doesn't exist
Expand All @@ -93,4 +99,16 @@ def main():
print(f"Documentation expanded successfully to {args.out}")

if __name__ == '__main__':
main()
main()

class ExpandDocs(Command, ABC):
"""Setuptools plugin for the project build."""

def run(self) -> None:
"""
Execute the actions intended by the command.
(Side effects **SHOULD** only take place when :meth:`run` is executed,
for example, creating new files or writing to the terminal output).
"""
main()
pass
Loading