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
6 changes: 4 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"name": "Go",
"image": "mcr.microsoft.com/devcontainers/go:2-1.24-bookworm",
"image": "mcr.microsoft.com/devcontainers/go:2-1.25-trixie",
"features": {
"ghcr.io/guiyomh/features/golangci-lint:0": {}
},
"customizations": {
"vscode": {
"extensions": [
"eamodio.gitlens"
"eamodio.gitlens",
"streetsidesoftware.code-spell-checker",
"esbenp.prettier-vscode"
]
}
}
Expand Down
16 changes: 15 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,24 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Setup NodeJS
uses: actions/setup-node@v6
with:
node-version: 24.x

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24'
go-version: '1.25'

- name: Sanity check
run: make sanity-check

- name: Lint
uses: golangci/golangci-lint-action@v9
with:
version: 'v2.5.0'
args: --timeout=5m --verbose

- name: Build
run: make build
Expand Down
14 changes: 6 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,16 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.24
go-version: 1.25

- name: Sanity check
run: make sanity-check

- name: Build binary
run: |
mkdir -p dist
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o dist/backup-${{ matrix.goos }}-${{ matrix.goarch }} ./backup/main.go
run: make release-${{ matrix.goos }}-${{ matrix.goarch }}

- name: Generate SHA256 checksum
run: |
for file in dist/*; do
sha256sum "$file" > "$file.sha256";
done
run: make checksums

- name: Upload binaries
uses: actions/upload-artifact@v4
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
BACKLOG/
logs/
*.log
dist/backup
dist/
49 changes: 49 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
version: "2"
linters:
default: all
disable:
- wsl # The linter 'wsl' is deprecated (since v2.2.0) due to: new major version. Replaced by wsl_v5
- paralleltest # not used in this project
- revive # temporarily disable
- exhaustruct # temporarily disable
- errcheck
- forbidigo
- gocritic
settings:
depguard:
rules:
main:
list-mode: strict
allow:
- bytes
- context
- errors
- fmt
- io
- log
- os
- path/filepath
- sort
- strings
- testing
- time
- github.com/spf13/cobra
- github.com/spf13/afero
- gopkg.in/yaml.v3
- backup-rsync/backup/internal
- backup-rsync/backup/cmd
errcheck:
check-type-assertions: true
check-blank: true
gosec:
excludes:
- G304 # ignore Potential file inclusion via variable
varnamelen:
ignore-decls:
- fs afero.Fs
formatters:
enable:
- gofmt
settings:
gofmt:
simplify: true
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"cSpell.words": [
"afero",
"golangci",
"gofmt",
"subpaths"
]
}
67 changes: 60 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,69 @@
# Makefile to build the project and place the binary in the dist/ directory

.PHONY: build clean deps test
# Build command with common flags
BUILD_CMD = CGO_ENABLED=0 go build -ldflags="-s -w"
PACKAGE = ./backup/main.go

deps:
go mod tidy
.PHONY: build clean test lint tidy checksums release sanity-check check-mod-tidy lint-config-check lint-fix format check-clean

build: deps
@mkdir -p dist
go build -o dist/backup ./backup/main.go
format:
go fmt ./...
@echo "OK: Code formatted."

lint-config-check:
@golangci-lint config path
@golangci-lint config verify
@echo "OK: Lint configuration is valid."

lint:
golangci-lint run ./...

lint-fix:
golangci-lint run --fix ./...

check-clean:
@git diff --quiet || (echo "ERROR: Working directory has uncommitted changes." && exit 1)
@echo "OK: Working directory is clean."

check-mod-tidy:
go mod tidy -diff
@echo "OK: No untidy module files detected."

sanity-check: format check-clean check-mod-tidy
@echo "OK: All sanity checks passed."

test:
go test ./... -v

tidy:
go mod tidy

build:
@mkdir -p dist
$(BUILD_CMD) -o dist/backup $(PACKAGE)

clean:
rm -rf dist
rm -rf dist

# Build for specific OS and architecture (e.g., make release-linux-amd64)
release-%:
@mkdir -p dist
GOOS=$(word 1,$(subst -, ,$*)) GOARCH=$(word 2,$(subst -, ,$*)) $(BUILD_CMD) -o dist/backup-$* $(PACKAGE)

checksums:
@for file in dist/*; do \
if [ "$${file##*.}" != "sha256" ]; then \
sha256sum "$$file" > "$$file.sha256"; \
fi; \
done

release: release-linux-amd64 release-darwin-amd64 release-windows-amd64 checksums
@echo
@echo "Binaries with sizes and checksums:"
@for file in dist/*; do \
if [ -f "$$file" ] && [ "$${file##*.}" != "sha256" ]; then \
size=$$(stat --printf="%s" "$$file"); \
checksum=$$(cat "$$file.sha256" | awk '{print $$1}'); \
printf "%-40s %-15s %-64s\n" "$$file" "Size: $$size bytes" "Checksum: $$checksum"; \
fi; \
done
71 changes: 39 additions & 32 deletions backup/cmd/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,32 @@ import (
"github.com/spf13/cobra"
)

const filePermission = 0644
const logDirPermission = 0755

func getLogPath(create bool) string {
logPath := fmt.Sprintf("logs/sync-%s", time.Now().Format("2006-01-02T15-04-05"))
logPath := "logs/sync-" + time.Now().Format("2006-01-02T15-04-05")
if create {
if err := os.MkdirAll(logPath, 0755); err != nil {
err := os.MkdirAll(logPath, logDirPermission)
if err != nil {
log.Fatalf("Failed to create log directory: %v", err)
}
}

return logPath
}

func executeSyncJobs(cfg internal.Config, simulate bool) {
logPath := getLogPath(true)

overallLogPath := fmt.Sprintf("%s/summary.log", logPath)
overallLogFile, err := os.OpenFile(overallLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
overallLogPath := logPath + "/summary.log"

overallLogFile, err := os.OpenFile(overallLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, filePermission)
if err != nil {
log.Fatalf("Failed to open overall log file: %v", err)
}
defer overallLogFile.Close()

overallLogger := log.New(overallLogFile, "", log.LstdFlags)

for _, job := range cfg.Jobs {
Expand All @@ -48,35 +55,35 @@ func listCommands(cfg internal.Config) {
}
}

var runCmd = &cobra.Command{
Use: "run",
Short: "Execute the sync jobs",
Run: func(cmd *cobra.Command, args []string) {
cfg := internal.LoadResolvedConfig(configPath)
executeSyncJobs(cfg, false)
},
}
func AddBackupCommands(rootCmd *cobra.Command, configPath string) {
var runCmd = &cobra.Command{
Use: "run",
Short: "Execute the sync jobs",
Run: func(cmd *cobra.Command, args []string) {
cfg := internal.LoadResolvedConfig(configPath)
executeSyncJobs(cfg, false)
},
}

var simulateCmd = &cobra.Command{
Use: "simulate",
Short: "Simulate the sync jobs",
Run: func(cmd *cobra.Command, args []string) {
cfg := internal.LoadResolvedConfig(configPath)
executeSyncJobs(cfg, true)
},
}
var simulateCmd = &cobra.Command{
Use: "simulate",
Short: "Simulate the sync jobs",
Run: func(cmd *cobra.Command, args []string) {
cfg := internal.LoadResolvedConfig(configPath)
executeSyncJobs(cfg, true)
},
}

var listCmd = &cobra.Command{
Use: "list",
Short: "List the commands that will be executed",
Run: func(cmd *cobra.Command, args []string) {
cfg := internal.LoadResolvedConfig(configPath)
listCommands(cfg)
},
}
var listCmd = &cobra.Command{
Use: "list",
Short: "List the commands that will be executed",
Run: func(cmd *cobra.Command, args []string) {
cfg := internal.LoadResolvedConfig(configPath)
listCommands(cfg)
},
}

func init() {
RootCmd.AddCommand(runCmd)
RootCmd.AddCommand(simulateCmd)
RootCmd.AddCommand(listCmd)
rootCmd.AddCommand(runCmd)
rootCmd.AddCommand(simulateCmd)
rootCmd.AddCommand(listCmd)
}
34 changes: 18 additions & 16 deletions backup/cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ import (
"github.com/spf13/cobra"
)

var AppFs = afero.NewOsFs()

var checkCmd = &cobra.Command{
Use: "check-coverage",
Short: "Check path coverage",
Run: func(cmd *cobra.Command, args []string) {
cfg := internal.LoadResolvedConfig(configPath)
uncoveredPaths := internal.ListUncoveredPaths(AppFs, cfg)
fmt.Println("Uncovered paths:")
for _, path := range uncoveredPaths {
fmt.Println(path)
}
},
}
func AddCheckCommands(rootCmd *cobra.Command, configPath string) {
var fs = afero.NewOsFs()

var checkCmd = &cobra.Command{
Use: "check-coverage",
Short: "Check path coverage",
Run: func(cmd *cobra.Command, args []string) {
cfg := internal.LoadResolvedConfig(configPath)
uncoveredPaths := internal.ListUncoveredPaths(fs, cfg)

fmt.Println("Uncovered paths:")

for _, path := range uncoveredPaths {
fmt.Println(path)
}
},
}

func init() {
RootCmd.AddCommand(checkCmd)
rootCmd.AddCommand(checkCmd)
}
Loading