yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
version_manager.cc
Go to the documentation of this file.
2
3#include <array>
4#include <chrono>
5#include <cstdio>
6#include <filesystem>
7#include <iomanip>
8#include <iostream>
9#include <memory>
10#include <sstream>
11
12#include "absl/strings/str_format.h"
13#include "util/log.h"
14
15namespace yaze {
16namespace core {
17
18namespace fs = std::filesystem;
19
21 : project_(project) {}
22
24 if (!project_ || project_->git_repository.empty())
25 return false;
26
27 fs::path git_dir = fs::path(project_->git_repository) / ".git";
28 return fs::exists(git_dir);
29}
30
32 if (!project_)
33 return absl::InvalidArgumentError("No project context");
34
35 // Ensure git repository path is set (default to project root)
36 if (project_->git_repository.empty()) {
37 project_->git_repository = "."; // Default to current dir
38 }
39
40 if (IsGitInitialized())
41 return absl::OkStatus();
42
43 return GitInit();
44}
45
46absl::StatusOr<SnapshotResult> VersionManager::CreateSnapshot(
47 const std::string& message) {
48 if (!project_)
49 return absl::InvalidArgumentError("No project context");
50
51 SnapshotResult result;
52 result.success = false;
53
54 // 1. Ensure Git is ready
55 if (!IsGitInitialized()) {
56 auto status = InitializeGit();
57 if (!status.ok())
58 return status;
59 }
60
61 // 2. Commit Code
62 auto add_status = GitAddAll();
63 if (!add_status.ok())
64 return add_status;
65
66 auto commit_status = GitCommit(message);
67 if (!commit_status.ok()) {
68 // It's possible there were no changes to commit, which is fine.
69 // We continue to ROM backup.
70 LOG_INFO("VersionManager", "Git commit skipped (no changes?)");
71 }
72
74
75 // 3. Backup ROM Artifact
76 // Generate timestamp
77 auto now = std::chrono::system_clock::now();
78 auto time_t = std::chrono::system_clock::to_time_t(now);
79 std::stringstream ss;
80 ss << std::put_time(std::localtime(&time_t), "%Y%m%d_%H%M%S");
81 std::string timestamp = ss.str();
82
83 auto backup_status = BackupRomArtifact(timestamp);
84 if (backup_status.ok()) {
85 result.rom_backup_path = "backups/snapshots/rom_" + timestamp + ".sfc";
86 } else {
87 LOG_ERROR("VersionManager", "Failed to backup ROM: %s",
88 backup_status.ToString().c_str());
89 }
90
91 result.success = true;
92 result.message = "Snapshot created successfully.";
93
94 // Update project build hash
96
97 return result;
98}
99
101 return GitRevParseHead();
102}
103
104std::vector<std::string> VersionManager::GetHistory(int limit) const {
105 std::string cmd =
106 absl::StrFormat("git log -n %d --pretty=format:\"%%h %%s\"", limit);
107 auto output = RunCommandOutput(cmd);
108 if (!output.ok())
109 return {};
110
111 // Split lines (basic)
112 std::vector<std::string> history;
113 std::istringstream stream(*output);
114 std::string line;
115 while (std::getline(stream, line)) {
116 if (!line.empty())
117 history.push_back(line);
118 }
119 return history;
120}
121
122// ============================================================================
123// Git Implementation
124// ============================================================================
125
127 return RunCommand("git init");
128}
129
131 // We specifically want to avoid adding the ROMs if they aren't ignored.
132 // But assuming user has .gitignore setup properly for Roms/*.sfc
133 return RunCommand("git add .");
134}
135
136absl::Status VersionManager::GitCommit(const std::string& message) {
137 std::string sanitized_msg = message;
138 // Basic sanitization for shell command (replace quotes)
139 std::replace(sanitized_msg.begin(), sanitized_msg.end(), '"', '\'');
140
141 return RunCommand(absl::StrFormat("git commit -m \"%s\"", sanitized_msg));
142}
143
145 auto res = RunCommandOutput("git rev-parse --short HEAD");
146 if (res.ok()) {
147 // Trim newline
148 std::string hash = *res;
149 hash.erase(hash.find_last_not_of(" \n\r\t") + 1);
150 return hash;
151 }
152 return "";
153}
154
155// ============================================================================
156// ROM Backup Implementation
157// ============================================================================
158
160 const std::string& timestamp_str) {
161 if (project_->output_folder.empty() || project_->build_target.empty()) {
162 return absl::FailedPreconditionError(
163 "Project output folder or build target not defined");
164 }
165
166 fs::path rom_path(project_->build_target);
167
168 // Resolve relative paths against project directory
169 fs::path project_dir = fs::path(project_->filepath).parent_path();
170 fs::path abs_rom_path = project_dir / rom_path;
171
172 if (!fs::exists(abs_rom_path)) {
173 return absl::NotFoundError(
174 absl::StrFormat("Built ROM not found at %s", abs_rom_path.string()));
175 }
176
177 // Determine backup location
178 fs::path backup_dir = project_dir / "backups" / "snapshots";
179 std::error_code ec;
180 fs::create_directories(backup_dir, ec);
181 if (ec)
182 return absl::InternalError("Could not create backup directory");
183
184 std::string filename = absl::StrFormat("rom_%s.sfc", timestamp_str);
185 fs::path backup_path = backup_dir / filename;
186
187 // Copy file
188 fs::copy_file(abs_rom_path, backup_path, fs::copy_options::overwrite_existing,
189 ec);
190 if (ec) {
191 return absl::InternalError(
192 absl::StrFormat("Failed to copy ROM: %s", ec.message()));
193 }
194
195 return absl::OkStatus();
196}
197
198// ============================================================================
199// System Helpers
200// ============================================================================
201
202absl::Status VersionManager::RunCommand(const std::string& cmd) {
203#if defined(YAZE_IOS)
204 (void)cmd;
205 return absl::FailedPreconditionError(
206 "Command execution is not available on iOS");
207#else
208 // Execute command in project root
209 std::string full_cmd;
210
211 fs::path project_dir = fs::path(project_->filepath).parent_path();
212 if (!project_dir.empty()) {
213 full_cmd = absl::StrFormat("cd \"%s\" && %s", project_dir.string(), cmd);
214 } else {
215 full_cmd = cmd;
216 }
217
218 int ret = std::system(full_cmd.c_str());
219 if (ret != 0) {
220 return absl::InternalError(
221 absl::StrFormat("Command failed: %s (Exit code %d)", cmd, ret));
222 }
223 return absl::OkStatus();
224#endif
225}
226
227absl::StatusOr<std::string> VersionManager::RunCommandOutput(
228 const std::string& cmd) const {
229#if defined(YAZE_IOS)
230 (void)cmd;
231 return absl::FailedPreconditionError(
232 "Command execution is not available on iOS");
233#else
234 std::string full_cmd;
235 fs::path project_dir = fs::path(project_->filepath).parent_path();
236 if (!project_dir.empty()) {
237 full_cmd = absl::StrFormat("cd \"%s\" && %s", project_dir.string(), cmd);
238 } else {
239 full_cmd = cmd;
240 }
241
242 std::array<char, 128> buffer;
243 std::string result;
244#ifdef _WIN32
245 std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(full_cmd.c_str(), "r"),
246 _pclose);
247#else
248 std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(full_cmd.c_str(), "r"),
249 pclose);
250#endif
251 if (!pipe) {
252 return absl::InternalError("popen() failed!");
253 }
254 while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
255 result += buffer.data();
256 }
257 return result;
258#endif
259}
260
261} // namespace core
262} // namespace yaze
absl::Status BackupRomArtifact(const std::string &timestamp_str)
absl::Status GitCommit(const std::string &message)
std::vector< std::string > GetHistory(int limit=10) const
project::YazeProject * project_
VersionManager(project::YazeProject *project)
absl::StatusOr< std::string > RunCommandOutput(const std::string &cmd) const
std::string GetCurrentHash() const
absl::StatusOr< SnapshotResult > CreateSnapshot(const std::string &message)
absl::Status RunCommand(const std::string &cmd)
std::string GitRevParseHead() const
#define LOG_ERROR(category, format,...)
Definition log.h:109
#define LOG_INFO(category, format,...)
Definition log.h:105
Modern project structure with comprehensive settings consolidation.
Definition project.h:84
std::string git_repository
Definition project.h:124
std::string output_folder
Definition project.h:117
std::string last_build_hash
Definition project.h:126