Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/build-and-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# .github/workflows/release.yaml

on:
release:
types: [created]

permissions:
contents: write
packages: write

jobs:
releases-matrix:
name: Release Go Binary
runs-on: ubuntu-latest
strategy:
matrix:
# build and publish in parallel: linux/386, linux/amd64, linux/arm64, windows/386, windows/amd64, darwin/amd64, darwin/arm64
goos: [linux, windows, darwin]
goarch: ["386", amd64, arm64]
exclude:
- goarch: "386"
goos: darwin
- goarch: arm64
goos: windows
steps:
- uses: actions/checkout@v3
- uses: wangyoucao577/go-release-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
binary_name: "syncommit"
4 changes: 2 additions & 2 deletions cmd/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"log"
"os"
"os/exec"
"syncommit/utils"
"syncommit/structs"

"github.com/spf13/cobra"
)
Expand All @@ -23,7 +23,7 @@ var commitCommand = &cobra.Command{
Short: "Add a commit to the sync repo",
Long: `Add a commit to the sync repo`,
Run: func(cmd *cobra.Command, args []string) {
err := os.Chdir(utils.RepoPath)
err := os.Chdir(structs.RepoPath)
if err != nil {
log.Fatal("failed to cd into repo directory. ", err.Error())
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"log"
"syncommit/scripts"
"syncommit/structs"
"syncommit/utils"

"github.com/spf13/cobra"
Expand All @@ -17,11 +18,11 @@ var initCommand = &cobra.Command{
Use: "init",
Short: "setup the current repository to sync with github",
Run: func(cmd *cobra.Command, args []string) {
privateRepoFound, err := utils.SearchDir(utils.ConfigFolderPath, utils.RepoLocation)
privateRepoFound, err := utils.SearchDir(structs.ConfigFolderPath, structs.RepoLocation)
if err != nil {
log.Fatal("failed to read directory")
}
privateRepoUrlFound, err := utils.SearchDir(utils.ConfigFolderPath, utils.RepoFileName)
privateRepoUrlFound, err := utils.SearchDir(structs.ConfigFolderPath, structs.RepoFileName)
if err != nil {
log.Fatal("failed to read directory")
}
Expand Down
12 changes: 3 additions & 9 deletions cmd/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package cmd
import (
"fmt"
"log"
"os"
"os/exec"
"syncommit/utils"
"syncommit/structs"

"github.com/spf13/cobra"
)
Expand All @@ -19,12 +17,8 @@ var pushCommand = &cobra.Command{
Short: "Pushes all the sync commits to github",
Long: `Pushes all the sync commits to github`,
Run: func(cmd *cobra.Command, args []string) {
err := os.Chdir(utils.RepoPath)
if err != nil {
log.Fatal("failed to cd into repo directory. ", err.Error())
}
commitCmd := exec.Command("git", "push", "-fu")
err = commitCmd.Run()
syncRepo := structs.GetRepoAtPath(structs.RepoPath)
err := syncRepo.Push()
if err != nil {
log.Fatal("failed to push. ", err.Error())
}
Expand Down
38 changes: 38 additions & 0 deletions cmd/sync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cmd

import (
"fmt"
"log"
"syncommit/structs"
"syncommit/utils"

"github.com/spf13/cobra"
)

func init() {
rootCmd.AddCommand(syncCommand)
}

var syncCommand = &cobra.Command{
Use: "sync",
Short: "sync all commits to private repo",
Long: `commits and pushes all your commits in the current project to github.`,
Run: func(cmd *cobra.Command, args []string) {
repo := structs.GetRepoAtPath(".")
allCommits := repo.GetRepoCommitsForCurrentAuthor()
syncRepo := structs.GetRepoAtPath(structs.RepoPath)
syncedCommits := syncRepo.GetRepoCommitsForCurrentAuthor()
commitsToSync := utils.FilterSyncedCommits(allCommits, syncedCommits)
for _, commit := range commitsToSync {
err := commit.Commit(branchName, repo.Name)
if err != nil {
log.Fatal("failed to sync repo.\nError: ", err.Error())
}
}
err := syncRepo.Push()
if err != nil {
log.Fatal("failed to push. ", err.Error())
}
fmt.Println("sync commits pushed successfully.")
},
}
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package main

import (
"log"
"syncommit/cmd"
"syncommit/utils"
)

func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
utils.RunChecks()
utils.SetupConfigFolder()
cmd.Execute()
Expand Down
4 changes: 3 additions & 1 deletion scripts/post-commit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ if [[ $(git config --get remote.origin.url) == *"github"* ]]; then
echo "skipping sync commit since remote is github."
exit 0
fi

commit_hash=$(git log -1 --format=%h)
commit_message=$(git log -n 1 HEAD --pretty=format:%s)
branch_name=$(git branch --show-current)
repo_name=$(basename -s .git `git config --get remote.origin.url`)

sync_message="commit: $commit_message on branch $branch_name on repo: $repo_name"
sync_message="hash: $commit_hash $commit_message on branch $branch_name on repo: $repo_name"
syncommit commit -m "$(echo $sync_message)"
exit 0
28 changes: 28 additions & 0 deletions structs/commit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package structs

import (
"fmt"
"log"
"os"
"os/exec"
"strings"
"time"
)

type Commit struct {
Hash string
Message string
Time time.Time
}

func (c *Commit) generateCommitMessage(repoName, branchName string) string {
return fmt.Sprintf("hash: %s %s on branch: %s on repo: %s", c.Hash, c.Message, strings.TrimSpace(repoName), strings.TrimSpace(branchName))
}

func (c *Commit) Commit(repoName, branchName string) error {
err := os.Chdir(RepoPath)
if err != nil {
log.Fatal("failed to cd into repo directory. ", err.Error())
}
return exec.Command("git", "commit", "-m", fmt.Sprintf("%q", c.generateCommitMessage(repoName, branchName)), "--allow-empty", fmt.Sprintf("--date='%s'", c.Time.Format(time.RFC1123Z))).Run()
}
118 changes: 118 additions & 0 deletions structs/repo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package structs

import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
)

const RepoFileName = ".repo"
const RepoLocation = "repo"

var HomePath = os.Getenv("HOME")

var ConfigFolderPath = filepath.Join(HomePath, "/.syncommit")
var RepoPath = filepath.Join(ConfigFolderPath, RepoLocation)

const hashPrefix = "hash: "

type Repo struct {
Name string
Path string
BranchName string
CurrentAuthorName string
CurrentAuthorEmail string
}

func (r *Repo) GetRepoCommitsForCurrentAuthor() (commits []Commit) {
cmd := exec.Command("git", "--no-pager", "log", fmt.Sprintf("--author=%s", r.CurrentAuthorEmail), "--pretty=format:%h$//%s$//%ad", "--date=unix", "--no-merges")
commitsBytes, err := cmd.Output()
if err != nil && err.Error() == "exit status 128" {
// User has no commits
return
}
if err != nil {
log.Fatal("Failed to get commits for current author.\nError: ", err.Error())
}
commitsString := strings.Split(string(commitsBytes), "\n")
for _, commitString := range commitsString {
if len(commitString) < 1 {
continue
}

var commit Commit
if r.Path != RepoPath {
commit = parseGeneralRepoCommitString(commitString)
} else {
if !strings.Contains(commitString, hashPrefix) {
continue
}
commit = parseSyncRepoCommitString(commitString)
}
commits = append(commits, commit)
}
return
}

func parseSyncRepoCommitString(commitString string) Commit {
hashWithMessageAndTime := strings.Split(commitString, "$//")
commitTimeStr, _ := strconv.Atoi(hashWithMessageAndTime[2])
originalCommitHash := strings.Replace(hashWithMessageAndTime[0], hashPrefix, "", 1)[0:7]
return Commit{Hash: originalCommitHash, Message: hashWithMessageAndTime[1], Time: time.Unix(int64(commitTimeStr), 0)}
}

func parseGeneralRepoCommitString(commitString string) Commit {
hashWithMessageAndTime := strings.Split(commitString, "$//")
commitTimeStr, _ := strconv.Atoi(hashWithMessageAndTime[2])
return Commit{Hash: hashWithMessageAndTime[0], Message: hashWithMessageAndTime[1], Time: time.Unix(int64(commitTimeStr), 0)}
}

func (r *Repo) Push() error {
cmd := exec.Command("git", "push", "-fu")
cmd.Dir = r.Path
return cmd.Run()
}

func GetRepoAtPath(path string) Repo {
err := os.Chdir(path)
if err != nil {
log.Fatal("failed to change directory,\nError: ", err.Error())
}
branchNameBytes, err := exec.Command("git", "branch", "--show-current").Output()
if err != nil {
log.Fatal("failed to get repo information.\nError: ", err.Error())
}
branchName := strings.ReplaceAll(string(branchNameBytes), "\n", "")
repoPathBytes, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err != nil {
log.Fatal("failed to get repo information.\nError: ", err.Error())
}
repoPath := strings.ReplaceAll(string(repoPathBytes), "\n", "")
repoNameBytes, err := exec.Command("basename", string(repoPath)).Output()
if err != nil {
log.Fatal("failed to get repo information.\nError: ", err.Error())
}
repoName := strings.ReplaceAll(string(repoNameBytes), "\n", "")
authorNameBytes, err := exec.Command("git", "config", "user.name").Output()
if err != nil {
log.Fatal("failed to get repo information.\nError: ", err.Error())
}
authorName := strings.TrimSpace(string(authorNameBytes))
authorEmailBytes, err := exec.Command("git", "config", "user.email").Output()
if err != nil {
log.Fatal("failed to get repo information.\nError:", err.Error())
}
authorEmail := strings.TrimSpace(string(string(authorEmailBytes)))
return Repo{
Name: repoName,
Path: repoPath,
BranchName: branchName,
CurrentAuthorName: authorName,
CurrentAuthorEmail: authorEmail,
}
}
35 changes: 28 additions & 7 deletions utils/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path/filepath"
"regexp"
"strings"
"syncommit/structs"
)

func ValidateGitUrl(gitUrl string) bool {
Expand All @@ -17,16 +18,16 @@ func ValidateGitUrl(gitUrl string) bool {
}

func ClonePrivateRepo(repoUrl string) error {
dirs, err := os.ReadDir(ConfigFolderPath)
dirs, err := os.ReadDir(structs.ConfigFolderPath)
if err != nil {
log.Fatal("failed to read the contents of ConfigFolderPath. ", err)
}
for _, dir := range dirs {
if dir.Name() == RepoLocation {
if dir.Name() == structs.RepoLocation {
return nil
}
}
cmd := exec.Command("git", "clone", "-q", repoUrl, filepath.Join(ConfigFolderPath, RepoLocation))
cmd := exec.Command("git", "clone", "-q", repoUrl, filepath.Join(structs.ConfigFolderPath, structs.RepoLocation))
return cmd.Run()

}
Expand All @@ -40,23 +41,43 @@ func GetPrivateRepo() {
if !validated {
log.Fatal("invalid git url. make sure it's the ssh url and the url is correct.")
}
file, err := os.Create(filepath.Join(ConfigFolderPath, RepoFileName))
file, err := os.Create(filepath.Join(structs.ConfigFolderPath, structs.RepoFileName))
if err != nil {
log.Fatal("failed to create .repo file. ", err)
}
defer file.Close()

_, err = file.WriteString(input)
if err != nil {
os.Remove(filepath.Join(ConfigFolderPath, RepoFileName))
os.Remove(filepath.Join(structs.ConfigFolderPath, structs.RepoFileName))
log.Fatal("failed to write to .repo file. ", err)
}
fmt.Println("Starting to clone the repo.")
err = ClonePrivateRepo(input)
if err != nil {
os.Remove(filepath.Join(ConfigFolderPath, RepoFileName))
os.Remove(filepath.Join(ConfigFolderPath, RepoLocation))
os.Remove(filepath.Join(structs.ConfigFolderPath, structs.RepoFileName))
os.Remove(filepath.Join(structs.ConfigFolderPath, structs.RepoLocation))
log.Fatal("failed to clone repo. make sure repo url is correct. ", err)
}
fmt.Println("Cloning successful.")
}

func createCommitMasterString(commits []structs.Commit) string {
var masterString = ""
for _, commit := range commits {
masterString = masterString + commit.Hash + " "
}
return masterString
}

func FilterSyncedCommits(allCommits []structs.Commit, syncedCommits []structs.Commit) []structs.Commit {
var commitsToSync []structs.Commit
masterString := createCommitMasterString(syncedCommits)
for _, commit := range allCommits {
if strings.Contains(masterString, commit.Hash) {
continue
}
commitsToSync = append(commitsToSync, commit)
}
return commitsToSync
}
Loading