diff --git a/maintenance/default.nix.sh b/maintenance/default.nix.sh index d7228fc..3cde197 100755 --- a/maintenance/default.nix.sh +++ b/maintenance/default.nix.sh @@ -1,17 +1,10 @@ #! /usr/bin/env nix-shell #! nix-shell -i bash -p bash curl gnugrep jq gawk libxml2 nix-prefetch-scripts +#: Above includes transitive dependencies of our libraries, would be great to specify them at the usage site. -IsRubyGem() { - test 200 = "$(curl -sL -w '%{http_code}' https://rubygems.org/api/v1/gems/$1.json -o /dev/null)"; -}; - -Map() { - while read Line; do $* $Line; done -}; - -Filter() { - X() { $* && (shift; echo $*;) }; Map X $* -}; +GemCache="$(mktemp ${TMPDIR:-/tmp}/GEMS.XXXXXXX)" +source "$(dirname $(realpath $0))/lib/rubygems.sh" +source "$(dirname $(realpath $0))/lib/prelude.sh" TopLevelDependencies() { ( # Nanoc isn't an explicit dependency: @@ -25,88 +18,6 @@ TopLevelDependencies() { ) | sort -u | Filter IsRubyGem }; -GetLatestVersion() { - curl -sL https://rubygems.org/api/v1/gems/$1.json | jq -r '.version' -}; - -read -r -d '' AWKPrelude <",str); - gsub("<","<",str); - gsub(/^= /,"== ",str); - return str; - } - function ver(str) { - n=split(str,xs,"."); - v=xs[1]; for(i=2;i<=n;i++) - v+=xs[i]*(10**(1-i)); - return v - } - function unwakka(str) { - if (str ~ /^~>/) { - sub(/^[^ ]+ */,"",str); - n=split(str,xs,".");xs[n-1]++;xs[n]=0; - return(sprintf(">= %s, < %s", str, join(xs,1,n,"."))); - } else return str - }; -EOF - -CompileConstraint() { - awk "$AWKPrelude"' - NF { - printf "%s;", $1; - sub(".*" $1 FS, ""); - sub($0, unwakka(unescape($0))); - split($0, xs, ", "); for (x in xs) - split(xs[x],ys," "); - printf "ver($0) %s %s && ", ys[1], ver(ys[2]) - print "1" - } - '; -}; - -SatisfyConstraint() { - GemName="$1"; Constraint="$2"; - curl -s https://rubygems.org/api/v1/versions/$GemName.json \ - | jq -r '.[] | .number' \ - | awk "$AWKPrelude ($Constraint){print;exit}" -}; - -GetDependencies() { - # The RubyGems API doesn't seem to permit retrieving the dependencies of any - # version other than the most recent, so we resort to scraping. - GemName="$1"; GemVersion="$2"; - curl -s https://rubygems.org/gems/$GemName/versions/$GemVersion \ - | xmllint --xpath '//div[@id="runtime_dependencies"]/div/a//text()' --html 2>/dev/null - \ - | CompileConstraint \ - | { IFS=";"; while read -r Name Constraint; do - Version="$(SatisfyConstraint "$Name" "$Constraint")" - test -n "$Name" && test -n "$Version" && echo "$Name" "$Version" - done; } -}; - -GetSHA256() { - nix-prefetch-url https://rubygems.org/downloads/$1.gem 2>/dev/null -}; - -GemCache="$(mktemp ${TMPDIR:-/tmp}/GEMS.XXXXXXX)"; - -Gem2Nix() { - GemName=$1; GemVersion="${2:-$(GetLatestVersion $GemName)}"; - grep -qE "^$GemName" "$GemCache" || { - echo $GemName >> "$GemCache"; - sed 's/^ //;/^$/d' <<< " - $GemName = buildRubyGem ({ - name = ''$GemName-$GemVersion''; - sha256 = ''$(GetSHA256 $GemName-$GemVersion)''; - gemPath = [$(GetDependencies "$GemName" "$GemVersion" | cut -d' ' -f1 | tr '\n' ' ')]; - ruby = ruby_2_2; - } // optionalAttrs (hasAttr ''$GemName'' hotfix) hotfix.$GemName); - "; GetDependencies "$GemName" "$GemVersion" | Map Gem2Nix - }; -}; - cat < {} }: let diff --git a/maintenance/lib/prelude.sh b/maintenance/lib/prelude.sh new file mode 100644 index 0000000..8f0a5b3 --- /dev/null +++ b/maintenance/lib/prelude.sh @@ -0,0 +1,9 @@ +#! /usr/bin/env bash + +Map() { + while read Line; do $* $Line; done +}; + +Filter() { + X() { $* && (shift; echo $*;) }; Map X $* +}; diff --git a/maintenance/lib/rubygems.sh b/maintenance/lib/rubygems.sh new file mode 100644 index 0000000..3768f44 --- /dev/null +++ b/maintenance/lib/rubygems.sh @@ -0,0 +1,87 @@ +#! /usr/bin/env bash + +source "$(dirname $(readlink -f ${BASH_SOURCE[0]}))/prelude.sh" + +IsRubyGem() { + test 200 = "$(curl -sL -w '%{http_code}' https://rubygems.org/api/v1/gems/$1.json -o /dev/null)"; +}; + +GetLatestVersion() { + curl -sL https://rubygems.org/api/v1/gems/$1.json | jq -r '.version' +}; + +read -r -d '' AWKPrelude <",str); + gsub("<","<",str); + gsub(/^= /,"== ",str); + return str; + } + function ver(str) { + n=split(str,xs,"."); + v=xs[1]; for(i=2;i<=n;i++) + v+=xs[i]*(10**(1-i)); + return v + } + function unwakka(str) { + if (str ~ /^~>/) { + sub(/^[^ ]+ */,"",str); + n=split(str,xs,".");xs[n-1]++;xs[n]=0; + return(sprintf(">= %s, < %s", str, join(xs,1,n,"."))); + } else return str + }; +EOF + +CompileConstraint() { + awk "$AWKPrelude"' + NF { + printf "%s;", $1; + sub(".*" $1 FS, ""); + sub($0, unwakka(unescape($0))); + split($0, xs, ", "); for (x in xs) + split(xs[x],ys," "); + printf "ver($0) %s %s && ", ys[1], ver(ys[2]) + print "1" + } + '; +}; + +SatisfyConstraint() { + GemName="$1"; Constraint="$2"; + curl -s https://rubygems.org/api/v1/versions/$GemName.json \ + | jq -r '.[] | .number' \ + | awk "$AWKPrelude ($Constraint){print;exit}" +}; + +GetDependencies() { + # The RubyGems API doesn't seem to permit retrieving the dependencies of any + # version other than the most recent, so we resort to scraping. + GemName="$1"; GemVersion="$2"; + curl -s https://rubygems.org/gems/$GemName/versions/$GemVersion \ + | xmllint --xpath '//div[@id="runtime_dependencies"]/div/a//text()' --html 2>/dev/null - \ + | CompileConstraint \ + | { IFS=";"; while read -r Name Constraint; do + Version="$(SatisfyConstraint "$Name" "$Constraint")" + test -n "$Name" && test -n "$Version" && echo "$Name" "$Version" + done; } +}; + +GetSHA256() { + nix-prefetch-url https://rubygems.org/downloads/$1.gem 2>/dev/null +}; + +Gem2Nix() { + GemName=$1; GemVersion="${2:-$(GetLatestVersion $GemName)}"; + grep -qE "^$GemName" "$GemCache" || { + echo $GemName >> "$GemCache"; + sed 's/^ //;/^$/d' <<< " + $GemName = buildRubyGem ({ + name = ''$GemName-$GemVersion''; + sha256 = ''$(GetSHA256 $GemName-$GemVersion)''; + gemPath = [$(GetDependencies "$GemName" "$GemVersion" | cut -d' ' -f1 | tr '\n' ' ')]; + ruby = ruby_2_2; + } // optionalAttrs (hasAttr ''$GemName'' hotfix) hotfix.$GemName); + "; GetDependencies "$GemName" "$GemVersion" | Map Gem2Nix + }; +};