fix: more robust -p failure handling
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing

See #576
This commit is contained in:
2025-08-24 13:05:03 +02:00
parent 42dde0930d
commit 4cb660c348
2 changed files with 100 additions and 25 deletions

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"path" "path"
"slices" "slices"
@ -56,9 +57,17 @@ It is quite easy to get rate limited by Docker Hub when running this command.
If you have a Hub account you can "docker login" and Abra will automatically If you have a Hub account you can "docker login" and Abra will automatically
use those details. use those details.
Push your new release to git.coopcloud.tech with "--publish/-p". This requires Publish your new release to git.coopcloud.tech with "--publish/-p". This
that you have permission to git push to these repositories and have your SSH requires that you have permission to git push to these repositories and have
keys configured on your account.`), your SSH keys configured on your account. Enable ssh-agent and make sure to add
your private key and enter your passphrase beforehand.
eval ` + "`ssh-agent`" + `
ssh-add ~/.ssh/<my-ssh-private-key-for-git-coopcloud-tech>`),
Example: ` # publish catalogue
eval ` + "`ssh-agent`" + `
ssh-add ~/.ssh/id_ed25519
abra catalogue generate -p`,
Args: cobra.RangeArgs(0, 1), Args: cobra.RangeArgs(0, 1),
ValidArgsFunction: func( ValidArgsFunction: func(
cmd *cobra.Command, cmd *cobra.Command,
@ -72,6 +81,10 @@ keys configured on your account.`),
recipeName = args[0] recipeName = args[0]
} }
if os.Getenv("SSH_AUTH_SOCK") == "" {
log.Warn(i18n.G("ssh: SSH_AUTH_SOCK missing, --publish/-p will fail. see \"abra catalogue generate --help\""))
}
if recipeName != "" { if recipeName != "" {
internal.ValidateRecipe(args, cmd.Name()) internal.ValidateRecipe(args, cmd.Name())
} }

View File

@ -19,6 +19,7 @@ import (
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/distribution/reference" "github.com/distribution/reference"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -45,7 +46,15 @@ major and therefore require intervention while doing the upgrade work.
Publish your new release to git.coopcloud.tech with "--publish/-p". This Publish your new release to git.coopcloud.tech with "--publish/-p". This
requires that you have permission to git push to these repositories and have requires that you have permission to git push to these repositories and have
your SSH keys configured on your account.`), your SSH keys configured on your account. Enable ssh-agent and make sure to add
your private key and enter your passphrase beforehand.
eval ` + "`ssh-agent`" + `
ssh-add ~/.ssh/<my-ssh-private-key-for-git-coopcloud-tech>`),
Example: ` # publish release
eval ` + "`ssh-agent`" + `
ssh-add ~/.ssh/id_ed25519
abra recipe release gitea -p`,
Args: cobra.RangeArgs(1, 2), Args: cobra.RangeArgs(1, 2),
ValidArgsFunction: func( ValidArgsFunction: func(
cmd *cobra.Command, cmd *cobra.Command,
@ -93,8 +102,24 @@ your SSH keys configured on your account.`),
log.Fatal(i18n.G("cannot specify tag and bump type at the same time")) log.Fatal(i18n.G("cannot specify tag and bump type at the same time"))
} }
repo, err := git.PlainOpen(recipe.Dir)
if err != nil {
log.Fatal(err)
}
preCommitHead, err := repo.Head()
if err != nil {
log.Fatal(err)
}
if tagString != "" { if tagString != "" {
if err := createReleaseFromTag(recipe, tagString, mainAppVersion); err != nil { if err := createReleaseFromTag(recipe, tagString, mainAppVersion); err != nil {
if cleanErr := cleanTag(recipe, tagString); cleanErr != nil {
log.Fatal(cleanErr)
}
if cleanErr := cleanCommit(recipe, preCommitHead); cleanErr != nil {
log.Fatal(cleanErr)
}
log.Fatal(err) log.Fatal(err)
} }
} }
@ -125,16 +150,25 @@ your SSH keys configured on your account.`),
} }
if len(tags) > 0 { if len(tags) > 0 {
log.Warn(i18n.G("previous git tags detected, assuming this is a new semver release")) log.Warn(i18n.G("previous git tags detected, assuming new semver release"))
if err := createReleaseFromPreviousTag(tagString, mainAppVersion, recipe, tags); err != nil { if err := createReleaseFromPreviousTag(tagString, mainAppVersion, recipe, tags); err != nil {
if cleanErr := cleanTag(recipe, tagString); cleanErr != nil {
log.Fatal(cleanErr)
}
if cleanErr := cleanCommit(recipe, preCommitHead); cleanErr != nil {
log.Fatal(cleanErr)
}
log.Fatal(err) log.Fatal(err)
} }
} else { } else {
log.Warn(i18n.G("no tag specified and no previous tag available for %s, assuming this is the initial release", recipe.Name)) log.Warn(i18n.G("no tag specified and no previous tag available for %s, assuming initial release", recipe.Name))
if err := createReleaseFromTag(recipe, tagString, mainAppVersion); err != nil { if err := createReleaseFromTag(recipe, tagString, mainAppVersion); err != nil {
if cleanUpErr := cleanUpTag(recipe, tagString); err != nil { if cleanErr := cleanTag(recipe, tagString); cleanErr != nil {
log.Fatal(cleanUpErr) log.Fatal(cleanErr)
}
if cleanErr := cleanCommit(recipe, preCommitHead); cleanErr != nil {
log.Fatal(cleanErr)
} }
log.Fatal(err) log.Fatal(err)
} }
@ -217,19 +251,19 @@ func createReleaseFromTag(recipe recipe.Recipe, tagString, mainAppVersion string
} }
if err := addReleaseNotes(recipe, tagString); err != nil { if err := addReleaseNotes(recipe, tagString); err != nil {
log.Fatal(err) return errors.New(i18n.G("failed to add release notes: %s", err.Error()))
} }
if err := commitRelease(recipe, tagString); err != nil { if err := commitRelease(recipe, tagString); err != nil {
log.Fatal(err) return errors.New(i18n.G("failed to commit changes: %s", err.Error()))
} }
if err := tagRelease(tagString, repo); err != nil { if err := tagRelease(tagString, repo); err != nil {
log.Fatal(err) return errors.New(i18n.G("failed to tag release: %s", err.Error()))
} }
if err := pushRelease(recipe, tagString); err != nil { if err := pushRelease(recipe, tagString); err != nil {
log.Fatal(err) return errors.New(i18n.G("failed to publish new release: %s", err.Error()))
} }
return nil return nil
@ -314,7 +348,7 @@ func addReleaseNotes(recipe recipe.Recipe, tag string) error {
} }
prompt := &survey.Input{ prompt := &survey.Input{
Message: i18n.G("Release Note (leave empty for no release note)"), Message: i18n.G("add release note? (leave empty to skip)"),
} }
var releaseNote string var releaseNote string
@ -406,9 +440,14 @@ func pushRelease(recipe recipe.Recipe, tagString string) error {
} }
if publish { if publish {
if os.Getenv("SSH_AUTH_SOCK") == "" {
return errors.New(i18n.G("ssh-agent not found. see \"abra recipe release --help\" and try again"))
}
if err := recipe.Push(internal.Dry); err != nil { if err := recipe.Push(internal.Dry); err != nil {
return err return err
} }
url := fmt.Sprintf("%s/src/tag/%s", recipe.GitURL, tagString) url := fmt.Sprintf("%s/src/tag/%s", recipe.GitURL, tagString)
log.Info(i18n.G("new release published: %s", url)) log.Info(i18n.G("new release published: %s", url))
} else { } else {
@ -484,7 +523,7 @@ func createReleaseFromPreviousTag(tagString, mainAppVersion string, recipe recip
} }
if lastGitTag.String() == tagString { if lastGitTag.String() == tagString {
log.Fatal(i18n.G("latest git tag (%s) and synced label (%s) are the same?", lastGitTag, tagString)) return errors.New(i18n.G("latest git tag (%s) and synced label (%s) are the same?", lastGitTag, tagString))
} }
if !internal.NoInput { if !internal.NoInput {
@ -494,43 +533,66 @@ func createReleaseFromPreviousTag(tagString, mainAppVersion string, recipe recip
var ok bool var ok bool
if err := survey.AskOne(prompt, &ok); err != nil { if err := survey.AskOne(prompt, &ok); err != nil {
log.Fatal(err) return err
} }
if !ok { if !ok {
log.Fatal(i18n.G("exiting as requested")) return errors.New(i18n.G("exiting as requested"))
} }
} }
if err := addReleaseNotes(recipe, tagString); err != nil { if err := addReleaseNotes(recipe, tagString); err != nil {
log.Fatal(err) return errors.New(i18n.G("failed to add release notes: %s", err.Error()))
} }
if err := commitRelease(recipe, tagString); err != nil { if err := commitRelease(recipe, tagString); err != nil {
log.Fatal(i18n.G("failed to commit changes: %s", err.Error())) return errors.New(i18n.G("failed to commit changes: %s", err.Error()))
} }
if err := tagRelease(tagString, repo); err != nil { if err := tagRelease(tagString, repo); err != nil {
log.Fatal(i18n.G("failed to tag release: %s", err.Error())) return errors.New(i18n.G("failed to tag release: %s", err.Error()))
} }
if err := pushRelease(recipe, tagString); err != nil { if err := pushRelease(recipe, tagString); err != nil {
log.Fatal(i18n.G("failed to publish new release: %s", err.Error())) return errors.New(i18n.G("failed to publish new release: %s", err.Error()))
} }
return nil return nil
} }
// cleanUpTag removes a freshly created tag // cleanCommit soft removes the latest release commit. No change are lost the
func cleanUpTag(recipe recipe.Recipe, tag string) error { // the commit itself is removed. This is the equivalent of `git reset HEAD~1`.
func cleanCommit(recipe recipe.Recipe, head *plumbing.Reference) error {
repo, err := git.PlainOpen(recipe.Dir) repo, err := git.PlainOpen(recipe.Dir)
if err != nil { if err != nil {
return err return errors.New(i18n.G("unable to open repo in %s: %s", recipe.Dir, err))
}
worktree, err := repo.Worktree()
if err != nil {
return errors.New(i18n.G("unable to open work tree in %s: %s", recipe.Dir, err))
}
opts := &git.ResetOptions{Commit: head.Hash(), Mode: git.MixedReset}
if err := worktree.Reset(opts); err != nil {
return errors.New(i18n.G("unable to soft reset %s: %s", recipe.Dir, err))
}
log.Debug(i18n.G("removed freshly created commit"))
return nil
}
// cleanTag removes a freshly created tag
func cleanTag(recipe recipe.Recipe, tag string) error {
repo, err := git.PlainOpen(recipe.Dir)
if err != nil {
return errors.New(i18n.G("unable to open repo in %s: %s", recipe.Dir, err))
} }
if err := repo.DeleteTag(tag); err != nil { if err := repo.DeleteTag(tag); err != nil {
if !strings.Contains(err.Error(), "not found") { if !strings.Contains(err.Error(), "not found") {
return err return errors.New(i18n.G("unable to delete tag %s: %s", tag, err))
} }
} }
@ -546,7 +608,7 @@ func getLabelVersion(recipe recipe.Recipe, prompt bool) (string, error) {
} }
if initTag == "" { if initTag == "" {
log.Fatal(i18n.G("unable to read version for %s from synced label. Did you try running \"abra recipe sync %s\" already?", recipe.Name, recipe.Name)) return "", errors.New(i18n.G("unable to read version for %s from synced label. Did you try running \"abra recipe sync %s\" already?", recipe.Name, recipe.Name))
} }
log.Warn(i18n.G("discovered %s as currently synced recipe label", initTag)) log.Warn(i18n.G("discovered %s as currently synced recipe label", initTag))