diff --git a/.github/runner/docker/gh-runner-self.dockerfile b/.github/runner/docker/gh-runner-self.dockerfile
deleted file mode 100644
index e5ae072ab..000000000
--- a/.github/runner/docker/gh-runner-self.dockerfile
+++ /dev/null
@@ -1,68 +0,0 @@
-FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy as build
-
-ARG TARGETOS
-ARG TARGETARCH
-ARG DOCKER_VERSION=27.5.1
-ARG BUILDX_VERSION=0.20.1
-ARG RUNNER_ARCH="x64"
-
-RUN apt update -y && apt install sudo curl unzip -y
-
-WORKDIR /actions-runner
-
-RUN RUNNER_VERSION=$(curl -s https://api.github.com/repos/actions/runner/releases/latest | grep "tag_name" | head -n 1 | awk '{print substr($2, 3, length($2)-4)}') \
- && curl -f -L -o runner.tar.gz https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${RUNNER_ARCH}-${RUNNER_VERSION}.tar.gz \
- && tar xzf ./runner.tar.gz \
- && rm runner.tar.gz
-
-RUN RUNNER_CONTAINER_HOOKS_VERSION=$(curl -s https://api.github.com/repos/actions/runner-container-hooks/releases/latest | grep "tag_name" | head -n 1 | awk '{print substr($2, 3, length($2)-4)}') \
- && curl -f -L -o runner-container-hooks.zip https://github.com/actions/runner-container-hooks/releases/download/v${RUNNER_CONTAINER_HOOKS_VERSION}/actions-runner-hooks-k8s-${RUNNER_CONTAINER_HOOKS_VERSION}.zip \
- && unzip ./runner-container-hooks.zip -d ./k8s \
- && rm runner-container-hooks.zip
-
-RUN export RUNNER_ARCH=${TARGETARCH} \
- && if [ "$RUNNER_ARCH" = "amd64" ]; then export DOCKER_ARCH=x86_64 ; fi \
- && if [ "$RUNNER_ARCH" = "arm64" ]; then export DOCKER_ARCH=aarch64 ; fi \
- && curl -fLo docker.tgz https://download.docker.com/${TARGETOS}/static/stable/${DOCKER_ARCH}/docker-${DOCKER_VERSION}.tgz \
- && tar zxvf docker.tgz \
- && rm -rf docker.tgz \
- && mkdir -p /usr/local/lib/docker/cli-plugins \
- && curl -fLo /usr/local/lib/docker/cli-plugins/docker-buildx \
- "https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-${TARGETARCH}" \
- && chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
-
-FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy
-
-ENV DEBIAN_FRONTEND=noninteractive
-ENV RUNNER_MANUALLY_TRAP_SIG=1
-ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
-ENV ImageOS=ubuntu22
-
-RUN apt update -y \
- && apt install -y --no-install-recommends sudo lsb-release gpg-agent software-properties-common curl jq unzip \
- && rm -rf /var/lib/apt/lists/*
-
-RUN add-apt-repository ppa:git-core/ppa \
- && apt update -y \
- && apt install -y git \
- && rm -rf /var/lib/apt/lists/*
-
-RUN adduser --disabled-password --gecos "" --uid 1001 runner \
- && groupadd docker --gid 123 \
- && usermod -aG sudo runner \
- && usermod -aG docker runner \
- && echo "%sudo ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers \
- && echo "Defaults env_keep += \"DEBIAN_FRONTEND\"" >> /etc/sudoers
-
-# Install own dependencies in final image
-RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
- && apt-get install -y nodejs \
- && apt-get install -y gh jq git
-
-WORKDIR /home/runner
-
-COPY --chown=runner:docker --from=build /actions-runner .
-COPY --from=build /usr/local/lib/docker/cli-plugins/docker-buildx /usr/local/lib/docker/cli-plugins/docker-buildx
-RUN install -o root -g root -m 755 docker/* /usr/bin/ && rm -rf docker
-
-USER runner
diff --git a/.github/workflows/bak/close_template_issue.yml b/.github/workflows/bak/close_template_issue.yml
deleted file mode 100644
index b87923bc4..000000000
--- a/.github/workflows/bak/close_template_issue.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-name: Auto-Close Wrong Template Issues
-on:
- issues:
- types: [opened]
-
-jobs:
- close_tteck_issues:
- if: github.repository == 'community-scripts/ProxmoxVE'
- runs-on: ubuntu-latest
- steps:
- - name: Auto-close if wrong Template issue detected
- uses: actions/github-script@v7
- with:
- script: |
- const issue = context.payload.issue;
- const content = `${issue.title}\n${issue.body}`;
- const issueNumber = issue.number;
-
- // Regex patterns (case-insensitive, flexible versioning)
- const patterns = [
- /Template\s+debian-13-standard_[\d.]+-[\d]+_amd64\.tar\.zst\s*\[(online|local)\]/i,
- /Template\s+debian-13-standard_[\d.]+-[\d]+_amd64\.tar\.zst\s+is\s+missing\s+or\s+corrupted/i,
- /Container\s+creation\s+failed\.?\s+Checking\s+if\s+template\s+is\s+corrupted\s+or\s+incomplete/i,
- /Template\s+is\s+valid,\s+but\s+container\s+creation\s+still\s+failed/i,
- /exit\s+code\s+0:\s+while\s+executing\s+command\s+bash\s+-c\s+"\$?\(curl\s+-fsSL\s+https:\/\/raw\.githubusercontent\.com\/[\w/-]+\/create_lxc\.sh\)"/i
- ];
-
- const matched = patterns.some((regex) => regex.test(content));
-
- if (matched) {
- const message = "π Hello!\n\n" +
- "It looks like you are referencing a **container creation issue with a Debian 13 template** (e.g. `debian-13-standard_13.x-x_amd64.tar.zst`).\n\n" +
- "We receive many similar reports about this, and it's not related to the scripts themselves but to **a Proxmox base template bug**.\n\n" +
- "Please refer to [discussion #8126](https://github.com/community-scripts/ProxmoxVE/discussions/8126) for details.\n" +
- "If your issue persists after following the guidance there, feel free to reopen this issue.\n\n" +
- "_This issue was automatically closed by a bot._";
-
- await github.rest.issues.createComment({
- ...context.repo,
- issue_number: issueNumber,
- body: message
- });
-
- await github.rest.issues.addLabels({
- ...context.repo,
- issue_number: issueNumber,
- labels: ["not planned"]
- });
-
- await github.rest.issues.update({
- ...context.repo,
- issue_number: issueNumber,
- state: "closed"
- });
- }
diff --git a/.github/workflows/bak/crawl-versions.yaml b/.github/workflows/bak/crawl-versions.yaml
deleted file mode 100644
index 4f8e9a003..000000000
--- a/.github/workflows/bak/crawl-versions.yaml
+++ /dev/null
@@ -1,126 +0,0 @@
-name: Crawl Versions from newreleases.io
-
-on:
- workflow_dispatch:
- schedule:
- # Runs at 12:00 AM and 12:00 PM UTC
- - cron: "0 0,12 * * *"
-
-permissions:
- contents: write
- pull-requests: write
-
-jobs:
- crawl-versions:
- if: github.repository == 'community-scripts/ProxmoxVE'
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v2
- with:
- repository: community-scripts/ProxmoxVE
- ref: main
-
- - name: Generate a token
- id: generate-token
- uses: actions/create-github-app-token@v1
- with:
- app-id: ${{ vars.APP_ID }}
- private-key: ${{ secrets.APP_PRIVATE_KEY }}
-
- - name: Crawl from newreleases.io
- env:
- token: ${{ secrets.NEWRELEASES_TOKEN }}
- run: |
- page=1
- projects_file="project_json"
- output_file="frontend/public/json/versions.json"
-
- echo "[]" > $output_file
-
- while true; do
-
- echo "Start loop on page: $page"
-
- projects=$(curl -s -H "X-Key: $token" "https://api.newreleases.io/v1/projects?page=$page")
- total_pages=$(echo "$projects" | jq -r '.total_pages')
-
- if [ -z "$total_pages" ] || [ "$total_pages" -eq 0 ]; then
- echo "No pages available. Exiting."
- exit 1
- fi
- if [ $page == $total_pages ]; then
- break
- fi
-
- if [ -z "$projects" ] || ! echo "$projects" | jq -e '.projects' > /dev/null; then
- echo "No more projects or invalid response. Exiting."
- break
- fi
-
- echo "$projects" > "$projects_file"
-
- jq -r '.projects[] | "\(.id) \(.name)"' "$projects_file" | while read -r id name; do
- version=$(curl -s -H "X-Key: $token" "https://api.newreleases.io/v1/projects/$id/latest-release")
- version_data=$(echo "$version" | jq -r '.version // empty')
- date=$(echo "$version" | jq -r '.date // empty')
- if [ -n "$version_data" ]; then
- jq --arg name "$name" --arg version "$version_data" --arg date "$date" \
- '. += [{"name": $name, "version": $version, "date": $date}]' "$output_file" > "$output_file.tmp" && mv "$output_file.tmp" "$output_file"
- fi
- done
- ((page++))
- done
-
- - name: Commit JSON
- env:
- GH_TOKEN: ${{ steps.generate-token.outputs.token }}
- run: |
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- git config --global user.name "GitHub Actions[bot]"
- git checkout -b update_versions || git checkout update_versions
- git add frontend/public/json/versions.json
- if git diff --cached --quiet; then
- echo "No changes detected."
- echo "changed=false" >> "$GITHUB_ENV"
- exit 0
- else
- echo "Changes detected:"
- git diff --stat --cached
- echo "changed=true" >> "$GITHUB_ENV"
- fi
- git commit -m "Update versions.json"
- git push origin update_versions --force
- gh pr create --title "[Github Action] Update versions.json" --body "Update versions.json, crawled from newreleases.io" --base main --head update_versions --label "automated pr"
-
- - name: Approve pull request
- if: env.changed == 'true'
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- PR_NUMBER=$(gh pr list --head "update_versions" --json number --jq '.[].number')
- if [ -n "$PR_NUMBER" ]; then
- gh pr review $PR_NUMBER --approve
- fi
-
- - name: Approve pull request and merge
- if: env.changed == 'true'
- env:
- GH_TOKEN: ${{ secrets.PAT_AUTOMERGE }}
- run: |
- PR_NUMBER=$(gh pr list --head "update_versions" --json number --jq '.[].number')
- if [ -n "$PR_NUMBER" ]; then
- gh pr review $PR_NUMBER --approve
- gh pr merge $PR_NUMBER --squash --admin
- fi
-
- - name: Re-approve pull request after update
- if: env.changed == 'true'
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- PR_NUMBER=$(gh pr list --head "update_versions" --json number --jq '.[].number')
- if [ -n "$PR_NUMBER" ]; then
- gh pr review $PR_NUMBER --approve
- fi
diff --git a/.github/workflows/bak/script-test.yml b/.github/workflows/bak/script-test.yml
deleted file mode 100644
index eb53c366d..000000000
--- a/.github/workflows/bak/script-test.yml
+++ /dev/null
@@ -1,175 +0,0 @@
-name: Run Scripts on PVE Node for testing
-permissions:
- pull-requests: write
-on:
- pull_request_target:
- branches:
- - main
- paths:
- - "install/**.sh"
- - "ct/**.sh"
-
-jobs:
- run-install-script:
- if: github.repository == 'community-scripts/ProxmoxVE'
- runs-on: pvenode
- steps:
- - name: Checkout PR branch
- 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: Get scripts
- id: check-install-script
- run: |
- ALL_FILES=()
- ADDED_FILES=()
- for FILE in ${{ env.SCRIPT }}; do
- if [[ $FILE =~ ^install/.*-install\.sh$ ]] || [[ $FILE =~ ^ct/.*\.sh$ ]]; then
- STRIPPED_NAME=$(basename "$FILE" | sed 's/-install//' | sed 's/\.sh$//')
- if [[ ! " ${ADDED_FILES[@]} " =~ " $STRIPPED_NAME " ]]; then
- ALL_FILES+=("$FILE")
- ADDED_FILES+=("$STRIPPED_NAME") # Mark this base file as added (without the path)
- fi
- fi
- done
- ALL_FILES=$(echo "${ALL_FILES[@]}" | xargs)
- echo "$ALL_FILES"
- echo "ALL_FILES=$ALL_FILES" >> $GITHUB_ENV
-
- - name: Run scripts
- id: run-install
- continue-on-error: true
- run: |
- set +e
- #run for each files in /ct
- for FILE in ${{ env.ALL_FILES }}; do
- STRIPPED_NAME=$(basename "$FILE" | sed 's/-install//' | sed 's/\.sh$//')
- echo "Running Test for: $STRIPPED_NAME"
- if grep -E -q 'read\s+-r\s+-p\s+".*"\s+\w+' "$FILE"; then
- echo "The script contains an interactive prompt. Skipping execution."
- continue
- fi
- if [[ $FILE =~ ^install/.*-install\.sh$ ]]; then
- CT_SCRIPT="ct/$STRIPPED_NAME.sh"
- if [[ ! -f $CT_SCRIPT ]]; then
- echo "No CT script found for $STRIPPED_NAME"
- ERROR_MSG="No CT script found for $FILE"
- echo "$ERROR_MSG" > result_$STRIPPED_NAME.log
- continue
- fi
- if grep -E -q 'read\s+-r\s+-p\s+".*"\s+\w+' "install/$STRIPPED_NAME-install.sh"; then
- echo "The script contains an interactive prompt. Skipping execution."
- continue
- fi
- echo "Found CT script for $STRIPPED_NAME"
- chmod +x "$CT_SCRIPT"
- RUNNING_FILE=$CT_SCRIPT
- elif [[ $FILE =~ ^ct/.*\.sh$ ]]; then
- INSTALL_SCRIPT="install/$STRIPPED_NAME-install.sh"
- if [[ ! -f $INSTALL_SCRIPT ]]; then
- echo "No install script found for $STRIPPED_NAME"
- ERROR_MSG="No install script found for $FILE"
- echo "$ERROR_MSG" > result_$STRIPPED_NAME.log
- continue
- fi
- echo "Found install script for $STRIPPED_NAME"
- chmod +x "$INSTALL_SCRIPT"
- RUNNING_FILE=$FILE
- if grep -E -q 'read\s+-r\s+-p\s+".*"\s+\w+' "ct/$STRIPPED_NAME.sh"; then
- echo "The script contains an interactive prompt. Skipping execution."
- continue
- fi
- fi
- git remote add community-scripts https://github.com/community-scripts/ProxmoxVE.git
- git fetch community-scripts
- rm -f .github/workflows/scripts/app-test/pr-build.func || true
- rm -f .github/workflows/scripts/app-test/pr-install.func || true
- rm -f .github/workflows/scripts/app-test/pr-alpine-install.func || true
- rm -f .github/workflows/scripts/app-test/pr-create-lxc.sh || true
- git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-build.func
- git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-install.func
- git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-alpine-install.func
- git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-create-lxc.sh
- chmod +x $RUNNING_FILE
- chmod +x .github/workflows/scripts/app-test/pr-create-lxc.sh
- chmod +x .github/workflows/scripts/app-test/pr-install.func
- chmod +x .github/workflows/scripts/app-test/pr-alpine-install.func
- chmod +x .github/workflows/scripts/app-test/pr-build.func
- sed -i 's|source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)|source .github/workflows/scripts/app-test/pr-build.func|g' "$RUNNING_FILE"
- echo "Executing $RUNNING_FILE"
- ERROR_MSG=$(./$RUNNING_FILE 2>&1 > /dev/null)
- echo "Finished running $FILE"
- if [ -n "$ERROR_MSG" ]; then
- echo "ERROR in $STRIPPED_NAME: $ERROR_MSG"
- echo "$ERROR_MSG" > result_$STRIPPED_NAME.log
- fi
- done
- set -e # Restore exit-on-error
-
- - name: Cleanup PVE Node
- run: |
- containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}' | awk '{print $1}')
-
- for container_id in $containers; do
- status=$(pct status $container_id | awk '{print $2}')
- if [[ $status == "running" ]]; then
- pct stop $container_id
- pct destroy $container_id
- fi
- done
-
- - name: Post error comments
- run: |
- ERROR="false"
- SEARCH_LINE=".github/workflows/scripts/app-test/pr-build.func: line 255:"
-
- # Get all existing comments on the PR
- EXISTING_COMMENTS=$(gh pr view ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --json comments --jq '.comments[].body')
-
- for FILE in ${{ env.ALL_FILES }}; do
- STRIPPED_NAME=$(basename "$FILE" | sed 's/-install//' | sed 's/\.sh$//')
- if [[ ! -f result_$STRIPPED_NAME.log ]]; then
- continue
- fi
- ERROR_MSG=$(cat result_$STRIPPED_NAME.log)
-
- if [ -n "$ERROR_MSG" ]; then
- CLEANED_ERROR_MSG=$(echo "$ERROR_MSG" | sed "s|$SEARCH_LINE.*||")
- COMMENT_BODY=":warning: The script _**$FILE**_ failed with the following message:
${CLEANED_ERROR_MSG}
"
-
- # Check if the comment already exists
- if echo "$EXISTING_COMMENTS" | grep -qF "$COMMENT_BODY"; then
- echo "Skipping duplicate comment for $FILE"
- else
- echo "Posting error message for $FILE"
- gh pr comment ${{ github.event.pull_request.number }} \
- --repo ${{ github.repository }} \
- --body "$COMMENT_BODY"
- ERROR="true"
- fi
- fi
- done
-
- echo "ERROR=$ERROR" >> $GITHUB_ENV
diff --git a/.github/workflows/bak/script_format.yml b/.github/workflows/bak/script_format.yml
deleted file mode 100644
index 64a2eda42..000000000
--- a/.github/workflows/bak/script_format.yml
+++ /dev/null
@@ -1,243 +0,0 @@
-name: Script Format Check
-permissions:
- pull-requests: write
-on:
- pull_request_target:
- branches:
- - main
- paths:
- - "install/*.sh"
- - "ct/*.sh"
-
-jobs:
- run-install-script:
- if: github.repository == 'community-scripts/ProxmoxVE'
- 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 -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)" ]] &&
- echo "Line 2 was $SECOND_LINE | Should be: source <(curl -fsSL 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-2026 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-2026 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
diff --git a/.github/workflows/bak/validate-filenames.yml b/.github/workflows/bak/validate-filenames.yml
deleted file mode 100644
index 211be06df..000000000
--- a/.github/workflows/bak/validate-filenames.yml
+++ /dev/null
@@ -1,158 +0,0 @@
-name: Validate filenames
-
-on:
- pull_request_target:
- paths:
- - "ct/*.sh"
- - "install/*.sh"
- - "frontend/public/json/*.json"
-
-jobs:
- check-files:
- if: github.repository == 'community-scripts/ProxmoxVE'
- name: Check changed files
- runs-on: ubuntu-latest
- permissions:
- pull-requests: write
-
- steps:
- - name: Get pull request information
- if: github.event_name == 'pull_request_target'
- uses: actions/github-script@v7
- id: pr
- with:
- script: |
- const { data: pullRequest } = await github.rest.pulls.get({
- ...context.repo,
- pull_number: context.payload.pull_request.number,
- });
- return pullRequest;
-
- - name: Checkout code
- uses: actions/checkout@v4
- with:
- fetch-depth: 0 # Ensure the full history is fetched for accurate diffing
- ref: ${{ github.event_name == 'pull_request_target' && fromJSON(steps.pr.outputs.result).merge_commit_sha || '' }}
-
- - name: Get changed files
- id: changed-files
- run: |
- if ${{ github.event_name == 'pull_request_target' }}; then
- echo "files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ steps.pr.outputs.result && fromJSON(steps.pr.outputs.result).merge_commit_sha }} | xargs)" >> $GITHUB_OUTPUT
- else
- echo "files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | xargs)" >> $GITHUB_OUTPUT
- fi
-
- - name: "Validate filenames in ct and install directory"
- if: always() && steps.changed-files.outputs.files != ''
- id: check-scripts
- run: |
- CHANGED_FILES=$(printf "%s\n" ${{ steps.changed-files.outputs.files }} | { grep -E '^(ct|install)/.*\.sh$' || true; })
-
- NON_COMPLIANT_FILES=""
- for FILE in $CHANGED_FILES; do
- BASENAME=$(echo "$(basename "${FILE%.*}")")
- if [[ ! "$BASENAME" =~ ^[a-z0-9-]+$ ]]; then
- NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE"
- fi
- done
-
- if [ -n "$NON_COMPLIANT_FILES" ]; then
- echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT
- echo "Non-compliant filenames found, change to lowercase:"
- for FILE in $NON_COMPLIANT_FILES; do
- echo "$FILE"
- done
- exit 1
- fi
-
- - name: "Validate filenames in json directory."
- if: always() && steps.changed-files.outputs.files != ''
- id: check-json
- run: |
- CHANGED_FILES=$(printf "%s\n" ${{ steps.changed-files.outputs.files }} | { grep -E '^json/.*\.json$' || true; })
-
- NON_COMPLIANT_FILES=""
- for FILE in $CHANGED_FILES; do
- BASENAME=$(echo "$(basename "${FILE%.*}")")
- if [[ ! "$BASENAME" =~ ^[a-z0-9-]+$ ]]; then
- NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE"
- fi
- done
-
- if [ -n "$NON_COMPLIANT_FILES" ]; then
- echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT
- echo "Non-compliant filenames found, change to lowercase:"
- for FILE in $NON_COMPLIANT_FILES; do
- echo "$FILE"
- done
- exit 1
- fi
-
- - name: Post results and comment
- if: always() && steps.check-scripts.outputs.files != '' && steps.check-json.outputs.files != '' && github.event_name == 'pull_request_target'
- uses: actions/github-script@v7
- with:
- script: |
- const result = "${{ job.status }}" === "success" ? "success" : "failure";
- const nonCompliantFiles = {
- script: "${{ steps.check-scripts.outputs.files }}",
- JSON: "${{ steps.check-json.outputs.files }}",
- };
-
- const issueNumber = context.payload.pull_request
- ? context.payload.pull_request.number
- : null;
- const commentIdentifier = "validate-filenames";
- let newCommentBody = `\n### Filename validation\n\n`;
-
- if (result === "failure") {
- newCommentBody += ":x: We found issues in the following changed files:\n\n";
- for (const [check, files] of Object.entries(nonCompliantFiles)) {
- if (files) {
- newCommentBody += `**${check.charAt(0).toUpperCase() + check.slice(1)} filename invalid:**\n${files
- .trim()
- .split(" ")
- .map((file) => `- ${file}`)
- .join("\n")}\n\n`;
- }
- }
- newCommentBody +=
- "Please change the filenames to lowercase and use only alphanumeric characters and dashes.\n";
- } else {
- newCommentBody += `:rocket: All files passed filename validation!\n`;
- }
-
- newCommentBody += `\n\n`;
-
- if (issueNumber) {
- const { data: comments } = await github.rest.issues.listComments({
- ...context.repo,
- issue_number: issueNumber,
- });
-
- const existingComment = comments.find(
- (comment) => comment.user.login === "github-actions[bot]",
- );
-
- if (existingComment) {
- if (existingComment.body.includes(commentIdentifier)) {
- const re = new RegExp(String.raw`[\s\S]*?`, "");
- newCommentBody = existingComment.body.replace(re, newCommentBody);
- } else {
- newCommentBody = existingComment.body + '\n\n---\n\n' + newCommentBody;
- }
-
- await github.rest.issues.updateComment({
- ...context.repo,
- comment_id: existingComment.id,
- body: newCommentBody,
- });
- } else {
- await github.rest.issues.createComment({
- ...context.repo,
- issue_number: issueNumber,
- body: newCommentBody,
- });
- }
- }
diff --git a/.github/workflows/push-to-gitea.yaml b/.github/workflows/push-to-gitea.yaml
deleted file mode 100644
index bd927f4a4..000000000
--- a/.github/workflows/push-to-gitea.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-name: Sync to Gitea
-
-on:
- push:
- branches:
- - main
-
-jobs:
- sync:
- if: github.repository == 'community-scripts/ProxmoxVE'
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout source repo
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Change all links to git.community-scripts.org
- run: |
- echo "Searching for files containing raw.githubusercontent.com URLs..."
-
- # Find all files containing GitHub raw URLs, excluding certain directories
- files_with_github_urls=$(grep -r "https://raw.githubusercontent.com/community-scripts/ProxmoxVE" . --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=.github/workflows --files-with-matches || true)
-
- if [ -n "$files_with_github_urls" ]; then
- echo "$files_with_github_urls" | while read file; do
- if [ -f "$file" ]; then
- sed -i 's|https://raw\.githubusercontent\.com/community-scripts/ProxmoxVE/|https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/|g' "$file"
- fi
- done
- else
- echo "No files found containing GitHub raw URLs"
- fi
-
-
-
- - name: Push to Gitea
- run: |
- git config --global user.name "Push From Github"
- git config --global user.email "actions@github.com"
- git remote add gitea https://$GITEA_USER:$GITEA_TOKEN@git.community-scripts.org/community-scripts/ProxmoxVE.git
- git add .
- git commit -m "Sync to Gitea"
- git push gitea --all --force
- env:
- GITEA_USER: ${{ secrets.GITEA_USERNAME }}
- GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
diff --git a/.github/workflows/scripts/app-test/pr-alpine-install.func b/.github/workflows/scripts/app-test/pr-alpine-install.func
deleted file mode 100644
index 89c57dcf5..000000000
--- a/.github/workflows/scripts/app-test/pr-alpine-install.func
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2021-2026 community-scripts ORG
-# Author: Michel Roegl-Brunner (michelroegl-brunner)
-# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
-
-color() {
- return
-}
-catch_errors() {
- set -Eeuo pipefail
- trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
-}
-
-# This function handles errors
-error_handler() {
- local line_number="$1"
- local command="$2"
- SCRIPT_NAME=$(basename "$0")
- local error_message="$SCRIPT_NAME: Failure in line $line_number while executing command $command"
- echo -e "\n$error_message"
- exit 0
-}
-verb_ip6() {
- STD=""
- return
-}
-
-msg_info() {
- local msg="$1"
- echo -ne "${msg}\n"
-}
-
-msg_ok() {
- local msg="$1"
- echo -e "${msg}\n"
-}
-
-msg_error() {
-
- local msg="$1"
- echo -e "${msg}\n"
-}
-
-RETRY_NUM=10
-RETRY_EVERY=3
-i=$RETRY_NUM
-
-setting_up_container() {
- while [ $i -gt 0 ]; do
- if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then
- break
- fi
- echo 1>&2 -en "No Network! "
- sleep $RETRY_EVERY
- i=$((i - 1))
- done
-
- if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then
- echo 1>&2 -e "\n No Network After $RETRY_NUM Tries"
- echo -e "Check Network Settings"
- exit 1
- fi
- msg_ok "Set up Container OS"
- msg_ok "Network Connected: $(hostname -i)"
-}
-
-network_check() {
- RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }')
- if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to $RESOLVEDIP"; fi
- set -e
-}
-
-update_os() {
- msg_info "Updating Container OS"
- $STD apk -U upgrade
- msg_ok "Updated Container OS"
-}
-
-motd_ssh() {
- return
-}
-
-customize() {
- return
-}
diff --git a/.github/workflows/scripts/app-test/pr-build.func b/.github/workflows/scripts/app-test/pr-build.func
deleted file mode 100644
index 6eadfb60d..000000000
--- a/.github/workflows/scripts/app-test/pr-build.func
+++ /dev/null
@@ -1,260 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2021-2026 community-scripts ORG
-# Author: Michel Roegl-Brunner (michelroegl-brunner)
-# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
-
-variables() {
- NSAPP=$(echo ${APP,,} | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces.
- var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP.
-
-}
-
-NEXTID=$(pvesh get /cluster/nextid)
-timezone=$(cat /etc/timezone)
-header_info() {
- return
-}
-
-base_settings() {
- # Default Settings
- CT_TYPE="1"
- DISK_SIZE="4"
- CORE_COUNT="1"
- RAM_SIZE="1024"
- VERBOSE="no"
- PW=""
- CT_ID=$NEXTID
- HN=$NSAPP
- BRG="vmbr0"
- NET="dhcp"
- GATE=""
- APT_CACHER=""
- APT_CACHER_IP=""
- DISABLEIP6="no"
- MTU=""
- SD=""
- NS=""
- MAC=""
- VLAN=""
- SSH="no"
- SSH_AUTHORIZED_KEY=""
- TAGS="community-script;"
-
- # Override default settings with variables from ct script
- CT_TYPE=${var_unprivileged:-$CT_TYPE}
- DISK_SIZE=${var_disk:-$DISK_SIZE}
- CORE_COUNT=${var_cpu:-$CORE_COUNT}
- RAM_SIZE=${var_ram:-$RAM_SIZE}
- VERB=${var_verbose:-$VERBOSE}
- TAGS="${TAGS}${var_tags:-}"
-
- # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts
- if [ -z "$var_os" ]; then
- var_os="debian"
- fi
- if [ -z "$var_version" ]; then
- var_version="12"
- fi
-}
-
-color() {
- # Colors
- YW=$(echo "\033[33m")
- YWB=$(echo "\033[93m")
- BL=$(echo "\033[36m")
- RD=$(echo "\033[01;31m")
- BGN=$(echo "\033[4;92m")
- GN=$(echo "\033[1;92m")
- DGN=$(echo "\033[32m")
-
- # Formatting
- CL=$(echo "\033[m")
- UL=$(echo "\033[4m")
- BOLD=$(echo "\033[1m")
- BFR="\\r\\033[K"
- HOLD=" "
- TAB=" "
-
- # Icons
- CM="${TAB}βοΈ${TAB}${CL}"
- CROSS="${TAB}βοΈ${TAB}${CL}"
- INFO="${TAB}π‘${TAB}${CL}"
- OS="${TAB}π₯οΈ${TAB}${CL}"
- OSVERSION="${TAB}π${TAB}${CL}"
- CONTAINERTYPE="${TAB}π¦${TAB}${CL}"
- DISKSIZE="${TAB}πΎ${TAB}${CL}"
- CPUCORE="${TAB}π§ ${TAB}${CL}"
- RAMSIZE="${TAB}π οΈ${TAB}${CL}"
- SEARCH="${TAB}π${TAB}${CL}"
- VERIFYPW="${TAB}π${TAB}${CL}"
- CONTAINERID="${TAB}π${TAB}${CL}"
- HOSTNAME="${TAB}π ${TAB}${CL}"
- BRIDGE="${TAB}π${TAB}${CL}"
- NETWORK="${TAB}π‘${TAB}${CL}"
- GATEWAY="${TAB}π${TAB}${CL}"
- DISABLEIPV6="${TAB}π«${TAB}${CL}"
- DEFAULT="${TAB}βοΈ${TAB}${CL}"
- MACADDRESS="${TAB}π${TAB}${CL}"
- VLANTAG="${TAB}π·οΈ${TAB}${CL}"
- ROOTSSH="${TAB}π${TAB}${CL}"
- CREATING="${TAB}π${TAB}${CL}"
- ADVANCED="${TAB}π§©${TAB}${CL}"
-}
-
-catch_errors() {
- set -Eeuo pipefail
- trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
-}
-
-# This function handles errors
-error_handler() {
- local line_number="$1"
- local command="$2"
- SCRIPT_NAME=$(basename "$0")
- local error_message="$SCRIPT_NAME: Failure in line $line_number while executing command $command"
- echo -e "\n$error_message"
- exit 100
-}
-
-msg_info() {
- local msg="$1"
- echo -ne "${msg}\n"
-}
-
-msg_ok() {
- local msg="$1"
- echo -e "${msg}\n"
-}
-
-msg_error() {
-
- local msg="$1"
- echo -e "${msg}\n"
-}
-start() {
- base_settings
- return
-}
-
-build_container() {
- # if [ "$VERB" == "yes" ]; then set -x; fi
-
- if [ "$CT_TYPE" == "1" ]; then
- FEATURES="keyctl=1,nesting=1"
- else
- FEATURES="nesting=1"
- fi
- TEMP_DIR=$(mktemp -d)
- pushd $TEMP_DIR >/dev/null
- if [ "$var_os" == "alpine" ]; then
- export FUNCTIONS_FILE_PATH="$(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/.github/workflows/scripts/app-test/pr-alpine-install.func)"
- else
- export FUNCTIONS_FILE_PATH="$(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/.github/workflows/scripts/app-test/pr-install.func)"
- fi
-
- export CACHER="$APT_CACHER"
- export CACHER_IP="$APT_CACHER_IP"
- export tz=""
- export DISABLEIPV6="$DISABLEIP6"
- export APPLICATION="$APP"
- export app="$NSAPP"
- export PASSWORD="$PW"
- export VERBOSE="$VERB"
- export SSH_ROOT="${SSH}"
- export SSH_AUTHORIZED_KEY
- export CTID="$CT_ID"
- export CTTYPE="$CT_TYPE"
- export PCT_OSTYPE="$var_os"
- export PCT_OSVERSION="$var_version"
- export PCT_DISK_SIZE="$DISK_SIZE"
- export tz="$timezone"
- export PCT_OPTIONS="
- -features $FEATURES
- -hostname $HN
- -tags $TAGS
- $SD
- $NS
- -net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU
- -onboot 1
- -cores $CORE_COUNT
- -memory $RAM_SIZE
- -unprivileged $CT_TYPE
- $PW
- "
- echo "Container ID: $CTID"
-
- # This executes create_lxc.sh and creates the container and .conf file
- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/.github/workflows/scripts/app-test/pr-create-lxc.sh)"
-
- LXC_CONFIG=/etc/pve/lxc/${CTID}.conf
- if [ "$CT_TYPE" == "0" ]; then
- cat <>$LXC_CONFIG
-# USB passthrough
-lxc.cgroup2.devices.allow: a
-lxc.cap.drop:
-lxc.cgroup2.devices.allow: c 188:* rwm
-lxc.cgroup2.devices.allow: c 189:* rwm
-lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir
-lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file
-lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file
-lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file
-lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file
-EOF
- fi
-
- if [ "$CT_TYPE" == "0" ]; then
- if [[ "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "ErsatzTV" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "Scrypted" || "$APP" == "Tdarr" || "$APP" == "Unmanic" || "$APP" == "Ollama" ]]; then
- cat <>$LXC_CONFIG
-# VAAPI hardware transcoding
-lxc.cgroup2.devices.allow: c 226:0 rwm
-lxc.cgroup2.devices.allow: c 226:128 rwm
-lxc.cgroup2.devices.allow: c 29:0 rwm
-lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file
-lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
-lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file
-EOF
- fi
- else
- if [[ "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "ErsatzTV" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "Scrypted" || "$APP" == "Tdarr" || "$APP" == "Unmanic" || "$APP" == "Ollama" ]]; then
- if [[ -e "/dev/dri/renderD128" ]]; then
- if [[ -e "/dev/dri/card0" ]]; then
- cat <>$LXC_CONFIG
-# VAAPI hardware transcoding
-dev0: /dev/dri/card0,gid=44
-dev1: /dev/dri/renderD128,gid=104
-EOF
- else
- cat <>$LXC_CONFIG
-# VAAPI hardware transcoding
-dev0: /dev/dri/card1,gid=44
-dev1: /dev/dri/renderD128,gid=104
-EOF
- fi
- fi
- fi
- fi
-
- # This starts the container and executes -install.sh
- msg_info "Starting LXC Container"
- pct start "$CTID"
- msg_ok "Started LXC Container"
-
- if [[ ! -f "/root/actions-runner/_work/ProxmoxVE/ProxmoxVE/install/$var_install.sh" ]]; then
- msg_error "No install script found for $APP"
- exit 1
- fi
- if [ "$var_os" == "alpine" ]; then
- sleep 3
- pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories
-http://dl-cdn.alpinelinux.org/alpine/latest-stable/main
-http://dl-cdn.alpinelinux.org/alpine/latest-stable/community
-EOF'
- pct exec "$CTID" -- ash -c "apk add bash >/dev/null"
- fi
- lxc-attach -n "$CTID" -- bash -c "$(cat /root/actions-runner/_work/ProxmoxVE/ProxmoxVE/install/$var_install.sh)"
-
-}
-
-description() {
- IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1)
-}
diff --git a/.github/workflows/scripts/app-test/pr-create-lxc.sh b/.github/workflows/scripts/app-test/pr-create-lxc.sh
deleted file mode 100644
index 4012599c9..000000000
--- a/.github/workflows/scripts/app-test/pr-create-lxc.sh
+++ /dev/null
@@ -1,163 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2021-2026 community-scripts ORG
-# Author: Michel Roegl-Brunner (michelroegl-brunner)
-# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
-
-color() {
- return
-}
-catch_errors() {
- set -Eeuo pipefail
- trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
-}
-
-# This function handles errors
-error_handler() {
- local exit_code="$?"
- local line_number="$1"
- local command="$2"
- local error_message="Failure in line $line_number: exit code $exit_code: while executing command $command"
- echo -e "\n$error_message"
- exit 100
-}
-verb_ip6() {
- return
-}
-
-msg_info() {
- local msg="$1"
- echo -ne "${msg}\n"
-}
-
-msg_ok() {
- local msg="$1"
- echo -e "${msg}\n"
-}
-
-msg_error() {
-
- local msg="$1"
- echo -e "${msg}\n"
-}
-
-VALIDCT=$(pvesm status -content rootdir | awk 'NR>1')
-if [ -z "$VALIDCT" ]; then
- msg_error "Unable to detect a valid Container Storage location."
- exit 1
-fi
-VALIDTMP=$(pvesm status -content vztmpl | awk 'NR>1')
-if [ -z "$VALIDTMP" ]; then
- msg_error "Unable to detect a valid Template Storage location."
- exit 1
-fi
-
-function select_storage() {
- local CLASS=$1
- local CONTENT
- local CONTENT_LABEL
- case $CLASS in
- container)
- CONTENT='rootdir'
- CONTENT_LABEL='Container'
- ;;
- template)
- CONTENT='vztmpl'
- CONTENT_LABEL='Container template'
- ;;
- *) false || {
- msg_error "Invalid storage class."
- exit 201
- } ;;
- esac
-
- # This Queries all storage locations
- local -a MENU
- while read -r line; do
- local TAG=$(echo $line | awk '{print $1}')
- local TYPE=$(echo $line | awk '{printf "%-10s", $2}')
- local FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
- local ITEM="Type: $TYPE Free: $FREE "
- local OFFSET=2
- if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
- local MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
- fi
- MENU+=("$TAG" "$ITEM" "OFF")
- done < <(pvesm status -content $CONTENT | awk 'NR>1')
-
- # Select storage location
- if [ $((${#MENU[@]} / 3)) -eq 1 ]; then
- printf ${MENU[0]}
- else
- msg_error "STORAGE ISSUES!"
- exit 202
- fi
-}
-
-[[ "${CTID:-}" ]] || {
- msg_error "You need to set 'CTID' variable."
- exit 203
-}
-[[ "${PCT_OSTYPE:-}" ]] || {
- msg_error "You need to set 'PCT_OSTYPE' variable."
- exit 204
-}
-
-# Test if ID is valid
-[ "$CTID" -ge "100" ] || {
- msg_error "ID cannot be less than 100."
- exit 205
-}
-
-# Test if ID is in use
-if pct status $CTID &>/dev/null; then
- echo -e "ID '$CTID' is already in use."
- unset CTID
- msg_error "Cannot use ID that is already in use."
- exit 206
-fi
-
-TEMPLATE_STORAGE=$(select_storage template) || exit
-
-CONTAINER_STORAGE=$(select_storage container) || exit
-
-pveam update >/dev/null
-
-TEMPLATE_SEARCH=${PCT_OSTYPE}-${PCT_OSVERSION:-}
-mapfile -t TEMPLATES < <(pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V)
-[ ${#TEMPLATES[@]} -gt 0 ] || {
- msg_error "Unable to find a template when searching for '$TEMPLATE_SEARCH'."
- exit 207
-}
-TEMPLATE="${TEMPLATES[-1]}"
-
-TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE"
-
-if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE"; then
- [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH"
- pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null ||
- {
- msg_error "A problem occurred while downloading the LXC template."
- exit 208
- }
-fi
-
-grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid
-grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid
-
-PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}})
-[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}")
-
-if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then
- [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH"
-
- pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null ||
- {
- msg_error "A problem occurred while re-downloading the LXC template."
- exit 208
- }
-
- if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then
- msg_error "A problem occurred while trying to create container after re-downloading template."
- exit 200
- fi
-fi
diff --git a/.github/workflows/scripts/app-test/pr-install.func b/.github/workflows/scripts/app-test/pr-install.func
deleted file mode 100644
index 1709a1c16..000000000
--- a/.github/workflows/scripts/app-test/pr-install.func
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2021-2026 community-scripts ORG
-# Author: Michel Roegl-Brunner (michelroegl-brunner)
-# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
-
-color() {
- return
-}
-
-catch_errors() {
- set -Euo pipefail
- trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
-}
-
-error_handler() {
- local line_number="$1"
- local command="$2"
- local error_message="Failure in line $line_number while executing command '$command'"
- echo -e "\n$error_message\n" >&2
- exit 1
-}
-
-verb_ip6() {
- STD="silent"
- silent() {
- "$@" >/dev/null 2>&1 || error_handler "${BASH_LINENO[0]}" "$*"
- }
- return
-}
-
-msg_info() {
- local msg="$1"
- echo -ne "${msg}\n"
-}
-
-msg_ok() {
- local msg="$1"
- echo -e "${msg}\n"
-}
-
-msg_error() {
-
- local msg="$1"
- echo -e "${msg}\n"
-}
-
-RETRY_NUM=10
-RETRY_EVERY=3
-setting_up_container() {
-
- sed -i "/$LANG/ s/\(^# \)//" /etc/locale.gen
- locale_line=$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print $1}' | head -n 1)
- echo "LANG=${locale_line}" >/etc/default/locale
- locale-gen >/dev/null
- export LANG=${locale_line}
- echo $tz >/etc/timezone
- ln -sf /usr/share/zoneinfo/$tz /etc/localtime
-
- for ((i = RETRY_NUM; i > 0; i--)); do
- if [ "$(hostname -I)" != "" ]; then
- break
- fi
- sleep $RETRY_EVERY
- done
- if [ "$(hostname -I)" = "" ]; then
- echo 1>&2 -e "\nNo Network After $RETRY_NUM Tries"
- echo -e "Check Network Settings"
- exit 101
- fi
- rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
- systemctl disable -q --now systemd-networkd-wait-online.service
-}
-
-network_check() {
- RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }')
- if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to $RESOLVEDIP"; fi
- set -e
-}
-
-update_os() {
- export DEBIAN_FRONTEND=noninteractive
- apt-get update >/dev/null 2>&1
- apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade >/dev/null
- rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
-}
-
-motd_ssh() {
- return
-}
-
-customize() {
- return
-}
diff --git a/.github/workflows/scripts/update-json.sh b/.github/workflows/scripts/update-json.sh
deleted file mode 100644
index 1711f7550..000000000
--- a/.github/workflows/scripts/update-json.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-FILE=$1
-TODAY=$(date -u +"%Y-%m-%d")
-
-if [[ -z "$FILE" ]]; then
- echo "No file specified. Exiting."
- exit 1
-fi
-
-if [[ ! -f "$FILE" ]]; then
- echo "File $FILE not found. Exiting."
- exit 1
-fi
-
-DATE_IN_JSON=$(jq -r '.date_created' "$FILE" 2>/dev/null || echo "")
-
-if [[ "$DATE_IN_JSON" != "$TODAY" ]]; then
- jq --arg date "$TODAY" '.date_created = $date' "$FILE" >tmp.json && mv tmp.json "$FILE"
-fi
diff --git a/.github/workflows/scripts/update_json_date.sh b/.github/workflows/scripts/update_json_date.sh
deleted file mode 100644
index 13305de83..000000000
--- a/.github/workflows/scripts/update_json_date.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env bash
-
-# Verzeichnis, das die JSON-Dateien enthΓ€lt
-json_dir="./json/*.json"
-
-current_date=$(date +"%Y-%m-%d")
-
-for json_file in $json_dir; do
- if [[ -f "$json_file" ]]; then
- current_json_date=$(jq -r '.date_created' "$json_file")
-
- if [[ "$current_json_date" != "$current_date" ]]; then
- echo "Updating $json_file with date $current_date"
- jq --arg date "$current_date" '.date_created = $date' "$json_file" >temp.json && mv temp.json "$json_file"
-
- git add "$json_file"
- git commit -m "Update date_created to $current_date in $json_file"
- else
- echo "Date in $json_file is already up to date."
- fi
- fi
-done
-git push origin HEAD
diff --git a/.github/workflows/update-json-date.yml b/.github/workflows/update-json-date.yml
deleted file mode 100644
index 7e4824052..000000000
--- a/.github/workflows/update-json-date.yml
+++ /dev/null
@@ -1,152 +0,0 @@
-name: Update JSON Date
-
-on:
- push:
- branches:
- - main
- paths:
- - "json/**.json"
- workflow_dispatch:
-
-jobs:
- update-app-files:
- if: github.repository == 'community-scripts/ProxmoxVE'
- runs-on: ubuntu-latest
-
- permissions:
- contents: write
- pull-requests: write
-
- steps:
- - name: Generate a token
- id: generate-token
- uses: actions/create-github-app-token@v1
- with:
- app-id: ${{ vars.APP_ID }}
- private-key: ${{ secrets.APP_PRIVATE_KEY }}
-
- - name: Generate a token for PR approval and merge
- id: generate-token-merge
- uses: actions/create-github-app-token@v1
- with:
- app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }}
- private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }}
-
- - name: Generate dynamic branch name
- id: timestamp
- run: echo "BRANCH_NAME=pr-update-json-$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV
-
- - name: Set up GH_TOKEN
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- echo "GH_TOKEN=${GH_TOKEN}" >> $GITHUB_ENV
-
- - name: Checkout Repository
- uses: actions/checkout@v4
- with:
- fetch-depth: 2 # Ensure we have the last two commits
-
- - name: Get Previous Commit
- id: prev_commit
- run: |
- PREV_COMMIT=$(git rev-parse HEAD^)
- echo "Previous commit: $PREV_COMMIT"
- echo "prev_commit=$PREV_COMMIT" >> $GITHUB_ENV
-
- - name: Get Newly Added JSON Files
- id: new_json_files
- run: |
- git diff --name-only --diff-filter=A ${{ env.prev_commit }} HEAD | grep '^json/.*\.json$' > new_files.txt || true
- echo "New files detected:"
- cat new_files.txt || echo "No new files."
-
- - name: Disable file mode changes
- run: git config core.fileMode false
-
- - name: Set up Git
- run: |
- git config --global user.name "GitHub Actions"
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
-
- - name: Change JSON Date
- id: change-json-date
- run: |
- current_date=$(date +"%Y-%m-%d")
- while IFS= read -r file; do
- # Skip empty lines
- [[ -z "$file" ]] && continue
-
- if [[ -f "$file" ]]; then
- echo "Processing $file..."
- current_json_date=$(jq -r '.date_created // empty' "$file")
- if [[ -z "$current_json_date" || "$current_json_date" != "$current_date" ]]; then
- echo "Updating $file with date $current_date"
- jq --arg date "$current_date" '.date_created = $date' "$file" > temp.json && mv temp.json "$file"
- else
- echo "Date in $file is already up to date."
- fi
- else
- echo "Warning: File $file not found!"
- fi
- done < new_files.txt
- rm new_files.txt
-
- - name: Check if there are any changes
- run: |
- echo "Checking for changes..."
- git add -A
- git status
- if git diff --cached --quiet; then
- echo "No changes detected."
- echo "changed=false" >> "$GITHUB_ENV"
- else
- echo "Changes detected:"
- git diff --stat --cached
- echo "changed=true" >> "$GITHUB_ENV"
- fi
-
- # Step 7: Commit and create PR if changes exist
- - name: Commit and create PR if changes exist
- if: env.changed == 'true'
- run: |
-
-
- git commit -m "Update date in json"
- git checkout -b ${{ env.BRANCH_NAME }}
- git push origin ${{ env.BRANCH_NAME }}
-
- gh pr create --title "[core] update date in json" \
- --body "This PR is auto-generated by a GitHub Action to update the date in json." \
- --head ${{ env.BRANCH_NAME }} \
- --base main \
- --label "automated pr"
- env:
- GH_TOKEN: ${{ steps.generate-token.outputs.token }}
-
- - name: Approve pull request
- if: env.changed == 'true'
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- PR_NUMBER=$(gh pr list --head "${{ env.BRANCH_NAME }}" --json number --jq '.[].number')
- if [ -n "$PR_NUMBER" ]; then
- gh pr review $PR_NUMBER --approve
- fi
-
- - name: Approve pull request and merge
- if: env.changed == 'true'
- env:
- GH_TOKEN: ${{ steps.generate-token-merge.outputs.token }}
- run: |
- git config --global user.name "github-actions-automege[bot]"
- git config --global user.email "github-actions-automege[bot]@users.noreply.github.com"
- PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number')
- if [ -n "$PR_NUMBER" ]; then
- gh pr review $PR_NUMBER --approve
- gh pr merge $PR_NUMBER --squash --admin
- fi
-
- - name: No changes detected
- if: env.changed == 'false'
- run: echo "No changes to commit. Workflow completed successfully."
diff --git a/.github/workflows/update-versions-github.yml b/.github/workflows/update-versions-github.yml
deleted file mode 100644
index 31d7f7af8..000000000
--- a/.github/workflows/update-versions-github.yml
+++ /dev/null
@@ -1,236 +0,0 @@
-name: Update GitHub Versions (New)
-
-on:
- workflow_dispatch:
- schedule:
- # Runs 4x daily: 00:00, 06:00, 12:00, 18:00 UTC
- - cron: "0 0,6,12,18 * * *"
-
-permissions:
- contents: write
- pull-requests: write
-
-env:
- VERSIONS_FILE: json/github-versions.json
- BRANCH_NAME: automated/update-github-versions
- AUTOMATED_PR_LABEL: "automated pr"
-
-jobs:
- update-github-versions:
- if: github.repository == 'community-scripts/ProxmoxVE'
- runs-on: ubuntu-latest
-
- steps:
- - name: Generate a token
- id: generate-token
- uses: actions/create-github-app-token@v1
- with:
- app-id: ${{ vars.APP_ID }}
- private-key: ${{ secrets.APP_PRIVATE_KEY }}
-
- - name: Generate a token for PR approval and merge
- id: generate-token-merge
- uses: actions/create-github-app-token@v1
- with:
- app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }}
- private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }}
-
- - name: Checkout Repository
- uses: actions/checkout@v4
- with:
- ref: main
-
- - name: Extract GitHub versions from install scripts
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- set -euo pipefail
-
- echo "========================================="
- echo " Extracting GitHub versions from scripts"
- echo "========================================="
-
- # Initialize versions array
- versions_json="[]"
-
- # Function to add a version entry
- add_version() {
- local slug="$1"
- local repo="$2"
- local version="$3"
- local pinned="$4"
- local date="$5"
-
- versions_json=$(echo "$versions_json" | jq \
- --arg slug "$slug" \
- --arg repo "$repo" \
- --arg version "$version" \
- --argjson pinned "$pinned" \
- --arg date "$date" \
- '. += [{"slug": $slug, "repo": $repo, "version": $version, "pinned": $pinned, "date": $date}]')
- }
-
- # Get list of slugs from JSON files
- echo ""
- echo "=== Scanning JSON files for slugs ==="
-
- for json_file in json/*.json; do
- [[ ! -f "$json_file" ]] && continue
-
- # Skip non-app JSON files
- basename_file=$(basename "$json_file")
- case "$basename_file" in
- metadata.json|versions.json|github-versions.json|dependency-check.json|update-apps.json)
- continue
- ;;
- esac
-
- # Extract slug from JSON
- slug=$(jq -r '.slug // empty' "$json_file" 2>/dev/null)
- [[ -z "$slug" ]] && continue
-
- # Find corresponding script (install script or addon script)
- install_script=""
- if [[ -f "install/${slug}-install.sh" ]]; then
- install_script="install/${slug}-install.sh"
- elif [[ -f "tools/addon/${slug}.sh" ]]; then
- install_script="tools/addon/${slug}.sh"
- else
- continue
- fi
-
- # Look for fetch_and_deploy_gh_release calls
- # Pattern: fetch_and_deploy_gh_release "app" "owner/repo" ["mode"] ["version"]
- while IFS= read -r line; do
- # Skip commented lines
- [[ "$line" =~ ^[[:space:]]*# ]] && continue
-
- # Extract repo and version from fetch_and_deploy_gh_release
- if [[ "$line" =~ fetch_and_deploy_gh_release[[:space:]]+\"[^\"]*\"[[:space:]]+\"([^\"]+)\"([[:space:]]+\"([^\"]+)\")?([[:space:]]+\"([^\"]+)\")? ]]; then
- repo="${BASH_REMATCH[1]}"
- mode="${BASH_REMATCH[3]:-tarball}"
- pinned_version="${BASH_REMATCH[5]:-latest}"
-
- # Check if version is pinned (not "latest" and not empty)
- is_pinned=false
- target_version=""
-
- if [[ -n "$pinned_version" && "$pinned_version" != "latest" ]]; then
- is_pinned=true
- target_version="$pinned_version"
- fi
-
- # Fetch version from GitHub
- if [[ "$is_pinned" == "true" ]]; then
- # For pinned versions, verify it exists and get date
- response=$(gh api "repos/${repo}/releases/tags/${target_version}" 2>/dev/null || echo '{}')
- if echo "$response" | jq -e '.tag_name' > /dev/null 2>&1; then
- version=$(echo "$response" | jq -r '.tag_name')
- date=$(echo "$response" | jq -r '.published_at // empty')
- add_version "$slug" "$repo" "$version" "true" "$date"
- echo "[$slug] β $version (pinned)"
- else
- echo "[$slug] β pinned version $target_version not found"
- fi
- else
- # Fetch latest release
- response=$(gh api "repos/${repo}/releases/latest" 2>/dev/null || echo '{}')
- if echo "$response" | jq -e '.tag_name' > /dev/null 2>&1; then
- version=$(echo "$response" | jq -r '.tag_name')
- date=$(echo "$response" | jq -r '.published_at // empty')
- add_version "$slug" "$repo" "$version" "false" "$date"
- echo "[$slug] β $version"
- else
- # Try tags as fallback
- version=$(gh api "repos/${repo}/tags" --jq '.[0].name // empty' 2>/dev/null || echo "")
- if [[ -n "$version" ]]; then
- add_version "$slug" "$repo" "$version" "false" ""
- echo "[$slug] β $version (from tags)"
- else
- echo "[$slug] β no version found"
- fi
- fi
- fi
-
- break # Only first match per script
- fi
- done < <(grep 'fetch_and_deploy_gh_release' "$install_script" 2>/dev/null || true)
-
- done
-
- # Save versions file
- echo "$versions_json" | jq --arg date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
- '{generated: $date, versions: (. | sort_by(.slug))}' > "$VERSIONS_FILE"
-
- total=$(echo "$versions_json" | jq 'length')
- echo ""
- echo "========================================="
- echo " Total versions extracted: $total"
- echo "========================================="
-
- - name: Check for changes
- id: check-changes
- run: |
- # Check if file is new (untracked) or has changes
- if [[ ! -f "$VERSIONS_FILE" ]]; then
- echo "changed=false" >> "$GITHUB_OUTPUT"
- echo "Versions file was not created"
- elif ! git ls-files --error-unmatch "$VERSIONS_FILE" &>/dev/null; then
- # File exists but is not tracked - it's new
- echo "changed=true" >> "$GITHUB_OUTPUT"
- echo "New file created: $VERSIONS_FILE"
- elif git diff --quiet "$VERSIONS_FILE" 2>/dev/null; then
- echo "changed=false" >> "$GITHUB_OUTPUT"
- echo "No changes detected"
- else
- echo "changed=true" >> "$GITHUB_OUTPUT"
- echo "Changes detected:"
- git diff --stat "$VERSIONS_FILE" 2>/dev/null || true
- fi
-
- - name: Commit and push changes
- if: steps.check-changes.outputs.changed == 'true'
- run: |
- git config --global user.name "github-actions[bot]"
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- git add "$VERSIONS_FILE"
- git commit -m "chore: update github-versions.json"
- git checkout -b $BRANCH_NAME || git checkout $BRANCH_NAME
- git push origin $BRANCH_NAME --force
-
- - name: Create pull request if not exists
- if: steps.check-changes.outputs.changed == 'true'
- env:
- GH_TOKEN: ${{ steps.generate-token.outputs.token }}
- run: |
- PR_EXISTS=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number')
- if [ -z "$PR_EXISTS" ]; then
- gh pr create --title "[Github Action] Update github-versions.json" \
- --body "This PR is auto-generated by a Github Action to update the github-versions.json file." \
- --head $BRANCH_NAME \
- --base main \
- --label "$AUTOMATED_PR_LABEL"
- fi
-
- - name: Approve pull request
- if: steps.check-changes.outputs.changed == 'true'
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number')
- if [ -n "$PR_NUMBER" ]; then
- gh pr review $PR_NUMBER --approve
- fi
-
- - name: Approve pull request and merge
- if: steps.check-changes.outputs.changed == 'true'
- env:
- GH_TOKEN: ${{ steps.generate-token-merge.outputs.token }}
- run: |
- git config --global user.name "github-actions-automege[bot]"
- git config --global user.email "github-actions-automege[bot]@users.noreply.github.com"
- PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number')
- if [ -n "$PR_NUMBER" ]; then
- gh pr review $PR_NUMBER --approve
- gh pr merge $PR_NUMBER --squash --admin
- fi