diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..34f696f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org). + +## [Unreleased] + +## [3.0.0] - 2020-03-10 +### Added +- Input variable `clean` for optional removal of contents in the `dst_path` before copying. +- Input variable `exclude` for path exclusion filter with glob patterns. +- Input variable `filter` for path filtering with glob patterns. +### Changed +- Renamed input variable `src_filter` to `file_filter`. + +## [2.0.0] - 2020-02-23 + +## [1.1.0] - 2019-08-29 + +## [1.0.1] - 2019-07-09 + +## 1.0.0 - 2019-07-09 + +[Unreleased]: https://github.com/andstor/copycat-action/compare/v3.0.0...HEAD +[3.0.0]: https://github.com/andstor/copycat-action/compare/v2.0.0...v3.0.0 +[2.0.0]: https://github.com/andstor/copycat-action/compare/v1.1.0...v2.0.0 +[1.1.0]: https://github.com/andstor/copycat-action/compare/v1.1.0...v1.0.1 +[1.0.1]: https://github.com/andstor/copycat-action/compare/v1.0.1...v1.0.1 +[1.0.1]: https://github.com/andstor/copycat-action/compare/v1.0.0...v1.0.1 diff --git a/Dockerfile b/Dockerfile index ae260c3..2254441 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ FROM alpine:3.10 +RUN apk add --no-cache bash RUN apk add --no-cache git COPY entrypoint.sh /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] +ENTRYPOINT ["/bin/bash", "-c", "/entrypoint.sh"] diff --git a/README.md b/README.md index b9c43d3..4334ad0 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The following example [workflow step](https://help.github.com/en/actions/configu ```yml - name: Copy - uses: andstor/copycat-action@v2 + uses: andstor/copycat-action@v3 with: personal_token: ${{ secrets.PERSONAL_TOKEN }} src_path: /. @@ -30,22 +30,38 @@ The following input variable options can/must be configured: |Input variable|Necessity|Description|Default| |--------------------|--------|-----------|-------| -|`src_path`|Required|The source path to the file(s) or folder(s) to copy from. For example, `/.` or `path/to/home.md`.|| -|`dst_path`|Optional|The destination path to copy the file(s) or folder(s) to. For example, `/wiki/` or `path/to/index.md`. |`src_path`| -|`dst_owner`|Required|The name of the owner of the repository to push to. For example, `andstor`.|| -|`dst_repo_name`|Required|The name of the repository to push to. For example, `copycat-action`.|| +|`src_path`|Required|The source path to the file(s) or folder(s) to copy from. For example `/.` or `path/to/home.md`.|| +|`dst_path`|Optional|The destination path to copy the file(s) or folder(s) to. For example `/wiki/` or `path/to/index.md`. |`src_path`| +|`dst_owner`|Required|The name of the owner of the repository to push to. For example `andstor`.|| +|`dst_repo_name`|Required|The name of the repository to push to. For example `copycat-action`.|| |`src_branch`|Optional|The branch name of the source repository.|`master`| |`dst_branch`|Optional|The branch name of the destination repository.|`master`| -|`src_filter`|Optional|A pattern for filtering files to be copied. For example `*.sh`|| +|`clean`|Optional|Set to `true` if the `dst_path` should be emptied before copying.|`false`| +|`file_filter`|Optional|A simple [pattern](https://www.gnu.org/software/findutils/manual/html_mono/find.html#Shell-Pattern-Matching) for filtering files to be copied. Acts on file basename. For example `*.sh`.|| +|`filter`|Optional|A glob pattern for filtering files to be copied. Acts on file paths. For example `**/!(*.*)`.|| +|`exclude`|Optional|A glob pattern for excluding paths. For example `*/tests/*`.|| |`src_wiki`|Optional|Set to `true` if the source repository you want to copy from is the GitHub Wiki.| `false`| |`dst_wiki`|Optional|Set to `true` if the destination repository you want to copy from is the GitHub Wiki.|`false`| |`username`|Optional|The GitHub username to associate commits made by this GitHub action.|[`GITHUB_ACTOR`](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables)| -|`email`|Optional|The email used for associating commits made by this GitHub action.|[`GITHUB_ACTOR`](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables)`@users.noreply.github.com`| +|`email`|Optional|The email used for associating commits made by this GitHub action.|[`GITHUB_ACTOR`](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables) `@users.noreply.github.com`| ## Secrets * `personal_token`: (required) GitHub Private Access Token used for the clone/push operations. To create it follow the [GitHub Documentation](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line). +## Filtering +Copycat provides several ways of filtering which files you want to copy. +All three types of filtering can be applied simultaneously. + +### Input variables +#### `file_filter` +The `file_filter`input variable allows you to filter file basenames. Hence, you can for example only copy all text files by setting `file_filter`to `*.txt`. The variable only accepts simple [patterns](https://www.gnu.org/software/findutils/manual/html_mono/find.html#Shell-Pattern-Matching). + +#### `filter` +The `filter` input variable provides extensive globbing support. It also supports extended globbing and globstar. The globbing applies to file paths. So, to for example match all files that doesn't have a file extention, the pattern could look like `**/!(*.*)`. + +#### `exclude` +The `exclude` input variable can be used to exclude certain paths. It will apply to the file paths. One are for example able to exclude certain deirectory names. Setting `exclude` to `*/tests/*` results in only copying files that don't lie inside a folder named `tests` (both directly and indirectly). Here, a file with for example the path `foo/tests/bar/baz.txt` would not be copied over. ## Examples @@ -63,7 +79,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Copycat - uses: andstor/copycat-action@v2 + uses: andstor/copycat-action@v3 with: personal_token: ${{ secrets.PERSONAL_TOKEN }} src_path: /. diff --git a/action.yml b/action.yml index b0c98cd..1db8a0a 100644 --- a/action.yml +++ b/action.yml @@ -28,8 +28,18 @@ inputs: description: 'The branch name of the destination repository' required: false default: 'master' - src_filter: - description: 'A pattern for filtering files to be copied' + clean: + description: 'Set to true if the dst_path should be emptied before copying' + default: false + required: false + file_filter: + description: 'A glob pattern for filtering file names' + required: false + filter: + description: 'A glob pattern for filtering file paths to be included for copying' + required: false + exclude: + description: 'A glob pattern for excluding paths' required: false src_wiki: description: 'Set to true if the source repository you want to copy from is the GitHub Wiki' @@ -56,7 +66,10 @@ runs: - ${{ inputs.dst_repo_name }} - ${{ inputs.src_branch }} - ${{ inputs.dst_branch }} - - ${{ inputs.src_filter }} + - ${{ inputs.clean }} + - ${{ inputs.file_filter }} + - ${{ inputs.filter }} + - ${{ inputs.exclude }} - ${{ inputs.src_wiki }} - ${{ inputs.dst_wiki }} - ${{ inputs.username }} diff --git a/entrypoint.sh b/entrypoint.sh index 034b94f..17b82ec 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,12 +1,14 @@ -#!/bin/sh +#!/bin/bash # # @author André Storhaug -# @date 2020-02-22 +# @date 2020-03-09 # @license MIT -# @version 2.0.0 +# @version 3.0.0 set -o pipefail +shopt -s extglob globstar nullglob dotglob + PERSONAL_TOKEN="$INPUT_PERSONAL_TOKEN" SRC_PATH="$INPUT_SRC_PATH" DST_PATH="$INPUT_DST_PATH" @@ -14,24 +16,28 @@ DST_OWNER="$INPUT_DST_OWNER" DST_REPO_NAME="$INPUT_DST_REPO_NAME" SRC_BRANCH="$INPUT_SRC_BRANCH" DST_BRANCH="$INPUT_DST_BRANCH" +CLEAN="$INPUT_CLEAN" +FILE_FILTER="$INPUT_FILE_FILTER" +FILTER="$INPUT_FILTER" +EXCLUDE="$INPUT_EXCLUDE" SRC_WIKI="$INPUT_SRC_WIKI" DST_WIKI="$INPUT_DST_WIKI" USERNAME="$INPUT_USERNAME" EMAIL="$INPUT_EMAIL" if [[ -z "$SRC_PATH" ]]; then - echo "SRC_PATH environment variable is missing. Cannot proceed." - exit 1 + echo "SRC_PATH environment variable is missing. Cannot proceed." + exit 1 fi if [[ -z "$DST_OWNER" ]]; then - echo "DST_OWNER environment variable is missing. Cannot proceed." - exit 1 + echo "DST_OWNER environment variable is missing. Cannot proceed." + exit 1 fi if [[ -z "$DST_REPO_NAME" ]]; then - echo "DST_REPO_NAME environment variable is missing. Cannot proceed." - exit 1 + echo "DST_REPO_NAME environment variable is missing. Cannot proceed." + exit 1 fi if [ "$SRC_WIKI" = "true" ]; then @@ -46,6 +52,10 @@ else DST_WIKI="" fi +if [[ -n "$EXCLUDE" && -z "$FILTER" ]]; then + FILTER="**" +fi + BASE_PATH=$(pwd) DST_PATH="${DST_PATH:-${SRC_PATH}}" @@ -60,15 +70,15 @@ SRC_REPO_NAME="${GITHUB_REPOSITORY#*/}${SRC_WIKI}" DST_REPO="${DST_OWNER}/${DST_REPO_NAME}${DST_WIKI}" DST_REPO_NAME="${DST_REPO_NAME}${DST_WIKI}" -DIR="${DST_PATH%/*}" +FINAL_SOURCE="${SRC_REPO_NAME}/${SRC_PATH}" git config --global user.name "${USERNAME}" git config --global user.email "${EMAIL}" -if [[ -z "$SRC_FILTER" ]]; then +if [[ -z "$FILE_FILTER" ]]; then echo "Copying \"${SRC_REPO_NAME}/${SRC_PATH}\" and pushing it to ${GITHUB_REPOSITORY}" else - echo "Copying files matching \"${SRC_FILTER}\" from \"${SRC_REPO_NAME}/${SRC_PATH}\" and pushing it to ${GITHUB_REPOSITORY}" + echo "Copying files matching \"${FILE_FILTER}\" from \"${SRC_REPO_NAME}/${SRC_PATH}\" and pushing it to ${GITHUB_REPOSITORY}" fi git clone --branch ${SRC_BRANCH} --single-branch --depth 1 https://${PERSONAL_TOKEN}@github.com/${SRC_REPO}.git @@ -78,8 +88,25 @@ if [ "$?" -ne 0 ]; then fi rm -rf ${SRC_REPO_NAME}/.git -if [[ -n "$SRC_FILTER" ]]; then - find ${SRC_REPO_NAME}/ -type f -not -name "${SRC_FILTER}" -exec rm {} \; +if [[ -n "$FILE_FILTER" ]]; then + find ${SRC_REPO_NAME}/ -type f -not -name "${FILE_FILTER}" -exec rm {} \; +fi + +if [[ -n "$FILTER" ]]; then + tmp_dir=$(mktemp -d -t ci-XXXXXXXXXX) + mkdir ${temp_dir}/${SRC_REPO_NAME} + cd ${SRC_REPO_NAME} + FINAL_SOURCE="${tmp_dir}/${SRC_REPO_NAME}/${SRC_PATH}" + for f in ${FILTER} ; do + [ -e "$f" ] || continue + [ -d "$f" ] && continue + if [[ -n "$EXCLUDE" ]] ; then + [[ $f == $EXCLUDE ]] && continue + fi + file_dir=$(dirname "${f}") + mkdir -p ${tmp_dir}/${SRC_REPO_NAME}/${file_dir} && cp ${f} ${tmp_dir}/${SRC_REPO_NAME}/${file_dir} + done + cd .. fi git clone --branch ${DST_BRANCH} --single-branch --depth 1 https://${PERSONAL_TOKEN}@github.com/${DST_REPO}.git @@ -88,14 +115,24 @@ if [ "$?" -ne 0 ]; then exit 1 fi -mkdir -p ${DST_REPO_NAME}/${DIR} || exit "$?" -cp -rf ${SRC_REPO_NAME}/${SRC_PATH} ${DST_REPO_NAME}/${DST_PATH} || exit "$?" +if [ "$CLEAN" = "true" ]; then + if [ -f "${DST_REPO_NAME}/${DST_PATH}" ] ; then + find ${DST_REPO_NAME}/${DST_PATH} -type f -not -path '*/\.git/*' -delete + elif [ -d "${DST_REPO_NAME}/${DST_PATH}" ] ; then + find ${DST_REPO_NAME}/${DST_PATH%/*}/* -type f -not -path '*/\.git/*' -delete + else + echo >&2 "Nothing to clean 🧽" + fi +fi + +mkdir -p ${DST_REPO_NAME}/${DST_PATH%/*} || exit "$?" +cp -rf ${FINAL_SOURCE} ${DST_REPO_NAME}/${DST_PATH} || exit "$?" cd ${DST_REPO_NAME} || exit "$?" -if [ -d "${BASE_PATH}/${SRC_REPO_NAME}/${SRC_PATH}" ]; then - COMMIT_MESSAGE="Update file(s) in \"${SRC_PATH}\" from \"${GITHUB_REPOSITORY}\"" +if [ -f "${BASE_PATH}/${FINAL_SOURCE}" ]; then + COMMIT_MESSAGE="Update file in \"${SRC_PATH}\" from \"${GITHUB_REPOSITORY}\"" else - COMMIT_MESSAGE="Update file \"${SRC_PATH}\" from \"${GITHUB_REPOSITORY}\"" + COMMIT_MESSAGE="Update file(s) \"${SRC_PATH}\" from \"${GITHUB_REPOSITORY}\"" fi if [ -z "$(git status --porcelain)" ]; then