From 3026d34c4f1fb5226be232e2ede90cb4fd31781b Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Mon, 9 Dec 2024 01:09:44 +0300 Subject: [PATCH 1/2] feat: support multiple patterns --- README.md | 18 +++++++++++++----- src/main/ts/config.ts | 3 ++- src/main/ts/index.ts | 4 ++-- src/main/ts/util.ts | 3 +++ src/test/ts/config.ts | 31 +++++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 78f9243..e9c5a75 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,13 @@ gh-pages publishing plugin for [semantic-release](https://github.com/semantic-release/semantic-release) -| Step | Description | -|--------------------|-------------| +| Step | Description | +|--------------------|------------------------------------------------------------------------------------------------| | `verifyConditions` | Verify the presence of the `GH_TOKEN` set via [environment variables](#environment-variables). | -| `publish` | Pushes commit to the [documentation branch](#options) | +| `publish` | Pushes commit to the [documentation branch](#options) | ## Install + ```bash # yarn yarn add @qiwi/semantic-release-gh-pages-plugin --dev @@ -21,7 +22,10 @@ npm i @qiwi/semantic-release-gh-pages-plugin -D ``` ## Usage -Describe plugin configuration in [package.json / .releaserc.js](https://github.com/semantic-release/semantic-release/blob/master/docs/01-usage/plugins.md#plugins-configuration-options) + +Describe plugin configuration +in [package.json / .releaserc.js](https://github.com/semantic-release/semantic-release/blob/master/docs/01-usage/plugins.md#plugins-configuration-options) + ```json { "release": { @@ -46,7 +50,9 @@ Describe plugin configuration in [package.json / .releaserc.js](https://github.c } } ``` + or even shorter if default settings are used: + ```json { "release": { @@ -64,6 +70,7 @@ or even shorter if default settings are used: ``` ## Configuration + ### Environment variables | Variable | Description | @@ -84,7 +91,8 @@ or even shorter if default settings are used: | `pullTagsBranch` | Target branch for tags fetching hook. If '' empty string, skips this action | `globalConfig.branch` \|\| `master` | | `dotfiles` | gh-pages [dotfiles](https://github.com/tschaub/gh-pages#optionsdotfiles) option | `false` | | `add` | gh-pages [add](https://github.com/tschaub/gh-pages#optionsadd) option | `false` | -| `pattern` | gh-pages [src](https://github.com/tschaub/gh-pages#optionssrc) option | `**/*` | +| `pattern` | gh-pages [src](https://github.com/tschaub/gh-pages#optionssrc) option. Use `:` to separate several values `**/*.md:**/*.png` | `**/*` | ## License + [MIT](./LICENSE) diff --git a/src/main/ts/config.ts b/src/main/ts/config.ts index 2f0431c..a3ad47c 100644 --- a/src/main/ts/config.ts +++ b/src/main/ts/config.ts @@ -134,7 +134,7 @@ export const resolveConfig = async (pluginConfig: TAnyMap, context: TContext, pa msg = DEFAULT_MSG, src = DEFAULT_SRC, dst = DEFAULT_DST, - pattern = DEFAULT_PATTERN, + pattern: _pattern = DEFAULT_PATTERN, add, dotfiles } = opts @@ -144,6 +144,7 @@ export const resolveConfig = async (pluginConfig: TAnyMap, context: TContext, pa const docsBranch = branches?.find(([from]: string[]) => from === ciBranch)?.[1] || branch const pullTagsBranch = anyDefined(opts.pullTagsBranch, ciBranch, opts._branch, DEFAULT_PULL_TAGS_BRANCH) const token = getToken(context.env, repo) + const pattern = _pattern.includes(':') ? _pattern.split(':') : _pattern debug('resolveConfig args:') debug('pluginConfig= %j', pluginConfig) diff --git a/src/main/ts/index.ts b/src/main/ts/index.ts index 7341256..9650a23 100644 --- a/src/main/ts/index.ts +++ b/src/main/ts/index.ts @@ -1,7 +1,6 @@ /** @module semantic-release-gh-pages-plugin */ import AggregateError from 'aggregate-error' -import fs from 'node:fs' import { isEqual } from 'lodash' import path from 'node:path' @@ -9,6 +8,7 @@ import { resolveConfig } from './config' import { publish as ghpagesPublish } from './ghpages' import { IPushOpts,TContext } from './interface' import { render } from './tpl' +import { isDirectory } from './util' export * from './defaults' @@ -35,7 +35,7 @@ export const verifyConditions = async (pluginConfig: any, context: TContext) => throw new AggregateError(['package.json repository.url does not match github.com pattern']) } - if (!fs.existsSync(src) || !fs.lstatSync(src).isDirectory()) { + if (!isDirectory(src)) { logger.error('Resolved docs src path=', path.resolve(src)) throw new AggregateError(['docs source directory does not exist']) } diff --git a/src/main/ts/util.ts b/src/main/ts/util.ts index a1e4212..e0b3285 100644 --- a/src/main/ts/util.ts +++ b/src/main/ts/util.ts @@ -1,4 +1,5 @@ import { ICallable } from '@qiwi/substrate-types' +import fs from 'node:fs' export const catchToSmth = (fn: ICallable, smth?: any) => { return (...args: any[]) => { @@ -12,3 +13,5 @@ export const catchToSmth = (fn: ICallable, smth?: any) => { } export const anyDefined = (...args: any[]) => args.find(item => item !== undefined) + +export const isDirectory = (path: string) => fs.existsSync(path) && fs.lstatSync(path).isDirectory() diff --git a/src/test/ts/config.ts b/src/test/ts/config.ts index b774502..65c705e 100644 --- a/src/test/ts/config.ts +++ b/src/test/ts/config.ts @@ -147,6 +147,37 @@ describe('config', () => { }) }) + it('supports multiple pattern definition', async () => { + const step = 'publish' + const token = 'token' + const pluginConfig = { + pattern: '**/*.md:**/*.html' + } + const context = { + logger, + branch, + options: globalConfig, + cwd, + env: { GITHUB_TOKEN: token } + } + const config = await resolveConfig(pluginConfig, context, undefined, step) + + expect(config).toEqual({ + add: undefined, + ciBranch: 'master', + docsBranch: DEFAULT_BRANCH, + dotfiles: undefined, + dst: DEFAULT_DST, + enterprise: DEFAULT_ENTERPRISE, + msg: DEFAULT_MSG, + src: DEFAULT_SRC, + token, + repo: repositoryUrl, + pullTagsBranch: DEFAULT_PULL_TAGS_BRANCH, + pattern: ['**/*.md', '**/*.html'], + }) + }) + it('overrides `docBranch` with `branches` value if defined', async () => { const step = 'publish' const path = PLUGIN_PATH From c04c1e61e2761f49110ea554f1bc874235d46da3 Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Mon, 9 Dec 2024 01:17:10 +0300 Subject: [PATCH 2/2] perf: replace execa with zurk --- package.json | 4 ++-- src/main/ts/ghpages.ts | 20 +++++++------------- src/test/ts/ghpages.ts | 34 +++++++++++++--------------------- src/test/ts/index.ts | 38 +++++++++++++++++--------------------- yarn.lock | 7 ++++++- 5 files changed, 45 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index c979296..011a6c5 100644 --- a/package.json +++ b/package.json @@ -48,14 +48,14 @@ "@qiwi/substrate-types": "^2.1.0", "aggregate-error": "^3.1.0", "debug": "^4.4.0", - "execa": "^5.1.1", "gh-pages": "^6.2.0", "git-url-parse": "^16.0.0", "lodash": "^4.17.21", "queuefy": "^1.2.1", "read-pkg": "^5.2.0", "then-request": "^6.0.2", - "tslib": "^2.8.1" + "tslib": "^2.8.1", + "zurk": "^0.9.0" }, "devDependencies": { "@types/debug": "^4.1.12", diff --git a/src/main/ts/ghpages.ts b/src/main/ts/ghpages.ts index c7cccb8..5befcce 100644 --- a/src/main/ts/ghpages.ts +++ b/src/main/ts/ghpages.ts @@ -1,6 +1,6 @@ /** @module semantic-release-gh-pages-plugin */ -import execa from 'execa' +import { $ } from 'zurk' import { clean, publish as ghpagePublish, PublishOptions } from 'gh-pages' import { queuefy } from 'queuefy' @@ -21,19 +21,13 @@ export const pullTags = (opts: IPushOpts): Promise => { const repo = '' + opts.repo const pullTagsBranch = '' + opts.pullTagsBranch - const execaOpts = { - env: opts.env, - cwd: opts.cwd - } - return execa('git', [ - 'pull', - '--tags', - '--force', - repo, - pullTagsBranch - ], execaOpts) - .catch(console.log) + return $({ + env: opts.env, + cwd: opts.cwd, + shell: false + })`git pull --tags --force ${repo} ${pullTagsBranch}` + .catch(console.error) } /** diff --git a/src/test/ts/ghpages.ts b/src/test/ts/ghpages.ts index 943051f..7ee8037 100644 --- a/src/test/ts/ghpages.ts +++ b/src/test/ts/ghpages.ts @@ -3,12 +3,13 @@ import { ICallable } from '@qiwi/substrate-types' import { IPushOpts, TAnyMap } from '../../main/ts/interface' describe('ghpages', () => { - const fakeExeca = jest.fn(() => Promise.resolve()) + const _$ = jest.fn(() => Promise.resolve()) + const __$ = jest.fn(() => _$) let pullTags: any beforeAll(() => { jest.resetModules() - jest.mock('execa', () => fakeExeca) + jest.mock('zurk', () => ({$: __$})) jest.mock('gh-pages', () => ({ clean: () => { /* noop */ }, publish: jest.fn((_src: string, _opts: TAnyMap, cb: ICallable) => cb()) @@ -17,11 +18,11 @@ describe('ghpages', () => { pullTags = require('../../main/ts/ghpages').pullTags }) - afterEach(fakeExeca.mockClear) + afterEach(_$.mockClear) afterAll(() => { jest.unmock('gh-pages') - jest.unmock('execa') + jest.unmock('zurk') jest.resetModules() }) @@ -45,31 +46,22 @@ describe('ghpages', () => { pullTagsBranch: '' } expect(await pullTags(opts)).toBeUndefined() - expect(fakeExeca).not.toHaveBeenCalled() + expect(_$).not.toHaveBeenCalled() }) - it('invokes execa with proper args', async () => { + it('invokes zurk with proper args', async () => { const opts: IPushOpts = { ...pushOptsStub, pullTagsBranch: 'foo' } - const execaOpts = { - cwd: opts.cwd, - env: opts.env - } await pullTags(opts) - expect(fakeExeca).toHaveBeenCalledWith( - 'git', - [ - 'pull', - '--tags', - '--force', - opts.repo, - opts.pullTagsBranch - ], - execaOpts - ) + expect(_$).toHaveBeenCalledWith(["git pull --tags --force ", " ", ""], "repo", "foo") + expect(__$).toHaveBeenCalledWith({ + cwd: opts.cwd, + env: opts.env, + shell: false + }) }) }) }) diff --git a/src/test/ts/index.ts b/src/test/ts/index.ts index 6b43c60..a8efae1 100644 --- a/src/test/ts/index.ts +++ b/src/test/ts/index.ts @@ -146,7 +146,9 @@ describe('index', () => { }) describe('publish', () => { - const fakeExeca = jest.fn(() => Promise.resolve()) + const _$ = jest.fn(() => Promise.resolve()) + const __$ = jest.fn(() => _$) + const fakeZurk = {$: __$} beforeAll(() => { jest.resetModules() @@ -165,14 +167,17 @@ describe('index', () => { } }) })) - jest.mock('execa', () => fakeExeca) + jest.mock('zurk', () => fakeZurk) }) - afterEach(fakeExeca.mockClear) + afterEach(() => { + __$.mockClear() + _$.mockClear() + }) afterAll(() => { jest.unmock('gh-pages') - jest.unmock('execa') + jest.unmock('zurk') jest.resetModules() }) @@ -208,27 +213,18 @@ describe('index', () => { dest: 'root', src: DEFAULT_PATTERN, } - const execaOpts = { - cwd, - env: { - GITHUB_TOKEN: 'token' - } - } const res = await publish(pluginConfig, context) const resolvedConfig = await resolveConfig(pluginConfig, context) - expect(fakeExeca).toHaveBeenCalledWith( - 'git', - [ - 'pull', - '--tags', - '--force', - expectedOpts.repo, - resolvedConfig.pullTagsBranch - ], - execaOpts - ) + expect(_$).toHaveBeenCalledWith(["git pull --tags --force ", " ", ""], expectedOpts.repo, resolvedConfig.pullTagsBranch) + expect(__$).toHaveBeenCalledWith({ + cwd, + env: { + GITHUB_TOKEN: 'token' + }, + shell: false + }) expect(log).toHaveBeenCalledWith('Publishing docs via gh-pages') expect(log).toHaveBeenCalledWith(`Docs published successfully, branch=doc-branch, src=docs, pattern=${DEFAULT_PATTERN}, dst=root`) diff --git a/yarn.lock b/yarn.lock index e286d50..65649ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2029,7 +2029,7 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -execa@^5.0.0, execa@^5.1.1: +execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -5066,3 +5066,8 @@ yocto-queue@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + +zurk@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/zurk/-/zurk-0.9.0.tgz#9dbea11ef84a871cfb542ddded8c7d42c8feeaf5" + integrity sha512-MfREZRQx5VySt3VtNrfP+UTALaTTIBquhSuJS/RYAs59YkwAicx6QrXyMMpvJu0wQdfWiJJAhAeOw4+OJLZ8ig==