diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index dada5509..5cfe821c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,8 +4,8 @@ contact_links: url: https://linuxserver.io/discord about: Realtime support / chat with the community and the team. - - name: Discourse discussion forum - url: https://discourse.linuxserver.io + - name: Github Discussions + url: https://github.com/orgs/linuxserver/discussions about: Post on our community forum. - name: Documentation diff --git a/.github/workflows/external_trigger.yml b/.github/workflows/external_trigger.yml index cc87ba6a..00b51ae7 100644 --- a/.github/workflows/external_trigger.yml +++ b/.github/workflows/external_trigger.yml @@ -10,7 +10,7 @@ jobs: external-trigger-master: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.1 + - uses: linuxserver/github-workflows/.github/actions/checkout@v1 - name: External Trigger if: github.ref == 'refs/heads/master' diff --git a/.github/workflows/external_trigger_scheduler.yml b/.github/workflows/external_trigger_scheduler.yml index 5738dd51..c7d5162f 100644 --- a/.github/workflows/external_trigger_scheduler.yml +++ b/.github/workflows/external_trigger_scheduler.yml @@ -12,7 +12,7 @@ jobs: external-trigger-scheduler: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.1 + - uses: linuxserver/github-workflows/.github/actions/checkout@v1 with: fetch-depth: '0' diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index c5d15101..966f6961 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -12,8 +12,8 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/first-interaction@v1 + - uses: linuxserver/github-workflows/.github/actions/first-interaction@v1 with: - issue-message: 'Thanks for opening your first issue here! Be sure to follow the relevant issue templates, or risk having this issue marked as invalid.' - pr-message: 'Thanks for opening this pull request! Be sure to follow the [pull request template](https://github.com/linuxserver/docker-jenkins-builder/blob/master/.github/PULL_REQUEST_TEMPLATE.md)!' - repo-token: ${{ secrets.GITHUB_TOKEN }} + issue_message: 'Thanks for opening your first issue here! Be sure to follow the relevant issue templates, or risk having this issue marked as invalid.' + pr_message: 'Thanks for opening this pull request! Be sure to follow the [pull request template](https://github.com/linuxserver/docker-jenkins-builder/blob/master/.github/PULL_REQUEST_TEMPLATE.md)!' + repo_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/package_trigger_scheduler.yml b/.github/workflows/package_trigger_scheduler.yml index 71bf0f28..fbc69ced 100644 --- a/.github/workflows/package_trigger_scheduler.yml +++ b/.github/workflows/package_trigger_scheduler.yml @@ -12,7 +12,7 @@ jobs: package-trigger-scheduler: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.1 + - uses: linuxserver/github-workflows/.github/actions/checkout@v1 with: fetch-depth: '0' diff --git a/Dockerfile b/Dockerfile index 2c9ba284..acea0afe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ LABEL maintainer="roxedus, thelamer" RUN \ echo "**** install build packages ****" && \ - YQ_VERSION=v4.52.5 &&\ + YQ_VERSION=v4.53.6 &&\ wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64 -O /usr/bin/yq &&\ chmod +x /usr/bin/yq && \ apk add --no-cache --upgrade \ diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64 index 31fe80e6..347b14e0 100644 --- a/Dockerfile.aarch64 +++ b/Dockerfile.aarch64 @@ -10,7 +10,7 @@ LABEL maintainer="roxedus, thelamer" RUN \ echo "**** install build packages ****" && \ - YQ_VERSION=v4.52.5 &&\ + YQ_VERSION=v4.53.6 &&\ wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_arm64 -O /usr/bin/yq &&\ chmod +x /usr/bin/yq && \ apk add --no-cache --upgrade \ diff --git a/Jenkinsfile b/Jenkinsfile index eda13bb1..a3fc9acb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -583,7 +583,7 @@ pipeline { pids[$!]="$i" done for p in "${!pids[@]}"; do - wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* ]] && exit 1; } + wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]] && exit 1; } done fi ''' @@ -652,7 +652,7 @@ pipeline { pids[$!]="$i" done for p in "${!pids[@]}"; do - wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* ]] && exit 1; } + wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]] && exit 1; } done fi ''' @@ -713,7 +713,7 @@ pipeline { pids[$!]="$i" done for p in "${!pids[@]}"; do - wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* ]] && exit 1; } + wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]] && exit 1; } done fi ''' @@ -895,10 +895,10 @@ pipeline { fi done docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${META_TAG} -t ${PUSHIMAGE}:latest -t ${PUSHIMAGE}:${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ - { if [[ "${PUSHIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${PUSHIMAGE}" != "${QUAYIMAGE}" && "${PUSHIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } if [ -n "${SEMVER}" ]; then docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ - { if [[ "${PUSHIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${PUSHIMAGE}" != "${QUAYIMAGE}" && "${PUSHIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } fi done ''' @@ -931,26 +931,26 @@ pipeline { fi done docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${META_TAG} -t ${MANIFESTIMAGE}:amd64-latest -t ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ - { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${META_TAG} -t ${MANIFESTIMAGE}:arm64v8-latest -t ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} || \ - { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } if [ -n "${SEMVER}" ]; then docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ - { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${SEMVER} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} || \ - { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } fi done for MANIFESTIMAGE in "${IMAGE}" "${GITLABIMAGE}" "${GITHUBIMAGE}" "${QUAYIMAGE}"; do docker buildx imagetools create -t ${MANIFESTIMAGE}:latest ${MANIFESTIMAGE}:amd64-latest ${MANIFESTIMAGE}:arm64v8-latest || \ - { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } docker buildx imagetools create -t ${MANIFESTIMAGE}:${META_TAG} ${MANIFESTIMAGE}:amd64-${META_TAG} ${MANIFESTIMAGE}:arm64v8-${META_TAG} || \ - { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } docker buildx imagetools create -t ${MANIFESTIMAGE}:${EXT_RELEASE_TAG} ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG} ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} || \ - { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } if [ -n "${SEMVER}" ]; then docker buildx imagetools create -t ${MANIFESTIMAGE}:${SEMVER} ${MANIFESTIMAGE}:amd64-${SEMVER} ${MANIFESTIMAGE}:arm64v8-${SEMVER} || \ - { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } + { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } fi done ''' diff --git a/ansible/roles/documentation/templates/README_SNIPPETS/GROUP_BADGES.j2 b/ansible/roles/documentation/templates/README_SNIPPETS/GROUP_BADGES.j2 index 7eec3f13..5acfad30 100644 --- a/ansible/roles/documentation/templates/README_SNIPPETS/GROUP_BADGES.j2 +++ b/ansible/roles/documentation/templates/README_SNIPPETS/GROUP_BADGES.j2 @@ -1,5 +1,5 @@ [![Blog]({{ lsio_shieldsio_static_blog }})]({{ lsio_blog_url }} "{{ lsio_blog_desc }}") [![Discord]({{ lsio_shieldsio_discord }})]({{ lsio_discord_url }} "{{ lsio_discord_desc }}") -[![Discourse]({{ lsio_shieldsio_discourse_topics }})]({{ lsio_discourse_url }} "{{ lsio_discourse_desc }}") +[![Discussions]({{ lsio_shieldsio_discussion }})]({{ lsio_discussion_url }} "{{ lsio_discussion_desc }}") [![GitHub]({{ lsio_shieldsio_static_github }})]({{ lsio_github_url }} "{{ lsio_github_desc }}") [![Open Collective]({{ lsio_shieldsio_opencollective_all }})]({{ lsio_opencollective_url }} "{{ lsio_opencollective_desc }}") diff --git a/ansible/roles/documentation/templates/README_SNIPPETS/GROUP_INFO.j2 b/ansible/roles/documentation/templates/README_SNIPPETS/GROUP_INFO.j2 index d93ff375..67c142d3 100644 --- a/ansible/roles/documentation/templates/README_SNIPPETS/GROUP_INFO.j2 +++ b/ansible/roles/documentation/templates/README_SNIPPETS/GROUP_INFO.j2 @@ -14,6 +14,6 @@ Find us at: * [Blog]({{ lsio_blog_url }}) - {{ lsio_blog_desc }} * [Discord]({{ lsio_discord_url }}) - {{ lsio_discord_desc }} -* [Discourse]({{ lsio_discourse_url }}) - {{ lsio_discourse_desc }} +* [Discussions]({{ lsio_discussion_url }}) - {{ lsio_discussion_desc }} * [GitHub]({{ lsio_github_url }}) - {{ lsio_github_desc }} * [Open Collective]({{ lsio_opencollective_url }}) - {{ lsio_opencollective_desc }} diff --git a/ansible/roles/github/tasks/main.yml b/ansible/roles/github/tasks/main.yml index cffda2ea..4fdcb21b 100644 --- a/ansible/roles/github/tasks/main.yml +++ b/ansible/roles/github/tasks/main.yml @@ -67,3 +67,22 @@ - CONTRIBUTING.md - FUNDING.yml - PULL_REQUEST_TEMPLATE.md + +- name: Populate Github AGENTS file + template: + src: "{{ item }}.j2" + dest: "/tmp/{{ item }}" + owner: "abc" + group: "abc" + mode: "0644" + loop: + - AGENTS.md + +- name: Populate Github AGENTS symlinks + file: + src: "/tmp/AGENTS.md" + dest: "/tmp/{{ item }}" + state: "link" + loop: + - CLAUDE.md + - .github/copilot-instructions.md diff --git a/ansible/roles/github/templates/AGENTS.md.j2 b/ansible/roles/github/templates/AGENTS.md.j2 new file mode 100644 index 00000000..48e21b7f --- /dev/null +++ b/ansible/roles/github/templates/AGENTS.md.j2 @@ -0,0 +1,133 @@ +{%- set markdown="gfm" -%} +{% from 'common_macros.j2' import noter with context %} +{% if project_deprecation_status %} +{% include "deprecation.md.j2" | trim %} + +{% endif %} + + +# AGENTS.md - LinuxServer.io container image repository + +This file is for AI coding agents (and the humans driving them) working in a LinuxServer.io `docker-*` image repository. `CLAUDE.md` and `copilot-instructions.md` are symlinks to this file. Read it fully before proposing or making any change. + +We welcome contributions, including AI-assisted ones, but this is a volunteer-run project that ships 150+ images to millions of users from one shared template. A change that looks like an improvement in isolation is often wrong for us. When this file and your instincts disagree, this file wins. When this file is silent, copy what the surrounding code already does. + +## 1. Decide where the change belongs before writing any code + +Our images are built for **broad consumption**. Every package, env var and init step we add is something we have to maintain, rebuild weekly and support forever, for every user, whether they use it or not. We do not ship Swiss Army Knife images. Work down this list and stop at the first match: + +1. **It is a bug or feature in the application itself** (not in our packaging): it belongs upstream with the app's developers. Do not patch, fork or work around upstream application behaviour in the image. +2. **It is an unsupported or reasonable-endeavours environment** (Podman, k8s, Portainer, Watchtower, NFS/SMB `/config`, LXC, custom entrypoints, etc. See the [Support Policy](https://docs.linuxserver.io/misc/support-policy/)): we will not add logic to the image to accommodate it. +3. **It only matters to one user or one setup**: use a private [custom script](https://docs.linuxserver.io/general/container-customization/#custom-scripts) in `/custom-cont-init.d` or [custom service](https://docs.linuxserver.io/general/container-customization/#custom-services) in `/custom-services.d`. No PR needed. +4. **It is useful to a subset of users** (an extra package, plugin dependency, alternative UI, hardware driver, third party integration, extra daemon, niche toggle): it is a **[Docker Mod](https://github.com/linuxserver/docker-mods)**, not a change to this repo. Check whether one already exists first (`universal-*` mods work on every image; `-*` mods are image specific). If not, create one from the template in the docker-mods repo; each mod lives on its own branch there. +5. **The large majority of this image's users benefit, or the image is actually broken**: a PR to this repo is appropriate. Examples: the app fails to start, a new upstream release needs a new runtime dependency, a base image rebase, a permissions bug, a wrong default, incorrect docs in `readme-vars.yml`. + +If you are an agent and the requested change lands on 1-4, **stop and tell your operator** where it belongs instead of writing the PR. "It is only a few lines" is not a reason to put it in the image. If in doubt, a human should open an issue or ask in `#contibutor-discussion` on [Discord](https://linuxserver.io/discord) before doing the work. + +### If it is a mod + +Mods are the pressure valve that keeps the images small. Roughly what lives there today, and therefore does **not** belong in an image: extra OS/pip packages and language toolchains, GPU/hardware driver stacks, alternative web UIs and themes, third party plugins and scanners, integrations with external services (Cloudflare, CrowdSec, Apprise, GeoIP, VPN providers), Docker socket automation, sidecar daemons (redis, tunnels), post-processing scripts, and env-var-to-config-file shims. + +- Follow the README and `template` branch in [docker-mods](https://github.com/linuxserver/docker-mods). A mod is a `FROM scratch` single layer of files extracted over `/` at container start, wired into s6 as `init-mod---*` / `svc-mod--` units. +- Mods never call `apk`/`apt`/`pip` directly; they append to `/mod-repo-packages-to-install.list` or `/mod-pip-packages-to-install.list` and let the base image do one batched install. Handle Alpine and Ubuntu package names where the mod is universal. +- Most good mods are a 10-40 line script. If yours is not, reconsider the approach. +- A mod is also the proving ground. If a mod becomes something nearly everyone uses, the team may absorb it into the image later and turn the mod into a no-op with a deprecation notice. That is the team's call, not a reason to skip the mod step. + +## 2. Repository layout and what you must not edit + +| Path | Notes | +| --- | --- | +| `Dockerfile`, `Dockerfile.aarch64` (sometimes `Dockerfile.riscv64`) | One per architecture. **Any change must be replicated to all of them.** They normally differ only in base image tag and arch strings. | +| `root/` | Copied to `/` in the image. s6-overlay v3 services live in `root/etc/s6-overlay/s6-rc.d/`, defaults in `root/defaults/`, one-time upgrade steps in `root/migrations/`. | +| `readme-vars.yml` | **Source of truth for the README, docs site, Unraid template and changelog.** Edit this. | +| `jenkins-vars.yml` | Build pipeline variables (version detection, CI test settings). Rarely needs touching. | +| `README.md` | **Generated. Never edit.** | +| `Jenkinsfile` | **Generated. Never edit.** | +| `package_versions.txt` | **Generated by CI. Never edit.** | +| `.github/**`, `.editorconfig`, `LICENSE`, `AGENTS.md`, `CLAUDE.md` | **Generated / globally distributed. Never edit here.** Changes go to [docker-jenkins-builder](https://github.com/linuxserver/docker-jenkins-builder). | + +Do not add new top level files or tooling: no `docker-compose.yml`, `Makefile`, test suites, linter configs, pre-commit hooks, extra workflows, `CHANGELOG.md`, `SECURITY.md`, devcontainers, or helper scripts. The repo is intentionally small. + +Some repos have multiple live branches that publish different tags (`nightly`, `develop`, `libtorrentv1`, etc.). Each is maintained independently. Target the branch the change applies to, never merge one into another, and do not assume `master` or `main` is the only one that matters. + +## 3. Code style: keep it simple, match what is there + +The goal of every image is the thinnest possible layer between our base image and the upstream app: install it, drop a sane default config into `/config` on first run, fix permissions, start it. Prefer deleting logic to adding it. The base image already handles PUID/PGID, `UMASK`, `TZ`, `FILE__` secrets, mods, custom scripts, cron and device permissions; never reimplement those. + +### Dockerfiles + +- Base is always `ghcr.io/linuxserver/baseimage-*`. Do not change distro, switch to upstream/distroless images, or add multi-stage complexity unless a maintainer asked for it. Base image rebases are done by the team. +- Follow the existing shape exactly: a single `RUN` chain joined with `&& \`, each step announced with `echo "**** doing thing ****"`, two space indentation, a `**** cleanup ****` step at the end removing `/tmp/*` and package caches. +- Packages are listed **one per line in alphabetical order**. Build-only dependencies go in a `build-dependencies` virtual package (Alpine) or are purged (Ubuntu) in the same layer. +- Do not hardcode or pin the application version. Keep the `if [ -z ${APP_VERSION+x} ]; then ...` pattern; CI supplies the version as a build arg and `jenkins-vars.yml` defines how it is detected. +- Do not add `HEALTHCHECK`, `USER`, `ENTRYPOINT` or `CMD`. The base image owns init (`/init`), and the container must start as root unless the image already documents non-root support. +- Do not touch the `LABEL maintainer` or `build_version` lines. + +### Init scripts and services (`root/etc/s6-overlay/s6-rc.d/`) + +- Naming: `init--config` (oneshot) and `svc-` (longrun). Wire them in using empty files in `dependencies.d/` and `user/contents.d/`, exactly as the existing ones do. Oneshots have `type`, `up` (the path to `run`) and `run`. No legacy `cont-init.d` / `services.d`. +- Every script starts with: + + ```bash + #!/usr/bin/with-contenv bash + # shellcheck shell=bash + ``` + +- Bash, four space indentation, `[[ ]]` tests, `"${VAR}"` quoting, short lowercase `#` comments. Scripts must be shellcheck clean. +- Use `lsiown` instead of `chown`, run the app as `abc` via `s6-setuidgid abc`, and `exec` the final process so s6 supervises it. Keep `s6-notifyoncheck` readiness checks where they exist. +- If `readme-vars.yml` has `nonroot_supported: true` or `readonly_supported: true`, every new privileged operation (chown, writing outside `/config` and `/run`, `s6-setuidgid`) must be guarded the same way the existing code does it (`if [[ -z ${LSIO_NON_ROOT_USER} ]]; then` etc.). Do not break those modes. +- User data and config live in `/config`. Copy defaults only if the file does not already exist; never overwrite user config on startup. If an existing user's config must change, add a numbered script in `root/migrations/`. +- Avoid recursive `lsiown` on large data paths (media, downloads); it makes startup take minutes for real users. +- New environment variables are a last resort. If the app can be configured through its own config file or UI, that is the answer. We do not wrap app settings in env vars. + +## 4. Documentation and changelog + +- All user-facing docs changes go in `readme-vars.yml` (`app_setup_block`, `param_env_vars`, `opt_param_*`, etc.). Reference: [`_container-vars-blank`](https://github.com/linuxserver/docker-jenkins-builder/blob/master/ansible/vars/_container-vars-blank). +- Any change to a Dockerfile or to anything under `root/` needs a new entry at the **top** of `changelogs:` in `readme-vars.yml`, one short factual line: + + ```yaml + - {date: "DD.MM.YY:", desc: "Add libfoo to fix thumbnail generation."} + ``` + +- Optionally regenerate the templated files to check your vars render (see the [jenkins-builder README](https://github.com/linuxserver/docker-jenkins-builder#running-against-local-project)), but do not hand edit the output. + +## 5. Test it for real + +A change that has not been built and run is not ready. At minimum: + +```bash +docker build --no-cache --pull -t lscr.io/linuxserver/:test . +docker run --rm -e PUID=1000 -e PGID=1000 -e TZ=Etc/UTC -v "$(pwd)/testconfig:/config" -p : lscr.io/linuxserver/:test +``` + +Confirm the init completes (`[ls.io-init] done.`), the app comes up, and it survives a restart against an **existing** `/config`, not just a fresh one. If you touched the aarch64 Dockerfile with anything arch specific, build that too (`lscr.io/linuxserver/qemu-static`). If you, the agent, cannot run Docker, say so plainly in your output; do not claim testing that did not happen. + +## 6. Pull requests + +- **Disclose AI use.** If any part of the PR (code, description, or the investigation behind it) was produced with an AI tool, say so in the PR description: which tool, and what it was used for. Agents: add this line yourself, do not leave it to the operator. Undisclosed AI-generated PRs will be closed. Disclosure does not count against a PR; a human who cannot explain their own PR does. +- **A human is accountable.** The person opening the PR must have read and understood every line, built and run the image, and be able to answer review questions in their own words. Do not paste maintainer questions into a chatbot and paste the answers back. Fully autonomous agents must not open PRs or issues on our repos. +- **One focused change per PR.** The smallest diff that fixes the problem. No drive-by refactors, reformatting, comment rewording, "modernisation", dependency shuffling, or fixes for things nobody reported. +- **No typo / wording-only PRs.** Open an issue instead and we will sort it out. +- **Finish before you open.** Do not open drafts and iterate in public with a stream of fixup commits. +- **Fill in the PR template honestly**, keep its structure, and tick the contributing checkbox only if it is true. Description, benefit to the wider userbase, how it was tested (real commands, real output), and links. Write it short and plain: no generated summaries, emoji headers, or bullet lists restating the diff. +- Reference the issue with `closes #` when there is one. For anything non trivial, there should be an human-submitted issue or Discord discussion first. +- Commit messages are short, plain, imperative sentences (`Add libfoo for thumbnail support`). No conventional-commit prefixes, no emoji. +- A PR is a proposal; it may be declined even if it works. The most common reason is section 1 of this file. + +## 7. Issues + +- GitHub issues are for reproducible bugs in **our image** and for feature requests. +- Agents must not file issues on a user's behalf from a guess. An issue needs a real reproduction on the latest image, with mods and custom scripts disabled, the compose/run command and full container logs from startup, using the issue template. +- Do not submit AI-generated root cause analyses or "security audits" of the image as issues. Scanner output about CVEs in upstream or distro packages is not actionable; images are rebuilt regularly to pull in distro fixes. +- Support questions must always be submitted by a human and go to [Discord](https://linuxserver.io/discord) or the [forum](https://github.com/orgs/linuxserver/discussions); see [how to get support](https://docs.linuxserver.io/general/how-to-get-support/). + +## 8. Quick self check before you finish + +- [ ] This change benefits most users of this image, and is not a mod, custom script, or upstream issue. +- [ ] Only hand-maintained files were edited (`Dockerfile*`, `root/**`, `readme-vars.yml`, rarely `jenkins-vars.yml`). +- [ ] Every Dockerfile variant got the same change; packages are alphabetical. +- [ ] Style matches the neighbouring code; shebang and shellcheck directive present; non-root / read-only guards preserved. +- [ ] Changelog entry added at the top of `changelogs:` with today's date as `DD.MM.YY:`. +- [ ] Image was built and run, including against an existing `/config`. +- [ ] The diff contains nothing the task did not require. +- [ ] PR description discloses AI use and a human has reviewed everything. diff --git a/ansible/roles/github/templates/PULL_REQUEST_TEMPLATE.md.j2 b/ansible/roles/github/templates/PULL_REQUEST_TEMPLATE.md.j2 index a6ef812b..28cbaaf3 100644 --- a/ansible/roles/github/templates/PULL_REQUEST_TEMPLATE.md.j2 +++ b/ansible/roles/github/templates/PULL_REQUEST_TEMPLATE.md.j2 @@ -9,7 +9,6 @@ [linuxserverurl]: {{ lsio_full_url }} [![{{ lsio_project_name }}](https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/linuxserver_medium.png)][linuxserverurl] - @@ -18,7 +17,6 @@ - @@ -27,11 +25,16 @@ ------------------------------ - - [ ] I have read the [contributing]({{ project_github_asset }}/.github/CONTRIBUTING.md) guideline and understand that I have made the correct modifications +- [ ] I have read the [contributing]({{ project_github_asset }}/.github/CONTRIBUTING.md) guideline and understand that I have made the correct modifications + + + +- [ ] An LLM assisted me in writing the code or text for this PR +- [ ] An LLM agent wrote the code or text for this PR ------------------------------ - + ## Description: @@ -44,6 +47,5 @@ - ## Source / References: diff --git a/ansible/roles/github/templates/config.yml.j2 b/ansible/roles/github/templates/config.yml.j2 index 7d2c089d..9a6d0d5e 100644 --- a/ansible/roles/github/templates/config.yml.j2 +++ b/ansible/roles/github/templates/config.yml.j2 @@ -4,8 +4,8 @@ contact_links: url: {{ lsio_discord_url }} about: Realtime support / chat with the community and the team. - - name: Discourse discussion forum - url: {{ lsio_discourse_url }} + - name: Github Discussions + url: {{ lsio_discussion_url }} about: Post on our community forum. - name: Documentation diff --git a/ansible/roles/github/templates/greetings.yml.j2 b/ansible/roles/github/templates/greetings.yml.j2 index 2b09358c..cc920d9a 100644 --- a/ansible/roles/github/templates/greetings.yml.j2 +++ b/ansible/roles/github/templates/greetings.yml.j2 @@ -14,6 +14,6 @@ jobs: steps: - uses: linuxserver/github-workflows/.github/actions/first-interaction@v1 with: - issue-message: '{% if project_deprecation_status %}This image is deprecated. We will not offer support for this image and it will not be updated. The [README]({{ project_github_asset }}/README.md) may have additional information. {% endif %}Thanks for opening your first issue here! Be sure to follow the relevant issue templates, or risk having this issue marked as invalid.' - pr-message: '{% if project_deprecation_status %}This image is deprecated. We will not offer support for this image and it will not be updated. The [README]({{ project_github_asset }}/README.md) may have additional information. {% endif %}Thanks for opening this pull request! Be sure to follow the [pull request template]({{ project_github_asset }}/.github/PULL_REQUEST_TEMPLATE.md)!' - repo-token: ${{ '{{' }} secrets.GITHUB_TOKEN {{ '}}' }} + issue_message: '{% if project_deprecation_status %}This image is deprecated. We will not offer support for this image and it will not be updated. The [README]({{ project_github_asset }}/README.md) may have additional information. {% endif %}Thanks for opening your first issue here! Be sure to follow the relevant issue templates, or risk having this issue marked as invalid.' + pr_message: '{% if project_deprecation_status %}This image is deprecated. We will not offer support for this image and it will not be updated. The [README]({{ project_github_asset }}/README.md) may have additional information. {% endif %}Thanks for opening this pull request! Be sure to follow the [pull request template]({{ project_github_asset }}/.github/PULL_REQUEST_TEMPLATE.md)!' + repo_token: ${{ '{{' }} secrets.GITHUB_TOKEN {{ '}}' }} diff --git a/ansible/vars/common.yml b/ansible/vars/common.yml index de75e470..52533c26 100644 --- a/ansible/vars/common.yml +++ b/ansible/vars/common.yml @@ -12,6 +12,7 @@ lsio_blog_url: "https://blog.{{ lsio_short_url }}" lsio_ci_url: "https://ci.{{ lsio_short_url }}" lsio_discord_url: "https://linuxserver.io/discord" lsio_discourse_url: "https://discourse.{{ lsio_short_url }}" +lsio_discussion_url: "https://github.com/orgs/{{ lsio_project_name_short }}/discussions" lsio_docker_hub_url: "https://hub.docker.com/r/{{ lsio_project_name_short }}" lsio_docs_url: "https://docs.{{ lsio_short_url }}" lsio_github_url: "https://github.com/{{ lsio_project_name_short }}" @@ -37,6 +38,7 @@ arch_armhf: "armhf" lsio_blog_desc: "all the things you can do with our containers including How-To guides, opinions and much more!" lsio_discord_desc: "realtime support / chat with the community and the team." lsio_discourse_desc: "post on our community forum." +lsio_discussion_desc: "post on our community forum." lsio_github_desc: "view the source for all of our repositories." lsio_mods_desc: "view available mods for this container." lsio_universal_mods_desc: "view available universal mods." @@ -59,6 +61,7 @@ lsio_badge_url_parameters_scarfio: "color={{ lsio_badge_color }}&label-color={{ lsio_scarfio_pulls: "https://scarf.sh/installs-badge/{{ lsio_project_name_short }}-ci/{{ lsio_project_name_short }}%2F{{ project_name }}?{{ lsio_badge_url_parameters_scarfio }}&package-type=docker" lsio_shieldsio_discord: "https://img.shields.io/discord/354974912613449730.svg?{{ lsio_badge_url_parameters }}&label=Discord&logo=discord" lsio_shieldsio_discourse_topics: "https://img.shields.io/discourse/https/discourse.{{ lsio_short_url }}/topics.svg?{{ lsio_badge_url_parameters }}&logo=discourse" +lsio_shieldsio_discussion: "https://img.shields.io/badge/Github-Discussions-94398d?{{ lsio_badge_url_parameters }}&logo=github" lsio_shieldsio_docker_pulls: "https://img.shields.io/docker/pulls/{{ lsio_project_name_short }}/{{ project_name }}.svg?{{ lsio_badge_url_parameters }}&label=pulls&logo=docker" lsio_shieldsio_docker_stars: "https://img.shields.io/docker/stars/{{ lsio_project_name_short }}/{{ project_name }}.svg?{{ lsio_badge_url_parameters }}&label=stars&logo=docker" lsio_shieldsio_dynamic_ci: "https://img.shields.io/badge/dynamic/yaml?{{ lsio_badge_url_parameters }}&label=CI&query=CI&url=https%3A%2F%2Fci-tests.linuxserver.io%2Flinuxserver%2F{{ project_name }}%2F{{ release_tag }}%2Fci-status.yml" diff --git a/package_versions.txt b/package_versions.txt index ec2ab467..e60829ea 100644 --- a/package_versions.txt +++ b/package_versions.txt @@ -1,131 +1,138 @@ -NAME VERSION TYPE -acl-libs 2.3.2-r1 apk -alpine-baselayout 3.7.0-r0 apk -alpine-baselayout-data 3.7.0-r0 apk -alpine-keys 2.5-r0 apk -ansible 11.6.0 python -ansible 11.6.0-r0 apk -ansible-core 2.18.6 python -ansible-core 2.18.6-r0 apk -ansible-core-pyc 2.18.6-r0 apk -ansible-pyc 11.6.0-r0 apk -apk-tools 2.14.9-r3 apk -bash 5.2.37-r0 apk -bcrypt 4.3.0 python -brotli-libs 1.1.0-r2 apk -busybox 1.37.0-r20 apk -busybox-binsh 1.37.0-r20 apk -c-ares 1.34.6-r0 apk -ca-certificates 20250911-r0 apk -ca-certificates-bundle 20250911-r0 apk -catatonit 0.2.1-r0 apk -cffi 1.17.1 python -coreutils 9.7-r1 apk -coreutils-env 9.7-r1 apk -coreutils-fmt 9.7-r1 apk -coreutils-sha512sum 9.7-r1 apk -cryptography 44.0.3 python -curl 8.14.1-r2 apk -findutils 4.10.0-r0 apk -gdbm 1.24-r0 apk -github.com/a8m/envsubst v1.4.2 go-module -github.com/alecthomas/participle/v2 v2.1.1 go-module -github.com/dimchansky/utfbom v1.1.1 go-module -github.com/elliotchance/orderedmap v1.7.1 go-module -github.com/fatih/color v1.18.0 go-module -github.com/goccy/go-json v0.10.4 go-module -github.com/goccy/go-yaml v1.13.3 go-module -github.com/jinzhu/copier v0.4.0 go-module -github.com/magiconair/properties v1.8.9 go-module -github.com/mattn/go-colorable v0.1.13 go-module -github.com/mattn/go-isatty v0.0.20 go-module -github.com/mikefarah/yq/v4 v0.0.0-20250112031739-8bf425b4d134 go-module -github.com/pelletier/go-toml/v2 v2.2.3 go-module -github.com/spf13/cobra v1.8.1 go-module -github.com/spf13/pflag v1.0.5 go-module -github.com/yuin/gopher-lua v1.1.1 go-module -golang.org/x/net v0.33.0 go-module -golang.org/x/sys v0.28.0 go-module -golang.org/x/text v0.21.0 go-module -gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 go-module -gopkg.in/yaml.v3 v3.0.1 go-module -grafana-ansible-collection 2.1.4 npm -jinja2 3.1.6 python -jq 1.8.1-r0 apk -libapk2 2.14.9-r3 apk -libattr 2.5.2-r2 apk -libbsd 0.12.2-r0 apk -libbz2 1.0.8-r6 apk -libcrypto3 3.5.5-r0 apk -libcurl 8.14.1-r2 apk -libexpat 2.7.4-r0 apk -libffi 3.4.8-r0 apk -libgcc 14.2.0-r6 apk -libidn2 2.3.7-r0 apk -libintl 0.24.1-r0 apk -libmd 1.1.0-r0 apk -libncursesw 6.5_p20250503-r0 apk -libpanelw 6.5_p20250503-r0 apk -libproc2 4.0.4-r3 apk -libpsl 0.21.5-r3 apk -libssl3 3.5.5-r0 apk -libstdc++ 14.2.0-r6 apk -libunistring 1.3-r0 apk -linux-pam 1.7.0-r4 apk -markupsafe 3.0.2 python -mpdecimal 4.0.1-r0 apk -musl 1.2.5-r10 apk -musl-utils 1.2.5-r10 apk -ncurses-terminfo-base 6.5_p20250503-r0 apk -netcat-openbsd 1.229.1-r0 apk -nghttp2-libs 1.65.0-r0 apk -oniguruma 6.9.10-r0 apk -packaging 25.0 python -paramiko 3.5.0 python -procps-ng 4.0.4-r3 apk -py3-bcrypt 4.3.0-r0 apk -py3-bcrypt-pyc 4.3.0-r0 apk -py3-cffi 1.17.1-r1 apk -py3-cffi-pyc 1.17.1-r1 apk -py3-cparser 2.22-r1 apk -py3-cparser-pyc 2.22-r1 apk -py3-cryptography 44.0.3-r0 apk -py3-cryptography-pyc 44.0.3-r0 apk -py3-jinja2 3.1.6-r0 apk -py3-jinja2-pyc 3.1.6-r0 apk -py3-markupsafe 3.0.2-r0 apk -py3-markupsafe-pyc 3.0.2-r0 apk -py3-packaging 25.0-r0 apk -py3-packaging-pyc 25.0-r0 apk -py3-paramiko 3.5.0-r0 apk -py3-paramiko-pyc 3.5.0-r0 apk -py3-parsing 3.2.5-r0 apk -py3-parsing-pyc 3.2.5-r0 apk -py3-pynacl 1.5.0-r6 apk -py3-pynacl-pyc 1.5.0-r6 apk -py3-resolvelib 1.1.0-r0 apk -py3-resolvelib-pyc 1.1.0-r0 apk -py3-yaml 6.0.2-r0 apk -py3-yaml-pyc 6.0.2-r0 apk -pyc 3.12.12-r0 apk -pycparser 2.22 python -pynacl 1.5.0 python -pyparsing 3.2.5 python -python3 3.12.12-r0 apk -python3-pyc 3.12.12-r0 apk -python3-pycache-pyc0 3.12.12-r0 apk -pyyaml 6.0.2 python -readline 8.2.13-r1 apk -resolvelib 1.1.0 python -scanelf 1.3.8-r1 apk -shadow 4.17.3-r0 apk -skalibs-libs 2.14.4.0-r0 apk -sqlite-libs 3.49.2-r1 apk -ssl_client 1.37.0-r20 apk -stdlib go1.23.4 go-module -tzdata 2025c-r0 apk -utmps-libs 0.1.3.1-r0 apk -xz-libs 5.8.1-r0 apk -yaml 0.2.5-r2 apk -zlib 1.3.1-r2 apk -zstd-libs 1.5.7-r0 apk +NAME VERSION TYPE +acl-libs 2.3.2-r1 apk +alpine-baselayout 3.7.0-r0 apk +alpine-baselayout-data 3.7.0-r0 apk +alpine-keys 2.5-r0 apk +ansible 11.6.0 python +ansible 11.6.0-r0 apk +ansible-core 2.18.6 python +ansible-core 2.18.6-r0 apk +ansible-core-pyc 2.18.6-r0 apk +ansible-pyc 11.6.0-r0 apk +apk-tools 2.14.10-r0 apk +bash 5.2.37-r0 apk +bcrypt 4.3.0 python +brotli-libs 1.1.0-r2 apk +busybox 1.37.0-r20 apk +busybox-binsh 1.37.0-r20 apk +c-ares 1.34.8-r0 apk +ca-certificates 20260611-r0 apk +ca-certificates-bundle 20260611-r0 apk +catatonit 0.2.1-r0 apk +cffi 1.17.1 python +coreutils 9.7-r1 apk +coreutils-env 9.7-r1 apk +coreutils-fmt 9.7-r1 apk +coreutils-sha512sum 9.7-r1 apk +cryptography 44.0.3 python +curl 8.14.1-r3 apk +findutils 4.10.0-r0 apk +gdbm 1.24-r0 apk +github.com/a8m/envsubst v1.4.3 go-module +github.com/agext/levenshtein v1.2.1 go-module +github.com/alecthomas/participle/v2 v2.1.4 go-module +github.com/apparentlymart/go-textseg/v15 v15.0.0 go-module +github.com/dimchansky/utfbom v1.1.1 go-module +github.com/elliotchance/orderedmap v1.8.0 go-module +github.com/fatih/color v1.18.0 go-module +github.com/go-ini/ini v1.67.0 go-module +github.com/goccy/go-json v0.10.5 go-module +github.com/goccy/go-yaml v1.19.2 go-module +github.com/google/go-cmp v0.6.0 go-module +github.com/hashicorp/hcl/v2 v2.24.0 go-module +github.com/jinzhu/copier v0.4.0 go-module +github.com/magiconair/properties v1.8.10 go-module +github.com/mattn/go-colorable v0.1.14 go-module +github.com/mattn/go-isatty v0.0.20 go-module +github.com/mikefarah/yq/v4 v4.52.5 go-module +github.com/mitchellh/go-wordwrap v1.0.1 go-module +github.com/pelletier/go-toml/v2 v2.2.4 go-module +github.com/spf13/cobra v1.10.2 go-module +github.com/spf13/pflag v1.0.10 go-module +github.com/yuin/gopher-lua v1.1.1 go-module +github.com/zclconf/go-cty v1.18.0 go-module +go.yaml.in/yaml/v4 v4.0.0-rc.3 go-module +golang.org/x/net v0.52.0 go-module +golang.org/x/sys v0.42.0 go-module +golang.org/x/text v0.35.0 go-module +gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 go-module +grafana-ansible-collection 2.1.4 npm +jinja2 3.1.6 python +jq 1.8.2-r0 apk +libapk2 2.14.10-r0 apk +libattr 2.5.2-r2 apk +libbsd 0.12.2-r0 apk +libbz2 1.0.8-r6 apk +libcrypto3 3.5.8-r0 apk +libcurl 8.14.1-r3 apk +libexpat 2.8.4-r0 apk +libffi 3.4.8-r0 apk +libgcc 14.2.0-r6 apk +libidn2 2.3.7-r0 apk +libintl 0.24.1-r0 apk +libmd 1.1.0-r0 apk +libncursesw 6.5_p20250503-r0 apk +libpanelw 6.5_p20250503-r0 apk +libproc2 4.0.4-r3 apk +libpsl 0.21.5-r3 apk +libssl3 3.5.8-r0 apk +libstdc++ 14.2.0-r6 apk +libunistring 1.3-r0 apk +linux-pam 1.7.0-r4 apk +markupsafe 3.0.2 python +mpdecimal 4.0.1-r0 apk +musl 1.2.5-r12 apk +musl-utils 1.2.5-r12 apk +ncurses-terminfo-base 6.5_p20250503-r0 apk +netcat-openbsd 1.229.1-r0 apk +nghttp2-libs 1.69.0-r0 apk +oniguruma 6.9.10-r0 apk +packaging 25.0 python +paramiko 3.5.0 python +procps-ng 4.0.4-r3 apk +py3-bcrypt 4.3.0-r0 apk +py3-bcrypt-pyc 4.3.0-r0 apk +py3-cffi 1.17.1-r1 apk +py3-cffi-pyc 1.17.1-r1 apk +py3-cparser 2.22-r1 apk +py3-cparser-pyc 2.22-r1 apk +py3-cryptography 44.0.3-r0 apk +py3-cryptography-pyc 44.0.3-r0 apk +py3-jinja2 3.1.6-r0 apk +py3-jinja2-pyc 3.1.6-r0 apk +py3-markupsafe 3.0.2-r0 apk +py3-markupsafe-pyc 3.0.2-r0 apk +py3-packaging 25.0-r0 apk +py3-packaging-pyc 25.0-r0 apk +py3-paramiko 3.5.0-r0 apk +py3-paramiko-pyc 3.5.0-r0 apk +py3-parsing 3.2.5-r0 apk +py3-parsing-pyc 3.2.5-r0 apk +py3-pynacl 1.5.0-r6 apk +py3-pynacl-pyc 1.5.0-r6 apk +py3-resolvelib 1.1.0-r0 apk +py3-resolvelib-pyc 1.1.0-r0 apk +py3-yaml 6.0.2-r0 apk +py3-yaml-pyc 6.0.2-r0 apk +pyc 3.12.14-r0 apk +pycparser 2.22 python +pynacl 1.5.0 python +pyparsing 3.2.5 python +python3 3.12.14-r0 apk +python3-pyc 3.12.14-r0 apk +python3-pycache-pyc0 3.12.14-r0 apk +pyyaml 6.0.2 python +readline 8.2.13-r1 apk +resolvelib 1.1.0 python +scanelf 1.3.8-r1 apk +shadow 4.17.3-r0 apk +skalibs-libs 2.14.4.0-r0 apk +sqlite-libs 3.49.2-r1 apk +ssl_client 1.37.0-r20 apk +stdlib go1.26.1 go-module +tzdata 2026c-r0 apk +utmps-libs 0.1.3.1-r0 apk +xz-libs 5.8.4-r0 apk +yaml 0.2.5-r2 apk +zlib 1.3.2-r0 apk +zstd-libs 1.5.7-r0 apk