provision.ymlΒΆ

---
- name: "πŸ” List the accounts this instance knows"
  ansible.builtin.shell: | # nocheck: container-exec-resolver  runtime-resolved CID, delegated to the hosting node
    container exec -i --user {{ GITEA_USER }} {{ GITEA_API_LOCAL_CID | quote }} \
      /app/gitea/gitea admin user list -c {{ GITEA_CONFIG }}
  register: gitea_mcp_accounts
  changed_when: false
  delegate_to: "{{ GITEA_API_HOST_NODE }}"
  run_once: true

- name: "πŸ‘€ Create the account the MCP token belongs to"
  ansible.builtin.shell: | # nocheck: container-exec-resolver  runtime-resolved CID, delegated to the hosting node
    container exec -i -e GITEA_MCP_USER_PASSWORD --user {{ GITEA_USER }} {{ GITEA_API_LOCAL_CID | quote }} sh -lc '
      /app/gitea/gitea admin user create \
        --username "{{ GITEA_MCP_USERNAME }}" \
        --password "$GITEA_MCP_USER_PASSWORD" \
        --email "{{ GITEA_MCP_USER_EMAIL }}" \
        --must-change-password=false \
        -c {{ GITEA_CONFIG }}
    '
  environment:
    GITEA_MCP_USER_PASSWORD: "{{ GITEA_MCP_USER_PASSWORD }}"
  when: GITEA_MCP_USERNAME not in gitea_mcp_accounts.stdout.split()
  changed_when: true
  delegate_to: "{{ GITEA_API_HOST_NODE }}"
  run_once: true
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"

- name: "πŸ”‘ Set the account's password to the one this deployment declares"
  ansible.builtin.shell: | # nocheck: container-exec-resolver  runtime-resolved CID, delegated to the hosting node
    container exec -i -e GITEA_MCP_USER_PASSWORD --user {{ GITEA_USER }} {{ GITEA_API_LOCAL_CID | quote }} sh -lc '
      /app/gitea/gitea admin user change-password \
        --username "{{ GITEA_MCP_USERNAME }}" \
        --password "$GITEA_MCP_USER_PASSWORD" \
        --must-change-password=false \
        -c {{ GITEA_CONFIG }}
    '
  environment:
    GITEA_MCP_USER_PASSWORD: "{{ GITEA_MCP_USER_PASSWORD }}"
  changed_when: true
  delegate_to: "{{ GITEA_API_HOST_NODE }}"
  run_once: true
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"

- name: "πŸ—‘ Revoke an access token left over under the same name"
  ansible.builtin.shell: | # nocheck: container-exec-resolver  runtime-resolved CID, delegated to the hosting node
    container exec -i -e GITEA_MCP_BASIC_AUTH {{ GITEA_API_LOCAL_CID | quote }} sh -lc '
      {{ lookup('timeout', 30) | curl(5, 0) }} -o /dev/null -w "%{http_code}" \
        -X DELETE \
        -u "$GITEA_MCP_BASIC_AUTH" \
        "{{ GITEA_INTERNAL_API_URL ~ '/users/' ~ GITEA_MCP_USERNAME ~ '/tokens/' ~ GITEA_MCP_TOKEN_NAME }}"
    '
  environment:
    GITEA_MCP_BASIC_AUTH: "{{ GITEA_MCP_USERNAME ~ ':' ~ GITEA_MCP_USER_PASSWORD }}"
  register: gitea_mcp_token_revoke
  changed_when: gitea_mcp_token_revoke.stdout | trim == '204'
  failed_when: gitea_mcp_token_revoke.stdout | trim not in ['204', '404']
  delegate_to: "{{ GITEA_API_HOST_NODE }}"
  run_once: true
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"

- name: "πŸ”‘ Mint the personal access token MCP clients present"
  ansible.builtin.shell: | # nocheck: container-exec-resolver  runtime-resolved CID, delegated to the hosting node
    container exec -i --user {{ GITEA_USER }} {{ GITEA_API_LOCAL_CID | quote }} \
      /app/gitea/gitea admin user generate-access-token \
        --username {{ GITEA_MCP_USERNAME | quote }} \
        --token-name {{ GITEA_MCP_TOKEN_NAME | quote }} \
        --scopes {{ GITEA_MCP_TOKEN_SCOPES | quote }} \
        --raw \
        -c {{ GITEA_CONFIG }}
  register: gitea_mcp_mint
  changed_when: true
  delegate_to: "{{ GITEA_API_HOST_NODE }}"
  run_once: true
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"

- name: "πŸ”§ Take the minted token from the mint output"
  ansible.builtin.set_fact:
    gitea_mcp_token: "{{ gitea_mcp_mint.stdout_lines | last | trim }}"
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
  run_once: true

- name: "πŸ›‘ Refuse an instance that issued no usable token"
  ansible.builtin.assert:
    that:
      - gitea_mcp_token | length >= 40
    fail_msg: >-
      Gitea returned no access token, so every MCP client would be configured
      with an empty bearer and fail at first use.

- name: "πŸ’Ύ Persist the token so MCP clients can read it"
  ansible.builtin.include_role:
    name: sys-token-store
    tasks_from: write.yml
  vars:
    sys_token_store_user_key: "{{ lookup('config', application_id, 'mcp.credential.owner') }}"
    sys_token_store_app: "{{ application_id }}"
    sys_token_store_token: "{{ gitea_mcp_token }}"
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"