Skip to content

Commit 887f376

Browse files
committed
feat: implement PushCategoryCommand for pushing locale markdown files
- Added a new command `PushCategoryCommand` to push locale markdown files as separate OCI layers from a specified scroll directory. - Removed the deprecated `PushMetaCommand` and updated the `PushCommand` to include the new category command. - Enhanced the OCI client to support pushing categories with a regex pattern for file matching.
1 parent 4c579b1 commit 887f376

6 files changed

Lines changed: 108 additions & 75 deletions

File tree

cmd/registry_push.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ var PushCommand = &cobra.Command{
9494
}
9595

9696
func init() {
97-
PushCommand.AddCommand(PushMetaCommand)
97+
PushCommand.AddCommand(PushCategoryCommand)
9898

9999
PushCommand.Flags().StringVarP(&minRam, "min-ram", "r", minRam, "Minimum RAM required to run the application. (Will be added as a manifest annotation gg.druid.scroll.minRam)")
100100
PushCommand.Flags().StringVarP(&minCpu, "min-cpu", "c", minCpu, "Minimum CPU required to run the application. (Will be added as a manifest annotation gg.druid.scroll.minCpu)")

cmd/registry_push_category.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"path"
6+
7+
"github.com/highcard-dev/daemon/internal/core/services/registry"
8+
"github.com/highcard-dev/daemon/internal/utils/logger"
9+
"github.com/spf13/cobra"
10+
"go.uber.org/zap"
11+
)
12+
13+
var pushCategoryNamePattern string
14+
15+
// druid push category <repo> <category> <scrollDir>
16+
var PushCategoryCommand = &cobra.Command{
17+
Use: "category",
18+
Short: "Push locale markdown files (e.g. de-DE.md) from a scroll directory as separate OCI layers.",
19+
Args: cobra.RangeArgs(2, 3),
20+
RunE: func(cmd *cobra.Command, args []string) error {
21+
22+
credStore := LoadRegistryStore()
23+
if !credStore.HasCredentials() {
24+
return fmt.Errorf("no registry credentials configured. Please use `druid registry login` to set them")
25+
}
26+
27+
repo := args[0]
28+
category := args[1]
29+
scrollDir := "."
30+
if len(args) == 3 {
31+
scrollDir = args[2]
32+
}
33+
34+
fullPath := path.Join(cwd, scrollDir)
35+
36+
logger.Log().Info("Pushing "+repo+" category to registry", zap.String("scrollDir", fullPath))
37+
38+
ociClient := registry.NewOciClient(credStore)
39+
40+
_, err := ociClient.PushCategory(fullPath, repo, category, pushCategoryNamePattern)
41+
42+
if err != nil {
43+
return err
44+
}
45+
46+
logger.Log().Info("Pushed " + repo + " category to registry")
47+
return nil
48+
},
49+
}
50+
51+
func init() {
52+
PushCategoryCommand.Flags().StringVar(&pushCategoryNamePattern, "match", "", "Regexp matching file basenames to push (default: locale markdown like de-DE.md)")
53+
}

cmd/registry_push_meta.go

Lines changed: 0 additions & 45 deletions
This file was deleted.

internal/core/ports/services_ports.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ type OciRegistryInterface interface {
8585
PullSelective(dir string, artifact string, includeData bool, progress *domain.SnapshotProgress) error
8686
CanUpdateTag(descriptor v1.Descriptor, folder string, tag string) (bool, error)
8787
Push(folder string, repo string, tag string, overrides map[string]string, packMeta bool, scrollFile *domain.File) (v1.Descriptor, error)
88-
PushMeta(folder string, repo string) (v1.Descriptor, error)
8988
}
9089

