oneclickpush 0.1.0 → 0.2.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 +22 -6
- package/cli.js +11 -6
- package/index.d.ts +14 -2
- package/index.js +31 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ in one command.
|
|
|
9
9
|
[complicated](https://github.com/tschaub/gh-pages/tree/main/lib),
|
|
10
10
|
[buggy](https://github.com/tschaub/gh-pages/issues), and
|
|
11
11
|
[unmaintained](https://github.com/tschaub/gh-pages/issues/657#issue-4979248775); `oneclickpush` is
|
|
12
|
-
none of these, has zero dependencies and only uses Node
|
|
12
|
+
none of these, has zero dependencies and only uses built-in Node APIs + `git`.
|
|
13
13
|
|
|
14
14
|
## Quick Start
|
|
15
15
|
|
|
@@ -32,7 +32,9 @@ Options:
|
|
|
32
32
|
-e, --exclude <glob> Paths to leave out, relative to dist (default: "**/.DS_Store")
|
|
33
33
|
-b, --branch <name> Target branch (default: "gh-pages")
|
|
34
34
|
-m, --message <msg> Commit message (default: "Updates")
|
|
35
|
-
-
|
|
35
|
+
-r, --remote <name|url> Remote name or repo URL to publish to (default: "origin")
|
|
36
|
+
-a, --author <author> Commit author, as "Name <email>"
|
|
37
|
+
-f, --force Force push new commit with no history
|
|
36
38
|
-n, --dry-run Show what would change, without committing or pushing
|
|
37
39
|
-v, --verbose Log git commands
|
|
38
40
|
-V, --version Print version
|
|
@@ -55,6 +57,13 @@ npx oneclickpush -d public -b release -m "deploy: v1.0.0"
|
|
|
55
57
|
# Exclude source maps and test files
|
|
56
58
|
npx oneclickpush -d dist -e "**/*.map" -e "**/tests/**"
|
|
57
59
|
|
|
60
|
+
# Deploy to a different remote or repository URL
|
|
61
|
+
npx oneclickpush -d dist -r upstream
|
|
62
|
+
npx oneclickpush -d dist -r git@github.com:user/repo.git
|
|
63
|
+
|
|
64
|
+
# Set commit author (useful in CI environments)
|
|
65
|
+
npx oneclickpush -d dist -a "Deploy Bot <bot@example.com>"
|
|
66
|
+
|
|
58
67
|
# Force push fresh history
|
|
59
68
|
npx oneclickpush -d dist --force
|
|
60
69
|
```
|
|
@@ -76,6 +85,8 @@ const changes = await publish({
|
|
|
76
85
|
exclude: ['**/.DS_Store', '**/*.map'],
|
|
77
86
|
branch: 'gh-pages',
|
|
78
87
|
message: 'Deploy updates',
|
|
88
|
+
remote: 'origin',
|
|
89
|
+
author: 'Deploy Bot <bot@example.com>',
|
|
79
90
|
force: false,
|
|
80
91
|
dryRun: false,
|
|
81
92
|
verbose: false
|
|
@@ -96,19 +107,22 @@ if (changes) {
|
|
|
96
107
|
| `exclude` | `string[]` | `["**/.DS_Store"]` | Paths to exclude relative to `dist` |
|
|
97
108
|
| `branch` | `string` | `"gh-pages"` | Target git branch |
|
|
98
109
|
| `message` | `string` | `"Updates"` | Git commit message |
|
|
110
|
+
| `remote` | `string` | `"origin"` | Remote name or repository URL/path to publish to |
|
|
111
|
+
| `author` | `string` | — | Commit author and committer, as `"Name <email>"` |
|
|
99
112
|
| `force` | `boolean` | `false` | Force push fresh commit with no previous history |
|
|
100
113
|
| `dryRun` | `boolean` | `false` | Return staged changes without committing or pushing |
|
|
101
114
|
| `verbose` | `boolean` | `false` | Log executed git commands |
|
|
102
115
|
|
|
103
116
|
## How It Works
|
|
104
117
|
|
|
105
|
-
1. Resolves the
|
|
118
|
+
1. Resolves the target repository URL from git remote configuration or uses the provided remote
|
|
119
|
+
URL/path directly.
|
|
106
120
|
2. Creates an isolated temporary directory and fetches the target branch (or initializes it if it
|
|
107
121
|
doesn't exist).
|
|
108
|
-
3. Clears the target branch index and stages the contents of `dist` (
|
|
122
|
+
3. Clears the target branch index and stages the contents of `dist` (minus `exclude` patterns)
|
|
109
123
|
directly via Git `work-tree`.
|
|
110
124
|
4. Checks for differences (`git diff --cached --name-status`). If there are changes, commits and
|
|
111
|
-
pushes back to
|
|
125
|
+
pushes back to the remote repository.
|
|
112
126
|
5. Cleans up the temporary directory.
|
|
113
127
|
|
|
114
128
|
> [!WARNING]
|
|
@@ -116,10 +130,12 @@ if (changes) {
|
|
|
116
130
|
|
|
117
131
|
### Notes
|
|
118
132
|
|
|
119
|
-
-
|
|
133
|
+
- Dotfiles (except `.DS_Store`) are included by default; use `-e` to exclude.
|
|
120
134
|
- Contents in target branch are emptied and replaced with the contents of `dist` (WYSIWYG).
|
|
121
135
|
- If `.nojekyll` or `CNAME` files are required, add them to `dist`; use a post-build step if needed.
|
|
122
136
|
- If the target branch does not exist, it will be created.
|
|
137
|
+
- When `--remote` is a repository URL or path, running from within an existing git repository is not
|
|
138
|
+
required.
|
|
123
139
|
|
|
124
140
|
## License
|
|
125
141
|
|
package/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { publish } from './index.js'
|
|
|
5
5
|
|
|
6
6
|
const HELP = `Usage: oneclickpush [options]
|
|
7
7
|
|
|
8
|
-
Replace the contents of a remote git branch with that of a
|
|
8
|
+
Replace the contents of a remote git branch with that of a
|
|
9
9
|
local directory.
|
|
10
10
|
|
|
11
11
|
Options:
|
|
@@ -14,15 +14,18 @@ Options:
|
|
|
14
14
|
(default: "**/.DS_Store")
|
|
15
15
|
-b, --branch <name> Target branch (default: "gh-pages")
|
|
16
16
|
-m, --message <msg> Commit message (default: "Updates")
|
|
17
|
-
-
|
|
18
|
-
|
|
17
|
+
-r, --remote <name|url> Remote name or repo URL to publish to
|
|
18
|
+
(default: "origin")
|
|
19
|
+
-a, --author <author> Commit author, as "Name <email>"
|
|
20
|
+
-f, --force Force push new commit with no history
|
|
21
|
+
-n, --dry-run Show what would change, without
|
|
19
22
|
committing or pushing
|
|
20
23
|
-v, --verbose Log git commands
|
|
21
24
|
-V, --version Print version
|
|
22
25
|
-h, --help Show this help
|
|
23
26
|
|
|
24
|
-
--exclude takes glob patterns (*, **, ?) and can be repeated.
|
|
25
|
-
Patterns are matched against the full path, so use **/name to
|
|
27
|
+
--exclude takes glob patterns (*, **, ?) and can be repeated.
|
|
28
|
+
Patterns are matched against the full path, so use **/name to
|
|
26
29
|
match at any depth.`
|
|
27
30
|
|
|
28
31
|
const { values: v } = parseArgs({
|
|
@@ -30,8 +33,10 @@ const { values: v } = parseArgs({
|
|
|
30
33
|
dist: { type: 'string', short: 'd', default: '.' },
|
|
31
34
|
exclude: { type: 'string', short: 'e', multiple: true, default: ['**/.DS_Store'] },
|
|
32
35
|
branch: { type: 'string', short: 'b', default: 'gh-pages' },
|
|
33
|
-
force: { type: 'boolean', short: 'f', default: false },
|
|
34
36
|
message: { type: 'string', short: 'm', default: 'Updates' },
|
|
37
|
+
remote: { type: 'string', short: 'r', default: 'origin' },
|
|
38
|
+
author: { type: 'string', short: 'a' },
|
|
39
|
+
force: { type: 'boolean', short: 'f', default: false },
|
|
35
40
|
'dry-run': { type: 'boolean', short: 'n', default: false },
|
|
36
41
|
verbose: { type: 'boolean', short: 'v', default: false },
|
|
37
42
|
version: { type: 'boolean', short: 'V', default: false },
|
package/index.d.ts
CHANGED
|
@@ -16,7 +16,15 @@ export type OneclickpushOptions = {
|
|
|
16
16
|
*/
|
|
17
17
|
message?: string
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Remote name of the current repo, or a repository URL or path
|
|
20
|
+
*/
|
|
21
|
+
remote?: string
|
|
22
|
+
/**
|
|
23
|
+
* Commit author and committer as "Name <email>"
|
|
24
|
+
*/
|
|
25
|
+
author?: string
|
|
26
|
+
/**
|
|
27
|
+
* Force push new commit with no history
|
|
20
28
|
*/
|
|
21
29
|
force?: boolean
|
|
22
30
|
/**
|
|
@@ -34,7 +42,9 @@ export type OneclickpushOptions = {
|
|
|
34
42
|
* @prop {string[]} [exclude] Paths to leave out, relative to dist (default: `**\/.DS_Store`)
|
|
35
43
|
* @prop {string} [branch="gh-pages"] Target branch
|
|
36
44
|
* @prop {string} [message="Updates"] Commit message
|
|
37
|
-
* @prop {
|
|
45
|
+
* @prop {string} [remote="origin"] Remote name of the current repo, or a repository URL or path
|
|
46
|
+
* @prop {string} [author] Commit author and committer as "Name <email>"
|
|
47
|
+
* @prop {boolean} [force=false] Force push new commit with no history
|
|
38
48
|
* @prop {boolean} [dryRun=false] Show what would change, without committing or pushing
|
|
39
49
|
* @prop {boolean} [verbose=false] Log git commands
|
|
40
50
|
*/
|
|
@@ -49,6 +59,8 @@ export declare function publish({
|
|
|
49
59
|
exclude,
|
|
50
60
|
branch,
|
|
51
61
|
message,
|
|
62
|
+
remote,
|
|
63
|
+
author,
|
|
52
64
|
force,
|
|
53
65
|
dryRun,
|
|
54
66
|
verbose
|
package/index.js
CHANGED
|
@@ -10,7 +10,9 @@ import { join, resolve } from 'node:path'
|
|
|
10
10
|
* @prop {string[]} [exclude] Paths to leave out, relative to dist (default: `**\/.DS_Store`)
|
|
11
11
|
* @prop {string} [branch="gh-pages"] Target branch
|
|
12
12
|
* @prop {string} [message="Updates"] Commit message
|
|
13
|
-
* @prop {
|
|
13
|
+
* @prop {string} [remote="origin"] Remote name of the current repo, or a repository URL or path
|
|
14
|
+
* @prop {string} [author] Commit author and committer as "Name <email>"
|
|
15
|
+
* @prop {boolean} [force=false] Force push new commit with no history
|
|
14
16
|
* @prop {boolean} [dryRun=false] Show what would change, without committing or pushing
|
|
15
17
|
* @prop {boolean} [verbose=false] Log git commands
|
|
16
18
|
*/
|
|
@@ -26,6 +28,8 @@ export async function publish({
|
|
|
26
28
|
exclude = ['**/.DS_Store'],
|
|
27
29
|
branch = 'gh-pages',
|
|
28
30
|
message = 'Updates',
|
|
31
|
+
remote = 'origin',
|
|
32
|
+
author,
|
|
29
33
|
force = false,
|
|
30
34
|
dryRun = false,
|
|
31
35
|
verbose = false
|
|
@@ -33,13 +37,34 @@ export async function publish({
|
|
|
33
37
|
dist = resolve(dist)
|
|
34
38
|
const run = promisify(execFile)
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
// Env vars beat git config, so this also overrides any identity already set in CI
|
|
41
|
+
/** @type {Record<string, string>} */
|
|
42
|
+
let identity = {}
|
|
43
|
+
if (author) {
|
|
44
|
+
const m = /^(.+?)\s*<(.+)>$/.exec(author)
|
|
45
|
+
if (!m) throw new Error('--author must look like "Name <email>"')
|
|
46
|
+
identity = {
|
|
47
|
+
GIT_AUTHOR_NAME: m[1],
|
|
48
|
+
GIT_AUTHOR_EMAIL: m[2],
|
|
49
|
+
GIT_COMMITTER_NAME: m[1],
|
|
50
|
+
GIT_COMMITTER_EMAIL: m[2]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @type {(args: string[], cwd?: string, env?: Record<string, string>) => Promise<string>} */
|
|
55
|
+
const git = async (args, cwd, env) => {
|
|
38
56
|
if (verbose) console.error(`$ git ${args.join(' ')}`)
|
|
39
|
-
return (await run('git', args, { cwd })).stdout.trim()
|
|
57
|
+
return (await run('git', args, { cwd, env: { ...process.env, ...env } })).stdout.trim()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// A remote name resolves to its url; anything else is used as a url or path as-is
|
|
61
|
+
let url = remote
|
|
62
|
+
try {
|
|
63
|
+
url = await git(['remote', 'get-url', remote])
|
|
64
|
+
} catch {
|
|
65
|
+
if (/^[\w-]+$/.test(remote)) throw new Error(`Remote "${remote}" not found`)
|
|
40
66
|
}
|
|
41
67
|
|
|
42
|
-
const url = await git(['config', '--get', 'remote.origin.url'])
|
|
43
68
|
const tmp = await mkdtemp(join(tmpdir(), 'oneclickpush-'))
|
|
44
69
|
|
|
45
70
|
try {
|
|
@@ -77,7 +102,7 @@ export async function publish({
|
|
|
77
102
|
const status = await git(['diff', '--cached', '--name-status'], tmp)
|
|
78
103
|
if (!status || dryRun) return status
|
|
79
104
|
|
|
80
|
-
await git(['commit', '-q', '-m', message], tmp)
|
|
105
|
+
await git(['commit', '-q', '-m', message], tmp, identity)
|
|
81
106
|
await git(['push', '-q', ...(force ? ['--force'] : []), 'origin', branch], tmp)
|
|
82
107
|
return status
|
|
83
108
|
} finally {
|