/bin/sh: 1: Syntax error: Unterminated quoted string in dockerfile - bash

I need to have variable inside file constants.js with quotes ('here variable'). Trying do it in dockerfile, but getting his error, can't figure it out. Tried escaping quotes, but its not working. Doing it with double quotes - works, but I need single. Any idea?
FROM debian:stretch-slim
ENV GIT_BRANCH=develop
ENV GIT_COMMIT_TAG=26bf94075e6d5f2c575a291680e905e15aa0c81f
COPY constants.js .
RUN { \
/bin/bash -c '{ \
sed -i -r "s/^([[:blank:]]*APP_VERSION:[[:blank:]]*).*/\1\'\${GIT_COMMIT_TAG:0:7}\',/" constants.js ; \
}' ; \
}
CMD ["sleep", "inf"]
File constants.js looks like:
const constants = {
APP_NAME: 'app',
APP_VERSION: '10.0.0',
};
module.exports = {
constants,
};
#UPDATE1
Longer
/bin/bash -c
in Dockerfile:
if [ ! -z "$GIT_BRANCH" ]; then \
GIT_BRANCH="${GIT_BRANCH}/" ; \
fi ; \
echo "${GIT_BRANCH}${GIT_COMMIT_TAG:0:7}" > .release.txt ; \
sed -i -r "s/^([[:blank:]]*APP_VERSION:[[:blank:]]*).*/\1'\${GIT_COMMIT_TAG:0:7}',/" constants.js ; \
appver=$(awk '"'"'/APP_VERSION/ { print $2 }'"'"' constants.js) ; \
echo "${appver:1:-2}" > .appversion ; \
so thats why I use bash -c.

You don't need to specify /bin/bash -c. Instead, just use RUN and immediately the command you need to execute. It is necessary to remove a few extra /. The final Dockerfile is given below
Dockerfile
FROM debian:stretch-slim
ENV GIT_BRANCH=develop
ENV GIT_COMMIT_TAG=26bf94075e6d5f2c575a291680e905e15aa0c81f
COPY constants.js .
RUN sed -i -r "s/^([[:blank:]]*APP_VERSION:[[:blank:]]*).*/\1\'$GIT_COMMIT_TAG:0:7',/" constants.js ;
CMD ["sleep", "inf"]
Resulted constants.js file
const constants = {
APP_NAME: 'app',
APP_VERSION: '26bf94075e6d5f2c575a291680e905e15aa0c81f:0:7',
};
module.exports = {
constants,
};
UPD #1
For execute some logic that does not fit on one line, the best solution would be to put it in a separate bash script.
Thus, you add setup.sh and change the Dockerfile
Dockerfile
FROM debian:stretch-slim
ENV GIT_BRANCH=develop
ENV GIT_COMMIT_TAG=26bf94075e6d5f2c575a291680e905e15aa0c81f
COPY ["constants.js", "setup.sh", "./"]
RUN chmod +x ./setup.sh && ./setup.sh
setup.sh
if [ ! -z "$GIT_BRANCH" ]; then
GIT_BRANCH="${GIT_BRANCH}/"
fi
CUTTED_GIT_COMMIT_TAG=$(echo "$GIT_COMMIT_TAG" | cut -c -7)
echo "${GIT_BRANCH}${CUTTED_GIT_COMMIT_TAG}" > .release.txt
sed -i -r "s/^([[:blank:]]*APP_VERSION:[[:blank:]]*).*/\1\'${CUTTED_GIT_COMMIT_TAG}',/" constants.js
APP_VERSION=$(awk '"'"'/APP_VERSION/ { print $2 }'"'"' constants.js)
CUTTED_APP_VERSION=$(echo "$appver" | cut -c 2- | rev | cut -c 1- | rev)
echo $CUTTED_APP_VERSION > .appversion

OK, so sed should looks like this:
sed -r -i "/APP_VERSION/ s/'\''(.*)'\''/'\''${GIT_COMMIT_TAG:0:7}'\''/" constants.js ; \
Then its working like it should.

Related

How can I pass a directory argument to this fzf/ripgrep bash script for previewing search results?

