diff --git a/.travis.yml b/.travis.yml index bcbd000f6b9ad5b0510f804ac4a3b19306b39c03..063b78a63e5b9f52dce0e99c56a2a6f91353ceb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,10 @@ before_install: - export PATH=~/bin:"$PATH" language: go go: - - 1.4.2 + - 1.5.1 script: script/test install: script/cached-bundle install --without development --deployment --jobs=3 --retry=3 +after_success: script/publish-release notifications: email: false env: @@ -19,3 +20,4 @@ env: - AMAZON_S3_BUCKET=ci-cache - AMAZON_ACCESS_KEY_ID=AKIAJQCVTDEWQHRPBPGQ - secure: "XAZv5xyNjWt7F9hG0MZhDANVJ5h/ajEZvfJOEIZRQlE3X5x6oVgI8blLh/MmlRSF0kIyLckcn6t2ccjSOvwN2hca5bwZSjIqoKbJyNe2cmkxfi2432vEOu3Ve6PT5hZWX4R5RgT+xWyMjIJcdF3gUMP7ErXl64aEncBzeW6OoXM=" + - secure: "eroPaeI0ohBaUjuc/y22VgyN+GDHeWXPIMAZTvSkNZfwL+Oxy861aeawi0zQeduEYon3fSfMBbOxJbrA+6IMU0W+DlR7TTBJ9dbGmeTFCu6ypJRJJtaE5/Kn9PwKjyG6XiPR/YR6818Jiv6yVCLQspjFbhCuKeFoHcu7RAmazKE=" diff --git a/script/build b/script/build index 09ad698cbee83c2424537c1b76871bd2c4237f85..070bd911e7754defd09101868a5406d091d53ad8 100755 --- a/script/build +++ b/script/build @@ -41,7 +41,7 @@ up_to_date() { build_hub() { setup_gopath - [ -n "$1" ] && (up_to_date "$1" || go build -ldflags "-X github.com/github/hub/version.Version `./script/version`" -o "$1") + [ -n "$1" ] && (up_to_date "$1" || go build -ldflags "-X github.com/github/hub/version.Version=`./script/version`" -o "$1") } test_hub() { @@ -60,6 +60,9 @@ case "$1" in test ) test_hub ;; +files ) + find_source_files + ;; -h | --help ) sed -ne '/^#/!q;s/.\{1,2\}//;1d;p' < "$0" exit diff --git a/script/changelog b/script/changelog new file mode 100755 index 0000000000000000000000000000000000000000..3e474f9a45bc82261bec33b6e06b6e9bd1eb6582 --- /dev/null +++ b/script/changelog @@ -0,0 +1,21 @@ +#!/bin/bash +# vi:ft=sh: +# Usage: script/changelog [HEAD] +# +# Show changes to runtime files between HEAD and previous release tag. +set -e + +head="${1:-HEAD}" + +for sha in `git rev-list -n 100 --first-parent "$head"^`; do + previous_tag="$(git tag -l --points-at "$sha" 'v*' 2>/dev/null || true)" + [ -z "$previous_tag" ] || break +done + +if [ -z "$previous_tag" ]; then + echo "Couldn't detect previous version tag" >&2 + exit 1 +fi + +git log --no-merges --format='%C(auto,green)* %s%C(auto,reset)%n%w(0,2,2)%+b' \ + --reverse "${previous_tag}..${head}" -- `script/build files` diff --git a/script/cross-compile b/script/cross-compile new file mode 100755 index 0000000000000000000000000000000000000000..c94f79a793d02bb7353860be8a2abfec403b1854 --- /dev/null +++ b/script/cross-compile @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Usage: script/cross-compile +# +# Packages the project over a matrix of supported OS and architectures and +# prints the asset filenames and labels suitable for upload. +set -e + +version="${1?}" + +echo ' + darwin amd64 OS X + freebsd 386 FreeBSD 32-bit + freebsd amd64 FreeBSD 64-bit + linux 386 Linux 32-bit + linux amd64 Linux 64-bit + windows 386 Windows 32-bit + windows amd64 Windows 64-bit +' | { + while read os arch label; do + [ -n "$os" ] || continue + + label="hub ${version} for ${label}" + if ! file="$(script/package "$os" "$arch" "$version")"; then + echo "packaging $label failed" >&2 + continue + fi + + printf "%s\t%s\n" "$file" "$label" + done +} diff --git a/script/github-release b/script/github-release new file mode 100755 index 0000000000000000000000000000000000000000..b6037a25f4b533cb0972d78423c2ea3850c279a7 --- /dev/null +++ b/script/github-release @@ -0,0 +1,66 @@ +#!/usr/bin/env ruby +# Usage: script/cross-compile | script/github-release +# +# Takes in a list of asset filenames + labels via stdin and uploads them to the +# corresponding release on GitHub. The release is created as a draft first if +# missing and its body is the git changelog since the previous tagged release. +require "json" +require "cgi" + +def github_host() ENV.fetch("GITHUB_HOST", "https://api.github.com") end +def oauth_token() ENV["GITHUB_OAUTH"] end + +def escape(str) CGI.escape(str.to_s) end + +def api(*args) + path = args.shift + cmd = ["curl", "-s", "--netrc"] + cmd << ( path.include?("://") ? path : File.join(github_host, path) ) + cmd << "-H" << "Authorization: token #{oauth_token}" if oauth_token + + if args.last.is_a? Hash + payload = JSON.dump(args.pop) + cmd << "--data" << "@-" + cmd << "-H" << "Content-Type: application/json" + end + + cmd.concat args + + IO.popen(cmd, payload ? "r+" : "r") do |curl| + if payload + curl.write payload + curl.close_write + end + JSON.parse(curl.read) + end +end + +repo = ENV["TRAVIS_REPO_SLUG"] +project_name, version = ARGV + +release = api("/repos/#{repo}/releases").find { |rel| + rel.fetch("id") && rel.fetch("tag_name") == "v#{version}" +} + +unless release + release = api "/repos/#{repo}/releases", + tag_name: "v#{version}", + name: "#{project_name} #{version}", + body: `script/changelog`, + draft: true, + prerelease: version.include?('-') +end + +upload_url = release.fetch("upload_url") + +STDIN.each do |line| + filename, label = line.chomp.split("\t", 2) + name = File.basename filename + + if asset = release["assets"].find { |a| a.fetch("name") == name } + api asset.fetch("url"), "-X", "DELETE" + end + + upload = upload_url.sub(/\{\?.+?\}/, "?name=#{escape name}&label=#{escape label}") + api upload, "-H", "Content-Type: application/octet-stream", "--data-binary", "@#{filename}" +end diff --git a/script/install.bat b/script/install.bat new file mode 100644 index 0000000000000000000000000000000000000000..2eb5f97c13614f9ebd7a48826f666874ed3f7d80 --- /dev/null +++ b/script/install.bat @@ -0,0 +1,40 @@ +@echo off +CLS + +:checkPrivileges +NET FILE 1>NUL 2>NUL +if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges ) + +:getPrivileges +if '%1'=='ELEV' (shift & goto gotPrivileges) +echo. +echo ************************************** +echo Installing GitHub CLI as Administrator +echo ************************************** + +setlocal DisableDelayedExpansion +set "batchPath=%~0" +setlocal EnableDelayedExpansion +echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs" +echo UAC.ShellExecute "!batchPath!", "ELEV", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs" +"%temp%\OEgetPrivileges.vbs" +exit /B + +:gotPrivileges + +setlocal & cd /d %~dp0 + +set HUB_BIN_PATH="%LOCALAPPDATA%\GitHubCLI\bin" +IF EXIST %HUB_BIN_PATH% GOTO DIRECTORY_EXISTS +mkdir %HUB_BIN_PATH% +set "path=%PATH%;%HUB_BIN_PATH:"=%" +1>NUL setx PATH "%PATH%" /M +:DIRECTORY_EXISTS + +:: Delete any existing programs +2>NUL del /q %HUB_BIN_PATH%\hub* + +1>NUL copy .\bin\hub.exe %HUB_BIN_PATH%\hub.exe + +echo hub.exe installed successfully. Press any key to exit +pause > NUL diff --git a/script/install.sh b/script/install.sh new file mode 100644 index 0000000000000000000000000000000000000000..869880c20e577b29965ca8ac904a608aae558d53 --- /dev/null +++ b/script/install.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Usage: [sudo] [prefix=/usr/local] ./install +set -e + +case "$1" in +'-h' | '--help' ) + sed -ne '/^#/!q;s/.\{1,2\}//;1d;p' < "$0" + exit 0 + ;; +esac + +if [[ $BASH_SOURCE == */* ]]; then + cd "${BASH_SOURCE%/*}" +fi + +prefix="${PREFIX:-$prefix}" +prefix="${prefix:-/usr/local}" + +for src in `find bin share -type f`; do + dest="${prefix}/${src}" + mkdir -p "${dest%/*}" + install "$src" "$dest" +done diff --git a/script/package b/script/package index 0a6efedc4ed672cb41a420cae86071461fc3dfda..93405813b687f4bca125735ab8c3c30d1dee8d52 100755 --- a/script/package +++ b/script/package @@ -1,166 +1,63 @@ -#!/usr/bin/env ruby -# Usage: script/package +#!/usr/bin/env bash +# Usage: script/package # -# Packages `hub` for release for current platform - -require "fileutils" -include FileUtils - -module OS - class << self - def type - if darwin? - "darwin" - elsif linux? - "linux" - elsif windows? - "windows" - else - raise "Unknown OS type #{RUBY_PLATFORM}" - end - end - - def friendly_name - if darwin? - "mac" - elsif linux? - "linux" - elsif windows? - "windows" - else - raise "Unknown OS type #{RUBY_PLATFORM}" - end - end - - def windows? - (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil - end - - def windows_64? - windows? && /x64/ =~ RUBY_PLATFORM - end - - def darwin? - (/darwin/ =~ RUBY_PLATFORM) != nil - end - - def linux? - (/linux/ =~ RUBY_PLATFORM) != nil - end - end -end - -class Packer - class << self - def pack! - self.new.pack! - end - end - - attr_reader :version - - def initialize - @version = parse_version! - end - - def pack! - unless ENV["SKIP_TOOLCHAIN"] - install_gox! - build_toolchain! - end - run_tests! unless OS.windows? || ENV["SKIP_TEST"] # cukes don't run on Windows - build_hub! - cp_assets - tar_gzip - end - - private - - def exec!(cmd) - io = IO.popen(cmd) - begin - while line = io.gets - puts line.chomp - end - ensure - io.close - end - - raise "Fail to execute #{cmd}" unless $?.to_i == 0 - end - - # Returns the root path to paths - def root_path(*paths) - current = File.expand_path(File.dirname(__FILE__)) # current is the target folder - File.expand_path File.join(current, "..", paths) - end - - def glob_dir(path) - Dir[path].select { |d| File.directory?(d) } - end - - def parse_version! - content = File.read root_path("version", "version.go") - match = /var Version = "(.+)"/.match content - raise "Fail to parse Hub version" unless match - - match[1] - end - - def install_gox! - puts "Installing github.com/mitchellh/gox" - result = system "go get github.com/mitchellh/gox" - raise "Fail to install gox" unless result - end - - def build_toolchain! - puts "Building Go toolchain" - result = system "gox -build-toolchain -os=#{OS.type}" - raise "Fail to build Go toolchain" unless result - end - - def run_tests! - puts "Running Hub tests" - - bootstrap_script = root_path("script", "bootstrap") - exec!(bootstrap_script) - - test_script = root_path("script", "test") - exec!(test_script) - end - - def build_hub! - puts "Building for #{OS.friendly_name}" - release_version = `script/version`.strip - output = root_path("target", "{{.Dir}}-#{OS.friendly_name}-{{.Arch}}-#{version}", "{{.Dir}}") - # gox doesn't build for 64 bit and 32 bit on 64 bit Windows - # specifying osarch for Windows - # see https://github.com/mitchellh/gox/issues/19#issuecomment-68117016 - osarch = OS.windows? ? "windows/#{OS.windows_64? ? "amd64" : "386"}" : "" - cmd = "gox -os=#{OS.type} -output=#{output} -ldflags \"-X github.com/github/hub/version.Version #{release_version}\"" - cmd += " -osarch=#{osarch}" unless osarch.empty? - exec!(cmd) - end - - def cp_assets - path = root_path("target", "*#{OS.friendly_name}*") - glob_dir(path).each do |dir| - puts "Copying assets to #{dir}" - ["README.md", "LICENSE", "etc/", "man/"].each do |f| - cp_r f, File.join(dir, f) - end - end - end - - def tar_gzip - path = root_path("target", "*#{OS.friendly_name}*") - glob_dir(path).each do |dir| - puts "Archiving #{dir}" - Dir.chdir(root_path("target")) do - exec!("tar -zcf #{File.basename(dir)}.tar.gz #{File.basename(dir)}") - end - end - end -end - -Packer.pack! +# Packages the project as a release asset and prints the archive's filename. +set -e + +os="${1?}" +arch="${2?}" +version="${3?}" + +release="hub-${os}-${arch}-${version}" + +case "$os" in + darwin | freebsd | linux | netbsd | openbsd | solaris | windows ) ;; + * ) echo "unsupported OS: $os" >&2; exit 1 ;; +esac + +case "$arch" in + 386 | amd64 ) ;; + * ) echo "unsupported arch: $arch" >&2; exit 1 ;; +esac + +export GOOS="$os" +export GOARCH="$arch" + +tmpdir="tmp/${release}" +rm -rf "$tmpdir" + +exename="${tmpdir}/bin/hub" +[ "$os" != "windows" ] || exename="${exename}.exe" +mkdir -p "${exename%/*}" +script/build -o "$exename" + +crlf() { + sed $'s/$/\r/' "$1" > "$2" +} + +if [ "$os" = "windows" ]; then + crlf README.md "${tmpdir}/README.txt" + crlf LICENSE "${tmpdir}/LICENSE.txt" + crlf man/hub.1.html "${tmpdir}/hub.html" + crlf script/install.bat "${tmpdir}/install.bat" +else + cp -R README.md LICENSE etc "$tmpdir" + mkdir -p "${tmpdir}/share/man/man1" + cp man/hub.1 "${tmpdir}/share/man/man1" + cp script/install.sh "${tmpdir}/install" + chmod +x "${tmpdir}/install" +fi + +if [ "$os" = "windows" ]; then + file="${PWD}/${tmpdir}.zip" + rm -f "$file" + pushd "$tmpdir" >/dev/null + zip -r "$file" * >/dev/null +else + file="${PWD}/${tmpdir}.tgz" + rm -f "$file" + pushd "${tmpdir%/*}" >/dev/null + tar -czf "$file" "$release" +fi + +echo "$file" diff --git a/script/publish-release b/script/publish-release new file mode 100755 index 0000000000000000000000000000000000000000..da404248736d1bdcff889e2570ae6d57c5095e6a --- /dev/null +++ b/script/publish-release @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -e + +if [[ $TRAVIS_TAG == v* ]] && [ "$TRAVIS_OS_NAME" = "linux" ] && [ -n "$GITHUB_OAUTH" ]; then + version="${TRAVIS_TAG#v}" + script/cross-compile "$version" | script/github-release hub "$version" +fi