Junie Help

Junie GitLab CI/CD

Run AI tasks from GitLab issues or MRs with Junie GitLab CI/CD.

How it works

Junie GitLab CI/CD integrates Junie into your GitLab CI/CD pipeline.

When you tag @junie in a comment to a GitLab issue or merge request, the agent is invoked to run the task. Junie runs a CI pipeline to execute the task, creates a new Merge Request with the changes, and posts a comment with a link to the created MR.

Junie gitlab issue comment

Why use Junie GitLab CI/CD?

  • Delegate tasks to Junie. Junie creates merge requests with code changes from issue comments.

  • Have Junie review MRs. Tag and instruct @junie in a comment to an existing merge request to have the agent review and fix the changes before merging.

  • Fix issues in projects without opening the IDE. When running the task, the agent is aware of your entire project structure, agent guidelines, and existing code patterns the same way as it would when working directly in the IDE.

  • Iterate on tasks with follow-up instructions. The MR submitted by Junie needs another iteration? Tag @junie in a comment to it and provide follow-up instructions.

  • Work on multiple tasks at a time. Junie GitLab CI/CD can work on multiple tasks simultaneously, excluding parallel runs on the same MR branch.

Setup

Docker installation

  1. Pull the Docker image:

    docker pull registry.jetbrains.team/p/matterhorn/public/junie-gitlab:latest
  2. Run the container with the required environment variables:

    docker run -d \ -e JUNIE_API_KEY=your_token_here \ -e GITLAB_HOST=gitlab_host \ -p 8080:8080 \ registry.jetbrains.team/p/matterhorn/public/junie-gitlab:latest

Variable

Description

Required

JUNIE_API_KEY

Your authentication token for the Junie. Get token here.

Yes

GITLAB_HOST

Your organization's GitLab host (e.g., https://gitlab.com)

Yes

GITLAB_IGNORE_CERTIFICATE_ERRORS

Set to true to ignore SSL certificate errors when connecting to GitLab. Default is false.

No

GITLAB_PIPELINE_CONFIGURATION_PATH

Path to the GitLab pipeline configuration file. Default is .gitlab-ci.yml.

No

GitLab repository setup

  1. Copy and add the .gitlab-ci.yml file to your project in GitLab.

workflow: rules: - if: $CI_PIPELINE_SOURCE == "api" variables: EJ_FOLDER_WORK: "/builds/workspace" # a directory with all files generated by Junie ENV_FILE: "/builds/workspace/env_vars.env" GIT_DEPTH: 1 before_script: - export GIT_REMOTE_URL="${CI_SERVER_PROTOCOL}://oauth2:${APP_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" - export DOT_MATTERHORN="$EJ_FOLDER_WORK/.matterhorn" - | if [ -n "$ISSUE_ID" ]; then export JUNIE_BRANCH="junie-issue-$ISSUE_ID-$CI_PIPELINE_ID" export JUNIE_COMMIT_MSG="[Junie] Automated changes for issue #$ISSUE_ID" else export SHORT_REF=$(echo $CI_COMMIT_REF_NAME | cut -c1-8) export JUNIE_BRANCH="junie-$SHORT_REF" export JUNIE_COMMIT_MSG="[Junie] Automated changes ($SHORT_REF)" fi - apt-get update - apt-get install -y git jq || true - mkdir -p $EJ_FOLDER_WORK - npm install -g @jetbrains/junie-cli - printenv > $ENV_FILE after_script: - | while IFS='=' read -r key value; do [[ -z "$key" ]] && continue if [[ "$key" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then if [ -z "${!key+x}" ]; then export "$key"="$value" || continue fi fi done < "$ENV_FILE" - rm $ENV_FILE - export JUNIE_SUMMARY="$(jq -r '.content.output' "$DOT_MATTERHORN"/issue_md/issue.issue)" || true - export JUNIE_SUMMARY_ESCAPED=$(printf "%s" "$JUNIE_SUMMARY" | jq -R -c . | sed 's/^"\(.*\)"$/\1/') || true - | EJ_RUNNER_INFO_DATA=$(echo "$EJ_RUNNER_INFO" \ | jq --arg JUNIE_BRANCH "$JUNIE_BRANCH" --arg JUNIE_SUMMARY_ESCAPED "$JUNIE_SUMMARY_ESCAPED" --arg CI_JOB_STATUS "$CI_JOB_STATUS" --arg CI_PIPELINE_URL "$CI_PIPELINE_URL" '.sourceBranch = $JUNIE_BRANCH | .junieResult = $JUNIE_SUMMARY_ESCAPED | .jobStatus = $CI_JOB_STATUS | .jobUrl = $CI_PIPELINE_URL') echo "EJ_RUNNER_INFO:" echo "$EJ_RUNNER_INFO" echo "EJ_RUNNER_INFO_DATA:" echo "$EJ_RUNNER_INFO_DATA" curl -v -L --request POST "${APP_API_URL}/runner/results" \ --header "X-Gitlab-Token: $APP_TOKEN" \ --header "Content-Type: application/json" \ --data "$EJ_RUNNER_INFO_DATA" - mkdir -p "$CI_PROJECT_DIR/logs" - cp -r "$DOT_MATTERHORN"/* "$CI_PROJECT_DIR/logs/" stages: - build run_junie: stage: build image: node:24-trixie script: - echo "===== [STAGE] Setup & Run Junie =====" - echo "template - $MR_DESCRIPTION" - junie --cache-dir="$EJ_FOLDER_WORK" --output-format="json" --auth $JUNIE_API_KEY "$EJ_TASK" - git status - echo "===== [STAGE] Save Junie Summary =====" - | if ! ls $DOT_MATTERHORN/issue_md.* 1> /dev/null 2>&1; then echo "No $DOT_MATTERHORN/issue_md.* file found!" >&2 ls -la $DOT_MATTERHORN exit 1 fi - echo "===== [STAGE] Commit and Push Changes =====" - | if git lfs env > /dev/null 2>&1; then chown -R $(whoami) .git || true chmod -R u+w .git/hooks || true git lfs update --force fi - git config --global user.name "Junie" - git config --global user.email "Junie@jetbrains.com" - git config --global --add safe.directory "$CI_PROJECT_DIR" - git checkout -b "$JUNIE_BRANCH" || git checkout "$JUNIE_BRANCH" - git status - git add -A :!$ENV_FILE ':!*.orig' ':!*.rej' ':!*.class' - | if ! git diff --cached --quiet; then git commit -m "$JUNIE_COMMIT_MSG" git remote set-url origin "$GIT_REMOTE_URL" git push --set-upstream origin "$JUNIE_BRANCH" else echo "No changes to commit." fi artifacts: name: "junie-logs" paths: - logs/** expire_in: 1 day when: always
  1. Issue an access token named junie in Project > Settings > Access token or User settings > Access tokens with:

    • Role: Owner.

    • Scope: api, read_api, read_repository, write_repository.

  2. Configure a webhook on the GitLab side. To do so:

    1. Go to Project > Settings > Webhooks > Add new webhook.

    2. Set a URL pointing on your local (using reverse proxy, e.g. ngrok) or remote junie-gitlab instance: https://HOST/api/public/gitlab/webhooks.

    3. Enter your token to the Secret token field.

    4. Enable at least the Comments trigger.

26 January 2026