Configure Custom Git Repositories

Feature Overview

artifacthub-shim can load additional Tekton catalog resources from Git repositories. Repositories are declared in labeled ConfigMaps. If a Git repository is private or uses an internal CA, its repository entry can reference a Secret in the same watched namespace.

The same Git repository entry can also declare extraResources directories. Those directories are synchronized to the cluster as ConfigMaps when config.extraResourceSync.enabled=true.

Each nested repository name becomes the external catalog name used by Artifact Hub API paths, UI paths, and Tekton hub resolver catalog parameters. Treat it as a stable identifier.

Prerequisites

  • artifacthub-shim is installed and ready.
  • config.enableConfigMapSources is true.
  • You can create ConfigMaps and Secrets in the namespace watched by artifacthub-shim. By default, this is the install namespace.
  • To register globally visible repositories from another namespace, config.globalConfigMapSources.enabled must be true. This is enabled by default. config.globalConfigMapSources.allowedNamespaces can restrict which namespaces may promote repositories to global visibility.
  • To register namespace, project, or explicit-allowlist repositories, config.scopedConfigMapSources.enabled must be true. This is enabled by default.
  • The Git repository follows the expected catalog layout.
  • Runtime ConfigMap resource sync is enabled when you want to import extraResources directories. By default, only the kube-public namespace is allowed.

Repository Layout

The path field points to one resource-kind directory inside the Git repository. Under that path, each package has version directories.

task/
  run-script/
    0.1/
      README.md
      run-script.yaml
pipeline/
  build-deploy/
    0.2/
      README.md
      build-deploy.yaml
stepaction/
  echo-message/
    0.1/
      README.md
      echo-message.yaml

For each version directory, artifacthub-shim first looks for <package>.yaml or <package>.yml. If neither exists, it falls back to the single Tekton YAML file whose Kubernetes kind matches the configured source kind.

Repository ConfigMap

Each repository ConfigMap must have the artifacthub-shim.alauda.io/repository: "true" label and a repository.yaml data key. The repository.yaml payload must contain a gitRepositories list. Each gitRepositories[] item declares one Git source. Each nested repositories[] item declares one externally visible catalog repository loaded from that Git source. If any item in a ConfigMap is invalid, artifacthub-shim rejects the whole ConfigMap.

ConfigMaps in the primary watched namespace, configured by config.namespace, keep the existing behavior and default to global visibility. ConfigMaps in any other namespace default to visibility from only that namespace. Set artifacthub-shim.alauda.io/repository-visibility to global, project, or namespace to choose an explicit scope. Project membership is derived only from the owner Namespace label cpaas.io/project.

The annotation artifacthub-shim.alauda.io/repository-visible-namespaces takes precedence over the visibility label. Its comma-separated value is an exact Namespace allowlist; entries are trimmed and deduplicated and may span projects. An empty value, an invalid Namespace name, or a list with no existing Namespace invalidates the whole ConfigMap scope and removes its previous content from the serving index. When only some Namespace entries do not exist, those entries are ignored and the remaining existing Namespaces continue to receive the repository.

apiVersion: v1
kind: ConfigMap
metadata:
  name: artifacthub-shim-team-a-catalogs
  namespace: artifacthub-shim-system
  labels:
    artifacthub-shim.alauda.io/repository: "true"
data:
  repository.yaml: |
    gitRepositories:
      - url: https://git.example.com/team-a/tekton-catalog.git
        revision: main
        repositories:
          - name: team-a-tasks
            displayName: Team A Tasks
            kind: task
            path: task
          - name: team-a-pipelines
            displayName: Team A Pipelines
            kind: pipeline
            path: pipeline
          - name: team-a-stepactions
            displayName: Team A StepActions
            kind: stepaction
            path: stepaction
        extraResources:
          - path: config

Cross-namespace Global Repository ConfigMap

Use this pattern when a plugin or administrator owns the repository ConfigMap in a namespace different from the artifacthub-shim watched namespace, but the catalog should still be globally visible. Global repository ConfigMap discovery is enabled by default and covers all namespaces. The chart grants artifacthub-shim cluster-scoped read access to ConfigMaps, referenced Secrets, and Events while this feature is enabled.

apiVersion: v1
kind: ConfigMap
metadata:
  name: team-b-global-catalogs
  namespace: team-b-system
  labels:
    artifacthub-shim.alauda.io/repository: "true"
    artifacthub-shim.alauda.io/repository-visibility: "global"
data:
  repository.yaml: |
    gitRepositories:
      - url: https://git.example.com/team-b/tekton-catalog.git
        revision: main
        credentialRef:
          name: team-b-git-credential
        repositories:
          - name: team-b-global-tasks
            displayName: Team B Global Tasks
            kind: task
            path: task

If credentialRef.name is set, the Secret is resolved in the same namespace as the repository ConfigMap. Secret changes in cross-namespace source namespaces are picked up on the next repository refresh; updating the repository ConfigMap also triggers an immediate refresh.

Namespace-scoped Repository ConfigMap

This is the default for repository ConfigMaps outside the primary watched namespace. Only workloads and namespace-aware UI requests from team-a can use the catalog.

metadata:
  namespace: team-a
  labels:
    artifacthub-shim.alauda.io/repository: "true"
    artifacthub-shim.alauda.io/repository-visibility: "namespace"

Project-scoped Repository ConfigMap

All Namespaces whose cpaas.io/project label equals the owner Namespace label can use the catalog. If the owner has no project label, the scope is invalid.

metadata:
  namespace: team-a
  labels:
    artifacthub-shim.alauda.io/repository: "true"
    artifacthub-shim.alauda.io/repository-visibility: "project"

Exact Namespace allowlist

The annotation overrides any global, project, or namespace label:

metadata:
  namespace: team-a
  labels:
    artifacthub-shim.alauda.io/repository: "true"
    artifacthub-shim.alauda.io/repository-visibility: "project"
  annotations:
    artifacthub-shim.alauda.io/repository-visible-namespaces: team-a,team-c

Scoped ConfigMaps must not declare extraResources. A credentialRef always resolves a Secret in the repository ConfigMap Namespace and cannot reference a Secret in another Namespace.

Namespace-aware UI APIs filter Task, Pipeline, and StepAction records before search, pagination, batch lookup, and content reads. Hub ResolutionRequest creation is also checked by the extension validating webhook. A denied request reports CATALOG_VISIBILITY_DENIED; unknown catalogs and internal policy errors are allowed through so the Hub resolver can return its native result.

Supported kind values:

KindTekton resource
taskTask
pipelinePipeline
stepactionStepAction

Supported top-level repository.yaml fields:

FieldRequiredDescription
gitRepositoriesYesList of Git sources to register from this ConfigMap.

Supported gitRepositories[] fields:

FieldRequiredDescription
urlYesGit repository URL.
revisionYesBranch, tag, or commit to check out.
credentialRef.nameNoSecret name containing Git credentials.
repositoriesNoCatalog repositories loaded from this Git source. Required unless extraResources is set.
extraResourcesNoConfigMap resource directories synchronized from this Git source. Required unless repositories is set.

Supported gitRepositories[].repositories[] fields:

FieldRequiredDescription
nameYesStable catalog name used by resolver and API paths. Use this value as the hub resolver catalog parameter.
displayNameNoHuman-readable catalog name.
kindYesOne of task, pipeline, or stepaction.
pathYesRelative catalog path inside the Git repository.
disabledPackagesNoRepository-local package or version disable rules.
optionalNoWhen true, a missing or empty path is published as Ready with zero packages. Defaults to false.
legacyCatalogAliasesNoHistorical catalog names that resolve to this repository for the same kind. API responses still return the canonical name.

Optional kind paths and legacy catalog aliases

Use optional: true only when a repository entry intentionally represents a kind directory that may not exist yet. This is mainly useful for migrated Tekton Hub catalogs where the old catalog had only task/, only pipeline/, or mixed kind coverage. Strict validation remains the default for hand-written repository entries.

legacyCatalogAliases preserves old resolver references during migration. The alias is scoped by kind: a Pipeline alias does not resolve Tasks with the same catalog name. A self alias is ignored. If an alias points to multiple repositories or conflicts with another canonical repository name for that kind, the shim disables only that alias and keeps every canonical repository ready.

Example migrated catalog:

gitRepositories:
  - url: https://git.example.com/team-a/tekton-hub-catalog.git
    revision: main
    repositories:
      - name: team-a
        displayName: Team A Tasks
        kind: task
        path: task
        optional: true
      - name: team-a-pipelines
        displayName: Team A Pipelines
        kind: pipeline
        path: pipeline
        optional: true
        legacyCatalogAliases:
          - team-a
      - name: team-a-stepactions
        displayName: Team A StepActions
        kind: stepaction
        path: stepaction
        optional: true

