1{ lib
2, stdenv
3, python
4, buildPythonPackage
5, fetchFromGitHub
6, alembic
7, argcomplete
8, attrs
9, blinker
10, cached-property
11, cattrs
12, clickclick
13, colorlog
14, configupdater
15, connexion
16, cron-descriptor
17, croniter
18, cryptography
19, dataclasses
20, deprecated
21, dill
22, flask
23, flask-login
24, flask-appbuilder
25, flask-caching
26, flask-session
27, flask-wtf
28, GitPython
29, graphviz
30, gunicorn
31, httpx
32, iso8601
33, importlib-resources
34, importlib-metadata
35, inflection
36, itsdangerous
37, jinja2
38, jsonschema
39, lazy-object-proxy
40, linkify-it-py
41, lockfile
42, markdown
43, markupsafe
44, marshmallow-oneofschema
45, mdit-py-plugins
46, numpy
47, openapi-spec-validator
48, pandas
49, pathspec
50, pendulum
51, psutil
52, pygments
53, pyjwt
54, python-daemon
55, python-dateutil
56, python-nvd3
57, python-slugify
58, python3-openid
59, pythonOlder
60, pyyaml
61, rich
62, setproctitle
63, sqlalchemy
64, sqlalchemy-jsonfield
65, swagger-ui-bundle
66, tabulate
67, tenacity
68, termcolor
69, typing-extensions
70, unicodecsv
71, werkzeug
72, pytestCheckHook
73, freezegun
74, mkYarnPackage
75, writeScript
76
77# Extra airflow providers to enable
78, enabledProviders ? []
79}:
80let
81 version = "2.4.1";
82
83 airflow-src = fetchFromGitHub rec {
84 owner = "apache";
85 repo = "airflow";
86 rev = "refs/tags/${version}";
87 # Required because the GitHub archive tarballs don't appear to include tests
88 leaveDotGit = true;
89 sha256 = "sha256-HpPL/ocV7hRhYXsjfXMYvlP83Vh15kXyjBgubsaqaE8=";
90 };
91
92 # airflow bundles a web interface, which is built using webpack by an undocumented shell script in airflow's source tree.
93 # This replicates this shell script, fixing bugs in yarn.lock and package.json
94
95 airflow-frontend = mkYarnPackage {
96 name = "airflow-frontend";
97
98 src = "${airflow-src}/airflow/www";
99 packageJSON = ./package.json;
100 yarnLock = ./yarn.lock;
101 yarnNix = ./yarn.nix;
102
103 distPhase = "true";
104
105 # The webpack license plugin tries to create /licenses when given the
106 # original relative path
107 postPatch = ''
108 sed -i 's!../../../../licenses/LICENSES-ui.txt!licenses/LICENSES-ui.txt!' webpack.config.js
109 '';
110
111 configurePhase = ''
112 cp -r $node_modules node_modules
113 '';
114
115 buildPhase = ''
116 yarn --offline build
117 find package.json yarn.lock static/css static/js -type f | sort | xargs md5sum > static/dist/sum.md5
118 '';
119
120 installPhase = ''
121 mkdir -p $out/static/
122 cp -r static/dist $out/static
123 '';
124 };
125
126 # Import generated file with metadata for provider dependencies and imports.
127 # Enable additional providers using enabledProviders above.
128 providers = import ./providers.nix;
129 getProviderDeps = provider: map (dep: python.pkgs.${dep}) providers.${provider}.deps;
130 getProviderImports = provider: providers.${provider}.imports;
131 providerDependencies = lib.concatMap getProviderDeps enabledProviders;
132 providerImports = lib.concatMap getProviderImports enabledProviders;
133in
134buildPythonPackage rec {
135 pname = "apache-airflow";
136 inherit version;
137 src = airflow-src;
138
139 disabled = pythonOlder "3.6";
140
141 propagatedBuildInputs = [
142 alembic
143 argcomplete
144 attrs
145 blinker
146 cached-property
147 cattrs
148 clickclick
149 colorlog
150 configupdater
151 connexion
152 cron-descriptor
153 croniter
154 cryptography
155 deprecated
156 dill
157 flask
158 flask-appbuilder
159 flask-caching
160 flask-session
161 flask-wtf
162 flask-login
163 GitPython
164 graphviz
165 gunicorn
166 httpx
167 iso8601
168 importlib-resources
169 inflection
170 itsdangerous
171 jinja2
172 jsonschema
173 lazy-object-proxy
174 linkify-it-py
175 lockfile
176 markdown
177 markupsafe
178 marshmallow-oneofschema
179 mdit-py-plugins
180 numpy
181 openapi-spec-validator
182 pandas
183 pathspec
184 pendulum
185 psutil
186 pygments
187 pyjwt
188 python-daemon
189 python-dateutil
190 python-nvd3
191 python-slugify
192 python3-openid
193 pyyaml
194 rich
195 setproctitle
196 sqlalchemy
197 sqlalchemy-jsonfield
198 swagger-ui-bundle
199 tabulate
200 tenacity
201 termcolor
202 typing-extensions
203 unicodecsv
204 werkzeug
205 ] ++ lib.optionals (pythonOlder "3.7") [
206 dataclasses
207 ] ++ lib.optionals (pythonOlder "3.9") [
208 importlib-metadata
209 ] ++ providerDependencies;
210
211 buildInputs = [
212 airflow-frontend
213 ];
214
215 checkInputs = [
216 freezegun
217 pytestCheckHook
218 ];
219
220 # By default, source code of providers is included but unusable due to missing
221 # transitive dependencies. To enable a provider, add it to extraProviders
222 # above
223 INSTALL_PROVIDERS_FROM_SOURCES = "true";
224
225 postPatch = ''
226 substituteInPlace setup.cfg \
227 --replace "colorlog>=4.0.2, <5.0" "colorlog" \
228 --replace "flask-login>=0.6.2" "flask-login" \
229 --replace "pathspec~=0.9.0" "pathspec"
230 '' + lib.optionalString stdenv.isDarwin ''
231 # Fix failing test on Hydra
232 substituteInPlace airflow/utils/db.py \
233 --replace "/tmp/sqlite_default.db" "$TMPDIR/sqlite_default.db"
234 '';
235
236 # allow for gunicorn processes to have access to Python packages
237 makeWrapperArgs = [
238 "--prefix PYTHONPATH : $PYTHONPATH"
239 ];
240
241 postInstall = ''
242 cp -rv ${airflow-frontend}/static/dist $out/lib/${python.libPrefix}/site-packages/airflow/www/static
243 # Needed for pythonImportsCheck below
244 export HOME=$(mktemp -d)
245 '';
246
247 pythonImportsCheck = [
248 "airflow"
249 ] ++ providerImports;
250
251 preCheck = ''
252 export AIRFLOW_HOME=$HOME
253 export AIRFLOW__CORE__UNIT_TEST_MODE=True
254 export AIRFLOW_DB="$HOME/airflow.db"
255 export PATH=$PATH:$out/bin
256
257 airflow version
258 airflow db init
259 airflow db reset -y
260 '';
261
262 pytestFlagsArray = [
263 "tests/core/test_core.py"
264 ];
265
266 disabledTests = lib.optionals stdenv.isDarwin [
267 "bash_operator_kill" # psutil.AccessDenied
268 ];
269
270 # Updates yarn.lock and package.json
271 passthru.updateScript = writeScript "update.sh" ''
272 #!/usr/bin/env nix-shell
273 #!nix-shell -i bash -p common-updater-scripts curl pcre "python3.withPackages (ps: with ps; [ pyyaml ])" yarn2nix
274
275 set -euo pipefail
276
277 # Get new version
278 new_version="$(curl -s https://airflow.apache.org/docs/apache-airflow/stable/release_notes.html |
279 pcregrep -o1 'Airflow ([0-9.]+).' | head -1)"
280 update-source-version ${pname} "$new_version"
281
282 # Update frontend
283 cd ./pkgs/development/python-modules/apache-airflow
284 curl -O https://raw.githubusercontent.com/apache/airflow/$new_version/airflow/www/yarn.lock
285 curl -O https://raw.githubusercontent.com/apache/airflow/$new_version/airflow/www/package.json
286 # Note: for 2.3.4 a manual change was needed to get a fully resolved URL for
287 # caniuse-lite@1.0.30001312 (with the sha after the #). The error from yarn
288 # was 'Can't make a request in offline mode' from yarn. Corrected install by
289 # manually running yarn add caniuse-lite@1.0.30001312 and copying the
290 # requisite section from the generated yarn.lock.
291 yarn2nix > yarn.nix
292
293 # update provider dependencies
294 ./update-providers.py
295 '';
296
297 # Note on testing the web UI:
298 # You can (manually) test the web UI as follows:
299 #
300 # nix shell .#python3Packages.apache-airflow
301 # airflow db init
302 # airflow reset -y # WARNING: this will wipe any existing db state you might have!
303 # airflow standalone
304 #
305 # Then navigate to the localhost URL using the credentials printed, try
306 # triggering the 'example_bash_operator' and 'example_bash_operator' DAGs and
307 # see if they report success.
308
309 meta = with lib; {
310 description = "Programmatically author, schedule and monitor data pipelines";
311 homepage = "https://airflow.apache.org/";
312 license = licenses.asl20;
313 maintainers = with maintainers; [ bhipple gbpdt ingenieroariel ];
314 };
315}