Compare commits
No commits in common. "master" and "SYS-6285" have entirely different histories.
18 changed files with 51 additions and 212 deletions
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
Copyright (c) 2022 Andreas Neue <an@dnix.de>
|
||||
Copyright (c) 2022-25 the make-deploy authors (see CONTRIBUTORS)
|
||||
Copyright (c) 2022-23 the make-deploy authors (see CONTRIBUTORS)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
|
43
Makefile
43
Makefile
|
@ -1,7 +1,4 @@
|
|||
DEPLOY_START_DELAY = 0
|
||||
DEPLOY_PROJECT_DIR =
|
||||
|
||||
MANDATORY = DEPLOY_NAME DEPLOY_HOSTS DEPLOY_PATH SSH_USER
|
||||
|
||||
-include secrets.mk
|
||||
include config.mk
|
||||
|
@ -12,19 +9,20 @@ ifeq ($(DEPLOY_TYPE), copy)
|
|||
DEPLOY_TYPE = simple
|
||||
endif
|
||||
|
||||
project_dir = $(DEPLOY_PATH)/$(DEPLOY_NAME)$(DEPLOY_PROJECT_DIR)
|
||||
|
||||
include make-deploy/$(DEPLOY_TYPE).mk
|
||||
|
||||
.PHONY: self-update mandatory cleanup prerequisites prepare build test deploy pre-deploy post-deploy pre-local post-local upload pull start stop restart reload notify
|
||||
.PHONY: self-update mandatory prerequisites prepare build test deploy pre-deploy post-deploy pre-local post-local upload pull start stop restart reload
|
||||
|
||||
self-update:
|
||||
@- git submodule update --remote
|
||||
|
||||
deploy: mandatory cleanup prepare build test upload pre-deploy pre-local pull start notify post-local post-deploy reload
|
||||
deploy: mandatory prepare build test upload pre-deploy pre-local pull start post-local post-deploy reload
|
||||
|
||||
mandatory:
|
||||
@- echo "\n### mandatory check"
|
||||
ifndef MANDATORY-$(DEPLOY_TYPE)
|
||||
$(error MANDATORY-$(DEPLOY_TYPE) is not defined)
|
||||
endif
|
||||
@ $(foreach var,$(MANDATORY), \
|
||||
if test -z "${$(var)}"; then \
|
||||
echo "Missing mandatory variable: $(var)"; \
|
||||
|
@ -35,16 +33,7 @@ mandatory:
|
|||
prerequisites:
|
||||
@- echo "\n### prerequisites ..."
|
||||
@- ./prerequisites.sh
|
||||
|
||||
ifdef DEPLOY_CLEANUP
|
||||
cleanup:
|
||||
@- echo "\n### cleanup ..."
|
||||
@- $(foreach host,$(DEPLOY_HOSTS), \
|
||||
scp cleanup.sh $(SSH_USER)@$(host):/$(DEPLOY_PATH)/$(DEPLOY_NAME); \
|
||||
ssh $(SSH_USER)@$(host) "cd $(DEPLOY_PATH)/$(DEPLOY_NAME); sh cleanup.sh $(host)"; \
|
||||
)
|
||||
endif
|
||||
|
||||
|
||||
prepare:
|
||||
@- echo "\n### preparing ..."
|
||||
@- chmod 600 secrets.mk
|
||||
|
@ -109,13 +98,7 @@ ifdef DEPLOY_HOSTS
|
|||
@- echo "\n### uploading files"
|
||||
@- $(foreach host,$(DEPLOY_HOSTS), \
|
||||
ssh $(SSH_USER)@$(host) "mkdir -p $(DEPLOY_PATH)/$(DEPLOY_NAME)"; \
|
||||
rsync_xtra=`cat .rsync`; \
|
||||
rsync_xtra_host=`cat .rsync_$(host)`; \
|
||||
param="-ravv"; \
|
||||
param="$$param $$rsync_xtra"; \
|
||||
param="$$param $$rsync_xtra_host"; \
|
||||
param="$$param --exclude '*~' --exclude '.git*' --exclude '*.swp' . $(SSH_USER)@$(host):$(DEPLOY_PATH)/$(DEPLOY_NAME)"; \
|
||||
eval rsync $$param; \
|
||||
rsync -rav --exclude '*~' --exclude '.git*' --exclude '*.swp' . $(SSH_USER)@$(host):$(DEPLOY_PATH)/$(DEPLOY_NAME); \
|
||||
ssh $(SSH_USER)@$(host) "cd $(DEPLOY_PATH)/$(DEPLOY_NAME) && chmod o-r .env secrets*"; \
|
||||
)
|
||||
endif
|
||||
|
@ -125,17 +108,7 @@ pull: pull-$(DEPLOY_TYPE)
|
|||
start: start-$(DEPLOY_TYPE)
|
||||
|
||||
stop: stop-$(DEPLOY_TYPE)
|
||||
|
||||
notify:
|
||||
ifdef DEPLOY_NOTIFY_HOSTS
|
||||
ifdef DEPLOY_NOTIFY_MAIL
|
||||
ifdef DEPLOY_NOTIFY_MSG
|
||||
@- echo "\n### notify"
|
||||
@- echo "$$DEPLOY_NOTIFY_MSG :: Please deploy manually on following hosts: $$DEPLOY_NOTIFY_HOSTS" | mail -s "make-deploy notification" $(DEPLOY_MAIL)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
logs: logs-$(DEPLOY_TYPE)
|
||||
|
||||
restart: restart-$(DEPLOY_TYPE)
|
||||
|
|
15
README.md
15
README.md
|
@ -2,6 +2,8 @@
|
|||
|
||||
A simple Makefile based deployment system.
|
||||
|
||||
**make-deploy** was initially hosted at https://git.dnix.de/an/make-deploy. Since it has become heavily used at chefkoch.de for system deployments, and lots of code is committed there, we moved the repo to https://git.chefkoch.net/pub/make-deploy. A mirror still exists at https://git.dnix.de/mirror/make-deploy.
|
||||
|
||||
**make-deploy** is licensed under the terms of the MIT-License. See [LICENSE](LICENSE) for info.
|
||||
|
||||
## Installation
|
||||
|
@ -22,7 +24,6 @@ Example `config.mk`:
|
|||
DEPLOY_PATH = /srv
|
||||
DEPLOY_TYPE = compose
|
||||
DEPLOY_HOSTS = server01.example.com server02.example.com
|
||||
DEPLOY_SUBPATH = my-sub-path
|
||||
|
||||
DOCKER_IMAGE = dr.example.com/my-project:latest
|
||||
DOCKER_LOGIN = 1
|
||||
|
@ -35,8 +36,6 @@ Example `secrets.mk`:
|
|||
DOCKER_USER = root
|
||||
DOCKER_PASS = secret1234
|
||||
|
||||
`DEPLOY_SUBPATH` is used to define a location for `DEPLOY_TYPE` = compose that states where the docker-compose.yml is placed at. If that variable is undefined, it is expected to be in projects root.
|
||||
|
||||
By setting `DEPLOY_CONFIG_OVERRIDE`, an alternative config can be loaded to override existing configuration settings. This is useful in script calling make deploy or in a `.gitlab-ci.yml` in order to control settings for different targets.
|
||||
|
||||
## Usage
|
||||
|
@ -55,8 +54,6 @@ Relying only on GNU Make and simple CLI tools makes deployments robust and still
|
|||
|
||||
`mandatory`: Checks if all needed variables are set in config.mk.
|
||||
|
||||
`cleanup`: Runs `cleanup.sh` on each remote target, meant to be used to wipe contents of prior installations.
|
||||
|
||||
`prepare`: Creates needed directory for the deployment on the target system, sets secure file permissions for `secrets.mk`.
|
||||
|
||||
`build`: Calls `build.sh` locally. This is for building purposts, e.g. docker build and push.
|
||||
|
@ -134,11 +131,3 @@ systemd. (TBD)
|
|||
##### apt
|
||||
|
||||
APT deployment on debian-like distributions.
|
||||
|
||||
##### tf
|
||||
|
||||
Deploy via Terraform. Config is mostly done with terraform.
|
||||
|
||||
`TF_FLAGS`: What you think.
|
||||
|
||||
`TF_TARGET`: Limits deployment to `TF_TARGET`.
|
||||
|
|
2
apt.mk
2
apt.mk
|
@ -1,4 +1,4 @@
|
|||
MANDATORY = $(shell printenv MANDATORY) APT_PACKAGES
|
||||
MANDATORY-apt = APT_PACKAGES
|
||||
|
||||
pull-apt:
|
||||
# nop
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
MANDATORY = $(shell printenv MANDATORY) SSH_USER DEPLOY_COMPOSE_FILE DEPLOY_COMPOSE_PROJECT
|
||||
|
||||
ifndef DOCKER_LOGIN
|
||||
DOCKER_LOGIN = 0
|
||||
endif
|
||||
MANDATORY-compose-file = DEPLOY_HOSTS DEPLOY_TYPE TAG DEPLOY_COMPOSE_FILE DEPLOY_COMPOSE_PROJECT
|
||||
|
||||
pull-compose-file:
|
||||
@- echo "\n### pulling image(s)"
|
||||
|
@ -13,10 +9,7 @@ pull-compose-file:
|
|||
echo "\n#### perfom docker login with user $(DOCKER_USER) on $(DOCKER_REGISTRY)"; \
|
||||
ssh $(SSH_USER)@$(host) "docker login -u $(DOCKER_USER) -p $(DOCKER_PASS) $(DOCKER_REGISTRY)"; \
|
||||
fi; \
|
||||
if test -n "$(DOCKER_IMAGE)" ; \
|
||||
then \
|
||||
ssh $(SSH_USER)@$(host) "docker pull $(DOCKER_IMAGE)"; \
|
||||
fi; \
|
||||
ssh $(SSH_USER)@$(host) "docker pull $(DOCKER_IMAGE)"; \
|
||||
)
|
||||
|
||||
start-compose-file:
|
||||
|
|
48
compose.mk
48
compose.mk
|
@ -1,45 +1,39 @@
|
|||
ifdef DOCKER_LOGIN
|
||||
MANDATORY = $(shell printenv MANDATORY) DOCKER_USER DOCKER_PASS DOCKER_REGISTRY
|
||||
endif
|
||||
|
||||
ifndef DOCKER_LOGIN
|
||||
DOCKER_LOGIN = 0
|
||||
endif
|
||||
MANDATORY-compose = DEPLOY_HOSTS DEPLOY_TYPE TAG
|
||||
|
||||
pull-compose:
|
||||
@echo "\n### pulling image(s)"
|
||||
$(foreach host,$(DEPLOY_HOSTS), \
|
||||
@- echo "\n### pulling image(s)"
|
||||
@- $(foreach host,$(DEPLOY_HOSTS), \
|
||||
echo "$(host)"; \
|
||||
if test "$(DOCKER_LOGIN)" = "1" ; \
|
||||
if test $(DOCKER_LOGIN) -eq 1 ; \
|
||||
then \
|
||||
echo "\n#### perform docker login with user $(DOCKER_USER) on $(DOCKER_REGISTRY)"; \
|
||||
echo "\n#### perfom docker login with user $(DOCKER_USER) on $(DOCKER_REGISTRY)"; \
|
||||
ssh $(SSH_USER)@$(host) "docker login -u $(DOCKER_USER) -p $(DOCKER_PASS) $(DOCKER_REGISTRY)"; \
|
||||
fi; \
|
||||
if test -n "$(DOCKER_IMAGE)" ; \
|
||||
then \
|
||||
ssh $(SSH_USER)@$(host) "docker pull $(DOCKER_IMAGE)"; \
|
||||
fi; \
|
||||
ssh $(SSH_USER)@$(host) "docker pull $(DOCKER_IMAGE)"; \
|
||||
)
|
||||
|
||||
start-compose:
|
||||
@echo "\n### starting service(s)"
|
||||
$(foreach host,$(DEPLOY_HOSTS), \
|
||||
@- echo "\n### starting service(s)"
|
||||
@- $(foreach host,$(DEPLOY_HOSTS), \
|
||||
echo "$(host)"; \
|
||||
ssh $(SSH_USER)@$(host) "cd $(project_dir); docker-compose up -d"; \
|
||||
ssh $(SSH_USER)@$(host) "cd $(DEPLOY_PATH)/$(DEPLOY_NAME); docker-compose up -d"; \
|
||||
sleep $(DEPLOY_START_DELAY); \
|
||||
)
|
||||
|
||||
stop-compose:
|
||||
@- echo "\n### stopping service(s)"
|
||||
@- $(foreach host,$(DEPLOY_HOSTS), \
|
||||
echo "$(host)"; \
|
||||
ssh $(SSH_USER)@$(host) "cd $(DEPLOY_PATH)/$(DEPLOY_NAME); docker-compose down"; \
|
||||
)
|
||||
|
||||
logs-compose:
|
||||
@echo "\n### logs"
|
||||
$(foreach host,$(DEPLOY_HOSTS), \
|
||||
echo "$(host)"; \
|
||||
ssh $(SSH_USER)@$(host) "cd $(project_dir); docker-compose logs -f $(service)"; \
|
||||
)
|
||||
|
||||
@- echo "\n### logs"
|
||||
@- ssh $(SSH_USER)@$(host) "cd $(DEPLOY_PATH)/$(DEPLOY_NAME); docker-compose logs -f $(service)"
|
||||
|
||||
restart-compose:
|
||||
@echo "\n### restarting service(s)"
|
||||
$(foreach host,$(DEPLOY_HOSTS), \
|
||||
@- $(foreach host,$(DEPLOY_HOSTS), \
|
||||
echo "$(host)"; \
|
||||
ssh $(SSH_USER)@$(host) "cd $(project_dir); docker-compose restart"; \
|
||||
ssh $(SSH_USER)@$(host) "cd $(DEPLOY_PATH)/$(DEPLOY_NAME); docker-compose restart"; \
|
||||
sleep $(DEPLOY_START_DELAY); \
|
||||
)
|
||||
|
|
2
cron.mk
2
cron.mk
|
@ -1,4 +1,4 @@
|
|||
MANDATORY = $(shell printenv MANDATORY) CRON_USER
|
||||
MANDATORY-cron = true
|
||||
|
||||
start-cron:
|
||||
@- echo "\n### starting..."
|
||||
|
|
32
helm.mk
32
helm.mk
|
@ -1,4 +1,4 @@
|
|||
MANDATORY = $(shell printenv MANDATORY) K8S_CONTEXT HELM_CHART_NAME HELM_CHART_PATH HELM_VALUES_FILE K8S_NAMESPACE
|
||||
MANDATORY-helm = K8S_CONTEXT HELM_CHART_NAME HELM_CHART_PATH HELM_VALUES_FILE NAMESPACE
|
||||
# helm
|
||||
#
|
||||
# deploys helm charts on k8s via helm
|
||||
|
@ -10,47 +10,25 @@ start-helm:
|
|||
|
||||
ifdef TEST_MODE
|
||||
@- echo "Testing chart with 'helm template --debug'"
|
||||
@ envsubst < ${HELM_VALUES_FILE} | helm template ${HELM_CHART_NAME} ${HELM_CHART_PATH} --debug --values -
|
||||
@ envsubst < ${HELM_VALUES_FILE} | helm template ${HELM_CHART_NAME} ${HELM_CHART_PATH} --debug --values -
|
||||
endif
|
||||
|
||||
ifdef NAMESPACE
|
||||
@ $(eval K8S_NAMESPACE = $(NAMESPACE))
|
||||
K8S_NAMESPACE = $(NAMESPACE)
|
||||
endif
|
||||
|
||||
ifdef HELM_REPO_URL
|
||||
@- echo "Helm Repo: $(HELM_REPO_URL)"
|
||||
@ helm repo add $(HELM_CHART_NAME) $(HELM_REPO_URL)
|
||||
@- helm repo update $(HELM_CHART_NAME)
|
||||
endif
|
||||
|
||||
ifdef DEBUG_MODE
|
||||
@- echo "\n### deploying helm chart with --debug flag"
|
||||
@- echo "-- DEBUG DEPLOYMENT --------------------"
|
||||
@- echo " "
|
||||
@- echo "Namespace: $(K8S_NAMESPACE)"
|
||||
@- echo "Helm Chart: $(HELM_CHART_PATH)"
|
||||
@- echo "Helm Version (use latest if empty): $(HELM_CHART_VERSION)"
|
||||
@- echo "Values File: $(HELM_VALUES_FILE)"
|
||||
@- echo " "
|
||||
@- echo "----------------------------------"
|
||||
|
||||
@ envsubst < $(HELM_VALUES_FILE) | helm upgrade $(HELM_CHART_NAME) $(HELM_CHART_PATH) -n $(K8S_NAMESPACE) --install --debug --wait --values - --version "$(HELM_CHART_VERSION)"
|
||||
|
||||
else
|
||||
|
||||
@- echo "\n### deploying helm chart"
|
||||
@- echo "-- DEPLOYMENT --------------------"
|
||||
@- echo " "
|
||||
@- echo "Namespace: $(K8S_NAMESPACE)"
|
||||
@- echo "Helm Chart: $(HELM_CHART_PATH)"
|
||||
@- echo "Helm Version (use latest if empty): $(HELM_CHART_VERSION)"
|
||||
@- echo "Values File: $(HELM_VALUES_FILE)"
|
||||
@- echo " "
|
||||
@- echo "----------------------------------"
|
||||
|
||||
@ envsubst < $(HELM_VALUES_FILE) | helm upgrade $(HELM_CHART_NAME) $(HELM_CHART_PATH) -n $(K8S_NAMESPACE) --install --wait --values - --version "$(HELM_CHART_VERSION)"
|
||||
@ envsubst < $(HELM_VALUES_FILE) | helm upgrade $(HELM_CHART_NAME) $(HELM_CHART_PATH) -n $(K8S_NAMESPACE) --install --wait --values -
|
||||
|
||||
|
||||
endif
|
||||
|
||||
|
||||
pull-helm:
|
||||
|
|
7
k8s.mk
7
k8s.mk
|
@ -2,16 +2,15 @@
|
|||
#
|
||||
# deploys k8s
|
||||
|
||||
MANDATORY = $(shell printenv MANDATORY) K8S_CONTEXT
|
||||
K8S_MANIFEST_FILENAME ?= kubernetes.yaml
|
||||
MANDATORY-k8s = K8S_CONTEXT
|
||||
|
||||
start-k8s:
|
||||
@- echo "\n### starting deployment with k8s"
|
||||
@- echo "\n### setting kubernetes context to $(K8S_CONTEXT)"
|
||||
@ kubectl config use-context $(K8S_CONTEXT)
|
||||
|
||||
@- echo "\n### run kubectl apply -f $(K8S_MANIFEST_FILENAME) and secrets.yaml"
|
||||
@ cat $(K8S_MANIFEST_FILENAME) secrets.yaml | envsubst | kubectl apply $(K8S_ADDITIONAL_PARAMS) -f -
|
||||
@- echo "\n### run kubectl apply -f kubernetes.yaml and secrets.yaml"
|
||||
@ cat kubernetes.yaml secrets.yaml | envsubst | kubectl apply -f -
|
||||
|
||||
pull-k8s:
|
||||
# nop
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
MANDATORY = $(shell printenv MANDATORY) K8S_CONTEXT KUSTOMIZE_FOLDER NAMESPACE
|
||||
MANDATORY-kustomize = K8S_CONTEXT KUSTOMIZE_FOLDER NAMESPACE
|
||||
# kustomize
|
||||
#
|
||||
# this uses kustomize to install stuff on k8s
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
# simple
|
||||
MANDATORY-copy = DEPLOY_TYPE DEPLOY_HOSTS SSH_USER DEPLOY_PATH DEPLOY_NAME
|
||||
MANDATORY-simple = $(MANDATORY-copy)
|
||||
|
||||
# copy
|
||||
#
|
||||
# this just rsyncs the stuff to the remote server(s) and runs
|
||||
# build.sh, pre-/post-deploy, pre-/post-local, start.sh, etc.
|
||||
|
||||
ifndef DEPLOY_StART_DELAY
|
||||
DEPLOY_START_DELAY = 0
|
||||
endif
|
||||
|
||||
pull-simple:
|
||||
# nop
|
||||
|
||||
|
|
2
swarm.mk
2
swarm.mk
|
@ -1,3 +1,5 @@
|
|||
MANDATORY-swarm = DEPLOY_HOSTS DEPLOY_TYPE TAG
|
||||
|
||||
pull-swarm:
|
||||
|
||||
start-swarm:
|
||||
|
|
50
test.sh
50
test.sh
|
@ -1,50 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
TESTS=$DIR/make-deploy/testing
|
||||
|
||||
function prepdir() {
|
||||
mkdir $DIR/make-deploy
|
||||
cp -r * $DIR/make-deploy
|
||||
cd $DIR
|
||||
ln -s make-deploy/Makefile
|
||||
touch secrets.mk
|
||||
echo "::::::::: testing directory root: $DIR"
|
||||
}
|
||||
|
||||
function cleandir(){
|
||||
rm -fr $DIR
|
||||
echo "::::::::: cleanup done"
|
||||
}
|
||||
|
||||
function test_with_minimal_vars_apt(){
|
||||
echo "::::::::: $FUNCNAME ::::::::::::"
|
||||
cp $TESTS/apt_config.mk config.mk
|
||||
make deploy
|
||||
}
|
||||
|
||||
function test_with_minimal_vars_compose(){
|
||||
echo "::::::::: $FUNCNAME ::::::::::::"
|
||||
cp $TESTS/compose_config.mk config.mk
|
||||
cp $TESTS/docker-compose.yml .
|
||||
make deploy
|
||||
rm -f docker-compose.yml
|
||||
}
|
||||
|
||||
function test_with_minimal_vars_compose_file(){
|
||||
echo "::::::::: $FUNCNAME ::::::::::::"
|
||||
cp $TESTS/compose_file_config.mk config.mk
|
||||
cp $TESTS/docker-compose.yml .
|
||||
make deploy
|
||||
rm -f docker-compose.yml
|
||||
}
|
||||
|
||||
prepdir
|
||||
|
||||
test_with_minimal_vars_apt
|
||||
test_with_minimal_vars_compose
|
||||
test_with_minimal_vars_compose_file
|
||||
|
||||
cleandir
|
|
@ -1,6 +0,0 @@
|
|||
DEPLOY_NAME = make-deploy-test-apt
|
||||
DEPLOY_PATH = /tmp
|
||||
DEPLOY_HOSTS = localhost
|
||||
DEPLOY_TYPE = apt
|
||||
SSH_USER = $(USER)
|
||||
APT_PACKAGES = curl
|
|
@ -1,5 +0,0 @@
|
|||
DEPLOY_NAME = make-deploy-test-compose
|
||||
DEPLOY_PATH = /tmp
|
||||
DEPLOY_HOSTS = localhost
|
||||
DEPLOY_TYPE = compose
|
||||
SSH_USER = $(USER)
|
|
@ -1,7 +0,0 @@
|
|||
DEPLOY_NAME = make-deploy-test-compose-file
|
||||
DEPLOY_PATH = /tmp
|
||||
DEPLOY_HOSTS = localhost
|
||||
DEPLOY_TYPE = compose-file
|
||||
SSH_USER = $(USER)
|
||||
DEPLOY_COMPOSE_PROJECT = make-deploy-test-compose-file
|
||||
DEPLOY_COMPOSE_FILE = docker-compose.yml
|
|
@ -1,4 +0,0 @@
|
|||
services:
|
||||
curl:
|
||||
image: "alpine/curl"
|
||||
command: -s -o /dev/null https://www.test.de
|
16
tf.mk
16
tf.mk
|
@ -1,16 +0,0 @@
|
|||
MANDATORY = $(shell printenv MANDATORY) TF_FLAGS
|
||||
|
||||
# tf
|
||||
#
|
||||
# deploys via tf apply
|
||||
#
|
||||
start-tf:
|
||||
ifdef TF_TARGET
|
||||
terraform apply $(TF_FLAGS) -t $(TF_TARGET)
|
||||
else
|
||||
terraform apply $(TF_FLAGS)
|
||||
endif
|
||||
|
||||
|
||||
pull-tf:
|
||||
# nop
|
Loading…
Add table
Add a link
Reference in a new issue