9190
type CronManagerInterface interface {

internal/core/services/registry/oci.go

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/http"
99
"os"
1010
"path/filepath"
11+
"regexp"
1112
"strings"
1213
"sync/atomic"
1314
"time"
@@ -357,19 +358,58 @@ func (c *OciClient) packFolders(fs *file.Store, dirs []string, artifactType doma
357358
return fileDescriptors, nil
358359
}
359360

360-
func (c *OciClient) createMetaDescriptors(fs *file.Store, folder string, fsPath string) ([]v1.Descriptor, error) {
361+
// DefaultCategoryMarkdownPattern matches locale markdown basenames such as de-DE.md (used by PushCategory).
362+
const DefaultCategoryMarkdownPattern = `^[a-z]{2}-[A-Z]{2}\.md$`
363+
364+
// createMetaDescriptors packs children of folder/fsPath as meta layers. If namePattern is non-empty,
365+
// it must be a valid regexp; only non-empty regular files whose basename matches are included (directories are skipped).
366+
// If namePattern is empty, every immediate child is passed to packFolders (directories may become archives).
367+
func (c *OciClient) createMetaDescriptors(fs *file.Store, folder string, fsPath string, namePattern string) ([]v1.Descriptor, error) {
361368
metaPath := filepath.Join(folder, fsPath)
362369
exists, _ := utils.FileExists(metaPath)
363370
if !exists {
364-
return []v1.Descriptor{}, fmt.Errorf("meta file %s not found", metaPath)
371+
return nil, fmt.Errorf("meta file %s not found", metaPath)
365372
}
366-
fsFileNames := []string{}
367-
subitems, _ := os.ReadDir(metaPath)
368-
for _, subitem := range subitems {
369-
fsFileNames = append(fsFileNames, subitem.Name())
373+
374+
entries, err := os.ReadDir(metaPath)
375+
if err != nil {
376+
return nil, fmt.Errorf("read meta directory: %w", err)
377+
}
378+
379+
var fileRE *regexp.Regexp
380+
if namePattern != "" {
381+
fileRE, err = regexp.Compile(namePattern)
382+
if err != nil {
383+
return nil, fmt.Errorf("invalid name pattern: %w", err)
384+
}
385+
}
386+
387+
names := make([]string, 0, len(entries))
388+
for _, e := range entries {
389+
if fileRE != nil {
390+
if e.IsDir() || !fileRE.MatchString(e.Name()) {
391+
continue
392+
}
393+
fi, err := e.Info()
394+
if err != nil {
395+
return nil, fmt.Errorf("stat %s: %w", filepath.Join(metaPath, e.Name()), err)
396+
}
397+
if !fi.Mode().IsRegular() {
398+
continue
399+
}
400+
if fi.Size() == 0 {
401+
logger.Log().Warn("Skipping empty meta file", zap.String("path", filepath.Join(metaPath, e.Name())))
402+
continue
403+
}
404+
}
405+
names = append(names, e.Name())
370406
}
371407

372-
return c.packFolders(fs, fsFileNames, domain.ArtifactTypeScrollMeta, fsPath)
408+
if fileRE != nil && len(names) == 0 {
409+
return nil, fmt.Errorf("no files matching pattern %q in %s", namePattern, metaPath)
410+
}
411+
412+
return c.packFolders(fs, names, domain.ArtifactTypeScrollMeta, fsPath)
373413
}
374414

375415
// pushBlobWithRetry pushes a single blob from src to dst, retrying up to
@@ -425,7 +465,7 @@ func pushBlobWithRetry(ctx context.Context, src *file.Store, dst *remote.Reposit
425465
}
426466

427467
// copyToRegistry handles progress tracking, per-layer retries, and the
428-
// final manifest push via oras.Copy. It is shared by Push and PushMeta.
468+
// final manifest push via oras.Copy. It is shared by Push and PushCategory.
429469
func copyToRegistry(ctx context.Context, fs *file.Store, repo *remote.Repository, tag string, layers []v1.Descriptor, maxRetries int) error {
430470
var totalBytes atomic.Int64
431471
for _, desc := range layers {
@@ -547,7 +587,7 @@ func (c *OciClient) Push(folder string, repo string, tag string, overrides map[s
547587

548588
// Pack meta descriptors.
549589
if packMeta {
550-
metaDescriptors, err := c.createMetaDescriptors(fs, folder, ".meta")
590+
metaDescriptors, err := c.createMetaDescriptors(fs, folder, ".meta", "")
551591
if err != nil {
552592
return v1.Descriptor{}, err
553593
}
@@ -641,7 +681,8 @@ func (c *OciClient) Push(folder string, repo string, tag string, overrides map[s
641681
return rootManifestDescriptor, nil
642682
}
643683

644-
func (c *OciClient) PushMeta(scrollDir string, repo string) (v1.Descriptor, error) {
684+
func (c *OciClient) PushCategory(dir string, repo string, category string) (v1.Descriptor, error) {
685+
645686
ctx := context.Background()
646687

647688
// Authenticate before doing any expensive local work.
@@ -653,13 +694,13 @@ func (c *OciClient) PushMeta(scrollDir string, repo string) (v1.Descriptor, erro
653694
return v1.Descriptor{}, err
654695
}
655696

656-
fs, err := file.New(scrollDir)
697+
fs, err := file.New(dir)
657698
if err != nil {
658699
return v1.Descriptor{}, err
659700
}
660701
defer fs.Close()
661702

662-
manifestDescriptors, err := c.createMetaDescriptors(fs, scrollDir, ".meta")
703+
manifestDescriptors, err := c.createMetaDescriptors(fs, dir, "", DefaultCategoryMarkdownPattern)
663704
if err != nil {
664705
return v1.Descriptor{}, err
665706
}
@@ -675,7 +716,7 @@ func (c *OciClient) PushMeta(scrollDir string, repo string) (v1.Descriptor, erro
675716
zap.String("digest", rootManifestDescriptor.Digest.String()),
676717
)
677718

678-
tag := "meta"
719+
tag := "druid-category--" + category
679720
if err = fs.Tag(ctx, rootManifestDescriptor, tag); err != nil {
680721
return v1.Descriptor{}, err
681722
}

test/mock/services.go

Lines changed: 0 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)