summary history branches tags files
commit:05032516a2ba192d4ddfa0e463de2baf7763e9a3
author:Trevor Bentley
committer:Trevor Bentley
date:Thu Jan 16 02:26:29 2025 +0100
parents:44aa26733d447f2474d8a8ece35f997ffbfda6aa
copy per-repo asset files by git content rather than file path
diff --git a/src/generate.rs b/src/generate.rs
line changes: +38/-15
index b81e2a4..5421196
--- a/src/generate.rs
+++ b/src/generate.rs
@@ -996,26 +996,49 @@ impl GitsyGenerator {
             Some(repo_desc) => {
                 let parsed_repo = parsed_repo.expect("ERROR: attempted to fill repo assets without a repository");
                 let repo = repo.expect("ERROR: attempted to fill repo assets without a repository");
-                //let repo_path = repo.path().to_str().expect("ERROR: repository has no path!");
                 if repo_desc.asset_files.is_some() {
                     let target_dir = self.settings.outputs.repo_assets::<GitFile>(Some(&parsed_repo), None);
                     for src_file in repo_desc.asset_files.as_ref().unwrap() {
-                        let src_file = self.settings.outputs.asset(src_file, Some(parsed_repo), Some(repo));
+                        // Read the asset file contents from the git repo
+                        let src_file = PathBuf::from(src_file);
+                        let src_contents = self.settings.outputs
+                            .asset_contents(&src_file, Some(parsed_repo), Some(repo))?;
+
+                        // Determine the output path
                         let mut dst_file = PathBuf::from(&target_dir);
-                        dst_file.push(src_file.file_name().expect(&format!(
-                            "Failed to copy repo asset file: {} ({})",
-                            src_file.display(),
-                            repo_desc.name.as_deref().unwrap_or_default()
-                        )));
-                        std::fs::copy(&src_file, &dst_file).expect(&format!(
-                            "Failed to copy repo asset file: {} ({})",
-                            src_file.display(),
-                            repo_desc.name.as_deref().unwrap_or_default()
-                        ));
+                        let basename = src_file.file_name()
+                            .ok_or(GitsyError::kind(
+                                GitsyErrorKind::Settings,
+                                Some(&format!(
+                                    "ERROR: repo asset file missing filename: {}",
+                                    src_file.display()
+                                ))))?;
+                        dst_file.push(basename);
+
+                        // Open destination file for writing
+                        let mut file = File::create(&dst_file)
+                            .map_err(|e| GitsyError::sourced_kind(
+                                GitsyErrorKind::Settings,
+                                Some(&format!(
+                                    "ERROR: unable to open repo asset file for writing: {}",
+                                    dst_file.display()
+                                )),
+                                e))?;
+
+                        // Write to disk
+                        file.write(&src_contents)
+                            .map_err(|e| GitsyError::sourced_kind(
+                                GitsyErrorKind::Settings,
+                                Some(&format!(
+                                    "ERROR: unable to write repo asset file: {}",
+                                    dst_file.display()
+                                )),
+                                e))?;
+
                         if let Ok(meta) = std::fs::metadata(dst_file) {
                             bytes += meta.len() as usize;
                         }
-                        loud!(" - copied asset: {}", src_file.display());
+                        loud!(" - copied repo asset: {}", src_file.display());
                     }
                 }
             }
@@ -1023,7 +1046,7 @@ impl GitsyGenerator {
                 if self.settings.asset_files.is_some() {
                     let target_dir = self.settings.outputs.global_assets::<GitFile>(None, None);
                     for src_file in self.settings.asset_files.as_ref().unwrap() {
-                        let src_file = self.settings.outputs.asset(src_file, None, None);
+                        let src_file = self.settings.outputs.asset_path(src_file, None, None);
                         let mut dst_file = PathBuf::from(&target_dir);
                         dst_file.push(
                             src_file
@@ -1035,7 +1058,7 @@ impl GitsyGenerator {
                         if let Ok(meta) = std::fs::metadata(dst_file) {
                             bytes += meta.len() as usize;
                         }
-                        loud!(" - copied asset: {}", src_file.display());
+                        loud!(" - copied global asset: {}", src_file.display());
                     }
                 }
             }

diff --git a/src/settings.rs b/src/settings.rs
line changes: +48/-2
index 02ec29e..0df1912
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -21,7 +21,7 @@
  * along with Itsy-Gitsy.  If not, see <http://www.gnu.org/licenses/>.
  */
 use crate::git::GitRepo;
-use crate::util::SafePathVar;
+use crate::util::{GitsyError, GitsyErrorKind, SafePathVar};
 use crate::{error, louder};
 use clap::Parser;
 use git2::Repository;
@@ -276,7 +276,53 @@ impl GitsySettingsOutputs {
         }
     }
 
-    pub fn asset<P: AsRef<Path>>(&self, asset: &P, parsed_repo: Option<&GitRepo>, repo: Option<&Repository>) -> PathBuf {
+    pub fn asset_contents<P: AsRef<Path>>(&self, asset: &P,
+                                          parsed_repo: Option<&GitRepo>,
+                                          repo: Option<&Repository>) -> Result<Vec<u8>, GitsyError> {
+        let tmpl_path = asset.as_ref().to_path_buf();
+        let asset_path = substitute_path_vars(&tmpl_path, parsed_repo, Some(self));
+
+        // try to get a copy of the file contents from the git repo
+        match parsed_repo {
+            Some(p_repo) => {
+                match p_repo.all_files.iter().find(|f| f.path == asset_path.to_string_lossy()) {
+                    Some(f) => {
+                        match repo {
+                            Some(repo) => {
+                                let oid = git2::Oid::from_str(&f.id)?;
+                                // the only happy path is if the repo exists, the file exists, and
+                                // the contents load.
+                                Ok(repo.find_blob(oid)?.content().to_owned())
+                            },
+                            None => {
+                                Err(GitsyError::kind(
+                                    GitsyErrorKind::Settings,
+                                    Some(&format!(
+                                        "ERROR: not provided git repo for asset file: {}",
+                                        asset.as_ref().to_string_lossy()
+                                    ))))
+                            }
+                        }
+                    },
+                    _ => {
+                        Err(GitsyError::kind(
+                            GitsyErrorKind::Settings,
+                            Some(&format!(
+                                "ERROR: could not find repo asset file: {}",
+                                asset.as_ref().to_string_lossy()
+                            ))))
+                    }
+                }
+            },
+            None => {
+                Err(GitsyError::kind(
+                    GitsyErrorKind::Settings,
+                    Some("ERROR: requested per-repo asset file without a repo.")))
+            }
+        }
+    }
+
+    pub fn asset_path<P: AsRef<Path>>(&self, asset: &P, parsed_repo: Option<&GitRepo>, repo: Option<&Repository>) -> PathBuf {
         let tmpl_path = asset.as_ref().to_path_buf();
         let asset_path = substitute_path_vars(&tmpl_path, parsed_repo, Some(self));
         let full_path = match repo {