With this configuration, old Pipeline references that still use catalog: team-a resolve to the canonical team-a-pipelines repository. New references should use catalog: team-a-pipelines directly.

Supported gitRepositories[].extraResources[] fields:

FieldRequiredDescription
pathYesRelative resource directory path inside the Git repository. The directory is scanned recursively.

ConfigMap Resource Sync

extraResources currently imports only apiVersion: v1, kind: ConfigMap documents that opt in with metadata.labels["artifacthub-shim.alauda.io/import"] set to "true". Add this label to the ConfigMap YAML stored in the Git repository. Unlabeled ConfigMaps, non-ConfigMap resources, and unrelated files are skipped. Each imported ConfigMap must set metadata.name and metadata.namespace. The namespace must be listed in config.extraResourceSync.allowedNamespaces; the chart default is kube-public.

apiVersion: v1
kind: ConfigMap
metadata:
  name: team-a-tool-image
  namespace: kube-public
  labels:
    artifacthub-shim.alauda.io/import: "true"
data:
  image: registry.example.com/team-a/tool:v1

Resource paths must stay inside the Git repository checkout. Symbolic links are not followed during resource scans.

The resource sync controller automatically stamps synchronized in-cluster ConfigMaps with artifacthub-shim ownership labels such as artifacthub-shim.alauda.io/controller-namespace, artifacthub-shim.alauda.io/controller-name, and artifacthub-shim.alauda.io/resource-set. If a same-name ConfigMap already exists but does not have matching ownership labels, artifacthub-shim skips it and never adopts or overwrites it.

When two resource sources declare the same desired ConfigMap, the conflict is handled per ConfigMap object. Other ConfigMaps from the same sources continue to sync. If the built-in catalog declares one of the duplicates, the built-in catalog version wins; otherwise the duplicate ConfigMap is skipped until the source manifests are made unique.

When a synchronized ConfigMap disappears from the source directory, artifacthub-shim deletes it from the cluster. To keep a synchronized ConfigMap after it is removed from Git, add this annotation to the in-cluster ConfigMap:

metadata:
  annotations:
    artifacthub-shim.alauda.io/resource-policy: keep

Supported Credentials

The referenced Secret can contain one or more of these keys:

Secret keyUse
usernameHTTPS username.
passwordHTTPS password.
tokenHTTPS token. Used as the password when password is empty.
sshPrivateKeySSH private key.
ssh-privatekeyAlternative SSH private key key name.
known_hostsSSH known hosts content. Required when sshPrivateKey or ssh-privatekey is set.
ca.crtCA bundle for internal HTTPS Git servers.

Each Git repository can reference a different Secret.

Examples

Public HTTPS Repository

Use this pattern when the Git repository does not require authentication.

apiVersion: v1
kind: ConfigMap
metadata:
  name: artifacthub-shim-public-tasks
  namespace: artifacthub-shim-system
  labels:
    artifacthub-shim.alauda.io/repository: "true"
data:
  repository.yaml: |
    gitRepositories:
      - url: https://git.example.com/platform/public-tekton-catalog.git
        revision: main
        repositories:
          - name: public-tasks
            displayName: Public Tasks
            kind: task
            path: task

HTTPS Repository with Token

Use token for Git providers that accept personal access tokens or deploy tokens over HTTPS. When username is omitted, artifacthub-shim uses oauth2 as the username for the Git credential prompt.

apiVersion: v1
kind: Secret
metadata:
  name: team-a-token
  namespace: artifacthub-shim-system
type: Opaque
stringData:
  token: <git-token>
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: artifacthub-shim-team-a-tasks
  namespace: artifacthub-shim-system
  labels:
    artifacthub-shim.alauda.io/repository: "true"
data:
  repository.yaml: |
    gitRepositories:
      - url: https://git.example.com/team-a/tekton-catalog.git
        revision: main
        credentialRef:
          name: team-a-token
        repositories:
          - name: team-a-tasks
            displayName: Team A Tasks
            kind: task
            path: task

HTTPS Repository with Username and Password

Use this pattern when your Git server requires a username and password pair.

apiVersion: v1
kind: Secret
metadata:
  name: team-b-basic-auth
  namespace: artifacthub-shim-system
