mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-10-23 22:02:19 +00:00
chore(workflows): remove obsolete Gitea CI workflow files for GitHub Actions integration
This commit is contained in:
3 files changed
-441
No files matched your search
@@ -1,70 +0,0 @@
|
||||
# After pushing a tag like release_* to Gitea, wait for GitHub Actions to finish
|
||||
# (Build release + Build Linux release), then create/update the GitHub release and upload assets.
|
||||
#
|
||||
# Repository secrets (Gitea):
|
||||
# GH_PAT GitHub PAT: contents:write, actions:read (upload release assets, list workflow runs)
|
||||
# GH_REPOSITORY GitHub repo as owner/name (example: Sudo-Ivan/MeshChatX)
|
||||
#
|
||||
# The tag must exist on GitHub (mirror) so workflows run there for the same ref.
|
||||
|
||||
name: Sync tag release to GitHub
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "release_*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Tag to sync to GitHub (required when not run from a tag ref)
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish-github:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 240
|
||||
steps:
|
||||
- name: Install tools
|
||||
run: |
|
||||
set -eu
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y --no-install-recommends git curl jq ca-certificates unzip
|
||||
|
||||
- name: Checkout
|
||||
run: |
|
||||
set -eu
|
||||
SERVER="${GITEA_SERVER_URL:-${GITHUB_SERVER_URL:-}}"
|
||||
REPO="${GITEA_REPOSITORY:-${GITHUB_REPOSITORY:-}}"
|
||||
if [ -z "$SERVER" ] || [ -z "$REPO" ]; then
|
||||
echo "Set GITEA_SERVER_URL/GITEA_REPOSITORY (or GitHub equivalents)." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "${GITEA_TOKEN:-}" ] || [ -n "${GITHUB_TOKEN:-}" ]; then
|
||||
TOKEN="${GITEA_TOKEN:-$GITHUB_TOKEN}"
|
||||
git config --global credential.helper "!f() { echo username=x-access-token; echo password=${TOKEN}; }; f"
|
||||
fi
|
||||
git init -q && git remote add origin "${SERVER}/${REPO}.git"
|
||||
git fetch -q --depth=1 origin "${GITHUB_SHA}" && git checkout -q FETCH_HEAD
|
||||
|
||||
- name: Publish GitHub release from Actions artifacts
|
||||
env:
|
||||
GH_REPOSITORY: ${{ secrets.GH_REPOSITORY }}
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
TIMEOUT_SEC: "14400"
|
||||
POLL_INTERVAL: "60"
|
||||
run: |
|
||||
set -eu
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG="${{ github.ref_name }}"
|
||||
fi
|
||||
if [ -z "$TAG" ] || [ "$TAG" = "master" ] || [ "$TAG" = "dev" ]; then
|
||||
echo "Set workflow input tag= to the GitHub tag, or run this workflow from a tag ref." >&2
|
||||
exit 1
|
||||
fi
|
||||
export TAG
|
||||
bash scripts/ci/wait-github-workflows-and-upload-release.sh
|
||||
@@ -1,135 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Download Windows/macOS build artifacts from GitHub Actions (build-release.yml)
|
||||
# and attach them to an existing Gitea release. Best-effort: missing platforms are skipped.
|
||||
#
|
||||
# Primary CI and Linux release binaries live under .github/workflows/ (see README.md).
|
||||
# Linux artifacts are produced by build-linux-release.yml on GitHub if you extend this script.
|
||||
#
|
||||
# Required env: TAG, GITHUB_REPOSITORY, GITHUB_PAT, GITEA_API_URL, GITEA_REPOSITORY, GITEA_TOKEN
|
||||
set -euo pipefail
|
||||
|
||||
TAG="${TAG:?set TAG to the release tag (e.g. v1.2.3)}"
|
||||
GITHUB_REPOSITORY="${GITHUB_REPOSITORY:?}"
|
||||
GITHUB_PAT="${GITHUB_PAT:?}"
|
||||
GITEA_API_URL="${GITEA_API_URL:?}"
|
||||
GITEA_REPOSITORY="${GITEA_REPOSITORY:?}"
|
||||
GITEA_TOKEN="${GITEA_TOKEN:?}"
|
||||
|
||||
GITEA_API_URL="${GITEA_API_URL%/}"
|
||||
GH_API="https://api.github.com/repos/${GITHUB_REPOSITORY}"
|
||||
AUTH_GH=(-H "Authorization: Bearer ${GITHUB_PAT}" -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28")
|
||||
AUTH_GITEA=(-H "Authorization: token ${GITEA_TOKEN}" -H "Accept: application/json")
|
||||
|
||||
WORKDIR=$(mktemp -d)
|
||||
trap 'rm -rf "${WORKDIR}"' EXIT
|
||||
|
||||
enc_tag() {
|
||||
printf '%s' "$1" | jq -sRr @uri
|
||||
}
|
||||
|
||||
log() {
|
||||
printf '%s\n' "$*" >&2
|
||||
}
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
log "Error: jq is required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COMMIT_SHA=""
|
||||
if COMMIT_JSON=$(curl -sS -f "${AUTH_GH[@]}" "${GH_API}/commits/$(enc_tag "$TAG")" 2>/dev/null); then
|
||||
COMMIT_SHA=$(printf '%s' "$COMMIT_JSON" | jq -r '.sha // empty')
|
||||
fi
|
||||
|
||||
TAG_ENC=$(enc_tag "$TAG")
|
||||
RUNS_URL="${GH_API}/actions/workflows/build-release.yml/runs?event=push&branch=${TAG_ENC}&per_page=30"
|
||||
RUN_ID=""
|
||||
if RUNS_JSON=$(curl -sS -f "${AUTH_GH[@]}" "$RUNS_URL" 2>/dev/null); then
|
||||
RUN_ID=$(printf '%s' "$RUNS_JSON" | jq -r '
|
||||
[.workflow_runs[] | select(.conclusion == "success")]
|
||||
| sort_by(.created_at) | reverse | .[0].id // empty
|
||||
')
|
||||
fi
|
||||
|
||||
if [ -z "$RUN_ID" ] && [ -n "$COMMIT_SHA" ]; then
|
||||
if RUNS_JSON=$(curl -sS -f "${AUTH_GH[@]}" "${GH_API}/actions/workflows/build-release.yml/runs?per_page=50" 2>/dev/null); then
|
||||
RUN_ID=$(printf '%s' "$RUNS_JSON" | jq -r --arg sha "$COMMIT_SHA" '
|
||||
[.workflow_runs[] | select(.head_sha == $sha and .conclusion == "success")]
|
||||
| sort_by(.created_at) | reverse | .[0].id // empty
|
||||
')
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$RUN_ID" ]; then
|
||||
log "No successful GitHub Actions run found for build-release.yml (tag=${TAG}). Skipping GitHub artifact sync."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Using GitHub workflow run id=${RUN_ID} for tag ${TAG}"
|
||||
|
||||
ART_JSON=$(curl -sS "${AUTH_GH[@]}" "${GH_API}/actions/runs/${RUN_ID}/artifacts?per_page=100" || true)
|
||||
N=$(printf '%s' "${ART_JSON:-{}}" | jq -r '(.artifacts // []) | length')
|
||||
if [ "${N:-0}" -eq 0 ]; then
|
||||
log "No artifacts on GitHub run ${RUN_ID} (or API error). Nothing to download."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
STAGE="${WORKDIR}/stage"
|
||||
mkdir -p "$STAGE"
|
||||
|
||||
printf '%s' "$ART_JSON" | jq -r '.artifacts[] | "\(.id)|\(.name)|\(.archive_download_url)"' | while IFS='|' read -r _art_id art_name dl_url; do
|
||||
case "$art_name" in
|
||||
meshchatx-windows-*|meshchatx-macos-*) ;;
|
||||
*)
|
||||
log "Skipping artifact with unexpected name: ${art_name}"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
ZIP="${WORKDIR}/$(echo "$art_name" | tr '/' '_').zip"
|
||||
log "Downloading ${art_name}..."
|
||||
if ! curl -sS -fL "${AUTH_GH[@]}" -o "$ZIP" "$dl_url"; then
|
||||
log "Warning: failed to download ${art_name}; continuing."
|
||||
continue
|
||||
fi
|
||||
EX="${WORKDIR}/ex-${art_name}"
|
||||
mkdir -p "$EX"
|
||||
if ! unzip -q -o "$ZIP" -d "$EX" 2>/dev/null; then
|
||||
log "Warning: unzip failed for ${art_name}; continuing."
|
||||
continue
|
||||
fi
|
||||
find "$EX" -type f \( -name '*.exe' -o -name '*.dmg' -o -name '*.blockmap' -o -name '*.yml' -o -name '*.yaml' \) -print0 2>/dev/null \
|
||||
| while IFS= read -r -d '' f; do
|
||||
base=$(basename "$f")
|
||||
cp -f "$f" "${STAGE}/${base}"
|
||||
log "Staged ${base}"
|
||||
done
|
||||
done
|
||||
|
||||
STAGED_N=$(find "$STAGE" -mindepth 1 -maxdepth 1 -type f 2>/dev/null | wc -l)
|
||||
STAGED_N=${STAGED_N//[[:space:]]/}
|
||||
if [ "${STAGED_N:-0}" -eq 0 ]; then
|
||||
log "No .exe/.dmg (or related) files extracted from GitHub artifacts. Nothing to upload."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REL_JSON=$(curl -sS "${AUTH_GITEA[@]}" "${GITEA_API_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/tags/${TAG}")
|
||||
REL_ID=$(printf '%s' "$REL_JSON" | jq -r '.id // empty')
|
||||
if [ -z "$REL_ID" ] || [ "$REL_ID" = "null" ]; then
|
||||
log "Error: No Gitea release for tag '${TAG}'. Create the Gitea release first, then re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Uploading to Gitea release id=${REL_ID} (${GITEA_REPOSITORY}@${TAG})"
|
||||
|
||||
find "$STAGE" -maxdepth 1 -type f -print0 | while IFS= read -r -d '' f; do
|
||||
base=$(basename "$f")
|
||||
NAME_ENC=$(printf '%s' "$base" | jq -sRr @uri)
|
||||
if curl -sS -f "${AUTH_GITEA[@]}" -F "attachment=@${f}" \
|
||||
"${GITEA_API_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/${REL_ID}/assets?name=${NAME_ENC}" >/dev/null; then
|
||||
log "Uploaded ${base}"
|
||||
else
|
||||
log "Warning: upload failed or duplicate for ${base}; continuing."
|
||||
fi
|
||||
done
|
||||
|
||||
log "Done."
|
||||
@@ -1,236 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Wait for successful GitHub Actions runs for TAG, download artifacts from listed workflows,
|
||||
# then create or update a GitHub release and upload binaries.
|
||||
#
|
||||
# Required env: TAG, GH_REPOSITORY (owner/repo), GH_PAT
|
||||
# Optional: WORKFLOWS (space-separated, default: build-release.yml build-linux-release.yml)
|
||||
# TIMEOUT_SEC (default 14400), POLL_INTERVAL (default 60), DRAFT (default false)
|
||||
# RELEASE_BODY_FILE (path to markdown; default tries ./release-body.md from cwd)
|
||||
set -euo pipefail
|
||||
|
||||
TAG="${TAG:?set TAG (e.g. v1.2.3 or release_1.2.3)}"
|
||||
GH_REPOSITORY="${GH_REPOSITORY:?set GH_REPOSITORY to owner/repo on github.com}"
|
||||
GH_PAT="${GH_PAT:?set GH_PAT (fine-grained or classic PAT with contents:write, actions:read)}"
|
||||
WORKFLOWS="${WORKFLOWS:-build-release.yml build-linux-release.yml}"
|
||||
TIMEOUT_SEC="${TIMEOUT_SEC:-14400}"
|
||||
POLL_INTERVAL="${POLL_INTERVAL:-60}"
|
||||
DRAFT="${DRAFT:-false}"
|
||||
|
||||
GH_API="https://api.github.com/repos/${GH_REPOSITORY}"
|
||||
AUTH=(-H "Authorization: Bearer ${GH_PAT}" -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28")
|
||||
|
||||
WORKDIR=$(mktemp -d)
|
||||
trap 'rm -rf "${WORKDIR}"' EXIT
|
||||
|
||||
log() {
|
||||
printf '%s\n' "$*" >&2
|
||||
}
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
log "Error: jq is required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
enc_tag() {
|
||||
printf '%s' "$1" | jq -sRr @uri
|
||||
}
|
||||
|
||||
latest_success_run_id() {
|
||||
local wf="$1"
|
||||
local tag="$2"
|
||||
local tag_enc run_id runs_json commit_sha runs_json2
|
||||
|
||||
tag_enc=$(enc_tag "$tag")
|
||||
run_id=""
|
||||
if runs_json=$(curl -sS -f "${AUTH[@]}" "${GH_API}/actions/workflows/${wf}/runs?event=push&branch=${tag_enc}&per_page=30" 2>/dev/null); then
|
||||
run_id=$(printf '%s' "$runs_json" | jq -r '
|
||||
[.workflow_runs[] | select(.conclusion == "success")]
|
||||
| sort_by(.created_at) | reverse | .[0].id // empty
|
||||
')
|
||||
fi
|
||||
|
||||
if [ -z "$run_id" ]; then
|
||||
if ! commit_sha=$(curl -sS -f "${AUTH[@]}" "${GH_API}/commits/${tag_enc}" | jq -r '.sha // empty'); then
|
||||
commit_sha=""
|
||||
fi
|
||||
if [ -n "$commit_sha" ] && runs_json2=$(curl -sS -f "${AUTH[@]}" "${GH_API}/actions/workflows/${wf}/runs?per_page=80" 2>/dev/null); then
|
||||
run_id=$(printf '%s' "$runs_json2" | jq -r --arg sha "$commit_sha" '
|
||||
[.workflow_runs[] | select(.head_sha == $sha and .conclusion == "success")]
|
||||
| sort_by(.created_at) | reverse | .[0].id // empty
|
||||
')
|
||||
fi
|
||||
fi
|
||||
printf '%s' "$run_id"
|
||||
}
|
||||
|
||||
deadline=$(( $(date +%s) + TIMEOUT_SEC ))
|
||||
declare -A RUN_IDS=()
|
||||
|
||||
log "Waiting for workflows (${WORKFLOWS}) on tag ${TAG} (timeout ${TIMEOUT_SEC}s)..."
|
||||
|
||||
while [ "$(date +%s)" -lt "$deadline" ]; do
|
||||
all_set=1
|
||||
for wf in $WORKFLOWS; do
|
||||
rid=$(latest_success_run_id "$wf" "$TAG")
|
||||
if [ -n "$rid" ]; then
|
||||
RUN_IDS["$wf"]=$rid
|
||||
else
|
||||
all_set=0
|
||||
fi
|
||||
done
|
||||
if [ "$all_set" = 1 ]; then
|
||||
break
|
||||
fi
|
||||
log "Not all workflows succeeded yet; sleeping ${POLL_INTERVAL}s..."
|
||||
sleep "$POLL_INTERVAL"
|
||||
done
|
||||
|
||||
for wf in $WORKFLOWS; do
|
||||
if [ -z "${RUN_IDS[$wf]:-}" ]; then
|
||||
log "Timeout or missing successful run for ${wf} (tag=${TAG})."
|
||||
exit 1
|
||||
fi
|
||||
log "Using ${wf} run_id=${RUN_IDS[$wf]}"
|
||||
done
|
||||
|
||||
STAGE="${WORKDIR}/stage"
|
||||
mkdir -p "$STAGE"
|
||||
|
||||
download_and_stage_run() {
|
||||
local wf="$1"
|
||||
local rid="$2"
|
||||
local art_json n
|
||||
|
||||
art_json=$(curl -sS "${AUTH[@]}" "${GH_API}/actions/runs/${rid}/artifacts?per_page=100" || true)
|
||||
n=$(printf '%s' "${art_json:-{}}" | jq -r '(.artifacts // []) | length')
|
||||
if [ "${n:-0}" -eq 0 ]; then
|
||||
log "No artifacts for ${wf} run ${rid}."
|
||||
return
|
||||
fi
|
||||
|
||||
printf '%s' "$art_json" | jq -r '.artifacts[] | "\(.name)|\(.archive_download_url)"' | while IFS='|' read -r art_name dl_url; do
|
||||
case "$wf" in
|
||||
build-release.yml)
|
||||
case "$art_name" in
|
||||
meshchatx-windows-*|meshchatx-macos-*) ;;
|
||||
*)
|
||||
log "Skipping artifact ${art_name} for ${wf}"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
build-linux-release.yml)
|
||||
case "$art_name" in
|
||||
meshchatx-linux-release-*) ;;
|
||||
*)
|
||||
log "Skipping artifact ${art_name} for ${wf}"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
log "Unknown workflow file in download filter: ${wf}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
zip_path="${WORKDIR}/$(echo "$art_name" | tr '/' '_').zip"
|
||||
log "Downloading ${art_name}..."
|
||||
if ! curl -sS -fL "${AUTH[@]}" -o "$zip_path" "$dl_url"; then
|
||||
log "Warning: download failed for ${art_name}"
|
||||
continue
|
||||
fi
|
||||
ex="${WORKDIR}/ex-${art_name}"
|
||||
mkdir -p "$ex"
|
||||
if ! unzip -q -o "$zip_path" -d "$ex" 2>/dev/null; then
|
||||
log "Warning: unzip failed for ${art_name}"
|
||||
continue
|
||||
fi
|
||||
find "$ex" -type f \( \
|
||||
-name '*.AppImage' -o -name '*.deb' -o -name '*.rpm' -o -name '*.whl' -o \
|
||||
-name '*.exe' -o -name '*.dmg' -o -name '*.blockmap' -o \
|
||||
-name 'latest*.yml' -o -name 'latest*.yaml' -o -name '*-linux.yml' -o -name '*-linux.yaml' -o \
|
||||
-name 'meshchatx-frontend.zip' -o -name 'sbom.cyclonedx.json' -o -name '*.cosign.bundle' -o -name '*.intoto.jsonl' \
|
||||
\) -print0 2>/dev/null | while IFS= read -r -d '' f; do
|
||||
cp -f "$f" "${STAGE}/$(basename "$f")"
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
for wf in $WORKFLOWS; do
|
||||
download_and_stage_run "$wf" "${RUN_IDS[$wf]}"
|
||||
done
|
||||
|
||||
file_count=$(find "$STAGE" -mindepth 1 -maxdepth 1 -type f 2>/dev/null | wc -l)
|
||||
file_count=${file_count//[[:space:]]/}
|
||||
if [ "${file_count:-0}" -eq 0 ]; then
|
||||
log "No release files staged after downloads."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BODY_FILE="${RELEASE_BODY_FILE:-}"
|
||||
if [ -z "$BODY_FILE" ] && [ -f "./release-body.md" ]; then
|
||||
BODY_FILE="./release-body.md"
|
||||
fi
|
||||
BODY_JSON=$(jq -n '""')
|
||||
if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then
|
||||
BODY_JSON=$(jq -Rs . < "$BODY_FILE")
|
||||
fi
|
||||
|
||||
log "Creating or locating GitHub release for tag ${TAG}..."
|
||||
REL_GET=$(curl -sS -w '%{http_code}' -o "${WORKDIR}/rel.json" "${AUTH[@]}" "${GH_API}/releases/tags/${TAG}" || true)
|
||||
HTTP="${REL_GET: -3}"
|
||||
REL_ID=""
|
||||
if [ "$HTTP" = "200" ]; then
|
||||
REL_ID=$(jq -r '.id // empty' "${WORKDIR}/rel.json")
|
||||
UPLOAD_URL=$(jq -r '.upload_url // empty' "${WORKDIR}/rel.json")
|
||||
log "Release exists id=${REL_ID}"
|
||||
else
|
||||
if [ "$DRAFT" = "true" ] || [ "$DRAFT" = "1" ]; then
|
||||
draft_json="true"
|
||||
else
|
||||
draft_json="false"
|
||||
fi
|
||||
payload=$(jq -n \
|
||||
--arg tag "$TAG" \
|
||||
--arg name "$TAG" \
|
||||
--argjson draft "$draft_json" \
|
||||
--argjson body "$BODY_JSON" \
|
||||
'{tag_name: $tag, name: $name, body: body, draft: draft}')
|
||||
if ! curl -sS -f "${AUTH[@]}" -X POST -H "Content-Type: application/json" \
|
||||
-d "$payload" "${GH_API}/releases" -o "${WORKDIR}/newrel.json"; then
|
||||
log "Failed to create release for ${TAG}"
|
||||
exit 1
|
||||
fi
|
||||
REL_ID=$(jq -r '.id // empty' "${WORKDIR}/newrel.json")
|
||||
UPLOAD_URL=$(jq -r '.upload_url // empty' "${WORKDIR}/newrel.json")
|
||||
log "Created release id=${REL_ID}"
|
||||
fi
|
||||
|
||||
if [ -z "$REL_ID" ] || [ -z "$UPLOAD_URL" ]; then
|
||||
log "Could not resolve release id or upload_url."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASE_UPLOAD="${UPLOAD_URL%\{*}"
|
||||
|
||||
EXISTING_NAMES=$(curl -sS "${AUTH[@]}" "${GH_API}/releases/${REL_ID}/assets" | jq -c '[.[].name]' 2>/dev/null || echo '[]')
|
||||
|
||||
while IFS= read -r -d '' f; do
|
||||
base=$(basename "$f")
|
||||
if jq -n -e --argjson names "$EXISTING_NAMES" --arg n "$base" '$names | index($n) != null' >/dev/null 2>&1; then
|
||||
log "Skipping existing asset: ${base}"
|
||||
continue
|
||||
fi
|
||||
enc=$(printf '%s' "$base" | jq -sRr @uri)
|
||||
log "Uploading ${base}..."
|
||||
if ! curl -sS -f "${AUTH[@]}" -X POST \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"$f" \
|
||||
"${BASE_UPLOAD}?name=${enc}" >/dev/null; then
|
||||
log "Upload failed for ${base}"
|
||||
exit 1
|
||||
fi
|
||||
done < <(find "$STAGE" -maxdepth 1 -type f -print0)
|
||||
|
||||
log "Done publishing assets to GitHub release ${TAG}."
|
||||
Reference in new issue
Block a user