diff --git a/akka-docs/_sphinx/themes/akka/layout.html b/akka-docs/_sphinx/themes/akka/layout.html index 0bd735c446..0d46ef708e 100644 --- a/akka-docs/_sphinx/themes/akka/layout.html +++ b/akka-docs/_sphinx/themes/akka/layout.html @@ -6,6 +6,7 @@ {% extends "basic/layout.html" %} {% set script_files = script_files + ['_static/theme_extras.js'] %} {% set css_files = css_files + ['_static/print.css'] %} +{% set is_snapshot = version.endswith("-SNAPSHOT") %} {# do not display relbars #} {% block relbar1 %}{% endblock %} @@ -37,7 +38,11 @@ {%- endif -%}

{{ shorttitle|e }}

Version {{ version|e }}

+ {%- if is_snapshot -%}

PDF

+ {%- else -%} +

PDF

+ {%- endif -%} {%- endblock %}
diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index bc596dc126..b92bd7a611 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -24,10 +24,10 @@ object AkkaBuild extends Build { lazy val akka = Project( id = "akka", base = file("."), - settings = parentSettings ++ Unidoc.settings ++ rstdocSettings ++ Seq( + settings = parentSettings ++ Release.settings ++ Unidoc.settings ++ Rstdoc.settings ++ Publish.versionSettings ++ Seq( parallelExecution in GlobalScope := false, - Unidoc.unidocExclude := Seq(samples.id, tutorials.id), - rstdocDirectory <<= baseDirectory / "akka-docs" + Publish.defaultPublishTo in ThisBuild <<= crossTarget / "repository", + Unidoc.unidocExclude := Seq(samples.id, tutorials.id) ), aggregate = Seq(actor, testkit, actorTests, stm, remote, slf4j, amqp, mailboxes, akkaSbtPlugin, samples, tutorials, docs) ) @@ -266,7 +266,7 @@ object AkkaBuild extends Build { // Settings - override lazy val settings = super.settings ++ buildSettings ++ Publish.versionSettings + override lazy val settings = super.settings ++ buildSettings lazy val baseSettings = Defaults.defaultSettings ++ Publish.settings @@ -349,23 +349,6 @@ object AkkaBuild extends Build { compileInputs in MultiJvm <<= (compileInputs in MultiJvm) dependsOn (ScalariformKeys.format in MultiJvm), ScalariformKeys.preferences in MultiJvm := formattingPreferences ) - - // reStructuredText docs - - val rstdocDirectory = SettingKey[File]("rstdoc-directory") - val rstdoc = TaskKey[File]("rstdoc", "Build the reStructuredText documentation.") - - lazy val rstdocSettings = Seq(rstdoc <<= rstdocTask) - - def rstdocTask = (rstdocDirectory, streams) map { - (dir, s) => { - s.log.info("Building reStructuredText documentation...") - val exitCode = Process(List("make", "clean", "html", "pdf"), dir) ! s.log - if (exitCode != 0) sys.error("Failed to build docs.") - s.log.info("Done building docs.") - dir - } - } } // Dependencies diff --git a/project/Publish.scala b/project/Publish.scala index 1fb9039faa..9cea85af3c 100644 --- a/project/Publish.scala +++ b/project/Publish.scala @@ -1,16 +1,19 @@ package akka import sbt._ -import Keys._ +import sbt.Keys._ +import sbt.Project.Initialize import java.io.File object Publish { final val Snapshot = "-SNAPSHOT" + val defaultPublishTo = SettingKey[File]("default-publish-to") + lazy val settings = Seq( crossPaths := false, pomExtra := akkaPomExtra, - publishTo := akkaPublishTo, + publishTo <<= akkaPublishTo, credentials ++= akkaCredentials, organizationName := "Typesafe Inc.", organizationHomepage := Some(url("http://www.typesafe.com")) @@ -32,11 +35,12 @@ object Publish { } - def akkaPublishTo: Option[Resolver] = { - val property = Option(System.getProperty("akka.publish.repository")) - val repo = property map { "Akka Publish Repository" at _ } - val m2repo = Path.userHome / ".m2" /"repository" - repo orElse Some(Resolver.file("Local Maven Repository", m2repo)) + def akkaPublishTo: Initialize[Option[Resolver]] = { + defaultPublishTo { default => + val property = Option(System.getProperty("akka.publish.repository")) + val repo = property map { "Akka Publish Repository" at _ } + repo orElse Some(Resolver.file("Default Local Repository", default)) + } } def akkaCredentials: Seq[Credentials] = { @@ -44,17 +48,11 @@ object Publish { property map (f => Credentials(new File(f))) toSeq } - def stampVersion = Command.command("stamp-version") { state => - append((version in ThisBuild ~= stamp) :: Nil, state) - } + // timestamped versions - // TODO: replace with extracted.append when updated to sbt 0.10.1 - def append(settings: Seq[Setting[_]], state: State): State = { + def stampVersion = Command.command("stamp-version") { state => val extracted = Project.extract(state) - import extracted._ - val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings) - val newStructure = Load.reapply(session.original ++ append, structure) - Project.setProject(session, newStructure, state) + extracted.append(List(version in ThisBuild ~= stamp), state) } def stamp(version: String): String = { diff --git a/project/Release.scala b/project/Release.scala new file mode 100644 index 0000000000..6b6f5643bc --- /dev/null +++ b/project/Release.scala @@ -0,0 +1,34 @@ +package akka + +import sbt._ +import sbt.Keys._ +import java.io.File + +object Release { + val releaseDirectory = SettingKey[File]("release-directory") + + lazy val settings: Seq[Setting[_]] = commandSettings ++ Seq( + releaseDirectory <<= crossTarget / "release" + ) + + lazy val commandSettings = Seq( + commands += buildReleaseCommand + ) + + def buildReleaseCommand = Command.command("build-release") { state => + val extracted = Project.extract(state) + val release = extracted.get(releaseDirectory) + val releaseVersion = extracted.get(version) + val projectRef = extracted.get(thisProjectRef) + val repo = extracted.get(Publish.defaultPublishTo) + val state1 = extracted.runAggregated(publish in projectRef, state) + val (state2, api) = extracted.runTask(Unidoc.unidoc, state1) + val (state3, docs) = extracted.runTask(Rstdoc.rstdoc, state2) + IO.delete(release) + IO.createDirectory(release) + IO.copyDirectory(repo, release / "releases") + IO.copyDirectory(api, release / "api" / "akka" / releaseVersion) + IO.copyDirectory(docs, release / "docs" / "akka" / releaseVersion) + state3 + } +} diff --git a/project/Rstdoc.scala b/project/Rstdoc.scala new file mode 100644 index 0000000000..fb38f756ac --- /dev/null +++ b/project/Rstdoc.scala @@ -0,0 +1,34 @@ +package akka + +import sbt._ +import sbt.Keys._ +import java.io.File + +object Rstdoc { + val rstdocDirectory = SettingKey[File]("rstdoc-directory") + val rstdocTarget = SettingKey[File]("rstdoc-target") + val rstdoc = TaskKey[File]("rstdoc", "Build the reStructuredText documentation.") + + lazy val settings = Seq( + rstdocDirectory <<= baseDirectory / "akka-docs", + rstdocTarget <<= crossTarget / "rstdoc", + rstdoc <<= rstdocTask + ) + + def rstdocTask = (rstdocDirectory, rstdocTarget, streams) map { + (dir, target, s) => { + s.log.info("Building reStructuredText documentation...") + val logger = new ProcessLogger { + def info(o: => String): Unit = s.log.debug(o) + def error(e: => String): Unit = s.log.debug(e) + def buffer[T](f: => T): T = f + } + val exitCode = Process(List("make", "clean", "html", "pdf"), dir) ! logger + if (exitCode != 0) sys.error("Failed to build docs.") + s.log.info("Creating reStructuredText documentation successful.") + IO.copyDirectory(dir / "_build" / "html", target) + IO.copyFile(dir / "_build" / "latex" / "Akka.pdf", target / "Akka.pdf") + target + } + } +} diff --git a/project/Unidoc.scala b/project/Unidoc.scala index 7fffd98a27..209fda53c7 100644 --- a/project/Unidoc.scala +++ b/project/Unidoc.scala @@ -1,8 +1,8 @@ package akka import sbt._ -import Keys._ -import Project.Initialize +import sbt.Keys._ +import sbt.Project.Initialize object Unidoc { val unidocDirectory = SettingKey[File]("unidoc-directory") diff --git a/project/scripts/find-replace b/project/scripts/find-replace new file mode 100755 index 0000000000..d0b6035032 --- /dev/null +++ b/project/scripts/find-replace @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# +# Find and replace across all source files. +# This script will be called as part of the release script. + +# get the source location for this script; handles symlinks +function get_script_path { + local source="${BASH_SOURCE[0]}" + while [ -h "${source}" ] ; do + source="$(readlink "${source}")"; + done + echo ${source} +} + +# path, name, and dir for this script +declare -r script_path=$(get_script_path) +declare -r script_name=$(basename "${script_path}") +declare -r script_dir="$(cd -P "$(dirname "${script_path}")" && pwd)" + +# print usage info +function usage { + echo "Usage: ${script_name} find_expr replace_expr" +} + +function echolog { + echo "[${script_name}] $@" +} + +declare -r find_expr=$1 +declare -r replace_expr=$2 + +if [ -z "$find_expr" ]; then + usage + exit 1 +fi + +echolog "$find_expr --> $replace_expr" + +# exclude directories from search + +declare exclude_dirs=".git dist deploy embedded-repo lib_managed project/boot project/scripts src_managed target" + +echolog "excluding directories: $exclude_dirs" + +exclude_opts="\(" +op="-path" +for dir in $exclude_dirs; do + exclude_opts="${exclude_opts} ${op} '*/${dir}/*'" + op="-or -path" +done +exclude_opts="${exclude_opts} \) -prune -o" + +# replace in files + +search="find . ${exclude_opts} -type f -print0 | xargs -0 grep -Il \"$find_expr\"" + +files=$(eval "$search") + +simple_diff="diff --old-line-format='[$script_name] - %l +' --new-line-format='[$script_name] + %l +' --changed-group-format='%<%>' --unchanged-group-format=''" + +for file in $files; do + echolog $file + # escape / for sed + sedfind=$(echo $find_expr | sed 's/\//\\\//g') + sedreplace=$(echo $replace_expr | sed 's/\//\\\//g') + sed -i '.sed' "s/${sedfind}/${sedreplace}/g" $file + eval "$simple_diff $file.sed $file" + rm -f $file.sed +done + +# replace in file names + +search="find . ${exclude_opts} -type f -name \"*${find_expr}*\" -print" + +files=$(eval "$search") + +for file in $files; do + dir=$(dirname $file) + name=$(basename $file) + newname=$(echo $name | sed "s/${find_expr}/${replace_expr}/g") + echolog "$file --> $newname" + mv $file $dir/$newname +done diff --git a/project/scripts/find-replace.sh b/project/scripts/find-replace.sh deleted file mode 100644 index fc21a8aa9f..0000000000 --- a/project/scripts/find-replace.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -# Find and replace across all source files. -# -# Example usage: -# -# sh project/scripts/find-replace.sh 1.1-SNAPSHOT 1.1-RC1 -# -# This script will be called as part of the sbt release script. - -FIND=$1 -REPLACE=$2 - -if [ -z "$FIND" ]; then - echo "Usage: find-replace.sh FIND REPLACE" - exit 1 -fi - -echo -echo "Find and replace: $FIND --> $REPLACE" - - -# Exclude directories from search - -excludedirs=".git dist deploy embedded-repo lib_managed project/boot project/scripts src_managed target" - -echo "Excluding directories: $excludedirs" - -excludeopts="\(" -op="-path" -for dir in $excludedirs; do - excludeopts="${excludeopts} ${op} '*/${dir}/*'" - op="-or -path" -done -excludeopts="${excludeopts} \) -prune -o" - - -# Replace in files - -search="find . ${excludeopts} -type f -print0 | xargs -0 grep -Il \"${FIND}\"" - -echo $search -echo - -files=$(eval "$search") - -simplediff="diff --old-line-format='- %l -' --new-line-format='+ %l -' --changed-group-format='%<%>' --unchanged-group-format=''" - -for file in $files; do - echo - echo $file - # escape / for sed - sedfind=$(echo $FIND | sed 's/\//\\\//g') - sedreplace=$(echo $REPLACE | sed 's/\//\\\//g') - sed -i '.sed' "s/${sedfind}/${sedreplace}/g" $file - eval "$simplediff $file.sed $file" - rm -f $file.sed -done - -echo - - -# Replace in file names - -search="find . ${excludeopts} -type f -name \"*${FIND}*\" -print" - -echo $search -echo - -files=$(eval "$search") - -for file in $files; do - dir=$(dirname $file) - name=$(basename $file) - newname=$(echo $name | sed "s/${FIND}/${REPLACE}/g") - echo "$file --> $newname" - mv $file $dir/$newname -done - -echo diff --git a/project/scripts/push-release.sh b/project/scripts/push-release.sh deleted file mode 100644 index c58282144f..0000000000 --- a/project/scripts/push-release.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -VERSION=$1 - -if [ -z "$VERSION" ]; then - echo "Usage: push-release.sh VERSION" - exit 1 -fi - -source ~/.akka-release - -if [ -z "$AKKA_RELEASE_SERVER" ]; then - echo "Need AKKA_RELEASE_SERVER to be specified" - exit 1 -fi - -if [ -z "$AKKA_RELEASE_PATH" ]; then - echo "Need AKKA_RELEASE_PATH to be specified" - exit 1 -fi - -ref=$(git symbolic-ref HEAD 2> /dev/null) -branch=${ref#refs/heads/} - -git push origin $branch -git push origin --tags - -release="target/release/${VERSION}" -tmp="/tmp/akka-release-${VERSION}" - -rsync -avz ${release}/ ${AKKA_RELEASE_SERVER}:${tmp}/ -echo "Verify sudo on $AKKA_RELEASE_SERVER" -ssh -t ${AKKA_RELEASE_SERVER} sudo -v -ssh -t ${AKKA_RELEASE_SERVER} sudo rsync -rpt ${tmp}/ ${AKKA_RELEASE_PATH} -ssh -t ${AKKA_RELEASE_SERVER} rm -rf ${tmp} diff --git a/project/scripts/release b/project/scripts/release old mode 100644 new mode 100755 index 847e8c350a..1870102161 --- a/project/scripts/release +++ b/project/scripts/release @@ -1,10 +1,234 @@ -sh git checkout -b releasing-{{release.arg1}} -set akka.release true -clean -script find-replace.sh {{project.version}} {{release.arg1}} -script find-replace.sh //[[:space:]]*release:[[:space:]]* -reload -build-release -sh git add . -sh git commit -am 'Update version for release {{project.version}}' -sh git tag -m 'Version {{project.version}}' v{{project.version}} +#!/usr/bin/env bash +# +# Release script for Akka. + +# defaults +declare -r default_server="akka.io" +declare -r default_path="/akka/www" + +# settings +declare -r release_dir="target/release" +declare release_server=${default_server} +declare release_path=${default_path} + +# flags +unset run_tests + +# get the source location for this script; handles symlinks +function get_script_path { + local source="${BASH_SOURCE[0]}" + while [ -h "${source}" ] ; do + source="$(readlink "${source}")"; + done + echo ${source} +} + +# path, name, and dir for this script +declare -r script_path=$(get_script_path) +declare -r script_name=$(basename "${script_path}") +declare -r script_dir="$(cd -P "$(dirname "${script_path}")" && pwd)" + +# print usage info +function usage { + cat <&2 +} + +# fail the script with an error message +function fail { + echoerr "$@" + exit 1 +} + +# process options and set flags +while true; do + case "$1" in + -h | --help ) usage; exit 1 ;; + -t | --run-tests ) run_tests=true; shift ;; + -s | --server ) release_server=$2; shift 2 ;; + -p | --path ) release_path=$2; shift 2 ;; + * ) break ;; + esac +done + +if [ $# != "1" ]; then + usage + fail "A release version must be specified" +fi + +declare -r version=$1 +declare -r publish_path="${release_server}:${release_path}" + +# check for a git command +type -P git &> /dev/null || fail "git command not found" + +# check for an sbt command +type -P sbt &> /dev/null || fail "sbt command not found" + +# get the current git branch +function get_current_branch { + local ref=$(git symbolic-ref HEAD 2> /dev/null) + local branch=${ref#refs/heads/} + echo "${branch}" +} + +# get the current project version from sbt +# a little messy as the ansi escape codes are included +function get_current_version { + local result=$(sbt version | tail -1 | cut -f2) + # remove ansi escape code from end + local code0=$(echo -e "\033[0m") + echo ${result%$code0} +} + +# store the current git branch for cleaning up +declare -r initial_branch=$(get_current_branch) + +# check we have an initial branch +[[ "${initial_branch}" ]] || fail "Not on a git branch" + +# check that we have a clean status +[[ -z "$(git status --porcelain)" ]] || { + git status + fail "There are uncommitted changes - please commit before releasing" +} + +# the branch we'll release on +declare -r release_branch="releasing-${version}" + +# try to run a cleanup command - these shouldn't actually fail +function safely { + "$@" || fail "Failed to clean up release - please check current state" +} + +# perform a clean up when a failure has occurred +function git_cleanup { + echoerr "Cleaning up..." + local branch=$(get_current_branch) + safely git reset --hard + safely git clean -f + if [ "${branch}" == "${release_branch}" ]; then + safely git checkout ${initial_branch} + safely git branch -d ${release_branch} + local tags=$(git tag -l) + [[ "${tags}" == *v${version}* ]] && safely git tag -d v${version} + fi + echoerr "Cleaned up failed release" +} + +# clean up and fail the script with an error message +function bail_out { + echoerr "Bailing out!" + git_cleanup + fail "$@" +} + +# bail out for signals +function signal_bail_out { + echoerr "Interrupted by signal" + bail_out "Received signal to stop release" +} + +# bail out on signals +trap signal_bail_out SIGHUP SIGINT SIGTERM + +# try to run a command or otherwise bail out +function try { + "$@" || bail_out "Failed to create release" +} + +echolog "Creating release ${version} ..." +echolog "Publishing to ${publish_path}" +[[ $run_tests ]] && echolog "All tests will be run" + +# try ssh'ing to the release server +echolog "Checking ssh connection to ${release_server}" +try ssh -t ${release_server} echo "Successfully contacted release server." + +echolog "Getting current project version from sbt..." +declare -r current_version=$(get_current_version) +echolog "Current version is ${current_version}" + +# check out a release branch +try git checkout -b ${release_branch} + +# find and replace the version +try ${script_dir}/find-replace ${current_version} ${version} + +# start clean +try sbt clean + +# run the tests if specified +if [ $run_tests ]; then + echolog "Running all tests..." + try sbt test + echolog "All tests are green" +fi + +# build the release +echolog "Building the release..." +try sbt build-release +echolog "Successfully created local release" + +# commit and tag this release +echolog "Committing and tagging..." +try git add . +try git commit -am "Update version for release ${version}" +try git tag -m "Version ${version}" v${version} + +# the point of no return... we're now pushing out to servers + +# use a special failure from now on +function arrgh { + cat 1>&2 <