type: Opaque
stringData:
  username: <git-username>
  password: <git-password>
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: artifacthub-shim-team-b-pipelines
  namespace: artifacthub-shim-system
  labels:
    artifacthub-shim.alauda.io/repository: "true"
data:
  repository.yaml: |
    gitRepositories:
      - url: https://git.example.com/team-b/tekton-catalog.git
        revision: release-1.0
        credentialRef:
          name: team-b-basic-auth
        repositories:
          - name: team-b-pipelines
            displayName: Team B Pipelines
            kind: pipeline
            path: pipeline

SSH Repository with known_hosts

Use sshPrivateKey or ssh-privatekey for SSH authentication. The Secret must also provide known_hosts; host key verification is strict by default.

apiVersion: v1
kind: Secret
metadata:
  name: team-c-ssh
  namespace: artifacthub-shim-system
type: Opaque
stringData:
  sshPrivateKey: |
    -----BEGIN OPENSSH PRIVATE KEY-----
    <private-key>
    -----END OPENSSH PRIVATE KEY-----
  known_hosts: |
    git.example.com ssh-ed25519 <host-key>
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: artifacthub-shim-team-c-stepactions
  namespace: artifacthub-shim-system
  labels:
    artifacthub-shim.alauda.io/repository: "true"
data:
  repository.yaml: |
    gitRepositories:
      - url: ssh://git@git.example.com/team-c/tekton-catalog.git
        revision: main
        credentialRef:
          name: team-c-ssh
        repositories:
          - name: team-c-stepactions
            displayName: Team C StepActions
            kind: stepaction
            path: stepaction

HTTPS Repository with Internal CA

Use ca.crt when the Git server presents a certificate signed by an internal CA. You can combine ca.crt with token, username, or password in the same Secret.

apiVersion: v1
kind: Secret
metadata:
  name: internal-git-token
  namespace: artifacthub-shim-system
type: Opaque
stringData:
  token: <git-token>
  ca.crt: |
    -----BEGIN CERTIFICATE-----
    <ca-certificate>
    -----END CERTIFICATE-----
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: artifacthub-shim-internal-tasks
  namespace: artifacthub-shim-system
  labels:
    artifacthub-shim.alauda.io/repository: "true"
data:
  repository.yaml: |
    gitRepositories:
      - url: https://git.internal.example.com/devops/tekton-catalog.git
        revision: main
        credentialRef:
          name: internal-git-token
        repositories:
          - name: internal-tasks
            displayName: Internal Tasks
            kind: task
            path: task

Use with the Hub Resolver

The nested repositories[].name is the catalog value used by the hub resolver. For example, the team-a-tasks repository declared above is consumed with catalog: team-a-tasks:

apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: run-team-a-task
spec:
  taskRef:
    resolver: hub
    params:
      - name: type
        value: artifact
      - name: catalog
        value: team-a-tasks
      - name: kind
        value: task
      - name: name
        value: run-script
      - name: version
        value: "0.1.0"
  params:
    - name: script
      value: echo docs-verify

Verification

After applying the ConfigMap and optional Secret, inspect the Events on the repository ConfigMap:

kubectl describe configmap artifacthub-shim-team-a-catalogs \
  -n artifacthub-shim-system

Expected Events:

  • RepositoryConfigAccepted means repository.yaml was accepted.
  • RepositorySourceReady means the repository source was indexed and is now visible to the DevOps Hub UI and hub resolver clients.

You can also list recent repository ConfigMap Events directly:

kubectl get events -n artifacthub-shim-system \
  --field-selector involvedObject.kind=ConfigMap,involvedObject.name=artifacthub-shim-team-a-catalogs

If the repository does not become visible, check for RepositoryConfigInvalid, RepositorySourceInvalid, or RepositorySourceDegraded Events first. Use the artifacthub-shim pod logs as the secondary source for full Git or manifest parsing details.

For persistent Warning states, artifacthub-shim records the Warning again on each refresh or resource sync attempt. Kubernetes usually keeps this as one Event with an increasing count and updated last timestamp. If event TTL cleanup removes the previous Event, the next attempt can create it again.

Kubernetes Events are still historical and may keep older Warning entries until event TTL cleanup. Prefer the latest repository Event and /api/v1/snapshot source statuses when deciding whether the current configuration is healthy.

Learn More