Search code examples
sshgitlabgitlab-ciln

ln not always working over SSH


I am making a deployment script using GitLab's CD. I've got a script:

- ssh USER@HOST "cd domains/$DOMAIN/ && mkdir build-$CI_JOB_ID"
- rsync -ar --port=22 * USER@HOST :domains/$DOMAIN/build-$CI_JOB_ID
- ssh USER@HOST  "cd domains/$DOMAIN/ && ln -sfv build-$CI_JOB_ID/public public_html && ls -la"
- ssh USER@HOST  "cd domains/$DOMAIN/ && ls | grep '^build\-.*$' | grep -Ev '^build-$CI_JOB_ID$' | xargs rm -rf"

everything works fine but not ln command. It works only 50% of the time. Here are logs from ` jobs runnning one after another.

Job 1 with $CI_JOB_ID = 76337215 worked properly. Link is correct.

$ ssh USER@HOST "cd domains/$DOMAIN/ && ln -sfv build-$CI_JOB_ID/public public_html && ls -la"
public_html -> build-76337215/public
total 20
drwx--x--x   5 USER  1000   7 Jun 20 22:15 .
drwx--x--x  23 USER  1000  23 Jun 19 16:34 ..
-rw-r--r--   1 USER  1000  39 Jun 17 22:12 .htaccess
drwxr-xr-x  12 USER  1000  20 Jun 20 22:07 build-76335972
drwxr-xr-x  12 USER  1000  20 Jun 20 22:14 build-76337215
drwxr-xr-x   2 USER  1000   4 Jun 20 11:48 logs
lrwxr-xr-x   1 USER  1000  21 Jun 20 22:15 public_html -> build-76337215/public

Job 2 with $CI_JOB_ID = 76339729 did not work. Link is still to old 76337215 from Job 1.

$ ssh USER@HOST "cd domains/$DOMAIN/ && ln -sfv build-$CI_JOB_ID/public public_html && ls -la"
public_html/public -> build-76339729/public
total 20
drwx--x--x   5 USER  1000   7 Jun 20 22:28 .
drwx--x--x  23 USER  1000  23 Jun 19 16:34 ..
-rw-r--r--   1 USER  1000  39 Jun 17 22:12 .htaccess
drwxr-xr-x  12 USER  1000  20 Jun 20 22:14 build-76337215
drwxr-xr-x  12 USER  1000  20 Jun 20 22:28 build-76339729
drwxr-xr-x   2 USER  1000   4 Jun 20 11:48 logs
lrwxr-xr-x   1 USER  1000  21 Jun 20 22:15 public_html -> build-76337215/public

What I am doing wrong? Why it is not working 100% of the time?


Solution

  • The problem is that if public_html already exists and is a directory (or a symlink to a directory), then your ln command creates a new link in that directory, rather than replacing public_html.

    Use the -T option to avoid this:

    ln -sfTv build-$CI_JOB_ID/public public_html
    

    alternately, you can use the -n option to not dereference a link

    ln -sfnv build-$CI_JOB_ID/public public_html
    

    this will replace public_html if it is a symlink and create the symlink in the subdirectory if it is a real directory (-T would give an error in the latter case).