Skip to content

ci: publish and deploy on merge to development instead of main - #58

Merged
abergasov merged 1 commit into
developmentfrom
ci/deploy-on-development
Aug 11, 2026
Merged

ci: publish and deploy on merge to development instead of main#58
abergasov merged 1 commit into
developmentfrom
ci/deploy-on-development

Conversation

@Taranpreet26311

@Taranpreet26311 Taranpreet26311 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Moves the Docker Image Publish trigger from main to development. Tag (v*) behaviour is unchanged.

Change

 on:
   push:
     branches:
-      - main
+      - development
     tags:
       - v*

Resulting behaviour

Event Publishes Deploys
merge → development gateway:<sha> + gateway:dev-latest bumps digest in gitops, triggers dev cluster deploy
tag v* gateway:<tag> + gateway:latest triggers Ansible VM deploy
merge → main nothing nothing

:latest stays tag-driven and is not affected.

Notes

  • The development branch has been created from main.
  • Also renames the deploy-dev step labels and token id from (main) to (development) so they describe what actually triggers them.
  • git push origin main in the gitops step is intentionally unchanged — that is the gitops repo's default branch, not this one.

Sequencing

This must merge to main for the change to take effect on both branches: for push events GitHub uses the workflow file from the pushed ref, so until main carries this, pushes to main would still publish. After merge, bring development up to date with main.

Summary by CodeRabbit

  • Chores
    • Updated automated Docker publishing to run from the development branch.
    • Renamed development deployment references for clearer environment alignment.

The Docker Image Publish workflow now triggers on push to `development`
rather than `main`. Tag (`v*`) behaviour is unchanged.

Effect:
  merge -> development : builds, pushes gateway:<sha> + gateway:dev-latest,
                         bumps the digest in gitops, triggers the dev cluster deploy
  tag v*               : builds, pushes gateway:<tag> + gateway:latest,
                         triggers the Ansible VM deploy
  merge -> main        : no longer publishes or deploys

`:latest` remains tag-driven and is untouched by this change.

Also renames the deploy-dev step labels and token id from "(main)" to
"(development)" so they match what actually triggers them. The `git push
origin main` on line 118 is the gitops repo's default branch and is
deliberately left alone.
@Taranpreet26311
Taranpreet26311 changed the base branch from main to development August 11, 2026 10:41
@abergasov
abergasov merged commit 9a12b43 into development Aug 11, 2026
9 of 10 checks passed
@abergasov
abergasov deleted the ci/deploy-on-development branch August 11, 2026 10:44
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Docker publishing workflow now runs on pushes to development instead of main. The development deployment steps use dev-specific token and step identifiers. Infrastructure dispatch behavior remains unchanged, including the deploy_gateways event, dev-latest tag, and built image digest.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers: abergasov, cryptofewka, swarna1101

🚥 Pre-merge checks | ✅ 7 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title describes the workflow change but does not follow the required type(domain/pkg): format because it uses the unsupported type ci and omits the domain/package. Use a valid type and domain/package, such as chore(ci): publish and deploy on merge to development instead of main.
Behavior Safety ⚠️ Warning The workflow changes production publishing and deployment from main to development, but the commit adds no focused workflow test or validation; only .github/workflows/docker-publish.yml changed. Add focused workflow validation covering development pushes, main pushes, v* tags, and the renamed token-step reference before merging.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Scope Discipline ✅ Passed The parent-to-HEAD diff changes only .github/workflows/docker-publish.yml and contains only the stated branch trigger and development-label/token renames; no unrelated files or dependencies changed.
Over-Engineering ✅ Passed The patch changes one workflow with 5 additions and 5 deletions: the branch and matching labels/ID reference. It adds no caches, helper layers, signature churn, or tests.
Security ✅ Passed Parent diff shows only the development trigger and matching step/token renames; no new credentials, shell interpolation, crypto, or secret logging was added.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/deploy-on-development

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/docker-publish.yml:
- Line 11: Restrict the Docker Hub credential currently exposed through the
docker job’s DOCKER_TOKEN environment to the docker/login-action step only. Pass
secrets.OPT_DOCKER_ACCESS_TOKEN directly to that action or define a step-scoped
environment variable, and remove the job-level secret scope while preserving the
existing login behavior.
- Line 11: Add job-level timeout-minutes values to both the docker and
deploy-dev jobs in the workflow, choosing bounds appropriate for their build and
deployment durations. Ensure each publishing job has a finite timeout while
preserving the existing job steps and behavior.
- Line 11: Add workflow-level permissions in the Docker publish workflow,
setting GITHUB_TOKEN access to contents: read. Preserve write access only
through the existing narrowly scoped GitHub App tokens used by the docker and
deploy-dev jobs.
- Line 11: Add a workflow-level concurrency declaration in the Docker publishing
workflow using one fixed shared group for all runs, rather than keying it by
github.ref, and set cancel-in-progress to false so publishing runs queue without
cancellation.
- Around line 176-183: Update both token-generation steps identified by
infra-dispatch-token and infra-dispatch-token-dev to explicitly set
permission-contents to write in their action inputs. Keep the existing
repository and authentication configuration unchanged.
🪄 Autofix

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: Repository: getoptimum/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ca1af303-64d3-46af-988b-63edbd4470e8

