mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-03-13 04:09:09 +00:00
Compare commits
2 Commits
main
...
cleanup_wo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
febce9c0b0 | ||
|
|
4f93b1f287 |
68
.github/runner/docker/gh-runner-self.dockerfile
generated
vendored
68
.github/runner/docker/gh-runner-self.dockerfile
generated
vendored
@@ -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
|
||||
55
.github/workflows/bak/close_template_issue.yml
generated
vendored
55
.github/workflows/bak/close_template_issue.yml
generated
vendored
@@ -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"
|
||||
});
|
||||
}
|
||||
126
.github/workflows/bak/crawl-versions.yaml
generated
vendored
126
.github/workflows/bak/crawl-versions.yaml
generated
vendored
@@ -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
|
||||
175
.github/workflows/bak/script-test.yml
generated
vendored
175
.github/workflows/bak/script-test.yml
generated
vendored
@@ -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: <br> <div><strong>${CLEANED_ERROR_MSG}</strong></div>"
|
||||
|
||||
# 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
|
||||
243
.github/workflows/bak/script_format.yml
generated
vendored
243
.github/workflows/bak/script_format.yml
generated
vendored
@@ -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]}<NUMBER>'" >> "$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: <br> <div><strong>${ERROR_MSG}</strong></div>"
|
||||
|
||||
|
||||
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
|
||||
158
.github/workflows/bak/validate-filenames.yml
generated
vendored
158
.github/workflows/bak/validate-filenames.yml
generated
vendored
@@ -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 = `<!-- ${commentIdentifier}-start -->\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<!-- ${commentIdentifier}-end -->`;
|
||||
|
||||
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`<!-- ${commentIdentifier}-start -->[\s\S]*?<!-- ${commentIdentifier}-end -->`, "");
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
164
.github/workflows/close-discussion.yml
generated
vendored
164
.github/workflows/close-discussion.yml
generated
vendored
@@ -1,164 +0,0 @@
|
||||
name: Close Discussion on PR Merge
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
close-discussion:
|
||||
if: github.repository == 'community-scripts/ProxmoxVE'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set Up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install zx @octokit/graphql
|
||||
|
||||
- name: Close Discussion
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
npx zx << 'EOF'
|
||||
import { graphql } from "@octokit/graphql";
|
||||
|
||||
(async function () {
|
||||
try {
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
const commitSha = process.env.GITHUB_SHA;
|
||||
const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/");
|
||||
|
||||
if (!token || !commitSha || !owner || !repo) {
|
||||
console.log("Missing required environment variables.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const graphqlWithAuth = graphql.defaults({
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
// Find PR from commit SHA
|
||||
const searchQuery = `
|
||||
query($owner: String!, $repo: String!, $sha: GitObjectID!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
object(oid: $sha) {
|
||||
... on Commit {
|
||||
associatedPullRequests(first: 1) {
|
||||
nodes {
|
||||
number
|
||||
body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const prResult = await graphqlWithAuth(searchQuery, {
|
||||
owner,
|
||||
repo,
|
||||
sha: commitSha,
|
||||
});
|
||||
|
||||
const pr = prResult.repository.object.associatedPullRequests.nodes[0];
|
||||
if (!pr) {
|
||||
console.log("No PR found for this commit.");
|
||||
return;
|
||||
}
|
||||
|
||||
const prNumber = pr.number;
|
||||
const prBody = pr.body;
|
||||
|
||||
const match = prBody.match(/#(\d+)/);
|
||||
if (!match) {
|
||||
console.log("No discussion ID found in PR body.");
|
||||
return;
|
||||
}
|
||||
|
||||
const discussionNumber = match[1];
|
||||
console.log(`Extracted Discussion Number: ${discussionNumber}`);
|
||||
|
||||
// Fetch GraphQL discussion ID
|
||||
const discussionQuery = `
|
||||
query($owner: String!, $repo: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
discussion(number: $number) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
let discussionQLId;
|
||||
try {
|
||||
const discussionResponse = await graphqlWithAuth(discussionQuery, {
|
||||
owner,
|
||||
repo,
|
||||
number: parseInt(discussionNumber, 10),
|
||||
});
|
||||
|
||||
discussionQLId = discussionResponse.repository.discussion.id;
|
||||
if (!discussionQLId) {
|
||||
console.log("Failed to fetch discussion GraphQL ID.");
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Discussion not found or error occurred while fetching discussion:", error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Post comment
|
||||
const commentMutation = `
|
||||
mutation($discussionId: ID!, $body: String!) {
|
||||
addDiscussionComment(input: { discussionId: $discussionId, body: $body }) {
|
||||
comment { id body }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const commentResponse = await graphqlWithAuth(commentMutation, {
|
||||
discussionId: discussionQLId,
|
||||
body: `Merged with PR #${prNumber}`,
|
||||
});
|
||||
|
||||
const commentId = commentResponse.addDiscussionComment.comment.id;
|
||||
if (!commentId) {
|
||||
console.log("Failed to post the comment.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Comment Posted Successfully! Comment ID: ${commentId}`);
|
||||
|
||||
// Mark comment as answer
|
||||
const markAnswerMutation = `
|
||||
mutation($id: ID!) {
|
||||
markDiscussionCommentAsAnswer(input: { id: $id }) {
|
||||
discussion { id title }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
await graphqlWithAuth(markAnswerMutation, { id: commentId });
|
||||
|
||||
console.log("Comment marked as answer successfully!");
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
EOF
|
||||
38
.github/workflows/create-docker-for-runner.yml
generated
vendored
38
.github/workflows/create-docker-for-runner.yml
generated
vendored
@@ -1,38 +0,0 @@
|
||||
name: Build and Publish Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '.github/runner/docker/**'
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository == 'community-scripts/ProxmoxVE'
|
||||
runs-on: ubuntu-latest #To ensure it always builds we use the github runner with all the right tooling
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
repo_name=${{ github.repository }} # Get repository name
|
||||
repo_name_lower=$(echo $repo_name | tr '[:upper:]' '[:lower:]') # Convert to lowercase
|
||||
docker build -t ghcr.io/$repo_name_lower/gh-runner-self:latest -f .github/runner/docker/gh-runner-self.dockerfile .
|
||||
|
||||
- name: Push Docker image to GHCR
|
||||
run: |
|
||||
repo_name=${{ github.repository }} # Get repository name
|
||||
repo_name_lower=$(echo $repo_name | tr '[:upper:]' '[:lower:]') # Convert to lowercase
|
||||
docker push ghcr.io/$repo_name_lower/gh-runner-self:latest
|
||||
29
.github/workflows/delete-json-branch.yml
generated
vendored
29
.github/workflows/delete-json-branch.yml
generated
vendored
@@ -1,29 +0,0 @@
|
||||
|
||||
name: Delete JSON date PR Branch
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
delete_branch:
|
||||
if: github.repository == 'community-scripts/ProxmoxVE'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Delete PR Update Branch
|
||||
if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'pr-update-json-')
|
||||
run: |
|
||||
PR_BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||
echo "Deleting branch $PR_BRANCH..."
|
||||
|
||||
# Avoid deleting the default branch (e.g., main)
|
||||
if [[ "$PR_BRANCH" != "main" ]]; then
|
||||
git push origin --delete "$PR_BRANCH"
|
||||
else
|
||||
echo "Skipping deletion of the main branch"
|
||||
fi
|
||||
48
.github/workflows/push-to-gitea.yaml
generated
vendored
48
.github/workflows/push-to-gitea.yaml
generated
vendored
@@ -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 }}
|
||||
85
.github/workflows/scripts/app-test/pr-alpine-install.func
generated
vendored
85
.github/workflows/scripts/app-test/pr-alpine-install.func
generated
vendored
@@ -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
|
||||
}
|
||||
260
.github/workflows/scripts/app-test/pr-build.func
generated
vendored
260
.github/workflows/scripts/app-test/pr-build.func
generated
vendored
@@ -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 <<EOF >>$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 <<EOF >>$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 <<EOF >>$LXC_CONFIG
|
||||
# VAAPI hardware transcoding
|
||||
dev0: /dev/dri/card0,gid=44
|
||||
dev1: /dev/dri/renderD128,gid=104
|
||||
EOF
|
||||
else
|
||||
cat <<EOF >>$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 <app>-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 <<EOF >/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)
|
||||
}
|
||||
163
.github/workflows/scripts/app-test/pr-create-lxc.sh
generated
vendored
163
.github/workflows/scripts/app-test/pr-create-lxc.sh
generated
vendored
@@ -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
|
||||
93
.github/workflows/scripts/app-test/pr-install.func
generated
vendored
93
.github/workflows/scripts/app-test/pr-install.func
generated
vendored
@@ -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
|
||||
}
|
||||
20
.github/workflows/scripts/update-json.sh
generated
vendored
20
.github/workflows/scripts/update-json.sh
generated
vendored
@@ -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
|
||||
23
.github/workflows/scripts/update_json_date.sh
generated
vendored
23
.github/workflows/scripts/update_json_date.sh
generated
vendored
@@ -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
|
||||
152
.github/workflows/update-json-date.yml
generated
vendored
152
.github/workflows/update-json-date.yml
generated
vendored
@@ -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."
|
||||
236
.github/workflows/update-versions-github.yml
generated
vendored
236
.github/workflows/update-versions-github.yml
generated
vendored
@@ -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
|
||||
Reference in New Issue
Block a user