Skip to content

GitDagBundle GitHub App auth fails on Linux: askpass script exec'd while still open for writing (ETXTBSY) #73425

Description

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?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions