diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..259df18 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,82 @@ +name: Run on LocalStack + +on: + push: + paths-ignore: + - '**.md' + branches: + - master + pull_request: + branches: + - master + workflow_dispatch: + +permissions: + contents: read + +jobs: + pipeline: + name: ${{ matrix.sample }} + runs-on: ubuntu-latest + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + sample: + - 1-codebuild-compile-s3-upload + - 2-codebuild-test-codeartifact-publish + defaults: + run: + working-directory: ${{ matrix.sample }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + # LocalStack stops tracking a build whose image is still downloading, + # so pull the CodeBuild images (about 17 GB) before starting the pipeline + - name: Pull the CodeBuild images + run: | + # Anonymous ECR Public pulls are rate limited per IP, which runners share + for image in public.ecr.aws/codebuild/amazonlinux-x86_64-standard:5.0 localstack/aws-codebuild-local:2; do + for attempt in 1 2 3 4 5; do + docker pull -q "$image" && break + [ "$attempt" = 5 ] && exit 1 + echo "Pull of $image failed, retrying in 60s" + sleep 60 + done + done + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install lstk + run: | + npm install -g @localstack/lstk + lstk setup aws --force --non-interactive + + - name: Start LocalStack + env: + LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} + # Forwarded as CODEPIPELINE_GH_TOKEN, used to download the source archives from GitHub + LOCALSTACK_CODEPIPELINE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: lstk start + + - name: Run the pipeline + env: + MAX_WAIT_SECONDS: 1200 + run: bash run.sh + + - name: Show pipeline state and LocalStack logs + if: failure() + run: | + lstk aws codepipeline get-pipeline-state --name demo-pipeline || true + lstk aws codepipeline list-action-executions --pipeline-name demo-pipeline || true + builds=$(lstk aws codebuild list-builds --query 'ids' --output text || true) + [ -n "$builds" ] && lstk aws codebuild batch-get-builds --ids $builds \ + --query 'builds[].{id: id, status: buildStatus, phases: phases[].[phaseType, phaseStatus, contexts[0].message]}' || true + docker ps -a + docker images + df -h / + lstk logs | grep -vE 'ListPipelineExecutions|ListActionExecutions' | tail -400 diff --git a/1-codebuild-compile-s3-upload/run.sh b/1-codebuild-compile-s3-upload/run.sh index a6ae52d..88f483f 100755 --- a/1-codebuild-compile-s3-upload/run.sh +++ b/1-codebuild-compile-s3-upload/run.sh @@ -30,10 +30,22 @@ lstk aws codepipeline create-pipeline --pipeline file://./pipeline.json lstk aws codepipeline list-pipeline-executions --pipeline-name demo-pipeline # Wait for pipeline to finish +MAX_WAIT_SECONDS=${MAX_WAIT_SECONDS:-1800} status= -until [[ $status =~ Succeeded ]]; do - status=$(lstk aws codepipeline list-pipeline-executions --pipeline-name demo-pipeline --output text --query pipelineExecutionSummaries[0].status) +waited=0 +until [[ $status == Succeeded ]]; do + if [[ $status =~ ^(Failed|Stopped|Superseded)$ ]]; then + echo "Pipeline execution ended with status $status" + lstk aws codepipeline get-pipeline-state --name demo-pipeline + exit 1 + fi + if (( waited >= MAX_WAIT_SECONDS )); then + echo "Pipeline did not finish within ${MAX_WAIT_SECONDS}s, last status: $status" + exit 1 + fi sleep 5 + waited=$((waited + 5)) + status=$(lstk aws codepipeline list-pipeline-executions --pipeline-name demo-pipeline --output text --query 'pipelineExecutionSummaries[0].status') done # Retrieve build @@ -42,4 +54,4 @@ lstk aws s3 cp s3://output-bucket/fzf/fzf . # Try build file fzf chmod +x fzf -./fzf --version +./fzf --version | grep '^0.62 ' diff --git a/2-codebuild-test-codeartifact-publish/run.sh b/2-codebuild-test-codeartifact-publish/run.sh index 9c14207..b264ba5 100755 --- a/2-codebuild-test-codeartifact-publish/run.sh +++ b/2-codebuild-test-codeartifact-publish/run.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash set -xeuo pipefail @@ -40,10 +40,22 @@ lstk aws s3 mb s3://demo-artif-bucket lstk aws codepipeline create-pipeline --pipeline file://./demo-pipeline.json # Wait for the pipeline to finish +MAX_WAIT_SECONDS=${MAX_WAIT_SECONDS:-1800} status= -until [[ $status =~ Succeeded ]]; do - status=$(lstk aws codepipeline list-pipeline-executions --pipeline-name demo-pipeline --output text --query pipelineExecutionSummaries[0].status) +waited=0 +until [[ $status == Succeeded ]]; do + if [[ $status =~ ^(Failed|Stopped|Superseded)$ ]]; then + echo "Pipeline execution ended with status $status" + lstk aws codepipeline get-pipeline-state --name demo-pipeline + exit 1 + fi + if (( waited >= MAX_WAIT_SECONDS )); then + echo "Pipeline did not finish within ${MAX_WAIT_SECONDS}s, last status: $status" + exit 1 + fi sleep 5 + waited=$((waited + 5)) + status=$(lstk aws codepipeline list-pipeline-executions --pipeline-name demo-pipeline --output text --query 'pipelineExecutionSummaries[0].status') done # Configure NPM to work with the local CodeArtifact repository @@ -52,3 +64,6 @@ lstk aws codeartifact login --tool npm --domain demo-domain --repository demo-re # Try to download the NPM package npm pack my-lodash-fork + +# Check the downloaded package is the fork published by the pipeline +tar -xzOf my-lodash-fork-*.tgz package/package.json | grep '"name": "my-lodash-fork"' diff --git a/README.md b/README.md index 8f79bf9..30d82da 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,19 @@ export LOCALSTACK_AUTH_TOKEN= export CODEPIPELINE_GH_TOKEN= ``` +If you start LocalStack with `lstk start`, export the token as `LOCALSTACK_CODEPIPELINE_GH_TOKEN` instead. `lstk` only forwards `LOCALSTACK_`-prefixed variables to the container, which strips the prefix. + +Each `run.sh` fails if the pipeline execution fails or takes longer than `MAX_WAIT_SECONDS` (default 1800). + +Pull the CodeBuild images before the first run: + +```bash +docker pull public.ecr.aws/codebuild/amazonlinux-x86_64-standard:5.0 +docker pull localstack/aws-codebuild-local:2 +``` + +LocalStack stops tracking a build if its image is still downloading a few seconds after the build starts, and the pipeline then stays `InProgress`. + ## Samples - [1-codebuild-compile-s3-upload](1-codebuild-compile-s3-upload/README.md): Builds `fzf` with CodeBuild and uploads the resulting binary to S3 through CodePipeline.