diff --git a/.dockerignore b/.dockerignore index 533e519..323e743 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ * !rootfs/ +!Dockerfile diff --git a/.editorconfig b/.editorconfig index 9adc5ba..04aa034 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,7 +21,7 @@ indent_size = 4 indent_style = tab indent_size = 4 -[{*.sh,*.bats,post_push,post_push.tmpl.php}] +[*.{sh,bats}] indent_style = space indent_size = 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3a95d37 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,105 @@ +name: CI + +on: + pull_request: + push: + schedule: + - cron: '0 10 * * 5' + +env: + PUBLISH: ${{ github.event_name == 'push' + && (startsWith(github.ref, 'refs/tags/0') + || startsWith(github.ref, 'refs/tags/1') + || startsWith(github.ref, 'refs/tags/2') + || startsWith(github.ref, 'refs/tags/3') + || startsWith(github.ref, 'refs/tags/4') + || startsWith(github.ref, 'refs/tags/5') + || startsWith(github.ref, 'refs/tags/6') + || startsWith(github.ref, 'refs/tags/7') + || startsWith(github.ref, 'refs/tags/8') + || startsWith(github.ref, 'refs/tags/9')) }} + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: docker/setup-buildx-action@v1 + + - uses: satackey/action-docker-layer-caching@v0.0.11 + continue-on-error: true + if: ${{ env.PUBLISH != 'true' && github.ref != 'refs/heads/master' }} + - run: make docker.image no-cache=no tag=build-${{ github.run_number }} + if: ${{ env.PUBLISH != 'true' && github.ref != 'refs/heads/master' }} + + - run: make docker.image no-cache=yes tag=build-${{ github.run_number }} + if: ${{ env.PUBLISH == 'true' || github.ref == 'refs/heads/master' }} + + - run: make npm.install + - run: make test.docker tag=build-${{ github.run_number }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GCR_BOT_PAT }} + if: ${{ env.PUBLISH == 'true' }} + - name: Login to Quay.io + uses: docker/login-action@v1 + with: + registry: quay.io + username: instrumentisto+bot + password: ${{ secrets.QUAYIO_ROBOT_TOKEN }} + if: ${{ env.PUBLISH == 'true' }} + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: instrumentistobot + password: ${{ secrets.DOCKERHUB_BOT_PASS }} + if: ${{ env.PUBLISH == 'true' }} + + - run: make docker.tags of=build-${{ github.run_number }} + if: ${{ env.PUBLISH == 'true' }} + - run: make docker.push + if: ${{ env.PUBLISH == 'true' }} + + # On GitHub Container Registry README is automatically updated on pushes. + - name: Update README on Quay.io + uses: christian-korneck/update-container-description-action@v1 + env: + DOCKER_APIKEY: ${{ secrets.QUAYIO_API_TOKEN }} + with: + provider: quay + destination_container_repo: quay.io/instrumentisto/pure-ftpd + readme_file: README.md + if: ${{ env.PUBLISH == 'true' }} + - name: Update README on Docker Hub + uses: christian-korneck/update-container-description-action@v1 + env: + DOCKER_USER: instrumentistobot + DOCKER_PASS: ${{ secrets.DOCKERHUB_BOT_PASS }} + with: + provider: dockerhub + destination_container_repo: instrumentisto/pure-ftpd + readme_file: README.md + if: ${{ env.PUBLISH == 'true' }} + + - name: Parse release version from Git tag + id: release + run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} + if: ${{ env.PUBLISH == 'true' }} + - name: Parse CHANGELOG link + id: changelog + run: echo ::set-output name=LINK::https://github.com/${{ github.repository }}/blob/${{ steps.release.outputs.VERSION }}/CHANGELOG.md#$(sed -n '/^## \[${{ steps.release.outputs.VERSION }}\]/{s/^## \[\(.*\)\][^0-9]*\([0-9].*\)/\1--\2/;s/[^0-9a-z-]*//g;p;}' CHANGELOG.md) + if: ${{ env.PUBLISH == 'true' }} + - name: Release on GitHub + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.release.outputs.VERSION }} + release_name: ${{ steps.release.outputs.VERSION }} + body: | + [Changelog](${{ steps.changelog.outputs.LINK }}) + if: ${{ env.PUBLISH == 'true' }} diff --git a/.gitignore b/.gitignore index 007c538..3f4c570 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ /.idea/ -*.iml +/*.iml .DS_Store /node_modules/ +/package-lock.json /yarn.lock /yarn-error.log diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 028e6e4..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: bash - -sudo: false - -services: - - docker - -before_script: - - make image no-cache=yes VERSION=test - - make deps.bats - -script: - - make test VERSION=test - -notifications: - email: - on_success: never - on_failure: always diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..627126d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +Pure-FTPd image changelog +========================= + +All user visible changes to this project will be documented in this file. This project uses [Semantic Versioning 2.0.0]. + + + + +## [1.0.49-r0] ยท 2021-01-25 +[1.0.49-r0]: /../../tree/1.0.49-r0 + +[Diff](/../../compare/1.0.48...1.0.49-r0) + +### Upgraded + +- [Pure-FTPd] 1.0.49: +- [s6-overlay] 2.0.0.1: +- [Alpine Linux] 3.12: + + + + +## Previous releases + +See [GitHub releases](/../../releases). + + + + + +[Alpine Linux]: https://www.alpinelinux.org +[Pure-FTPd]: https://github.com/jedisct1/pure-ftpd +[s6-overlay]: https://github.com/just-containers/s6-overlay +[Semantic Versioning 2.0.0]: https://semver.org diff --git a/Dockerfile b/Dockerfile index f0f2ded..0ad0aa5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,13 @@ # https://hub.docker.com/_/alpine FROM alpine:3.12 +ARG pure_ftpd_ver=1.0.49 +ARG s6_overlay_ver=2.0.0.1 +ARG build_rev=0 + +LABEL org.opencontainers.image.source="\ + https://github.com/instrumentisto/pure-ftpd-docker-image" + # Build and install Pure-FTPd RUN apk update \ @@ -25,7 +32,7 @@ RUN apk update \ \ # Download and prepare Pure-FTPd sources && curl -fL -o /tmp/pure-ftpd.tar.gz \ - https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.49.tar.gz \ + https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-${pure_ftpd_ver}.tar.gz \ && (echo "7e7d4c81c5237624051bde53db6d9abcbae565a4e8a88627d81d369064b475f4b56305c105ed275264cce068844caad25b2014f41e2540058553222151fe3af8 /tmp/pure-ftpd.tar.gz" \ | sha512sum -c -) \ && tar -xzf /tmp/pure-ftpd.tar.gz -C /tmp/ \ @@ -71,7 +78,7 @@ RUN apk update \ RUN apk add --update --no-cache --virtual .tool-deps \ curl \ && curl -fL -o /tmp/s6-overlay.tar.gz \ - https://github.com/just-containers/s6-overlay/releases/download/v2.0.0.1/s6-overlay-amd64.tar.gz \ + https://github.com/just-containers/s6-overlay/releases/download/v${s6_overlay_ver}/s6-overlay-amd64.tar.gz \ && tar -xzf /tmp/s6-overlay.tar.gz -C / \ \ # Cleanup unnecessary stuff diff --git a/Makefile b/Makefile index 3f82165..566014e 100644 --- a/Makefile +++ b/Makefile @@ -1,101 +1,131 @@ -# This Makefile automates possible operations of this project. -# -# Images and description on Docker Hub will be automatically rebuilt on -# pushes to `master` branch of this repo and on updates of parent image. -# -# Note! Docker Hub `post_push` hook must be always up-to-date with default -# values of current Makefile. To update it just use: -# make post-push-hook -# -# It's still possible to build, tag and push images manually. Just use: -# make release - - -IMAGE_NAME := instrumentisto/pure-ftpd -VERSION ?= 1.0.49 -TAGS ?= 1.0.49,1.0,1,latest - +############################### +# Common defaults/definitions # +############################### comma := , + +# Checks two given strings for equality. eq = $(if $(or $(1),$(2)),$(and $(findstring $(1),$(2)),\ $(findstring $(2),$(1))),1) -# Build Docker image. -# -# Usage: -# make image [VERSION=] -# [no-cache=(no|yes)] -image: - docker build --network=host --force-rm \ - $(if $(call eq,$(no-cache),yes),--no-cache --pull,) \ - -t $(IMAGE_NAME):$(VERSION) . +###################### +# Project parameters # +###################### + +PURE_FTPD_VER ?= $(strip \ + $(shell grep 'ARG pure_ftpd_ver=' Dockerfile | cut -d '=' -f2)) +S6_OVERLAY_VER ?= $(strip \ + $(shell grep 'ARG s6_overlay_ver=' Dockerfile | cut -d '=' -f2)) +BUILD_REV ?= $(strip \ + $(shell grep 'ARG build_rev=' Dockerfile | cut -d '=' -f2)) + +NAMESPACES := instrumentisto \ + ghcr.io/instrumentisto \ + quay.io/instrumentisto +NAME := pure-ftpd +TAGS ?= $(PURE_FTPD_VER)-r$(BUILD_REV) \ + $(PURE_FTPD_VER) \ + $(strip $(shell echo $(PURE_FTPD_VER) | cut -d '.' -f1,2)) \ + $(strip $(shell echo $(PURE_FTPD_VER) | cut -d '.' -f1)) \ + latest +VERSION ?= $(word 1,$(subst $(comma), ,$(TAGS))) + + + + +########### +# Aliases # +########### + +image: docker.image + +push: docker.push + +release: git.release + +tags: docker.tags + +test: test.docker + + + + +################### +# Docker commands # +################### +docker-namespaces = $(strip $(if $(call eq,$(namespaces),),\ + $(NAMESPACES),$(subst $(comma), ,$(namespaces)))) +docker-tags = $(strip $(if $(call eq,$(tags),),\ + $(TAGS),$(subst $(comma), ,$(tags)))) -# Tag Docker image with given tags. +# Build Docker image with the given tag. # # Usage: -# make tags [VERSION=] -# [TAGS=[,...]] - -tags: - $(foreach tag,$(subst $(comma), ,$(TAGS)),\ - $(call tags.do,$(VERSION),$(tag))) -define tags.do - $(eval from := $(strip $(1))) - $(eval to := $(strip $(2))) - docker tag $(IMAGE_NAME):$(from) $(IMAGE_NAME):$(to) -endef +# make docker.image [tag=($(VERSION)|)]] [no-cache=(no|yes)] +# [PURE_FTPD_VER=] +# [S6_OVERLAY_VER=] +# [BUILD_REV=] +docker.image: + docker build --network=host --force-rm \ + $(if $(call eq,$(no-cache),yes),--no-cache --pull,) \ + --build-arg pure_ftpd_ver=$(PURE_FTPD_VER) \ + --build-arg s6_overlay_ver=$(S6_OVERLAY_VER) \ + --build-arg build_rev=$(BUILD_REV) \ + -t instrumentisto/$(NAME):$(if $(call eq,$(tag),),$(VERSION),$(tag)) ./ -# Manually push Docker images to Docker Hub. +# Manually push Docker images to container registries. # # Usage: -# make push [TAGS=[,...]] - -push: - $(foreach tag,$(subst $(comma), ,$(TAGS)),\ - $(call push.do,$(tag))) -define push.do - $(eval tag := $(strip $(1))) - docker push $(IMAGE_NAME):$(tag) +# make docker.push [tags=($(TAGS)|[,...])] +# [namespaces=($(NAMESPACES)|[,...])] + +docker.push: + $(foreach tag,$(subst $(comma), ,$(docker-tags)),\ + $(foreach namespace,$(subst $(comma), ,$(docker-namespaces)),\ + $(call docker.push.do,$(namespace),$(tag)))) +define docker.push.do + $(eval repo := $(strip $(1))) + $(eval tag := $(strip $(2))) + docker push $(repo)/$(NAME):$(tag) endef - -# Make manual release of Docker images to Docker Hub. +# Tag Docker image with the given tags. # # Usage: -# make release [VERSION=] [no-cache=(no|yes)] -# [TAGS=[,...]] +# make docker.tags [of=($(VERSION)|)] +# [tags=($(TAGS)|[,...])] +# [namespaces=($(NAMESPACES)|[,...])] -release: | image tags push +docker-tags-of = $(if $(call eq,$(of),),$(VERSION),$(of)) +docker.tags: + $(foreach tag,$(subst $(comma), ,$(docker-tags)),\ + $(foreach namespace,$(subst $(comma), ,$(docker-namespaces)),\ + $(call docker.tags.do,$(docker-tags-of),$(namespace),$(tag)))) +define docker.tags.do + $(eval from := $(strip $(1))) + $(eval repo := $(strip $(2))) + $(eval to := $(strip $(3))) + docker tag instrumentisto/$(NAME):$(from) $(repo)/$(NAME):$(to) +endef -# Create `post_push` Docker Hub hook. -# -# When Docker Hub triggers automated build all the tags defined in `post_push` -# hook will be assigned to built image. It allows to link the same image with -# different tags, and not to build identical image for each tag separately. -# See details: -# http://windsock.io/automated-docker-image-builds-with-multiple-tags -# -# Usage: -# make post-push-hook [TAGS=[,...]] +docker.test: test.docker + -post-push-hook: - @mkdir -p hooks/ - docker run --rm -i -v "$(PWD)/post_push.tmpl.php":/post_push.php:ro \ - php:alpine php -f /post_push.php -- \ - --image_tags='$(TAGS)' \ - > hooks/post_push +#################### +# Testing commands # +#################### # Run Bats tests for Docker image. # @@ -103,26 +133,68 @@ post-push-hook: # https://github.com/bats-core/bats-core # # Usage: -# make test [VERSION=] +# make test.docker [tag=($(VERSION)|)] -test: +test.docker: ifeq ($(wildcard node_modules/.bin/bats),) - @make deps.bats + @make npm.install +endif + IMAGE=instrumentisto/$(NAME):$(if $(call eq,$(tag),),$(VERSION),$(tag)) \ + node_modules/.bin/bats \ + --timing $(if $(call eq,$(CI),),--pretty,--formatter tap) \ + tests/main.bats + + + + +################ +# NPM commands # +################ + +# Resolve project NPM dependencies. +# +# Usage: +# make npm.install [dockerized=(no|yes)] + +npm.install: +ifeq ($(dockerized),yes) + docker run --rm --network=host -v "$(PWD)":/app/ -w /app/ \ + node \ + make npm.install dockerized=no +else + npm install endif - IMAGE=$(IMAGE_NAME):$(VERSION) node_modules/.bin/bats test/suite.bats -# Resolve project dependencies for running tests with Yarn. + +################ +# Git commands # +################ + +# Release project version (apply version tag and push). # # Usage: -# make deps.bats +# make git.release [ver=($(VERSION)|)] + +git-release-tag = $(strip $(if $(call eq,$(ver),),$(VERSION),$(ver))) + +git.release: +ifeq ($(shell git rev-parse $(git-release-tag) >/dev/null 2>&1 && echo "ok"),ok) + $(error "Git tag $(git-release-tag) already exists") +endif + git tag $(git-release-tag) master + git push origin refs/tags/$(git-release-tag) + -deps.bats: - docker run --rm -v "$(PWD)":/app -w /app \ - node:alpine \ - yarn install --non-interactive --no-progress +################## +# .PHONY section # +################## -.PHONY: image tags push release post-push-hook test deps.bats +.PHONY: image push release tags test \ + docker.image docker.push docker.tags docker.test \ + git.release \ + npm.install \ + test.docker diff --git a/README.md b/README.md index 38cfbe5..a4c7031 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,23 @@ -Pure-FTPd Docker Image +Pure-FTPd Docker image ====================== -[![GitHub release](https://img.shields.io/github/release/instrumentisto/pure-ftpd-docker-image.svg)](https://hub.docker.com/r/instrumentisto/pure-ftpd/tags) [![Build Status](https://travis-ci.org/instrumentisto/pure-ftpd-docker-image.svg?branch=master)](https://travis-ci.org/instrumentisto/pure-ftpd-docker-image) [![Docker Pulls](https://img.shields.io/docker/pulls/instrumentisto/pure-ftpd.svg)](https://hub.docker.com/r/instrumentisto/pure-ftpd) [![Uses](https://img.shields.io/badge/uses-s6--overlay-blue.svg)][21] +[![Release](https://img.shields.io/github/v/release/instrumentisto/pure-ftpd-docker-image "Release")](https://github.com/instrumentisto/pure-ftpd-docker-image/releases) +[![CI](https://github.com/instrumentisto/pure-ftpd-docker-image/workflows/CI/badge.svg?branch=master "CI")](https://github.com/instrumentisto/pure-ftpd-docker-image/actions?query=workflow%3ACI+branch%3Amaster) +[![Docker Hub](https://img.shields.io/docker/pulls/instrumentisto/pure-ftpd?label=Docker%20Hub%20pulls "Docker Hub pulls")](https://hub.docker.com/r/instrumentisto/pure-ftpd) +[![Uses](https://img.shields.io/badge/uses-s6--overlay-blue.svg "Uses s6-overlay")](https://github.com/just-containers/s6-overlay) + +[Docker Hub](https://hub.docker.com/r/instrumentisto/pure-ftpd) +| [GitHub Container Registry](https://github.com/orgs/instrumentisto/packages/container/package/pure-ftpd) +| [Quay.io](https://quay.io/repository/instrumentisto/pure-ftpd) + +[Changelog](https://github.com/instrumentisto/pure-ftpd-docker-image/blob/master/CHANGELOG.md) + + + + +## Supported tags and respective `Dockerfile` links + +- [`1.0.49-r0`, `1.0.49`, `1.0`, `1`, `latest`][201] @@ -88,17 +104,24 @@ This variant is highly recommended when final image size being as small as possi ### `X` -Latest version of `X` Pure-FTPd major version. +Latest tag of `X` Pure-FTPd's major version. ### `X.Y` -Latest version of `X.Y` Pure-FTPd minor version. +Latest tag of `X.Y` Pure-FTPd's minor version. ### `X.Y.Z` -Concrete `X.Y.Z` version of Pure-FTPd. +Latest tag of a concrete `X.Y.Z` version of Pure-FTPd. + + +### `X.Y.Z-rN` + +Concrete `N` image revision tag of a Pure-FTPd's concrete `X.Y.Z` version. + +Once build, it's never updated. @@ -137,7 +160,7 @@ The [sources][90] for producing `instrumentisto/pure-ftpd` Docker images are lic ## Issues -We can't notice comments in the DockerHub so don't use them for reporting issue or asking question. +We can't notice comments in the [DockerHub] (or other container registries) so don't use them for reporting issue or asking question. If you have any problems with or questions about this image, please contact us through a [GitHub issue][3]. @@ -145,6 +168,8 @@ If you have any problems with or questions about this image, please contact us t +[DockerHub]: https://hub.docker.com + [1]: http://alpinelinux.org [2]: https://hub.docker.com/_/alpine [3]: https://github.com/instrumentisto/pure-ftpd-docker-image/issues @@ -161,3 +186,5 @@ If you have any problems with or questions about this image, please contact us t [91]: https://github.com/instrumentisto/pure-ftpd-docker-image/blob/master/LICENSE.md [92]: https://download.pureftpd.org/pub/pure-ftpd/doc/COPYING [101]: https://nvd.nist.gov/view/vuln/search-results?query=pure-ftpd&search_type=all&cves=on + +[201]: https://github.com/instrumentisto/pure-ftpd-docker-image/blob/master/Dockerfile diff --git a/hooks/post_push b/hooks/post_push deleted file mode 100644 index 9bff774..0000000 --- a/hooks/post_push +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# AUTOMATICALLY GENERATED -# DO NOT EDIT THIS FILE DIRECTLY, USE /post_push.tmpl.php - -set -e - -# Parse image name for repo name -tagStart=$(expr index "$IMAGE_NAME" :) -repoName=${IMAGE_NAME:0:tagStart-1} - -# Tag and push image for each additional tag -for tag in {1.0.49,1.0,1,latest}; do - docker tag $IMAGE_NAME ${repoName}:${tag} - docker push ${repoName}:${tag} -done diff --git a/post_push.tmpl.php b/post_push.tmpl.php deleted file mode 100644 index 2c21034..0000000 --- a/post_push.tmpl.php +++ /dev/null @@ -1,16 +0,0 @@ - -#!/bin/bash -# AUTOMATICALLY GENERATED -# DO NOT EDIT THIS FILE DIRECTLY, USE /post_push.tmpl.php - -set -e - -# Parse image name for repo name -tagStart=$(expr index "$IMAGE_NAME" :) -repoName=${IMAGE_NAME:0:tagStart-1} - -# Tag and push image for each additional tag -for tag in {}; do - docker tag $IMAGE_NAME ${repoName}:${tag} - docker push ${repoName}:${tag} -done diff --git a/test/suite.bats b/tests/main.bats similarity index 63% rename from test/suite.bats rename to tests/main.bats index 39265c1..2c0ae38 100644 --- a/test/suite.bats +++ b/tests/main.bats @@ -1,23 +1,6 @@ #!/usr/bin/env bats -@test "post_push hook is up-to-date" { - run sh -c "cat Makefile | grep 'TAGS ?= ' | cut -d ' ' -f 3" - [ "$status" -eq 0 ] - [ ! "$output" = '' ] - expected="$output" - - run sh -c "cat hooks/post_push | grep 'for tag in' \ - | cut -d '{' -f 2 \ - | cut -d '}' -f 1" - [ "$status" -eq 0 ] - [ ! "$output" = '' ] - actual="$output" - - [ "$actual" = "$expected" ] -} - - @test "pure-ftpd is installed" { run docker run --rm --entrypoint sh $IMAGE -c 'which pure-ftpd' [ "$status" -eq 0 ] @@ -29,7 +12,7 @@ } @test "pure-ftpd has correct version" { - run sh -c "cat Makefile | grep 'VERSION ?= ' | cut -d ' ' -f 3" + run sh -c "cat Dockerfile | grep 'ARG pure_ftpd_ver=' | cut -d '=' -f2" [ "$status" -eq 0 ] [ ! "$output" = '' ] expected="$output" @@ -57,7 +40,7 @@ @test "PURE_PASSWDFILE is converted to PURE_DBFILE on container start" { run docker run --rm \ - -v $(pwd)/test/resources/pureftpd.passwd:/etc/pureftpd.passwd:ro \ + -v $(pwd)/tests/resources/pureftpd.passwd:/etc/pureftpd.passwd:ro \ $IMAGE test -f /etc/pureftpd.pdb [ "$status" -eq 0 ] } diff --git a/test/resources/pureftpd.passwd b/tests/resources/pureftpd.passwd similarity index 100% rename from test/resources/pureftpd.passwd rename to tests/resources/pureftpd.passwd