mirror of
https://hub.psychoinformatics.de/actions/process-upload.git
synced 2026-10-06 17:06:18 +00:00
237 lines
6.6 KiB
YAML
237 lines
6.6 KiB
YAML
name: Process upload
|
|
description: Move or normalize one uploaded file, remove the source, and optionally commit and push the result
|
|
|
|
inputs:
|
|
source-dir:
|
|
description: Directory containing pending uploads
|
|
required: false
|
|
default: ingest
|
|
|
|
file-pattern:
|
|
description: Filename pattern passed to find, such as *.csv or *.xlsx
|
|
required: false
|
|
default: "*"
|
|
|
|
source-file:
|
|
description: >
|
|
Optional exact repository-relative source path. If omitted, exactly one
|
|
matching file must be present directly under source-dir.
|
|
required: false
|
|
default: ""
|
|
|
|
target-file:
|
|
description: Repository-relative destination path
|
|
required: true
|
|
|
|
fetch-command:
|
|
description: >
|
|
Optional shell command to materialize INPUT_FILE before processing.
|
|
Useful for git-annex-backed files.
|
|
required: false
|
|
default: ""
|
|
|
|
normalizer:
|
|
description: >
|
|
Optional shell command. It must read INPUT_FILE and write OUTPUT_FILE.
|
|
If empty, the input is moved unchanged.
|
|
required: false
|
|
default: ""
|
|
|
|
commit-and-push:
|
|
description: Commit and push the resulting worktree changes
|
|
required: false
|
|
default: "true"
|
|
|
|
commit-message:
|
|
description: Commit subject
|
|
required: false
|
|
default: "chore: process uploaded file"
|
|
|
|
commit-user-name:
|
|
description: Git commit author name
|
|
required: false
|
|
default: "Workflow runner"
|
|
|
|
commit-user-email:
|
|
description: Optional Git commit author email
|
|
required: false
|
|
default: ""
|
|
|
|
outputs:
|
|
source-file:
|
|
description: The processed source path
|
|
value: ${{ steps.locate.outputs.source-file }}
|
|
|
|
target-file:
|
|
description: The generated target path
|
|
value: ${{ steps.locate.outputs.target-file }}
|
|
|
|
runs:
|
|
using: composite
|
|
|
|
steps:
|
|
- name: Locate upload
|
|
id: locate
|
|
shell: bash
|
|
env:
|
|
SOURCE_DIR: ${{ inputs.source-dir }}
|
|
FILE_PATTERN: ${{ inputs.file-pattern }}
|
|
SOURCE_FILE: ${{ inputs.source-file }}
|
|
TARGET_FILE: ${{ inputs.target-file }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if [[ "$SOURCE_DIR" = /* || "$TARGET_FILE" = /* ]]; then
|
|
echo "source-dir and target-file must be repository-relative paths." >&2
|
|
exit 1
|
|
fi
|
|
|
|
case "$SOURCE_DIR/$TARGET_FILE" in
|
|
*/../*|../*|*/..|..)
|
|
echo "Path traversal is not allowed." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [[ -n "$SOURCE_FILE" ]]; then
|
|
case "$SOURCE_FILE" in
|
|
/*|../*|*/../*|..)
|
|
echo "Invalid source-file path." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [[ ! -f "$SOURCE_FILE" && ! -L "$SOURCE_FILE" ]]; then
|
|
echo "Source file does not exist: $SOURCE_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
source="$SOURCE_FILE"
|
|
else
|
|
if [[ ! -d "$SOURCE_DIR" ]]; then
|
|
echo "No upload directory exists: $SOURCE_DIR"
|
|
exit 0
|
|
fi
|
|
|
|
mapfile -d '' files < <(
|
|
find -- "$SOURCE_DIR" \
|
|
-maxdepth 1 \
|
|
\( -type f -o -type l \) \
|
|
-name "$FILE_PATTERN" \
|
|
! -name '.gitkeep' \
|
|
! -name '.gitignore' \
|
|
-print0
|
|
)
|
|
|
|
case "${#files[@]}" in
|
|
0)
|
|
echo "No matching upload found in $SOURCE_DIR."
|
|
exit 0
|
|
;;
|
|
|
|
1)
|
|
source="${files[0]}"
|
|
;;
|
|
|
|
*)
|
|
echo "More than one upload was found in $SOURCE_DIR:" >&2
|
|
printf ' %q\n' "${files[@]}" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
if [[ "$source" == "$TARGET_FILE" ]]; then
|
|
echo "Source and target must be different." >&2
|
|
exit 1
|
|
fi
|
|
|
|
{
|
|
printf 'source-file=%s\n' "$source"
|
|
printf 'target-file=%s\n' "$TARGET_FILE"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Fetch content
|
|
if: ${{ inputs.fetch-command != '' && steps.locate.outputs.source-file != '' }}
|
|
shell: bash
|
|
env:
|
|
INPUT_FILE: ${{ steps.locate.outputs.source-file }}
|
|
OUTPUT_FILE: ${{ steps.locate.outputs.target-file }}
|
|
FETCH_COMMAND: ${{ inputs.fetch-command }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
# FETCH_COMMAND is workflow configuration. The uploaded filename is
|
|
# passed through the quoted INPUT_FILE environment variable.
|
|
bash -c "$FETCH_COMMAND"
|
|
|
|
- name: Move upload unchanged
|
|
if: ${{ inputs.normalizer == '' && steps.locate.outputs.source-file != '' }}
|
|
shell: bash
|
|
env:
|
|
INPUT_FILE: ${{ steps.locate.outputs.source-file }}
|
|
OUTPUT_FILE: ${{ steps.locate.outputs.target-file }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
mkdir -p -- "$(dirname -- "$OUTPUT_FILE")"
|
|
mv -f -- "$INPUT_FILE" "$OUTPUT_FILE"
|
|
|
|
- name: Normalize upload
|
|
if: ${{ inputs.normalizer != '' && steps.locate.outputs.source-file != '' }}
|
|
shell: bash
|
|
env:
|
|
INPUT_FILE: ${{ steps.locate.outputs.source-file }}
|
|
OUTPUT_FILE: ${{ steps.locate.outputs.target-file }}
|
|
NORMALIZER: ${{ inputs.normalizer }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
mkdir -p -- "$(dirname -- "$OUTPUT_FILE")"
|
|
rm -f -- "$OUTPUT_FILE"
|
|
|
|
# NORMALIZER is trusted workflow configuration. It must read
|
|
# "$INPUT_FILE" and write "$OUTPUT_FILE".
|
|
bash -c "$NORMALIZER"
|
|
|
|
if [[ ! -f "$OUTPUT_FILE" && ! -L "$OUTPUT_FILE" ]]; then
|
|
echo "Normalizer did not create $OUTPUT_FILE." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# The original upload is removed only after successful normalization.
|
|
rm -f -- "$INPUT_FILE"
|
|
|
|
- name: Commit and push changes
|
|
if: ${{ inputs.commit-and-push == 'true' && steps.locate.outputs.source-file != '' }}
|
|
shell: bash
|
|
env:
|
|
INPUT_FILE: ${{ steps.locate.outputs.source-file }}
|
|
COMMIT_MESSAGE: ${{ inputs.commit-message }}
|
|
COMMIT_USER_NAME: ${{ inputs.commit-user-name }}
|
|
COMMIT_USER_EMAIL: ${{ inputs.commit-user-email }}
|
|
ACTOR: ${{ forgejo.actor }}
|
|
SERVER_URL: ${{ forgejo.server_url }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
git add -A
|
|
|
|
if git diff --cached --quiet; then
|
|
echo "No changes to commit."
|
|
exit 0
|
|
fi
|
|
|
|
if [[ -z "$COMMIT_USER_EMAIL" ]]; then
|
|
server_host="${SERVER_URL#*://}"
|
|
COMMIT_USER_EMAIL="${ACTOR}@${server_host}"
|
|
fi
|
|
|
|
git config user.name "$COMMIT_USER_NAME"
|
|
git config user.email "$COMMIT_USER_EMAIL"
|
|
|
|
git commit \
|
|
-m "$COMMIT_MESSAGE" \
|
|
-m "source: $INPUT_FILE"
|
|
|
|
git push
|