📥 Commits

Reviewing files that changed from the base of the PR and between 7936160 and f2cd3fa.

📒 Files selected for processing (1)
  • .github/workflows/docker-publish.yml

push:
branches:
- main
- development

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Limit the Docker Hub token to the login step.

DOCKER_TOKEN is job-scoped at Line 20, so every action and shell step in the docker job receives the credential. The new development trigger increases the code paths that receive this secret.

Pass secrets.OPT_DOCKER_ACCESS_TOKEN only to docker/login-action, or define it as a step-scoped environment variable.

As per path instructions, workflows must avoid secret exposure and use least-privilege secret scope.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-publish.yml at line 11, Restrict the Docker Hub
credential currently exposed through the docker job’s DOCKER_TOKEN environment
to the docker/login-action step only. Pass secrets.OPT_DOCKER_ACCESS_TOKEN
directly to that action or define a step-scoped environment variable, and remove
the job-level secret scope while preserving the existing login behavior.

Source: Path instructions


🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add bounded timeouts to both publishing jobs.

The docker and deploy-dev jobs have no job-level timeout-minutes. A multi-platform build or gh api dispatch can remain active indefinitely when a runner or external service hangs.

Add bounded values appropriate to the build and deployment durations.

As per path instructions, workflows must define job timeouts.

Also applies to: 176-192

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-publish.yml at line 11, Add job-level
timeout-minutes values to both the docker and deploy-dev jobs in the workflow,
choosing bounds appropriate for their build and deployment durations. Ensure
each publishing job has a finite timeout while preserving the existing job steps
and behavior.

Source: Path instructions


🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
file=".github/workflows/docker-publish.yml"

if ! rg -n -C 4 '^\s*permissions:' "$file"; then
  echo "No explicit permissions block found" >&2
  exit 1
fi

Repository: getoptimum/optimum-gateway

Length of output: 507


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
file=".github/workflows/docker-publish.yml"

printf '%s\n' '--- workflow header and triggers ---'
sed -n '1,45p' "$file"

printf '%s\n' '--- permissions, concurrency, timeouts, and jobs ---'
rg -n -C 3 '^[[:space:]]*(permissions|concurrency|timeout-minutes):|^[[:space:]]{2}[A-Za-z0-9_-]+:' "$file"

printf '%s\n' '--- complete job definitions around permissions and credentials ---'
sed -n '40,230p' "$file"

Repository: getoptimum/optimum-gateway

Length of output: 9464


Add workflow-level least-privilege permissions. docker and deploy-dev have no job-level permissions and currently inherit repository defaults for GITHUB_TOKEN, which may allow writes. Add permissions: contents: read at workflow scope. Keep write access in the narrowly scoped GitHub App tokens.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-publish.yml at line 11, Add workflow-level
permissions in the Docker publish workflow, setting GITHUB_TOKEN access to
contents: read. Preserve write access only through the existing narrowly scoped
GitHub App tokens used by the docker and deploy-dev jobs.

Source: Path instructions


🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
file=".github/workflows/docker-publish.yml"

if ! rg -n '^\s*concurrency:' "$file"; then
  echo "No workflow concurrency group found" >&2
  exit 1
fi

Repository: getoptimum/optimum-gateway

Length of output: 200


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
file=".github/workflows/docker-publish.yml"

printf '%s\n' '--- workflow structure ---'
ast-grep outline "$file" --lang yaml 2>/dev/null || true

printf '%s\n' '--- relevant declarations ---'
rg -n -C 3 '^(name:|on:|  [A-Za-z0-9_-]+:|permissions:|concurrency:|jobs:)|timeout-minutes|uses:|docker/login-action|docker/build-push-action|deploy-dev|GitHub App|permission-|DOCKER_TOKEN|git push|repository_dispatch|workflow_dispatch' "$file" || true

printf '%s\n' '--- numbered workflow sections ---'
sed -n '1,240p' "$file"

