appleboy / appleboy/ssh-action

entrypoint.sh: closing EOF delimiter never written to $GITHUB_OUTPUT when capture_stdout=true and remote command fails

Open Beginner friendly
#416 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Shell
Stars
6.2k
Forks
679
Avg merge
35m
Merged PRs (30d)
1

Description

## Summary

When `capture_stdout: true` and the remote command exits non-zero, `entrypoint.sh` dies (via `set -e` +
`pipefail`) **before** writing the closing `EOF` delimiter to `$GITHUB_OUTPUT`. The GitHub Actions
runner then fails the step with a confusing secondary error that masks the real remote command failure:

```
Error: Unable to process file command 'output' successfully.
Invalid value. Matching delimiter not found 'EOF'
```

## Root cause

`entrypoint.sh` (pinned `0ff4204d59e8e51228ff73bce53f80d53301dee2`, v1.2.5):

```bash
set -euo pipefail
...
if [[ "${INPUT_CAPTURE_STDOUT}" == 'true' ]]; then
echo 'stdout<> "${GITHUB_OUTPUT}"
"${TARGET}" "$@" | tee -a "${GITHUB_OUTPUT}"
echo 'EOF' >> "${GITHUB_OUTPUT}"
else
"${TARGET}" "$@"
fi
```

With `pipefail`, if `${TARGET}` (drone-ssh, running the user's remote command) exits non-zero, the
whole `TARGET | tee` pipeline returns non-zero. With `set -e` also active, the script exits right there
— the next line (`echo 'EOF' >> "${GITHUB_OUTPUT}"`) never runs. The output file ends up with the
opening `stdout<> "${GITHUB_OUTPUT}"
"$@" | tee -a "${GITHUB_OUTPUT}"
echo 'EOF' >> "${GITHUB_OUTPUT}"
}
fail_like_remote_command() { echo "some real error"; exit 1; }

GITHUB_OUTPUT=/tmp/mock_output.txt
: > "$GITHUB_OUTPUT"
run_entrypoint_snippet fail_like_remote_command
cat "$GITHUB_OUTPUT"
```

Output: `stdout<.outputs.stdout` being valid on the failure path
breaks silently (in our case, a diagnostics-extraction step that never runs with useful data).

## Suggested fix

Write the closing delimiter unconditionally, regardless of `${TARGET}`'s exit code — e.g. capture the
pipe's exit status via `${PIPESTATUS[0]}` instead of relying on `set -e`/`pipefail` to propagate it, and
explicitly `exit` with that status only *after* the closing `echo 'EOF' >> "${GITHUB_OUTPUT}"`:

```bash
if [[ "${INPUT_CAPTURE_STDOUT}" == 'true' ]]; then
echo 'stdout<> "${GITHUB_OUTPUT}"
set +e
"${TARGET}" "$@" | tee -a "${GITHUB_OUTPUT}"
status="${PIPESTATUS[0]}"
set -e
echo 'EOF' >> "${GITHUB_OUTPUT}"
exit "${status}"
else
"${TARGET}" "$@"
fi
```

Happy to open a PR with this if useful — wanted to confirm the root cause and preferred fix shape first.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in entrypoint.sh, focusing on the capture_stdout branch and its TARGET | tee pipeline under set -euo pipefail. Reproduce the failure with the isolated shell snippet, then verify that a failed remote command still writes the closing EOF delimiter to GITHUB_OUTPUT while preserving the command's non-zero status.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions, shell
Domain
ci-cd, devops
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.