Claude
Skills
Sign in
Back

fix-docker-image-org-mismatch

Included with Lifetime
$97 forever

Fix Docker GHCR push failures when repository organization changes but IMAGE\_NAME remains hardcoded to old org

ci-cddockerghcrgithub-actionsauthenticationorganization-migration

What this skill does


# Fix Docker Image Organization Mismatch

## Overview

| Field | Value |
| --- | --- |
| **Date** | 2026-02-09 |
| **Objective** | Fix Docker Build workflow failure after repository transfer to new GitHub organization |
| **Root Cause** | IMAGE\_NAME hardcoded to old org while GITHUB\_TOKEN scoped to new org |
| **Outcome** | ✅ All Docker images successfully pushed to GHCR under correct organization |
| **Issue** | PR #3123 - Docker CI failure on HomericIntelligence/ProjectOdyssey |

## When to Use This Skill

**Primary Trigger**: Docker workflow fails with GHCR push errors after repository org change

**Specific Indicators**:

1. Error message: `could not parse reference: ghcr.io/{OldOrg}/{repo}:tag`
2. Error message: `unable to parse registry reference`
3. Docker build succeeds but push to GHCR fails with authentication error
4. Repository was recently transferred to a new GitHub organization
5. GITHUB\_TOKEN scope shows different org than IMAGE\_NAME in workflow

**Example Error**:

```bash
[0000] ERROR could not determine source: errors occurred attempting to resolve 'ghcr.io/mvillmow/ProjectOdyssey:main':
  - docker: could not parse reference: ghcr.io/mvillmow/ProjectOdyssey:main
  - oci-registry: unable to parse registry reference="ghcr.io/mvillmow/ProjectOdyssey:main"
```

## Problem Background

### Why This Happens

When a repository is transferred to a new GitHub organization:

1. GitHub workflows automatically get a `GITHUB\_TOKEN` scoped to the **new org**
2. Hardcoded `IMAGE\_NAME` variables still reference the **old org**
3. Docker tries to push to `ghcr.io/old-org/repo` using a token for `new-org`
4. GHCR rejects the push due to permission mismatch

### Key Constraint

**Docker image names MUST be lowercase**, but `github.repository` preserves the
original repository name case (e.g., `HomericIntelligence/ProjectOdyssey`). This
causes failures in tools that manually construct Docker image references.

## Verified Workflow

### 1. Identify All Hardcoded Image Names

Search for hardcoded org references in Docker-related files:

```bash
# Find IMAGE\_NAME in workflows
grep -r "IMAGE\_NAME" .github/workflows/

# Find GHCR references in workflows
grep -r "ghcr.io" .github/workflows/

# Find in Justfile/Makefile
grep -r "REPO\_NAME\|IMAGE\_NAME" justfile Makefile 2>/dev/null
```

**Files to check**:

- `.github/workflows/docker.yml`
- `.github/workflows/release.yml`
- `justfile` or `Makefile`
- Any Docker Compose files

### 2. Update Workflow IMAGE\_NAME

**Fix Pattern**: Replace hardcoded org with lowercase new org name

```yaml
# .github/workflows/docker.yml
env:
  REGISTRY: ghcr.io
  # BEFORE (BROKEN):
  IMAGE\_NAME: oldorg/projectname

  # AFTER (FIXED):
  IMAGE\_NAME: neworg/projectname  # lowercase org name
```

**Critical**: Image name must be **all lowercase** for Docker/GHCR compatibility.

### 3. Update Release Workflow

If you have a separate release workflow, add `IMAGE\_NAME` env var at job level:

```yaml
# .github/workflows/release.yml
jobs:
  publish-docker:
    runs-on: ubuntu-latest
    env:
      # Add this env var
      IMAGE\_NAME: neworg/projectname  # lowercase

    steps:
      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          tags: |
            ghcr.io/${{ env.IMAGE\_NAME }}:${{ needs.validate-version.outputs.version }}
            ghcr.io/${{ env.IMAGE\_NAME }}:latest
```

**Replace**:

- `ghcr.io/${{ github.repository }}` → `ghcr.io/${{ env.IMAGE\_NAME }}`

### 4. Update Build System References

If using Justfile or Makefile:

```justfile
# justfile
REGISTRY := "ghcr.io"
# BEFORE:
REPO\_NAME := "oldorg/oldreponame"

# AFTER:
REPO\_NAME := "neworg/projectname"  # lowercase
```

### 5. Run Pre-commit and Verify

