From c82c38b498f1124b7ed7240ed0277e0ac40f0bf0 Mon Sep 17 00:00:00 2001 From: Adam Setch <adam.setch@outlook.com> Date: Wed, 22 Feb 2023 01:41:56 -0500 Subject: [PATCH] fix: auto-replace for pinDigest without currentDigest or shortDigest (#20317) --- .../update/branch/auto-replace.spec.ts | 247 +++++++++++++++++- .../repository/update/branch/auto-replace.ts | 18 +- 2 files changed, 262 insertions(+), 3 deletions(-) diff --git a/lib/workers/repository/update/branch/auto-replace.spec.ts b/lib/workers/repository/update/branch/auto-replace.spec.ts index 4a1abc13f0..e5082c25bc 100644 --- a/lib/workers/repository/update/branch/auto-replace.spec.ts +++ b/lib/workers/repository/update/branch/auto-replace.spec.ts @@ -909,7 +909,7 @@ describe('workers/repository/update/branch/auto-replace', () => { const tf = codeBlock` module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "3.14.2" + version = "3.14.2" } `; upgrade.manager = 'terraform'; @@ -1038,5 +1038,250 @@ describe('workers/repository/update/branch/auto-replace', () => { ` ); }); + + it('docker: updates with pinDigest enabled but no currentDigest value', async () => { + const dockerfile = codeBlock` + FROM ubuntu:18.04 + `; + upgrade.manager = 'dockerfile'; + upgrade.depName = 'ubuntu'; + upgrade.currentValue = '18.04'; + upgrade.currentDigest = undefined; + upgrade.depIndex = 0; + upgrade.pinDigests = true; + upgrade.updateType = 'replacement'; + upgrade.replaceString = 'ubuntu:18.04'; + upgrade.newName = 'alpine'; + upgrade.newValue = '3.16'; + upgrade.newDigest = 'sha256:p0o9i8u7z6t5r4e3w2q1'; + upgrade.packageFile = 'Dockerfile'; + const res = await doAutoReplace(upgrade, dockerfile, reuseExistingBranch); + expect(res).toBe( + codeBlock` + FROM alpine:3.16 + ` + ); + }); + + it('docker: updates with pinDigest enabled and a currentDigest value', async () => { + const dockerfile = codeBlock` + FROM ubuntu:18.04@sha256:q1w2e3r4t5z6u7i8o9p0 + `; + upgrade.manager = 'dockerfile'; + upgrade.depName = 'ubuntu'; + upgrade.currentValue = '18.04'; + upgrade.currentDigest = 'sha256:q1w2e3r4t5z6u7i8o9p0'; + upgrade.depIndex = 0; + upgrade.pinDigests = true; + upgrade.updateType = 'replacement'; + upgrade.replaceString = 'ubuntu:18.04@sha256:q1w2e3r4t5z6u7i8o9p0'; + upgrade.newName = 'alpine'; + upgrade.newValue = '3.16'; + upgrade.newDigest = 'sha256:p0o9i8u7z6t5r4e3w2q1'; + upgrade.packageFile = 'Dockerfile'; + const res = await doAutoReplace(upgrade, dockerfile, reuseExistingBranch); + expect(res).toBe( + codeBlock` + FROM alpine:3.16@sha256:p0o9i8u7z6t5r4e3w2q1 + ` + ); + }); + + it('regex: updates with pinDigest enabled but no currentDigest value', async () => { + const yml = 'image: "some.url.com/my-repository:1.0"'; + upgrade.manager = 'regex'; + upgrade.pinDigests = true; + upgrade.depName = 'some.url.com/my-repository'; + upgrade.currentValue = '1.0'; + upgrade.currentDigest = undefined; + upgrade.depIndex = 0; + upgrade.replaceString = 'image: "some.url.com/my-repository:1.0"'; + upgrade.packageFile = 'k8s/base/defaults.yaml'; + upgrade.newName = 'some.other.url.com/some-new-repo'; + upgrade.newValue = '3.16'; + upgrade.newDigest = 'sha256:p0o9i8u7z6t5r4e3w2q1'; + upgrade.matchStrings = [ + 'image:\\s*?\\\'?\\"?(?<depName>[^:\\\'\\"]+):(?<currentValue>[^@\\\'\\"]+)@?(?<currentDigest>[^\\s\\\'\\"]+)?\\"?\\\'?\\s*', + ]; + const res = await doAutoReplace(upgrade, yml, reuseExistingBranch); + expect(res).toBe('image: "some.other.url.com/some-new-repo:3.16"'); + }); + + it('regex: updates with pinDigest enabled and a currentDigest value', async () => { + const yml = + 'image: "some.url.com/my-repository:1.0@sha256:q1w2e3r4t5z6u7i8o9p0"'; + upgrade.manager = 'regex'; + upgrade.pinDigests = true; + upgrade.depName = 'some.url.com/my-repository'; + upgrade.currentValue = '1.0'; + upgrade.currentDigest = 'sha256:q1w2e3r4t5z6u7i8o9p0'; + upgrade.depIndex = 0; + upgrade.replaceString = + 'image: "some.url.com/my-repository:1.0@sha256:q1w2e3r4t5z6u7i8o9p0"'; + upgrade.packageFile = 'k8s/base/defaults.yaml'; + upgrade.newName = 'some.other.url.com/some-new-repo'; + upgrade.newValue = '3.16'; + upgrade.newDigest = 'sha256:p0o9i8u7z6t5r4e3w2q1'; + upgrade.matchStrings = [ + 'image:\\s*[\\\'\\"]?(?<depName>[^:]+):(?<currentValue>[^@]+)?@?(?<currentDigest>[^\\s\\\'\\"]+)?[\\\'\\"]?\\s*', + ]; + const res = await doAutoReplace(upgrade, yml, reuseExistingBranch); + expect(res).toBe( + 'image: "some.other.url.com/some-new-repo:3.16@sha256:p0o9i8u7z6t5r4e3w2q1"' + ); + }); + + it('github-actions: update with newValue only', async () => { + const githubAction = codeBlock` + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1.0.0 + `; + upgrade.manager = 'github-actions'; + upgrade.autoReplaceStringTemplate = + '{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}'; + upgrade.depName = 'actions/checkout'; + upgrade.currentValue = 'v1.0.0'; + upgrade.currentDigest = undefined; + upgrade.currentDigestShort = undefined; + upgrade.depIndex = 0; + upgrade.pinDigests = true; + upgrade.updateType = 'replacement'; + upgrade.replaceString = 'actions/checkout@v1.0.0'; + upgrade.newValue = 'v2.0.0'; + upgrade.newDigest = undefined; + upgrade.packageFile = 'workflows/build.yml'; + const res = await doAutoReplace( + upgrade, + githubAction, + reuseExistingBranch + ); + expect(res).toBe( + codeBlock` + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.0.0 + ` + ); + }); + + it('github-actions: update with newValue and newDigest', async () => { + const githubAction = codeBlock` + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1.0.0 + `; + upgrade.manager = 'github-actions'; + upgrade.autoReplaceStringTemplate = + '{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}'; + upgrade.depName = 'actions/checkout'; + upgrade.currentValue = 'v1.0.0'; + upgrade.currentDigest = undefined; + upgrade.currentDigestShort = undefined; + upgrade.depIndex = 0; + upgrade.pinDigests = true; + upgrade.updateType = 'replacement'; + upgrade.replaceString = 'actions/checkout@v1.0.0'; + upgrade.newValue = 'v2.0.0'; + upgrade.newDigest = '1cf887'; + upgrade.packageFile = 'workflows/build.yml'; + const res = await doAutoReplace( + upgrade, + githubAction, + reuseExistingBranch + ); + expect(res).toBe( + codeBlock` + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@1cf887 # v2.0.0 + ` + ); + }); + + it('github-actions: updates with pinDigest enabled but no currentDigest value', async () => { + const githubAction = codeBlock` + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1.0.0 + `; + upgrade.manager = 'github-actions'; + upgrade.autoReplaceStringTemplate = + '{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}'; + upgrade.depName = 'actions/checkout'; + upgrade.currentValue = 'v1.0.0'; + upgrade.currentDigest = undefined; + upgrade.currentDigestShort = undefined; + upgrade.depIndex = 0; + upgrade.pinDigests = true; + upgrade.updateType = 'replacement'; + upgrade.replaceString = 'actions/checkout@v1.0.0'; + upgrade.newName = 'some-other-action/checkout'; + upgrade.newValue = 'v2.0.0'; + upgrade.newDigest = '1cf887'; + upgrade.packageFile = 'workflows/build.yml'; + const res = await doAutoReplace( + upgrade, + githubAction, + reuseExistingBranch + ); + expect(res).toBe( + codeBlock` + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: some-other-action/checkout@v2.0.0 + ` + ); + }); + + it('github-actions: updates with pinDigest enabled and a currentDigest value', async () => { + const githubAction = codeBlock` + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@2485f4 # tag=v1.0.0 + `; + upgrade.manager = 'github-actions'; + upgrade.autoReplaceStringTemplate = + '{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}'; + upgrade.depName = 'actions/checkout'; + upgrade.currentValue = 'v1.0.0'; + upgrade.currentDigestShort = '2485f4'; + upgrade.depIndex = 0; + upgrade.pinDigests = true; + upgrade.updateType = 'replacement'; + upgrade.replaceString = 'actions/checkout@2485f4 # tag=v1.0.0'; + upgrade.newName = 'some-other-action/checkout'; + upgrade.newValue = 'v2.0.0'; + upgrade.newDigest = '1cf887'; + upgrade.packageFile = 'workflow.yml'; + const res = await doAutoReplace( + upgrade, + githubAction, + reuseExistingBranch + ); + expect(res).toBe( + codeBlock` + jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: some-other-action/checkout@1cf887 # tag=v2.0.0 + ` + ); + }); }); }); diff --git a/lib/workers/repository/update/branch/auto-replace.ts b/lib/workers/repository/update/branch/auto-replace.ts index 1b905801ed..2843f15c1d 100644 --- a/lib/workers/repository/update/branch/auto-replace.ts +++ b/lib/workers/repository/update/branch/auto-replace.ts @@ -41,6 +41,7 @@ export async function confirmIfDepUpdated( } catch (err) /* istanbul ignore next */ { logger.debug({ manager, packageFile, err }, 'Failed to parse newContent'); } + if (!newUpgrade!) { logger.debug(`No newUpgrade in ${packageFile!}`); return false; @@ -61,6 +62,7 @@ export async function confirmIfDepUpdated( ); return false; } + if (newValue && newUpgrade.currentValue !== newValue) { logger.debug( { @@ -73,15 +75,21 @@ export async function confirmIfDepUpdated( ); return false; } + if (!newDigest) { return true; } if (newUpgrade.currentDigest === newDigest) { return true; } - if (!currentDigest && !pinDigests) { - return true; + if (!currentDigest) { + if (!pinDigests) { + return true; + } else if (newDigest) { + return true; + } } + // istanbul ignore next return false; } @@ -147,6 +155,7 @@ export async function doAutoReplace( currentValue, newValue, currentDigest, + currentDigestShort, newDigest, autoReplaceStringTemplate, } = upgrade; @@ -203,6 +212,11 @@ export async function doAutoReplace( regEx(escapeRegExp(currentDigest), 'g'), newDigest ); + } else if (currentDigestShort && newDigest) { + newString = newString.replace( + regEx(escapeRegExp(currentDigestShort), 'g'), + newDigest + ); } } if (!firstUpdate && (await confirmIfDepUpdated(upgrade, existingContent))) { -- GitLab