Skip to content
Draft
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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ By default, the action does not need any token configuration and uses the provid
| `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** |
| `attempt-limit` | How many rebase attempts to make before suspending the job. This option defaults to `3` and may be useful to increase when there are multiple deployments in a single branch. | `with` | **No** |
| `git-lfs` | Enable Git Large File Storage (LFS) support for handling large files. This should be set to `true` if your deployment folder contains files tracked by Git LFS. Defaults to `false`. | `with` | **No** |
| `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** |

Expand All @@ -132,6 +133,33 @@ This value is also set as a step output as `deployment-status`.

---

### Using Git Large File Storage (LFS) 📦

If your project contains files larger than 100MB, you'll need to use [Git Large File Storage (LFS)](https://git-lfs.github.com/) to avoid GitHub's file size limits. This action supports LFS files when the `git-lfs` input is set to `true`.

```yml
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: build
git-lfs: true
```

When `git-lfs` is enabled, the action will:
1. Install and configure Git LFS in the deployment environment
2. Properly handle LFS pointer files during the deployment process
3. Ensure large files are correctly pushed to GitHub Pages

**Important:** Make sure your large files are already tracked by LFS in your source repository before deploying. You can track files with LFS by running:

```bash
git lfs track "*.pck" # Example for .pck files
git add .gitattributes
git commit -m "Track large files with LFS"
```

---

### Using an SSH Deploy Key 🔑

If you'd prefer to use an SSH deploy key as opposed to a token you must first generate a new SSH key by running the following terminal command, replacing the email with one connected to your GitHub account.
Expand Down
104 changes: 104 additions & 0 deletions __tests__/git.lfs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Initial env variable setup for tests.
process.env['INPUT_FOLDER'] = 'build'
process.env['GITHUB_SHA'] = '123'

import {TestFlag} from '../src/constants'
import {execute} from '../src/execute'
import {generateWorktree} from '../src/worktree'

jest.mock('../src/execute', () => ({
__esModule: true,
execute: jest.fn(() => ({stdout: '', stderr: ''}))
}))

jest.mock('@actions/core', () => ({
setFailed: jest.fn(),
getInput: jest.fn(),
setOutput: jest.fn(),
isDebug: jest.fn(),
info: jest.fn()
}))

describe('Git LFS functionality', () => {
afterEach(() => {
jest.clearAllMocks()
})

describe('generateWorktree with LFS', () => {
it('should configure git lfs in worktree when gitLfs is enabled', async () => {
await generateWorktree(
{
hostname: 'github.com',
workspace: 'somewhere',
singleCommit: false,
branch: 'gh-pages',
folder: '',
silent: true,
gitLfs: true,
isTest: TestFlag.NONE
},
'worktree',
false
)

expect(execute).toHaveBeenCalledWith(
'git lfs install',
'somewhere/worktree',
true
)
})

it('should not configure git lfs in worktree when gitLfs is disabled', async () => {
await generateWorktree(
{
hostname: 'github.com',
workspace: 'somewhere',
singleCommit: false,
branch: 'gh-pages',
folder: '',
silent: true,
gitLfs: false,
isTest: TestFlag.NONE
},
'worktree',
false
)

expect(execute).not.toHaveBeenCalledWith(
'git lfs install',
'somewhere/worktree',
true
)
})

it('should throw error if git lfs installation fails in worktree', async () => {
;(execute as jest.Mock).mockImplementation((command: string) => {
if (command === 'git lfs install') {
throw new Error('LFS install failed')
}
return {stdout: '', stderr: ''}
})

try {
await generateWorktree(
{
hostname: 'github.com',
workspace: 'somewhere',
singleCommit: false,
branch: 'gh-pages',
folder: '',
silent: true,
gitLfs: true,
isTest: TestFlag.NONE
},
'worktree',
false
)
} catch (error) {
expect(error instanceof Error && error.message).toContain(
'There was an error configuring Git LFS in worktree: LFS install failed ❌'
)
}
})
})
})
50 changes: 50 additions & 0 deletions __tests__/git.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,56 @@ describe('git', () => {
await init(action)
expect(execute).toHaveBeenCalledTimes(7)
})

