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
18 changes: 9 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,25 +153,25 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}

- name: Verify Homebrew formula publish
- name: Verify Homebrew cask publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ steps.release_meta.outputs.release_tag }}
run: |
set -euo pipefail
RELEASE_VERSION="${RELEASE_TAG#v}"
for attempt in $(seq 1 18); do
FORMULA_B64="$(gh api repos/git-fire/homebrew-tap/contents/Formula/git-rain.rb --jq '.content' 2>/dev/null || true)"
if [ -n "$FORMULA_B64" ]; then
FORMULA_CONTENT="$(printf '%s' "$FORMULA_B64" | tr -d '\n' | base64 -d)"
if printf '%s' "$FORMULA_CONTENT" | grep -q "version \"${RELEASE_VERSION}\"" && \
printf '%s' "$FORMULA_CONTENT" | grep -q "/download/${RELEASE_TAG}/"; then
echo "Homebrew formula updated for ${RELEASE_TAG}."
CASK_B64="$(gh api repos/git-fire/homebrew-tap/contents/Casks/git-rain.rb --jq '.content' 2>/dev/null || true)"
if [ -n "$CASK_B64" ]; then
CASK_CONTENT="$(printf '%s' "$CASK_B64" | tr -d '\n' | base64 -d)"
if printf '%s' "$CASK_CONTENT" | grep -q "version \"${RELEASE_VERSION}\"" && \
printf '%s' "$CASK_CONTENT" | grep -q "/download/${RELEASE_TAG}/"; then
echo "Homebrew cask updated for ${RELEASE_TAG}."
exit 0
fi
fi
echo "Homebrew formula not updated for ${RELEASE_TAG} yet (attempt ${attempt}/18)."
echo "Homebrew cask not updated for ${RELEASE_TAG} yet (attempt ${attempt}/18)."
sleep 10
done
echo "Homebrew formula did not update for ${RELEASE_TAG}; failing release gate."
echo "Homebrew cask did not update for ${RELEASE_TAG}; failing release gate."
exit 1
13 changes: 9 additions & 4 deletions .goreleaser.stable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,24 +97,29 @@ changelog:
- title: Other
order: 999

brews:
homebrew_casks:
- name: git-rain
repository:
owner: git-fire
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
pull_request:
enabled: false
directory: Formula
commit_author:
name: goreleaserbot
email: bot@goreleaser.com
commit_msg_template: "chore: update git-rain to {{ .Tag }}"
homepage: "https://github.com/git-fire/git-rain"
description: "Sync local git repositories from their remotes — the reverse of git-fire"
license: MIT
install: bin.install "git-rain"
test: system "#{bin}/git-rain", "--version"
binaries:
- git-rain
hooks:
post:
install: |
if OS.mac?
system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/git-rain"]
end

