Skip to content

Commit

Permalink
initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
d12frosted committed Jun 27, 2017
0 parents commit d1a5bab
Show file tree
Hide file tree
Showing 4 changed files with 316 additions and 0 deletions.
24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.PHONY: all install

# Customisable
SCHEDULE=0 */4 * * * # any valid cron expression
CONFIG_FILE=$(XDG_CONFIG_HOME)/mirror-elpa.sh

# Non-customisable
path=$(PWD)/mirror-elpa
job:=$(SCHEDULE) $(path) $(CONFIG_FILE)
tmp:=$(shell mktemp)

all:
# no-op
# specified so that `make && make install` won't accidentally install twice

install:
-crontab -l 2> /dev/null > $(tmp)
@if grep -Eq "/mirror-elpa $(CONFIG_FILE)$$" $(tmp); then \
echo "\033[1;33mWARN: crontab already contains a reference to mirror-elpa with $(CONFIG_FILE)."; \
echo "This is likely to result in the job being run more often than" \
"expected.\033[0m"; \
fi
echo "$(job)" >> $(tmp)
crontab $(tmp)
53 changes: 53 additions & 0 deletions README.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
* =mirror-elpa=

(To) Mirror Emacs Lisp package archives. Used to create and maintain mirrors
under Git repository.

Note that currently only GitHub is supported out of box as a remote for the
mirror. Though you can use =mirror-elpa= to maintain local mirror or mirror on
any other Git hosting.

** Prerequisites

The following software must be installed on your system:

- [[https://stedolan.github.io/jq/][jq]]
- [[http://git-scm.org/][Git]]
- [[http://curl.haxx.se/][cURL]]
- [[https://www.gnu.org/software/emacs/][Emacs]]

** Usage

#+BEGIN_SRC bash
$ git clone https://github.com/d12frosted/mirror-elpa
$ cd mirror-elpa
$ ./configure
$ make install
#+END_SRC

You can also specify how often to sync mirror when running make install
(defaults to every 4 hours) and location to resulting configuration file
(defaults to =$XDG_CONFIG_HOME/mirror-elpa.sh=:

#+BEGIN_SRC bash
$ make install SCHEDULE="0 0 * * *" CONFIG_FILE="~/mirror-elpa.sh"
#+END_SRC

=SCHEDULE= can be set to any valid [[https://en.wikipedia.org/wiki/Cron#CRON_expression][CRON expression]].

Resulting configuration file can be used for more grained control over
=mirror-elpa= behaviour.

- Set value of =mirror_path= to avoid using temporary folder for mirror.
- Set value of =mirror_repo_branch_name= to change branch name (default is
=master=).
- Define =git_config_hook= function that is called before changes to repository
are committed. This is a good place to configure Git.
- Set value of =mirror_repo= if you wish to use local mirror or mirror hosted
somewhere else rather than on GitHub. Empty string means local mirror (script
doesn't push).

** Credits

- [[https://github.com/dochang][Desmond O. Chang]], thanks for [[https://github.com/dochang/elpa-clone][elpa-clone]].
- [[https://github.com/ninrod][Filipe Silva]], thanks for keeping this project alive.
149 changes: 149 additions & 0 deletions configure
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/usr/bin/env bash

# Exit script on errors
set -ue

# Read arguments
show_help=
for arg in "$@"; do
case "$arg" in
"-h"|--help)
show_help=stdin
;;
*)
>&2 echo "Unrecognised argument: $arg"
show_help=stderr
break
;;
esac
done

# Display help if necessary
help() {
echo "Usage: $(basename "$0") [options]"
echo " -h, --help print this message and exit"
echo
echo "Report bugs at https://github.com/d12frosted/mirror-elpa/issues"
exit
}
[ "$show_help" = "stdin" ] && help
[ "$show_help" = "stderr" ] && >&2 help

config_file=$XDG_CONFIG_HOME/mirror-elpa.sh

printf "Config file location: "
[ -n "$config_file" ] && printf "%s " "$config_file"
read -r i_config_file
[ -z "$i_config_file" ] && i_config_file="$config_file"
[ -n "$i_config_file" ] && config_file="$i_config_file"

# Declare config variables in case $config_file doesn't
owner=
repo=
name=
email=
message=
access_token=

[ -f "$config_file" ] && source "$config_file"

check() {
"$@" > /dev/null 2>&1;
local ret="$?"
if [ "$ret" = 0 ]; then
echo "\033[32;32mok\033[0m" # ok
else
echo "\033[31;31merror\033[0m"
fi
return $ret
}

while :; do
printf "GitHub repository owner: "
[ -n "$owner" ] && printf "%s " "$owner"
read -r i_owner
[ -z "$i_owner" ] && i_owner="$owner"
[ -n "$i_owner" ] && break
done

while :; do
printf "GitHub repository name: "
[ -n "$repo" ] && printf "%s " "$repo"
read -r i_repo
[ -z "$i_repo" ] && i_repo="$repo"
[ -n "$i_repo" ] && break
done

owner="$i_owner"
repo="$i_repo"

while :; do
echo "mirror-elpa needs to be authorized to access $i_owner/$i_repo."
echo "Please create a token with 'repo' permissions here:"
echo "https://github.com/settings/tokens/new"
echo "Alternatively, hit return without entering anything to have mirror-elpa create one for you."
printf "GitHub API token: "
read -r i_access_token
[ -n "$i_access_token" ] && break

# Generate an API token
read -rp "Your GitHub username: ($i_owner) " i_auth_user
read -rsp "Your GitHub password: " i_auth_password
echo # output blank line after password

[ "$i_auth_user" ] || i_auth_user="$i_owner"

github() {
curl -s "https://api.github.com/$1" \
-u "$i_auth_user:$i_auth_password" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.github.v3+json" \
$(shift 1; echo "$@")
}
response=$(github authorizations -X POST -d @- <<-JSON
{
"scopes": [
"repo"
],
"note": "mirror-elpa@$(hostname)$(pwd)",
"note_url": "https://github.com/d12frosted/mirror-elpa"
}
JSON
)
if echo "$response" | grep '"token"'; then
echo "$response" | jq -r .token | read i_access_token
else
printf "\033[31;31mError: \033[0m"
echo "$response" | jq -r .message
echo "You'll need to create a GitHub API token manually."
fi

[ -n "$i_access_token" ] && break
done

[ -n "$i_access_token" ] && access_token="$i_access_token"

[ -z "$name" ] && name="A Friendly Bot"
printf "Bot name: ($name) "
read i_name
[ -n "$i_name" ] && name="$i_name"

[ -z "$email" ] &&
email="$(echo "$name" | sed "s/ /./g" | tr '[:upper:]' '[:lower:]')@example"
printf "Bot email: ($email) "
read i_email
[ -n "$i_email" ] && email="$i_email"

[ -z "$message" ] && message="Sync archives"
printf "Commit message: ($message) "
read i_message
[ -n "$i_message" ] && message="$i_message"

printf "owner=%q\n" "$owner" > "$config_file"
printf "repo=%q\n" "$repo" >> "$config_file"
printf "name=%q\n" "$name" >> "$config_file"
printf "email=%q\n" "$email" >> "$config_file"
printf "message=%q\n" "$message" >> "$config_file"
printf "access_token=%q\n" "$access_token" >> "$config_file"

echo "Successfully wrote configuration to $config_file"
90 changes: 90 additions & 0 deletions mirror-elpa
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env bash

set -ue

# defaults
mirror_path=""
mirror_repo_remote_name="origin"
mirror_repo_branch_name="master"
elpa_clone_path="$HOME/.cache/elpa-clone"
elpa_clone_url="https://github.com/dochang/elpa-clone.git"
email=""
name=""
access_token=""

PATH=/usr/local/bin:/usr/bin:/bin

function git_config_hook {
log "Git config hook"
}

# load configs
config_file=$1
[ -f "$config_file" ] && source "$config_file"

# setup url
MIRROR_REPO="https://${owner}:${access_token}@github.com/${owner}/${repo}.git"

function log {
echo "[$(date '+%d/%m/%y %H:%M:%S')]" "$@"
}

function clone {
log "Updating mirror for $2 ($1)"
emacs -l "$elpa_clone_path/elpa-clone.el" -nw --batch --eval="(elpa-clone \"$1\" \"$mirror_path/$2\")"
}

if [[ "$mirror_path" = "" ]]; then
mirror_path="$(mktemp -d)"
fi

trap '[ "$?" -eq 0 ] || log Error! Could not update elpa mirrors!' EXIT

log "Start updating elpa mirrors"
log "mirror_path: $mirror_path"
log "MIRROR_REPO: $MIRROR_REPO"
log "elpa_clone_path: $elpa_clone_path"
log "PATH: $PATH"

if [[ ! -d $elpa_clone_path ]]; then
log "elpa-clone tool is missing, installing it..."
git clone --depth 1 "$elpa_clone_url" "$elpa_clone_path"
else
log "updating elpa-clone"
cd "$elpa_clone_path"
git fetch origin
git reset --hard origin/master
fi

if [[ ! -d "$mirror_path" ]]; then
log "cloning mirror repository"
git clone --depth 1 "$MIRROR_REPO" "$mirror_path"
fi

cd "$mirror_path"

# if [ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1; then
# log "updating mirror repository"
# git fetch "$mirror_repo_remote_name"
# git reset --hard "${mirror_repo_remote_name}/${mirror_repo_branch_name}"
# fi

clone "http://orgmode.org/elpa/" "org"
clone "https://elpa.gnu.org/packages/" "gnu"
clone "rsync://melpa.org/packages/" "melpa"
clone "rsync://stable.melpa.org/packages/" "stable-melpa"

if [ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1; then
log "Configure repository"
git config user.name "$name"
git config user.email "$email"
git_config_hook
git add --all
log "Committing and pushing all changes to mirror repository"
git commit -m "snapshot $(date '+%d/%m/%y %H:%M:%S')"
if [[ $"MIRROR_REPO" != "" ]]; then
git push "$MIRROR_REPO" "$mirror_repo_branch_name"
fi
fi

log "Done updating elpa mirrors"

0 comments on commit d1a5bab

Please sign in to comment.