fzf/ripgrep can search an alternate directory from the command line with something like.
rg something ./sompath | fzf
The fzf documentation has a nice little bash script for generating a preview of rg's results that I call with a shell alias and then open the file with vim:
#!/usr/bin/env bash
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
INITIAL_QUERY="${*:-}"
IFS=: read -ra selected < <(
FZF_DEFAULT_COMMAND="$RG_PREFIX $(printf %q "$INITIAL_QUERY")" \
fzf --ansi \
--color "hl:-1:underline,hl+:-1:underline:reverse" \
--disabled --query "$INITIAL_QUERY" \
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
--bind "alt-enter:unbind(change,alt-enter)+change-prompt(2. fzf> )+enable-search+clear-query" \
--prompt '1. ripgrep> ' \
--delimiter : \
--preview "$BAT_CMD --color=always {1} --highlight-line {2}" \
--preview-window 'up,60%,border-bottom,+{2}+3/3,~3'
)
[ -n "${selected[0]}" ] && nvim "${selected[0]}" "+${selected[1]}"
It's very cool and works great. Unfortunately, there's no way to pass in a directory argument into this script. So you have to cd into the desired directory, do the search, and cd back.
Instead, I'd like to do something like this:
search_script initial_query ./some_dir
But my bash skills are weak and I'm not really sure what the best approach is for processing an optional directory argument.
The script has to somehow be smart enough to recognize when a directory argument is passed and when it isn't. I'm not sure if some kind of option string like --dir is the best way to go or what. And I'm wondering if I might be missing something really obvious solution, too.
Thanks.
I was able to cargo cult some little bash snippets and piece something that seems to do the trick and detect if last argument is a directory:
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
INITIAL_QUERY="${*:-}"
last="${#: -1}"
if [[ -d $last ]]; then
INITIAL_QUERY="${#:1:$#-1}";
dir=$last
echo $INITIAL_QUERY
fi
IFS=: read -ra selected < <(
FZF_DEFAULT_COMMAND="$RG_PREFIX $(printf %q "$INITIAL_QUERY") $dir" \
fzf --ansi \
--color "hl:-1:underline,hl+:-1:underline:reverse" \
--disabled --query "$INITIAL_QUERY" \
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} $dir || true" \
--bind "alt-enter:unbind(change,alt-enter)+change-prompt(2. fzf> )+enable-search+clear-query" \
--prompt '1. ripgrep> ' \
--delimiter : \
--preview "bat --color=always {1} --highlight-line {2}" \
--preview-window 'up,60%,border-bottom,+{2}+3/3,~3'
)
[ -n "${selected[0]}" ] && nvim "${selected[0]}" "+${selected[1]}"
If you think there is a simpler way or a better, I'd love to hear.

How to use bash variables in Jenkins multi-line shell script

I have unsuccessfully tried to use bash variables in Jenkins pipeline.
My first attempt
sh """#!/bin/bash
for file in *.map; do
filename=`basename $file .map`
echo "##### uploading ${$filename}"
curl -X POST ${SERVER_URL}/assets/v1/sourcemaps \
-F service_name="${SERVICE_NAME}" \
-F service_version="${revision}" \
-F bundle_filepath="${main_url}${filename}" \
-F sourcemap="#${filename}.map" &
done
wait
"""
Resulted in exception: MissingPropertyException: No such property: file
The second attempt, after seeing this answer https://stackoverflow.com/a/35047530/9590251
sh """#!/bin/bash
for file in *.map; do
filename=`basename \$file .map`
echo "##### uploading \$filename"
curl -X POST ${SERVER_URL}/assets/v1/sourcemaps \
-F service_name="${SERVICE_NAME}" \
-F service_version="${revision}" \
-F bundle_filepath="${main_url}\$filename" \
-F sourcemap="#\$filename.map" &
done
wait
"""
Simply omitted bash variables. So $filename was empty.
How do I need to property encode bash variables in this scenario?
Try this:
sh """#!/bin/bash
set -x
for file in *.map; do
filename="\$(basename "\$file" .map)"
echo "Uploading \$filename"
curl -X POST "${SERVER_URL}/assets/v1/sourcemaps" \
-F service_name="${SERVICE_NAME}" \
-F service_version="${revision}" \
-F bundle_filepath="${main_url}\$filename" \
-F sourcemap="#\${filename}.map" &
done
wait
"""

Trying to implement a loop in Makefile target

This is my target, where i want to run a loop for each element in the list variable.
The problem is the loop runs but the test variable value is passed as empty
list = mlflow emr
common=$(someDir)/common
.PHONY:build
build:
for var in $(list); do \
cd ${common}; \
test=$(git diff --name-only --diff-filter=AM master | grep ^$(var)/); \
if [ "$(test)" != "" ]; then \
echo "condition met"; \
else \
echo "It is Not Setup"; \
fi \
done
Error:
bash-5.0# sudo make build n=1
for var in mlflow emr; do \
cd /mak/epa-toolchain/common; \
test=; \
if [ "" != "" ]; then \
echo "condition met"; \
else \
echo "It is Not Setup"; \
fi \
done
It is Not Setup
It is Not Setup
The $ is a special character to make: it introduces a make variable reference. So this:
$(git diff --name-only --diff-filter=AM master | grep ^$(var)/)
is not a shell $(...) command, it's a make variable with a very strange name. Wherever you want the shell to see $ you have to escape it as $$:
$$(git diff --name-only --diff-filter=AM master | grep ^$$var/)
(note you have to change $(var) to $$var because the former is a reference to a make variable var, but you are looping in the shell which sets the shell variable var).
Ditto this:
if [ "$(test)" != "" ]; then \
has to be:
if [ "$$test" != "" ]; then \
because test is a shell variable you just assigned, not a make variable.

