Hi,

The env2 script has a similar purpose as the env script currently used in
OpenWrt. It based on env but adds new features to make it possible to track
a whole OpenWrt configuration. env does only track changes to the .config
file and the files/ directory. The env2 script permits to track arbitrary
files in git. It also implements SVN support. So you can save a coherent and
reproducible state of the environment. 

For example with env2 it is possible to track a adm5120 configuration like
this:

        env2 new adm5120

This creates a new git branch and adds the .config, feeds.conf and files/
to it. It also adds the file .svn/entries for the OpenWrt base directory
and the configured feeds.
(The .svn/entries are needed for tracking the SVN Revision and tag you use
in your branches; so env2 adds some more files the branch by default)

Add configuration for an adm5120 router

        env2 add target/linux/adm5120/Makefile
        env2 add target/linux/adm5120/router_le/config-2.6.32

Add build images

        env2 add bin/adm5120/openwrt-adm5120-br-6104kp-squashfs-xmodem.bin
        env2 add bin/adm5120/openwrt-adm5120-rootfs.tgz

Like in the env script you can also base you new configuration on the old
one by anwsering "y" to "Do you want to clone the current environment"

        env2 new adm5120-2.6.32.9
        (respond "y" when ask to clone)
        (edit KERNEL_VERSION to 2.6.32.9 in target/linux/adm5120/Makefile)
        evn2 save

Now you have two branches, one configured for the default kernel, one for
the 2.6.32.9 kernel.

If you now want to work on another router, say an ar71xxx you would do
the following:

        env2 new ar71xx
        env2 add target/linux/ar71xx/Makefile
        env2 add target/linux/ar71xx/config-2.6.32

To switch back to the adm5120 configuration you can do
        
        env2 switch adm5120

The switch command in env2 does remove the symlinked files of the current
branch from the OpenWrt base directory, switches to the new branch and
symlinks the files of the new branch to the OpenWrt base directory. 

After that it checks if the new SVN URL differs from the old one and
performs a "svn switch" if necessary. If the SVN Revision differs a "svn
update -r $REV" is also peformed. After that it runs "scripts/feeds/update
-i" and "make package/symlinks". Finally it deletes all unwanted files that
came along with the svn update (see "env2 unwanted").

To see information the current branch (SVN URL and Revision, branch name)

        env2 info

To remove a file from the branch (AND the environment)

        env2 remove target/linux/ar71xx/Makefile

The env2 script also wraps "git diff", so you can do:
        env2 gdiff adm5120 adm5120-2.6.32.9 --summary

env2 symlinks like env all files from the env2 directory to the OpenWRT
base directory. To remove the symlinks and copy all files from you current
branch to the OpenWrt base directory do

        env2 disable

Because the script restores automatically SVN revisions there might be some
files you have deleted. For example patches you do not want to apply. With
env2 you can automatically remove these files using the following commands

        env2 unwanted package/opkg/patches/009-remove-upgrade-all.patch

This will delete the patch everytime when a SVN update pulls it in.
If you decide that you want this file again you can do:

        env2 rewanted package/opkg/patches/009-remove-upgrade-all.patch

(Note: there might be a more general, better way to do this in the
implementation. This is just a quick hack to also permit keeping track of
deletions.)

The rest of the commands are the same as in you know them from the env
script.

The env2 assumes that it is located in the scripts/ directory below the
OpenWrt base directory. By doing so it is possible to call it independently
from the current $(PWD). So you might just add the scripts directory to you
$PATH and env2 should work from anywhere.

I have tested this script and it works for me. But this is the first release
and there will be most certainly bugs. Happy testing!

Oliver Ripka
Index: .gitignore
===================================================================
--- .gitignore	(revision 21044)
+++ .gitignore	(working copy)
@@ -1,5 +1,6 @@
 .*.swp
 /env
+/env2
 /dl
 /.config
 /.config.old
