@@ -56,13 +56,68 @@ function dna::get_super_project_acronym() {
5656 return 0
5757}
5858
59+ function dna::portable_copy() {
60+ # Copy function using rsync with backup functionality
61+ # Arguments: source destination
62+ local source=" $1 "
63+ local destination=" $2 "
64+ local super_project_root=" ${3:- $(pwd)} "
65+
66+ # Use rsync with backup functionality (no --update flag to preserve existing files)
67+ if [[ -d " $source " ]]; then
68+ # For directories, ensure trailing slash for proper rsync behavior
69+ rsync --backup --suffix=' .old' --recursive " ${source%/ } /" " $destination " || return 1
70+ else
71+ # For files
72+ rsync --backup --suffix=' .old' " $source " " $destination " || return 1
73+ fi
74+
75+ # Validate file ownership and permissions match the super project
76+ dna::validate_file_ownership_and_permissions " $destination " " $super_project_root " || return 1
77+
78+ return 0
79+ }
80+
81+ function dna::validate_file_ownership_and_permissions() {
82+ # Validate that copied files/directories have ownership and permissions matching the super project
83+ # Arguments: target_path super_project_root
84+ local target_path=" $1 "
85+ local super_project_root=" $2 "
86+
87+ # Get super project ownership and permissions
88+ local super_project_owner
89+ local super_project_group
90+ # local super_project_perms
91+
92+ super_project_owner=$( stat -c ' %U' " $super_project_root " 2> /dev/null || stat -f ' %Su' " $super_project_root " 2> /dev/null)
93+ super_project_group=$( stat -c ' %G' " $super_project_root " 2> /dev/null || stat -f ' %Sg' " $super_project_root " 2> /dev/null)
94+ # super_project_perms=$(stat -c '%a' "$super_project_root" 2>/dev/null || stat -f '%A' "$super_project_root" 2>/dev/null)
95+
96+ # Recursively fix ownership and permissions for the target path
97+ if [[ -d " $target_path " ]]; then
98+ # For directories, apply to all contents
99+ find " $target_path " -type f -exec chown " ${super_project_owner} :${super_project_group} " {} \; 2> /dev/null || true
100+ find " $target_path " -type d -exec chown " ${super_project_owner} :${super_project_group} " {} \; 2> /dev/null || true
101+ find " $target_path " -type f -exec chmod 644 {} \; 2> /dev/null || true
102+ find " $target_path " -type d -exec chmod 755 {} \; 2> /dev/null || true
103+ else
104+ # For files
105+ chown " ${super_project_owner} :${super_project_group} " " $target_path " 2> /dev/null || true
106+ chmod 644 " $target_path " 2> /dev/null || true
107+ fi
108+
109+ return 0
110+ }
111+
59112
60113function dna::init_command() {
61114 local super_project_root
62115 super_project_root=$( pwd)
63116 local line_format=" ${MSG_LINE_CHAR_BUILDER_LVL2} "
64117 local line_style=" ${MSG_LINE_STYLE_LVL2} "
65118
119+ # rsync is now a required dependency and should be available
120+
66121 # ....cli......................................................................................
67122 while [[ $# -gt 0 ]]; do
68123 case " $1 " in
@@ -87,7 +142,7 @@ function dna::init_command() {
87142
88143 # Check if .dockerized_norlab already exists
89144 if [[ -d " .dockerized_norlab" ]]; then
90- n2st::print_msg_warning " This project is already DNA initialized since ${MSG_DIMMED_FORMAT} .dockerized_norlab${MSG_END_FORMAT} directory already exists.\nIf you continue, missing file will be created and existing one will be updated ."
145+ n2st::print_msg_warning " This project is already DNA initialized since ${MSG_DIMMED_FORMAT} .dockerized_norlab${MSG_END_FORMAT} directory already exists.\nIf you continue, existing file and directories with the same name will be safeguarded with the suffix '.old', not overriden ."
91146 read -r -n 1 -p " Do you want to continue [y/N]" option_update
92147 if [[ " ${option_update} " == " y" || " ${option_update} " == " Y" ]]; then
93148 :
@@ -113,12 +168,12 @@ function dna::init_command() {
113168 n2st::print_msg " Initializing DNA project: ${super_project_name} in ${super_project_root} "
114169
115170 # Copy template files
116- sudo cp --update -r " ${DNA_LIB_PATH} /template/.dockerized_norlab/" . || return 1
171+ dna::portable_copy " ${DNA_LIB_PATH} /template/.dockerized_norlab/" . " $super_project_root " || return 1
117172
118173 cd " ${super_project_root} /.dockerized_norlab/" || return 1
119174
120175 # Rename the super project DNA meta .env file
121- sudo mv --force " ${super_project_root} /.dockerized_norlab/.env.PLACEHOLDER_SUPER_PROJECT_NAME" " ${super_project_root} /.dockerized_norlab/.env.${super_project_name} " || return 1
176+ mv -f " ${super_project_root} /.dockerized_norlab/.env.PLACEHOLDER_SUPER_PROJECT_NAME" " ${super_project_root} /.dockerized_norlab/.env.${super_project_name} " || return 1
122177
123178 # Replace placeholders in the .env.dna file
124179 cd " ${super_project_root} /.dockerized_norlab/configuration/" || return 1
@@ -149,28 +204,28 @@ function dna::init_command() {
149204 cd " ${super_project_root} " || return 1
150205
151206 {
152- sudo mkdir -p artifact/optuna_storage &&
153- sudo mkdir -p external_data &&
154- sudo mkdir -p src/launcher/configs &&
155- sudo mkdir -p src/dna_example &&
156- sudo mkdir -p tests/test_dna_example
207+ mkdir -p artifact/optuna_storage &&
208+ mkdir -p external_data &&
209+ mkdir -p src/launcher/configs &&
210+ mkdir -p src/dna_example &&
211+ mkdir -p tests/test_dna_example
157212 } || return 1
158213
159- sudo cp --update -r " ${DNA_LIB_PATH} /template/artifact/README.md" artifact/ || return 1
160- sudo cp --update -r " ${DNA_LIB_PATH} /template/artifact/optuna_storage/README.md" artifact/optuna_storage/ || return 1
161- sudo cp --update -r " ${DNA_LIB_PATH} /template/external_data/README.md" external_data/ || return 1
162- sudo cp --update -r " ${DNA_LIB_PATH} /template/src/launcher" src/ || return 1
163- sudo cp --update -r " ${DNA_LIB_PATH} /template/src/dna_example" src/ || return 1
214+ dna::portable_copy " ${DNA_LIB_PATH} /template/artifact/README.md" artifact/ " $super_project_root " || return 1
215+ dna::portable_copy " ${DNA_LIB_PATH} /template/artifact/optuna_storage/README.md" artifact/optuna_storage/ " $super_project_root " || return 1
216+ dna::portable_copy " ${DNA_LIB_PATH} /template/external_data/README.md" external_data/ " $super_project_root " || return 1
217+ dna::portable_copy " ${DNA_LIB_PATH} /template/src/launcher" src/ " $super_project_root " || return 1
218+ dna::portable_copy " ${DNA_LIB_PATH} /template/src/dna_example" src/ " $super_project_root " || return 1
164219 if [[ ! -f " src/README.md" ]]; then
165- sudo cp --update -r " ${DNA_LIB_PATH} /template/src/README.md" src/ || return 1
220+ dna::portable_copy " ${DNA_LIB_PATH} /template/src/README.md" src/ " $super_project_root " || return 1
166221 fi
167- sudo cp --update -r " ${DNA_LIB_PATH} /template/tests" . || return 1
222+ dna::portable_copy " ${DNA_LIB_PATH} /template/tests" . " $super_project_root " || return 1
168223
169224 # ....Create root README.md files if it does't exist...........................................
170225 cd " ${super_project_root} " || return 1
171226
172227 if [[ ! -f " README.md" ]]; then
173- sudo touch " README.md" || return 1
228+ touch " README.md" || return 1
174229 cat > " README.md" << EOF
175230# ${super_project_name}
176231
185240
186241 if [[ ! -f " .gitignore" ]]; then
187242 # Case: file does not exist => copy template
188- sudo cp " ${DNA_LIB_PATH} /template/.gitignore" .gitignore || return 1
243+ dna::portable_copy " ${DNA_LIB_PATH} /template/.gitignore" .gitignore " $super_project_root " || return 1
189244 else
190245 # Case: file exist => append required .gitignore entries
191246 cat >> " .gitignore" << EOF
207262
208263 if [[ ! -f " .dockerignore" ]]; then
209264 # Case: file does not exist => copy template
210- sudo cp " ${DNA_LIB_PATH} /template/.dockerignore" .dockerignore || return 1
265+ dna::portable_copy " ${DNA_LIB_PATH} /template/.dockerignore" .dockerignore " $super_project_root " || return 1
211266 else
212267 # Case: file exist => append required .gitignore entries
213268 cat >> " .dockerignore" << EOF
0 commit comments