Repository: getoptimum/optimum-gateway

Length of output: 12763


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

path = Path(".github/workflows/docker-publish.yml")
text = path.read_text()

top_level = [
    line for line in text.splitlines()
    if line and not line.startswith((" ", "\t")) and re.match(r"^[A-Za-z0-9_-]+:", line)
]
print("top_level_keys:", [line.split(":", 1)[0] for line in top_level])
print("workflow_concurrency:", bool(re.search(r"(?m)^concurrency\s*:", text)))
print("docker_push_gitops_main:", bool(re.search(r"(?m)^\s*git push origin main\s*$", text)))
print("development_trigger:", bool(re.search(r"(?m)^\s*-\s*development\s*$", text)))
print("deploy_dev_dispatches:", "client_payload[digest]" in text and "dev-latest" in text)
print("docker_timeout:", bool(re.search(r"(?ms)^  docker:\n.*?^\s+timeout-minutes\s*:", text)))
print("deploy_dev_timeout:", bool(re.search(r"(?ms)^  deploy-dev:\n.*?^\s+timeout-minutes\s*:", text)))
PY

Repository: getoptimum/optimum-gateway

Length of output: 339


Add a shared workflow-level concurrency group

The workflow has no concurrency declaration. Concurrent runs can race on git push origin main and dispatch deployment digests out of order. Use one group for all publishing runs, not a group keyed by github.ref, because both branch and tag runs update the same GitOps branch. Set cancel-in-progress: false.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-publish.yml at line 11, Add a workflow-level
concurrency declaration in the Docker publishing workflow using one fixed shared
group for all runs, rather than keying it by github.ref, and set
cancel-in-progress to false so publishing runs queue without cancellation.

Source: Path instructions

Comment on lines +176 to 183
- name: Generate token for infrastructure dispatch (development)
id: infra-dispatch-token-dev
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
owner: getoptimum
repositories: infrastructure

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
curl -fsSL \
  "https://raw.githubusercontent.com/actions/create-github-app-token/29824e69f54612133e76f7eaac726eef6c875baf/action.yml" |
  rg -n 'permission-contents'

Repository: getoptimum/optimum-gateway

Length of output: 190


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- workflow structure and token references ---'
rg -n -C 5 \
  'create-github-app-token|infra-dispatch-token|repository_dispatch|DOCKER_TOKEN|permissions:|concurrency:|timeout-minutes|uses:' \
  .github/workflows/docker-publish.yml

printf '%s\n' '--- pinned action metadata ---'
curl -fsSL \
  'https://raw.githubusercontent.com/actions/create-github-app-token/29824e69f54612133e76f7eaac726eef6c875baf/action.yml' |
  sed -n '1,180p'

Repository: getoptimum/optimum-gateway

Length of output: 16818


🌐 Web query:

site:github.com/actions/create-github-app-token README permissions inputs omitted all permissions installation token repository dispatch Contents write

💡 Result:

When using the actions/create-github-app-token GitHub Action, you can explicitly define permissions for the generated installation access token by using the permission-{permission-name} input format [1][2]. By default, if no permission-* inputs are provided, the generated token inherits all permissions granted to the GitHub App installation [3][2]. However, it is a recommended security practice to explicitly list only the permissions required for your specific workflow use case [1][2]. Regarding your specific scenario: 1. Repository Dispatch: Creating a repository_dispatch event requires the contents: write permission [4][5]. If you are manually configuring permissions and find your workflow failing with a "Resource not accessible by integration" error, ensure that permission-contents: write is explicitly included in your action configuration [4][5]. 2. Omitted Permissions: If you previously relied on the default behavior (inheriting all permissions) and are now experiencing "Resource not accessible by integration" errors after adding permission-* inputs, it is likely that you have omitted a required permission [6]. Because the action now strictly enforces the permissions you define, the token will lack any permissions not explicitly specified in your input list [7][6]. To troubleshoot or resolve missing permission issues: - Verify that the GitHub App installation actually possesses the required permissions (permissions must be granted to the installation, not just the app) [2]. - If you are unsure which permissions are required for a specific API endpoint, consult the GitHub REST API documentation for that endpoint [5]. - If you wish to revert to default behavior, you can remove all permission-* inputs, causing the token to inherit all installation permissions again [6]. Alternatively, you can explicitly add all necessary permissions one by one until the error resolves [6].

Citations:


🌐 Web query:

site:docs.github.com REST repositories create a repository dispatch event GitHub App installation access token Contents write

💡 Result:

