nexo/services/push-proxy/Makefile

843 lines
33 KiB
Makefile

# ====================================================================================
# Variables
## General Variables
# Branch Variables
PROTECTED_BRANCH := master
CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
# Use repository name as application name
APP_NAME := $(shell basename -s .git `git config --get remote.origin.url`)
APP_COMMIT := $(shell git rev-parse HEAD)
# Check if we are in protected branch, if yes use `protected_branch_name-sha` as app version.
# Else check if we are in a release tag, if yes use the tag as app version, else use `dev-sha` as app version.
APP_VERSION ?= $(shell if [ $(PROTECTED_BRANCH) = $(CURRENT_BRANCH) ]; then echo $(PROTECTED_BRANCH); else (git describe --abbrev=0 --exact-match --tags 2>/dev/null || echo dev-$(APP_COMMIT)) ; fi)
APP_VERSION_NO_V := $(patsubst v%,%,$(APP_VERSION))
GIT_VERSION ?= $(shell git describe --tags --always --dirty)
GIT_TREESTATE = clean
DIFF = $(shell git diff --quiet >/dev/null 2>&1; if [ $$? -eq 1 ]; then echo "1"; fi)
ifeq ($(DIFF), 1)
GIT_TREESTATE = dirty
endif
GO_INSTALL = ./scripts/go_install.sh
TOOLS_BIN_DIR := $(abspath bin)
# Get current date and format like: 2022-04-27 11:32
BUILD_DATE := $(shell date +%Y-%m-%d\ %H:%M)
# Get version information for plugins that depend on a semver version
BUILD_HASH = $(shell git rev-parse --short HEAD)
BUILD_TAG_LATEST = $(shell git describe --tags --match 'v*' --abbrev=0)
BUILD_TAG_CURRENT = $(shell git tag --points-at HEAD)
## General Configuration Variables
# We don't need make's built-in rules.
MAKEFLAGS += --no-builtin-rules
# Be pedantic about undefined variables.
MAKEFLAGS += --warn-undefined-variables
# Set help as default target
.DEFAULT_GOAL := help
# App Code location
CONFIG_APP_CODE += ./
## Docker Variables
# Docker executable
DOCKER := $(shell which docker)
# Dockerfile's location
DOCKER_FILE ?= ./docker/Dockerfile
# Docker options to inherit for all docker run commands
DOCKER_OPTS += --rm --platform "linux/amd64"
# Registry to upload images
DOCKER_REGISTRY ?= docker.io
DOCKER_REGISTRY_REPO ?= mattermost/${APP_NAME}-daily
# Registry credentials
DOCKER_USER ?= user
DOCKER_PASSWORD ?= password
## Latest Docker tags
# if we are on a latest semver APP_VERSION tag, also push latest
ifneq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),)
ifeq ($(shell git tag -l --sort=v:refname | tail -n1),$(APP_VERSION))
LATEST_DOCKER_TAG = -t $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:latest
endif
endif
## Docker Images
DOCKER_IMAGE_GO ?= "golang:${GO_VERSION}"
DOCKER_IMAGE_GOLINT ?= "golangci/golangci-lint:v1.64.4@sha256:e83b903d722c12402c9d88948a6cac42ea0e34bf336fc6a170ade9adeecb2d0e"
DOCKER_IMAGE_DOCKERLINT ?= "hadolint/hadolint:v2.12.0"
DOCKER_IMAGE_COSIGN ?= "bitnami/cosign:1.8.0@sha256:8c2c61c546258fffff18b47bb82a65af6142007306b737129a7bd5429d53629a"
DOCKER_IMAGE_GH_CLI ?= "ghcr.io/supportpal/github-gh-cli:2.31.0@sha256:71371e36e62bd24ddd42d9e4c720a7e9954cb599475e24d1407af7190e2a5685"
# To build FIPS-compliant push-proxy: make build-fips
# Requires Docker to be installed and running
FIPS_ENABLED ?= false
BUILD_IMAGE_FIPS ?= cgr.dev/mattermost.com/go-msft-fips:1.24.6
BASE_IMAGE_FIPS ?= cgr.dev/mattermost.com/glibc-openssl-fips:15.1
## Cosign Variables
# The public key
COSIGN_PUBLIC_KEY ?= akey
# The private key
COSIGN_KEY ?= akey
# The passphrase used to decrypt the private key
COSIGN_PASSWORD ?= password
## Go Variables
# Go executable
GO := $(shell which go)
# Extract GO version from go.mod file
GO_VERSION ?= $(shell grep -E '^go' go.mod | awk {'print $$2'})
# LDFLAGS
GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/internal/version.gitVersion=$(GIT_VERSION)"
GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/internal/version.buildHash=$(BUILD_HASH)"
GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/internal/version.buildTagLatest=$(BUILD_TAG_LATEST)"
GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/internal/version.buildTagCurrent=$(BUILD_TAG_CURRENT)"
GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/internal/version.gitTreeState=$(GIT_TREESTATE)"
GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/internal/version.buildDate=$(BUILD_DATE)"
# Architectures to build for
GO_BUILD_PLATFORMS ?= linux-amd64 linux-arm64 freebsd-amd64
GO_BUILD_PLATFORMS_ARTIFACTS = $(foreach cmd,$(addprefix go-build/,${APP_NAME}),$(addprefix $(cmd)-,$(GO_BUILD_PLATFORMS)))
# Build options
GO_BUILD_OPTS += -trimpath
GO_TEST_OPTS += -v -timeout=180s
# Temporary folder to output compiled binaries artifacts
GO_OUT_BIN_DIR := ./dist
## Github Variables
# A github access token that provides access to upload artifacts under releases
GITHUB_TOKEN ?= a_token
# Github organization
GITHUB_ORG := mattermost
# Most probably the name of the repo
GITHUB_REPO := ${APP_NAME}
## FIPS Docker Variables
APP_NAME_FIPS ?= mattermost/mattermost-push-proxy-fips
APP_VERSION_FIPS ?= $(APP_VERSION)-fips
APP_VERSION_NO_V_FIPS ?= $(APP_VERSION_NO_V)-fips
## Architecture Variables (default to ARM64 for local builds)
TARGET_OS ?= linux
TARGET_ARCH ?= arm64
OUTDATED_VER := master
OUTDATED_BIN := go-mod-outdated
OUTDATED_GEN := $(TOOLS_BIN_DIR)/$(OUTDATED_BIN)
# ====================================================================================
# Colors
BLUE := $(shell printf "\033[34m")
YELLOW := $(shell printf "\033[33m")
RED := $(shell printf "\033[31m")
GREEN := $(shell printf "\033[32m")
CYAN := $(shell printf "\033[36m")
CNone := $(shell printf "\033[0m")
# ====================================================================================
# Logger
TIME_LONG = `date +%Y-%m-%d' '%H:%M:%S`
TIME_SHORT = `date +%H:%M:%S`
TIME = $(TIME_SHORT)
INFO = echo ${TIME} ${BLUE}[ .. ]${CNone}
WARN = echo ${TIME} ${YELLOW}[WARN]${CNone}
ERR = echo ${TIME} ${RED}[FAIL]${CNone}
OK = echo ${TIME} ${GREEN}[ OK ]${CNone}
FAIL = (echo ${TIME} ${RED}[FAIL]${CNone} && false)
# ====================================================================================
# Verbosity control hack
VERBOSE ?= 0
AT_0 := @
AT_1 :=
AT = $(AT_$(VERBOSE))
# ====================================================================================
# Used for semver bumping
CURRENT_VERSION := $(shell git describe --abbrev=0 --tags)
VERSION_PARTS := $(subst ., ,$(subst v,,$(CURRENT_VERSION)))
MAJOR := $(word 1,$(VERSION_PARTS))
MINOR := $(word 2,$(VERSION_PARTS))
PATCH := $(word 3,$(VERSION_PARTS))
# Check if current branch is protected
define check_protected_branch
@current_branch=$$(git rev-parse --abbrev-ref HEAD); \
if ! echo "$(PROTECTED_BRANCH)" | grep -wq "$$current_branch"; then \
echo "Error: Tagging is only allowed from $(PROTECTED_BRANCH) branch. You are on $$current_branch branch."; \
exit 1; \
fi
endef
# Check if there are pending pulls
define check_pending_pulls
@git fetch; \
current_branch=$$(git rev-parse --abbrev-ref HEAD); \
if [ "$$(git rev-parse HEAD)" != "$$(git rev-parse origin/$$current_branch)" ]; then \
echo "Error: Your branch is not up to date with upstream. Please pull the latest changes before performing a release"; \
exit 1; \
fi
endef
# ====================================================================================
# Targets
help: ## to get help
@echo "Usage:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |\
awk 'BEGIN {FS = ":.*?## "}; {printf "make ${CYAN}%-30s${CNone} %s\n", $$1, $$2}'
.PHONY: build
build: go-build-docker ## to build
.PHONY: build-all
build-all: build build-fips ## to build both normal and FIPS versions
.PHONY: release
release: build github-release ## to build and release artifacts
.PHONY: release-fips
release-fips: build-fips ## to build and release FIPS artifacts
.PHONY: release-all
release-all: build-all github-release-all ## to build and release both versions
.PHONY: package
package: go-build package-software ## to build, package
.PHONY: package-fips
package-fips: build-fips ## to build FIPS version
.PHONY: package-all
package-all: package package-fips ## to build, package both versions
.PHONY: sign
sign: docker-sign docker-verify ## to sign the artifact and perform verification
.PHONY: lint
lint: go-lint docker-lint ## to lint
.PHONY: test
test: go-test ## to test
.PHONY: patch minor major
patch: ## to bump patch version (semver)
$(call check_protected_branch)
$(call check_pending_pulls)
@$(eval PATCH := $(shell echo $$(($(PATCH)+1))))
@$(INFO) Bumping $(APP_NAME) to Patch version $(MAJOR).$(MINOR).$(PATCH)
git tag -s -a v$(MAJOR).$(MINOR).$(PATCH) -m "Bumping $(APP_NAME) to Patch version $(MAJOR).$(MINOR).$(PATCH)"
git push origin v$(MAJOR).$(MINOR).$(PATCH)
@$(OK) Bumping $(APP_NAME) to Patch version $(MAJOR).$(MINOR).$(PATCH)
minor: ## to bump minor version (semver)
$(call check_protected_branch)
$(call check_pending_pulls)
@$(eval MINOR := $(shell echo $$(($(MINOR)+1))))
@$(eval PATCH := 0)
@$(INFO) Bumping $(APP_NAME) to Minor version $(MAJOR).$(MINOR).$(PATCH)
git tag -s -a v$(MAJOR).$(MINOR).$(PATCH) -m "Bumping $(APP_NAME) to Minor version $(MAJOR).$(MINOR).$(PATCH)"
git push origin v$(MAJOR).$(MINOR).$(PATCH)
@$(OK) Bumping $(APP_NAME) to Minor version $(MAJOR).$(MINOR).$(PATCH)
major: ## to bump major version (semver)
$(call check_protected_branch)
$(call check_pending_pulls)
$(eval MAJOR := $(shell echo $$(($(MAJOR)+1))))
$(eval MINOR := 0)
$(eval PATCH := 0)
@$(INFO) Bumping $(APP_NAME) to Major version $(MAJOR).$(MINOR).$(PATCH)
git tag -s -a v$(MAJOR).$(MINOR).$(PATCH) -m "Bumping $(APP_NAME) to Major version $(MAJOR).$(MINOR).$(PATCH)"
git push origin v$(MAJOR).$(MINOR).$(PATCH)
@$(OK) Bumping $(APP_NAME) to Major version $(MAJOR).$(MINOR).$(PATCH)
package-software: ## to package the binary
@$(INFO) Packaging
$(AT) for file in $(GO_OUT_BIN_DIR)/mattermost-push-proxy-*; do \
[[ "$$file" == *.tar.gz ]] && continue; \
target=$$(basename $$file); \
mkdir -p $(GO_OUT_BIN_DIR)/$${target}_temp/bin; \
cp -RL config $(GO_OUT_BIN_DIR)/$${target}_temp/config; \
echo $(APP_VERSION) > $(GO_OUT_BIN_DIR)/$${target}_temp/config/build.txt; \
cp LICENSE.txt NOTICE.txt README.md $(GO_OUT_BIN_DIR)/$${target}_temp; \
mkdir $(GO_OUT_BIN_DIR)/$${target}_temp/logs; \
mv $$file $(GO_OUT_BIN_DIR)/$${target}_temp/bin/mattermost-push-proxy; \
mv $(GO_OUT_BIN_DIR)/$${target}_temp $(GO_OUT_BIN_DIR)/$${target}; \
tar -czf $(GO_OUT_BIN_DIR)/$${target}.tar.gz -C $(GO_OUT_BIN_DIR) $${target}; \
rm -r $(GO_OUT_BIN_DIR)/$${target}; \
done
@$(OK) Packaging
.PHONY: package-software-fips
package-software-fips: ## to package the FIPS binary
@$(INFO) Packaging FIPS
$(AT) for file in $(GO_OUT_BIN_DIR)/mattermost-push-proxy-*-fips; do \
[[ "$$file" == *.tar.gz ]] && continue; \
target=$$(basename $$file); \
mkdir -p $(GO_OUT_BIN_DIR)/$${target}_temp/bin; \
cp -RL config $(GO_OUT_BIN_DIR)/$${target}_temp/config; \
echo $(APP_VERSION)-fips > $(GO_OUT_BIN_DIR)/$${target}_temp/config/build.txt; \
cp LICENSE.txt NOTICE.txt README.md $(GO_OUT_BIN_DIR)/$${target}_temp; \
mkdir $(GO_OUT_BIN_DIR)/$${target}_temp/logs; \
mv $$file $(GO_OUT_BIN_DIR)/$${target}_temp/bin/mattermost-push-proxy; \
mv $(GO_OUT_BIN_DIR)/$${target}_temp $(GO_OUT_BIN_DIR)/$${target}; \
tar -czf $(GO_OUT_BIN_DIR)/$${target}.tar.gz -C $(GO_OUT_BIN_DIR) $${target}; \
rm -r $(GO_OUT_BIN_DIR)/$${target}; \
done
@$(OK) Packaging FIPS
.PHONY: docker-build
docker-build: ## to build the docker image
@$(INFO) Performing Docker build ${APP_NAME}:${APP_VERSION_NO_V}
$(AT)$(DOCKER) buildx build \
--no-cache --pull --platform linux/amd64,linux/arm64 \
-f ${DOCKER_FILE} . \
-t ${APP_NAME}:${APP_VERSION_NO_V} || ${FAIL}
@$(OK) Performing Docker build ${APP_NAME}:${APP_VERSION_NO_V}
## --------------------------------------
## Regular Multi-Architecture Build Targets
## --------------------------------------
.PHONY: build-image-amd64-with-tags
build-image-amd64-with-tags: go-build-amd64 package-software ## Build Docker image for AMD64 with tags
@echo "Building mattermost-push-proxy Docker Image for AMD64"
docker build --no-cache --pull \
--build-arg TARGETOS=linux \
--build-arg TARGETARCH=amd64 \
-f ${DOCKER_FILE} \
-t ${APP_NAME}:${APP_VERSION_NO_V}-amd64 \
-t ${APP_NAME}:${APP_VERSION_NO_V} \
.
.PHONY: build-image-arm64-with-tags
build-image-arm64-with-tags: go-build-arm64 package-software ## Build Docker image for ARM64 with tags
@echo "Building mattermost-push-proxy Docker Image for ARM64"
docker build --no-cache --pull \
--build-arg TARGETOS=linux \
--build-arg TARGETARCH=arm64 \
-f ${DOCKER_FILE} \
-t ${APP_NAME}:${APP_VERSION_NO_V}-arm64 \
-t ${APP_NAME}:${APP_VERSION_NO_V} \
.
.PHONY: docker-build-parallel-with-tags
docker-build-parallel-with-tags: ## Build Docker images for both architectures in parallel
@echo "Building mattermost-push-proxy Docker Images for both platforms in parallel"
$(MAKE) build-image-amd64-with-tags &
$(MAKE) build-image-arm64-with-tags &
wait
@echo "Creating multi-platform manifests with clean tags"
docker manifest create ${APP_NAME}:${APP_VERSION_NO_V} \
--amend ${APP_NAME}:${APP_VERSION_NO_V}-amd64 \
--amend ${APP_NAME}:${APP_VERSION_NO_V}-arm64
@echo "✅ Multi-platform manifests created"
.PHONY: docker-push-with-tags
docker-push-with-tags: ## Push Docker images with unified tags
@echo "Pushing Docker images to registry"
docker push ${APP_NAME}:${APP_VERSION_NO_V}-amd64
docker push ${APP_NAME}:${APP_VERSION_NO_V}-arm64
docker manifest push ${APP_NAME}:${APP_VERSION_NO_V}
@echo "Cleaning up intermediate architecture-specific tags from registry"
docker rmi ${APP_NAME}:${APP_VERSION_NO_V}-amd64
docker rmi ${APP_NAME}:${APP_VERSION_NO_V}-arm64
@echo "✅ Multi-platform images pushed with unified tags"
@echo "✅ Intermediate architecture-specific tags cleaned up from registry"
.PHONY: cleanup-tags
cleanup-tags: ## Clean up intermediate architecture-specific tags from registry
@echo "Cleaning up intermediate architecture-specific tags from registry"
@echo "Removing AMD64 tag: ${APP_NAME}:${APP_VERSION_NO_V}-amd64"
docker rmi ${APP_NAME}:${APP_VERSION_NO_V}-amd64 2>/dev/null || true
@echo "Removing ARM64 tag: ${APP_NAME}:${APP_VERSION_NO_V}-arm64"
docker rmi ${APP_NAME}:${APP_VERSION_NO_V}-arm64 2>/dev/null || true
@echo "✅ Intermediate architecture-specific tags cleaned up from registry"
.PHONY: build-image-fips
build-image-fips: ## Build the FIPS docker image for mattermost-push-proxy
@echo "Building mattermost-push-proxy FIPS Docker Image for $(TARGET_ARCH)"
docker build --no-cache --pull \
--build-arg BUILD_IMAGE=$(BUILD_IMAGE_FIPS) \
--build-arg BASE_IMAGE=$(BASE_IMAGE_FIPS) \
--build-arg TARGETOS=$(TARGET_OS) \
--build-arg TARGETARCH=$(TARGET_ARCH) \
-f docker/Dockerfile.fips \
-t $(APP_NAME_FIPS):$(APP_VERSION_NO_V) .
.PHONY: buildx-image-fips
buildx-image-fips: ## Builds and pushes the FIPS docker image for mattermost-push-proxy
@echo "Building mattermost-push-proxy FIPS Docker Image with buildx"
docker buildx build --no-cache --pull \
--platform linux/amd64,linux/arm64 \
--build-arg BUILD_IMAGE=$(BUILD_IMAGE_FIPS) \
--build-arg BASE_IMAGE=$(BASE_IMAGE_FIPS) \
--build-arg TARGETOS=linux \
--build-arg TARGETARCH=amd64 \
-f docker/Dockerfile.fips \
-t $(APP_NAME_FIPS):$(APP_VERSION_NO_V) \
--push .
## --------------------------------------
## FIPS Multi-Architecture Build Targets (Fast, Parallel)
## --------------------------------------
.PHONY: build-image-fips-amd64-with-tags
build-image-fips-amd64-with-tags: ## Build FIPS Docker image for AMD64 with tags
@echo "Building mattermost-push-proxy FIPS Docker Image for AMD64"
docker build --no-cache --pull \
--build-arg BUILD_IMAGE=$(BUILD_IMAGE_FIPS) \
--build-arg BASE_IMAGE=$(BASE_IMAGE_FIPS) \
--build-arg TARGETOS=linux \
--build-arg TARGETARCH=amd64 \
-f docker/Dockerfile.fips \
-t $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-amd64 \
-t $(APP_NAME_FIPS):$(APP_VERSION_NO_V) \
.
.PHONY: build-image-fips-arm64-with-tags
build-image-fips-arm64-with-tags: ## Build FIPS Docker image for ARM64 with tags
@echo "Building mattermost-push-proxy FIPS Docker Image for ARM64"
docker build --no-cache --pull \
--build-arg BUILD_IMAGE=$(BUILD_IMAGE_FIPS) \
--build-arg BASE_IMAGE=$(BASE_IMAGE_FIPS) \
--build-arg TARGETOS=linux \
--build-arg TARGETARCH=arm64 \
-f docker/Dockerfile.fips \
-t $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-arm64 \
-t $(APP_NAME_FIPS):$(APP_VERSION_NO_V) \
.
.PHONY: docker-build-fips-parallel-with-tags
docker-build-fips-parallel-with-tags: ## Build FIPS Docker images for both architectures in parallel (FAST)
@echo "Building mattermost-push-proxy FIPS Docker Images for both platforms in parallel"
$(MAKE) build-image-fips-amd64-with-tags &
$(MAKE) build-image-fips-arm64-with-tags &
wait
@echo "Creating multi-platform manifests with clean tags"
docker manifest create $(APP_NAME_FIPS):$(APP_VERSION_NO_V) \
--amend $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-amd64 \
--amend $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-arm64
@echo "✅ Multi-platform manifests created"
.PHONY: docker-push-fips-with-tags
docker-push-fips-with-tags: ## Push FIPS Docker images with unified tags
@echo "Pushing FIPS Docker images to registry"
docker push $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-amd64
docker push $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-arm64
docker manifest push $(APP_NAME_FIPS):$(APP_VERSION_NO_V)
@echo "Cleaning up intermediate architecture-specific tags from registry"
docker rmi $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-amd64
docker rmi $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-arm64
@echo "✅ FIPS multi-platform images pushed with unified tags"
@echo "✅ Intermediate architecture-specific tags cleaned up from registry"
.PHONY: github-release-fips
github-release-fips: ## Create GitHub release for FIPS version
@echo "Creating GitHub release for FIPS version"
gh release create v$(APP_VERSION_FIPS) \
--title "Release v$(APP_VERSION_FIPS) (FIPS)" \
--notes "FIPS-compliant release of mattermost-push-proxy" \
--target main
@echo "✅ GitHub release created for FIPS version"
.PHONY: cleanup-fips-tags
cleanup-fips-tags: ## Clean up intermediate FIPS architecture-specific tags from registry
@echo "Cleaning up intermediate FIPS architecture-specific tags from registry"
@echo "Removing AMD64 tag: $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-amd64"
docker rmi $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-amd64 2>/dev/null || true
@echo "Removing ARM64 tag: $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-arm64"
docker rmi $(APP_NAME_FIPS):$(APP_VERSION_NO_V)-arm64 2>/dev/null || true
@echo "✅ Intermediate FIPS architecture-specific tags cleaned up from registry"
.PHONY: docker-push
docker-push: ## to push the docker image
@$(INFO) Pushing to registry...
$(AT)$(DOCKER) buildx build \
--no-cache --pull --platform linux/amd64,linux/arm64 \
-f ${DOCKER_FILE} . \
-t $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V} $(LATEST_DOCKER_TAG) --push || ${FAIL}
@$(OK) Pushing to registry $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}
.PHONY: docker-push-fips
docker-push-fips: ## to push the FIPS docker image (builds for default TARGET_ARCH: $(TARGET_ARCH))
@$(INFO) Pushing FIPS to registry...
$(AT)$(DOCKER) build \
--no-cache --pull \
--platform $(TARGET_OS)/$(TARGET_ARCH) \
-f ${DOCKER_FILE}.fips . \
-t $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips || ${FAIL}
$(AT)$(DOCKER) push $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips || ${FAIL}
@$(OK) Pushing FIPS to registry $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips
.PHONY: docker-push-fips-linux-amd64
docker-push-fips-linux-amd64: ## to push the FIPS docker image for Linux AMD64
@$(INFO) Pushing FIPS Linux AMD64 to registry...
$(AT)$(DOCKER) build \
--no-cache --pull \
--platform linux/amd64 \
-f ${DOCKER_FILE}.fips . \
-t $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips-linux-amd64 || ${FAIL}
$(AT)$(DOCKER) push $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips-linux-amd64 || ${FAIL}
@$(OK) Pushing FIPS Linux AMD64 to registry $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips-linux-amd64
.PHONY: docker-push-fips-linux-arm64
docker-push-fips-linux-arm64: ## to push the FIPS docker image for Linux ARM64
@$(INFO) Pushing FIPS Linux ARM64 to registry...
$(AT)$(DOCKER) build \
--no-cache --pull \
--platform linux/arm64 \
-f ${DOCKER_FILE}.fips . \
-t $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips-linux-arm64 || ${FAIL}
$(AT)$(DOCKER) push $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips-linux-arm64 || ${FAIL}
@$(OK) Pushing FIPS Linux ARM64 to registry $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION_NO_V}-fips-linux-arm64
.PHONY: docker-push-fips-parallel
docker-push-fips-parallel: ## to push FIPS docker images for both architectures in parallel
@$(INFO) Pushing FIPS Docker images for both architectures in parallel
$(AT)$(MAKE) docker-push-fips-linux-amd64 &
$(AT)$(MAKE) docker-push-fips-linux-arm64 &
wait
@$(OK) FIPS Docker images pushed for both architectures
.PHONY: docker-sign
docker-sign: ## to sign the docker image
@$(INFO) Signing the docker image...
$(AT)echo "$${COSIGN_KEY}" > cosign.key && \
$(DOCKER) run ${DOCKER_OPTS} \
--entrypoint '/bin/sh' \
-v $(PWD):/app -w /app \
-e COSIGN_PASSWORD=${COSIGN_PASSWORD} \
-e HOME="/tmp" \
${DOCKER_IMAGE_COSIGN} \
-c \
"echo Signing... && \
cosign login $(DOCKER_REGISTRY) -u ${DOCKER_USER} -p ${DOCKER_PASSWORD} && \
cosign sign --key cosign.key $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION}" || ${FAIL}
# if we are on a latest semver APP_VERSION tag, also sign latest tag
ifneq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),)
ifeq ($(shell git tag -l --sort=v:refname | tail -n1),$(APP_VERSION))
$(DOCKER) run ${DOCKER_OPTS} \
--entrypoint '/bin/sh' \
-v $(PWD):/app -w /app \
-e COSIGN_PASSWORD=${COSIGN_PASSWORD} \
-e HOME="/tmp" \
${DOCKER_IMAGE_COSIGN} \
-c \
"echo Signing... && \
cosign login $(DOCKER_REGISTRY) -u ${DOCKER_USER} -p ${DOCKER_PASSWORD} && \
cosign sign --key cosign.key $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:latest" || ${FAIL}
endif
endif
$(AT)rm -f cosign.key || ${FAIL}
@$(OK) Signing the docker image: $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION}
.PHONY: docker-verify
docker-verify: ## to verify the docker image
@$(INFO) Verifying the published docker image...
$(AT)echo "$${COSIGN_PUBLIC_KEY}" > cosign_public.key && \
$(DOCKER) run ${DOCKER_OPTS} \
--entrypoint '/bin/sh' \
-v $(PWD):/app -w /app \
${DOCKER_IMAGE_COSIGN} \
-c \
"echo Verifying... && \
cosign verify --key cosign_public.key $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION}" || ${FAIL}
# if we are on a latest semver APP_VERSION tag, also verify latest tag
ifneq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),)
ifeq ($(shell git tag -l --sort=v:refname | tail -n1),$(APP_VERSION))
$(DOCKER) run ${DOCKER_OPTS} \
--entrypoint '/bin/sh' \
-v $(PWD):/app -w /app \
${DOCKER_IMAGE_COSIGN} \
-c \
"echo Verifying... && \
cosign verify --key cosign_public.key $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:latest" || ${FAIL}
endif
endif
$(AT)rm -f cosign_public.key || ${FAIL}
@$(OK) Verifying the published docker image: $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION}
.PHONY: docker-sbom
docker-sbom: ## to print a sbom report
@$(INFO) Performing Docker sbom report...
$(AT)$(DOCKER) sbom ${APP_NAME}:${APP_VERSION} || ${FAIL}
@$(OK) Performing Docker sbom report
.PHONY: docker-scan
docker-scan: ## to print a vulnerability report
@$(INFO) Performing Docker scan report...
$(AT)$(DOCKER) scan ${APP_NAME}:${APP_VERSION} || ${FAIL}
@$(OK) Performing Docker scan report
.PHONY: docker-scout
@$(INFO) Performing Docker scout report...
$(AT)$(DOCKER) scout cves ${APP_NAME}:${APP_VERSION} || ${FAIL}
@$(OK) Performing Docker scout report
.PHONY: docker-lint
docker-lint: ## to lint the Dockerfile
@$(INFO) Dockerfile linting...
$(AT)$(DOCKER) run -i ${DOCKER_OPTS} \
${DOCKER_IMAGE_DOCKERLINT} \
< ${DOCKER_FILE} || ${FAIL}
@$(OK) Dockerfile linting
.PHONY: docker-login
docker-login: ## to login to a container registry
@$(INFO) Dockerd login to container registry ${DOCKER_REGISTRY}...
$(AT) echo "${DOCKER_PASSWORD}" | $(DOCKER) login --password-stdin -u ${DOCKER_USER} $(DOCKER_REGISTRY) || ${FAIL}
@$(OK) Dockerd login to container registry ${DOCKER_REGISTRY}...
go-build: $(GO_BUILD_PLATFORMS_ARTIFACTS) ## to build binaries
.PHONY: go-build-amd64
go-build-amd64: go-build/$(APP_NAME)-linux-amd64 ## Build AMD64 binary only
.PHONY: go-build-arm64
go-build-arm64: go-build/$(APP_NAME)-linux-arm64 ## Build ARM64 binary only
.PHONY: go-build
go-build/%:
@$(INFO) go build $*...
$(AT)target="$*"; \
command="${APP_NAME}"; \
platform_ext="$${target#$$command-*}"; \
platform="$${platform_ext%.*}"; \
export GOOS="$${platform%%-*}"; \
export GOARCH="$${platform#*-}"; \
echo export GOOS=$${GOOS}; \
echo export GOARCH=$${GOARCH}; \
CGO_ENABLED=0 \
$(GO) build ${GO_BUILD_OPTS} \
-ldflags '${GO_LDFLAGS}' \
-o ${GO_OUT_BIN_DIR}/$* \
${CONFIG_APP_CODE} || ${FAIL}
@$(OK) go build $*
.PHONY: go-build-docker
go-build-docker: # to build binaries under a controlled docker dedicated go container using DOCKER_IMAGE_GO
@$(INFO) go build docker
$(AT)$(DOCKER) run \
-v $(PWD):/app -w /app \
$(DOCKER_IMAGE_GO) \
/bin/sh -c \
"cd /app && \
make go-build" || ${FAIL}
@$(OK) go build docker
## --------------------------------------
## FIPS Build Targets
## --------------------------------------
_build-fips-internal: ## Internal FIPS build target (used by Dockerfile.fips and build-fips)
@echo "Building mattermost-push-proxy (FIPS)"
@mkdir -p $(GO_OUT_BIN_DIR)
GO111MODULE=on GOOS=$(TARGET_OS) GOARCH=$(TARGET_ARCH) CGO_ENABLED=1 go build -tags=fips,goexperiment.opensslcrypto -trimpath -o $(GO_OUT_BIN_DIR)/mattermost-push-proxy ./main.go
.PHONY: build-fips
build-fips: ## Build the mattermost-push-proxy with FIPS-compliant settings using containerized build
@echo "Building mattermost-push-proxy (FIPS - $(TARGET_ARCH))"
docker run --rm -v $(shell pwd):/app -w /app \
--entrypoint="" \
-e TARGET_OS=$(TARGET_OS) \
-e TARGET_ARCH=$(TARGET_ARCH) \
-e CGO_ENABLED=1 \
-e GOFIPS=1 \
-e GOEXPERIMENT=systemcrypto \
-e HOST_UID=$(shell id -u) \
-e HOST_GID=$(shell id -g) \
$(BUILD_IMAGE_FIPS) \
sh -c "cd /app && make _build-fips-internal TARGET_OS=\$$TARGET_OS TARGET_ARCH=\$$TARGET_ARCH && mv $(GO_OUT_BIN_DIR)/mattermost-push-proxy $(GO_OUT_BIN_DIR)/mattermost-push-proxy-fips-$(TARGET_ARCH) && chown \$$HOST_UID:\$$HOST_GID $(GO_OUT_BIN_DIR)/mattermost-push-proxy-fips-$(TARGET_ARCH)"
.PHONY: build-fips-amd64
build-fips-amd64: ## Build the mattermost-push-proxy with FIPS-compliant settings for AMD64
$(MAKE) build-fips TARGET_ARCH=amd64
.PHONY: build-fips-arm64
build-fips-arm64: ## Build the mattermost-push-proxy with FIPS-compliant settings for ARM64
$(MAKE) build-fips TARGET_ARCH=arm64
.PHONY: build-fips-all
build-fips-all: build-fips-amd64 build-fips-arm64 ## Build FIPS binaries for both architectures
.PHONY: go-run
go-run: ## to run locally for development
@$(INFO) running locally...
$(AT)$(GO) run ${GO_BUILD_OPTS} ${CONFIG_APP_CODE} || ${FAIL}
@$(OK) running locally
.PHONY: go-test
go-test: ## to run tests
@$(INFO) testing...
$(AT)$(DOCKER) run ${DOCKER_OPTS} \
-v $(PWD):/app -w /app \
$(DOCKER_IMAGE_GO) \
/bin/sh -c \
"cd /app && \
go test ${GO_TEST_OPTS} ./... " || ${FAIL}
@$(OK) testing
.PHONY: go-mod-check
go-mod-check: ## to check go mod files consistency
@$(INFO) Checking go mod files consistency...
$(AT)$(GO) mod tidy
$(AT)git --no-pager diff --exit-code go.mod go.sum || \
(${WARN} Please run "go mod tidy" and commit the changes in go.mod and go.sum. && ${FAIL} ; exit 128 )
@$(OK) Checking go mod files consistency
.PHONY: go-lint
go-lint: ## to lint go code
@$(INFO) App linting...
$(AT)$(DOCKER) run ${DOCKER_OPTS} \
-v $(PWD):/app -w /app \
${DOCKER_IMAGE_GOLINT} \
golangci-lint run ./... || ${FAIL}
@$(OK) App linting
.PHONY: go-doc
go-doc: ## to generate documentation
@$(INFO) Generating Documentation...
$(AT)$(GO) run ./scripts/env_config.go ./docs/env_config.md || ${FAIL}
@$(OK) Generating Documentation
.PHONY: check-modules
check-modules: $(OUTDATED_GEN) ## Check outdated modules
@echo Checking outdated modules
$(GO) list -mod=mod -u -m -json all | $(OUTDATED_GEN) -update -direct
.PHONY: update-modules
update-modules: ## Update all modules to latest versions
@echo Updating modules
$(GO) get -u ./...
$(GO) mod tidy
.PHONY: scan
scan: ## Scan Docker image for vulnerabilities using Docker Scout
@echo Running Docker Scout vulnerability scan
@if ! docker images -q ${APP_NAME}:${APP_VERSION_NO_V} | grep -q .; then \
echo "❌ Image ${APP_NAME}:${APP_VERSION_NO_V} not found locally. Please build it first with:"; \
echo " make build-image-amd64-with-tags (or build-image-arm64-with-tags)"; \
exit 1; \
fi
docker scout cves ${APP_NAME}:${APP_VERSION_NO_V}
.PHONY: scan-fips
scan-fips: ## Scan FIPS Docker image for vulnerabilities using Docker Scout
@echo Running Docker Scout vulnerability scan for FIPS image
@if ! docker images -q $(APP_NAME_FIPS):$(APP_VERSION_NO_V) | grep -q .; then \
echo "❌ Image $(APP_NAME_FIPS):$(APP_VERSION_NO_V) not found locally. Please build it first with:"; \
echo " make build-image-fips-amd64-with-tags (or build-image-fips-arm64-with-tags)"; \
exit 1; \
fi
docker scout cves $(APP_NAME_FIPS):$(APP_VERSION_NO_V)
.PHONY: grype
grype: ## Scan Docker image for vulnerabilities using Grype
@echo Running Grype vulnerability scan
@if ! docker images -q ${APP_NAME}:${APP_VERSION_NO_V} | grep -q .; then \
echo "❌ Image ${APP_NAME}:${APP_VERSION_NO_V} not found locally. Please build it first with:"; \
echo " make build-image-amd64-with-tags (or build-image-arm64-with-tags)"; \
exit 1; \
fi
grype docker:${APP_NAME}:${APP_VERSION_NO_V} -o table --only-fixed
.PHONY: grype-fips
grype-fips: ## Scan FIPS Docker image for vulnerabilities using Grype
@echo Running Grype vulnerability scan for FIPS image
@if ! docker images -q $(APP_NAME_FIPS):$(APP_VERSION_NO_V) | grep -q .; then \
echo "❌ Image $(APP_NAME_FIPS):$(APP_VERSION_NO_V) not found locally. Please build it first with:"; \
echo " make build-image-fips-amd64-with-tags (or build-image-fips-arm64-with-tags)"; \
exit 1; \
fi
grype docker:$(APP_NAME_FIPS):$(APP_VERSION_NO_V) -o table --only-fixed
.PHONY: security-all
security-all: ## Run all vulnerability scans (Docker Scout and Grype) for both regular and FIPS images
@echo "🔍 Running comprehensive security scans for all images..."
@echo ""
@echo "=========================================="
@echo "📊 Docker Scout - Regular Image"
@echo "=========================================="
$(MAKE) scan
@echo ""
@echo "=========================================="
@echo "📊 Docker Scout - FIPS Image"
@echo "=========================================="
$(MAKE) scan-fips
@echo ""
@echo "=========================================="
@echo "🛡️ Grype - Regular Image"
@echo "=========================================="
$(MAKE) grype
@echo ""
@echo "=========================================="
@echo "🛡️ Grype - FIPS Image"
@echo "=========================================="
$(MAKE) grype-fips
@echo ""
@echo "✅ All security scans completed!"
.PHONY: security-build-and-scan
security-build-and-scan: ## Build images and run comprehensive security scans
@echo "🚀 Building images and running comprehensive security scans..."
@echo ""
@echo "=========================================="
@echo "🔨 Building Regular ARM64 Image"
@echo "=========================================="
$(MAKE) build-image-arm64-with-tags
@echo ""
@echo "=========================================="
@echo "🔨 Building FIPS ARM64 Image"
@echo "=========================================="
$(MAKE) build-image-fips-arm64-with-tags
@echo ""
@echo "🔍 Starting security scans..."
$(MAKE) security-all
.PHONY: github-release
github-release: ## to publish a release and relevant artifacts to GitHub
@$(INFO) Generating github-release http://github.com/$(GITHUB_ORG)/$(GITHUB_REPO)/releases/tag/$(APP_VERSION) ...
ifeq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),)
$(error "We only support releases from semver tags")
else
$(AT)$(DOCKER) run \
-v $(PWD):/app -w /app \
-e GITHUB_TOKEN=${GITHUB_TOKEN} \
$(DOCKER_IMAGE_GH_CLI) \
/bin/sh -c \
"git config --global --add safe.directory /app && cd /app && \
gh release create $(APP_VERSION) --generate-notes $(GO_OUT_BIN_DIR)/*" || ${FAIL}
endif
@$(OK) Generating github-release http://github.com/$(GITHUB_ORG)/$(GITHUB_REPO)/releases/tag/$(APP_VERSION) ...
.PHONY: github-release-all
github-release-all: ## to publish both normal and FIPS releases to GitHub
@$(INFO) Generating both releases...
$(AT)$(MAKE) github-release
$(AT)$(MAKE) github-release-fips
@$(OK) Generating both releases
.PHONY: clean
clean: ## to clean-up
@$(INFO) cleaning /${GO_OUT_BIN_DIR} folder...
$(AT)rm -rf ${GO_OUT_BIN_DIR} || ${FAIL}
@$(OK) cleaning /${GO_OUT_BIN_DIR} folder
## --------------------------------------
## Tooling Binaries
## --------------------------------------
$(OUTDATED_GEN): ## Build go-mod-outdated.
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) github.com/psampaz/go-mod-outdated $(OUTDATED_BIN) $(OUTDATED_VER)