Skip to content

Commit 8bd847a

Browse files
authored
Merge pull request #51 from slashtechno/customizable-max-backups
Customizable max backups, Ntfy support, and concurrency
2 parents d3e1054 + aa12ccf commit 8bd847a

12 files changed

Lines changed: 305 additions & 41 deletions

File tree

.vscode/launch.json

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"request": "launch",
88
"mode": "auto",
99
"program": "${workspaceFolder}",
10-
"args": ["backup"],
10+
"args": ["backup", "--run-type", "${input:runType}"],
1111
"console": "integratedTerminal"
1212
},
1313
{
@@ -16,9 +16,30 @@
1616
"request": "launch",
1717
"mode": "auto",
1818
"program": "${workspaceFolder}",
19-
// "args": ["backup", "continuous", "--interval", "5s"],
20-
"args": ["backup", "continuous"],
19+
// "args": ["backup", "continuous", "--run-type", "${input:runType}", "--interval", "5s", "--max-backups", "5"],
20+
"args": ["backup", "continuous", "--run-type", "${input:runType}", "--interval", "${input:interval}", "--max-backups", "${input:maxBackups}"],
2121
"console": "integratedTerminal"
2222
},
2323
],
24+
"inputs": [
25+
{
26+
"id": "runType",
27+
"type": "pickString",
28+
"description": "Select the run type for continuous backup",
29+
"options": ["clone", "fetch", "dry-run"],
30+
"default": "full"
31+
},
32+
{
33+
"id": "interval",
34+
"type": "promptString",
35+
"description": "Enter the interval for continuous backup",
36+
"default": "5s"
37+
},
38+
{
39+
"id": "maxBackups",
40+
"type": "promptString",
41+
"description": "Enter the maximum number of backups to keep",
42+
"default": "2"
43+
}
44+
]
2445
}

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# gobackup-github
2-
[![Build on multiple platforms](https://github.com/slashtechno/gobackup-github/actions/workflows/go-build.yml/badge.svg)](https://github.com/slashtechno/gobackup-github/actions/workflows/go-build.yml)![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/slashtechno/gobackup-github)[![Create and publish a Docker image](https://github.com/slashtechno/gobackup-github/actions/workflows/docker.yml/badge.svg?branch=main)](https://github.com/slashtechno/gobackup-github/actions/workflows/docker.yml)
3-
Go program that utilizes the Github API to backup all a user's repositories, including repositories that have been stored. Multiple users can be backed up, including all members of a GitHub organization.
2+
[![GitHub Actions Build Workflow Status](https://img.shields.io/github/actions/workflow/status/slashtechno/gobackup-github/go-build.yml?style=for-the-badge&label=Build&labelColor=%2344cc11&color=%23555555)](https://github.com/slashtechno/gobackup-github/actions/workflows/go-build.yml) ![Static Badge](https://img.shields.io/badge/open-source-_?style=for-the-badge&labelColor=%23ef4041&color=%23c13a3a) [![GitHub Actions Docker Workflow Status](https://img.shields.io/github/actions/workflow/status/slashtechno/gobackup-github/docker.yml?style=for-the-badge&label=Docker%20Image%20Build)](https://github.com/slashtechno/gobackup-github/actions/workflows/docker.yml)
3+
4+
Go program that utilizes the Github API to backup a user's repositories, including repositories that have been starred. Multiple users can be backed up, including all members of a GitHub organization.
45
![Demo](demo.gif)
56

67
### Setup and Usage
78
1. Create a Github personal access token with the following scopes: `read:user, repo`
89
2. Either download a binary for your system from releases, or build the program and add it to your PATH with `go install`.
910
3. Copy `config.example.yaml` to `config.yaml` and fill in the fields.
1011
- In addition, command line flags can be used to specify configuration options. Use the `help` command or the `--help` flag for more information.
12+
- It is recommended to read through the `config.example.yaml` file to understand the configuration options.
1113
4. Run the program with `gobackup-github backup`
1214
- To perform a rolling backup, run `gobackup-github backup continuous`
1315

@@ -20,5 +22,5 @@ To create a container that runs on boot and performs a rolling backup every 24 h
2022
#### Docker Compose
2123
You can also just run `docker compose up -d` to start the container with automatic restarts, assuming you have a `docker-compose.yml` file in the same directory as the `config.yaml` file. You can also edit the `docker-compose.yml` file to change configuration and to manage the rolling backup, if needed.
2224

23-
### Why?
24-
I wanted a simple way to backup my Github repositories. I also wanted to learn Go and APIs. Thus, I began this project in 2022 as a way to create my first Go project, use the Github API, and make a utility to backup Github repositories.
25+
### Contributing
26+
Pull Requests are welcome!

cassette.tape

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
Output demo.gif
12
Type "gobackup-github backup"
23
Sleep 500ms
34
Enter
@@ -14,4 +15,4 @@ Enter
1415
Sleep 500ms
1516
Type "ls backup/slashtechno"
1617
Enter
17-
Sleep 5.5s
18+
Sleep 3.2s

cmd/backup.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@ var backupCmd = &cobra.Command{
2020
Run: func(cmd *cobra.Command, args []string) {
2121
err := backup.StartBackup(
2222
backup.BackupConfig{
23-
Usernames: internal.Viper.GetStringSlice("usernames"),
24-
InOrg: internal.Viper.GetStringSlice("in-org"),
25-
BackupStars: internal.Viper.GetBool("stars"),
26-
Token: internal.Viper.GetString("token"),
27-
Output: internal.Viper.GetString("output"),
28-
RunType: internal.Viper.GetString("run-type"),
23+
Usernames: internal.Viper.GetStringSlice("usernames"),
24+
InOrg: internal.Viper.GetStringSlice("in-org"),
25+
BackupStars: internal.Viper.GetBool("stars"),
26+
Token: internal.Viper.GetString("token"),
27+
Output: internal.Viper.GetString("output"),
28+
RunType: internal.Viper.GetString("run-type"),
29+
NtfyUrl: internal.Viper.GetString("ntfy-url"),
30+
RecurseSubmodules: internal.Viper.GetUint("recurse-submodules"),
2931
},
3032
// Pass an empty interval as this is a one-time backup
3133
"",
34+
0,
3235
)
3336
if err != nil {
3437
log.Error("Backup failed", "err", err)
@@ -67,8 +70,16 @@ func init() {
6770
internal.Viper.BindPFlag("backup-stars", backupCmd.PersistentFlags().Lookup("stars"))
6871
internal.Viper.SetDefault("backup-stars", false)
6972

73+
backupCmd.PersistentFlags().Uint("recurse-submodules", 0, "Recurse submodules")
74+
internal.Viper.BindPFlag("recurse-submodules", backupCmd.PersistentFlags().Lookup("recurse-submodules"))
75+
internal.Viper.SetDefault("recurse-submodules", false)
76+
7077
backupCmd.PersistentFlags().String("run-type", "", "`Type of backup: clone` (clone the repositories), `fetch` (fetch the repositories and write to output if it ends in .json or `repositories.json` in output), `dry-run` (fetch the repositories and print the output). Default is `clone`")
7178
internal.Viper.BindPFlag("run-type", backupCmd.PersistentFlags().Lookup("run-type"))
7279
internal.Viper.SetDefault("run-type", "clone")
7380

81+
backupCmd.PersistentFlags().String("ntfy-url", "", "Ntfy URL to send a notification to after backup")
82+
internal.Viper.BindPFlag("ntfy-url", backupCmd.PersistentFlags().Lookup("ntfy-url"))
83+
internal.Viper.SetDefault("ntfy-url", "")
84+
7485
}

cmd/continuous.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,21 @@ import (
1414
var continuousCmd = &cobra.Command{
1515
Use: "continuous --interval INTERVAL",
1616
Short: "Start a rolling backup that backs up repositories at a set interval",
17-
Long: `Start a rolling backup that backs up repositories at a set interval. The output directory will be emptied each time the backup is run.`,
17+
Long: `Start a rolling backup that backs up repositories at a set interval.`,
1818
Run: func(cmd *cobra.Command, args []string) {
1919
err := backup.StartBackup(
2020
backup.BackupConfig{
21-
Usernames: internal.Viper.GetStringSlice("usernames"),
22-
InOrg: internal.Viper.GetStringSlice("in-org"),
23-
BackupStars: internal.Viper.GetBool("stars"),
24-
Token: internal.Viper.GetString("token"),
25-
Output: internal.Viper.GetString("output"),
26-
RunType: internal.Viper.GetString("run-type"),
21+
Usernames: internal.Viper.GetStringSlice("usernames"),
22+
InOrg: internal.Viper.GetStringSlice("in-org"),
23+
BackupStars: internal.Viper.GetBool("stars"),
24+
Token: internal.Viper.GetString("token"),
25+
Output: internal.Viper.GetString("output"),
26+
RunType: internal.Viper.GetString("run-type"),
27+
NtfyUrl: internal.Viper.GetString("ntfy-url"),
28+
RecurseSubmodules: internal.Viper.GetUint("recurse-submodules"),
2729
},
28-
// Pass an empty interval as this is a one-time backup
2930
internal.Viper.GetString("interval"),
31+
internal.Viper.GetInt("max-backups"),
3032
)
3133
if err != nil {
3234
log.Error("Backup failed", "err", err)
@@ -41,4 +43,8 @@ func init() {
4143
internal.Viper.BindPFlag("interval", continuousCmd.Flags().Lookup("interval"))
4244
internal.Viper.SetDefault("interval", "24h")
4345

46+
continuousCmd.Flags().IntP("max-backups", "n", 0, "Number of backups to keep")
47+
internal.Viper.BindPFlag("max-backups", continuousCmd.Flags().Lookup("max-backups"))
48+
internal.Viper.SetDefault("max-backups", 1)
49+
4450
}

config.example.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ log-level: info
1212
output: backup
1313
# GitHub token with read access to the repositories and user
1414
token: ""
15-
# List of usernames to fetch. If neither usernames or in-org is set, it will fetch the token owner's repositories
15+
# List of usernames to fetch. If neither usernames or in-org are set (or an empty string is passed), the authenticated user will (also) be fetched. Fetching the authenticated user also fetches repositories shared with the authenticated user.
1616
usernames: []
1717
# `clone` (clone the repositories), `fetch` (fetch the repositories and write to output if it ends in .json or `repositories.json` in output), `dry-run` (fetch the repositories and print the output)
18-
run-type: clone
18+
run-type: clone
19+
# Ntfy URL to optionally send a notification to upon completion. If you don't want to use ntfy.sh, you can use a self-hosted instance of ntfy.
20+
ntfy-url: ""
21+
# Submodule depth to include. If set to 0 (default), submodules will not be initialized.
22+
recurse-submodules: 10

demo.gif

-119 KB
Loading

go.mod

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ require (
1919
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
2020
github.com/mattn/go-isatty v0.0.20 // indirect
2121
github.com/mattn/go-runewidth v0.0.16 // indirect
22+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
2223
github.com/muesli/termenv v0.15.2 // indirect
2324
github.com/rivo/uniseg v0.4.7 // indirect
25+
github.com/schollz/progressbar/v3 v3.14.6 // indirect
26+
golang.org/x/term v0.23.0 // indirect
2427
)
2528

2629
require (
@@ -34,6 +37,7 @@ require (
3437
github.com/fsnotify/fsnotify v1.7.0 // indirect
3538
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
3639
github.com/go-git/go-billy/v5 v5.5.0 // indirect
40+
github.com/go-resty/resty/v2 v2.14.0
3741
github.com/gofri/go-github-ratelimit v1.1.0
3842
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
3943
github.com/hashicorp/hcl v1.0.0 // indirect
@@ -58,7 +62,7 @@ require (
5862
golang.org/x/crypto v0.25.0 // indirect
5963
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
6064
golang.org/x/net v0.27.0 // indirect
61-
golang.org/x/sys v0.22.0 // indirect
65+
golang.org/x/sys v0.24.0 // indirect
6266
golang.org/x/text v0.16.0 // indirect
6367
gopkg.in/ini.v1 v1.67.0 // indirect
6468
gopkg.in/warnings.v0 v0.1.2 // indirect

0 commit comments

Comments
 (0)