+++ /dev/null
-#!/bin/bash
-
-store_packages() {
- local context="$1"
- local builddir="$2"
-
- local package
-
- while read -r package; do
- if ! foundry_context_add_file "$context" "build" "$package"; then
- log_error "Could not store artifact $package in $context"
- return 1
- fi
- done < <(find "$builddir" -type f -name "*.deb")
-
- return 0
-}
-
-increase_version() {
- local verrel="$1"
-
- local version
- local unixtime
-
- version="${verrel%-*}"
-
- if ! unixtime=$(date +"%s"); then
- return 1
- fi
-
- echo "$version-$unixtime"
- return 0
-}
-
-make_changelog_entry() {
- local package="$1"
- local version="$2"
- local branch="$3"
- local ref="$4"
-
- local datetime
-
- if ! datetime=$(date -R); then
- return 1
- fi
-
- cat <<EOF
-$package ($version) unstable; urgency=medium
-
- * Automatic build from $branch branch [$ref]
-
- -- Build Bot <buildbot@m10k.eu> $datetime
-EOF
-
- return 0
-}
-
-prepend_changelog() {
- local changelog="$1"
- local branch="$2"
- local ref="$3"
-
- local package
- local prev_version
- local next_version
- local prev_changelog
- local updated_changelog
-
- if ! prev_changelog=$(< "$changelog"); then
- log_error "Could not read changelog"
-
- elif ! package=$(grep -m 1 -oP '^\K[^ ]+' <<< "$prev_changelog"); then
- log_error "Could not parse package name from changelog"
-
- elif ! prev_version=$(grep -m 1 -oP '^[^ ]+ \(\K[^\)]+' <<< "$prev_changelog"); then
- log_error "Could not parse previous version from changelog"
-
- elif ! next_version=$(increase_version "$prev_version"); then
- log_error "Could not increase version"
-
- elif ! updated_changelog=$(make_changelog_entry "$package" \
- "$next_version" \
- "$branch" \
- "$ref"); then
- log_error "Could not make changlog entry"
-
- elif ! printf '%s\n\n\n%s\n' "$updated_changelog" \
- "$prev_changelog" > "$changelog"; then
- log_error "Could not write to changelog"
-
- else
- return 0
- fi
-
- return 1
-}
-
-build() {
- local context="$1"
- local repository="$2"
- local branch="$3"
- local ref="$4"
- local builddir="$5"
-
- local output
- local err
-
- err=0
-
- if ! output=$(git clone "$repository" "$builddir/sources" 2>&1) ||
- ! output+=$(cd "$builddir/sources" 2>&1 && git checkout "$branch" 2>&1); then
- err=1
- fi
-
- if ! foundry_context_log "$context" "build" <<< "$output"; then
- log_error "Could not log to $context"
- return 1
- fi
-
- if (( err != 0 )); then
- return 1
- fi
-
- if [[ "$branch" == "unstable" ]]; then
- if ! prepend_changelog "$builddir/sources/debian/changelog" \
- "$branch" \
- "$ref"; then
- log_error "Could not add entry to $builddir/sources/debian/changelog"
- return 1
- fi
- fi
-
- if ! output=$(cd "$builddir/sources" && dpkg-buildpackage -us -uc 2>&1); then
- err=1
- fi
-
- if ! foundry_context_log "$context" "build" <<< "$output"; then
- log_error "Could not log to $context"
- return 1
- fi
-
- if (( err != 0 )); then
- return 1
- fi
-
- if ! store_packages "$context" "$builddir"; then
- log_error "Could not store packages for $context"
- return 1
- fi
-
- return 0
-}
-
-send_build_notification() {
- local endpoint="$1"
- local topic="$2"
- local context="$3"
- local repository="$4"
- local branch="$5"
- local ref="$6"
- local result="$7"
-
- local buildmsg
- local artifacts
-
- artifacts=()
-
- if ! buildmsg=$(foundry_msg_build_new "$context" \
- "$repository" \
- "$branch" \
- "$ref" \
- "$result" \
- artifacts); then
- log_error "Could not make build message"
- return 1
- fi
-
- log_info "Sending build message to $topic"
- if ! ipc_endpoint_publish "$endpoint" "$topic" "$buildmsg"; then
- log_error "Could not publish message on $endpoint to $topic"
- return 1
- fi
-
- return 0
-}
-
-handle_commit_message() {
- local endpoint="$1"
- local publish_to="$2"
- local commit="$3"
-
- local buildable_branches
- local repository
- local branch
- local ref
- local context_name
- local context
- local builddir
- local -i result
- local -i err
-
- buildable_branches=(
- "master"
- "stable"
- "testing"
- "unstable"
- )
- result=0
- err=0
-
- if ! branch=$(foundry_msg_commit_get_branch "$commit"); then
- log_warn "No branch in commit message"
- return 1
- fi
-
- if ! array_contains "$branch" "${buildable_branches[@]}"; then
- log_warn "Refusing to build from $branch branch"
- return 0
- fi
-
- if ! repository=$(foundry_msg_commit_get_repository "$commit"); then
- log_warn "No repository in commit message"
- return 1
- fi
-
- if ! ref=$(foundry_msg_commit_get_ref "$commit"); then
- log_warn "No ref in commit message"
- return 1
- fi
-
- context_name="${repository##*/}"
-
- if ! context=$(foundry_context_new "$context_name"); then
- log_error "Could not create a context for $context_name"
- return 1
- fi
-
- inst_set_status "Building $context"
-
- if ! builddir=$(mktemp -d); then
- log_error "Could not make temporary build directory"
- return 1
- fi
-
- log_info "Building $context in $builddir"
- if ! build "$context" "$repository" "$branch" "$ref" "$builddir"; then
- result=1
- fi
-
- log_info "Finished build of $context with status $result"
-
- if ! send_build_notification "$endpoint" "$publish_to" "$context" \
- "$repository" "$branch" "$ref" "$result"; then
- err=1
- fi
-
- if ! rm -rf "$builddir"; then
- log_warn "Could not remove temporary build directory $builddir"
- fi
-
- return "$err"
-}
-
-dispatch_tasks() {
- local endpoint_name="$1"
- local watch="$2"
- local publish_to="$3"
-
- local endpoint
-
- if ! endpoint=$(ipc_endpoint_open "$endpoint_name"); then
- log_error "Could not open endpoint $endpoint_name"
- return 1
- fi
-
- if ! ipc_endpoint_subscribe "$endpoint" "$watch"; then
- log_error "Could not subscribe to $watch"
- return 1
- fi
-
- while inst_running; do
- local msg
- local data
- local msgtype
-
- inst_set_status "Awaiting commit messages"
-
- if ! msg=$(ipc_endpoint_recv "$endpoint" 5); then
- continue
- fi
-
- if ! data=$(ipc_msg_get_data "$msg"); then
- log_warn "Dropping malformed message"
- continue
- fi
-
- if ! msgtype=$(foundry_msg_get_type "$data") ||
- [[ "$msgtype" != "commit" ]]; then
- log_warn "Dropping message with unexpected type"
- continue
- fi
-
- inst_set_status "Handling commit message"
-
- handle_commit_message "$endpoint" "$publish_to" "$data"
- done
-
- return 0
-}
-
-main() {
- local endpoint
- local watch
- local publish_to
-
- opt_add_arg "e" "endpoint" "v" "pub/buildbot" "The IPC endpoint to listen on"
- opt_add_arg "w" "watch" "v" "commits" "The topic to watch for commit messages"
- opt_add_arg "p" "publish-to" "v" "builds" "The topic to publish builds under"
-
- if ! opt_parse "$@"; then
- return 1
- fi
-
- endpoint=$(opt_get "endpoint")
- watch=$(opt_get "watch")
- publish_to=$(opt_get "publish-to")
-
- if ! inst_start dispatch_tasks "$endpoint" "$watch" "$publish_to"; then
- return 1
- fi
-
- return 0
-}
-
-{
- if ! . toolbox.sh; then
- exit 1
- fi
-
- if ! include "log" "opt" "inst" "ipc" "foundry/msg" "foundry/context"; then
- exit 1
- fi
-
- main "$@"
- exit "$?"
-}
+++ /dev/null
-#!/bin/bash
-
-make_repo_config() {
- local domain="$1"
- local codename="$2"
- local architectures="$3"
- local gpgkeyid="$4"
- local description="$5"
-
- echo "Origin: $domain"
- echo "Label: $domain"
- echo "Codename: $codename"
- echo "Architectures: $architectures"
- echo "Components: main"
- echo "Description: $description"
- echo "SignWith: $gpgkeyid"
-
- return 0
-}
-
-repo_init() {
- local repo="$1"
- local domain="$2"
- local codename="$3"
- local arch="$4"
- local gpgkeyid="$5"
- local description="$6"
-
- local config
-
- if ! mkdir -p "$repo/conf" "$repo/incoming" "$repo/failed" &>/dev/null; then
- log_error "Could not create directory structure in $repo"
- return 1
- fi
-
- config=$(make_repo_config "$domain" "$codename" "$arch" \
- "$gpgkeyid" "$description")
-
- if ! echo "$config" > "$repo/conf/distributions"; then
- return 1
- fi
-
- return 0
-}
-
-repo_add_package() {
- local repository="$1"
- local codename="$2"
- local package="$3"
-
- log_info "Adding $package to $repository:$codename"
-
- if ! reprepro -b "$repository" includedeb \
- "$codename" "$package"; then
- return 1
- fi
-
- return 0
-}
-
-verify_package() {
- local package="$1"
-
- log_info "Verifying signature on $package"
- if ! dpkg-sig --verify "$package" | log_info "dpkg-sig --verify \"$package\""; then
- log_error "Could not verify signature on $package"
-
- return 1
- fi
-
- log_info "Good signature on $package"
-
- return 0
-}
-
-process_new_package() {
- local context="$1"
- local package="$2"
- local repo="$3"
- local codename="$4"
-
- local failed
- local logoutput
-
- failed=true
-
- log_info "[#$context] New package: $package"
-
- if ! logoutput=$(verify_package "$package" 2>&1); then
- log_error "[#$context] Invalid signature on package $package"
- elif ! logoutput+=$(repo_add_package "$repo" "$codename" "$package" 2>&1); then
- log_error "[#$context] Could not process $package"
- else
- log_info "[#$context] $package successfully added to $repo:$codename"
- failed=false
- fi
-
- if "$failed"; then
- if ! log_output+=$(mv "$package" "$repo/failed/." 2>&1); then
- log_error "[#$context] Could not move $package to $repo/failed/."
- fi
- else
- if ! log_output+=$(rm "$package" 2>&1); then
- log_error "[#$context] Could not remove $package"
- fi
- fi
-
- if ! foundry_context_log "$context" "dist" <<< "$logoutput"; then
- log_error "Could not log to dist log of $context"
- return 1
- fi
-
- return 0
-}
-
-publish_result() {
- local endpoint="$1"
- local publish_to="$2"
- local repository="$3"
- local branch="$4"
- local ref="$5"
- local distribution="$6"
- local artifacts=("${@:7}")
-
- local message
-
- if ! message=$(foundry_msg_dist_new "$repository" \
- "$branch" \
- "$ref" \
- "$distribution" \
- "${artifacts[@]}"); then
- log_error "Could not make dist message"
- return 1
- fi
-
- if ! ipc_endpoint_publish "$endpoint" "$publish_to" "$message"; then
- log_error "Could not publish message to $publish_to"
- return 1
- fi
-
- return 0
-}
-
-process_sign_message() {
- local repo="$1"
- local codename="$2"
- local signmsg="$3"
- local endpoint="$4"
- local publish_to="$5"
-
- local artifacts
- local artifact
- local context
- local repository
- local branch
- local ref
- local distributed
-
- distributed=()
-
- if ! repository=$(foundry_msg_sign_get_repository "$signmsg") ||
- ! branch=$(foundry_msg_sign_get_branch "$signmsg") ||
- ! ref=$(foundry_msg_sign_get_ref "$signmsg") ||
- ! context=$(foundry_msg_sign_get_context "$signmsg"); then
- log_warn "Dropping malformed message"
- return 1
- fi
-
- if [[ "$branch" == "unstable" ]]; then
- codename="unstable"
- fi
-
- readarray -t artifacts < <(foundry_context_get_files "$context" "signed")
-
- for artifact in "${artifacts[@]}"; do
- local artifact_name
- local extension
-
- artifact_name="${artifact##*/}"
- extension="${artifact_name##*.}"
-
- if [[ "$extension" != "deb" ]]; then
- log_debug "Skipping non-deb artifact $artifact_name"
- continue
- fi
-
- if process_new_package "$context" "$artifact" "$repo" "$codename"; then
- distributed+=("$artifact_name")
- else
- log_error "Could not distribute $artifact_name"
- fi
- done
-
- if (( ${#distributed[@]} == 0 )); then
- log_error "No artifacts distributed"
- return 1
- fi
-
- if ! publish_result "$endpoint" "$publish_to" "$repository" "$branch" \
- "$ref" "$repo" "${distributed[@]}"; then
- log_error "Failed to publish results for $context"
- return 1
- fi
-
- return 0
-}
-
-watch_new_packages() {
- local endpoint_name="$1"
- local watch="$2"
- local publish_to="$3"
- local repo="$4"
- local codename="$5"
-
- local endpoint
-
- if ! endpoint=$(ipc_endpoint_open "$endpoint_name"); then
- log_error "Could not listen on IPC endpoint $endpoint_name"
- return 1
- fi
-
- if ! ipc_endpoint_subscribe "$endpoint" "$watch"; then
- log_error "Could not subscribe to $watch"
- return 1
- fi
-
- while inst_running; do
- local msg
- local signmsg
- local msgtype
-
- inst_set_status "Waiting for sign messages"
-
- if ! msg=$(ipc_endpoint_recv "$endpoint" 5); then
- continue
- fi
-
- if ! signmsg=$(ipc_msg_get_data "$msg"); then
- log_warn "Dropping message without data"
- continue
- fi
-
- if ! msgtype=$(foundry_msg_get_type "$signmsg"); then
- log_warn "Dropping message without type"
- continue
- fi
-
- if [[ "$msgtype" != "sign" ]]; then
- log_warn "Dropping message with unexpected type $msgtype"
- continue
- fi
-
- process_sign_message "$repo" "$codename" "$signmsg" \
- "$endpoint" "$publish_to"
- done
-
- return 0
-}
-
-looks_like_a_repository() {
- local path="$1"
-
- if ! [ -d "$path" ]; then
- return 1
- fi
-
- if ! [ -d "$path/incoming" ]; then
- return 1
- fi
-
- return 0
-}
-
-main() {
- local path
- local codename
- local endpoint
- local watch
- local publish_to
- local name
- local arch
- local gpgkey
- local desc
-
- opt_add_arg "e" "endpoint" "v" "pub/distbot" "The IPC endpoint to listen on"
- opt_add_arg "w" "watch" "v" "signs" \
- "The topic to watch for sign messages"
- opt_add_arg "p" "publish-to" "v" "dists" \
- "The topic to publish dist messages under"
-
- opt_add_arg "n" "name" "rv" "" "The name of the repository"
- opt_add_arg "o" "output" "rv" "" "The path to the repository"
- opt_add_arg "c" "codename" "v" "stable" \
- "The codename of the distribution (default: stable)"
- opt_add_arg "a" "arch" "rv" "" \
- "Comma separated list of supported architectures"
- opt_add_arg "k" "gpg-key" "rv" "" \
- "The GPG key used for signing"
- opt_add_arg "d" "description" "rv" "" \
- "Description of the repository"
-
- if ! opt_parse "$@"; then
- return 1
- fi
-
- path=$(opt_get "output")
- codename=$(opt_get "codename")
- endpoint=$(opt_get "endpoint")
- watch=$(opt_get "watch")
- publish_to=$(opt_get "publish-to")
- name=$(opt_get "name")
- arch=$(opt_get "arch")
- gpgkey=$(opt_get "gpg-key")
- desc=$(opt_get "description")
-
- if ! looks_like_a_repository "$path"; then
- # Create new repository
- log_info "Initializing repository $name:$codename in $path"
-
- if ! repo_init "$path" "$name" "$codename" "$arch" "$gpgkey" "$desc"; then
- log_error "Could not initialize repository"
- return 1
- fi
- fi
-
- inst_start watch_new_packages "$endpoint" "$watch" "$publish_to" "$path" "$codename"
-
- return 0
-}
-
-{
- if ! . toolbox.sh; then
- exit 1
- fi
-
- if ! include "log" "opt" "queue" "inst" "ipc" "foundry/msg" "foundry/context"; then
- exit 1
- fi
-
- main "$@"
- exit "$?"
-}
+++ /dev/null
-#!/bin/bash
-
-publish_results() {
- local endpoint="$1"
- local topic="$2"
- local key="$3"
- local context="$4"
- local repository="$5"
- local branch="$6"
- local ref="$7"
-
- local sign
-
- if ! sign=$(foundry_msg_sign_new "$context" \
- "$key" \
- "$repository" \
- "$branch" \
- "$ref"); then
- log_error "Could not make sign message"
- return 1
- fi
-
- if ! ipc_endpoint_publish "$endpoint" "$topic" "$sign"; then
- log_error "Could not publish sign message"
- return 1
- fi
-
- return 0
-}
-
-handle_build_message() {
- local endpoint="$1"
- local publish_to="$2"
- local buildmsg="$3"
- local signer_key="$4"
-
- local repository
- local branch
- local ref
- local build_context
- local context_name
- local context
- local artifact
- local signlog
- local result
-
- if ! result=$(foundry_msg_build_get_result "$buildmsg") ||
- ! repository=$(foundry_msg_build_get_repository "$buildmsg") ||
- ! branch=$(foundry_msg_build_get_branch "$buildmsg") ||
- ! ref=$(foundry_msg_build_get_ref "$buildmsg") ||
- ! build_context=$(foundry_msg_build_get_context "$buildmsg"); then
- log_warn "Malformed build message. Dropping."
- return 1
- fi
-
- if ! is_digits "$result" ||
- (( result != 0 )); then
- log_warn "Not signing $repository#$branch [$ref] (build result was $result)"
- return 1
- fi
-
- context_name="${repository##*/}"
-
- if ! context=$(foundry_context_new "$context_name"); then
- log_error "Could not make new context for $context_name"
- return 1
- fi
-
- inst_set_status "Signing $context"
-
- signlog=""
- result=0
-
- while read -r artifact; do
- if [[ "$artifact" != *".deb" ]]; then
- continue
- fi
-
- if ! signlog+=$(dpkg-sig --sign "builder" \
- -k "$signer_key" \
- "$artifact" 2>&1); then
- log_error "Could not sign $artifact with key $signer_key"
- result=1
-
- elif ! signlog+=$(foundry_context_add_file "$context" \
- "signed" \
- "$artifact" 2>&1); then
- log_error "Could not add $artifact to context $context"
- result=1
- fi
- done < <(foundry_context_get_files "$build_context" "build")
-
- if ! foundry_context_log "$context" "sign" <<< "$signlog"; then
- log_error "Could not log to context $context"
- result=1
- fi
-
- if (( result == 0 )); then
- if ! publish_results "$endpoint" "$publish_to" \
- "$signer_key" "$context" \
- "$repository" "$branch" \
- "$ref"; then
- log_error "Could not publish results to $publish_to"
- result=1
- fi
- else
- if ! publish_results "$endpoint" "signbot_errors" \
- "$signer_key" "$context" \
- "$repository" "$branch" \
- "$ref"; then
- log_error "Could not send error to signbot_errors"
- fi
- fi
-
- return "$result"
-}
-
-dispatch_tasks() {
- local endpoint_name="$1"
- local watch="$2"
- local publish_to="$3"
- local signer_key="$4"
-
- local endpoint
-
- if ! endpoint=$(ipc_endpoint_open "$endpoint_name"); then
- log_error "Could not open IPC endpoint $endpoint_name"
- return 1
- fi
-
- if ! ipc_endpoint_subscribe "$endpoint" "$watch"; then
- log_error "Could not subscribe to $watch"
- return 1
- fi
-
- while inst_running; do
- local msg
- local data
- local msgtype
-
- inst_set_status "Watching for build messages"
-
- if ! msg=$(ipc_endpoint_recv "$endpoint" 5); then
- continue
- fi
-
- if ! data=$(ipc_msg_get_data "$msg"); then
- log_warn "Received message without data. Dropping."
- continue
- fi
-
- if ! msgtype=$(foundry_msg_get_type "$data") ||
- [[ "$msgtype" != "build" ]]; then
- log_warn "Received message with unexpected type. Dropping."
- continue
- fi
-
- inst_set_status "Handling build message"
- handle_build_message "$endpoint" "$publish_to" "$data" "$signer_key"
- done
-
- return 0
-}
-
-main() {
- local endpoint
- local watch
- local publish_to
- local key
-
- opt_add_arg "e" "endpoint" "v" "pub/signbot" "The IPC endpoint to listen on"
- opt_add_arg "w" "watch" "v" "builds" "The topic to watch for build messages"
- opt_add_arg "p" "publish-to" "v" "signs" "The topic to publish signs under"
- opt_add_arg "k" "gpg-key" "rv" "" "Fingerprint of the key to sign with"
-
- if ! opt_parse "$@"; then
- return 1
- fi
-
- endpoint=$(opt_get "endpoint")
- watch=$(opt_get "watch")
- publish_to=$(opt_get "publish-to")
- key=$(opt_get "gpg-key")
-
- if ! inst_start dispatch_tasks "$endpoint" "$watch" "$publish_to" "$key"; then
- return 1
- fi
-
- return 0
-}
-
-{
- if ! . toolbox.sh; then
- exit 1
- fi
-
- if ! include "is" "log" "opt" "inst" "ipc" "foundry/context" "foundry/msg"; then
- exit 1
- fi
-
- main "$@"
- exit "$?"
-}
--- /dev/null
+#!/bin/bash
+
+store_packages() {
+ local context="$1"
+ local builddir="$2"
+
+ local package
+
+ while read -r package; do
+ if ! foundry_context_add_file "$context" "build" "$package"; then
+ log_error "Could not store artifact $package in $context"
+ return 1
+ fi
+ done < <(find "$builddir" -type f -name "*.deb")
+
+ return 0
+}
+
+increase_version() {
+ local verrel="$1"
+
+ local version
+ local unixtime
+
+ version="${verrel%-*}"
+
+ if ! unixtime=$(date +"%s"); then
+ return 1
+ fi
+
+ echo "$version-$unixtime"
+ return 0
+}
+
+make_changelog_entry() {
+ local package="$1"
+ local version="$2"
+ local branch="$3"
+ local ref="$4"
+
+ local datetime
+
+ if ! datetime=$(date -R); then
+ return 1
+ fi
+
+ cat <<EOF
+$package ($version) unstable; urgency=medium
+
+ * Automatic build from $branch branch [$ref]
+
+ -- Build Bot <buildbot@m10k.eu> $datetime
+EOF
+
+ return 0
+}
+
+prepend_changelog() {
+ local changelog="$1"
+ local branch="$2"
+ local ref="$3"
+
+ local package
+ local prev_version
+ local next_version
+ local prev_changelog
+ local updated_changelog
+
+ if ! prev_changelog=$(< "$changelog"); then
+ log_error "Could not read changelog"
+
+ elif ! package=$(grep -m 1 -oP '^\K[^ ]+' <<< "$prev_changelog"); then
+ log_error "Could not parse package name from changelog"
+
+ elif ! prev_version=$(grep -m 1 -oP '^[^ ]+ \(\K[^\)]+' <<< "$prev_changelog"); then
+ log_error "Could not parse previous version from changelog"
+
+ elif ! next_version=$(increase_version "$prev_version"); then
+ log_error "Could not increase version"
+
+ elif ! updated_changelog=$(make_changelog_entry "$package" \
+ "$next_version" \
+ "$branch" \
+ "$ref"); then
+ log_error "Could not make changlog entry"
+
+ elif ! printf '%s\n\n\n%s\n' "$updated_changelog" \
+ "$prev_changelog" > "$changelog"; then
+ log_error "Could not write to changelog"
+
+ else
+ return 0
+ fi
+
+ return 1
+}
+
+build() {
+ local context="$1"
+ local repository="$2"
+ local branch="$3"
+ local ref="$4"
+ local builddir="$5"
+
+ local output
+ local err
+
+ err=0
+
+ if ! output=$(git clone "$repository" "$builddir/sources" 2>&1) ||
+ ! output+=$(cd "$builddir/sources" 2>&1 && git checkout "$branch" 2>&1); then
+ err=1
+ fi
+
+ if ! foundry_context_log "$context" "build" <<< "$output"; then
+ log_error "Could not log to $context"
+ return 1
+ fi
+
+ if (( err != 0 )); then
+ return 1
+ fi
+
+ if [[ "$branch" == "unstable" ]]; then
+ if ! prepend_changelog "$builddir/sources/debian/changelog" \
+ "$branch" \
+ "$ref"; then
+ log_error "Could not add entry to $builddir/sources/debian/changelog"
+ return 1
+ fi
+ fi
+
+ if ! output=$(cd "$builddir/sources" && dpkg-buildpackage -us -uc 2>&1); then
+ err=1
+ fi
+
+ if ! foundry_context_log "$context" "build" <<< "$output"; then
+ log_error "Could not log to $context"
+ return 1
+ fi
+
+ if (( err != 0 )); then
+ return 1
+ fi
+
+ if ! store_packages "$context" "$builddir"; then
+ log_error "Could not store packages for $context"
+ return 1
+ fi
+
+ return 0
+}
+
+send_build_notification() {
+ local endpoint="$1"
+ local topic="$2"
+ local context="$3"
+ local repository="$4"
+ local branch="$5"
+ local ref="$6"
+ local result="$7"
+
+ local buildmsg
+ local artifacts
+
+ artifacts=()
+
+ if ! buildmsg=$(foundry_msg_build_new "$context" \
+ "$repository" \
+ "$branch" \
+ "$ref" \
+ "$result" \
+ artifacts); then
+ log_error "Could not make build message"
+ return 1
+ fi
+
+ log_info "Sending build message to $topic"
+ if ! ipc_endpoint_publish "$endpoint" "$topic" "$buildmsg"; then
+ log_error "Could not publish message on $endpoint to $topic"
+ return 1
+ fi
+
+ return 0
+}
+
+handle_commit_message() {
+ local endpoint="$1"
+ local publish_to="$2"
+ local commit="$3"
+
+ local buildable_branches
+ local repository
+ local branch
+ local ref
+ local context_name
+ local context
+ local builddir
+ local -i result
+ local -i err
+
+ buildable_branches=(
+ "master"
+ "stable"
+ "testing"
+ "unstable"
+ )
+ result=0
+ err=0
+
+ if ! branch=$(foundry_msg_commit_get_branch "$commit"); then
+ log_warn "No branch in commit message"
+ return 1
+ fi
+
+ if ! array_contains "$branch" "${buildable_branches[@]}"; then
+ log_warn "Refusing to build from $branch branch"
+ return 0
+ fi
+
+ if ! repository=$(foundry_msg_commit_get_repository "$commit"); then
+ log_warn "No repository in commit message"
+ return 1
+ fi
+
+ if ! ref=$(foundry_msg_commit_get_ref "$commit"); then
+ log_warn "No ref in commit message"
+ return 1
+ fi
+
+ context_name="${repository##*/}"
+
+ if ! context=$(foundry_context_new "$context_name"); then
+ log_error "Could not create a context for $context_name"
+ return 1
+ fi
+
+ inst_set_status "Building $context"
+
+ if ! builddir=$(mktemp -d); then
+ log_error "Could not make temporary build directory"
+ return 1
+ fi
+
+ log_info "Building $context in $builddir"
+ if ! build "$context" "$repository" "$branch" "$ref" "$builddir"; then
+ result=1
+ fi
+
+ log_info "Finished build of $context with status $result"
+
+ if ! send_build_notification "$endpoint" "$publish_to" "$context" \
+ "$repository" "$branch" "$ref" "$result"; then
+ err=1
+ fi
+
+ if ! rm -rf "$builddir"; then
+ log_warn "Could not remove temporary build directory $builddir"
+ fi
+
+ return "$err"
+}
+
+dispatch_tasks() {
+ local endpoint_name="$1"
+ local watch="$2"
+ local publish_to="$3"
+
+ local endpoint
+
+ if ! endpoint=$(ipc_endpoint_open "$endpoint_name"); then
+ log_error "Could not open endpoint $endpoint_name"
+ return 1
+ fi
+
+ if ! ipc_endpoint_subscribe "$endpoint" "$watch"; then
+ log_error "Could not subscribe to $watch"
+ return 1
+ fi
+
+ while inst_running; do
+ local msg
+ local data
+ local msgtype
+
+ inst_set_status "Awaiting commit messages"
+
+ if ! msg=$(ipc_endpoint_recv "$endpoint" 5); then
+ continue
+ fi
+
+ if ! data=$(ipc_msg_get_data "$msg"); then
+ log_warn "Dropping malformed message"
+ continue
+ fi
+
+ if ! msgtype=$(foundry_msg_get_type "$data") ||
+ [[ "$msgtype" != "commit" ]]; then
+ log_warn "Dropping message with unexpected type"
+ continue
+ fi
+
+ inst_set_status "Handling commit message"
+
+ handle_commit_message "$endpoint" "$publish_to" "$data"
+ done
+
+ return 0
+}
+
+main() {
+ local endpoint
+ local watch
+ local publish_to
+
+ opt_add_arg "e" "endpoint" "v" "pub/buildbot" "The IPC endpoint to listen on"
+ opt_add_arg "w" "watch" "v" "commits" "The topic to watch for commit messages"
+ opt_add_arg "p" "publish-to" "v" "builds" "The topic to publish builds under"
+
+ if ! opt_parse "$@"; then
+ return 1
+ fi
+
+ endpoint=$(opt_get "endpoint")
+ watch=$(opt_get "watch")
+ publish_to=$(opt_get "publish-to")
+
+ if ! inst_start dispatch_tasks "$endpoint" "$watch" "$publish_to"; then
+ return 1
+ fi
+
+ return 0
+}
+
+{
+ if ! . toolbox.sh; then
+ exit 1
+ fi
+
+ if ! include "log" "opt" "inst" "ipc" "foundry/msg" "foundry/context"; then
+ exit 1
+ fi
+
+ main "$@"
+ exit "$?"
+}
--- /dev/null
+#!/bin/bash
+
+make_repo_config() {
+ local domain="$1"
+ local codename="$2"
+ local architectures="$3"
+ local gpgkeyid="$4"
+ local description="$5"
+
+ echo "Origin: $domain"
+ echo "Label: $domain"
+ echo "Codename: $codename"
+ echo "Architectures: $architectures"
+ echo "Components: main"
+ echo "Description: $description"
+ echo "SignWith: $gpgkeyid"
+
+ return 0
+}
+
+repo_init() {
+ local repo="$1"
+ local domain="$2"
+ local codename="$3"
+ local arch="$4"
+ local gpgkeyid="$5"
+ local description="$6"
+
+ local config
+
+ if ! mkdir -p "$repo/conf" "$repo/incoming" "$repo/failed" &>/dev/null; then
+ log_error "Could not create directory structure in $repo"
+ return 1
+ fi
+
+ config=$(make_repo_config "$domain" "$codename" "$arch" \
+ "$gpgkeyid" "$description")
+
+ if ! echo "$config" > "$repo/conf/distributions"; then
+ return 1
+ fi
+
+ return 0
+}
+
+repo_add_package() {
+ local repository="$1"
+ local codename="$2"
+ local package="$3"
+
+ log_info "Adding $package to $repository:$codename"
+
+ if ! reprepro -b "$repository" includedeb \
+ "$codename" "$package"; then
+ return 1
+ fi
+
+ return 0
+}
+
+verify_package() {
+ local package="$1"
+
+ log_info "Verifying signature on $package"
+ if ! dpkg-sig --verify "$package" | log_info "dpkg-sig --verify \"$package\""; then
+ log_error "Could not verify signature on $package"
+
+ return 1
+ fi
+
+ log_info "Good signature on $package"
+
+ return 0
+}
+
+process_new_package() {
+ local context="$1"
+ local package="$2"
+ local repo="$3"
+ local codename="$4"
+
+ local failed
+ local logoutput
+
+ failed=true
+
+ log_info "[#$context] New package: $package"
+
+ if ! logoutput=$(verify_package "$package" 2>&1); then
+ log_error "[#$context] Invalid signature on package $package"
+ elif ! logoutput+=$(repo_add_package "$repo" "$codename" "$package" 2>&1); then
+ log_error "[#$context] Could not process $package"
+ else
+ log_info "[#$context] $package successfully added to $repo:$codename"
+ failed=false
+ fi
+
+ if "$failed"; then
+ if ! log_output+=$(mv "$package" "$repo/failed/." 2>&1); then
+ log_error "[#$context] Could not move $package to $repo/failed/."
+ fi
+ else
+ if ! log_output+=$(rm "$package" 2>&1); then
+ log_error "[#$context] Could not remove $package"
+ fi
+ fi
+
+ if ! foundry_context_log "$context" "dist" <<< "$logoutput"; then
+ log_error "Could not log to dist log of $context"
+ return 1
+ fi
+
+ return 0
+}
+
+publish_result() {
+ local endpoint="$1"
+ local publish_to="$2"
+ local repository="$3"
+ local branch="$4"
+ local ref="$5"
+ local distribution="$6"
+ local artifacts=("${@:7}")
+
+ local message
+
+ if ! message=$(foundry_msg_dist_new "$repository" \
+ "$branch" \
+ "$ref" \
+ "$distribution" \
+ "${artifacts[@]}"); then
+ log_error "Could not make dist message"
+ return 1
+ fi
+
+ if ! ipc_endpoint_publish "$endpoint" "$publish_to" "$message"; then
+ log_error "Could not publish message to $publish_to"
+ return 1
+ fi
+
+ return 0
+}
+
+process_sign_message() {
+ local repo="$1"
+ local codename="$2"
+ local signmsg="$3"
+ local endpoint="$4"
+ local publish_to="$5"
+
+ local artifacts
+ local artifact
+ local context
+ local repository
+ local branch
+ local ref
+ local distributed
+
+ distributed=()
+
+ if ! repository=$(foundry_msg_sign_get_repository "$signmsg") ||
+ ! branch=$(foundry_msg_sign_get_branch "$signmsg") ||
+ ! ref=$(foundry_msg_sign_get_ref "$signmsg") ||
+ ! context=$(foundry_msg_sign_get_context "$signmsg"); then
+ log_warn "Dropping malformed message"
+ return 1
+ fi
+
+ if [[ "$branch" == "unstable" ]]; then
+ codename="unstable"
+ fi
+
+ readarray -t artifacts < <(foundry_context_get_files "$context" "signed")
+
+ for artifact in "${artifacts[@]}"; do
+ local artifact_name
+ local extension
+
+ artifact_name="${artifact##*/}"
+ extension="${artifact_name##*.}"
+
+ if [[ "$extension" != "deb" ]]; then
+ log_debug "Skipping non-deb artifact $artifact_name"
+ continue
+ fi
+
+ if process_new_package "$context" "$artifact" "$repo" "$codename"; then
+ distributed+=("$artifact_name")
+ else
+ log_error "Could not distribute $artifact_name"
+ fi
+ done
+
+ if (( ${#distributed[@]} == 0 )); then
+ log_error "No artifacts distributed"
+ return 1
+ fi
+
+ if ! publish_result "$endpoint" "$publish_to" "$repository" "$branch" \
+ "$ref" "$repo" "${distributed[@]}"; then
+ log_error "Failed to publish results for $context"
+ return 1
+ fi
+
+ return 0
+}
+
+watch_new_packages() {
+ local endpoint_name="$1"
+ local watch="$2"
+ local publish_to="$3"
+ local repo="$4"
+ local codename="$5"
+
+ local endpoint
+
+ if ! endpoint=$(ipc_endpoint_open "$endpoint_name"); then
+ log_error "Could not listen on IPC endpoint $endpoint_name"
+ return 1
+ fi
+
+ if ! ipc_endpoint_subscribe "$endpoint" "$watch"; then
+ log_error "Could not subscribe to $watch"
+ return 1
+ fi
+
+ while inst_running; do
+ local msg
+ local signmsg
+ local msgtype
+
+ inst_set_status "Waiting for sign messages"
+
+ if ! msg=$(ipc_endpoint_recv "$endpoint" 5); then
+ continue
+ fi
+
+ if ! signmsg=$(ipc_msg_get_data "$msg"); then
+ log_warn "Dropping message without data"
+ continue
+ fi
+
+ if ! msgtype=$(foundry_msg_get_type "$signmsg"); then
+ log_warn "Dropping message without type"
+ continue
+ fi
+
+ if [[ "$msgtype" != "sign" ]]; then
+ log_warn "Dropping message with unexpected type $msgtype"
+ continue
+ fi
+
+ process_sign_message "$repo" "$codename" "$signmsg" \
+ "$endpoint" "$publish_to"
+ done
+
+ return 0
+}
+
+looks_like_a_repository() {
+ local path="$1"
+
+ if ! [ -d "$path" ]; then
+ return 1
+ fi
+
+ if ! [ -d "$path/incoming" ]; then
+ return 1
+ fi
+
+ return 0
+}
+
+main() {
+ local path
+ local codename
+ local endpoint
+ local watch
+ local publish_to
+ local name
+ local arch
+ local gpgkey
+ local desc
+
+ opt_add_arg "e" "endpoint" "v" "pub/distbot" "The IPC endpoint to listen on"
+ opt_add_arg "w" "watch" "v" "signs" \
+ "The topic to watch for sign messages"
+ opt_add_arg "p" "publish-to" "v" "dists" \
+ "The topic to publish dist messages under"
+
+ opt_add_arg "n" "name" "rv" "" "The name of the repository"
+ opt_add_arg "o" "output" "rv" "" "The path to the repository"
+ opt_add_arg "c" "codename" "v" "stable" \
+ "The codename of the distribution (default: stable)"
+ opt_add_arg "a" "arch" "rv" "" \
+ "Comma separated list of supported architectures"
+ opt_add_arg "k" "gpg-key" "rv" "" \
+ "The GPG key used for signing"
+ opt_add_arg "d" "description" "rv" "" \
+ "Description of the repository"
+
+ if ! opt_parse "$@"; then
+ return 1
+ fi
+
+ path=$(opt_get "output")
+ codename=$(opt_get "codename")
+ endpoint=$(opt_get "endpoint")
+ watch=$(opt_get "watch")
+ publish_to=$(opt_get "publish-to")
+ name=$(opt_get "name")
+ arch=$(opt_get "arch")
+ gpgkey=$(opt_get "gpg-key")
+ desc=$(opt_get "description")
+
+ if ! looks_like_a_repository "$path"; then
+ # Create new repository
+ log_info "Initializing repository $name:$codename in $path"
+
+ if ! repo_init "$path" "$name" "$codename" "$arch" "$gpgkey" "$desc"; then
+ log_error "Could not initialize repository"
+ return 1
+ fi
+ fi
+
+ inst_start watch_new_packages "$endpoint" "$watch" "$publish_to" "$path" "$codename"
+
+ return 0
+}
+
+{
+ if ! . toolbox.sh; then
+ exit 1
+ fi
+
+ if ! include "log" "opt" "queue" "inst" "ipc" "foundry/msg" "foundry/context"; then
+ exit 1
+ fi
+
+ main "$@"
+ exit "$?"
+}
--- /dev/null
+#!/bin/bash
+
+publish_results() {
+ local endpoint="$1"
+ local topic="$2"
+ local key="$3"
+ local context="$4"
+ local repository="$5"
+ local branch="$6"
+ local ref="$7"
+
+ local sign
+
+ if ! sign=$(foundry_msg_sign_new "$context" \
+ "$key" \
+ "$repository" \
+ "$branch" \
+ "$ref"); then
+ log_error "Could not make sign message"
+ return 1
+ fi
+
+ if ! ipc_endpoint_publish "$endpoint" "$topic" "$sign"; then
+ log_error "Could not publish sign message"
+ return 1
+ fi
+
+ return 0
+}
+
+handle_build_message() {
+ local endpoint="$1"
+ local publish_to="$2"
+ local buildmsg="$3"
+ local signer_key="$4"
+
+ local repository
+ local branch
+ local ref
+ local build_context
+ local context_name
+ local context
+ local artifact
+ local signlog
+ local result
+
+ if ! result=$(foundry_msg_build_get_result "$buildmsg") ||
+ ! repository=$(foundry_msg_build_get_repository "$buildmsg") ||
+ ! branch=$(foundry_msg_build_get_branch "$buildmsg") ||
+ ! ref=$(foundry_msg_build_get_ref "$buildmsg") ||
+ ! build_context=$(foundry_msg_build_get_context "$buildmsg"); then
+ log_warn "Malformed build message. Dropping."
+ return 1
+ fi
+
+ if ! is_digits "$result" ||
+ (( result != 0 )); then
+ log_warn "Not signing $repository#$branch [$ref] (build result was $result)"
+ return 1
+ fi
+
+ context_name="${repository##*/}"
+
+ if ! context=$(foundry_context_new "$context_name"); then
+ log_error "Could not make new context for $context_name"
+ return 1
+ fi
+
+ inst_set_status "Signing $context"
+
+ signlog=""
+ result=0
+
+ while read -r artifact; do
+ if [[ "$artifact" != *".deb" ]]; then
+ continue
+ fi
+
+ if ! signlog+=$(dpkg-sig --sign "builder" \
+ -k "$signer_key" \
+ "$artifact" 2>&1); then
+ log_error "Could not sign $artifact with key $signer_key"
+ result=1
+
+ elif ! signlog+=$(foundry_context_add_file "$context" \
+ "signed" \
+ "$artifact" 2>&1); then
+ log_error "Could not add $artifact to context $context"
+ result=1
+ fi
+ done < <(foundry_context_get_files "$build_context" "build")
+
+ if ! foundry_context_log "$context" "sign" <<< "$signlog"; then
+ log_error "Could not log to context $context"
+ result=1
+ fi
+
+ if (( result == 0 )); then
+ if ! publish_results "$endpoint" "$publish_to" \
+ "$signer_key" "$context" \
+ "$repository" "$branch" \
+ "$ref"; then
+ log_error "Could not publish results to $publish_to"
+ result=1
+ fi
+ else
+ if ! publish_results "$endpoint" "signbot_errors" \
+ "$signer_key" "$context" \
+ "$repository" "$branch" \
+ "$ref"; then
+ log_error "Could not send error to signbot_errors"
+ fi
+ fi
+
+ return "$result"
+}
+
+dispatch_tasks() {
+ local endpoint_name="$1"
+ local watch="$2"
+ local publish_to="$3"
+ local signer_key="$4"
+
+ local endpoint
+
+ if ! endpoint=$(ipc_endpoint_open "$endpoint_name"); then
+ log_error "Could not open IPC endpoint $endpoint_name"
+ return 1
+ fi
+
+ if ! ipc_endpoint_subscribe "$endpoint" "$watch"; then
+ log_error "Could not subscribe to $watch"
+ return 1
+ fi
+
+ while inst_running; do
+ local msg
+ local data
+ local msgtype
+
+ inst_set_status "Watching for build messages"
+
+ if ! msg=$(ipc_endpoint_recv "$endpoint" 5); then
+ continue
+ fi
+
+ if ! data=$(ipc_msg_get_data "$msg"); then
+ log_warn "Received message without data. Dropping."
+ continue
+ fi
+
+ if ! msgtype=$(foundry_msg_get_type "$data") ||
+ [[ "$msgtype" != "build" ]]; then
+ log_warn "Received message with unexpected type. Dropping."
+ continue
+ fi
+
+ inst_set_status "Handling build message"
+ handle_build_message "$endpoint" "$publish_to" "$data" "$signer_key"
+ done
+
+ return 0
+}
+
+main() {
+ local endpoint
+ local watch
+ local publish_to
+ local key
+
+ opt_add_arg "e" "endpoint" "v" "pub/signbot" "The IPC endpoint to listen on"
+ opt_add_arg "w" "watch" "v" "builds" "The topic to watch for build messages"
+ opt_add_arg "p" "publish-to" "v" "signs" "The topic to publish signs under"
+ opt_add_arg "k" "gpg-key" "rv" "" "Fingerprint of the key to sign with"
+
+ if ! opt_parse "$@"; then
+ return 1
+ fi
+
+ endpoint=$(opt_get "endpoint")
+ watch=$(opt_get "watch")
+ publish_to=$(opt_get "publish-to")
+ key=$(opt_get "gpg-key")
+
+ if ! inst_start dispatch_tasks "$endpoint" "$watch" "$publish_to" "$key"; then
+ return 1
+ fi
+
+ return 0
+}
+
+{
+ if ! . toolbox.sh; then
+ exit 1
+ fi
+
+ if ! include "is" "log" "opt" "inst" "ipc" "foundry/context" "foundry/msg"; then
+ exit 1
+ fi
+
+ main "$@"
+ exit "$?"
+}
--- /dev/null
+#!/bin/bash
+
+_add_to_watchlist() {
+ local name="$1"
+ local value="$2"
+
+ # assume master if user didn't specify a branch
+ if [[ "$value" != *"#"* ]]; then
+ value+="#master"
+ fi
+
+ watchlist+=("$value")
+ return 0
+}
+
+watch_to_repository() {
+ local watch="$1"
+
+ if [[ "$watch" == *"#"* ]]; then
+ echo "${watch%#*}"
+ else
+ echo "$watch"
+ fi
+
+ return 0
+}
+
+watch_to_branch() {
+ local watch="$1"
+
+ if [[ "$watch" == *"#"* ]]; then
+ echo "${watch##*#}"
+ else
+ echo "master"
+ fi
+
+ return 0
+}
+
+fetch_head_smart_http() {
+ local watch="$1"
+
+ local repository
+ local branch
+ local url
+ local re
+ local data
+ local ref
+
+ repository=$(watch_to_repository "$watch")
+ branch=$(watch_to_branch "$watch")
+
+ re="00[0-9a-f]{2}\\K[0-9a-f]{40} refs/heads/$branch"
+ url="$repository/info/refs?service=git-upload-pack"
+
+ if ! data=$(curl --get --silent --location "$url" 2>/dev/null |
+ grep -oP "$re" --binary-files=text); then
+ return 1
+ fi
+
+ ref="${data%% *}"
+ echo "$ref"
+ return 0
+}
+
+fetch_head_dumb_http() {
+ local watch="$1"
+
+ local repository
+ local branch
+ local info
+ local line
+ local re
+
+ repository=$(watch_to_repository "$watch")
+ branch=$(watch_to_branch "$watch")
+
+ # I don't know what it is that git puts between the hash
+ # and the "refs/heads" part, but it's not whitespaces
+ re="^([0-9a-fA-F]+).*refs/heads/$branch"
+
+ if ! info=$(curl --get --silent --location "$repository/info/refs" 2>/dev/null); then
+ return 1
+ fi
+
+ if ! line=$(grep -m 1 "refs/heads/$branch$" <<< "$info"); then
+ return 1
+ fi
+
+ if ! [[ "$line" =~ $re ]]; then
+ return 1
+ fi
+
+ echo "${BASH_REMATCH[1]}"
+ return 0
+}
+
+fetch_head_remote() {
+ local watch="$1"
+
+ local head
+
+ if ! head=$(fetch_head_smart_http "$watch"); then
+ if ! head=$(fetch_head_dumb_http "$watch"); then
+ return 1
+ fi
+ fi
+
+ echo "$head"
+ return 0
+}
+
+fetch_head_local() {
+ local watch="$1"
+
+ local repository
+ local branch
+ local head
+
+ repository=$(watch_to_repository "$watch")
+ branch=$(watch_to_branch "$watch")
+
+ if [ -d "$repository/.git" ]; then
+ # "normal" repository
+ if ! head=$(< "$repository/.git/refs/heads/$branch"); then
+ return 1
+ fi
+ else
+ # bare repository
+ if ! head=$(< "$repository/refs/heads/$branch"); then
+ return 1
+ fi
+ fi
+
+ echo "$head"
+ return 0
+}
+
+fetch_head() {
+ local url="$1"
+
+ local repository
+ local branch
+
+ local head
+ local fetch
+
+ case "$url" in
+ "http://"*|"https://"*|"ftp://"*)
+ fetch=fetch_head_remote
+ ;;
+
+ *)
+ fetch=fetch_head_local
+ ;;
+ esac
+
+ if ! head=$("$fetch" "$url"); then
+ return 1
+ fi
+
+ echo "$head"
+ return 0
+}
+
+fetch_heads() {
+ declare -n dst="$1"
+ local watchlist=("${@:2}")
+
+ local watch
+
+ for watch in "${watchlist[@]}"; do
+ dst["$watch"]=$(fetch_head "$watch")
+ done
+
+ return 0
+}
+
+send_notification() {
+ local endpoint="$1"
+ local topic="$2"
+ local watch="$3"
+ local ref="$4"
+
+ local repository
+ local branch
+ local msg
+
+ repository=$(watch_to_repository "$watch")
+ branch=$(watch_to_branch "$watch")
+ msg=$(foundry_msg_commit_new "$repository" "$branch" "$ref")
+
+ if ! ipc_endpoint_publish "$endpoint" "$topic" "$msg"; then
+ return 1
+ fi
+
+ return 0
+}
+
+_watch() {
+ local topic="$1"
+ local interval="$2"
+ local watchlist=("${@:3}")
+
+ local endpoint
+ declare -A old_heads
+ declare -A new_heads
+
+ if ! endpoint=$(ipc_endpoint_open); then
+ return 1
+ fi
+
+ while inst_running; do
+ local watch
+
+ inst_set_status "Checking ${#watchlist[@]} repositories for updates"
+ log_info "Checking ${#watchlist[@]} repositories for updates"
+
+ fetch_heads new_heads "${watchlist[@]}"
+
+ for watch in "${watchlist[@]}"; do
+ local old_head
+ local new_head
+
+ old_head="${old_heads[$watch]}"
+ new_head="${new_heads[$watch]}"
+
+ if [[ "$old_head" != "$new_head" ]]; then
+ log_info "HEAD has changed on $watch"
+
+ if send_notification "$endpoint" "$topic" \
+ "$watch" "$new_head"; then
+ old_heads["$watch"]="$new_head"
+ else
+ log_warn "Could not publish to $topic"
+ fi
+ fi
+ done
+
+ inst_set_status "Sleeping for $interval seconds"
+ sleep "$interval"
+ done
+
+ return 0
+}
+
+main() {
+ local watchlist
+ local interval
+ local publish_to
+
+ opt_add_arg "r" "repository" "rv" "" \
+ "Repository to watch for updates" \
+ "" _add_to_watchlist
+ opt_add_arg "p" "publish-to" "v" "commits" \
+ "Topic to publish notifications"
+ opt_add_arg "i" "interval" "v" 30 \
+ "Update check interval" "^[0-9]+$"
+ opt_add_arg "n" "name" "rv" "" \
+ "The name of this instance"
+
+ if ! opt_parse "$@"; then
+ return 1
+ fi
+
+ publish_to=$(opt_get "publish-to")
+ interval=$(opt_get "interval")
+
+ inst_start _watch "$publish_to" "$interval" "${watchlist[@]}"
+
+ return 0
+}
+
+{
+ if ! . toolbox.sh; then
+ exit 1
+ fi
+
+ if ! include "log" "opt" "inst" "ipc" "foundry/msg"; then
+ exit 1
+ fi
+
+ main "$@"
+ exit "$?"
+}
+++ /dev/null
-#!/bin/bash
-
-_add_to_watchlist() {
- local name="$1"
- local value="$2"
-
- # assume master if user didn't specify a branch
- if [[ "$value" != *"#"* ]]; then
- value+="#master"
- fi
-
- watchlist+=("$value")
- return 0
-}
-
-watch_to_repository() {
- local watch="$1"
-
- if [[ "$watch" == *"#"* ]]; then
- echo "${watch%#*}"
- else
- echo "$watch"
- fi
-
- return 0
-}
-
-watch_to_branch() {
- local watch="$1"
-
- if [[ "$watch" == *"#"* ]]; then
- echo "${watch##*#}"
- else
- echo "master"
- fi
-
- return 0
-}
-
-fetch_head_smart_http() {
- local watch="$1"
-
- local repository
- local branch
- local url
- local re
- local data
- local ref
-
- repository=$(watch_to_repository "$watch")
- branch=$(watch_to_branch "$watch")
-
- re="00[0-9a-f]{2}\\K[0-9a-f]{40} refs/heads/$branch"
- url="$repository/info/refs?service=git-upload-pack"
-
- if ! data=$(curl --get --silent --location "$url" 2>/dev/null |
- grep -oP "$re" --binary-files=text); then
- return 1
- fi
-
- ref="${data%% *}"
- echo "$ref"
- return 0
-}
-
-fetch_head_dumb_http() {
- local watch="$1"
-
- local repository
- local branch
- local info
- local line
- local re
-
- repository=$(watch_to_repository "$watch")
- branch=$(watch_to_branch "$watch")
-
- # I don't know what it is that git puts between the hash
- # and the "refs/heads" part, but it's not whitespaces
- re="^([0-9a-fA-F]+).*refs/heads/$branch"
-
- if ! info=$(curl --get --silent --location "$repository/info/refs" 2>/dev/null); then
- return 1
- fi
-
- if ! line=$(grep -m 1 "refs/heads/$branch$" <<< "$info"); then
- return 1
- fi
-
- if ! [[ "$line" =~ $re ]]; then
- return 1
- fi
-
- echo "${BASH_REMATCH[1]}"
- return 0
-}
-
-fetch_head_remote() {
- local watch="$1"
-
- local head
-
- if ! head=$(fetch_head_smart_http "$watch"); then
- if ! head=$(fetch_head_dumb_http "$watch"); then
- return 1
- fi
- fi
-
- echo "$head"
- return 0
-}
-
-fetch_head_local() {
- local watch="$1"
-
- local repository
- local branch
- local head
-
- repository=$(watch_to_repository "$watch")
- branch=$(watch_to_branch "$watch")
-
- if [ -d "$repository/.git" ]; then
- # "normal" repository
- if ! head=$(< "$repository/.git/refs/heads/$branch"); then
- return 1
- fi
- else
- # bare repository
- if ! head=$(< "$repository/refs/heads/$branch"); then
- return 1
- fi
- fi
-
- echo "$head"
- return 0
-}
-
-fetch_head() {
- local url="$1"
-
- local repository
- local branch
-
- local head
- local fetch
-
- case "$url" in
- "http://"*|"https://"*|"ftp://"*)
- fetch=fetch_head_remote
- ;;
-
- *)
- fetch=fetch_head_local
- ;;
- esac
-
- if ! head=$("$fetch" "$url"); then
- return 1
- fi
-
- echo "$head"
- return 0
-}
-
-fetch_heads() {
- declare -n dst="$1"
- local watchlist=("${@:2}")
-
- local watch
-
- for watch in "${watchlist[@]}"; do
- dst["$watch"]=$(fetch_head "$watch")
- done
-
- return 0
-}
-
-send_notification() {
- local endpoint="$1"
- local topic="$2"
- local watch="$3"
- local ref="$4"
-
- local repository
- local branch
- local msg
-
- repository=$(watch_to_repository "$watch")
- branch=$(watch_to_branch "$watch")
- msg=$(foundry_msg_commit_new "$repository" "$branch" "$ref")
-
- if ! ipc_endpoint_publish "$endpoint" "$topic" "$msg"; then
- return 1
- fi
-
- return 0
-}
-
-_watch() {
- local topic="$1"
- local interval="$2"
- local watchlist=("${@:3}")
-
- local endpoint
- declare -A old_heads
- declare -A new_heads
-
- if ! endpoint=$(ipc_endpoint_open); then
- return 1
- fi
-
- while inst_running; do
- local watch
-
- inst_set_status "Checking ${#watchlist[@]} repositories for updates"
- log_info "Checking ${#watchlist[@]} repositories for updates"
-
- fetch_heads new_heads "${watchlist[@]}"
-
- for watch in "${watchlist[@]}"; do
- local old_head
- local new_head
-
- old_head="${old_heads[$watch]}"
- new_head="${new_heads[$watch]}"
-
- if [[ "$old_head" != "$new_head" ]]; then
- log_info "HEAD has changed on $watch"
-
- if send_notification "$endpoint" "$topic" \
- "$watch" "$new_head"; then
- old_heads["$watch"]="$new_head"
- else
- log_warn "Could not publish to $topic"
- fi
- fi
- done
-
- inst_set_status "Sleeping for $interval seconds"
- sleep "$interval"
- done
-
- return 0
-}
-
-main() {
- local watchlist
- local interval
- local publish_to
-
- opt_add_arg "r" "repository" "rv" "" \
- "Repository to watch for updates" \
- "" _add_to_watchlist
- opt_add_arg "p" "publish-to" "v" "commits" \
- "Topic to publish notifications"
- opt_add_arg "i" "interval" "v" 30 \
- "Update check interval" "^[0-9]+$"
- opt_add_arg "n" "name" "rv" "" \
- "The name of this instance"
-
- if ! opt_parse "$@"; then
- return 1
- fi
-
- publish_to=$(opt_get "publish-to")
- interval=$(opt_get "interval")
-
- inst_start _watch "$publish_to" "$interval" "${watchlist[@]}"
-
- return 0
-}
-
-{
- if ! . toolbox.sh; then
- exit 1
- fi
-
- if ! include "log" "opt" "inst" "ipc" "foundry/msg"; then
- exit 1
- fi
-
- main "$@"
- exit "$?"
-}