From 475e75b44e108b5ce8c533a9d0e5078a3f166b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Sun, 18 Apr 2021 19:00:19 +0200 Subject: [PATCH] Move main package to root and add Docker image (#127) - Move main package to root. - Update golanci-lint configuration. - Build and push Docker container image. - Refine GitHub Actions workflow. --- .devcontainer/Dockerfile | 35 ++ .devcontainer/devcontainer.json | 21 +- .../library-scripts/common-debian.sh | 395 ++++++++++++++++++ .../docker-in-docker-debian.sh | 186 +++++++++ .github/workflows/build.yml | 44 +- .github/workflows/release.yml | 19 +- .golangci.yml | 20 +- .goreleaser.yml | 18 +- .vscode/tasks.json | 4 +- Dockerfile | 3 + Makefile | 30 +- README.md | 67 ++- go.sum | 1 - cmd/seed/main.go => main.go | 0 cmd/seed/main_test.go => main_test.go | 0 15 files changed, 739 insertions(+), 104 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/library-scripts/common-debian.sh create mode 100644 .devcontainer/library-scripts/docker-in-docker-debian.sh create mode 100644 Dockerfile rename cmd/seed/main.go => main.go (100%) rename cmd/seed/main_test.go => main_test.go (100%) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..3cf7d45 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,35 @@ +FROM golang:1.16 + +# [Option] Install zsh +ARG INSTALL_ZSH="true" +# [Option] Upgrade OS packages to their latest versions +ARG UPGRADE_PACKAGES="false" +# [Option] Enable non-root Docker access in container +ARG ENABLE_NONROOT_DOCKER="true" +# [Option] Use the OSS Moby Engine instead of the licensed Docker Engine +ARG USE_MOBY="true" + +# Install needed packages and setup non-root user. Use a separate RUN statement to add your +# own dependencies. A user of "automatic" attempts to reuse an user ID if one already exists. +ARG USERNAME=automatic +ARG USER_UID=1000 +ARG USER_GID=$USER_UID +COPY library-scripts/*.sh /tmp/library-scripts/ +RUN apt-get update \ + && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" \ + # Use Docker script from script library to set things up + && /bin/bash /tmp/library-scripts/docker-in-docker-debian.sh "${ENABLE_NONROOT_DOCKER}" "${USERNAME}" "${USE_MOBY}" \ + # Clean up + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts/ + +VOLUME [ "/var/lib/docker" ] + +# Setting the ENTRYPOINT to docker-init.sh will start up the Docker Engine +# inside the container "overrideCommand": false is set in devcontainer.json. +# The script will also execute CMD if you need to alter startup behaviors. +ENTRYPOINT [ "/usr/local/share/docker-init.sh" ] +CMD [ "sleep", "infinity" ] + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9c82f99..a566764 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,26 +1,29 @@ // For format details, see https://aka.ms/vscode-remote/devcontainer.json or // https://github.com/microsoft/vscode-dev-containers { - "name": "Go", - "image": "golang:1.16", - "runArgs": [ - "--cap-add=SYS_PTRACE", - "--security-opt", - "seccomp=unconfined" - ], + "name": "Go with Docker in Docker", + "dockerFile": "Dockerfile", + "runArgs": ["--init", "--privileged"], + "overrideCommand": false, + // Set *default* container specific settings.json values on container create. "settings": { - "terminal.integrated.shell.linux": "/bin/bash", + "terminal.integrated.shell.linux": "/bin/zsh", "go.gopath": "/go" }, + // Add the IDs of extensions you want installed when the container is created. "extensions": [ - "golang.Go" + "golang.Go", + "ms-azuretools.vscode-docker" ], + // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], + // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "apt-get update && apt-get install -y git && make install" + // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. // "remoteUser": "vscode" } \ No newline at end of file diff --git a/.devcontainer/library-scripts/common-debian.sh b/.devcontainer/library-scripts/common-debian.sh new file mode 100644 index 0000000..1bb6a23 --- /dev/null +++ b/.devcontainer/library-scripts/common-debian.sh @@ -0,0 +1,395 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/master/script-library/docs/common.md +# Maintainer: The VS Code and Codespaces Teams +# +# Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag] [install Oh My Zsh! flag] [Add non-free packages] + +INSTALL_ZSH=${1:-"true"} +USERNAME=${2:-"automatic"} +USER_UID=${3:-"automatic"} +USER_GID=${4:-"automatic"} +UPGRADE_PACKAGES=${5:-"true"} +INSTALL_OH_MYS=${6:-"true"} +ADD_NON_FREE_PACKAGES=${7:-"false"} + +set -e + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Ensure that login shells get the correct path if the user updated the PATH using ENV. +rm -f /etc/profile.d/00-restore-env.sh +echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh +chmod +x /etc/profile.d/00-restore-env.sh + +# If in automatic mode, determine if a user already exists, if not use vscode +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=vscode + fi +elif [ "${USERNAME}" = "none" ]; then + USERNAME=root + USER_UID=0 + USER_GID=0 +fi + +# Load markers to see which steps have already run +MARKER_FILE="/usr/local/etc/vscode-dev-containers/common" +if [ -f "${MARKER_FILE}" ]; then + echo "Marker file found:" + cat "${MARKER_FILE}" + source "${MARKER_FILE}" +fi + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Function to call apt-get if needed +apt-get-update-if-needed() +{ + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies +if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then + + PACKAGE_LIST="apt-utils \ + git \ + openssh-client \ + gnupg2 \ + iproute2 \ + procps \ + lsof \ + htop \ + net-tools \ + psmisc \ + curl \ + wget \ + rsync \ + ca-certificates \ + unzip \ + zip \ + nano \ + vim-tiny \ + less \ + jq \ + lsb-release \ + apt-transport-https \ + dialog \ + libc6 \ + libgcc1 \ + libkrb5-3 \ + libgssapi-krb5-2 \ + libicu[0-9][0-9] \ + liblttng-ust0 \ + libstdc++6 \ + zlib1g \ + locales \ + sudo \ + ncdu \ + man-db \ + strace \ + manpages \ + manpages-dev " + + # Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian + if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then + CODENAME="$(cat /etc/os-release | grep -oE '^VERSION_CODENAME=.+$' | cut -d'=' -f2)" + sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${CODENAME} main/deb http:\/\/deb\.debian\.org\/debian ${CODENAME} main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${CODENAME} main/deb http:\/\/deb\.debian\.org\/debian ${CODENAME} main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${CODENAME}-updates main/deb http:\/\/deb\.debian\.org\/debian ${CODENAME}-updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${CODENAME}-updates main/deb http:\/\/deb\.debian\.org\/debian ${CODENAME}-updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${CODENAME}-backports main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${CODENAME}-backports main contrib non-free/" /etc/apt/sources.list + echo "Running apt-get update..." + apt-get update + PACKAGE_LIST="${PACKAGE_LIST} manpages-posix manpages-posix-dev" + else + apt-get-update-if-needed + fi + + # Install libssl1.1 if available + if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then + PACKAGE_LIST="${PACKAGE_LIST} libssl1.1" + fi + + # Install appropriate version of libssl1.0.x if available + LIBSSL=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '') + if [ "$(echo "$LIBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then + if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then + # Debian 9 + PACKAGE_LIST="${PACKAGE_LIST} libssl1.0.2" + elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then + # Ubuntu 18.04, 16.04, earlier + PACKAGE_LIST="${PACKAGE_LIST} libssl1.0.0" + fi + fi + + echo "Packages to verify are installed: ${PACKAGE_LIST}" + apt-get -y install --no-install-recommends ${PACKAGE_LIST} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 ) + + PACKAGES_ALREADY_INSTALLED="true" +fi + +# Get to latest versions of all packages +if [ "${UPGRADE_PACKAGES}" = "true" ]; then + apt-get-update-if-needed + apt-get -y upgrade --no-install-recommends + apt-get autoremove -y +fi + +# Ensure at least the en_US.UTF-8 UTF-8 locale is available. +# Common need for both applications and things like the agnoster ZSH theme. +if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then + echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen + locale-gen + LOCALE_ALREADY_SET="true" +fi + +# Create or update a non-root user to match UID/GID. +if id -u ${USERNAME} > /dev/null 2>&1; then + # User exists, update if needed + if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -G $USERNAME)" ]; then + groupmod --gid $USER_GID $USERNAME + usermod --gid $USER_GID $USERNAME + fi + if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then + usermod --uid $USER_UID $USERNAME + fi +else + # Create user + if [ "${USER_GID}" = "automatic" ]; then + groupadd $USERNAME + else + groupadd --gid $USER_GID $USERNAME + fi + if [ "${USER_UID}" = "automatic" ]; then + useradd -s /bin/bash --gid $USERNAME -m $USERNAME + else + useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME + fi +fi + +# Add add sudo support for non-root user +if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then + echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME + chmod 0440 /etc/sudoers.d/$USERNAME + EXISTING_NON_ROOT_USER="${USERNAME}" +fi + +# ** Shell customization section ** +if [ "${USERNAME}" = "root" ]; then + USER_RC_PATH="/root" +else + USER_RC_PATH="/home/${USERNAME}" +fi + +# .bashrc/.zshrc snippet +RC_SNIPPET="$(cat << 'EOF' + +if [ -z "${USER}" ]; then export USER=$(whoami); fi +if [[ "${PATH}" != *"$HOME/.local/bin"* ]]; then export PATH="${PATH}:$HOME/.local/bin"; fi + +# Display optional first run image specific notice if configured and terminal is interactive +if [ -t 1 ] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then + if [ -f "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" ]; then + cat "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" + elif [ -f "/workspaces/.codespaces/shared/first-run-notice.txt" ]; then + cat "/workspaces/.codespaces/shared/first-run-notice.txt" + fi + mkdir -p "$HOME/.config/vscode-dev-containers" + # Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it + ((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &) +fi + +EOF +)" + +# code shim, it fallbacks to code-insiders if code is not available +cat << 'EOF' > /usr/local/bin/code +#!/bin/sh + +get_in_path_except_current() { + which -a "$1" | grep -A1 "$0" | grep -v "$0" +} + +code="$(get_in_path_except_current code)" + +if [ -n "$code" ]; then + exec "$code" "$@" +elif [ "$(command -v code-insiders)" ]; then + exec code-insiders "$@" +else + echo "code or code-insiders is not installed" >&2 + exit 127 +fi +EOF +chmod +x /usr/local/bin/code + +# Codespaces bash and OMZ themes - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme +CODESPACES_BASH="$(cat \ +<<'EOF' + +# Codespaces bash prompt theme +__bash_prompt() { + local userpart='`export XIT=$? \ + && [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \ + && [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`' + local gitbranch='`\ + export BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null); \ + if [ "${BRANCH}" = "HEAD" ]; then \ + export BRANCH=$(git describe --contains --all HEAD 2>/dev/null); \ + fi; \ + if [ "${BRANCH}" != "" ]; then \ + echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \ + && if git ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \ + echo -n " \[\033[1;33m\]✗"; \ + fi \ + && echo -n "\[\033[0;36m\]) "; \ + fi`' + local lightblue='\[\033[1;34m\]' + local removecolor='\[\033[0m\]' + PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ " + unset -f __bash_prompt +} +__bash_prompt + +EOF +)" +CODESPACES_ZSH="$(cat \ +<<'EOF' +__zsh_prompt() { + local prompt_username + if [ ! -z "${GITHUB_USER}" ]; then + prompt_username="@${GITHUB_USER}" + else + prompt_username="%n" + fi + PROMPT="%{$fg[green]%}${prompt_username} %(?:%{$reset_color%}➜ :%{$fg_bold[red]%}➜ )" + PROMPT+='%{$fg_bold[blue]%}%~%{$reset_color%} $(git_prompt_info)%{$fg[white]%}$ %{$reset_color%}' + unset -f __zsh_prompt +} +ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[cyan]%}(%{$fg_bold[red]%}" +ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} " +ZSH_THEME_GIT_PROMPT_DIRTY=" %{$fg_bold[yellow]%}✗%{$fg_bold[cyan]%})" +ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg_bold[cyan]%})" +__zsh_prompt +EOF +)" + +# Add notice that Oh My Bash! has been removed from images and how to provide information on how to install manually +OMB_README="$(cat \ +<<'EOF' +"Oh My Bash!" has been removed from this image in favor of a simple shell prompt. If you +still wish to use it, remove "~/.oh-my-bash" and install it from: https://github.com/ohmybash/oh-my-bash +You may also want to consider "Bash-it" as an alternative: https://github.com/bash-it/bash-it +See here for infomation on adding it to your image or dotfiles: https://aka.ms/codespaces/omb-remove +EOF +)" +OMB_STUB="$(cat \ +<<'EOF' +#!/usr/bin/env bash +if [ -t 1 ]; then + cat $HOME/.oh-my-bash/README.md +fi +EOF +)" + +# Add RC snippet and custom bash prompt +if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then + echo "${RC_SNIPPET}" >> /etc/bash.bashrc + echo "${CODESPACES_BASH}" >> "${USER_RC_PATH}/.bashrc" + if [ "${USERNAME}" != "root" ]; then + echo "${CODESPACES_BASH}" >> "/root/.bashrc" + fi + chown ${USERNAME}:${USERNAME} "${USER_RC_PATH}/.bashrc" + RC_SNIPPET_ALREADY_ADDED="true" +fi + +# Add stub for Oh My Bash! +if [ ! -d "${USER_RC_PATH}/.oh-my-bash}" ] && [ "${INSTALL_OH_MYS}" = "true" ]; then + mkdir -p "${USER_RC_PATH}/.oh-my-bash" "/root/.oh-my-bash" + echo "${OMB_README}" >> "${USER_RC_PATH}/.oh-my-bash/README.md" + echo "${OMB_STUB}" >> "${USER_RC_PATH}/.oh-my-bash/oh-my-bash.sh" + chmod +x "${USER_RC_PATH}/.oh-my-bash/oh-my-bash.sh" + if [ "${USERNAME}" != "root" ]; then + echo "${OMB_README}" >> "/root/.oh-my-bash/README.md" + echo "${OMB_STUB}" >> "/root/.oh-my-bash/oh-my-bash.sh" + chmod +x "/root/.oh-my-bash/oh-my-bash.sh" + fi + chown -R "${USERNAME}:${USERNAME}" "${USER_RC_PATH}/.oh-my-bash" +fi + +# Optionally install and configure zsh and Oh My Zsh! +if [ "${INSTALL_ZSH}" = "true" ]; then + if ! type zsh > /dev/null 2>&1; then + apt-get-update-if-needed + apt-get install -y zsh + fi + if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then + echo "${RC_SNIPPET}" >> /etc/zsh/zshrc + ZSH_ALREADY_INSTALLED="true" + fi + + # Adapted, simplified inline Oh My Zsh! install steps that adds, defaults to a codespaces theme. + # See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for offical script. + OH_MY_INSTALL_DIR="${USER_RC_PATH}/.oh-my-zsh" + if [ ! -d "${OH_MY_INSTALL_DIR}" ] && [ "${INSTALL_OH_MYS}" = "true" ]; then + TEMPLATE_PATH="${OH_MY_INSTALL_DIR}/templates/zshrc.zsh-template" + USER_RC_FILE="${USER_RC_PATH}/.zshrc" + umask g-w,o-w + mkdir -p ${OH_MY_INSTALL_DIR} + git clone --depth=1 \ + -c core.eol=lf \ + -c core.autocrlf=false \ + -c fsck.zeroPaddedFilemode=ignore \ + -c fetch.fsck.zeroPaddedFilemode=ignore \ + -c receive.fsck.zeroPaddedFilemode=ignore \ + "https://github.com/ohmyzsh/ohmyzsh" "${OH_MY_INSTALL_DIR}" 2>&1 + echo -e "$(cat "${TEMPLATE_PATH}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${USER_RC_FILE} + sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="codespaces"/g' ${USER_RC_FILE} + mkdir -p ${OH_MY_INSTALL_DIR}/custom/themes + echo "${CODESPACES_ZSH}" > "${OH_MY_INSTALL_DIR}/custom/themes/codespaces.zsh-theme" + # Shrink git while still enabling updates + cd "${OH_MY_INSTALL_DIR}" + git repack -a -d -f --depth=1 --window=1 + # Copy to non-root user if one is specified + if [ "${USERNAME}" != "root" ]; then + cp -rf "${USER_RC_FILE}" "${OH_MY_INSTALL_DIR}" /root + chown -R ${USERNAME}:${USERNAME} "${USER_RC_PATH}" + fi + fi +fi + +# Write marker file +mkdir -p "$(dirname "${MARKER_FILE}")" +echo -e "\ + PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\ + LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\ + EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\ + RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\ + ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}" + +echo "Done!" diff --git a/.devcontainer/library-scripts/docker-in-docker-debian.sh b/.devcontainer/library-scripts/docker-in-docker-debian.sh new file mode 100644 index 0000000..bbd54c7 --- /dev/null +++ b/.devcontainer/library-scripts/docker-in-docker-debian.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/master/script-library/docs/docker-in-docker.md +# Maintainer: The VS Code and Codespaces Teams +# +# Syntax: ./docker-in-docker-debian.sh [enable non-root docker access flag] [non-root user] [use moby] + +ENABLE_NONROOT_DOCKER=${1:-"true"} +USERNAME=${2:-"automatic"} +USE_MOBY=${3:-"true"} + +set -e + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Determine the appropriate non-root user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then + USERNAME=root +fi + +# Function to run apt-get if needed +apt-get-update-if-needed() +{ + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Install docker/dockerd dependencies if missing +if ! dpkg -s apt-transport-https curl ca-certificates lsb-release lxc pigz iptables > /dev/null 2>&1 || ! type gpg > /dev/null 2>&1; then + apt-get-update-if-needed + apt-get -y install --no-install-recommends apt-transport-https curl ca-certificates lsb-release lxc pigz iptables gnupg2 +fi + +# Swap to legacy iptables for compatibility +update-alternatives --set iptables /usr/sbin/iptables-legacy +update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy + +# Install Docker / Moby CLI if not already installed +if type docker > /dev/null 2>&1 && type dockerd > /dev/null 2>&1; then + echo "Docker / Moby CLI and Engine already installed." +else + if [ "${USE_MOBY}" = "true" ]; then + DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]') + CODENAME=$(lsb_release -cs) + curl -s https://packages.microsoft.com/keys/microsoft.asc | (OUT=$(apt-key add - 2>&1) || echo $OUT) + echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-${DISTRO}-${CODENAME}-prod ${CODENAME} main" > /etc/apt/sources.list.d/microsoft.list + apt-get update + apt-get -y install --no-install-recommends moby-cli moby-buildx moby-engine + else + curl -fsSL https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/gpg | (OUT=$(apt-key add - 2>&1) || echo $OUT) + echo "deb [arch=amd64] https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]') $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list + apt-get update + apt-get -y install --no-install-recommends docker-ce-cli docker-ce + fi +fi + +echo "Finished installing docker / moby" + +# Install Docker Compose if not already installed +if type docker-compose > /dev/null 2>&1; then + echo "Docker Compose already installed." +else + LATEST_COMPOSE_VERSION=$(curl -sSL "https://api.github.com/repos/docker/compose/releases/latest" | grep -o -P '(?<="tag_name": ").+(?=")') + curl -sSL "https://github.com/docker/compose/releases/download/${LATEST_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose +fi + +# If init file already exists, exit +if [ -f "/usr/local/share/docker-init.sh" ]; then + echo "/usr/local/share/docker-init.sh already exists, so exiting." + exit 0 +fi +echo "docker-init doesnt exist..." + +# Add user to the docker group +if [ "${ENABLE_NONROOT_DOCKER}" = "true" ]; then + if ! getent group docker > /dev/null 2>&1; then + groupadd docker + fi + usermod -aG docker ${USERNAME} +fi + +tee /usr/local/share/docker-init.sh > /dev/null \ +<< 'EOF' +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +sudoIf() +{ + if [ "$(id -u)" -ne 0 ]; then + sudo "$@" + else + "$@" + fi +} + +# explicitly remove dockerd and containerd PID file to ensure that it can start properly if it was stopped uncleanly +# ie: docker kill +sudoIf find /run /var/run -iname 'docker*.pid' -delete || : +sudoIf find /run /var/run -iname 'container*.pid' -delete || : + +set -e + +## Dind wrapper script from docker team +# Maintained: https://github.com/moby/moby/blob/master/hack/dind + +export container=docker + +if [ -d /sys/kernel/security ] && ! sudoIf mountpoint -q /sys/kernel/security; then + sudoIf mount -t securityfs none /sys/kernel/security || { + echo >&2 'Could not mount /sys/kernel/security.' + echo >&2 'AppArmor detection and --privileged mode might break.' + } +fi + +# Mount /tmp (conditionally) +if ! sudoIf mountpoint -q /tmp; then + sudoIf mount -t tmpfs none /tmp +fi + +# cgroup v2: enable nesting +if [ -f /sys/fs/cgroup/cgroup.controllers ]; then + # move the init process (PID 1) from the root group to the /init group, + # otherwise writing subtree_control fails with EBUSY. + sudoIf mkdir -p /sys/fs/cgroup/init + sudoIf echo 1 > /sys/fs/cgroup/init/cgroup.procs + # enable controllers + sudoIf sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ + > /sys/fs/cgroup/cgroup.subtree_control +fi +## Dind wrapper over. + +# Handle DNS +set +e +cat /etc/resolv.conf | grep -i 'internal.cloudapp.net' +if [ $? -eq 0 ] +then + echo "Setting dockerd Azure DNS." + CUSTOMDNS="--dns 168.63.129.16" +else + echo "Not setting dockerd DNS manually." + CUSTOMDNS="" +fi +set -e + +# Start docker/moby engine +( sudoIf dockerd $CUSTOMDNS > /tmp/dockerd.log 2>&1 ) & + +set +e + +# Execute whatever commands were passed in (if any). This allows us +# to set this script to ENTRYPOINT while still executing the default CMD. +exec "$@" +EOF + +chmod +x /usr/local/share/docker-init.sh +chown ${USERNAME}:root /usr/local/share/docker-init.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab21a28..61c9aef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,15 +1,9 @@ name: build -on: - push: - branches: - - main - pull_request: - branches: - - '*' +on: [push, pull_request] jobs: - ci-build: + ci: strategy: fail-fast: false matrix: @@ -21,31 +15,11 @@ jobs: shell: bash steps: - uses: actions/checkout@v2 - with: - fetch-depth: 0 - uses: actions/setup-go@v2 with: go-version: 1.16 - - id: go-cache-paths - name: Get Go Cache paths - run: | - echo "::set-output name=go-build::$(go env GOCACHE)" - echo "::set-output name=go-mod::$(go env GOMODCACHE)" - - - name: Go Build Cache - uses: actions/cache@v2 - with: - path: ${{ steps.go-cache-paths.outputs.go-build }} - key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }} - - - name: Go Mod Cache - uses: actions/cache@v2 - with: - path: ${{ steps.go-cache-paths.outputs.go-mod }} - key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} - - name: Build run: make ci - name: Upload coverage @@ -65,3 +39,17 @@ jobs: with: file: ./coverage.out flags: ${{ runner.os }} + + release-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-go@v2 + with: + go-version: 1.16 + + - name: Release test + run: make build \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4a5cdc..b90476a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,23 +17,8 @@ jobs: with: go-version: 1.16 - - id: go-cache-paths - name: Get Go Cache paths - run: | - echo "::set-output name=go-build::$(go env GOCACHE)" - echo "::set-output name=go-mod::$(go env GOMODCACHE)" - - - name: Go Build Cache - uses: actions/cache@v2 - with: - path: ${{ steps.go-cache-paths.outputs.go-build }} - key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }} - - - name: Go Mod Cache - uses: actions/cache@v2 - with: - path: ${{ steps.go-cache-paths.outputs.go-mod }} - key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} + - name: "Docker login" + run: docker login docker.pkg.github.com -u docker -p ${{ secrets.GITHUB_TOKEN }} - name: Release run: make release diff --git a/.golangci.yml b/.golangci.yml index 20444de..551f8bc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -30,40 +30,36 @@ linters: - varcheck - bodyclose - depguard - - dogsled - dupl - exportloopref + - forcetypeassert - funlen - - gochecknoglobals - - gochecknoinits + - gci - gocognit - goconst - gocritic - gocyclo - godot - - gofmt - gofumpt - - goimports - golint - gomnd - goprintffuncname - gosec - - lll + - ifshort - misspell - - nakedret - noctx - nolintlint - rowserrcheck - sqlclosecheck - stylecheck + - thelper + - tparallel - unconvert - unparam - whitespace - - errorlint - - tparallel - - wrapcheck - # - godox - + # - errorlint + # - goerr113 + # - wrapcheck issues: # enable issues excluded by default exclude-use-default: false diff --git a/.goreleaser.yml b/.goreleaser.yml index 809288d..7573dcd 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -2,8 +2,7 @@ before: hooks: - go mod download builds: -- main: ./cmd/seed/main.go - env: +- env: - CGO_ENABLED=0 goos: - darwin @@ -18,3 +17,18 @@ archives: release: github: prerelease: auto +dockers: + - binaries: + - "{{ .ProjectName }}" + image_templates: + - "docker.pkg.github.com/golang-templates/seed/{{ .ProjectName }}:latest" + - "docker.pkg.github.com/golang-templates/seed/{{ .ProjectName }}:{{ .Major }}" + - "docker.pkg.github.com/golang-templates/seed/{{ .ProjectName }}:{{ .Major }}.{{ .Minor }}" + - "docker.pkg.github.com/golang-templates/seed/{{ .ProjectName }}:{{ .Major }}.{{ .Minor }}.{{ .Patch }}" + build_flag_templates: + - "--pull" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.name={{.ProjectName}}" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source={{.GitURL}}" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 884078f..c22650f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,9 +4,9 @@ "version": "2.0.0", "tasks": [ { - "label": "dev", + "label": "make", "type": "shell", - "command": "make dev", + "command": "make", "problemMatcher": [ "$go" ], diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9cf7253 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM scratch +COPY seed / +ENTRYPOINT ["/seed"] diff --git a/Makefile b/Makefile index d851b1c..0e0d48c 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,14 @@ .PHONY: dev dev: ## dev build -dev: clean install generate build fmt lint test mod-tidy build-snapshot +dev: clean install generate vet fmt lint test mod-tidy .PHONY: ci ci: ## CI build ci: dev diff .PHONY: clean -clean: ## remove files created during build +clean: ## remove files created during build pipeline $(call print-target) rm -rf dist rm -f coverage.* @@ -24,10 +24,10 @@ generate: ## go generate $(call print-target) go generate ./... -.PHONY: build -build: ## go build +.PHONY: vet +vet: ## go vet $(call print-target) - go build -o /dev/null ./... + go vet ./... .PHONY: fmt fmt: ## go fmt @@ -51,17 +51,18 @@ mod-tidy: ## go mod tidy go mod tidy cd tools && go mod tidy -.PHONY: build-snapshot -build-snapshot: ## goreleaser --snapshot --skip-publish --rm-dist - $(call print-target) - goreleaser --snapshot --skip-publish --rm-dist - .PHONY: diff diff: ## git diff $(call print-target) git diff --exit-code RES=$$(git status --porcelain) ; if [ -n "$$RES" ]; then echo $$RES && exit 1 ; fi +.PHONY: build +build: ## goreleaser --snapshot --skip-publish --rm-dist +build: install + $(call print-target) + goreleaser --snapshot --skip-publish --rm-dist + .PHONY: release release: ## goreleaser --rm-dist release: install @@ -70,20 +71,13 @@ release: install .PHONY: run run: ## go run - @go run -race ./cmd/seed + @go run -race . .PHONY: go-clean go-clean: ## go clean build, test and modules caches $(call print-target) go clean -r -i -cache -testcache -modcache -.PHONY: docker -docker: ## run in golang container, example: make docker run="make ci" - docker run --rm \ - -v $(CURDIR):/repo $(args) \ - -w /repo \ - golang:1.16 $(run) - .PHONY: help help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/README.md b/README.md index 1a1d21f..1adaaae 100644 --- a/README.md +++ b/README.md @@ -35,13 +35,46 @@ It includes: 1. Sign up on [Codecov](https://codecov.io/) and configure [Codecov GitHub Application](https://github.com/apps/codecov) for all repositories. 1. Click the `Use this template` button (alt. clone or download this repository). 1. Replace all occurences of `golang-templates/seed` to `your_org/repo_name` in all files. -1. Rename folder `cmd/seed` to `cmd/app_name` and update [.goreleaser.yml](.goreleaser.yml) accordingly. +1. Replace all occurences of `seed` to `repo_name` in [Dockerfile](Dockerfile). 1. Update [LICENSE](LICENSE) and [README.md](README.md). +## Setup + +Below you can find sample instructions on how to set up the development environment. +Of course you can use other tools like [GoLand](https://www.jetbrains.com/go/), [Vim](https://github.com/fatih/vim-go), [Emacs](https://github.com/dominikh/go-mode.el). However take notice that the Visual Studio Go extension is [officially supported](https://blog.golang.org/vscode-go) by the Go team. + +### Local Machine + +Follow these steps if you are OK installing and using Go on your machine. + +1. Install [Go](https://golang.org/doc/install). +1. Install [Visual Studio Code](https://code.visualstudio.com/). +1. Install [Go extension](https://code.visualstudio.com/docs/languages/go). +1. Clone and open this repository. +1. `F1` -> `Go: Install/Update Tools` -> (select all) -> OK. + +### Development Container + +Follow these steps if you do not want to install Go on your machine and you prefer to use a Development Container instead. + +1. Install [Visual Studio Code](https://code.visualstudio.com/). +1. Follow [Developing inside a Container - Getting Started](https://code.visualstudio.com/docs/remote/containers#_getting-started). +1. Clone and open this repository. +1. `F1` -> `Remote-Containers: Reopen in Container`. +1. `F1` -> `Go: Install/Update Tools` -> (select all) -> OK. + +The Development Container configuration mixes [Docker in Docker](https://github.com/microsoft/vscode-dev-containers/tree/master/containers/docker-in-docker) and [Go](https://github.com/microsoft/vscode-dev-containers/tree/master/containers/go) definitions. Thanks to it you can use `go`, `docker`, `docker-compose` inside the container. + ## Build -- Terminal: `make help` to get help for make targets. -- Visual Studio Code: `Terminal` → `Run Build Task... (CTRL+ALT+B)` to execute a fast build. +### Terminal + +- `make` - execute the build pipeline. +- `make help` - print help for provided [Make targets](Makefile). + +### Visual Studio Code + + `Terminal` → `Run Build Task... (Ctrl+Shift+B or ⇧⌘B)` to execute the build pipeline. ## Release @@ -54,12 +87,14 @@ _CAUTION_: Make sure to understand the consequences before you bump the major ve Remember to update Go version in [.github/workflows](.github/workflows), [Makefile](Makefile) and [devcontainer.json](.devcontainer/devcontainer.json). Notable files: + - [devcontainer.json](.devcontainer/devcontainer.json) - Visual Studio Code Remote Container configuration, - [.github/workflows](.github/workflows) - GitHub Actions workflows, - [.github/dependabot.yml](.github/dependabot.yml) - Dependabot configuration, - [.vscode](.vscode) - Visual Studio Code configuration files, - [.golangci.yml](.golangci.yml) - golangci-lint configuration, - [.goreleaser.yml](.goreleaser.yml) - GoReleaser configuration, +- [Dockerfile](Dockerfile) - Dockerfile used by GoReleaser to create a container image, - [Makefile](Makefile) - Make targets used for development, [CI build](.github/workflows) and [.vscode/tasks.json](.vscode/tasks.json), - [go.mod](go.mod) - [Go module definition](https://github.com/golang/go/wiki/Modules#gomod), - [tools.go](tools.go) - [build tools](https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module). @@ -68,7 +103,7 @@ Notable files: ### Why Visual Studio Code editor configuration -Developers that use Visual Studio Code can take advantage of the editor configuration. While others do not have to care about it. Setting configs for each repo is unnecessary time consuming. VS Code is the most popular Go editor ([survey](https://blog.golang.org/survey2019-results)) and it is officially [supported by the Go team](https://blog.golang.org/vscode-go). +Developers that use Visual Studio Code can take advantage of the editor configuration. While others do not have to care about it. Setting configs for each repo is unnecessary time consuming. VS Code is the most popular Go editor ([survey](https://blog.golang.org/survey2019-results)) and it is officially [supported by the Go team](https://blog.golang.org/vscode-go). You can always remove the [.devcontainer](.devcontainer) and [.vscode](.vscode) directories if it really does not help you. @@ -77,7 +112,7 @@ You can always remove the [.devcontainer](.devcontainer) and [.vscode](.vscode) GitHub Actions is out-of-the-box if you are already using GitHub. [Here](https://github.com/mvdan/github-actions-golang) you can learn how to use it for Go. -However, changing to any other CI server should be very simple, because this repository has build logic and tooling installation in Makefile. +However, changing to any other CI server should be very simple, because this repository has build logic and tooling installation in Makefile. You can also use the `docker` make target to run the build using a docker container. @@ -90,20 +125,22 @@ Alternatively use [WSL (Windows Subsystem for Linux)](https://docs.microsoft.com ### How can I create an application installation script 1. Install [GoDownloader](https://github.com/goreleaser/godownloader). -1. Execute: -```bash -godownloader --repo=your_org/repo_name > ./install.sh -``` +1. Execute: + + ```bash + godownloader --repo=your_org/repo_name > ./install.sh + ``` + +1. Push `install.sh` to your repository. -3. Push `install.sh` to your repository. 1. Add installation instructions to your `README.md` e.g.: -```bash -curl -sSfL https://raw.githubusercontent.com/your_org/repo_name/main/install.sh | sh -s -- -b /usr/local/bin -``` + ```bash + curl -sSfL https://raw.githubusercontent.com/your_org/repo_name/main/install.sh | sh -s -- -b /usr/local/bin + ``` -### How can I create a Docker image, deb/rpm/snap package, Homebrew Tap, Scoop App Manifest etc. +### How can I customize the release or add deb/rpm/snap packages, Homebrew Tap, Scoop App Manifest etc Take a look at GoReleaser [docs](https://goreleaser.com/customization/) as well as [its repo](https://github.com/goreleaser/goreleaser/) how it is dogfooding its functionality. @@ -119,7 +156,7 @@ release: prerelease: auto ``` -Alternatively, you can completly remove the usage of GoReleaser if you prefer handcrafted release notes. +Alternatively, you can completly remove the usage of GoReleaser if you prefer handcrafted release notes. Take a look how it is done in [taskflow](https://github.com/pellared/taskflow). ### Why the code coverage results are not accurate diff --git a/go.sum b/go.sum index 26500d5..acb88a4 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,6 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/cmd/seed/main.go b/main.go similarity index 100% rename from cmd/seed/main.go rename to main.go diff --git a/cmd/seed/main_test.go b/main_test.go similarity index 100% rename from cmd/seed/main_test.go rename to main_test.go