Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .claude/skills/update-deps/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
name: update-deps
description: Check the latest upstream version for every renovate-annotated dependency in the Dockerfile, then rewrite the pinned version literals in place. Use when the user asks to "update deps", "bump deps", "update dockerfile deps", or refresh image dependencies. Git operations are out of scope.
---

# Update Dockerfile dependencies

Scan `Dockerfile`, resolve each renovate-annotated dependency's latest upstream version, and rewrite the pinned `version=...` literal in place. The skill stops after the file edits. Branching, committing, and PR creation are not part of this skill.

## When to use

- User asks to update deps, bump deps, update dockerfile deps, or refresh image dependencies.

## When NOT to use

- Updating Drupal / Composer / Vortex packages. Use the dedicated `/update-drupal`, `/update-vortex`, or Composer-related skills.
- Editing `versions-config.json` or `goss.yaml`. Both track runtime detection; neither pins versions.

## Step 1 - Discover entries

Read `Dockerfile`. Find every block matching the Renovate custom-manager regex from `renovate.json`:

```regex
#\s*renovate:\s*datasource=(?<datasource>\S+)\s+depName=(?<depName>\S+)(?:\s+versioning=(?<versioning>\S+))?(?:\s+extractVersion=(?<extractVersion>\S+))?\s*\n(?:.*\n)*?.*?(?:ENV|ARG|RUN)\s+.*?version=(?<currentValue>[^\s&]+)
```

For each match, record `{datasource, depName, versioning, extractVersion, currentValue, lineNumber}`.

Also capture the base image digest pin from the two `FROM` lines (builder + final):

- Pattern: `FROM php:8.4-cli-bookworm@sha256:<digest>`
- Datasource is `docker`. Both `FROM` lines must end up with the same new digest.

## Step 2 - Resolve latest per datasource

Make one Bash call per command. Do not chain with `&&`, do not pipe with `|`; the global hook blocks both. If a step needs JSON processing, save the response to `.artifacts/tmp/` first, then run `jq` against the file in a separate call.

If any single resolver fails, record the dep as `error` and continue. Do not abort the whole run.

### `github-releases`

1. `gh api repos/<depName>/releases/latest --jq .tag_name` returns e.g. `v0.11.0`, `43`, or `docker-v29.5.2`.
2. If the API returns 404 (no releases published, only tags), fall back to `gh api repos/<depName>/tags --jq ".[0].name"`.
3. Normalize the tag to a bare version: strip any leading `v` and any non-semver prefix such as `docker-v` (moby/moby uses this).
4. If `extractVersion` is something other than `^(?<version>.*)$`, apply that regex literally to the tag instead.

### `npm`

1. `npm view <depName> version` returns the latest semver, no prefix.

### `node` (with `versioning=node`)

1. `curl -fsSL https://nodejs.org/dist/index.json -o .artifacts/tmp/node-index.json`
2. `jq -r 'map(select(.lts != false))[0].version' .artifacts/tmp/node-index.json` returns the latest LTS, e.g. `v24.15.0`. The filter is intentionally pipe-free.
3. Strip the leading `v`.

### `docker` (base image)

1. `docker buildx imagetools inspect php:8.4-cli-bookworm --raw` prints the raw index JSON to stdout; redirect with `> .artifacts/tmp/php-index.json` is fine because it is a redirection, not a pipe.
2. `jq -r ".manifests[0].digest" .artifacts/tmp/php-index.json` is one option, but the value the Dockerfile pins is the index digest, not a per-arch manifest digest. The simplest reliable command is `docker manifest inspect --verbose php:8.4-cli-bookworm` and read the top-level `Digest` field for the named tag.
3. Fallback: `docker pull php:8.4-cli-bookworm`, then `docker inspect --format "{{index .RepoDigests 0}}" php:8.4-cli-bookworm`, then strip the `php@` prefix.

## Step 3 - Print the diff

Render a markdown table so the user sees exactly what will change:

```markdown
| Dependency | Datasource | Current | Latest | Status |
|-----------------|-----------------|-----------|-----------|------------|
| kcov | github-releases | 43 | 43 | up-to-date |
| docker (moby) | github-releases | 28.5.2 | 29.5.2 | bump |
| php (base) | docker | sha256:ca | sha256:7f | bump |
```

If nothing is behind, stop here and report "All dependencies are up to date.". Do not touch `Dockerfile`.

