1 Commits

Author SHA1 Message Date
dd9b8f1ebe chore(deps): update module github.com/spf13/cobra to v1 2025-05-29 13:07:23 +00:00
19 changed files with 307 additions and 454 deletions

View File

@ -1 +0,0 @@
app

View File

@ -1,13 +1,12 @@
# Editor configuration, see http://editorconfig.org
root = true root = true
[*] [*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space indent_style = space
insert_final_newline = false indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = false
[{Makefile,*.go}] [Makefile]
indent_style = tab indent_style = tab

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
Makefile eol=lf

View File

@ -1,29 +0,0 @@
name: "Lint Golang files"
on:
pull_request:
types: [ "opened", "reopened", "synchronize" ]
push:
branches: [ '**' ]
tags-ignore: [ '**' ]
workflow_dispatch: {}
permissions:
contents: read
jobs:
golangci:
name: "Run golang CI linter"
runs-on: ${{ matrix.os }}
strategy:
matrix:
go: [ stable ]
os: [ ubuntu-latest-amd64, ubuntu-latest-arm64 ]
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/setup-go@v5.5.0
with:
go-version: ${{ matrix.go }}
- uses: golangci/golangci-lint-action@v8.0.0
with:
version: v2.4.0 # renovate: datasource=github-releases depName=golangci/golangci-lint

View File

@ -1,42 +0,0 @@
name: "Run Golang tests"
on:
pull_request:
types: [ "opened", "reopened", "synchronize" ]
push:
branches: [ '**' ]
tags-ignore: [ '**' ]
workflow_dispatch: {}
jobs:
# integration-test:
# name: "Run integration tests"
# runs-on: ${{ matrix.os }}
# strategy:
# matrix:
# go: [ stable ]
# os: [ ubuntu-latest-amd64, ubuntu-latest-arm64 ]
# steps:
# - uses: actions/checkout@v4.3.0
# - uses: actions/setup-go@v5.5.0
# with:
# go-version: ${{ matrix.go }}
# - env:
# GOPROXY: ${{ vars.GOPROXY }}
# run: make test/integration
unit-test:
name: "Run unit tests"
runs-on: ${{ matrix.os }}
strategy:
matrix:
go: [ stable ]
os: [ ubuntu-latest-amd64, ubuntu-latest-arm64 ]
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/setup-go@v5.5.0
with:
go-version: ${{ matrix.go }}
- env:
GOPROXY: ${{ vars.GOPROXY }}
run: make test/unit

View File

@ -1,19 +0,0 @@
name: "Lint Markdown files"
on:
pull_request:
types: [ "opened", "reopened", "synchronize" ]
push:
branches: [ '*' ]
tags-ignore: [ '*' ]
workflow_dispatch: {}
jobs:
markdown-lint:
name: "Run markdown linter"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.3.0
- uses: DavidAnson/markdownlint-cli2-action@v20.0.0
with:
globs: '**/*.md'

View File

@ -1,52 +0,0 @@
name: "Release"
on:
push:
tags: [ '**' ]
permissions:
contents: write
jobs:
release:
name: "Release application"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.3.0
- uses: docker/setup-qemu-action@v3.6.0
- uses: actions/setup-go@v5.5.0
with:
go-version: stable
- uses: docker/login-action@v3.5.0
with:
registry: git.cryptic.systems
username: ${{ github.repository_owner }}
password: ${{ secrets.GIT_CRYPTIC_SYSTEMS_PACKAGE_REGISTRY_TOKEN }}
- env:
GITEA_TOKEN: ${{ secrets.GIT_CRYPTIC_SYSTEMS_PACKAGE_REGISTRY_TOKEN }}
GONOSUMDB: ${{ vars.GONOSUMDB }}
GOPROXY: ${{ vars.GOPROXY }}
uses: goreleaser/goreleaser-action@v6.4.0
with:
version: v2.11.2 # renovate: datasource=github-releases depName=goreleaser/goreleaser
args: release --clean
sync-to-hub-docker-io:
needs:
- release
runs-on: ubuntu-latest
steps:
- name: Copy images to docker.io
run: |
TAG=$(echo ${{ github.ref_name }} | sed 's/v//gm')
apt-get update --yes
apt-get install --yes skopeo
skopeo copy \
--all \
--dest-password ${{ secrets.DOCKER_IO_PASSWORD }} \
--dest-username ${{ secrets.DOCKER_IO_USERNAME }} \
--src-password ${{ secrets.GIT_CRYPTIC_SYSTEMS_PACKAGE_REGISTRY_TOKEN }} \
--src-username volker.raschek \
docker://git.cryptic.systems/volker.raschek/docker-hub-description-updater:${TAG} \
docker://docker.io/volkerraschek/dhcu:${TAG}

View File

@ -1,20 +0,0 @@
name: Update Docker Hub Description
on:
push:
branches: [ 'master' ]
paths: [ 'README.md' ]
workflow_dispatch: {}
jobs:
update-description-on-hub-docker-io:
runs-on:
- ubuntu-latest
steps:
- uses: actions/checkout@v4.3.0
- uses: peter-evans/dockerhub-description@v4.0.2
with:
username: ${{ secrets.DOCKER_IO_USERNAME }}
password: ${{ secrets.DOCKER_IO_PASSWORD }}
repository: volkerraschek/dhdu
readme-filepath: README.md

View File

@ -1,13 +0,0 @@
version: "2"
linters:
default: standard
enable:
- errname
- gosec
exclusions:
rules: []
warn-unused: true
run:
tests: true

View File

@ -1,136 +0,0 @@
# markdownlint YAML configuration
# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml
# Default state for all rules
default: true
# Path to configuration file to extend
extends: null
# MD003/heading-style/header-style - Heading style
MD003:
# Heading style
style: "atx"
# MD004/ul-style - Unordered list style
MD004:
style: "dash"
# MD007/ul-indent - Unordered list indentation
MD007:
# Spaces for indent
indent: 2
# Whether to indent the first level of the list
start_indented: false
# MD009/no-trailing-spaces - Trailing spaces
MD009:
# Spaces for line break
br_spaces: 2
# Allow spaces for empty lines in list items
list_item_empty_lines: false
# Include unnecessary breaks
strict: false
# MD010/no-hard-tabs - Hard tabs
MD010:
# Include code blocks
code_blocks: true
# MD012/no-multiple-blanks - Multiple consecutive blank lines
MD012:
# Consecutive blank lines
maximum: 1
# MD013/line-length - Line length
MD013:
# Number of characters
line_length: 120
# Number of characters for headings
heading_line_length: 120
# Number of characters for code blocks
code_block_line_length: 120
# Include code blocks
code_blocks: false
# Include tables
tables: false
# Include headings
headings: true
# Strict length checking
strict: false
# Stern length checking
stern: false
# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines
MD022:
# Blank lines above heading
lines_above: 1
# Blank lines below heading
lines_below: 1
# MD025/single-title/single-h1 - Multiple top-level headings in the same document
MD025:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD026/no-trailing-punctuation - Trailing punctuation in heading
MD026:
# Punctuation characters
punctuation: ".,;:!。,;:!"
# MD029/ol-prefix - Ordered list item prefix
MD029:
# List style
style: "one_or_ordered"
# MD030/list-marker-space - Spaces after list markers
MD030:
# Spaces for single-line unordered list items
ul_single: 1
# Spaces for single-line ordered list items
ol_single: 1
# Spaces for multi-line unordered list items
ul_multi: 1
# Spaces for multi-line ordered list items
ol_multi: 1
# MD033/no-inline-html - Inline HTML
MD033:
# Allowed elements
allowed_elements: []
# MD035/hr-style - Horizontal rule style
MD035:
# Horizontal rule style
style: "---"
# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading
MD036:
# Punctuation characters
punctuation: ".,;:!?。,;:!?"
# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading
MD041:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD044/proper-names - Proper names should have the correct capitalization
MD044:
# List of proper names
names: []
# Include code blocks
code_blocks: false
# MD046/code-block-style - Code block style
MD046:
# Block style
style: "fenced"
# MD048/code-fence-style - Code fence style
MD048:
# Code fence syle
style: "backtick"

253
Makefile
View File

@ -1,114 +1,195 @@
EXECUTABLE=docker-hub-description-updater # VERSION
VERSION?=$(shell git describe --abbrev=0)+hash.$(shell git rev-parse --short HEAD) # If no version is specified as a parameter of make, the last git hash
# value is taken.
# VERSION?=$(shell git describe --abbrev=0)+hash.$(shell git rev-parse --short HEAD)
VERSION?=0.0.0+hash.$(shell git rev-parse --short HEAD)
# Destination directory and prefix to place the compiled binaries, documentaions # EXECUTABLE
# and other files. # Executable binary which should be compiled for different architecures
DESTDIR?= EXECUTABLE:=dhdu
PREFIX?=/usr/local
# LINUX_EXECUTABLES AND TARGETS
LINUX_EXECUTABLES:=\
linux/amd64/${EXECUTABLE} \
linux/arm/5/${EXECUTABLE} \
linux/arm/7/${EXECUTABLE}
LINUX_EXECUTABLE_TARGETS:=${LINUX_EXECUTABLES:%=bin/%}
# UNIX_EXECUTABLES AND TARGETS
# Define all executables for different architectures and operation systems
UNIX_EXECUTABLES:=\
${LINUX_EXECUTABLES}
UNIX_EXECUTABLE_TARGETS:=\
${LINUX_EXECUTABLE_TARGETS}
# EXECUTABLE_TARGETS
# Include all UNIX and Windows targets.
EXECUTABLES:=\
${UNIX_EXECUTABLES}
EXECUTABLE_TARGETS:=\
${UNIX_EXECUTABLE_TARGETS}
# CONTAINER_RUNTIME # CONTAINER_RUNTIME
# The CONTAINER_RUNTIME variable will be used to specified the path to a # The CONTAINER_RUNTIME variable will be used to specified the path to a
# container runtime. This is needed to start and run a container image. # container runtime. This is needed to start and run a container images.
CONTAINER_RUNTIME?=$(shell which podman) CONTAINER_RUNTIME?=$(shell which docker)
# DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_REGISTRY_NAME # BUILD_IMAGE
# Defines the name of the new container to be built using several variables. # Definition of the container build image, in which the Binary are compiled from
DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_REGISTRY_NAME:=git.cryptic.systems # source code
DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_REGISTRY_USER:=volker.raschek BUILD_IMAGE_REGISTRY:=docker.io
BUILD_IMAGE_NAMESPACE:=volkerraschek
BUILD_IMAGE_NAME:=build-image
BUILD_IMAGE_VERSION:=latest
BUILD_IMAGE_FULL=${BUILD_IMAGE_REGISTRY}/${BUILD_IMAGE_NAMESPACE}/${BUILD_IMAGE_NAME}:${BUILD_IMAGE_VERSION:v%=%}
BUILD_IMAGE_SHORT=${BUILD_IMAGE_NAMESPACE}/${BUILD_IMAGE_NAME}:${BUILD_IMAGE_VERSION:v%=%}
DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_NAMESPACE?=${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_REGISTRY_USER} # BASE_IMAGE
DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_NAME:=${EXECUTABLE} # Definition of the base container image
DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_VERSION?=latest BASE_IMAGE_REGISTRY:=docker.io
DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_FULLY_QUALIFIED=${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_REGISTRY_NAME}/${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_NAMESPACE}/${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_NAME}:${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_VERSION} BASE_IMAGE_NAMESPACE:=library
BASE_IMAGE_NAME:=alpine
BASE_IMAGE_VERSION:=3.11.2
BASE_IMAGE_FULL=${BASE_IMAGE_REGISTRY}/${BASE_IMAGE_NAMESPACE}/${BASE_IMAGE_NAME}:${BASE_IMAGE_VERSION:v%=%}
BASE_IMAGE_SHORT=${BASE_IMAGE_NAMESPACE}/${BASE_IMAGE_NAME}:${BASE_IMAGE_VERSION:v%=%}
# BIN # CONTAINER_IMAGE
# Definition of the container image
CONTAINER_IMAGE_REGISTRY:=docker.io
CONTAINER_IMAGE_NAMESPACE:=volkerraschek
CONTAINER_IMAGE_NAME:=${EXECUTABLE}
CONTAINER_IMAGE_VERSION:=latest
CONTAINER_IMAGE_FULL=${CONTAINER_IMAGE_REGISTRY}/${CONTAINER_IMAGE_NAMESPACE}/${CONTAINER_IMAGE_NAME}:${CONTAINER_IMAGE_VERSION:v%=%}
CONTAINER_IMAGE_SHORT=${CONTAINER_IMAGE_NAMESPACE}/${CONTAINER_IMAGE_NAME}:${CONTAINER_IMAGE_VERSION:v%=%}
README_FILE:=README.md
# BINARIES
# ============================================================================== # ==============================================================================
docker-hub-description-updater: PHONY:=all
${EXECUTABLE}: bin/tmp/${EXECUTABLE}
all: ${EXECUTABLE_TARGETS}
bin/linux/amd64/${EXECUTABLE}: bindata
CGO_ENABLED=0 \ CGO_ENABLED=0 \
GOPROXY=$(shell go env GOPROXY) \ GOOS=linux \
go build -ldflags "-X 'main.version=${VERSION}'" -o ${@} main.go GOARCH=amd64 \
GOPROXY=${GOPROXY} \
GOPRIVATE=${GOPRIVATE} \
go build -ldflags "-X main.version=${VERSION}" -o ${@}
bin/linux/arm/5/${EXECUTABLE}: bindata
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=arm \
GOARM=5 \
GOPROXY=${GOPROXY} \
GOPRIVATE=${GOPRIVATE} \
go build -ldflags "-X main.version=${VERSION}" -o ${@}
bin/linux/arm/7/${EXECUTABLE}: bindata
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=arm \
GOARM=5 \
GOPROXY=${GOPROXY} \
GOPRIVATE=${GOPRIVATE} \
go build -ldflags "-X main.version=${VERSION}" -o ${@}
bin/tmp/${EXECUTABLE}: bindata
GOPROXY=${GOPROXY} \
GOPRIVATE=${GOPRIVATE} \
go build -ldflags "-X main.version=${VERSION}" -o ${@}
# BINDATA
# ==============================================================================
BINDATA_TARGETS := \
pkg/hub/bindata.go
PHONY+=bindata
bindata: ${BINDATA_TARGETS}
pkg/hub/bindata.go:
go-bindata -pkg hub -o ${@} README.md
# TEST
# ==============================================================================
PHONY+=test
test: clean bin/tmp/${EXECUTABLE}
REGISTRY_USER=${REGISTRY_USER} \
REGISTRY_PASSWORD=${REGISTRY_PASSWORD} \
REGISTRY_NAMESPACE=${REGISTRY_NAMESPACE} \
CONTAINER_IMAGE_NAME=${CONTAINER_IMAGE_NAME} \
README_FILE=${README_FILE} \
go test -v ./pkg/...
# CLEAN # CLEAN
# ============================================================================== # ==============================================================================
PHONY+=clean PHONY+=clean
clean: clean:
rm --force --recursive docker-hub-description-updater rm --force ${EXECUTABLE} || true
rm --force --recursive bin || true
rm --force --recursive ${BINDATA_TARGETS} || true
# TESTS # CONTAINER IMAGE STEPS
# ============================================================================== # ==============================================================================
PHONY+=test/unit PHONY+=container-image/build/amd64
test/unit: container-image/build/amd64:
CGO_ENABLED=0 \
GOPROXY=$(shell go env GOPROXY) \
go test -v -p 1 -coverprofile=coverage.txt -covermode=count -timeout 1200s ./pkg/...
PHONY+=test/integration
test/integration:
CGO_ENABLED=0 \
GOPROXY=$(shell go env GOPROXY) \
go test -v -p 1 -count=1 -timeout 1200s ./it/...
PHONY+=test/coverage
test/coverage: test/unit
CGO_ENABLED=0 \
GOPROXY=$(shell go env GOPROXY) \
go tool cover -html=coverage.txt
# GOLANGCI-LINT
# ==============================================================================
PHONY+=golangci-lint
golangci-lint:
golangci-lint run --concurrency=$(shell nproc)
# INSTALL
# ==============================================================================
PHONY+=uninstall
install: docker-hub-description-updater
install --directory ${DESTDIR}/etc/bash_completion.d
./docker-hub-description-updater completion bash > ${DESTDIR}/etc/bash_completion.d/${EXECUTABLE}
install --directory ${DESTDIR}${PREFIX}/bin
install --mode 0755 ${EXECUTABLE} ${DESTDIR}${PREFIX}/bin/${EXECUTABLE}
install --directory ${DESTDIR}${PREFIX}/share/licenses/${EXECUTABLE}
install --mode 0644 LICENSE ${DESTDIR}${PREFIX}/share/licenses/${EXECUTABLE}/LICENSE
# UNINSTALL
# ==============================================================================
PHONY+=uninstall
uninstall:
-rm --force --recursive \
${DESTDIR}/etc/bash_completion.d/${EXECUTABLE} \
${DESTDIR}${PREFIX}/bin/${EXECUTABLE} \
${DESTDIR}${PREFIX}/share/licenses/${EXECUTABLE}
# BUILD CONTAINER IMAGE
# ==============================================================================
PHONY+=container-image/build
container-image/build:
${CONTAINER_RUNTIME} build \ ${CONTAINER_RUNTIME} build \
--build-arg BASE_IMAGE=${BASE_IMAGE_FULL} \
--build-arg BUILD_IMAGE=${BUILD_IMAGE_FULL} \
--build-arg EXECUTABLE=${EXECUTABLE} \
--build-arg EXECUTABLE_TARGET=bin/linux/amd64/${EXECUTABLE} \
--build-arg GOPROXY \
--build-arg GOPRIVATE \
--build-arg VERSION=${VERSION} \ --build-arg VERSION=${VERSION} \
--file Dockerfile \ --file Dockerfile \
--no-cache \ --no-cache \
--pull \ --tag ${CONTAINER_IMAGE_FULL} \
--tag ${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_FULLY_QUALIFIED} \ --tag ${CONTAINER_IMAGE_SHORT} \
. .
# DELETE CONTAINER IMAGE PHONY+=container-image/push/amd64
# ============================================================================== container-image/push/amd64: container-image/build/amd64
PHONY:=container-image/delete ${CONTAINER_RUNTIME} login ${CONTAINER_IMAGE_REGISTRY} \
container-image/delete: --username ${CONTAINER_IMAGE_REGISTRY_USER} \
- ${CONTAINER_RUNTIME} image rm ${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_FULLY_QUALIFIED} --password ${CONTAINER_IMAGE_REGISTRY_PASSWORD}
${CONTAINER_RUNTIME} push ${CONTAINER_IMAGE_FULL}
# PUSH CONTAINER IMAGE # CONTAINER STEPS - BINARY
# ============================================================================== # ==============================================================================
PHONY+=container-image/push PHONY+=container-run/all
container-image/push: container-run/all:
echo ${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_REGISTRY_PASSWORD} | ${CONTAINER_RUNTIME} login ${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_REGISTRY_NAME} --username ${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_REGISTRY_USER} --password-stdin $(MAKE) container-run COMMAND=${@:container-run/%=%}
${CONTAINER_RUNTIME} push ${DOCKERHUB_DESCRIPTION_UPDATER_IMAGE_FULLY_QUALIFIED}
PHONY+=${UNIX_EXECUTABLE_TARGETS:%=container-run/%}
${UNIX_EXECUTABLE_TARGETS:%=container-run/%}:
$(MAKE) container-run COMMAND=${@:container-run/%=%}
# CONTAINER STEPS - CLEAN
# ==============================================================================
PHONY+=container-run/clean
container-run/clean:
$(MAKE) container-run COMMAND=${@:container-run/%=%}
# GENERAL CONTAINER COMMAND
# ==============================================================================
PHONY+=container-run
container-run:
${CONTAINER_RUNTIME} run \
--rm \
--volume ${PWD}:/workspace \
${BUILD_IMAGE_FULL} \
make ${COMMAND} \
VERSION=${VERSION} \
GOPROXY=${GOPROXY} \
GOPRIVATE=${GOPRIVATE} \
# PHONY # PHONY
# ============================================================================== # ==============================================================================
# Declare the contents of the PHONY variable as phony. We keep that information
# in a variable so we can use it in if_changed.
.PHONY: ${PHONY} .PHONY: ${PHONY}

View File

@ -1,5 +1,8 @@
# docker hub description updater # docker hub description updater
[![Build Status](https://travis-ci.com/volker-raschek/docker-hub-description-updater.svg?branch=master)](https://travis-ci.com/volker-raschek/docker-hub-description-updater)
[![Go Report Card](https://goreportcard.com/badge/github.com/volker-raschek/docker-hub-description-updater)](https://goreportcard.com/report/github.com/volker-raschek/docker-hub-description-updater)
[![GoDoc Reference](https://godoc.org/github.com/volker-raschek/docker-hub-description-updater?status.svg)](http://godoc.org/github.com/volker-raschek/docker-hub-description-updater)
[![Docker Pulls](https://img.shields.io/docker/pulls/volkerraschek/dhdu)](https://hub.docker.com/r/volkerraschek/dhdu) [![Docker Pulls](https://img.shields.io/docker/pulls/volkerraschek/dhdu)](https://hub.docker.com/r/volkerraschek/dhdu)
By specifying the login data for [hub.docker.com](https://hub.docker.com) you By specifying the login data for [hub.docker.com](https://hub.docker.com) you
@ -7,6 +10,8 @@ can update the short and long description of a docker repository.
## Usage ## Usage
Test
The examples below describe two ways to update the full description of the The examples below describe two ways to update the full description of the
docker hub repository. First by the binary and second by a container based way. docker hub repository. First by the binary and second by a container based way.

View File

@ -2,6 +2,7 @@ package cmd
import ( import (
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
@ -18,7 +19,7 @@ var (
) )
// Execute a // Execute a
func Execute(version string) error { func Execute(version string) {
rootCmd := &cobra.Command{ rootCmd := &cobra.Command{
Use: "dhdu", Use: "dhdu",
Short: "docker hub description updater (dhdu)", Short: "docker hub description updater (dhdu)",
@ -30,7 +31,7 @@ func Execute(version string) error {
rootCmd.Flags().StringVarP(&dockerHubPassword, "password", "p", "", "Docker Hub Password") rootCmd.Flags().StringVarP(&dockerHubPassword, "password", "p", "", "Docker Hub Password")
rootCmd.Flags().StringVarP(&dockerHubRepository, "repository", "r", "", "Docker Hub Repository") rootCmd.Flags().StringVarP(&dockerHubRepository, "repository", "r", "", "Docker Hub Repository")
rootCmd.Flags().StringVarP(&dockerHubUser, "username", "u", "", "Docker Hub Username") rootCmd.Flags().StringVarP(&dockerHubUser, "username", "u", "", "Docker Hub Username")
return rootCmd.Execute() rootCmd.Execute()
} }
func runE(cmd *cobra.Command, args []string) error { func runE(cmd *cobra.Command, args []string) error {
@ -38,30 +39,29 @@ func runE(cmd *cobra.Command, args []string) error {
file := args[0] file := args[0]
if len(dockerHubUser) <= 0 { if len(dockerHubUser) <= 0 {
return fmt.Errorf("no user defined over flags") return fmt.Errorf("No user defined over flags")
} }
if len(dockerHubPassword) <= 0 { if len(dockerHubPassword) <= 0 {
return fmt.Errorf("no password defined over flags") return fmt.Errorf("No password defined over flags")
} }
if len(dockerHubNamespace) <= 0 { if len(dockerHubNamespace) <= 0 {
log.Printf("no namespace defined over flags: Use docker username %v instead", dockerHubUser) log.Printf("No namespace defined over flags: Use docker username %v instead", dockerHubUser)
dockerHubNamespace = dockerHubUser dockerHubNamespace = dockerHubUser
} }
if len(dockerHubRepository) <= 0 { if len(dockerHubRepository) <= 0 {
return fmt.Errorf("nNo repository defined over flags") return fmt.Errorf("No repository defined over flags")
} }
if _, err := os.Stat(file); os.IsNotExist(err) && len(file) <= 0 { if _, err := os.Stat(file); os.IsNotExist(err) && len(file) <= 0 {
return fmt.Errorf("can not find file: %v", file) return fmt.Errorf("Can not find file: %v", file)
} }
// #nosec G304 f, err := ioutil.ReadFile(file)
f, err := os.ReadFile(file)
if err != nil { if err != nil {
return fmt.Errorf("can not read file %v: %v", file, err) return fmt.Errorf("Can not read file %v: %v", file, err)
} }
fullDescription := string(f) fullDescription := string(f)

16
go.mod
View File

@ -1,10 +1,16 @@
module github.com/volker-raschek/docker-hub-description-updater module github.com/volker-raschek/docker-hub-description-updater
go 1.24 go 1.18
require github.com/spf13/cobra v1.10.1
require ( require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.9 // indirect github.com/stretchr/testify v1.4.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
) )

25
go.sum
View File

@ -1,10 +1,27 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=

View File

@ -9,5 +9,5 @@ var (
) )
func main() { func main() {
_ = cmd.Execute(version) cmd.Execute(version)
} }

View File

@ -3,11 +3,13 @@ package hub
import "errors" import "errors"
var ( var (
errFailedToCreateRequest = errors.New("failed to create http request") errorFailedToCreateRequest = errors.New("Failed to create http request")
errFailedToParseJSON = errors.New("failed to parse json") errorFailedToParseJSON = errors.New("Failed to parse json")
errFailedToParseURL = errors.New("failed to parse url") errorFailedToParseURL = errors.New("Failed to parse url")
errFailedToSendRequest = errors.New("failed to send http request") errorFailedToSendRequest = errors.New("Failed to send http request")
errNoNamespaceDefined = errors.New("no Namespace defined") errorNoUserDefined = errors.New("No User defined")
errNoRepositoryDefined = errors.New("no Repository defined") errorNoPasswordDefined = errors.New("No Password defined")
errUnexpectedHTTPStatuscode = errors.New("unexpected HTTP-Statuscode") errorNoNamespaceDefined = errors.New("No Namespace defined")
errorNoRepositoryDefined = errors.New("No Repository defined")
errorUnexpectedHTTPStatuscode = errors.New("Unexpected HTTP-Statuscode")
) )

View File

@ -25,22 +25,22 @@ type Hub struct {
func (h *Hub) GetRepository(namespace string, name string) (*types.Repository, error) { func (h *Hub) GetRepository(namespace string, name string) (*types.Repository, error) {
if len(namespace) <= 0 { if len(namespace) <= 0 {
return nil, errNoNamespaceDefined return nil, errorNoNamespaceDefined
} }
if len(name) <= 0 { if len(name) <= 0 {
return nil, errNoRepositoryDefined return nil, errorNoRepositoryDefined
} }
rawURL := fmt.Sprintf("%v/repositories/%v/%v", dockerHubAPI, namespace, name) rawURL := fmt.Sprintf("%v/repositories/%v/%v", dockerHubAPI, namespace, name)
url, err := url.Parse(rawURL) url, err := url.Parse(rawURL)
if err != nil { if err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToParseURL, err) return nil, fmt.Errorf("%v: %v", errorFailedToParseURL, err)
} }
req, err := http.NewRequest(http.MethodGet, url.String(), nil) req, err := http.NewRequest(http.MethodGet, url.String(), nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToCreateRequest, err) return nil, fmt.Errorf("%v: %v", errorFailedToCreateRequest, err)
} }
if h.token == nil { if h.token == nil {
@ -54,18 +54,18 @@ func (h *Hub) GetRepository(namespace string, name string) (*types.Repository, e
resp, err := h.client.Do(req) resp, err := h.client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToSendRequest, err) return nil, fmt.Errorf("%v: %v", errorFailedToSendRequest, err)
} }
defer func() { _ = resp.Body.Close() }() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%v: expect %v, received %v", errUnexpectedHTTPStatuscode, http.StatusOK, resp.StatusCode) return nil, fmt.Errorf("%v: expect %v, received %v", errorUnexpectedHTTPStatuscode, http.StatusOK, resp.StatusCode)
} }
repository := new(types.Repository) repository := new(types.Repository)
jsonDecoder := json.NewDecoder(resp.Body) jsonDecoder := json.NewDecoder(resp.Body)
if err := jsonDecoder.Decode(repository); err != nil { if err := jsonDecoder.Decode(repository); err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToParseJSON, err) return nil, fmt.Errorf("%v: %v", errorFailedToParseJSON, err)
} }
return repository, nil return repository, nil
@ -75,30 +75,30 @@ func (h *Hub) getToken() (*types.Token, error) {
loginBuffer := new(bytes.Buffer) loginBuffer := new(bytes.Buffer)
jsonEncoder := json.NewEncoder(loginBuffer) jsonEncoder := json.NewEncoder(loginBuffer)
if err := jsonEncoder.Encode(h.credentials); err != nil { if err := jsonEncoder.Encode(h.credentials); err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToParseJSON, err) return nil, fmt.Errorf("%v: %v", errorFailedToParseJSON, err)
} }
rawURL := fmt.Sprintf("%v/users/login/", dockerHubAPI) rawURL := fmt.Sprintf("%v/users/login/", dockerHubAPI)
req, err := http.NewRequest(http.MethodPost, rawURL, loginBuffer) req, err := http.NewRequest(http.MethodPost, rawURL, loginBuffer)
if err != nil { if err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToCreateRequest, err) return nil, fmt.Errorf("%v: %v", errorFailedToCreateRequest, err)
} }
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
resp, err := h.client.Do(req) resp, err := h.client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToCreateRequest, err) return nil, fmt.Errorf("%v: %v", errorFailedToCreateRequest, err)
} }
defer func() { _ = resp.Body.Close() }() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%v: expect %v, received %v", errUnexpectedHTTPStatuscode, http.StatusOK, resp.StatusCode) return nil, fmt.Errorf("%v: expect %v, received %v", errorUnexpectedHTTPStatuscode, http.StatusOK, resp.StatusCode)
} }
token := new(types.Token) token := new(types.Token)
jsonDecoder := json.NewDecoder(resp.Body) jsonDecoder := json.NewDecoder(resp.Body)
if err := jsonDecoder.Decode(token); err != nil { if err := jsonDecoder.Decode(token); err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToParseJSON, err) return nil, fmt.Errorf("%v: %v", errorFailedToParseJSON, err)
} }
return token, nil return token, nil
@ -108,11 +108,11 @@ func (h *Hub) getToken() (*types.Token, error) {
func (h *Hub) PatchRepository(repository *types.Repository) (*types.Repository, error) { func (h *Hub) PatchRepository(repository *types.Repository) (*types.Repository, error) {
if len(repository.Namespcace) <= 0 { if len(repository.Namespcace) <= 0 {
return nil, errNoNamespaceDefined return nil, errorNoNamespaceDefined
} }
if len(repository.Name) <= 0 { if len(repository.Name) <= 0 {
return nil, errNoRepositoryDefined return nil, errorNoRepositoryDefined
} }
if h.token == nil { if h.token == nil {
@ -127,7 +127,7 @@ func (h *Hub) PatchRepository(repository *types.Repository) (*types.Repository,
rawURL := fmt.Sprintf("%v/repositories/%v/%v", dockerHubAPI, repository.Namespcace, repository.Name) rawURL := fmt.Sprintf("%v/repositories/%v/%v", dockerHubAPI, repository.Namespcace, repository.Name)
patchURL, err := url.Parse(rawURL) patchURL, err := url.Parse(rawURL)
if err != nil { if err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToParseURL, err) return nil, fmt.Errorf("%v: %v", errorFailedToParseURL, err)
} }
data := &url.Values{} data := &url.Values{}
@ -136,24 +136,24 @@ func (h *Hub) PatchRepository(repository *types.Repository) (*types.Repository,
req, err := http.NewRequest(http.MethodPatch, patchURL.String(), nil) req, err := http.NewRequest(http.MethodPatch, patchURL.String(), nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToCreateRequest, err) return nil, fmt.Errorf("%v: %v", errorFailedToCreateRequest, err)
} }
req.Header.Set("Authorization", fmt.Sprintf("JWT %v", h.token.Token)) req.Header.Set("Authorization", fmt.Sprintf("JWT %v", h.token.Token))
resp, err := h.client.Do(req) resp, err := h.client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToCreateRequest, err) return nil, fmt.Errorf("%v: %v", errorFailedToCreateRequest, err)
} }
defer func() { _ = resp.Body.Close() }() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%v: expect %v, received %v", errUnexpectedHTTPStatuscode, http.StatusOK, resp.StatusCode) return nil, fmt.Errorf("%v: expect %v, received %v", errorUnexpectedHTTPStatuscode, http.StatusOK, resp.StatusCode)
} }
patchedRepository := new(types.Repository) patchedRepository := new(types.Repository)
jsonDecoder := json.NewDecoder(resp.Body) jsonDecoder := json.NewDecoder(resp.Body)
if err := jsonDecoder.Decode(h.token); err != nil { if err := jsonDecoder.Decode(h.token); err != nil {
return nil, fmt.Errorf("%v: %v", errFailedToParseJSON, err) return nil, fmt.Errorf("%v: %v", errorFailedToParseJSON, err)
} }
return patchedRepository, nil return patchedRepository, nil

