diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..bffa156 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help fixing issues +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots & Logs** +If applicable, add screenshots/logs to help explain your problem. + +**Please complete the following information:** + - OS: [e.g. Fedora Linux 42 x86_64] + - Architecture [e.g. amd64, arm64] + - Docker Version + - Remediarr Version [e.g. 0.1.9] + +**Additional context** +Add any other context about the problem here. Such as logs from Remediarr , Sonarr, Radarr. .env file(with API removed) docker compose file as well. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/changelog-config.json b/.github/changelog-config.json new file mode 100644 index 0000000..9611b8c --- /dev/null +++ b/.github/changelog-config.json @@ -0,0 +1,15 @@ +{ + "template": "# Changelog for {{currentTag}}\n\n{{#conventionalCommits}}\n- {{#scope}}**{{scope}}:** {{/scope}}{{subject}}\n{{/conventionalCommits}}\n", + "categories": [ + { "title": "### πŸš€ Features", "labels": ["feat", "feature"], "collapseAfter": 0 }, + { "title": "### πŸ› Fixes", "labels": ["fix", "bug"], "collapseAfter": 0 }, + { "title": "### 🧹 Chore", "labels": ["chore"], "collapseAfter": 0 }, + { "title": "### πŸ“ Docs", "labels": ["docs"], "collapseAfter": 0 }, + { "title": "### πŸ”§ Refactor", "labels": ["refactor"], "collapseAfter": 0 } + ], + "replacers": [], + "sort": { + "order": "ASC", + "onProperty": "scope" + } +} \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..60f7cc1 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,18 @@ +name-template: 'v$NEXT_PATCH_VERSION' +tag-template: 'v$NEXT_PATCH_VERSION' +categories: + - title: 'πŸš€ Features' + labels: ['feature', 'enhancement'] + - title: 'πŸ› Fixes' + labels: ['fix', 'bug'] + - title: '🧰 Maintenance' + labels: ['chore', 'maintenance', 'deps'] +change-template: '- $TITLE (#$NUMBER) by @$AUTHOR' +no-changes-template: 'Minor internal improvements.' +template: | + ## What’s Changed + $CHANGES + + ## Container Images + - `ghcr.io/${{ repo_owner }}/${{ repo_name }}:latest` + - `ghcr.io/${{ repo_owner }}/${{ repo_name }}:$NEXT_PATCH_VERSION` diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000..26a1ba0 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,35 @@ +name: Generate Changelog on Tag + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + changelog: + runs-on: ubuntu-latest + + steps: + - name: Checkout (full history) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build changelog from commits + id: build + uses: mikepenz/release-changelog-builder-action@v4 + with: + configuration: .github/changelog-config.json + ignorePreReleases: false + toTag: ${{ github.ref_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create / Update GitHub Release notes + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + body: ${{ steps.build.outputs.changelog }} \ No newline at end of file diff --git a/.github/workflows/ghcr-dev.yml b/.github/workflows/ghcr-dev.yml new file mode 100644 index 0000000..885eb6e --- /dev/null +++ b/.github/workflows/ghcr-dev.yml @@ -0,0 +1,67 @@ +name: Build & Push DEV to DockerHub + +on: + pull_request: + branches: [ dev ] + push: + branches: [ dev ] + +permissions: + contents: read + +jobs: + docker: + runs-on: ubuntu-latest + + env: + # PRs: single-arch build (faster). Push to dev: multi-arch. + PLATFORMS: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Read VERSION and compute DEV tags + run: | + if [[ ! -f VERSION ]]; then echo "VERSION file missing"; exit 1; fi + RAW=$(tr -d ' \n' < VERSION) + if [[ -z "$RAW" ]]; then echo "VERSION empty"; exit 1; fi + # Remove 'v' prefix if present for version number + if [[ "$RAW" =~ ^v ]]; then NUM="${RAW#v}"; else NUM="$RAW"; fi + + # Get repository name (assumes format: owner/repo) + REPO_NAME=$(echo "${GITHUB_REPOSITORY}" | cut -d'/' -f2) + + echo "VERSION_NUM=$NUM" >> $GITHUB_ENV + echo "DEV_VERSION=${NUM}-dev" >> $GITHUB_ENV + echo "DOCKER_REPO=${{ secrets.DOCKER_HUB_USERNAME }}/${REPO_NAME}" >> $GITHUB_ENV + echo "DEV_TAGS=${{ secrets.DOCKER_HUB_USERNAME }}/${REPO_NAME}:${NUM}-dev,${{ secrets.DOCKER_HUB_USERNAME }}/${REPO_NAME}:dev" >> $GITHUB_ENV + echo "Computed DEV_VERSION=${NUM}-dev" + + - name: Set up QEMU + if: ${{ env.PLATFORMS == 'linux/amd64,linux/arm64' }} + uses: docker/setup-qemu-action@v3 + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to DockerHub + if: ${{ github.event_name != 'pull_request' }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + + - name: Build & (maybe) push + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: ${{ env.PLATFORMS }} + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ env.DEV_TAGS }} + build-args: | + APP_VERSION=${{ env.DEV_VERSION }} + cache-from: type=gha + cache-to: type=gha,mode=max + provenance: false diff --git a/.github/workflows/ghcr-main.yml b/.github/workflows/ghcr-main.yml new file mode 100644 index 0000000..dc1e2e0 --- /dev/null +++ b/.github/workflows/ghcr-main.yml @@ -0,0 +1,107 @@ +name: Build & Publish to DockerHub (from VERSION + Release) + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +permissions: + contents: write # create tag + release + pull-requests: read + +jobs: + docker: + runs-on: ubuntu-latest + + env: + PLATFORMS: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Read VERSION + id: ver + run: | + if [[ ! -f VERSION ]]; then echo "VERSION missing"; exit 1; fi + RAW=$(tr -d ' \n' < VERSION) + if [[ -z "$RAW" ]]; then echo "VERSION empty"; exit 1; fi + if [[ "$RAW" =~ ^v ]]; then TAG="$RAW"; NUM="${RAW#v}"; else TAG="v${RAW}"; NUM="$RAW"; fi + + # Get repository name for DockerHub + REPO_NAME=$(echo "${GITHUB_REPOSITORY}" | cut -d'/' -f2) + + echo "TAG=$TAG" >> $GITHUB_ENV + echo "NUM=$NUM" >> $GITHUB_ENV + echo "DOCKER_REPO=${{ secrets.DOCKER_HUB_USERNAME }}/${REPO_NAME}" >> $GITHUB_ENV + echo "DOCKER_TAGS=${{ secrets.DOCKER_HUB_USERNAME }}/${REPO_NAME}:${NUM},${{ secrets.DOCKER_HUB_USERNAME }}/${REPO_NAME}:latest" >> $GITHUB_ENV + echo "Resolved TAG=$TAG NUM=$NUM" + + - name: Create tag (if missing) + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + uses: actions/github-script@v7 + with: + script: | + const tag = process.env.TAG; + const sha = context.sha; + const { owner, repo } = context.repo; + try { + await github.rest.git.getRef({ owner, repo, ref: `tags/${tag}` }); + core.info(`Tag ${tag} exists`); + } catch (e) { + if (e.status === 404) { + core.info(`Creating tag ${tag} -> ${sha}`); + await github.rest.git.createRef({ owner, repo, ref: `refs/tags/${tag}`, sha }); + } else { throw e; } + } + + - name: Update draft release notes + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + id: drafter + uses: release-drafter/release-drafter@v6 + with: + config-name: release-drafter.yml + publish: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish GitHub Release + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.TAG }} + name: NFOGuard ${{ env.TAG }} + draft: false + prerelease: false + body: ${{ steps.drafter.outputs.body }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + if: ${{ env.PLATFORMS == 'linux/amd64,linux/arm64' }} + uses: docker/setup-qemu-action@v3 + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to DockerHub + if: ${{ github.event_name != 'pull_request' }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + + - name: Build & Push (main) + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: ${{ github.event_name != 'pull_request' }} + platforms: ${{ env.PLATFORMS }} + tags: ${{ env.DOCKER_TAGS }} + build-args: | + APP_VERSION=${{ env.NUM }} + cache-from: type=gha + cache-to: type=gha,mode=max + provenance: false diff --git a/.github/workflows/notify-issues.yml b/.github/workflows/notify-issues.yml new file mode 100644 index 0000000..a762f25 --- /dev/null +++ b/.github/workflows/notify-issues.yml @@ -0,0 +1,66 @@ +name: Notify on new issues + +on: + issues: + types: [opened] + workflow_dispatch: {} + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Validate required secrets + run: | + if [[ -z "${{ secrets.GOTIFY_URL }}" ]]; then + echo "Error: GOTIFY_URL secret is not set" + exit 1 + fi + if [[ -z "${{ secrets.GOTIFY_TOKEN }}" ]]; then + echo "Error: GOTIFY_TOKEN secret is not set" + exit 1 + fi + echo "Secrets validation passed" + + - name: Build and send Gotify notification + env: + GOTIFY_URL: ${{ secrets.GOTIFY_URL }} + GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }} + run: | + set -euo pipefail + + # Remove trailing slash from URL if present + GOTIFY_URL="${GOTIFY_URL%/}" + + # Validate URL format + if [[ ! "$GOTIFY_URL" =~ ^https?:// ]]; then + echo "Error: GOTIFY_URL must start with http:// or https://" + exit 1 + fi + + # Build the notification payload + TITLE="New issue in ${{ github.repository }}" + MESSAGE="Issue #${{ github.event.issue.number }}: ${{ github.event.issue.title }} + By: ${{ github.actor }} + ${{ github.event.issue.html_url }}" + + # Create JSON payload + PAYLOAD=$(jq -n \ + --arg title "$TITLE" \ + --arg message "$MESSAGE" \ + --argjson priority 5 \ + '{title: $title, message: $message, priority: $priority}') + + echo "Sending notification to Gotify..." + echo "URL: ${GOTIFY_URL}/message" + echo "Payload: $PAYLOAD" + + # Send the notification using header authentication + curl -X POST "${GOTIFY_URL}/message" \ + -H "Content-Type: application/json" \ + -H "X-Gotify-Key: ${GOTIFY_TOKEN}" \ + -d "$PAYLOAD" \ + --fail \ + --show-error \ + --silent + + echo "Notification sent successfully!" diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..d51a8e4 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,21 @@ +name: Release Drafter +on: + push: + branches: [ main ] + pull_request: + types: [opened, reopened, synchronize, closed] + branches: [ main ] + +permissions: + contents: write + pull-requests: read + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v6 + with: + config-name: release-drafter.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}