## Step 4 - Edit the Dockerfile

For every `bump` row, use the `Edit` tool on `Dockerfile`:

- Include the preceding `# renovate: ...` comment line in `old_string` so the replacement is unambiguous when `version=<value>` is not unique on its own.
- Replace only the `<value>` after `version=`. Do not reformat surrounding lines.

For the base image bump, replace the `@sha256:<old>` substring with `@sha256:<new>` using `replace_all: true` (the digest substring is unique to the base image, so this safely covers both `FROM` lines).

After each edit, re-read the affected line to confirm the change landed correctly.

The skill ends here. Do not stage, commit, or push.

## Notes

- `goss.yaml` and `versions-config.json` do not pin versions and are not touched.
- README's "Included packages" section is regenerated post-merge by `.github/workflows/update-readme.yml`.
- Yarn is intentionally pinned to v1.x (classic). `npm view yarn version` returns the latest 1.x; Yarn Berry is published as `@yarnpkg/cli`.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
!.claude
!.claude/skills
!.claude/skills/*
.claude/settings.local.json
Comment on lines +1 to +4
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Simplify gitignore logic by removing redundant negation patterns.

The negation patterns (lines 1-3) attempt to explicitly include .claude/skills, but Git tracks all files by default unless explicitly ignored. Negation patterns only make sense when negating a previous ignore rule. Since .claude is not being ignored anywhere, these negation patterns are redundant and potentially confusing.

If the intent is to track .claude/skills/* while ignoring settings.local.json, only line 4 is needed.

♻️ Simplified approach
-!.claude
-!.claude/skills
-!.claude/skills/*
 .claude/settings.local.json

Alternatively, if you want to ignore everything in .claude/ except the skills/ subdirectory:

-!.claude
-!.claude/skills
-!.claude/skills/*
+.claude/*
+!.claude/skills/
 .claude/settings.local.json
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
!.claude
!.claude/skills
!.claude/skills/*
.claude/settings.local.json
.claude/settings.local.json
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.gitignore around lines 1 - 4, Remove the redundant negation lines "!
.claude", "!.claude/skills", and "!.claude/skills/*" from .gitignore and keep
only the explicit ignore for ".claude/settings.local.json"; if your real intent
was to ignore all files under .claude except the skills directory instead,
replace the current lines with a pattern that first ignores ".claude/*" and then
selectively unignore ".claude/skills/" and its contents (use ".claude/*",
"!.claude/skills/", "!.claude/skills/**") while preserving the
".claude/settings.local.json" ignore.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not adopting. The negation patterns counteract a rule in the user's global ~/.gitignore that excludes .claude/ across all repos. Without these explicit un-ignores, the project-scoped Claude skill files in .claude/skills/ would be silently ignored. The negations look redundant when viewing only the project gitignore, but they are load-bearing once the global rule is in play.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlexSkrypnyk, understood — thank you for the explanation. That context makes the negation patterns completely sensible: they exist to override a global ~/.gitignore rule that blanket-ignores .claude/ across all repositories, ensuring the project-scoped skill files in .claude/skills/ are still tracked. I'll record this so I don't flag it in future reviews.


✏️ Learnings added
Learnt from: AlexSkrypnyk
Repo: drevops/ci-runner PR: 383
File: .gitignore:1-4
Timestamp: 2026-05-21T06:18:22.340Z
Learning: In the `drevops/ci-runner` repository, the `.gitignore` negation patterns `!.claude`, `!.claude/skills`, and `!.claude/skills/*` are intentional and load-bearing. They counteract a common global `~/.gitignore` rule that excludes the `.claude/` directory across all repos. Without these explicit un-ignores in the project-level `.gitignore`, files under `.claude/skills/` would be silently ignored. Do not flag these negation patterns as redundant.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ RUN version=1.13.0 && \
# Install Docker.
# @see https://download.docker.com/linux/static/stable/x86_64
# renovate: datasource=github-releases depName=moby/moby extractVersion=^(?<version>.*)$
RUN version=28.5.2 && \
RUN version=29.5.2 && \
curl -L -o "/tmp/docker-${version}.tgz" "https://download.docker.com/linux/static/stable/x86_64/docker-${version}.tgz" && \
tar -xz -C /tmp -f "/tmp/docker-${version}.tgz" && \
mv /tmp/docker/* /usr/bin && \
Expand Down