Skip to content

fix(deployment): respect pinned commit SHA for deployments#9292

Open
lucaspluchon wants to merge 1 commit intocoollabsio:nextfrom
lucaspluchon:fix/pinned-commit-propagation
Open

fix(deployment): respect pinned commit SHA for deployments#9292
lucaspluchon wants to merge 1 commit intocoollabsio:nextfrom
lucaspluchon:fix/pinned-commit-propagation

Conversation

@lucaspluchon
Copy link
Copy Markdown

@lucaspluchon lucaspluchon commented Mar 29, 2026

Thank you for coolify, using it for my everyday life and projects, just wanna fix this because it can be annoying sometimes 💜

Changes

The Commit SHA field in Git Source doesnt actually do anything right now. You can set a specific commit but deploys still pull the latest from the branch every time.

Turns out the deploy function just ignores the saved SHA and defaults to "HEAD". And even if it didnt, the job runs git ls-remote after and overwrites it anyway with the branch head.

Fixed both spots so now the pinned SHA is actually used. Rollbacks and PR deploys stil work as before, they keep using their own commit.

Issues

Category

  • Bug fix
  • Improvement
  • New feature
  • Adding new one click service
  • Fixing or updating existing one click service

Preview

No visual changes, its a backend fix for deployment commit resolution.

AI Assistance

  • AI was NOT used to create this PR
  • AI was used (please describe below)

If AI was used:

  • Tools used: Claude Code
  • How extensively: Used to explore the codebase and help identify root cause. I reviewed and tested all changes myself.

Testing

  1. Set a specific commit SHA in the app git source settings
  2. Clicked deploy, checked the deployment queue and it has the right SHA now instead of HEAD
  3. Removed the SHA (back to HEAD), deployed again and it uses HEAD like normal
  4. Tested rollback, it still uses its own commit and doesnt get overriden by the pin
  5. Force rebuild with a pinned SHA, works correctly
  6. php artisan test --compact tests/Unit/PinnedCommitDeploymentTest.php - 3 pass
  7. php artisan test --compact tests/Feature/ApplicationRollbackTest.php - no regressions
  8. php artisan test --compact tests/Unit/GitLsRemoteParsingTest.php - no regressions

Contributor Agreement

Important

  • I have read and understood the contributor guidelines. If I have failed to follow any guideline, I understand that this PR may be closed without review.
  • I have searched existing issues and pull requests (including closed ones) to ensure this isn't a duplicate.
  • I have tested all the changes thoroughly with a local development instance of Coolify and I am confident that they will work as expected when a maintainer tests them.

Read git_commit_sha from the application model when queuing a
deployment so the pinned SHA is used instead of defaulting to HEAD.
Prevent git ls-remote from overwriting the pinned SHA in the
deployment job. Rollback and PR deployments are unaffected.

Fixes coollabsio#9204
Related: coollabsio#6881
@lucaspluchon
Copy link
Copy Markdown
Author

@coderabbitai review this bro

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

@lucaspluchon Ha, I'll be back... with a full review! 🤖

Hasta la vista, unreviewed code — let me take a look at this PR that fixes the pinned commit SHA being ignored during deployments. No more "HEAD" running the show when you specifically told it which commit to use! I shall analyze every line like I'm scanning for John Connor. 🌮

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

Walkthrough

This pull request implements support for pinned git commits in application deployments. The ApplicationDeploymentJob class is updated to detect and utilize a pinned git_commit_sha configuration for non-pull-request, non-rollback deployments, storing the pinned commit to the deployment queue. The queue_application_deployment() helper function is modified to conditionally override the commit argument with the application's pinned git_commit_sha under matching conditions. A new test suite PinnedCommitDeploymentTest verifies the pinned commit propagation behavior, ensuring the correct commit is used when explicit commits are provided versus when relying on pinned values, and validates behavior when the pinned SHA is 'HEAD'.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Jobs/ApplicationDeploymentJob.php`:
- Around line 2192-2202: The pinned-commit branch unconditionally overwrites any
commit already set earlier; change the logic so the pinned SHA is only applied
when no explicit commit was provided upstream. Concretely, update the
hasPinnedCommit check or add a guard before assigning $this->commit and updating
$this->application_deployment_queue->commit so it only sets them if
$this->commit is empty/null (or equals a default like ''/ 'HEAD'), leaving
existing explicit $this->commit untouched.

In `@bootstrap/helpers/applications.php`:
- Around line 34-38: The current logic unconditionally overrides $commit with
$pinnedCommit when not a PR/rollback, which discards any explicitly provided
commit; change it so $pinnedCommit is applied only when no explicit commit was
supplied (e.g., $commit is empty or null) — keep the checks around
$pull_request_id and $rollback, and update the block using the variables
$pinnedCommit, $application->git_commit_sha and $commit so that you assign
$commit = $pinnedCommit only if $commit is not already set or is an empty
string.

