Update module gorm.io/driver/postgres to v1.5.5 - autoclosed #9
12 changed files with 160 additions and 63 deletions
|
@ -128,7 +128,7 @@ via web interface or API.
|
||||||
The following environment variables can be used to modify application behavior.
|
The following environment variables can be used to modify application behavior.
|
||||||
|
|
||||||
| Variable | Purpose | Default/Description |
|
| Variable | Purpose | Default/Description |
|
||||||
|:-----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|
|
|:-----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `TZ` | The time zone (**recommended** to set it properly, background tasks depend on it) | Defaults to `Europe/Berlin`, can be any time zone according to _tz database_ |
|
| `TZ` | The time zone (**recommended** to set it properly, background tasks depend on it) | Defaults to `Europe/Berlin`, can be any time zone according to _tz database_ |
|
||||||
| `ADMIN_USER` | Admin user name for login | Not set by default, you need to explicitly set it to user name |
|
| `ADMIN_USER` | Admin user name for login | Not set by default, you need to explicitly set it to user name |
|
||||||
| `ADMIN_PASSWORD` | Admin password for login | Not set by default, you need to explicitly set it to a secure random |
|
| `ADMIN_PASSWORD` | Admin password for login | Not set by default, you need to explicitly set it to a secure random |
|
||||||
|
@ -154,6 +154,7 @@ The following environment variables can be used to modify application behavior.
|
||||||
| | | |
|
| | | |
|
||||||
| `LOGGING_LEVEL` | Logging level. Possible are `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. Setting to `debug` enables high verbosity output. | Defaults to `info` |
|
| `LOGGING_LEVEL` | Logging level. Possible are `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. Setting to `debug` enables high verbosity output. | Defaults to `info` |
|
||||||
| `LOGGING_ENCODING` | Logging encoding. Possible are `console` and `json` | Defaults to `json` |
|
| `LOGGING_ENCODING` | Logging encoding. Possible are `console` and `json` | Defaults to `json` |
|
||||||
|
| `LOGGING_DIRECTORY` | Logging directory. When set, logs will be added to a file called `upda.log` in addition to the standard output. Ensure that upda has access permissions. Use an external program for log rotation if desired. | |
|
||||||
| | | |
|
| | | |
|
||||||
| `WEBHOOKS_TOKEN_LENGTH` | The length of the token | Defaults to `16`, positive number |
|
| `WEBHOOKS_TOKEN_LENGTH` | The length of the token | Defaults to `16`, positive number |
|
||||||
| | | |
|
| | | |
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
# Deployment
|
# Deployment
|
||||||
|
|
||||||
|
## Native
|
||||||
|
|
||||||
|
Download the binary for your operating system. Next, use the binary or execute it locally.
|
||||||
|
|
||||||
|
See the provided systemd service example [upda.service](./contrib/upda.service) to deploy on a UNIX/Linux machine.
|
||||||
|
|
||||||
|
## Container
|
||||||
|
|
||||||
Use one of the provided `docker-compose` examples, edit to your needs. Then issue `docker compose up` command.
|
Use one of the provided `docker-compose` examples, edit to your needs. Then issue `docker compose up` command.
|
||||||
|
|
||||||
All applications should be up and running.
|
All applications should be up and running.
|
||||||
|
@ -10,7 +18,7 @@ Default image user is `appuser` (`uid=2033`) and group is `appgroup` (`gid=2033`
|
||||||
|
|
||||||
The following examples are available
|
The following examples are available
|
||||||
|
|
||||||
## Postgres
|
### Postgres
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: '3.9'
|
version: '3.9'
|
||||||
|
@ -78,7 +86,7 @@ volumes:
|
||||||
external: false
|
external: false
|
||||||
```
|
```
|
||||||
|
|
||||||
## SQLite
|
### SQLite
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: '3.9'
|
version: '3.9'
|
||||||
|
@ -111,14 +119,13 @@ services:
|
||||||
image: git.myservermanager.com/varakh/upda:latest
|
image: git.myservermanager.com/varakh/upda:latest
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- DB_SQLITE_FILE=/data/upda.db
|
|
||||||
- ADMIN_USER=admin
|
- ADMIN_USER=admin
|
||||||
- ADMIN_PASSWORD=changeit
|
- ADMIN_PASSWORD=changeit
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
volumes:
|
volumes:
|
||||||
- upda-app-vol:/data
|
- upda-app-vol:/home/appuser
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8080:8080"
|
- "127.0.0.1:8080:8080"
|
||||||
|
|
||||||
|
@ -127,7 +134,7 @@ volumes:
|
||||||
external: false
|
external: false
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reverse proxy
|
## Reverse proxy
|
||||||
|
|
||||||
You may want to use a proxy in front of them on your host, e.g., nginx. Here's a configuration snippet which should do
|
You may want to use a proxy in front of them on your host, e.g., nginx. Here's a configuration snippet which should do
|
||||||
the work.
|
the work.
|
||||||
|
|
14
_doc/contrib/upda.service
Normal file
14
_doc/contrib/upda.service
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[Unit]
|
||||||
|
Description=upda
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
# Using a dynamic user drops privileges and sets some security defaults
|
||||||
|
# See https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
|
||||||
|
DynamicUser=yes
|
||||||
|
# All environment variables for upda can be put into this file
|
||||||
|
# upda picks them up (on each restart)
|
||||||
|
EnvironmentFile=/etc/upda.conf
|
||||||
|
# Requires upda' binary to be installed at this location, e.g., via package manager or copying it over manually
|
||||||
|
ExecStart=/usr/local/bin/upda-server
|
6
go.mod
6
go.mod
|
@ -12,14 +12,14 @@ require (
|
||||||
github.com/go-co-op/gocron-redis-lock v1.3.0
|
github.com/go-co-op/gocron-redis-lock v1.3.0
|
||||||
github.com/go-playground/validator/v10 v10.17.0
|
github.com/go-playground/validator/v10 v10.17.0
|
||||||
github.com/go-resty/resty/v2 v2.11.0
|
github.com/go-resty/resty/v2 v2.11.0
|
||||||
github.com/google/uuid v1.5.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/redis/go-redis/v9 v9.4.0
|
github.com/redis/go-redis/v9 v9.4.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/urfave/cli/v2 v2.27.1
|
github.com/urfave/cli/v2 v2.27.1
|
||||||
go.uber.org/zap v1.26.0
|
go.uber.org/zap v1.26.0
|
||||||
gorm.io/driver/postgres v1.5.4
|
gorm.io/driver/postgres v1.5.5
|
||||||
gorm.io/driver/sqlite v1.5.4
|
gorm.io/driver/sqlite v1.5.4
|
||||||
gorm.io/gorm v1.25.5
|
gorm.io/gorm v1.25.6
|
||||||
moul.io/zapgorm2 v1.3.0
|
moul.io/zapgorm2 v1.3.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -110,6 +110,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
@ -355,11 +357,15 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
|
gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
|
||||||
gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
|
gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
|
||||||
|
gorm.io/driver/postgres v1.5.5 h1:r1VBTQQrOAlUux3JI9V7rdxVWBPPnzxa315qNJUzmjI=
|
||||||
|
gorm.io/driver/postgres v1.5.5/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
|
||||||
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
|
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
|
||||||
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||||
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
|
||||||
|
gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
moul.io/zapgorm2 v1.3.0 h1:+CzUTMIcnafd0d/BvBce8T4uPn6DQnpIrz64cyixlkk=
|
moul.io/zapgorm2 v1.3.0 h1:+CzUTMIcnafd0d/BvBce8T4uPn6DQnpIrz64cyixlkk=
|
||||||
moul.io/zapgorm2 v1.3.0/go.mod h1:nPVy6U9goFKHR4s+zfSo1xVFaoU7Qgd5DoCdOfzoCqs=
|
moul.io/zapgorm2 v1.3.0/go.mod h1:nPVy6U9goFKHR4s+zfSo1xVFaoU7Qgd5DoCdOfzoCqs=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
|
|
@ -13,7 +13,7 @@ func newHealthHandler() *healthHandler {
|
||||||
return &healthHandler{}
|
return &healthHandler{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *healthHandler) showHealth(c *gin.Context) {
|
func (h *healthHandler) show(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, api.DataResponse{Data: gin.H{
|
c.JSON(http.StatusOK, api.DataResponse{Data: gin.H{
|
||||||
"healthy": true,
|
"healthy": true,
|
||||||
}})
|
}})
|
||||||
|
|
|
@ -14,7 +14,7 @@ func newInfoHandler(a *appConfig) *infoHandler {
|
||||||
return &infoHandler{appConfig: *a}
|
return &infoHandler{appConfig: *a}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *infoHandler) showInfo(c *gin.Context) {
|
func (h *infoHandler) show(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, api.DataResponse{Data: gin.H{
|
c.JSON(http.StatusOK, api.DataResponse{Data: gin.H{
|
||||||
"name": Name,
|
"name": Name,
|
||||||
"version": Version,
|
"version": Version,
|
||||||
|
|
|
@ -16,7 +16,7 @@ func newWebhookInvocationHandler(i *webhookInvocationService, w *webhookService)
|
||||||
return &webhookInvocationHandler{invocationService: *i, webhookService: *w}
|
return &webhookInvocationHandler{invocationService: *i, webhookService: *w}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *webhookInvocationHandler) executeWebhookGeneric(c *gin.Context) {
|
func (h *webhookInvocationHandler) execute(c *gin.Context) {
|
||||||
tokenHeader := c.GetHeader(HeaderWebhookToken)
|
tokenHeader := c.GetHeader(HeaderWebhookToken)
|
||||||
webhookId := c.Param("id")
|
webhookId := c.Param("id")
|
||||||
|
|
||||||
|
|
|
@ -79,10 +79,10 @@ func Start() {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
apiPublicGroup := router.Group("/api/v1")
|
apiPublicGroup := router.Group("/api/v1")
|
||||||
apiPublicGroup.GET("/health", healthHandler.showHealth)
|
apiPublicGroup.GET("/health", healthHandler.show)
|
||||||
apiPublicGroup.GET("/info", infoHandler.showInfo)
|
apiPublicGroup.GET("/info", infoHandler.show)
|
||||||
|
|
||||||
apiPublicGroup.POST("/webhooks/:id", webhookInvocationHandler.executeWebhookGeneric)
|
apiPublicGroup.POST("/webhooks/:id", webhookInvocationHandler.execute)
|
||||||
|
|
||||||
apiAuthGroup := router.Group("/api/v1", gin.BasicAuth(gin.Accounts{
|
apiAuthGroup := router.Group("/api/v1", gin.BasicAuth(gin.Accounts{
|
||||||
env.authConfig.adminUser: env.authConfig.adminPassword,
|
env.authConfig.adminUser: env.authConfig.adminPassword,
|
||||||
|
|
|
@ -8,6 +8,9 @@ const (
|
||||||
envLoggingEncoding = "LOGGING_ENCODING"
|
envLoggingEncoding = "LOGGING_ENCODING"
|
||||||
loggingEncodingDefault = "json"
|
loggingEncodingDefault = "json"
|
||||||
|
|
||||||
|
envLoggingDirectory = "LOGGING_DIRECTORY"
|
||||||
|
loggingFileNameDefault = "upda.log"
|
||||||
|
|
||||||
envTZ = "TZ"
|
envTZ = "TZ"
|
||||||
tzDefault = "Europe/Berlin"
|
tzDefault = "Europe/Berlin"
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package server
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.myservermanager.com/varakh/upda/util"
|
||||||
"github.com/adrg/xdg"
|
"github.com/adrg/xdg"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"moul.io/zapgorm2"
|
"moul.io/zapgorm2"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -111,6 +113,19 @@ func bootstrapEnvironment() *Environment {
|
||||||
loggingEncoderConfig = zap.NewDevelopmentEncoderConfig()
|
loggingEncoderConfig = zap.NewDevelopmentEncoderConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logPaths := []string{"stderr"}
|
||||||
|
loggingDirectory := os.Getenv(envLoggingDirectory)
|
||||||
|
|
||||||
|
if loggingDirectory != "" {
|
||||||
|
logFile := filepath.Join(loggingDirectory, loggingFileNameDefault)
|
||||||
|
|
||||||
|
if err = util.CreateFileWithParent(logFile); err != nil {
|
||||||
|
log.Fatalf("Log file '%s' cannot be created: %v", loggingDirectory, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logPaths = append(logPaths, logFile)
|
||||||
|
}
|
||||||
|
|
||||||
var zapConfig *zap.Config
|
var zapConfig *zap.Config
|
||||||
if isDebug {
|
if isDebug {
|
||||||
zapConfig = &zap.Config{
|
zapConfig = &zap.Config{
|
||||||
|
@ -118,8 +133,8 @@ func bootstrapEnvironment() *Environment {
|
||||||
Development: isDevelopment,
|
Development: isDevelopment,
|
||||||
Encoding: loggingEncoding,
|
Encoding: loggingEncoding,
|
||||||
EncoderConfig: loggingEncoderConfig,
|
EncoderConfig: loggingEncoderConfig,
|
||||||
OutputPaths: []string{"stderr"},
|
OutputPaths: logPaths,
|
||||||
ErrorOutputPaths: []string{"stderr"},
|
ErrorOutputPaths: logPaths,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
zapConfig = &zap.Config{
|
zapConfig = &zap.Config{
|
||||||
|
@ -131,8 +146,8 @@ func bootstrapEnvironment() *Environment {
|
||||||
},
|
},
|
||||||
Encoding: loggingEncoding,
|
Encoding: loggingEncoding,
|
||||||
EncoderConfig: loggingEncoderConfig,
|
EncoderConfig: loggingEncoderConfig,
|
||||||
OutputPaths: []string{"stderr"},
|
OutputPaths: logPaths,
|
||||||
ErrorOutputPaths: []string{"stderr"},
|
ErrorOutputPaths: logPaths,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,6 +280,10 @@ func bootstrapEnvironment() *Environment {
|
||||||
dbFile := os.Getenv(envDbSqliteFile)
|
dbFile := os.Getenv(envDbSqliteFile)
|
||||||
zap.L().Sugar().Infof("Using database file '%s'", dbFile)
|
zap.L().Sugar().Infof("Using database file '%s'", dbFile)
|
||||||
|
|
||||||
|
if err = util.CreateFileWithParent(dbFile); err != nil {
|
||||||
|
zap.L().Sugar().Fatalf("Database file '%s' cannot be created: %v", dbFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
if db, err = gorm.Open(sqlite.Open(dbFile), gormConfig); err != nil {
|
if db, err = gorm.Open(sqlite.Open(dbFile), gormConfig); err != nil {
|
||||||
zap.L().Sugar().Fatalf("Could not setup database: %v", err)
|
zap.L().Sugar().Fatalf("Could not setup database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
47
util/file.go
Normal file
47
util/file.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateFileWithParent creates a file and all prefixed directories first
|
||||||
|
func CreateFileWithParent(file string) error {
|
||||||
|
if file == "" {
|
||||||
|
return errors.New("assert: blank values are not allowed for 'file'")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
parentDir := filepath.Dir(file)
|
||||||
|
|
||||||
|
if err = os.MkdirAll(parentDir, os.ModePerm); err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("cannot create parent directory '%v': %v", parentDir, fmt.Errorf("%w", err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = os.Stat(file); errors.Is(err, os.ErrNotExist) {
|
||||||
|
var f *os.File
|
||||||
|
f, err = os.Create(file)
|
||||||
|
defer f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("cannot create file '%v': %v", file, fmt.Errorf("%w", err)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDirectoryRecursively creates a directory recursively
|
||||||
|
func CreateDirectoryRecursively(dir string) error {
|
||||||
|
if dir == "" {
|
||||||
|
return errors.New("assert: blank values are not allowed for 'dir'")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("cannot create parent directory '%v': %v", dir, fmt.Errorf("%w", err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
Loading…
Reference in a new issue