From 3e09802db83aa0cac44931f86fc6c0e9b03d2392 Mon Sep 17 00:00:00 2001 From: "octopus.oreillyit.nz" Date: Mon, 23 Mar 2026 16:47:00 +1300 Subject: [PATCH] Add CI workflows for dual-image channel pattern - Non-prod workflow: builds on push to main, pushes to acme-nonprod registry, creates Octopus release in Non-Prod channel - Prod workflow: builds on v* tags, pushes to acme-prod registry, creates Octopus release in Prod channel - Simple Alpine Dockerfile for demo purposes --- .gitea/workflows/build-nonprod.yml | 62 ++++++++++++++++++++++++++++++ .gitea/workflows/build-prod.yml | 62 ++++++++++++++++++++++++++++++ Dockerfile | 3 ++ README.md | 17 +++++++- 4 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 .gitea/workflows/build-nonprod.yml create mode 100644 .gitea/workflows/build-prod.yml create mode 100644 Dockerfile diff --git a/.gitea/workflows/build-nonprod.yml b/.gitea/workflows/build-nonprod.yml new file mode 100644 index 0000000..51bd476 --- /dev/null +++ b/.gitea/workflows/build-nonprod.yml @@ -0,0 +1,62 @@ +name: Build Non-Prod Image + +on: + push: + branches: [main] + +env: + REGISTRY: gitea.oreillyit.nz + IMAGE: acme-nonprod/demo-app + OCTOPUS_SERVER: https://taniwha.octopus.app + OCTOPUS_SPACE_ID: Spaces-102 + OCTOPUS_PROJECT_ID: Projects-101 + OCTOPUS_CHANNEL_ID: Channels-124 + +jobs: + build-and-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Generate version + id: version + run: echo "sha=sha-${GITHUB_SHA::8}" >> "$GITHUB_OUTPUT" + + - name: Login to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Build and push + run: | + docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ steps.version.outputs.sha }} . + docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ steps.version.outputs.sha }} + + - name: Create Octopus Release + run: | + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + -H "X-Octopus-ApiKey: ${{ secrets.OCTOPUS_API_KEY }}" \ + -H "Content-Type: application/json" \ + "${{ env.OCTOPUS_SERVER }}/api/${{ env.OCTOPUS_SPACE_ID }}/releases" \ + -d '{ + "ProjectId": "${{ env.OCTOPUS_PROJECT_ID }}", + "ChannelId": "${{ env.OCTOPUS_CHANNEL_ID }}", + "Version": "${{ steps.version.outputs.sha }}", + "SelectedPackages": [ + { + "ActionName": "Deploy Non-Prod Image", + "PackageReferenceName": "demo-app", + "Version": "${{ steps.version.outputs.sha }}" + } + ] + }') + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + BODY=$(echo "$RESPONSE" | sed '$d') + echo "Response: $BODY" + if [ "$HTTP_CODE" -ge 400 ]; then + echo "::error::Failed to create release (HTTP $HTTP_CODE)" + exit 1 + fi + echo "Release created successfully" diff --git a/.gitea/workflows/build-prod.yml b/.gitea/workflows/build-prod.yml new file mode 100644 index 0000000..674e49f --- /dev/null +++ b/.gitea/workflows/build-prod.yml @@ -0,0 +1,62 @@ +name: Build Prod Image + +on: + push: + tags: ['v*'] + +env: + REGISTRY: gitea.oreillyit.nz + IMAGE: acme-prod/demo-app + OCTOPUS_SERVER: https://taniwha.octopus.app + OCTOPUS_SPACE_ID: Spaces-102 + OCTOPUS_PROJECT_ID: Projects-101 + OCTOPUS_CHANNEL_ID: Channels-125 + +jobs: + build-and-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Generate version + id: version + run: echo "sha=sha-${GITHUB_SHA::8}" >> "$GITHUB_OUTPUT" + + - name: Login to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Build and push + run: | + docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ steps.version.outputs.sha }} . + docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ steps.version.outputs.sha }} + + - name: Create Octopus Release + run: | + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + -H "X-Octopus-ApiKey: ${{ secrets.OCTOPUS_API_KEY }}" \ + -H "Content-Type: application/json" \ + "${{ env.OCTOPUS_SERVER }}/api/${{ env.OCTOPUS_SPACE_ID }}/releases" \ + -d '{ + "ProjectId": "${{ env.OCTOPUS_PROJECT_ID }}", + "ChannelId": "${{ env.OCTOPUS_CHANNEL_ID }}", + "Version": "prod-${{ steps.version.outputs.sha }}", + "SelectedPackages": [ + { + "ActionName": "Deploy Prod Image", + "PackageReferenceName": "demo-app", + "Version": "${{ steps.version.outputs.sha }}" + } + ] + }') + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + BODY=$(echo "$RESPONSE" | sed '$d') + echo "Response: $BODY" + if [ "$HTTP_CODE" -ge 400 ]; then + echo "::error::Failed to create release (HTTP $HTTP_CODE)" + exit 1 + fi + echo "Release created successfully" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4da8eb6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.21 +LABEL org.opencontainers.image.source="https://gitea.oreillyit.nz/octopus.oreillyit.nz/demo-app" +CMD ["echo", "Hello from demo-app"] diff --git a/README.md b/README.md index 0b6bfcb..37b3346 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ -# demo-app +# Demo App — Dual Image Channel Pattern -Demo app for dual-image channel pattern \ No newline at end of file +Demonstrates Octopus Deploy's channel pattern for routing non-prod and prod container images through separate lifecycles. + +## How it works + +- **Push to `main`** → builds non-prod image → pushes to `acme-nonprod/demo-app` → creates Octopus release in Non-Prod channel +- **Push a `v*` tag** → builds prod image → pushes to `acme-prod/demo-app` → creates Octopus release in Prod channel + +Each channel has its own lifecycle: +- Non-Prod: Development → Staging (cannot reach Production) +- Prod: Staging → Production + +## Octopus Space + +https://taniwha.octopus.app/app#/Spaces-102