Apache Airflow Provider(s)
git
Versions of Apache Airflow Providers
apache-airflow-providers-git==0.5.0
Apache Airflow version
3.2.2
Operating System
Debian GNU/Linux 12 (bookworm) — apache/airflow:3.2.2-python3.10 image
Deployment
Official Apache Airflow Helm Chart
Deployment details
Chart 1.22.0, KubernetesExecutor. GitDagBundle configured with a git connection using GitHub App auth (github_app_id, github_installation_id, key_file in extras, HTTPS host, no password).
What happened
Every GitDagBundle clone/fetch that uses GitHub App authentication fails on Linux:
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
cmdline: git clone -v --bare -- https://github.com/<org>/<repo>.git /tmp/airflow/dag_bundles/<bundle>/bare
stderr: 'Cloning into bare repository '/tmp/airflow/dag_bundles/<bundle>/bare'...
fatal: cannot exec '/tmp/tmp64wabz1p.sh': Text file busy
fatal: could not read Username for 'https://github.com': terminal prompts disabled
'
The token exchange itself succeeds (Successfully obtained GitHub App installation access token is logged). The failure is in GitHook._github_app_askpass_env (hooks/git.py, lines 246–294 in 0.5.0):
with tempfile.NamedTemporaryFile(mode="w", suffix=".sh", delete=True) as askpass_script:
askpass_script.write(...)
askpass_script.flush()
os.chmod(askpass_script.name, stat.S_IRWXU)
...
os.environ["GIT_ASKPASS"] = askpass_script.name
...
yield
The file object stays open for writing for the whole yield. Linux refuses to execve a file that has a writer open (ETXTBSY), so git cannot run the askpass helper, falls back to prompting, and fails because GIT_TERMINAL_PROMPT=0. For GitHub App auth the askpass helper is the only credential path (the token is not spliced into the URL, since _process_git_auth_url runs in __init__ before the token exists), so App auth cannot work on Linux at all. macOS does not enforce ETXTBSY, which is presumably why this passed local testing.
_passphrase_askpass_env (lines 317–350) has the same pattern for SSH_ASKPASS and should fail the same way when a passphrase is set.
What you think should happen instead
The askpass script should be closed before git is invoked. E.g. tempfile.mkstemp(), write, os.close(), chmod, and os.unlink() in finally (Python 3.12's delete_on_close=False would also do, but the provider supports 3.10). #64105 replaces this mechanism with a credential helper that takes the token from the environment; if that lands first it fixes this as a side effect.
How to reproduce
Minimal reproduction of the exec failure, run inside apache/airflow:3.2.2-python3.10:
import tempfile, os, stat, subprocess
with tempfile.NamedTemporaryFile(mode="w", suffix=".sh", delete=True) as f:
f.write("#!/bin/sh\necho hello\n"); f.flush(); os.chmod(f.name, stat.S_IRWXU)
subprocess.run([f.name]) # OSError: [Errno 26] Text file busy
f = tempfile.NamedTemporaryFile(mode="w", suffix=".sh", delete=False)
f.write("#!/bin/sh\necho hello\n"); f.close(); os.chmod(f.name, stat.S_IRWXU)
subprocess.run([f.name]) # prints hello
End to end: configure a GitDagBundle against a private GitHub repo with a git connection carrying github_app_id, github_installation_id and key_file extras on Linux; the first bare clone fails as above.
Anything else
Reproduced with git 2.39.5 (the version in the official image) and GitPython 3.1.x. Happens on every attempt, in both the DAG processor and KubernetesExecutor task pods.
Are you willing to submit PR?
Code of Conduct
Apache Airflow Provider(s)
git
Versions of Apache Airflow Providers
apache-airflow-providers-git==0.5.0
Apache Airflow version
3.2.2
Operating System
Debian GNU/Linux 12 (bookworm) —
apache/airflow:3.2.2-python3.10imageDeployment
Official Apache Airflow Helm Chart
Deployment details
Chart 1.22.0, KubernetesExecutor.
GitDagBundleconfigured with agitconnection using GitHub App auth (github_app_id,github_installation_id,key_filein extras, HTTPS host, no password).What happened
Every
GitDagBundleclone/fetch that uses GitHub App authentication fails on Linux:The token exchange itself succeeds (
Successfully obtained GitHub App installation access tokenis logged). The failure is inGitHook._github_app_askpass_env(hooks/git.py, lines 246–294 in 0.5.0):The file object stays open for writing for the whole
yield. Linux refuses toexecvea file that has a writer open (ETXTBSY), so git cannot run the askpass helper, falls back to prompting, and fails becauseGIT_TERMINAL_PROMPT=0. For GitHub App auth the askpass helper is the only credential path (the token is not spliced into the URL, since_process_git_auth_urlruns in__init__before the token exists), so App auth cannot work on Linux at all. macOS does not enforceETXTBSY, which is presumably why this passed local testing._passphrase_askpass_env(lines 317–350) has the same pattern forSSH_ASKPASSand should fail the same way when a passphrase is set.What you think should happen instead
The askpass script should be closed before git is invoked. E.g.
tempfile.mkstemp(), write,os.close(),chmod, andos.unlink()infinally(Python 3.12'sdelete_on_close=Falsewould also do, but the provider supports 3.10). #64105 replaces this mechanism with a credential helper that takes the token from the environment; if that lands first it fixes this as a side effect.How to reproduce
Minimal reproduction of the exec failure, run inside
apache/airflow:3.2.2-python3.10:End to end: configure a
GitDagBundleagainst a private GitHub repo with agitconnection carryinggithub_app_id,github_installation_idandkey_fileextras on Linux; the first bare clone fails as above.Anything else
Reproduced with git 2.39.5 (the version in the official image) and GitPython 3.1.x. Happens on every attempt, in both the DAG processor and KubernetesExecutor task pods.
Are you willing to submit PR?
Code of Conduct