Jira & Confluence
[!NOTE|label:reference:]
jira
[!NOTE]
X-Atlassian-Token
X-Force-Accept-Language
X-AAccountId
myself
[!NOTE|label:references:]
$ curl -s https://jira.sample.com/rest/api/2/myself
# or via `user?username=<id>`
$ curl -s https://jira.sample.com/rest/api/2/user?username=marslo
get info
# get timezone $ curl -s https://jira.sample.com/rest/api/2/mypreferences?key=jira.user.timezone Asia/Shanghai # get locale $ curl --request GET \ --url 'https://jira.sample.com/rest/api/2/mypreferences/locale' \ --header 'Accept: application/json'
set info
# set timezone $ curl --request PUT \ --url 'https://jira.sample.com/rest/api/2/mypreferences?key={key}' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --data '"<string>"' # set local $ curl --request PUT \ --url 'https://jira.sample.com/rest/api/2/mypreferences/locale' \ --user 'email@example.com:<api_token>' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --data '{ "locale": "en_US" }'
check fields
$ curl -s \
-k \
-X GET https://${jiraName}/rest/api/2/issue/${jiraID} |
jq --raw-output
check attachment
check attachment ID
$ curl -s \ -k \ -X GET https://${jiraName}/rest/api/2/issue/${jiraID}?fields=attachment | jq --raw-output .fields.attachment[].id
get attachments download url
$ curl -s \ -k \ -X GET https://${jiraName}/rest/api/2/issue/${jiraID}?fields=attachment | jq --raw-output .fields.attachment[].content
download all attachments in Jira
-I replace-str Replace occurrences of replace-str in the initial-arguments with names read from standard in- put. Also, unquoted blanks do not terminate input items; instead the separator is the new- line character. Implies -x and -L 1.
$ curl -s \ -k \ -X GET https://${jiraName}/rest/api/2/issue/${jiraID}?fields=attachment | jq --raw-output .fields.attachment[].content | xargs -I '{}' curl -sgOJL '{}'
list all projects
$ curl -fsSL -XGET https://jira.sample.com/rest/api/2/project |
jq -r '.[] | [.key, .name] | join(" | ")' |
column -s '|' -t
search issue by JQL
[!TIP]
JQL pattern rule:
remove all space
%3D
instead of=
project=abc
->project%3Dabc
%20CONDITION%20
instead ofCONDITION
AND
->%20AND%20
OR
->%20OR%20
format JQL
[!TIP]
$ jql='project = abc AND issuetype = release order by updated desc' $ jql=$(printf %s "${jql}" | jq -sRr @uri)
$ jql="$(sed 's/ //g;s/AND/ AND /g;s/OR/ OR /g;s/IN/ IN /g;s/IS/ IS /g' <<< "${jql}")" $ jql="$(printf %s "${jql}" | jq -sRr @uri)" # i.e.: $ jql='project = abc AND issuetype = release' $ jql="$(sed 's/ //g;s/AND/ AND /g;s/OR/ OR /g;s/IN/ IN /g;s/IS/ IS /g' <<< "${jql}")" $ echo $jql project=abc AND issuetype=release $ jql="$(printf %s "${jql}" | jq -sRr @uri)" $ echo $jql project%3Dabc%20AND%20issuetype%3Drelease
api
[!NOTE]
maxResults
:integer
startAt
:integer
validateQuery
:string
fields
:array<string>
expand
:string
properties
:array<string>
fieldsByKeys
:boolean
sample:
search?jql=${jql}&maxResults=100&startAt=0
$ curl --silent \ --insecure \ --globoff \ --netrc-file ~/.netrc \ -XGET \ "https://jira.sample.com/rest/api/2/search?jql=${jql}" | jq -r ${jqOpt} # i.e.: $ curlOpt='--silent --insecure --globoff --netrc-file ~/.netrc' $ url='https://jira.sample.com/rest/api/2' $ queryParams="startAt=0&maxResults=10" $ jql='project = ABC AND issuetype = Release ORDER BY updated ASC' # copy from Jira website $ jql="$(printf %s "${jql}" | jq -sRr @uri)" $ curl "${curlOpt}" "${url}/search?jql=${jql}&${queryParams}" | jq -r '.issues[]' | jq -r '. | [.key, .fields.summary, .fields.status.name, .fields.issuetype.name, .fields.updated, .fields.created] | join("|")' | while IFS='|' read -r _key _summary _status _issuetype _updated _created; do echo "- [${_key}] - ${_summary}" echo " - status : ${_status}" echo " - issuetype : ${_issuetype}" echo " - created : ${_created}" echo " - updated : ${_updated}" done
$ curl --silent \ --insecure \ --globoff \ --netrc-file ~/.netrc \ -XGET \ "https://jira.sample.com/rest/api/2/search?jql=${jql}" | jq -r ${jqOpt} # i.e.: $ curlOpt='--silent --insecure --globoff --netrc-file ~/.netrc' $ url='https://jira.sample.com/rest/api/2' $ queryParams="startAt=0&maxResults=10" $ jql='project = abc AND issuetype = release' # copy from Jira website $ jql="$(sed 's/ //g;s/AND/ AND /g;s/OR/ OR /g;s/IN/ IN /g;s/IS/ IS /g' <<< "${jql}")" $ jql="$(printf %s "${jql}" | jq -sRr @uri)" $ curl "${curlOpt}" "${url}/search?jql=${jql}&${queryParams}" | jq -r '.issues[]' | jq -r '. | [.key, .fields.summary, .fields.status.name, .fields.issuetype.name, .fields.updated, .fields.created] | join("|")' | while IFS='|' read -r _key _summary _status _issuetype _updated _created; do echo "- [${_key}] - ${_summary}" echo " - status : ${_status}" echo " - issuetype : ${_issuetype}" echo " - created : ${_created}" echo " - updated : ${_updated}" done
[get email address](Get Email Addresses For Users)
$ curl GET https://jira.sample.com/rest/api/2/user?key=JIRAUSER10100 |
jq -r
api token
[!NOTE|label:references:]
[!NOTE]
to list all tokens for single user:
$ curl -v -XGET \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token/ | jq -r
to list tokens for all users:
$ curl -v \ -X GET \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/tokensByFilter | jq -r
$ curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r
get id, created, last access, and valid timestamp
function epoch2timestamp() { [[ 0 != $1 ]] && echo $(date -d @$(( $1/1000 )) +%FT%T.%3N%Z) || echo "0"; } $ while read -r _id _created _lastAccess _validUntil; do echo "${_id} $(epoch2timestamp ${_created}) $(epoch2timestamp ${_lastAccess}) $(epoch2timestamp ${_validUntil})"; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .id, .created, .lastAccessed, .validUntil ] | join( "\t" )' ) | column -t 537 2024-01-31T21:32:51.000PST 2024-03-26T23:30:30.000PDT 2024-07-31T21:32:51.000PDT 579 2024-03-26T23:19:16.000PDT 0 2024-09-26T23:19:16.000PDT # with header $ ( echo "ID CREATED LASTACCESS VALIDUNTIL"; while read -r _id _created _lastAccess _validUntil; do echo "${_id} $(epoch2timestamp ${_created}) $(epoch2timestamp ${_lastAccess}) $(epoch2timestamp ${_validUntil})"; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .id, .created, .lastAccessed, .validUntil ] | join( "\t" )' ) ) | column -t ID CREATED LASTACCESS VALIDUNTIL 537 2024-01-31T21:32:51.000PST 2024-03-26T23:33:33.000PDT 2024-07-31T21:32:51.000PDT 579 2024-03-26T23:19:16.000PDT 0 2024-09-26T23:19:16.000PDT
or
$ curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .created, .lastAccessed, .validUntil ] | join("\n")' | xargs -r -I{} bash -c "et=\"{}\"; date -d @\$(( \${et}/1000 )) +%c" Mon 18 Dec 2023 10:25:58 AM PST Tue 26 Mar 2024 10:21:30 PM PDT Tue 18 Jun 2024 10:25:58 AM PDT # or $ while read -r _d; do date -d @$(( ${_d}/1000 )) +%c; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .created, .lastAccessed, .validUntil ] | join("\n")' ) Mon 18 Dec 2023 10:25:58 AM PST Tue 26 Mar 2024 10:14:58 PM PDT Tue 18 Jun 2024 10:25:58 AM PDT
[!TIP]
expiration keywords
tokenValidityTimeInMonths
tokenExpirationDateTime
tokenExpirationDateTimeMillis
$ curl -v -d '{"tokenDescription":"<token-description>"}' \ -X POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token
create new token with expiration time
[!NOTE|label:references:]
with
tokenValidityTimeInMonths
$ curl -v \ -d '{"tokenDescription":"<token-description>", "tokenValidityTimeInMonths" : 1}' \ -X POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token
with
tokenExpirationDateTime
$ curl -v \ -d '{"tokenDescription":"Custom expiration", "tokenExpirationDateTime" : "2020-10-19T10:29:00.000+02:00"}' \ -X POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token
$ curl -s \ -d '{"tokenDescription":"marslo-token-api-test", "tokenValidityTimeInMonths" : 6}' \ -X POST \ --header "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | jq -r .plainTextToken NdxEToKfDsjUE7tct1ePP6erE1xdDsEAa64BOT
create token for another users
$ curl -v \ -d '{"tokenDescription":"token for another user", "tokenForUserKey":"JIRAUSER10105"}' \ POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token # or with token validity time $ curl -v \ -d '{"tokenDescription":"token for another user", "tokenForUserKey":"JIRAUSER10105","tokenValidityTimeInMonths":12}' \ POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token
$ curl -v \ -d '{"tokenDescription":"Updated token description"}' \ -X PATCH \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token/<token-id>
[!NOTE|label:references:]
$ curl -v \ -X DELETE \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token/<token-id>
# list before token deleted $ ( echo "ID CREATED LASTACCESS VALIDUNTIL"; while read -r _id _created _lastAccess _validUntil; do echo "${_id} $(epoch2timestamp ${_created}) $(epoch2timestamp ${_lastAccess}) $(epoch2timestamp ${_validUntil})"; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .id, .created, .lastAccessed, .validUntil ] | join( "\t" )' ) ) | column -t ID CREATED LASTACCESS VALIDUNTIL 537 2024-01-31T21:32:51.000PST 2024-03-26T23:40:02.000PDT 2024-07-31T21:32:51.000PDT 579 2024-03-26T23:19:16.000PDT 0 2024-09-26T23:19:16.000PDT 580 2024-03-26T23:36:02.000PDT 0 2024-09-26T23:36:02.000PDT 581 2024-03-26T23:36:11.000PDT 0 2024-09-26T23:36:11.000PDT # delete $ curl -s -D- \ -X DELETE \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token/581 HTTP/2 200 date: Wed, 27 Mar 2024 06:41:06 GMT content-type: application/json;charset=UTF-8 x-arequestid: 1421x248002x2 x-anodeid: jiraprod5 referrer-policy: strict-origin-when-cross-origin x-xss-protection: 1; mode=block x-content-type-options: nosniff x-frame-options: SAMEORIGIN content-security-policy: sandbox strict-transport-security: max-age=31536000 set-cookie: JSESSIONID=731380C1F17B2F9A59E211B0442AAFFB; Path=/; Secure; HttpOnly x-seraph-loginreason: OK set-cookie: atlassian.xsrf.token=A8KN-1NAU-M55V-EQSR_1811ae8601112c05430589e686032924599718c9_lin; Path=/; Secure; SameSite=None x-asessionid: k5q153 x-ausername: marslo cache-control: no-cache, no-store, no-transform true # curl without header $ curl -s \ -X DELETE \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token/580 true # verify $ ( echo "ID CREATED LASTACCESS VALIDUNTIL"; while read -r _id _created _lastAccess _validUntil; do echo "${_id} $(epoch2timestamp ${_created}) $(epoch2timestamp ${_lastAccess}) $(epoch2timestamp ${_validUntil})"; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .id, .created, .lastAccessed, .validUntil ] | join( "\t" )' ) ) | column -t ID CREATED LASTACCESS VALIDUNTIL 537 2024-01-31T21:32:51.000PST 2024-03-26T23:44:32.000PDT 2024-07-31T21:32:51.000PDT 579 2024-03-26T23:19:16.000PDT 0 2024-09-26T23:19:16.000PDT
$ curl GET \ https://jira.sample.com/rest/api/2/user?username=some.username | jq -r
userFilter
valid user key
read above how to get a user key for a name or by email address; if you want to filter for more than one user, repeat that parameter for as many users you want to filter for
descriptionFilter
search term
string to search for in all token descriptions
notValidAfter
epoch Unix timestamp
tokens not valid anymore after that date/ time in milliseconds
tokenScope
integer
0 = no scope (all pre 1.5.0 tokens), 1 = read-only, 2 = read/ write
fromCreated
epoch Unix timestamp
-
untilCreated
epoch Unix timestamp
-
fromLastUsed
epoch Unix timestamp
-
untilLastUsed
epoch Unix timestamp
-
fromExpiresDuring
epoch Unix timestamp
-
untilExpiresDuring
epoch Unix timestamp
-
list token expired after certain time
$ curl "https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/tokensByFilter?notValidAfter=1688918956972" | jq -r # with limit and page $ curl "https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/tokensByFilter?page=0&limit=1¬ValidAfter=1688918956972" | jq -r
[!NOTE|label:references:]
$ openssl genrsa -out jira_privatekey.pem 1024
$ openssl req -newkey rsa:1024 -x509 -key jira_privatekey.pem -out jira_publickey.cer -days 365
$ openssl pkcs8 -topk8 -nocrypt -in jira_privatekey.pem -out jira_privatekey.pcks8
$ openssl x509 -pubkey -noout -in jira_publickey.cer > jira_publickey.pem
icons and priority
priority
[!NOTE|label:references:]
$ for _i in "blocker.png" "blocker.svg" "critical.png" "critical.svg" "high.png" "high.svg" "highest.png" "highest.svg" "low.png" "low.svg" "lowest.png" "lowest.svg" "major.png" "major.svg" "medium.png" "medium.svg" "minor.png" "minor.svg" "trivial.png" "trivial.svg"; do
echo "--> ${_i}"
curl -O https://jira-trigger-plugin.atlassian.net/images/icons/priorities/${_i}
done
confluence
$ confluenceName='confluence.domain.com'
$ pageID='143765713'
get page id:
$ curl -s https://${confluenceName}/rest/api/user/current | jq -r
get info
$ curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq --raw-output
get space
$ curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq .space.key
get title
$ curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq .title
get page history
$ curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq .version.number
get next version
currentVer=$(curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq .version.number) newVer=$((currentVer+1))
publish to confluence
[!NOTE|label:references:]
$ url="https://${confluenceName}/rest/api/content/${pageID}"
$ page=$(curl -s ${url})
$ space=$(echo "${page}" | jq .space.key)
$ title=$(echo "${page}" | jq .title)
$ currentVer=$(echo "${page}" | jq .version.number)
$ newVer=$((currentVer+1))
$ cat > a.json << EOF
{
"id": "${pageID}",
"type": "page",
"title": ${title},
"space": {"key": ${space}},
"body": {
"storage": {
"value": "<h1>Hi confluence</h1>",
"representation": "storage"
}
},
"version": {"number":${newVer}}
}
EOF
$ curl -s \
-i \
-X PUT \
-H 'Content-Type: application/json' \
--data "$(cat a.json)" \
https://${confluenceName}/rest/api/content/${pageID}
result
plugins
create excerpt
include excerpt
result
insert svg
insert svg image
[!TIP|label:get svg image:]
...
->Attachments
preview the image and get info from URL :
...&preview=/506105287/513281481/k8s-ha-cluster-stacked-etcd.v3.svg
<img width="800" src="https://ewiki.sample.com/download/attachments/506105287/513281481/k8s-ha-cluster-stacked-etcd.v3.svg">
edit source via
[!TIP|label:references:]
<p><ac:image ac:width="301"><ri:attachment ri:filename="SVG_logo.svg" /></ac:image></p>
CLI
[!NOTE|label:references:]
running with docker images
$ $ docker run -ti bobswiftapps/acli:latest acli -a getClientInfo
# or
$ docker run -ti bobswiftapps/acli:latest /bin/bash
bash-4.4# acli -a getClientInfo
# with env
$ cat .env
examplegear=jira -s https://examplegear.atlassian.net -u anonymous
examplegear_confluence=confluence -s https://examplegear.atlassian.net/wiki -u anonymous
$ docker run --env-file=.env -ti bobswiftapps/acli:latest /bin/bash
bash-5.0# acli $examplegear -a getServerInfo
# or
docker run -e examplegear='jira -s https://examplegear.atlassian.net -u anonymous' -ti bobswiftapps/acli:latest /bin/bash
bash-4.4# acli ${examplegear} -a getServerInfo
# with acli.properties
$ docker run -v ${PWD}/acli.properties:/opt/acli/acli.properties \
-ti bobswiftapps/acli:latest
# or prior to version 11.0.0, use:
$ docker run -v ${PWD}/acli.properties:/opt/atlassian-cli/acli.properties \
-ti bobswiftapps/acli:latest
# or with `ACLI_CONFIG` env
$ docker run -v ${PWD}/acli.properties:/tmp/acli.properties \
-e ACLI_CONFIG=/tmp/acli.properties \
-ti bobswiftapps/acli:latest \
acli -a getServerInfo --outputFormat 2
$ acli system setSecureProperty --name my.secret --secret -
Enter secure value: <secret value prompt>
Secure properties file does not yet exist. Creating...
Enter new secure properties password: <new password prompt>
Confirm secure properties password: <new password prompt>
Secure properties file created.
Value for key 'foo' set in secure properties file.
Last updated
Was this helpful?