From 25fb6351be0413e49378bccdfd0e55870b78a4cf Mon Sep 17 00:00:00 2001 From: Marc Ole Bulling Date: Fri, 8 Aug 2025 23:29:53 +0200 Subject: [PATCH] Added Docker release for CLI, updated documentation, added docker-specific features to CLI --- ...r-publish-cli-multiarch-release-custom.yml | 32 +++++++++++ Dockerfile cli-tool | 20 +++++++ Dockerfile.cli-tool | 20 +++++++ cmd/cli-uploader/Main.go | 32 +++++++++-- cmd/cli-uploader/cliconfig/cliconfig.go | 4 ++ cmd/cli-uploader/cliflags/cliflags.go | 53 ++++++++++++++++++- docs/advanced.rst | 49 ++++++++++++++++- 7 files changed, 202 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/docker-publish-cli-multiarch-release-custom.yml create mode 100644 Dockerfile cli-tool create mode 100644 Dockerfile.cli-tool diff --git a/.github/workflows/docker-publish-cli-multiarch-release-custom.yml b/.github/workflows/docker-publish-cli-multiarch-release-custom.yml new file mode 100644 index 0000000..f558800 --- /dev/null +++ b/.github/workflows/docker-publish-cli-multiarch-release-custom.yml @@ -0,0 +1,32 @@ +name: Docker Publish CLI Custom Release Multiarch + +on: + workflow_dispatch: + inputs: + tagname: + description: 'Tag name to be built' + required: true + + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - run: git checkout tags/${{ github.event.inputs.tagname }} + - name: install buildx + id: buildx + uses: crazy-max/ghaction-docker-buildx@v1 + with: + version: latest + - name: login to docker hub + run: echo "${{ secrets.DOCKER_PW }}" | docker login -u "${{ secrets.DOCKER_USER }}" --password-stdin + - name: build and push the image + run: | + docker buildx build -f Dockerfile.cli-tool --push --tag f0rc3/gokapi-cli:${{ github.event.inputs.tagname }} --platform linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 . diff --git a/Dockerfile cli-tool b/Dockerfile cli-tool new file mode 100644 index 0000000..caf93c2 --- /dev/null +++ b/Dockerfile cli-tool @@ -0,0 +1,20 @@ +FROM golang:1.24.0-alpine AS build_base + +## Usage: +## docker build . -f Dockerfile.cli-tool -t gokapi-cli +## docker run --rm -v gokapi-cli-config:/app/config gokapi-cli + +RUN mkdir /compile +COPY go.mod /compile +RUN cd /compile && go mod download + +COPY . /compile + +RUN cd /compile && go generate ./... && CGO_ENABLED=0 go build -ldflags="-s -w -X 'github.com/forceu/gokapi/internal/environment.IsDocker=true' -X 'github.com/forceu/gokapi/internal/environment.Builder=Project Docker File' -X 'github.com/forceu/gokapi/internal/environment.BuildTime=$(date)'" -o /compile/gokapi-cli github.com/forceu/gokapi/cmd/cli-uploader + +FROM alpine:3.19 + +COPY --from=build_base /compile/gokapi-cli /app/gokapi-cli +WORKDIR /app + +ENTRYPOINT ["/app/gokapi-cli"] diff --git a/Dockerfile.cli-tool b/Dockerfile.cli-tool new file mode 100644 index 0000000..2e809e3 --- /dev/null +++ b/Dockerfile.cli-tool @@ -0,0 +1,20 @@ +FROM golang:1.24.0-alpine AS build_base + +## Usage: +## docker build . -f Dockerfile.cli-tool -t gokapi-cli +## docker run -it --rm -v gokapi-cli-config:/app/config gokapi-cli + +RUN mkdir /compile +COPY go.mod /compile +RUN cd /compile && go mod download + +COPY . /compile + +RUN cd /compile && go generate ./... && CGO_ENABLED=0 go build -ldflags="-s -w -X 'github.com/forceu/gokapi/internal/environment.IsDocker=true' -X 'github.com/forceu/gokapi/internal/environment.Builder=Project Docker File' -X 'github.com/forceu/gokapi/internal/environment.BuildTime=$(date)'" -o /compile/gokapi-cli github.com/forceu/gokapi/cmd/cli-uploader + +FROM alpine:3.19 + +COPY --from=build_base /compile/gokapi-cli /app/gokapi-cli +WORKDIR /app + +ENTRYPOINT ["/app/gokapi-cli"] diff --git a/cmd/cli-uploader/Main.go b/cmd/cli-uploader/Main.go index 296d262..ca94b9a 100644 --- a/cmd/cli-uploader/Main.go +++ b/cmd/cli-uploader/Main.go @@ -6,14 +6,17 @@ import ( "github.com/forceu/gokapi/cmd/cli-uploader/cliapi" "github.com/forceu/gokapi/cmd/cli-uploader/cliconfig" "github.com/forceu/gokapi/cmd/cli-uploader/cliflags" + "github.com/forceu/gokapi/internal/environment" + "github.com/forceu/gokapi/internal/helper" "os" ) func main() { + cliflags.Init(cliconfig.DockerFolderConfigFile, cliconfig.DockerFolderUpload) mode := cliflags.Parse() switch mode { case cliflags.ModeLogin: - cliconfig.CreateLogin() + doLogin() case cliflags.ModeLogout: doLogout() case cliflags.ModeUpload: @@ -23,6 +26,11 @@ func main() { } } +func doLogin() { + checkDockerFolders() + cliconfig.CreateLogin() +} + func processUpload() { cliconfig.Load() uploadParam := cliflags.GetUploadParameters() @@ -36,11 +44,12 @@ func processUpload() { if uploadParam.JsonOutput { jsonStr, _ := json.Marshal(result) fmt.Println(string(jsonStr)) - } else { - fmt.Println("File uploaded successfully") - fmt.Println("File ID: " + result.Id) - fmt.Println("File Download URL: " + result.UrlDownload) + return } + fmt.Println("Upload successful") + fmt.Println("File Name: " + result.Name) + fmt.Println("File ID: " + result.Id) + fmt.Println("File Download URL: " + result.UrlDownload) } func doLogout() { @@ -52,3 +61,16 @@ func doLogout() { } fmt.Println("Logged out. To login again, run: gokapi-cli login") } + +func checkDockerFolders() { + if !environment.IsDockerInstance() { + return + } + if !helper.FolderExists(cliconfig.DockerFolderConfig) { + fmt.Println("Warning: Docker folder does not exist, configuration will be lost when creating a new container") + helper.CreateDir(cliconfig.DockerFolderConfig) + } + if !helper.FolderExists(cliconfig.DockerFolderUpload) { + helper.CreateDir(cliconfig.DockerFolderUpload) + } +} diff --git a/cmd/cli-uploader/cliconfig/cliconfig.go b/cmd/cli-uploader/cliconfig/cliconfig.go index 84dfb7e..54197bd 100644 --- a/cmd/cli-uploader/cliconfig/cliconfig.go +++ b/cmd/cli-uploader/cliconfig/cliconfig.go @@ -15,6 +15,10 @@ import ( const minGokapiVersionInt = 20100 const minGokapiVersionStr = "2.1.0" +const DockerFolderConfig = "/app/config/" +const DockerFolderConfigFile = DockerFolderConfig + "config.json" +const DockerFolderUpload = "/upload/" + type configFile struct { Url string `json:"Url"` Apikey string `json:"Apikey"` diff --git a/cmd/cli-uploader/cliflags/cliflags.go b/cmd/cli-uploader/cliflags/cliflags.go index e4d3448..d44c02c 100644 --- a/cmd/cli-uploader/cliflags/cliflags.go +++ b/cmd/cli-uploader/cliflags/cliflags.go @@ -2,7 +2,9 @@ package cliflags import ( "fmt" + "github.com/forceu/gokapi/internal/environment" "os" + "path/filepath" "strconv" ) @@ -13,6 +15,9 @@ const ( ModeInvalid ) +var dockerConfigFile string +var dockerUploadFolder string + type UploadConfig struct { File string JsonOutput bool @@ -22,6 +27,11 @@ type UploadConfig struct { Password string } +func Init(dockerConfigPath, dockerUploadPath string) { + dockerConfigFile = dockerConfigPath + dockerUploadFolder = dockerUploadPath +} + func Parse() int { if len(os.Args) < 2 { printUsage() @@ -59,8 +69,17 @@ func GetUploadParameters() UploadConfig { } } if result.File == "" { - fmt.Println("ERROR: Missing parameter -f") - os.Exit(2) + if environment.IsDockerInstance() { + ok, dockerFile := getDockerUpload() + if !ok { + fmt.Println("ERROR: Missing parameter -f and no file or more than one file found in " + dockerUploadFolder) + os.Exit(2) + } + result.File = dockerFile + } else { + fmt.Println("ERROR: Missing parameter -f") + os.Exit(2) + } } if result.ExpiryDownloads < 0 { result.ExpiryDownloads = 0 @@ -71,7 +90,37 @@ func GetUploadParameters() UploadConfig { return result } +func getDockerUpload() (bool, string) { + if !environment.IsDockerInstance() { + return false, "" + } + entries, err := os.ReadDir(dockerUploadFolder) + if err != nil { + return false, "" + } + + var fileName string + var fileFound bool + for _, entry := range entries { + if entry.Type().IsRegular() { + if fileFound { + // More than one file exist + return false, "" + } + fileName = entry.Name() + fileFound = true + } + } + if !fileFound { + return false, "" + } + return true, filepath.Join(dockerUploadFolder, fileName) +} + func GetConfigLocation() string { + if environment.IsDockerInstance() { + return dockerConfigFile + } for i := 2; i < len(os.Args); i++ { switch os.Args[i] { case "-c": diff --git a/docs/advanced.rst b/docs/advanced.rst index 65fe0cb..99c0e8e 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -210,13 +210,20 @@ Migrating Redis (``127.0.0.1:6379, User: test, Password: 1234, Prefix: gokapi_, CLI Tool ******************************** -Gokapi also has a CLI tool that allows uploads from the command line. Binaries are avaible on the release page (``gokapi-cli``) for Linux, Windows and MacOs. To compile it yourself, download the repository and run ``make build-cli`` in the top directory. +Gokapi also has a CLI tool that allows uploads from the command line. Binaries are avaible on the `Github release page `_ for Linux, Windows and MacOS. To compile it yourself, download the repository and run ``make build-cli`` in the top directory. + +Alternatively you can use the tool with Docker, although it will be slightly less user-friendly. + +.. note:: + + Gokapi v2.1.0 or newer is required to use the CLI tool. Login ================================= First you need to login with the command ``gokapi-cli login``. You will then be asked for your server URL and a valid API key with upload permission. If end-to-end encryption is enabled, you will also need to enter your encyption key. By default the login data is saved to ``gokapi-cli.json``, but you can define a different location with the ``-c`` parameter. + To logout, either delete the configuration file or run ``gokapi-cli logout``. .. warning:: @@ -224,6 +231,17 @@ To logout, either delete the configuration file or run ``gokapi-cli logout``. The configuration file contains the login data as plain text. +Docker +--------------------------------- + +If you are using Docker, your config will always be saved to ``/app/config/config.json`` and the location cannot be changed. To login, execute the following command: + + docker run -it --rm -v gokapi-cli-config:/app/config docker.io/f0rc3/gokapi-cli:latest login + +The volume ``gokapi-cli-config:/app/config`` is not required if you re-use the container, but it is still highly recommended. If the volume is not mounted, you will need to log in again after every new container creation. + + + Upload ================================= @@ -244,11 +262,40 @@ To upload a file, simply run ``gokapi-cli upload -f /path/to/file``. By default | -c [path] | Use the configuration file specified | +-----------------------------+---------------------------------------------------+ +Example: Uploading the file ``/tmp/example``. It will expire in 10 days, has unlimited downloads and requires the password ``abcd``: +:: + gokapi-cli upload -f /tmp/example --expiry-days 10 --password abcd + + .. warning:: If you are using end-to-end encryption, do not upload other encrypted files simultaneously to avoid race conditions. + + +Docker +--------------------------------- + +As a Docker container cannot access your host files without a volume, you will need to mount the folder that contains your file to upload and then specify the internal file path with ``-f``. If no ``-f`` parameter is supplied and only a single file exists in the container folder ``/upload/``, this file will be uploaded. + +Example: Uploading the file ``/tmp/example``. It will expire after 5 downloads, has no time expiry and has no password. +:: + + docker run --rm -v gokapi-cli-config:/app/config -v /tmp/:/upload/ docker.io/f0rc3/gokapi-cli:latest upload -f /upload/example --expiry-downloads 5 + +Example: Uploading the file ``/tmp/single/example``. There is no other file in the folder ``/tmp/single/``. +:: + + docker run --rm -v gokapi-cli-config:/app/config -v /tmp/single/:/upload/ docker.io/f0rc3/gokapi-cli:latest upload + +Example: Uploading the file ``/tmp/multiple/example``. There are other files in the folder ``/tmp/multiple/``. +:: + + docker run --rm -v gokapi-cli-config:/app/config -v /tmp/multiple/example:/upload/example docker.io/f0rc3/gokapi-cli:latest upload + + + .. _api: