Skip to content

Commit

Permalink
Complete overhaul with GitHub Classroom API and BubbleTea library int…
Browse files Browse the repository at this point in the history
…egration
  • Loading branch information
emersonmello committed Sep 13, 2024
1 parent d2af1df commit fecb1ee
Show file tree
Hide file tree
Showing 45 changed files with 2,136 additions and 1,105 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ go.work
/vendor/
/Godeps/

.vscode/
.vscode/
.idea/
/claro
39 changes: 39 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
before:
hooks:
- go mod tidy

builds:
- env:
- CGO_ENABLED=0
goos:
- freebsd
- linux
- windows
- darwin
goarch:
- amd64
- arm64
- arm
- "386"
goarm:
- 6
- 7
ldflags:
- -s -w
- -X github.com/emersonmello/claro/cmd.Version={{.Version}}
- -X github.com/emersonmello/claro/cmd.Commit={{.Commit}}
- -X github.com/emersonmello/claro/cmd.Date={{.CommitDate}}

archives:
- format: binary
name_template: "{{ .Os }}-{{ .Arch }}"
checksum:
name_template: "checksums.txt"
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 Emerson Ribeiro de Mello
Copyright (c) 2022-2024 Emerson Ribeiro de Mello <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
141 changes: 34 additions & 107 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,145 +9,72 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/emersonmello/claro)](https://goreportcard.com/report/github.com/emersonmello/claro)
[![Compiling](https://github.com/emersonmello/claro/actions/workflows/release.yaml/badge.svg)](https://github.com/emersonmello/claro/actions/workflows/release.yaml)
[![Plataform](https://img.shields.io/badge/Download%20binaries%20for-Linux%20%7C%20macOS-lightgreen)](https://github.com/emersonmello/claro/releases/latest)
# Overview

**claro** (**cla**ss**ro**om) is a cli tool that offers a simple interface that allows the teacher to clone all student repositories at once for grading and then send grades at once to all these repositories.

**claro** was inspired by the [Git-Gud-tool](https://github.com/NikolaiMagnussen/Git-Gud-tool) and it was created to make my life simpler as a teacher who relies on Github Classroom. It is suitable for scenarios where the teacher needs to manually grade each assignment and the Github Classroom autograding is not an option.

**claro** relies on [Github's REST API](https://docs.github.com/en/rest) because currently Github Classroom does not offer an API and it has features that are not present in [Github Classroom Assistant](https://classroom.github.com/assistant). The best one: it is a [CLI](https://clig.dev) :heart:!

# Overview

**claro** provides:
- mass clone of Github repositories
- a template in Markdown for grading (it creates one file per repository)
- customization (commit message, grade sheet title, grading file, etc.)
- access to [Github Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) from operating system keyring (i.e. macOS Keychain, Gnome Keyring), environment var or claro's config file

## Requirements
- [git](https://git-scm.com/docs/git) command line application configured properly
```bash
git config --global user.email "[email protected]"
git config --global user.name "Your Name"
```
- [Git credential store](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) configured properly
- See [section below](#authenticating-with-git-command-line-to-access-repositories-on-github)
**claro** (**cla**ss**ro**om) is a [CLI](https://clig.dev) tool designed to simplify the grading process for educators using [GitHub Classroom](classroom.github.com), especially in cases where manual grading is necessary and autograding is not an option. **claro** is an alternative to the [GitHub Classroom Assistant](https://classroom.github.com/assistant) and [Github CLI](https://cli.github.com), which currently lacks bulk processing features for grading. With claro, you can efficiently clone all student repositories, grade them, and push the grades back.

**claro** offers:
- Bulk cloning and pushing of student assignment repositories
- Automatic generation of a Markdown grading template (one file per repository)
- Customization options (commit messages, grade sheet titles, grading files, etc.)
- Integration with [Github Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) via your operating system’s keyring (e.g., macOS Keychain, Gnome Keyring)

## Usage
![claro help message](images/claro.png)

### Straight workflow
## Straight workflow

1. Create a Github Classroom assignment with a distinguish repository prefix
- Example: `2022-01-assignment-01`
2. Clone all repositories from a Github Classroom organization with a specific assignment prefix
- Example: `claro clone github-organization 2022-01-assignment-01`
1. Clone all repositories from a Github Classroom assignment
- Example: `claro clone`

![cloning](images/clone.gif)

3. Grade each student's work and write down the feedback in the respective Markdown file
2. Grade each student's work and write down the feedback in the respective Markdown file
- Tip: Use your best Markdown editor for this

![grading](images/grading.gif)

4. Push the grading
- Example: `claro push 2022-01-assignment-01`
3. Upload student grades to GitHub
- Example: `claro push <directory-with-student-submissions>`

![pushing](images/push.gif)

## Other commands
## Additional commands

![claro help message](images/claro.png)
### Pulls the latest changes from all student repositories

- Example: `claro pull <directory-with-student-submissions>`

### Pull students repositories
![pulling](images/pull.gif)

**claro** offers a `pull` command suitable for workflows where students make incremental deliveries to the same GitHub Classroom repository.
### Add a GitHub Personal Access Token to the operating system keyring

- Example:
```bash
claro pull 2022-01-assignment-01
```
### List all repositories which start with a specific prefix
- Example: `claro token add`

Before cloning multiple repositories, you might want to check which repositories will be cloned with a specific prefix. **claro** offers the `list` command for that.
### Remove a GitHub Personal Access Token from the operating system keyring

- Example:
```bash
claro list 2022-01-assignment-01
```
- Example: `claro token del`

### Customize commit message, grading filename, grading string

You can customize the commit message, grading filename, and grading string using the `config` command. The new values will be stored in the **claro**'s config file (default `$HOME/.config/claro/config.env`).

The **claro** default strings are:

- **Grading filename:** `GRADING.md`
- It will be created and pushed to student repository
- **Grade string:** `Grade: `
- It will be inside grading file
- **Commit message:** `Graded project, the file containing the grade is in the root directory`
- **Commit message:** `This project has been graded. The file containing the grade is located in the root directory.`
- It is the commit message
- **Grade sheet title** `Feedback`
- It will be inside grading file as title 1 (# Feedback)

You can change the values using **claro** `config` command. The new values will be stored in the **claro**'s config file (default `$HOME/.claro.env`).
- Examples:
- `claro config filename "Correcao.md"`
- `claro config grade "Nota: "`
- `claro config message "Correção finalizada, veja arquivo na raiz do repositório"`
- `claro config title "Comentários"`

## Storing claro's GitHub Personal Access Token in the OS keyring

**claro** only supports HTTPS remote URL (git over SSH is so [annoying](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-authentication-to-github#authenticating-with-the-command-line)).

**claro** consumes [Github's REST API](https://docs.github.com/en/rest) to fetch the list of repositories (assignments) from a GitHub Classroom organization. So, **claro** uses a [GitHub Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
- **claro** will try to get GitHub Personal Access Token from: (1) operating system keyring; (2) environment var (`GH_TOKEN`); (3) **claro**'s config file (default `$HOME/.claro.env`)

You can inform the token whenever you clone repositories, or you can save the token in the operating system keyring (recommended) or in the **claro**'s config file. Please, have a look at [Authenticating with git command line to access repositories on GitHub](#authenticating-with-git-command-line-to-access-repositories-on-github).

- To save the token (**claro** will try to save to the OS keyring first and then to the config file.)
- `claro config token add`
- To delete the token from OS keyring
- `claro config token del`

## Authenticating with git command line to access repositories on GitHub



> Using an HTTPS remote URL has some advantages compared with using SSH. It's easier to set up than SSH, and usually works through strict firewalls and proxies. However, it also prompts you to enter your GitHub credentials every time you pull or push a repository. ([GitHub Docs](https://docs.github.com/en/get-started/getting-started-with-git/why-is-git-always-asking-for-my-password)).
To access repositories on GitHub from the command line application over HTTPS you must authenticate with a [GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-authentication-to-github#authenticating-with-the-command-line).

You can avoid being prompted for your password (personal access token) by configuring Git to cache your credentials for you on [git credential storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). Git works with several credential helper:

- **Unsafe and simple way** (not recommended!)
- This method stores your password in plaintext on disk, protected only by filesystem permissions (default `$HOME/.git-credentials`)
- on Linux, macOS or Windows
```bash
git config --global credential.helper store
```
- The first time git will ask the username and password. Subsequent request will use the credentials from the store (default `$HOME/.git-credentials`).
- Example:
`https://username:[email protected]`
- **Safe with a little complexity**
- This method stores your password encrypted on disk in the operating system keyring service.
- You can use native operating system keyring
- Linux [Secret Service](https://specifications.freedesktop.org/secret-service/latest/) dbus interface, which is provided by [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring)
- Use [Seahorse app](https://wiki.gnome.org/Apps/Seahorse) to create a default collection with `login` name
- Open `seahorse`; go to `file->new->password keyring`; when asked for a name, use: `login`

```bash
# ------------------------------------------------#
# on Ubuntu Linux 22.04 LTS
# ------------------------------------------------#
sudo apt-get install libsecret-1-0 libsecret-1-dev g++ make
cd /usr/share/doc/git/contrib/credential/libsecret && sudo make
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret
# ------------------------------------------------#
```
- macOS Keychain (`/usr/bin/security`)
- `git config --global credential.helper osxkeychain`
- Multi-plataform (Linux, macOS or Windows)
- You can also use a credential helper like [Git Credential Manager](https://github.com/GitCredentialManager/git-credential-manager)
![alt text](images/config.gif)


## GitHub Personal Access Token

**claro** uses Git to clone, pull, and push repositories. To streamline this process, **claro** will prompt you for your GitHub credentials each time you perform these actions. This can be tedious, especially when handling multiple repositories. To avoid repeated prompts, **claro** will check if [Git's credential storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) is configured on its first run. If not, it will set up Git to store credentials in cache (e.g., `git config --global credential.helper cache`). You will be asked for your GitHub Personal Access Token only once, and it will remain cached until the cache expires.

**claro** also requires a [GitHub Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) to access the [Github's Classroom API](https://docs.github.com/en/rest/classroom?apiVersion=2022-11-28) when cloning repositories. This token is not stored in Git's credential storage, so it is best kept in your operating system's keyring. You can store it using the token add command. Once saved, you won’t need to re-enter it each time you use **claro**. For instructions on how to use your operating system's keyring, [see this guide](https://git-scm.com/doc/credential-helpers).
78 changes: 0 additions & 78 deletions cmd/clone.go

This file was deleted.

33 changes: 33 additions & 0 deletions cmd/clone/clone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Package clone
package clone

/*
Copyright © 2022-2024 Emerson Ribeiro de Mello <[email protected]>
*/

import (
"fmt"

tea "github.com/charmbracelet/bubbletea"
"github.com/emersonmello/claro/internal"
"github.com/emersonmello/claro/internal/tui"
"github.com/spf13/cobra"
)

// Clone represents the clone command
func Clone() *cobra.Command {
cloneCmd := &cobra.Command{
Use: "clone",
Short: "Clone all students assignments from a GitHub Classroom",
RunE: func(cmd *cobra.Command, args []string) error {
if !tui.GitHubCliInstalled {
tui.UserGitHubPAT = internal.GetAndSaveToken()
}
if _, err := tea.NewProgram(internal.NewCloneModel()).Run(); err != nil {
fmt.Println("Error running program:", err)
}
return nil
},
}
return cloneCmd
}
15 changes: 0 additions & 15 deletions cmd/config.go

This file was deleted.

21 changes: 21 additions & 0 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Package config
package config

/*
Copyright © 2022-2024 Emerson Ribeiro de Mello <[email protected]>
*/

import (
"github.com/emersonmello/claro/internal"
"github.com/spf13/cobra"
)

// Config represents the clone command
func Config() *cobra.Command {
configCmd := &cobra.Command{
Use: "config",
Short: "Configure claro's properties (commit message, filename, etc)",
RunE: internal.ConfigCmd,
}
return configCmd
}
Loading

0 comments on commit fecb1ee

Please sign in to comment.