release:
github:
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ func fetchOnly(repoPath string, opts git.RainOptions) error {
}
cmd := exec.Command("git", fetchArgs...)
cmd.Dir = repoPath
git.PrepareNetworkGit(cmd)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("%s: %w (output: %s)", strings.Join(append([]string{"git"}, fetchArgs...), " "), err, strings.TrimSpace(string(out)))
}
Expand Down
1 change: 1 addition & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ func TestFetchFailureMessage(t *testing.T) {
{"authentication", "Authentication failed for git@github.com", "could not authenticate with remote — check your credentials and try again"},
{"permission denied", "git@github.com: Permission denied (publickey)", "could not authenticate with remote — check your credentials and try again"},
{"could not read", "fatal: could not read from remote", "could not authenticate with remote — check your credentials and try again"},
{"terminal prompts disabled", "fatal: could not read Username for 'https://github.com': terminal prompts disabled", "could not authenticate with remote — check your credentials and try again"},
{"401", "fatal: HTTP 401: Unauthorized", "could not authenticate with remote — check your credentials and try again"},
{"403", "fatal: HTTP 403 forbidden", "could not authenticate with remote — check your credentials and try again"},
{"could not resolve", "fatal: unable to access ...: Could not resolve host", "could not reach remote — check your network and try again"},
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/charmbracelet/bubbles v1.0.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/git-fire/git-harness v0.3.1
github.com/git-fire/git-testkit v0.2.0
github.com/gofrs/flock v0.12.1
github.com/mattn/go-runewidth v0.0.19
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/git-fire/git-harness v0.3.1 h1:+fCQ9nMS3YI7r9iVGrU95oefH8N86XjxcYSqsin/1aE=
github.com/git-fire/git-harness v0.3.1/go.mod h1:cNvjYdbowpoO/KdJwtGenhySx+EkgoQJujuANJpRpj4=
github.com/git-fire/git-testkit v0.2.0 h1:IFzOxMdNTE5A4lnzbFz62h2R44+3qVy27Xj1KWyGi1Y=
github.com/git-fire/git-testkit v0.2.0/go.mod h1:YlJlkY9JfGdYTe9o9W3l+gv9BPj05FGu6HK36Z5jwVA=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
Expand Down
13 changes: 13 additions & 0 deletions internal/git/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package git

import (
"os/exec"

harnessgit "github.com/git-fire/git-harness/git"
)

// PrepareNetworkGit configures cmd for fetch/push operations that may contact
// a remote. Delegates to git-harness so credential behavior stays aligned with git-fire.
func PrepareNetworkGit(cmd *exec.Cmd) {
harnessgit.PrepareNetworkGit(cmd)
}
39 changes: 39 additions & 0 deletions internal/git/command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package git

import (
"os/exec"
"strings"
"testing"

testutil "github.com/git-fire/git-testkit"
)

func TestFetchFailureReason_TerminalPromptsDisabled(t *testing.T) {
got := fetchFailureReason([]byte("fatal: could not read Username for 'https://github.com': terminal prompts disabled"))
want := "could not authenticate with remote — check your credentials and try again"
if got != want {
t.Fatalf("fetchFailureReason() = %q, want %q", got, want)
}
}

func TestNetworkFetch_UnauthenticatedHTTPSFailsWithoutPrompt(t *testing.T) {
repo := testutil.CreateTestRepo(t, testutil.RepoOptions{
Name: "https-fetch-repo",
Remotes: map[string]string{
"origin": "https://github.com/git-rain/nonexistent-repo-auth-test.git",
},
})

cmd := exec.Command("git", "fetch", "--all")
cmd.Dir = repo
PrepareNetworkGit(cmd)
output, err := cmd.CombinedOutput()
if err == nil {
t.Fatal("expected fetch to fail without credentials")
}
msg := strings.ToLower(string(output) + " " + err.Error())
if !strings.Contains(msg, "terminal prompts disabled") &&
!strings.Contains(msg, "could not read username") {
t.Fatalf("expected non-interactive auth failure, got output=%q err=%v", strings.TrimSpace(string(output)), err)
}
}
1 change: 1 addition & 0 deletions internal/git/fetch_mainline.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func MainlineFetchRemotes(repoPath string, opts RainOptions) (RainResult, error)
}
cmd := exec.Command("git", args...)
cmd.Dir = repoPath
PrepareNetworkGit(cmd)
if output, err := cmd.CombinedOutput(); err != nil {
failedReason[remote] = fetchFailureReason(output)
}
Expand Down
1 change: 1 addition & 0 deletions internal/git/rain.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ func RainRepository(repoPath string, opts RainOptions) (RainResult, error) {
}
cmd := exec.Command("git", fetchArgs...)
cmd.Dir = repoPath
PrepareNetworkGit(cmd)
if output, fetchErr := cmd.CombinedOutput(); fetchErr != nil {
// Freeze gracefully — could not reach remote (auth, network, etc.).
// This is not a hard failure; the repo is untouched. Try again later.
Expand Down
Loading