From 037cfb345ef9115b2eb8f164e7ba00e1594b30f8 Mon Sep 17 00:00:00 2001 From: PWall Date: Wed, 10 May 2023 10:59:58 +0200 Subject: [PATCH 1/4] Added bats for unit-testing --- .gitmodules | 9 +++++++++ test/bats | 1 + test/test_helper/bats-assert | 1 + test/test_helper/bats-support | 1 + 4 files changed, 12 insertions(+) create mode 100644 .gitmodules create mode 160000 test/bats create mode 160000 test/test_helper/bats-assert create mode 160000 test/test_helper/bats-support diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b7efcb4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "test/bats"] + path = test/bats + url = https://github.com/bats-core/bats-core.git +[submodule "test/test_helper/bats-support"] + path = test/test_helper/bats-support + url = https://github.com/bats-core/bats-support.git +[submodule "test/test_helper/bats-assert"] + path = test/test_helper/bats-assert + url = https://github.com/bats-core/bats-assert.git diff --git a/test/bats b/test/bats new file mode 160000 index 0000000..4417a96 --- /dev/null +++ b/test/bats @@ -0,0 +1 @@ +Subproject commit 4417a96cfc5e4afc493df94eeef8af4eec760163 diff --git a/test/test_helper/bats-assert b/test/test_helper/bats-assert new file mode 160000 index 0000000..44913ff --- /dev/null +++ b/test/test_helper/bats-assert @@ -0,0 +1 @@ +Subproject commit 44913ffe6020d1561c4c4d1e26cda8e07a1f374f diff --git a/test/test_helper/bats-support b/test/test_helper/bats-support new file mode 160000 index 0000000..3c8fadc --- /dev/null +++ b/test/test_helper/bats-support @@ -0,0 +1 @@ +Subproject commit 3c8fadc5097c9acfc96d836dced2bb598e48b009 From 5520368043935c84ea4cb668ba6f08752244e3a4 Mon Sep 17 00:00:00 2001 From: PWall Date: Wed, 10 May 2023 11:08:03 +0200 Subject: [PATCH 2/4] Moved entrypoint functionality to functions Created functions.sh file --- Dockerfile | 1 + entrypoint.sh | 77 +++++++---------------------------------------- functions.sh | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 67 deletions(-) create mode 100644 functions.sh diff --git a/Dockerfile b/Dockerfile index db90a79..15e86dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,5 +3,6 @@ FROM alpine:latest RUN apk add --no-cache git git-lfs openssh-client COPY entrypoint.sh /entrypoint.sh +COPY functions.sh /functions.sh ENTRYPOINT ["/entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh index 82048df..758aa63 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,19 +3,10 @@ set -e # if a command fails it stops the execution set -u # script fails if trying to access to an undefined variable +. ./functions.sh + echo "[+] Action start" -SOURCE_BEFORE_DIRECTORY="${1}" -SOURCE_DIRECTORY="${2}" -DESTINATION_GITHUB_USERNAME="${3}" -DESTINATION_REPOSITORY_NAME="${4}" -GITHUB_SERVER="${5}" -USER_EMAIL="${6}" -USER_NAME="${7}" -DESTINATION_REPOSITORY_USERNAME="${8}" -TARGET_BRANCH="${9}" -COMMIT_MESSAGE="${10}" -TARGET_DIRECTORY="${11}" -CREATE_TARGET_BRANCH_IF_NEEDED="${12}" +getArgs "$@" if [ -z "$DESTINATION_REPOSITORY_USERNAME" ] then @@ -32,24 +23,11 @@ fi if [ -n "${SSH_DEPLOY_KEY:=}" ] then echo "[+] Using SSH_DEPLOY_KEY" - - # Inspired by https://github.com/leigholiver/commit-with-deploy-key/blob/main/entrypoint.sh , thanks! - mkdir --parents "$HOME/.ssh" - DEPLOY_KEY_FILE="$HOME/.ssh/deploy_key" - echo "${SSH_DEPLOY_KEY}" > "$DEPLOY_KEY_FILE" - chmod 600 "$DEPLOY_KEY_FILE" - - SSH_KNOWN_HOSTS_FILE="$HOME/.ssh/known_hosts" - ssh-keyscan -H "$GITHUB_SERVER" > "$SSH_KNOWN_HOSTS_FILE" - - export GIT_SSH_COMMAND="ssh -i "$DEPLOY_KEY_FILE" -o UserKnownHostsFile=$SSH_KNOWN_HOSTS_FILE" - - GIT_CMD_REPOSITORY="git@$GITHUB_SERVER:$DESTINATION_REPOSITORY_USERNAME/$DESTINATION_REPOSITORY_NAME.git" - + sshDeployKey elif [ -n "${API_TOKEN_GITHUB:=}" ] then echo "[+] Using API_TOKEN_GITHUB" - GIT_CMD_REPOSITORY="https://$DESTINATION_REPOSITORY_USERNAME:$API_TOKEN_GITHUB@$GITHUB_SERVER/$DESTINATION_REPOSITORY_USERNAME/$DESTINATION_REPOSITORY_NAME.git" + githubToken else echo "::error::API_TOKEN_GITHUB and SSH_DEPLOY_KEY are empty. Please fill one (recommended the SSH_DEPLOY_KEY)" exit 1 @@ -65,29 +43,10 @@ echo "[+] Enable git lfs" git lfs install echo "[+] Cloning destination git repository $DESTINATION_REPOSITORY_NAME" -# Setup git -git config --global user.email "$USER_EMAIL" -git config --global user.name "$USER_NAME" - -{ - git clone --single-branch --depth 1 --branch "$TARGET_BRANCH" "$GIT_CMD_REPOSITORY" "$CLONE_DIR" -} || { - if [ "$CREATE_TARGET_BRANCH_IF_NEEDED" = "true" ] - then - # Default branch of the repository is cloned. Later on the required branch - # will be created - git clone --single-branch --depth 1 "$GIT_CMD_REPOSITORY" "$CLONE_DIR" - else - false - fi -} || { - echo "::error::Could not clone the destination repository. Command:" - echo "::error::git clone --single-branch --branch $TARGET_BRANCH $GIT_CMD_REPOSITORY $CLONE_DIR" - echo "::error::(Note that if they exist USER_NAME and API_TOKEN is redacted by GitHub)" - echo "::error::Please verify that the target repository exist AND that it contains the destination branch name, and is accesible by the API_TOKEN_GITHUB OR SSH_DEPLOY_KEY" - exit 1 +setupGit + +cloneRepo -} ls -la "$CLONE_DIR" TEMP_DIR=$(mktemp -d) @@ -117,19 +76,7 @@ echo "[+] List contents of $SOURCE_DIRECTORY" ls "$SOURCE_DIRECTORY" echo "[+] Checking if local $SOURCE_DIRECTORY exist" -if [ ! -d "$SOURCE_DIRECTORY" ] -then - echo "ERROR: $SOURCE_DIRECTORY does not exist" - echo "This directory needs to exist when push-to-another-repository is executed" - echo - echo "In the example it is created by ./build.sh: https://github.com/cpina/push-to-another-repository-example/blob/main/.github/workflows/ci.yml#L19" - echo - echo "If you want to copy a directory that exist in the source repository" - echo "to the target repository: you need to clone the source repository" - echo "in a previous step in the same build section. For example using" - echo "actions/checkout@v2. See: https://github.com/cpina/push-to-another-repository-example/blob/main/.github/workflows/ci.yml#L16" - exit 1 -fi +checkSource echo "[+] Copying contents of source repository folder $SOURCE_DIRECTORY to folder $TARGET_DIRECTORY in git repo $DESTINATION_REPOSITORY_NAME" cp -ra "$SOURCE_DIRECTORY"/. "$CLONE_DIR/$TARGET_DIRECTORY" @@ -147,11 +94,7 @@ echo "[+] Set directory is safe ($CLONE_DIR)" # TODO: review before releasing it as a version git config --global --add safe.directory "$CLONE_DIR" - -if [ "$CREATE_TARGET_BRANCH_IF_NEEDED" = "true" ] -then - git switch -c "$TARGET_BRANCH" -fi +switchBranch echo "[+] Adding git commit" git add . diff --git a/functions.sh b/functions.sh new file mode 100644 index 0000000..17d9c13 --- /dev/null +++ b/functions.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +getArgs() { + SOURCE_BEFORE_DIRECTORY="${1}" + SOURCE_DIRECTORY="${2}" + DESTINATION_GITHUB_USERNAME="${3}" + DESTINATION_REPOSITORY_NAME="${4}" + GITHUB_SERVER="${5}" + USER_EMAIL="${6}" + USER_NAME="${7}" + DESTINATION_REPOSITORY_USERNAME="${8}" + TARGET_BRANCH="${9}" + COMMIT_MESSAGE="${10}" + TARGET_DIRECTORY="${11}" + CREATE_TARGET_BRANCH_IF_NEEDED="${12}" +} + +sshDeployKey() { + mkdir --parents "$HOME/.ssh" + DEPLOY_KEY_FILE="$HOME/.ssh/deploy_key" + echo "${SSH_DEPLOY_KEY}" > "$DEPLOY_KEY_FILE" + chmod 600 "$DEPLOY_KEY_FILE" + + SSH_KNOWN_HOSTS_FILE="$HOME/.ssh/known_hosts" + ssh-keyscan -H "$GITHUB_SERVER" > "$SSH_KNOWN_HOSTS_FILE" + + export GIT_SSH_COMMAND="ssh -i "$DEPLOY_KEY_FILE" -o UserKnownHostsFile=$SSH_KNOWN_HOSTS_FILE" + + GIT_CMD_REPOSITORY="git@$GITHUB_SERVER:$DESTINATION_REPOSITORY_USERNAME/$DESTINATION_REPOSITORY_NAME.git" +} + +githubToken() { + GIT_CMD_REPOSITORY="https://$DESTINATION_REPOSITORY_USERNAME:$API_TOKEN_GITHUB@$GITHUB_SERVER/$DESTINATION_REPOSITORY_USERNAME/$DESTINATION_REPOSITORY_NAME.git" +} + +setupGit() { + git config --global user.email "$USER_EMAIL" + git config --global user.name "$USER_NAME" +} + +cloneRepo() { + { + git clone --single-branch --depth 1 --branch "$TARGET_BRANCH" "$GIT_CMD_REPOSITORY" "$CLONE_DIR" + } || { + if [ "$CREATE_TARGET_BRANCH_IF_NEEDED" = "true" ] + then + # Default branch of the repository is cloned. Later on the required branch + # will be created + git clone --single-branch --depth 1 "$GIT_CMD_REPOSITORY" "$CLONE_DIR" + else + false + fi + } || { + echo "::error::Could not clone the destination repository. Command:" + echo "::error::git clone --single-branch --branch $TARGET_BRANCH $GIT_CMD_REPOSITORY $CLONE_DIR" + echo "::error::(Note that if they exist USER_NAME and API_TOKEN is redacted by GitHub)" + echo "::error::Please verify that the target repository exist AND that it contains the destination branch name, and is accesible by the API_TOKEN_GITHUB OR SSH_DEPLOY_KEY" + exit 1 + } +} + +checkSource() { + if [ ! -d "$SOURCE_DIRECTORY" ] + then + echo "ERROR: $SOURCE_DIRECTORY does not exist" + echo "This directory needs to exist when push-to-another-repository is executed" + echo + echo "In the example it is created by ./build.sh: https://github.com/cpina/push-to-another-repository-example/blob/main/.github/workflows/ci.yml#L19" + echo + echo "If you want to copy a directory that exist in the source repository" + echo "to the target repository: you need to clone the source repository" + echo "in a previous step in the same build section. For example using" + echo "actions/checkout@v2. See: https://github.com/cpina/push-to-another-repository-example/blob/main/.github/workflows/ci.yml#L16" + exit 1 + fi +} + +switchBranch() { + if [ "$CREATE_TARGET_BRANCH_IF_NEEDED" = "true" ] + then + git switch -c "$TARGET_BRANCH" + fi +} From 9432cdfed9512b6b1f93a1f0e0720572b0b08f58 Mon Sep 17 00:00:00 2001 From: PWall Date: Wed, 10 May 2023 11:10:13 +0200 Subject: [PATCH 3/4] Added tests --- test/test.bats | 184 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 test/test.bats diff --git a/test/test.bats b/test/test.bats new file mode 100644 index 0000000..d02546d --- /dev/null +++ b/test/test.bats @@ -0,0 +1,184 @@ +#!/bin/sh + +set -e +setup() { + load 'test_helper/bats-support/load' + load 'test_helper/bats-assert/load' + load '../functions.sh' +} + +_var_setup() { + SSH_DEPLOY_KEY="SSH TEST" + DESTINATION_REPOSITORY_USERNAME="UserTest" + DESTINATION_REPOSITORY_NAME="RepoTest" + GITHUB_SERVER="github.com" +} + +_mockup_repo() { + cd "$1" + git init + git checkout -b main + touch "TEST" + git add ./TEST + git config user.name "Test" + git config user.email "test@example.com" + git commit -m "TEST_COMMIT" +} + +@test "Script arguments" { + getArgs 1 2 3 4 5 6 7 8 9 10 11 12 + assert [ $SOURCE_BEFORE_DIRECTORY = "1" ] + assert [ $SOURCE_DIRECTORY = "2" ] + assert [ $DESTINATION_GITHUB_USERNAME = "3" ] + assert [ $DESTINATION_REPOSITORY_NAME = "4" ] + assert [ $GITHUB_SERVER = "5" ] + assert [ $USER_EMAIL = "6" ] + assert [ $USER_NAME = "7" ] + assert [ $DESTINATION_REPOSITORY_USERNAME = "8" ] + assert [ $TARGET_BRANCH = "9" ] + assert [ $COMMIT_MESSAGE = "10" ] + assert [ $TARGET_DIRECTORY = "11" ] + assert [ $CREATE_TARGET_BRANCH_IF_NEEDED = "12" ] +} + +@test "Create SSH keys" { + _var_setup + HOME="$BATS_TEST_TMPDIR" + GITHUB_KEYS="KNOWN TEST" + ssh-keyscan() { + echo "KNOWN TEST" + } + + sshDeployKey + + assert [ -d "$BATS_TEST_TMPDIR/.ssh" ] + assert [ -f "$BATS_TEST_TMPDIR/.ssh/deploy_key" ] + assert [ "$(cat $BATS_TEST_TMPDIR/.ssh/deploy_key)" = "$SSH_DEPLOY_KEY" ] + assert [ -f "$BATS_TEST_TMPDIR/.ssh/known_hosts" ] + assert [ "$(cat $BATS_TEST_TMPDIR/.ssh/known_hosts)" = "$GITHUB_KEYS" ] + assert [ "$GIT_CMD_REPOSITORY" = "git@$GITHUB_SERVER:$DESTINATION_REPOSITORY_USERNAME/$DESTINATION_REPOSITORY_NAME.git" ] + assert [ "$GIT_SSH_COMMAND" = "ssh -i $BATS_TEST_TMPDIR/.ssh/deploy_key -o UserKnownHostsFile=$BATS_TEST_TMPDIR/.ssh/known_hosts" ] +} + +@test "Put github host in kwown_hosts" { + skip "Network test" + _var_setup + HOME="$BATS_TEST_TMPDIR" + # Get only the ssh keys (not hostnames) + GITHUB_KEYS=$(ssh-keyscan "github.com" | cut -d " " -f 3) + + sshDeployKey + for KEY in $GITHUB_KEYS; do + # Check if the ssh keys are in known hosts + assert grep -q "$KEY" "$BATS_TEST_TMPDIR/.ssh/known_hosts" + done +} + +@test "Set Github token in GIT_CMD_REPOSITORY" { + _var_setup + API_TOKEN_GITHUB="TokenTest" + githubToken + assert [ "$GIT_CMD_REPOSITORY" = "https://$DESTINATION_REPOSITORY_USERNAME:$API_TOKEN_GITHUB@$GITHUB_SERVER/$DESTINATION_REPOSITORY_USERNAME/$DESTINATION_REPOSITORY_NAME.git" ] +} + +@test "Setup git" { + export HOME="$BATS_TEST_TMPDIR" + USER_NAME="TestUser" + USER_EMAIL="TestEmail@example.com" + setupGit + assert [ -f "$BATS_TEST_TMPDIR/.gitconfig" ] + assert grep -q "$USER_NAME" "$BATS_TEST_TMPDIR/.gitconfig" + assert grep -q "$USER_EMAIL" "$BATS_TEST_TMPDIR/.gitconfig" +} + +@test "When source directory exist do nothing" { + SOURCE_DIRECTORY="$BATS_TEST_TMPDIR/SourceTest" + mkdir --parents $SOURCE_DIRECTORY + run checkSource + assert_output "" +} + +@test "When source directory is missing exit" { + SOURCE_DIRECTORY="$BATS_TEST_TMPDIR/SourceTest" + run checkSource + assert_failure +} + +@test "Switch branch when \$CREATE_TARGET is true" { + mkdir "$BATS_TEST_TMPDIR/Repo" + _mockup_repo "$BATS_TEST_TMPDIR/Repo" + CREATE_TARGET_BRANCH_IF_NEEDED="true" + TARGET_BRANCH="TestBranch" + switchBranch + assert [ "$(git branch --show-current --format="%(refname:short)")" = "$TARGET_BRANCH" ] +} + +@test "Only switch branch when \$CREATE_TARGET is true" { + mkdir "$BATS_TEST_TMPDIR/Repo" + _mockup_repo "$BATS_TEST_TMPDIR/Repo" + CREATE_TARGET_BRANCH_IF_NEEDED="false" + TARGET_BRANCH="TestBranch" + switchBranch + assert [ "$(git branch --show-current --format="%(refname:short)")" = "main" ] +} + +@test "Clone repo (network)" { + skip "Network test" + TARGET_BRANCH="main" + GIT_CMD_REPOSITORY="https://github.com/cpina/github-action-push-to-another-repository.git" + CLONE_DIR="$BATS_TEST_TMPDIR" + run cloneRepo + assert_success +} + +@test "Clone repo (local)" { + TARGET_BRANCH="main" + GIT_CMD_REPOSITORY="$BATS_TEST_TMPDIR/Repo" + CLONE_DIR="$BATS_TEST_TMPDIR/RepoCloned" + mkdir --parent "$CLONE_DIR" + mkdir --parent "$GIT_CMD_REPOSITORY" + _mockup_repo "$GIT_CMD_REPOSITORY" + run cloneRepo + assert_success +} + +@test "When cloning repo and \$CREATE_TARGET is true create missing branch" { + TARGET_BRANCH="TestBranch" + GIT_CMD_REPOSITORY="$BATS_TEST_TMPDIR/Repo" + CLONE_DIR="$BATS_TEST_TMPDIR/RepoCloned" + CREATE_TARGET_BRANCH_IF_NEEDED="true" + mkdir --parent "$GIT_CMD_REPOSITORY" + _mockup_repo "$GIT_CMD_REPOSITORY" + run cloneRepo + assert_success +} + +@test "When cloning with missing branch and \$CREATE_TARGET is false exit" { + TARGET_BRANCH="TestBranch" + GIT_CMD_REPOSITORY="$BATS_TEST_TMPDIR/Repo" + CLONE_DIR="$BATS_TEST_TMPDIR/RepoCloned" + CREATE_TARGET_BRANCH_IF_NEEDED="false" + mkdir --parent "$CLONE_DIR" + mkdir --parent "$GIT_CMD_REPOSITORY" + _mockup_repo "$GIT_CMD_REPOSITORY" + run cloneRepo + assert_failure +} + +@test "When cloning non existing repo exit" { + TARGET_BRANCH="TestBranch" + GIT_CMD_REPOSITORY="$BATS_TEST_TMPDIR/Repo" + CLONE_DIR="$BATS_TEST_TMPDIR/RepoCloned" + CREATE_TARGET_BRANCH_IF_NEEDED="false" + run cloneRepo + assert_failure +} + +@test "When cloning non existing repo exit (network)" { + TARGET_BRANCH="TestBranch" + GIT_CMD_REPOSITORY="http://example.example/" + CLONE_DIR="$BATS_TEST_TMPDIR/RepoCloned" + CREATE_TARGET_BRANCH_IF_NEEDED="false" + run cloneRepo + assert_failure +} From a2e083e01c8506d549d0db9a27115146dfa280aa Mon Sep 17 00:00:00 2001 From: PWall Date: Wed, 10 May 2023 11:19:07 +0200 Subject: [PATCH 4/4] Separated cloneRepo into 3 functions --- functions.sh | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/functions.sh b/functions.sh index 17d9c13..c9f56f6 100644 --- a/functions.sh +++ b/functions.sh @@ -38,25 +38,31 @@ setupGit() { git config --global user.name "$USER_NAME" } +cloneSimple() { + git clone --single-branch --depth 1 --branch "$TARGET_BRANCH" "$GIT_CMD_REPOSITORY" "$CLONE_DIR" +} + +cloneCreateBranch() { + if [ "$CREATE_TARGET_BRANCH_IF_NEEDED" = "true" ] + then + # Default branch of the repository is cloned. Later on the required branch + # will be created + git clone --single-branch --depth 1 "$GIT_CMD_REPOSITORY" "$CLONE_DIR" + else + false + fi +} + +cloneError() { + echo "::error::Could not clone the destination repository. Command:" + echo "::error::git clone --single-branch --branch $TARGET_BRANCH $GIT_CMD_REPOSITORY $CLONE_DIR" + echo "::error::(Note that if they exist USER_NAME and API_TOKEN is redacted by GitHub)" + echo "::error::Please verify that the target repository exist AND that it contains the destination branch name, and is accesible by the API_TOKEN_GITHUB OR SSH_DEPLOY_KEY" + exit 1 +} + cloneRepo() { - { - git clone --single-branch --depth 1 --branch "$TARGET_BRANCH" "$GIT_CMD_REPOSITORY" "$CLONE_DIR" - } || { - if [ "$CREATE_TARGET_BRANCH_IF_NEEDED" = "true" ] - then - # Default branch of the repository is cloned. Later on the required branch - # will be created - git clone --single-branch --depth 1 "$GIT_CMD_REPOSITORY" "$CLONE_DIR" - else - false - fi - } || { - echo "::error::Could not clone the destination repository. Command:" - echo "::error::git clone --single-branch --branch $TARGET_BRANCH $GIT_CMD_REPOSITORY $CLONE_DIR" - echo "::error::(Note that if they exist USER_NAME and API_TOKEN is redacted by GitHub)" - echo "::error::Please verify that the target repository exist AND that it contains the destination branch name, and is accesible by the API_TOKEN_GITHUB OR SSH_DEPLOY_KEY" - exit 1 - } + cloneSimple || cloneCreateBranch || cloneError } checkSource() {