Index: scripts/env2
===================================================================
--- scripts/env2	(revision 0)
+++ scripts/env2	(revision 0)
@@ -0,0 +1,453 @@
+#!/usr/bin/env bash
+
+# Script path
+spath=$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")
+
+# OpenWrt checkout, assuming it is one level down from the
+# script directory in which this script should be.
+BASEDIR=`readlink -f $(dirname $spath)/..`
+
+ENVNAME="env2"
+ENVDIR="$BASEDIR/$ENVNAME"
+
+UNWANTED=".unwanted"
+
+#DEBUG=true
+#DISABLE_SVN=true
+
+usage() {
+	cat <<EOF
+Usage: $0 [options] <command> [arguments]
+Commands:
+	help                This help text
+	list                List environments
+	clear               Delete all environment and revert to flat config/files
+	new <name>          Create a new environment
+	switch <name>       Switch to a different environment
+	delete <name>       Delete an environment
+	rename <newname>    Rename the current environment
+	diff <opt>          Show differences between current state and environment
+	save                Save your changes to the environment
+	revert              Revert your changes since last save
+	add <file>          Add a file to be tracked in the current environment
+	remove <file>       Remove a file from the current environment
+	disable             Substitutes symlinks to $ENVNAME with copies of the current branch
+	unwanted <file>     Adds <file> to the list of unwanted files
+	info                Information about the current branch
+	gdiff <1> <2> <opt> Diff between two branches (takes git diff options like --summary)
+	unwanted <file>     Mark a file to be deleted from the OpenWrt environment
+	rewanted <file>    Makr a file not to be delete from OpenWrt enviroment (only after unwanted)
+Options:
+EOF
+	exit ${1:-1}
+}
+op (){
+	[ "$DEBUG" = "true" ] && echo "Executing: $1" 
+	[ "$2" = "1" ] && $1 
+}
+
+decho (){
+	[ "$DEBUG" = "true" ] && echo "$1"
+}
+
+relative_name() {
+	REL_NAME=`readlink -f $1`
+	REL_NAME=${REL_NAME#$2/}
+}
+
+svn_parse_info (){
+	cd "$1"
+	URL=`svn info | grep 'URL: ' | cut -d ' ' -f2`
+	REVISION=`svn info | grep 'Revision: ' | cut -d ' ' -f2`
+	cd $BASEDIR
+}
+
+svn_save_info (){
+	cd $BASEDIR
+	local DIR=$1
+	local ENTRY="$1/.svn/entries"
+	if [ ! -e "$ENTRY" ];then
+		echo "Not a SVN repository can not save info"
+		return
+	fi
+
+	add_file "$ENTRY"
+	decho "Saved SVN info for $DIR"
+}
+
+svn_restore_checkout (){
+	local DIR=$1
+	svn_parse_info "$BASEDIR/$DIR"
+	local CUR_REV=$REVISION
+	local CUR_URL=$URL
+	svn_parse_info "$ENVDIR/$DIR"
+	local SAVED_REV=$REVISION
+	local SAVED_URL=$URL
+
+	
+	if [ "$CUR_URL" != "$SAVED_URL" ];then
+		echo "SVN: Current $CUR_URL -> saved: $SAVED_URL"
+		op "svn switch $SAVED_URL"
+	else
+		decho "URL for $DIR was already $SAVED_URL, not switching"
+	fi
+
+	if [ -e $1 ]; then
+		cd $1
+		if [ "$CUR_REV" != "$SAVED_REV" ]; then
+			echo "SVN: Current $CUR_REV -> saved: $SAVED_REV"
+			op "svn update  -r $SAVED_REV"
+			case "$DIR" in
+				*feeds*)
+					op "$BASEDIR/scripts/feeds/update -i"
+					cd $BASEDIR/
+					op "make package/symlinks"
+					;;
+				*) #core svn repository
+					;;
+			esac	
+			cd $BASEDIR
+		else 
+			decho "SVN checkout $DIR was already on revision $SAVED_REV, not updating."
+		fi
+	fi
+	echo "Reverted SVN checkout $DIR"
+}
+
+
+svn_restore (){
+	[ "$DISABLE_SVN" = "true" ] && return 
+	cd $ENVDIR
+	local FEEDF="$ENVDIR/feeds.conf"
+	[ ! -e "feeds.conf" ] && FEEDF="$FEEDF.default" 
+		
+	feeds=`grep -v '^#' $FEEDF | cut -d' ' -f2`
+
+	for FEED in ${fee...@]}; do
+		svn_restore_checkout "feeds/$FEED"
+	done
+	svn_restore_checkout "."
+	apply_unwanted
+}
+
+
+svn_save (){
+	cd $BASEDIR
+	[ "$DISABLE_SVN" = "true" ] && return
+	local FEEDF="$BASEDIR/feeds.conf"
+	
+	[ ! -e "$FEEDF" ] && FEEDF="$FEEDF.default"
+	
+	add_file "$FEEDF"
+	feeds=`grep -v '^#' $FEEDF | cut -d' ' -f2`
+	for FEED in ${fee...@]}; do
+		svn_save_info "feeds/$FEED"
+	done
+	svn_save_info "."
+}
+
+svn_info (){
+	cd $BASEDIR
+	local FEEDF="$BASEDIR/feeds.conf"
+	[ ! -e "$FEEDF" ] && FEEDF="$FEEDF.default"
+	feeds=`grep -v '^#' $FEEDF | cut -d' ' -f2`
+	for FEED in ${fee...@]}; do
+		svn_parse_info "feeds/$FEED"
+		echo -e "[$FEED] $URL:$REVISION"
+	done
+	svn_parse_info "."
+	echo "[Core] $URL:$REVISION"
+}
+
+add_file() {
+	local LNAME=`readlink -f $1`
+	relative_name $LNAME $BASEDIR
+	LNAME=$REL_NAME
+	cd $BASEDIR
+	if [ -L $LNAME ]; then
+		echo "File already linked to env2"
+	else
+		cp --parents -a $LNAME $ENVNAME
+		echo "Added file: $LNAME"
+	fi
+}
+
+error() {
+	echo "$0: $*"
+	exit 1
+}
+
+ask_bool() {
+	local DEFAULT="$1"; shift
+	local def defstr val
+	case "$DEFAULT" in
+		1) def=0; defstr="Y/n";;
+		0) def=1; defstr="y/N";;
+		*) def=;  defstr="y/n";;
+	esac
+	while [ -z "$val" ]; do
+		local VAL
+
+		echo -n "$* ($defstr): "
+		read VAL
+		case "$VAL" in
+			y*|Y*) val=0;;
+			n*|N*) val=1;;
+			*) val="$def";;
+		esac
+	done
+	return "$val"
+}
+
+add_unwanted (){
+	local LNAME=`readlink -f $1`
+	relative_name $LNAME $BASEDIR
+	LNAME=$REL_NAME
+	cd $BASEDIR
+	touch $ENVDIR/$UNWANTED
+	echo "$LNAME" > $ENVDIR/$UNWANTED
+	sort -u $ENVDIR/$UNWANTED | grep -v "^$" > $ENVDIR/$UNWANTED.tmp
+	mv $ENVDIR/$UNWANTED.tmp $ENVDIR/$UNWANTED
+	apply_unwanted
+	cd $ENVDIR
+	git add .unwanted
+}
+
+remove_unwanted (){
+	local LNAME=`readlink -f $1`
+	relative_name $LNAME $BASEDIR
+	LNAME=$REL_NAME
+	cd $BASEDIR
+
+	if [ -e $ENVDIR/$UNWANTED ]; then
+		sort -u $ENVDIR/$UNWANTED | grep -v "^$" | grep -v "$LNAME" > $ENVDIR/$UNWANTED.tmp
+		mv $ENVDIR/$UNWANTED.tmp $ENVDIR/$UNWANTED
+	else
+		echo "$ENVDIR/$UNWANTED does not exist"
+	fi
+}
+
+apply_unwanted (){
+	if [ -e $ENVDIR/$UNWANTED ]; then	
+		cat "$ENVDIR/$UNWANTED" | while read ITEM; do
+			echo "Deleting $ITEM"
+			[ -e "$BASEDIR/$ITEM" ] && rm "$BASEDIR/$ITEM"
+		done
+	fi
+}
+
+env_init() {
+	local CREATE="$1"
+	if [ -z "$CREATE" ]; then
+		[ -d "$ENVDIR" ] || exit 0
+	fi
+	[ -x "$(which git 2>/dev/null)" ] || error "Git is not installed"
+	mkdir -p "$ENVDIR" || error "Failed to create the environment directory"
+	cd "$ENVDIR" || error "Failed to switch to the environment directory"
+	[ -d .git ] || { 
+		git init &&
+		cd "$BASEDIR" &&
+		add_file README &&
+		cd "$ENVDIR"
+		git add . && 
+		git commit -q -m "Initial import"
+	} || {
+		rm -rf .git
+		error "Failed to initialize the environment directory"
+	}
+}
+
+env_sync_data() {
+	git add .
+	git add -u
+}
+
+env_sync() {
+	local STR="$1"
+	env_sync_data
+	git commit -m "${STR:-Update} at $(date)"
+}
+
+env_link_config() {
+	cd $ENVDIR
+	local files=`find $ENVDIR -type f | grep -v '.git' | grep -v '.svn' | grep -v ".unwanted"`
+	for GFILE in ${fil...@]}; do
+		cd $ENVDIR
+		relative_name $GFILE $ENVDIR
+		LNAME=$REL_NAME
+		cd $BASEDIR
+		op "rm -f $LNAME" 1
+		case $1 in
+			unlink) ;;
+			copy)   mkdir -p `dirname $LNAME`
+				op "cp $ENVDIR/$LNAME $LNAME" 1;;
+			*) 	mkdir -p `dirname $LNAME`
+				op "ln -s $ENVDIR/$LNAME $LNAME" 1;;
+		esac
+	done
+}
+
+env_do_reset() {
+	git reset --hard HEAD
+	git clean -d -f
+}
+
+env_list() {
+	env_init
+	git branch | grep -vE '^. master$'
+}
+
+env_diff() {
+	cd $BASEDIR
+	env_init
+	env_sync_data
+	git diff --cached $1
+	#env_link_config
+}
+
+env_save() {
+	env_init
+	env_sync
+	env_link_config
+}
+
+env_revert() {
+	env_init
+	env_do_reset
+	env_link_config
+}
+
+env_ask_sync() {
+	env_sync_data
+	LINES="$(env_diff | wc -l)" # implies env_init
+	[ "$LINES" -gt 0 ] && {
+		if ask_bool 1 "Do you want to save your changes"; then
+			env_sync
+		else
+			env_do_reset
+		fi
+	}
+}
+
+env_clear() {
+	if ask_bool 1 "Do you want to keep your current config and files"; then
+		echo "Keeping config"
+	else
+		rm -rf "$ENVDIR"
+	fi
+	cd "$BASEDIR"
+}
+
+env_delete() {
+	local name="${1##*/}"
+	env_init
+	[ -z "$name" ] && usage
+	branch="$(git branch | grep '^\* ' | awk '{print $2}')"
+	[ "$name" = "$branch" ] && error "cannot delete the currently selected environment"
+	git branch -D "$name"
+}
+
+env_switch() {
+	local name="${1##*/}"
+	[ -z "$name" ] && usage
+
+	env_init
+	env_ask_sync
+	git branch | grep "^  $name$" && env_link_config "unlink"
+	cd $ENVDIR
+	git checkout "$name" || error "environment '$name' not found"
+	env_link_config
+	svn_restore
+}
+
+env_rename() {
+	local NAME="${1##*/}"
+	env_init
+	git branch -m "$NAME"
+}
+
+
+env_add() {
+	relative_name $1 $BASEDIR
+	cd $BASEDIR
+	cp --parents $REL_NAME $ENVDIR/
+	cd $ENVDIR
+	git add $REL_NAME
+	env_sync
+	env_link_config
+}
+
+env_remove() {
+	relative_name $1
+	cd $BASEDIR
+	rm $REL_NAME
+	cd $ENVDIR
+	rm $REL_NAME
+	env_sync
+}
+
+env_disable() {
+	env_init
+	env_ask_sync
+	env_link_config "copy"
+	svn_restore
+}
+
+env_info() {
+	cd $ENVDIR
+	local BRANCH=$(git branch | grep '^\* ' | awk '{print $2}')
+	cd $BASEDIR
+	echo "GIT Branch: $BRANCH"
+	svn_info 
+}
+
+env_gdiff() {
+	cd $ENVDIR
+	git diff $3 $1 $2
+}
+
+env_new() {
+	local NAME="$1"
+	local branch
+	local from="master"
+
+	[ -z "$NAME" ] && usage
+	env_init 1
+	
+	branch="$(git branch | grep '^\* ' | awk '{print $2}')"
+	if [ -n "$branch" -a "$branch" != "master" ]; then
+		env_ask_sync
+		if ask_bool 0 "Do you want to clone the current environment?"; then
+			from="$branch"
+		fi
+	fi
+	git checkout -b "$1" "$from"
+	if [ "$from" = "master" ]; then
+		cd $BASEDIR
+		add_file .config 
+		add_file files 
+		svn_save 
+	fi
+	env_link_config
+}
+COMMAND="$1"; shift
+case "$COMMAND" in
+	help) usage 0;;
+	new) env_new "$@";;
+	list) env_list "$@";;
+	clear) env_clear "$@";;
+	switch) env_switch "$@";;
+	delete) env_delete "$@";;
+	rename) env_rename "$@";;
+	diff) env_diff "$@";;
+	add) env_add "$@";;
+	remove) env_remove "$@";;
+	save) env_save "$@";;
+	revert) env_revert "$@";;
+	disable) env_disable "$@";;
+	info) env_info "$@";;
+	gdiff) env_gdiff "$@";;
+	unwanted) add_unwanted "$@";;
+	rewanted) remove_unwanted "$@";;
+	*) usage;;
+esac

Property changes on: scripts/env2
___________________________________________________________________
Added: svn:executable
   + *

_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to