Skip to content

Instantly share code, notes, and snippets.

@mgoellnitz
Last active January 23, 2023 15:02
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mgoellnitz/c7c5effb4045d5484bf649eca674a374 to your computer and use it in GitHub Desktop.
Save mgoellnitz/c7c5effb4045d5484bf649eca674a374 to your computer and use it in GitHub Desktop.
Bitbucket Snippet Command Line Tool
#!/bin/bash
#
# Copyright 2017-2021 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/>.
#
# defaults
API="https://api.bitbucket.org/2.0"
PRIVATE=true
BITBUCKETUSER=$BITBUCKET_USER
if [ ! -z $BITBUCKETUSER ] ; then
TEAM=`echo $BITBUCKETUSER|cut -d ':' -f 1`
fi
if [ $(which jq|wc -l) = 0 ] ; then
echo "To use this tool, jq must be installed."
exit
fi
if [ "$#" == "0" ]; then
echo "Bitbucket User Snippet Command Line Tool 'bucketsnip.sh'"
echo ""
echo "$0 [-u user] [-t team] [-s snippet title] [-l] [-p] [filenames...]"
echo ""
echo "If 'filename' points to an existing file, it is uploaded to Bitbucket with an optional title."
echo "If 'filename' describes a filename present in the user's snippets, it is downloaded."
echo "If 'filename' is not present, the contents of the current directory are used as file-set."
echo ""
echo " -l list snippets"
echo ""
echo " -u user name - optionally with app password appended after ':' - default \$BITBUCKET_USER"
echo ""
echo " -t team to use instead of users snippets"
echo ""
echo " -s snippet title - defaults to filename for single file snippets or to "$(basename `pwd`)
echo ""
echo " -p make snippet public"
echo ""
exit 1
fi
PSTART=`echo $1|sed -e 's/^\(.\).*/\1/g'`
while [ "$PSTART" = "-" ] ; do
if [ "$1" = "-u" ] ; then
shift
BITBUCKETUSER="$1"
TEAM=`echo $BITBUCKETUSER|cut -d ':' -f 1`
fi
if [ "$1" = "-t" ] ; then
shift
TEAM="$1"
fi
if [ "$1" = "-s" ] ; then
shift
SNIPPET="$1"
fi
if [ "$1" = "-p" ] ; then
PRIVATE=false
fi
if [ "$1" = "-l" ] ; then
LISTING=true
fi
shift
PSTART=`echo $1|sed -e 's/^\(.\).*/\1/g'`
done
if [ -z "$BITBUCKETUSER" ] ; then
echo "Cannot work without the context of a bitbucket user name. Sorry..."
exit 1
fi
if [ ! -z "$LISTING" ] ; then
echo "Listing snippets of $TEAM"
SNIPPETS=$(curl -k --basic -u $BITBUCKETUSER ${API}/snippets/${TEAM} 2> /dev/null|jq '.values[]|.id'|sed -e s/^\"//g|sed -e s/\"$//g)
for SNID in $SNIPPETS ; do
# echo $SNID
ITEM=$(curl -k --basic -u $BITBUCKETUSER ${API}/snippets/${TEAM}/${SNID} 2> /dev/null)
TITLE=$(echo $ITEM|jq '.title'|sed -e s/^\"//g|sed -e s/\"$//g)
UPDATE=$(echo $ITEM|jq '.updated_on'|sed -e s/^\"//g|sed -e s/\"$//g|sed -e 's/\..*//g'|sed -e 's/T/ /g')
PRIVATE=$(echo $ITEM|jq '.is_private'|sed -e s/^\"//g|sed -e s/\"$//g|sed -e s/true/private/g|sed -e s/false/public/g)
FILES=$(echo $ITEM|jq '(.files|keys)'|sed -e s/^\"//g|sed -e s/\"$//g)
echo -n "$PRIVATE $TITLE ($UPDATE) "
echo $FILES
# LISTING=$(curl -k --basic -u $BITBUCKETUSER ${API}/snippets/${TEAM}/${SNID} 2> /dev/null |jq '.title,.updated_on,.scm,.is_private,(.files|keys)'|sed -e s/^\"//g|sed -e s/\"$//g)
# echo $LISTING
done
else
FILES=$*
if [ -z "$FILES" ] ; then
FILES=$(ls)
fi
if [ -z "$SNIPPET" ] ; then
if [ "$#" == "1" ] ; then
SNIPPET=$(basename $1)
echo "Checking for present single file snippet"
SNIPPETS=$(curl -k --basic -u $BITBUCKETUSER ${API}/snippets/${TEAM} 2> /dev/null|jq '.values[]|.id'|sed -e s/^\"//g|sed -e s/\"$//g)
for SNID in $SNIPPETS ; do
JSON=$(curl -k --basic -u $BITBUCKETUSER ${API}/snippets/${TEAM}/${SNID} 2> /dev/null)
if [ $(echo $JSON|jq ".files[].links.self.href"|wc -l) == 1 ] ; then
LISTING=$(echo $JSON|jq ".files[\"$SNIPPET\"].links.self.href"|sed -e s/^\"//g|sed -e s/\"$//g)
if [ ! -z "$LISTING" ] ; then
if [ "$LISTING" != "null" ] ; then
SNIPPET=$(curl -k --basic -u $BITBUCKETUSER ${API}/snippets/${TEAM} 2> /dev/null|jq ".values[]|select(.id==\"${SNID}\")|.title"|sed -e s/^\"//g|sed -e s/\"$//g)
echo "Discovered $SNIPPET for snippet containing solely $1"
fi
fi
fi
done
else
SNIPPET=$(basename `pwd`)
fi
fi
SNID=$(curl -k --basic -u $BITBUCKETUSER ${API}/snippets/${TEAM} 2> /dev/null|jq ".values[]|select(.title==\"${SNIPPET}\")|.id"|sed -e s/^\"//g|sed -e s/\"$//g)
if [ -z "$SNID" ] ; then
echo -n "Creating"
test "true" == "$PRIVATE" && echo -n " private"
echo " snippet $SNIPPET in $TEAM"
DATA=""
for FILEPATH in $FILES ; do
if [ -f $FILEPATH ] ; then
FILE=$(basename $FILEPATH)
# echo $FILE $FILEPATH
DATA="$DATA -F file=@$FILEPATH"
fi
done
curl -k -X POST --basic -F title="$SNIPPET" -F is_private="$PRIVATE" $DATA -u $BITBUCKETUSER ${API}/snippets/${TEAM} 2> /dev/null | jq .title
else
echo "Updating snippet $SNIPPET in $TEAM"
DATA=""
DOWNLOADS=""
for FILEPATH in $FILES ; do
if [ -f $FILEPATH ] ; then
DATA="$DATA -F file=@$FILEPATH"
else
DOWNLOADS="$DOWNLOADS $FILEPATH"
fi
done
if [ ! -z "$DATA" ] ; then
curl -k -X PUT --basic -F title="$SNIPPET" -F is_private="$PRIVATE" $DATA -u $BITBUCKETUSER ${API}/snippets/${TEAM}/${SNID} 2> /dev/null | jq .title
else
echo "Nothing to upload"
fi
echo "Downloading missing files... $DOWNLOADS"
JSON=$(curl -k --basic -u $BITBUCKETUSER ${API}/snippets/${TEAM}/${SNID}?pagelen=100 2> /dev/null |jq '.')
LISTING=$(echo $JSON |jq '(.files|keys)')
LISTING=$(echo $LISTING|sed -e 's/\",.\"/ /g' -e 's/.\ \"//g' -e 's/\"\ .//g')
# echo $LISTING
if [ $# == 0 ] ; then
echo "No additional arguments"
for ELEMENT in $LISTING ; do
if [ ! -f "$ELEMENT" ] ; then
echo "Adding $ELEMENT to download list"
DOWNLOADS+=" $ELEMENT"
fi
done
fi
for ELEMENT in $LISTING ; do
for FILE in $DOWNLOADS ; do
if [ "$ELEMENT" == "$FILE" ] ; then
echo "Downloading $FILE"
URL=$(echo $JSON | jq '.|.files["'$FILE'"].links.self.href'|sed -e 's/\"//g')
curl -k --basic -u $BITBUCKETUSER ${URL} 2> /dev/null > $FILE
fi
done
done
fi
fi
@SidIcarus
Copy link

Line 162 should be updated to be DOWNLOADS+=" $ELEMENT"

@mgoellnitz
Copy link
Author

@SidIcarus Thanks!

@duanebc
Copy link

duanebc commented Jul 3, 2020

After staring at documentation for several hours and getting nowhere I found this post and your for loop for multipart file uploads helped me. So thank you for that. I wanted to provide feedback that, by default the pagelen is 30, which in my case I have 57, so I'm not guaranteed to have what I want in the list, I think others that come here may also benefit.

`get_snippet_by_id_by_title(){
title="$1"
bit_bucket_creds="$2"
url="https://api.bitbucket.org/2.0/snippets/{slug}?q=role='member'&pagelen=300"
curl -s -L $url -X GET
--header "authorization: Basic ${bit_bucket_creds}" |
jq -r --arg title "$title" '. |
.values |
.[] |
select(.title == $title) |
.id
'
}

bit_bucket_creds=$(cat ~/.bit_bucket_hash)
title='TITLE OF SNIPPET'
get_snippet_by_id_by_title "$title" "$bit_bucket_creds"`

I keep modifying it. I realized if you don't have credentials passed in you still qualify for public snippets and since I was getting a return, I didn't notice. I'll check for errors later.

https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering
https://developer.atlassian.com/bitbucket/api/2/reference/resource/snippets/%7Bworkspace%7D#get

@mgoellnitz
Copy link
Author

Hi, thanks for the note. I considered 57 files worth a "real repository". I now try to use a default of 100 instead of your 300.
Yes, access to public snippets is intentional in my snippet/gist tools. In fact, I maintain them using the tools itself. (gitlab sadly has no place for snippets not relating to any project)

https://bitbucket.org/provocon/workspace/snippets/

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