diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fa23407..830ee64 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,12 +1,12 @@ { "name": "gic", "image": "mcr.microsoft.com/devcontainers/go:dev-1.23", - // "runArgs": [ - // "--gpus", "all" - // ], + "runArgs": [ + "--gpus", "all" + ], "hostRequirements": { "cpus": 4, - // "memory": "16gb" + "memory": "16gb" }, "features": { "ghcr.io/devcontainers/features/common-utils:2": { @@ -17,9 +17,9 @@ "ghcr.io/devcontainers/features/azure-cli:1": { "installBicep": true }, - // "ghcr.io/prulloac/devcontainer-features/ollama:1": { - // "pull": "phi3.5" - // } + "ghcr.io/prulloac/devcontainer-features/ollama:1": { + "pull": "phi3.5" + }, "ghcr.io/stuartleeks/dev-container-features/shell-history:0": {} }, diff --git a/.vscode/launch.json b/.vscode/launch.json index a999a91..680d451 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,8 @@ "request": "launch", "mode": "auto", "program": "main.go", - "cwd": "/workspaces/gic" + "cwd": "/workspaces/gic", + "envFile": "${workspaceFolder}/.env", } ] } diff --git a/README.md b/README.md index 6a31317..0d8c072 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,154 @@ Reducing cognitive load by automating commit message generation, allowing developers to focus on coding instead of crafting messages. A tool that helps developers generate git commit messages based on the `git diff` of staged files, following instructions. It's ideal for use alongside [Semantic Release](https://github.com/semantic-release/semantic-release). -## AzureAD +## How to use it -Remember to assign the `Azure Cognitive Openai User` role to any user that is going to consume the resource. +The flow is simple. Install the binary, create a config (`.gic` or `.gic.yaml`) file in your project root, and run `gic` in your terminal. The tool will generate a commit message based on the staged files and the instructions provided in the `.gic` file. -In your `.gic` config you would add: +![flow](docs/flow.png) +![alt text](image.png) + +### Install the binary + +Run the following command to install the latest version of `gic`: + +```bash +curl -sSfL https://raw.githubusercontent.com/jsburckhardt/gic/main/script/install-gic.sh | sh -s +``` + +or if you prefer, add the `devcontainer` feature to your `.devcontainer/devcontainer.json` file: + +```json + "features": { + "ghcr.io/jsburckhardt/devcontainer-features/gic:1": {} + } +``` + +### Create a `.gic` file + +There are different options to create the config file. + +- You can create it by using the [sample.gic.yaml](https://raw.githubusercontent.com/jsburckhardt/gic/main/sample.gic.yaml) file in the repository. + +- The second option is to ask gic to create a basic config file. Run the following command in your terminal: + + ```bash + gic --create-sample-config + ``` + +## Config file sample ```yaml +llm_instructions: | + You are a commit message generator that follows the semantic release format based on Angular commit guidelines. The user will provide a git diff, and your task is to analyze the changes and generate a SINGLE appropriate git commit message. The message should clearly indicate the type of changes (e.g., feat, fix, chore, docs, style, refactor, test, build, ci, perf, or revert), a brief summary of the change in imperative mood, and optionally include a scope in parentheses. If applicable, include a body with additional details and a footer with references to any related issues or breaking changes. + Commit message can have more than one scope and can be multiline. + + Use breaking change only and only if the change is a feature based on code changes. + + Example Format between ~~~: + + ~~~ + (): + + [optional body] + + [optional footer(s)] + ~~~ + + return ONLY and ONLY the commit message with no ~~~. + + Example Usage: + + Input: git commit diff: ... + Output: A commit message following the format based on the analysis of the diff. + + Example Commit Messages: + + feat(api): add new endpoint for user authentication + fix(ui): resolve button alignment issue on mobile devices + chore(deps): update dependencies to latest versions + docs(readme): add instructions for setting up the project locally + refactor(auth): simplify token validation logic + test(auth): add unit tests for login functionality + perf(core): improve rendering performance by optimizing the DOM updates + +############################################ +##### sample ollama +# connection_type: "ollama" +# azure_endpoint: "http://127.0.0.1:11434/" +# model_deployment_name: "phi3.5" + +############################################ +# sample azure +# connection_type: "azure" +# azure_endpoint: "https://generic-aoai-01.openai.azure.com/" +# model_deployment_name: "gpt-4o-mini" + +############################################ +##### sample azure_ad connection_type: "azure_ad" -azure_endpoint: "https://.openai.azure.com/" -model_deployment_name: "" +azure_endpoint: "https://generic-aoai-01.openai.azure.com/" +model_deployment_name: "gpt-4o-mini" +should_commit: true # this will not only print the commit message but also commit the changes +tokens: 4000 +api_version: "2024-02-15-preview" ``` -## Ollama Locally in your devcontainer (or any machine) +## Customizing the config + +### Using Azure OpenAI resources + +There are two common ways to authenticate with AOAI resources. The first one is by using a **api_key** and the second one is by using an **azure_ad** token. + +- **api_key**: For this flow. You'll need to configure the set `connection_type` to `azure`. Add the details for `azure_endpoint`, `tokens`, `model_deployment_name`, and `api_version`. Additonally you'll need to add the environment variable `API_KEY` with the value of the key. + + ```yaml + # .gic + connection_type: "azure" + azure_endpoint: "https://.openai.azure.com/" + model_deployment_name: "" + api_version: "" + tokens: 4000 + commit: false + llm_instructions: | + - Generate a commit message based on the staged files + - follow semantic release guidelines + ``` -Here is an example to run ollama in your devcontainer and pulling phi3.5 image. + ```bash + # In the terminal + export API_KEY= + ``` + +- **azure_ad**: For this flow. You'll need to configure the set `connection_type` to `azure_ad`. Add the details for `azure_endpoint`, `model_deployment_name`, and `api_version`. Additonally, you'll to assign the Azure Cognitive Openai User role to any user that is going to consume the resource. + + ```yaml + connection_type: "azure_ad" + azure_endpoint: "https://.openai.azure.com/" + model_deployment_name: "" + api_version: "" + tokens: 4000 + commit: false + llm_instructions: | + - Generate a commit message based on the staged files + - follow semantic release guidelines + ``` + +### Using LLMs hosted in Ollama Locally in your devcontainer (or any machine) + +>[!NOTE] +>When using Ollama, it mounts the model to memory the first time you run it, which can take some time. After 5 minutes of inactivity, Ollama offloads the model. + + +Here is an example to run `ollama` in your `devcontainer` and pulling phi3.5 image. ```json ... +// add this args to load the GPUs to your devcontainer +"runArgs": [ + "--gpus", "all" +], +// add the ollama feature and pull a model e.g. phi3.5 "features": { "ghcr.io/prulloac/devcontainer-features/ollama:1": { "pull": "phi3.5" @@ -34,26 +164,36 @@ In your `.gic` config you would add: connection_type: "ollama" azure_endpoint: "http://127.0.0.1:11434/" model_deployment_name: "phi3.5" +tokens: 4000 +commit: false +llm_instructions: | + - Generate a commit message based on the staged files + - follow semantic release guidelines ``` +## Different outputs per model + >[!CAUTION] >When choosing a model validate the instructions and generation matches what you expect. As an example, noticed phi3.5 didn't really generated a commit message with the requested instructions. More details in [here](#different-outputs-per-model). -## Different outputs per model +For this sample we are doing some Documentation changes in the `README.md` file. The instructions are the same for all models. Instructions: ```yaml llm_instructions: | - + - Generate a commit message based on the staged files + - follow semantic release guidelines ``` ### phi3.5 ```bash +commit message: ```\nGenerate a commit message for staged files following semantic release guidelines using Ollama locally or Azure Cognitive OpenAI service. The tool, configured through `.gic` config file, auto-generates messages to streamline the process and maintain consistency in your project's history.\n``` ``` -### gpt-4o-mini +### gpt-4o ```bash +commit message: docs: update README with detailed usage instructions and config options\n\n- Reorganized sections for better clarity and usability\n- Added installation instructions for the `gic` binary\n- Provided steps for creating and customizing the `.gic` config file\n- Included guidance on using Azure OpenAI resources with different authentication methods\n- Detailed instructions on using LLMs hosted locally via Ollama\n- Enhanced documentation with images and sample commands" ``` diff --git a/docs/flow.excalidraw b/docs/flow.excalidraw new file mode 100644 index 0000000..ae64df6 --- /dev/null +++ b/docs/flow.excalidraw @@ -0,0 +1,472 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "QV8uB_cf1BbP2MRSlP4su", + "type": "rectangle", + "x": 381.7599792480469, + "y": 257.82411193847656, + "width": 150.18441772460938, + "height": 79.4813232421875, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 279745161, + "version": 40, + "versionNonce": 509191879, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "jSmokzYcDwvmtDcZVRpGt" + }, + { + "id": "j_HJDocJNwqX_FY1sdzIl", + "type": "arrow" + } + ], + "updated": 1726206145791, + "link": null, + "locked": false + }, + { + "id": "jSmokzYcDwvmtDcZVRpGt", + "type": "text", + "x": 388.93223571777344, + "y": 285.0647735595703, + "width": 135.83990478515625, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 910357449, + "version": 16, + "versionNonce": 2080986439, + "isDeleted": false, + "boundElements": null, + "updated": 1726206132545, + "link": null, + "locked": false, + "text": "Install Binary", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "baseline": 17, + "containerId": "QV8uB_cf1BbP2MRSlP4su", + "originalText": "Install Binary", + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 74, + "versionNonce": 1247479209, + "isDeleted": false, + "id": "ShIM_C-jbJVToCVmltv7z", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 602.2503509521484, + "y": 257.82411193847656, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 150.18441772460938, + "height": 79.4813232421875, + "seed": 253602983, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "1amWIp1Y5P0amLR2IsJoh" + }, + { + "id": "j_HJDocJNwqX_FY1sdzIl", + "type": "arrow" + }, + { + "id": "e64YUXlovto_y4qXGN9rw", + "type": "arrow" + } + ], + "updated": 1726206159847, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 75, + "versionNonce": 205844105, + "isDeleted": false, + "id": "1amWIp1Y5P0amLR2IsJoh", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 614.6426239013672, + "y": 272.5647735595703, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 125.39987182617188, + "height": 50, + "seed": 1345290183, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1726206474493, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Create / \nconfigure .gic", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ShIM_C-jbJVToCVmltv7z", + "originalText": "Create / configure .gic", + "lineHeight": 1.25, + "baseline": 42 + }, + { + "type": "rectangle", + "version": 129, + "versionNonce": 1264674247, + "isDeleted": false, + "id": "vECBL-A5es3x6L0XcfNqQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 822.74072265625, + "y": 257.82411193847656, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 150.18441772460938, + "height": 79.4813232421875, + "seed": 384758953, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "FZEOO-PxrEazbjcSDCZQe" + }, + { + "id": "e64YUXlovto_y4qXGN9rw", + "type": "arrow" + }, + { + "id": "1n56d3vNSA6GF3C9JgLaw", + "type": "arrow" + } + ], + "updated": 1726206162389, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 128, + "versionNonce": 1125678345, + "isDeleted": false, + "id": "FZEOO-PxrEazbjcSDCZQe", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 844.0229873657227, + "y": 285.0647735595703, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 107.61988830566406, + "height": 25, + "seed": 1952216969, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1726206138621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "stage files", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vECBL-A5es3x6L0XcfNqQ", + "originalText": "stage files", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "rectangle", + "version": 164, + "versionNonce": 1729748999, + "isDeleted": false, + "id": "FWouZ1c7-aX6iKEGw1RHc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1043.2310943603516, + "y": 257.82411193847656, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 150.18441772460938, + "height": 79.4813232421875, + "seed": 1130781449, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "WFYaiHVvZEjbL760jNSo4" + }, + { + "id": "1n56d3vNSA6GF3C9JgLaw", + "type": "arrow" + } + ], + "updated": 1726206162389, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 173, + "versionNonce": 1530029735, + "isDeleted": false, + "id": "WFYaiHVvZEjbL760jNSo4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1106.1033172607422, + "y": 285.0647735595703, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 24.439971923828125, + "height": 25, + "seed": 1037570537, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1726206132546, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "gic", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FWouZ1c7-aX6iKEGw1RHc", + "originalText": "gic", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "id": "j_HJDocJNwqX_FY1sdzIl", + "type": "arrow", + "x": 543.5654907226562, + "y": 297.4664406970717, + "width": 47.40673828125, + "height": 0.314666748046875, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 566333129, + "version": 40, + "versionNonce": 1550839175, + "isDeleted": false, + "boundElements": null, + "updated": 1726206167504, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 47.40673828125, + 0.314666748046875 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "QV8uB_cf1BbP2MRSlP4su", + "focus": 0.05565282860641122, + "gap": 11.62109375 + }, + "endBinding": { + "elementId": "ShIM_C-jbJVToCVmltv7z", + "focus": -0.09202357547573803, + "gap": 11.278121948242188 + }, + "startArrowhead": null, + "endArrowhead": "triangle" + }, + { + "id": "e64YUXlovto_y4qXGN9rw", + "type": "arrow", + "x": 762.3154907226562, + "y": 298.6245200497517, + "width": 48.28564453125, + "height": 0.62933349609375, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1979026023, + "version": 33, + "versionNonce": 527060391, + "isDeleted": false, + "boundElements": null, + "updated": 1726206173513, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 48.28564453125, + 0.62933349609375 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "ShIM_C-jbJVToCVmltv7z", + "focus": -0.0011727263301429115, + "gap": 9.880722045898438 + }, + "endBinding": { + "elementId": "vECBL-A5es3x6L0XcfNqQ", + "focus": -0.06940233898445695, + "gap": 12.13958740234375 + }, + "startArrowhead": null, + "endArrowhead": "triangle" + }, + { + "id": "1n56d3vNSA6GF3C9JgLaw", + "type": "arrow", + "x": 984.5377197265625, + "y": 297.3522776190496, + "width": 48.10107421875, + "height": 0.314697265625, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1508022857, + "version": 27, + "versionNonce": 1061594279, + "isDeleted": false, + "boundElements": null, + "updated": 1726206167504, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 48.10107421875, + 0.314697265625 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "vECBL-A5es3x6L0XcfNqQ", + "focus": 0.0040859279345153605, + "gap": 11.612579345703125 + }, + "endBinding": { + "elementId": "FWouZ1c7-aX6iKEGw1RHc", + "focus": -0.039941510446498234, + "gap": 10.592300415039062 + }, + "startArrowhead": null, + "endArrowhead": "triangle" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/flow.png b/docs/flow.png new file mode 100644 index 0000000..75144e3 Binary files /dev/null and b/docs/flow.png differ