diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..2807006 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,78 @@ +name: build + +on: + schedule: + - cron: "0 12 * * *" + push: + pull_request: + +env: + IMAGE_NAME: atmoz/sftp + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # for proper signature verification + submodules: true # for shunit2 + + - name: Build debian image + run: | + docker build . \ + --pull=true \ + --file=Dockerfile \ + --tag="$IMAGE_NAME:debian" \ + --tag="$IMAGE_NAME:latest" \ + --label="org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ + --label="org.opencontainers.image.revision=$GITHUB_SHA" \ + --label="org.opencontainers.image.created=$(date --rfc-3339=seconds)" + + - name: Test debian image + run: tests/run $IMAGE_NAME:debian + + - name: Build alpine image + run: | + docker build . \ + --pull=true \ + --file=Dockerfile-alpine \ + --tag="$IMAGE_NAME:alpine" \ + --label="org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ + --label="org.opencontainers.image.revision=$GITHUB_SHA" \ + --label="org.opencontainers.image.created=$(date --rfc-3339=seconds)" + + - name: Test alpine image + run: tests/run $IMAGE_NAME:alpine + + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + ignore: tests/shunit2 + + - name: Verify signature + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + uses: ./.github/actions/git-verify-ref + + - name: Push images to Docker Hub registry + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + run: | + echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login \ + -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin + + docker push $IMAGE_NAME # no tags specified to include all tags + + - name: Push images to GitHub registry + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com \ + -u ${{ github.actor }} --password-stdin + + TAG_DEBIAN=docker.pkg.github.com/$GITHUB_REPOSITORY/debian + TAG_ALPINE=docker.pkg.github.com/$GITHUB_REPOSITORY/alpine + docker tag $IMAGE_NAME:debian $TAG_DEBIAN + docker tag $IMAGE_NAME:alpine $TAG_ALPINE + docker push $TAG_DEBIAN + docker push $TAG_ALPINE + diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index ce9714e..0000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Docker - -on: - schedule: - - cron: "0 12 * * *" - push: - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - submodules: true - - - name: Verify signature - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: ./.github/actions/git-verify-ref - - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master - with: - ignore: tests/shunit2 - - - name: Run tests - run: tests/run - - push: - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - needs: test - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Define tags - id: define_tags - run: | - if [ "$GITHUB_REF" == "refs/heads/master" ]; then - echo "::set-output name=tags::debian" - else - echo "::set-output name=tags::" - fi - - - name: Build and push Docker images - uses: docker/build-push-action@v1.1.0 - with: - # Username used to log in to a Docker registry. If not set then no login will occur - username: ${{ secrets.DOCKER_HUB_USERNAME }} - # Password or personal access token used to log in to a Docker registry. If not set then no login will occur - password: ${{ secrets.DOCKER_HUB_PASSWORD }} - # Server address of Docker registry. If not set then will default to Docker Hub - #registry: # optional - # Docker repository to tag the image with - repository: atmoz/sftp - # Comma-delimited list of tags. These will be added to the registry/repository to form the image's tags - tags: ${{ steps.define_tags.outputs.tags }} # optional - # Automatically tags the built image with the git reference as per the readme - tag_with_ref: true # optional - # Automatically tags the built image with the git short SHA as per the readme - tag_with_sha: false # optional - # Path to the build context - #path: # optional, default is . - # Path to the Dockerfile (Default is '{path}/Dockerfile') - #dockerfile: # optional - # Sets the target stage to build - #target: # optional - # Always attempt to pull a newer version of the image - always_pull: true # optional - # Comma-delimited list of build-time variables - #build_args: # optional - # Comma-delimited list of images to consider as cache sources - #cache_froms: # optional - # Comma-delimited list of labels to add to the built image - #labels: # optional - # Adds labels with git repository information to the built image - add_git_labels: true # optional - # Whether to push the image - #push: # optional, default is true diff --git a/Dockerfile-alpine b/Dockerfile-alpine new file mode 100644 index 0000000..72a28c7 --- /dev/null +++ b/Dockerfile-alpine @@ -0,0 +1,21 @@ +FROM alpine:latest +MAINTAINER Adrian Dvergsdal [atmoz.net] + +# Steps done in one RUN layer: +# - Install packages +# - Fix default group (1000 does not exist) +# - OpenSSH needs /var/run/sshd to run +# - Remove generic host keys, entrypoint generates unique keys +RUN echo "@community http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \ + apk add --no-cache bash shadow@community openssh openssh-sftp-server && \ + sed -i 's/GROUP=1000/GROUP=100/' /etc/default/useradd && \ + mkdir -p /var/run/sshd && \ + rm -f /etc/ssh/ssh_host_*key* + +COPY files/sshd_config /etc/ssh/sshd_config +COPY files/create-sftp-user /usr/local/bin/ +COPY files/entrypoint / + +EXPOSE 22 + +ENTRYPOINT ["/entrypoint"] diff --git a/tests/run b/tests/run index 5412e72..406ae9a 100755 --- a/tests/run +++ b/tests/run @@ -1,20 +1,29 @@ #!/bin/bash # See: https://github.com/kward/shunit2 +argImage=$1 +argOutput=${2:-"quiet"} +argCleanup=${3:-"cleanup"} +testDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +imageName="$argImage" +sshKeyPri="/tmp/atmoz_sftp_test_rsa" +sshKeyPub="/tmp/atmoz_sftp_test_rsa.pub" + if [ $UID != 0 ] && ! groups | grep -qw docker; then echo "Run with sudo/root or add user $USER to group 'docker'" exit 1 fi -argBuild=${1:-"build"} -argOutput=${2:-"quiet"} -argCleanup=${3:-"cleanup"} -testDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -buildDir="$testDir/.." -imageName="atmoz/sftp_test" -buildOptions=(--tag "$imageName") -sshKeyPri="/tmp/atmoz_sftp_test_rsa" -sshKeyPub="/tmp/atmoz_sftp_test_rsa.pub" +if [ ! -f "$testDir/shunit2/shunit2" ]; then + echo "Could not find shunit2 in $testDir/shunit2." + echo "Run 'git submodule update --init'" + exit 2 +fi + +if [ -z "$argImage" ]; then + echo "Missing image name" + exit 3 +fi if [ "$argOutput" == "quiet" ]; then redirect="/dev/null" @@ -22,12 +31,6 @@ else redirect="/dev/stdout" fi -if [ ! -f "$testDir/shunit2/shunit2" ]; then - echo "Could not find shunit2 in $testDir/shunit2." - echo "Run 'git submodule update --init'" - exit 2 -fi - # clear argument list (or shunit2 will try to use them) set -- @@ -36,16 +39,6 @@ set -- ############################################################################## function oneTimeSetUp() { - if [ "$argBuild" == "build" ]; then - buildOptions+=("--no-cache" "--pull=true") - fi - - # Build image - if ! docker build "${buildOptions[@]}" "$buildDir"; then - echo "Build failed" - exit 1 - fi - # Generate temporary ssh keys for testing if [ ! -f "$sshKeyPri" ]; then ssh-keygen -t rsa -f "$sshKeyPri" -N '' > "$redirect" 2>&1 @@ -55,12 +48,6 @@ function oneTimeSetUp() { chmod go-rw "$sshKeyPri" } -function oneTimeTearDown() { - if [ "$argCleanup" == "cleanup" ]; then - docker image rm "$imageName" > "$redirect" 2>&1 - fi -} - function setUp() { # shellcheck disable=SC2154 containerName="atmoz_sftp_${_shunit_test_}"