ADD FUNC TO CHANGE VISIBILTY FOR GH PACKAGES
Maintainers usually reply within 1 day
@patrick-hermann-sva is already working on this.
Since Oct 27, 2025.
Assessment
This issue has not been assessed yet.
Description
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
// GitHub API base URL
const baseURL = "https://api.github.com"
func ghRequest(url, token string) (map[string]interface{}, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
}
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err == io.EOF {
return nil, nil
} else if err != nil {
return nil, err
}
return result, nil
}
func ghRequestList(url, token string) ([]map[string]interface{}, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
}
var result []map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return result, nil
}
func findInList(list []map[string]interface{}, name string) map[string]interface{} {
for _, pkg := range list {
if pkg["name"] == name {
return pkg
}
}
return nil
}
// 🔒 Function to change a package's visibility
func setPackageVisibility(org, pkgName, visibility, token string) error {
url := fmt.Sprintf("%s/orgs/%s/packages/container/%s/visibility", baseURL, org, pkgName)
body := map[string]string{"visibility": visibility}
jsonBody, _ := json.Marshal(body)
req, err := http.NewRequest("PATCH", url, bytes.NewReader(jsonBody))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
responseBody, _ := io.ReadAll(resp.Body)
if resp.StatusCode >= 400 {
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(responseBody))
}
fmt.Printf("✅ Updated visibility to %s (HTTP %d)\n", visibility, resp.StatusCode)
return nil
}
func main() {
org := "stuttgart-things"
pkgName := "flux-source"
token := os.Getenv("GITHUB_TOKEN")
if token == "" {
log.Fatal("❌ Missing GITHUB_TOKEN in environment")
}
listURL := fmt.Sprintf("%s/orgs/%s/packages?package_type=container", baseURL, org)
fmt.Printf("🔎 Listing from: %s\n", listURL)
list, err := ghRequestList(listURL, token)
if err != nil {
log.Printf("⚠️ Error listing packages: %v\n", err)
}
found := findInList(list, pkgName)
if found != nil {
fmt.Printf("✅ Found %s (visibility: %s)\n", found["name"], found["visibility"])
return
}
directURL := fmt.Sprintf("%s/orgs/%s/packages/container/%s", baseURL, org, pkgName)
fmt.Printf("🔍 Not found in list, trying direct lookup: %s\n", directURL)
direct, err := ghRequest(directURL, token)
if err != nil {
log.Fatalf("❌ Package not found: %v", err)
}
name := direct["name"]
visibility := direct["visibility"]
versionCount := direct["version_count"]
fmt.Printf("✅ Found package: %s\n", name)
fmt.Printf(" → Visibility: %s\n", visibility)
fmt.Printf(" → Versions: %.0f\n", versionCount)
// 🔄 Example: change visibility to public
newVisibility := "public" // or "private"
if visibility != newVisibility {
fmt.Printf("⚙️ Changing visibility from %s → %s...\n", visibility, newVisibility)
if err := setPackageVisibility(org, pkgName, newVisibility, token); err != nil {
log.Fatalf("❌ Failed to update visibility: %v", err)
}
} else {
fmt.Println("ℹ️ Package already has desired visibility.")
}
}
This is not a bug in your Go code. It’s a GitHub REST API limitation for org-owned GHCR packages:
For organization-owned container packages (flux-source under stuttgart-things), the API does not allow changing visibility at all.
That’s why the PATCH endpoint returns 404 Not Found, even though the package exists and your token is valid.
Only user-owned packages can currently have their visibility changed via API.
- Dominant language
- Go
- Stars
- 3
- Forks
- 0
- Avg merge
- 21h 53m
- Merged PRs (30d)
- 26
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from stuttgart-things/dagger
-
enhancement good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
stuttgart-things/dagger#311 ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 72/100
stuttgart-things/dagger#400 · 1 comment ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
stuttgart-things/dagger#386 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 48/100
stuttgart-things/dagger#385 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
stuttgart-things/dagger#262 · 3 comments ·
Maintainers usually reply within 1 day
All issues in stuttgart-things/dagger
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
blinklabs-io/dingo#5636 ·
Maintainers usually reply within 1 day
-
automated-analysis bug cookie safe-outputs tool-improvement workflow-issue
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
github/gh-aw#71952 · 3 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
JetBrains/youtrack-cli#130 ·
Maintainers usually reply within 1 day
-
Cloudflare Pages secret sync throws TypeError when deployment_configs[environment] is undefinedOpen
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Infisical/infisical#8892 · 1 comment ·
Maintainers usually reply within 1 day
-
windvane requests one more page than it needsPossibly taken @TassioSales claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
projectdiscovery/subfinder#1979 ·
Maintainers usually reply within 3 days