Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
|
989e1dd466 | ||
|
397884c2e5 | ||
5e65cd8901 | |||
c0c69f6d63 | |||
a71bfedd03 | |||
ff17cb3470 | |||
856dbb1224 | |||
015e7a1c77 | |||
d266866ae9 | |||
151acc6d00 | |||
838c68baef | |||
5c3578f8fb | |||
|
2110663617 | ||
|
91b07f2f90 | ||
|
c29425e838 | ||
|
8aed1b03e4 | ||
|
d88367000b | ||
87ebff5bca |
19 changed files with 129 additions and 28 deletions
|
@ -1,4 +1,5 @@
|
|||
FROM debian:buster
|
||||
ARG BASE_IMAGE=debian:buster
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
# pi-gen
|
||||
|
||||
_Tool used to create the raspberrypi.org Raspbian images_
|
||||
Tool used to create Raspberry Pi OS images. (Previously known as Raspbian).
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
pi-gen runs on Debian based operating systems. Currently it is only supported on
|
||||
pi-gen runs on Debian-based operating systems. Currently it is only supported on
|
||||
either Debian Buster or Ubuntu Xenial and is known to have issues building on
|
||||
earlier releases of these systems. On other Linux distributions it may be possible
|
||||
to use the Docker build described below.
|
||||
|
||||
To install the required dependencies for pi-gen you should run:
|
||||
To install the required dependencies for `pi-gen` you should run:
|
||||
|
||||
```bash
|
||||
apt-get install coreutils quilt parted qemu-user-static debootstrap zerofree zip \
|
||||
|
@ -68,7 +68,7 @@ The following environment variables are supported:
|
|||
system for each build stage, amounting to tens of gigabytes in the case of
|
||||
Raspbian.
|
||||
|
||||
**CAUTION**: If your working directory is on an NTFS partition you probably won't be able to build. Make sure this is a proper Linux filesystem.
|
||||
**CAUTION**: If your working directory is on an NTFS partition you probably won't be able to build: make sure this is a proper Linux filesystem.
|
||||
|
||||
* `DEPLOY_DIR` (Default: `"$BASE_DIR/deploy"`)
|
||||
|
||||
|
@ -124,12 +124,24 @@ The following environment variables are supported:
|
|||
|
||||
* `WPA_ESSID`, `WPA_PASSWORD` and `WPA_COUNTRY` (Default: unset)
|
||||
|
||||
If these are set, they are use to configure `wpa_supplicant.conf`, so that the Raspberry Pi can automatically connect to a wifi network on first boot. If `WPA_ESSID` is set and `WPA_PASSWORD` is unset an unprotected wifi network will be configured. If set, `WPA_PASSWORD` must be between 8 and 63 characters.
|
||||
If these are set, they are use to configure `wpa_supplicant.conf`, so that the Raspberry Pi can automatically connect to a wireless network on first boot. If `WPA_ESSID` is set and `WPA_PASSWORD` is unset an unprotected wireless network will be configured. If set, `WPA_PASSWORD` must be between 8 and 63 characters.
|
||||
|
||||
* `ENABLE_SSH` (Default: `0`)
|
||||
|
||||
Setting to `1` will enable ssh server for remote log in. Note that if you are using a common password such as the defaults there is a high risk of attackers taking over you Raspberry Pi.
|
||||
|
||||
* `PUBKEY_SSH_FIRST_USER` (Default: unset)
|
||||
|
||||
Setting this to a value will make that value the contents of the FIRST_USER_NAME's ~/.ssh/authorized_keys. Obviously the value should
|
||||
therefore be a valid authorized_keys file. Note that this does not
|
||||
automatically enable SSH.
|
||||
|
||||
* `PUBKEY_ONLY_SSH` (Default: `0`)
|
||||
|
||||
* Setting to `1` will disable password authentication for SSH and enable
|
||||
public key authentication. Note that if SSH is not enabled this will take
|
||||
effect when SSH becomes enabled.
|
||||
|
||||
* `STAGE_LIST` (Default: `stage*`)
|
||||
|
||||
If set, then instead of working through the numeric stages in order, this list will be followed. For example setting to `"stage0 stage1 mystage stage2"` will run the contents of `mystage` before stage2. Note that quotes are needed around the list. An absolute or relative path can be given for stages outside the pi-gen directory.
|
||||
|
@ -264,7 +276,7 @@ maintenance and allows for more easy customization.
|
|||
|
||||
- **Stage 2** - lite system. This stage produces the Raspbian-Lite image. It
|
||||
installs some optimized memory functions, sets timezone and charmap
|
||||
defaults, installs fake-hwclock and ntp, wifi and bluetooth support,
|
||||
defaults, installs fake-hwclock and ntp, wireless LAN and bluetooth support,
|
||||
dphys-swapfile, and other basics for managing the hardware. It also
|
||||
creates necessary groups and gives the pi user access to sudo and the
|
||||
standard console hardware permission groups.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# TouchPrint
|
||||
TouchPrint is a lightweight fork of Raspberry Pi OS whos purpose is to host OctoPrint and to display it on a connected screen.
|
||||
TouchPrint is very customizable, and both the GUI and/or OctoPrint can be disabled if you already have an existing setup to perform those tasks.
|
||||
TouchPrint is a lightweight fork of Raspberry Pi OS whos purpose is to securely host OctoPrint and to (optionally) display it on a connected screen.
|
||||
TouchPrint is very customizable. Both the GUI and/or OctoPrint can be disabled if you already have an existing setup to perform these tasks.
|
||||
|
||||
# Table of Contents
|
||||
- [Screenshots](#screenshots)
|
||||
|
@ -44,6 +44,7 @@ TODO
|
|||
| Ubuntu/Debian | `apt install xz-utils` |
|
||||
| Arch/Manjaro | `pacman -S xz` |
|
||||
| Mac OS | `brew install xz` |
|
||||
| FreeBSD | `pkg install xz-utils` |
|
||||
|
||||
4. Run `xz -d -c IMAGE_NAME.img.xz | sudo dd of=/dev/sdX`, replacing IMAGE\_NAME and /dev/sdX with the appropriate paths, and adding flags per your preferences.
|
||||
|
||||
|
|
7
TODO.md
7
TODO.md
|
@ -3,10 +3,12 @@
|
|||
## First time/TP-Config
|
||||
- Add network configuration (replace nmtui)
|
||||
- Add hostname configuration (replace nmtui)
|
||||
- Add timezone configuration (replace tzdata)
|
||||
- Import/export settings
|
||||
- Enable/Disable HTTP redirect from port 80
|
||||
|
||||
## OctoPrint
|
||||
- Python 3
|
||||
## Nginx
|
||||
- Update deprecated "ssl" option to "listen PORT ssl;"
|
||||
|
||||
## Misc
|
||||
- Cleanup
|
||||
|
@ -16,3 +18,4 @@
|
|||
## Things to consider
|
||||
- Wayland using labwc/waybox
|
||||
- Detect if frontend did not load correctly (probably not easy)
|
||||
- Onboard
|
||||
|
|
|
@ -74,7 +74,17 @@ fi
|
|||
# Modify original build-options to allow config file to be mounted in the docker container
|
||||
BUILD_OPTS="$(echo "${BUILD_OPTS:-}" | sed -E 's@\-c\s?([^ ]+)@-c /config@')"
|
||||
|
||||
${DOCKER} build -t pi-gen "${DIR}"
|
||||
# Check the arch of the machine we're running on. If it's 64-bit, use a 32-bit base image instead
|
||||
case "$(uname -m)" in
|
||||
x86_64|aarch64)
|
||||
BASE_IMAGE=i386/debian:buster
|
||||
;;
|
||||
*)
|
||||
BASE_IMAGE=debian:buster
|
||||
;;
|
||||
esac
|
||||
${DOCKER} build --build-arg BASE_IMAGE=${BASE_IMAGE} -t pi-gen "${DIR}"
|
||||
|
||||
if [ "${CONTAINER_EXISTS}" != "" ]; then
|
||||
trap 'echo "got CTRL+C... please wait 5s" && ${DOCKER} stop -t 5 ${CONTAINER_NAME}_cont' SIGINT SIGTERM
|
||||
time ${DOCKER} run --rm --privileged \
|
||||
|
|
8
build.sh
8
build.sh
|
@ -172,6 +172,7 @@ export WPA_ESSID
|
|||
export WPA_PASSWORD
|
||||
export WPA_COUNTRY
|
||||
export ENABLE_SSH="${ENABLE_SSH:-0}"
|
||||
export PUBKEY_ONLY_SSH="${PUBKEY_ONLY_SSH:-0}"
|
||||
|
||||
export LOCALE_DEFAULT="${LOCALE_DEFAULT:-en_US.UTF-8}"
|
||||
|
||||
|
@ -182,6 +183,8 @@ export TIMEZONE_DEFAULT="${TIMEZONE_DEFAULT:-UTC}"
|
|||
|
||||
export GIT_HASH=${GIT_HASH:-"$(git rev-parse HEAD)"}
|
||||
|
||||
export PUBKEY_SSH_FIRST_USER
|
||||
|
||||
export CLEAN
|
||||
export IMG_NAME
|
||||
export APT_PROXY
|
||||
|
@ -227,6 +230,11 @@ if [[ -n "${WPA_PASSWORD}" && ${#WPA_PASSWORD} -lt 8 || ${#WPA_PASSWORD} -gt 63
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${PUBKEY_ONLY_SSH}" = "1" && -z "${PUBKEY_SSH_FIRST_USER}" ]]; then
|
||||
echo "Must set 'PUBKEY_SSH_FIRST_USER' to a valid SSH public key if using PUBKEY_ONLY_SSH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${WORK_DIR}"
|
||||
log "Begin ${BASE_DIR}"
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
rm -f "${ROOTFS_DIR}/etc/apt/apt.conf.d/51cache"
|
||||
find "${ROOTFS_DIR}/var/lib/apt/lists/" -type f -delete
|
||||
on_chroot << EOF
|
||||
apt-get update
|
||||
apt-get -y dist-upgrade
|
||||
|
|
|
@ -19,8 +19,6 @@ if [ -d "${ROOTFS_DIR}/home/${FIRST_USER_NAME}/.config" ]; then
|
|||
chmod 700 "${ROOTFS_DIR}/home/${FIRST_USER_NAME}/.config"
|
||||
fi
|
||||
|
||||
echo "Part 3"
|
||||
rm -f "${ROOTFS_DIR}/etc/apt/apt.conf.d/51cache"
|
||||
rm -f "${ROOTFS_DIR}/usr/bin/qemu-arm-static"
|
||||
|
||||
echo "Part 4"
|
||||
|
|
|
@ -40,8 +40,32 @@ BOOT_LENGTH=$(echo "$PARTED_OUT" | grep -e '^1:' | cut -d':' -f 4 | tr -d B)
|
|||
ROOT_OFFSET=$(echo "$PARTED_OUT" | grep -e '^2:' | cut -d':' -f 2 | tr -d B)
|
||||
ROOT_LENGTH=$(echo "$PARTED_OUT" | grep -e '^2:' | cut -d':' -f 4 | tr -d B)
|
||||
|
||||
BOOT_DEV=$(losetup --show -f -o "${BOOT_OFFSET}" --sizelimit "${BOOT_LENGTH}" "${IMG_FILE}")
|
||||
ROOT_DEV=$(losetup --show -f -o "${ROOT_OFFSET}" --sizelimit "${ROOT_LENGTH}" "${IMG_FILE}")
|
||||
echo "Mounting BOOT_DEV..."
|
||||
cnt=0
|
||||
until BOOT_DEV=$(losetup --show -f -o "${BOOT_OFFSET}" --sizelimit "${BOOT_LENGTH}" "${IMG_FILE}"); do
|
||||
if [ $cnt -lt 5 ]; then
|
||||
cnt=$((cnt + 1))
|
||||
echo "Error in losetup for BOOT_DEV. Retrying..."
|
||||
sleep 5
|
||||
else
|
||||
echo "ERROR: losetup for BOOT_DEV failed; exiting"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Mounting ROOT_DEV..."
|
||||
cnt=0
|
||||
until ROOT_DEV=$(losetup --show -f -o "${ROOT_OFFSET}" --sizelimit "${ROOT_LENGTH}" "${IMG_FILE}"); do
|
||||
if [ $cnt -lt 5 ]; then
|
||||
cnt=$((cnt + 1))
|
||||
echo "Error in losetup for ROOT_DEV. Retrying..."
|
||||
sleep 5
|
||||
else
|
||||
echo "ERROR: losetup for ROOT_DEV failed; exiting"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "/boot: offset $BOOT_OFFSET, length $BOOT_LENGTH"
|
||||
echo "/: offset $ROOT_OFFSET, length $ROOT_LENGTH"
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ sed "${NOOBS_DIR}/os.json" -i -e "s|UNRELEASED|${IMG_DATE}|"
|
|||
sed "${NOOBS_DIR}/os.json" -i -e "s|NOOBS_NAME|${NOOBS_NAME}|"
|
||||
sed "${NOOBS_DIR}/os.json" -i -e "s|NOOBS_DESCRIPTION|${NOOBS_DESCRIPTION}|"
|
||||
sed "${NOOBS_DIR}/os.json" -i -e "s|RELEASE|${RELEASE}|"
|
||||
sed "${NOOBS_DIR}/os.json" -i -e "s|KERNEL|$(cat "${STAGE_WORK_DIR}/kernel_version")|"
|
||||
|
||||
sed "${NOOBS_DIR}/release_notes.txt" -i -e "s|UNRELEASED|${IMG_DATE}|"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"description": "NOOBS_DESCRIPTION",
|
||||
"feature_level": 35120124,
|
||||
"kernel": "4.19",
|
||||
"kernel": "KERNEL",
|
||||
"name": "NOOBS_NAME",
|
||||
"password": "raspberry",
|
||||
"release_date": "UNRELEASED",
|
||||
|
|
|
@ -17,8 +17,32 @@ BOOT_LENGTH=$(echo "$PARTED_OUT" | grep -e '^1:' | cut -d':' -f 4 | tr -d B)
|
|||
ROOT_OFFSET=$(echo "$PARTED_OUT" | grep -e '^2:' | cut -d':' -f 2 | tr -d B)
|
||||
ROOT_LENGTH=$(echo "$PARTED_OUT" | grep -e '^2:' | cut -d':' -f 4 | tr -d B)
|
||||
|
||||
BOOT_DEV=$(losetup --show -f -o "${BOOT_OFFSET}" --sizelimit "${BOOT_LENGTH}" "${IMG_FILE}")
|
||||
ROOT_DEV=$(losetup --show -f -o "${ROOT_OFFSET}" --sizelimit "${ROOT_LENGTH}" "${IMG_FILE}")
|
||||
echo "Mounting BOOT_DEV..."
|
||||
cnt=0
|
||||
until BOOT_DEV=$(losetup --show -f -o "${BOOT_OFFSET}" --sizelimit "${BOOT_LENGTH}" "${IMG_FILE}"); do
|
||||
if [ $cnt -lt 5 ]; then
|
||||
cnt=$((cnt + 1))
|
||||
echo "Error in losetup for BOOT_DEV. Retrying..."
|
||||
sleep 5
|
||||
else
|
||||
echo "ERROR: losetup for BOOT_DEV failed; exiting"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Mounting ROOT_DEV..."
|
||||
cnt=0
|
||||
until ROOT_DEV=$(losetup --show -f -o "${ROOT_OFFSET}" --sizelimit "${ROOT_LENGTH}" "${IMG_FILE}"); do
|
||||
if [ $cnt -lt 5 ]; then
|
||||
cnt=$((cnt + 1))
|
||||
echo "Error in losetup for ROOT_DEV. Retrying..."
|
||||
sleep 5
|
||||
else
|
||||
echo "ERROR: losetup for ROOT_DEV failed; exiting"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "/boot: offset $BOOT_OFFSET, length $BOOT_LENGTH"
|
||||
echo "/: offset $ROOT_OFFSET, length $ROOT_LENGTH"
|
||||
|
||||
|
@ -30,6 +54,9 @@ mount "$BOOT_DEV" "${STAGE_WORK_DIR}/rootfs/boot"
|
|||
|
||||
ln -sv "/lib/systemd/system/apply_noobs_os_config.service" "$ROOTFS_DIR/etc/systemd/system/multi-user.target.wants/apply_noobs_os_config.service"
|
||||
|
||||
KERNEL_VER="$(zgrep -oPm 1 "Linux version \K(.*)$" "${STAGE_WORK_DIR}/rootfs/usr/share/doc/raspberrypi-kernel/changelog.Debian.gz" | cut -f-2 -d.)"
|
||||
echo "$KERNEL_VER" > "${STAGE_WORK_DIR}/kernel_version"
|
||||
|
||||
bsdtar --numeric-owner --format gnutar -C "${STAGE_WORK_DIR}/rootfs/boot" -cpf - . | xz -T0 > "${NOOBS_DIR}/boot.tar.xz"
|
||||
umount "${STAGE_WORK_DIR}/rootfs/boot"
|
||||
bsdtar --numeric-owner --format gnutar -C "${STAGE_WORK_DIR}/rootfs" --one-file-system -cpf - . | xz -T0 > "${NOOBS_DIR}/root.tar.xz"
|
||||
|
|
|
@ -14,11 +14,9 @@ parted
|
|||
unzip
|
||||
build-essential
|
||||
manpages-dev
|
||||
python
|
||||
bash-completion
|
||||
gdb
|
||||
pkg-config
|
||||
python-rpi.gpio
|
||||
v4l-utils
|
||||
avahi-daemon
|
||||
hardlink
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
+++ stage2/rootfs/boot/cmdline.txt
|
||||
@@ -1 +1 @@
|
||||
-console=serial0,115200 console=tty1 root=ROOTDEV rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
|
||||
+console=serial0,115200 console=tty1 root=ROOTDEV rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet init=/usr/lib/raspi-config/init_resize.sh
|
||||
+console=serial0,115200 console=tty12 root=ROOTDEV rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet loglevel=3 init=/usr/lib/raspi-config/init_resize.sh
|
||||
|
|
|
@ -12,6 +12,18 @@ install -m 644 files/console-setup "${ROOTFS_DIR}/etc/default/"
|
|||
|
||||
install -m 755 files/rc.local "${ROOTFS_DIR}/etc/"
|
||||
|
||||
if [ -n "${PUBKEY_SSH_FIRST_USER}" ]; then
|
||||
install -v -m 0700 -o 1000 -g 1000 -d "${ROOTFS_DIR}"/home/"${FIRST_USER_NAME}"/.ssh
|
||||
echo "${PUBKEY_SSH_FIRST_USER}" >"${ROOTFS_DIR}"/home/"${FIRST_USER_NAME}"/.ssh/authorized_keys
|
||||
chown 1000:1000 "${ROOTFS_DIR}"/home/"${FIRST_USER_NAME}"/.ssh/authorized_keys
|
||||
chmod 0600 "${ROOTFS_DIR}"/home/"${FIRST_USER_NAME}"/.ssh/authorized_keys
|
||||
fi
|
||||
|
||||
if [ "${PUBKEY_ONLY_SSH}" = "1" ]; then
|
||||
sed -i -Ee 's/^#?[[:blank:]]*PubkeyAuthentication[[:blank:]]*no[[:blank:]]*$/PubkeyAuthentication yes/
|
||||
s/^#?[[:blank:]]*PasswordAuthentication[[:blank:]]*yes[[:blank:]]*$/PasswordAuthentication no/' "${ROOTFS_DIR}"/etc/ssh/sshd_config
|
||||
fi
|
||||
|
||||
on_chroot << EOF
|
||||
systemctl disable hwclock.sh
|
||||
systemctl disable nfs-common
|
||||
|
|
|
@ -26,7 +26,12 @@ network={
|
|||
EOL
|
||||
fi
|
||||
|
||||
# Disable wifi on 5GHz models
|
||||
# Disable wifi on 5GHz models if WPA_COUNTRY is not set
|
||||
mkdir -p "${ROOTFS_DIR}/var/lib/systemd/rfkill/"
|
||||
if [ -n "$WPA_COUNTRY" ]; then
|
||||
echo 0 > "${ROOTFS_DIR}/var/lib/systemd/rfkill/platform-3f300000.mmcnr:wlan"
|
||||
echo 0 > "${ROOTFS_DIR}/var/lib/systemd/rfkill/platform-fe300000.mmcnr:wlan"
|
||||
else
|
||||
echo 1 > "${ROOTFS_DIR}/var/lib/systemd/rfkill/platform-3f300000.mmcnr:wlan"
|
||||
echo 1 > "${ROOTFS_DIR}/var/lib/systemd/rfkill/platform-fe300000.mmcnr:wlan"
|
||||
fi
|
||||
|
|
|
@ -12,7 +12,7 @@ mkdir -p ${ROOTFS_DIR}/etc/systemd/system/getty@tty1.service.d/
|
|||
cat > ${ROOTFS_DIR}/etc/systemd/system/getty@tty1.service.d/override.conf << 'EOF'
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=-/usr/sbin/agetty --autologin root --noclear %I $TERM
|
||||
ExecStart=-/usr/sbin/agetty --skip-login --nonewline --noissue --autologin root --noclear %I $TERM
|
||||
EOF
|
||||
|
||||
on_chroot << EOF
|
||||
|
|
|
@ -41,7 +41,7 @@ recommended_menu () {
|
|||
echo $(( $i * 100 / ${#RECOMMEND_MENU[@]} )) | dialog --title "Plugin Manager" --gauge "Installing ${RECOMMEND_MENU[$i]}" 10 50
|
||||
case ${RECOMMEND_MENU[$i]} in
|
||||
"OctoPrint-Dashboard") install_package "https://github.com/StefanCohen/OctoPrint-Dashboard/archive/master.zip" || error_install ${RECOMMEND_MENU[$i]}; install_package "https://github.com/OllisGit/OctoPrint-DisplayLayerProgress/releases/latest/download/master.zip" || error_install ${RECOMMEND_MENU[$i]};;
|
||||
"ExcludeRegion") install_package "https://github.com/bradcfisher/OctoPrint-ExcludeRegionPlugin/archive/feature/python3_compat.zip" || error_install ${RECOMMEND_MENU[$i]};;
|
||||
"ExcludeRegion") install_package "https://github.com/bradcfisher/OctoPrint-ExcludeRegionPlugin/archive/master.zip" || error_install ${RECOMMEND_MENU[$i]};;
|
||||
"NavbarTemp") install_package "https://github.com/imrahil/OctoPrint-NavbarTemp/archive/master.zip" || error_install ${RECOMMEND_MENU[$i]};;
|
||||
"PrintTimeGenius") install_package "https://github.com/eyal0/OctoPrint-PrintTimeGenius/archive/master.zip" || error_install ${RECOMMEND_MENU[$i]};;
|
||||
"HeaterTimeout") install_package "https://github.com/google/OctoPrint-HeaterTimeout/archive/master.zip" || error_install ${RECOMMEND_MENU[$i]};;
|
||||
|
|
|
@ -3,7 +3,6 @@ xserver-xorg-video-fbdev
|
|||
xserver-xorg-video-fbturbo
|
||||
xinit
|
||||
menu-xdg
|
||||
zenity
|
||||
xdg-utils
|
||||
gvfs-backends
|
||||
gvfs-fuse
|
||||
|
|
Loading…
Reference in a new issue