In `@tests/Unit/PinnedCommitDeploymentTest.php`:
- Around line 21-61: Add tests asserting that an explicit non-PR commit passed
to the deployment flow is not overridden by an application's pinned SHA: 1) add
a unit test for the queue helper (queue_application_deployment) that sets
$this->application->git_commit_sha to a pinned SHA, calls
queue_application_deployment(...) with a commit => $explicitSha, then inspects
the queued job payload to assert it contains $explicitSha (and not the pinned
SHA); 2) add a unit test for ApplicationDeploymentJob::check_git_if_build_needed
(or invoke the job run path used in tests) that constructs a job payload
containing commit => $explicitSha while the Application model has git_commit_sha
set to the pinned SHA, then runs the job method and assert the job uses
$explicitSha for checkout/build decisions (i.e., the checked-out commit in
results/commands contains $explicitSha and not the pinned SHA). Reference
functions: queue_application_deployment(),
ApplicationDeploymentJob::check_git_if_build_needed(), and
Application::setGitImportSettings() when locating where to add/verify the
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: db7ba094-cc75-4eaa-b16e-01718e0e6968

📥 Commits

Reviewing files that changed from the base of the PR and between 9ccdbf6 and 923b9fd.

📒 Files selected for processing (3)
  • app/Jobs/ApplicationDeploymentJob.php
  • bootstrap/helpers/applications.php
  • tests/Unit/PinnedCommitDeploymentTest.php

Comment on lines +2192 to +2202
// Skip overwriting the commit when the application has a pinned SHA
$pinnedCommit = str(data_get($this->application, 'git_commit_sha'))->trim()->value();
$hasPinnedCommit = $this->pull_request_id === 0 && ! $this->rollback && $pinnedCommit !== '' && $pinnedCommit !== 'HEAD';

if ($hasPinnedCommit) {
$this->commit = $pinnedCommit;
if ($this->application_deployment_queue->commit !== $pinnedCommit) {
$this->application_deployment_queue->commit = $pinnedCommit;
$this->application_deployment_queue->save();
}
} elseif ($this->saved_outputs->get('git_commit_sha') && ! $this->rollback) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Job-level pinned commit logic also overrides explicit queued commits.

Line 2194 currently applies pinned SHA without checking whether $this->commit was intentionally set upstream. This can still deploy the wrong revision even after queue-time fixes. Skynet behavior detected. 😄

💡 Suggested fix
         $pinnedCommit = str(data_get($this->application, 'git_commit_sha'))->trim()->value();
-        $hasPinnedCommit = $this->pull_request_id === 0 && ! $this->rollback && $pinnedCommit !== '' && $pinnedCommit !== 'HEAD';
+        $hasPinnedCommit = $this->pull_request_id === 0
+            && ! $this->rollback
+            && ($this->commit === 'HEAD' || blank($this->commit))
+            && $pinnedCommit !== ''
+            && strcasecmp($pinnedCommit, 'HEAD') !== 0;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Jobs/ApplicationDeploymentJob.php` around lines 2192 - 2202, The
pinned-commit branch unconditionally overwrites any commit already set earlier;
change the logic so the pinned SHA is only applied when no explicit commit was
provided upstream. Concretely, update the hasPinnedCommit check or add a guard
before assigning $this->commit and updating
$this->application_deployment_queue->commit so it only sets them if
$this->commit is empty/null (or equals a default like ''/ 'HEAD'), leaving
existing explicit $this->commit untouched.

Comment on lines +34 to +38
if ($pull_request_id === 0 && ! $rollback) {
$pinnedCommit = str($application->git_commit_sha ?? '')->trim()->value();
if ($pinnedCommit !== '' && $pinnedCommit !== 'HEAD') {
$commit = $pinnedCommit;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Pinned SHA override is terminating explicit commit inputs (wrong revision risk).

At Line 34, pinned SHA is applied unconditionally for non-PR/non-rollback flows, even when a real commit was explicitly passed (webhook/API/manual). That makes deployments ignore the requested commit and can queue the wrong target commit.

💡 Suggested fix
-    if ($pull_request_id === 0 && ! $rollback) {
+    if ($pull_request_id === 0 && ! $rollback && ($commit === 'HEAD' || blank($commit))) {
         $pinnedCommit = str($application->git_commit_sha ?? '')->trim()->value();
-        if ($pinnedCommit !== '' && $pinnedCommit !== 'HEAD') {
+        if ($pinnedCommit !== '' && strcasecmp($pinnedCommit, 'HEAD') !== 0) {
             $commit = $pinnedCommit;
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ($pull_request_id === 0 && ! $rollback) {
$pinnedCommit = str($application->git_commit_sha ?? '')->trim()->value();
if ($pinnedCommit !== '' && $pinnedCommit !== 'HEAD') {
$commit = $pinnedCommit;
}
if ($pull_request_id === 0 && ! $rollback && ($commit === 'HEAD' || blank($commit))) {
$pinnedCommit = str($application->git_commit_sha ?? '')->trim()->value();
if ($pinnedCommit !== '' && strcasecmp($pinnedCommit, 'HEAD') !== 0) {
$commit = $pinnedCommit;
}
}
🧰 Tools
🪛 PHPMD (2.15.0)

[error] 34-34: The variable $pull_request_id is not named in camelCase. (undefined)

(CamelCaseVariableName)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bootstrap/helpers/applications.php` around lines 34 - 38, The current logic
unconditionally overrides $commit with $pinnedCommit when not a PR/rollback,
which discards any explicitly provided commit; change it so $pinnedCommit is
applied only when no explicit commit was supplied (e.g., $commit is empty or
null) — keep the checks around $pull_request_id and $rollback, and update the
block using the variables $pinnedCommit, $application->git_commit_sha and
$commit so that you assign $commit = $pinnedCommit only if $commit is not
already set or is an empty string.

