Compare commits
7 Commits
50200abc0c
...
94d5e3d0f6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
94d5e3d0f6 | ||
![]() |
ecd6b0d4cf | ||
![]() |
b30b4f158c | ||
![]() |
376471cd3a | ||
![]() |
d1b6ae381c | ||
![]() |
5e772c118e | ||
![]() |
8c741b5b7c |
66
.github/workflows/sync.yaml
vendored
Normal file
66
.github/workflows/sync.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# GitHub Actions workflow file to sync an external repository to this GitHub mirror.
|
||||||
|
# This file was automatically generated by go-github-sync.
|
||||||
|
#
|
||||||
|
# The workflow does the following:
|
||||||
|
# - Runs on a scheduled basis (and can also be triggered manually)
|
||||||
|
# - Clones the GitHub mirror repository
|
||||||
|
# - Fetches changes from the primary external repository
|
||||||
|
# - Applies those changes to the mirror repository
|
||||||
|
# - Pushes the updated content back to the GitHub mirror
|
||||||
|
#
|
||||||
|
# Authentication is handled by the GITHUB_TOKEN secret provided by GitHub Actions.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Validate Github Actions Environment
|
||||||
|
run: if [ "$GITHUB_ACTIONS" != "true" ]; then echo 'This script must be run in a GitHub Actions environment.'; exit 1; fi
|
||||||
|
- name: Checkout GitHub Mirror
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Configure Git
|
||||||
|
run: |-
|
||||||
|
git config user.name 'GitHub Actions'
|
||||||
|
git config user.email 'actions@github.com'
|
||||||
|
- env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
name: Sync Primary Repository
|
||||||
|
run: |-
|
||||||
|
# Add the primary repository as a remote
|
||||||
|
git remote add primary https://i2pgit.org/go-i2p/go-github-sync.git
|
||||||
|
|
||||||
|
# Fetch the latest changes from the primary repository
|
||||||
|
git fetch primary
|
||||||
|
|
||||||
|
# Check if the primary branch exists in the primary repository
|
||||||
|
if git ls-remote --heads primary main | grep -q main; then
|
||||||
|
echo "Primary branch main found in primary repository"
|
||||||
|
else
|
||||||
|
echo "Error: Primary branch main not found in primary repository"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if we're already on the mirror branch
|
||||||
|
if git rev-parse --verify --quiet main; then
|
||||||
|
git checkout main
|
||||||
|
else
|
||||||
|
# Create the mirror branch if it doesn't exist
|
||||||
|
git checkout -b main
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Force-apply all changes from primary, overriding any conflicts
|
||||||
|
echo "Performing force sync from primary/main to main"
|
||||||
|
git reset --hard primary/main
|
||||||
|
|
||||||
|
|
||||||
|
# Push changes back to the mirror repository
|
||||||
|
git push origin main
|
||||||
|
name: Sync Primary Repository to GitHub Mirror
|
||||||
|
"on":
|
||||||
|
push: {}
|
||||||
|
schedule:
|
||||||
|
- cron: 0 * * * *
|
||||||
|
workflow_dispatch: {}
|
60
README.md
60
README.md
@@ -1,2 +1,60 @@
|
|||||||
# go-github-sync
|
# go-github-sync
|
||||||
Automatically set up github sync
|
|
||||||
|
A Go tool that generates and sets up GitHub Actions workflows to automatically sync external repositories to GitHub mirrors.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `go-github-sync` tool creates GitHub Actions workflows that periodically pull changes from a primary repository and push them to a GitHub mirror repository. It can either output the workflow YAML file or directly set it up in the target GitHub repository.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://github.com/go-i2p/go-github-sync.git
|
||||||
|
|
||||||
|
# Build the tool
|
||||||
|
cd go-github-sync
|
||||||
|
go build -o github-sync ./cmd/github-sync
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic usage
|
||||||
|
github-sync --primary https://example.org/repo.git --mirror https://github.com/user/repo
|
||||||
|
|
||||||
|
# Output workflow to file
|
||||||
|
github-sync --primary https://example.org/repo.git --mirror https://github.com/user/repo --output workflow.yml
|
||||||
|
|
||||||
|
# Setup workflow in GitHub repository
|
||||||
|
github-sync --primary https://example.org/repo.git --mirror https://github.com/user/repo --setup
|
||||||
|
```
|
||||||
|
|
||||||
|
### Command Line Options
|
||||||
|
|
||||||
|
- `--primary`, `-p`: Primary repository URL (required)
|
||||||
|
- `--mirror`, `-m`: GitHub mirror repository URL (required, auto-detected if possible)
|
||||||
|
- `--primary-branch`: Primary repository branch name (default: "main")
|
||||||
|
- `--mirror-branch`: GitHub mirror repository branch name (default: "main")
|
||||||
|
- `--interval`, `-i`: Sync interval - hourly, daily, weekly (default: "hourly")
|
||||||
|
- `--force`: Force sync by overwriting mirror with primary content (default: true)
|
||||||
|
- `--output`, `-o`: Output file for workflow YAML (default: ".github/workflows/sync.yaml")
|
||||||
|
- `--setup`: Automatically setup the workflow in the GitHub repository
|
||||||
|
- `--verbose`, `-v`: Enable verbose logging
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- GitHub token (needed when using `--setup` flag)
|
||||||
|
- Set via `GITHUB_TOKEN` or `GH_TOKEN` environment variable
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- github.com/google/go-github/v61
|
||||||
|
- github.com/spf13/cobra
|
||||||
|
- go.uber.org/zap
|
||||||
|
- golang.org/x/oauth2
|
||||||
|
- gopkg.in/yaml.v3
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License
|
@@ -4,6 +4,7 @@ package config
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -50,17 +51,16 @@ var (
|
|||||||
// AddFlags adds the configuration flags to the given command.
|
// AddFlags adds the configuration flags to the given command.
|
||||||
func AddFlags(cmd *cobra.Command) {
|
func AddFlags(cmd *cobra.Command) {
|
||||||
cmd.Flags().StringVarP(&primaryRepo, "primary", "p", "", "Primary repository URL (required)")
|
cmd.Flags().StringVarP(&primaryRepo, "primary", "p", "", "Primary repository URL (required)")
|
||||||
cmd.Flags().StringVarP(&mirrorRepo, "mirror", "m", "", "GitHub mirror repository URL (required)")
|
cmd.Flags().StringVarP(&mirrorRepo, "mirror", "m", detectGithubRemote(), "GitHub mirror repository URL (required)")
|
||||||
cmd.Flags().StringVar(&primaryBranch, "primary-branch", "main", "Primary repository branch name")
|
cmd.Flags().StringVar(&primaryBranch, "primary-branch", "main", "Primary repository branch name")
|
||||||
cmd.Flags().StringVar(&mirrorBranch, "mirror-branch", "main", "GitHub mirror repository branch name")
|
cmd.Flags().StringVar(&mirrorBranch, "mirror-branch", "main", "GitHub mirror repository branch name")
|
||||||
cmd.Flags().StringVarP(&syncInterval, "interval", "i", "hourly", "Sync interval (hourly, daily, weekly)")
|
cmd.Flags().StringVarP(&syncInterval, "interval", "i", "hourly", "Sync interval (hourly, daily, weekly)")
|
||||||
cmd.Flags().BoolVar(&forceSync, "force", true, "Force sync by overwriting mirror with primary content")
|
cmd.Flags().BoolVar(&forceSync, "force", true, "Force sync by overwriting mirror with primary content")
|
||||||
cmd.Flags().StringVarP(&outputFile, "output", "o", "", "Output file for workflow YAML (writes to stdout if not specified)")
|
cmd.Flags().StringVarP(&outputFile, "output", "o", ".github/workflows/sync.yaml", "Output file for workflow YAML (writes to stdout if not specified)")
|
||||||
cmd.Flags().BoolVar(&setupWorkflow, "setup", false, "Automatically setup the workflow in the GitHub repository")
|
cmd.Flags().BoolVar(&setupWorkflow, "setup", false, "Automatically setup the workflow in the GitHub repository")
|
||||||
cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
|
cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
|
||||||
|
|
||||||
cmd.MarkFlagRequired("primary")
|
cmd.MarkFlagRequired("primary")
|
||||||
cmd.MarkFlagRequired("mirror")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load parses the flags and environment variables to build the configuration.
|
// Load parses the flags and environment variables to build the configuration.
|
||||||
@@ -106,3 +106,34 @@ func Load() (*Config, error) {
|
|||||||
|
|
||||||
return &config, nil
|
return &config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// detectGithubRemote attempts to detect a GitHub remote URL from the current git repository
|
||||||
|
func detectGithubRemote() string {
|
||||||
|
// Execute git remote -v command
|
||||||
|
cmd := exec.Command("git", "remote", "-v")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the output to find GitHub remotes
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, "github.com") && strings.Contains(line, "(push)") {
|
||||||
|
// Extract the GitHub repository URL
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
url := parts[1]
|
||||||
|
// Convert SSH URL to HTTPS URL if needed
|
||||||
|
if strings.HasPrefix(url, "git@github.com:") {
|
||||||
|
url = strings.Replace(url, "git@github.com:", "https://github.com/", 1)
|
||||||
|
}
|
||||||
|
// Remove .git suffix if present
|
||||||
|
url = strings.TrimSuffix(url, ".git")
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
@@ -81,6 +81,7 @@ func generateWorkflowYAML(data WorkflowTemplate) (string, error) {
|
|||||||
workflow := map[string]interface{}{
|
workflow := map[string]interface{}{
|
||||||
"name": "Sync Primary Repository to GitHub Mirror",
|
"name": "Sync Primary Repository to GitHub Mirror",
|
||||||
"on": map[string]interface{}{
|
"on": map[string]interface{}{
|
||||||
|
"push": map[string]interface{}{},
|
||||||
"schedule": []map[string]string{
|
"schedule": []map[string]string{
|
||||||
{"cron": data.CronSchedule},
|
{"cron": data.CronSchedule},
|
||||||
},
|
},
|
||||||
@@ -90,6 +91,10 @@ func generateWorkflowYAML(data WorkflowTemplate) (string, error) {
|
|||||||
"sync": map[string]interface{}{
|
"sync": map[string]interface{}{
|
||||||
"runs-on": "ubuntu-latest",
|
"runs-on": "ubuntu-latest",
|
||||||
"steps": []map[string]interface{}{
|
"steps": []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"name": "Validate Github Actions Environment",
|
||||||
|
"run": "if [ \"$GITHUB_ACTIONS\" != \"true\" ]; then echo 'This script must be run in a GitHub Actions environment.'; exit 1; fi",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Checkout GitHub Mirror",
|
"name": "Checkout GitHub Mirror",
|
||||||
"uses": "actions/checkout@v3",
|
"uses": "actions/checkout@v3",
|
||||||
|
Reference in New Issue
Block a user