oneclickpush 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -39
- package/cli.js +74 -36
- package/index.d.ts +26 -15
- package/index.js +34 -20
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -16,27 +16,28 @@ none of these, has zero dependencies and only uses built-in Node APIs + `git`.
|
|
|
16
16
|
Deploy your build directory (e.g. `dist`) to the `gh-pages` branch:
|
|
17
17
|
|
|
18
18
|
```text
|
|
19
|
-
$
|
|
20
|
-
$ npx oneclickpush
|
|
19
|
+
$ npx oneclickpush dist
|
|
21
20
|
```
|
|
22
21
|
|
|
23
22
|
## CLI Usage
|
|
24
23
|
|
|
25
24
|
```text
|
|
26
|
-
Usage: oneclickpush [options]
|
|
25
|
+
Usage: oneclickpush <dir> [options]
|
|
27
26
|
|
|
28
27
|
Replace the contents of a remote git branch with that of a local directory.
|
|
29
28
|
|
|
29
|
+
Arguments:
|
|
30
|
+
dir Directory to deploy (required)
|
|
31
|
+
|
|
30
32
|
Options:
|
|
31
|
-
-
|
|
32
|
-
-e, --exclude <glob> Paths to leave out, relative to dist (default: "**/.DS_Store")
|
|
33
|
+
-e, --exclude <glob> Paths to leave out, relative to dir (default: "**/.DS_Store")
|
|
33
34
|
-b, --branch <name> Target branch (default: "gh-pages")
|
|
34
35
|
-m, --message <msg> Commit message (default: "Updates")
|
|
35
|
-
-r, --remote <name|url> Remote name or repo URL to
|
|
36
|
+
-r, --remote <name|url> Remote name or repo URL to deploy to (default: "origin")
|
|
36
37
|
-a, --author <author> Commit author, as "Name <email>"
|
|
37
38
|
-f, --force Force push new commit with no history
|
|
38
39
|
-n, --dry-run Show what would change, without committing or pushing
|
|
39
|
-
-
|
|
40
|
+
-l, --log-level <level> Output: silent, normal or verbose (default: "normal")
|
|
40
41
|
-V, --version Print version
|
|
41
42
|
-h, --help Show this help
|
|
42
43
|
```
|
|
@@ -45,27 +46,46 @@ Options:
|
|
|
45
46
|
> `--exclude` takes glob patterns (`*`, `**`, `?`) and can be repeated. Patterns are matched against
|
|
46
47
|
> the full path, so use `**/name` to match at any depth.
|
|
47
48
|
|
|
49
|
+
### Log levels
|
|
50
|
+
|
|
51
|
+
| Level | Behavior |
|
|
52
|
+
| --------- | -------------------------------------------------------------------- |
|
|
53
|
+
| `silent` | Prints nothing, not even errors. Check the exit code instead. |
|
|
54
|
+
| `normal` | Prints the result and error messages (plus a spinner in a terminal). |
|
|
55
|
+
| `verbose` | Also logs every git command to stderr and prints full errors. |
|
|
56
|
+
|
|
57
|
+
> [!WARNING]
|
|
58
|
+
> If your remote URL contains a token (e.g. `https://user:TOKEN@github.com/o/r.git`), git error
|
|
59
|
+
> messages shown at `normal` and `verbose` may include it. Use `-l silent` in CI, or keep tokens out
|
|
60
|
+
> of the URL with a credential helper.
|
|
61
|
+
|
|
48
62
|
### Examples
|
|
49
63
|
|
|
50
64
|
```bash
|
|
51
65
|
# Preview what would be deployed without pushing
|
|
52
|
-
npx oneclickpush
|
|
66
|
+
npx oneclickpush build --dry-run
|
|
53
67
|
|
|
54
68
|
# Custom branch and commit message
|
|
55
|
-
npx oneclickpush
|
|
69
|
+
npx oneclickpush public -b release -m "deploy: v1.0.0"
|
|
56
70
|
|
|
57
71
|
# Exclude source maps and test files
|
|
58
|
-
npx oneclickpush
|
|
72
|
+
npx oneclickpush dist -e "**/*.map" -e "**/tests/**"
|
|
59
73
|
|
|
60
74
|
# Deploy to a different remote or repository URL
|
|
61
|
-
npx oneclickpush
|
|
62
|
-
npx oneclickpush
|
|
75
|
+
npx oneclickpush dist -r upstream
|
|
76
|
+
npx oneclickpush dist -r git@github.com:user/repo.git
|
|
63
77
|
|
|
64
78
|
# Set commit author (useful in CI environments)
|
|
65
|
-
npx oneclickpush
|
|
79
|
+
npx oneclickpush dist -a "Deploy Bot <bot@example.com>"
|
|
66
80
|
|
|
67
81
|
# Force push fresh history
|
|
68
|
-
npx oneclickpush
|
|
82
|
+
npx oneclickpush dist --force
|
|
83
|
+
|
|
84
|
+
# Silent output (useful in CI, check the exit code)
|
|
85
|
+
npx oneclickpush dist -l silent
|
|
86
|
+
|
|
87
|
+
# Log every git command
|
|
88
|
+
npx oneclickpush dist -l verbose
|
|
69
89
|
```
|
|
70
90
|
|
|
71
91
|
## Programmatic API
|
|
@@ -73,15 +93,15 @@ npx oneclickpush -d dist --force
|
|
|
73
93
|
You can also use `oneclickpush` as an ES module:
|
|
74
94
|
|
|
75
95
|
```bash
|
|
76
|
-
# Install
|
|
96
|
+
# Install
|
|
77
97
|
npm i oneclickpush
|
|
78
98
|
```
|
|
79
99
|
|
|
80
100
|
```javascript
|
|
81
|
-
import {
|
|
101
|
+
import { deploy } from 'oneclickpush'
|
|
82
102
|
|
|
83
|
-
const changes = await
|
|
84
|
-
|
|
103
|
+
const changes = await deploy({
|
|
104
|
+
dir: 'dist',
|
|
85
105
|
exclude: ['**/.DS_Store', '**/*.map'],
|
|
86
106
|
branch: 'gh-pages',
|
|
87
107
|
message: 'Deploy updates',
|
|
@@ -89,29 +109,41 @@ const changes = await publish({
|
|
|
89
109
|
author: 'Deploy Bot <bot@example.com>',
|
|
90
110
|
force: false,
|
|
91
111
|
dryRun: false,
|
|
92
|
-
|
|
112
|
+
logLevel: 'normal'
|
|
93
113
|
})
|
|
94
114
|
|
|
95
115
|
if (changes) {
|
|
96
|
-
console.log('
|
|
116
|
+
console.log('Deployed changes:\n' + changes)
|
|
97
117
|
} else {
|
|
98
|
-
console.log('Nothing to
|
|
118
|
+
console.log('Nothing to deploy')
|
|
99
119
|
}
|
|
100
120
|
```
|
|
101
121
|
|
|
122
|
+
The default values are also exported as `DEFAULTS`:
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
import { DEFAULTS } from 'oneclickpush'
|
|
126
|
+
|
|
127
|
+
console.log(DEFAULTS.branch) // "gh-pages"
|
|
128
|
+
```
|
|
129
|
+
|
|
102
130
|
### Options
|
|
103
131
|
|
|
104
|
-
| Option
|
|
105
|
-
|
|
|
106
|
-
| `
|
|
107
|
-
| `exclude`
|
|
108
|
-
| `branch`
|
|
109
|
-
| `message`
|
|
110
|
-
| `remote`
|
|
111
|
-
| `author`
|
|
112
|
-
| `force`
|
|
113
|
-
| `dryRun`
|
|
114
|
-
| `
|
|
132
|
+
| Option | Type | Default | Description |
|
|
133
|
+
| ---------- | ----------------------------------- | ------------------ | --------------------------------------------------- |
|
|
134
|
+
| `dir` | `string` | - (required) | Directory to deploy |
|
|
135
|
+
| `exclude` | `string[]` | `["**/.DS_Store"]` | Paths to exclude relative to `dir` |
|
|
136
|
+
| `branch` | `string` | `"gh-pages"` | Target git branch |
|
|
137
|
+
| `message` | `string` | `"Updates"` | Git commit message |
|
|
138
|
+
| `remote` | `string` | `"origin"` | Remote name or repository URL/path to deploy to |
|
|
139
|
+
| `author` | `string` | - | Commit author and committer, as `"Name <email>"` |
|
|
140
|
+
| `force` | `boolean` | `false` | Force push fresh commit with no previous history |
|
|
141
|
+
| `dryRun` | `boolean` | `false` | Return staged changes without committing or pushing |
|
|
142
|
+
| `logLevel` | `"silent" \| "normal" \| "verbose"` | `"normal"` | Only `"verbose"` has an effect: logs git commands |
|
|
143
|
+
|
|
144
|
+
> [!NOTE]
|
|
145
|
+
> The library never prints anything except at `"verbose"`. `"silent"` and `"normal"` only differ in
|
|
146
|
+
> the CLI.
|
|
115
147
|
|
|
116
148
|
## How It Works
|
|
117
149
|
|
|
@@ -119,7 +151,7 @@ if (changes) {
|
|
|
119
151
|
URL/path directly.
|
|
120
152
|
2. Creates an isolated temporary directory and fetches the target branch (or initializes it if it
|
|
121
153
|
doesn't exist).
|
|
122
|
-
3. Clears the target branch index and stages the contents of `
|
|
154
|
+
3. Clears the target branch index and stages the contents of `dir` (minus `exclude` patterns)
|
|
123
155
|
directly via Git `work-tree`.
|
|
124
156
|
4. Checks for differences (`git diff --cached --name-status`). If there are changes, commits and
|
|
125
157
|
pushes back to the remote repository.
|
|
@@ -130,12 +162,14 @@ if (changes) {
|
|
|
130
162
|
|
|
131
163
|
### Notes
|
|
132
164
|
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
|
|
138
|
-
|
|
165
|
+
- The target branch is fully replaced with the contents of `dir` (WYSIWYG), and it's created if it
|
|
166
|
+
doesn't exist yet. If you need files like `.nojekyll` or `CNAME`, put them in `dir`, adding a
|
|
167
|
+
post-build step if your build tool wipes the directory.
|
|
168
|
+
- Dotfiles are included by default, except `.DS_Store`. Use `-e` to exclude more, but note that it
|
|
169
|
+
replaces the default list, so repeat `-e "**/.DS_Store"` if you still want that file excluded.
|
|
170
|
+
- When `--remote` is a repository URL or path, you don't need to run from inside a git repository.
|
|
171
|
+
- In dry-run output, identical content moved to a new path appears as a rename (`R100`).
|
|
172
|
+
- A directory starting with `-` must come after `--`, e.g. `oneclickpush -- -weird-dir`.
|
|
139
173
|
|
|
140
174
|
## License
|
|
141
175
|
|
package/cli.js
CHANGED
|
@@ -1,48 +1,67 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { parseArgs } from 'node:util'
|
|
3
3
|
import { readFileSync } from 'node:fs'
|
|
4
|
-
import {
|
|
4
|
+
import { deploy, DEFAULTS } from './index.js'
|
|
5
5
|
|
|
6
|
-
const HELP = `Usage: oneclickpush [options]
|
|
6
|
+
const HELP = `Usage: oneclickpush <dir> [options]
|
|
7
7
|
|
|
8
8
|
Replace the contents of a remote git branch with that of a
|
|
9
9
|
local directory.
|
|
10
10
|
|
|
11
|
+
Arguments:
|
|
12
|
+
dir Directory to deploy (required)
|
|
13
|
+
|
|
11
14
|
Options:
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
(default: "origin")
|
|
15
|
+
-e, --exclude <glob> Paths to leave out, relative to dir
|
|
16
|
+
(default: "${DEFAULTS.exclude.join(', ')}")
|
|
17
|
+
-b, --branch <name> Target branch (default: "${DEFAULTS.branch}")
|
|
18
|
+
-m, --message <msg> Commit message (default: "${DEFAULTS.message}")
|
|
19
|
+
-r, --remote <name|url> Remote name or repo URL to deploy to
|
|
20
|
+
(default: "${DEFAULTS.remote}")
|
|
19
21
|
-a, --author <author> Commit author, as "Name <email>"
|
|
20
22
|
-f, --force Force push new commit with no history
|
|
21
23
|
-n, --dry-run Show what would change, without
|
|
22
24
|
committing or pushing
|
|
23
|
-
-
|
|
25
|
+
-l, --log-level <level> Output: silent, normal or verbose
|
|
26
|
+
(default: "${DEFAULTS.logLevel}")
|
|
24
27
|
-V, --version Print version
|
|
25
28
|
-h, --help Show this help
|
|
26
29
|
|
|
27
30
|
--exclude takes glob patterns (*, **, ?) and can be repeated.
|
|
28
31
|
Patterns are matched against the full path, so use **/name to
|
|
29
|
-
match at any depth
|
|
32
|
+
match at any depth.
|
|
33
|
+
|
|
34
|
+
Log levels: "silent" prints nothing (check the exit code),
|
|
35
|
+
"normal" prints the result and errors, "verbose" also logs git
|
|
36
|
+
commands and full errors.`
|
|
37
|
+
|
|
38
|
+
const LEVELS = ['silent', 'normal', 'verbose']
|
|
30
39
|
|
|
31
|
-
const { values: v } =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
const { values: v, positionals } = (() => {
|
|
41
|
+
try {
|
|
42
|
+
return parseArgs({
|
|
43
|
+
allowPositionals: true,
|
|
44
|
+
options: {
|
|
45
|
+
exclude: { type: 'string', short: 'e', multiple: true, default: DEFAULTS.exclude },
|
|
46
|
+
branch: { type: 'string', short: 'b', default: DEFAULTS.branch },
|
|
47
|
+
message: { type: 'string', short: 'm', default: DEFAULTS.message },
|
|
48
|
+
remote: { type: 'string', short: 'r', default: DEFAULTS.remote },
|
|
49
|
+
author: { type: 'string', short: 'a' },
|
|
50
|
+
force: { type: 'boolean', short: 'f', default: DEFAULTS.force },
|
|
51
|
+
'dry-run': { type: 'boolean', short: 'n', default: DEFAULTS.dryRun },
|
|
52
|
+
'log-level': { type: 'string', short: 'l', default: DEFAULTS.logLevel },
|
|
53
|
+
version: { type: 'boolean', short: 'V', default: false },
|
|
54
|
+
help: { type: 'boolean', short: 'h', default: false }
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
} catch (/** @type {any} */ e) {
|
|
58
|
+
if (!e.code?.startsWith('ERR_PARSE_ARGS_')) throw e
|
|
59
|
+
// Keep only the first sentence, Node appends a long "-- " hint to some messages
|
|
60
|
+
const msg = e.message.split('. ')[0].replace(/\.$/, '')
|
|
61
|
+
console.error(`${msg}, run "oneclickpush --help" for usage`)
|
|
62
|
+
process.exit(1)
|
|
44
63
|
}
|
|
45
|
-
})
|
|
64
|
+
})()
|
|
46
65
|
|
|
47
66
|
if (v.help) {
|
|
48
67
|
console.log(HELP)
|
|
@@ -55,27 +74,46 @@ if (v.version) {
|
|
|
55
74
|
process.exit(0)
|
|
56
75
|
}
|
|
57
76
|
|
|
58
|
-
|
|
77
|
+
if (positionals.length !== 1) {
|
|
78
|
+
console.error(
|
|
79
|
+
positionals.length === 0
|
|
80
|
+
? 'Missing required argument <dir>, run "oneclickpush --help" for usage'
|
|
81
|
+
: 'Expected exactly one directory, run "oneclickpush --help" for usage'
|
|
82
|
+
)
|
|
83
|
+
process.exit(1)
|
|
84
|
+
}
|
|
85
|
+
const dir = positionals[0]
|
|
86
|
+
|
|
87
|
+
const level = /** @type {import('./index.js').OcpOptionsLogLevel} */ (v['log-level'])
|
|
88
|
+
if (!LEVELS.includes(level)) {
|
|
89
|
+
console.error(`Invalid --log-level "${level}", expected one of: ${LEVELS.join(', ')}`)
|
|
90
|
+
process.exit(1)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Dead simple spinner (normal level, TTY only)
|
|
59
94
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
60
95
|
let i = 0
|
|
61
96
|
const spin =
|
|
62
|
-
process.stderr.isTTY &&
|
|
97
|
+
process.stderr.isTTY && level === 'normal'
|
|
63
98
|
? setInterval(() => process.stderr.write(`\r${frames[i++ % frames.length]}`), 100)
|
|
64
99
|
: null
|
|
65
100
|
const stop = () => spin && (clearInterval(spin), process.stderr.write('\r \r'))
|
|
66
101
|
|
|
67
102
|
try {
|
|
68
|
-
const changes = await
|
|
103
|
+
const changes = await deploy({ ...v, dir, logLevel: level, dryRun: v['dry-run'] })
|
|
69
104
|
stop()
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
105
|
+
if (level !== 'silent') {
|
|
106
|
+
console.log(
|
|
107
|
+
!changes
|
|
108
|
+
? 'Nothing to deploy'
|
|
109
|
+
: v['dry-run']
|
|
110
|
+
? `Dry run, would deploy:\n${changes}`
|
|
111
|
+
: 'Deployed!'
|
|
112
|
+
)
|
|
113
|
+
}
|
|
77
114
|
} catch (/** @type {any} */ e) {
|
|
78
115
|
stop()
|
|
79
|
-
console.error(
|
|
116
|
+
if (level === 'verbose') console.error(e)
|
|
117
|
+
else if (level === 'normal') console.error(e.message)
|
|
80
118
|
process.exit(1)
|
|
81
119
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type OcpOptionsLogLevel = 'silent' | 'normal' | 'verbose'
|
|
2
|
+
/** @typedef {'silent' | 'normal' | 'verbose'} OcpOptionsLogLevel */
|
|
3
|
+
export declare const DEFAULTS: {
|
|
4
|
+
exclude: string[]
|
|
5
|
+
branch: string
|
|
6
|
+
message: string
|
|
7
|
+
remote: string
|
|
8
|
+
force: boolean
|
|
9
|
+
dryRun: boolean
|
|
10
|
+
logLevel: OcpOptionsLogLevel
|
|
11
|
+
}
|
|
12
|
+
export type OcpOptions = {
|
|
2
13
|
/**
|
|
3
|
-
* Directory to
|
|
14
|
+
* Directory to deploy (required)
|
|
4
15
|
*/
|
|
5
|
-
|
|
16
|
+
dir: string
|
|
6
17
|
/**
|
|
7
|
-
* Paths to leave out, relative to
|
|
18
|
+
* Paths to leave out, relative to dir (default: `**\/.DS_Store`)
|
|
8
19
|
*/
|
|
9
20
|
exclude?: string[]
|
|
10
21
|
/**
|
|
@@ -32,30 +43,30 @@ export type OneclickpushOptions = {
|
|
|
32
43
|
*/
|
|
33
44
|
dryRun?: boolean
|
|
34
45
|
/**
|
|
35
|
-
*
|
|
46
|
+
* Only "verbose" logs (git commands, to stderr)
|
|
36
47
|
*/
|
|
37
|
-
|
|
48
|
+
logLevel?: OcpOptionsLogLevel
|
|
38
49
|
}
|
|
39
50
|
/**
|
|
40
|
-
* @typedef {object}
|
|
41
|
-
* @prop {string}
|
|
42
|
-
* @prop {string[]} [exclude] Paths to leave out, relative to
|
|
51
|
+
* @typedef {object} OcpOptions
|
|
52
|
+
* @prop {string} dir Directory to deploy (required)
|
|
53
|
+
* @prop {string[]} [exclude] Paths to leave out, relative to dir (default: `**\/.DS_Store`)
|
|
43
54
|
* @prop {string} [branch="gh-pages"] Target branch
|
|
44
55
|
* @prop {string} [message="Updates"] Commit message
|
|
45
56
|
* @prop {string} [remote="origin"] Remote name of the current repo, or a repository URL or path
|
|
46
57
|
* @prop {string} [author] Commit author and committer as "Name <email>"
|
|
47
58
|
* @prop {boolean} [force=false] Force push new commit with no history
|
|
48
59
|
* @prop {boolean} [dryRun=false] Show what would change, without committing or pushing
|
|
49
|
-
* @prop {
|
|
60
|
+
* @prop {OcpOptionsLogLevel} [logLevel="normal"] Only "verbose" logs (git commands, to stderr)
|
|
50
61
|
*/
|
|
51
62
|
/**
|
|
52
63
|
* Replace the contents of a git branch with the contents of a directory.
|
|
53
64
|
* Returns the staged changes as `git diff --name-status` text or "" if nothing changed.
|
|
54
|
-
* @param {
|
|
65
|
+
* @param {OcpOptions} options
|
|
55
66
|
* @returns {Promise<string>}
|
|
56
67
|
*/
|
|
57
|
-
export declare function
|
|
58
|
-
|
|
68
|
+
export declare function deploy({
|
|
69
|
+
dir,
|
|
59
70
|
exclude,
|
|
60
71
|
branch,
|
|
61
72
|
message,
|
|
@@ -63,5 +74,5 @@ export declare function publish({
|
|
|
63
74
|
author,
|
|
64
75
|
force,
|
|
65
76
|
dryRun,
|
|
66
|
-
|
|
67
|
-
}:
|
|
77
|
+
logLevel
|
|
78
|
+
}: OcpOptions): Promise<string>
|
package/index.js
CHANGED
|
@@ -4,37 +4,50 @@ import { mkdtemp, rm } from 'node:fs/promises'
|
|
|
4
4
|
import { tmpdir } from 'node:os'
|
|
5
5
|
import { join, resolve } from 'node:path'
|
|
6
6
|
|
|
7
|
+
/** @typedef {'silent' | 'normal' | 'verbose'} OcpOptionsLogLevel */
|
|
8
|
+
|
|
9
|
+
export const DEFAULTS = {
|
|
10
|
+
exclude: ['**/.DS_Store'],
|
|
11
|
+
branch: 'gh-pages',
|
|
12
|
+
message: 'Updates',
|
|
13
|
+
remote: 'origin',
|
|
14
|
+
force: false,
|
|
15
|
+
dryRun: false,
|
|
16
|
+
logLevel: /** @type {OcpOptionsLogLevel} */ ('normal')
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
/**
|
|
8
|
-
* @typedef {object}
|
|
9
|
-
* @prop {string}
|
|
10
|
-
* @prop {string[]} [exclude] Paths to leave out, relative to
|
|
20
|
+
* @typedef {object} OcpOptions
|
|
21
|
+
* @prop {string} dir Directory to deploy (required)
|
|
22
|
+
* @prop {string[]} [exclude] Paths to leave out, relative to dir (default: `**\/.DS_Store`)
|
|
11
23
|
* @prop {string} [branch="gh-pages"] Target branch
|
|
12
24
|
* @prop {string} [message="Updates"] Commit message
|
|
13
25
|
* @prop {string} [remote="origin"] Remote name of the current repo, or a repository URL or path
|
|
14
26
|
* @prop {string} [author] Commit author and committer as "Name <email>"
|
|
15
27
|
* @prop {boolean} [force=false] Force push new commit with no history
|
|
16
28
|
* @prop {boolean} [dryRun=false] Show what would change, without committing or pushing
|
|
17
|
-
* @prop {
|
|
29
|
+
* @prop {OcpOptionsLogLevel} [logLevel="normal"] Only "verbose" logs (git commands, to stderr)
|
|
18
30
|
*/
|
|
19
31
|
|
|
20
32
|
/**
|
|
21
33
|
* Replace the contents of a git branch with the contents of a directory.
|
|
22
34
|
* Returns the staged changes as `git diff --name-status` text or "" if nothing changed.
|
|
23
|
-
* @param {
|
|
35
|
+
* @param {OcpOptions} options
|
|
24
36
|
* @returns {Promise<string>}
|
|
25
37
|
*/
|
|
26
|
-
export async function
|
|
27
|
-
|
|
28
|
-
exclude =
|
|
29
|
-
branch =
|
|
30
|
-
message =
|
|
31
|
-
remote =
|
|
38
|
+
export async function deploy({
|
|
39
|
+
dir,
|
|
40
|
+
exclude = DEFAULTS.exclude,
|
|
41
|
+
branch = DEFAULTS.branch,
|
|
42
|
+
message = DEFAULTS.message,
|
|
43
|
+
remote = DEFAULTS.remote,
|
|
32
44
|
author,
|
|
33
|
-
force =
|
|
34
|
-
dryRun =
|
|
35
|
-
|
|
45
|
+
force = DEFAULTS.force,
|
|
46
|
+
dryRun = DEFAULTS.dryRun,
|
|
47
|
+
logLevel = DEFAULTS.logLevel
|
|
36
48
|
}) {
|
|
37
|
-
|
|
49
|
+
if (!dir) throw new Error('dir is required')
|
|
50
|
+
dir = resolve(dir)
|
|
38
51
|
const run = promisify(execFile)
|
|
39
52
|
|
|
40
53
|
// Env vars beat git config, so this also overrides any identity already set in CI
|
|
@@ -53,8 +66,9 @@ export async function publish({
|
|
|
53
66
|
|
|
54
67
|
/** @type {(args: string[], cwd?: string, env?: Record<string, string>) => Promise<string>} */
|
|
55
68
|
const git = async (args, cwd, env) => {
|
|
56
|
-
if (verbose) console.error(`$ git ${args.join(' ')}`)
|
|
57
|
-
|
|
69
|
+
if (logLevel === 'verbose') console.error(`$ git ${args.join(' ')}`)
|
|
70
|
+
const { stdout } = await run('git', args, { cwd, env: { ...process.env, ...env } })
|
|
71
|
+
return stdout.trim()
|
|
58
72
|
}
|
|
59
73
|
|
|
60
74
|
// A remote name resolves to its url; anything else is used as a url or path as-is
|
|
@@ -82,21 +96,21 @@ export async function publish({
|
|
|
82
96
|
await git(['remote', 'add', 'origin', url], tmp)
|
|
83
97
|
}
|
|
84
98
|
|
|
85
|
-
// Empty the index, then stage everything from
|
|
99
|
+
// Empty the index, then stage everything from dir except excluded paths
|
|
86
100
|
await git(['read-tree', '--empty'], tmp)
|
|
87
101
|
await git(
|
|
88
102
|
[
|
|
89
103
|
'--git-dir',
|
|
90
104
|
join(tmp, '.git'),
|
|
91
105
|
'--work-tree',
|
|
92
|
-
|
|
106
|
+
dir,
|
|
93
107
|
'add',
|
|
94
108
|
'--force',
|
|
95
109
|
'--',
|
|
96
110
|
'.',
|
|
97
111
|
...exclude.map((p) => `:(exclude,glob)${p}`)
|
|
98
112
|
],
|
|
99
|
-
|
|
113
|
+
dir
|
|
100
114
|
)
|
|
101
115
|
|
|
102
116
|
const status = await git(['diff', '--cached', '--name-status'], tmp)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oneclickpush",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "One-click GitHub Pages deployer",
|
|
5
5
|
"author": "Jason Lee <jason@zerodevx.com>",
|
|
6
6
|
"scripts": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"keywords": [
|
|
34
34
|
"one-click",
|
|
35
35
|
"gh-pages",
|
|
36
|
-
"deploy"
|
|
36
|
+
"deploy",
|
|
37
|
+
"cli"
|
|
37
38
|
]
|
|
38
39
|
}
|