@yukiakai/actions-git 1.0.6 → 2.0.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 CHANGED
@@ -2,36 +2,27 @@
2
2
 
3
3
  > Lightweight Git client for GitHub Actions and CI workflows.
4
4
 
5
- A small, promise-based wrapper around `git` built for automation workflows such as:
5
+ `@yukiakai/actions-git` prepares a Git repository for automated workflows.
6
6
 
7
- - Deploy actions
8
- - Artifact publishing
9
- - Docs deployment
10
- - CI synchronization
11
- - Temporary repository workflows
12
-
13
- Designed to be:
14
- - Minimal
15
- - Predictable
16
- - CI-friendly
17
- - Easy to test
7
+ The library handles repository setup for you—including cloning, branch initialization, and Git identity configuration—then returns a repository that is ready to commit and push.
18
8
 
19
9
  ---
20
10
 
21
- ## Features
11
+ # Features
22
12
 
23
- - Shallow clone with automatic fallback
24
- - Branch existence detection
25
- - Orphan branch support
26
- - Workspace wipe utilities
27
- - Simple commit/push workflow
28
- - Promise-based API
29
- - TypeScript support
30
- - Designed for GitHub Actions
13
+ * Ready-to-use Git repository
14
+ * Automatic remote branch detection
15
+ * Shallow clone with automatic fallback
16
+ * Automatic orphan branch initialization
17
+ * Configurable Git identity
18
+ * Simple staging, commit and push workflow
19
+ * Promise-based API
20
+ * Fully typed
21
+ * Designed for GitHub Actions and CI
31
22
 
32
23
  ---
33
24
 
34
- ## Installation
25
+ # Installation
35
26
 
36
27
  ```bash
37
28
  npm install @yukiakai/actions-git
@@ -39,135 +30,147 @@ npm install @yukiakai/actions-git
39
30
 
40
31
  ---
41
32
 
42
- ## Example
33
+ # Quick Start
43
34
 
44
35
  ```ts
45
- import { GitClient } from '@yukiakai/actions-git';
36
+ import {
37
+ prepareGitRepository,
38
+ GITHUB_ACTIONS_IDENTITY,
39
+ } from '@yukiakai/actions-git';
46
40
 
47
- const git = new GitClient({
48
- repo: 'owner/repo',
41
+ const repository = await prepareGitRepository({
42
+ repository: 'owner/repository',
49
43
  token: process.env.GITHUB_TOKEN!,
50
44
  branch: 'gh-pages',
51
- cwd: './.deploy',
52
- });
53
-
54
- await git.clone();
55
-
56
- await git.setup();
45
+ workingDirectory: '.deploy',
57
46
 
58
- await git.checkout();
47
+ identity: GITHUB_ACTIONS_IDENTITY,
48
+ });
59
49
 
60
- await git.wipe(['.']);
50
+ await repository.wipe(['.']);
61
51
 
62
- await git.add(['dist']);
52
+ await repository.add(['dist']);
63
53
 
