diff --git a/README.md b/README.md index aa17e74..fd4034f 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ This GitHub Action provides the following features: with `.templateignore` in the root directory in the same format as `.gitignore`. Follow the [glob pattern][glob-pattern] in defining the files and folders that the action should excluded. +- Add support for updates and removal of files inside the downstream repository + as configured inside the `cookiecutter.json` file, allowing easier migration + of existing files and removal of old files as part of standard updates. [glob-pattern]: https://en.wikipedia.org/wiki/Glob_(programming) diff --git a/bin/prepare b/bin/prepare index b4e1652..8f13143 100755 --- a/bin/prepare +++ b/bin/prepare @@ -51,9 +51,7 @@ rm -f "${TMP}.tar.gz" # with preference to the local copy as so to ensure any new configuration # settings are set with the defaults and local overrides are kept show_step "Merging the upstream and downstream cookiecutter.json files" -jq -s '.[0] * .[1]' \ - "${TEMPLATES}/cookiecutter.json" \ - "cookiecutter.json" \ +merge_cookiecutters "${TEMPLATES}/cookiecutter.json" "cookiecutter.json" \ > "${TMP}/cookiecutter.json" # Once merged, move the configuration into the template directory so it can be # used to re-render the upstream templates based on the local settings (it will diff --git a/lib/prepare.sh b/lib/prepare.sh index 8c3381f..520741a 100644 --- a/lib/prepare.sh +++ b/lib/prepare.sh @@ -2,8 +2,26 @@ set -euo pipefail +# Call GitHub and get the commit SHA value of the source branch we're going to +# use for the templates to render and apply over the top function get_template_commit { gh api "/repos/${REPOSITORY}/branches/${BRANCH}" \ --jq .commit.sha 2> /dev/null exit ${?} } + +# Merge all downstream values over the top of upstream as the base +# configuration, except for .changes.updates and .changes.removes which should +# be then taken from upstream override downstream (in effect a two-way merge) +function merge_cookiecutters { + local upstream="${1}" + local downstream="${2}" + + jq -s '.[0] * .[1] + + { "changes": + { "updates":.[0].changes.updates, + "removes":.[0].changes.removes } }' \ + "${upstream}" \ + "${downstream}" + return ${?} +} diff --git a/tests/assets/prepare/downstream.json b/tests/assets/prepare/downstream.json new file mode 100644 index 0000000..ed3dcdc --- /dev/null +++ b/tests/assets/prepare/downstream.json @@ -0,0 +1,41 @@ +{ + "repository": { + "owner": "n3tuk", + "name": "downstream" + }, + "provider": "null", + "name": "module", + "components": { + "terraform": { + "version": "1.4.6" + }, + "examples": [ + "default", + "complete" + ], + "submodules": [], + "terratest": { + "options": {} + } + }, + "changes": { + "updates": [ + { + "source": "a.txt", + "destination": "b.txt" + }, + { + "source": "b.txt", + "destination": "c.txt" + } + ], + "removes": [ + { + "source": "y.txt" + }, + { + "source": "z.txt" + } + ] + } +} diff --git a/tests/assets/prepare/upstream.json b/tests/assets/prepare/upstream.json new file mode 100644 index 0000000..e0eff71 --- /dev/null +++ b/tests/assets/prepare/upstream.json @@ -0,0 +1,40 @@ +{ + "repository": { + "owner": "n3tuk", + "name": "upstream" + }, + "provider": "null", + "name": "module", + "components": { + "terraform": { + "version": "1.4.6" + }, + "examples": [ + "default", + "complete" + ], + "submodules": [], + "terratest": { + "options": {} + } + }, + "changes": { + "updates": [ + { + "source": "c.txt", + "destination": "d.txt" + } + ], + "removes": [ + { + "source": "x.txt" + }, + { + "source": "y.txt" + }, + { + "source": "z.txt" + } + ] + } +} diff --git a/tests/prepare.bats b/tests/prepare.bats index 8934aa5..a034bd8 100644 --- a/tests/prepare.bats +++ b/tests/prepare.bats @@ -2,12 +2,13 @@ bats_load_library "bats-support" bats_load_library "bats-assert" +bats_load_library "bats-file" load "helpers/common" load "helpers/gh" setup() { - set_environment_variables + set_environment_variables prepare } teardown() { @@ -21,3 +22,31 @@ teardown() { assert_output "${COMMIT}" assert_success } + +@test "merge_cookiecutters() correctly two-way merges cookiecutter files" { + source lib/common.sh + source lib/prepare.sh + run merge_cookiecutters \ + "${TEMPLATES}/upstream.json" \ + "${TEMPLATES}/downstream.json" + + assert_output --partial '"owner": "n3tuk"' + # Prove this has not been overridden in the merge of downstream over upstream + assert_output --partial '"name": "downstream"' + # Prove these have been overridden in the merge of upstream over downstream + assert_output --partial '"source": "c.txt"' + assert_output --partial '"destination": "d.txt"' + assert_output --partial '"source": "x.txt"' + assert_output --partial '"source": "y.txt"' + assert_output --partial '"source": "z.txt"' + + # Prove this has been overridden in the merge of downstream over upstream + refute_output --partial '"name": "upstream"' + # Prove these have been overridden in the merge of upstream over downstream + refute_output --partial '"source": "a.txt"' + refute_output --partial '"destination": "b.txt"' + refute_output --partial '"source": "b.txt"' + refute_output --partial '"destination": "c.txt"' + + assert_success +}