From 007a4304ee0a4e54f0c7d6bbec983699d5aca168 Mon Sep 17 00:00:00 2001 From: Andrey Helldar Date: Thu, 15 Jan 2026 16:37:17 +0300 Subject: [PATCH 1/5] Upgrade API --- .github/preview-updater.yml | 6 +- README.md | 110 ++++++++------ resources/schema.json | 12 +- src/main.ts | 47 +++--- src/types/config.ts | 108 ++++---------- src/types/data.ts | 4 + src/types/image.ts | 4 + src/types/lockFile.ts | 7 + src/types/package.ts | 9 +- src/types/repository.ts | 26 ++++ src/utils/config.ts | 6 +- src/utils/icons.ts | 12 +- src/utils/image.ts | 69 +++++---- src/utils/packageManagers.ts | 12 +- src/utils/preview.ts | 6 +- src/utils/repository.ts | 14 +- src/utils/strings.ts | 2 +- tests/fixtures/configs/composer-dev.yml | 7 +- .../fixtures/configs/composer-global-dev.yml | 9 +- tests/fixtures/configs/custom-dev.yml | 7 +- tests/fixtures/configs/custom-global-dev.yml | 9 +- tests/fixtures/configs/npm-dev.yml | 7 +- tests/fixtures/configs/npm-global-dev.yml | 9 +- tests/fixtures/configs/preview.yml | 39 ++--- tests/fixtures/configs/yarn-dev.yml | 7 +- tests/fixtures/configs/yarn-global-dev.yml | 9 +- tests/helpers/config.ts | 17 ++- tests/helpers/filesystem.ts | 6 +- .../__snapshots__/filesystem.test.ts.snap | 115 +++++++++++++++ tests/unit/__snapshots__/preview.test.ts.snap | 138 +++++++++--------- tests/unit/filesystem.test.ts | 73 ++------- tests/unit/icons.test.ts | 10 +- tests/unit/packageManagers.test.ts | 6 +- tests/unit/preview.test.ts | 135 +++++------------ 34 files changed, 549 insertions(+), 508 deletions(-) create mode 100644 src/types/data.ts create mode 100644 src/types/image.ts create mode 100644 src/types/lockFile.ts create mode 100644 src/types/repository.ts create mode 100644 tests/unit/__snapshots__/filesystem.test.ts.snap diff --git a/.github/preview-updater.yml b/.github/preview-updater.yml index cdbb2a7..5522c49 100644 --- a/.github/preview-updater.yml +++ b/.github/preview-updater.yml @@ -1,7 +1,9 @@ # $schema: ../resources/schema.json +package: + manager: 'uses:' + name: 'TheDragonCode/github-preview-updater@v2' + image: parameters: icon: 'photograph' - packageManager: 'uses:' - packageName: 'TheDragonCode/github-preview-updater@v1' diff --git a/README.md b/README.md index 4480d09..e97db2c 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,9 @@ The action is setting the following outputs: ## Configuration > [!TIP] -> +> > Support for working with a global settings file at the organization level (the `.github` repository). -> +> > For example, > > - template is `https://github.com//.github/blob/main/`. @@ -82,47 +82,7 @@ All fields are optional—omitted ones fall back to defaults. ```yaml # $schema: https://raw.githubusercontent.com/TheDragonCode/github-preview-updater/refs/heads/main/resources/schema.json -path: - readme: README.md # Target file to update - -image: - url: https://banners.beyondco.de/{title}.png - parameters: - pattern: topography - style: style_2 - fontSize: 100px - icon: code - - # Declares the use of the package manager. - # It is a regular string that will be substituted into the URL address. - # - # Reserved words: composer | npm | yarn | auto | none - # - # Any package manager name can be specified. - # - # By default, auto - packageManager: auto - - # By default, the package name is taken from the composer.json or package.json file. - packageName: '' - - # Add a prefix for global installation (`composer global require`, `npm install -g`) - # The parameter will be ignored when a non-standard package manager name is specified in - # the `packageManager` parameter. - packageGlobal: false - - # Add a prefix for dev installation (`composer require --dev`, `npm install --save-dev`) - # The parameter will be ignored when a non-standard package manager name is specified in - # the `packageManager` parameter - packageDev: false - - # By default, the repository name will be used. - # For example, for https://github.com/TheDragonCode/github-preview-updater, it will take `preview-updater` - # and convert it to `Preview Updater`. - title: '' # Fallbacks to repo name (Title Case) - - # By default, the package description will be used (the ` description ` key in composer.json or package.json). - description: '' # Fallbacks to owner name or package description +path: README.md # Target file to update repository: commit: @@ -138,12 +98,76 @@ repository: body: '' assignees: [ ] labels: [ 'preview' ] + +data: + # By default, the repository name will be used. + # For example, for https://github.com/TheDragonCode/github-preview-updater, it will take `preview-updater` + # and convert it to `Preview Updater`. + title: '' # Fallbacks to repo name (Title Case) + + # By default, the package description will be used (the ` description ` key in composer.json or package.json). + description: '' # Fallbacks to owner name or package description + +package: + # Declares the use of the package manager. + # It is a regular string that will be substituted into the URL address. + # + # Reserved words: composer | npm | yarn | auto | none + # + # Any package manager name can be specified. + # + # By default, auto + manager: auto + + # Add a prefix for global installation (`composer global require`, `npm install -g`) + # The parameter will be ignored when a non-standard package manager name is specified in + # the `packageManager` parameter. + global: false + + # Add a prefix for dev installation (`composer require --dev`, `npm install --save-dev`) + # The parameter will be ignored when a non-standard package manager name is specified in + # the `packageManager` parameter + dev: false + + # By default, the package name is taken from the composer.json or package.json file. + name: '' + +image: + url: https://banners.beyondco.de/{title}.png + + # May contain any keys and values + parameters: + pattern: topography + style: style_2 + fontSize: 100px + icon: code + ``` Currently, the project generates previews through [banners.beyondco.de](https://banners.beyondco.de) and the parameters are specified for it. But you can use any other service by replacing the URL. +### Information on how link formation works + +An object is created, containing combined data from the [`data`](src/types/data.ts) and [ +`image.parameters`](src/types/image.ts) objects. + +A link is taken from the `image.url` parameter and using the `replace` method, everything is replaced in it according to +the `{key}` pattern, where `key` is the object key from the merged object. + +For example, if the `image.url` parameter is `https://banners.beyondco.de/{title}/{foo}-{bar}.png`, and the `data` +object is `{ title: 'Qwe rty', foo: 'asd', bar: 'zxc' }`, then the resulting link will be +`https://banners.beyondco.de/Qwe%20rty/asd-zxc.png`. + +After this, the original `image.parameters` object is taken and query parameters are formed from it. + +For example, if the `image.parameters` object is `{ foo: 'asd', bar: 'zxc' }`, then the resulting query parameters +will be `?foo=asd&bar=zxc`. + +Ultimately, the image link will look like this: +`https://banners.beyondco.de/Qwe%20rty/asd-zxc.png?foo=asd&bar=zxc`. + ## License This project is licensed under the [MIT License](LICENSE.md). diff --git a/resources/schema.json b/resources/schema.json index e7c2818..c7dec01 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -5,15 +5,9 @@ "additionalProperties": false, "properties": { "path": { - "type": "object", - "additionalProperties": false, - "properties": { - "readme": { - "type": "string", - "description": "Target README file where the banner will be inserted", - "default": "README.md" - } - } + "type": "string", + "description": "Target README file where the banner will be inserted", + "default": "README.md" }, "image": { "type": "object", diff --git a/src/main.ts b/src/main.ts index 6734af9..b4ba784 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,7 +9,9 @@ import { setOutputs } from "./utils/outputs"; import { getPackageManager } from "./utils/packageManagers"; import { titleCase } from "./utils/strings"; import { readConfig } from "./utils/config"; +import type { LockFile } from "./types/lockFile"; import type { Package } from "./types/package"; +import type { Data } from "./types/data"; const previewUpdater = async () => { // Inputs @@ -29,12 +31,15 @@ const previewUpdater = async () => { ); // Read names - const packageData: Package = getPackageManager(config); + const packageLock: LockFile = getPackageManager(config); - config.image.parameters.packageName ||= packageData.name; - config.image.parameters.title ||= titleCase(config.repository.repo); - config.image.parameters.description ||= - packageData.description || config.repository.owner; + config.package ||= {}; + config.data ||= {}; + + config.package.name ||= packageLock.name; + config.data.title ||= titleCase(config.repository.repo); + config.data.description ||= + packageLock.description || config.repository.owner; // Show working directory info(`Working directory: ${config.directory}`); @@ -44,11 +49,11 @@ const previewUpdater = async () => { await repo.authenticate(); // Read file - const content = readFile(config, config.path.readme); - const preview = setPreview(content, config, packageData); + const content = readFile(config, config.readme); + const preview = setPreview(content, config, packageLock); if (content === preview) { - info(`File "${config.path.readme}" is up to date`); + info(`File "${config.readme}" is up to date`); return; } @@ -61,8 +66,8 @@ const previewUpdater = async () => { await repo.checkoutBranch(!branchExists); // Write a file - info(`Update readme in "${config.path.readme}" file`); - writeFile(config, config.path.readme, preview); + info(`Update readme in "${config.readme}" file`); + writeFile(config, config.readme, preview); // Stage and commit changes await repo.stage(); @@ -76,19 +81,15 @@ const previewUpdater = async () => { const pullRequestNumber: number = pullRequest.data.number; const pullRequestUrl: string = pullRequest.data.html_url; - if (config.repository.pullRequest.assignees.length > 0) { - await repo.assignee( - pullRequestNumber, - config.repository.pullRequest.assignees, - ); - } - - if (config.repository.pullRequest.labels.length > 0) { - await repo.addLabels( - pullRequestNumber, - config.repository.pullRequest.labels, - ); - } + // Set labels and assignees + await repo.assignee( + pullRequestNumber, + config.repository.pullRequest.assignees, + ); + await repo.addLabels( + pullRequestNumber, + config.repository.pullRequest.labels, + ); info( `Preview created in Pull Request #${pullRequestNumber}: ${pullRequestUrl}`, diff --git a/src/types/config.ts b/src/types/config.ts index dc0e423..9cfe98d 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,95 +1,26 @@ -export interface ImageParameters { - pattern: string; - style: string; - fontSize: string; - icon?: string; - - packageManager: "composer" | "npm" | "yarn" | "auto" | "none" | string; - packageName?: string; - packageGlobal: boolean; - packageDev: boolean; - - title?: string; - description?: string; -} - -export interface Image { - url: string; - parameters: ImageParameters; -} - -export interface Author { - name: string; - email: string; -} - -export interface Commit { - branch: string; - title: string; - body?: string; - author: Author; -} - -export interface PullRequest { - title: string; - body?: string; - assignees: string[]; - labels: string[]; -} - -export interface Repository { - owner?: string; - repo?: string; - - commit: Commit; - pullRequest: PullRequest; -} - -export interface Path { - readme: string; -} +import type { Repository } from "./repository"; +import type { Image } from "./image"; +import type { Data } from "./data"; +import type { Package } from "./package"; export interface Config { directory?: string; - path: Path; + readme: string; - image: Image; repository: Repository; + + data?: Data; + package?: Package; + image?: Image; } export const defaultConfig: Config = { - directory: undefined, - path: { - readme: "README.md", - }, - - image: { - url: "https://banners.beyondco.de/{title}.png", - - parameters: { - pattern: "topography", - style: "style_2", - fontSize: "100px", - icon: undefined, - - packageManager: "auto", - packageGlobal: false, - packageDev: false, - packageName: undefined, - - title: undefined, - description: undefined, - }, - }, + readme: "README.md", repository: { - owner: undefined, - repo: undefined, - commit: { branch: "preview/banner-{random}", title: "docs(preview): Update preview", - body: undefined, author: { name: "github-actions", @@ -99,9 +30,26 @@ export const defaultConfig: Config = { pullRequest: { title: "Update preview", - body: undefined, assignees: [], labels: ["preview"], }, }, + + package: { + manager: "auto", + global: false, + dev: false, + }, + + image: { + url: "https://banners.beyondco.de/{title}.png", + + parameters: { + pattern: "topography", + style: "style_2", + fontSize: "100px", + md: "1", + showWatermark: "1", + }, + }, }; diff --git a/src/types/data.ts b/src/types/data.ts new file mode 100644 index 0000000..a24fd6d --- /dev/null +++ b/src/types/data.ts @@ -0,0 +1,4 @@ +export interface Data { + title?: string; + description?: string; +} diff --git a/src/types/image.ts b/src/types/image.ts new file mode 100644 index 0000000..3dfa9b1 --- /dev/null +++ b/src/types/image.ts @@ -0,0 +1,4 @@ +export interface Image { + url: string; + parameters: Record; +} diff --git a/src/types/lockFile.ts b/src/types/lockFile.ts new file mode 100644 index 0000000..9c190bd --- /dev/null +++ b/src/types/lockFile.ts @@ -0,0 +1,7 @@ +export interface LockFile { + name: string; + description: string; + + dependencies?: Record[]; + require?: Record[]; +} diff --git a/src/types/package.ts b/src/types/package.ts index 2ea1a0e..c960405 100644 --- a/src/types/package.ts +++ b/src/types/package.ts @@ -1,7 +1,6 @@ export interface Package { - name: string; - description: string; - - dependencies?: Record[]; - require?: Record[]; + manager: "composer" | "npm" | "yarn" | "auto" | "none" | string; + global: boolean; + dev: boolean; + name?: string; } diff --git a/src/types/repository.ts b/src/types/repository.ts new file mode 100644 index 0000000..06d34a8 --- /dev/null +++ b/src/types/repository.ts @@ -0,0 +1,26 @@ +export interface Author { + name: string; + email: string; +} + +export interface Commit { + branch: string; + title: string; + body?: string; + author: Author; +} + +export interface PullRequest { + title: string; + body?: string; + assignees: string[]; + labels: string[]; +} + +export interface Repository { + owner?: string; + repo?: string; + + commit: Commit; + pullRequest: PullRequest; +} diff --git a/src/utils/config.ts b/src/utils/config.ts index 294b347..725ff40 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -33,9 +33,9 @@ export const readRemoteConfig = async ( return {}; } - const url = `https://raw.githubusercontent.com/${owner}/.github/refs/heads/main/${filename}`; - - const data: string = await readRemoteFile(url); + const data: string = await readRemoteFile( + `https://raw.githubusercontent.com/${owner}/.github/refs/heads/main/${filename}`, + ); if (data === "") { return {}; diff --git a/src/utils/icons.ts b/src/utils/icons.ts index c25470b..1f2ed4e 100644 --- a/src/utils/icons.ts +++ b/src/utils/icons.ts @@ -1,5 +1,6 @@ import type { Icon } from "../types/icons"; -import type { Package } from "../types/package"; +import type { LockFile } from "../types/lockFile"; +import type { Image } from "../types/image"; export const phpIcons: Icon[] = [ { query: "laravel/", icon: "https://laravel.com/img/logomark.min.svg" }, @@ -33,7 +34,14 @@ const find = ( return undefined; }; -export const detectIcon = (packageData: Package): string => { +export const detectIcon = ( + image: Image | undefined, + packageData: LockFile, +): string => { + if (image?.parameters?.icon) { + return image.parameters.icon; + } + if (packageData?.require !== undefined) { const phpIcon: string | undefined = find(packageData.require, phpIcons); diff --git a/src/utils/image.ts b/src/utils/image.ts index 16a9b42..987ea03 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -1,8 +1,9 @@ -import type { Config, ImageParameters } from "../types/config"; +import type { Config } from "../types/config"; import { hasComposer, hasNpm, hasYarn } from "./packageManagers"; -import { encodeUri } from "./strings"; -import type { Package } from "../types/package"; +import type { LockFile } from "../types/lockFile"; import { detectIcon } from "./icons"; +import type { Package } from "../types/package"; +import { encodeUri } from "./strings"; const command = (manager: string, dev: boolean, global: boolean): string => { switch (manager) { @@ -34,9 +35,9 @@ const detectPackageManager = (config: Config): string => { }; const packageManager = (config: Config): string => { - const global: boolean = config.image.parameters.packageGlobal; - const dev: boolean = config.image.parameters.packageDev; - let name: string = config.image.parameters.packageManager; + const global: boolean = config.package?.global || false; + const dev: boolean = config.package?.dev || false; + let name: string = config.package?.manager || "auto"; if (name === "none") { return ""; @@ -50,49 +51,47 @@ const packageManager = (config: Config): string => { return command(name, dev, global); } - return config.image.parameters.packageManager.trim(); + return name.trim(); }; -const packageName = (image: ImageParameters): string => { - if (image.packageManager === "none") { +const packageName = (data?: Package): string => { + if (data?.manager === "none") { return ""; } - return image?.packageName || ""; + return data?.name || ""; }; const render = ( config: Config, - packageData: Package, + packageData: LockFile, theme: "light" | "dark", ): string => { - const image = config.image.parameters; - - const params = new URLSearchParams({ - theme: theme, - pattern: image.pattern, - style: image.style, - fontSize: image.fontSize, - images: image.icon || detectIcon(packageData), - packageManager: packageManager(config), - packageName: packageName(image), - description: image.description || "", - md: "1", - showWatermark: "1", - }); - - return ( - config.image.url.replace("{title}", encodeUri(image.title)) + - "?" + - params.toString() - ); + let url: string = config.image?.url || ""; + const parameters: Record = config.image?.parameters || {}; + + parameters.theme = theme; + + parameters.packageManager = packageManager(config); + parameters.packageName = packageName(config.package); + + parameters.title = config.data?.title || ""; + parameters.description = config.data?.description || ""; + + parameters.images = detectIcon(config.image, packageData); + + for (const [key, value] of Object.entries(parameters)) { + url = url.replace(`{${key}}`, encodeUri(value)); + } + + return `${url}?${(new URLSearchParams(parameters)).toString()}`; }; -export const getImages = (config: Config, packageData: Package): string => { - const title = config.image.parameters.title; +export const getImages = (config: Config, packageData: LockFile): string => { + const title: string | undefined = config.data?.title; - const light = render(config, packageData, "light"); - const dark = render(config, packageData, "dark"); + const light: string = render(config, packageData, "light"); + const dark: string = render(config, packageData, "dark"); return ` diff --git a/src/utils/packageManagers.ts b/src/utils/packageManagers.ts index c413b1c..0bf2d1f 100644 --- a/src/utils/packageManagers.ts +++ b/src/utils/packageManagers.ts @@ -1,6 +1,6 @@ import { fileExists, readFile } from "./filesystem"; import type { Config } from "../types/config"; -import type { Package } from "../types/package"; +import type { LockFile } from "../types/lockFile"; export const hasComposer = (config: Config): boolean => fileExists(config, "composer.json"); @@ -11,13 +11,13 @@ export const hasNpm = (config: Config): boolean => export const hasYarn = (config: Config): boolean => fileExists(config, "yarn.lock"); -export const getComposer = (config: Config): Package => - JSON.parse(readFile(config, "composer.json")); +export const getComposer = (config: Config): LockFile => + JSON.parse(readFile(config, "composer.json")); -export const getNpm = (config: Config): Package => - JSON.parse(readFile(config, "package.json")); +export const getNpm = (config: Config): LockFile => + JSON.parse(readFile(config, "package.json")); -export const getPackageManager = (config: Config): Package => { +export const getPackageManager = (config: Config): LockFile => { if (hasComposer(config)) { return getComposer(config); } diff --git a/src/utils/preview.ts b/src/utils/preview.ts index f82a663..2949cab 100644 --- a/src/utils/preview.ts +++ b/src/utils/preview.ts @@ -1,17 +1,17 @@ import { getImages } from "./image"; import type { Config } from "../types/config"; import { removeImages, titleCase } from "./strings"; -import type { Package } from "../types/package"; +import type { LockFile } from "../types/lockFile"; const hasHeader = (content: string) => content.match(/^#\s+/); export const setPreview = ( content: string, config: Config, - packageData: Package, + packageData: LockFile, ): string => { if (!hasHeader(content)) { - const title = titleCase(config.image.parameters.title); + const title = titleCase(config.data?.title); content = `# ${title}\n\n${content}`; } diff --git a/src/utils/repository.ts b/src/utils/repository.ts index c479600..d7af3b1 100644 --- a/src/utils/repository.ts +++ b/src/utils/repository.ts @@ -73,10 +73,10 @@ export class Repository { async stage() { try { - await exec(`git add ${this._config.path.readme}`); + await exec(`git add ${this._config.readme}`); } catch (error) { // @ts-expect-error - error.message = `Error staging file "${this._config.path.readme}": ${error.message}`; + error.message = `Error staging file "${this._config.readme}": ${error.message}`; throw error; } @@ -94,7 +94,7 @@ export class Repository { await exec(`git commit -m "${message}"`); } catch (error) { // @ts-expect-error - error.message = `Error committing file "${this._config.path.readme}": ${error.message}`; + error.message = `Error committing file "${this._config.readme}": ${error.message}`; throw error; } @@ -143,6 +143,10 @@ export class Repository { async assignee(issueNumber: number, assignees: string[]) { try { + if (assignees.length === 0) { + return; + } + return this._octokit.rest.issues.addAssignees(< RestEndpointMethodTypes["issues"]["addAssignees"]["parameters"] >{ @@ -161,6 +165,10 @@ export class Repository { async addLabels(issueNumber: number, labels: string[]) { try { + if (labels.length === 0) { + return; + } + return this._octokit.rest.issues.addLabels(< RestEndpointMethodTypes["issues"]["addLabels"]["parameters"] >{ diff --git a/src/utils/strings.ts b/src/utils/strings.ts index 62bbf07..16d7723 100644 --- a/src/utils/strings.ts +++ b/src/utils/strings.ts @@ -8,7 +8,7 @@ const normalizeWords = (value: string): string => { return value; }; -export const titleCase = (title: string | undefined) => { +export const titleCase = (title?: string) => { if (title === "" || title === undefined) { return ""; } diff --git a/tests/fixtures/configs/composer-dev.yml b/tests/fixtures/configs/composer-dev.yml index a78d3bc..4d05d90 100644 --- a/tests/fixtures/configs/composer-dev.yml +++ b/tests/fixtures/configs/composer-dev.yml @@ -1,4 +1,3 @@ -image: - parameters: - packageManager: composer - packageDev: true +package: + manager: composer + dev: true diff --git a/tests/fixtures/configs/composer-global-dev.yml b/tests/fixtures/configs/composer-global-dev.yml index e37f224..801302c 100644 --- a/tests/fixtures/configs/composer-global-dev.yml +++ b/tests/fixtures/configs/composer-global-dev.yml @@ -1,5 +1,4 @@ -image: - parameters: - packageManager: composer - packageGlobal: true - packageDev: true +package: + manager: composer + global: true + dev: true diff --git a/tests/fixtures/configs/custom-dev.yml b/tests/fixtures/configs/custom-dev.yml index 827255b..be08400 100644 --- a/tests/fixtures/configs/custom-dev.yml +++ b/tests/fixtures/configs/custom-dev.yml @@ -1,4 +1,3 @@ -image: - parameters: - packageManager: foo - packageDev: true +package: + manager: foo + dev: true diff --git a/tests/fixtures/configs/custom-global-dev.yml b/tests/fixtures/configs/custom-global-dev.yml index e94de39..937d124 100644 --- a/tests/fixtures/configs/custom-global-dev.yml +++ b/tests/fixtures/configs/custom-global-dev.yml @@ -1,5 +1,4 @@ -image: - parameters: - packageManager: foo - packageGlobal: true - packageDev: true +package: + manager: foo + global: true + dev: true diff --git a/tests/fixtures/configs/npm-dev.yml b/tests/fixtures/configs/npm-dev.yml index 6d83cae..df11b23 100644 --- a/tests/fixtures/configs/npm-dev.yml +++ b/tests/fixtures/configs/npm-dev.yml @@ -1,4 +1,3 @@ -image: - parameters: - packageManager: npm - packageDev: true +package: + manager: npm + dev: true diff --git a/tests/fixtures/configs/npm-global-dev.yml b/tests/fixtures/configs/npm-global-dev.yml index 52ef753..c0b75a6 100644 --- a/tests/fixtures/configs/npm-global-dev.yml +++ b/tests/fixtures/configs/npm-global-dev.yml @@ -1,5 +1,4 @@ -image: - parameters: - packageManager: npm - packageGlobal: true - packageDev: true +package: + manager: npm + global: true + dev: true diff --git a/tests/fixtures/configs/preview.yml b/tests/fixtures/configs/preview.yml index daf43f2..0944e0d 100644 --- a/tests/fixtures/configs/preview.yml +++ b/tests/fixtures/configs/preview.yml @@ -1,20 +1,4 @@ -path: - readme: 'README-foo.md' - -image: - url: 'https://example.com/image.png' - parameters: - pattern: 'cage' - style: 'style_1' - fontSize: '123px' - icon: 'cog' - - packageManager: 'yarn' - packageGlobal: true - packageName: 'foo/bar' - - title: 'Foo Bar' - description: 'Lorem ipsum dolor sit amet.' +readme: 'README-foo.md' repository: commit: @@ -35,3 +19,24 @@ repository: labels: - foo3 - foo4 + +data: + title: 'Foo Bar' + description: 'Lorem ipsum dolor sit amet.' + +package: + manager: yarn + global: true + name: 'foo/bar' + +image: + url: 'https://example.com/image.png' + parameters: + pattern: 'cage' + style: 'style_1' + fontSize: '123px' + icon: 'cog' + qwe: 'rty' + foo: 'Foo' + bar: 'Bar' + baz: 'Baz' diff --git a/tests/fixtures/configs/yarn-dev.yml b/tests/fixtures/configs/yarn-dev.yml index 5673344..ec9bd98 100644 --- a/tests/fixtures/configs/yarn-dev.yml +++ b/tests/fixtures/configs/yarn-dev.yml @@ -1,4 +1,3 @@ -image: - parameters: - packageManager: yarn - packageDev: true +package: + manager: yarn + dev: true diff --git a/tests/fixtures/configs/yarn-global-dev.yml b/tests/fixtures/configs/yarn-global-dev.yml index 1642818..6bef439 100644 --- a/tests/fixtures/configs/yarn-global-dev.yml +++ b/tests/fixtures/configs/yarn-global-dev.yml @@ -1,5 +1,4 @@ -image: - parameters: - packageManager: yarn - packageGlobal: true - packageDev: true +package: + manager: yarn + global: true + dev: true diff --git a/tests/helpers/config.ts b/tests/helpers/config.ts index 79cdfeb..d52fd01 100644 --- a/tests/helpers/config.ts +++ b/tests/helpers/config.ts @@ -1,18 +1,23 @@ import { type Config, defaultConfig } from "../../src/types/config"; import { deepmerge } from "deepmerge-ts"; +import { readConfig } from "../../src/utils/config"; export const rawTestConfig: Config = { directory: process.cwd(), - image: { - parameters: { - packageName: "TheDragonCode/github-preview-updater", - title: "Preview Updater", - description: "Lightweight preview update in your repository", - }, + data: { + title: "Some title", + description: "Some description", + }, + + package: { + name: "foo/bar-baz", }, }; export const testConfig: Config = ( deepmerge(defaultConfig, rawTestConfig) ); + +export const readTestConfig = async (filename: string): Promise => + await readConfig({ directory: process.cwd() }, filename); diff --git a/tests/helpers/filesystem.ts b/tests/helpers/filesystem.ts index 5125ffd..3a5a0f7 100644 --- a/tests/helpers/filesystem.ts +++ b/tests/helpers/filesystem.ts @@ -1,7 +1,7 @@ import { readFile } from "../../src/utils/filesystem"; import { setPreview } from "../../src/utils/preview"; import { testConfig } from "./config"; -import type { Package } from "../../src/types/package"; +import type { LockFile } from "../../src/types/lockFile"; import { getNpm } from "../../src/utils/packageManagers"; import type { Config } from "../../src/types/config"; import { merge } from "../../src/utils/merge"; @@ -16,6 +16,6 @@ export const getReadme = (filename: string, config?: Config): string => { return setPreview(content, config, getNpm(config)); }; -export const getPackage = (filename: string): Package => { - return JSON.parse(readFile(testConfig, filename)); +export const getPackage = (filename: string): LockFile => { + return JSON.parse(readFile(testConfig, filename)); }; diff --git a/tests/unit/__snapshots__/filesystem.test.ts.snap b/tests/unit/__snapshots__/filesystem.test.ts.snap new file mode 100644 index 0000000..ceea376 --- /dev/null +++ b/tests/unit/__snapshots__/filesystem.test.ts.snap @@ -0,0 +1,115 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`custom config 1`] = `"README-foo.md"`; + +exports[`custom config 2`] = ` +{ + "commit": { + "author": { + "email": "some_username@example.com", + "name": "some_username", + }, + "body": "Eu assum suscipit, vel veniam eu sadipscing kasd invidunt elit wisi.", + "branch": "qwerty", + "title": "Foo Bar Commit", + }, + "pullRequest": { + "assignees": [ + "foo1", + "foo2", + ], + "body": "Eu assum suscipit, vel veniam eu sadipscing kasd invidunt elit wisi.", + "labels": [ + "foo3", + "foo4", + ], + "title": "Foo Bar Baz Pull Request", + }, +} +`; + +exports[`custom config 3`] = ` +{ + "description": "Lorem ipsum dolor sit amet.", + "title": "Foo Bar", +} +`; + +exports[`custom config 4`] = ` +{ + "dev": false, + "global": true, + "manager": "yarn", + "name": "foo/bar", +} +`; + +exports[`custom config 5`] = ` +{ + "parameters": { + "bar": "Bar", + "baz": "Baz", + "fontSize": "123px", + "foo": "Foo", + "icon": "cog", + "md": "1", + "pattern": "cage", + "qwe": "rty", + "showWatermark": "1", + "style": "style_1", + }, + "url": "https://example.com/image.png", +} +`; + +exports[`read config 1`] = `"README.md"`; + +exports[`read config 2`] = ` +{ + "commit": { + "author": { + "email": "github-actions@github.com", + "name": "github-actions", + }, + "branch": "preview/banner-{random}", + "title": "docs(preview): Update preview", + }, + "pullRequest": { + "assignees": [], + "labels": [ + "preview", + ], + "title": "Update preview", + }, +} +`; + +exports[`read config 3`] = ` +{ + "description": "Some description", + "title": "Some title", +} +`; + +exports[`read config 4`] = ` +{ + "dev": false, + "global": false, + "manager": "uses:", + "name": "foo/bar-baz", +} +`; + +exports[`read config 5`] = ` +{ + "parameters": { + "fontSize": "100px", + "icon": "photograph", + "md": "1", + "pattern": "topography", + "showWatermark": "1", + "style": "style_2", + }, + "url": "https://banners.beyondco.de/{title}.png", +} +`; diff --git a/tests/unit/__snapshots__/preview.test.ts.snap b/tests/unit/__snapshots__/preview.test.ts.snap index dacef53..fb69bd6 100644 --- a/tests/unit/__snapshots__/preview.test.ts.snap +++ b/tests/unit/__snapshots__/preview.test.ts.snap @@ -1,134 +1,130 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`composer dev 1`] = ` -"# Preview Updater +exports[`custom config 1`] = ` +"# Some Title - - Preview Updater + + Some title Something " `; -exports[`composer global dev 1`] = ` -"# Preview Updater +exports[`custom config 2`] = ` +"# Some Title - - Preview Updater + + Some title Something " `; -exports[`custom dev 1`] = ` -"# Preview Updater +exports[`custom config 3`] = ` +"# Some Title - - Preview Updater + + Some title Something " `; -exports[`custom global dev 1`] = ` -"# Preview Updater +exports[`custom config 4`] = ` +"# Some Title - - Preview Updater + + Some title Something " `; -exports[`empty 1`] = ` -"# Preview Updater +exports[`custom config 5`] = ` +"# Some Title - - Preview Updater + + Some title +Something " `; -exports[`html-tag 1`] = ` -"# Preview Updater +exports[`custom config 6`] = ` +"# Some Title - - Preview Updater + + Some title -## Usage - Something " `; -exports[`just-text 1`] = ` -"# Preview Updater +exports[`custom config 7`] = ` +"# Some Title - - Preview Updater + + Some title Something " `; -exports[`npm dev 1`] = ` -"# Preview Updater +exports[`custom config 8`] = ` +"# Some Title - - Preview Updater + + Some title Something " `; -exports[`npm global dev 1`] = ` -"# Preview Updater +exports[`default config 1`] = ` +"# Some Title - - Preview Updater + + Some title -Something " `; -exports[`with-one-image 1`] = ` -"# Preview Updater +exports[`default config 2`] = ` +"# Some Title - - Preview Updater + + Some title -## Usage - Something " `; -exports[`with-one-image-without-header 1`] = ` +exports[`default config 3`] = ` "# Preview Updater - - Preview Updater + + Some title ## Usage @@ -137,12 +133,12 @@ Something " `; -exports[`with-two-images 1`] = ` -"# Preview Updater +exports[`default config 4`] = ` +"# Some Title - - Preview Updater + + Some title ## Usage @@ -151,12 +147,12 @@ Something " `; -exports[`with-two-images-without-header 1`] = ` +exports[`default config 5`] = ` "# Preview Updater - - Preview Updater + + Some title ## Usage @@ -165,12 +161,12 @@ Something " `; -exports[`without-all 1`] = ` -"# Preview Updater +exports[`default config 6`] = ` +"# Some Title - - Preview Updater + + Some title ## Usage @@ -179,12 +175,12 @@ Something " `; -exports[`without-images 1`] = ` -"# Preview Updater +exports[`default config 7`] = ` +"# Some Title - - Preview Updater + + Some title ## Usage @@ -193,26 +189,30 @@ Something " `; -exports[`yarn dev 1`] = ` +exports[`default config 8`] = ` "# Preview Updater - - Preview Updater + + Some title +## Usage + Something " `; -exports[`yarn global dev 1`] = ` +exports[`default config 9`] = ` "# Preview Updater - - Preview Updater + + Some title +## Usage + Something " `; diff --git a/tests/unit/filesystem.test.ts b/tests/unit/filesystem.test.ts index bacc811..6984f71 100644 --- a/tests/unit/filesystem.test.ts +++ b/tests/unit/filesystem.test.ts @@ -1,79 +1,36 @@ import { rawTestConfig } from "../helpers/config"; -import { type Config, defaultConfig } from "../../src/types/config"; +import type { Config } from "../../src/types/config"; import { CONFIG_PATH } from "../../src/utils/inputs"; import { readConfig } from "../../src/utils/config"; test("read config", async () => { - const data: Config = await readConfig( + const config: Config = await readConfig( rawTestConfig, CONFIG_PATH.defaultValue, ); - expect(data.directory).toBe(process.cwd()); + expect(config.directory).toBe(process.cwd()); + expect(config.readme).toMatchSnapshot(); - expect(data.image.parameters.title).toBe("Preview Updater"); - expect(data.image.parameters.description).toBe( - "Lightweight preview update in your repository", - ); - - expect(data.path.readme).toBe(defaultConfig.path.readme); - expect(data.image.url).toBe(defaultConfig.image.url); - expect(data.image.parameters.pattern).toBe( - defaultConfig.image.parameters.pattern, - ); - - expect(data.image.parameters.packageManager).toBe("uses:"); - expect(data.image.parameters.packageName).toBe( - "TheDragonCode/github-preview-updater", - ); - expect(data.image.parameters.icon).toBe("photograph"); + expect(config.repository).toMatchSnapshot(); + expect(config.data).toMatchSnapshot(); + expect(config.package).toMatchSnapshot(); + expect(config.image).toMatchSnapshot(); }); test("custom config", async () => { - const data: Config = await readConfig( + const config: Config = await readConfig( { directory: process.cwd(), }, "tests/fixtures/configs/preview.yml", ); - expect(data.path.readme).toBe("README-foo.md"); - - expect(data.image.url).toBe("https://example.com/image.png"); - expect(data.image.parameters.pattern).toBe("cage"); - expect(data.image.parameters.style).toBe("style_1"); - - expect(data.image.parameters.fontSize).toBe("123px"); - expect(data.image.parameters.icon).toBe("cog"); - - expect(data.image.parameters.packageManager).toBe("yarn"); - expect(data.image.parameters.packageGlobal).toBe(true); - expect(data.image.parameters.packageName).toBe("foo/bar"); - - expect(data.image.parameters.title).toBe("Foo Bar"); - expect(data.image.parameters.description).toBe( - "Lorem ipsum dolor sit amet.", - ); - - expect(data.repository.commit.branch).toBe("qwerty"); - expect(data.repository.commit.title).toBe("Foo Bar Commit"); - expect(data.repository.commit.body).toBe( - "Eu assum suscipit, vel veniam eu sadipscing kasd invidunt elit wisi.", - ); - - expect(data.repository.commit.author.name).toBe("some_username"); - expect(data.repository.commit.author.email).toBe( - "some_username@example.com", - ); - - expect(data.repository.pullRequest.title).toBe("Foo Bar Baz Pull Request"); - expect(data.repository.pullRequest.body).toBe( - "Eu assum suscipit, vel veniam eu sadipscing kasd invidunt elit wisi.", - ); - - expect(data.repository.pullRequest.assignees.length).toBe(2); - expect(data.repository.pullRequest.assignees.join("-")).toBe("foo1-foo2"); + expect(config.directory).toBe(process.cwd()); + expect(config.readme).toMatchSnapshot(); - expect(data.repository.pullRequest.labels.length).toBe(2); - expect(data.repository.pullRequest.labels.join("-")).toBe("foo3-foo4"); + expect(config.repository).toMatchSnapshot(); + expect(config.data).toMatchSnapshot(); + expect(config.package).toMatchSnapshot(); + expect(config.image).toMatchSnapshot(); }); diff --git a/tests/unit/icons.test.ts b/tests/unit/icons.test.ts index 3425bd1..d652651 100644 --- a/tests/unit/icons.test.ts +++ b/tests/unit/icons.test.ts @@ -9,29 +9,29 @@ import { test("composer laravel", () => { const data = getPackage("tests/fixtures/packages/composer-laravel.json"); - expect(detectIcon(data)).toBe(phpIcons[0].icon); + expect(detectIcon(undefined, data)).toBe(phpIcons[0].icon); }); test("composer illuminate", () => { const data = getPackage("tests/fixtures/packages/composer-illuminate.json"); - expect(detectIcon(data)).toBe(phpIcons[1].icon); + expect(detectIcon(undefined, data)).toBe(phpIcons[1].icon); }); test("composer symfony", () => { const data = getPackage("tests/fixtures/packages/composer-symfony.json"); - expect(detectIcon(data)).toBe(phpIcons[2].icon); + expect(detectIcon(undefined, data)).toBe(phpIcons[2].icon); }); test("composer default", () => { const data = getPackage("tests/fixtures/packages/composer-default.json"); - expect(detectIcon(data)).toBe(defaultPhpIcon); + expect(detectIcon(undefined, data)).toBe(defaultPhpIcon); }); test("npm default", () => { const data = getPackage("tests/fixtures/packages/npm-default.json"); - expect(detectIcon(data)).toBe(defaultJsIcon); + expect(detectIcon(undefined, data)).toBe(defaultJsIcon); }); diff --git a/tests/unit/packageManagers.test.ts b/tests/unit/packageManagers.test.ts index 2da9b81..c032333 100644 --- a/tests/unit/packageManagers.test.ts +++ b/tests/unit/packageManagers.test.ts @@ -2,13 +2,13 @@ import { hasComposer, hasNpm, hasYarn } from "../../src/utils/packageManagers"; import { testConfig } from "../helpers/config"; test("composer", () => { - expect(hasComposer(testConfig)).toBe(false); + expect(hasComposer(testConfig)).toBeFalsy(); }); test("npm", () => { - expect(hasNpm(testConfig)).toBe(true); + expect(hasNpm(testConfig)).toBeTruthy(); }); test("yarn", () => { - expect(hasYarn(testConfig)).toBe(false); + expect(hasYarn(testConfig)).toBeFalsy(); }); diff --git a/tests/unit/preview.test.ts b/tests/unit/preview.test.ts index a395141..23d6ede 100644 --- a/tests/unit/preview.test.ts +++ b/tests/unit/preview.test.ts @@ -1,99 +1,42 @@ import { getReadme } from "../helpers/filesystem"; import type { Config } from "../../src/types/config"; -import { readConfig } from "../../src/utils/config"; - -test("empty", () => expect(getReadme("empty.md")).toMatchSnapshot()); - -test("just-text", () => expect(getReadme("just-text.md")).toMatchSnapshot()); - -test("with-one-image", () => - expect(getReadme("with-one-image.md")).toMatchSnapshot()); - -test("with-one-image-without-header", () => - expect(getReadme("with-one-image-without-header.md")).toMatchSnapshot()); - -test("with-two-images", () => - expect(getReadme("with-two-images.md")).toMatchSnapshot()); - -test("with-two-images-without-header", () => - expect(getReadme("with-two-images-without-header.md")).toMatchSnapshot()); - -test("without-all", () => - expect(getReadme("without-all.md")).toMatchSnapshot()); - -test("without-images", () => - expect(getReadme("without-images.md")).toMatchSnapshot()); - -test("html-tag", () => expect(getReadme("html-tag.md")).toMatchSnapshot()); - -test("composer dev", async () => { - const config: Config = await readConfig( - { directory: process.cwd() }, - "tests/fixtures/configs/composer-dev.yml", - ); - - expect(getReadme("just-text.md", config)).toMatchSnapshot(); -}); - -test("composer global dev", async () => { - const config: Config = await readConfig( - { directory: process.cwd() }, - "tests/fixtures/configs/composer-global-dev.yml", - ); - - expect(getReadme("just-text.md", config)).toMatchSnapshot(); -}); - -test("npm dev", async () => { - const config: Config = await readConfig( - { directory: process.cwd() }, - "tests/fixtures/configs/npm-dev.yml", - ); - - expect(getReadme("just-text.md", config)).toMatchSnapshot(); -}); - -test("npm global dev", async () => { - const config: Config = await readConfig( - { directory: process.cwd() }, - "tests/fixtures/configs/npm-global-dev.yml", - ); - - expect(getReadme("just-text.md", config)).toMatchSnapshot(); -}); - -test("yarn dev", async () => { - const config: Config = await readConfig( - { directory: process.cwd() }, - "tests/fixtures/configs/yarn-dev.yml", - ); - - expect(getReadme("just-text.md", config)).toMatchSnapshot(); -}); - -test("yarn global dev", async () => { - const config: Config = await readConfig( - { directory: process.cwd() }, - "tests/fixtures/configs/yarn-global-dev.yml", - ); - - expect(getReadme("just-text.md", config)).toMatchSnapshot(); -}); - -test("custom dev", async () => { - const config: Config = await readConfig( - { directory: process.cwd() }, - "tests/fixtures/configs/custom-dev.yml", - ); - - expect(getReadme("just-text.md", config)).toMatchSnapshot(); -}); - -test("custom global dev", async () => { - const config: Config = await readConfig( - { directory: process.cwd() }, - "tests/fixtures/configs/custom-global-dev.yml", - ); - - expect(getReadme("just-text.md", config)).toMatchSnapshot(); +import { readTestConfig } from "../helpers/config"; + +test("default config", () => { + const files: string[] = [ + "empty.md", + "just-text.md", + "with-one-image.md", + "with-one-image-without-header.md", + "with-two-images.md", + "with-two-images-without-header.md", + "without-all.md", + "without-images.md", + "html-tag.md", + ]; + + for (const file of files) { + expect(getReadme(file)).toMatchSnapshot(); + } +}); + +test("custom config", async () => { + const files: string[] = [ + "composer-dev.yml", + "composer-global-dev.yml", + "npm-dev.yml", + "npm-global-dev.yml", + "yarn-dev.yml", + "yarn-global-dev.yml", + "custom-dev.yml", + "custom-global-dev.yml", + ]; + + for (const file of files) { + const config: Config = await readTestConfig( + `tests/fixtures/configs/${file}`, + ); + + expect(getReadme("just-text.md", config)).toMatchSnapshot(); + } }); From ef92f8862475620a410f598c60b091b1e181db25 Mon Sep 17 00:00:00 2001 From: Andrey Helldar Date: Thu, 15 Jan 2026 16:52:46 +0300 Subject: [PATCH 2/5] Update schema --- .junie/guidelines.md | 16 +++++ resources/schema.json | 154 +++++++++++++++++++++------------------- src/types/config.ts | 4 +- src/types/image.ts | 4 +- src/types/package.ts | 6 +- src/types/repository.ts | 20 +++--- 6 files changed, 112 insertions(+), 92 deletions(-) create mode 100644 .junie/guidelines.md diff --git a/.junie/guidelines.md b/.junie/guidelines.md new file mode 100644 index 0000000..f65564c --- /dev/null +++ b/.junie/guidelines.md @@ -0,0 +1,16 @@ +## Triggers + +### Update schema + +Ignore the following properties: + +- `directory` +- `repository.owner` +- `repository.repo` + +Steps: + +1. Carefully study the `Config` interface in the `src/types/config.ts` file. +2. Study its dependencies. +3. Memorize the overall relationship scheme of these interfaces. +4. Update the JSON schema in the `resources/schema.json` file in accordance with the new requirements. diff --git a/resources/schema.json b/resources/schema.json index c7dec01..0b7b5ae 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -4,85 +4,11 @@ "type": "object", "additionalProperties": false, "properties": { - "path": { + "readme": { "type": "string", "description": "Target README file where the banner will be inserted", "default": "README.md" }, - "image": { - "type": "object", - "additionalProperties": false, - "properties": { - "url": { - "type": "string", - "description": "Banner generation service URL", - "default": "https://banners.beyondco.de/{title}.png" - }, - "parameters": { - "type": "object", - "additionalProperties": false, - "properties": { - "pattern": { - "type": "string", - "default": "topography" - }, - "style": { - "type": "string", - "default": "style_2" - }, - "fontSize": { - "type": "string", - "default": "100px" - }, - "icon": { - "type": "string", - "default": "code" - }, - "packageManager": { - "description": "Package manager name. Reserved values: composer | npm | yarn | auto | none", - "anyOf": [ - { - "type": "string", - "enum": [ - "composer", - "npm", - "yarn", - "auto", - "none" - ] - }, - { - "type": "string" - } - ], - "default": "auto" - }, - "packageName": { - "type": "string", - "description": "Package name. By default taken from composer.json or package.json" - }, - "packageGlobal": { - "type": "boolean", - "description": "Add prefix for global installation", - "default": false - }, - "packageDev": { - "type": "boolean", - "description": "Add a prefix for dev installation", - "default": false - }, - "title": { - "type": "string", - "description": "Banner title. The repository name is used by default" - }, - "description": { - "type": "string", - "description": "Package description. The repository description is used by default" - } - } - } - } - }, "repository": { "type": "object", "additionalProperties": false, @@ -157,6 +83,84 @@ } } } + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "title": { + "type": "string", + "description": "Banner title. Defaults to repository name" + }, + "description": { + "type": "string", + "description": "Banner description. Defaults to repository description or owner" + } + } + }, + "package": { + "type": "object", + "additionalProperties": false, + "properties": { + "manager": { + "description": "Package manager name. Reserved values: composer | npm | yarn | auto | none", + "anyOf": [ + { + "type": "string", + "enum": [ + "composer", + "npm", + "yarn", + "auto", + "none" + ] + }, + { + "type": "string" + } + ], + "default": "auto" + }, + "global": { + "type": "boolean", + "description": "Add prefix for global installation", + "default": false + }, + "dev": { + "type": "boolean", + "description": "Add a prefix for dev installation", + "default": false + }, + "name": { + "type": "string", + "description": "Package name. By default taken from composer.json or package.json" + } + } + }, + "image": { + "type": "object", + "additionalProperties": false, + "properties": { + "url": { + "type": "string", + "description": "Banner generation service URL", + "default": "https://banners.beyondco.de/{title}.png" + }, + "parameters": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Query parameters passed to the image service", + "default": { + "pattern": "topography", + "style": "style_2", + "fontSize": "100px", + "md": "1", + "showWatermark": "1" + } + } + } } } } diff --git a/src/types/config.ts b/src/types/config.ts index 9cfe98d..2ab275b 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -5,9 +5,9 @@ import type { Package } from "./package"; export interface Config { directory?: string; - readme: string; + readme?: string; - repository: Repository; + repository?: Repository; data?: Data; package?: Package; diff --git a/src/types/image.ts b/src/types/image.ts index 3dfa9b1..442e919 100644 --- a/src/types/image.ts +++ b/src/types/image.ts @@ -1,4 +1,4 @@ export interface Image { - url: string; - parameters: Record; + url?: string; + parameters?: Record; } diff --git a/src/types/package.ts b/src/types/package.ts index c960405..77ce9f6 100644 --- a/src/types/package.ts +++ b/src/types/package.ts @@ -1,6 +1,6 @@ export interface Package { - manager: "composer" | "npm" | "yarn" | "auto" | "none" | string; - global: boolean; - dev: boolean; + manager?: "composer" | "npm" | "yarn" | "auto" | "none" | string; + global?: boolean; + dev?: boolean; name?: string; } diff --git a/src/types/repository.ts b/src/types/repository.ts index 06d34a8..fff7c06 100644 --- a/src/types/repository.ts +++ b/src/types/repository.ts @@ -1,26 +1,26 @@ export interface Author { - name: string; - email: string; + name?: string; + email?: string; } export interface Commit { - branch: string; - title: string; + branch?: string; + title?: string; body?: string; - author: Author; + author?: Author; } export interface PullRequest { - title: string; + title?: string; body?: string; - assignees: string[]; - labels: string[]; + assignees?: string[]; + labels?: string[]; } export interface Repository { owner?: string; repo?: string; - commit: Commit; - pullRequest: PullRequest; + commit?: Commit; + pullRequest?: PullRequest; } From 2f1161dfe71164c9e1302c5e80275bd4ee0f167b Mon Sep 17 00:00:00 2001 From: Andrey Helldar Date: Thu, 15 Jan 2026 17:25:30 +0300 Subject: [PATCH 3/5] Refactor repository configuration to use shared default values. --- .junie/guidelines.md | 10 +++++-- src/libs/defaults.ts | 51 ++++++++++++++++++++++++++++++++++++ src/main.ts | 22 +++++++++++----- src/types/config.ts | 42 +---------------------------- src/utils/config.ts | 3 ++- src/utils/repository.ts | 58 +++++++++++++++++++++++++++-------------- tests/helpers/config.ts | 3 ++- 7 files changed, 119 insertions(+), 70 deletions(-) create mode 100644 src/libs/defaults.ts diff --git a/.junie/guidelines.md b/.junie/guidelines.md index f65564c..f7406a4 100644 --- a/.junie/guidelines.md +++ b/.junie/guidelines.md @@ -1,6 +1,12 @@ ## Triggers -### Update schema +### JSON schema + +Aliases: +- `update schema` +- `upgrade schema` +- `schema` +- `json schema` Ignore the following properties: @@ -10,7 +16,7 @@ Ignore the following properties: Steps: -1. Carefully study the `Config` interface in the `src/types/config.ts` file. +1. Carefully study the `Config` interface in the `src/types/defaults.ts` file. 2. Study its dependencies. 3. Memorize the overall relationship scheme of these interfaces. 4. Update the JSON schema in the `resources/schema.json` file in accordance with the new requirements. diff --git a/src/libs/defaults.ts b/src/libs/defaults.ts new file mode 100644 index 0000000..b84e495 --- /dev/null +++ b/src/libs/defaults.ts @@ -0,0 +1,51 @@ +import type { Config } from "../types/config"; +import type { Image } from "../types/image"; +import type { Package } from "../types/package"; +import type { Author, Commit, PullRequest } from "../types/repository"; + +export const defaultPackage: Package = { + manager: "auto", + global: false, + dev: false, +}; + +export const defaultImage: Image = { + url: "https://banners.beyondco.de/{title}.png", + + parameters: { + pattern: "topography", + style: "style_2", + fontSize: "100px", + md: "1", + showWatermark: "1", + }, +}; + +export const defaultAuthor: Author = { + name: "github-actions", + email: "github-actions@github.com", +}; + +export const defaultCommit: Commit = { + branch: "preview/banner-{random}", + title: "docs(preview): Update preview", + + author: defaultAuthor, +}; + +export const defaultPullRequest: PullRequest = { + title: "Update preview", + labels: ["preview"], +}; + +export const defaultConfig: Config = { + readme: "README.md", + + repository: { + commit: defaultCommit, + pullRequest: defaultPullRequest, + }, + + package: defaultPackage, + image: defaultImage, +}; diff --git a/src/main.ts b/src/main.ts index b4ba784..fcef22e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,8 +10,12 @@ import { getPackageManager } from "./utils/packageManagers"; import { titleCase } from "./utils/strings"; import { readConfig } from "./utils/config"; import type { LockFile } from "./types/lockFile"; -import type { Package } from "./types/package"; import type { Data } from "./types/data"; +import { + defaultConfig, + defaultPackage, + defaultPullRequest, +} from "./libs/defaults"; const previewUpdater = async () => { // Inputs @@ -33,13 +37,15 @@ const previewUpdater = async () => { // Read names const packageLock: LockFile = getPackageManager(config); - config.package ||= {}; + config.readme ||= defaultConfig.readme || "README.md"; + + config.package ||= defaultPackage; config.data ||= {}; config.package.name ||= packageLock.name; - config.data.title ||= titleCase(config.repository.repo); + config.data.title ||= titleCase(config.repository?.repo); config.data.description ||= - packageLock.description || config.repository.owner; + packageLock.description || config.repository?.owner; // Show working directory info(`Working directory: ${config.directory}`); @@ -84,11 +90,15 @@ const previewUpdater = async () => { // Set labels and assignees await repo.assignee( pullRequestNumber, - config.repository.pullRequest.assignees, + config.repository?.pullRequest?.assignees || + defaultPullRequest.assignees || + [], ); await repo.addLabels( pullRequestNumber, - config.repository.pullRequest.labels, + config.repository?.pullRequest?.labels || + defaultPullRequest.labels || + [], ); info( diff --git a/src/types/config.ts b/src/types/config.ts index 2ab275b..077069a 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,7 +1,7 @@ import type { Repository } from "./repository"; -import type { Image } from "./image"; import type { Data } from "./data"; import type { Package } from "./package"; +import type { Image } from "./image"; export interface Config { directory?: string; @@ -13,43 +13,3 @@ export interface Config { package?: Package; image?: Image; } - -export const defaultConfig: Config = { - readme: "README.md", - - repository: { - commit: { - branch: "preview/banner-{random}", - title: "docs(preview): Update preview", - - author: { - name: "github-actions", - email: "github-actions@github.com", - }, - }, - - pullRequest: { - title: "Update preview", - assignees: [], - labels: ["preview"], - }, - }, - - package: { - manager: "auto", - global: false, - dev: false, - }, - - image: { - url: "https://banners.beyondco.de/{title}.png", - - parameters: { - pattern: "topography", - style: "style_2", - fontSize: "100px", - md: "1", - showWatermark: "1", - }, - }, -}; diff --git a/src/utils/config.ts b/src/utils/config.ts index 725ff40..379cda3 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,9 +1,10 @@ -import { type Config, defaultConfig } from "../types/config"; +import type { Config } from "../types/config"; import * as yaml from "js-yaml"; import { readFile, readRemoteFile } from "./filesystem"; import { merge } from "./merge"; import { info } from "@actions/core"; import * as url from "node:url"; +import { defaultConfig } from "../libs/defaults"; export const readConfig = async ( config: Config, diff --git a/src/utils/repository.ts b/src/utils/repository.ts index d7af3b1..6456b21 100644 --- a/src/utils/repository.ts +++ b/src/utils/repository.ts @@ -3,6 +3,11 @@ import type { GitHub } from "@actions/github/lib/utils"; import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types"; import { exec } from "./processes"; import { randomString } from "./strings"; +import { + defaultAuthor, + defaultCommit, + defaultPullRequest, +} from "../libs/defaults"; export class Repository { private _config: Config; @@ -16,14 +21,18 @@ export class Repository { } async authenticate() { - try { - const author = this._config.repository.commit.author; + const authorName = + this._config.repository?.commit?.author?.name || defaultAuthor.name; + const authorEmail = + this._config.repository?.commit?.author?.name || + defaultAuthor.email; - await exec(`git config user.name "${author.name}"`); - await exec(`git config user.email "${author.email}"`); + try { + await exec(`git config user.name "${authorName}"`); + await exec(`git config user.email "${authorEmail}"`); } catch (error) { // @ts-expect-error - error.message = `Error authenticating user "${author.name}" with e-mail "${author.email}": ${error.message}`; + error.message = `Error authenticating user "${authorName}" with e-mail "${author.email}": ${error.message}`; throw error; } @@ -84,8 +93,12 @@ export class Repository { async commit() { try { - let message = this._config.repository.commit.title; - const body = this._config.repository.commit.body || ""; + let message = + this._config.repository?.commit?.title || defaultCommit.title; + const body = + this._config.repository?.commit?.body || + defaultCommit.body || + ""; if (body !== "") { message += `\n${body}`; @@ -126,10 +139,15 @@ export class Repository { return this._octokit.rest.pulls.create(< RestEndpointMethodTypes["pulls"]["create"]["parameters"] >{ - owner: this._config.repository.owner, - repo: this._config.repository.repo, - title: this._config.repository.pullRequest.title, - body: this._config.repository.pullRequest.body, + owner: this._config.repository?.owner, + repo: this._config.repository?.repo, + title: + this._config.repository?.pullRequest?.title || + defaultPullRequest.title, + body: + this._config.repository?.pullRequest?.body || + defaultPullRequest.body || + "", head: this.branchName(), base: defaultBranch, }); @@ -150,8 +168,8 @@ export class Repository { return this._octokit.rest.issues.addAssignees(< RestEndpointMethodTypes["issues"]["addAssignees"]["parameters"] >{ - owner: this._config.repository.owner, - repo: this._config.repository.repo, + owner: this._config.repository?.owner, + repo: this._config.repository?.repo, issue_number: issueNumber, assignees: assignees, }); @@ -172,8 +190,8 @@ export class Repository { return this._octokit.rest.issues.addLabels(< RestEndpointMethodTypes["issues"]["addLabels"]["parameters"] >{ - owner: this._config.repository.owner, - repo: this._config.repository.repo, + owner: this._config.repository?.owner, + repo: this._config.repository?.repo, issue_number: issueNumber, labels, }); @@ -187,10 +205,12 @@ export class Repository { branchName(): string { if (this._currentBranch === "") { - this._currentBranch = this._config.repository.commit.branch.replace( - "{random}", - randomString(), - ); + const branch: string = + this._config.repository?.commit?.branch || + defaultCommit.branch || + "preview/{random}"; + + this._currentBranch = branch.replace("{random}", randomString()); } return this._currentBranch; diff --git a/tests/helpers/config.ts b/tests/helpers/config.ts index d52fd01..3a65eb4 100644 --- a/tests/helpers/config.ts +++ b/tests/helpers/config.ts @@ -1,6 +1,7 @@ -import { type Config, defaultConfig } from "../../src/types/config"; +import type { Config } from "../../src/types/config"; import { deepmerge } from "deepmerge-ts"; import { readConfig } from "../../src/utils/config"; +import { defaultConfig } from "../../src/libs/defaults"; export const rawTestConfig: Config = { directory: process.cwd(), From 09b4e492977d337fe91c3a901bdcb32c408ac7b7 Mon Sep 17 00:00:00 2001 From: Andrey Helldar Date: Thu, 15 Jan 2026 17:26:09 +0300 Subject: [PATCH 4/5] Remove unused 'assignees' field from filesystem test snapshot --- tests/unit/__snapshots__/filesystem.test.ts.snap | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/__snapshots__/filesystem.test.ts.snap b/tests/unit/__snapshots__/filesystem.test.ts.snap index ceea376..5ad59bf 100644 --- a/tests/unit/__snapshots__/filesystem.test.ts.snap +++ b/tests/unit/__snapshots__/filesystem.test.ts.snap @@ -75,7 +75,6 @@ exports[`read config 2`] = ` "title": "docs(preview): Update preview", }, "pullRequest": { - "assignees": [], "labels": [ "preview", ], From 44919869290ecbed2cce9b50d7e5d50b4d8d1ca0 Mon Sep 17 00:00:00 2001 From: Andrey Helldar Date: Thu, 15 Jan 2026 17:27:23 +0300 Subject: [PATCH 5/5] Add default values for repository, package, and image schema fields --- resources/schema.json | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/resources/schema.json b/resources/schema.json index 0b7b5ae..9710653 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -12,10 +12,34 @@ "repository": { "type": "object", "additionalProperties": false, + "default": { + "commit": { + "branch": "preview/banner-{random}", + "title": "docs(preview): Update preview", + "author": { + "name": "github-actions", + "email": "github-actions@github.com" + } + }, + "pullRequest": { + "title": "Update preview", + "labels": [ + "preview" + ] + } + }, "properties": { "commit": { "type": "object", "additionalProperties": false, + "default": { + "branch": "preview/banner-{random}", + "title": "docs(preview): Update preview", + "author": { + "name": "github-actions", + "email": "github-actions@github.com" + } + }, "properties": { "branch": { "type": "string", @@ -101,6 +125,11 @@ "package": { "type": "object", "additionalProperties": false, + "default": { + "manager": "auto", + "global": false, + "dev": false + }, "properties": { "manager": { "description": "Package manager name. Reserved values: composer | npm | yarn | auto | none", @@ -140,6 +169,16 @@ "image": { "type": "object", "additionalProperties": false, + "default": { + "url": "https://banners.beyondco.de/{title}.png", + "parameters": { + "pattern": "topography", + "style": "style_2", + "fontSize": "100px", + "md": "1", + "showWatermark": "1" + } + }, "properties": { "url": { "type": "string",