Skip to content

chore: refactor release flow to have separate release and deployment stage#2648

Merged
stalniy merged 1 commit intomainfrom
chore/release-flow
Feb 4, 2026
Merged

chore: refactor release flow to have separate release and deployment stage#2648
stalniy merged 1 commit intomainfrom
chore/release-flow

Conversation

@stalniy
Copy link
Contributor

@stalniy stalniy commented Feb 2, 2026

Why

  1. It's when release creation and deployment are separate things. Then it's easy to substitute one or another.
  2. Allows to unify CI across all apps
  3. Allows to display release versions on deployments list instead of pointing all the time to main branch

Summary by CodeRabbit

  • New Features

    • Added a centralized "all apps" release orchestrator that detects affected apps (Next.js vs backend) and triggers per-app release pipelines.
    • Introduced reusable setup and release workflows to standardize image tag resolution and multi-stage releases.
  • Chores

    • Converted many workflows from push-based triggers to manual dispatch with explicit image_tag inputs.
    • Consolidated release flows to use setup-driven deploy steps and removed the provider-console release workflow.

@stalniy stalniy requested a review from a team as a code owner February 2, 2026 12:58
@stalniy stalniy force-pushed the chore/release-flow branch from 0b4141c to a6830e7 Compare February 2, 2026 12:58
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 2, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds an orchestration workflow and several reusable workflows to convert multiple app release flows from push-triggered build/release to a setup-driven, workflow_dispatch orchestration that normalizes image tags, computes affected apps, and triggers per-app reusable release/deploy workflows.

Changes

