Skip to content

Merge pull request #1142 from docker/dependabot/github_actions/docker… #102

Merge pull request #1142 from docker/dependabot/github_actions/docker…

Merge pull request #1142 from docker/dependabot/github_actions/docker… #102

Workflow file for this run

name: update-deps
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
on:
workflow_dispatch:
schedule:
- cron: '0 9 * * *'
push:
branches:
- 'main'
jobs:
update:
runs-on: ubuntu-24.04
environment: update-deps # secrets are gated by this environment
timeout-minutes: 10
permissions:
contents: write
pull-requests: write
strategy:
fail-fast: false
matrix:
dep:
- docker
- buildx
- buildkit
- compose
- cosign
- regctl
- undock
steps:
-
name: GitHub auth token from GitHub App
id: write-app
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.GHACTIONS_REPO_WRITE_CLIENT_ID }}
private-key: ${{ secrets.GHACTIONS_REPO_WRITE_PRIVATE_KEY }}
owner: docker
repositories: actions-toolkit
permission-contents: write
permission-pull-requests: write
permission-workflows: write
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ steps.write-app.outputs.token }}
fetch-depth: 0
persist-credentials: false
-
name: Update dependency
id: update
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
INPUT_DEP: ${{ matrix.dep }}
with:
script: |
const fs = require('fs');
const path = require('path');
const dep = core.getInput('dep');
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function formatList(values) {
const quoted = values.map(value => `\`${value}\``);
if (quoted.length === 1) {
return quoted[0];
}
if (quoted.length === 2) {
return `${quoted[0]} and ${quoted[1]}`;
}
return `${quoted.slice(0, -1).join(', ')}, and ${quoted.at(-1)}`;
}
function unique(values) {
return [...new Set(values)];
}
function stripLeadingV(value) {
return value.startsWith('v') ? value.slice(1) : value;
}
function stripDockerTag(value) {
return value.replace(/^docker-v/, '').replace(/^v/, '');
}
function majorMinor(value) {
const match = value.match(/^(\d+\.\d+)/);
if (!match) {
throw new Error(`Unable to derive major.minor version from ${value}`);
}
return match[1];
}
function readJson(relativePath) {
const absolutePath = path.join(process.env.GITHUB_WORKSPACE, relativePath);
return JSON.parse(fs.readFileSync(absolutePath, 'utf8'));
}
function readLatestTag(relativePath) {
const tag = readJson(relativePath)?.latest?.tag_name;
if (!tag) {
throw new Error(`Unable to resolve latest tag from ${relativePath}`);
}
return tag;
}
function dockerfileArgPattern(key) {
return new RegExp(`^(ARG ${escapeRegExp(key)}=)(.+)$`, 'm');
}
function workflowEnvPattern(key) {
return new RegExp(`^( ${escapeRegExp(key)}: ")([^"]*)(")$`, 'm');
}
const dependencyConfigs = {
docker: {
name: 'Docker version',
branch: 'deps/docker-version',
sourceUrl: 'https://github.com/docker/actions-toolkit/blob/main/.github/docker-releases.json',
async resolve() {
const tag = readLatestTag('.github/docker-releases.json');
const version = majorMinor(stripDockerTag(tag));
return {
titleValue: version,
targets: [
{
path: 'dev.Dockerfile',
key: 'DOCKER_VERSION',
value: version,
pattern: dockerfileArgPattern('DOCKER_VERSION')
}
]
};
}
},
buildx: {
name: 'Buildx version',
branch: 'deps/buildx-version',
sourceUrl: 'https://github.com/docker/actions-toolkit/blob/main/.github/buildx-releases.json',
async resolve() {
const tag = readLatestTag('.github/buildx-releases.json');
return {
titleValue: tag,
targets: [
{
path: 'dev.Dockerfile',
key: 'BUILDX_VERSION',
value: stripLeadingV(tag),
pattern: dockerfileArgPattern('BUILDX_VERSION')
},
{
path: '.github/workflows/test.yml',
key: 'BUILDX_VERSION',
value: tag,
pattern: workflowEnvPattern('BUILDX_VERSION')
}
]
};
}
},
buildkit: {
name: 'BuildKit image',
branch: 'deps/buildkit-image',
sourceUrl: 'https://github.com/moby/buildkit/releases/latest',
async resolve({github}) {
const release = await github.rest.repos.getLatestRelease({
owner: 'moby',
repo: 'buildkit'
});
const image = `moby/buildkit:${release.data.tag_name}`;
return {
titleValue: image,
targets: [
{
path: '.github/workflows/test.yml',
key: 'BUILDKIT_IMAGE',
value: image,
pattern: workflowEnvPattern('BUILDKIT_IMAGE')
}
]
};
}
},
compose: {
name: 'Compose version',
branch: 'deps/compose-version',
sourceUrl: 'https://github.com/docker/actions-toolkit/blob/main/.github/compose-releases.json',
async resolve() {
const tag = readLatestTag('.github/compose-releases.json');
return {
titleValue: tag,
targets: [
{
path: 'dev.Dockerfile',
key: 'COMPOSE_VERSION',
value: stripLeadingV(tag),
pattern: dockerfileArgPattern('COMPOSE_VERSION')
}
]
};
}
},
undock: {
name: 'Undock version',
branch: 'deps/undock-version',
sourceUrl: 'https://github.com/docker/actions-toolkit/blob/main/.github/undock-releases.json',
async resolve() {
const tag = readLatestTag('.github/undock-releases.json');
return {
titleValue: tag,
targets: [
{
path: 'dev.Dockerfile',
key: 'UNDOCK_VERSION',
value: stripLeadingV(tag),
pattern: dockerfileArgPattern('UNDOCK_VERSION')
}
]
};
}
},
regctl: {
name: 'Regctl version',
branch: 'deps/regctl-version',
sourceUrl: 'https://github.com/docker/actions-toolkit/blob/main/.github/regclient-releases.json',
async resolve() {
const tag = readLatestTag('.github/regclient-releases.json');
return {
titleValue: tag,
targets: [
{
path: 'dev.Dockerfile',
key: 'REGCTL_VERSION',
value: tag,
pattern: dockerfileArgPattern('REGCTL_VERSION')
}
]
};
}
},
cosign: {
name: 'Cosign version',
branch: 'deps/cosign-version',
sourceUrl: 'https://github.com/docker/actions-toolkit/blob/main/.github/cosign-releases.json',
async resolve() {
const tag = readLatestTag('.github/cosign-releases.json');
return {
titleValue: tag,
targets: [
{
path: 'dev.Dockerfile',
key: 'COSIGN_VERSION',
value: tag,
pattern: dockerfileArgPattern('COSIGN_VERSION')
}
]
};
}
}
};
const config = dependencyConfigs[dep];
if (!config) {
core.setFailed(`Unknown dependency ${dep}`);
return;
}
const resolved = await config.resolve({github});
const currentValues = [];
const changedFiles = [];
for (const target of resolved.targets) {
const absolutePath = path.join(process.env.GITHUB_WORKSPACE, target.path);
const content = fs.readFileSync(absolutePath, 'utf8');
const match = content.match(target.pattern);
if (!match) {
throw new Error(`Missing ${target.key} in ${target.path}`);
}
currentValues.push(match[2]);
if (match[2] === target.value) {
continue;
}
const updatedContent = content.replace(target.pattern, (...args) => {
const groups = args.slice(1, -2);
const prefix = groups[0];
const suffix = groups[2] || '';
return `${prefix}${target.value}${suffix}`;
});
fs.writeFileSync(absolutePath, updatedContent, 'utf8');
changedFiles.push(target.path);
}
core.info(`Resolved ${config.name} from ${config.sourceUrl}`);
if (changedFiles.length === 0) {
core.info(`No workspace changes needed for ${config.name}`);
} else {
core.info(`New ${config.name} ${resolved.titleValue} found`);
}
core.setOutput('branch', config.branch);
core.setOutput('title', `chore(deps): update ${config.name} to ${resolved.titleValue}`);
core.setOutput('before', formatList(unique(currentValues)));
core.setOutput('files', formatList(unique(changedFiles)));
core.setOutput('source-url', config.sourceUrl);
-
name: Create pull request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
base: main
branch: ${{ steps.update.outputs.branch }}
token: ${{ steps.write-app.outputs.token }}
commit-message: ${{ steps.update.outputs.title }}
title: ${{ steps.update.outputs.title }}
signoff: true
sign-commits: true
delete-branch: true
body: |
This updates the pinned value from ${{ steps.update.outputs.before }} in ${{ steps.update.outputs.files }}.
The source of truth for this update is ${{ steps.update.outputs.source-url }}.