Skip to content

Commit 2bd85e2

Browse files
committed
chore 🧹: initial commit
Signed-off-by: Victor Hang <[email protected]>
0 parents  commit 2bd85e2

File tree

17 files changed

+761
-0
lines changed

17 files changed

+761
-0
lines changed

.github/dependabot.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: gomod
4+
directory: /
5+
schedule:
6+
interval: weekly
7+
open-pull-requests-limit: 99
8+
- package-ecosystem: github-actions
9+
directory: /
10+
schedule:
11+
interval: weekly
12+
open-pull-requests-limit: 99

.github/release.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
changelog:
2+
exclude:
3+
labels:
4+
- ignore-for-release
5+
authors:
6+
- Victor Hang
7+
categories:
8+
- title: Breaking Changes 🛠
9+
labels:
10+
- Semver-Major
11+
- breaking-change
12+
- title: Features 🎉
13+
labels:
14+
- Semver-Minor
15+
- enhancement
16+
- title: Enhancements ✨
17+
labels:
18+
- '*'
19+
exclude:
20+
labels:
21+
- dependencies
22+
- title: 👒 Dependencies
23+
labels:
24+
- dependencies

.github/workflows/build.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: GoReleaser Dry Run
2+
on:
3+
pull_request:
4+
jobs:
5+
release:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- name: Checkout code
9+
uses: actions/checkout@v4
10+
- name: Set up Go
11+
uses: actions/setup-go@v5
12+
with:
13+
go-version: '1.22.6'
14+
- name: Run GoReleaser (dry run)
15+
uses: goreleaser/goreleaser-action@v6
16+
with:
17+
args: release --snapshot # Perform a dry run
18+
version: '~> v2'

.github/workflows/linting.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Go Lint and Format Check
2+
on:
3+
pull_request:
4+
jobs:
5+
lint_and_format:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- name: Checkout Code
9+
uses: actions/checkout@v4
10+
- name: Set up Go
11+
uses: actions/setup-go@v5
12+
with:
13+
go-version: '1.22.6'
14+
- name: Install Tools
15+
run: |
16+
go install mvdan.cc/gofumpt@latest
17+
go install github.com/segmentio/golines@latest
18+
- name: Run gofumpt
19+
run: |
20+
gofumpt -d . | tee gofumpt_output.txt
21+
if [ -s gofumpt_output.txt ]; then
22+
echo "gofumpt found issues:"
23+
cat gofumpt_output.txt
24+
exit 1
25+
fi
26+
- name: Run golines
27+
run: |
28+
golines --max-len=140 . --dry-run | tee golines_output.txt
29+
if [ -s golines_output.txt ]; then
30+
echo "golines found lines exceeding 140 characters:"
31+
cat golines_output.txt
32+
exit 1
33+
fi

.github/workflows/release.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: goreleaser
2+
on:
3+
push:
4+
tags:
5+
- '*'
6+
permissions:
7+
contents: write
8+
jobs:
9+
goreleaser:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Check out code
13+
uses: actions/checkout@v4
14+
with:
15+
fetch-depth: 0
16+
- name: Set up Go
17+
uses: actions/setup-go@v5
18+
with:
19+
go-version: '1.22.6'
20+
- name: Run GoReleaser
21+
uses: goreleaser/goreleaser-action@v6
22+
with:
23+
distribution: goreleaser
24+
version: '~> v2'
25+
args: release --clean
26+
workdir: ./
27+
env:
28+
GITHUB_TOKEN: ${{ secrets.DIDACTIKLABS_GITHUB_TOKEN }}

.github/workflows/unittest.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Go Test
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
branches: [main]
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- name: Set up Go
13+
uses: actions/setup-go@v5
14+
with:
15+
go-version: '1.22.6'
16+
- name: Run tests with coverage
17+
run: |
18+
go test -coverprofile=coverage.out ./...

