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 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 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..0add49d 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,39 @@ 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 `-`. | + +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 +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 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/asset-repo/docker-server/create_repository b/scripts/asset-repo/docker-server/create_repository index e3b7564..f19785d 100644 --- a/scripts/asset-repo/docker-server/create_repository +++ b/scripts/asset-repo/docker-server/create_repository @@ -1,3 +1,7 @@ #!/bin/bash -np nrn patch --nrn "$NRN" --body "{\"docker.repository_uri\":\"$DOCKER_SERVER_URI\"}" \ No newline at end of file +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/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..f5c4b8e --- /dev/null +++ b/scripts/asset-repo/ecr/create_repository @@ -0,0 +1,53 @@ +#!/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" + DESCRIBE_OUTPUT=$(aws ecr describe-repositories \ + --repository-names "$ECR_REPOSITORY_NAME" \ + --region "$AWS_REGION" \ + --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" + 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 + +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" + +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" 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 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)