it('should configure git lfs when gitLfs is enabled', async () => {
Object.assign(action, {
hostname: 'github.com',
silent: false,
repositoryPath: 'JamesIves/github-pages-deploy-action',
token: '123',
branch: 'branch',
folder: '.',
gitLfs: true,
pusher: {
name: 'asd',
email: 'as@cat'
},
isTest: TestFlag.HAS_CHANGED_FILES
})

await init(action)
expect(execute).toHaveBeenCalledTimes(8)
expect(execute).toHaveBeenCalledWith(
'git lfs install',
action.workspace,
false
)
})

it('should not configure git lfs when gitLfs is disabled', async () => {
Object.assign(action, {
hostname: 'github.com',
silent: false,
repositoryPath: 'JamesIves/github-pages-deploy-action',
token: '123',
branch: 'branch',
folder: '.',
gitLfs: false,
pusher: {
name: 'asd',
email: 'as@cat'
},
isTest: TestFlag.HAS_CHANGED_FILES
})

await init(action)
expect(execute).toHaveBeenCalledTimes(7)
expect(execute).not.toHaveBeenCalledWith(
'git lfs install',
action.workspace,
false
)
})
})

describe('deploy', () => {
Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ inputs:
required: false
default: 3

git-lfs:
description: 'Enable Git Large File Storage (LFS) support for handling large files. This should be set to true if your deployment folder contains files tracked by Git LFS.'
required: false
default: false

outputs:
deployment-status:
description: 'The status of the deployment that indicates if the run failed or passed. Possible outputs include: success|failed|skipped'
3 changes: 3 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export default tseslint.config(
jest.configs['flat/recommended'],
eslint.configs.recommended,
...tseslint.configs.recommended,
{
ignores: ['lib/**/*']
},
{
languageOptions: {
globals: {
Expand Down
5 changes: 5 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export interface ActionInterface {
folderPath?: string
/** Whether to force-push or attempt to merge existing changes. */
force?: boolean
/** Enable Git Large File Storage (LFS) support for handling large files. */
gitLfs?: boolean | null
/** How many times to attempt to merge existing changes into the remote HEAD. */
attemptLimit?: number
/** Determines test scenarios the action is running in. */
Expand Down Expand Up @@ -97,6 +99,9 @@ export const action: ActionInterface = {
force: !isNullOrUndefined(getInput('force'))
? getInput('force').toLowerCase() === 'true'
: true,
gitLfs: !isNullOrUndefined(getInput('git-lfs'))
? getInput('git-lfs').toLowerCase() === 'true'
: false,
attemptLimit: !isNullOrUndefined(getInput('attempt-limit'))
? parseInt(getInput('attempt-limit'), 10)
: 3,
Expand Down
21 changes: 21 additions & 0 deletions src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,27 @@ export async function init(action: ActionInterface): Promise<void | Error> {
action.workspace,
action.silent
)

// Configure Git LFS if enabled
if (action.gitLfs) {
info('Configuring Git LFS…')
try {
await execute(
`git lfs install`,
action.workspace,
action.silent
)
info('Git LFS configured… 📦')
} catch (error) {
throw new Error(
`There was an error configuring Git LFS: ${suppressSensitiveInformation(
extractErrorMessage(error),
action
)} ❌`
)
}
}

info('Git configured… 🔧')
} catch (error) {
throw new Error(
Expand Down
21 changes: 21 additions & 0 deletions src/worktree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,27 @@ export async function generateWorktree(
} catch {
info('Unable to set worktree temp directory as a safe directory…')
}

/**
* Configure Git LFS in the worktree if enabled.
*/
if (action.gitLfs) {
try {
await execute(
`git lfs install`,
`${action.workspace}/${worktreedir}`,
action.silent
)
info('Git LFS configured in worktree… 📦')
} catch (error) {
throw new Error(
`There was an error configuring Git LFS in worktree: ${suppressSensitiveInformation(
extractErrorMessage(error),
action
)} ❌`
)
}
}
} catch (error) {
throw new Error(
`There was an error creating the worktree: ${suppressSensitiveInformation(
Expand Down
Loading