about summary refs log tree commit diff
path: root/pkgs/common-updater/unstable-updater.nix
blob: d982fc6110ba7294ed137294c7b62bea302e0416 (plain) (blame)
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
{ lib
, writeShellApplication
, coreutils
, git
, nix
, common-updater-scripts
}:

# This is an updater for unstable packages that should always use the latest
# commit.
{ url ? null # The git url, if empty it will be set to src.gitRepoUrl
, branch ? null
, hardcodeZeroVersion ? false # Use a made-up version "0" instead of latest tag. Use when there is no previous release, or the project's tagging system is incompatible with what we expect from versions
, tagFormat ? "*" # A `git describe --tags --match '<format>'` pattern that tags must match to be considered
, tagPrefix ? null # strip this prefix from a tag name
, tagConverter ? null # A command to convert more complex tag formats. It receives the git tag via stdin and should convert it into x.y.z format to stdout
, shallowClone ? true
}:

assert lib.asserts.assertMsg (tagPrefix == null || tagConverter == null) "Can only use either tagPrefix or tagConverter!";

let
  updateScript = writeShellApplication {
    name = "unstable-update-script";
    runtimeInputs = [
      common-updater-scripts
      coreutils
      git
      nix
    ];
    text = ''
      set -ex

      url=""
      branch=""
      hardcode_zero_version=""
      tag_format=""
      tag_prefix=""
      tag_converter=""
      shallow_clone=""
      : "''${systemArg:=}"

      while (( $# > 0 )); do
          flag="$1"
          shift 1
          case "$flag" in
            --url=*)
              url="''${flag#*=}"
              ;;
            --branch=*)
              branch="''${flag#*=}"
              ;;
            --hardcode-zero-version)
              hardcode_zero_version=1
              ;;
            --tag-format=*)
              tag_format="''${flag#*=}"
              ;;
            --tag-prefix=*)
              tag_prefix="''${flag#*=}"
              ;;
            --tag-converter=*)
              tag_converter="''${flag#*=}"
              ;;
            --shallow-clone)
              shallow_clone=1
              ;;
            *)
              echo "$0: unknown option ‘''${flag}’"
              exit 1
              ;;
          esac
      done

      # By default we set url to src.gitRepoUrl
      if [[ -z "$url" ]]; then
          # system argument cannot be passed as 1 argument
          # shellcheck disable=SC2086
          url="$(nix-instantiate $systemArg --eval -E \
                     "with import ./. {}; $UPDATE_NIX_ATTR_PATH.src.gitRepoUrl" \
              | tr -d '"')"
      fi

      # Get info about HEAD from a shallow git clone
      tmpdir="$(mktemp -d)"

      cloneArgs=()

      if [[ "$shallow_clone" == "1" ]]; then
          cloneArgs+=(--depth=1)
      fi

      if [[ -n "$branch" ]]; then
          cloneArgs+=(--branch="$branch")
      fi

      git clone "''${cloneArgs[@]}" "$url" "$tmpdir"
      getLatestVersion() {
          git describe --tags --abbrev=0 --match "''${tag_format}" 2> /dev/null || true
      }

      pushd "$tmpdir"
      commit_date="$(git show -s --pretty='format:%cs')"
      commit_sha="$(git show -s --pretty='format:%H')"
      last_tag=""
      if [[ -z "$hardcode_zero_version" ]]; then
          if [[ "$shallow_clone" == "1" ]]; then
              depth=100
              while (( depth < 10000 )); do
                  last_tag="$(getLatestVersion)"
                  if [[ -n "$last_tag" ]]; then
                      break
                  fi
                  git fetch --depth="$depth" --tags
                  depth=$(( depth * 2 ))
              done

              if [[ -z "$last_tag" ]]; then
                  # To be extra sure, check if full history helps with finding a tag
                  git fetch --tags
                  last_tag="$(getLatestVersion)"
              fi
          else
              last_tag="$(getLatestVersion)"
          fi
          if [[ -z "$last_tag" ]]; then
              last_tag="0"
          fi
          if [[ -n "$tag_prefix" ]]; then
              echo "Stripping prefix '$tag_prefix' from tag '$last_tag'"
              last_tag="''${last_tag#"''${tag_prefix}"}"
          fi
          if [[ -n "$tag_converter" ]]; then
              echo "Running '$last_tag' through: $tag_converter"
              last_tag="$(echo "''${last_tag}" | ''${tag_converter})"
          fi
      else
          last_tag="0"
      fi
      if [[ ! "$last_tag" =~ ^[[:digit:]] ]]; then
          echo "Last tag '$last_tag' does not start with a digit" > /dev/stderr
          exit 1
      fi
      new_version="$last_tag-unstable-$commit_date"
      popd
      # rm -rf "$tmpdir"

      # update the nix expression
      update-source-version \
          "$UPDATE_NIX_ATTR_PATH" \
          "$new_version" \
          --rev="$commit_sha"
    '';
  };

in
[
  (lib.getExe updateScript)
  "--url=${builtins.toString url}"
  "--tag-format=${tagFormat}"
] ++ lib.optionals (branch != null) [
  "--branch=${branch}"
] ++ lib.optionals (tagPrefix != null) [
  "--tag-prefix=${tagPrefix}"
] ++ lib.optionals (tagConverter != null) [
  "--tag-converter=${tagConverter}"
] ++ lib.optionals hardcodeZeroVersion [
  "--hardcode-zero-version"
] ++ lib.optionals shallowClone [
  "--shallow-clone"
]