To trigger a repository dispatch event using a GitHub App, you must make a POST request to the GitHub REST API endpoint: POST /repos/{owner}/{repo}/dispatches [1][2]. To perform this action using a GitHub App installation access token, the app must have at least read-level access to the "Contents" repository permission [3]. This permission is required to subscribe to and trigger the repository_dispatch event [3][4]. When making the request, you must provide: 1. event_type: A custom string (100 characters or fewer) used as the event name [1][2]. 2. client_payload: An optional JSON object containing data that your GitHub Actions workflow or GitHub App can use [1][2]. Authentication is performed by including the GitHub App installation access token in the Authorization header of your request: Authorization: Bearer YOUR_INSTALLATION_ACCESS_TOKEN [5].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- pinned action README permission behavior ---'
curl -fsSL \
  'https://raw.githubusercontent.com/actions/create-github-app-token/29824e69f54612133e76f7eaac726eef6c875baf/README.md' |
  rg -n -C 8 'permission-|all permissions|permissions' | head -160

printf '%s\n' '--- official repository dispatch documentation ---'
curl -fsSL \
  'https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28' |
  python3 - <<'PY'
import sys
from html.parser import HTMLParser

class Text(HTMLParser):
    def __init__(self):
        super().__init__()
        self.parts = []
    def handle_data(self, data):
        value = " ".join(data.split())
        if value:
            self.parts.append(value)

p = Text()
p.feed(sys.stdin.read())
text = " ".join(p.parts)
needle = "Create a repository dispatch event"
pos = text.find(needle)
if pos < 0:
    print("dispatch section not found")
else:
    print(text[pos:pos + 5000])
PY

Repository: getoptimum/optimum-gateway

Length of output: 4191


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

doc=/tmp/github-repos-rest.html
curl -fsSL \
  'https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28' \
  -o "$doc"

printf '%s\n' '--- repository dispatch section in official REST docs ---'
rg -in -C 12 \
  'create a repository dispatch event|repository_dispatch|Contents.*(read|write)|fine-grained' \
  "$doc" | head -240

printf '%s\n' '--- official REST API description ---'
api=/tmp/github-rest-api.yaml
curl -fsSL \
  'https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.yaml' \
  -o "$api"

rg -n -C 16 \
  '/repos/\{owner\}/\{repo\}/dispatches|repository dispatch|contents.*write|contents.*read' \
  "$api" | head -240

Repository: getoptimum/optimum-gateway

Length of output: 50383


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from html.parser import HTMLParser
from html import unescape

path = "/tmp/github-repos-rest.html"

class Parser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.out = []
    def handle_data(self, data):
        text = " ".join(data.split())
        if text:
            self.out.append(text)

parser = Parser()
with open(path, encoding="utf-8") as f:
    parser.feed(f.read())

text = " ".join(parser.out)
start = text.find("Create a repository dispatch event")
if start < 0:
    raise SystemExit("dispatch section not found")

# Stop before the next endpoint heading.
end = text.find("Get the hash algorithm for a repository", start)
if end < 0:
    end = start + 7000

print(text[start:end][:9000])
PY

Repository: getoptimum/optimum-gateway

Length of output: 200


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from html import unescape
from html.parser import HTMLParser

path = "/tmp/github-repos-rest.html"
raw = open(path, encoding="utf-8").read()
needle = 'id="create-a-repository-dispatch-event"'
positions = []
pos = 0
while True:
    pos = raw.find(needle, pos)
    if pos < 0:
        break
    positions.append(pos)
    pos += len(needle)

print("heading occurrences:", len(positions), positions)
for i, pos in enumerate(positions):
    print(f"\n--- occurrence {i + 1} at {pos} ---")
    fragment = raw[max(0, pos - 500):pos + 12000]
    parser = HTMLParser()
    parts = []
    parser.handle_data = lambda data: parts.append(" ".join(data.split()))
    parser.feed(fragment)
    text = " ".join(x for x in parts if x)
    print(text[:9000])
PY

Repository: getoptimum/optimum-gateway

Length of output: 3600


Restrict both infrastructure App tokens.

Set permission-contents: write on infra-dispatch-token and infra-dispatch-token-dev. Both tokens call repos/getoptimum/infrastructure/dispatches. Without explicit permission-* inputs, the action inherits all permissions granted to the installation.

🧰 Tools
🪛 zizmor (1.29.0)

[error] 178-178: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions

(github-app)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-publish.yml around lines 176 - 183, Update both
token-generation steps identified by infra-dispatch-token and
infra-dispatch-token-dev to explicitly set permission-contents to write in their
action inputs. Keep the existing repository and authentication configuration
unchanged.

Sources: Path instructions, Linters/SAST tools

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