Skip to content
Snippets Groups Projects
Unverified Commit 27051723 authored by Rhys Arkins's avatar Rhys Arkins Committed by GitHub
Browse files

feat: GitHub Actions docker image support (#3197)

Detect GitHub Actions workflow files and pin/update any Docker image references found within.

Closes #2750
parent c8ba3e6b
No related branches found
No related tags found
No related merge requests found
Showing with 240 additions and 0 deletions
...@@ -1268,6 +1268,19 @@ const options = [ ...@@ -1268,6 +1268,19 @@ const options = [
mergeable: true, mergeable: true,
cli: false, cli: false,
}, },
{
name: 'github-actions',
description:
'Configuration object for GitHub Actions workflow renovation. Also inherits settings from `docker` object.',
stage: 'package',
type: 'json',
default: {
fileMatch: ['^\\.github/main.workflow$'],
pinDigests: true,
},
mergeable: true,
cli: false,
},
{ {
name: 'composer', name: 'composer',
description: 'Configuration object for composer.json files', description: 'Configuration object for composer.json files',
......
const { getDep } = require('../dockerfile/extract');
module.exports = {
extractPackageFile,
};
function extractPackageFile(content) {
logger.debug('github-actions.extractPackageFile()');
const deps = [];
let lineNumber = 0;
for (const line of content.split('\n')) {
const match = line.match(/^\s+uses = "docker:\/\/([^"]+)"\s*$/);
if (match) {
const currentFrom = match[1];
const dep = getDep(currentFrom);
logger.debug(
{
depName: dep.depName,
currentValue: dep.currentValue,
currentDigest: dep.currentDigest,
},
'Docker image inside GitHub Actions'
);
dep.lineNumber = lineNumber;
dep.versionScheme = 'docker';
deps.push(dep);
}
lineNumber += 1;
}
if (!deps.length) {
return null;
}
return { deps };
}
const { extractPackageFile } = require('./extract');
const { updateDependency } = require('./update');
const language = 'docker';
module.exports = {
extractPackageFile,
language,
updateDependency,
};
const { getNewFrom } = require('../dockerfile/update');
module.exports = {
updateDependency,
};
function updateDependency(fileContent, upgrade) {
try {
const newFrom = getNewFrom(upgrade);
logger.debug(`github-actions.updateDependency(): ${newFrom}`);
const lines = fileContent.split('\n');
const lineToChange = lines[upgrade.lineNumber];
const imageLine = new RegExp(/^(\s+uses = "docker:\/\/)[^"]+("\s*)$/);
if (!lineToChange.match(imageLine)) {
logger.debug('No image line found');
return null;
}
const newLine = lineToChange.replace(imageLine, `$1${newFrom}$2`);
if (newLine === lineToChange) {
logger.debug('No changes necessary');
return fileContent;
}
lines[upgrade.lineNumber] = newLine;
return lines.join('\n');
} catch (err) {
logger.info({ err }, 'Error setting new github-actions value');
return null;
}
}
...@@ -8,6 +8,7 @@ const managerList = [ ...@@ -8,6 +8,7 @@ const managerList = [
'composer', 'composer',
'docker-compose', 'docker-compose',
'dockerfile', 'dockerfile',
'github-actions',
'gitlabci', 'gitlabci',
'gomod', 'gomod',
'gradle', 'gradle',
......
workflow "Build and Publish" {
on = "push"
resolves = "Docker Publish"
}
action "Shell Lint" {
uses = "actions/bin/shellcheck@master"
args = "entrypoint.sh"
}
action "Docker Lint" {
uses = "docker://replicated/dockerfilelint"
args = ["Dockerfile"]
}
action "Build" {
needs = ["Shell Lint", "Docker Lint"]
uses = "actions/docker/cli@master"
args = "build -t conventional-commits ."
}
action "Docker Tag" {
needs = ["Build"]
uses = "actions/docker/tag@master"
args = "conventional-commits bcoe/conventional-commits --no-latest"
}
action "Publish Filter" {
needs = ["Build"]
uses = "actions/bin/filter@master"
args = "branch master"
}
action "Node_6_Test" {
needs = "Node_6_Install"
runs = "yarn test"
uses = "docker://node:6@sha256:7b65413af120ec5328077775022c78101f103258a1876ec2f83890bce416e896"
}
action "Docker Login" {
needs = ["Publish Filter"]
uses = "actions/docker/login@master"
secrets = ["DOCKER_USERNAME", "DOCKER_PASSWORD"]
}
action "Docker Publish" {
needs = ["Docker Tag", "Docker Login"]
uses = "actions/docker/cli@master"
args = "push bcoe/conventional-commits"
}
...@@ -11,6 +11,7 @@ Array [ ...@@ -11,6 +11,7 @@ Array [
"composer", "composer",
"docker-compose", "docker-compose",
"dockerfile", "dockerfile",
"github-actions",
"gitlabci", "gitlabci",
"gomod", "gomod",
"gradle", "gradle",
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`lib/manager/github-actions/extract extractPackageFile() extracts multiple image lines from docker_container 1`] = `
Array [
Object {
"currentDepTag": "replicated/dockerfilelint",
"currentDigest": undefined,
"currentFrom": "replicated/dockerfilelint",
"currentValue": undefined,
"datasource": "docker",
"depName": "replicated/dockerfilelint",
"lineNumber": 11,
"versionScheme": "docker",
},
Object {
"commitMessageTopic": "Node.js",
"currentDepTag": "node:6",
"currentDigest": "sha256:7b65413af120ec5328077775022c78101f103258a1876ec2f83890bce416e896",
"currentFrom": "node:6@sha256:7b65413af120ec5328077775022c78101f103258a1876ec2f83890bce416e896",
"currentValue": "6",
"datasource": "docker",
"depName": "node",
"lineNumber": 36,
"versionScheme": "docker",
},
]
`;
const fs = require('fs');
const {
extractPackageFile,
} = require('../../../lib/manager/github-actions/extract');
const workflow1 = fs.readFileSync(
'test/_fixtures/github-actions/main.workflow.1',
'utf8'
);
describe('lib/manager/github-actions/extract', () => {
describe('extractPackageFile()', () => {
let config;
beforeEach(() => {
config = {};
});
it('returns null for empty', () => {
expect(extractPackageFile('nothing here', config)).toBe(null);
});
it('extracts multiple image lines from docker_container', () => {
const res = extractPackageFile(workflow1, config);
expect(res.deps).toMatchSnapshot();
expect(res.deps).toHaveLength(2);
});
});
});
const fs = require('fs');
const dcUpdate = require('../../../lib/manager/github-actions/update');
const workflow1 = fs.readFileSync(
'test/_fixtures/github-actions/main.workflow.1',
'utf8'
);
describe('manager/github-actions/update', () => {
describe('updateDependency', () => {
it('replaces existing uses value', () => {
const upgrade = {
lineNumber: 11,
depName: 'replicated/dockerfilelint',
newDigest: 'sha256:abcdefghijklmnop',
};
const res = dcUpdate.updateDependency(workflow1, upgrade);
expect(res).not.toEqual(workflow1);
expect(res.includes(upgrade.newDigest)).toBe(true);
});
it('returns same', () => {
const upgrade = {
lineNumber: 11,
depName: 'replicated/dockerfilelint',
};
const res = dcUpdate.updateDependency(workflow1, upgrade);
expect(res).toEqual(workflow1);
});
it('returns null if mismatch', () => {
const upgrade = {
lineNumber: 12,
newFrom: 'registry:2.6.2@sha256:abcdefghijklmnop',
};
const res = dcUpdate.updateDependency(workflow1, upgrade);
expect(res).toBe(null);
});
it('returns null if error', () => {
const res = dcUpdate.updateDependency(null, null);
expect(res).toBe(null);
});
});
});
...@@ -29,6 +29,9 @@ Object { ...@@ -29,6 +29,9 @@ Object {
"dockerfile": Array [ "dockerfile": Array [
Object {}, Object {},
], ],
"github-actions": Array [
Object {},
],
"gitlabci": Array [ "gitlabci": Array [
Object {}, Object {},
], ],
......
...@@ -231,6 +231,10 @@ See https://renovatebot.com/docs/config-presets for details. ...@@ -231,6 +231,10 @@ See https://renovatebot.com/docs/config-presets for details.
The primary use case for this option is if you are following a pre-release tag of a certain dependency, e.g. `typescript` "insiders" build. When it's configured, Renovate bypasses its normal major/minor/patch logic and stable/unstable logic and simply raises a PR if the tag does not match your current version. The primary use case for this option is if you are following a pre-release tag of a certain dependency, e.g. `typescript` "insiders" build. When it's configured, Renovate bypasses its normal major/minor/patch logic and stable/unstable logic and simply raises a PR if the tag does not match your current version.
## github-actions
Add to this configuration setting if you need to override any of the GitHub Actions default settings. Use the `docker` config object instead if you wish for configuration to apply across all Docker-related package managers.
## gitlabci ## gitlabci
Add to this configuration setting if you need to override any of the GitLab CI default settings. Use the `docker` config object instead if you wish for configuration to apply across all Docker-related package managers. Add to this configuration setting if you need to override any of the GitLab CI default settings. Use the `docker` config object instead if you wish for configuration to apply across all Docker-related package managers.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment