AutomationFlowsDevOps › Ci Artifact Completeness Gate (git Push, Sentry Artifact Verification, Commit)

Ci Artifact Completeness Gate (git Push, Sentry Artifact Verification, Commit)

ByWeblineIndia @weblineindia on n8n.io

This workflow acts as a CI/CD quality gate for mobile app crash-symbolication artifacts. Whenever a new commit is pushed to GitHub, the workflow automatically checks the corresponding Sentry release and confirms whether required build artifacts (dSYM or ProGuard + mapping.txt)…

Event trigger★★★★☆ complexity10 nodesGithub TriggerHTTP Request
DevOps Trigger: Event Nodes: 10 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #11828 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "ZdMbuyTs5MXQpA1p",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "CI Artifact Completeness Gate",
  "tags": [],
  "nodes": [
    {
      "id": "9b94178b-9527-46bc-9e84-3673675c0951",
      "name": "Verify Artifacts",
      "type": "n8n-nodes-base.code",
      "position": [
        1040,
        0
      ],
      "parameters": {
        "jsCode": "\n\n// Let's use the simplest, most common way to get the data when running 'Once for All Items'\n// which is usually $json, or, more safely, $input.\n\nconst inputData = $input.all();\n\n// Step 1: Ensure we have an array of the file objects\nconst sentryFiles = Array.isArray(inputData) ? inputData : [];\n\n// Step 2: Handle empty or invalid input\n// Using standard JavaScript 'Array.isArray'\nif (sentryFiles.length === 0) {\n    return [{\n        json: { status: 'failure', description: 'No files returned from Sentry for analysis.' }\n    }];\n}\n\n// Step 3: Extract file names for easier checking\n// The map function can be executed directly on sentryFiles\n\nconst fileNames = sentryFiles.map(file => file.json.name);\n\n// Step 4: Check for dSYM (Priority Check)\nconst hasDSYM = fileNames.some(name => /\\.dSYM$/i.test(name));\n\n// Step 5: Check for ProGuard/Mapping (Fallback Check)\nconst hasProguard = fileNames.some(name => /proguard\\.txt$/i.test(name));\nconst hasMapping = fileNames.some(name => /mapping\\.txt$/i.test(name));\n\n// Step 6: Determine final status based on logic\nlet status, description;\n\n// Condition 1: dSYM exists (highest priority)\nif (hasDSYM) {\n    status = 'success';\n    description = 'Success: Valid dSYM artifact found.';\n} \n// Condition 2: dSYM does NOT exist, but BOTH ProGuard and Mapping exist\nelse if (hasProguard && hasMapping) {\n    status = 'success';\n    description = 'Success: dSYM not found, but ProGuard and Mapping.txt artifacts are complete.';\n} \n// Condition 3: Failure\nelse {\n    const missing = [];\n    if (!hasProguard) missing.push('proguard.txt');\n    if (!hasMapping) missing.push('mapping.txt');\n    \n    status = 'failure';\n    description = `Failure: Neither dSYM was found, nor was the complete set of ProGuard artifacts. Missing: ${missing.join(' and ')}`;\n}\n\n// Step 7: Return the final result\nreturn [{ json: { status, description } }];\n\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "81aa32b4-3621-4a87-94bd-d7d997ae9879",
      "name": "GithubPushTrigger",
      "type": "n8n-nodes-base.githubTrigger",
      "position": [
        -288,
        0
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "repo_owner"
        },
        "events": [
          "push"
        ],
        "options": {
          "insecureSSL": false
        },
        "repository": {
          "__rl": true,
          "mode": "list",
          "value": "repo_name",
          "cachedResultUrl": "https://github.com/repo_owner/repo_name",
          "cachedResultName": "repo_name"
        }
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "971f3b61-87b6-483d-974b-829917719e3b",
      "name": "Check Sentry Artifacts Releases",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -16,
        0
      ],
      "parameters": {
        "url": "=https://sentry.io/api/0/projects/org_slug/proj_slug/releases/\n",
        "options": {},
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            }
          ]
        },
        "nodeCredentialType": "sentryIoApi"
      },
      "typeVersion": 4.3
    },
    {
      "id": "0ac8589e-fb9d-4c24-a161-10dce31874da",
      "name": "Check Sentry Artifacts files",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        816,
        0
      ],
      "parameters": {
        "url": "=https://sentry.io/api/0/projects/org_slug/proj_slug/releases/{{ $json.version }}/files/\n",
        "options": {},
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            }
          ]
        },
        "nodeCredentialType": "sentryIoApi"
      },
      "typeVersion": 4.3,
      "alwaysOutputData": false
    },
    {
      "id": "cbe6788f-1026-4a02-a841-7b2dabe861a5",
      "name": "Update Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2320,
        0
      ],
      "parameters": {
        "url": "=https://api.github.com/repos/{{ $json.repoDetail.repoFullName }}/statuses/{{ $json.repoDetail.repoCommitSha }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "state",
              "value": "success"
            },
            {
              "name": "description",
              "value": "Artifacts successfully verified."
            }
          ]
        },
        "nodeCredentialType": "githubApi"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d9d95c07-078f-4015-ac47-d983486ee468",
      "name": "Artifacts Validation and Get Repository Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2016,
        0
      ],
      "parameters": {
        "jsCode": "\n\n// Let's use the simplest, most common way to get the data when running 'Once for All Items'\n// which is usually $json, or, more safely, $input.\nconst repoCommitSha = $node[\"GithubPushTrigger\"].json[\"body\"][\"after\"];\nconst repoFullName = $node[\"GithubPushTrigger\"].json[\"body\"][\"repository\"][\"full_name\"];\nconst repoDetail = {repoFullName, repoCommitSha};\nconst inputData = $input.all();\n\n// Step 7: Return the final result\nif ($input.first().json.status === 'success') {\nreturn [{ json: { inputData, repoDetail } }];\n} else {\n  return []\n}\n\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "0280557b-b520-4d7f-b887-8c4447660986",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1280,
        -352
      ],
      "parameters": {
        "width": 544,
        "height": 928,
        "content": "# How It Works\n### This workflow functions as a critical CI/CD Quality Gate, automatically verifying that all essential build artifacts are uploaded to Sentry before allowing code to be merged. The process is initiated by a Webhook or a GitHub Push event, which provides the specific release version and commit SHA. The workflow then uses HTTP Requests to query the Sentry API, retrieving a list of all files associated with that release. A Code node runs custom logic to verify completeness, checking for required files like dSYMs, ProGuard, or Mapping files using file name patterns. If verification succeeds, a final HTTP Request updates the commit status on GitHub to success, allowing the associated Pull Request to be merged.\n\n# Setup Steps\n## Add Trigger\nSet up a Webhook or a GitHub Trigger (for push events) to start the workflow.\n\n## Connect Accounts\nAdd credentials for both Sentry.io API and GitHub API.\n\n## Configure Requests\nUpdate the Sentry API URLs in the HTTP Request nodes with your organization and project slugs, ensuring they correctly interpolate the release version from the trigger.\n\n## Update GitHub Status\nConfigure the final HTTP Request node to post the verification status (success/failure) to your repository's commit status endpoint using the commit SHA."
      },
      "typeVersion": 1
    },
    {
      "id": "6723f1d4-5de5-4aae-bf80-d71e1a9cb331",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        -272
      ],
      "parameters": {
        "color": 7,
        "width": 912,
        "height": 576,
        "content": "## GithubPushTrigger & Check Sentry Artifacts Releases\nThe workflow begins with a GitHub Push Trigger (or Webhook) that starts the process upon a code push, capturing the vital commit_sha and repository details. Immediately, an HTTP Request node authenticates with the Sentry API to execute a GET request to the /releases/ endpoint. This step's purpose is to retrieve metadata for all existing Sentry releases within the project, which is necessary to locate and verify the files for the specific release associated with the triggering commit."
      },
      "typeVersion": 1
    },
    {
      "id": "0c252ab9-2b89-4300-a53d-a9447d08dbd4",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        -272
      ],
      "parameters": {
        "color": 7,
        "width": 976,
        "height": 576,
        "content": "## Get Sentry Artifacts Files and  Verify\nThe Check Sentry Artifacts files node first executes an HTTP GET request using a dynamic URL (e.g., .../releases/{{ $json.version }}/files/) to retrieve the full array of uploaded artifact objects for the specific release from the Sentry API. This artifact list is then consumed by the Verify Artifacts Code node, which contains the core business logic to validate completeness. This node implements a priority check: the release is deemed a success if a dSYM file is present, OR as a fallback, if both proguard.txt and mapping.txt are found. The final output of this validation step is an object providing the resulting status ('success' or 'failure') and a corresponding descriptive message."
      },
      "typeVersion": 1
    },
    {
      "id": "89ce57c9-0dde-43d2-84fb-e9847cd36b4f",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        -272
      ],
      "parameters": {
        "color": 7,
        "width": 1072,
        "height": 576,
        "content": "## Validate Artifacts and Updae Status\nThe Check Sentry Artifacts files node retrieves the uploaded artifacts list, which is validated by the Verify Artifacts Code node (checking for a dSYM OR both proguard.txt and mapping.txt).\nThe Artifacts Validation node enforces the CI gate: if the check fails, the workflow stops, preventing the merge. If successful, it passes the Commit SHA to the final Update Status node. This node then sends a successful HTTP POST request to the GitHub Commit Statuses API, which displays a green checkmark on the commit, signaling that the artifact gate has been successfully passed."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "3d6c6712-905b-4cb6-95aa-81e287c0bfa5",
  "connections": {
    "Verify Artifacts": {
      "main": [
        [
          {
            "node": "Artifacts Validation and Get Repository Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GithubPushTrigger": {
      "main": [
        [
          {
            "node": "Check Sentry Artifacts Releases",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Sentry Artifacts files": {
      "main": [
        [
          {
            "node": "Verify Artifacts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Sentry Artifacts Releases": {
      "main": [
        [
          {
            "node": "Check Sentry Artifacts files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Artifacts Validation and Get Repository Data": {
      "main": [
        [
          {
            "node": "Update Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow acts as a CI/CD quality gate for mobile app crash-symbolication artifacts. Whenever a new commit is pushed to GitHub, the workflow automatically checks the corresponding Sentry release and confirms whether required build artifacts (dSYM or ProGuard + mapping.txt)…

Source: https://n8n.io/workflows/11828/ — original creator credit. Request a take-down →

More DevOps workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

DevOps

Comprehensive LLM Usage Tracker & Cost Monitor with Node-Level Analytics. Uses n8n, executeWorkflowTrigger, stopAndError. Event-driven trigger; 19 nodes.

n8n, Execute Workflow Trigger, Stop And Error
DevOps

Health-Monitor. Uses httpRequest. Scheduled trigger; 6 nodes.

HTTP Request
DevOps

Need help? Want access to this workflow + many more paid workflows + live Q&A sessions with a top verified n8n creator?

Mcp Trigger, Sentry Io Tool
DevOps

Code Github. Uses manualTrigger, stickyNote, n8n, httpRequest. Event-driven trigger; 25 nodes.

n8n, HTTP Request, GitHub +1
DevOps

Code Github. Uses manualTrigger, stickyNote, httpRequest, noOp. Event-driven trigger; 24 nodes.

HTTP Request, GitHub, Execute Command +1