From 10f4172349c531df71dfa9c2928f5810351ba2b5 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 28 Feb 2025 13:59:54 +0100 Subject: [PATCH] Shell Format Workflow (#2400) --- .github/workflows/script_format.yml | 243 ++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 .github/workflows/script_format.yml diff --git a/.github/workflows/script_format.yml b/.github/workflows/script_format.yml new file mode 100644 index 000000000..a86a512b4 --- /dev/null +++ b/.github/workflows/script_format.yml @@ -0,0 +1,243 @@ +name: Script Format Check +permissions: + pull-requests: write +on: + pull_request_target: + branches: + - main + paths: + - 'install/*.sh' + - 'ct/*.sh' + +jobs: + run-install-script: + runs-on: pvenode + steps: + - name: Checkout PR branch (supports forks) + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + fetch-depth: 0 + + - name: Add Git safe directory + run: | + git config --global --add safe.directory /__w/ProxmoxVE/ProxmoxVE + + - name: Set up GH_TOKEN + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "GH_TOKEN=${GH_TOKEN}" >> $GITHUB_ENV + + - name: Get Changed Files + run: | + CHANGED_FILES=$(gh pr diff ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --name-only) + CHANGED_FILES=$(echo "$CHANGED_FILES" | tr '\n' ' ') + echo "Changed files: $CHANGED_FILES" + echo "SCRIPT=$CHANGED_FILES" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check scripts + id: run-install + continue-on-error: true + run: | + for FILE in ${{ env.SCRIPT }}; do + STRIPPED_NAME=$(basename "$FILE" | sed 's/-install//' | sed 's/\.sh$//') + echo "Running Test for: $STRIPPED_NAME" + FILE_STRIPPED="${FILE##*/}" + LOG_FILE="result_$FILE_STRIPPED.log" + + if [[ $FILE =~ ^ct/.*\.sh$ ]]; then + + FIRST_LINE=$(sed -n '1p' "$FILE") + [[ "$FIRST_LINE" != "#!/usr/bin/env bash" ]] && echo "Line 1 was $FIRST_LINE | Should be: #!/usr/bin/env bash" >> "$LOG_FILE" + SECOND_LINE=$(sed -n '2p' "$FILE") + [[ "$SECOND_LINE" != "source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)" ]] && + echo "Line 2 was $SECOND_LINE | Should be: source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)" >> "$LOG_FILE" + THIRD_LINE=$(sed -n '3p' "$FILE") + if ! [[ "$THIRD_LINE" =~ ^#\ Copyright\ \(c\)\ [0-9]{4}-[0-9]{4}\ community-scripts\ ORG$ || "$THIRD_LINE" =~ ^Copyright\ \(c\)\ [0-9]{4}-[0-9]{4}\ tteck$ ]]; then + echo "Line 3 was $THIRD_LINE | Should be: # Copyright (c) 2021-2025 community-scripts ORG" >> "$LOG_FILE" + fi + + EXPECTED_AUTHOR="# Author:" + EXPECTED_LICENSE="# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE" + EXPECTED_SOURCE="# Source:" + EXPECTED_EMPTY="" + + for i in {4..7}; do + LINE=$(sed -n "${i}p" "$FILE") + + case $i in + 4) + [[ $LINE == $EXPECTED_AUTHOR* ]] || printf "Line %d was: '%s' | Should start with: '%s'\n" "$i" "$LINE" "$EXPECTED_AUTHOR" >> $LOG_FILE + ;; + 5) + [[ "$LINE" == "$EXPECTED_LICENSE" ]] || printf "Line %d was: '%s' | Should be: '%s'\n" "$i" "$LINE" "$EXPECTED_LICENSE" >> $LOG_FILE + ;; + 6) + [[ $LINE == $EXPECTED_SOURCE* ]] || printf "Line %d was: '%s' | Should start with: '%s'\n" "$i" "$LINE" "$EXPECTED_SOURCE" >> $LOG_FILE + ;; + 7) + [[ -z $LINE ]] || printf "Line %d was: '%s' | Should be empty\n" "$i" "$LINE" >> $LOG_FILE + ;; + esac + done + + + EXPECTED_PREFIXES=( + "APP=" + "var_tags=" + "var_cpu=" # Must be a number + "var_ram=" # Must be a number + "var_disk=" # Must be a number + "var_os=" # Must be debian, alpine, or ubuntu + "var_version=" + "var_unprivileged=" # Must be 0 or 1 + ) + + + for i in {8..15}; do + LINE=$(sed -n "${i}p" "$FILE") + INDEX=$((i - 8)) + + case $INDEX in + 2|3|4) # var_cpu, var_ram, var_disk (must be numbers) + if [[ "$LINE" =~ ^${EXPECTED_PREFIXES[$INDEX]}([0-9]+)$ ]]; then + continue # Valid + else + echo "Line $i was '$LINE' | Should be: '${EXPECTED_PREFIXES[$INDEX]}'" >> "$LOG_FILE" + fi + ;; + 5) # var_os (must be debian, alpine, or ubuntu) + if [[ "$LINE" =~ ^var_os=(debian|alpine|ubuntu)$ ]]; then + continue # Valid + else + echo "Line $i was '$LINE' | Should be: 'var_os=[debian|alpine|ubuntu]'" >> "$LOG_FILE" + fi + ;; + 7) # var_unprivileged (must be 0 or 1) + if [[ "$LINE" =~ ^var_unprivileged=[01]$ ]]; then + continue # Valid + else + echo "Line $i was '$LINE' | Should be: 'var_unprivileged=[0|1]'" >> "$LOG_FILE" + fi + ;; + *) # Other lines (must start with expected prefix) + if [[ "$LINE" == ${EXPECTED_PREFIXES[$INDEX]}* ]]; then + continue # Valid + else + echo "Line $i was '$LINE' | Should start with '${EXPECTED_PREFIXES[$INDEX]}'" >> "$LOG_FILE" + fi + ;; + esac + done + + for i in {16..20}; do + LINE=$(sed -n "${i}p" "$FILE") + EXPECTED=( + "header_info \"$APP\"" + "variables" + "color" + "catch_errors" + "function update_script() {" + ) + [[ "$LINE" != "${EXPECTED[$((i-16))]}" ]] && echo "Line $i was $LINE | Should be: ${EXPECTED[$((i-16))]}" >> "$LOG_FILE" + done + cat "$LOG_FILE" + elif [[ $FILE =~ ^install/.*-install\.sh$ ]]; then + + FIRST_LINE=$(sed -n '1p' "$FILE") + [[ "$FIRST_LINE" != "#!/usr/bin/env bash" ]] && echo "Line 1 was $FIRST_LINE | Should be: #!/usr/bin/env bash" >> "$LOG_FILE" + + SECOND_LINE=$(sed -n '2p' "$FILE") + [[ -n "$SECOND_LINE" ]] && echo "Line 2 should be empty" >> "$LOG_FILE" + + THIRD_LINE=$(sed -n '3p' "$FILE") + if ! [[ "$THIRD_LINE" =~ ^#\ Copyright\ \(c\)\ [0-9]{4}-[0-9]{4}\ community-scripts\ ORG$ || "$THIRD_LINE" =~ ^Copyright\ \(c\)\ [0-9]{4}-[0-9]{4}\ tteck$ ]]; then + echo "Line 3 was $THIRD_LINE | Should be: # Copyright (c) 2021-2025 community-scripts ORG" >> "$LOG_FILE" + fi + + EXPECTED_AUTHOR="# Author:" + EXPECTED_LICENSE="# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE" + EXPECTED_SOURCE="# Source:" + EXPECTED_EMPTY="" + + for i in {4..7}; do + LINE=$(sed -n "${i}p" "$FILE") + + case $i in + 4) + [[ $LINE == $EXPECTED_AUTHOR* ]] || printf "Line %d was: '%s' | Should start with: '%s'\n" "$i" "$LINE" "$EXPECTED_AUTHOR" >> $LOG_FILE + ;; + 5) + [[ "$LINE" == "$EXPECTED_LICENSE" ]] || printf "Line %d was: '%s' | Should be: '%s'\n" "$i" "$LINE" "$EXPECTED_LICENSE" >> $LOG_FILE + ;; + 6) + [[ $LINE == $EXPECTED_SOURCE* ]] || printf "Line %d was: '%s' | Should start with: '%s'\n" "$i" "$LINE" "$EXPECTED_SOURCE" >> $LOG_FILE + ;; + 7) + [[ -z $LINE ]] || printf "Line %d was: '%s' | Should be empty\n" "$i" "$LINE" >> $LOG_FILE + ;; + esac + done + + [[ "$(sed -n '8p' "$FILE")" != 'source /dev/stdin <<< "$FUNCTIONS_FILE_PATH"' ]] && echo 'Line 8 should be: source /dev/stdin <<< "$FUNCTIONS_FILE_PATH"' >> "$LOG_FILE" + + for i in {9..14}; do + LINE=$(sed -n "${i}p" "$FILE") + EXPECTED=( + "color" + "verb_ip6" + "catch_errors" + "setting_up_container" + "network_check" + "update_os" + ) + [[ "$LINE" != "${EXPECTED[$((i-9))]}" ]] && echo "Line $i was $LINE | Should be: ${EXPECTED[$((i-9))]}" >> "$LOG_FILE" + done + + [[ -n "$(sed -n '15p' "$FILE")" ]] && echo "Line 15 should be empty" >> "$LOG_FILE" + [[ "$(sed -n '16p' "$FILE")" != 'msg_info "Installing Dependencies"' ]] && echo 'Line 16 should be: msg_info "Installing Dependencies"' >> "$LOG_FILE" + + LAST_3_LINES=$(tail -n 3 "$FILE") + [[ "$LAST_3_LINES" != *"$STD apt-get -y autoremove"* ]] && echo 'Third to last line should be: $STD apt-get -y autoremove' >> "$LOG_FILE" + [[ "$LAST_3_LINES" != *"$STD apt-get -y autoclean"* ]] && echo 'Second to last line should be: $STD apt-get -y clean' >> "$LOG_FILE" + [[ "$LAST_3_LINES" != *'msg_ok "Cleaned"'* ]] && echo 'Last line should be: msg_ok "Cleaned"' >> "$LOG_FILE" + cat "$LOG_FILE" + fi + + done + + + - name: Post error comments + run: | + ERROR="false" + for FILE in ${{ env.SCRIPT }}; do + FILE_STRIPPED="${FILE##*/}" + LOG_FILE="result_$FILE_STRIPPED.log" + echo $LOG_FILE + if [[ ! -f $LOG_FILE ]]; then + continue + fi + ERROR_MSG=$(cat $LOG_FILE) + + if [ -n "$ERROR_MSG" ]; then + echo "Posting error message for $FILE" + echo ${ERROR_MSG} + gh pr comment ${{ github.event.pull_request.number }} \ + --repo ${{ github.repository }} \ + --body ":warning: The script _**$FILE**_ has the following formatting errors:
${ERROR_MSG}
" + + + ERROR="true" + fi + done + echo "ERROR=$ERROR" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Fail if error + if: ${{ env.ERROR == 'true' }} + run: exit 1