diff --git a/README.md b/README.md index 9ebfb9668..43ec05cde 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ By default, the action does not need any token configuration and uses the provid | `commit-message` | If you need to customize the commit message for an integration you can do so. | `with` | **No** | | `clean` | You can use this option to delete files from your deployment destination that no longer exist in your deployment source. One use case is if your project generates hashed files that vary from build to build. Using `clean` will not affect `.git`, `.github`, or `.ssh` directories. This option is turned on by default and can be toggled off by setting it to `false`. | `with` | **No** | | `clean-exclude` | If you need to use `clean` but you'd like to preserve certain files or folders you can use this option. This should contain each pattern as a single line in a multiline string. | `with` | **No** | +| `exclude` | Custom files and folders to exclude from deployment. This should contain each pattern as a single line in a multiline string. If omitted, the action excludes `.git`, `.github` and `.ssh` by default. | `with` | **No** | | `dry-run` | Do not actually push back, but use `--dry-run` on `git push` invocations instead. | `with` | **No** | | `single-commit` | This option can be toggled to `true` if you'd prefer to have a single commit on the deployment branch instead of maintaining the full history. **Using this option will also cause any existing history to be wiped from the deployment branch**. | `with` | **No** | | `force` | Force-push new deployments to overwrite the previous version; otherwise, attempt to rebase new deployments onto any existing ones. This option is turned on by default and can be toggled off by setting it to `false`, which may be useful if there are multiple deployments in a single branch. | `with` | **No** | @@ -116,6 +117,8 @@ By default, the action does not need any token configuration and uses the provid | `silent` | Silences the action output preventing it from displaying git messages. | `with` | **No** | | `tag` | Add a tag to the commit. Only works when `dry-run` is not used. | `with` | **No** | +**⚠️ Warning:** By default, the action excludes `.git`, `.github`, and `.ssh` during deployment to help prevent accidental circular workflows and deployment conflicts. If you provide `exclude`, those defaults are replaced by your custom list. + With the action correctly configured you should see the workflow trigger the deployment under the configured conditions. #### Deployment Status diff --git a/__tests__/git.test.ts b/__tests__/git.test.ts index 9d02bba9f..6ee53dd17 100644 --- a/__tests__/git.test.ts +++ b/__tests__/git.test.ts @@ -347,6 +347,60 @@ describe('git', () => { expect(rmRF).toHaveBeenCalledTimes(1) }) + it('should use default deploy excludes when no custom excludes are provided', async () => { + Object.assign(action, { + hostname: 'github.com', + silent: false, + folder: 'assets', + folderPath: 'assets', + branch: 'branch', + token: '123', + pusher: { + name: 'asd', + email: 'as@cat' + }, + isTest: TestFlag.HAS_CHANGED_FILES + }) + + await deploy(action) + + const rsyncCommand = (execute as jest.Mock).mock.calls.find( + ([cmd]) => typeof cmd === 'string' && cmd.startsWith('rsync ') + )?.[0] + + expect(rsyncCommand).toContain('--exclude .ssh') + expect(rsyncCommand).toMatch(/--exclude \.git(\s|$)/) + expect(rsyncCommand).toContain('--exclude .github') + }) + + it('should use custom deploy excludes instead of defaults', async () => { + Object.assign(action, { + hostname: 'github.com', + silent: false, + folder: 'assets', + folderPath: 'assets', + branch: 'branch', + token: '123', + exclude: ['.github', 'file.xml'], + pusher: { + name: 'asd', + email: 'as@cat' + }, + isTest: TestFlag.HAS_CHANGED_FILES + }) + + await deploy(action) + + const rsyncCommand = (execute as jest.Mock).mock.calls.find( + ([cmd]) => typeof cmd === 'string' && cmd.startsWith('rsync ') + )?.[0] + + expect(rsyncCommand).toContain('--exclude .github') + expect(rsyncCommand).toContain('--exclude file.xml') + expect(rsyncCommand).not.toContain('--exclude .ssh') + expect(rsyncCommand).not.toMatch(/--exclude \.git(\s|$)/) + }) + it('should gracefully handle target folder', async () => { Object.assign(action, { hostname: 'github.com', diff --git a/action.yml b/action.yml index 941fa119a..75012cf38 100644 --- a/action.yml +++ b/action.yml @@ -57,6 +57,10 @@ inputs: description: 'If you need to use clean but you would like to preserve certain files or folders you can use this option. This should contain each pattern as a single line in a multiline string.' required: false + exclude: + description: 'Custom files and folders to exclude from deployment. This should contain each pattern as a single line in a multiline string. If omitted, the action excludes .git, .github and .ssh by default.' + required: false + dry-run: description: 'Do not actually push back, but use `--dry-run` on `git push` invocations instead.' required: false diff --git a/src/constants.ts b/src/constants.ts index 83ea7a160..6483d7803 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -26,6 +26,8 @@ export interface ActionInterface { clean?: boolean | null /** If you need to use CLEAN but you'd like to preserve certain files or folders you can use this option. */ cleanExclude?: string[] + /** Custom list of files or folders to exclude from deployment. */ + exclude?: string[] /** If you need to customize the commit message for an integration you can do so. */ commitMessage?: string /** The hostname of which the GitHub Workflow is being run on, ie: github.com */ @@ -106,6 +108,7 @@ export const action: ActionInterface = { cleanExclude: (getInput('clean-exclude') || '') .split('\n') .filter(l => l !== ''), + exclude: (getInput('exclude') || '').split('\n').filter(l => l !== ''), hostname: process.env.GITHUB_SERVER_URL ? stripProtocolFromUrl(process.env.GITHUB_SERVER_URL) : 'github.com', diff --git a/src/git.ts b/src/git.ts index e9619c8b7..ac5839169 100644 --- a/src/git.ts +++ b/src/git.ts @@ -160,6 +160,25 @@ export async function deploy(action: ActionInterface): Promise { } } + const defaultExcludes = [ + DefaultExcludedFiles.SSH, + DefaultExcludedFiles.GIT, + DefaultExcludedFiles.GITHUB + ] + const deployExcludes = + action.exclude && action.exclude.length > 0 + ? action.exclude + : defaultExcludes + const rsyncExcludeList = [...deployExcludes] + + if (action.folderPath === action.workspace) { + rsyncExcludeList.push(temporaryDeploymentDirectory) + } + + const rsyncExcludes = rsyncExcludeList + .map(item => `--exclude ${item}`) + .join(' ') + if (action.targetFolder) { info(`Creating target folder if it doesn't already exist… 📌`) await mkdirP(`${temporaryDeploymentDirectory}/${action.targetFolder}`) @@ -190,13 +209,7 @@ export async function deploy(action: ActionInterface): Promise { : '' }` : '' - } --exclude ${DefaultExcludedFiles.SSH} --exclude ${ - DefaultExcludedFiles.GIT - } --exclude ${DefaultExcludedFiles.GITHUB} ${ - action.folderPath === action.workspace - ? `--exclude ${temporaryDeploymentDirectory}` - : '' - }`, + } ${rsyncExcludes}`, action.workspace, action.silent )