From a7f422705157a456f97825713a64806231ebda24 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Mon, 29 Jun 2026 17:47:10 -0300 Subject: [PATCH 1/8] feat: add CREATE_CODE_REPOSITORY and CREATE_ASSET_REPOSITORY toggle flags --- scripts/asset-repo/create_asset_repository | 5 +++++ scripts/code-repo/create_code_repository | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/scripts/asset-repo/create_asset_repository b/scripts/asset-repo/create_asset_repository index 86f5c19..6398fe6 100644 --- a/scripts/asset-repo/create_asset_repository +++ b/scripts/asset-repo/create_asset_repository @@ -1,5 +1,10 @@ #!/bin/bash +if [[ "${CREATE_ASSET_REPOSITORY,,}" == "false" ]]; then + echo "CREATE_ASSET_REPOSITORY=false — skipping asset repository creation" + return 0 +fi + ASSET_REPOSITORY=$(np provider list --categories assets-repository --nrn "$NRN" --format json | jq ".results[0]") if [[ -n "$ASSET_REPOSITORY_PROVIDER" ]]; then diff --git a/scripts/code-repo/create_code_repository b/scripts/code-repo/create_code_repository index 7d4aa47..b640753 100644 --- a/scripts/code-repo/create_code_repository +++ b/scripts/code-repo/create_code_repository @@ -1,5 +1,10 @@ #!/bin/bash +if [[ "${CREATE_CODE_REPOSITORY,,}" == "false" ]]; then + echo "CREATE_CODE_REPOSITORY=false — skipping code repository creation" + return 0 +fi + CURRENT_DIR=$(dirname "${BASH_SOURCE[0]}") REPOSITORY_URL=$(echo "$APPLICATION" | jq -r .repository_url) From fb2a4e842e5fd99a2fbf40d152a741dc4dc0c62e Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Mon, 29 Jun 2026 17:49:40 -0300 Subject: [PATCH 2/8] refactor: migrate docker-server asset persistence to np application patch --- scripts/asset-repo/docker-server/create_repository | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/asset-repo/docker-server/create_repository b/scripts/asset-repo/docker-server/create_repository index e3b7564..e83fbd5 100644 --- a/scripts/asset-repo/docker-server/create_repository +++ b/scripts/asset-repo/docker-server/create_repository @@ -1,3 +1,4 @@ #!/bin/bash -np nrn patch --nrn "$NRN" --body "{\"docker.repository_uri\":\"$DOCKER_SERVER_URI\"}" \ No newline at end of file +np application patch --id "$APPLICATION_ID" \ + --body "{\"settings\": {\"asset\": {\"docker_server\": {\"uri\": \"$DOCKER_SERVER_URI\"}}}}" From 808ce54cfa4ae410e2fd5446cb497863520e940b Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Mon, 29 Jun 2026 17:51:50 -0300 Subject: [PATCH 3/8] feat: add ECR asset repository provider --- scripts/asset-repo/ecr/build_context | 12 ++++++ scripts/asset-repo/ecr/create_repository | 37 +++++++++++++++++++ .../asset-repo/ecr/generate_repository_uri | 18 +++++++++ 3 files changed, 67 insertions(+) create mode 100755 scripts/asset-repo/ecr/build_context create mode 100755 scripts/asset-repo/ecr/create_repository create mode 100755 scripts/asset-repo/ecr/generate_repository_uri diff --git a/scripts/asset-repo/ecr/build_context b/scripts/asset-repo/ecr/build_context new file mode 100755 index 0000000..3d1d1c6 --- /dev/null +++ b/scripts/asset-repo/ecr/build_context @@ -0,0 +1,12 @@ +#!/bin/bash + +if [[ -z "$AWS_REGION" ]]; then + echo "ERROR: AWS_REGION is required for the ECR asset provider but is not set." + exit 1 +fi + +echo "Using AWS_REGION: $AWS_REGION" + +export AWS_REGION +export ECR_REPOSITORY_PATH +export ECR_USE_NAMESPACE diff --git a/scripts/asset-repo/ecr/create_repository b/scripts/asset-repo/ecr/create_repository new file mode 100755 index 0000000..9bf7606 --- /dev/null +++ b/scripts/asset-repo/ecr/create_repository @@ -0,0 +1,37 @@ +#!/bin/bash + +echo "Creating ECR repository: $ECR_REPOSITORY_NAME in region $AWS_REGION" + +CREATE_OUTPUT=$(aws ecr create-repository \ + --repository-name "$ECR_REPOSITORY_NAME" \ + --region "$AWS_REGION" \ + --output json 2>&1) +CREATE_STATUS=$? + +if [[ $CREATE_STATUS -eq 0 ]]; then + REPOSITORY=$(echo "$CREATE_OUTPUT" | jq '.repository') +elif echo "$CREATE_OUTPUT" | grep -q "RepositoryAlreadyExistsException"; then + echo "ECR repository already exists, fetching its details" + REPOSITORY=$(aws ecr describe-repositories \ + --repository-names "$ECR_REPOSITORY_NAME" \ + --region "$AWS_REGION" \ + --output json | jq '.repositories[0]') +else + echo "ERROR: failed to create ECR repository:" + echo "$CREATE_OUTPUT" + exit 1 +fi + +ECR_REPOSITORY_URI=$(echo "$REPOSITORY" | jq -r '.repositoryUri') +ECR_REPOSITORY_ARN=$(echo "$REPOSITORY" | jq -r '.repositoryArn') + +if [[ -z "$ECR_REPOSITORY_URI" || "$ECR_REPOSITORY_URI" == "null" ]]; then + echo "ERROR: could not resolve ECR repository URI from AWS response." + exit 1 +fi + +echo "ECR repository URI: $ECR_REPOSITORY_URI" +echo "ECR repository ARN: $ECR_REPOSITORY_ARN" + +np application patch --id "$APPLICATION_ID" \ + --body "{\"settings\": {\"asset\": {\"ecr\": {\"uri\": \"$ECR_REPOSITORY_URI\", \"arn\": \"$ECR_REPOSITORY_ARN\"}}}}" diff --git a/scripts/asset-repo/ecr/generate_repository_uri b/scripts/asset-repo/ecr/generate_repository_uri new file mode 100755 index 0000000..8cfdec0 --- /dev/null +++ b/scripts/asset-repo/ecr/generate_repository_uri @@ -0,0 +1,18 @@ +#!/bin/bash + +ECR_REPOSITORY_NAME="" + +if [[ -n "$ECR_REPOSITORY_PATH" ]]; then + ECR_REPOSITORY_NAME="$ECR_REPOSITORY_PATH/" +fi + +SEPARATOR="-" +if [[ "$ECR_USE_NAMESPACE" == "true" ]]; then + SEPARATOR="/" +fi + +ECR_REPOSITORY_NAME="${ECR_REPOSITORY_NAME}${NAMESPACE_SLUG}${SEPARATOR}${APPLICATION_SLUG}" + +echo "ECR repository name: $ECR_REPOSITORY_NAME" + +export ECR_REPOSITORY_NAME From 8c3ecd874c6c93b1d9aa86ec676c79b32bce16f5 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Mon, 29 Jun 2026 17:54:29 -0300 Subject: [PATCH 4/8] fix: surface ECR describe errors and guard repository ARN --- scripts/asset-repo/ecr/create_repository | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/asset-repo/ecr/create_repository b/scripts/asset-repo/ecr/create_repository index 9bf7606..effe246 100755 --- a/scripts/asset-repo/ecr/create_repository +++ b/scripts/asset-repo/ecr/create_repository @@ -12,10 +12,17 @@ if [[ $CREATE_STATUS -eq 0 ]]; then REPOSITORY=$(echo "$CREATE_OUTPUT" | jq '.repository') elif echo "$CREATE_OUTPUT" | grep -q "RepositoryAlreadyExistsException"; then echo "ECR repository already exists, fetching its details" - REPOSITORY=$(aws ecr describe-repositories \ + DESCRIBE_OUTPUT=$(aws ecr describe-repositories \ --repository-names "$ECR_REPOSITORY_NAME" \ --region "$AWS_REGION" \ - --output json | jq '.repositories[0]') + --output json 2>&1) + DESCRIBE_STATUS=$? + if [[ $DESCRIBE_STATUS -ne 0 ]]; then + echo "ERROR: failed to describe existing ECR repository:" + echo "$DESCRIBE_OUTPUT" + exit 1 + fi + REPOSITORY=$(echo "$DESCRIBE_OUTPUT" | jq '.repositories[0]') else echo "ERROR: failed to create ECR repository:" echo "$CREATE_OUTPUT" @@ -30,6 +37,11 @@ if [[ -z "$ECR_REPOSITORY_URI" || "$ECR_REPOSITORY_URI" == "null" ]]; then exit 1 fi +if [[ -z "$ECR_REPOSITORY_ARN" || "$ECR_REPOSITORY_ARN" == "null" ]]; then + echo "ERROR: could not resolve ECR repository ARN from AWS response." + exit 1 +fi + echo "ECR repository URI: $ECR_REPOSITORY_URI" echo "ECR repository ARN: $ECR_REPOSITORY_ARN" From 3f07487019fda102bed560768e6e73cc25322d66 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Mon, 29 Jun 2026 17:56:43 -0300 Subject: [PATCH 5/8] docs: document toggle flags and ECR asset provider --- CHANGELOG.md | 12 ++++++++++++ README.md | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fd6465..d05a7b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to `application-lifecycle-manager` will be documented in thi The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html) once it reaches a stable API. +## [Unreleased] + +### Added +- `CREATE_CODE_REPOSITORY` and `CREATE_ASSET_REPOSITORY` flags to toggle code and asset + repository creation independently. +- AWS ECR asset repository provider. + +### Changed +- Migrated docker-server asset persistence from the deprecated `np nrn patch` + (`docker.repository_uri`) to `np application patch` (`settings.asset.docker_server.uri`). + + ## [0.2.0] - 2025-11-13 ### Added diff --git a/README.md b/README.md index 997fde3..638a3c9 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The code repository workflow is composed of the following tasks: You must configure your asset repository provider through **nullplatform platform settings** or the **nullplatform Terraform provider**. -> **Note:** At the moment, this repository supports **Docker Server** repositories only. +> **Note:** This repository supports **Docker Server** and **AWS ECR** asset repositories. #### Workflow @@ -79,6 +79,37 @@ The asset repository workflow is composed of the following tasks: - **Create repository** Creates a namespace/folder in your Docker registry for the new application and stores its location in the nullplatform API. +#### Using AWS ECR + +To use AWS ECR as the asset repository provider, set `ASSET_REPOSITORY_PROVIDER=ecr` in the +agent environment along with: + +| Variable | Required | Description | +|-----------------------|----------|-------------------------------------------------------------------| +| `AWS_REGION` | yes | Region where the ECR repository is created. | +| `ECR_REPOSITORY_PATH` | no | Optional prefix prepended to the repository name. | +| `ECR_USE_NAMESPACE` | no | When `true`, joins namespace and application with `/` instead of `-`. | + +**Prerequisites:** the agent host must have the `aws` CLI installed and AWS credentials +available in its environment (for example, via IRSA on EKS). The bound IAM role needs +`ecr:CreateRepository` and `ecr:DescribeRepositories`. The repository URI and ARN are read +from the AWS API response and persisted to the application's `settings.asset.ecr`. + +--- + +## Toggling repository creation + +You can disable either side of the lifecycle independently with environment variables set +on the agent: + +| Variable | Default | Effect | +|----------------------------|---------|-----------------------------------------------------| +| `CREATE_CODE_REPOSITORY` | enabled | Set to `false` to skip code repository creation. | +| `CREATE_ASSET_REPOSITORY` | enabled | Set to `false` to skip asset repository creation. | + +Any value other than `false` (case-insensitive), including unset, keeps the default +behavior of creating the repository. + --- ## Installing the application lifecycle manager From 05cd66e190d299f319b191fde9ec020c79782b36 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Mon, 29 Jun 2026 18:01:19 -0300 Subject: [PATCH 6/8] refactor: build asset patch bodies with jq and document ECR name rules --- README.md | 2 ++ scripts/asset-repo/docker-server/create_repository | 7 +++++-- scripts/asset-repo/ecr/create_repository | 8 ++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 638a3c9..0add49d 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,8 @@ agent environment along with: | `ECR_REPOSITORY_PATH` | no | Optional prefix prepended to the repository name. | | `ECR_USE_NAMESPACE` | no | When `true`, joins namespace and application with `/` instead of `-`. | +The generated repository name must satisfy ECR naming rules (lowercase, made up of `[a-z0-9._/-]`, no leading slash). Choose `ECR_REPOSITORY_PATH` and namespace/application slugs accordingly. + **Prerequisites:** the agent host must have the `aws` CLI installed and AWS credentials available in its environment (for example, via IRSA on EKS). The bound IAM role needs `ecr:CreateRepository` and `ecr:DescribeRepositories`. The repository URI and ARN are read diff --git a/scripts/asset-repo/docker-server/create_repository b/scripts/asset-repo/docker-server/create_repository index e83fbd5..f19785d 100644 --- a/scripts/asset-repo/docker-server/create_repository +++ b/scripts/asset-repo/docker-server/create_repository @@ -1,4 +1,7 @@ #!/bin/bash -np application patch --id "$APPLICATION_ID" \ - --body "{\"settings\": {\"asset\": {\"docker_server\": {\"uri\": \"$DOCKER_SERVER_URI\"}}}}" +BODY=$(jq -n \ + --arg uri "$DOCKER_SERVER_URI" \ + '{settings: {asset: {docker_server: {uri: $uri}}}}') + +np application patch --id "$APPLICATION_ID" --body "$BODY" diff --git a/scripts/asset-repo/ecr/create_repository b/scripts/asset-repo/ecr/create_repository index effe246..f5c4b8e 100755 --- a/scripts/asset-repo/ecr/create_repository +++ b/scripts/asset-repo/ecr/create_repository @@ -45,5 +45,9 @@ fi echo "ECR repository URI: $ECR_REPOSITORY_URI" echo "ECR repository ARN: $ECR_REPOSITORY_ARN" -np application patch --id "$APPLICATION_ID" \ - --body "{\"settings\": {\"asset\": {\"ecr\": {\"uri\": \"$ECR_REPOSITORY_URI\", \"arn\": \"$ECR_REPOSITORY_ARN\"}}}}" +BODY=$(jq -n \ + --arg uri "$ECR_REPOSITORY_URI" \ + --arg arn "$ECR_REPOSITORY_ARN" \ + '{settings: {asset: {ecr: {uri: $uri, arn: $arn}}}}') + +np application patch --id "$APPLICATION_ID" --body "$BODY" From dec2c3f57b408bd184ad31d78da124a22e3d4426 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Mon, 29 Jun 2026 18:02:32 -0300 Subject: [PATCH 7/8] chore: gitignore .claude and .env --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b99c808..5fc5a46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .idea -.DS_Store \ No newline at end of file +.DS_Store + +.env +.claude From 70dd7e710464c0fccede2d229f3b89de6886d4e5 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Wed, 1 Jul 2026 01:53:18 -0300 Subject: [PATCH 8/8] ci: pass one dir per shellcheck job to fix quoted find --- .github/workflows/pr-checks.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index f1fc1ca..638c13e 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -8,7 +8,13 @@ permissions: contents: read jobs: - shellcheck: + # The reusable workflow runs `find "$SCRIPT_DIRS"`, quoting the value as a + # single path — so a space-separated list breaks. Pass one path per job. + shellcheck-entrypoint: uses: nullplatform/actions-nullplatform/.github/workflows/shellcheck.yml@main with: - script_dirs: entrypoint scripts + script_dirs: entrypoint + shellcheck-scripts: + uses: nullplatform/actions-nullplatform/.github/workflows/shellcheck.yml@main + with: + script_dirs: scripts