Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split local git operations from github operations #49

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
.docker
coverage.html
coverage.out
tmp/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to exclude tmp/ if we have this:

temp_dir=$(mktemp -d)
trap "rm -r ${temp_dir}" EXIT

58 changes: 0 additions & 58 deletions Dockerfile.effective
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep this file for the GH action to work.

This file was deleted.

1 change: 0 additions & 1 deletion build/.gitignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably don't want to check in the binary so may need to keep this file.

This file was deleted.

Binary file added build/update-from-template
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest we upload the binary to the the GitHub Release asset when we have the Publish workflow complete in the repository template.

Binary file not shown.
26 changes: 17 additions & 9 deletions cmd/update-from-template/arguments.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"errors"
"flag"
"fmt"
"os"
Expand All @@ -21,21 +22,28 @@ func parseCommandLineArguments() (*Arguments, error) {
dir2 := flag.String("destination-dir", "", "Destination directory")
file1 := flag.String("app-config-file", "", "Configuration file of the Update from Template app")
file2 := flag.String("template-config-file", "", "Configuration file for the Repository Template update")
var err error = nil

flag.Parse()
if *dir1 == "" || *dir2 == "" {
return &Arguments{}, fmt.Errorf("%s", "Please, provide both source and destination directory paths")
err = errors.Join(err, fmt.Errorf("Please provide both --source-dir and --destination-dir directory paths"))
}
if *file1 == "" {
return &Arguments{}, fmt.Errorf("%s", "Please, provide app configuration file path")
err = errors.Join(err, fmt.Errorf("Please provide --app-config-file path"))
}
if *file2 == "" {
return &Arguments{}, fmt.Errorf("%s", "Please, provide template configuration file path")
err = errors.Join(err, fmt.Errorf("Please provide --template-config-file path"))
}

return &Arguments{
SourceDirectory: *dir1,
DestinationDirectory: *dir2,
AppConfigurationFile: *file1,
TemplateConfigurationFile: *file2,
}, nil
if err != nil {
return nil, err
} else {

return &Arguments{
SourceDirectory: *dir1,
DestinationDirectory: *dir2,
AppConfigurationFile: *file1,
TemplateConfigurationFile: *file2,
}, nil
}
}
34 changes: 33 additions & 1 deletion cmd/update-from-template/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -50,9 +51,14 @@ func main() {
// Parse the command-line arguments
args, err := parseCommandLineArguments()
if err != nil {
log.Fatalf("Error while parsing command-line arguments: %s\n", err)
log.Fatalf("Error while parsing command-line arguments:\n%s\n", err)
}

err = validateArgs(args)
if err != nil {
log.Fatalf("Argument error:\n%v\n", err)
}

// Parse the config file
config, err := parseConfigFiles(args.AppConfigurationFile, args.TemplateConfigurationFile)
if err != nil {
Expand All @@ -70,6 +76,32 @@ func main() {
printAsJson(result)
}

func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}

func validateArgs(args *Arguments) error {
source := args.SourceDirectory
target := args.DestinationDirectory
var sourceError error = nil
var targetError error = nil

if !fileExists(source) {
sourceError = fmt.Errorf("Source directory %s does not exist.", source)
} else if !fileExists(filepath.Join(source, ".git")) {
sourceError = fmt.Errorf("Source directory %s is not a git repository (or is bare).", source)
}

if !fileExists(target) {
targetError = fmt.Errorf("Target directory %s does not exist.", target)
} else if !fileExists(filepath.Join(target, ".git")) {
targetError = fmt.Errorf("Target directory %s is not a git repository (or is bare).", target)
}

return errors.Join(sourceError, targetError)
}

func walk(dir1, dir2 *string) (map[string]FileInfo, map[string]FileInfo, error) {

source := make(map[string]FileInfo)
Expand Down
47 changes: 8 additions & 39 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ function main() {
fetch-repositories-content
prune-legacy-updates
checkout-new-branch
produce-list-of-files-to-update
update-files
patch-target
push-and-create-pull-request
}

Expand Down Expand Up @@ -140,53 +139,23 @@ function checkout-new-branch() {
git checkout -b update-from-template-${build_timestamp}
}

function produce-list-of-files-to-update() {

cd $dest_dir
/update-from-template \
--source-dir $src_dir \
--destination-dir $dest_dir \
--app-config-file /update-from-template.yaml \
--template-config-file $dest_dir/scripts/config/repository-template.yaml \
> $list_of_files_to_update_json
}

function update-files() {

# Update files
to_update=$(
cat $list_of_files_to_update_json \
| jq -r '.comparison | to_entries[] | select(.value.action == "update") | .key'
)
echo "$to_update" | while IFS= read -r file; do
dir=$(dirname "$file")
mkdir -p $dest_dir/$dir
cp $src_dir/$file $dest_dir/$file
done
# Delete files
to_delete=$(
cat $list_of_files_to_update_json \
| jq -r '.comparison | to_entries[] | select(.value.action == "delete") | .key'
)
echo "$to_delete" | while IFS= read -r file; do
rm -rf $dest_dir/$file
done
function patch-target() {
./patch-target.sh "${src_dir}" \
"${dest_dir}" \
"Update from template ${build_datetime_local}"
}

function push-and-create-pull-request() {

cd $dest_dir
# Add and commit changes
git add -A
git commit -m "Update from template ${build_datetime_local}"

[ -z "$github_token" ] && return

# Push and create new PR
git push -u origin update-from-template-${build_timestamp}
# Get the commit message from the target repo log rather than reconstructing it
body=$(git log -1 --format=format:%s)
gh pr create \
--title "Update from template" \
--body "Update from template ${build_datetime_local}"
--body "$body"
}

function is-arg-true() {
Expand Down
110 changes: 110 additions & 0 deletions patch-target.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following line has to be included in the Dockerfile (right after gpg.sh) for this to work as a GH Action:

COPY --from=builder /github/workspace/patch-target.sh /

Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/bin/bash

set -euo pipefail

function usage() {
cat <<USAGE
Usage: ${0} <source-dir> <target-dir> <commit-message>

Commits a patch to <target-dir> with the relevant changes from
<source-dir> and the given <commit-message>, and outputs a JSON
document with the files that were changed.

Environment variables

\$UPDATE_BINARY: Set this to the path of the update-from-template
executable. Defaults to '/update-from-template'. Currently set to
'${update_binary}'.

\$UPDATE_CONFIG_FILE: Set this to the path of the config file for
the update-from-template executable. Defaults to '/.config.yaml'.
Currently set to '${update_config_file}'.

USAGE
}

src_dir=${1:-}
dest_dir=${2:-}
commit_msg=${3:-}
update_binary=${UPDATE_BINARY:-/update-from-template}
update_config_file=${UPDATE_CONFIG_FILE:-/.config.yaml}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be /update-from-template.yaml rather than /.config.yaml. It is copied to the root level in the Dockerfile from the project's directory scripts/config/update-from-template.yaml.



# If we only rely on `set -u` for our error handling, we don't get
# very descriptive error output. This gives us a chance to be more
# helpful.
# use echo "failure message" | fail
function fail() {
cat >&2
echo >&2
usage >&2
exit 1
}

[ -z "${src_dir}" ] && echo "No source or target directory given" | fail
[ -z "${dest_dir}" ] && echo "No target directory given" | fail
[ -z "$commit_msg" ] && echo "No commit message given." | fail
[ -z "$update_binary" ] && echo "\$UPDATE_BINARY is set to the empty string" | fail
[ ! -e $update_binary ] && echo "$update_binary does not exist. Please set \$UPDATE_BINARY." | fail
[ ! -x $update_binary ] && echo "$update_binary is not executable" | fail
[ ! -d $src_dir ] && echo "Source directory $src_dir does not exist." | fail
[ ! -d $dest_dir ] && echo "Target directory $dest_dir does not exist." | fail
[ ! -d "$src_dir/.git" ] && echo "$src_dir is not a git working tree." | fail
[ ! -d "$dest_dir/.git" ] && echo "$dest_dir is not a git working tree." | fail

temp_dir=$(mktemp -d)
trap "rm -r ${temp_dir}" EXIT

list_of_files_to_update_json=$temp_dir/update-from-template.json

function main() {
produce-list-of-files-to-update
update-files
commit-changes
cat $list_of_files_to_update_json
}

function produce-list-of-files-to-update() {

# cd $dest_dir
$update_binary \
--source-dir $src_dir \
--destination-dir $dest_dir \
--app-config-file $update_config_file \
--template-config-file $dest_dir/scripts/config/.repository-template.yaml \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file name should be repository-template.yaml instead of .repository-template.yaml (no dot). The dot prefix is used when files are located at the top level directory. We don't need to do that as we store all the configuration files in the scripts/config directory.

> $list_of_files_to_update_json
}

function update-files() {

# Update files
to_update=$(
cat $list_of_files_to_update_json \
| jq -r '.comparison | to_entries[] | select(.value.action == "update") | .key'
)
echo "$to_update" | while IFS= read -r file; do
dir=$(dirname "$file")
mkdir -p $dest_dir/$dir
cp $src_dir/$file $dest_dir/$file
done
# Delete files
to_delete=$(
cat $list_of_files_to_update_json \
| jq -r '.comparison | to_entries[] | select(.value.action == "delete") | .key'
)
echo "$to_delete" | while IFS= read -r file; do
if [ ! -z "$file" ]; then rm -r $dest_dir/$file; fi
done
}

function commit-changes() {

cd $dest_dir
# Add and commit changes
git add -A
git commit -m "${commit_msg}"

}

main $*
exit 0
1 change: 0 additions & 1 deletion scripts/docker/docker.lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ function docker-build() {
_create-effective-dockerfile
_create-effective-version
docker build \
--progress=plain \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file comes from the repository template, therefore we probably don't want to change it.

--platform linux/amd64 \
--build-arg IMAGE="${DOCKER_IMAGE}" \
--build-arg TITLE="${DOCKER_TITLE}" \
Expand Down