etlrelay 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- etlrelay-0.1.0/.github/workflows/ci.yml +64 -0
- etlrelay-0.1.0/.github/workflows/publish-testpypi.yml +176 -0
- etlrelay-0.1.0/.github/workflows/release-pypi.yml +242 -0
- etlrelay-0.1.0/.gitignore +55 -0
- etlrelay-0.1.0/CHANGELOG.md +25 -0
- etlrelay-0.1.0/DOCS_SETUP.md +62 -0
- etlrelay-0.1.0/LICENSE +201 -0
- etlrelay-0.1.0/NOTICE +4 -0
- etlrelay-0.1.0/PKG-INFO +435 -0
- etlrelay-0.1.0/README.md +419 -0
- etlrelay-0.1.0/SECURITY.md +17 -0
- etlrelay-0.1.0/docs/api-reference.md +41 -0
- etlrelay-0.1.0/docs/class_diagrams/file_sink_class_diagram.md +265 -0
- etlrelay-0.1.0/docs/class_diagrams/file_source_class_diagram.md +251 -0
- etlrelay-0.1.0/docs/class_diagrams/local_filesystem_class_diagram.md +341 -0
- etlrelay-0.1.0/docs/class_diagrams/path_validator_class_diagram.md +171 -0
- etlrelay-0.1.0/docs/class_diagrams/pipeline_class_diagram.md +293 -0
- etlrelay-0.1.0/docs/class_diagrams/reader_and_writer_class_diagram.md +366 -0
- etlrelay-0.1.0/docs/concepts.md +32 -0
- etlrelay-0.1.0/docs/development/release-automation.md +99 -0
- etlrelay-0.1.0/docs/development.md +40 -0
- etlrelay-0.1.0/docs/etl_relay_v1_roadmap.md +266 -0
- etlrelay-0.1.0/docs/getting-started.md +73 -0
- etlrelay-0.1.0/docs/index.md +34 -0
- etlrelay-0.1.0/docs/security.md +15 -0
- etlrelay-0.1.0/examples/copy_csv.py +35 -0
- etlrelay-0.1.0/flake.lock +93 -0
- etlrelay-0.1.0/flake.nix +65 -0
- etlrelay-0.1.0/mkdocs.yml +62 -0
- etlrelay-0.1.0/pyproject.toml +75 -0
- etlrelay-0.1.0/release-automation.md +99 -0
- etlrelay-0.1.0/src/etlrelay/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/core/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/core/batch.py +30 -0
- etlrelay-0.1.0/src/etlrelay/core/processor.py +33 -0
- etlrelay-0.1.0/src/etlrelay/core/reader.py +36 -0
- etlrelay-0.1.0/src/etlrelay/core/sink.py +33 -0
- etlrelay-0.1.0/src/etlrelay/core/source.py +34 -0
- etlrelay-0.1.0/src/etlrelay/core/writer.py +34 -0
- etlrelay-0.1.0/src/etlrelay/pipeline/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/pipeline/pipeline.py +85 -0
- etlrelay-0.1.0/src/etlrelay/processors/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/readers/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/readers/csv.py +36 -0
- etlrelay-0.1.0/src/etlrelay/security/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/security/path.py +65 -0
- etlrelay-0.1.0/src/etlrelay/sinks/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/sinks/file.py +60 -0
- etlrelay-0.1.0/src/etlrelay/sources/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/sources/file.py +50 -0
- etlrelay-0.1.0/src/etlrelay/storage/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/storage/filesystem.py +55 -0
- etlrelay-0.1.0/src/etlrelay/storage/local.py +78 -0
- etlrelay-0.1.0/src/etlrelay/writers/__init__.py +0 -0
- etlrelay-0.1.0/src/etlrelay/writers/csv.py +37 -0
- etlrelay-0.1.0/tests/functional/test_csv_pipeline.py +44 -0
- etlrelay-0.1.0/tests/integration/sinks/__init__.py +0 -0
- etlrelay-0.1.0/tests/integration/sinks/test_file.py +53 -0
- etlrelay-0.1.0/tests/integration/sources/__init__.py +0 -0
- etlrelay-0.1.0/tests/integration/sources/test_file.py +37 -0
- etlrelay-0.1.0/tests/unit/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/core/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/core/test_batch.py +26 -0
- etlrelay-0.1.0/tests/unit/core/test_processor.py +30 -0
- etlrelay-0.1.0/tests/unit/core/test_sink.py +14 -0
- etlrelay-0.1.0/tests/unit/core/test_source.py +21 -0
- etlrelay-0.1.0/tests/unit/pipeline/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/pipeline/test_pipeline.py +205 -0
- etlrelay-0.1.0/tests/unit/readers/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/readers/test_csv.py +44 -0
- etlrelay-0.1.0/tests/unit/security/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/security/test_path.py +340 -0
- etlrelay-0.1.0/tests/unit/sinks/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/sinks/test_file.py +150 -0
- etlrelay-0.1.0/tests/unit/sources/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/sources/test_file.py +181 -0
- etlrelay-0.1.0/tests/unit/storage/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/storage/test_local.py +128 -0
- etlrelay-0.1.0/tests/unit/writers/__init__.py +0 -0
- etlrelay-0.1.0/tests/unit/writers/test_csv.py +71 -0
- etlrelay-0.1.0/uv.lock +1151 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- dev
|
|
7
|
+
- main
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- dev
|
|
11
|
+
- main
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
quality:
|
|
22
|
+
name: quality
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- name: Check out repository
|
|
26
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
27
|
+
|
|
28
|
+
- name: Set up uv
|
|
29
|
+
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: true
|
|
32
|
+
|
|
33
|
+
- name: Install Python
|
|
34
|
+
run: uv python install 3.12
|
|
35
|
+
|
|
36
|
+
- name: Install locked project and development dependencies
|
|
37
|
+
run: uv sync --locked --all-groups
|
|
38
|
+
|
|
39
|
+
- name: Run tests with branch coverage
|
|
40
|
+
run: >-
|
|
41
|
+
uv run pytest
|
|
42
|
+
--cov=etlrelay
|
|
43
|
+
--cov-branch
|
|
44
|
+
--cov-report=term-missing
|
|
45
|
+
--cov-report=xml
|
|
46
|
+
--cov-fail-under=90.1
|
|
47
|
+
|
|
48
|
+
- name: Check lint
|
|
49
|
+
run: uv run ruff check src tests
|
|
50
|
+
|
|
51
|
+
- name: Check formatting
|
|
52
|
+
run: uv run ruff format --check src tests
|
|
53
|
+
|
|
54
|
+
- name: Check types
|
|
55
|
+
run: uv run pyright
|
|
56
|
+
|
|
57
|
+
- name: Build documentation
|
|
58
|
+
run: uv run mkdocs build --strict
|
|
59
|
+
|
|
60
|
+
- name: Build source distribution and wheel
|
|
61
|
+
run: uv build
|
|
62
|
+
|
|
63
|
+
- name: Validate distribution metadata
|
|
64
|
+
run: uvx --from 'twine>=7,<8' twine check dist/*
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
name: Publish to TestPyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
name: verify and build
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
outputs:
|
|
14
|
+
version: ${{ steps.metadata.outputs.version }}
|
|
15
|
+
distribution: ${{ steps.metadata.outputs.distribution }}
|
|
16
|
+
steps:
|
|
17
|
+
- name: Check out repository
|
|
18
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
|
+
|
|
20
|
+
- name: Require the dev branch
|
|
21
|
+
shell: bash
|
|
22
|
+
run: |
|
|
23
|
+
if [[ "$GITHUB_REF_NAME" != "dev" ]]; then
|
|
24
|
+
echo "TestPyPI publishing must be run from the dev branch; selected: $GITHUB_REF_NAME" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
- name: Set up uv
|
|
29
|
+
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: true
|
|
32
|
+
|
|
33
|
+
- name: Install Python
|
|
34
|
+
run: uv python install 3.12
|
|
35
|
+
|
|
36
|
+
- name: Read package metadata
|
|
37
|
+
id: metadata
|
|
38
|
+
shell: bash
|
|
39
|
+
run: |
|
|
40
|
+
python - <<'PY' >> "$GITHUB_OUTPUT"
|
|
41
|
+
import os
|
|
42
|
+
import re
|
|
43
|
+
import tomllib
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
|
|
46
|
+
project = tomllib.loads(Path("pyproject.toml").read_text())["project"]
|
|
47
|
+
distribution = re.sub(r"[-_.]+", "-", project["name"]).lower()
|
|
48
|
+
if distribution != "etlrelay":
|
|
49
|
+
raise SystemExit(f"Expected distribution name 'etlrelay', got {project['name']!r}")
|
|
50
|
+
print(f"version={project['version']}")
|
|
51
|
+
print(f"distribution={distribution}")
|
|
52
|
+
PY
|
|
53
|
+
|
|
54
|
+
- name: Install locked project and development dependencies
|
|
55
|
+
run: uv sync --locked --all-groups
|
|
56
|
+
|
|
57
|
+
- name: Run tests with branch coverage
|
|
58
|
+
run: >-
|
|
59
|
+
uv run pytest
|
|
60
|
+
--cov=etlrelay
|
|
61
|
+
--cov-branch
|
|
62
|
+
--cov-report=term-missing
|
|
63
|
+
--cov-report=xml
|
|
64
|
+
--cov-fail-under=90.1
|
|
65
|
+
|
|
66
|
+
- name: Check lint
|
|
67
|
+
run: uv run ruff check src tests
|
|
68
|
+
|
|
69
|
+
- name: Check formatting
|
|
70
|
+
run: uv run ruff format --check src tests
|
|
71
|
+
|
|
72
|
+
- name: Check types
|
|
73
|
+
run: uv run pyright
|
|
74
|
+
|
|
75
|
+
- name: Build documentation
|
|
76
|
+
run: uv run mkdocs build --strict
|
|
77
|
+
|
|
78
|
+
- name: Build source distribution and wheel
|
|
79
|
+
run: uv build
|
|
80
|
+
|
|
81
|
+
- name: Validate distribution metadata
|
|
82
|
+
run: uvx --from 'twine>=7,<8' twine check dist/*
|
|
83
|
+
|
|
84
|
+
- name: Upload built distributions
|
|
85
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
86
|
+
with:
|
|
87
|
+
name: testpypi-distributions
|
|
88
|
+
path: dist/*
|
|
89
|
+
if-no-files-found: error
|
|
90
|
+
retention-days: 7
|
|
91
|
+
|
|
92
|
+
publish:
|
|
93
|
+
name: publish to TestPyPI
|
|
94
|
+
needs: build
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
environment:
|
|
97
|
+
name: testpypi
|
|
98
|
+
url: https://test.pypi.org/project/etlrelay/${{ needs.build.outputs.version }}/
|
|
99
|
+
permissions:
|
|
100
|
+
id-token: write
|
|
101
|
+
steps:
|
|
102
|
+
- name: Download built distributions
|
|
103
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
104
|
+
with:
|
|
105
|
+
name: testpypi-distributions
|
|
106
|
+
path: dist/
|
|
107
|
+
|
|
108
|
+
- name: Publish to TestPyPI
|
|
109
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
110
|
+
with:
|
|
111
|
+
repository-url: https://test.pypi.org/legacy/
|
|
112
|
+
|
|
113
|
+
smoke-test:
|
|
114
|
+
name: install and smoke-test TestPyPI package
|
|
115
|
+
needs: [build, publish]
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
steps:
|
|
118
|
+
- name: Check out source for runtime dependency metadata
|
|
119
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
120
|
+
|
|
121
|
+
- name: Set up uv
|
|
122
|
+
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
123
|
+
with:
|
|
124
|
+
enable-cache: true
|
|
125
|
+
|
|
126
|
+
- name: Install Python
|
|
127
|
+
run: uv python install 3.12
|
|
128
|
+
|
|
129
|
+
- name: Export runtime dependencies without the checkout package
|
|
130
|
+
run: >-
|
|
131
|
+
uv export
|
|
132
|
+
--no-dev
|
|
133
|
+
--no-default-groups
|
|
134
|
+
--no-emit-project
|
|
135
|
+
--format requirements.txt
|
|
136
|
+
--output-file /tmp/etlrelay-runtime-requirements.txt
|
|
137
|
+
|
|
138
|
+
- name: Create a clean environment and install runtime dependencies
|
|
139
|
+
run: |
|
|
140
|
+
uv venv --python 3.12 /tmp/etlrelay-smoke
|
|
141
|
+
uv pip install --python /tmp/etlrelay-smoke/bin/python \
|
|
142
|
+
-r /tmp/etlrelay-runtime-requirements.txt
|
|
143
|
+
|
|
144
|
+
- name: Wait for TestPyPI to index the release
|
|
145
|
+
env:
|
|
146
|
+
PACKAGE_VERSION: ${{ needs.build.outputs.version }}
|
|
147
|
+
shell: bash
|
|
148
|
+
run: |
|
|
149
|
+
for attempt in $(seq 1 24); do
|
|
150
|
+
if curl --fail --silent \
|
|
151
|
+
"https://test.pypi.org/pypi/etlrelay/${PACKAGE_VERSION}/json" \
|
|
152
|
+
>/dev/null; then
|
|
153
|
+
echo "TestPyPI has indexed etlrelay ${PACKAGE_VERSION}."
|
|
154
|
+
exit 0
|
|
155
|
+
fi
|
|
156
|
+
echo "Waiting for TestPyPI indexing (attempt ${attempt}/24)..."
|
|
157
|
+
sleep 5
|
|
158
|
+
done
|
|
159
|
+
echo "TestPyPI did not index etlrelay ${PACKAGE_VERSION} within two minutes." >&2
|
|
160
|
+
exit 1
|
|
161
|
+
|
|
162
|
+
- name: Install the published TestPyPI distribution
|
|
163
|
+
env:
|
|
164
|
+
PACKAGE_VERSION: ${{ needs.build.outputs.version }}
|
|
165
|
+
run: >-
|
|
166
|
+
uv pip install
|
|
167
|
+
--python /tmp/etlrelay-smoke/bin/python
|
|
168
|
+
--index-url https://test.pypi.org/simple/
|
|
169
|
+
--no-deps
|
|
170
|
+
"etlrelay==${PACKAGE_VERSION}"
|
|
171
|
+
|
|
172
|
+
- name: Smoke-test installed package outside the repository
|
|
173
|
+
run: |
|
|
174
|
+
cd /tmp
|
|
175
|
+
/tmp/etlrelay-smoke/bin/python -c \
|
|
176
|
+
"from etlrelay.core.batch import Batch; from etlrelay.pipeline.pipeline import Pipeline; print('ETLRelay installed-package imports succeeded')"
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
name: Publish release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
verify-build:
|
|
14
|
+
# Keep false while bootstrapping TestPyPI; set the repository variable to true
|
|
15
|
+
# only after the TestPyPI candidate has passed its smoke test.
|
|
16
|
+
if: github.ref == 'refs/heads/main' && vars.PYPI_RELEASE_ENABLED == 'true'
|
|
17
|
+
name: verify and build release
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
outputs:
|
|
20
|
+
version: ${{ steps.metadata.outputs.version }}
|
|
21
|
+
distribution: ${{ steps.metadata.outputs.distribution }}
|
|
22
|
+
already_published: ${{ steps.pypi-check.outputs.already_published }}
|
|
23
|
+
steps:
|
|
24
|
+
- name: Check out repository and tags
|
|
25
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
|
+
with:
|
|
27
|
+
fetch-depth: 0
|
|
28
|
+
|
|
29
|
+
- name: Set up uv
|
|
30
|
+
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
31
|
+
with:
|
|
32
|
+
enable-cache: true
|
|
33
|
+
|
|
34
|
+
- name: Install Python
|
|
35
|
+
run: uv python install 3.12
|
|
36
|
+
|
|
37
|
+
- name: Read package metadata
|
|
38
|
+
id: metadata
|
|
39
|
+
shell: bash
|
|
40
|
+
run: |
|
|
41
|
+
python - <<'PY' >> "$GITHUB_OUTPUT"
|
|
42
|
+
import re
|
|
43
|
+
import tomllib
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
|
|
46
|
+
project = tomllib.loads(Path("pyproject.toml").read_text())["project"]
|
|
47
|
+
distribution = re.sub(r"[-_.]+", "-", project["name"]).lower()
|
|
48
|
+
if distribution != "etlrelay":
|
|
49
|
+
raise SystemExit(f"Expected distribution name 'etlrelay', got {project['name']!r}")
|
|
50
|
+
print(f"version={project['version']}")
|
|
51
|
+
print(f"distribution={distribution}")
|
|
52
|
+
PY
|
|
53
|
+
|
|
54
|
+
- name: Check whether this exact version is already published
|
|
55
|
+
id: pypi-check
|
|
56
|
+
env:
|
|
57
|
+
PACKAGE_VERSION: ${{ steps.metadata.outputs.version }}
|
|
58
|
+
DISTRIBUTION: ${{ steps.metadata.outputs.distribution }}
|
|
59
|
+
shell: bash
|
|
60
|
+
run: |
|
|
61
|
+
status=$(curl --silent --show-error --output /dev/null \
|
|
62
|
+
--write-out '%{http_code}' \
|
|
63
|
+
"https://pypi.org/pypi/${DISTRIBUTION}/${PACKAGE_VERSION}/json")
|
|
64
|
+
|
|
65
|
+
if [[ "$status" == "200" ]]; then
|
|
66
|
+
echo "already_published=true" >> "$GITHUB_OUTPUT"
|
|
67
|
+
echo "${DISTRIBUTION} ${PACKAGE_VERSION} already exists on PyPI; publication will be skipped."
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [[ "$status" != "404" ]]; then
|
|
72
|
+
echo "Unexpected PyPI response HTTP ${status}; refusing to publish." >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
if git ls-remote --exit-code --tags origin "refs/tags/v${PACKAGE_VERSION}" >/dev/null 2>&1; then
|
|
77
|
+
echo "Release tag v${PACKAGE_VERSION} already exists, but that version was not found on PyPI." >&2
|
|
78
|
+
echo "Investigate this inconsistent release state before retrying." >&2
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
echo "already_published=false" >> "$GITHUB_OUTPUT"
|
|
83
|
+
echo "Version ${PACKAGE_VERSION} is not on PyPI; running release checks."
|
|
84
|
+
|
|
85
|
+
- name: Install locked project and development dependencies
|
|
86
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
87
|
+
run: uv sync --locked --all-groups
|
|
88
|
+
|
|
89
|
+
- name: Run tests with branch coverage
|
|
90
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
91
|
+
run: >-
|
|
92
|
+
uv run pytest
|
|
93
|
+
--cov=etlrelay
|
|
94
|
+
--cov-branch
|
|
95
|
+
--cov-report=term-missing
|
|
96
|
+
--cov-report=xml
|
|
97
|
+
--cov-fail-under=90.1
|
|
98
|
+
|
|
99
|
+
- name: Check lint
|
|
100
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
101
|
+
run: uv run ruff check src tests
|
|
102
|
+
|
|
103
|
+
- name: Check formatting
|
|
104
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
105
|
+
run: uv run ruff format --check src tests
|
|
106
|
+
|
|
107
|
+
- name: Check types
|
|
108
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
109
|
+
run: uv run pyright
|
|
110
|
+
|
|
111
|
+
- name: Build documentation
|
|
112
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
113
|
+
run: uv run mkdocs build --strict
|
|
114
|
+
|
|
115
|
+
- name: Build source distribution and wheel
|
|
116
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
117
|
+
run: uv build
|
|
118
|
+
|
|
119
|
+
- name: Validate distribution metadata
|
|
120
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
121
|
+
run: uvx --from 'twine>=7,<8' twine check dist/*
|
|
122
|
+
|
|
123
|
+
- name: Upload built distributions
|
|
124
|
+
if: steps.pypi-check.outputs.already_published == 'false'
|
|
125
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
126
|
+
with:
|
|
127
|
+
name: pypi-distributions
|
|
128
|
+
path: dist/*
|
|
129
|
+
if-no-files-found: error
|
|
130
|
+
retention-days: 7
|
|
131
|
+
|
|
132
|
+
publish:
|
|
133
|
+
name: publish to PyPI
|
|
134
|
+
needs: verify-build
|
|
135
|
+
if: needs.verify-build.outputs.already_published == 'false'
|
|
136
|
+
runs-on: ubuntu-latest
|
|
137
|
+
environment:
|
|
138
|
+
name: pypi
|
|
139
|
+
url: https://pypi.org/project/etlrelay/${{ needs.verify-build.outputs.version }}/
|
|
140
|
+
permissions:
|
|
141
|
+
id-token: write
|
|
142
|
+
steps:
|
|
143
|
+
- name: Download verified distributions
|
|
144
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
145
|
+
with:
|
|
146
|
+
name: pypi-distributions
|
|
147
|
+
path: dist/
|
|
148
|
+
|
|
149
|
+
- name: Publish to PyPI
|
|
150
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
151
|
+
|
|
152
|
+
publish-versioned-docs:
|
|
153
|
+
name: publish versioned documentation
|
|
154
|
+
needs: [verify-build, publish]
|
|
155
|
+
# Only make a docs snapshot after a new package version has actually reached PyPI.
|
|
156
|
+
if: ${{ needs.verify-build.outputs.already_published == 'false' && needs.publish.result == 'success' }}
|
|
157
|
+
runs-on: ubuntu-latest
|
|
158
|
+
permissions:
|
|
159
|
+
contents: write
|
|
160
|
+
steps:
|
|
161
|
+
- name: Check out the released source
|
|
162
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
163
|
+
with:
|
|
164
|
+
fetch-depth: 0
|
|
165
|
+
|
|
166
|
+
- name: Set up uv
|
|
167
|
+
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
168
|
+
with:
|
|
169
|
+
enable-cache: true
|
|
170
|
+
|
|
171
|
+
- name: Install Python
|
|
172
|
+
run: uv python install 3.12
|
|
173
|
+
|
|
174
|
+
- name: Install documentation and project dependencies
|
|
175
|
+
run: uv sync --locked --all-groups
|
|
176
|
+
|
|
177
|
+
- name: Configure Git identity for mike
|
|
178
|
+
run: |
|
|
179
|
+
git config user.name "github-actions[bot]"
|
|
180
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
181
|
+
|
|
182
|
+
- name: Publish the version and update the latest alias
|
|
183
|
+
env:
|
|
184
|
+
RELEASE_VERSION: ${{ needs.verify-build.outputs.version }}
|
|
185
|
+
run: |
|
|
186
|
+
uv run mike deploy --push --update-aliases "$RELEASE_VERSION" latest
|
|
187
|
+
uv run mike set-default --push latest
|
|
188
|
+
|
|
189
|
+
deploy-versioned-docs-to-github-pages:
|
|
190
|
+
name: deploy documentation to GitHub Pages
|
|
191
|
+
needs: publish-versioned-docs
|
|
192
|
+
runs-on: ubuntu-latest
|
|
193
|
+
permissions:
|
|
194
|
+
contents: read
|
|
195
|
+
pages: write
|
|
196
|
+
id-token: write
|
|
197
|
+
environment:
|
|
198
|
+
name: github-pages
|
|
199
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
200
|
+
steps:
|
|
201
|
+
- name: Configure GitHub Pages
|
|
202
|
+
uses: actions/configure-pages@v5
|
|
203
|
+
|
|
204
|
+
- name: Check out the mike-generated site
|
|
205
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
206
|
+
with:
|
|
207
|
+
ref: gh-pages
|
|
208
|
+
path: gh-pages
|
|
209
|
+
persist-credentials: false
|
|
210
|
+
|
|
211
|
+
- name: Prepare Pages artifact without Git metadata
|
|
212
|
+
run: |
|
|
213
|
+
mkdir site-artifact
|
|
214
|
+
rsync -a --exclude='.git' gh-pages/ site-artifact/
|
|
215
|
+
|
|
216
|
+
- name: Upload Pages artifact
|
|
217
|
+
uses: actions/upload-pages-artifact@v4
|
|
218
|
+
with:
|
|
219
|
+
path: site-artifact/
|
|
220
|
+
|
|
221
|
+
- name: Deploy to GitHub Pages
|
|
222
|
+
id: deployment
|
|
223
|
+
uses: actions/deploy-pages@v4
|
|
224
|
+
|
|
225
|
+
create-github-release:
|
|
226
|
+
name: create GitHub release
|
|
227
|
+
needs: [verify-build, publish]
|
|
228
|
+
if: needs.verify-build.outputs.already_published == 'false' && needs.publish.result == 'success'
|
|
229
|
+
runs-on: ubuntu-latest
|
|
230
|
+
permissions:
|
|
231
|
+
contents: write
|
|
232
|
+
steps:
|
|
233
|
+
- name: Create tag and GitHub release
|
|
234
|
+
env:
|
|
235
|
+
GH_TOKEN: ${{ github.token }}
|
|
236
|
+
PACKAGE_VERSION: ${{ needs.verify-build.outputs.version }}
|
|
237
|
+
run: |
|
|
238
|
+
gh release create "v${PACKAGE_VERSION}" \
|
|
239
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
240
|
+
--target "$GITHUB_SHA" \
|
|
241
|
+
--title "ETLRelay ${PACKAGE_VERSION}" \
|
|
242
|
+
--generate-notes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Python bytecode and caches
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Virtual environments
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
env/
|
|
10
|
+
ENV/
|
|
11
|
+
|
|
12
|
+
# Python packaging / build artifacts
|
|
13
|
+
build/
|
|
14
|
+
dist/
|
|
15
|
+
*.egg-info/
|
|
16
|
+
.eggs/
|
|
17
|
+
|
|
18
|
+
# Test and coverage artifacts
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
.coverage.*
|
|
22
|
+
htmlcov/
|
|
23
|
+
coverage.xml
|
|
24
|
+
|
|
25
|
+
# Type checking / linting caches
|
|
26
|
+
.pyright/
|
|
27
|
+
.ruff_cache/
|
|
28
|
+
|
|
29
|
+
# uv
|
|
30
|
+
.uv/
|
|
31
|
+
|
|
32
|
+
# IDEs and editors
|
|
33
|
+
.idea/
|
|
34
|
+
.vscode/
|
|
35
|
+
*.swp
|
|
36
|
+
*.swo
|
|
37
|
+
*~
|
|
38
|
+
|
|
39
|
+
# macOS
|
|
40
|
+
.DS_Store
|
|
41
|
+
|
|
42
|
+
# Nix
|
|
43
|
+
.direnv/
|
|
44
|
+
result
|
|
45
|
+
result-*
|
|
46
|
+
|
|
47
|
+
# Local environment / secrets
|
|
48
|
+
.env
|
|
49
|
+
.env.*
|
|
50
|
+
!.env.example
|
|
51
|
+
|
|
52
|
+
# Local ETLRelay test data
|
|
53
|
+
data/
|
|
54
|
+
|
|
55
|
+
site/
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to ETLRelay are documented here.
|
|
4
|
+
|
|
5
|
+
This project follows the principles of [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
Changes intended for the first public release are listed below. When preparing the release, move this content to a `0.1.0` section and add the actual release date.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Batch-oriented pipeline contracts for sources, processors, and sinks.
|
|
14
|
+
- Synchronous pipeline execution with processors applied in sequence.
|
|
15
|
+
- An Arrow-backed `Batch` representation.
|
|
16
|
+
- Local filesystem access through a stream-based `FileSystem` protocol and `LocalFileSystem` implementation.
|
|
17
|
+
- CSV reading and writing through separate reader and writer components.
|
|
18
|
+
- `FileSource` and `FileSink` composition of storage and format components.
|
|
19
|
+
- Path containment validation for local filesystem paths.
|
|
20
|
+
- Unit, integration, and functional tests for the initial local CSV pipeline.
|
|
21
|
+
|
|
22
|
+
### Notes
|
|
23
|
+
|
|
24
|
+
- This is an experimental pre-1.0 release. Public APIs may change between minor releases.
|
|
25
|
+
- The initial implementation focuses on local filesystem and CSV workflows. SQL, declarative YAML, CLI, Parquet, S3/HDFS, and generic API integrations are not part of this release unless separately implemented and tested.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# MkDocs setup instructions for ETLRelay
|
|
2
|
+
|
|
3
|
+
The original ETLRelay technology plan selected MkDocs for documentation, `uv` for Python dependency management, GitHub Actions for CI, and PyPI for distribution. MkDocs was selected in the plan but is not yet configured in the current documentation set. This package adds a minimal setup using Material for MkDocs for the theme and `mkdocstrings[python]` for API reference generation from source docstrings.
|
|
4
|
+
|
|
5
|
+
## 1. Add documentation dependencies
|
|
6
|
+
|
|
7
|
+
From the repository root, run:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv add --group docs "mkdocs>=1.6,<2" "mkdocs-material>=9,<10" "mkdocstrings[python]>=0.18,<1"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This should add a `docs` dependency group to `pyproject.toml` and update `uv.lock`. Keep these packages out of runtime dependencies.
|
|
14
|
+
|
|
15
|
+
Install the group:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv sync --locked --group docs
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 2. Add the supplied files
|
|
22
|
+
|
|
23
|
+
Copy `mkdocs.yml` and the `docs/` directory from this package into the repository root. Add `CHANGELOG.md`, `NOTICE`, and `SECURITY.md` at the repository root if they do not already exist. Merge rather than overwrite any existing project configuration.
|
|
24
|
+
|
|
25
|
+
## 3. Ignore generated output
|
|
26
|
+
|
|
27
|
+
Add this line to the repository `.gitignore` if it is not already present:
|
|
28
|
+
|
|
29
|
+
```gitignore
|
|
30
|
+
site/
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 4. Build and preview
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv run mkdocs serve
|
|
37
|
+
uv run mkdocs build --strict
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The API page references the expected `src/etlrelay` module layout. Review those import paths against the actual repository and fix any differences before considering the docs build complete.
|
|
41
|
+
|
|
42
|
+
## 5. Integrate with existing CI
|
|
43
|
+
|
|
44
|
+
Add this command to the existing GitHub Actions quality-check workflow after installing the `docs` dependency group:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv run mkdocs build --strict
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Do not create a duplicate CI workflow if an existing one already performs quality checks. A green strict build should be required for documentation changes.
|
|
51
|
+
|
|
52
|
+
## 6. Finish release documents
|
|
53
|
+
|
|
54
|
+
- `CHANGELOG.md`: move the initial-release entries under `[Unreleased]` to a `[0.1.0] - YYYY-MM-DD` heading with the actual release date when preparing the release tag.
|
|
55
|
+
- `NOTICE`: confirm the copyright holder and year, and ensure the notice is consistent with the existing `LICENSE` file.
|
|
56
|
+
- `SECURITY.md`: enable GitHub private vulnerability reporting before public release, or replace the reporting instructions with a verified private contact channel.
|
|
57
|
+
- Getting Started: run `examples/copy_csv.py` against the built and installed package. Correct the example if actual signatures differ.
|
|
58
|
+
- API reference: verify all paths listed in `docs/api-reference.md` exist in `src/etlrelay`.
|
|
59
|
+
|
|
60
|
+
## Hosting
|
|
61
|
+
|
|
62
|
+
MkDocs builds a static site in `site/`, which can be hosted on GitHub Pages or another static host. Hosting/deployment is intentionally not configured by this setup package because the repository URL, default branch, and GitHub Pages settings need to be confirmed in the actual repository. Add deployment after the strict local build passes.
|