Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
e7e6d49
Revert "revert push command"
dominik-przybyl-wttech Apr 9, 2024
f9e017d
optimize regex
dominik-przybyl-wttech Apr 9, 2024
bc0d52f
refactor
dominik-przybyl-wttech May 6, 2024
99124e4
Merge branch 'main' into push-command
dominik-przybyl-wttech May 7, 2024
cc821ff
refactor
dominik-przybyl-wttech May 8, 2024
2dfe735
refactor
dominik-przybyl-wttech May 8, 2024
521caac
refactor
dominik-przybyl-wttech May 9, 2024
4adbc46
refactor
dominik-przybyl-wttech May 14, 2024
b38c131
refactor
dominik-przybyl-wttech May 14, 2024
424bcd2
minor
dominik-przybyl-wttech May 14, 2024
1637553
refactor
dominik-przybyl-wttech May 14, 2024
e2588b1
refactor
dominik-przybyl-wttech May 15, 2024
43b5612
refactor
dominik-przybyl-wttech May 17, 2024
7125182
minor
dominik-przybyl-wttech May 17, 2024
222a19e
added tests
dominik-przybyl-wttech May 18, 2024
c9d72a2
minor
dominik-przybyl-wttech May 19, 2024
c09427c
minor
dominik-przybyl-wttech May 19, 2024
1fc058f
refactor
dominik-przybyl-wttech May 19, 2024
605fbaf
refactor after CR
dominik-przybyl-wttech May 21, 2024
c7765d7
typo
dominik-przybyl-wttech May 21, 2024
99d89c1
fixed the large file issue (greater than 64KB)
dominik-przybyl-wttech May 22, 2024
6fbcf2f
Merge branch 'main' into push-command
dominik-przybyl-wttech Jun 9, 2024
03cc839
minor
dominik-przybyl-wttech Jun 10, 2024
98b4fcc
added vault cli
dominik-przybyl-wttech Jun 6, 2024
eff71ee
Merge branch 'main' into push-command
dominik-przybyl-wttech Aug 5, 2024
8a2ca96
updated go dependencies
dominik-przybyl-wttech Aug 5, 2024
087e4ce
Merge branch 'small_fixes' into push-command
dominik-przybyl-wttech Aug 5, 2024
b94d5c8
Merge branch 'main' into push-command
dominik-przybyl-wttech Aug 6, 2024
589f19b
added test
dominik-przybyl-wttech Aug 20, 2024
63d8f57
minor
dominik-przybyl-wttech Aug 21, 2024
5fddff0
fix .content.xml file push
dominik-przybyl-wttech Aug 21, 2024
b67d822
fix .content.xml file push
dominik-przybyl-wttech Aug 22, 2024
5cfb16f
minor
dominik-przybyl-wttech Aug 22, 2024
c1c0406
refactor
dominik-przybyl-wttech Aug 22, 2024
5cbc41f
refactor
dominik-przybyl-wttech Aug 22, 2024
a8f78f8
refactor
dominik-przybyl-wttech Aug 22, 2024
9b85802
remove JCRContentFile const
dominik-przybyl-wttech Aug 22, 2024
e4707f0
simplify pull file
dominik-przybyl-wttech Aug 22, 2024
e1399aa
simplify pull file
dominik-przybyl-wttech Aug 22, 2024
9fefb71
Merge branch 'main' into vault-cli
dominik-przybyl-wttech Aug 23, 2024
88a3fa4
Merge branch 'main' into push-command
dominik-przybyl-wttech Aug 23, 2024
6a62db3
simplify pull file
dominik-przybyl-wttech Aug 23, 2024
055ae03
simplify pull file
dominik-przybyl-wttech Aug 23, 2024
c72db67
Merge branch 'main' into vault-cli
dominik-przybyl-wttech Aug 23, 2024
baad3aa
minor
dominik-przybyl-wttech Aug 23, 2024
69cc4bd
Merge branch 'push-command' into vault-cli
dominik-przybyl-wttech Aug 24, 2024
90cf57d
Merge branch 'simplify-pull-file' into push-command
dominik-przybyl-wttech Aug 24, 2024
9e77008
Merge branch 'push-command' into vault-cli
dominik-przybyl-wttech Aug 24, 2024
7dde3e5
minor
dominik-przybyl-wttech Aug 24, 2024
23879c1
minor
dominik-przybyl-wttech Aug 24, 2024
24b3278
Merge branch 'push-command' into vault-cli
dominik-przybyl-wttech Aug 24, 2024
a3e4dbe
minor
dominik-przybyl-wttech Aug 24, 2024
8b5e818
refactor
dominik-przybyl-wttech Aug 24, 2024
282be53
added download content using Vault-Cli
dominik-przybyl-wttech Aug 25, 2024
468f4a6
refactor
Aug 28, 2024
1ff7fd5
fixed namespace cleanup when filename contains namespace (e.g. _cq_, …
Sep 19, 2024
638500d
Merge branch 'fix-namespace-clean' into vault-cli
Sep 20, 2024
b8c14c8
refactor
Sep 20, 2024
0d9a620
refactor
Sep 23, 2024
07ee47b
refactor
Sep 24, 2024
da5f6b7
refactor
Sep 25, 2024
0bc8338
fixed cleanNamespaces()
Sep 26, 2024
5f79a8f
Revert "refactor"
Sep 27, 2024
755f642
minor
Sep 27, 2024
bb044d6
refactor
Sep 28, 2024
c4cc797
added rcpArgs
Sep 28, 2024
93798fc
refactor, move ContentManager from Instance object to AEM object
Sep 29, 2024
86ea33e
refactor Vault-Cli
Sep 29, 2024
70e5c70
added handle for multiple instances for push, copy command
Sep 29, 2024
f6adf2c
removed Vault-Cli from content commands
Sep 30, 2024
8011042
refactor
Sep 30, 2024
4e918fe
Merge branch 'main' into vault-cli
Oct 1, 2024
9c3e7c1
minor
Oct 1, 2024
5abd048
refactor
Oct 1, 2024
99b93f7
added integration tests
Oct 4, 2024
b15e06e
fixed int tests
Oct 8, 2024
2bcc49c
fixed int tests
Oct 8, 2024
73262cd
minor
Oct 9, 2024
7b49e97
restructured
Oct 9, 2024
d843a8e
docs
Oct 10, 2024
a0faffb
added update property to push command
Nov 12, 2024
dcaac90
renamed excludePatterns into filterRootExcludes
Nov 12, 2024
3c5d6b4
Minor
krystian-panek-vmltech Nov 13, 2024
8a7337d
Merge branch 'vault-cli' of github.com:wttech/aemc into vault-cli
krystian-panek-vmltech Nov 13, 2024
47bb19e
Minor
krystian-panek-vmltech Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,9 @@ java:
"arm64": "x64"
"aarch64": "x64"

vault:
"download_url": "https://repo1.maven.org/maven2/org/apache/jackrabbit/vault/vault-cli/3.7.2/vault-cli-3.7.2-bin.tar.gz"

base:
# Location of temporary files (downloaded AEM packages, etc)
tmp_dir: aem/home/tmp
Expand All @@ -490,6 +493,52 @@ output:
file: aem/home/var/log/aem.log
# Controls where outputs and logs should be written to when format is 'text' (console|file|both)
mode: console

# Content clean options
content:
clean:
# File patterns to be deleted
files_deleted:
- patterns:
- "**/.vlt"
- "**/.vlt*.tmp"
- "**/install/*.jar"
# File patterns to be flattened
files_flattened:
- "**/_cq_design_dialog/.content.xml"
- "**/_cq_dialog/.content.xml"
- "**/_cq_htmlTag/.content.xml"
- "**/_cq_template/.content.xml"
# Property patterns to be skipped, removed from cleaned file
properties_skipped:
- patterns: "jcr:uuid"
excluded_paths: [ "**/home/users/*", "**/home/groups/*" ]
- patterns: "cq:lastModified*"
excluded_paths: [ "**/content/experience-fragments/*" ]
- patterns: [ "dam:sha1", "dam:size" ]
included_paths: [ "**/content/dam/*.svg/*" ]
- patterns:
- "jcr:lastModified*"
- "jcr:created*"
- "jcr:isCheckedOut"
- "cq:lastReplicat*"
- "cq:lastRolledout*"
- "dam:extracted"
- "dam:assetState"
- "dc:modified"
- "*_x0040_*"
- "cq:name"
- "cq:parentPath"
- "dam:copiedAt"
- "dam:parentAssetID"
- "dam:relativePath"
# Mixin type patterns to be skipped, removed from cleaned file
mixin_types_skipped:
- patterns:
- "cq:ReplicationStatus"
- "mix:versionable"
# Unused namespaces to be skipped, removed from cleaned file
namespaces_skipped: true
```

Note that environment variables may be injected in any part of config file.
Expand Down
207 changes: 171 additions & 36 deletions cmd/aem/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import (
"github.com/spf13/cobra"
"github.com/wttech/aemc/pkg"
"github.com/wttech/aemc/pkg/common/pathx"
"github.com/wttech/aemc/pkg/common/timex"
"github.com/wttech/aemc/pkg/content"
"os"
"path/filepath"
"strings"
)

Expand All @@ -17,6 +20,7 @@ func (c *CLI) contentCmd() *cobra.Command {
}
cmd.AddCommand(c.contentCleanCmd())
cmd.AddCommand(c.contentPullCmd())
cmd.AddCommand(c.contentPushCmd())
cmd.AddCommand(c.contentDownloadCmd())
cmd.AddCommand(c.contentCopyCmd())
return cmd
Expand All @@ -38,17 +42,17 @@ func (c *CLI) contentCleanCmd() *cobra.Command {
c.Error(err)
return
}
path := dir
if path == "" {
path = file
}
if err = c.aem.ContentManager().Clean(path); err != nil {
c.Error(err)
return
}
if dir != "" {
if err = c.aem.ContentManager().CleanDir(dir); err != nil {
c.Error(err)
return
}
c.SetOutput("dir", dir)
} else if file != "" {
if err = c.aem.ContentManager().CleanFile(file); err != nil {
c.Error(err)
return
}
c.SetOutput("file", file)
}
c.Changed("content cleaned")
Expand All @@ -73,10 +77,14 @@ func (c *CLI) contentDownloadCmd() *cobra.Command {
return
}
pid, _ := cmd.Flags().GetString("pid")
if pid == "" {
pid = fmt.Sprintf("aemc:content-download:%s-SNAPSHOT", timex.FileTimestampForNow())
}
targetFile, _ := cmd.Flags().GetString("target-file")
filterRoots, _ := cmd.Flags().GetStringSlice("filter-roots")
filterRoots := determineFilterRoots(cmd)
filterFile, _ := cmd.Flags().GetString("filter-file")
if err = instance.ContentManager().Download(targetFile, pkg.PackageCreateOpts{
clean, _ := cmd.Flags().GetBool("clean")
if err = c.aem.ContentManager().Download(instance, targetFile, clean, pkg.PackageCreateOpts{
PID: pid,
FilterRoots: filterRoots,
FilterFile: filterFile,
Expand All @@ -94,6 +102,7 @@ func (c *CLI) contentDownloadCmd() *cobra.Command {
cmd.Flags().StringSliceP("filter-roots", "r", []string{}, "Vault filter root paths")
cmd.Flags().StringP("filter-file", "f", "", "Vault filter file path")
cmd.MarkFlagsOneRequired("filter-roots", "filter-file")
cmd.Flags().Bool("clean", false, "Normalize content after downloading")
return cmd
}

Expand All @@ -108,8 +117,6 @@ func (c *CLI) contentPullCmd() *cobra.Command {
c.Error(err)
return
}
clean, _ := cmd.Flags().GetBool("clean")
replace, _ := cmd.Flags().GetBool("replace")
dir, err := determineContentDir(cmd)
if err != nil {
c.Error(err)
Expand All @@ -120,21 +127,26 @@ func (c *CLI) contentPullCmd() *cobra.Command {
c.Error(err)
return
}
filterRoots := determineFilterRoots(cmd)
filterFile, _ := cmd.Flags().GetString("filter-file")
filterRootExcludes := determineFilterRootExcludes(cmd)
clean, _ := cmd.Flags().GetBool("clean")
replace, _ := cmd.Flags().GetBool("replace")
if dir != "" {
filterRoots, _ := cmd.Flags().GetStringSlice("filter-roots")
filterFile, _ := cmd.Flags().GetString("filter-file")
if err = instance.ContentManager().PullDir(dir, clean, replace, pkg.PackageCreateOpts{
if err = c.aem.ContentManager().PullDir(instance, dir, clean, replace, pkg.PackageCreateOpts{
PID: fmt.Sprintf("aemc:content-pull:%s-SNAPSHOT", timex.FileTimestampForNow()),
FilterRoots: filterRoots,
FilterFile: filterFile,
ContentDir: dir,
}); err != nil {
c.Error(err)
return
}
c.SetOutput("dir", dir)
} else if file != "" {
if err = instance.ContentManager().PullFile(file, clean, pkg.PackageCreateOpts{
ContentFile: file,
if err = c.aem.ContentManager().PullFile(instance, file, clean, replace, pkg.PackageCreateOpts{
PID: fmt.Sprintf("aemc:content-pull:%s-SNAPSHOT", timex.FileTimestampForNow()),
FilterRoots: filterRoots,
FilterRootExcludes: filterRootExcludes,
}); err != nil {
c.Error(err)
return
Expand All @@ -156,6 +168,66 @@ func (c *CLI) contentPullCmd() *cobra.Command {
return cmd
}

func (c *CLI) contentPushCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "push",
Aliases: []string{"ps"},
Short: "Push content from JCR root directory or local file to running instance",
Run: func(cmd *cobra.Command, args []string) {
instances, err := c.aem.InstanceManager().Some()
if err != nil {
c.Error(err)
return
}
dir, err := determineContentDir(cmd)
if err != nil {
c.Error(err)
return
}
file, err := determineContentFile(cmd)
if err != nil {
c.Error(err)
return
}
path := dir
if path == "" {
path = file
}
if !pathx.Exists(path) {
c.Error(fmt.Errorf("cannot push content as it does not exist '%s'", path))
return
}
filterRoots := determineFilterRoots(cmd)
filterRootExcludes := determineFilterRootExcludes(cmd)
clean, _ := cmd.Flags().GetBool("clean")
filterMode := determineFilterMode(cmd)
if err = c.aem.ContentManager().Push(instances, clean, pkg.PackageCreateOpts{
PID: fmt.Sprintf("aemc:content-push:%s-SNAPSHOT", timex.FileTimestampForNow()),
FilterRoots: filterRoots,
FilterRootExcludes: filterRootExcludes,
ContentPath: path,
FilterMode: filterMode,
}); err != nil {
c.Error(err)
return
}
if dir != "" {
c.SetOutput("dir", dir)
} else if file != "" {
c.SetOutput("file", file)
}
c.Changed("content pushed")
},
}
cmd.Flags().StringP("dir", "d", "", "JCR root path")
cmd.Flags().StringP("file", "f", "", "Local file path")
cmd.Flags().StringP("path", "p", "", "JCR root path or local file path")
cmd.MarkFlagsOneRequired("dir", "file", "path")
cmd.Flags().Bool("clean", false, "Normalize content while uploading")
cmd.Flags().Bool("update", false, "Existing content on running instance is updated, new content is added and none is deleted")
return cmd
}

func (c *CLI) contentCopyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "copy",
Expand All @@ -167,15 +239,16 @@ func (c *CLI) contentCopyCmd() *cobra.Command {
c.Error(err)
return
}
targetInstance, err := determineContentTargetInstance(cmd, c.aem.InstanceManager())
targetInstances, err := determineContentTargetInstances(cmd, c.aem.InstanceManager())
if err != nil {
c.Error(err)
return
}
filterRoots, _ := cmd.Flags().GetStringSlice("filter-roots")
filterRoots := determineFilterRoots(cmd)
filterFile, _ := cmd.Flags().GetString("filter-file")
clean, _ := cmd.Flags().GetBool("clean")
if err = instance.ContentManager().Copy(targetInstance, clean, pkg.PackageCreateOpts{
if err = c.aem.ContentManager().Copy(instance, targetInstances, clean, pkg.PackageCreateOpts{
PID: fmt.Sprintf("aemc:content-copy:%s-SNAPSHOT", timex.FileTimestampForNow()),
FilterRoots: filterRoots,
FilterFile: filterFile,
}); err != nil {
Expand All @@ -185,8 +258,8 @@ func (c *CLI) contentCopyCmd() *cobra.Command {
c.Changed("content copied")
},
}
cmd.Flags().StringP("instance-target-url", "u", "", "Destination instance URL")
cmd.Flags().StringP("instance-target-id", "i", "", "Destination instance ID")
cmd.Flags().StringSliceP("instance-target-url", "u", []string{}, "Destination instance URL")
cmd.Flags().StringSliceP("instance-target-id", "i", []string{}, "Destination instance ID")
cmd.MarkFlagsOneRequired("instance-target-url", "instance-target-id")
cmd.Flags().StringSliceP("filter-roots", "r", []string{}, "Vault filter root paths")
cmd.Flags().StringP("filter-file", "f", "", "Vault filter file path")
Expand All @@ -195,33 +268,41 @@ func (c *CLI) contentCopyCmd() *cobra.Command {
return cmd
}

func determineContentTargetInstance(cmd *cobra.Command, instanceManager *pkg.InstanceManager) (*pkg.Instance, error) {
var instance *pkg.Instance
url, _ := cmd.Flags().GetString("instance-target-url")
if url != "" {
instance, _ = instanceManager.NewByIDAndURL("remote_adhoc_target", url)
func determineContentTargetInstances(cmd *cobra.Command, instanceManager *pkg.InstanceManager) ([]pkg.Instance, error) {
var instances []pkg.Instance
urls, _ := cmd.Flags().GetStringSlice("instance-target-url")
for _, url := range urls {
instance, err := instanceManager.NewByIDAndURL("remote_adhoc_target", url)
if err != nil {
return nil, err
}
instances = append(instances, *instance)
}
id, _ := cmd.Flags().GetString("instance-target-id")
if id != "" {
instance = instanceManager.NewByID(id)
ids, _ := cmd.Flags().GetStringSlice("instance-target-id")
for _, id := range ids {
instance := instanceManager.NewByID(id)
instances = append(instances, *instance)
}
if instance == nil {
if instances == nil {
return nil, fmt.Errorf("missing 'instance-target-url' or 'instance-target-id'")
}
return instance, nil
return instances, nil
}

func determineContentDir(cmd *cobra.Command) (string, error) {
dir, _ := cmd.Flags().GetString("dir")
if dir != "" && !strings.Contains(dir, content.JCRRoot) {
return "", fmt.Errorf("content dir '%s' does not contain '%s'", dir, content.JCRRoot)
return "", fmt.Errorf("content directory '%s' does not contain '%s'", dir, content.JCRRoot)
}
if pathx.IsFile(dir) {
return "", fmt.Errorf("content directory '%s' is not a directory; consider using 'file' parameter", dir)
}
path, _ := cmd.Flags().GetString("path")
if path != "" && !strings.Contains(path, content.JCRRoot) {
return "", fmt.Errorf("content path '%s' does not contain '%s'", path, content.JCRRoot)
}
if path != "" && !pathx.Exists(path) {
return "", fmt.Errorf("content path does not exist: %s", path)
return "", fmt.Errorf("content path '%s' need to exist on file system; consider using 'dir' or 'file' parameter otherwise", path)
}
if path != "" && pathx.IsDir(path) {
return path, nil
Expand All @@ -234,15 +315,69 @@ func determineContentFile(cmd *cobra.Command) (string, error) {
if file != "" && !strings.Contains(file, content.JCRRoot) {
return "", fmt.Errorf("content file '%s' does not contain '%s'", file, content.JCRRoot)
}
if pathx.IsDir(file) {
return "", fmt.Errorf("content file '%s' is not a file; consider using 'dir' parameter", file)
}
path, _ := cmd.Flags().GetString("path")
if path != "" && !strings.Contains(path, content.JCRRoot) {
return "", fmt.Errorf("content path '%s' does not contain '%s'", path, content.JCRRoot)
}
if path != "" && !pathx.Exists(path) {
return "", fmt.Errorf("content path does not exist: %s", path)
return "", fmt.Errorf("content path '%s' need to exist on file system; consider using 'dir' or 'file' parameter otherwise", path)
}
if path != "" && pathx.IsFile(path) {
return path, nil
}
return file, nil
}

func determineFilterRoots(cmd *cobra.Command) []string {
filterRoots, _ := cmd.Flags().GetStringSlice("filter-roots")
if len(filterRoots) > 0 {
return filterRoots
}
filterFile, _ := cmd.Flags().GetString("filter-file")
if filterFile != "" {
return nil
}
dir, _ := determineContentDir(cmd)
if dir != "" {
return []string{pkg.DetermineFilterRoot(dir)}
}
file, _ := determineContentFile(cmd)
if file != "" {
return []string{pkg.DetermineFilterRoot(file)}
}
return nil
}

func determineFilterRootExcludes(cmd *cobra.Command) []string {
file, _ := determineContentFile(cmd)
if file == "" || !strings.HasSuffix(file, content.JCRContentFile) || content.IsPageContentFile(file) {
return nil
}

dir := filepath.Dir(file)
entries, err := os.ReadDir(dir)
if err != nil {
return nil
}

var filterRootExcludes []string
for _, entry := range entries {
if entry.Name() != content.JCRContentFile {
jcrPath := pkg.DetermineFilterRoot(filepath.Join(dir, entry.Name()))
excludePattern := fmt.Sprintf("%s(/.*)?", jcrPath)
filterRootExcludes = append(filterRootExcludes, excludePattern)
}
}
return filterRootExcludes
}

func determineFilterMode(cmd *cobra.Command) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dominik-przybyl-wttech do not map; pass as is

update, _ := cmd.Flags().GetBool("update")
if update {
return "update"
}
return ""
}
Loading
Loading