Compare commits
19 Commits
0.10.0-rc2
...
main
Author | SHA1 | Date | |
---|---|---|---|
7c3b740e14 | |||
2fbef41a3a | |||
6fb41e5300 | |||
1432f480c7 | |||
83af39771b | |||
4d1333202e | |||
55c24f070c | |||
229e8eb9da | |||
b3ab95750e | |||
de009921a2 | |||
d081bbaefa | |||
515b5466ca | |||
6965799bdc | |||
f75c9a6259 | |||
a43a092ba7 | |||
fa084a61d2 | |||
895a7fe7d6 | |||
742a726778 | |||
2b9a185aff |
@ -183,7 +183,7 @@ does not).`,
|
|||||||
if err := internal.RunCmdRemote(
|
if err := internal.RunCmdRemote(
|
||||||
cl,
|
cl,
|
||||||
app,
|
app,
|
||||||
requestTTY,
|
disableTTY,
|
||||||
app.Recipe.AbraShPath,
|
app.Recipe.AbraShPath,
|
||||||
targetServiceName, cmdName, parsedCmdArgs, remoteUser); err != nil {
|
targetServiceName, cmdName, parsedCmdArgs, remoteUser); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@ -238,7 +238,7 @@ func parseCmdArgs(args []string, isLocal bool) (bool, string) {
|
|||||||
var (
|
var (
|
||||||
local bool
|
local bool
|
||||||
remoteUser string
|
remoteUser string
|
||||||
requestTTY bool
|
disableTTY bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -259,11 +259,11 @@ func init() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
AppCmdCommand.Flags().BoolVarP(
|
AppCmdCommand.Flags().BoolVarP(
|
||||||
&requestTTY,
|
&disableTTY,
|
||||||
"tty",
|
"tty",
|
||||||
"T",
|
"T",
|
||||||
false,
|
false,
|
||||||
"request remote TTY",
|
"disable remote TTY",
|
||||||
)
|
)
|
||||||
|
|
||||||
AppCmdCommand.Flags().BoolVarP(
|
AppCmdCommand.Flags().BoolVarP(
|
||||||
|
@ -33,7 +33,7 @@ var AppCpCommand = &cobra.Command{
|
|||||||
abra app cp 1312.net myfile.txt app:/
|
abra app cp 1312.net myfile.txt app:/
|
||||||
|
|
||||||
# copy that file back to your current working directory locally
|
# copy that file back to your current working directory locally
|
||||||
abra app cp 1312.net app:/myfile.txt`,
|
abra app cp 1312.net app:/myfile.txt ./`,
|
||||||
Args: cobra.ExactArgs(3),
|
Args: cobra.ExactArgs(3),
|
||||||
ValidArgsFunction: func(
|
ValidArgsFunction: func(
|
||||||
cmd *cobra.Command,
|
cmd *cobra.Command,
|
||||||
|
@ -142,10 +142,14 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
|||||||
appStats.AutoUpdate = autoUpdate
|
appStats.AutoUpdate = autoUpdate
|
||||||
|
|
||||||
var newUpdates []string
|
var newUpdates []string
|
||||||
if version != "unknown" {
|
if version != "unknown" && chaosVersion == "unknown" {
|
||||||
|
if err := app.Recipe.EnsureExists(); err != nil {
|
||||||
|
log.Fatalf("unable to clone %s: %s", app.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
updates, err := app.Recipe.Tags()
|
updates, err := app.Recipe.Tags()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("unable to retrieve tags for %s: %s", app.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedVersion, err := tagcmp.Parse(version)
|
parsedVersion, err := tagcmp.Parse(version)
|
||||||
|
@ -109,6 +109,15 @@ var AppNewCommand = &cobra.Command{
|
|||||||
if err := recipe.EnsureLatest(); err != nil {
|
if err := recipe.EnsureLatest(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if recipeVersion == "" {
|
||||||
|
head, err := recipe.Head()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to retrieve latest commit for %s: %s", recipe.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
recipeVersion = formatter.SmallSHA(head.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +302,12 @@ func ensureServerFlag() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(servers) == 1 {
|
||||||
|
newAppServer = servers[0]
|
||||||
|
log.Infof("single server detected, choosing %s automatically", newAppServer)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if newAppServer == "" && !internal.NoInput {
|
if newAppServer == "" && !internal.NoInput {
|
||||||
prompt := &survey.Select{
|
prompt := &survey.Select{
|
||||||
Message: "Select app server:",
|
Message: "Select app server:",
|
||||||
|
@ -3,6 +3,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/app"
|
"coopcloud.tech/abra/pkg/app"
|
||||||
@ -213,9 +214,7 @@ beforehand. See "abra app backup" for more.`,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if upgradeReleaseNotes != "" && chosenUpgrade != "" {
|
if upgradeReleaseNotes == "" {
|
||||||
fmt.Print(upgradeReleaseNotes)
|
|
||||||
} else {
|
|
||||||
upgradeWarnMessages = append(
|
upgradeWarnMessages = append(
|
||||||
upgradeWarnMessages,
|
upgradeWarnMessages,
|
||||||
fmt.Sprintf("no release notes available for %s", chosenUpgrade),
|
fmt.Sprintf("no release notes available for %s", chosenUpgrade),
|
||||||
@ -337,6 +336,11 @@ func getReleaseNotes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if note != "" {
|
if note != "" {
|
||||||
|
// NOTE(d1): trim any final newline on the end of the note itself before
|
||||||
|
// we manually handle newlines (for multiple release notes and
|
||||||
|
// ensuring space between the warning messages)
|
||||||
|
note = strings.TrimSuffix(note, "\n")
|
||||||
|
|
||||||
*upgradeReleaseNotes += fmt.Sprintf("%s\n", note)
|
*upgradeReleaseNotes += fmt.Sprintf("%s\n", note)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
func RunCmdRemote(
|
func RunCmdRemote(
|
||||||
cl *dockerClient.Client,
|
cl *dockerClient.Client,
|
||||||
app appPkg.App,
|
app appPkg.App,
|
||||||
requestTTY bool,
|
disableTTY bool,
|
||||||
abraSh, serviceName, cmdName, cmdArgs, remoteUser string) error {
|
abraSh, serviceName, cmdName, cmdArgs, remoteUser string) error {
|
||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
filters.Add("name", fmt.Sprintf("^%s_%s", app.StackName(), serviceName))
|
filters.Add("name", fmt.Sprintf("^%s_%s", app.StackName(), serviceName))
|
||||||
@ -84,8 +84,10 @@ func RunCmdRemote(
|
|||||||
}
|
}
|
||||||
|
|
||||||
execCreateOpts.Cmd = cmd
|
execCreateOpts.Cmd = cmd
|
||||||
execCreateOpts.Tty = requestTTY
|
|
||||||
if !requestTTY {
|
execCreateOpts.Tty = true
|
||||||
|
if disableTTY {
|
||||||
|
execCreateOpts.Tty = false
|
||||||
log.Debugf("not requesting a remote TTY")
|
log.Debugf("not requesting a remote TTY")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ func DeployOverview(
|
|||||||
app appPkg.App,
|
app appPkg.App,
|
||||||
deployedVersion string,
|
deployedVersion string,
|
||||||
toDeployVersion string,
|
toDeployVersion string,
|
||||||
info string,
|
releaseNotes string,
|
||||||
warnMessages []string,
|
warnMessages []string,
|
||||||
) error {
|
) error {
|
||||||
deployConfig := "compose.yml"
|
deployConfig := "compose.yml"
|
||||||
@ -85,8 +85,8 @@ func DeployOverview(
|
|||||||
|
|
||||||
fmt.Println(overview)
|
fmt.Println(overview)
|
||||||
|
|
||||||
if info != "" {
|
if releaseNotes != "" {
|
||||||
fmt.Println(info)
|
fmt.Print(releaseNotes)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, msg := range warnMessages {
|
for _, msg := range warnMessages {
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package recipe
|
package recipe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/log"
|
"coopcloud.tech/abra/pkg/log"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
gitCfg "github.com/go-git/go-git/v5/config"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,7 +17,16 @@ var RecipeFetchCommand = &cobra.Command{
|
|||||||
Use: "fetch [recipe | --all] [flags]",
|
Use: "fetch [recipe | --all] [flags]",
|
||||||
Aliases: []string{"f"},
|
Aliases: []string{"f"},
|
||||||
Short: "Clone recipe(s) locally",
|
Short: "Clone recipe(s) locally",
|
||||||
|
Long: `Using "--force/-f" Git syncs an existing recipe. It does not erase unstaged changes.`,
|
||||||
Args: cobra.RangeArgs(0, 1),
|
Args: cobra.RangeArgs(0, 1),
|
||||||
|
Example: ` # fetch from recipe catalogue
|
||||||
|
abra recipe fetch gitea
|
||||||
|
|
||||||
|
# fetch from remote recipe
|
||||||
|
abra recipe fetch git.foo.org/recipes/myrecipe
|
||||||
|
|
||||||
|
# fetch with ssh remote for hacking
|
||||||
|
abra recipe fetch gitea --ssh`,
|
||||||
ValidArgsFunction: func(
|
ValidArgsFunction: func(
|
||||||
cmd *cobra.Command,
|
cmd *cobra.Command,
|
||||||
args []string,
|
args []string,
|
||||||
@ -36,10 +49,39 @@ var RecipeFetchCommand = &cobra.Command{
|
|||||||
|
|
||||||
ensureCtx := internal.GetEnsureContext()
|
ensureCtx := internal.GetEnsureContext()
|
||||||
if recipeName != "" {
|
if recipeName != "" {
|
||||||
r := internal.ValidateRecipe(args, cmd.Name())
|
r := recipe.Get(recipeName)
|
||||||
if err := r.Ensure(ensureCtx); err != nil {
|
if _, err := os.Stat(r.Dir); !os.IsNotExist(err) {
|
||||||
log.Fatal(err)
|
if !force {
|
||||||
|
log.Warnf("%s is already fetched", r.Name)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = internal.ValidateRecipe(args, cmd.Name())
|
||||||
|
|
||||||
|
if sshRemote {
|
||||||
|
if r.SSHURL == "" {
|
||||||
|
log.Warnf("unable to discover SSH remote for %s", r.Name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := git.PlainOpen(r.Dir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("unable to open %s: %s", r.Dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = repo.DeleteRemote("origin"); err != nil {
|
||||||
|
log.Fatalf("unable to remove default remote in %s: %s", r.Dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := repo.CreateRemote(&gitCfg.RemoteConfig{
|
||||||
|
Name: "origin",
|
||||||
|
URLs: []string{r.SSHURL},
|
||||||
|
}); err != nil {
|
||||||
|
log.Fatalf("unable to set SSH remote in %s: %s", r.Dir, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +103,8 @@ var RecipeFetchCommand = &cobra.Command{
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
fetchAllRecipes bool
|
fetchAllRecipes bool
|
||||||
|
sshRemote bool
|
||||||
|
force bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -71,4 +115,20 @@ func init() {
|
|||||||
false,
|
false,
|
||||||
"fetch all recipes",
|
"fetch all recipes",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
RecipeFetchCommand.Flags().BoolVarP(
|
||||||
|
&sshRemote,
|
||||||
|
"ssh",
|
||||||
|
"s",
|
||||||
|
false,
|
||||||
|
"automatically set ssh remote",
|
||||||
|
)
|
||||||
|
|
||||||
|
RecipeFetchCommand.Flags().BoolVarP(
|
||||||
|
&force,
|
||||||
|
"force",
|
||||||
|
"f",
|
||||||
|
false,
|
||||||
|
"force re-fetch",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -19,7 +19,6 @@ require (
|
|||||||
github.com/google/go-cmp v0.7.0
|
github.com/google/go-cmp v0.7.0
|
||||||
github.com/moby/sys/signal v0.7.1
|
github.com/moby/sys/signal v0.7.1
|
||||||
github.com/moby/term v0.5.2
|
github.com/moby/term v0.5.2
|
||||||
github.com/muesli/reflow v0.3.0
|
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/schollz/progressbar/v3 v3.18.0
|
github.com/schollz/progressbar/v3 v3.18.0
|
||||||
golang.org/x/term v0.30.0
|
golang.org/x/term v0.30.0
|
||||||
|
4
go.sum
4
go.sum
@ -634,7 +634,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
|||||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
|
||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||||
@ -694,8 +693,6 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D
|
|||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
|
||||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
|
||||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
@ -813,7 +810,6 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
|||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/log"
|
"coopcloud.tech/abra/pkg/log"
|
||||||
@ -22,46 +25,81 @@ func gitCloneIgnoreErr(err error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone runs a git clone which accounts for different default branches.
|
// Clone runs a git clone which accounts for different default branches. This
|
||||||
|
// function respects Ctrl+C (SIGINT) calls from the user, cancelling the
|
||||||
|
// context and deleting the (typically) half-baked clone of the repository.
|
||||||
|
// This avoids broken state for future clone / recipe ops.
|
||||||
func Clone(dir, url string) error {
|
func Clone(dir, url string) error {
|
||||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
ctx := context.Background()
|
||||||
log.Debugf("git clone: %s", dir, url)
|
ctx, cancelCtx := context.WithCancel(ctx)
|
||||||
|
|
||||||
_, err := git.PlainClone(dir, false, &git.CloneOptions{
|
sigIntCh := make(chan os.Signal, 1)
|
||||||
URL: url,
|
signal.Notify(sigIntCh, os.Interrupt)
|
||||||
Tags: git.AllTags,
|
defer func() {
|
||||||
ReferenceName: plumbing.ReferenceName("refs/heads/main"),
|
signal.Stop(sigIntCh)
|
||||||
SingleBranch: true,
|
cancelCtx()
|
||||||
})
|
}()
|
||||||
|
|
||||||
if err != nil && gitCloneIgnoreErr(err) {
|
errCh := make(chan error)
|
||||||
log.Debugf("git clone: %s cloned successfully", dir)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
go func() {
|
||||||
log.Debug("git clone: main branch failed, attempting master branch")
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
|
log.Debugf("git clone: %s", url)
|
||||||
|
|
||||||
_, err := git.PlainClone(dir, false, &git.CloneOptions{
|
_, err := git.PlainCloneContext(ctx, dir, false, &git.CloneOptions{
|
||||||
URL: url,
|
URL: url,
|
||||||
Tags: git.AllTags,
|
Tags: git.AllTags,
|
||||||
ReferenceName: plumbing.ReferenceName("refs/heads/master"),
|
ReferenceName: plumbing.ReferenceName("refs/heads/main"),
|
||||||
SingleBranch: true,
|
SingleBranch: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil && gitCloneIgnoreErr(err) {
|
if err != nil && gitCloneIgnoreErr(err) {
|
||||||
log.Debugf("git clone: %s cloned successfully", dir)
|
log.Debugf("git clone: %s cloned successfully", dir)
|
||||||
return nil
|
errCh <- nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
errCh <- fmt.Errorf("git clone %s: cancelled due to interrupt", dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Debug("git clone: main branch failed, attempting master branch")
|
||||||
|
|
||||||
|
_, err := git.PlainCloneContext(ctx, dir, false, &git.CloneOptions{
|
||||||
|
URL: url,
|
||||||
|
Tags: git.AllTags,
|
||||||
|
ReferenceName: plumbing.ReferenceName("refs/heads/master"),
|
||||||
|
SingleBranch: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil && gitCloneIgnoreErr(err) {
|
||||||
|
log.Debugf("git clone: %s cloned successfully", dir)
|
||||||
|
errCh <- nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("git clone: %s cloned successfully", dir)
|
||||||
|
} else {
|
||||||
|
log.Debugf("git clone: %s already exists", dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("git clone: %s cloned successfully", dir)
|
errCh <- nil
|
||||||
} else {
|
}()
|
||||||
log.Debugf("git clone: %s already exists", dir)
|
|
||||||
|
select {
|
||||||
|
case <-sigIntCh:
|
||||||
|
cancelCtx()
|
||||||
|
fmt.Println() // NOTE(d1): newline after ^C
|
||||||
|
if err := os.RemoveAll(dir); err != nil {
|
||||||
|
return fmt.Errorf("unable to clean up git clone of %s: %s", dir, err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("git clone %s: cancelled due to interrupt", dir)
|
||||||
|
case err := <-errCh:
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
48
pkg/git/clone_test.go
Normal file
48
pkg/git/clone_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"coopcloud.tech/abra/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClone(t *testing.T) {
|
||||||
|
dir := path.Join(config.RECIPES_DIR, "gitea")
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
|
||||||
|
gitURL := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, "gitea")
|
||||||
|
if err := Clone(dir, gitURL); err != nil {
|
||||||
|
t.Fatalf("unable to git clone gitea: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||||
|
t.Fatal("gitea repo was not cloned successfully")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCancelGitClone(t *testing.T) {
|
||||||
|
dir := path.Join(config.RECIPES_DIR, "gitea")
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
p, err := os.FindProcess(os.Getpid())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to find current process: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Signal(syscall.SIGINT)
|
||||||
|
}()
|
||||||
|
|
||||||
|
gitURL := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, "gitea")
|
||||||
|
if err := Clone(dir, gitURL); err == nil {
|
||||||
|
t.Fatal("cloning should have been interrupted")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(dir); err != nil && !os.IsNotExist(err) {
|
||||||
|
t.Fatal("recipe repo was not deleted")
|
||||||
|
}
|
||||||
|
}
|
@ -15,8 +15,10 @@ import (
|
|||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Warn = "warn"
|
var (
|
||||||
var Critical = "critical"
|
Warn = "warn"
|
||||||
|
Critical = "critical"
|
||||||
|
)
|
||||||
|
|
||||||
type LintFunction func(recipe.Recipe) (bool, error)
|
type LintFunction func(recipe.Recipe) (bool, error)
|
||||||
|
|
||||||
@ -194,7 +196,7 @@ func LintForErrors(recipe recipe.Recipe) error {
|
|||||||
|
|
||||||
ok, err := rule.Function(recipe)
|
ok, err := rule.Function(recipe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("lint %s: %s", rule.Ref, err)
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("lint error in %s configs: \"%s\" failed lint checks (%s)", recipe.Name, rule.Description, rule.Ref)
|
return fmt.Errorf("lint error in %s configs: \"%s\" failed lint checks (%s)", recipe.Name, rule.Description, rule.Ref)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
ABRA_VERSION="0.9.0-beta"
|
ABRA_VERSION="0.10.0-beta"
|
||||||
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/toolshed/abra/releases/tags/$ABRA_VERSION"
|
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/toolshed/abra/releases/tags/$ABRA_VERSION"
|
||||||
RC_VERSION="0.10.0-rc2-beta"
|
RC_VERSION="0.10.0-beta"
|
||||||
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/toolshed/abra/releases/tags/$RC_VERSION"
|
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/toolshed/abra/releases/tags/$RC_VERSION"
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
|
@ -3,5 +3,5 @@ STACK := abra_installer_script
|
|||||||
default: deploy
|
default: deploy
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
@DOCKER_CONTEXT=swarm.autonomic.zone docker stack rm $(STACK) && \
|
@DOCKER_CONTEXT=swarm-0.coopcloud.tech docker stack rm $(STACK) && \
|
||||||
DOCKER_CONTEXT=swarm.autonomic.zone docker stack deploy -c compose.yml $(STACK)
|
DOCKER_CONTEXT=swarm-0.coopcloud.tech docker stack deploy -c compose.yml $(STACK)
|
||||||
|
@ -401,8 +401,6 @@ teardown(){
|
|||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@test "ignore env version on new deploy" {
|
@test "ignore env version on new deploy" {
|
||||||
tagHash=$(_get_tag_hash "0.1.0+1.20.0")
|
|
||||||
|
|
||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||||
--no-input --no-converge-checks
|
--no-input --no-converge-checks
|
||||||
assert_success
|
assert_success
|
||||||
|
@ -8,6 +8,7 @@ setup_file(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
teardown_file(){
|
teardown_file(){
|
||||||
|
_undeploy_app
|
||||||
_rm_app
|
_rm_app
|
||||||
_rm_server
|
_rm_server
|
||||||
}
|
}
|
||||||
@ -18,6 +19,7 @@ setup(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
teardown(){
|
teardown(){
|
||||||
|
_reset_recipe
|
||||||
_undeploy_app
|
_undeploy_app
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +89,10 @@ teardown(){
|
|||||||
assert_success
|
assert_success
|
||||||
refute_output --partial "$TEST_RECIPE"
|
refute_output --partial "$TEST_RECIPE"
|
||||||
assert_output --partial "foo-recipe"
|
assert_output --partial "foo-recipe"
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/servers/foo.com"
|
||||||
|
assert_success
|
||||||
|
assert_not_exists "$ABRA_DIR/servers/foo.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "output is machine readable" {
|
@test "output is machine readable" {
|
||||||
@ -98,3 +104,36 @@ teardown(){
|
|||||||
|
|
||||||
assert_output --partial "$expectedOutput"
|
assert_output --partial "$expectedOutput"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "list with status fetches recipe" {
|
||||||
|
_deploy_app
|
||||||
|
|
||||||
|
run $ABRA app ls --status
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app ls --status
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "list with chaos version" {
|
||||||
|
run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
|
--no-input --no-converge-checks --chaos
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app ls --status
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "+U"
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/servers/foo.com"
|
||||||
|
assert_success
|
||||||
|
assert_not_exists "$ABRA_DIR/servers/foo.com"
|
||||||
|
}
|
||||||
|
@ -250,3 +250,10 @@ teardown(){
|
|||||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
assert_success
|
assert_success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "automatically select single server" {
|
||||||
|
# NOTE(d1): no --no-input required, single server available
|
||||||
|
run $ABRA app new "$TEST_RECIPE" --domain "$TEST_APP_DOMAIN"
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
}
|
||||||
|
@ -25,13 +25,3 @@ teardown(){
|
|||||||
run "$HOME/.local/bin/abra" -v
|
run "$HOME/.local/bin/abra" -v
|
||||||
assert_output --partial 'beta'
|
assert_output --partial 'beta'
|
||||||
}
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
|
||||||
@test "install release candidate from script" {
|
|
||||||
run bash -c 'curl https://install.abra.coopcloud.tech | bash -s -- --rc'
|
|
||||||
assert_success
|
|
||||||
|
|
||||||
assert_exists "$HOME/.local/bin/abra"
|
|
||||||
run "$HOME/.local/bin/abra" -v
|
|
||||||
assert_output --partial '-rc'
|
|
||||||
}
|
|
||||||
|
@ -5,6 +5,16 @@ setup() {
|
|||||||
_common_setup
|
_common_setup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
assert_success
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/git_coopcloud_tech_coop-cloud_matrix-synapse"
|
||||||
|
assert_success
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/git_coopcloud_tech_coop-cloud_matrix-synapse"
|
||||||
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@test "recipe fetch all" {
|
@test "recipe fetch all" {
|
||||||
run rm -rf "$ABRA_DIR/recipes/matrix-synapse"
|
run rm -rf "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
@ -35,3 +45,81 @@ setup() {
|
|||||||
run $ABRA recipe fetch matrix-synapse --all
|
run $ABRA recipe fetch matrix-synapse --all
|
||||||
assert_failure
|
assert_failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "do not refetch without --force" {
|
||||||
|
run $ABRA recipe fetch matrix-synapse
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
|
||||||
|
run $ABRA recipe fetch matrix-synapse
|
||||||
|
assert_output --partial "already fetched"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "refetch with --force" {
|
||||||
|
run $ABRA recipe fetch matrix-synapse
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
|
||||||
|
run $ABRA recipe fetch matrix-synapse --force
|
||||||
|
assert_success
|
||||||
|
refute_output --partial "already fetched"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "refetch with --force does not erase unstaged changes" {
|
||||||
|
run $ABRA recipe fetch matrix-synapse
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
|
||||||
|
run bash -c "echo foo >> $ABRA_DIR/recipes/matrix-synapse/foo"
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse/foo"
|
||||||
|
|
||||||
|
run $ABRA recipe fetch matrix-synapse --force
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fetch with --ssh" {
|
||||||
|
run $ABRA recipe fetch matrix-synapse --ssh
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/matrix-synapse" remote -v
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "ssh://"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "re-fetch with --ssh/--force" {
|
||||||
|
run $ABRA recipe fetch matrix-synapse
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/matrix-synapse" remote -v
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "https://"
|
||||||
|
|
||||||
|
run $ABRA recipe fetch matrix-synapse --ssh --force
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/matrix-synapse" remote -v
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "ssh://"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fetch remote recipe" {
|
||||||
|
run $ABRA recipe fetch git.coopcloud.tech/coop-cloud/matrix-synapse
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/git_coopcloud_tech_coop-cloud_matrix-synapse"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "remote recipe do not refetch without --force" {
|
||||||
|
run $ABRA recipe fetch git.coopcloud.tech/coop-cloud/matrix-synapse
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/git_coopcloud_tech_coop-cloud_matrix-synapse"
|
||||||
|
|
||||||
|
run $ABRA recipe fetch git.coopcloud.tech/coop-cloud/matrix-synapse
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "already fetched"
|
||||||
|
}
|
||||||
|
@ -68,3 +68,27 @@ teardown(){
|
|||||||
assert_output --partial 'fooUser'
|
assert_output --partial 'fooUser'
|
||||||
assert_output --partial 'foo@example.com'
|
assert_output --partial 'foo@example.com'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "recipe new, app new, no releases, latest commit" {
|
||||||
|
recipeName="foobar"
|
||||||
|
|
||||||
|
run $ABRA recipe new "$recipeName"
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/$recipeName"
|
||||||
|
|
||||||
|
currentHash=$(git -C "$ABRA_DIR/recipes/$recipeName" show -s --format="%H")
|
||||||
|
domain="$recipeName.$TEST_APP_SERVER"
|
||||||
|
|
||||||
|
run $ABRA app new "$recipeName" \
|
||||||
|
--no-input \
|
||||||
|
--server "$TEST_SERVER" \
|
||||||
|
--domain "$domain"
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "version: ${currentHash:0:8}"
|
||||||
|
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$domain.env"
|
||||||
|
|
||||||
|
run grep -q "TYPE=$recipeName:${currentHash:0:8}" \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$domain.env"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
@ -26,14 +26,3 @@ teardown(){
|
|||||||
run "$HOME/.local/bin/abra" -v
|
run "$HOME/.local/bin/abra" -v
|
||||||
assert_output --partial 'beta'
|
assert_output --partial 'beta'
|
||||||
}
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
|
||||||
@test "abra upgrade release candidate" {
|
|
||||||
run $ABRA upgrade --rc
|
|
||||||
assert_success
|
|
||||||
assert_output --partial 'Public interest infrastructure'
|
|
||||||
|
|
||||||
assert_exists "$HOME/.local/bin/abra"
|
|
||||||
run "$HOME/.local/bin/abra" -v
|
|
||||||
assert_output --partial '-rc'
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user