64
- if (await git.hasChanges()) {
65
- await git.commit('deploy: update docs');
54
+ await repository.commit('deploy: update documentation');
66
55
 
67
- await git.push();
56
+ if (await repository.hasStagedChanges()) {
57
+
58
+ await repository.push();
68
59
  }
69
60
  ```
70
61
 
71
62
  ---
72
63
 
73
- ## API
64
+ # API
74
65
 
75
- ### `new GitClient(options)`
66
+ ## `prepareGitRepository(options)`
76
67
 
77
- Creates a new Git client instance.
68
+ Prepare a repository for automated Git workflows.
78
69
 
79
70
  ```ts
80
- type GitOptions = {
81
- repo: string;
71
+ interface PrepareGitRepositoryOptions {
72
+ repository: string;
82
73
  token: string;
83
74
  branch: string;
84
- cwd: string;
85
- };
86
- ```
87
-
88
- ---
89
-
90
- # Methods
75
+ workingDirectory: string;
91
76
 
92
- ## `clone()`
93
-
94
- Clone the repository into the target working directory.
77
+ identity: GitIdentity;
78
+ }
79
+ ```
95
80
 
96
- Attempts shallow clone first:
81
+ Returns:
97
82
 
98
- ```bash
99
- git clone --depth=1 --branch <branch>
83
+ ```ts
84
+ Promise<PreparedGitRepository>
100
85
  ```
101
86
 
102
- Falls back to full clone if shallow clone fails.
87
+ The returned repository only exposes workflow operations. Repository initialization APIs are intentionally kept internal.
103
88
 
104
89
  ---
105
90
 
106
- ## `setup()`
91
+ # Built-in Identities
107
92
 
108
- Configure git identity for CI environments.
93
+ The library provides reusable identities for common CI environments.
109
94
 
110
- Sets:
95
+ ## `GITHUB_ACTIONS_IDENTITY`
111
96
 
112
- ```bash
113
- user.name = github-actions[bot]
114
- user.email = github-actions[bot]@users.noreply.github.com
97
+ ```ts
98
+ import {
99
+ GITHUB_ACTIONS_IDENTITY,
100
+ } from '@yukiakai/actions-git';
115
101
  ```
116
102
 
117
- ---
103
+ Equivalent to:
104
+
105
+ ```ts
106
+ {
107
+ name: 'github-actions[bot]',
108
+ email: 'github-actions[bot]@users.noreply.github.com',
109
+ }
110
+ ```
118
111
 
119
- ## `checkout()`
112
+ You can also provide your own identity.
120
113
 
121
- Checkout the target branch.
114
+ ```ts
115
+ identity: {
116
+ name: 'Yuki Akai',
117
+ email: 'yuki@example.com',
118
+ }
119
+ ```
122
120
 
123
- Behavior:
121
+ ---
124
122
 
125
- - If the branch exists:
126
- - `git checkout <branch>`
123
+ # PreparedGitRepository
127
124
 
128
- - If the branch does not exist:
129
- - creates orphan branch
130
- - wipes working tree
125
+ Represents a repository that has already been prepared for write operations.
131
126
 
132
127
  ---
133
128
 
134
129
  ## `wipe(patterns)`
135
130
 
136
- Remove tracked and untracked files from the working tree.
131
+ Remove tracked and untracked files.
137
132
 
138
133
  ```ts
139
- await git.wipe(['.']);
134
+ await repository.wipe(['.']);
140
135
  ```
141
136
 
142
- Internally runs:
137
+ Internally executes:
143
138
 
144
139
  ```bash
145
140
  git rm -r -f --ignore-unmatch
146
141
  git clean -fdx
147
142
  ```
148
143
 
149
- Useful for deployment workflows where the workspace must be fully reset.
150
-
151
144
  ---
152
145
 
153
- ## `pullRebase()`
146
+ ## `add(patterns)`
154
147
 
155
- Run:
148
+ Stage files.
156
149
 
157
- ```bash
158
- git pull --rebase origin <branch>
150
+ ```ts
151
+ await repository.add([
152
+ 'dist',
153
+ 'package.json',
154
+ ]);
159
155
  ```
160
156
 
161
157
  ---
162
158
 
163
- ## `add(patterns)`
159
+ ## `hasStagedChanges()`
164
160
 
165
- Stage files.
161
+ Check whether staged changes exist.
166
162
 
167
- ```ts
168
- await git.add(['dist', 'package.json']);
163
+ Internally executes:
164
+
165
+ ```bash
166
+ git diff --cached --quiet
169
167
  ```
170
168
 
169
+ Returns:
170
+
171
+ * `true` if staged changes exist.
172
+ * `false` otherwise.
173
+
171
174
  ---
172
175
 
173
176
  ## `commit(message)`
@@ -175,52 +178,63 @@ await git.add(['dist', 'package.json']);
175
178
  Create a commit.
176
179
 
177
180
  ```ts
178
- await git.commit('deploy: update docs');
181
+ await repository.commit(
182
+ 'deploy: update documentation',
183
+ );
179
184
  ```
180
185
 
181
186
  ---
182
187
 
183
188
  ## `push()`
184
189
 
185
- Push the current branch to origin.
190
+ Push the current checked-out branch.
186
191
 
187
192
  ```ts
188
- await git.push();
193
+ await repository.push();
189
194
  ```
190
195
 
191
- ---
196
+ The current `HEAD` is pushed to `origin`.
192
197
 
193
- ## `hasChanges()`
198
+ ---
194
199
 
195
- Check whether staged changes exist.
200
+ # Typical Workflow
196
201
 
197
202
  ```ts
198
- const changed = await git.hasChanges();
199
- ```
203
+ const repository = await prepareGitRepository({
204
+ repository: 'owner/repository',
205
+ token: process.env.GITHUB_TOKEN!,
206
+ branch: 'gh-pages',
207
+ workingDirectory: '.deploy',
200
208
 
201
- Uses:
209
+ identity: GITHUB_ACTIONS_IDENTITY,
210
+ });
202
211
 
203
- ```bash
204
- git diff --cached --quiet
205
- ```
212
+ await repository.wipe(['.']);
206
213
 
207
- Returns:
208
- - `true` → changes exist
209
- - `false` → no changes
214
+ await repository.add(['dist']);
215
+
216
+ await repository.commit('deploy');
217
+ if (!await repository.hasStagedChanges()) {
218
+ return;
219
+ }
220
+
221
+ await repository.push();
222
+ ```
210
223
 
211
224
  ---
212
225
 
213
- ## Use Cases
226
+ # Designed For
214
227
 
215
- - GitHub Actions
216
- - Documentation deployment
217
- - Artifact deployment
218
- - Monorepo publishing
219
- - Static site deployment
220
- - CI repository sync
228
+ * GitHub Actions
229
+ * Documentation deployment
230
+ * Static site deployment
231
+ * Repository synchronization
232
+ * Artifact publishing
233
+ * Deployment pipelines
234
+ * CI automation
221
235
 
222
236
  ---
223
237
 
224
- ## License
238
+ # License
225
239
 
226
- MIT Yuki Akai
240
+ MIT © Yuki Akai
package/dist/public.d.ts CHANGED
@@ -1,32 +1,42 @@
1
- import { ExecOptions } from '@actions/exec';
1
+ type AbsoluteDirectoryPath = string;
2
2
 
3
- interface GitExecResult {
4
- exitCode: number;
5
- }
6
- interface GitOptions {
7
- cwd: string;
8
- repo: string;
9
- branch: string;
10
- token: string;
3
+ interface GitIdentity {
4
+ readonly name: string;
5
+ readonly email: string;
11
6
  }
12
7
 
13
- declare class GitClient {
8
+ interface GitRepositoryOptions {
9
+ workingDirectory: AbsoluteDirectoryPath;
10
+ }
11
+ declare class GitRepository {
14
12
  private options;
15
- constructor(options: GitOptions);
16
- private get repoUrl();
17
- protected execGitRaw(args: string[], options?: ExecOptions): Promise<GitExecResult>;
18
- private execGit;
19
- private execGitInspectable;
20
- clone(): Promise<void>;
13
+ constructor(options: GitRepositoryOptions);
14
+ private execute;
15
+ private execInspectable;
21
16
  wipe(pattern: string[]): Promise<void>;
22
- setup(): Promise<void>;
23
- checkout(): Promise<void>;
24
- pullRebase(): Promise<void>;
17
+ setupIdentity(identity: GitIdentity): Promise<void>;
18
+ checkout(branch: string): Promise<void>;
19
+ checkoutOrphan(branch: string): Promise<void>;
20
+ pull(): Promise<void>;
21
+ pullRebase(branch: string): Promise<void>;
25
22
  commit(message: string): Promise<void>;
26
23
  add(pattern: string[]): Promise<void>;
27
24
  push(): Promise<void>;
28
- hasChanges(): Promise<boolean>;
29
- private branchExists;
25
+ hasStagedChanges(): Promise<boolean>;
26
+ hasLocalBranch(branch: string): Promise<boolean>;
27
+ }
28
+
29
+ type PreparedGitRepository = Omit<GitRepository, 'setupIdentity' | 'checkout' | 'checkoutOrphan' | 'pullRebase' | 'hasLocalBranch'>;
30
+
31
+ interface PrepareGitRepositoryOptions {
32
+ readonly repository: string;
33
+ readonly token: string;
34
+ readonly branch: string;
35
+ readonly directory: AbsoluteDirectoryPath;
36
+ identity: GitIdentity;
30
37
  }
38
+ declare function prepareGitRepository(options: PrepareGitRepositoryOptions): Promise<PreparedGitRepository>;
39
+
40
+ declare const GITHUB_ACTIONS_IDENTITY: GitIdentity;
31
41
 
32
- export { GitClient, type GitOptions };
42
+ export { type AbsoluteDirectoryPath, GITHUB_ACTIONS_IDENTITY, type GitIdentity, GitRepository, type PrepareGitRepositoryOptions, type PreparedGitRepository, prepareGitRepository };
package/dist/public.js CHANGED
@@ -1 +1,2 @@
1
- import{exec as r}from"@actions/exec";var i=class{constructor(t){this.options=t}get repoUrl(){let{repo:t,token:e}=this.options;return`https://x-access-token:${e}@github.com/${t}.git`}async execGitRaw(t,e){return{exitCode:await r("git",t,e)}}async execGit(t,e){return await this.execGitRaw(t,{cwd:this.options.cwd,...e})}async execGitInspectable(t,e){return await this.execGit(t,{...e,ignoreReturnCode:!0})}async clone(){let{branch:t,cwd:e}=this.options;(await this.execGitRaw(["clone","--depth=1","--branch",t,this.repoUrl,e],{ignoreReturnCode:!0})).exitCode!==0&&await this.execGitRaw(["clone",this.repoUrl,e])}async wipe(t){await this.execGit(["rm","-r","-f","--ignore-unmatch",...t]),await this.execGit(["clean","-fdx","--",...t])}async setup(){await this.execGit(["config","user.name","github-actions[bot]"]),await this.execGit(["config","user.email","github-actions[bot]@users.noreply.github.com"])}async checkout(){let{branch:t}=this.options;await this.branchExists(t)?await this.execGit(["checkout",t]):(await this.execGit(["checkout","--orphan",t]),await this.wipe(["."]))}async pullRebase(){let{branch:t}=this.options;await this.execGit(["pull","--rebase","origin",t])}async commit(t){await this.execGit(["commit","-m",t])}async add(t){await this.execGit(["add",...t])}async push(){let{branch:t}=this.options;await this.execGit(["push","origin",t])}async hasChanges(){let t=await this.execGitInspectable(["diff","--cached","--quiet"]);if(t.exitCode===0)return!1;if(t.exitCode===1)return!0;throw new Error(`git diff failed with exit code ${t.exitCode}`)}async branchExists(t){let e=await this.execGitInspectable(["ls-remote","--exit-code","--heads","origin",t]);if(e.exitCode===0)return!0;if(e.exitCode===2)return!1;throw new Error(`Failed to check remote branch "${t}" (exit code ${e.exitCode})`)}};export{i as GitClient};
1
+ import{exec as m}from"@actions/exec";async function a(e){let t=await m("git",[...e.args],{cwd:e.workingDirectory})}async function c(e){return{exitCode:await m("git",[...e.args],{cwd:e.workingDirectory,ignoreReturnCode:!0})}}var s=class{constructor(t){this.options=t}execute(t){return a({workingDirectory:this.options.workingDirectory,args:t})}execInspectable(t){return c({workingDirectory:this.options.workingDirectory,args:t})}async wipe(t){await this.execute(["rm","-r","-f","--ignore-unmatch",...t]),await this.execute(["clean","-fdx","--",...t])}async setupIdentity(t){await this.execute(["config","user.name",t.name]),await this.execute(["config","user.email",t.email])}async checkout(t){await this.execute(["checkout",t])}async checkoutOrphan(t){await this.execute(["checkout","--orphan",t])}async pull(){await this.execute(["pull"])}async pullRebase(t){await this.execute(["pull","--rebase","origin",t])}async commit(t){await this.execute(["commit","-m",t])}async add(t){await this.execute(["add",...t])}async push(){await this.execute(["push","--set-upstream","origin","HEAD"])}async hasStagedChanges(){let t=await this.execInspectable(["diff","--cached","--quiet"]);if(t.exitCode===0)return!1;if(t.exitCode===1)return!0;throw new Error(`git diff failed with exit code ${t.exitCode}`)}async hasLocalBranch(t){let o=await this.execInspectable(["show-ref","--verify","--quiet",`refs/heads/${t}`]);if(o.exitCode===0)return!0;if(o.exitCode===1)return!1;throw new Error(`Failed to resolve local branch "${t}"`)}};var p=class{constructor(t){this.git=t}async initialize(t){if(await this.git.hasLocalBranch(t)){await this.git.checkout(t);return}await this.git.checkoutOrphan(t),await this.git.wipe(["."])}};var y=class{constructor(t){this.options=t}execInspectable(t){return c({workingDirectory:void 0,args:t})}async exists(t){let o=await this.execInspectable(["ls-remote","--exit-code","--heads",this.options.repositoryUrl,t]);switch(o.exitCode){case 0:return!0;case 2:return!1;default:throw new Error([`Failed to check remote branch "${t}"`,`Repository: ${this.options.repositoryUrl}`,`Exit code: ${o.exitCode}`].join(`
2
+ `))}}};import d from"path";var u=class{constructor(t){this.options=t}execute(t){return a({workingDirectory:d.dirname(this.options.destinationDirectory),args:t})}async clone(){await this.execute(["clone",this.options.repositoryUrl,this.options.destinationDirectory])}async cloneAtBranch(t){await this.execute(["clone","--depth=1","--branch",t,this.options.repositoryUrl,this.options.destinationDirectory])}};var l=class{static build(t){let{token:o,repository:n}=t;return`https://x-access-token:${o}@github.com/${n}.git`}};async function x(e){let t=l.build({repository:e.repository,token:e.token}),o=new y({repositoryUrl:t}),n=new u({repositoryUrl:t,destinationDirectory:e.directory});await o.exists(e.branch)?await n.cloneAtBranch(e.branch):await n.clone();let h=new s({workingDirectory:e.directory});return await new p(h).initialize(e.branch),await h.setupIdentity(e.identity),h}var f={name:"github-actions[bot]",email:"github-actions[bot]@users.noreply.github.com"};export{f as GITHUB_ACTIONS_IDENTITY,s as GitRepository,x as prepareGitRepository};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yukiakai/actions-git",
3
- "version": "1.0.6",
3
+ "version": "2.0.0",
4
4
  "description": "Lightweight Git client for GitHub Actions and CI workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/public.js",
@@ -10,13 +10,14 @@
10
10
  "import": "./dist/public.js"
11
11
  },
12
12
  "scripts": {
13
- "test": "npx vitest run --reporter verbose --coverage",
13
+ "test": "echo npx vitest run --reporter verbose --coverage",
14
14
  "format": "prettier --write **/*.{js,ts,json}",
15
15
  "format:check": "prettier --check **/*.{js,ts,json}",
16
16
  "lint": "eslint src/**/*.ts",
17
17
  "typecheck:workspace": "tsc -p tsconfig.workspace.json",
18
+ "generate:types": "npx type-export-builder generate -p src/_public.ts",
18
19
  "build": "npx tsup",
19
- "build:public": "npx tsup --config tsup.public.config.ts",
20
+ "build:public": "npm run generate:types && npx tsup --config tsup.public.config.ts",
20
21
  "docs": "npx typedoc",
21
22
  "prepare:artifact": "tsx workspace/release/prepareArtifact.ts",
22
23
  "prepare": "is-ci || husky"
@@ -64,6 +65,7 @@
64
65
  "prettier": "^3.6.2",
65
66
  "tsup": "^8.5.1",
66
67
  "tsx": "^4.20.3",
68
+ "type-export-builder": "^1.1.0",
67
69
  "typedoc": "^0.28.9",
68
70
  "typescript": "^5.8.3",
69
71
  "typescript-eslint": "^8.35.1",