Skip to content
Open
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
26 changes: 26 additions & 0 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: Copilot Setup Steps

"on":
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml

jobs:
copilot-setup-steps:
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Install base tooling for cloud agent sessions
run: |
sudo apt-get update
sudo apt-get install -y jq make
python3 -m pip install --user yamllint==1.38.0
200 changes: 200 additions & 0 deletions .github/workflows/docker-dependency-updater.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
---
name: Docker Dependency Updater

"on":
schedule:
- cron: "0 5 * * 1"
push:
paths:
- Dockerfile
- .github/workflows/docker-dependency-updater.yml
workflow_dispatch:

permissions:
Comment on lines +9 to +13
contents: write
pull-requests: write
issues: write

jobs:
schedule-copilot-session:
if: github.event_name == 'schedule'
runs-on: ubuntu-24.04
steps:
- name: Create scheduled Copilot issue
uses: actions/github-script@v8
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const title = "chore: scheduled dependency upgrade session";

const existing = await github.rest.issues.listForRepo({
owner,
repo,
state: "open",
creator: "github-actions[bot]",
per_page: 100
});

const alreadyOpen = existing.data.some((issue) => issue.title === title);
if (alreadyOpen) {
core.info("Scheduled Copilot issue is already open. Skipping.");
return;
}

await github.rest.issues.create({
owner,
repo,
title,
body: [
"@copilot please run this workflow in `workflow_dispatch` mode and apply dynamic dependency upgrades.",
"",
"Scope:",
"- Check Dockerfile dependency pins and ARG versions for available updates",
"- Update versions when available",
"- Validate with the repository build/tests",
"- Open or update a PR with the changes"
].join("\n")
});

update-docker-dependencies:
if: github.event_name == 'workflow_dispatch' || github.event_name == 'push'
runs-on: ubuntu-24.04

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Update Dockerfile dependency pins
id: update
shell: bash
run: |
set -euo pipefail

dockerfile="Dockerfile"
base_image="$(awk '$1 == "FROM" { print $2; exit }' "${dockerfile}")"
update_arg() {
local arg_name="$1"
local new_value="$2"
sed -i -E "s|^ARG ${arg_name}=.*$|ARG ${arg_name}=${new_value}|" "${dockerfile}"
}

mapfile -t apt_packages < <(
awk '
/apt-get install -y --no-install-recommends/ { in_block=1; next }
in_block && /&&/ { in_block=0 }
in_block {
gsub(/\\/, "", $0)
gsub(/^[[:space:]]+/, "", $0)
if ($0 ~ /^[[:alnum:].+-]+=/) {
split($0, parts, "=")
print parts[1]
}
Comment on lines +85 to +92
}
' "${dockerfile}"
)

if [ "${#apt_packages[@]}" -gt 0 ]; then
apt_versions="$(
docker run --rm "${base_image}" bash -s -- "${apt_packages[@]}" <<'EOF'
set -euo pipefail
apt-get update >/dev/null
for pkg in "$@"; do
version="$(apt-cache madison "${pkg}" | awk 'NR==1 {print $3}')"
if [ -n "${version}" ]; then
printf '%s=%s\n' "${pkg}" "${version}"
fi
done
EOF
)"

while IFS='=' read -r pkg version; do
[ -z "${pkg}" ] && continue
PKG="${pkg}" VERSION="${version}" perl -0pi -e 's/\Q$ENV{PKG}=\E[0-9A-Za-z.+~:-]+/$ENV{PKG}=$ENV{VERSION}/g' "${dockerfile}"
done <<< "${apt_versions}"
fi

while IFS='=' read -r arg_name current_value; do
[ -z "${arg_name}" ] && continue
latest_value=""

pip_package="$(
awk -v arg="${arg_name}" '
$0 ~ "==\\$\\{" arg "\\}" {
match($0, /[A-Za-z0-9_.-]+==\$\{[A-Za-z0-9_]+\}/)
if (RSTART > 0) {
token=substr($0, RSTART, RLENGTH)
split(token, parts, "==")
print parts[1]
exit
}
}
' "${dockerfile}"
)"
if [ -n "${pip_package}" ]; then
latest_value="$(
curl -fsSL "https://pypi.org/pypi/${pip_package}/json" 2>/dev/null \
| jq -r '.info.version // empty' 2>/dev/null || true
)"
fi

if [ -z "${latest_value}" ]; then
github_repo="$(
grep -m1 -E "github\\.com/[^/]+/[^/]+/releases/download/.+\\$\\{${arg_name}\\}" "${dockerfile}" \
| sed -nE 's#.*github\.com/([^/]+/[^/]+)/releases/download/.*#\1#p' || true
)"
if [ -n "${github_repo}" ]; then
latest_value="$(
curl -fsSL "https://api.github.com/repos/${github_repo}/releases/latest" 2>/dev/null \
| jq -r '.tag_name // empty | ltrimstr("v")' 2>/dev/null || true
)"
fi
fi

if [ -z "${latest_value}" ] && grep -Eq "google-cloud-sdk-(\\$\\{${arg_name}\\}|\\$${arg_name})" "${dockerfile}"; then
latest_value="$(
curl -fsSL https://dl.google.com/dl/cloudsdk/channels/rapid/components-2.json 2>/dev/null \
| jq -r '.version // empty' 2>/dev/null || true
)"
fi

if [ -z "${latest_value}" ] && grep -Eq "awscli-exe-linux-.*(\\$\\{${arg_name}\\}|\\$${arg_name})" "${dockerfile}"; then
latest_value="$(
curl -fsSL https://raw.githubusercontent.com/aws/aws-cli/v2/CHANGELOG.rst 2>/dev/null \
| awk '/^[0-9]+\.[0-9]+\.[0-9]+/ { print $1; exit }' || true
)"
fi

if [ -n "${latest_value}" ] && [ "${latest_value}" != "${current_value}" ]; then
update_arg "${arg_name}" "${latest_value}"
fi
done < <(awk '/^ARG [A-Z0-9_]+=/ { split($2, kv, "="); print kv[1] "=" kv[2] }' "${dockerfile}")

if git diff --quiet -- "${dockerfile}"; then
echo "changed=false" >> "${GITHUB_OUTPUT}"
else
echo "changed=true" >> "${GITHUB_OUTPUT}"
fi
Comment on lines +175 to +177

- name: Validate Docker build
if: github.event_name == 'workflow_dispatch' && steps.update.outputs.changed == 'true'
run: docker build --progress=plain -t dependency-update-validation .

- name: Create pull request
if: github.event_name == 'workflow_dispatch' && steps.update.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v7

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Docker Dependency Updater' step
Uses Step
uses 'peter-evans/create-pull-request' with ref 'v7', not a pinned commit hash
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
fqjony marked this conversation as resolved.
with:
commit-message: "chore(deps): update Docker dependency pins"
title: "chore(deps): update Docker dependency pins"
body: |
## Summary

Automated update of Docker dependency pins in `Dockerfile`, including:

- apt package version pins
- ARG-based tool versions discoverable from Dockerfile usage patterns

Dependabot remains the primary updater for supported ecosystems (Docker base image and GitHub Actions).
This workflow handles Dockerfile dependency pins that Dependabot does not update.
branch: automation/docker-dependency-updates
delete-branch: true
Loading