diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8bcc9fc5..c087b18d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,4 +38,4 @@ jobs: uses: actions/checkout@v4 - name: Run build - run: make + run: sh taskw cli:all diff --git a/.gitignore b/.gitignore index 1ccd8037..174d8d2c 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,11 @@ *.so *.dylib +# Package Files # +*.jar +*.war +*.ear + # Test binary, built with `go test -c` *.test @@ -235,4 +240,10 @@ dist/ !*.code-workspace # Built Visual Studio Code Extensions -*.vsix \ No newline at end of file +*.vsix + +# AEM Compose +aem/home/ +.task/ +.env +.env.* diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100755 index 00000000..346d645f --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/Makefile b/Makefile deleted file mode 100644 index dbf96e97..00000000 --- a/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -.GIT_COMMIT=$(shell git rev-parse HEAD) -.GIT_COMMIT_DATE=$(shell git log -1 --date=format:'%Y-%m-%dT%H:%M:%S' --format=%cd) -.GIT_VERSION=$(shell git describe --tags 2>/dev/null || echo "$(.GIT_COMMIT)") -.LD_FLAGS=$(shell echo "-s -w -X main.appVersion=${.GIT_VERSION} -X main.appCommit=${.GIT_COMMIT} -X main.appCommitDate=${.GIT_COMMIT_DATE}") -.TAGS=timetzdata - -all: deps test vet fmt lint install - -deps: - go install github.com/mgechev/revive@v1.3.3 - go mod tidy - -test: - go test ./... - -int_test: - go test -tags=int_test ./... - -vet: - go vet ./... - -fmt: - gofmt -l -w . - -lint: - revive -config revive.toml -formatter friendly ./... - -build: - go build -tags "${.TAGS}" --ldflags "${.LD_FLAGS}" -o bin/aem ./cmd/aem - -install: - go install -tags "${.TAGS}" --ldflags "${.LD_FLAGS}" ./cmd/aem - -other_build: - GOARCH=amd64 GOOS=darwin go build -tags "${.TAGS}" --ldflags "${.LD_FLAGS}" -o bin/aem.darwin ./cmd/aem - GOARCH=amd64 GOOS=linux go build -tags "${.TAGS}" --ldflags "${.LD_FLAGS}" -o bin/aem.linux ./cmd/aem - GOARCH=amd64 GOOS=windows go build -tags "${.TAGS}" --ldflags "${.LD_FLAGS}" -o bin/aem.exe ./cmd/aem - -clean: - go clean - rm -fr bin diff --git a/Taskfile.aem.yml b/Taskfile.aem.yml new file mode 100755 index 00000000..a3e6d00b --- /dev/null +++ b/Taskfile.aem.yml @@ -0,0 +1,99 @@ +version: '3' + +tasks: + init: + desc: initialize project + cmds: + - sh aemw project init -V ALL + + setup: + desc: start and provision AEM instances + cmds: + - task: start + - task: provision + - task: check + + resetup: + desc: destroy then setup again AEM instances + cmds: + - task: destroy + - task: setup + + start: + desc: start AEM instances + aliases: [ up ] + cmd: sh aemw instance launch + + stop: + desc: stop AEM instances + aliases: [ down ] + cmd: sh aemw instance stop + + restart: + desc: restart AEM instances + cmds: + - task: stop + - task: start + + destroy: + desc: destroy AEM instances + prompt: This will permanently delete all configured AEM instances and their data. Continue? + deps: [ stop ] + cmd: sh aemw instance destroy + + status: + desc: check status of AEM instances + env: + AEM_OUTPUT_VALUE: ALL + cmd: sh aemw instance status + + tail: + desc: tail logs of AEM instances + cmd: tail -f aem/home/var/instance/*/crx-quickstart/logs/{stdout,error}.log + + tail:author: + desc: tail logs of AEM author instance + cmd: tail -f aem/home/var/instance/author/crx-quickstart/logs/{stdout,error}.log + + tail:publish: + desc: tail logs of AEM publish instance + cmd: tail -f aem/home/var/instance/publish/crx-quickstart/logs/{stdout,error}.log + + provision: + desc: provision AEM instances by installing packages and applying configurations + aliases: [ configure ] + cmds: + - task: provision:repl-agent-publish + - task: provision:crx + + provision:repl-agent-publish: + desc: configure replication agent on AEM author instance + internal: true + cmd: | + PROPS=" + enabled: true + transportUri: {{.AEM_PUBLISH_HTTP_URL}}/bin/receive?sling:authRequestLogin=1 + transportUser: {{.AEM_PUBLISH_USER}} + transportPassword: {{.AEM_PUBLISH_PASSWORD}} + userId: admin + " + echo "$PROPS" | sh aemw repl agent setup -A --location "author" --name "publish" + + provision:crx: + desc: enable CRX/DE on AEM instances + internal: true + cmd: 'sh aemw osgi config save --pid "org.apache.sling.jcr.davex.impl.servlets.SlingDavExServlet" --input-string "alias: /crx/server"' + + check: + deps: [ author:check, publish:check ] + + author:check: + desc: check health of AEM author instance + cmds: + - curl -s -u "{{.AEM_AUTHOR_USER}}:{{.AEM_AUTHOR_PASSWORD}}" "{{.AEM_AUTHOR_HTTP_URL}}/libs/granite/core/content/login.html" | grep -q "QUICKSTART_HOMEPAGE" + - curl -s -u "{{.AEM_AUTHOR_USER}}:{{.AEM_AUTHOR_PASSWORD}}" "{{.AEM_AUTHOR_HTTP_URL}}/etc/replication/agents.author/publish.test.html" | grep -q "Replication (TEST) of /content successful" + + publish:check: + desc: check health of AEM publish instance + cmd: curl -s -u "{{.AEM_PUBLISH_USER}}:{{.AEM_PUBLISH_PASSWORD}}" "{{.AEM_PUBLISH_HTTP_URL}}/libs/granite/core/content/login.html" | grep -q "QUICKSTART_HOMEPAGE" + diff --git a/Taskfile.cli.yml b/Taskfile.cli.yml new file mode 100755 index 00000000..23662f0c --- /dev/null +++ b/Taskfile.cli.yml @@ -0,0 +1,148 @@ +version: '3' + +tasks: + all: + desc: Run deps, test, vet, fmt, lint, install + cmds: + - task: deps + - task: test + - task: vet + - task: fmt + - task: lint + - task: install + + deps: + desc: Install dependencies + cmds: + - go install github.com/mgechev/revive@v1.3.3 + - go mod tidy + + test: + desc: Run tests + cmds: + - go test ./... + + test:int: + desc: Run integration tests + cmds: + - go test -tags=int_test ./... + + vet: + desc: Run go vet + cmds: + - go vet ./... + + fmt: + desc: Format code + cmds: + - gofmt -l -w . + + lint: + desc: Run linter + cmds: + - revive -config revive.toml -formatter friendly ./... + + build: + desc: Build the binary + vars: + GIT_COMMIT: + sh: git rev-parse HEAD + GIT_COMMIT_DATE: + sh: git log -1 --date=format:'%Y-%m-%dT%H:%M:%S' --format=%cd + GIT_VERSION: + sh: git describe --tags 2>/dev/null || echo "{{.GIT_COMMIT}}" + LD_FLAGS: "-s -w -X main.appVersion={{.GIT_VERSION}} -X main.appCommit={{.GIT_COMMIT}} -X main.appCommitDate={{.GIT_COMMIT_DATE}}" + TAGS: timetzdata + cmds: + - go build -tags "{{.TAGS}}" --ldflags "{{.LD_FLAGS}}" -o bin/aem ./cmd/aem + + build:other: + desc: Build binaries for other platforms + vars: + GIT_COMMIT: + sh: git rev-parse HEAD + GIT_COMMIT_DATE: + sh: git log -1 --date=format:'%Y-%m-%dT%H:%M:%S' --format=%cd + GIT_VERSION: + sh: git describe --tags 2>/dev/null || echo "{{.GIT_COMMIT}}" + LD_FLAGS: "-s -w -X main.appVersion={{.GIT_VERSION}} -X main.appCommit={{.GIT_COMMIT}} -X main.appCommitDate={{.GIT_COMMIT_DATE}}" + TAGS: timetzdata + cmds: + - GOARCH=amd64 GOOS=darwin go build -tags "{{.TAGS}}" --ldflags "{{.LD_FLAGS}}" -o bin/aem.darwin ./cmd/aem + - GOARCH=amd64 GOOS=linux go build -tags "{{.TAGS}}" --ldflags "{{.LD_FLAGS}}" -o bin/aem.linux ./cmd/aem + - GOARCH=amd64 GOOS=windows go build -tags "{{.TAGS}}" --ldflags "{{.LD_FLAGS}}" -o bin/aem.exe ./cmd/aem + + install: + desc: Install the binary + vars: + GIT_COMMIT: + sh: git rev-parse HEAD + GIT_COMMIT_DATE: + sh: git log -1 --date=format:'%Y-%m-%dT%H:%M:%S' --format=%cd + GIT_VERSION: + sh: git describe --tags 2>/dev/null || echo "{{.GIT_COMMIT}}" + LD_FLAGS: "-s -w -X main.appVersion={{.GIT_VERSION}} -X main.appCommit={{.GIT_COMMIT}} -X main.appCommitDate={{.GIT_COMMIT_DATE}}" + TAGS: timetzdata + cmds: + - go install -tags "{{.TAGS}}" --ldflags "{{.LD_FLAGS}}" ./cmd/aem + + clean: + desc: Clean build artifacts + cmds: + - go clean + - rm -fr bin + + release: + desc: Release a new version + vars: + VERSION: "" + cmds: + - | + set -e + + VERSION="{{.CLI_ARGS | default ""}}" + VERSION_TAG="v$VERSION" + + VERSION_CURRENT_TAG=$(git describe --tags --abbrev=0 || echo "v0.0.0") + VERSION_CURRENT_TAG="${VERSION_CURRENT_TAG#v}" # remove leading 'v', preserve leading zeros + + if [ -z "$VERSION" ]; then + echo "Release version is not specified!" + echo "Last released: ${VERSION_CURRENT_TAG}" + exit 1 + fi + + GIT_STAT=$(git diff --stat || true) + + if [ "$GIT_STAT" != '' ]; then + echo "Unable to release. Uncommitted changes detected!" + exit 1 + fi + + echo "" + echo "Releasing $VERSION_TAG" + echo "" + + echo "Bumping version in files" + bump_version() { + local file="$1" + if [ "$(uname)" = "Darwin" ]; then + sed -i '' 's/AEM_CLI_VERSION:-"[^\"]*"/AEM_CLI_VERSION:-"'"$VERSION"'"/g' "$file" + # shellcheck disable=SC2016 + sed -i '' 's/aem\@v[^\`]*\`/aem@v'"$VERSION"\`'/g' "$file" + else + sed -i 's/AEM_CLI_VERSION:-"[^\"]*"/AEM_CLI_VERSION:-"'"$VERSION"'"/g' "$file" + # shellcheck disable=SC2016 + sed -i 's/aem\@v[^\`]*\`/aem@v'"$VERSION"\`'/g' "$file" + fi + } + bump_version "README.MD" + bump_version "pkg/project/common/aemw" + + echo "Pushing version bump" + git commit -a -m "Release $VERSION_TAG" || echo "No changes to commit" + git push || echo "Nothing to push" + + echo "Pushing release tag '$VERSION_TAG'" + git tag "$VERSION_TAG" + git push origin "$VERSION_TAG" || echo "Tag already exists or cannot push" diff --git a/Taskfile.yml b/Taskfile.yml old mode 100755 new mode 100644 index c7d01d99..fe434d93 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,72 +1,16 @@ -# Task tool documentation: -# 1) Basics: https://taskfile.dev/usage -# 2) Naming conventions: https://taskfile.dev/styleguide - version: '3' env: AEM_ENV: '{{.AEM_ENV | default "local"}}' AEM_INSTANCE_PROCESSING_MODE: auto AEM_OUTPUT_VALUE: NONE + AEM_JAVA_ENABLED: true dotenv: - '.env' # VCS-ignored, user-specific - '.env.{{.AEM_ENV}}' # VCS-ignored, env-specific - '{{.AEM_ENV}}.env' # VCS-tracked, env-specific -tasks: - - release: - desc: Release a new version - vars: - VERSION: "" - cmds: - - | - set -e - - VERSION="{{.CLI_ARGS | default ""}}" - VERSION_TAG="v$VERSION" - - VERSION_CURRENT_TAG=$(git describe --tags --abbrev=0 || echo "v0.0.0") - VERSION_CURRENT_TAG="${VERSION_CURRENT_TAG#v}" # remove leading 'v', preserve leading zeros - - if [ -z "$VERSION" ]; then - echo "Release version is not specified!" - echo "Last released: ${VERSION_CURRENT_TAG}" - exit 1 - fi - - GIT_STAT=$(git diff --stat || true) - - if [ "$GIT_STAT" != '' ]; then - echo "Unable to release. Uncommitted changes detected!" - exit 1 - fi - - echo "" - echo "Releasing $VERSION_TAG" - echo "" - - echo "Bumping version in files" - bump_version() { - local file="$1" - if [ "$(uname)" = "Darwin" ]; then - sed -i '' 's/AEM_CLI_VERSION:-"[^\"]*"/AEM_CLI_VERSION:-"'"$VERSION"'"/g' "$file" - # shellcheck disable=SC2016 - sed -i '' 's/aem\@v[^\`]*\`/aem@v'"$VERSION"\`'/g' "$file" - else - sed -i 's/AEM_CLI_VERSION:-"[^\"]*"/AEM_CLI_VERSION:-"'"$VERSION"'"/g' "$file" - # shellcheck disable=SC2016 - sed -i 's/aem\@v[^\`]*\`/aem@v'"$VERSION"\`'/g' "$file" - fi - } - bump_version "README.MD" - bump_version "pkg/project/common/aemw" - - echo "Pushing version bump" - git commit -a -m "Release $VERSION_TAG" || echo "No changes to commit" - git push || echo "Nothing to push" - - echo "Pushing release tag '$VERSION_TAG'" - git tag "$VERSION_TAG" - git push origin "$VERSION_TAG" || echo "Tag already exists or cannot push" \ No newline at end of file +includes: + cli: ./Taskfile.cli.yml + aem: ./Taskfile.aem.yml \ No newline at end of file diff --git a/aem/default/etc/aem.yml b/aem/default/etc/aem.yml new file mode 100755 index 00000000..48c84853 --- /dev/null +++ b/aem/default/etc/aem.yml @@ -0,0 +1,326 @@ +# AEM instances to work with +instance: + + # Full details of local or remote instances + config: + local_author: + active: [[.Env.AEM_AUTHOR_ACTIVE | default true ]] + http_url: [[.Env.AEM_AUTHOR_HTTP_URL | default "http://127.0.0.1:4502" ]] + user: [[.Env.AEM_AUTHOR_USER | default "admin" ]] + password: [[.Env.AEM_AUTHOR_PASSWORD | default "admin" ]] + run_modes: [ local ] + jvm_opts: + - -server + - -Djava.awt.headless=true + - -Djava.io.tmpdir=[[canonicalPath .Path "aem/home/tmp"]] + - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=[[.Env.AEM_AUTHOR_DEBUG_ADDR | default "0.0.0.0:14502" ]] + - -Duser.language=en + - -Duser.country=US + - -Duser.timezone=UTC + start_opts: [] + secret_vars: + - ACME_SECRET=value + env_vars: + - ACME_VAR=value + sling_props: [] + local_publish: + active: [[.Env.AEM_PUBLISH_ACTIVE | default true ]] + http_url: [[.Env.AEM_PUBLISH_HTTP_URL | default "http://127.0.0.1:4503" ]] + user: [[.Env.AEM_PUBLISH_USER | default "admin" ]] + password: [[.Env.AEM_PUBLISH_PASSWORD | default "admin" ]] + run_modes: [ local ] + jvm_opts: + - -server + - -Djava.awt.headless=true + - -Djava.io.tmpdir=[[canonicalPath .Path "aem/home/tmp"]] + - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=[[.Env.AEM_PUBLISH_DEBUG_ADDR | default "0.0.0.0:14503" ]] + - -Duser.language=en + - -Duser.country=US + - -Duser.timezone=UTC + start_opts: [] + secret_vars: + - ACME_SECRET=value + env_vars: + - ACME_VAR=value + sling_props: [] + + # Tuning performance & reliability + # 'auto' - for more than 1 local instances - 'serial', otherwise 'parallel' + # 'parallel' - for working with remote instances + # 'serial' - for working with local instances + processing_mode: auto + + # HTTP client settings + http: + timeout: 10m + debug: false + disable_warn: true + + # State checking + check: + # Time to wait before first state checking (to avoid false-positives) + warmup: 1s + # Time to wait for next state checking + interval: 6s + # Number of successful check attempts that indicates end of checking + done_threshold: 4 + # Wait only for those instances whose state has been changed internally (unaware of external changes) + await_strict: true + # Max time to wait for the instance to be healthy after executing the start script or e.g deploying a package + await_started: + timeout: 30m + # Max time to wait for the instance to be stopped after executing the stop script + await_stopped: + timeout: 10m + # Max time in which socket connection to instance should be established + reachable: + skip: false + timeout: 3s + # Bundle state tracking + bundle_stable: + skip: false + symbolic_names_ignored: [] + # OSGi events tracking + event_stable: + skip: false + # Topics indicating that instance is not stable + topics_unstable: + - "org/osgi/framework/ServiceEvent/*" + - "org/osgi/framework/FrameworkEvent/*" + - "org/osgi/framework/BundleEvent/*" + # Ignored service names to handle known issues + details_ignored: + - "*.*MBean" + - "org.osgi.service.component.runtime.ServiceComponentRuntime" + - "java.util.ResourceBundle" + received_max_age: 5s + # OSGi components state tracking + component_stable: + skip: false + pids: + include: ['com.day.crx.packaging.*', 'org.apache.sling.installer.*'] + exclude: ['org.apache.sling.installer.hc.*', 'org.apache.sling.installer.core.impl.console.*'] + match: + "disabled": [] + "no config": [] + "unsatisfied (reference)": [] + "satisfied": [] + # Sling Installer tracking + installer: + skip: false + # JMX state checking + state: true + # Pause Installation nodes checking + pause: true + # Specific endpoints / paths (like login page) + path_ready: + timeout: 10s + login_page: + skip: false + path: "/libs/granite/core/content/login.html" + status_code: 200 + contained_text: QUICKSTART_HOMEPAGE + + # Managed locally (set up automatically) + local: + # Current runtime dir (Sling launchpad, JCR repository) + unpack_dir: "aem/home/var/instance" + # Archived runtime dir (AEM backup files '*.aemb.zst') + backup_dir: "aem/home/var/backup" + + # Status discovery (timezone, AEM version, etc) + status: + timeout: 500ms + + # JCR Repository + repo: + property_change_ignored: + # AEM assigns them automatically + - "jcr:created" + - "cq:lastModified" + # AEM encrypts it right after changing by replication agent setup command + - "transportPassword" + + # CRX Package Manager + package: + # Force re-uploading/installing of snapshot AEM packages (just built / unreleased) + snapshot_patterns: [ "**/*-SNAPSHOT.zip" ] + snapshot_ignored: false + # Use checksums to avoid re-deployments when snapshot AEM packages are unchanged + snapshot_deploy_skipping: true + # Disable following workflow launchers for a package deployment time only + toggled_workflows: [/libs/settings/workflow/launcher/config/asset_processing_on_sdk_*,/libs/settings/workflow/launcher/config/update_asset_*,/libs/settings/workflow/launcher/config/dam_*] + # Also sub-packages + install_recursive: true + # Use slower HTML endpoint for deployments but with better troubleshooting + install_html: + enabled: false + # Print HTML directly to console instead of writing to file + console: false + # Fail on case 'installed with errors' + strict: true + # Number of changes after which the commit to the repository is performed + install_save_threshold: 1024 + # Allows to relax dependency handling if needed + install_dependency_handling: required + # Controls how 'rep:policy' nodes are handled during import + install_ac_handling: '' + # Determines whether a copy of the package is created during installation (saves disk space but prevents uninstallation) + install_extract_only: snapshot + + # 'SSL By Default' + ssl: + setup_timeout: 30s + + # OSGi Framework + osgi: + shutdown_delay: 3s + + bundle: + install: + start: true + start_level: 20 + refresh_packages: true + + # Force re-uploading/installing of snapshot OSGi bundles (just built / unreleased) + snapshot_patterns: [ "**/*-SNAPSHOT.jar" ] + snapshot_ignored: false + # Use checksums to avoid re-installations when snapshot OSGi bundles are unchanged + snapshot_install_skipping: true + + # OAK Repository + oak: + index: + await_not_reindexed_timeout: 60m + + # Crypto Support + crypto: + key_bundle_symbolic_name: com.adobe.granite.crypto.file + + # Replication + replication: + bundle_symbolic_name: com.day.cq.cq-replication + + # Workflow Manager + workflow: + launcher: + lib_root: /libs/settings/workflow/launcher + config_root: /conf/global/settings/workflow/launcher + toggle_retry: + timeout: 10m + delay: 10s + +base: + # Location of library files (AEM SDK ZIP, Quickstart JAR & License, Crypto keys, service packs, additional packages, etc.) + lib_dir: aem/home/lib + # Location of temporary files (downloaded AEM packages, etc) + tmp_dir: aem/home/tmp + # Location of supportive tools (downloaded Java, OakRun, unpacked AEM SDK) + tool_dir: aem/home/opt + +input: + format: yml + file: STDIN + +output: + format: text + log: + # File path of logs written especially when output format is different than 'text' + 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 + +vendor: + # AEM instance source files + quickstart: + # AEM SDK ZIP or JAR + dist_file: 'aem/home/lib/{aem-sdk,cq-quickstart}-*.{zip,jar}' + # AEM License properties file + license_file: "aem/home/lib/license.properties" + + # AEM SDK source files + sdk: + # Controls which distribution of dispatcher is unpacked (auto|unix|windows) + os: auto + + # JDK used to: run AEM instances, build OSGi bundles, assemble AEM packages + java: + # Require following versions before e.g running AEM instances + version_constraints: [">= 1.8, < 1.9", ">= 11, < 12", ">= 17, < 18", ">= 21, < 22"] + + # Pre-installed local JDK dir + # a) keep it empty to download open source Java automatically for current OS and architecture + # b) set it to absolute path or to env var '[[.Env.JAVA_HOME]]' to indicate where closed source Java like Oracle is installed + home_dir: "" + + # Auto-installed JDK options + download: + # Source URL with template vars support + url: "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.25%2B9/OpenJDK11U-jdk_[[.Arch]]_[[.Os]]_hotspot_11.0.25_9.[[.ArchiveExt]]" + # Map source URL template vars to be compatible with Adoptium Java + replacements: + # Var 'Os' (GOOS) + "darwin": "mac" + # Var 'Arch' (GOARCH) + "x86_64": "x64" + "amd64": "x64" + "386": "x86-32" + # enforce non-ARM Java as some AEM features are not working on ARM (e.g Scene7) + "arm64": "x64" + "aarch64": "x64" + + # Oak Run tool options (offline instance management) + oak_run: + download_url: "https://repo1.maven.org/maven2/org/apache/jackrabbit/oak-run/1.72.0/oak-run-1.72.0.jar" + store_path: "crx-quickstart/repository/segmentstore" + + # Pre-installed local JAR file + # a) keep it empty to download it automatically + # b) set it to an absolute path to skip downloading + jar_file: "" + +# Content-related 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 diff --git a/aemw b/aemw new file mode 100644 index 00000000..d9f13603 --- /dev/null +++ b/aemw @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +aem "$@" diff --git a/local.env b/local.env new file mode 100755 index 00000000..8efb421c --- /dev/null +++ b/local.env @@ -0,0 +1,11 @@ +# Variables shared to both AEM Compose and Task tool + +AEM_AUTHOR_USER=admin +AEM_AUTHOR_PASSWORD=admin +AEM_AUTHOR_HTTP_URL=http://localhost:4502 +AEM_AUTHOR_DEBUG_ADDR=127.0.0.1:14502 + +AEM_PUBLISH_USER=admin +AEM_PUBLISH_PASSWORD=admin +AEM_PUBLISH_HTTP_URL=http://localhost:4503 +AEM_PUBLISH_DEBUG_ADDR=127.0.0.1:14503 diff --git a/mvnw b/mvnw new file mode 100755 index 00000000..34a21de1 --- /dev/null +++ b/mvnw @@ -0,0 +1,352 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# AEM Compose - Ensure Java is vendored and prepared +# ---------------------------------------------------------------------------- +AEM_JAVA_ENABLED=${AEM_JAVA_ENABLED:-true} +if [ "${AEM_JAVA_ENABLED}" = true ]; then + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + is_java_valid() { + [ -n "$1" ] && [ -d "$1" ] && [ -x "$1/bin/java" ] + } + get_java_home() { + (cd "${SCRIPT_DIR}" && sh aemw vendor list -V javaHome 2>/dev/null) || true + } + prepare_java() { + (cd "${SCRIPT_DIR}" && sh aemw vendor prepare) + } + show_prep_error() { + if [ -n "$1" ]; then + echo "AEM Compose - Vendor tools cannot be prepared (exit $1). Cannot continue!" + else + echo "AEM Compose - Vendor tools cannot be prepared. Cannot continue!" + fi + echo "Run command 'sh aemw vendor prepare' from the main project directory and inspect the error." + } + AEM_JAVA_HOME="$(get_java_home)" + if ! is_java_valid "$AEM_JAVA_HOME"; then + echo "AEM Compose - Vendor tools not prepared (Java). Preparing now..." + if ! prepare_java; then + rc=$? + show_prep_error "$rc" + exit $rc + fi + AEM_JAVA_HOME="$(get_java_home)" + if ! is_java_valid "$AEM_JAVA_HOME"; then + show_prep_error + exit 1 + fi + echo "AEM Compose - Vendor tools prepared. Java available at '${AEM_JAVA_HOME}'" + fi + + JAVA_HOME="$AEM_JAVA_HOME"; export JAVA_HOME +fi + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +# TODO Workaround for: https://issues.apache.org/jira/browse/MWRAPPER-122 ; +# TODO A few 'if' statements are amended to apply the 'cygpath' conversion also for 'mingw' +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if [ "$cygwin" = true ] || [ "$mingw" = true ]; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if [ "$cygwin" = true ] || [ "$mingw" = true ]; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if [ "$cygwin" = true ] || [ "$mingw" = true ]; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" \ No newline at end of file diff --git a/pkg/project/common/aemw b/pkg/project/common/aemw index 45121039..5ee10d57 100755 --- a/pkg/project/common/aemw +++ b/pkg/project/common/aemw @@ -2,6 +2,29 @@ VERSION=${AEM_CLI_VERSION:-"2.0.16"} +# Check required tools +# ==================== + +check_command() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Error! Required command '$1' is not installed" + exit 1 + fi +} + +check_command curl +check_command uname + +# Check archive tools (OS-dependent, checked after detection) +check_archive_tools() { + if [ "$1" = "windows" ]; then + check_command unzip + else + check_command tar + check_command gzip + fi +} + # Define API # ========== @@ -34,7 +57,7 @@ detect_arch() { armv6*) arch="armv6" ;; armv7*) arch="armv7" ;; esac - echo ${arch} + echo "${arch}" } # https://github.com/client9/shlib/blob/master/http_download.sh @@ -42,16 +65,30 @@ download_file() { local_file=$1 source_url=$2 header=$3 - if [ -z "$header" ]; then - code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") - else - code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") - fi - if [ "$code" != "200" ]; then - echo "Error! Downloading file from URL '$source_url' received HTTP status '$code'" - return 1 - fi - return 0 + attempt=1 + max_attempts=3 + while [ $attempt -le $max_attempts ]; do + if [ -z "$header" ]; then + code=$(curl -w '%{http_code}' -skL --connect-timeout 30 --max-time 300 -o "$local_file" "$source_url") + else + code=$(curl -w '%{http_code}' -skL --connect-timeout 30 --max-time 300 -H "$header" -o "$local_file" "$source_url") + fi + if [ "$code" = "200" ]; then + return 0 + fi + if [ "$code" = "000" ]; then + echo "Warning: Cannot connect to '$source_url' (SSL/network issue). Try manual download. (attempt $attempt/$max_attempts)" + else + echo "Warning: Downloading file from URL '$source_url' received HTTP status '$code' (attempt $attempt/$max_attempts)" + fi + rm -f "$local_file" + if [ $attempt -lt $max_attempts ]; then + sleep 3 + fi + attempt=$((attempt + 1)) + done + echo "Error! Downloading file from URL '$source_url' failed after $max_attempts attempts" + return 1 } download_file_once () { @@ -60,7 +97,10 @@ download_file_once () { if [ ! -f "${FILE}" ]; then mkdir -p "$(dirname "$FILE")" FILE_TMP="$2.tmp" - download_file "$FILE_TMP" "$URL" + if ! download_file "$FILE_TMP" "$URL"; then + rm -f "$FILE_TMP" + return 1 + fi mv "$FILE_TMP" "$FILE" fi } @@ -72,8 +112,16 @@ unarchive_file() { rm -fr "$DIR" mkdir -p "$DIR" if [ "${FILE##*.}" = "zip" ] ; then + if ! unzip -t "$FILE" > /dev/null 2>&1; then + echo "Error! Archive file '$FILE' is corrupted or not a valid zip" + return 1 + fi unzip "$FILE" -d "$DIR" else + if ! gzip -t "$FILE" 2>/dev/null; then + echo "Error! Archive file '$FILE' is corrupted or not a valid gzip" + return 1 + fi tar -xf "$FILE" -C "$DIR" fi } @@ -85,6 +133,8 @@ unarchive_file() { OS=$(detect_os) ARCH=$(detect_arch) +check_archive_tools "$OS" + AEM_DIR="aem" HOME_DIR="${AEM_DIR}/home" DOWNLOAD_DIR="${HOME_DIR}/opt" @@ -104,8 +154,21 @@ BIN_EXEC_FILE="${BIN_ARCHIVE_DIR}/${BIN_NAME}" if [ "${VERSION}" != "installed" ] ; then if [ ! -f "${BIN_EXEC_FILE}" ]; then mkdir -p "${BIN_ARCHIVE_DIR}" - download_file_once "${BIN_DOWNLOAD_URL}" "${BIN_ARCHIVE_FILE}" - unarchive_file "${BIN_ARCHIVE_FILE}" "${BIN_ARCHIVE_DIR}" + if ! download_file_once "${BIN_DOWNLOAD_URL}" "${BIN_ARCHIVE_FILE}"; then + echo "Error! Cannot download AEM Compose CLI" + echo "Manual download: ${BIN_DOWNLOAD_URL}" + echo "Place the file at: ${BIN_ARCHIVE_FILE}" + exit 1 + fi + if ! unarchive_file "${BIN_ARCHIVE_FILE}" "${BIN_ARCHIVE_DIR}"; then + rm -f "${BIN_ARCHIVE_FILE}" + echo "Error! Cannot unarchive AEM Compose CLI" + exit 1 + fi + if [ ! -f "${BIN_EXEC_FILE}" ]; then + echo "Error! AEM Compose CLI binary '${BIN_EXEC_FILE}' not found after extraction" + exit 1 + fi chmod +x "${BIN_EXEC_FILE}" fi aem() { diff --git a/pkg/project/common/taskw b/pkg/project/common/taskw index 32eb08e0..42519f7c 100755 --- a/pkg/project/common/taskw +++ b/pkg/project/common/taskw @@ -2,6 +2,28 @@ VERSION=${TASK_VERSION:-"3.40.0"} +# Check required tools +# ==================== + +check_command() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Error! Required command '$1' is not installed" + exit 1 + fi +} + +check_command curl +check_command uname + +check_archive_tools() { + if [ "$1" = "windows" ]; then + check_command unzip + else + check_command tar + check_command gzip + fi +} + # Define API # ========== @@ -9,7 +31,7 @@ VERSION=${TASK_VERSION:-"3.40.0"} detect_os() { os=$(uname -s | tr '[:upper:]' '[:lower:]') - # fixed up for https://git hub.com/client9/shlib/issues/3 + # fixed up for https://github.com/client9/shlib/issues/3 case "$os" in msys*) os="windows" ;; mingw*) os="windows" ;; @@ -34,7 +56,7 @@ detect_arch() { armv6*) arch="armv6" ;; armv7*) arch="armv7" ;; esac - echo ${arch} + echo "${arch}" } # https://github.com/client9/shlib/blob/master/http_download.sh @@ -42,16 +64,30 @@ download_file() { local_file=$1 source_url=$2 header=$3 - if [ -z "$header" ]; then - code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") - else - code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") - fi - if [ "$code" != "200" ]; then - echo "Error! Downloading file from URL '$source_url' received HTTP status '$code'" - return 1 - fi - return 0 + attempt=1 + max_attempts=3 + while [ $attempt -le $max_attempts ]; do + if [ -z "$header" ]; then + code=$(curl -w '%{http_code}' -skL --connect-timeout 30 --max-time 300 -o "$local_file" "$source_url") + else + code=$(curl -w '%{http_code}' -skL --connect-timeout 30 --max-time 300 -H "$header" -o "$local_file" "$source_url") + fi + if [ "$code" = "200" ]; then + return 0 + fi + if [ "$code" = "000" ]; then + echo "Warning: Cannot connect to '$source_url' (SSL/network issue). Try manual download. (attempt $attempt/$max_attempts)" + else + echo "Warning: Downloading file from URL '$source_url' received HTTP status '$code' (attempt $attempt/$max_attempts)" + fi + rm -f "$local_file" + if [ $attempt -lt $max_attempts ]; then + sleep 3 + fi + attempt=$((attempt + 1)) + done + echo "Error! Downloading file from URL '$source_url' failed after $max_attempts attempts" + return 1 } download_file_once () { @@ -60,7 +96,10 @@ download_file_once () { if [ ! -f "${FILE}" ]; then mkdir -p "$(dirname "$FILE")" FILE_TMP="$2.tmp" - download_file "$FILE_TMP" "$URL" + if ! download_file "$FILE_TMP" "$URL"; then + rm -f "$FILE_TMP" + return 1 + fi mv "$FILE_TMP" "$FILE" fi } @@ -72,8 +111,16 @@ unarchive_file() { rm -fr "$DIR" mkdir -p "$DIR" if [ "${FILE##*.}" = "zip" ] ; then + if ! unzip -t "$FILE" > /dev/null 2>&1; then + echo "Error! Archive file '$FILE' is corrupted or not a valid zip" + return 1 + fi unzip "$FILE" -d "$DIR" else + if ! gzip -t "$FILE" 2>/dev/null; then + echo "Error! Archive file '$FILE' is corrupted or not a valid gzip" + return 1 + fi tar -xf "$FILE" -C "$DIR" fi } @@ -85,6 +132,8 @@ unarchive_file() { OS=$(detect_os) ARCH=$(detect_arch) +check_archive_tools "$OS" + AEM_DIR="aem" HOME_DIR="${AEM_DIR}/home" DOWNLOAD_DIR="${HOME_DIR}/opt" @@ -103,8 +152,21 @@ BIN_EXEC_FILE="${BIN_ARCHIVE_DIR}/${BIN_NAME}" if [ ! -f "${BIN_EXEC_FILE}" ]; then mkdir -p "${BIN_ARCHIVE_DIR}" - download_file_once "${BIN_DOWNLOAD_URL}" "${BIN_ARCHIVE_FILE}" - unarchive_file "${BIN_ARCHIVE_FILE}" "${BIN_ARCHIVE_DIR}" + if ! download_file_once "${BIN_DOWNLOAD_URL}" "${BIN_ARCHIVE_FILE}"; then + echo "Error! Cannot download Task CLI" + echo "Manual download: ${BIN_DOWNLOAD_URL}" + echo "Place the file at: ${BIN_ARCHIVE_FILE}" + exit 1 + fi + if ! unarchive_file "${BIN_ARCHIVE_FILE}" "${BIN_ARCHIVE_DIR}"; then + rm -f "${BIN_ARCHIVE_FILE}" + echo "Error! Cannot unarchive Task CLI" + exit 1 + fi + if [ ! -f "${BIN_EXEC_FILE}" ]; then + echo "Error! Task CLI binary '${BIN_EXEC_FILE}' not found after extraction" + exit 1 + fi chmod +x "${BIN_EXEC_FILE}" fi