.goreleaser.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
version: 2
2+
before:
3+
hooks:
4+
- go mod tidy
5+
builds:
6+
- binary: ginx
7+
main: ./
8+
env:
9+
- CGO_ENABLED=0
10+
goos:
11+
- linux
12+
- windows
13+
- darwin
14+
goarch:
15+
- amd64
16+
- arm64
17+
- arm
18+
ldflags:
19+
- -s -w -X github.com/didactiklabs/ginx/cmd.version=v{{- .Version }}
20+
archives:
21+
- format: tar.gz
22+
# this name template makes the OS and Arch compatible with the results of `uname`.
23+
name_template: >-
24+
{{ .ProjectName }}_v{{- .Version }}_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }}
25+
# use zip for windows archives
26+
format_overrides:
27+
- goos: windows
28+
format: zip
29+
files:
30+
- README.md
31+
- LICENSE
32+
- docs/*

LICENSE

Whitespace-only changes.

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<div align="center">
2+
<h1> Ginx </h1>
3+
4+
![star]
5+
[![Downloads][downloads-badge]][releases]
6+
![version]
7+
8+
</div>
9+
10+
**ginx** is a lightweight CLI tool designed to monitor changes in remote Git repositories and execute custom commands when updates occur. It is ideal for automating tasks, deploying applications, or triggering workflows whenever the target repository is updated.
11+
12+
## Table of Contents
13+
14+
- [Features](#features)
15+
- [Installation](#installation)
16+
- [Usage](#usage)
17+
- [License](#license)
18+
19+
## Features
20+
21+
- **Monitor Remote Repositories**: Watch specific branches for updates.
22+
- **Custom Commands**: Execute any command when changes are detected.
23+
- **Configurable Intervals**: Set the polling frequency for repository checks.
24+
- **Flexible Logging**: Adjust log levels for better visibility or minimal noise.
25+
- **Version Display**: Check the current version of the tool.
26+
27+
## Installation
28+
29+
To install `ytui`, follow the instructions for your operating system.
30+
Ensure that you have the required dependencies installed.
31+
32+
1. **Install binary**
33+
34+
ginx runs on most major platforms. If your platform isn't listed below,
35+
please [open an issue][issues].
36+
37+
Please note that binaries are available on the release pages, you can extract the archives for your
38+
platform and manually install it.
39+
40+
## Usage
41+
42+
See [Documentations](docs/ginx.md).
43+
44+
## License
45+
46+
![licence]
47+
48+
`ginx` is open-source and available under the [LICENCE](LICENSE).
49+
50+
For more detailed usage, you can always use `ginx --help`.
51+
52+
[licence]: https://img.shields.io/github/license/didactiklabs/ginx
53+
[downloads-badge]: https://img.shields.io/github/downloads/didactiklabs/ginx/total?logo=github&logoColor=white&style=flat-square
54+
[releases]: https://github.com/didactiklabs/ginx/releases
55+
[star]: https://img.shields.io/github/stars/didactiklabs/ginx
56+
[version]: https://img.shields.io/github/v/release/didactiklabs/ginx

cmd/root.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"time"
8+
9+
"github.com/go-git/go-git/v5"
10+
"github.com/spf13/cobra"
11+
"go.uber.org/zap"
12+
"go.uber.org/zap/zapcore"
13+
14+
"github.com/didactiklabs/ginx/internal/utils"
15+
)
16+
17+
var (
18+
versionFlag bool
19+
version string
20+
logLevelFlag string
21+
sourceFlag string
22+
branchFlag string
23+
pollIntervalFlag int
24+
)
25+
26+
var RootCmd = &cobra.Command{
27+
Use: "ginx [flags] -- <command>",
28+
Short: "ginx",
29+
Long: `
30+
Ginx is a cli tool that watch a remote repository and run an arbitrary command on changes/updates.
31+
`,
32+
PersistentPreRun: func(cmd *cobra.Command, args []string) {
33+
// Initialize configuration here
34+
if versionFlag {
35+
fmt.Printf("%s", version)
36+
} else {
37+
initConfig()
38+
}
39+
},
40+
Run: func(cmd *cobra.Command, args []string) {
41+
var r *git.Repository
42+
var err error
43+
source := sourceFlag
44+
branch := branchFlag
45+
interval := time.Duration(pollIntervalFlag) * time.Second
46+
dir, err := os.MkdirTemp("", "ginx-*")
47+
if err != nil {
48+
utils.Logger.Fatal("Failed to create temporary directory.", zap.Error(err))
49+
}
50+
51+
if !utils.IsRepoCloned(source) {
52+
utils.Logger.Info("Cloning repository.", zap.String("url", source), zap.String("branch", branch))
53+
r, err = utils.CloneRepo(source, branch, dir)
54+
if err != nil {
55+
utils.Logger.Fatal("Failed to clone repository.", zap.Error(err))
56+
}
57+
} else {
58+
r, err = git.PlainOpen(dir)
59+
utils.Logger.Info("Repository already exist, open directory repository.", zap.String("directory", dir))
60+
if err != nil {
61+
utils.Logger.Fatal("Failed to open existing directory repository.", zap.Error(err))
62+
}
63+
}
64+
65+
log.Println("Starting remote repository watcher...")
66+
for {
67+
// Get the latest commit hash from the remote repository
68+
remoteCommit, err := utils.GetLatestRemoteCommit(r, branch)
69+
utils.Logger.Debug("Fetched remote commit.", zap.String("remoteCommit", remoteCommit))
70+
if err != nil {
71+
utils.Logger.Fatal("error fetching local commit.", zap.Error(err))
72+
}
73+
74+
// Get the latest commit hash from the local repository
75+
localCommit, err := utils.GetLatestLocalCommit(dir)
76+
utils.Logger.Debug("Fetched local commit.", zap.String("localCommit", localCommit))
77+
if err != nil {
78+
utils.Logger.Fatal("error fetching local commit.", zap.Error(err))
79+
}
80+
81+
if remoteCommit != localCommit {
82+
log.Println("Detected remote changes. Pulling for latest changes...")
83+
if err := utils.PullRepo(r); err != nil {
84+
utils.Logger.Info("Failed to pull. Recloning repository.", zap.String("url", source))
85+
err := os.RemoveAll(dir)
86+
if err != nil {
87+
utils.Logger.Fatal("error removing directory.", zap.Error(err))
88+
}
89+
_, err = utils.CloneRepo(source, branch, dir)
90+
if err != nil {
91+
utils.Logger.Fatal("Failed to clone repository.", zap.Error(err))
92+
}
93+
}
94+
if len(args) > 0 {
95+
utils.Logger.Info("Running command.", zap.String("command", args[0]), zap.Any("args", args[1:]))
96+
if err := utils.RunCommand(dir, args[0], args[1:]...); err != nil {
97+
utils.Logger.Error("Failed to run command.", zap.Error(err))
98+
}
99+
}
100+
} else {
101+
utils.Logger.Info("No changes detected in remote repository.", zap.String("url", source))
102+
}
103+
time.Sleep(interval)
104+
}
105+
},
106+
Args: cobra.ArbitraryArgs,
107+
}
108+
109+
func initConfig() {
110+
// Your configuration initialization logic
111+
logLevel := zapcore.InfoLevel //nolint:all
112+
switch logLevelFlag {
113+
case "debug":
114+
logLevel = zapcore.DebugLevel
115+
case "error":
116+
logLevel = zapcore.ErrorLevel
117+
default:
118+
logLevel = zapcore.InfoLevel
119+
}
120+
utils.InitializeLogger(logLevel)
121+
utils.Logger.Info("Initialized configuration.")
122+
}
123+
124+
func Execute() {
125+
err := RootCmd.Execute()
126+
if err != nil {
127+
os.Exit(1)
128+
}
129+
}
130+
131+
func init() {
132+
RootCmd.Flags().BoolVarP(&versionFlag, "version", "v", false, "display version information")
133+
RootCmd.PersistentFlags().StringVarP(&logLevelFlag, "log-level", "l", "info", "override log level (debug, info, error)")
134+
RootCmd.PersistentFlags().StringVarP(&sourceFlag, "source", "s", "", "git repository to watch")
135+
RootCmd.PersistentFlags().StringVarP(&branchFlag, "branch", "b", "main", "branch to watch")
136+
RootCmd.PersistentFlags().IntVarP(&pollIntervalFlag, "interval", "n", 30, "interval in seconds to poll the remote repo")
137+
}

0 commit comments

Comments
 (0)