```bash
# Run all linting
just pre-commit-all

# Check for remaining old org references
grep -r "oldorg/projectname\|oldorg/oldreponame" \
  --include="*.md" --include="*.yml" --include="*.toml" .

# Verify no hardcoded paths remain
grep -r "/home/olduser" --include="*.md" --include="*.py" --include="*.sh" .
```

### 6. Commit and Create PR

```bash
# Commit changes
git add .github/workflows/docker.yml \
        .github/workflows/release.yml \
        justfile

git commit -m "fix(ci): update IMAGE\_NAME for new organization

- Update IMAGE\_NAME from oldorg to neworg
- Fix GHCR push authentication failures
- Ensure lowercase image names for Docker compatibility

Fixes Docker Build workflow where GITHUB\_TOKEN scoped to
new org could not push to old org's GHCR.
"

# Create PR
gh pr create \
  --title "fix(ci): update Docker image org references" \
  --body "Fixes GHCR push failures after org transfer" \
  --label "ci-cd"
```

### 7. Verify Docker Build Success

After PR is merged:

```bash
# Check Docker workflow status
gh run list --workflow="Docker Build and Publish" --branch main --limit 1

# View details
gh run view <run-id>

# Verify images pushed correctly
gh run view <run-id> --log | grep "pushing manifest"
```

## Failed Attempts

| Attempt | Why It Failed | Lesson Learned |
| --- | --- | --- |
| Used `${{ github.repository }}` directly in tags | Preserves case (e.g., `OrgName/Repo`), Docker requires lowercase | Always use explicit lowercase IMAGE\_NAME env var |
| Only updated docker.yml, missed release.yml | Release workflow still used old org, failed on releases | Search all workflows, not just primary Docker workflow |
| Forgot to update Justfile REPO\_NAME | Local `just docker-build` commands failed | Check all build system files (Just, Make, scripts) |
| Tried `${{ github.repository_owner }}` | Still case-sensitive, doesn't include repo name | Hardcode full `org/repo` in lowercase |
| Re-ran failed jobs immediately | Flaky Mojo tests caused false failures | Wait for full workflow completion before judging success |

## Results & Parameters

### Successful Configuration

**docker.yml**:

```yaml
env:
  REGISTRY: ghcr.io
  IMAGE\_NAME: homericintelligence/projectodyssey  # lowercase
```

**release.yml**:

```yaml
jobs:
  publish-docker:
    env:
      IMAGE\_NAME: homericintelligence/projectodyssey  # lowercase
    steps:
      - name: Build and push
        with:
          tags: |
            ghcr.io/${{ env.IMAGE\_NAME }}:${{ version }}
            ghcr.io/${{ env.IMAGE\_NAME }}:latest
```

**justfile**:

```justfile
REGISTRY := "ghcr.io"
REPO\_NAME := "homericintelligence/projectodyssey"
```

### Verification Results

✅ **All Docker jobs passed**:

- build-and-push (production): 4m34s ✅
- build-and-push (runtime): 6m18s ✅
- build-and-push (ci): 3m41s ✅
- security-scan: 1m18s ✅
- test-images: 1m47s ✅

✅ **Images successfully pushed**:

- `ghcr.io/homericintelligence/projectodyssey:main`
- `ghcr.io/homericintelligence/projectodyssey:main-ci`
- `ghcr.io/homericintelligence/projectodyssey:main-prod`
- Plus SHA-tagged variants

### Performance Impact

- No performance impact - purely configuration fix
- Build times remain the same
- Authentication now works correctly with GITHUB\_TOKEN

## Additional Context

### Common Pitfall: Case Sensitivity

Docker image names **must be lowercase**, but GitHub variables preserve case:

- `github.repository` → `HomericIntelligence/ProjectOdyssey` (mixed case) ❌
- `IMAGE\_NAME` → `homericintelligence/projectodyssey` (lowercase) ✅

### Related Issues

This fix also addresses:

1. SBOM generation failures (anchore/sbom-action uses image name)
2. Security scanning failures (Trivy uses image name)
3. Image testing failures (test-images job needs correct reference)

### Scope of Changes

Typical files to update:

- `.github/workflows/docker.yml` (primary)
- `.github/workflows/release.yml` (if exists)
- `justfile` or `Makefile` (local builds)
- Documentation references to old GHCR URLs

## Cross-Repository Applicability

This skill applies to **any repository** that:

1. Uses GitHub Actions to build Docker images
2. Pushes to GitHub Container Registry (GHCR)
3. Was transferred to a new organization
4. Has hardcoded IMAGE\_NAME or

Related in ci-cd