Comment on lines +21 to +61
test('setGitImportSettings uses pinned commit when no explicit commit is passed', function () {
$pinnedSha = str_repeat('a', 40);
$this->application->git_commit_sha = $pinnedSha;

$result = $this->application->setGitImportSettings(
deployment_uuid: 'test-uuid',
git_clone_command: 'git clone',
public: true,
);

expect($result)->toContain($pinnedSha);
});

test('setGitImportSettings prefers explicit commit over pinned commit', function () {
$pinnedSha = str_repeat('a', 40);
$explicitSha = str_repeat('b', 40);
$this->application->git_commit_sha = $pinnedSha;

$result = $this->application->setGitImportSettings(
deployment_uuid: 'test-uuid',
git_clone_command: 'git clone',
public: true,
commit: $explicitSha,
);

expect($result)
->toContain($explicitSha)
->not->toContain($pinnedSha);
});

test('setGitImportSettings does not checkout when git_commit_sha is HEAD', function () {
$this->application->git_commit_sha = 'HEAD';

$result = $this->application->setGitImportSettings(
deployment_uuid: 'test-uuid',
git_clone_command: 'git clone',
public: true,
);

expect($result)->not->toContain('advice.detachedHead=false checkout');
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Coverage gap: changed queue/job commit precedence paths are not asserted.

These tests validate Application::setGitImportSettings(), but the regression risk in this PR is in queue_application_deployment() and ApplicationDeploymentJob::check_git_if_build_needed() precedence rules. Please add tests that verify explicit non-PR commit input is not overridden by pinned SHA.

I can draft two focused tests:

  1. queue helper keeps explicit commit when pinned SHA exists,
  2. job keeps queued explicit commit when pinned SHA exists.

Based on learnings: "Every code change must be programmatically tested; write a new test or update an existing test, then run affected tests to ensure they pass."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Unit/PinnedCommitDeploymentTest.php` around lines 21 - 61, Add tests
asserting that an explicit non-PR commit passed to the deployment flow is not
overridden by an application's pinned SHA: 1) add a unit test for the queue
helper (queue_application_deployment) that sets
$this->application->git_commit_sha to a pinned SHA, calls
queue_application_deployment(...) with a commit => $explicitSha, then inspects
the queued job payload to assert it contains $explicitSha (and not the pinned
SHA); 2) add a unit test for ApplicationDeploymentJob::check_git_if_build_needed
(or invoke the job run path used in tests) that constructs a job payload
containing commit => $explicitSha while the Application model has git_commit_sha
set to the pinned SHA, then runs the job method and assert the job uses
$explicitSha for checkout/build decisions (i.e., the checked-out commit in
results/commands contains $explicitSha and not the pinned SHA). Reference
functions: queue_application_deployment(),
ApplicationDeploymentJob::check_git_if_build_needed(), and
Application::setGitImportSettings() when locating where to add/verify the
behavior.

@Iisyourdad
Copy link
Copy Markdown
Contributor

Iisyourdad commented Apr 10, 2026

Hey @lucaspluchon I really like Coolify too and it really helps me out. Other than Coderabbitai's suggestions, here are my findings aswell.

I think there is still a precedence bug in the deployment commit flow that needs to be looked at again:

In bootstrap/helpers/applications.php, queue_application_deployment() now rewrites any non-PR/non-rollback $commit to the application's pinned git_commit_sha, even when the caller already supplied an explicit commit. That means webhook/API/manual deployments can silently deploy a different revision than the one they asked for.

In app/Jobs/ApplicationDeploymentJob.php, check_git_if_build_needed() repeats the same override later in the job. So even if the queue helper were fixed, the job can still replace the queued commit before checkout/build. Pinned SHA needs to be a fallback only when the commit is blank or HEAD; explicit commits should win.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants