@liuli-util/cli 3.16.0 → 3.18.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +30 -42
- package/README.zh-CN.md +86 -0
- package/dist/bin.js +72 -72
- package/dist/bin.js.map +3 -3
- package/dist/commands/deploy/DeployService.d.ts +26 -7
- package/dist/commands/deploy/DeployService.d.ts.map +1 -1
- package/dist/commands/deploy/index.d.ts.map +1 -1
- package/dist/commands/deploy/util/validate.d.ts +8 -0
- package/dist/commands/deploy/util/validate.d.ts.map +1 -0
- package/package.json +74 -72
- package/src/commands/deploy/DeployService.ts +103 -46
- package/src/commands/deploy/__tests__/DeployService.test.ts +52 -19
- package/src/commands/deploy/__tests__/simpleGit.test.ts +26 -1
- package/src/commands/deploy/index.ts +2 -1
- package/src/commands/deploy/util/validate.ts +18 -0
- package/src/commands/sync/__tests__/.temp/package.json +16 -0
- package/templates/cli/package.json +1 -5
- package/templates/cli/src/bin.ts +1 -1
- package/templates/cli/tsconfig.json +28 -28
- package/templates/lib/package.json +3 -10
- package/templates/lib/tsconfig.json +28 -28
- package/tsconfig.json +34 -34
- package/src/commands/esbuild/__tests__/.temp/getDeps/package.json +0 -1
- package/src/commands/generate/__tests__/.temp/test-cli/CHANGELOG.md +0 -1
- package/src/commands/generate/__tests__/.temp/test-cli/README.md +0 -1
- package/src/commands/generate/__tests__/.temp/test-cli/bin.js +0 -3
- package/src/commands/generate/__tests__/.temp/test-cli/package.json +0 -44
- package/src/commands/generate/__tests__/.temp/test-cli/src/bin.ts +0 -13
- package/src/commands/generate/__tests__/.temp/test-cli/src/index.ts +0 -1
- package/src/commands/generate/__tests__/.temp/test-cli/tsconfig.json +0 -28
@@ -19,13 +19,14 @@ export declare enum DeployTypeEnum {
|
|
19
19
|
}
|
20
20
|
export interface BaseDeployOptions {
|
21
21
|
cwd: string;
|
22
|
+
debug: boolean;
|
22
23
|
type: DeployTypeEnum;
|
23
24
|
}
|
24
|
-
export
|
25
|
+
export interface SftpDeployOptions extends Omit<BaseDeployOptions, 'type'> {
|
26
|
+
dist: string;
|
25
27
|
dest: string;
|
26
|
-
remote: string;
|
27
28
|
sshConfig: ConnectOptions;
|
28
|
-
}
|
29
|
+
}
|
29
30
|
/**
|
30
31
|
* sftp 集成到远端
|
31
32
|
*/
|
@@ -35,10 +36,28 @@ export declare class SftpDeployService implements IDeployService {
|
|
35
36
|
deploy(): EventExtPromise<void, DeployEvents>;
|
36
37
|
validate(): [isValidate: boolean, errorText: string];
|
37
38
|
}
|
38
|
-
export
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
export interface GhPagesDeployOptions extends Omit<BaseDeployOptions, 'type'> {
|
40
|
+
/**
|
41
|
+
* 推送的本地目录
|
42
|
+
*/
|
43
|
+
dist: string;
|
44
|
+
/**
|
45
|
+
* 推送的远端目录,默认为分支根目录
|
46
|
+
*/
|
47
|
+
dest?: string;
|
48
|
+
/**
|
49
|
+
* 推送的项目 git 地址,默认为当前项目
|
50
|
+
*/
|
51
|
+
repo?: string;
|
52
|
+
/**
|
53
|
+
* 推送的远端,默认为 origin
|
54
|
+
*/
|
55
|
+
remote?: string;
|
56
|
+
/**
|
57
|
+
* 远端分支名,默认为 gh-pages
|
58
|
+
*/
|
59
|
+
branch?: string;
|
60
|
+
}
|
42
61
|
/**
|
43
62
|
* 将本地静态资源推送到 gh-pages 远端
|
44
63
|
*/
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"DeployService.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy/DeployService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAe,MAAM,oBAAoB,CAAA;AACjE,OAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;
|
1
|
+
{"version":3,"file":"DeployService.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy/DeployService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAe,MAAM,oBAAoB,CAAA;AACjE,OAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAWzD,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;IACpD;;OAEG;IACH,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;CAC9C;AAED,oBAAY,cAAc;IACxB,OAAO,aAAa;IACpB,IAAI,SAAS;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,OAAO,CAAA;IACd,IAAI,EAAE,cAAc,CAAA;CACrB;AAED,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACxE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,cAAc,CAAA;CAC1B;AAED;;GAEG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iBAAiB;IAEvD,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,YAAY,CAAC;IAc7C,QAAQ,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC;CAsBrD;AAED,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAC3E;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,oBAAoB;IAE1D,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,YAAY,CAAC;IA8E7C,QAAQ,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC;CAkBlD"}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAInC,eAAO,MAAM,aAAa,
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAInC,eAAO,MAAM,aAAa,SAG0E,CAAA"}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { JSONSchemaType } from 'ajv';
|
2
|
+
/**
|
3
|
+
* 使用 ajv 校验数据
|
4
|
+
* @param schema json 模式配置
|
5
|
+
* @param data 校验的数据
|
6
|
+
*/
|
7
|
+
export declare function validate<T>(schema: JSONSchemaType<T>, data: T): [isValid: boolean, errorText: string];
|
8
|
+
//# sourceMappingURL=validate.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../../src/commands/deploy/util/validate.ts"],"names":[],"mappings":"AAAA,OAAY,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAIzC;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAQrG"}
|
package/package.json
CHANGED
@@ -1,72 +1,74 @@
|
|
1
|
-
{
|
2
|
-
"name": "@liuli-util/cli",
|
3
|
-
"version": "3.
|
4
|
-
"description": "一个针对于库和 CLI 应用程序打包的零配置 CLI",
|
5
|
-
"main": "dist/index.js",
|
6
|
-
"module": "dist/index.esm.js",
|
7
|
-
"types": "dist/index.d.ts",
|
8
|
-
"license": "MIT",
|
9
|
-
"
|
10
|
-
"
|
11
|
-
|
12
|
-
|
13
|
-
"
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
},
|
18
|
-
"
|
19
|
-
"
|
20
|
-
"
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
"
|
26
|
-
"
|
27
|
-
|
28
|
-
|
29
|
-
"
|
30
|
-
"
|
31
|
-
"
|
32
|
-
"
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"
|
40
|
-
"
|
41
|
-
"
|
42
|
-
"
|
43
|
-
"
|
44
|
-
"
|
45
|
-
|
46
|
-
|
47
|
-
"
|
48
|
-
"
|
49
|
-
"
|
50
|
-
|
51
|
-
|
52
|
-
"@types/
|
53
|
-
"@types/
|
54
|
-
"
|
55
|
-
"
|
56
|
-
"
|
57
|
-
"
|
58
|
-
"
|
59
|
-
"
|
60
|
-
"
|
61
|
-
"
|
62
|
-
|
63
|
-
|
64
|
-
"
|
65
|
-
"
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
"
|
70
|
-
"
|
71
|
-
|
72
|
-
}
|
1
|
+
{
|
2
|
+
"name": "@liuli-util/cli",
|
3
|
+
"version": "3.18.0",
|
4
|
+
"description": "一个针对于库和 CLI 应用程序打包的零配置 CLI",
|
5
|
+
"main": "dist/index.js",
|
6
|
+
"module": "dist/index.esm.js",
|
7
|
+
"types": "dist/index.d.ts",
|
8
|
+
"license": "MIT",
|
9
|
+
"bin": {
|
10
|
+
"liuli-cli": "./bin.js"
|
11
|
+
},
|
12
|
+
"jest": {
|
13
|
+
"preset": "ts-jest",
|
14
|
+
"moduleNameMapper": {
|
15
|
+
"lodash-es": "lodash"
|
16
|
+
}
|
17
|
+
},
|
18
|
+
"publishConfig": {
|
19
|
+
"access": "public",
|
20
|
+
"registry": "https://registry.npmjs.org/"
|
21
|
+
},
|
22
|
+
"dependencies": {
|
23
|
+
"@liuli-util/commitlint-standard-config": "^0.1.6",
|
24
|
+
"@liuli-util/eslint-config-react-ts": "^0.1.0",
|
25
|
+
"@liuli-util/eslint-config-ts": "^0.2.0",
|
26
|
+
"@liuli-util/prettier-standard-config": "^0.1.0",
|
27
|
+
"ajv": "^8.8.2",
|
28
|
+
"ajv-formats": "^2.1.1",
|
29
|
+
"ajv-i18n": "^4.2.0",
|
30
|
+
"chokidar": "^3.5.2",
|
31
|
+
"commander": "^8.2.0",
|
32
|
+
"conf": "^10.1.1",
|
33
|
+
"enquirer": "^2.3.6",
|
34
|
+
"esbuild": "^0.13.2",
|
35
|
+
"fs-extra": "^10.0.0",
|
36
|
+
"glob": "^7.2.0",
|
37
|
+
"glob-promise": "^4.2.0",
|
38
|
+
"json5": "^2.2.0",
|
39
|
+
"lodash-es": "^4.17.21",
|
40
|
+
"simple-git": "^2.45.1",
|
41
|
+
"spinnies": "^0.5.1",
|
42
|
+
"ssh2": "^1.5.0",
|
43
|
+
"ssh2-sftp-client": "^7.2.1",
|
44
|
+
"ts-morph": "^12.0.0"
|
45
|
+
},
|
46
|
+
"devDependencies": {
|
47
|
+
"@types/find-cache-dir": "^3.2.1",
|
48
|
+
"@types/fs-extra": "^9.0.13",
|
49
|
+
"@types/glob": "^7",
|
50
|
+
"@types/jest": "^27.0.2",
|
51
|
+
"@types/lodash-es": "^4.17.5",
|
52
|
+
"@types/node": "^16.9.6",
|
53
|
+
"@types/ssh2-sftp-client": "^7.0.0",
|
54
|
+
"comlink": "^4.3.1",
|
55
|
+
"esno": "^0.9.1",
|
56
|
+
"jest": "^27.2.1",
|
57
|
+
"lodash": "^4.17.21",
|
58
|
+
"rimraf": "^3.0.2",
|
59
|
+
"ts-jest": "^27.0.5",
|
60
|
+
"type-fest": "^2.3.4",
|
61
|
+
"typescript": "^4.4.3"
|
62
|
+
},
|
63
|
+
"repository": {
|
64
|
+
"type": "git",
|
65
|
+
"url": "https://github.com/rxliuli/liuli-tools/tree/master/apps/liuli-cli"
|
66
|
+
},
|
67
|
+
"scripts": {
|
68
|
+
"setup": "pnpm build",
|
69
|
+
"build": "rimraf dist && pnpm start build cli",
|
70
|
+
"dev": "esno src/bin.ts build cli -w",
|
71
|
+
"start": "esno src/bin.ts"
|
72
|
+
},
|
73
|
+
"readme": "# @liuli-util/cli\r\n\r\n> [中文](https://github.com/rxliuli/liuli-tools/tree/master/apps/liuli-cli/README.zh-CN.md)\r\n\r\nA zero-configuration CLI packaged for libraries and CLI applications.\r\n\r\n## Getting started\r\n\r\n### Install\r\n\r\n```sh\r\nyarn add -D @liuli-util/cli # local installation\r\nnpm i -g @liuli-util/cli # install globally\r\n```\r\n\r\n### Bale\r\n\r\n```sh\r\nyarn liuli-cli build lib # package library\r\nyarn liuli-cli build cli # package cli reference program\r\n```\r\n\r\n> Add the `-w` option to start the watch mode of rollup, the packaged dist/ will not be compressed and the dependencies will not be included in the bundle.\r\n\r\n![Monitor Mode](https://liuli.dev/images/liuli-cli%20%E7%9B%91%E8%A7%86%E6%A8%A1%E5%BC%8F.gif)\r\n\r\n### Generate\r\n\r\n```sh\r\nyarn liuli-cli generate <name> --template lib # Generate ts-lib project\r\nyarn liuli-cli generate <name> --template cli # Generate cli project\r\n```\r\n\r\nutil also supports interactive project creation\r\n\r\n```shell\r\nyarn liuli-cli generate\r\n```\r\n\r\n![Liuli-cli interactively create screenshots](https://liuli.dev/images/liuli-cli%20%E4%BA%A4%E4%BA%92%E5%BC%8F%E5%88%9B %E5%BB%BA%E6%88%AA%E5%9B%BE.gif)\r\n\r\n### Sync configuration\r\n\r\n```shell\r\nyarn liuli-cli sync\r\n```\r\n\r\nWhich configuration needs to be synced in package.json\r\n\r\n```json\r\n{\r\n \"sync\": [\"prettier\", \"workspaces\", \"commitlint\", \"simplehooks\"]\r\n}\r\n```\r\n\r\nCurrently supported configuration items\r\n\r\n- prettier\r\n- commitlint\r\n- simplehooks\r\n- workspaces\r\n- gitignore\r\n- eslint-ts\r\n- eslint-vue-ts\r\n- jest\r\n\r\nFuture goals: By default will include checking the synchronization of the cli itself (if it needs to be used outside of a monorepo), eslint/style-lint etc., and implementing an interactive cli when not configured\r\n\r\n> Note: Currently only the dependencies are synced and no installation is performed\r\n\r\nInteractive initialization synchronization configuration is also supported\r\n\r\n```shell\r\nyarn liuli-cli sync init\r\n```\r\n\r\n## design concept\r\n\r\n- Convention over configuration, configuration should not be provided if possible. VitePress does this too, reference: https://vitepress.vuejs.org/#lighter-page-weight This leads to some constraints, including the following\r\n - When packaging the library, the entry file must be `src/index.ts`, and the export file must be `dist/index.esm.js` and `dist/index.js`\r\n - When packaging the CLI, the entry file must be `src/bin.ts`, and the exit file must be `dist/bin.js`\r\n - All dependencies will be treated as external dependencies when packaging lib, and all dependencies will be bundled when packaging cli\r\n\r\n## FAQ\r\n\r\n### Why not bundle external dependencies\r\n\r\nThe main reason is that you want to leave the bundling work to the final application, avoid bundling the same dependencies repeatedly, and also avoid dealing with the problem of using `worker_threads` directly based on the file system in nodejs.\r\n"
|
74
|
+
}
|
@@ -7,25 +7,13 @@ import Ajv from 'ajv'
|
|
7
7
|
import localize from 'ajv-i18n/localize'
|
8
8
|
import simpleGit from 'simple-git'
|
9
9
|
import { nodeCacheDir } from '../../utils/nodeCacheDir'
|
10
|
+
import { performance, PerformanceObserver } from 'perf_hooks'
|
11
|
+
import { validate } from './util/validate'
|
10
12
|
|
11
13
|
export interface DeployEvents {
|
12
14
|
process(title: string): void
|
13
15
|
}
|
14
16
|
|
15
|
-
/**
|
16
|
-
* 使用 ajv 校验数据
|
17
|
-
* @param schema
|
18
|
-
* @param data
|
19
|
-
*/
|
20
|
-
function ajvValidate(schema: object, data: object): [isValid: boolean, errorText: string] {
|
21
|
-
const ajv = new Ajv({ allErrors: true, messages: false })
|
22
|
-
const res = ajv.validate(schema, data)
|
23
|
-
if (!res) {
|
24
|
-
localize.zh(ajv.errors)
|
25
|
-
}
|
26
|
-
return [res, ajv.errorsText()]
|
27
|
-
}
|
28
|
-
|
29
17
|
/**
|
30
18
|
* 部署服务接口
|
31
19
|
*/
|
@@ -44,12 +32,13 @@ export enum DeployTypeEnum {
|
|
44
32
|
|
45
33
|
export interface BaseDeployOptions {
|
46
34
|
cwd: string
|
35
|
+
debug: boolean
|
47
36
|
type: DeployTypeEnum
|
48
37
|
}
|
49
38
|
|
50
|
-
export
|
39
|
+
export interface SftpDeployOptions extends Omit<BaseDeployOptions, 'type'> {
|
40
|
+
dist: string
|
51
41
|
dest: string
|
52
|
-
remote: string
|
53
42
|
sshConfig: ConnectOptions
|
54
43
|
}
|
55
44
|
|
@@ -67,38 +56,57 @@ export class SftpDeployService implements IDeployService {
|
|
67
56
|
...this.options.sshConfig,
|
68
57
|
privateKey,
|
69
58
|
})
|
70
|
-
await client.mkdir(this.options.
|
71
|
-
await client.uploadDir(path.resolve(this.options.cwd, this.options.
|
59
|
+
await client.mkdir(this.options.dest, true)
|
60
|
+
await client.uploadDir(path.resolve(this.options.cwd, this.options.dist), this.options.dest)
|
72
61
|
await client.end()
|
73
62
|
})
|
74
63
|
}
|
75
64
|
|
76
65
|
validate(): [isValidate: boolean, errorText: string] {
|
77
|
-
return
|
66
|
+
return validate<SftpDeployOptions>(
|
78
67
|
{
|
79
68
|
type: 'object',
|
80
69
|
properties: {
|
70
|
+
debug: { type: 'boolean' },
|
81
71
|
cwd: { type: 'string' },
|
72
|
+
dist: { type: 'string' },
|
82
73
|
dest: { type: 'string' },
|
83
|
-
remote: { type: 'string' },
|
84
74
|
sshConfig: {
|
85
75
|
type: 'object',
|
86
76
|
properties: {
|
87
77
|
host: { type: 'string' },
|
88
78
|
username: { type: 'string' },
|
89
79
|
},
|
90
|
-
},
|
80
|
+
} as any,
|
91
81
|
},
|
92
|
-
required: ['cwd', '
|
82
|
+
required: ['cwd', 'dist', 'dest', 'sshConfig', 'debug'],
|
93
83
|
},
|
94
84
|
this.options,
|
95
85
|
)
|
96
86
|
}
|
97
87
|
}
|
98
88
|
|
99
|
-
export
|
100
|
-
|
101
|
-
|
89
|
+
export interface GhPagesDeployOptions extends Omit<BaseDeployOptions, 'type'> {
|
90
|
+
/**
|
91
|
+
* 推送的本地目录
|
92
|
+
*/
|
93
|
+
dist: string
|
94
|
+
/**
|
95
|
+
* 推送的远端目录,默认为分支根目录
|
96
|
+
*/
|
97
|
+
dest?: string
|
98
|
+
/**
|
99
|
+
* 推送的项目 git 地址,默认为当前项目
|
100
|
+
*/
|
101
|
+
repo?: string
|
102
|
+
/**
|
103
|
+
* 推送的远端,默认为 origin
|
104
|
+
*/
|
105
|
+
remote?: string
|
106
|
+
/**
|
107
|
+
* 远端分支名,默认为 gh-pages
|
108
|
+
*/
|
109
|
+
branch?: string
|
102
110
|
}
|
103
111
|
|
104
112
|
/**
|
@@ -108,48 +116,97 @@ export class GhPagesDeployService implements IDeployService {
|
|
108
116
|
constructor(private readonly options: GhPagesDeployOptions) {}
|
109
117
|
|
110
118
|
deploy(): EventExtPromise<void, DeployEvents> {
|
111
|
-
const defaultRemote = 'origin'
|
112
|
-
const defaultBranch = 'gh-pages'
|
119
|
+
const defaultRemote = this.options.remote ?? 'origin'
|
120
|
+
const defaultBranch = this.options.branch ?? 'gh-pages'
|
113
121
|
return PromiseUtil.wrapOnEvent(async (events: DeployEvents) => {
|
114
|
-
|
122
|
+
const obs = new PerformanceObserver((perfObserverList) => {
|
123
|
+
if (this.options.debug) {
|
124
|
+
console.log(perfObserverList.getEntries())
|
125
|
+
}
|
126
|
+
})
|
127
|
+
obs.observe({ type: 'mark' })
|
128
|
+
|
129
|
+
function mark(title: string) {
|
130
|
+
performance.mark(title)
|
131
|
+
events.process(title)
|
132
|
+
}
|
133
|
+
mark('开始推送')
|
115
134
|
const git = simpleGit(this.options.cwd)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
135
|
+
if (!this.options.repo) {
|
136
|
+
mark('获取当前项目的远端配置')
|
137
|
+
const originRemote = (await git.getRemotes(true)).find((item) => item.name === defaultRemote)
|
138
|
+
if (!originRemote) {
|
139
|
+
throw new Error('当前目录不是一个 git 项目或没有配置 git remote')
|
140
|
+
}
|
141
|
+
this.options.repo = originRemote.refs.fetch
|
120
142
|
}
|
121
143
|
const ghPagesRoot = path.resolve(nodeCacheDir('liuli-cli'), 'gh-pages')
|
122
|
-
const originRepoName =
|
144
|
+
const originRepoName = this.options.repo.replace(new RegExp('[/:]', 'g'), '_')
|
123
145
|
const localRepoPath = path.resolve(ghPagesRoot, originRepoName)
|
146
|
+
let forcePush = false
|
124
147
|
if (!(await pathExists(localRepoPath))) {
|
125
|
-
|
126
|
-
|
148
|
+
mark('克隆项目')
|
149
|
+
//如果失败,应该尝试克隆主分支,然后 checkout
|
150
|
+
try {
|
151
|
+
await git.clone(this.options.repo, localRepoPath, {
|
152
|
+
'--branch': defaultBranch,
|
153
|
+
'--single-branch': null,
|
154
|
+
'--depth': 1,
|
155
|
+
})
|
156
|
+
} catch (e) {
|
157
|
+
mark(`未找到 ${defaultBranch} 分支,尝试自动创建`)
|
158
|
+
await git.clone(this.options.repo, localRepoPath, {
|
159
|
+
'--single-branch': null,
|
160
|
+
'--depth': 1,
|
161
|
+
})
|
162
|
+
await git.cwd(localRepoPath)
|
163
|
+
await git.checkout({
|
164
|
+
'--orphan': defaultBranch,
|
165
|
+
})
|
166
|
+
forcePush = true
|
167
|
+
}
|
127
168
|
} else {
|
169
|
+
await git.cwd(localRepoPath)
|
170
|
+
mark('更新项目')
|
128
171
|
await git.pull()
|
129
172
|
}
|
130
|
-
|
131
|
-
const remoteDestPath = path.join(localRepoPath, this.options.
|
173
|
+
mark('复制文件')
|
174
|
+
const remoteDestPath = path.join(localRepoPath, this.options.dest ?? './')
|
132
175
|
await mkdirp(remoteDestPath)
|
133
|
-
await copy(path.resolve(this.options.cwd, this.options.
|
134
|
-
|
176
|
+
await copy(path.resolve(this.options.cwd, this.options.dist), remoteDestPath)
|
177
|
+
mark('提交所有文件')
|
135
178
|
await git.cwd(localRepoPath)
|
136
179
|
await git.add('-A')
|
137
|
-
await git.
|
138
|
-
|
139
|
-
|
180
|
+
if ((await git.status()).files.length === 0) {
|
181
|
+
mark('没有任何提交,跳过提交')
|
182
|
+
} else {
|
183
|
+
await git.commit('Updates gh-pages by liuli-cli')
|
184
|
+
}
|
185
|
+
mark('推送到远端')
|
186
|
+
if ((await git.status()).ahead > 0 || forcePush) {
|
187
|
+
await git.push(defaultRemote, defaultBranch)
|
188
|
+
mark('完成推送')
|
189
|
+
} else {
|
190
|
+
mark('没有更新,跳过推送')
|
191
|
+
}
|
192
|
+
obs.disconnect()
|
140
193
|
})
|
141
194
|
}
|
142
195
|
|
143
196
|
validate(): [isValid: boolean, errorText: string] {
|
144
|
-
return
|
197
|
+
return validate<GhPagesDeployOptions>(
|
145
198
|
{
|
146
199
|
type: 'object',
|
147
200
|
properties: {
|
201
|
+
debug: { type: 'boolean' },
|
148
202
|
cwd: { type: 'string' },
|
149
|
-
|
150
|
-
|
203
|
+
dist: { type: 'string' },
|
204
|
+
dest: { type: 'string', nullable: true },
|
205
|
+
repo: { type: 'string', nullable: true },
|
206
|
+
remote: { type: 'string', nullable: true },
|
207
|
+
branch: { type: 'string', nullable: true },
|
151
208
|
},
|
152
|
-
required: ['
|
209
|
+
required: ['debug', 'cwd', 'dist'],
|
153
210
|
},
|
154
211
|
this.options,
|
155
212
|
)
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import * as path from 'path'
|
2
|
-
import { mkdirp, remove, writeFile } from 'fs-extra'
|
3
|
-
import { GhPagesDeployService, SftpDeployOptions, SftpDeployService } from '../DeployService'
|
2
|
+
import { mkdirp, remove, unlink, writeFile } from 'fs-extra'
|
3
|
+
import { GhPagesDeployOptions, GhPagesDeployService, SftpDeployOptions, SftpDeployService } from '../DeployService'
|
4
4
|
import { execPromise } from '../util/execPromise'
|
5
|
+
import { nodeCacheDir } from '../../../utils/nodeCacheDir'
|
5
6
|
|
6
7
|
const tempPath = path.resolve(__dirname, '.temp')
|
7
8
|
beforeAll(async () => {
|
@@ -25,14 +26,15 @@ beforeAll(async () => {
|
|
25
26
|
)
|
26
27
|
})
|
27
28
|
|
28
|
-
describe('测试 SftpDeployService', () => {
|
29
|
+
describe.skip('测试 SftpDeployService', () => {
|
29
30
|
const options: SftpDeployOptions = {
|
31
|
+
debug: false,
|
30
32
|
cwd: tempPath,
|
31
|
-
|
32
|
-
|
33
|
+
dist: 'dist',
|
34
|
+
dest: process.env.dest!,
|
33
35
|
sshConfig: {
|
34
|
-
host:
|
35
|
-
username:
|
36
|
+
host: process.env.host,
|
37
|
+
username: process.env.username,
|
36
38
|
},
|
37
39
|
}
|
38
40
|
it('基本示例', async () => {
|
@@ -55,21 +57,52 @@ describe('测试 SftpDeployService', () => {
|
|
55
57
|
})
|
56
58
|
|
57
59
|
describe('测试 GhPagesDeployService', () => {
|
58
|
-
const
|
60
|
+
const options: GhPagesDeployOptions = {
|
61
|
+
debug: false,
|
59
62
|
cwd: tempPath,
|
60
|
-
|
61
|
-
|
63
|
+
dist: 'dist',
|
64
|
+
dest: '/',
|
65
|
+
repo: 'https://github.com/rxliuli/test-git.git',
|
66
|
+
remote: 'origin',
|
67
|
+
branch: 'gh-pages',
|
68
|
+
}
|
69
|
+
const ghPagesDeployService = new GhPagesDeployService(options)
|
70
|
+
const distPath = path.resolve(tempPath, 'dist')
|
71
|
+
const mock = jest.fn().mockImplementation((title) => console.log(title))
|
72
|
+
beforeEach(async () => {
|
73
|
+
await writeFile(
|
74
|
+
path.resolve(distPath, 'index.html'),
|
75
|
+
`<!DOCTYPE html>
|
76
|
+
<html lang="en">
|
77
|
+
<head>
|
78
|
+
<meta charset="UTF-8" />
|
79
|
+
<title>测试部署</title>
|
80
|
+
</head>
|
81
|
+
<body>
|
82
|
+
<h1>测试部署</h1>
|
83
|
+
<p>${Date.now()}</p>
|
84
|
+
</body>
|
85
|
+
</html>
|
86
|
+
`,
|
87
|
+
)
|
88
|
+
mock.mockClear()
|
62
89
|
})
|
63
90
|
it('基本示例', async () => {
|
64
|
-
await ghPagesDeployService.deploy().on('process',
|
91
|
+
await ghPagesDeployService.deploy().on('process', mock)
|
92
|
+
console.log(mock.mock.calls)
|
93
|
+
expect(mock.mock.calls.some((item: string[]) => item.includes('完成推送'))).toBeTruthy()
|
65
94
|
}, 10_000)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
await
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
95
|
+
it('测试没有任何修改', async () => {
|
96
|
+
await writeFile(path.resolve(distPath, 'index.html'), 'test')
|
97
|
+
await ghPagesDeployService.deploy().on('process', mock)
|
98
|
+
await ghPagesDeployService.deploy().on('process', mock)
|
99
|
+
expect(mock.mock.calls.some((item: string[]) => item.includes('没有任何提交,跳过提交'))).toBeTruthy()
|
100
|
+
expect(mock.mock.calls.some((item: string[]) => item.includes('没有更新,跳过推送'))).toBeTruthy()
|
101
|
+
}, 20_000)
|
102
|
+
it('测试首次拉取代码', async () => {
|
103
|
+
const dir = path.resolve(nodeCacheDir('liuli-cli'), 'gh-pages', options.repo!.replace(new RegExp('[/:]', 'g'), '_'))
|
104
|
+
await remove(dir)
|
105
|
+
await ghPagesDeployService.deploy().on('process', mock)
|
106
|
+
expect(mock.mock.calls.some((item: string[]) => item.includes('克隆项目'))).toBeTruthy()
|
74
107
|
}, 10_000)
|
75
108
|
})
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import simpleGit, { SimpleGit } from 'simple-git'
|
2
2
|
import * as path from 'path'
|
3
|
-
import { mkdirp, remove } from 'fs-extra'
|
3
|
+
import { mkdirp, pathExists, remove } from 'fs-extra'
|
4
|
+
import * as os from 'os'
|
4
5
|
|
5
6
|
describe('测试 simple-git', () => {
|
6
7
|
async function getOriginRemote() {
|
@@ -26,4 +27,28 @@ describe('测试 simple-git', () => {
|
|
26
27
|
const originRepoName = originRemote.refs.fetch.replace(new RegExp('[/:]', 'g'), '_')
|
27
28
|
await git.clone(originRemote.refs.fetch, path.resolve(tempPath, originRepoName), { '--branch': 'gh-pages' })
|
28
29
|
}, 100_000)
|
30
|
+
it('测试 git status', async () => {
|
31
|
+
await git.add('-A')
|
32
|
+
const status = await git.status()
|
33
|
+
console.log('status: ', status.files)
|
34
|
+
})
|
35
|
+
})
|
36
|
+
|
37
|
+
describe.skip('测试真实场景', () => {
|
38
|
+
const gitPath = path.resolve(
|
39
|
+
os.homedir(),
|
40
|
+
'./AppData/Local/liuli-cli/Cache/gh-pages/https___github.com_rxliuli_webos.git',
|
41
|
+
)
|
42
|
+
const git = simpleGit(gitPath)
|
43
|
+
it('测试获取提交状态', async () => {
|
44
|
+
const res = await git.status()
|
45
|
+
console.log(res)
|
46
|
+
})
|
47
|
+
it('测试获取更新', async () => {
|
48
|
+
const res = await git.pull()
|
49
|
+
console.log(res)
|
50
|
+
}, 100_000)
|
51
|
+
it('测试 git add -A', async () => {
|
52
|
+
await git.add('-A')
|
53
|
+
})
|
29
54
|
})
|
@@ -4,4 +4,5 @@ import * as path from 'path'
|
|
4
4
|
|
5
5
|
export const deployCommand = new Command('deploy')
|
6
6
|
.description('部署项目到远端')
|
7
|
-
.
|
7
|
+
.option('--debug', '是否开启调试模式')
|
8
|
+
.action((options: { debug?: boolean }) => deploy({ cwd: path.resolve(), debug: !!options.debug }))
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import Ajv, { JSONSchemaType } from 'ajv'
|
2
|
+
import localize from 'ajv-i18n/localize'
|
3
|
+
import formatsPlugin from 'ajv-formats'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* 使用 ajv 校验数据
|
7
|
+
* @param schema json 模式配置
|
8
|
+
* @param data 校验的数据
|
9
|
+
*/
|
10
|
+
export function validate<T>(schema: JSONSchemaType<T>, data: T): [isValid: boolean, errorText: string] {
|
11
|
+
const ajv = new Ajv({ allErrors: true, messages: false })
|
12
|
+
formatsPlugin(ajv)
|
13
|
+
const res = ajv.validate(schema, data)
|
14
|
+
if (!res) {
|
15
|
+
localize.zh(ajv.errors)
|
16
|
+
}
|
17
|
+
return [res, ajv.errorsText()]
|
18
|
+
}
|