diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index d00f633..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,62 +0,0 @@ -version: 2 - build: - docker: - - image: circleci/golang:1-stretch-browsers-legacy - environment: - IMAGE_NAME: "cdalvaro/nerd-fonts-patcher" - - steps: - - checkout - - - setup_remote_docker: - version: 18.03.1-ce - - - run: - name: Docker info - command: | - docker version - docker info - - - restore_cache: - keys: - - cache-v2-{{ .Branch }} - paths: - - /tmp/cache/layers.tar - - - run: - name: Loading docker cache - command: | - if [[ -f /tmp/cache/layers.tar ]]; then - echo "Loading cache ..." - docker load -i /tmp/cache/layers.tar - docker image ls - else - echo "Couldn't find any caches" - fi - - - run: - name: Build docker image - command: | - docker build \ - --pull \ - --cache-from=${IMAGE_NAME} \ - --build-arg BUILD_DATE="$(date +"%Y-%m-%d %H:%M:%S%:z")" \ - --build-arg VCS_REF=$(git rev-parse --short HEAD) \ - -t ${IMAGE_NAME}:$(cat VERSION) . - - - run: - name: Generate docker build image cache - command: | - mkdir -p /tmp/cache/ - docker save -o /tmp/cache/layers.tar ${IMAGE_NAME} - - - save_cache: - key: cache-v2-{{ .Branch }} - paths: - - /tmp/cache/layers.tar - -workflows: - version: 2 - build: - jobs: - - build diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b044658 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: cdalvaro diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..56fc822 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + + # Configuration for Dockerfile + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + # Disable all pull requests for Docker dependencies + open-pull-requests-limit: 0 diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..480589d --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + - keep-alive +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..c092e22 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,114 @@ +name: Build and test Docker image + +on: + pull_request: + branches: + - main + paths-ignore: + - "./**/*.md" + - "LICENSE" + +env: + IMAGE_NAME: localhost:5000/cdalvaro/docker-nerd-fonts-patcher:${{ github.sha }} + REGISTRY_PATH: ${{ github.workspace }}/registry + CACHE_PATH: /tmp/.buildx-cache + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + driver-opts: network=host + + - name: Start Docker registry + run: | + docker run --rm --detach --publish 5000:5000 \ + --volume ${REGISTRY_PATH}:/var/lib/registry \ + --name registry registry:2 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: ${{ env.CACHE_PATH }} + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Build docker-nerd-fonts-patcher image + uses: docker/build-push-action@v3 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64,linux/arm/v7 + cache-from: | + type=local,src=${{ env.CACHE_PATH }} + ghcr.io/cdalvaro/docker-nerd-fonts-patcher:latest + cache-to: type=local,dest=${{ env.CACHE_PATH }} + push: true + tags: ${{ env.IMAGE_NAME }} + + - name: Stop Docker registry + run: docker stop registry + + - name: Upload Docker registry data for testing + uses: actions/upload-artifact@v3 + with: + name: docker-registry-data + path: ${{ env.REGISTRY_PATH }}/ + + test: + name: Test + runs-on: ubuntu-latest + needs: build + strategy: + matrix: + platform: [linux/amd64, linux/arm64, linux/arm/v7] + env: + DOCKER_CLI_EXPERIMENTAL: enabled + PLATFORM: ${{ matrix.platform }} + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Download Docker registry data from build job + uses: actions/download-artifact@v3 + with: + name: docker-registry-data + path: ${{ env.REGISTRY_PATH }} + + - name: Enable Docker experimental + run: | + # Enable docker daemon experimental support. + echo '{"experimental": true}' | sudo tee /etc/docker/daemon.json + sudo systemctl restart docker + # Install QEMU multi-architecture support for docker buildx. + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + + - name: Start Docker registry + run: | + docker run --rm --detach --publish 5000:5000 \ + --volume ${REGISTRY_PATH}:/var/lib/registry \ + --name registry registry:2 + sleep 10 + + - name: Import Docker images + run: docker pull --platform ${{ matrix.platform }} ${IMAGE_NAME} + + - name: Docker inspect + run: docker buildx imagetools inspect ${IMAGE_NAME} | grep '${{ matrix.platform }}' + + - name: Install ttx tool + run: sudo apt update -y && sudo apt install -y python3-fonttools fonttools + + - name: Execute tests + run: bash tests/patch-test.sh diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 0000000..df62dcd --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,25 @@ +name: Lint Code + +on: + pull_request: + branches: + - main + +jobs: + lint: + name: Super Linter + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Lint code base + uses: github/super-linter@v4.9.6 + env: + VALIDATE_ALL_CODEBASE: false + VALIDATE_DOCKERFILE_HADOLINT: true + VALIDATE_BASH: true + DEFAULT_BRANCH: main + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..fbff301 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,123 @@ +name: Publish Docker image + +on: + push: + branches: + - main + paths-ignore: + - './**/*.md' + - 'LICENSE' + release: + types: + - published + +env: + IMAGE_NAME: cdalvaro/docker-nerd-fonts-patcher + PLATFORMS: linux/amd64,linux/arm64,linux/arm/v7 + CACHE_PATH: /tmp/.buildx-docker-nerd-fonts-patcher-cache + EXTRA_REGISTRIES: ghcr.io quay.io + +jobs: + metadata: + name: Metadata + runs-on: ubuntu-latest + outputs: + tags: ${{ steps.tags.outputs.tags }} + vcs_ref: ${{ steps.vcs_ref.outputs.vcs_ref }} + created_on: ${{ steps.created_on.outputs.created_on }} + steps: + - name: Image Tags + id: tags + run: | + IMAGE_TAG="${{ github.event.release.tag_name }}" + [ -z "${IMAGE_TAG}" ] && IMAGE_TAG='latest' + + DOCKER_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}" + TAGS="${DOCKER_IMAGE}" + for registry in ${EXTRA_REGISTRIES}; do + TAGS="${TAGS},${registry}/${DOCKER_IMAGE}" + done + + echo "Image Tag: '${IMAGE_TAG}'" + echo "Docker image: '${DOCKER_IMAGE}'" + echo "Tags: ${TAGS}" + + echo ::set-output name=tags::${TAGS} + + - name: VCS ref + id: vcs_ref + run: | + VCS_REF="${GITHUB_SHA::8}" + echo "VCS ref: ${VCS_REF}" + echo ::set-output name=vcs_ref::${VCS_REF} + + - name: Created On + id: created_on + run: | + CREATED_ON="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" + echo "Created on: ${CREATED_ON}" + echo ::set-output name=created_on::${CREATED_ON} + + - name: Dump environment + if: contains(toJSON(github.event.commits.*.message), 'ci(debug)') == true + run: env | sort + - name: Dump GitHub context + if: contains(toJSON(github.event.commits.*.message), 'ci(debug)') == true + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "${GITHUB_CONTEXT}" + + build-publish: + name: Build and publish + runs-on: ubuntu-latest + needs: metadata + if: contains(toJSON(github.event.commits.*.message), 'ci(debug)') == false + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: ${{ env.CACHE_PATH }} + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Login to Docker Container Registry + uses: docker/login-action@v2 + with: + username: ${{ github.repository_owner }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.CR_PAT }} + + - name: Login to Quay.io Container Registry + uses: docker/login-action@v2 + with: + registry: quay.io + username: ${{ secrets.QUAYIO_USERNAME }} + password: ${{ secrets.QUAYIO_PASSWORD }} + + - name: Build and publish + uses: docker/build-push-action@v3 + with: + platforms: ${{ env.PLATFORMS }} + build-args: | + VCS_REF=${{ needs.metadata.outputs.vcs_ref }} + BUILD_DATE=${{ needs.metadata.outputs.created_on }} + cache-from: type=local,src=${{ env.CACHE_PATH }} + cache-to: type=local,dest=${{ env.CACHE_PATH }} + push: true + tags: ${{ needs.metadata.outputs.tags }} diff --git a/.gitignore b/.gitignore index 809c1f7..f2f1626 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Default directories in/ out/ +tests/assets/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b7a6aa..2062b1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # Changelog -The following list only reflects changes made on this project. Please, refer to the Nerd Fonts [Release Notes](https://www.nerdfonts.com/releases) for the complete list of changes. +The following list only reflects changes made on this project. Please, refer to the [Nerd Fonts 2.2.0 Release Notes](https://www.nerdfonts.com/releases) for the complete list of changes. + +**2.2.0** + +* Upgrade Nerd Fonts to `2.2.0` +* Change Docker base image to `ubuntu:jammy-20220801` +* CI: Replace CircleCI with GitHub Actions +* CI: Improve build time **2.1.0** diff --git a/Dockerfile b/Dockerfile index 0a3d83d..55ede6b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,58 +1,45 @@ -FROM ubuntu:bionic-20200112 +FROM ubuntu:jammy-20220801 ARG BUILD_DATE ARG VCS_REF -ENV DEBIAN_FRONTEND=noninteractive +# https://github.com/ryanoasis/nerd-fonts/releases +ENV NERDFONTS_VERSION="v2.2.0" +ENV IMAGE_VERSION="${NERDFONTS_VERSION}" -ENV FONTFORGE_VERSION=20190801 \ - LIBSPIRO_VERSION=20190731 \ - LIBUNINAMESLIST_VERSION=20190701 \ - NERDFONTS_VERSION=v2.1.0 +ENV BUILD_DIR="/build" \ + NERDFONTS_DIR="/nerd-fonts" -ENV BUILD_DIR=/build \ - NERDFONTS_DIR=/nerd-fonts +ENV INPUT_DIR="${NERDFONTS_DIR}/in" \ + OUTPUT_DIR="${NERDFONTS_DIR}/out" \ + REPOSITORY_DIR="${NERDFONTS_DIR}/repo" +RUN mkdir -p "${INPUT_DIR}" "${OUTPUT_DIR}" "${REPOSITORY_DIR}" -ENV INPUT_DIR=${NERDFONTS_DIR}/in \ - OUTPUT_DIR=${NERDFONTS_DIR}/out \ - REPOSITORY_DIR=${NERDFONTS_DIR}/repo - -# Install packages -RUN apt-get update \ - && apt-get install --yes --quiet --no-install-recommends \ - git ca-certificates packaging-dev pkg-config python3-dev \ - libpango1.0-dev libglib2.0-dev libxml2-dev giflib-tools \ - libjpeg-dev libtiff-dev libspiro-dev build-essential \ - automake flex bison unifont \ - && apt-get clean --yes \ - && rm -rf /var/lib/apt/lists/* - -# Install fontforge +# Install nerd fonts COPY assets/build/ ${BUILD_DIR} -RUN chmod +x ${BUILD_DIR}/install.sh \ - && ${BUILD_DIR}/install.sh - -# Download nerd-fonts -RUN git clone --branch "${NERDFONTS_VERSION}" --depth 1 \ - https://github.com/ryanoasis/nerd-fonts.git ${REPOSITORY_DIR} \ - && rm -rf ${REPOSITORY_DIR}/patched-fonts -WORKDIR ${REPOSITORY_DIR} - -LABEL \ - maintainer="github@cdalvaro.io" \ - org.label-schema.vendor=cdalvaro \ - org.label-schema.name="Nerd Fonts Patcher" \ - org.label-schema.version=${NERDFONTS_VERSION} \ - org.label-schema.description="Dockerized Nerd Fonts Patcher" \ - org.label-schema.url="https://github.com/cdalvaro/docker-nerd-fonts-patcher" \ - org.label-schema.vcs-url="https://github.com/cdalvaro/docker-nerd-fonts-patcher.git" \ - org.label-schema.vcs-ref=${VCS_REF} \ - org.label-schema.build-date=${BUILD_DATE} \ - org.label-schema.docker.schema-version="1.0" \ - com.cdalvaro.saltstack-master.license=MIT +WORKDIR ${BUILD_DIR} +RUN bash ${BUILD_DIR}/install.sh + +LABEL org.opencontainers.image.title="Dockerized Nerd Fonts Patcher" +LABEL org.opencontainers.image.description="Nerd Fonts ${NERDFONTS_VERSION} containerized" +LABEL org.opencontainers.image.documentation="https://github.com/cdalvaro/docker-nerd-fonts-patcher/blob/${IMAGE_VERSION}/README.md" +LABEL org.opencontainers.image.url="https://github.com/cdalvaro/docker-nerd-fonts-patcher" +LABEL org.opencontainers.image.source="https://github.com/cdalvaro/docker-nerd-fonts-patcher.git" +LABEL org.opencontainers.image.authors="Carlos Álvaro " +LABEL org.opencontainers.image.vendor="cdalvaro" +LABEL org.opencontainers.image.created="${BUILD_DATE}" +LABEL org.opencontainers.image.version="${IMAGE_VERSION}" +LABEL org.opencontainers.image.revision="${VCS_REF}" +LABEL org.opencontainers.image.base.name="ubuntu:jammy-20220801" +LABEL org.opencontainers.image.base.digest="sha256:42ba2dfce475de1113d55602d40af18415897167d47c2045ec7b6d9746ff148f" +LABEL org.opencontainers.image.licenses="MIT" # Entrypoint COPY entrypoint.sh /sbin/entrypoint.sh RUN chmod +x /sbin/entrypoint.sh +# Shared resources +VOLUME [ "${INPUT_DIR}", "${OUTPUT_DIR}" ] + +WORKDIR ${REPOSITORY_DIR} ENTRYPOINT ["/sbin/entrypoint.sh"] diff --git a/Makefile b/Makefile index e39c320..c7a5dc6 100644 --- a/Makefile +++ b/Makefile @@ -6,19 +6,21 @@ help: @echo "" @echo " 1. make build - build the nerd-fonts-patcher image" @echo " 2. make release - build the nerd-fonts-patcher image with the version tag" - @echo " 3. make patch - patch monospace fonts inside $(shell pwd)/in directory with the complete set of glyphs" + @echo " 3. make patch - patch monospace fonts inside '$(shell pwd)/in' directory with the complete set of glyphs" build: - @docker build --tag=cdalvaro/nerd-fonts-patcher . + @docker build --tag=cdalvaro/docker-nerd-fonts-patcher:latest . release: build - @docker build --tag=cdalvaro/nerd-fonts-patcher:$(shell cat VERSION) . + @docker tag cdalvaro/docker-nerd-fonts-patcher:latest \ + cdalvaro/docker-nerd-fonts-patcher:$(shell cat VERSION) . patch: @docker run --rm \ --volume $(shell pwd)/in:/nerd-fonts/in:ro \ - --volume $(shell pwd)/out:/nerd-fonts/out \ + --volume $(shell pwd)/out:/nerd-fonts/out \ --user $(shell id -u):$(shell id -g) \ - cdalvaro/nerd-fonts-patcher:latest \ - --quiet --no-progressbars \ - --mono --adjust-line-height --complete --careful + -- \ + cdalvaro/docker-nerd-fonts-patcher:latest \ + --quiet --no-progressbars \ + --mono --adjust-line-height --complete --careful diff --git a/README.md b/README.md index 2aab67f..9044b55 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Nerd Fonts Patcher v2.1.0 +# Nerd Fonts Patcher v2.2.0 Dockerfile to build a Nerd Fonts Patcher image for the Docker opensource container platform. @@ -18,10 +18,10 @@ Just copy all your fonts you want to patch into `$(pwd)/in` directory and execut ```sh docker run --rm \ - --volume $(pwd)/in:/nerd-fonts/in:ro \ + --volume $(pwd)/in:/nerd-fonts/in \ --volume $(pwd)/out:/nerd-fonts/out \ --user $(id -u):$(id -g) \ - cdalvaro/nerd-fonts-patcher:2.1.0 \ + ghcr.io/cdalvaro/docker-nerd-fonts-patcher:latest \ --quiet --no-progressbars \ --mono --adjust-line-height --complete --careful ``` @@ -33,16 +33,22 @@ More information is available at the [official documentation][patch-your-own-fon ## Available Sources -This image can be downloaded from [Dockerhub](https://hub.docker.com/r/cdalvaro/nerd-fonts-patcher/) +This image can be downloaded from [Dockerhub](https://hub.docker.com/r/cdalvaro/docker-nerd-fonts-patcher/) ```sh -docker pull cdalvaro/nerd-fonts-patcher:latest +docker pull cdalvaro/docker-nerd-fonts-patcher:latest ``` -or from [Quay.io](https://quay.io/repository/cdalvaro/nerd-fonts-patcher) too. +from [Quay.io](https://quay.io/repository/cdalvaro/docker-nerd-fonts-patcher) too. ```sh -docker pull quay.io/cdalvaro/nerd-fonts-patcher +docker pull quay.io/cdalvaro/docker-nerd-fonts-patcher +``` + +or from [GitHub Container Registry](https://ghcr.io/cdalvaro/docker-nerd-fonts-patcher) too. + +```sh +docker pull ghcr.io/cdalvaro/docker-nerd-fonts-patcher ``` [vorillaz-devicons]:https://vorillaz.github.io/devicons/ diff --git a/VERSION b/VERSION index 50aea0e..e3a4f19 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.0 \ No newline at end of file +2.2.0 \ No newline at end of file diff --git a/assets/build/functions.sh b/assets/build/functions.sh new file mode 100644 index 0000000..36d621a --- /dev/null +++ b/assets/build/functions.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash + +set -e + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: log_debug +# DESCRIPTION: Echo debug information to stdout. +#---------------------------------------------------------------------------------------------------------------------- +function log_debug() { + if [[ "${DEBUG,,}" == true || "${ECHO_DEBUG,,}" == true ]]; then + echo "[DEBUG] - $*" + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: log_info +# DESCRIPTION: Echo information to stdout. +#---------------------------------------------------------------------------------------------------------------------- +function log_info() { + echo "[INFO] - $*" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: log_warn +# DESCRIPTION: Echo warning information to stdout. +#---------------------------------------------------------------------------------------------------------------------- +function log_warn() { + (echo >&2 "[WARN] - $*") +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: log_error +# DESCRIPTION: Echo errors to stderr. +#---------------------------------------------------------------------------------------------------------------------- +function log_error() { + (echo >&2 "[ERROR] - $*") +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: is_arm32 +# DESCRIPTION: Check whether the platform is ARM 32-bits or not. +#---------------------------------------------------------------------------------------------------------------------- +function is_arm32() { + uname -m | grep -qE 'armv7l' +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: is_arm32 +# DESCRIPTION: Check whether the platform is ARM 64-bits or not. +#---------------------------------------------------------------------------------------------------------------------- +function is_arm64() { + uname -m | grep -qE 'arm64|aarch64' +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: is_arm32 +# DESCRIPTION: Check whether the platform is ARM or not. +#---------------------------------------------------------------------------------------------------------------------- +function is_arm() { + is_arm32 || is_arm64 +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: install_pkgs +# DESCRIPTION: Install packages using apt-get install. +#---------------------------------------------------------------------------------------------------------------------- +function install_pkgs() { + apt-get install --no-install-recommends --yes "$@" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: download +# DESCRIPTION: Download the content from the given URL and save it into the specified file. +#---------------------------------------------------------------------------------------------------------------------- +function download() { + local URL="$1"; shift + local FILE_NAME="$1"; shift + + local WGET_ARGS=(--quiet) + is_arm32 && WGET_ARGS+=(--no-check-certificate) + + log_info "Downloading ${FILE_NAME} from ${URL} ..." + wget "${WGET_ARGS[@]}" "$@" -O "${FILE_NAME}" "${URL}" + if [[ -f "${FILE_NAME}" ]]; then + log_debug "Success!" + else + log_error "Failed to download ${URL}" + exit 1 + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: check_sha256 +# DESCRIPTION: Compute the SHA256 hash for the given file and check if it matches the expected one. +#---------------------------------------------------------------------------------------------------------------------- +function check_sha256() { + local FILE="${1}" + local SHA256="${2}" + + log_info "Checking ${FILE} SHA256 hash ..." + if echo "${SHA256} ${FILE}" | shasum -a 256 -c --status -; then + log_debug "SHA256 hash for ${FILE} matches! (${SHA256})" + else + local HASH + HASH=$(shasum -a 256 "${FILE}" | awk '{print $1}') + log_error "SHA256 checksum mismatch for ${FILE}" + log_error "Expected: ${SHA256}" + log_error " Got: ${HASH}" + exit 1 + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: extract +# DESCRIPTION: Extract the given .tar.gz into the current directory. +#---------------------------------------------------------------------------------------------------------------------- +function extract() { + local FILE="${1}"; shift + local EXTRACT_DIR="${1}"; shift + + log_info "Unpacking file: ${FILE}" + mkdir -p "${EXTRACT_DIR}" + tar xzf "${FILE}" --directory="${EXTRACT_DIR}" --strip-components 1 "$@" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: build_and_install +# DESCRIPTION: Build and install the given package from the current directory using cmake. +#---------------------------------------------------------------------------------------------------------------------- +function build_and_install() { + local PACKAGE_NAME="${1}"; shift + local CMAKE_ARGS=( + -Wno-dev + -DCMAKE_BUILD_TYPE=Release + ) + + CMAKE_ARGS+=("$@") + + CMAKE_BUILD_DIR="cmake-build-release" + mkdir -p "${CMAKE_BUILD_DIR}" + pushd "${CMAKE_BUILD_DIR}" + + log_info "Building and installing ${PACKAGE_NAME} ..." + log_debug "CMAKE_ARGS: ${CMAKE_ARGS[*]}" + cmake "${CMAKE_ARGS[@]}" .. + cmake --build . --target install --config Release + + popd +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: install_fontforge +# DESCRIPTION: Install fontforge +#---------------------------------------------------------------------------------------------------------------------- +function install_fontforge() { + local VERSION="${1}" + local SHA256="${2}" + + local URL="https://github.com/fontforge/fontforge/archive/refs/tags/${VERSION}.tar.gz" + local FILE="fontforge.tar.gz" + local EXTRACT_DIR="fontforge-build" + + download "${URL}" "${FILE}" + check_sha256 "${FILE}" "${SHA256}" + extract "${FILE}" "${EXTRACT_DIR}" + + BUILD_OPTS=( + -DENABLE_GUI=OFF + ) + + (cd $EXTRACT_DIR && build_and_install "fontforge ${VERSION}" "${BUILD_OPTS[@]}") +} diff --git a/assets/build/install.sh b/assets/build/install.sh index f348c05..bcfc923 100644 --- a/assets/build/install.sh +++ b/assets/build/install.sh @@ -2,32 +2,48 @@ set -e -# Install libspiro -git clone --branch "${LIBSPIRO_VERSION}" --depth 1 https://github.com/fontforge/libspiro.git && \ - cd libspiro && \ - autoreconf -i && \ - automake --foreign -Wall && \ - ./configure && \ - make install && \ - cd .. - -# Install libuninameslist -git clone --branch "${LIBUNINAMESLIST_VERSION}" --depth 1 https://github.com/fontforge/libuninameslist.git && \ - cd libuninameslist && \ - autoreconf -i && \ - automake --foreign && \ - ./configure && \ - make install && \ - cd .. +export DEBIAN_FRONTEND=noninteractive + +FUNCTIONS_FILE="${BUILD_DIR}/functions.sh" +# shellcheck source=assets/build/functions.sh +source "${FUNCTIONS_FILE}" + +log_info "Installing required packages and build dependencies ..." +REQUIRED_PACKAGES=( + libpng16-16 zlib1g libtiff5 libjpeg8 libxml2 libspiro1 libgif7 \ + libiconv-hook1 libfreetype6 libcairo2 libpango1.0-0 libwoff1 \ + libuninameslist1 libreadline8 libpython3.10 python3 unifont \ + python3-setuptools python3-dev python3-fonttools fonttools +) + +BUILD_DEPENDENCIES=( + libjpeg-dev libtiff5-dev libpng-dev libfreetype6-dev libgif-dev \ + libxml2-dev libpango1.0-dev libcairo2-dev libspiro-dev \ + libuninameslist-dev libreadline-dev libwoff-dev ca-certificates \ + ninja-build cmake build-essential openssl wget gettext git \ + apt-transport-https +) + +apt-get update +install_pkgs "${REQUIRED_PACKAGES[@]}" "${BUILD_DEPENDENCIES[@]}" # Install fontforge -git clone --branch "${FONTFORGE_VERSION}" --depth 1 https://github.com/fontforge/fontforge.git && \ - cd fontforge && \ - ./bootstrap && \ - ./configure && \ - make install && \ - ldconfig && \ - cd .. - -# Cleanup -rm -rf ${BUILD_DIR} +FONTFORGE_VERSION="20220308" +FONTFORGE_SHA256="58bbc759eb102263be835e6c006b1c16b508ba3d0252acd5389062826764f7a5" +install_fontforge "${FONTFORGE_VERSION}" "${FONTFORGE_SHA256}" + +# Download nerd-fonts +NERDFONTS_SHA256="55a1f872582914fe2e2c8ff02c42cc4a02f5add8ce94119d21d3ff1c0cafea8c" +NERDFONTS_URL="https://github.com/ryanoasis/nerd-fonts/archive/refs/tags/${NERDFONTS_VERSION}.tar.gz" +NERDFONTS_FILE_NAME="nerd-fonts.tar.gz" +download "${NERDFONTS_URL}" "${NERDFONTS_FILE_NAME}" --progress=bar --show-progress +check_sha256 "${NERDFONTS_FILE_NAME}" "${NERDFONTS_SHA256}" +extract "${NERDFONTS_FILE_NAME}" "${REPOSITORY_DIR}" --exclude='patched-fonts' + +# Purge build dependencies and cleanup apt +apt-get purge -y --auto-remove "${BUILD_DEPENDENCIES[@]}" +apt-get clean --yes +rm -rf /var/lib/apt/lists/* +rm -rf "${BUILD_DIR}" + +export -n DEBIAN_FRONTEND diff --git a/entrypoint.sh b/entrypoint.sh index c466f92..38dcc70 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,7 +2,7 @@ set -e -# Auxliar functions +# Auxiliary functions RESET='\033[0m' RED='\033[1;31m' GREEN='\033[1;32m' @@ -10,53 +10,56 @@ YELLOW='\033[1;33m' CYAN='\033[1;36m' function log_info() { - echo -e "${GREEN}Info${RESET}: $@" + echo -e "${GREEN}Info${RESET}: $*" } function log_warn() { - echo -e "${YELLOW}Warning${RESET}: $@" + echo -e "${YELLOW}Warning${RESET}: $*" } function log_error() { - (>&2 echo -e "${RED}Error${RESET}: $@") + (>&2 echo -e "${RED}Error${RESET}: $*") } # Validate arguments options=() while [[ $# -gt 0 ]]; do - param="$1"; shift - case "$param" in - -h|--help) - exec fontforge -script font-patcher --help ;; - -v|--version) - exec fontforge -script font-patcher --version ;; - -out|--outputdir) - log_warn "Output directory cannot be modified. Default is: ${CYAN}${OUTPUT_DIR}/${RESET}" - shift ;; - *) - options+=("$param") ;; - esac + param="$1"; shift + case "$param" in + -h|--help) + exec fontforge -script font-patcher --help ;; + -v|--version) + exec fontforge -script font-patcher --version ;; + -out|--outputdir) + log_warn "Output directory cannot be modified. Default is: ${CYAN}${OUTPUT_DIR}/${RESET}" + shift ;; + *) + options+=("$param") ;; + esac done # Check whether output directory exists if [[ ! -d ${OUTPUT_DIR} ]]; then - log_error "Directory ${CYAN}${OUTPUT_DIR}/${RESET} does not exists. You must create an output volume like this: ${CYAN}"'--volume $(pwd)/out:'"${OUTPUT_DIR}${RESET}" - exit 1 + log_error "Directory ${CYAN}${OUTPUT_DIR}/${RESET} does not exists. You must create an output volume like this: ${CYAN}--volume \$(pwd)/out:${OUTPUT_DIR}${RESET}" + exit 1 fi # Get fonts available in the input directory -fonts=( $(find ${INPUT_DIR}/ -type f -iregex '.*\.\(otf\|ttf\)$' 2>/dev/null) ) +fonts=() +while IFS='' read -r line; do + fonts+=("$line") +done < <(find "${INPUT_DIR}/" -type f -iregex '.*\.\(otf\|ttf\)$' 2>/dev/null) # Check whether there are fonts to patch if [ ${#fonts[@]} -eq 0 ]; then - log_error "There are no ${CYAN}.otf${RESET} neither ${CYAN}.ttf${RESET} fonts inside ${CYAN}${INPUT_DIR}/${RESET} directory" - exit 1 + log_error "There are no ${CYAN}.otf${RESET} neither ${CYAN}.ttf${RESET} fonts inside ${CYAN}${INPUT_DIR}/${RESET} directory" + exit 1 fi # Patch fonts for font in "${fonts[@]}"; do - log_info "Patching font ${CYAN}${font}${RESET} ..." - fontforge -script font-patcher -out ${OUTPUT_DIR}/ ${options[@]} "${font}" + log_info "Patching font ${CYAN}${font}${RESET} ..." + fontforge -script font-patcher -out "${OUTPUT_DIR}/" "${options[@]}" "${font}" done exit 0 diff --git a/hooks/build b/hooks/build deleted file mode 100644 index ffb2053..0000000 --- a/hooks/build +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -# Docker Daemon Build Hook -# $IMAGE_NAME var is injected into the build so the tag is correct. - -docker build \ - --build-arg=BUILD_DATE="$(date +"%Y-%m-%d %H:%M:%S%:z")" \ - --build-arg=VCS_REF="$(git rev-parse --short HEAD)" \ - -t ${IMAGE_NAME} . diff --git a/tests/lib/common.sh b/tests/lib/common.sh new file mode 100644 index 0000000..a5ae7a3 --- /dev/null +++ b/tests/lib/common.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash + +#--- ENV VARIABLE --------------------------------------------------------------------------------------------------- +# NAME: IMAGE_NAME +# DESCRIPTION: The name and tag of the Docker image. Default: 'cdalvaro/docker-nerd-fonts-patcher:latest'. +#---------------------------------------------------------------------------------------------------------------------- +export IMAGE_NAME=${IMAGE_NAME:-'cdalvaro/docker-nerd-fonts-patcher:latest'} + +#--- ENV VARIABLE --------------------------------------------------------------------------------------------------- +# NAME: PLATFORM +# DESCRIPTION: The platform to run the tests on. Default: the current platform. +#---------------------------------------------------------------------------------------------------------------------- +export PLATFORM=${PLATFORM:-$(docker version --format='{{.Server.Os}}/{{.Server.Arch}}')} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: lowercase +# DESCRIPTION: Lowercase a string. +#---------------------------------------------------------------------------------------------------------------------- +function lowercase() { + echo "$1" | tr '[:upper:]' '[:lower:]' +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: log_debug +# DESCRIPTION: Echo debug information to stdout. +#---------------------------------------------------------------------------------------------------------------------- +function log_debug() { + if [[ $(lowercase "${DEBUG}") == true || $(lowercase "${ECHO_DEBUG}") == true ]]; then + echo "[DEBUG] - $*" + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: log_info +# DESCRIPTION: Echo information to stdout. +#---------------------------------------------------------------------------------------------------------------------- +function log_info() { + echo "[INFO] - $*" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: log_warn +# DESCRIPTION: Echo warning information to stdout. +#---------------------------------------------------------------------------------------------------------------------- +function log_warn() { + (echo >&2 "[WARN] - $*") +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: log_error +# DESCRIPTION: Echo errors to stderr. +#---------------------------------------------------------------------------------------------------------------------- +function log_error() { + (echo >&2 "[ERROR] - $*") +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: is_arm32 +# DESCRIPTION: Check whether the platform is ARM 32-bits or not. +#---------------------------------------------------------------------------------------------------------------------- +function is_arm32() { + uname -m | grep -qE 'armv7l' +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: is_arm32 +# DESCRIPTION: Check whether the platform is ARM 64-bits or not. +#---------------------------------------------------------------------------------------------------------------------- +function is_arm64() { + uname -m | grep -qE 'arm64|aarch64' +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: is_arm32 +# DESCRIPTION: Check whether the platform is ARM or not. +#---------------------------------------------------------------------------------------------------------------------- +function is_arm() { + is_arm32 || is_arm64 +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: ok +# DESCRIPTION: Print a successfull message. +#---------------------------------------------------------------------------------------------------------------------- +function ok() +{ + echo "✅ $*" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: error +# DESCRIPTION: Print an error message, show the salt-master log and exit with code 1. +#---------------------------------------------------------------------------------------------------------------------- +function error() +{ + echo "🔥 $*" + return 1 +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: download +# DESCRIPTION: Download the content from the given URL and save it into the specified file. +#---------------------------------------------------------------------------------------------------------------------- +function download() { + local URL="$1"; shift + local FILE_NAME="$1"; shift + + local WGET_ARGS=(--quiet) + is_arm32 && WGET_ARGS+=(--no-check-certificate) + + log_info "Downloading ${FILE_NAME} from ${URL} ..." + wget "${WGET_ARGS[@]}" "$@" -O "${FILE_NAME}" "${URL}" + if [[ -f "${FILE_NAME}" ]]; then + log_debug "Success!" + else + log_error "Failed to download ${URL}" + return 1 + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: check_sha256 +# DESCRIPTION: Compute the SHA256 hash for the given file and check if it matches the expected one. +#---------------------------------------------------------------------------------------------------------------------- +function check_sha256() { + local FILE="${1}" + local SHA256="${2}" + + log_info "Checking ${FILE} SHA256 hash ..." + if echo "${SHA256} ${FILE}" | shasum -a 256 -c --status -; then + log_debug "SHA256 hash for ${FILE} matches! (${SHA256})" + else + local HASH + HASH=$(shasum -a 256 "${FILE}" | awk '{print $1}') + log_error "SHA256 checksum mismatch for ${FILE}" + log_error "Expected: ${SHA256}" + log_error " Got: ${HASH}" + return 1 + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: check_equal +# DESCRIPTION: Check if the given value is equal to the expected value. +#---------------------------------------------------------------------------------------------------------------------- +function check_equal() +{ + local current="$1" + local expected="$2" + local message="$3" + + output=$(cat </dev/null 2>&1 ; pwd -P )" +COMMON_FILE="${SCRIPT_PATH}/lib/common.sh" + +# shellcheck source=tests/lib/common.sh +source "${COMMON_FILE}" +[ "$(lowercase "${DEBUG}")" == true ] && set -vx + +log_info "🧪 Running patch tests ..." + +### GIVEN +FIRACODE_VERSION="6.2" +FIRACODE_SHA256="0949915ba8eb24d89fd93d10a7ff623f42830d7c5ffc3ecbf960e4ecad3e3e79" +FIRACODE_FILE_NAME="FiraCode.zip" +FIRACODE_URL="https://github.com/tonsky/FiraCode/releases/download/${FIRACODE_VERSION}/Fira_Code_v${FIRACODE_VERSION}.zip" + +NERDFONTS_VERSION="$(cat VERSION)" +FIRACODE_NERD_FONT_SHA256="7ad2fdab3e95405b45644425d74238eb8463f2547ad6ff076bcc383ccba1c9c6" +FIRACODE_NERD_FONT_FILE_NAME="FiraCodeNerdFont.zip" +FIRACODE_NERD_FONT_URL="https://github.com/ryanoasis/nerd-fonts/releases/download/v${NERDFONTS_VERSION}/FiraCode.zip" + +WORK_DIR="${SCRIPT_PATH}/assets" +rm -rf "${WORK_DIR}" && mkdir -p "${WORK_DIR}" +pushd "${WORK_DIR}" + +INPUT_DIR="$(pwd)/in" +OUTPUT_DIR="$(pwd)/out" + +### THEN +log_info "==> Downloading original Fira Code ${FIRACODE_VERSION} font ..." +download "${FIRACODE_URL}" "${FIRACODE_FILE_NAME}" +check_sha256 "${FIRACODE_FILE_NAME}" "${FIRACODE_SHA256}" || error "SHA256 checksum mismatch for ${FIRACODE_URL}" + +mkdir -p "${INPUT_DIR}/" "${OUTPUT_DIR}/" +unzip -q "${FIRACODE_FILE_NAME}" 'ttf/*' -d "${INPUT_DIR}/" +mv "${INPUT_DIR}"/ttf/* "${INPUT_DIR}/" +rmdir "${INPUT_DIR}/ttf" + +log_info "==> Patching original Fira Code font ..." +patch_fonts "${INPUT_DIR}" "${OUTPUT_DIR}" --complete + +log_info "==> Downloading patched Fira Code font ..." +download "${FIRACODE_NERD_FONT_URL}" "${FIRACODE_NERD_FONT_FILE_NAME}" +check_sha256 "${FIRACODE_NERD_FONT_FILE_NAME}" "${FIRACODE_NERD_FONT_SHA256}" || error "SHA256 checksum mismatch for ${FIRACODE_NERD_FONT_URL}" + +log_info "==> Comparing own patched fonts with nerd font patched fonts ..." +mkdir -p patched/ +unzip -q "${FIRACODE_NERD_FONT_FILE_NAME}" -d patched/ + +while IFS='' read -r FONT; do + ttx --recalc-timestamp -o custom_patched_font.ttx "${OUTPUT_DIR}/${FONT}" + sed -E -i 's/.*<(checkSumAdjustment|modified|FFTimeStamp|sourceModified).*//g' custom_patched_font.ttx + ttx --recalc-timestamp -o nerdfonts_patched_font.ttx "$(pwd)/patched/${FONT}" + sed -E -i 's/.*<(checkSumAdjustment|modified|FFTimeStamp|sourceModified).*//g' nerdfonts_patched_font.ttx + diff custom_patched_font.ttx nerdfonts_patched_font.ttx +done < <(find "${OUTPUT_DIR}" -type f -iname '*.ttf' -exec basename {} \; 2>/dev/null) + +popd