summarylogtreecommitdiffstats
path: root/scripts.gh-upload-artifact.sh
blob: df01b07b152f9c735f39f76f9da2d0ac314e7520 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/usr/bin/env bash

usage() {
cat <<EOF
$0 --repo=<owner/repo> --tag=<release tag | @> --file=<artifact path> --token=<github api token>

Usage:

  This script uploads the given artifact to a GitHub release.

  It takes 4 required options:

  --repo        | The <org>/<repo> combo to upload the artifact to      env:\$GITHUB_REPO     =${repo:-<unset>}
  --tag         | The tag / release name to upload the artifact to      env:\$GITHUB_RELEASE  =${tag:-<unset>}
  --file        | The file path to the artifact to upload               env:\$GITHUB_ARTIFACT =${artifact:-<unset>}
  --token       | The API token to use when authenticating with GitHub  env:\$GITHUB_TOKEN    =$([[-n "$token" ]] && printf '<*****>' || printf '<unset>')
  --dry-run     | Don't actually upload the artifact                    env:\$GITHUB_DRYRUN   =$([[-n "$dry" ]] && printf 'true' || printf 'false')
  --help        | Print this help

  Each option may also be set via the associated environment variable, with the command line taking precedence.

  You may optionally force the uploaded artifact to have a different name from the given file, by suffixing --file
  with a ':<name-override>'. For example, '/path/to/artifact_with.dirty+name.tar.gz:artifact.tar.gz' will resolve
  the uploaded artifact's filename to 'artifact.tar.gz'.

  To select the most recent tag on the remote, you can use the special value '@' when setting --tag.

Examples:

  1. $(basename $0) --tag=v1.0.0 --repo github/octocat --file ./cute-kat.jpg --token=dae3re34...

  2. GITHUB_TOKEN=4wf4dec... GITHUB_ARTIFACT=/assets/cats.json $(basename $0) --repo=github/octocat --tag=v2.0.0

  3. $(basename $0) --tag=@ --repo github/octocat --file ./dirty_m3sszy.kitty.tar.xz:kitty.tar.xz --token f43rffe...

EOF
}

requires() {
  local t v efails xfails emsg
  for t in "$@"; do
    v=${t#*:}

    case $t in
      e:*)
        [[ -n "${!v}" ]] || efails+=("$v")
      ;;
      x:*|*)
        command -v "${v}" &>/dev/null || xfails+=("$v")
      ;;
    esac
  done

  [[ -n "${efails}" ]] && emsg=$(printf 'Missing required variable(s): %s' "${efails[*]}")
  [[ -n "${xfails}" ]] && emsg=$(printf '%s\nMissing required executable(s): %s' "$emsg" "${xfails[*]}")
  [[ -n "$emsg" ]] && die 3 "$(printf '%s\n[INFO]: Use --help for more information' "$emsg")"
}

cli() {
  local GOPTS=$(getopt -o hd --long help,dry-run,repo:,tag:,file:,token: -n "gh-upload-artifact" -- "$@")
  eval set -- "$GOPTS"

  while (( $# > 0 )); do
    case "$1" in
      --repo)         repo="$2";        shift 2 ;;
      --tag)          tag="$2";         shift 2 ;;
      --file)         artifact="$2";    shift 2 ;;
      --token)        token="$2";       shift 2 ;;
      -h|--help)      help=1;           shift   ;;
      -d|--dry-run)   dry=1;            shift   ;;
      --)             shift;            break   ;;
      *) die 1 "Unknown option: $1" 1           ;;
    esac
  done
}

set_filename() {
  artifact=${1%:*}
  filename=${1##*:}
  [[ -n "$filename" ]] || filename=$(basename $artifact)
}

die() {
  echo "[ERROR]: $2" >&2
  [ -n "$3" ] && usage >&2
  exit $1
}

setup() {
  requires x:getopt x:curl x:jq

  # Script inputs
  repo=${GITHUB_REPO}
  tag=${GITHUB_RELEASE}
  artifact=${GITHUB_ARTIFACT}
  token=${GITHUB_TOKEN}
  dry=${GITHUB_DRYRUN:+1}
  filename=""
  help=-1

  cli "$@"
  (( help > 0 )) && usage && exit 0

  requires e:repo e:tag e:artifact e:token

  set_filename "$artifact"

  # Constants
  GH_RESPONSE=$(mktemp)
  GH_AUTH="Authorization: token $token"
  CURL_ARGS="-sSL"
  [[ "$tag" == '@' ]] \
    && GH_TAG="latest" \
    || GH_TAG="tags/$tag"
}

main() {
  setup "$@"
  trap 'rm -f $GH_RESPONSE' EXIT

  # Fetch release JSON blob
  curl "$CURL_ARGS" -o "$GH_RESPONSE" -H "$GH_AUTH" "https://api.github.com/repos/$repo/releases/$GH_TAG"
  (( $? > 0 )) && die 1 "$(cat <(printf "Invalid repo, token or network issue!\n") $GH_RESPONSE)"

  # Get ID of the asset based on given filename.
  id=$(jq -r '.id' "$GH_RESPONSE")
  (( $? > 0 )) && die 1 "$(cat <(printf "Failed to get release id for tag '%s' from response:\n" "$tag") $GH_RESPONSE)"

  # Construct url
  upload_uri="https://uploads.github.com/repos/$repo/releases/$id/assets?name=$filename"

  # Upload artifact
  if [[ -n "$dry" ]]; then
    echo "[INFO]: Would upload $artifact to $upload_uri" >&2
  else
    curl "$CURL_ARGS" \
      -H "$GH_AUTH" \
      -H "Content-Type: $(file -b --mime-type $artifact)" \
      --data-binary @"$artifact" \
      -o "$GH_RESPONSE" \
      $upload_uri

    (( $? > 0 )) \
      && die 1 "$(cat <(printf "Failed to upload $filename to $repo:\n") $GH_RESPONSE)" \
      || jq '.' "$GH_RESPONSE"
  fi
}

[[ -n "$Debug" ]] && set -x
main "$@"