Interpolate variable in command substitution in Makefile

I am trying to do variable interpolation inside a command substitution in a Makefile.
I have this code:
setup:
mkdir -p data_all ; \
for i in $(shell jq -r 'keys | #tsv' assets.json) ; do \
git_url=$(shell jq -r ".$$i" assets.json) ; \
git clone $$git_url data_all/$$i ; \
done
The code is failing, however, because $$i does not expand in the "shell" line that sets git_url.
How do I interpolate the variable $i in the "shell" line that sets git_url?
You mixed up make functions ($(shell ...)) and true shell constructs. When writing a recipe the simplest is to write it first in plain shell:
mkdir -p data_all ; \
for i in $( jq -r 'keys | #tsv' assets.json ) ; do \
git_url=$( jq -r ".$i" assets.json ) ; \
git clone $git_url data_all/$i ; \
done
And then escaping the unwanted $ expansion by make:
mkdir -p data_all ; \
for i in $$( jq -r 'keys | #tsv' assets.json ) ; do \
git_url=$$( jq -r ".$$i" assets.json ) ; \
git clone $$git_url data_all/$$i ; \
done

BASH - how echo works inside EOF tags

I would like to execute the followings:
PASSWORD="mypassword"
RUNCOMMAND=$(cat <<EOF
echo $PASSWORD | sudo -S sudo echo "this is it babe"
EOF
)
But instead of this is it babe, I get the following result:
mypassword | sudo -S sudo echo "this is it babe"
I tried with cat <<\EOF, cat <<'EOF' still no luck.
Any ideas?
You are confusing a heredoc with a pipeline.
heredoc with variable expansion:
cat <<EOF
some text, possibly with variables: ${HOME} / $(whoami)
EOF
some text, possibly with variables: /home/attie / attie
heredoc without variable expansion:
cat <<"EOF"
some text, possibly with variables: ${HOME} / $(whoami)
EOF
some text, possibly with variables: ${HOME} / $(whoami)
pipeline with variable expansion (note the quotes, "):
echo "some text, possibly with variables: ${HOME} / $(whoami)" | cat
some text, possibly with variables: /home/attie / attie
pipeline without variable expansion (note the quotes, '):
echo 'some text, possibly with variables: ${HOME} / $(whoami)' | cat
some text, possibly with variables: ${HOME} / $(whoami)
${...} expands an environment variable
$(...) runs a command, and substitutes its stdout
It also looks like you're trying to have your password entered into sudo - this won't work, as sudo will repoen the terminal to acquire your password, before passing it's stdin to the final application.
You are starting from a false premise, that eval $RUNCOMMAND is something you should do. It is not; variables are for data, functions are for code.
run_command () {
docker_run_options=(
--restart=always
--name "${USER_NAME}_$(date +%Y%m%d-%H%M%S)"
-d
-e "VIRTUAL_HOST=$USER_VIRTUAL_HOST"
-e "VIRTUAL_PORT=$USER_VIRTUAL_PORT"
-e "PORT=$USER_VIRTUAL_PORT"
-p "$USER_VIRTUAL_PORT:$USER_VIRTUAL_PORT"
)
echo "$1" | sudo -S sudo docker run "${docker_run_options[#]}" "$USER_IMAGE"
}
fun_run_command () {
run_command "PASSWORD"
}
The final solution is rather simple:
PASSWORD="mypassword"
RUNCOMMAND=$(cat <<EOF
echo $PASSWORD | sudo -S sudo echo "this is it babe"
EOF
)
And execute it via eval:
eval $RUNCOMMAND
Sorry for stealing your times with this obvious problem guys:)
The usecase for the above is to echo a given command before really executing it.
Like this:
fun_run_command(){
# execute the final command
echo `eval $RUNCOMMAND`
}
fun_echo_command(){
# echo the command which will be launched (fun_run_command())
echo ${RUNCOMMAND//$PASSWORD/PASSWORD}
}
RUNCOMMAND=$(cat <<EOF
echo $PASSWORD | sudo -S sudo docker run --restart=always \
--name ${USER_NAME}_`date +%Y%m%d-%H%M%S` \
-d \
-e "VIRTUAL_HOST=$USER_VIRTUAL_HOST" \
-e "VIRTUAL_PORT=$USER_VIRTUAL_PORT" \
-e "PORT=$USER_VIRTUAL_PORT" \
-p $USER_VIRTUAL_PORT:$USER_VIRTUAL_PORT \
$USER_IMAGE
EOF
)
As you can see the command what I launch is quite long,
so it is always make sense to doublecheck what is executed by the script.
Having copy&paste the same command to multiple function is prone to error.

Resources