From b86364fe414329dd3e6ec0b064039a7580a7d995 Mon Sep 17 00:00:00 2001 From: WrinklyNinja Date: Thu, 19 Sep 2013 23:44:59 +0100 Subject: [PATCH] Removed Subversion support (issue #63). --- README.md | 26 +- docs/BOSS Readme.html | 4 +- docs/licenses/Apache License v2.0.txt | 86 ---- docs/licenses/Licenses.txt | 1 - src/api/api.cpp | 5 +- src/api/api.h | 8 +- src/archive.py | 2 - src/backend/error.h | 4 +- src/backend/globals.cpp | 1 - src/backend/globals.h | 1 - src/backend/network.cpp | 598 ++++++++++---------------- src/backend/network.h | 31 -- src/gui/main.cpp | 2 +- src/installer.nsi | 7 - 14 files changed, 231 insertions(+), 545 deletions(-) diff --git a/README.md b/README.md index b15072a8..b7ef96b1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ BOSS is intended to make using mods easier, and mod users should still possess a ## Downloading BOSS -At the moment, BOSSv3 is in alpha testing, and so is not recommended for use unless for testing purposes. The load orders it produces should not be used in-game unless found to be valid by manual checking beforehand. Any releases can be found by clicking on the releases button above the coloured bar on the repository homepage. +At the moment, BOSSv3 is in beta testing, and so is not recommended for use unless for testing purposes. The load orders it produces should not be used in-game unless found to be valid by manual checking beforehand. Any releases can be found by clicking on the releases button above the coloured bar on the repository homepage. ## About This Repository @@ -40,32 +40,10 @@ BOSS requires the following libraries: BOSS expects all libraries' folders to be present alongside the BOSS repository folder that contains this readme, or otherwise installed such that the compiler and linker used can find them without suppling additional paths. All paths below are relative to the folder(s) containing the libraries and BOSS. -BOSS can also make use of [GraphVis](http://www.graphviz.org/Download_windows.php) and [Subversion](http://sourceforge.net/projects/win32svn) binaries. If provided, they should be installed as detailed below. +BOSS can also make use of [GraphVis](http://www.graphviz.org/Download_windows.php) binaries. If provided, they should be installed as detailed below. Alphanum, Libespm and PugiXML do not require any additional setup. The rest of the libraries must be built separately. -### Subversion - -Put the following binaries into ```resources/svn/``` in the BOSS repository root. - -* int3_svn.dll -* libapr-1.dll -* libapriconv-1.dll -* libaprutil-1.dll -* libdb48.dll -* libeay32.dll -* libsasl.dll -* libsvn_client-1.dll -* libsvn_delta-1.dll -* libsvn_diff-1.dll -* libsvn_fs-1.dll -* libsvn_ra-1.dll -* libsvn_repos-1.dll -* libsvn_subr-1.dll -* libsvn_wc-1.dll -* ssleay32.dll -* svn.exe - ### GraphVis Put the following binaries into `resources/graphvis/` in the BOSS repository root, then run `dot.exe -c`. diff --git a/docs/BOSS Readme.html b/docs/BOSS Readme.html index 0d17ae13..9193e8d6 100644 --- a/docs/BOSS Readme.html +++ b/docs/BOSS Readme.html @@ -384,7 +384,7 @@ To translate the BOSS application:
  • Zanderat -

    BOSS is written in C/C++ and makes use of Subversion and the Alphanum, Boost, libespm, libgit2, libloadorder, PugiXML, wxWidgets and yaml-cpp libraries. BOSS's reports are written in XHTML/CSS/Javascript and make use of Polyfill.js and svgweb to provide Internet Explorer 8 compatibility. Copyright license information for all these may be found here. +

    BOSS is written in C/C++ and makes use of GraphVis and the Alphanum, Boost, libespm, libgit2, libloadorder, PugiXML, wxWidgets and yaml-cpp libraries. BOSS's reports are written in XHTML/CSS/Javascript and make use of Polyfill.js and svgweb to provide Internet Explorer 8 compatibility. Copyright license information for all these may be found here.

    Project Members

    @@ -549,6 +549,6 @@ There are three key pieces of information that are used to accurately describe a
  • BOSS files that are edited are now stored in a folder within %LOCALAPPDATA%.
  • BOSS Log retitled BOSS Report.
  • BOSS Log redesigned. -
  • Masterlist updater now uses Subversion. +
  • Masterlist updater now uses Git.
  • BOSS now sorts plugins according to their parsed contents, supplemented by metadata supplied by the masterlist and userlist. diff --git a/docs/licenses/Apache License v2.0.txt b/docs/licenses/Apache License v2.0.txt index 99eeca04..a16c46af 100644 --- a/docs/licenses/Apache License v2.0.txt +++ b/docs/licenses/Apache License v2.0.txt @@ -176,89 +176,3 @@ of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -SUBVERSION SUBCOMPONENTS: - -Subversion includes a number of subcomponents with separate copyright -notices and license terms. Your use of the source code for the these -subcomponents is subject to the terms and conditions of the following -licenses. - -For portions of the Python bindings test suite at -subversion/bindings/swig/python/tests/trac/: - - I. Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - - II. Copyright (C) 2003, 2004, 2005 Edgewall Software - Copyright (C) 2003, 2004, 2005 Jonas Borgström - Copyright (C) 2005 Christopher Lenz - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - the documentation and/or other materials provided with the - distribution. - 3. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS - OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/docs/licenses/Licenses.txt b/docs/licenses/Licenses.txt index 55e53825..a7beba85 100644 --- a/docs/licenses/Licenses.txt +++ b/docs/licenses/Licenses.txt @@ -16,7 +16,6 @@ BOSS uses a number of libraries, and their licenses are listed in this file. BOSS is also distributed with some binaries for the following programs: -* Subversion - Apache License v2.0; see "Apache License v2.0.txt" for the text. * GraphViz - Eclipse Public License v1.0; the text is not included as allowed by the license. Going by what the EPL FAQ page says, simply running the GraphVis binaries through system calls does not make BOSS a "derivative work", and so it is not bound by the EPL, and so it's OK for BOSS to use GraphVis even though the EPL and GPL are incompatible. diff --git a/src/api/api.cpp b/src/api/api.cpp index 2c1013a4..abb0df0b 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -56,8 +56,9 @@ const unsigned int boss_error_invalid_args = boss::error::invalid_args; const unsigned int boss_error_no_tag_map = boss::error::no_tag_map; const unsigned int boss_error_path_not_found = boss::error::path_not_found; const unsigned int boss_error_no_game_detected = boss::error::no_game_detected; -const unsigned int boss_error_subversion_error = boss::error::subversion_error; -const unsigned int boss_return_max = boss_error_subversion_error; +const unsigned int boss_error_windows_error = boss::error::windows_error; +const unsigned int boss_error_sorting_error = boss::error::windows_error; +const unsigned int boss_return_max = boss_error_sorting_error; // The following are the games identifiers used by the API. const unsigned int boss_game_tes4 = boss::g_game_tes4; diff --git a/src/api/api.h b/src/api/api.h index 87b3f948..a2939796 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -123,12 +123,8 @@ BOSS_API extern const unsigned int boss_error_invalid_args; ///< Invalid argume BOSS_API extern const unsigned int boss_error_no_tag_map; ///< No Bash Tag map has been generated yet. BOSS_API extern const unsigned int boss_error_path_not_found; ///< A file or folder path could not be found. BOSS_API extern const unsigned int boss_error_no_game_detected; ///< The given game could not be found. - -/** - @brief An error occurred during a Subversion operation. - @details This code should never be seen client-side, as the API does not use any Subversion processes, but is provided in the interests of completeness and in case of library bugs returning it. -*/ -BOSS_API extern const unsigned int boss_error_subversion_error; +BOSS_API extern const unsigned int boss_error_windows_error; ///< An error occurred during a call to the Windows API. +BOSS_API extern const unsigned int boss_error_sorting_error; ///< An error occurred while sorting plugins. /** @brief Matches the value of the highest-numbered return code. diff --git a/src/archive.py b/src/archive.py index 177f6500..18ac9d0d 100755 --- a/src/archive.py +++ b/src/archive.py @@ -8,7 +8,6 @@ # build/BOSS.exe # resources/graphvis # resources/svgweb -# resources/svn # resources/icon.ico # resources/polyfill.js # resources/script.js @@ -40,7 +39,6 @@ shutil.copy( os.path.join('..', 'build', 'BOSS.exe'), temp_path ) shutil.copytree( os.path.join('..', 'resources', 'graphvis'), os.path.join(temp_path, 'resources', 'graphvis') ) shutil.copytree( os.path.join('..', 'resources', 'svgweb'), os.path.join(temp_path, 'resources', 'svgweb') ) -shutil.copytree( os.path.join('..', 'resources', 'svn'), os.path.join(temp_path, 'resources', 'svn') ) shutil.copy( os.path.join('..', 'resources', 'icon.ico'), os.path.join(temp_path, 'resources') ) shutil.copy( os.path.join('..', 'resources', 'polyfill.js'), os.path.join(temp_path, 'resources') ) shutil.copy( os.path.join('..', 'resources', 'script.js'), os.path.join(temp_path, 'resources') ) diff --git a/src/backend/error.h b/src/backend/error.h index 5963b138..a51189f3 100644 --- a/src/backend/error.h +++ b/src/backend/error.h @@ -37,7 +37,7 @@ namespace boss { unsigned int code() const { return _code; } const char * what() const throw() { return _what.c_str(); } - + /* These must not be changed for API stability. */ static const unsigned int ok = 0; static const unsigned int liblo_error = 1; static const unsigned int path_write_fail = 2; @@ -49,7 +49,7 @@ namespace boss { static const unsigned int no_tag_map = 8; static const unsigned int path_not_found = 9; static const unsigned int no_game_detected = 10; - static const unsigned int subversion_error = 11; + //11 was subversion_error, and was removed along with svn support. static const unsigned int git_error = 12; static const unsigned int windows_error = 13; static const unsigned int sorting_error = 14; diff --git a/src/backend/globals.cpp b/src/backend/globals.cpp index d7a97796..daa5f450 100644 --- a/src/backend/globals.cpp +++ b/src/backend/globals.cpp @@ -56,7 +56,6 @@ namespace boss { const boost::filesystem::path g_path_js = boost::filesystem::current_path() / "resources" / "script.js"; const boost::filesystem::path g_path_polyfill = boost::filesystem::current_path() / "resources" / "polyfill.js"; const boost::filesystem::path g_path_l10n = boost::filesystem::current_path() / "resources" / "l10n"; - const boost::filesystem::path g_path_svn = boost::filesystem::current_path() / "resources" / "svn" / "svn.exe"; const boost::filesystem::path g_path_graphvis = boost::filesystem::current_path() / "resources" / "graphvis" / "dot.exe"; const boost::filesystem::path g_path_svgweb = boost::filesystem::current_path() / "resources" / "svgweb" / "svg.js"; const boost::filesystem::path g_path_local = GetLocalAppDataPath() / "BOSS"; diff --git a/src/backend/globals.h b/src/backend/globals.h index 8decce06..15dbf9ee 100644 --- a/src/backend/globals.h +++ b/src/backend/globals.h @@ -58,7 +58,6 @@ namespace boss { extern const boost::filesystem::path g_path_css; extern const boost::filesystem::path g_path_js; extern const boost::filesystem::path g_path_polyfill; - extern const boost::filesystem::path g_path_svn; extern const boost::filesystem::path g_path_log; extern const boost::filesystem::path g_path_l10n; extern const boost::filesystem::path g_path_graphvis; diff --git a/src/backend/network.cpp b/src/backend/network.cpp index ed1ab520..64239831 100644 --- a/src/backend/network.cpp +++ b/src/backend/network.cpp @@ -37,40 +37,6 @@ namespace fs = boost::filesystem; namespace boss { - //Gets revision + date string. - string GetRevision(const std::string& buffer) { - string revision, date; - size_t pos1, pos2; - - pos1 = buffer.rfind("Revision: "); - if (pos1 == string::npos) - return ""; - - pos2 = buffer.find('\n', pos1); - - revision = buffer.substr(pos1+10, pos2-pos1-10); - - pos1 = buffer.find("Last Changed Date: ", pos2); - pos2 = buffer.find(' ', pos1+19); - - date = buffer.substr(pos1+19, pos2-pos1-19); - - return revision + " (" + date + ")"; - } - - //Gets repository URL string. - string GetURL(const std::string& buffer) { - size_t pos1, pos2; - - pos1 = buffer.rfind("Repository Root: "); - if (pos1 == string::npos) - return ""; - - pos2 = buffer.find('\n', pos1); - - return buffer.substr(pos1+17, pos2-pos1-17); - } - struct pointers_struct { pointers_struct() : repo(NULL), remote(NULL), cfg(NULL), obj(NULL), commit(NULL) {} @@ -108,373 +74,247 @@ namespace boss { std::string UpdateMasterlist(Game& game, std::list& parsingErrors, std::list& plugins, std::list& messages) { - //First need to decide how the masterlist is updated: using Git or Subversion? - //Look at the update URL to decide. + /* List of Git operations (porcelain commands shown, will need to implement using plumbing in the API though): - if (!boost::iends_with(game.URL(), ".git")) { //Subversion + 1. Check if there is already a Git repository in the game's BOSS subfolder. - //Check if the Subversion binary exists. - if (!fs::exists(g_path_svn)) { - throw boss::error(boss::error::path_not_found, "Could not find \"svn.exe\". Subversion cannot be used to update the masterlist."); - } + Since the masterlists will each be in the root of a separate repository, just check if there is a `.git` folder present. - string command, output, revision; - //First check if the working copy is set up or not. - command = g_path_svn.string() + " info \"" + game.MasterlistPath().string() + "\""; + 2a. If there is, compare its remote URL with the URL that BOSS is currently set to use. - BOOST_LOG_TRIVIAL(trace) << "Checking to see if the working copy is set up or not for the masterlist at \"" + game.MasterlistPath().string() + "\""; - bool success = RunCommand(command, output); + The current remote can be gotten using `git config --get remote.origin.url`. - revision = GetRevision(output); - if (game.URL().empty()) { - if (!revision.empty()) - return revision; - else - return "N/A"; - } + 3a. If the URLs are different, then update the remote URL to the one given by BOSS. - if (!success) { - BOOST_LOG_TRIVIAL(trace) << "Working copy is not set up, checking out repository."; - //Working copy not set up, perform a checkout. - command = g_path_svn.string() + " co --depth empty " + game.URL().substr(0, game.URL().rfind('/')) + " \"" + game.MasterlistPath().parent_path().string() + "\\.\""; - if (!RunCommand(command, output)) { - BOOST_LOG_TRIVIAL(error) << "Subversion could not perform a checkout. Details: " << output; - throw error(error::subversion_error, "Subversion could not perform a checkout. Details: " + output); - } - } else { - //A working copy exists, but we need to make sure that it points to the right repository. - BOOST_LOG_TRIVIAL(trace) << "Comparing working copy repository URL with BOSS's URL"; + The remote URL can be changed using `git remote set-url origin ` - command = g_path_svn.string() + " info \"" + game.MasterlistPath().string() + "\""; + 2b. If there isn't already a Git repo present, initialise one using the URL given (remembering to split the URL so that it ends in `.git`). - if (!RunCommand(command, output)) { - BOOST_LOG_TRIVIAL(error) << "Subversion could not get the repository URL. Details: " << output; - throw error(error::subversion_error, "Subversion could not get the repository URL. Details: " + output); - } + `git init` + `git remote add origin ` - string url = GetURL(output); - //Now compare URLs. - if (url != game.URL()) { - BOOST_LOG_TRIVIAL(trace) << "URLs do not match: relocating the working copy."; + 3b. Now set up sparse checkout support, so that even if the repository has other files, the user's BOSS install only gets the masterlist added to it. - command = g_path_svn.string() + " relocate " + game.URL(); + `git config core.sparseCheckout true` + `echo masterlist.yaml >> .git/info/sparse-checkout` - if (!RunCommand(command, output)) { - BOOST_LOG_TRIVIAL(error) << "Subversion could not relocate the working copy. Details: " << output; - throw error(error::subversion_error, "Subversion could not relocate the working copy. Details: " + output); - } - } - } + 4. Now update the repository. - //Now update masterlist. - BOOST_LOG_TRIVIAL(trace) << "Performing Subversion update of masterlist."; - command = g_path_svn.string() + " update \"" + game.MasterlistPath().string() + "\""; - if (!RunCommand(command, output)) { - BOOST_LOG_TRIVIAL(error) << "Subversion could not update the masterlist. Details: " << output; - throw error(error::subversion_error, "Subversion could not update the masterlist. Details: " + output); - } + `git reset --hard HEAD` is required to undo any roll-backs that were done in the local repository. + `git pull origin master` - bool parsingFailed = false; - do { - //Now get the masterlist revision. - BOOST_LOG_TRIVIAL(trace) << "Getting the new masterlist version."; - command = g_path_svn.string() + " info \"" + game.MasterlistPath().string() + "\""; - if (!RunCommand(command, output)) { - BOOST_LOG_TRIVIAL(error) << "Subversion could not read the masterlist revision number. Details: " << output; - throw error(error::subversion_error, "Subversion could not read the masterlist revision number. Details: " + output); - } + 5. Get the masterlist's commit hash. - BOOST_LOG_TRIVIAL(trace) << "Reading the masterlist version from the svn info output."; - std::string newRevision = GetRevision(output); + 6. Test the masterlist to see if it parses OK (and evals conditions OK). - //Check if revision has changed. If it hasn't, exit early. - if (newRevision == revision) - return revision; - else - revision = newRevision; + 7a. If it has errors, checkout one revision earlier. - try { - //Now test masterlist to see if it parses OK. - BOOST_LOG_TRIVIAL(trace) << "Testing the new masterlist to see if it parses OK."; - YAML::Node mlist = YAML::LoadFile(game.MasterlistPath().string()); + `git checkout HEAD~1 masterlist.yaml` - if (mlist["globals"]) - messages = mlist["globals"].as< list >(); - if (mlist["plugins"]) - plugins = mlist["plugins"].as< list >(); + 8a. Go back to step (5). - for (list::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { - it->EvalAllConditions(game, g_lang_any); - } + 7b. If it doesn't have errors, finish. - for (list::iterator it=messages.begin(), endIt=messages.end(); it != endIt; ++it) { - it->EvalCondition(game, g_lang_any); - } + */ + pointers_struct ptrs; + const git_transfer_progress * stats = NULL; + std::string httpURL; - parsingFailed = false; - - } catch (exception& e) { - parsingFailed = true; - - //Roll back one revision if there's an error. - BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Masterlist revision " + revision + ": " + e.what(); - parsingErrors.push_back(boss::Message(boss::g_message_error, "Masterlist revision " + revision + ": " + e.what())); - - - command = g_path_svn.string() + " update --revision PREV \"" + game.MasterlistPath().string() + "\""; - if (!RunCommand(command, output)) { - BOOST_LOG_TRIVIAL(error) << "Subversion could not update the masterlist. Details: " << output; - throw error(error::subversion_error, "Subversion could not update the masterlist. Details: " + output); - } - } - } while (parsingFailed); - - return revision; - } else { //Git. - /* List of operations (porcelain commands shown, will need to implement using plumbing in the API though): - - 1. Check if there is already a Git repository in the game's BOSS subfolder. - - Since the masterlists will each be in the root of a separate repository, just check if there is a `.git` folder present. - - 2a. If there is, compare its remote URL with the URL that BOSS is currently set to use. - - The current remote can be gotten using `git config --get remote.origin.url`. - - - 3a. If the URLs are different, then update the remote URL to the one given by BOSS. - - The remote URL can be changed using `git remote set-url origin ` - - 2b. If there isn't already a Git repo present, initialise one using the URL given (remembering to split the URL so that it ends in `.git`). - - `git init` - `git remote add origin ` - - - 3b. Now set up sparse checkout support, so that even if the repository has other files, the user's BOSS install only gets the masterlist added to it. - - `git config core.sparseCheckout true` - `echo masterlist.yaml >> .git/info/sparse-checkout` - - 4. Now update the repository. - - `git reset --hard HEAD` is required to undo any roll-backs that were done in the local repository. - `git pull origin master` - - 5. Get the masterlist's commit hash. - - 6. Test the masterlist to see if it parses OK (and evals conditions OK). - - 7a. If it has errors, checkout one revision earlier. - - `git checkout HEAD~1 masterlist.yaml` - - 8a. Go back to step (5). - - 7b. If it doesn't have errors, finish. - - */ - pointers_struct ptrs; - const git_transfer_progress * stats = NULL; - std::string httpURL; - - //If the URL is a HTTPS URL, convert it to a HTTP URL, because the build of libgit2 BOSS uses doesn't support HTTPS. - BOOST_LOG_TRIVIAL(trace) << "Checking URL type."; - httpURL = game.URL(); - if (boost::istarts_with(httpURL, "https")) { - BOOST_LOG_TRIVIAL(info) << "HTTPS URL found. Converting to a HTTP URL."; - httpURL.erase(4, 1); - } - - BOOST_LOG_TRIVIAL(trace) << "Checking for a Git repository."; - - //Checking for a ".git" folder. - if (fs::exists(game.MasterlistPath().parent_path() / ".git")) { - //Repository exists. Open it. - BOOST_LOG_TRIVIAL(trace) << "Existing repository found, attempting to open it."; - handle_error(git_repository_open(&ptrs.repo, game.MasterlistPath().parent_path().string().c_str()), ptrs); - - BOOST_LOG_TRIVIAL(trace) << "Attempting to get info on the repository remote."; - - //Now get remote info. - handle_error(git_remote_load(&ptrs.remote, ptrs.repo, "origin"), ptrs); - - BOOST_LOG_TRIVIAL(trace) << "Getting the remote URL."; - - //Get the remote URL. - const char * url = git_remote_url(ptrs.remote); - - BOOST_LOG_TRIVIAL(trace) << "Checking to see if remote URL matches URL in settings."; - - //Check if the URLs match. - if (url != httpURL) { - BOOST_LOG_TRIVIAL(trace) << "URLs do not match, setting repository URL to URL in settings."; - //The URLs don't match. Change the remote URL to match the one BOSS has. - handle_error(git_remote_set_url(ptrs.remote, httpURL.c_str()), ptrs); - - //Now save change. - handle_error(git_remote_save(ptrs.remote), ptrs); - } - } else { - BOOST_LOG_TRIVIAL(trace) << "Repository doesn't exist, initialising a new repository."; - //Repository doesn't exist. Set up a repository. - handle_error(git_repository_init(&ptrs.repo, game.MasterlistPath().parent_path().string().c_str(), false), ptrs); - - BOOST_LOG_TRIVIAL(trace) << "Setting the new repository's remote."; - - //Now set the repository's remote. - handle_error(git_remote_create(&ptrs.remote, ptrs.repo, "origin", httpURL.c_str()), ptrs); - - BOOST_LOG_TRIVIAL(trace) << "Getting the repository config."; - - handle_error(git_repository_config(&ptrs.cfg, ptrs.repo), ptrs); - - BOOST_LOG_TRIVIAL(trace) << "Setting the repository up for sparse checkouts."; - - handle_error(git_config_set_bool(ptrs.cfg, "core.sparseCheckout", true), ptrs); - - //Now add the masterlist file to the list of files to be checked out. We can actually just overwrite anything that was there previously, since it's only one file. - - BOOST_LOG_TRIVIAL(trace) << "Adding the masterlist to the list of files to be checked out."; - - boss::ofstream out(game.MasterlistPath().parent_path() / ".git/info/sparse-checkout"); - - out << "masterlist.yaml"; - - out.close(); - - } - - BOOST_LOG_TRIVIAL(trace) << "Fetching updates from remote."; - - //Now pull from the remote repository. This involves a fetch followed by a merge. First perform the fetch. - - stats = git_remote_stats(ptrs.remote); - - //Open a connection to the remote repository. - - BOOST_LOG_TRIVIAL(trace) << "Connecting to remote."; - - handle_error(git_remote_connect(ptrs.remote, GIT_DIRECTION_FETCH), ptrs); - - // Download the files needed. Skipping progress info for now, see for an example of how to do that. It uses pthreads, but Boost.Thread should work fine. - - BOOST_LOG_TRIVIAL(trace) << "Downloading changes from remote."; - - handle_error(git_remote_download(ptrs.remote, NULL, NULL), ptrs); - - BOOST_LOG_TRIVIAL(info) << "Received " << stats->indexed_objects << " of " << stats->total_objects << " objects in " << stats->received_bytes << " bytes."; - - bool exitEarly = false; - if (stats->received_bytes == 0) //No update received. - exitEarly = true; - - // Disconnect from the remote repository. - - BOOST_LOG_TRIVIAL(trace) << "Disconnecting from remote."; - - git_remote_disconnect(ptrs.remote); - - // Update references in case they've changed. - - BOOST_LOG_TRIVIAL(trace) << "Updating references for remote."; - - handle_error(git_remote_update_tips(ptrs.remote), ptrs); - - // Now start the merging. Not entirely sure what's going on here, but it looks like libgit2's merge API is incomplete, you can create some git_merge_head objects, but can't do anything with them... - - // Thankfully, we don't really need a merge, we just need to replace whatever's in the working directory with the relevant file from FETCH_HEAD, which was updated in the fetching step before. - - // The porcelain equivalent is `git checkout FETCH_HEAD masterlist.yaml` - - BOOST_LOG_TRIVIAL(trace) << "Setting up checkout parameters."; - - char * paths[] = { "masterlist.yaml" }; - - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; - opts.checkout_strategy = GIT_CHECKOUT_FORCE; //Make sure the existing file gets overwritten. - opts.paths.strings = paths; - opts.paths.count = 1; - - //Next, we need to do a looping checkout / parsing check / roll-back. - - git_object_free(ptrs.obj); //Free object since it will be reallocated in loop. - - bool parsingFailed = false; - unsigned int rollbacks = 0; - char revision[10]; - do { - BOOST_LOG_TRIVIAL(trace) << "Getting the Git object for the tree at FETCH_HEAD - " << rollbacks << "."; - - //Get the commit hash so that we can report the revision if there is an error. - string filespec = "FETCH_HEAD~" + IntToString(rollbacks); - git_object * mlistObj; - - list messages; - list plugins; - handle_error(git_revparse_single(&ptrs.obj, ptrs.repo, filespec.c_str()), ptrs); - - BOOST_LOG_TRIVIAL(trace) << "Checking out the tree at FETCH_HEAD - " << rollbacks << "."; - - //Now we can do the checkout. - handle_error(git_checkout_tree(ptrs.repo, ptrs.obj, &opts), ptrs); - - BOOST_LOG_TRIVIAL(trace) << "Getting the hash for the tree."; - - const git_oid * mlistOid = git_object_id(ptrs.obj); - - BOOST_LOG_TRIVIAL(trace) << "Converting and recording the first 10 hex characters of the hash."; - - git_oid_tostr(&revision[0], 10, mlistOid); - - BOOST_LOG_TRIVIAL(trace) << "Freeing the masterlist object."; - - git_object_free(ptrs.obj); - - if (exitEarly) { - ptrs.free(); - return string(revision); - } - - BOOST_LOG_TRIVIAL(trace) << "Testing masterlist parsing."; - - //Now try parsing the masterlist. - try { - YAML::Node mlist = YAML::LoadFile(game.MasterlistPath().string()); - - if (mlist["globals"]) - messages = mlist["globals"].as< list >(); - if (mlist["plugins"]) - plugins = mlist["plugins"].as< list >(); - - for (list::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { - it->EvalAllConditions(game, g_lang_any); - } - - for (list::iterator it=messages.begin(), endIt=messages.end(); it != endIt; ++it) { - it->EvalCondition(game, g_lang_any); - } - - parsingFailed = false; - - } catch (std::exception& e) { - parsingFailed = true; - rollbacks++; - - //Roll back one revision if there's an error. - BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Masterlist revision " + string(revision) + ": " + e.what(); - - parsingErrors.push_back(boss::Message(boss::g_message_error, "Masterlist revision " + string(revision) + ": " + e.what())); - } - } while (parsingFailed); - - //Finally, free memory. - ptrs.free(); - - return string(revision); + //If the URL is a HTTPS URL, convert it to a HTTP URL, because the build of libgit2 BOSS uses doesn't support HTTPS. + BOOST_LOG_TRIVIAL(trace) << "Checking URL type."; + httpURL = game.URL(); + if (boost::istarts_with(httpURL, "https")) { + BOOST_LOG_TRIVIAL(info) << "HTTPS URL found. Converting to a HTTP URL."; + httpURL.erase(4, 1); } + + BOOST_LOG_TRIVIAL(trace) << "Checking for a Git repository."; + + //Checking for a ".git" folder. + if (fs::exists(game.MasterlistPath().parent_path() / ".git")) { + //Repository exists. Open it. + BOOST_LOG_TRIVIAL(trace) << "Existing repository found, attempting to open it."; + handle_error(git_repository_open(&ptrs.repo, game.MasterlistPath().parent_path().string().c_str()), ptrs); + + BOOST_LOG_TRIVIAL(trace) << "Attempting to get info on the repository remote."; + + //Now get remote info. + handle_error(git_remote_load(&ptrs.remote, ptrs.repo, "origin"), ptrs); + + BOOST_LOG_TRIVIAL(trace) << "Getting the remote URL."; + + //Get the remote URL. + const char * url = git_remote_url(ptrs.remote); + + BOOST_LOG_TRIVIAL(trace) << "Checking to see if remote URL matches URL in settings."; + + //Check if the URLs match. + if (url != httpURL) { + BOOST_LOG_TRIVIAL(trace) << "URLs do not match, setting repository URL to URL in settings."; + //The URLs don't match. Change the remote URL to match the one BOSS has. + handle_error(git_remote_set_url(ptrs.remote, httpURL.c_str()), ptrs); + + //Now save change. + handle_error(git_remote_save(ptrs.remote), ptrs); + } + } else { + BOOST_LOG_TRIVIAL(trace) << "Repository doesn't exist, initialising a new repository."; + //Repository doesn't exist. Set up a repository. + handle_error(git_repository_init(&ptrs.repo, game.MasterlistPath().parent_path().string().c_str(), false), ptrs); + + BOOST_LOG_TRIVIAL(trace) << "Setting the new repository's remote."; + + //Now set the repository's remote. + handle_error(git_remote_create(&ptrs.remote, ptrs.repo, "origin", httpURL.c_str()), ptrs); + + BOOST_LOG_TRIVIAL(trace) << "Getting the repository config."; + + handle_error(git_repository_config(&ptrs.cfg, ptrs.repo), ptrs); + + BOOST_LOG_TRIVIAL(trace) << "Setting the repository up for sparse checkouts."; + + handle_error(git_config_set_bool(ptrs.cfg, "core.sparseCheckout", true), ptrs); + + //Now add the masterlist file to the list of files to be checked out. We can actually just overwrite anything that was there previously, since it's only one file. + + BOOST_LOG_TRIVIAL(trace) << "Adding the masterlist to the list of files to be checked out."; + + boss::ofstream out(game.MasterlistPath().parent_path() / ".git/info/sparse-checkout"); + + out << "masterlist.yaml"; + + out.close(); + + } + + BOOST_LOG_TRIVIAL(trace) << "Fetching updates from remote."; + + //Now pull from the remote repository. This involves a fetch followed by a merge. First perform the fetch. + + stats = git_remote_stats(ptrs.remote); + + //Open a connection to the remote repository. + + BOOST_LOG_TRIVIAL(trace) << "Connecting to remote."; + + handle_error(git_remote_connect(ptrs.remote, GIT_DIRECTION_FETCH), ptrs); + + // Download the files needed. Skipping progress info for now, see for an example of how to do that. It uses pthreads, but Boost.Thread should work fine. + + BOOST_LOG_TRIVIAL(trace) << "Downloading changes from remote."; + + handle_error(git_remote_download(ptrs.remote, NULL, NULL), ptrs); + + BOOST_LOG_TRIVIAL(info) << "Received " << stats->indexed_objects << " of " << stats->total_objects << " objects in " << stats->received_bytes << " bytes."; + + bool exitEarly = false; + if (stats->received_bytes == 0) //No update received. + exitEarly = true; + + // Disconnect from the remote repository. + + BOOST_LOG_TRIVIAL(trace) << "Disconnecting from remote."; + + git_remote_disconnect(ptrs.remote); + + // Update references in case they've changed. + + BOOST_LOG_TRIVIAL(trace) << "Updating references for remote."; + + handle_error(git_remote_update_tips(ptrs.remote), ptrs); + + // Now start the merging. Not entirely sure what's going on here, but it looks like libgit2's merge API is incomplete, you can create some git_merge_head objects, but can't do anything with them... + + // Thankfully, we don't really need a merge, we just need to replace whatever's in the working directory with the relevant file from FETCH_HEAD, which was updated in the fetching step before. + + // The porcelain equivalent is `git checkout FETCH_HEAD masterlist.yaml` + + BOOST_LOG_TRIVIAL(trace) << "Setting up checkout parameters."; + + char * paths[] = { "masterlist.yaml" }; + + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + opts.checkout_strategy = GIT_CHECKOUT_FORCE; //Make sure the existing file gets overwritten. + opts.paths.strings = paths; + opts.paths.count = 1; + + //Next, we need to do a looping checkout / parsing check / roll-back. + + git_object_free(ptrs.obj); //Free object since it will be reallocated in loop. + + bool parsingFailed = false; + unsigned int rollbacks = 0; + char revision[10]; + do { + BOOST_LOG_TRIVIAL(trace) << "Getting the Git object for the tree at FETCH_HEAD - " << rollbacks << "."; + + //Get the commit hash so that we can report the revision if there is an error. + string filespec = "FETCH_HEAD~" + IntToString(rollbacks); + git_object * mlistObj; + + list messages; + list plugins; + handle_error(git_revparse_single(&ptrs.obj, ptrs.repo, filespec.c_str()), ptrs); + + BOOST_LOG_TRIVIAL(trace) << "Checking out the tree at FETCH_HEAD - " << rollbacks << "."; + + //Now we can do the checkout. + handle_error(git_checkout_tree(ptrs.repo, ptrs.obj, &opts), ptrs); + + BOOST_LOG_TRIVIAL(trace) << "Getting the hash for the tree."; + + const git_oid * mlistOid = git_object_id(ptrs.obj); + + BOOST_LOG_TRIVIAL(trace) << "Converting and recording the first 10 hex characters of the hash."; + + git_oid_tostr(&revision[0], 10, mlistOid); + + BOOST_LOG_TRIVIAL(trace) << "Freeing the masterlist object."; + + git_object_free(ptrs.obj); + + if (exitEarly) { + ptrs.free(); + return string(revision); + } + + BOOST_LOG_TRIVIAL(trace) << "Testing masterlist parsing."; + + //Now try parsing the masterlist. + try { + YAML::Node mlist = YAML::LoadFile(game.MasterlistPath().string()); + + if (mlist["globals"]) + messages = mlist["globals"].as< list >(); + if (mlist["plugins"]) + plugins = mlist["plugins"].as< list >(); + + for (list::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { + it->EvalAllConditions(game, g_lang_any); + } + + for (list::iterator it=messages.begin(), endIt=messages.end(); it != endIt; ++it) { + it->EvalCondition(game, g_lang_any); + } + + parsingFailed = false; + + } catch (std::exception& e) { + parsingFailed = true; + rollbacks++; + + //Roll back one revision if there's an error. + BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Masterlist revision " + string(revision) + ": " + e.what(); + + parsingErrors.push_back(boss::Message(boss::g_message_error, "Masterlist revision " + string(revision) + ": " + e.what())); + } + } while (parsingFailed); + + //Finally, free memory. + ptrs.free(); + + return string(revision); } } diff --git a/src/backend/network.h b/src/backend/network.h index dfd53c58..6bf1639f 100644 --- a/src/backend/network.h +++ b/src/backend/network.h @@ -32,37 +32,6 @@ namespace boss { - /* Masterlist updating is carried out using subversion's executables via system() since it's API has too many dependencies to build myself. - - Basic workflow is: - - 1. Check if there's a working copy present in the masterlist's directory. - 2. If not, perform a sparse checkout of the remote folder containing the masterlist, at a folder-only depth. - 3. SVN update the masterlist to the latest available revision. - 4. Check whether the masterlist parses without errors. - 5. If not, revert the masterlist back one revision. Loop back to step 4 until it parses OK. - 6. Record the revision of the masterlist for display in the BOSS report. - - Also need to record error messages for display in the BOSS report, including any parsing errors encountered in step 4, even if a working masterlist was found. - - ```svn info``` on a non-working-copy returns "svn: '.' is not a working copy". - - ```svn co --depth empty http://better-oblivion-sorting-software.googlecode.com/svn/data/boss-skyrim/ .``` - followed by - ```svn update masterlist.txt``` - works as desired when in BOSS's Skyrim folder. - - ```svn update --revision PREV masterlist.txt``` - would then roll the masterlist back one revision. - - ```svn info masterlist.txt``` returns a bunch of info: the line "Revision: XXXX" contains the revision number. - */ - - /* Also looking into adding Git support (issue #29). Git support would involve sparse checkouts of the masterlists into each game folder, with the masterlists sitting in the root of each game's repository online. - - So when using Git, the Game::URL() function would return the path to the repository, and BOSS would expect that the masterlist be in the root directory of the repository, which is not unreasonable. - */ - std::string UpdateMasterlist(Game& game, std::list& parsingErrors, std::list& plugins, std::list& messages); } #endif diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 7dac0547..7c98801c 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -640,7 +640,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { try { system(command.c_str()); - // if (RunCommand(command, output)) //This hangs for graphvis, for some reason. Works for svn though... + // if (RunCommand(command, output)) //This hangs for graphvis, for some reason. fs::remove(temp); } catch(boss::error& e) { messages.push_back(boss::Message(boss::g_message_error, (format(loc::translate("Failed to generate graph image. Details: %1%")) % e.what()).str())); diff --git a/src/installer.nsi b/src/installer.nsi index 40ebcb19..d25d246a 100644 --- a/src/installer.nsi +++ b/src/installer.nsi @@ -269,10 +269,6 @@ FunctionEnd SetOutPath "$INSTDIR\resources\svgweb" File "..\resources\svgweb\*" - ;Install svn files. - SetOutPath "$INSTDIR\resources\svn" - File "..\resources\svn\*" - ;Install resource files. SetOutPath "$INSTDIR\resources" File "..\resources\icon.ico" @@ -392,9 +388,6 @@ FunctionEnd Delete "$INSTDIR\resources\script.js" Delete "$INSTDIR\resources\style.css" - ;Remove SVN files. - RMDir /r "$INSTDIR\resources\svn" - ;Remove graphvis files. RMDir /r "$INSTDIR\resources\graphvis"