diff --git a/.github/jactionlint.yaml b/.github/jactionlint.yaml new file mode 100644 index 0000000000..07d42f9c26 --- /dev/null +++ b/.github/jactionlint.yaml @@ -0,0 +1 @@ +profile: pedantic diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock new file mode 100644 index 0000000000..0de0334bcc --- /dev/null +++ b/.github/workflows/actions.lock @@ -0,0 +1,83 @@ +# This file is machine-generated by `gh actions-lock`. +# Do not edit by hand; run `gh actions-lock` to update. +# Docs: https://gh.io/actions-lockfile +version: 'v0.0.3' +workflows: + '.github/workflows/build.yaml': + - 'actions/checkout@v7.0.1' + - 'actions/upload-artifact@v7.0.2' + - 'docker/build-push-action@v7.4.0' + - 'docker/setup-buildx-action@v4.4.1' + '.github/workflows/clean-manifests.yaml': + - 'actions/checkout@v7.0.1' + - 'actions/upload-artifact@v7.0.2' + '.github/workflows/lock-threads.yaml': + - 'dessant/lock-threads@v6.0.2' + '.github/workflows/main.yaml': + - 'actions/checkout@v7.0.1' + - 'actions/download-artifact@v8.0.2' + - 'docker/login-action@v4.7.0' + '.github/workflows/merge-forward.yaml': + - 'actions/checkout@v7.0.1' + '.github/workflows/nightly.yaml': + - 'actions/checkout@v7.0.1' + - 'actions/download-artifact@v8.0.2' + - 'docker/login-action@v4.7.0' + '.github/workflows/pr-quality.yaml': + - 'peakoss/anti-slop@v0.3.0' + '.github/workflows/pull-request.yaml': + - 'actions/download-artifact@v8.0.2' + - 'docker/login-action@v4.7.0' + '.github/workflows/release.yaml': + - 'actions/checkout@v7.0.1' + - 'actions/download-artifact@v8.0.2' + - 'docker/login-action@v4.7.0' + '.github/workflows/request-docs-update.yaml': [] + '.github/workflows/stale.yaml': + - 'actions/stale@v11.0.0' +dependencies: + 'actions/checkout@v7.0.1': + ref: 'v7.0.1' + commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' + owner_id: 44036562 + repo_id: 197814629 + 'actions/download-artifact@v8.0.2': + ref: 'v8.0.2' + commit: 'sha1-9000827ccba6bdab643e8b6fd33ac0654aef8333' + owner_id: 44036562 + repo_id: 192626254 + 'actions/stale@v11.0.0': + ref: 'v11.0.0' + commit: 'sha1-4391f3da665fdf50b6810c1a66712fb9ba21aa93' + owner_id: 44036562 + repo_id: 200437066 + 'actions/upload-artifact@v7.0.2': + ref: 'v7.0.2' + commit: 'sha1-cf430e030ddbb5b0abf93d22962f4752f3646cd9' + owner_id: 44036562 + repo_id: 192625955 + 'dessant/lock-threads@v6.0.2': + ref: 'v6.0.2' + commit: 'sha1-89ae32b08ed1a541efecbab17912962a5e38981c' + owner_id: 7513068 + repo_id: 113918130 + 'docker/build-push-action@v7.4.0': + ref: 'v7.4.0' + commit: 'sha1-c3c9e263c25d99ce0380d002d59b67737d91b0dc' + owner_id: 5429470 + repo_id: 241092383 + 'docker/login-action@v4.7.0': + ref: 'v4.7.0' + commit: 'sha1-81c6f93d4bc991e510b342a00b419b3bb4efc5c6' + owner_id: 5429470 + repo_id: 287743349 + 'docker/setup-buildx-action@v4.4.1': + ref: 'v4.4.1' + commit: 'sha1-f87e5991a6d7451dcb8d9637bfbc97413f497069' + owner_id: 5429470 + repo_id: 288485773 + 'peakoss/anti-slop@v0.3.0': + ref: 'v0.3.0' + commit: 'sha1-57858eead489d08b255fab2af45a506c2ca6eab2' + owner_id: 227272980 + repo_id: 1153153658 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 71efd82429..5e79fe2c4e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -38,7 +38,7 @@ jobs: coolify: ${{ inputs.coolify || steps.plan.outputs.coolify }} steps: - name: Check out the repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: ref: ${{ inputs.ref }} persist-credentials: false @@ -71,7 +71,7 @@ jobs: runner: ubuntu-26.04-arm steps: - name: Check out the repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: ref: ${{ inputs.ref }} persist-credentials: false @@ -85,12 +85,12 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Set up Buildx - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # https://github.com/docker/setup-buildx-action/releases/tag/v4.4.1 + uses: docker/setup-buildx-action@v4.4.1 with: cache-binary: false - name: Build the image - uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # https://github.com/docker/build-push-action/releases/tag/v7.4.0 + uses: docker/build-push-action@v7.4.0 with: context: . file: docker/production/Dockerfile @@ -103,7 +103,7 @@ jobs: GIT_SHA=${{ steps.args.outputs.sha }} - name: Upload the image - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # https://github.com/actions/upload-artifact/releases/tag/v7.0.2 + uses: actions/upload-artifact@v7.0.2 with: name: image-${{ matrix.arch }} path: ${{ runner.temp }}/image diff --git a/.github/workflows/chore-lock-closed-issues-discussions-and-prs.yml b/.github/workflows/chore-lock-closed-issues-discussions-and-prs.yml deleted file mode 100644 index 6fd8212e61..0000000000 --- a/.github/workflows/chore-lock-closed-issues-discussions-and-prs.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Lock closed Issues, Discussions, and PRs - -on: - schedule: - - cron: '0 1 * * *' - -permissions: - issues: write - discussions: write - pull-requests: write - -jobs: - lock-threads: - runs-on: ubuntu-latest - steps: - - name: Lock threads after 30 days of inactivity - uses: dessant/lock-threads@89ae32b08ed1a541efecbab17912962a5e38981c # v6.0.2 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - issue-inactive-days: '30' - discussion-inactive-days: '30' - pr-inactive-days: '30' diff --git a/.github/workflows/chore-manage-pr-branch.yaml b/.github/workflows/chore-manage-pr-branch.yaml deleted file mode 100644 index 65c81ab717..0000000000 --- a/.github/workflows/chore-manage-pr-branch.yaml +++ /dev/null @@ -1,182 +0,0 @@ -name: Manage PR Branch - -# Runs *after* the "PR Quality" workflow finishes. This is required because -# PR Quality may close a PR that fails its checks, so we must wait for it to -# complete before deciding whether to retarget the PR's base branch. -on: - workflow_run: - workflows: ["PR Quality"] - types: - - completed - -permissions: - contents: read - pull-requests: write - -concurrency: - group: manage-pr-branch-${{ github.event.workflow_run.head_sha }} - cancel-in-progress: true - -jobs: - manage-branch: - runs-on: ubuntu-latest - steps: - - name: Retarget PR base branch based on category - uses: actions/github-script@v7 - with: - script: | - const run = context.payload.workflow_run; - - // Branch routing based on the "Category" section of the PR body. - // Bug fixes and one-click service changes ship in patch releases -> main. - // Everything else (features, improvements) -> next. - const MAIN_BRANCH = 'main'; - const NEXT_BRANCH = 'next'; - - // Maintainers/collaborators are trusted to pick their own base branch. - const EXEMPT_ASSOCIATIONS = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']); - - // Resolve the open PR from the triggering run. - // - // PR Quality runs on `pull_request_target`, so `run.head_sha` is the - // *base* branch tip, not the PR head — a commit-based lookup finds - // nothing. Instead match on the source branch (`head_branch`) and its - // owner (`head_repository.owner.login`), which uniquely identify the PR - // via the `owner:branch` head filter. This also works for forked PRs, - // where `workflow_run.pull_requests` is empty. - const headOwner = run.head_repository?.owner?.login; - const headBranch = run.head_branch; - - let prRef; - if (headOwner && headBranch) { - const { data: openPrs } = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - head: `${headOwner}:${headBranch}`, - per_page: 100, - }); - prRef = openPrs[0]; - } - - // Fallback: same-repo PRs may also be resolvable by commit association. - if (!prRef) { - const { data: associated } = await github.rest.repos.listPullRequestsAssociatedWithCommit({ - owner: context.repo.owner, - repo: context.repo.repo, - commit_sha: run.head_sha, - }); - prRef = associated.find(pr => pr.state === 'open'); - } - - if (!prRef) { - core.info('No open PR associated with this run (possibly closed by PR Quality). Skipping.'); - return; - } - - // Fetch the full PR to get an up-to-date body, base ref, and state. - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: prRef.number, - }); - - if (pr.state !== 'open') { - core.info(`PR #${pr.number} is not open. Skipping.`); - return; - } - - // Skip PRs opened by owners/members/collaborators — they choose their own base. - if (EXEMPT_ASSOCIATIONS.has(pr.author_association)) { - core.info(`PR #${pr.number} author association is ${pr.author_association}. Skipping.`); - return; - } - - // Skip if a maintainer has already changed the base branch manually. - const timeline = await github.paginate(github.rest.issues.listEventsForTimeline, { - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - per_page: 100, - }); - - const baseChanges = timeline.filter(e => e.event === 'base_ref_changed'); - for (const change of baseChanges) { - const actor = change.actor?.login; - if (!actor) { - continue; - } - try { - const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ - owner: context.repo.owner, - repo: context.repo.repo, - username: actor, - }); - // admin/maintain/write => trusted maintainer. - if (['admin', 'maintain', 'write'].includes(perm.permission)) { - core.info(`Base branch was changed manually by ${actor} (${perm.permission}). Skipping.`); - return; - } - } catch (error) { - core.info(`Could not resolve permission for ${actor}: ${error.message}`); - } - } - - // Parse the checked category checkboxes from the PR body. - const body = pr.body ?? ''; - const checked = []; - const checkboxRegex = /^\s*-\s*\[([ xX])\]\s*(.+?)\s*$/gm; - let match; - while ((match = checkboxRegex.exec(body)) !== null) { - if (match[1].toLowerCase() === 'x') { - checked.push(match[2].toLowerCase()); - } - } - - const includesAny = (labels) => labels.some(label => checked.some(c => c.includes(label))); - - const mainCategories = ['bug fix', 'adding new one click service', 'fixing or updating existing one click service']; - const nextCategories = ['improvement', 'new feature']; - - const wantsMain = includesAny(mainCategories); - const wantsNext = includesAny(nextCategories); - - if (!wantsMain && !wantsNext) { - core.info('No category selected in the PR body. Skipping.'); - return; - } - - // If categories from both groups are checked, prefer next: features and - // improvements can only be released from the development branch. - const targetBranch = wantsNext ? NEXT_BRANCH : MAIN_BRANCH; - - if (pr.base.ref === targetBranch) { - core.info(`PR #${pr.number} already targets ${targetBranch}. Nothing to do.`); - return; - } - - const previousBranch = pr.base.ref; - - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: pr.number, - base: targetBranch, - }); - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: [ - `Based on the selected category, this PR's base branch was automatically changed from \`${previousBranch}\` to \`${targetBranch}\`.`, - '', - targetBranch === MAIN_BRANCH - ? 'Bug fixes and one-click service changes target `main`.' - : 'New features and improvements target `next`.', - '', - 'If you believe this is incorrect, please let a maintainer know.', - ].join('\n'), - }); - - core.info(`Retargeted PR #${pr.number}: ${previousBranch} -> ${targetBranch}.`); diff --git a/.github/workflows/chore-manage-stale-issues-and-prs.yml b/.github/workflows/chore-manage-stale-issues-and-prs.yml deleted file mode 100644 index d610055497..0000000000 --- a/.github/workflows/chore-manage-stale-issues-and-prs.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Manage Stale Issues and PRs - -on: - schedule: - - cron: '0 2 * * *' - -permissions: - issues: write - pull-requests: write - -jobs: - manage-stale: - runs-on: ubuntu-latest - steps: - - name: Manage stale issues and PRs - uses: actions/stale@v9 - id: stale - with: - stale-issue-message: 'This issue will be automatically closed in a few days if no response is received. Please provide an update with the requested information.' - stale-pr-message: 'This pull request requires attention. If no changes or response is received within the next few days, it will be automatically closed. Please update your PR or leave a comment with the requested information.' - close-issue-message: 'This issue has been automatically closed due to inactivity.' - close-pr-message: 'Thank you for your contribution. Due to inactivity, this PR was automatically closed. If you would like to continue working on this change in the future, feel free to reopen this PR or submit a new one.' - days-before-stale: 14 - days-before-close: 7 - stale-issue-label: '⏱︎ Stale' - stale-pr-label: '⏱︎ Stale' - only-labels: '💤 Waiting for feedback, 💤 Waiting for changes' - remove-stale-when-updated: true - operations-per-run: 100 - labels-to-remove-when-unstale: '⏱︎ Stale, 💤 Waiting for feedback, 💤 Waiting for changes' - close-issue-reason: 'not_planned' - exempt-all-milestones: false diff --git a/.github/workflows/clean-manifests.yaml b/.github/workflows/clean-manifests.yaml index 1679ad669f..a0b8287afe 100644 --- a/.github/workflows/clean-manifests.yaml +++ b/.github/workflows/clean-manifests.yaml @@ -28,7 +28,7 @@ jobs: timeout-minutes: 60 steps: - name: Check out the repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 with: persist-credentials: false @@ -41,7 +41,7 @@ jobs: - name: Upload the list of manifests to delete if: ${{ always() }} - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + uses: actions/upload-artifact@v7.0.2 with: name: manifests-to-delete path: scripts/manifests-to-delete.tsv diff --git a/.github/workflows/cleanup-ghcr-untagged.yml b/.github/workflows/cleanup-ghcr-untagged.yml deleted file mode 100644 index b66767cda8..0000000000 --- a/.github/workflows/cleanup-ghcr-untagged.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Cleanup Untagged GHCR Images - -on: - workflow_dispatch: - -permissions: - packages: write - -jobs: - cleanup-all-packages: - runs-on: ubuntu-latest - strategy: - matrix: - package: ['coolify', 'coolify-helper'] - steps: - - name: Delete untagged ${{ matrix.package }} images - uses: actions/delete-package-versions@v5 - with: - package-name: ${{ matrix.package }} - package-type: 'container' - min-versions-to-keep: 0 - delete-only-untagged-versions: 'true' diff --git a/.github/workflows/lock-threads.yaml b/.github/workflows/lock-threads.yaml new file mode 100644 index 0000000000..aa8c8912e3 --- /dev/null +++ b/.github/workflows/lock-threads.yaml @@ -0,0 +1,30 @@ +name: Lock threads + +on: + schedule: + - cron: "0 1 * * *" + +permissions: {} + +cache-mode: none + +concurrency: + group: lock-threads + cancel-in-progress: false + +jobs: + lock: + name: Lock threads + permissions: + issues: write # lock the closed issues + discussions: write # lock the closed discussions + pull-requests: write # lock the closed pull requests + runs-on: ubuntu-26.04 # https://github.com/actions/runner-images + timeout-minutes: 10 + steps: + - name: Lock closed threads that were inactive for the last 30 days + uses: dessant/lock-threads@v6.0.2 + with: + issue-inactive-days: 30 + discussion-inactive-days: 30 + pr-inactive-days: 30 diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 8f64941d57..f0dbd9a07c 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -29,7 +29,7 @@ jobs: build: ${{ steps.plan.outputs.build }} steps: - name: Check out main - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: ref: main fetch-depth: 0 @@ -67,13 +67,13 @@ jobs: timeout-minutes: 30 steps: - name: Download the built images - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # https://github.com/actions/download-artifact/releases/tag/v8.0.2 + uses: actions/download-artifact@v8.0.2 with: pattern: image-* path: ${{ runner.temp }}/images - name: Log in to ghcr.io - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # https://github.com/docker/login-action/releases/tag/v4.7.0 + uses: docker/login-action@v4.7.0 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/merge-forward.yaml b/.github/workflows/merge-forward.yaml index cd18c98edb..9d948ea118 100644 --- a/.github/workflows/merge-forward.yaml +++ b/.github/workflows/merge-forward.yaml @@ -26,7 +26,7 @@ jobs: timeout-minutes: 10 steps: - name: Check out the repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 49755a44e7..e21e1269f1 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -29,7 +29,7 @@ jobs: nightly: ${{ steps.plan.outputs.nightly }} steps: - name: Check out next - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: ref: next fetch-depth: 0 @@ -73,20 +73,20 @@ jobs: line: ${{ fromJSON(needs.plan.outputs.nightly).lines }} steps: - name: Download the built images - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # https://github.com/actions/download-artifact/releases/tag/v8.0.2 + uses: actions/download-artifact@v8.0.2 with: pattern: image-* path: ${{ runner.temp }}/images - name: Log in to ghcr.io - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # https://github.com/docker/login-action/releases/tag/v4.7.0 + uses: docker/login-action@v4.7.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Log in to docker.io - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # https://github.com/docker/login-action/releases/tag/v4.7.0 + uses: docker/login-action@v4.7.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -118,7 +118,7 @@ jobs: timeout-minutes: 10 steps: - name: Check out the nightly - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: ref: ${{ fromJSON(needs.plan.outputs.nightly).revision }} persist-credentials: false diff --git a/.github/workflows/pr-quality.yaml b/.github/workflows/pr-quality.yaml index 5913496ddc..878b453ee2 100644 --- a/.github/workflows/pr-quality.yaml +++ b/.github/workflows/pr-quality.yaml @@ -1,19 +1,31 @@ -name: PR Quality - -permissions: - contents: read - issues: read - pull-requests: write +name: PR quality on: + # Checks pull requests from forks too, it checks out and runs no code of the pull request + # jactionlint ignore=dangerous-triggers pull_request_target: types: [opened, reopened] +permissions: {} + +cache-mode: none + +concurrency: + group: pr-quality-${{ github.event.pull_request.number }} + cancel-in-progress: false + jobs: - pr-quality: - runs-on: ubuntu-latest + check: + name: Check pull request + permissions: + contents: read + issues: read # read the linked issues + pull-requests: write # comment on, label and close the pull request + runs-on: ubuntu-26.04 # https://github.com/actions/runner-images + timeout-minutes: 10 steps: - - uses: peakoss/anti-slop@v0 + - name: Check the pull request + uses: peakoss/anti-slop@v0.3.0 with: # General Settings max-failures: 4 @@ -37,7 +49,7 @@ jobs: max-emoji-count: 2 max-code-references: 5 require-linked-issue: false - blocked-terms: | + blocked-terms: | STRAWBERRY 🤖 Generated with Claude Code Generated with Claude Code diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 0f511c716d..42a585f23d 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -61,13 +61,13 @@ jobs: timeout-minutes: 30 steps: - name: Download the built images - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # https://github.com/actions/download-artifact/releases/tag/v8.0.2 + uses: actions/download-artifact@v8.0.2 with: pattern: image-* path: ${{ runner.temp }}/images - name: Log in to ghcr.io - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # https://github.com/docker/login-action/releases/tag/v4.7.0 + uses: docker/login-action@v4.7.0 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d6614d9027..ab98ab102a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -37,7 +37,7 @@ jobs: releases: ${{ steps.plan.outputs.releases }} steps: - name: Check out the repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 0 persist-credentials: false @@ -74,25 +74,25 @@ jobs: release: ${{ fromJSON(needs.plan.outputs.releases) }} steps: - name: Check out the repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: persist-credentials: false - name: Download the built images - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # https://github.com/actions/download-artifact/releases/tag/v8.0.2 + uses: actions/download-artifact@v8.0.2 with: pattern: image-* path: ${{ runner.temp }}/images - name: Log in to ghcr.io - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # https://github.com/docker/login-action/releases/tag/v4.7.0 + uses: docker/login-action@v4.7.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Log in to docker.io - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # https://github.com/docker/login-action/releases/tag/v4.7.0 + uses: docker/login-action@v4.7.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -136,7 +136,7 @@ jobs: tags: ${{ steps.plan.outputs.tags }} steps: - name: Check out the repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 0 persist-credentials: false @@ -148,14 +148,14 @@ jobs: echo "tags=$tags" | tee -a "$GITHUB_OUTPUT" - name: Log in to ghcr.io - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # https://github.com/docker/login-action/releases/tag/v4.7.0 + uses: docker/login-action@v4.7.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Log in to docker.io - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # https://github.com/docker/login-action/releases/tag/v4.7.0 + uses: docker/login-action@v4.7.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -190,7 +190,7 @@ jobs: timeout-minutes: 10 steps: - name: Check out the repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # https://github.com/actions/checkout/releases/tag/v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/chore-pr-comments.yml b/.github/workflows/request-docs-update.yaml similarity index 68% rename from .github/workflows/chore-pr-comments.yml rename to .github/workflows/request-docs-update.yaml index 821fef1778..03db87b200 100644 --- a/.github/workflows/chore-pr-comments.yml +++ b/.github/workflows/request-docs-update.yaml @@ -1,15 +1,30 @@ -name: Add comment based on label -on: - pull_request_target: - types: - - labeled +name: Request docs update -permissions: - pull-requests: write +on: + # Comments on pull requests from forks too, it checks out and runs no code of the pull request + # jactionlint ignore=dangerous-triggers + pull_request_target: + types: [labeled] + +permissions: {} + +cache-mode: none + +concurrency: + group: request-docs-update-${{ github.event.pull_request.number }} + cancel-in-progress: false + +defaults: + run: + shell: bash jobs: - add-comment: - runs-on: ubuntu-latest + comment: + name: Request docs update (${{ matrix.label }}) + permissions: + pull-requests: write # comment on the pull request + runs-on: ubuntu-26.04 # https://github.com/actions/runner-images + timeout-minutes: 10 strategy: matrix: include: @@ -33,20 +48,15 @@ jobs: Coolify Docs Repository: https://github.com/coollabsio/coolify-docs How to Contribute to the Docs: https://coolify.io/docs/get-started/contribute/documentation - # - label: "✨ Enhancement" - # body: | - # It appears to us that you are making an enhancement to Coolify. - # We kindly ask you to also review and update the Coolify Documentation to include information about this enhancement if applicable. - # This will help ensure that our documentation remains accurate and up-to-date for all users. steps: - - name: Add comment + - name: Comment on the pull request if: >- - (github.event.label.name == matrix.label || github.event.label.name == '📑 Waiting for Docs PR') + ${{ (github.event.label.name == matrix.label || github.event.label.name == '📑 Waiting for Docs PR') && contains(github.event.pull_request.labels.*.name, matrix.label) - && contains(github.event.pull_request.labels.*.name, '📑 Waiting for Docs PR') - run: gh pr comment "$NUMBER" --body "$BODY" + && contains(github.event.pull_request.labels.*.name, '📑 Waiting for Docs PR') }} env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} NUMBER: ${{ github.event.pull_request.number }} BODY: ${{ matrix.body }} + run: gh pr comment "$NUMBER" --body "$BODY" diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 0000000000..5484314ebd --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,37 @@ +name: Stale + +on: + schedule: + - cron: "0 2 * * *" + +permissions: {} + +cache-mode: none + +concurrency: + group: stale + cancel-in-progress: false + +jobs: + stale: + name: Close stale issues and pull requests + permissions: + issues: write # label, comment on and close the stale issues + pull-requests: write # label, comment on and close the stale pull requests + runs-on: ubuntu-26.04 # https://github.com/actions/runner-images + timeout-minutes: 10 + steps: + - name: Close the issues and pull requests that wait for feedback or changes + uses: actions/stale@v11.0.0 + with: + only-labels: "💤 Waiting for feedback, 💤 Waiting for changes" + days-before-stale: 14 + days-before-close: 7 + stale-issue-label: "⏱︎ Stale" + stale-pr-label: "⏱︎ Stale" + labels-to-remove-when-unstale: "⏱︎ Stale, 💤 Waiting for feedback, 💤 Waiting for changes" + stale-issue-message: This issue will be automatically closed in a few days if no response is received. Please provide an update with the requested information. + stale-pr-message: This pull request requires attention. If no changes or response is received within the next few days, it will be automatically closed. Please update your PR or leave a comment with the requested information. + close-issue-message: This issue has been automatically closed due to inactivity. + close-pr-message: Thank you for your contribution. Due to inactivity, this PR was automatically closed. If you would like to continue working on this change in the future, feel free to reopen this PR or submit a new one. + operations-per-run: 100 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d47cb9a17..e8eb867ebf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +### Security + +- Refuse to update, force-push or delete the git tag of a release, with a tag protection rule on GitHub +- Refuse to delete or force-push `main`, `next`, release and maintenance branches, with branch protection rules on GitHub +- Protect archive branches from updates, deletions and force-pushing, with branch protection rules on GitHub +- Harden the CI workflows + - Require every action to be pinned to a full-length commit SHA + - Grant every job the least permissions it needs and state the reason for every write permission + - Keep the Docker Hub credentials and the CDN token in the `release` environment, which only the branches that release can use + - Keep the tokens that delete images in the `cleanup` environment, which only `main` can use + - Lock every action and the actions it calls to a commit in `.github/workflows/actions.lock`, together with the IDs of its owner and repository, which a commit SHA alone does not check + - Run without the GitHub Actions cache, layer caches and cached tool binaries, so no run restores what another run wrote + - Fix every finding of jactionlint pedantic + ### Added - Choose the update channel of an instance on the update page: stable, preview with betas and release candidates, nightly with a warning that breaking changes are possible, or a custom image such as `pr-1234` @@ -48,6 +62,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Removed +- Remove the workflow that deleted every untagged image on ghcr.io, one run would delete the platform images and attestations of every release +- Remove the workflow that moved pull requests to `main` or `next` by their category, for now - Remove the Windows support with Docker Desktop, `docker-compose.windows.yml` and `.env.windows-docker-desktop.example`, and the testing host image on Docker Hub, which only it used - Remove the Claude workflow and the temporary workflow and image of the PHP base image - Remove the git-cliff workflow that generated `CHANGELOG.md`, it is written by hand @@ -60,4 +76,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Maintenance +- Add `scripts/clean-manifests.sh` and a workflow that delete the unused manifests from ghcr.io and docker.io, such as the leftovers of the old workflows and the images of closed pull requests, and keep every release, every moving tag, the nightlies of the last 14 days and the images of open pull requests +- Give every job a name, a timeout, a fixed runner image and a concurrency group +- Bump `actions/stale` to v11, GitHub Actions removed the Node.js 20 it ran on - Describe the new release process in `AGENTS.md`