diff --git a/.github/workflows/changelog-pr.yaml b/.github/workflows/changelog-pr.yaml new file mode 100644 index 0000000..2bf643e --- /dev/null +++ b/.github/workflows/changelog-pr.yaml @@ -0,0 +1,263 @@ +name: Create Changelog Pull Request + +on: + push: + branches: ["main"] + workflow_dispatch: + +jobs: + update-changelog-pull-request: + runs-on: runner-cluster-htl-set + env: + CONFIG_PATH: .github/changelog-pr-config.json + BRANCH_NAME: github-action-update-changelog + AUTOMATED_PR_LABEL: "automated pr" + 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: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get latest dates in changelog + run: | + DATES=$(grep -E '^## [0-9]{4}-[0-9]{2}-[0-9]{2}' CHANGELOG.md | head -n 2 | awk '{print $2}') + + LATEST_DATE=$(echo "$DATES" | sed -n '1p') + SECOND_LATEST_DATE=$(echo "$DATES" | sed -n '2p') + TODAY=$(date -u +%Y-%m-%d) + + echo "TODAY=$TODAY" >> $GITHUB_ENV + if [[ "$LATEST_DATE" == "$TODAY" ]]; then + echo "LATEST_DATE=$SECOND_LATEST_DATE" >> $GITHUB_ENV + else + echo "LATEST_DATE=$LATEST_DATE" >> $GITHUB_ENV + fi + + - name: Get categorized pull requests + id: get-categorized-prs + uses: actions/github-script@v7 + with: + script: | + async function main() { + const fs = require('fs').promises; + const path = require('path'); + + const configPath = path.resolve(process.env.CONFIG_PATH); + const fileContent = await fs.readFile(configPath, 'utf-8'); + const changelogConfig = JSON.parse(fileContent); + + const categorizedPRs = changelogConfig.map(obj => ({ + ...obj, + notes: [], + subCategories: obj.subCategories ?? ( + obj.labels.includes("update script") ? [ + { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, + { title: "✨ New Features", labels: ["feature"], notes: [] }, + { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, + { title: "🔧 Refactor", labels: ["refactor"], notes: [] }, + ] : + obj.labels.includes("maintenance") ? [ + { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, + { title: "✨ New Features", labels: ["feature"], notes: [] }, + { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, + { title: "📡 API", labels: ["api"], notes: [] }, + { title: "Github", labels: ["github"], notes: [] }, + { title: "📝 Documentation", labels: ["documentation"], notes: [] }, + { title: "🔧 Refactor", labels: ["refactor"], notes: [] } + ] : + obj.labels.includes("website") ? [ + { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, + { title: "✨ New Features", labels: ["feature"], notes: [] }, + { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, + { title: "Script Information", labels: ["json"], notes: [] } + ] : [] + ) + })); + + const latestDateInChangelog = new Date(process.env.LATEST_DATE); + latestDateInChangelog.setUTCHours(23, 59, 59, 999); + + const { data: pulls } = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + base: "main", + state: "closed", + sort: "updated", + direction: "desc", + per_page: 100, + }); + + pulls.filter(pr => + pr.merged_at && + new Date(pr.merged_at) > latestDateInChangelog && + !pr.labels.some(label => + ["invalid", "wontdo", process.env.AUTOMATED_PR_LABEL].includes(label.name.toLowerCase()) + ) + ).forEach(pr => { + + const prLabels = pr.labels.map(label => label.name.toLowerCase()); + if (pr.user.login === "push-app-to-main") { + const scriptName = pr.title; + if (scriptName) { + try { + const { data: relatedIssues } = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: "ProxmoxVED", + state: "all", + label: "Started Migration To ProxmoxVE", + per_page: 5 + }); + const matchingIssue = relatedIssues.find(issue => + issue.title.toLowerCase().includes(scriptName.toLowerCase()) + ); + if (matchingIssue) { + const issueAuthor = matchingIssue.user.login; + const issueAuthorUrl = `https://github.com/${issueAuthor}`; + const prNote = `- ${pr.title} [@${issueAuthor}](${issueAuthorUrl}) ([#${pr.number}](${pr.html_url}))`; + } else { + const prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; + } + } catch (error) { + console.error(`Error fetching related issues: ${error}`); + const prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; + } + } else { + const prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; + + }else{ + const prNote = `- ${pr.title} [@${pr.user.login}](https://github.com/${pr.user.login}) ([#${pr.number}](${pr.html_url}))`; + } + const updateScriptsCategory = categorizedPRs.find(category => + category.labels.some(label => prLabels.includes(label)) + ); + + if (updateScriptsCategory) { + + const subCategory = updateScriptsCategory.subCategories.find(sub => + sub.labels.some(label => prLabels.includes(label)) + ); + + if (subCategory) { + subCategory.notes.push(prNote); + } else { + updateScriptsCategory.notes.push(prNote); + } + } + }); + + console.log(JSON.stringify(categorizedPRs, null, 2)); + + return categorizedPRs; + } + main().catch(error => { + console.error("Error in script:", error); + }); + + - name: Update CHANGELOG.md + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs').promises; + const path = require('path'); + + const today = process.env.TODAY; + const latestDateInChangelog = process.env.LATEST_DATE; + const changelogPath = path.resolve('CHANGELOG.md'); + const categorizedPRs = ${{ steps.get-categorized-prs.outputs.result }}; + + console.log(JSON.stringify(categorizedPRs, null, 2)); + + + let newReleaseNotes = `## ${today}\n\n`; + for (const { title, notes, subCategories } of categorizedPRs) { + const hasSubcategories = subCategories && subCategories.length > 0; + const hasMainNotes = notes.length > 0; + const hasSubNotes = hasSubcategories && subCategories.some(sub => sub.notes && sub.notes.length > 0); + + + + if (hasMainNotes || hasSubNotes) { + newReleaseNotes += `### ${title}\n\n`; + } + + if (hasMainNotes) { + newReleaseNotes += ` ${notes.join("\n")}\n\n`; + } + if (hasSubcategories) { + for (const { title: subTitle, notes: subNotes } of subCategories) { + if (subNotes && subNotes.length > 0) { + newReleaseNotes += ` - #### ${subTitle}\n\n`; + newReleaseNotes += ` ${subNotes.join("\n ")}\n\n`; + } + } + } + } + + const changelogContent = await fs.readFile(changelogPath, 'utf-8'); + const changelogIncludesTodaysReleaseNotes = changelogContent.includes(`\n## ${today}`); + + const regex = changelogIncludesTodaysReleaseNotes + ? new RegExp(`## ${today}.*(?=## ${latestDateInChangelog})`, "gs") + : new RegExp(`(?=## ${latestDateInChangelog})`, "gs"); + + const newChangelogContent = changelogContent.replace(regex, newReleaseNotes); + await fs.writeFile(changelogPath, newChangelogContent); + + - name: Check for changes + id: verify-diff + run: | + git diff --quiet . || echo "changed=true" >> $GITHUB_ENV + + - name: Commit and push changes + if: env.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 CHANGELOG.md + git commit -m "Update CHANGELOG.md" + git checkout -b $BRANCH_NAME || git checkout $BRANCH_NAME + git push origin $BRANCH_NAME --force + + - name: Create pull request if not exists + if: env.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 CHANGELOG.md" \ + --body "This PR is auto-generated by a Github Action to update the CHANGELOG.md file." \ + --head $BRANCH_NAME \ + --base main \ + --label "$AUTOMATED_PR_LABEL" + fi + + - name: Approve pull request + if: env.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: Re-approve pull request after update + if: env.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 diff --git a/ct/librenms.sh b/ct/librenms.sh new file mode 100644 index 0000000..84e231e --- /dev/null +++ b/ct/librenms.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://librenms.org + +APP="Librenms" +var_tags="monitoring" +var_cpu="2" +var_ram="2048" +var_disk="4" +var_os="debian" +var_version="12" +var_unprivileged="1" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [ ! -d /opt/librenms ]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_error "Update not supported!" + + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/openproject${CL}" diff --git a/install/librenms-install.sh b/install/librenms-install.sh new file mode 100644 index 0000000..a4458ae --- /dev/null +++ b/install/librenms-install.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/opf/openproject + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + sudo \ + curl \ + mc \ + lsb-release \ + ca-certificates \ + wget \ + acl \ + fping \ + graphviz \ + imagemagick \ + mariadb-client \ + mariadb-server \ + mtr-tiny \ + nginx-full \ + nmap \ + php8.2-{cli,fpm,gd,gmp,mbstring,mysql,snmp,xml,zip} \ + python3-{dotenv,pymysql,redis,setuptools,systemd,pip} \ + rrdtool \ + snmp \ + snmpd \ + unzip \ + whois +msg_ok "Installed Dependencies" + +msg_info "Add User" +$STD useradd librenms -d /opt/librenms -M -r -s "$(which bash)" +msg_ok "Add User" + +msg_info "Setup Librenms" +tmp_file=$(mktemp) +RELEASE=$(curl -s https://api.github.com/repos/librenms/librenms/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +wget -q https://github.com/librenms/librenms/archive/refs/tags/${RELEASE}.tar.gz -O $tmp_file +tar -xzf $tmp_file -C /opt +chown -R librenms:librenms /opt/librenms +chmod 771 /opt/librenms +setfacl -d -m g::rwx /opt/librenms/rrd /opt/librenms/logs /opt/librenms/bootstrap/cache/ /opt/librenms/storage/ +setfacl -R -m g::rwx /opt/librenms/rrd /opt/librenms/logs /opt/librenms/bootstrap/cache/ /opt/librenms/storage/ +msg_ok "Setup Librenms" + +msg_info "Setup Composer" +cd /opt +wget -q https://getcomposer.org/composer-stable.phar +mv composer-stable.phar /usr/bin/composer +chmod +x /usr/bin/composer +msg_ok "Setup Composer" + +msg_info "Setup PHP" +sed -i 's/;date.timezone =/date.timezone = UTC/' /etc/php/8.2/cli/php.ini +sed -i 's/;date.timezone =/date.timezone = UTC/' /etc/php/8.2/fpm/php.ini +msg_ok "Setup PHP" + +msg_info "Setup MariaDB" +cat > /etc/mysql/mariadb.conf.d/50-server.cnf << 'EOF' +[mysqld] +innodb_file_per_table=1 +lower_case_table_names=0 +EOF + +systemctl enable -q --now mariadb + + + +msg_ok "Setup MariaDB" + + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned"