From ee6c40f800108277bd15fd5a45e6a694f7795f6e Mon Sep 17 00:00:00 2001 From: Joshua Tang <joshuaystang@gmail.com> Date: Mon, 7 Aug 2023 16:04:54 +1000 Subject: [PATCH] fix(manager/pub): extract sdk constraint correctly (#23367) Co-authored-by: Sergei Zharinov <zharinov@users.noreply.github.com> Co-authored-by: Michael Kriese <michael.kriese@visualon.de> --- lib/modules/manager/pub/artifacts.spec.ts | 1 - lib/modules/manager/pub/artifacts.ts | 22 ++++------- lib/modules/manager/pub/schema.ts | 13 +++++++ lib/modules/manager/pub/utils.spec.ts | 30 +++++++++++++++ lib/modules/manager/pub/utils.ts | 18 +++++++++ lib/util/schema-utils.spec.ts | 45 +++++++++++++++++++++++ lib/util/schema-utils.ts | 10 +++++ 7 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 lib/modules/manager/pub/schema.ts create mode 100644 lib/modules/manager/pub/utils.spec.ts create mode 100644 lib/modules/manager/pub/utils.ts diff --git a/lib/modules/manager/pub/artifacts.spec.ts b/lib/modules/manager/pub/artifacts.spec.ts index 2ae1fb8bb7..c7b8aaf903 100644 --- a/lib/modules/manager/pub/artifacts.spec.ts +++ b/lib/modules/manager/pub/artifacts.spec.ts @@ -41,7 +41,6 @@ const updateArtifact: UpdateArtifact = { describe('modules/manager/pub/artifacts', () => { beforeEach(() => { jest.resetAllMocks(); - jest.resetModules(); env.getChildProcessEnv.mockReturnValue(envMock.basic); GlobalConfig.set(adminConfig); diff --git a/lib/modules/manager/pub/artifacts.ts b/lib/modules/manager/pub/artifacts.ts index 16b4de4b22..613cd364ce 100644 --- a/lib/modules/manager/pub/artifacts.ts +++ b/lib/modules/manager/pub/artifacts.ts @@ -9,19 +9,8 @@ import { readLocalFile, writeLocalFile, } from '../../../util/fs'; -import { regEx } from '../../../util/regex'; import type { UpdateArtifact, UpdateArtifactsResult } from '../types'; - -function getFlutterConstraint(lockFileContent: string): string | undefined { - return regEx(/^\tflutter: ['"](?<flutterVersion>.*)['"]$/m).exec( - lockFileContent - )?.groups?.flutterVersion; -} - -function getDartConstraint(lockFileContent: string): string | undefined { - return regEx(/^\tdart: ['"](?<dartVersion>.*)['"]$/m).exec(lockFileContent) - ?.groups?.dartVersion; -} +import { parsePubspecLock } from './utils'; export async function updateArtifacts({ packageFileName, @@ -63,9 +52,12 @@ export async function updateArtifacts({ ); } - const constraint = isFlutter - ? config.constraints?.flutter ?? getFlutterConstraint(oldLockFileContent) - : config.constraints?.dart ?? getDartConstraint(oldLockFileContent); + let constraint = config.constraints?.[toolName]; + if (!constraint) { + const pubspecLock = parsePubspecLock(lockFileName, oldLockFileContent); + constraint = pubspecLock?.sdks[toolName]; + } + const execOptions: ExecOptions = { cwdFile: packageFileName, docker: {}, diff --git a/lib/modules/manager/pub/schema.ts b/lib/modules/manager/pub/schema.ts new file mode 100644 index 0000000000..dbf72be5c7 --- /dev/null +++ b/lib/modules/manager/pub/schema.ts @@ -0,0 +1,13 @@ +import { z } from 'zod'; +import { Yaml } from '../../../util/schema-utils'; + +export const PubspecLockSchema = z.object({ + sdks: z.object({ + dart: z.string(), + flutter: z.string().optional(), + }), +}); + +export type PubspecLockSchema = z.infer<typeof PubspecLockSchema>; + +export const PubspecLockYaml = Yaml.pipe(PubspecLockSchema); diff --git a/lib/modules/manager/pub/utils.spec.ts b/lib/modules/manager/pub/utils.spec.ts new file mode 100644 index 0000000000..c236d4c0be --- /dev/null +++ b/lib/modules/manager/pub/utils.spec.ts @@ -0,0 +1,30 @@ +import { codeBlock } from 'common-tags'; +import { parsePubspecLock } from './utils'; + +describe('modules/manager/pub/utils', () => { + describe('parsePubspeckLock', () => { + const fileName = 'pubspec.lock'; + + it('load and parse successfully', () => { + const pubspecLock = codeBlock` + sdks: + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" + `; + const actual = parsePubspecLock(fileName, pubspecLock); + expect(actual).toMatchObject({ + sdks: { dart: '>=3.0.0 <4.0.0', flutter: '>=3.10.0' }, + }); + }); + + it('invalid yaml', () => { + const actual = parsePubspecLock(fileName, 'clearly-invalid'); + expect(actual).toBeNull(); + }); + + it('invalid schema', () => { + const actual = parsePubspecLock(fileName, 'clearly:\n\tinvalid: lock'); + expect(actual).toBeNull(); + }); + }); +}); diff --git a/lib/modules/manager/pub/utils.ts b/lib/modules/manager/pub/utils.ts new file mode 100644 index 0000000000..7aa7f05e0e --- /dev/null +++ b/lib/modules/manager/pub/utils.ts @@ -0,0 +1,18 @@ +import { logger } from '../../../logger'; +import { PubspecLockSchema, PubspecLockYaml } from './schema'; + +export function parsePubspecLock( + fileName: string, + fileContent: string +): PubspecLockSchema | null { + const res = PubspecLockYaml.safeParse(fileContent); + if (res.success) { + return res.data; + } else { + logger.debug( + { err: res.error, fileName }, + `Error parsing pubspec lockfile.` + ); + } + return null; +} diff --git a/lib/util/schema-utils.spec.ts b/lib/util/schema-utils.spec.ts index 9be7ac651c..c194236ce6 100644 --- a/lib/util/schema-utils.spec.ts +++ b/lib/util/schema-utils.spec.ts @@ -6,6 +6,7 @@ import { LooseRecord, Url, UtcDate, + Yaml, } from './schema-utils'; describe('util/schema-utils', () => { @@ -295,4 +296,48 @@ describe('util/schema-utils', () => { expect(() => Url.parse(urlStr)).toThrow('Invalid URL'); }); }); + + describe('Yaml', () => { + const Schema = Yaml.pipe( + z.object({ foo: z.array(z.object({ bar: z.literal('baz') })) }) + ); + + it('parses valid yaml', () => { + expect(Schema.parse('foo:\n- bar: baz')).toEqual({ + foo: [{ bar: 'baz' }], + }); + }); + + it('throws error for non-string', () => { + expect(Schema.safeParse(42)).toMatchObject({ + error: { + issues: [ + { + message: 'Expected string, received number', + code: 'invalid_type', + expected: 'string', + received: 'number', + path: [], + }, + ], + }, + success: false, + }); + }); + + it('throws error for invalid yaml', () => { + expect(Schema.safeParse('clearly: "invalid" "yaml"')).toMatchObject({ + error: { + issues: [ + { + message: 'Invalid YAML', + code: 'custom', + path: [], + }, + ], + }, + success: false, + }); + }); + }); }); diff --git a/lib/util/schema-utils.ts b/lib/util/schema-utils.ts index 0caa342e39..e200a04bd1 100644 --- a/lib/util/schema-utils.ts +++ b/lib/util/schema-utils.ts @@ -1,3 +1,4 @@ +import { load } from 'js-yaml'; import JSON5 from 'json5'; import { DateTime } from 'luxon'; import type { JsonValue } from 'type-fest'; @@ -232,3 +233,12 @@ export const Url = z.string().transform((str, ctx): URL => { return z.NEVER; } }); + +export const Yaml = z.string().transform((str, ctx): JsonValue => { + try { + return load(str, { json: true }) as JsonValue; + } catch (e) { + ctx.addIssue({ code: 'custom', message: 'Invalid YAML' }); + return z.NEVER; + } +}); -- GitLab