Skip to content

Instantly share code, notes, and snippets.

@mgoellnitz
Last active March 28, 2026 12:28
Show Gist options
  • Select an option

  • Save mgoellnitz/ba98c479ff1462cf12e0acc1a33298b6 to your computer and use it in GitHub Desktop.

Select an option

Save mgoellnitz/ba98c479ff1462cf12e0acc1a33298b6 to your computer and use it in GitHub Desktop.
GitLab Snippet Command Line Tool
#!/bin/sh
#
# Copyright 2016-2025 Martin Goellnitz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Find some fork at
# update gitlab api
# https://gist.github.com/dubiouscript/5553dce89497ffd9805dd0de16503e8d
# -dscript-
#
#
usage() {
echo "GitLab Project Code Snippet Command Line Tool '$(basename "$0")'" 1>&2
echo "" 1>&2
echo "$0 [ -u instance url] [-p project] [-t token] [-i] [-a] filename [Title]" 1>&2
echo "" 1>&2
echo "If 'filename' points to an existing file, it is uploaded to Gitlab with an optional title." 1>&2
echo "If 'filename' describes a filename present in the given project, it is downloaded." 1>&2
echo "If 'filename' describes a filename present in any of your projects and you don't mention one of them, the project is automatically discovered." 1>&2
echo "If 'filename' is not present, the available snippets in the project are listed." 1>&2
echo "" 1>&2
echo " -u instance base url - default https://gitlab.com" 1>&2
echo "" 1>&2
echo " -p project name with user or group name" 1>&2
echo "" 1>&2
echo " -t gitlab api token - default \$GITLAB_COM_TOKEN" 1>&2
echo "" 1>&2
echo " -d delete snippet" 1>&2
echo "" 1>&2
echo " -i make internally visible for logged in users" 1>&2
echo "" 1>&2
echo " -a make publicly visible for all" 1>&2
echo "" 1>&2
echo " -s make secret and only privately visible" 1>&2
echo "" 1>&2
echo " -h this help message" 1>&2
echo "" 1>&2
echo "So the default action is to list all projects available to the user identified with the access token." 1>&2
echo "" 1>&2
}
if [ "$(which jq|wc -l)" = 0 ] ; then
echo "To use this tool, jq must be installed." 1>&2
exit 1
fi
# defaults
GITLAB="https://gitlab.com"
TOKEN=$GITLAB_COM_TOKEN
VISIBILITY="private"
while getopts "adhip:st:u:" opt ; do
case "${opt}" in
a)
VISIBILITY="public"
;;
d)
DELETE="delete"
;;
h)
usage
exit
;;
i)
VISIBILITY="internal"
;;
p)
PROJECT=$OPTARG
;;
s)
VISIBILITY="private"
;;
t)
TOKEN=$OPTARG
;;
u)
GITLAB=$OPTARG
;;
*)
usage
exit 1
;;
esac
done
shift $((OPTIND-1))
API="${GITLAB}/api/v4"
URL=$API
FILEPATH=$1
TITLE=$2
HEADER="PRIVATE-TOKEN: $TOKEN"
# echo project: $PROJECT title: $TITLE file: $FILEPATH gitlab: $GITLAB with token $TOKEN
if [ ! -z "$PROJECT" ] ; then
PID=$(echo "$PROJECT"|sed -e 's/\//%2F/g')
# echo "project ID for $PROJECT is $PID"
URL="$API/projects/$PID"
CHECK=$(curl -k -H "$HEADER" "$URL" 2> /dev/null|grep "404"|wc -l)
if [ "$CHECK" -eq "1" ] ; then
echo "Unknown project $PROJECT on $GITLAB"
exit 1
fi
else
if [ -z "$TOKEN" ] ; then
echo "Cannot work without the context of a project or user. Sorry... Use -h for help."
exit 1
fi
fi
if [ -z "$FILEPATH" ] ; then
if [ -z "$PROJECT" ] ; then
echo "Listing snippet files available to the current user at $GITLAB"
else
echo "Listing snippet files in $GITLAB/$PROJECT"
fi
LISTING=$(curl -k -H "$HEADER" "$URL/snippets" 2> /dev/null)
MESSAGE=$(echo "$LISTING" | jq .message 2> /dev/null)
if [ -z "$MESSAGE" ] ; then
echo "$LISTING" |jq '.[]|.file_name'|sed -e s/^\"//g|sed -e s/\"$//g
else
echo "$LISTING" | jq .message 2> /dev/null|sed -e 's/"//g'
fi
else
FILE=$(basename "$FILEPATH")
SNID=$(curl -k -H "$HEADER" "$URL/snippets" 2> /dev/null|jq '.[]|select(.file_name=="'"$FILE"'")|.id')
PID=$(curl -k -H "$HEADER" "$URL/snippets" 2> /dev/null \
| jq '.[]|select(.file_name=="'"$FILE"'").project_id')
if [ ! -z "$SNID" ] ; then
# echo "Snippet $SNID project: $PID"
if [ "$PID" = "null" ] ; then
echo "Personal Snippet without Project Context."
echo ""
SNURL=${API}/snippets/${SNID}
else
SNURL=${API}/projects/$PID/snippets/${SNID}
fi
fi
# echo "SNURL: $SNURL"
if [ ! -z "$DELETE" ] ; then
if [ -z "$SNURL" ] ; then
echo "No snippet '$FILE' found in $GITLAB/$PROJECT to delete"
else
echo "Deleting snippet '$FILE' from $GITLAB/$PROJECT"
curl -k -X DELETE -H "$HEADER" "${SNURL}" 2> /dev/null
fi
else
if [ -f "$FILEPATH" ] ; then
CONTENT=$(sed -e 's/\r//' -e 's/\\/\\\\/g' -e's/\t/\\t/g' -e 's/"/\\"/g' -e 's/%/%%/g' "${FILEPATH}" \
| awk '{ printf($0 "\\n") }')
if [ -z "$SNURL" ] ; then
if [ -z "$TITLE" ] ; then
TITLE=$FILE
fi
if [ "$PID" = "null" ] ; then
echo "Creating personal snippet '$FILE' with title '$TITLE' in $GITLAB from '$FILEPATH'"
else
echo "Creating snippet '$FILE' with title '$TITLE' in $GITLAB/$PROJECT from '$FILEPATH'"
fi
RESULT=$(curl -X POST -H "$HEADER" -H "Content-Type: application/json" \
-d "{ \"title\": \"$TITLE\", \"description\": \"Authored with snip.sh from https://gitlab.com/dashboard/snippets\", \"visibility\": \"$VISIBILITY\", \"files\": [ { \"content\": \"$CONTENT\", \"file_path\": \"$FILE\" } ] }" \
"$URL/snippets" 2> /dev/null)
echo "$RESULT" | jq .
else
if [ -z "$TITLE" ] ; then
echo "Updating snippet '$FILE' in $GITLAB/$PROJECT"
curl -k -X PUT -H "$HEADER" -H "Content-Type: application/json" \
-d "{ \"file_name\": \"$FILE\", \"visibility\": \"$VISIBILITY\", \"content\": \"$CONTENT\" }" \
"${SNURL}" 2> /dev/null > /dev/null
else
echo "Updating snippet '$FILE' with title '$TITLE' in $GITLAB/$PROJECT"
curl -k -X PUT -H "$HEADER" -H "Content-Type: application/json" \
-d "{ \"title\": \"$TITLE\", \"file_name\": \"$FILE\", \"visibility\": \"$VISIBILITY\", \"content\": \"$CONTENT\" }" \
"${SNURL}" 2> /dev/null > /dev/null
fi
fi
else
if [ -z "$SNURL" ] ; then
echo "'$FILEPATH' not found in $GITLAB/$PROJECT"
exit 1
fi
echo "Fetching snippet '$FILE' as '$FILEPATH'"
# TODO: Check is perhaps obsolete
CHECK="$(curl -k -H "$HEADER" "${SNURL}" 2> /dev/null | grep ".*message.*404.*Not.Found" )"
if [ ! -z "$CHECK" ] ; then
echo "'$FILEPATH' not found in $GITLAB/$PROJECT"
exit 1
fi
curl -k -H "$HEADER" "${SNURL}/raw" 2> /dev/null > "$FILEPATH"
fi
fi
fi
@mgoellnitz
Copy link
Copy Markdown
Author

Just realized that I didn't commit some changes due to tests I did which didn't produce consistent code. So, have a look at the current version which seems still to be working for me for most situations (list, fetch, update).

@leggewie
Copy link
Copy Markdown

leggewie commented Sep 15, 2021

Thank you for this update. Line 18 is unchanged (backticks have been deprecated for command substitution for more than a decade) and thus the script still breaks for me. Even after fixing that, I still cannot create a successful snippet. I believe the URLADD variable is wrong, paths to projects on gitlab do not have /projects/ in the URI. I tried to fiddle with that but eventually gave up.

@mgoellnitz
Copy link
Copy Markdown
Author

Hi, so my V4 Update is now integrated but incomplete as you are saying. When trying to upload a snippet, do you use a project context or is it a user-based snippet?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment