safe-push 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 +102 -0
- package/dist/index.js +5 -4
- package/package.json +1 -1
- package/src/commands/push.ts +6 -4
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# safe-push
|
|
2
|
+
|
|
3
|
+
A Bun CLI tool for safe Git push operations. Detects changes to forbidden areas (default: `.github/`) and blocks pushes based on configurable conditions.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install
|
|
9
|
+
bun run build
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Global installation:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bun link
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### Check Push Permission
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
safe-push check # Display result in human-readable format
|
|
24
|
+
safe-push check --json # Output result as JSON
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Execute Push
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
safe-push push # Check and push if allowed
|
|
31
|
+
safe-push push --force # Bypass safety checks
|
|
32
|
+
safe-push push --dry-run # Show result without actually pushing
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Configuration Management
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
safe-push config init # Initialize configuration file
|
|
39
|
+
safe-push config init -f # Overwrite existing configuration
|
|
40
|
+
safe-push config show # Show current configuration
|
|
41
|
+
safe-push config path # Show configuration file path
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Push Permission Rules
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
Push Allowed = (No forbidden changes) AND (New branch OR Last commit is yours)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Forbidden Changes | New Branch | Last Commit Yours | Result |
|
|
51
|
+
|-------------------|------------|-------------------|---------|
|
|
52
|
+
| No | Yes | - | Allowed |
|
|
53
|
+
| No | No | Yes | Allowed |
|
|
54
|
+
| No | No | No | Blocked |
|
|
55
|
+
| Yes | - | - | Blocked |
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
**Path**: `~/.config/safe-push/config.jsonc`
|
|
60
|
+
|
|
61
|
+
```jsonc
|
|
62
|
+
{
|
|
63
|
+
// Forbidden paths (glob patterns)
|
|
64
|
+
"forbiddenPaths": [".github/"],
|
|
65
|
+
// Behavior on forbidden changes: "error" | "prompt"
|
|
66
|
+
"onForbidden": "error"
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Options
|
|
71
|
+
|
|
72
|
+
| Key | Type | Default | Description |
|
|
73
|
+
|-----|------|---------|-------------|
|
|
74
|
+
| `forbiddenPaths` | `string[]` | `[".github/"]` | Paths to block changes (glob patterns) |
|
|
75
|
+
| `onForbidden` | `"error" \| "prompt"` | `"error"` | Behavior when forbidden changes detected |
|
|
76
|
+
|
|
77
|
+
- `error`: Display error and exit
|
|
78
|
+
- `prompt`: Ask user for confirmation
|
|
79
|
+
|
|
80
|
+
## Author Detection
|
|
81
|
+
|
|
82
|
+
Local email is determined by the following priority:
|
|
83
|
+
|
|
84
|
+
1. Environment variable `SAFE_PUSH_EMAIL`
|
|
85
|
+
2. `git config user.email`
|
|
86
|
+
|
|
87
|
+
## Development
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Run in development
|
|
91
|
+
bun run dev -- check
|
|
92
|
+
|
|
93
|
+
# Type check
|
|
94
|
+
bun run typecheck
|
|
95
|
+
|
|
96
|
+
# Build
|
|
97
|
+
bun run build
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -7046,7 +7046,8 @@ function createCheckCommand() {
|
|
|
7046
7046
|
|
|
7047
7047
|
// src/commands/push.ts
|
|
7048
7048
|
function createPushCommand() {
|
|
7049
|
-
return new Command("push").description("Check and push if allowed").option("-f, --force", "Bypass safety checks").option("--dry-run", "Show what would be pushed without actually pushing").action(async (options) => {
|
|
7049
|
+
return new Command("push").description("Check and push if allowed").option("-f, --force", "Bypass safety checks").option("--dry-run", "Show what would be pushed without actually pushing").allowUnknownOption().action(async (options, command) => {
|
|
7050
|
+
const gitArgs = command.args;
|
|
7050
7051
|
try {
|
|
7051
7052
|
if (!await isGitRepository()) {
|
|
7052
7053
|
printError("Not a git repository");
|
|
@@ -7063,7 +7064,7 @@ function createPushCommand() {
|
|
|
7063
7064
|
printSuccess("Dry run: would push (checks bypassed)");
|
|
7064
7065
|
process.exit(0);
|
|
7065
7066
|
}
|
|
7066
|
-
const result2 = await execPush();
|
|
7067
|
+
const result2 = await execPush(gitArgs);
|
|
7067
7068
|
if (result2.success) {
|
|
7068
7069
|
printSuccess("Push successful");
|
|
7069
7070
|
process.exit(0);
|
|
@@ -7082,7 +7083,7 @@ function createPushCommand() {
|
|
|
7082
7083
|
printSuccess("Dry run: would push (user confirmed)");
|
|
7083
7084
|
process.exit(0);
|
|
7084
7085
|
}
|
|
7085
|
-
const result2 = await execPush();
|
|
7086
|
+
const result2 = await execPush(gitArgs);
|
|
7086
7087
|
if (result2.success) {
|
|
7087
7088
|
printSuccess("Push successful");
|
|
7088
7089
|
process.exit(0);
|
|
@@ -7101,7 +7102,7 @@ function createPushCommand() {
|
|
|
7101
7102
|
printSuccess("Dry run: would push");
|
|
7102
7103
|
process.exit(0);
|
|
7103
7104
|
}
|
|
7104
|
-
const result = await execPush();
|
|
7105
|
+
const result = await execPush(gitArgs);
|
|
7105
7106
|
if (result.success) {
|
|
7106
7107
|
printSuccess("Push successful");
|
|
7107
7108
|
process.exit(0);
|
package/package.json
CHANGED
package/src/commands/push.ts
CHANGED
|
@@ -18,7 +18,9 @@ export function createPushCommand(): Command {
|
|
|
18
18
|
.description("Check and push if allowed")
|
|
19
19
|
.option("-f, --force", "Bypass safety checks")
|
|
20
20
|
.option("--dry-run", "Show what would be pushed without actually pushing")
|
|
21
|
-
.
|
|
21
|
+
.allowUnknownOption()
|
|
22
|
+
.action(async (options: { force?: boolean; dryRun?: boolean }, command: Command) => {
|
|
23
|
+
const gitArgs = command.args;
|
|
22
24
|
try {
|
|
23
25
|
// Gitリポジトリ内か確認
|
|
24
26
|
if (!(await isGitRepository())) {
|
|
@@ -43,7 +45,7 @@ export function createPushCommand(): Command {
|
|
|
43
45
|
process.exit(0);
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
const result = await execPush();
|
|
48
|
+
const result = await execPush(gitArgs);
|
|
47
49
|
if (result.success) {
|
|
48
50
|
printSuccess("Push successful");
|
|
49
51
|
process.exit(0);
|
|
@@ -73,7 +75,7 @@ export function createPushCommand(): Command {
|
|
|
73
75
|
process.exit(0);
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
const result = await execPush();
|
|
78
|
+
const result = await execPush(gitArgs);
|
|
77
79
|
if (result.success) {
|
|
78
80
|
printSuccess("Push successful");
|
|
79
81
|
process.exit(0);
|
|
@@ -96,7 +98,7 @@ export function createPushCommand(): Command {
|
|
|
96
98
|
process.exit(0);
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
const result = await execPush();
|
|
101
|
+
const result = await execPush(gitArgs);
|
|
100
102
|
if (result.success) {
|
|
101
103
|
printSuccess("Push successful");
|
|
102
104
|
process.exit(0);
|