vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Create and edit releases from the browser
ec34301375902f6b08c0171bf91b98cbcc55c63c · Keenan Nemetz · 15d ago · parent 09b15120
Create and edit releases from the browser Maintainers get a New release form that writes the same annotated tag the CLI writes: tag name, target commit, and notes become the tag message, with the account as tagger. Edit notes rewrites the tag message only while no artifact is attached, matching the release-immutability rule; lightweight tags and releases with files stay locked. Tag names are validated, existing tags are refused, reserved page names are rejected, and both actions are audited.
core/git/git.go +32
| @@ -411,6 +411,38 @@ func (r *Repo) SetHEAD(branch string) error { | |||
| 411 | 411 | return nil | |
| 412 | 412 | } | |
| 413 | 413 | ||
| 414 | // ValidTag reports whether name is a syntactically valid tag. The leading-dash | ||
| 415 | // check keeps a name from being read as an option by git. | ||
| 416 | func (r *Repo) ValidTag(name string) bool { | ||
| 417 | if name == "" || strings.HasPrefix(name, "-") || strings.Contains(name, "\x00") { | ||
| 418 | return false | ||
| 419 | } | ||
| 420 | _, _, err := r.run(nil, "check-ref-format", "refs/tags/"+name) | ||
| 421 | return err == nil | ||
| 422 | } | ||
| 423 | |||
| 424 | // WriteAnnotatedTag creates, or with force moves, an annotated tag. The tagger is | ||
| 425 | // the account that asked for it, not the server process, so the tag explains who | ||
| 426 | // made the release. | ||
| 427 | func (r *Repo) WriteAnnotatedTag(name, target, message, tagger string, force bool) error { | ||
| 428 | if !r.ValidTag(name) { | ||
| 429 | return fmt.Errorf("%q is not a valid tag name", name) | ||
| 430 | } | ||
| 431 | args := []string{"tag", "-a"} | ||
| 432 | if force { | ||
| 433 | args = append(args, "-f") | ||
| 434 | } | ||
| 435 | args = append(args, name, "-m", message, target) | ||
| 436 | _, errs, err := r.runEnv([]string{ | ||
| 437 | "GIT_COMMITTER_NAME=" + tagger, | ||
| 438 | "GIT_COMMITTER_EMAIL=" + tagger + "@vole.local", | ||
| 439 | }, nil, args...) | ||
| 440 | if err != nil { | ||
| 441 | return fmt.Errorf("tag %s: %w: %s", name, err, errs) | ||
| 442 | } | ||
| 443 | return nil | ||
| 444 | } | ||
| 445 | |||
| 414 | 446 | // ValidBranch reports whether name is a syntactically valid branch. It checks | |
| 415 | 447 | // the full ref rather than using --branch, which would expand @{-1} and similar | |
| 416 | 448 | // history syntax instead of validating the bytes the caller wrote. | |