Skip to content

Commit f3091c1

Browse files
committed
auto commit to github
1 parent 6e0c49b commit f3091c1

File tree

7 files changed

+185
-26
lines changed

7 files changed

+185
-26
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
api.sh
22
*.cpp
3+
notes.txt
34

45
# Binaries for programs and plugins
56
*.exe

README.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
solve cses.fi problemset via command line. Inspired from awesome [leetcode-cli](https://github.com/skygragon/leetcode-cli).
33

44

5+
## How to use cses-cli
56
* Download the required binary from [https://github.com/ketankr9/cses-cli/releases](https://github.com/ketankr9/cses-cli/releases)
67
* Install [lynx](https://www.google.com/search?q=install+lynx+&oq=install+lynx). For ubuntu use ```sudo apt install lynx```
78
* Rename the binary to cses-cli and move it to PATH
@@ -13,8 +14,18 @@ cses-cli show 1742
1314
cses-cli solve 1742
1415
cses-cli submit 1742.task.cpp
1516
```
17+
## Auto Commit to your GitHub Repository (if needed)
18+
* Obtain access token for your repo. See [how to get token](https://github.com/skygragon/leetcode-cli-plugins/blob/master/docs/github.md#generate-token)
19+
* Configure cses-cli for github as follows
20+
```
21+
$$$ cses-cli github
22+
Token: 1f10d6065e78a2654a14xxxxxxxxxxxxxxxxxxxx
23+
Repository: cses-solutions
24+
Github Username: ketankr9
25+
Github Email: [email protected]
26+
```
1627

17-
**Example**
28+
## Screenshot
1829
```
1930
$$$ cses-cli login
2031
Username: test123xyz
@@ -97,11 +108,12 @@ Submission time:2020-03-07 13:56:29
97108
Language:C++17
98109
Status:READY
99110
Result:ACCEPTED
111+
Comitting to Github[===> ] ✔
100112
```
101113

102114
>I will add support for these features only if people show some love to this repo since current commit suffices my need.
103115
104116
* Supports only C++ currently, will add support for other languages on request.
105117
* A modifiable template code file.
106-
* Auto commit to Github repository
118+
* Auto commit to Github repository **UPDATE: Done**
107119
* Clean LaTex based $equation$ from problem statement. Eg $1 ≤ n ≤ 10^6$

github.go

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io/ioutil"
7+
"path/filepath"
8+
"time"
9+
10+
"github.com/google/go-github/github"
11+
"golang.org/x/oauth2"
12+
)
13+
14+
type githubConfig struct {
15+
Token string `json:"token"`
16+
SourceRepo string `json:"repository"`
17+
AuthorName string `json:"username"`
18+
AuthorEmail string `json:"email"`
19+
}
20+
21+
var client *github.Client
22+
var ctx = context.Background()
23+
24+
func getTree(ref *github.Reference, opts *githubConfig, sourceFiles string) (tree *github.Tree, err error) {
25+
content, err := ioutil.ReadFile(sourceFiles)
26+
if err != nil {
27+
fmt.Println(err)
28+
return nil, err
29+
}
30+
31+
entries := []*github.TreeEntry{
32+
{
33+
Path: github.String(filepath.Base(sourceFiles)),
34+
Type: github.String("blob"),
35+
Content: github.String(string(content)),
36+
Mode: github.String("100644"),
37+
},
38+
}
39+
40+
tree, _, err = client.Git.CreateTree(ctx, opts.AuthorName, opts.SourceRepo, *ref.Object.SHA, entries)
41+
return tree, err
42+
}
43+
44+
func pushCommit(ref *github.Reference, tree *github.Tree, opts *githubConfig) (err error) {
45+
46+
parent, _, err := client.Repositories.GetCommit(ctx, opts.AuthorName, opts.SourceRepo, *ref.Object.SHA)
47+
if err != nil {
48+
return err
49+
}
50+
parent.Commit.SHA = parent.SHA
51+
52+
commitMessage := "file updated"
53+
date := time.Now()
54+
author := &github.CommitAuthor{Date: &date, Name: &opts.AuthorName, Email: &opts.AuthorEmail}
55+
commit := &github.Commit{Author: author, Message: &commitMessage, Tree: tree, Parents: []*github.Commit{parent.Commit}}
56+
newCommit, _, err := client.Git.CreateCommit(ctx, opts.AuthorName, opts.SourceRepo, commit)
57+
if err != nil {
58+
return err
59+
}
60+
61+
ref.Object.SHA = newCommit.SHA
62+
_, _, err = client.Git.UpdateRef(ctx, opts.AuthorName, opts.SourceRepo, ref, false)
63+
return err
64+
}
65+
66+
func updateFile(sourceFiles string, opts *githubConfig) bool {
67+
68+
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: opts.Token})
69+
tc := oauth2.NewClient(ctx, ts)
70+
client = github.NewClient(tc)
71+
72+
ref, _, err := client.Git.GetRef(ctx, opts.AuthorName, opts.SourceRepo, "refs/heads/master")
73+
if err != nil {
74+
return false
75+
}
76+
if ref == nil {
77+
return false
78+
}
79+
80+
tree, err := getTree(ref, opts, sourceFiles)
81+
if err != nil {
82+
return false
83+
}
84+
85+
if err := pushCommit(ref, tree, opts); err != nil {
86+
return false
87+
}
88+
return true
89+
}

helper.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bufio"
55
"bytes"
66
"encoding/json"
7+
"fmt"
78
"io"
89
"io/ioutil"
910
"mime/multipart"
@@ -21,6 +22,10 @@ func check(e error) {
2122
}
2223
}
2324

25+
func validGithubConfig(opts *githubConfig) bool {
26+
return opts.Token != "" && opts.SourceRepo != "" && opts.AuthorName != "" && opts.AuthorEmail != ""
27+
}
28+
2429
func UserHomeDir() string {
2530
if runtime.GOOS == "windows" {
2631
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
@@ -33,11 +38,24 @@ func UserHomeDir() string {
3338
}
3439

3540
func updateConfig(sess *Session) {
36-
out, err := json.Marshal(sess)
41+
out, err := json.MarshalIndent(sess, "", " ")
3742
check(err)
3843
cacheSet("login.json", string(out), sess.Root)
3944
}
4045

46+
func updateIfNew(scanner *bufio.Scanner, src *string, text string) {
47+
if *src == "" {
48+
fmt.Print(text + ": ")
49+
} else {
50+
fmt.Print(text + "(" + *src + "): ")
51+
}
52+
scanner.Scan()
53+
val := scanner.Text()
54+
if val != "" {
55+
*src = val
56+
}
57+
}
58+
4159
func cacheSet(filename string, data string, root string) {
4260
f, err := os.Create(filepath.Join(root, filename))
4361
check(err)

main.go

+47-15
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ cses result 1068
3131
// optional
3232
cses stat 1068
3333
*/
34+
3435
type Session struct {
35-
Csrf string `json:"csrfToken"`
36-
User string `json:"username"`
37-
Cookie string `json:"cookie"`
38-
Root string `json:"root"`
39-
Editor string `json:"editor"`
36+
Csrf string `json:"csrf"`
37+
User string `json:"username"`
38+
Cookie string `json:"cookie"`
39+
Root string `json:"root"`
40+
Editor string `json:"editor"`
41+
Github githubConfig `json:"github"`
4042
}
4143

4244
var cpptemplate = `
@@ -78,9 +80,7 @@ func login(sess *Session, pass string) bool {
7880
func promtLogin(sess *Session) bool {
7981
scanner := bufio.NewScanner(os.Stdin)
8082

81-
fmt.Print("Username: ")
82-
scanner.Scan()
83-
sess.User = scanner.Text()
83+
updateIfNew(scanner, &sess.User, "Username")
8484

8585
fmt.Print("Password: ")
8686
scanner.Scan()
@@ -134,22 +134,26 @@ func list(sess *Session) {
134134

135135
}
136136

137-
func printResult(link string, sess *Session) {
137+
func printResult(link string, sess *Session) bool {
138138

139139
s := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
140140
s.Prefix = "PENDING "
141141
s.Start()
142142
defer s.Stop()
143143

144144
for true {
145-
status, text := printResultRequest(link, sess.Cookie)
145+
status, text, verdict := printResultRequest(link, sess.Cookie)
146146
s.Prefix = status + " "
147147

148148
if status == "READY" || status == "" {
149-
fmt.Println("\n" + text)
149+
fmt.Print("\n" + text)
150+
if verdict == "ACCEPTED" {
151+
return true
152+
}
150153
break
151154
}
152155
}
156+
return false
153157
}
154158

155159
func submit(filename string, sess *Session) {
@@ -170,7 +174,18 @@ func submit(filename string, sess *Session) {
170174
}
171175

172176
link := submitRequest(opts, filename, sess.Cookie)
173-
printResult(link, sess)
177+
178+
if verdict := printResult(link, sess); verdict && validGithubConfig(&sess.Github) {
179+
s := spinner.New(spinner.CharSets[36], 100*time.Millisecond)
180+
s.Prefix = "Comitting to Github"
181+
s.Start()
182+
defer s.Stop()
183+
if ok := updateFile(filename, &sess.Github); ok {
184+
fmt.Println("✔")
185+
} else {
186+
fmt.Println("✘")
187+
}
188+
}
174189
}
175190

176191
func getTask(task string, sess *Session) (string, bool) {
@@ -217,15 +232,29 @@ func solve(task string, sess *Session) {
217232

218233
if sess.Editor == "" {
219234
scanner := bufio.NewScanner(os.Stdin)
220-
fmt.Print("Editor: ")
221-
scanner.Scan()
222-
sess.Editor = scanner.Text()
223235

236+
updateIfNew(scanner, &sess.Editor, "Editor")
237+
238+
if sess.Editor == "" {
239+
fmt.Println("Editor still not configured")
240+
return
241+
}
224242
updateConfig(sess)
225243
}
226244
exec.Command(sess.Editor, filename).Output()
227245
}
228246

247+
func configureGithub(sess *Session) {
248+
scanner := bufio.NewScanner(os.Stdin)
249+
250+
updateIfNew(scanner, &sess.Github.Token, "Token")
251+
updateIfNew(scanner, &sess.Github.SourceRepo, "Repository")
252+
updateIfNew(scanner, &sess.Github.AuthorName, "Github Username")
253+
updateIfNew(scanner, &sess.Github.AuthorEmail, "Github Email")
254+
255+
updateConfig(sess)
256+
}
257+
229258
func stat(task string) {
230259
fmt.Println("#Todo")
231260
}
@@ -239,6 +268,7 @@ func main() {
239268
}
240269

241270
sess := &Session{}
271+
242272
isLogged := initSess(sess)
243273

244274
switch flag.Arg(0) {
@@ -270,6 +300,8 @@ func main() {
270300
return
271301
}
272302
submit(flag.Arg(1), sess)
303+
case "github":
304+
configureGithub(sess)
273305

274306
case "stat":
275307
fmt.Println("sw stat 1068")

requests.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func listRequest(cookie string) io.ReadCloser {
8383
return resp.Body
8484
}
8585

86-
func printResultRequest(link string, cookie string) (string, string) {
86+
func printResultRequest(link string, cookie string) (string, string, string) {
8787
req, err := http.NewRequest("GET", "https://cses.fi"+link, nil)
8888
check(err)
8989

@@ -99,7 +99,9 @@ func printResultRequest(link string, cookie string) (string, string) {
9999
status := doc.Find("#status").Text()
100100
text := doc.Find(".summary-table > tbody:nth-child(2)").Contents().Text()
101101

102-
return status, text
102+
verdict := doc.Find(".summary-table > tbody:nth-child(2) > tr:nth-child(6) > td:nth-child(2) > span:nth-child(1)").Contents().Text()
103+
104+
return status, text, verdict
103105
}
104106

105107
func downloadTask(task string) string {

test.sh

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
#!/bin/bash
22

3+
TASK="1068"
4+
35
echo "[*] rm -rf ~/.cses/"
46
rm -rf ~/.cses/
57

68
echo "[*] go run ./*.go login"
79
go run ./*.go login
810

11+
echo "[*] go run ./*.go github"
12+
go run ./*.go github
13+
914
echo "[*] go run ./*.go list"
1015
go run ./*.go list
1116

12-
echo "[*] go run ./*.go show 1742"
13-
go run ./*.go show 1742
17+
echo "[*] go run ./*.go show ${TASK}"
18+
go run ./*.go show "${TASK}"
1419

15-
echo "[*] go run ./*.go solve 1742"
16-
go run ./*.go solve 1742
20+
echo "[*] go run ./*.go solve ${TASK}"
21+
go run ./*.go solve "${TASK}"
1722

18-
echo "[*] go run ./*.go submit 1742.task.cpp"
19-
go run ./*.go submit 1742.task.cpp
23+
echo "[*] go run ./*.go submit ${TASK}.task.cpp"
24+
go run ./*.go submit "${TASK}".task.cpp

0 commit comments

Comments
 (0)