Skip to content

Commit 97017f2

Browse files
committed
Merge branch 'develop' into hotfix/json_error
# Conflicts: # src/main.rs
2 parents f0d336d + 8a837d1 commit 97017f2

File tree

10 files changed

+126
-59
lines changed

10 files changed

+126
-59
lines changed

.github/workflows/rust.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!#
2+
# Copyright (c) 2025 Hangzhou Guanwaii Technology Co,.Ltd.
3+
#
4+
# This source code is licensed under the MIT License,
5+
# which is located in the LICENSE file in the source tree's root directory.
6+
#
7+
# File: rust.yml
8+
# Author: mingcheng ([email protected])
9+
# File Created: 2025-03-05 11:10:40
10+
#
11+
# Modified By: mingcheng ([email protected])
12+
# Last Modified: 2025-03-05 11:40:19
13+
##
14+
15+
name: Cargo Build & Test
16+
17+
on:
18+
push:
19+
pull_request:
20+
21+
env:
22+
CARGO_TERM_COLOR: always
23+
24+
jobs:
25+
build_and_test:
26+
name: Rust project - latest
27+
runs-on: ubuntu-latest
28+
strategy:
29+
matrix:
30+
toolchain:
31+
- stable
32+
- beta
33+
- nightly
34+
steps:
35+
- uses: actions/checkout@v4
36+
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
37+
- run: rustup component add clippy && cargo clippy -- -D warnings
38+
- run: rustup component add rustfmt && cargo fmt --all -- --check
39+
- run: cargo build --verbose
40+
- run: cargo test --verbose

Cargo.lock

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
1919