Cohort / File(s) Summary
Master Orchestration
.github/workflows/all-release.yml
New orchestrator workflow that analyzes changes under apps/*/package.json, categorizes apps (Next.js vs backend), emits matrices, and invokes per-app reusable release workflows.
Reusable Workflows
.github/workflows/reusable-deploy-setup.yml, .github/workflows/reusable-release-app.yml, .github/workflows/reusable-release-nextjs-app.yml
New reusable workflows: deploy-setup (normalizes image_tag, queries registry), reusable-release-app (release→build→deploy for backends), reusable-release-nextjs-app (release + beta/prod builds and optional deploy).
Service Workflows (converted to workflow_dispatch + setup)
.github/workflows/console-api-release.yml, .github/workflows/console-web-release.yml, .github/workflows/indexer-release.yml, .github/workflows/notifications-release.yml, .github/workflows/provider-proxy-release.yml, .github/workflows/stats-web-release.yml, .github/workflows/tx-signer-release.yml
Replaced push triggers with workflow_dispatch accepting image_tag; removed release/build jobs and added setup job that calls reusable-deploy-setup.yml; deployment jobs now consume needs.setup.outputs.image_tag.
Removed Workflow
.github/workflows/provider-console-release.yml
Workflow deleted; its prior release/build responsibilities are replaced by the orchestrator and reusable workflows.
New Orchestrator/Release Runner
.github/workflows/all-release.yml, .github/workflows/reusable-release-*.yml
Introduces matrix outputs and conditional downstream invocation of reusable-release-app and reusable-release-nextjs-app via reusable workflows. (Grouped here to highlight cross-file control flow.)

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Events
    participant All as all-release.yml
    participant Analyzer as Commit Analyzer
    participant Matrix as Matrix Outputs<br/>(nextjs & backends)
    participant Reusable as Reusable Workflows<br/>(deploy-setup / release-app / release-nextjs)
    participant Registry as Container Registry / gh api
    participant DeployWF as Per-app Deploy Workflows

    GH->>All: push to main / workflow_dispatch
    All->>Analyzer: scan changed `apps/*/package.json`
    Analyzer->>Matrix: emit nextjs & backend matrices
    Matrix->>Reusable: invoke `reusable-release-nextjs-app` / `reusable-release-app` per app
    Reusable->>Registry: (via `reusable-deploy-setup`) normalize `image_tag` / query latest tags
    Reusable->>DeployWF: trigger per-app deploy workflows with computed `image_tag` and tag refs
    DeployWF->>GH: run deployment workflows (beta/prod)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • ygrishajev
  • baktun14

Poem

🐰 I hopped through yaml fields tonight,

stitched matrices by lantern light,
normalized tags with gentle paw,
and told each app precisely "go!" — hurrah,
now releases hum in tidy rows ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the PR: refactoring the release workflow to separate release creation and deployment stages.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/release-flow

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Reviews will stop working after February 8, 2026 if the new IP is not added to your allowlist.


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

@stalniy stalniy force-pushed the chore/release-flow branch 2 times, most recently from 71c8f40 to 5273bc4 Compare February 2, 2026 13:02
Copy link
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: 4

🤖 Fix all issues with AI agents
In @.github/workflows/all-release.yml:
- Around line 24-58: Replace the current commit-parsing approach that reads
github.event.commits (env COMMITS) with a git-diff based detection: ensure
actions/checkout@v6 is invoked with full history (fetch-depth: 0), run git diff
--name-only between `${{ github.event.before }}` and `${{ github.sha }}` to
produce the list of changed files, then feed that list into the existing
changedFiles/APP_ALIASES/workflowFile logic (the node script block that builds
changedFiles, apps, nextjs, backends) so it looks for paths matching
^apps/[^/]+/package\.json from the git-diff output instead of parsing commits
from COMMITS; keep the rest of the mapping (workflowFile, APP_ALIASES,
nextjs/backends filtering) unchanged.

In @.github/workflows/console-api-release.yml:
- Around line 1-17: Add an explicit top-level permissions block to the workflow
to limit GITHUB_TOKEN scope instead of relying on defaults: insert a
"permissions:" section at the root (right after the "on: workflow_dispatch"
block) and grant only the minimal rights the deploy needs (for example:
contents: read, packages: write, id-token: write) while keeping other
permissions unset or explicitly "none"; update the workflow inputs/uses
(workflow_dispatch and the reusable "uses:
./.github/workflows/reusable-deploy-setup.yml" with image_tag) unchanged so the
reusable job still receives the image_tag input.

In @.github/workflows/console-web-release.yml:
- Around line 1-17: The workflow uses workflow-level permissions instead of
per-job permissions; update the jobs to follow deny-by-default by adding
explicit job-level permissions where needed—e.g., add permissions: { contents:
read } to the setup job (job name "setup") and to the "test-beta" job that runs
actions/checkout@v4, ensure any other jobs that require access declare minimal
permissions (the "deploy-prod" job already does this), and remove or avoid
global workflow-level permissions so each job declares its own least-privilege
permissions.

In @.github/workflows/reusable-deploy-setup.yml:
- Around line 35-38: The gh api call that sets version from the registry can
return empty or an error payload, so capture its output into a variable (the
current version assignment), validate that it's a non-empty tag and not an error
object (e.g., check for empty string or presence of an error/message field), and
if validation fails log a clear error including $package_name and the gh
response and exit non‑zero to stop deployment; update the block around the gh
api call and the variables version and package_name accordingly to perform this
check and fail fast when the package or versions are missing.
🧹 Nitpick comments (6)
.github/workflows/reusable-deploy-setup.yml (1)

27-43: Consider adding set -euo pipefail for safer shell execution.

Without strict mode, commands can fail silently, and the script may continue with unexpected values. This is especially important since the output drives production deployments.

♻️ Proposed improvement
        run: |
+         set -euo pipefail
          tag="${{ inputs.image_tag }}"
          package_name="${tag%%/*}"
          version="${tag#*/}"
.github/workflows/indexer-release.yml (1)

13-16: Consider adding explicit permissions to the setup job.

CodeQL flags the missing permissions block. While the reusable workflow defines contents: read, adding explicit minimal permissions here provides defense-in-depth.

♻️ Proposed fix
  setup:
+   permissions:
+     contents: read
    uses: ./.github/workflows/reusable-deploy-setup.yml
    with:
      image_tag: indexer/${{ inputs.image_tag }}
.github/workflows/notifications-release.yml (1)

13-26: Add permissions blocks to setup and deploy-beta for consistency.

The deploy-prod job has explicit permissions while setup and deploy-beta do not. Adding minimal permissions to all jobs ensures consistent security posture and addresses the CodeQL warnings.

♻️ Proposed fix
  setup:
+   permissions:
+     contents: read
    uses: ./.github/workflows/reusable-deploy-setup.yml
    with:
      image_tag: notifications/${{ inputs.image_tag }}

  deploy-beta:
+   permissions:
+     contents: read
+     pull-requests: read
    needs: setup
    name: Deploy to beta
.github/workflows/provider-proxy-release.yml (1)

13-38: Consider adding permissions to setup and beta deploy jobs for consistency.

The test and production jobs have explicit permissions while setup and beta deploy jobs do not. This inconsistency is flagged by CodeQL. Adding minimal permissions provides defense-in-depth.

♻️ Proposed fix for setup and beta jobs
  setup:
+   permissions:
+     contents: read
    uses: ./.github/workflows/reusable-deploy-setup.yml
    with:
      image_tag: provider-proxy/${{ inputs.image_tag }}

  deploy-beta-mainnet:
+   permissions:
+     contents: read
+     pull-requests: read
    needs: setup
    name: Deploy to beta mainnet
    ...

  deploy-beta-sandbox:
+   permissions:
+     contents: read
+     pull-requests: read
    needs: setup
    name: Deploy to beta sandbox
.github/workflows/stats-web-release.yml (1)

13-26: Add permissions to setup and deploy-beta for consistency with deploy-prod.

Same pattern as other workflows - deploy-prod has explicit permissions while earlier jobs do not.

♻️ Proposed fix
  setup:
+   permissions:
+     contents: read
    uses: ./.github/workflows/reusable-deploy-setup.yml
    with:
      image_tag: stats-web/${{ inputs.image_tag }}

  deploy-beta:
+   permissions:
+     contents: read
+     pull-requests: read
    needs: setup
    name: Deploy to beta
.github/workflows/tx-signer-release.yml (1)

13-38: Add permissions to setup and beta deploy jobs for consistency.

The production jobs have explicit permissions while setup and beta jobs do not, creating inconsistency flagged by CodeQL.

♻️ Proposed fix
  setup:
+   permissions:
+     contents: read
    uses: ./.github/workflows/reusable-deploy-setup.yml
    with:
      image_tag: tx-signer/${{ inputs.image_tag }}

  deploy-beta-mainnet:
+   permissions:
+     contents: read
+     pull-requests: read
    needs: setup
    name: Deploy to beta mainnet
    ...

  deploy-beta-sandbox:
+   permissions:
+     contents: read
+     pull-requests: read
    needs: setup
    name: Deploy to beta sandbox

Comment on lines +24 to +58
- uses: actions/checkout@v6
- id: matrix
env:
COMMITS: ${{ toJson(github.event.commits) }}
# shellcheck disable=SC2016
run: |
node -e '
const fs = require("fs");
const commits = JSON.parse(process.env.COMMITS);

// App to deploy workflow mapping
const APP_ALIASES = {
"api": "console-api",
"deploy-web": "console-web",
};
const workflowFile = (app) => (APP_ALIASES[app] || app) + "-release.yml";

// Get all changed files from all commits
const changedFiles = commits.flatMap(c => [...c.added, ...c.modified]);
const apps = Array.from(new Set(
changedFiles
.filter(f => f.match(/^apps\/[^/]+\/package\.json$/))
.map(f => f.split("/")[1])
));

const nextjs = apps
.filter(a => fs.readdirSync("apps/" + a).some(f => f.startsWith("next.config")))
.map(a => ({ app: a, deploy_workflow: workflowFile(a) }));
const backends = apps
.filter(a => !nextjs.some(n => n.app === a) && a !== "log-collector")
.map(a => ({ app: a, deploy_workflow: workflowFile(a) }));

console.log("nextjs=" + JSON.stringify({ include: nextjs }));
console.log("backends=" + JSON.stringify({ include: backends }));
' >> "$GITHUB_OUTPUT"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

GitHub push event commits truncation limit cap maximum

💡 Result:

  • Activity Events API PushEvent (/events, user/org/repo events timelines): the payload.commits array is capped at 20 commits (historically), and you’re expected to fetch the rest via other APIs. This 20-commit cap is explicitly described as applying to “timeline events”. [1]
  • But note (important): GitHub announced that starting October 7, 2025, the Events API push payloads were trimmed (“removing commit summaries and counts”), and reports indicate payload.commits was removed from PushEvent in practice for some consumers. [2], [3]
  • Webhooks / GitHub Actions push event payload (on: push, webhook deliveries): commits is capped at 2048 commits per delivery. [4]

Sources:
[1] GitHub Docs — GitHub event types (PushEvent payload; “maximum of 20 commits… limit … timeline events only”) (docs.github.com)
[2] GitHub Changelog — Upcoming changes to GitHub Events API payloads (implemented Oct 7, 2025; push events trimmed) (github.blog)
[3] GitHub Community discussion noting payload.commits removal after the Oct 7, 2025 changes (github.com)
[4] GitHub Docs (Webhook push payload; “maximum of 2048 commits”) (docs.github.com)

Citations:


Use git diff to reliably detect all changed apps regardless of push size.
Line 27 reads github.event.commits, which is capped at 2048 commits in GitHub Actions push events; while a rare edge case, pushes exceeding this limit will miss package.json changes. Using git diff between ${{ github.event.before }} and ${{ github.sha }} with full fetch history is more robust and future-proof.

✅ Suggested fix using git diff
   setup:
     runs-on: ubuntu-latest
     outputs:
       backends: ${{ steps.matrix.outputs.backends }}
       nextjs: ${{ steps.matrix.outputs.nextjs }}
     steps:
-      - uses: actions/checkout@v6
+      - uses: actions/checkout@v6
+        with:
+          fetch-depth: 0
       - id: matrix
         env:
-          COMMITS: ${{ toJson(github.event.commits) }}
+          BEFORE: ${{ github.event.before }}
+          AFTER: ${{ github.sha }}
         # shellcheck disable=SC2016
         run: |
           node -e '
             const fs = require("fs");
-            const commits = JSON.parse(process.env.COMMITS);
+            const { execSync } = require("child_process");
 
             // App to deploy workflow mapping
             const APP_ALIASES = {
               "api": "console-api",
               "deploy-web": "console-web",
             };
             const workflowFile = (app) => (APP_ALIASES[app] || app) + "-release.yml";
 
             // Get all changed files from all commits
-            const changedFiles = commits.flatMap(c => [...c.added, ...c.modified]);
+            const changedFiles = execSync(`git diff --name-only ${process.env.BEFORE} ${process.env.AFTER}`)
+              .toString()
+              .trim()
+              .split("\n")
+              .filter(Boolean);
             const apps = Array.from(new Set(
               changedFiles
                 .filter(f => f.match(/^apps\/[^/]+\/package\.json$/))
                 .map(f => f.split("/")[1])
             ));
📝 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
- uses: actions/checkout@v6
- id: matrix
env:
COMMITS: ${{ toJson(github.event.commits) }}
# shellcheck disable=SC2016
run: |
node -e '
const fs = require("fs");
const commits = JSON.parse(process.env.COMMITS);
// App to deploy workflow mapping
const APP_ALIASES = {
"api": "console-api",
"deploy-web": "console-web",
};
const workflowFile = (app) => (APP_ALIASES[app] || app) + "-release.yml";
// Get all changed files from all commits
const changedFiles = commits.flatMap(c => [...c.added, ...c.modified]);
const apps = Array.from(new Set(
changedFiles
.filter(f => f.match(/^apps\/[^/]+\/package\.json$/))
.map(f => f.split("/")[1])
));
const nextjs = apps
.filter(a => fs.readdirSync("apps/" + a).some(f => f.startsWith("next.config")))
.map(a => ({ app: a, deploy_workflow: workflowFile(a) }));
const backends = apps
.filter(a => !nextjs.some(n => n.app === a) && a !== "log-collector")
.map(a => ({ app: a, deploy_workflow: workflowFile(a) }));
console.log("nextjs=" + JSON.stringify({ include: nextjs }));
console.log("backends=" + JSON.stringify({ include: backends }));
' >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v6
with:
fetch-depth: 0
- id: matrix
env:
BEFORE: ${{ github.event.before }}
AFTER: ${{ github.sha }}
# shellcheck disable=SC2016
run: |
node -e '
const fs = require("fs");
const { execSync } = require("child_process");
// App to deploy workflow mapping
const APP_ALIASES = {
"api": "console-api",
"deploy-web": "console-web",
};
const workflowFile = (app) => (APP_ALIASES[app] || app) + "-release.yml";
// Get all changed files from all commits
const changedFiles = execSync(`git diff --name-only ${process.env.BEFORE} ${process.env.AFTER}`)
.toString()
.trim()
.split("\n")
.filter(Boolean);
const apps = Array.from(new Set(
changedFiles
.filter(f => f.match(/^apps\/[^/]+\/package\.json$/))
.map(f => f.split("/")[1])
));
const nextjs = apps
.filter(a => fs.readdirSync("apps/" + a).some(f => f.startsWith("next.config")))
.map(a => ({ app: a, deploy_workflow: workflowFile(a) }));
const backends = apps
.filter(a => !nextjs.some(n => n.app === a) && a !== "log-collector")
.map(a => ({ app: a, deploy_workflow: workflowFile(a) }));
console.log("nextjs=" + JSON.stringify({ include: nextjs }));
console.log("backends=" + JSON.stringify({ include: backends }));
' >> "$GITHUB_OUTPUT"
🤖 Prompt for AI Agents
In @.github/workflows/all-release.yml around lines 24 - 58, Replace the current
commit-parsing approach that reads github.event.commits (env COMMITS) with a
git-diff based detection: ensure actions/checkout@v6 is invoked with full
history (fetch-depth: 0), run git diff --name-only between `${{
github.event.before }}` and `${{ github.sha }}` to produce the list of changed
files, then feed that list into the existing
changedFiles/APP_ALIASES/workflowFile logic (the node script block that builds
changedFiles, apps, nextjs, backends) so it looks for paths matching
^apps/[^/]+/package\.json from the git-diff output instead of parsing commits
from COMMITS; keep the rest of the mapping (workflowFile, APP_ALIASES,
nextjs/backends filtering) unchanged.

Comment on lines +1 to 17
name: Deploy Console Web

on:
push:
branches:
- main
paths:
- "apps/deploy-web/package.json"
workflow_dispatch:
inputs:
image_tag:
description: "Image tag to deploy (e.g., latest, 1.0.0, v1.0.0)"
required: true
type: string
default: latest

jobs:
release:
name: Create Release
uses: ./.github/workflows/reusable-create-github-release.yml
secrets: inherit
with:
app: deploy-web

build-beta:
needs: release
name: Build Beta Docker image
if: needs.release.outputs.git_tag != ''
uses: ./.github/workflows/reusable-build-image.yml
secrets: inherit
permissions:
contents: write
packages: write
setup:
uses: ./.github/workflows/reusable-deploy-setup.yml
with:
tag: ${{ needs.release.outputs.git_tag }}-beta
deployment-env: staging
image_tag: console-web/${{ inputs.image_tag }}

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

GitHub Actions GITHUB_TOKEN default permissions best practices security

💡 Result:

Best practices for GITHUB_TOKEN default permissions (security)

  1. Set the repository/org default to “Read repository contents” (restricted / read-only). This enforces least privilege by default and reduces blast radius if a workflow (or a third‑party action) is compromised. (docs.github.com)

    • Note: GitHub’s defaults can differ for older orgs/enterprises—instances created before Feb 2, 2023 may default to broader read/write unless you change the setting. (docs.github.com)
  2. Declare permissions explicitly per job (not globally), and only what that job needs. Prefer jobs.<job_id>.permissions so each job is forced to ask for the minimum scopes it needs. (wellarchitected.github.com)

  3. Avoid broad grants like write-all / read-all; use “deny by default” where possible. If a job doesn’t need repo access, set no permissions (e.g., permissions: {}) and then add only specific scopes to other jobs. (docs.github.com)

  4. Assume actions can access the token unless you restrict it. An action can read the token via the github.token context even if you didn’t explicitly pass ${{ secrets.GITHUB_TOKEN }}, so permission-scoping is your primary control. (docs.github.com)

  5. When GITHUB_TOKEN isn’t sufficient, prefer stronger patterns over long-lived PATs. Use a GitHub App installation token for narrowly-scoped repo/org access, and use OIDC to access cloud resources instead of storing cloud keys as secrets. (docs.github.com)

Minimal example (recommended pattern)

# default: no extra rights
permissions: {}

jobs:
  test:
    permissions:
      contents: read
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

  release:
    permissions:
      contents: write
      packages: write
    runs-on: ubuntu-latest
    steps:
      - run: ./release.sh

Citations:


🏁 Script executed:

# Check the reusable workflow to understand what permissions it needs
fd "reusable-deploy-setup.yml"

Repository: akash-network/console

Length of output: 47


🏁 Script executed:

# Search more broadly for reusable deploy setup workflow
rg "reusable-deploy-setup" --type yaml

Repository: akash-network/console

Length of output: 47


🏁 Script executed:

# List all .github/workflows files to find the reusable workflow
find .github/workflows -type f -name "*.yml" -o -name "*.yaml" 2>/dev/null | head -20

Repository: akash-network/console

Length of output: 906


🏁 Script executed:

# Search for any file with "reusable" in the name
find .github -name "*reusable*" -type f 2>/dev/null

Repository: akash-network/console

Length of output: 481


🏁 Script executed:

# Check the full content of console-web-release.yml
cat -n .github/workflows/console-web-release.yml

Repository: akash-network/console

Length of output: 1899


Use per-job permissions instead of global workflow-level permissions.

Per GitHub's security best practices, declare permissions explicitly at the job level (not workflow-level) with a deny-by-default approach. The deploy-prod job already follows this pattern correctly; apply the same approach to other jobs that need them. Specifically, test-beta runs actions/checkout@v4 and should declare permissions: { contents: read } to follow the principle of least privilege.

🔐 Recommended pattern
 jobs:
+  setup:
+    permissions: {}
     uses: ./.github/workflows/reusable-deploy-setup.yml
     with:
       image_tag: console-web/${{ inputs.image_tag }}
 
   deploy-beta:
+    permissions: {}
     needs: setup
     name: Deploy to beta
     uses: ./.github/workflows/reusable-deploy-k8s.yml
     secrets: inherit
 
   test-beta:
+    permissions:
+      contents: read
     runs-on: ubuntu-latest
     needs: deploy-beta
     steps:
       - uses: actions/checkout@v4

Note: deploy-prod already has correct job-level permissions.

🤖 Prompt for AI Agents
In @.github/workflows/console-web-release.yml around lines 1 - 17, The workflow
uses workflow-level permissions instead of per-job permissions; update the jobs
to follow deny-by-default by adding explicit job-level permissions where
needed—e.g., add permissions: { contents: read } to the setup job (job name
"setup") and to the "test-beta" job that runs actions/checkout@v4, ensure any
other jobs that require access declare minimal permissions (the "deploy-prod"
job already does this), and remove or avoid global workflow-level permissions so
each job declares its own least-privilege permissions.

Comment on lines +35 to +38
if [[ "$version" == "latest" ]]; then
# Fetch latest from registry
version=$(gh api "/orgs/${{ github.repository_owner }}/packages/container/${package_name}/versions" --jq '.[0].metadata.container.tags[0]')
fi
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling for the registry API call.

If the package doesn't exist, has no versions, or the API call fails, version will be empty or contain an error message, potentially causing a deployment with an invalid image tag.

🛡️ Proposed fix with error handling
          if [[ "$version" == "latest" ]]; then
            # Fetch latest from registry
-           version=$(gh api "/orgs/${{ github.repository_owner }}/packages/container/${package_name}/versions" --jq '.[0].metadata.container.tags[0]')
+           version=$(gh api "/orgs/${{ github.repository_owner }}/packages/container/${package_name}/versions" --jq '.[0].metadata.container.tags[0]') || {
+             echo "::error::Failed to fetch latest version for package ${package_name}"
+             exit 1
+           }
+           if [[ -z "$version" || "$version" == "null" ]]; then
+             echo "::error::No tags found for package ${package_name}"
+             exit 1
+           fi
          fi
📝 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 [[ "$version" == "latest" ]]; then
# Fetch latest from registry
version=$(gh api "/orgs/${{ github.repository_owner }}/packages/container/${package_name}/versions" --jq '.[0].metadata.container.tags[0]')
fi
if [[ "$version" == "latest" ]]; then
# Fetch latest from registry
version=$(gh api "/orgs/${{ github.repository_owner }}/packages/container/${package_name}/versions" --jq '.[0].metadata.container.tags[0]') || {
echo "::error::Failed to fetch latest version for package ${package_name}"
exit 1
}
if [[ -z "$version" || "$version" == "null" ]]; then
echo "::error::No tags found for package ${package_name}"
exit 1
fi
fi
🤖 Prompt for AI Agents
In @.github/workflows/reusable-deploy-setup.yml around lines 35 - 38, The gh api
call that sets version from the registry can return empty or an error payload,
so capture its output into a variable (the current version assignment), validate
that it's a non-empty tag and not an error object (e.g., check for empty string
or presence of an error/message field), and if validation fails log a clear
error including $package_name and the gh response and exit non‑zero to stop
deployment; update the block around the gh api call and the variables version
and package_name accordingly to perform this check and fail fast when the
package or versions are missing.

@stalniy stalniy force-pushed the chore/release-flow branch from 5273bc4 to 7c6797d Compare February 4, 2026 06:11
@stalniy stalniy merged commit e3d4752 into main Feb 4, 2026
28 of 30 checks passed
@stalniy stalniy deleted the chore/release-flow branch February 4, 2026 06:12
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.

3 participants

Comments