54
pkg/hub/hub_test.go Normal file
View File

@ -0,0 +1,54 @@
package hub
import (
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/volker-raschek/docker-hub-description-updater/pkg/types"
)
func TestPatchRepository(t *testing.T) {
dockerHubUser := os.Getenv("REGISTRY_USER")
if len(dockerHubUser) <= 0 {
t.Fatalf("Environment variable REGISTRY_USER is empty")
}
dockerHubPassword := os.Getenv("REGISTRY_PASSWORD")
if len(dockerHubPassword) <= 0 {
t.Fatalf("Environment variable REGISTRY_PASSWORD is empty")
}
dockerHubNamespace := os.Getenv("REGISTRY_NAMESPACE")
if len(dockerHubNamespace) <= 0 {
t.Fatalf("Environment variable REGISTRY_NAMESPACE is empty")
}
dockerHubRepository := os.Getenv("CONTAINER_IMAGE_NAME")
if len(dockerHubRepository) <= 0 {
t.Fatalf("Environment variable CONTAINER_IMAGE_NAME is empty")
}
loginCredentials := &types.LoginCredentials{
User: dockerHubUser,
Password: dockerHubPassword,
}
h := New(loginCredentials)
require := require.New(t)
readme, err := Asset("README.md")
require.NoError(err)
currentRepository, err := h.GetRepository(dockerHubNamespace, dockerHubRepository)
require.NoError(err)
expectedRepository := *currentRepository
expectedRepository.FullDescription = string(readme)
actualRepository, err := h.PatchRepository(&expectedRepository)
require.NoError(err)
require.EqualValues(&expectedRepository.FullDescription, actualRepository.FullDescription, "Full description not equal")
}