2020
[[package]]
2121
name = "aigitcommit"
22-
version = "1.2.0"
22+
version = "1.3.0"
2323
dependencies = [
2424
"arboard",
2525
"askama",
@@ -1153,9 +1153,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
11531153

11541154
[[package]]
11551155
name = "itoa"
1156-
version = "1.0.14"
1156+
version = "1.0.15"
11571157
source = "registry+https://github.com/rust-lang/crates.io-index"
1158-
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
1158+
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
11591159

11601160
[[package]]
11611161
name = "jobserver"
@@ -1698,9 +1698,9 @@ dependencies = [
16981698

16991699
[[package]]
17001700
name = "redox_syscall"
1701-
version = "0.5.9"
1701+
version = "0.5.10"
17021702
source = "registry+https://github.com/rust-lang/crates.io-index"
1703-
checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
1703+
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
17041704
dependencies = [
17051705
"bitflags 2.9.0",
17061706
]
@@ -1872,15 +1872,15 @@ dependencies = [
18721872

18731873
[[package]]
18741874
name = "rustversion"
1875-
version = "1.0.19"
1875+
version = "1.0.20"
18761876
source = "registry+https://github.com/rust-lang/crates.io-index"
1877-
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
1877+
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
18781878

18791879
[[package]]
18801880
name = "ryu"
1881-
version = "1.0.19"
1881+
version = "1.0.20"
18821882
source = "registry+https://github.com/rust-lang/crates.io-index"
1883-
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
1883+
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
18841884

18851885
[[package]]
18861886
name = "schannel"
@@ -2420,9 +2420,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
24202420

24212421
[[package]]
24222422
name = "unicode-ident"
2423-
version = "1.0.17"
2423+
version = "1.0.18"
24242424
source = "registry+https://github.com/rust-lang/crates.io-index"
2425-
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
2425+
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
24262426

24272427
[[package]]
24282428
name = "unicode-width"

Cargo.toml

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
[package]
22
name = "aigitcommit"
3-
version = "1.2.0"
4-
edition = "2021"
3+
version = "1.3.0"
4+
edition = "2024"
55
description = "A simple git commit message generator by OpenAI compaction model."
6-
license = "MIT"
6+
license-file = "LICENSE"
77
readme = "README.md"
8-
keywords = [
9-
"git",
10-
"commit",
11-
"message",
12-
"generator",
13-
"openai",
14-
"compaction",
15-
"model",
16-
]
17-
authors = ["mingcheng<[email protected]"]
8+
keywords = ["git", "openai", "tools"]
9+
authors = ["mingcheng <[email protected]"]
1810
repository = "https://github.com/mingcheng/aigitcommit"
11+
homepage = "https://github.com/mingcheng/aigitcommit"
12+
exclude = ["assets/*", "hooks/*"]
13+
categories = ["command-line-utilities", "development-tools::git-tools"]
1914

2015
[dependencies]
2116
reqwest = { version = "0.12.12", features = [

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# AIGitCommit
22

3+
[![Cargo Build & Test](https://github.com/mingcheng/aigitcommit/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/mingcheng/aigitcommit/actions/workflows/rust.yml)
4+
35
![screenshots](./assets/screenshots.png)
46

57
A simple tool to help you write better Git commit messages using AI.
@@ -28,7 +30,13 @@ AIGitCommit is still in the early stages of development, I suggest you to instal
2830
cargo install --git https://github.com/mingcheng/aigitcommit.git
2931
```
3032

31-
This command will auto-download the latest version of the project and install it to your cargo bin directory.
33+
or, You can install from [crates.io](https://crates.io/crates/aigitcommit)
34+
35+
```
36+
cargo install aigitcommit
37+
```
38+
39+
Those command will auto-download the latest version of the project and install it to your cargo bin directory.
3240

3341
## Configuration
3442

src/cli.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99
* File Created: 2025-03-03 19:31:27
1010
*
1111
* Modified By: mingcheng ([email protected])
12-
* Last Modified: 2025-03-04 16:31:19
12+
* Last Modified: 2025-03-05 00:25:24
1313
*/
1414

1515
use clap::Parser;
1616

1717
pub const CMD: &str = "aigitcommit";
1818
pub const CMD_ABOUT: &str = "A simple tool to help you write better Git commit messages using AI.";
19+
pub const CMD_ABOUT_URL: &str = "https://github.com/mingcheng/aigitcommit";
1920

2021
#[derive(Debug, Parser)]
2122
#[command(name = CMD)]

src/git.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
* File Created: 2025-03-01 21:55:54
1010
*
1111
* Modified By: mingcheng ([email protected])
12-
* Last Modified: 2025-03-04 11:29:20
12+
* Last Modified: 2025-03-05 10:12:04
1313
*/
1414

15-
use git2::{Repository, StatusOptions};
15+
use git2::{Repository, RepositoryOpenFlags, StatusOptions};
1616
use log::trace;
1717
use std::error::Error;
1818
use std::path::Path;
@@ -24,8 +24,16 @@ pub struct Git {
2424
impl Git {
2525
pub fn new(path: &str) -> Result<Git, Box<dyn Error>> {
2626
trace!("Opening repository at {}", path);
27-
let repository = Repository::open(path)?;
27+
// let repository = Repository::open(path)?;
28+
let repository = Repository::open_ext(path, RepositoryOpenFlags::empty(), vec![path])?;
29+
2830
trace!("Repository opened successfully");
31+
if let Some(work_dir) = repository.workdir() {
32+
trace!("The repository workdir is: {:?}", work_dir);
33+
} else {
34+
return Err("The repository has no workdir".into());
35+
}
36+
2937
Ok(Git { repository })
3038
}
3139

src/main.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
*/
1414

1515
use aigitcommit::cli::Cli;
16+
use aigitcommit::git::Git;
17+
use aigitcommit::openai;
1618
use aigitcommit::openai::OpenAI;
17-
use aigitcommit::{git, openai};
1819
use arboard::Clipboard;
1920
use async_openai::error::OpenAIError;
2021
use async_openai::types::{
@@ -26,7 +27,7 @@ use std::error::Error;
2627
use std::fs::File;
2728
use std::io::Write;
2829
use std::{env, fs};
29-
use tracing::{debug, trace, Level};
30+
use tracing::{Level, debug, trace};
3031

3132
#[tokio::main]
3233
async fn main() -> std::result::Result<(), Box<dyn Error>> {
@@ -56,7 +57,7 @@ async fn main() -> std::result::Result<(), Box<dyn Error>> {
5657

5758
trace!("Specified repository directory: {:?}", repo_dir);
5859
// Check if the directory is a valid git repository
59-
let repository = git::Git::new(repo_dir.to_str().unwrap_or("."))?;
60+
let repository = Git::new(repo_dir.to_str().unwrap_or("."))?;
6061

6162
// Get the diff and logs from the repository
6263
let diffs = repository.get_diff()?;
@@ -79,11 +80,11 @@ async fn main() -> std::result::Result<(), Box<dyn Error>> {
7980
let client = openai::OpenAI::new();
8081

8182
// Check if the OpenAI request is valid, if not, return error
82-
if client.check().await.is_err() {
83-
return Err(
84-
"OpenAI API check with error, please check your API key or configuration".into(),
85-
);
86-
};
83+
// if client.check().await.is_err() {
84+
// return Err(
85+
// "OpenAI API check with error, please check your API key or configuration".into(),
86+
// );
87+
// };
8788

8889
// Generate the prompt which will be sent to OpenAI API
8990
let content = OpenAI::prompt(&logs, &diffs)?;

src/openai.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,26 @@
99
* File Created: 2025-03-01 21:55:58
1010
*
1111
* Modified By: mingcheng ([email protected])
12-
* Last Modified: 2025-03-03 23:58:02
12+
* Last Modified: 2025-03-05 10:46:26
1313
*/
1414
use askama::Template;
1515
use async_openai::config::OPENAI_API_BASE;
1616
use async_openai::error::OpenAIError;
1717
use async_openai::{
18+
Client,
1819
config::OpenAIConfig,
1920
types::{ChatCompletionRequestMessage, CreateChatCompletionRequestArgs},
20-
Client,
2121
};
2222
use log::trace;
23+
use reqwest::header::{HeaderMap, HeaderValue};
2324
use reqwest::{ClientBuilder, Proxy};
2425
use std::env;
2526
use std::error::Error;
27+
use std::time::Duration;
2628
use tracing::debug;
2729

30+
use crate::cli;
31+
2832
#[derive(Template)]
2933
#[template(path = "user.txt")]
3034
struct PromptTemplate<'a> {
@@ -42,33 +46,52 @@ impl Default for OpenAI {
4246
}
4347

4448
impl OpenAI {
49+
/// Create a new OpenAI client instance.
50+
/// This function sets up the OpenAI client with the API key, base URL, and optional proxy settings.
4551
pub fn new() -> Self {
52+
// Set up OpenAI client configuration
4653
let ai_config = OpenAIConfig::new()
4754
.with_api_key(env::var("OPENAI_API_TOKEN").unwrap_or_else(|_| String::from("")))
4855
.with_api_base(
4956
env::var("OPENAI_API_BASE").unwrap_or_else(|_| String::from(OPENAI_API_BASE)),
50-
);
51-
let proxy_addr = env::var("OPENAI_APT_PROXY").unwrap_or_else(|_| String::from(""));
57+
)
58+
.with_org_id(cli::CMD);
59+
60+
// Set up HTTP client builder with default headers
61+
let mut http_client_builder = ClientBuilder::new().user_agent(cli::CMD).default_headers({
62+
let mut headers = HeaderMap::new();
63+
headers.insert("HTTP-Referer", HeaderValue::from_static(cli::CMD_ABOUT_URL));
64+
headers.insert("X-Title", HeaderValue::from_static(cli::CMD));
65+
headers
66+
});
5267

53-
let mut client = Client::with_config(ai_config);
68+
// Set up proxy if specified
69+
let proxy_addr = env::var("OPENAI_APT_PROXY").unwrap_or_else(|_| String::from(""));
5470
if !proxy_addr.is_empty() {
5571
trace!("Using proxy: {}", proxy_addr);
56-
client = client.with_http_client({
57-
let proxy = Proxy::all(proxy_addr).unwrap();
58-
ClientBuilder::new().proxy(proxy).build().unwrap()
59-
})
60-
};
72+
http_client_builder = http_client_builder.proxy(Proxy::all(proxy_addr).unwrap());
73+
}
74+
75+
// Set up timeout and build the HTTP client
76+
let http_client = http_client_builder
77+
.timeout(Duration::from_secs(10))
78+
.build()
79+
.unwrap();
6180

81+
let client = Client::with_config(ai_config).with_http_client(http_client);
6282
OpenAI { client }
6383
}
6484

85+
#[deprecated]
86+
/// Check if the OpenAI API is reachable.
6587
pub async fn check(&self) -> Result<(), Box<dyn Error>> {
6688
match self.client.models().list().await {
6789
Ok(_) => Ok(()),
6890
Err(e) => Err(e.into()),
6991
}
7092
}
7193

94+
/// Send a chat message to the OpenAI API and return the response.
7295
pub async fn chat(
7396
&self,
7497
model_name: &str,

templates/system.txt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Please generate a git commit message with the specified requirements.
1717
- Steer clear of excessive information.
1818
- Eliminate formal or superfluous language.
1919

20-
3. An emoji can be used appropriately at the end of the statement.
20+
3. An emoji can be used appropriately at the end of the statement for the first line.
2121

2222
4. Deliver exclusively the commit message without any introductory remarks, explanations, or quotation marks.
2323

@@ -27,16 +27,7 @@ Please generate a git commit message with the specified requirements.
2727

2828
Here are a few examples that will be demonstrated below.
2929

30-
```
3130
feat: add user auth system
3231

3332
- Add JWT tokens for API auth
3433
- Handle token refresh for long sessions
35-
```
36-
37-
```
38-
fix: resolve memory leak in worker pool
39-
40-
- Clean up idle connections
41-
- Add timeout for stale workers
42-
```

templates/user.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
The latest commit history from the repository is shown here.
1+
The latest commit history from the repository is shown here, quote by using the "```" charsets.
22

33
```
44
{{logs}}
55
```
66

7-
Additionally, a variety of content is provided below.
7+
Additionally, a variety of content is provided below, quote by using the "```" charsets.
88

99
```
1010
{{diffs}}

0 commit comments

Comments
 (0)