Newsflash
command + option + shift will toggle the editor window in XcodeAutomated Xcode Backups
- Added timestamp in file name
- Added backup every x builds instead of backing up every build
- Added zip folder and upload to ftp
- Added backup only files that have changes
Here is a script to backup your Xcode project during each build phase. This is pretty cool because it will save the backup in a versioned project folder inside a folder having the same name as the Xcode project.
This is not a replacement for a computer backup system, Subversion or GIT. This is an additional measure that ensures you will have an incremental backup of your project!
See this post for help setting up Subversion.
ChangeLog:
ChangeLog:
Added:
Monday January 19, 2009
Monday January 19, 2009
Added:
- Zip files
- Upload to ftp
- Folder named with timestamp
- Backup only files that have changed since last build
- Now have the ability to only backup every x number of builds
count starts with 1
to backup every 5th time you would set this value to 6
The build log displays info to let you know
when your next backup will occur
----------------------------------------------
BACKING UP PROJECT MESSAGE
Next backup in 2 builds.
----------------------------------------------
And when a backup was performed.
----------------------------------------------
BACKING UP PROJECT MESSAGE
Creating backup!
----------------------------------------------
Fixed:
Monday January 19, 2009
Folder name was inserting an unwanted new line character
TODO
Backup entire Project Folder every x builds
Using the Script
The script uses a config.yaml file for settings. It will create this automatically.
Config.yaml Explained
---
types_to_backup: [ '.m', '.c', '.h', '.applescript', '.scpt', '.xcodeproj', '.xib']
excluded_folders: ['build']
backup_dir: '#{ENV['HOME']}/Desktop/XcodeBackups'
backup_interval: '11'
time_format: '24'
skip_unchanged_files: 'YES'
ftp_upload: 'NO'
ftp_server: 'ftp.site_name.net'
ftp_user: 'user_name'
ftp_pass: 'password'
ftp_upload_dir: 'public_html/upload_directory_name/'
types_to_backup
Are only the files you WANT to backup. Only the specified types are backed up. Notice that each type includes the '.'
excluded_foldersAre exactly that. They will not be backed up.
backup_dirThis is where you want your backups to go. The default settings will create a folder on your desktop called 'XcodeBackups'. If you let the script create the config.yaml file the "ENV['HOME']" is automatically expanded. If not you need to hard code this path yourself.
backup_intervalThis setting will determine how many builds are performed between each backup.
It is 1 based so add one to the number.
To backup every 10 builds this setting will be 11.
This information is stored in ~/Library/Preferences/com.allanCraig.BackupXcode.plist
A new entry is added for each application in which you use this script.
Set to '24' will yield 24 hour format.
Set to anything else will yield 12 hour format.
Setting this to 'YES' will only backup files with a modification date greater than the last backup date. This significantly reduces disk space usage.
ftp_uploadLeave this set to 'NO' unless you know what you are doing. :)
FTP SectionPost a comment if you are unclear how to use this feature.
SetupMake the necessary changes to the DEFAULT_CONFIG settings in the script. You can always change these later by opening the config.yaml file located in '~/Library/Application Support/BackupXcode/config.yaml'
create a new 'Run Script' build phase in your project.
Double-click to open
Set the shell to '/usr/bin/ruby'
Paste the script into the window.
That should be it. If you have any problems just leave a comment.
Finished ResultHere is the result after building a few times on a project.
The date is "Year.Month.Day_Hours.Minutes.Seconds"
An Easier Way to Add to Your Projects
Instead of adding this script to each of your projects, save it as a command line utility. I have a nifty little script for this. Then you only have to add one line of code to any project you wish to automagically backup!
Last One, promise! Easy Install AppleScript
Instructions
- Download the Ruby code, unzip and place on your desktop
- Click here to open the script below in your default script editor!
- Open the Xcode project in which you wish to install this Run Script Phase.
- Click Run!
-- date: Sunday January 25, 2009
-- author: Craig Williams
-- desc: Creates a Run Script Build Phase in Xcode
(*
Instructions:
Script assumes you have made the Ruby file an executable.
Change theScript path to match the location of the Ruby executable
The path must be HFS not POSIX style.
*)
set theScript to read file ((path to desktop as Unicode text) & "BackupXcode")
tell application "Xcode"
try
set openProjects to every project
on error
display dialog "No Projects Open"
return
end try
set projectList to {}
if (count of openProjects) is greater than 1 then
repeat with i from 1 to count of openProjects
set thisProj to item i of openProjects
set end of projectList to (name of thisProj)
end repeat
set chosenProj to choose from list projectList ¬
with title ¬
"Add Run Script Build Phase" with prompt "Choose Project"
if chosenProj is false then return
set theProject to item 1 of chosenProj
else
set theProject to name of item 1 of openProjects
end if
tell target 1 of project theProject
set newRunScript to make new run script phase
tell newRunScript
set shell path to "/bin/sh"
set shell script to theScript
set name to "Automate Backup Build"
set run only when installing to false
set show environment variables to true
end tell
end tell
end tell
#!/usr/bin/env ruby -w
#################################################################################
# #
# BackupXcode.rb #
# #
# author: Craig Williams #
# created: 2009-01-17 #
# #
#################################################################################
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
#################################################################################
=begin
BackupXcodeProjectFiles is for backing up specific types of files
like .m, .h, .applescript, .xcodeproj, .xib in a project.
It is not designed to backup all the graphics and other misc
resources that do not change on a normal basis.
=end
=begin
ChangeLog:
Added:
Monday January 19, 2009
Added:
- Zip files
- Upload to ftp
- Folder named with timestamp
- Backup only files that have changed since last build
- Now have the ability to only backup every x number of builds
count starts with 1
to backup every 5th time you would set this value to 6
The build log displays info to let you know
when your next backup will occur
----------------------------------------------
BACKING UP PROJECT MESSAGE
Next backup in 2 builds.
----------------------------------------------
And when a backup was performed.
----------------------------------------------
BACKING UP PROJECT MESSAGE
Creating backup!
----------------------------------------------
Fixed:
Monday January 19, 2009
Folder name was inserting an unwanted new line character
TODO
Backup entire Project Folder every x builds
=end
# Code from O'Reilly's 'Ruby Cookbook' page 220
# Versions file or folder names
# Eg. MyProj, MyProj.001, MyProj.002
class File
def File.versioned_name(base, first_suffix='.0001')
suffix = nil
folder_name = base
while File.exists?(folder_name)
suffix = (suffix ? suffix.succ : first_suffix)
folder_name = base + suffix
end
return folder_name
end
end
class BackupXcodeProjectFiles
require 'yaml'
require 'fileutils'
require 'find'
require 'net/ftp'
require 'time'
CONFIG_YAML_PATH = "#{ENV['HOME']}/Library/Application Support/BackupXcode"
MESSAGE_HEADER = 'BACKING UP PROJECT MESSAGE'
DEFAULT_CONFIG = "---
types_to_backup: [ '.m', '.c', '.h', '.applescript', '.scpt', '.xcodeproj', '.xib']
excluded_folders: ['build']
backup_dir: '#{ENV['HOME']}/Desktop/XcodeBackups'
backup_interval: 3
time_format: '24'
skip_unchanged_files: 'YES'
ftp_upload: 'NO'
ftp_server: 'ftp.web_site.net'
ftp_user: 'user_name'
ftp_pass: 'user_password'
ftp_upload_dir: 'path/to/upload/'"
def initialize
load_config_file
instantiate_variables
end
def main_worker_bee
defaults_read
if backup_on_x_number_of_builds
log_message('Creating backup!')
create_parent_folder
create_archive_folder
create_versioned_folder
copy_files
zipped_file = create_zip_remove_folder
upload_to_ftp(zipped_file) if @ftp_upload == 'YES'
#reset default Count
defaults_write(1)
end
end
def instantiate_variables
@proj_dir = File.dirname(File.dirname(ENV['BUILD_DIR']))
@proj_name = ENV['PROJECT_NAME']
@backup_dir = @config['backup_dir']
@types_to_backup = @config['types_to_backup']
@excluded_folders = @config['excluded_folders']
@backup_interval = @config['backup_interval']
@time_format = @config['time_format']
@skip_unchanged_files = @config['skip_unchanged_files']
# ftp
@ftp_server = @config['ftp_server']
@ftp_user = @config['ftp_user']
@ftp_pass = @config['ftp_pass']
@ftp_upload_dir = @config['ftp_upload_dir']
@ftp_upload = @config['ftp_upload']
end
def backup_on_x_number_of_builds
# To backup during every build set backup_interval: in config.yaml to 'ALL'
return true if @backup_interval == 'ALL'
current_backup_count = defaults_read_count
if current_backup_count.to_i >= @backup_interval.to_i
return true
else
new_build_count = (current_backup_count.to_i + 1)
defaults_write(new_build_count)
build_log_statement(new_build_count)
return false
end
end
def build_log_statement(new_build_count)
next_backup_count = (@backup_interval.to_i - new_build_count)
msg = next_backup_count == 0 ? "Backup on next build!." : "Next backup in #{next_backup_count + 1} builds."
log_message(msg)
end
def create_parent_folder
begin
FileUtils.mkdir(@backup_dir) if !File.exists?(@backup_dir)
rescue
log_message("ERROR in create_parent_folder\nerror => #{$!}")
end
end
def create_archive_folder
begin
@archive_folder_name = "#{@backup_dir}/#{@proj_name}"
FileUtils.mkdir(@archive_folder_name) if !File.exists?(@archive_folder_name)
rescue
log_message("ERROR in create_archive_folder\nerror => #{$!}")
end
end
def create_versioned_folder
begin
time_stamp = (@time_format == '24' ? `date +'%Y.%m.%d_%H.%M.%S'` : `date +'%Y.%m.%d_%I.%M.%S'`)
folder_name = "#{time_stamp.chomp}_#{@proj_name}"
@archive_versioned_name = File.versioned_name("#{@archive_folder_name}/#{folder_name}")
FileUtils.mkdir(@archive_versioned_name)
rescue
log_message("ERROR in create_versioned_folder\nerror => #{$!}")
end
end
def copy_files
defaults_time = defaults_read_time
Find.find(@proj_dir) do |path|
Find.prune if @excluded_folders.include?(File.basename(path))
next if !@types_to_backup.include?(File.extname(path))
# We are skipping files that have not changed since last backup
if @skip_unchanged_files == 'YES'
file_time = File.mtime(path).to_i
next if file_time < defaults_time
end
begin
FileUtils.cp_r(path, @archive_versioned_name, :remove_destination => true)
rescue
# System will announce if there is an error backing up a file
`/usr/bin/osascript -e 'say "There was an error backing up files"'`
log_message("ERROR in copy_files\nerror => #{$!}")
end
end
end
# If no config.yaml file found create one
def load_config_file
@config_path = "#{CONFIG_YAML_PATH}/config.yaml"
if !File.exists?(@config_path)
create_config_file
log_message("No 'config.yaml' file found. \nCreating one in location ~/Library/Application Support/BackupXcode/config.yaml\nWith default settings.")
end
@config = YAML.load_file(@config_path)
end
def log_message(msg)
puts "\n\n----------------------------------------------"
puts MESSAGE_HEADER
puts msg
puts "----------------------------------------------\n\n"
end
def create_config_file
begin
FileUtils.mkdir(CONFIG_YAML_PATH) if !File.exists?(CONFIG_YAML_PATH)
File.open(@config_path, 'w') { |f| f.puts DEFAULT_CONFIG }
rescue
log_message("ERROR in create_config_file\nerror => #{$!}")
end
end
def defaults_write(new_build_count)
`defaults write com.allanCraig.BackupXcode '#{@proj_name}' -dict Time #{Time.now.to_i} Count '#{new_build_count}'`
end
def defaults_read
@defaults = `defaults read com.allanCraig.BackupXcode '#{@proj_name}'`
if @defaults == ''
log_message("There was an error reading defaults - maybe the file does not exist\nCreate one and return 1")
defaults_write(1)
log_message("Writing defaults because defaults were nil")
@defaults = `defaults read com.allanCraig.BackupXcode '#{@proj_name}'`
return 1
end
end
def defaults_read_count
defaults = @defaults.scan(/.+Count\s=\s(\d+).+/)
return defaults[0][0].to_i
end
def defaults_read_time
defaults = @defaults.scan(/.+Time\s=\s(\d+).+/)
return defaults[0][0].to_i
end
def create_zip_remove_folder
zipped_file = "#{@archive_versioned_name}.zip"
`zip -r '#{zipped_file}' '#{@archive_versioned_name}'`
FileUtils.rm_r(@archive_versioned_name)
return zipped_file
end
def upload_to_ftp(zipped_file)
ftp = Net::FTP.new(@ftp_server, @ftp_user, @ftp_pass)
begin
ftp.chdir(@ftp_upload_dir)
ftp.put(zipped_file)
ftp.close
log_message("Just finished uploading to ftp!")
rescue
log_message("There was an error uploading file\nError: #{$!}")
ftp.close
end
end
end
if __FILE__ == $0
backup_files = BackupXcodeProjectFiles.new
backup_files.main_worker_bee
backup_files.log_message('Looks like all went well!')
end