mix format #268
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: CI | |
| # Build and run tests in containers | |
| # Store test and prod images to GitHub Container Registry (GHCR) | |
| # Build prod image and push to AWS ECR | |
| # Run tests and quality checks on test image | |
| # Run security scans on code and prod images | |
| # Use matrix for multiple versions of Elixir, OTP, and OS | |
| # on: push | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - qa | |
| tags: [prod] | |
| paths-ignore: | |
| - 'README.md' | |
| # - '.github/**' | |
| - '.vscode' | |
| - '.gitignore' | |
| # Handled by otel.yml GitHub Action | |
| - .github/workflows/otel.yml | |
| - 'deploy/aws-otel-collector.Dockerfile' | |
| - 'otel/*' | |
| # Handled by newman.yml GitHub Action | |
| - 'deploy/newman.Dockerfile' | |
| pull_request: | |
| # branches: [main] | |
| types: [opened,synchronize,reopened,labeled,unlabeled] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| # Name of image | |
| IMAGE_NAME: foo-app | |
| # Name of org in GHCR Docker repository (must be lowercase) | |
| IMAGE_OWNER: ${{ github.repository_owner }} | |
| # ECR Docker repo org name (may be blank, otherwise must have trailing slash) | |
| ECR_IMAGE_OWNER: cogini/ | |
| # Tag for release images, used to find the latest deployed image. | |
| IMAGE_TAG: latest | |
| IMAGE_VER: ${{ github.sha }} | |
| # Registry for test images | |
| REGISTRY: ghcr.io/ | |
| # Registry for public images, default is docker.io | |
| PUBLIC_REGISTRY: '' | |
| MAIN_BRANCH: main | |
| # Give GitHub Actions access to AWS | |
| AWS_ENABLED: 1 | |
| # GitHub Environment secrets and variables | |
| # Docker Hub credentials to pull base images without rate limits | |
| # secrets.DOCKERHUB_USERNAME | |
| # secrets.DOCKERHUB_TOKEN | |
| # AWS Account | |
| # secrets.AWS_ACCOUNT_ID | |
| # AWS default region | |
| # vars.AWS_REGION | |
| # AWS role allowing GitHub Actions to access resources and deploy | |
| # secrets.AWS_ROLE_TO_ASSUME: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/cogini-foo-dev-app-github-action | |
| # S3 bucket where assets are deployed, e.g., for use with CloudFront CDN | |
| # vars.S3_BUCKET_ASSETS: cogini-foo-app-dev-app-assets | |
| # S3 bucket with data for testing | |
| # vars.S3_BUCKET_CI: cogini-prod-foo-ci | |
| # SSH key to access private repos during build | |
| # secrets.SSH_PRIVATE_KEY | |
| # GitHub access token to access other repositories during build | |
| # secrets.DEVOPS_ACCESS_TOKEN | |
| # AWS ECS deployment role names to put in task definition | |
| # secrets.TASK_ROLE_ARN: "arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/foo-app" | |
| # secrets.EXECUTION_ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/foo-ecs-task-execution-role | |
| # AWS Systems Manager Parameter Store prefix for config keys | |
| # vars.AWS_PS_PREFIX | |
| # AWS CloudFront distribution ID to invalidate cache after deploy | |
| # secrets.CLOUDFRONT_CDN_DISTRIBUTION_ID | |
| # AppSignal API key for error reporting | |
| # DataDog API key for reporting test results | |
| # secrets.ACTIONS_DD_API_KEY | |
| # secrets.APPSIGNAL_PUSH_API_KEY | |
| # secrets.DD_API_KEY | |
| # Oban Pro license | |
| # secrets.OBAN_KEY_FINGERPRINT | |
| # secrets.OBAN_LICENSE_KEY | |
| # secrets.GITLEAKS_LICENSE | |
| # secrets.SNYK_TOKEN | |
| # Target port for prod image tests | |
| APP_PORT: 4000 | |
| # Datadog | |
| # DD_API_KEY: ${{ secrets.ACTIONS_DD_API_KEY }} | |
| # DD_ENV: ci | |
| # DD_TAGS: "environment:ci" | |
| # MIX_ENV: foo | |
| ELIXIR_MODULE: PhoenixContainerExample | |
| ECS_CLUSTER: foo | |
| ECS_SERVICE: foo-app | |
| ECS_CONTAINER: foo-app | |
| ECS_CPU: 256 | |
| ECS_RAM: 2048 | |
| CODEDEPLOY_APPLICATION: foo-app | |
| CODEDEPLOY_DEPLOYMENT_GROUP: foo-app-ecs | |
| # CODEDEPLOY_BUCKET: foo-prod-bounce-public-deploy | |
| TASKDEF: ecs/task-definition.json | |
| ECS_SERVICE_WORKER: foo-worker | |
| ECS_CONTAINER_WORKER: foo-worker | |
| ECS_CPU_WORKER: 256 | |
| ECS_RAM_WORKER: 2048 | |
| TASKDEF_WORKER: ecs/task-definition.worker.json | |
| APPSPEC: ecs/appspec.yml | |
| # AWS SSM Parameter Store name prefix | |
| # AWS_PS_PREFIX: cogini/foo/dev | |
| # APPSIGNAL_APP_NAME: bounce | |
| # Name of environment for resources created by Terraform | |
| # TERRAFORM_ENV: dev | |
| # secrets.TASK_ROLE_ARN: "arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/foo-app" | |
| # secrets.EXECUTION_ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/foo-ecs-task-execution-role | |
| # GitHub Advanced Security, free for open source, otherwise a paid feature | |
| # https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security | |
| # https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning | |
| # https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github | |
| GITHUB_ADVANCED_SECURITY: 0 | |
| DEPLOY_DOCKER_HUB: 0 | |
| # Docker | |
| DOCKER_BUILDKIT: '1' | |
| COMPOSE_DOCKER_CLI_BUILD: '1' | |
| COMPOSE_FILE: docker-compose.gha.yml | |
| DOCKER_FILE: deploy/debian.Dockerfile | |
| jobs: | |
| config: | |
| name: Configure build | |
| runs-on: ubuntu-latest | |
| outputs: | |
| # Combinations of Elixir/Erlang/OS to run internal tests on | |
| # Generally this will match prod | |
| test-matrix: ${{ steps.common-matrix.outputs.result }} | |
| test-matrix-parallel: ${{ steps.test-matrix-parallel.outputs.result }} | |
| # Combinations for prod images | |
| # Used for the final deploy image as well as external tests | |
| prod-matrix: ${{ steps.prod-matrix.outputs.result }} | |
| # Combinations to deploy to prod (or another environment) | |
| # Generally this will only be a single variant | |
| deploy-matrix: ${{ steps.deploy-matrix.outputs.result }} | |
| # Combinations used to build assets | |
| # Generally this will only be a single variant | |
| assets-matrix: ${{ steps.assets-matrix.outputs.result }} | |
| # Select environment to deploy to based on git branch/tag: staging, production | |
| environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'prod' && 'production') }} | |
| # environment: staging | |
| # Whether to deploy to AWS. Only standard tags/branches deploy, not other dev branches. | |
| deploy: ${{ contains(fromJson('["main", "staging", "qa", "prod"]'), github.ref_name) }} | |
| image-tag: ${{ (github.ref_name == 'main' && 'test') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'latest') || github.ref_name }} | |
| # Give access to AWS | |
| aws-enabled: true | |
| steps: | |
| - uses: actions/github-script@v8 | |
| id: common-matrix | |
| # Specify versions of Erlang, Elixir, and base OS | |
| # in a combination supported by https://hub.docker.com/r/hexpm/elixir/tags | |
| with: | |
| script: | | |
| return { | |
| include: [ | |
| { | |
| os: "alma", | |
| elixir: "1.18.4", | |
| otp: "28.1", | |
| build_os_ver: "8", | |
| prod_os_ver: "8" | |
| }, | |
| { | |
| os: "centos7", | |
| elixir: "1.18.4", | |
| otp: "28.1", | |
| build_os_ver: "7.9.2009", | |
| prod_os_ver: "7.9.2009" | |
| }, | |
| { | |
| os: "debian", | |
| elixir: "1.18.4", | |
| otp: "28.1", | |
| build_os_ver: "bookworm-20250929-slim", | |
| prod_os_ver: "bookworm-slim" | |
| }, | |
| ] | |
| } | |
| - uses: actions/github-script@v8 | |
| id: test-matrix-parallel | |
| # Specify versions of Erlang, Elixir, and base OS | |
| # in a combination supported by https://hub.docker.com/r/hexpm/elixir/tags | |
| with: | |
| script: | | |
| let platforms = [ | |
| { | |
| os: "debian", | |
| elixir: "1.15.8", | |
| otp: "26.0.2", | |
| build_os_ver: "bookworm-20250811-slim", | |
| prod_os_ver: "bookworm-slim" | |
| }, | |
| ] | |
| let ci_nodes = [1, 2] | |
| let ci_node_total = ci_nodes.length | |
| let matrix = [] | |
| for (let i = 0; i < platforms.length; i++) { | |
| for (let j = 0; j < ci_nodes.length; j++) { | |
| matrix.push(Object.assign({}, platforms[i], {ci_node_total: ci_node_total, ci_node_index: ci_nodes[j]}) ) | |
| } | |
| } | |
| return { include: matrix } | |
| - uses: actions/github-script@v8 | |
| id: prod-matrix | |
| with: | |
| script: | | |
| return { | |
| include: [ | |
| { | |
| os: "alma", | |
| elixir: "1.18.4", | |
| otp: "28.1", | |
| build_os_ver: "8", | |
| prod_os_ver: "8" | |
| }, | |
| { | |
| os: "centos7", | |
| elixir: "1.18.4", | |
| otp: "28.1", | |
| build_os_ver: "7", | |
| prod_os_ver: "7" | |
| }, | |
| { | |
| os: "debian", | |
| elixir: "1.18.4", | |
| otp: "28.1", | |
| build_os_ver: "bookworm-20250929-slim", | |
| prod_os_ver: "bookworm-slim" | |
| }, | |
| ] | |
| } | |
| - uses: actions/github-script@v8 | |
| id: deploy-matrix | |
| with: | |
| script: | | |
| return { | |
| include: [ | |
| { | |
| os: "debian", | |
| elixir: "1.18.4", | |
| otp: "28.1", | |
| build_os_ver: "bookworm-20250929-slim", | |
| prod_os_ver: "bookworm-slim", | |
| }, | |
| ] | |
| } | |
| - uses: actions/github-script@v8 | |
| id: assets-matrix | |
| # Specify versions of Erlang, Elixir, and base OS | |
| # in a combination supported by https://hub.docker.com/r/hexpm/elixir/tags | |
| with: | |
| script: | | |
| return { | |
| include: [ | |
| { | |
| os: "debian", | |
| elixir: "1.18.4", | |
| otp: "28.1", | |
| build_os_ver: "bookworm-20250929-slim", | |
| prod_os_ver: "bookworm-slim", | |
| }, | |
| ] | |
| } | |
| create-release: | |
| name: Create GitHub release | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release-tag: build-${{ github.run_number }} | |
| upload-url: ${{ steps.release-view.outputs.upload-url }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_TAG: build-${{ github.run_number }} | |
| steps: | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| # - name: Tag repository | |
| # run: | | |
| # echo "$RELEASE_TAG" | |
| # git tag "$RELEASE_TAG" | |
| # git push origin "$RELEASE_TAG" | |
| - name: Create release | |
| if: ${{ github.run_attempt == '1' }} | |
| run: gh release create "$RELEASE_TAG" -n "Build $RELEASE_TAG" | |
| - name: Get release info | |
| id: release-view | |
| run: | | |
| UPLOAD_URL="$(gh release view "$RELEASE_TAG" --json uploadUrl --jq .uploadUrl | cut -d'{' -f'1')" | |
| echo "upload-url=$UPLOAD_URL" >> "$GITHUB_OUTPUT" | |
| build-test: | |
| name: Build test image | |
| needs: [config] | |
| permissions: | |
| # Interact with GitHub OIDC Token endpoint for AWS | |
| id-token: write | |
| contents: read | |
| # Push to ghcr.io repository | |
| packages: write | |
| runs-on: ubuntu-latest | |
| environment: ${{ needs.config.outputs.environment }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.config.outputs.test-matrix) }} | |
| env: | |
| DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| steps: | |
| # - name: Dump event | |
| # run: cat "$GITHUB_EVENT_PATH" | |
| # - name: Debug environment | |
| # run: | | |
| # echo "ref: ${{ github.ref }}" | |
| # echo "ref_name: ${{ github.ref_name }}" | |
| # echo "environment: ${{ needs.config.outputs.environment }}" | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| - name: Set variables | |
| shell: bash | |
| run: | | |
| echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> "$GITHUB_ENV" | |
| echo "run_id=${GITHUB_RUN_ID}" >> "$GITHUB_OUTPUT" | |
| echo "run_num=${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT" | |
| - name: Get branch name | |
| shell: bash | |
| run: echo "BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_ENV" | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| # https://github.com/marketplace/actions/docker-login | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # - name: Debug environment | |
| # # env.ACTIONS_STEP_DEBUG == 'true' | |
| # run: | | |
| # # echo "github.event_name: ${{ github.event_name }}" | |
| # # echo "ref: ${{ github.ref }}" | |
| # # echo "ref_name: ${{ github.ref_name }}" | |
| # # echo "environment: ${{ needs.config.outputs.environment }}" | |
| # # echo "components: ${{ needs.config.outputs.components }}" | |
| # # echo -n "AWS_ACCOUNT_ID: " | |
| # # echo ${{ secrets.AWS_ACCOUNT_ID }} | sed 's/./& /g' | |
| # # echo "AWS_REGION var: ${{ vars.AWS_REGION }}" | |
| # Pull public images without rate limits | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| # - name: Configure ssh keys | |
| # uses: webfactory/ssh-agent@v0.7.0 | |
| # # Configure machine key or deploy keys to access private repos from build | |
| # # https://github.com/marketplace/actions/webfactory-ssh-agent | |
| # # https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys | |
| # # ssh-keygen -t ed25519 -m pem -C "git@github.com:reachfh/api-utils.git" -f api-utils | |
| # with: | |
| # ssh-private-key: | | |
| # ${{ secrets.SSH_PRIVATE_KEY }} | |
| - name: Set env vars for builds | |
| run: | | |
| echo 'DATABASE_HOST=postgres' >> ./.env.test | |
| # - name: Set up QEMU for multi-platform builds | |
| # id: qemu | |
| # uses: docker/setup-qemu-action@v2 | |
| # with: | |
| # platforms: linux/amd64,linux/arm64 | |
| # - name: Display available platforms | |
| # run: echo "${{ steps.qemu.outputs.platforms }}" | |
| # - name: Configure AWS credentials | |
| # if: ${{ env.AWS_ENABLED == 1 }} | |
| # uses: aws-actions/configure-aws-credentials@v5 | |
| # # https://github.com/aws-actions/configure-aws-credentials | |
| # # https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services | |
| # with: | |
| # role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| # aws-region: ${{ vars.AWS_REGION }} | |
| # - name: Get test data files | |
| # run: | | |
| # mkdir -p test/bert | |
| # aws s3 cp s3://${{ vars.S3_BUCKET_CI }}/test-data.zip test-data.zip | |
| # unzip -o test-data.zip -d test/bert | |
| - name: Set up Docker buildx | |
| id: buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| driver-opts: network=host | |
| - name: Build test image and push to GHCR | |
| uses: docker/build-push-action@v6 | |
| with: | |
| file: ${{ env.DOCKER_FILE }} | |
| target: test-image | |
| build-args: | | |
| ELIXIR_VER=${{ matrix.elixir }} | |
| OTP_VER=${{ matrix.otp }} | |
| BUILD_OS_VER=${{ matrix.build_os_ver }} | |
| PROD_OS_VER=${{ matrix.prod_os_ver }} | |
| context: . | |
| builder: ${{ steps.buildx.outputs.name }} | |
| push: true | |
| cache-from: | | |
| type=registry,ref=ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:buildcache-test${{ env.VAR }}-${{ env.MAIN_BRANCH }} | |
| type=registry,ref=ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:buildcache-test${{ env.VAR }}-${{ github.ref_name }} | |
| cache-to: | | |
| type=registry,ref=ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:buildcache-test${{ env.VAR }}-${{ github.ref_name }},mode=max | |
| no-cache: ${{ github.run_attempt != '1' }} | |
| # platforms: linux/amd64,linux/arm64 | |
| # ssh: default | |
| tags: | | |
| ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:test${{ env.VAR }}${{ env.IMAGE_VER }} | |
| # secrets: | | |
| # "access_token=${{ secrets.DEVOPS_ACCESS_TOKEN }}" | |
| # "oban_key_fingerprint=${{ secrets.OBAN_KEY_FINGERPRINT }}" | |
| # "oban_license_key=${{ secrets.OBAN_LICENSE_KEY }}" | |
| test: | |
| name: Run tests | |
| needs: [build-test, config] | |
| permissions: | |
| packages: read | |
| # Upload JUnit report files | |
| # https://github.com/EnricoMi/publish-unit-test-result-action#permissions | |
| contents: read | |
| issues: read | |
| checks: write | |
| pull-requests: write | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.config.outputs.test-matrix-parallel) }} | |
| # ci_node_total: [2] | |
| # ci_node_index: [1, 2] | |
| env: | |
| DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # Pull public images without rate limits | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| - name: Pull repos | |
| run: | | |
| docker compose pull --quiet --include-deps test | |
| docker images --no-trunc | |
| - name: Start services | |
| run: docker compose up --detach test | |
| - name: Show docker logs | |
| if: failure() | |
| run: | | |
| docker compose logs --timestamps postgres | |
| docker compose logs --timestamps test | |
| docker compose ps --format json | jq . | |
| - name: Display health check results | |
| if: failure() | |
| run: | | |
| echo postgres | |
| docker inspect --format "{{json .State.Health }}" "$(docker compose ps -q postgres)" | jq | |
| - name: Debug env | |
| if: failure() | |
| run: | | |
| docker compose run test env | |
| # docker compose run test env PGPASSWORD=postgres /usr/bin/psql -w -h postgres -U postgres -d postgres -c "SELECT 1" | |
| # docker compose run test env PGPASSWORD=postgres /usr/lib/postgresql/13/bin/pg_isready -h postgres -p 5432 -d postgres -U postgres | |
| - name: Initialize test database | |
| run: docker compose run test mix 'do' ecto.setup | |
| - name: Run tests | |
| # continue-on-error: true | |
| # run: docker compose run test mix test --cover | |
| run: docker compose run test env MIX_TEST_PARTITION=${{ matrix.ci_node_index }} mix test --partitions ${{ matrix.ci_node_total }} | |
| # run: docker compose run test mix test | |
| - name: Run quality checks | |
| # run: docker compose run test mix 'do' format --check-formatted, deps.unlock --check-unused, credo --mute-exit-status --all, hex.audit, deps.audit, sobelow --exit --quiet --skip -i DOS.StringToAtom,Config.HTTPS | |
| run: docker compose run test mix 'do' format --check-formatted, deps.unlock --check-unused, credo --all, hex.audit, deps.audit, sobelow | |
| - name: Publish unit test results to GitHub | |
| uses: EnricoMi/publish-unit-test-result-action@v2 | |
| # Run even if tests fail | |
| if: (!cancelled()) | |
| with: | |
| # Volume mounted from local filesystem into build by docker compose | |
| files: junit-reports/*.xml | |
| # - name: Upload test results to Datadog | |
| # if: (!cancelled()) | |
| # continue-on-error: true | |
| # run: | | |
| # npm install -g @datadog/datadog-ci | |
| # datadog-ci junit upload --service api-graphql junit-reports/ | |
| test-dialyzer: | |
| name: Run dialyzer | |
| needs: [build-test, config] | |
| permissions: | |
| contents: read | |
| # Read from ghcr.io repository | |
| packages: read | |
| # Upload JUnit report files | |
| # https://github.com/EnricoMi/publish-unit-test-result-action#permissions | |
| checks: write | |
| pull-requests: write | |
| issues: read | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.config.outputs.test-matrix) }} | |
| env: | |
| DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| - name: Pull repos | |
| run: | | |
| docker compose pull --quiet test | |
| # docker images --no-trunc | |
| - name: Run dialyzer | |
| run: docker compose run test mix dialyzer --no-check --quiet-with-result --format github | |
| # run: docker compose run test mix dialyzer --no-check --ignore-exit-status --format github | |
| test-scan: | |
| name: Security scan code | |
| needs: [build-test] | |
| permissions: | |
| contents: read | |
| packages: read | |
| checks: write | |
| pull-requests: write | |
| issues: read | |
| # Upload SARIF report files | |
| security-events: write | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Checkout source | |
| uses: actions/checkout@v4 | |
| # with: | |
| # # Fetch all history for all branches and tags for Gitleaks | |
| # fetch-depth: 0 | |
| # - name: Manual Trivy Setup | |
| # uses: aquasecurity/setup-trivy@v0.2.1 | |
| # with: | |
| # cache: true | |
| # version: v0.52.2 | |
| - name: Scan code with trivy | |
| uses: aquasecurity/trivy-action@master | |
| # https://github.com/marketplace/actions/aqua-security-trivy | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: '.' | |
| ignore-unfixed: true | |
| severity: 'CRITICAL' | |
| # trivy-config: trivy.yaml | |
| format: 'table' | |
| # format: 'sarif' | |
| # format: ${{ env.GITHUB_ADVANCED_SECURITY == 1 && 'sarif' || 'table' }} | |
| # output: 'trivy-results.sarif' | |
| # skip-setup-trivy: true | |
| - name: Display scan results | |
| if: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }} | |
| run: cat trivy-results.sarif | jq . | |
| - name: Upload trivy scan results to GitHub Security tab | |
| if: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }} | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| category: trivy | |
| - name: Scan code with Grype | |
| uses: anchore/scan-action@v6 | |
| # https://github.com/marketplace/actions/anchore-container-scan | |
| id: scan-grype | |
| with: | |
| path: . | |
| output-format: table | |
| # output-format: 'sarif' | |
| # output-format: ${{ env.GITHUB_ADVANCED_SECURITY == 1 && 'sarif' || 'table' }} | |
| fail-build: false | |
| # severity-cutoff: critical | |
| - name: Display scan results | |
| if: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }} | |
| run: cat ${{ steps.scan-grype.outputs.sarif }} | jq . | |
| - name: Upload grype scan results to GitHub Security tab | |
| if: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }} | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: ${{ steps.scan-grype.outputs.sarif }} | |
| category: grype | |
| # - name: Scan with Gitleaks | |
| # uses: gitleaks/gitleaks-action@v2 | |
| # env: | |
| # GITHUB_TOKEN: ${{ github.token }} | |
| # GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} # Only required for Organizations, not personal accounts. | |
| # # upload sarif artifact when secrets are detected | |
| # GITLEAKS_ENABLE_UPLOAD_ARTIFACT: true | |
| build-prod: | |
| name: Build prod image | |
| needs: [config] | |
| permissions: | |
| # Interact with GitHub OIDC Token endpoint for AWS | |
| id-token: write | |
| contents: read | |
| # Push to ghcr.io repository | |
| packages: write | |
| runs-on: ubuntu-latest | |
| environment: ${{ needs.config.outputs.environment }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.config.outputs.prod-matrix) }} | |
| env: | |
| DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| # MIX_ENV: prod | |
| steps: | |
| - name: Dump event | |
| run: cat "$GITHUB_EVENT_PATH" | |
| - name: Debug environment | |
| run: | | |
| echo "github.event_name: ${{ github.event_name }}" | |
| echo "ref: ${{ github.ref }}" | |
| echo "ref_name: ${{ github.ref_name }}" | |
| echo "environment: ${{ needs.config.outputs.environment }}" | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Configure AWS credentials | |
| if: needs.config.outputs.aws-enabled | |
| uses: aws-actions/configure-aws-credentials@v5 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| aws-region: ${{ vars.AWS_REGION }} | |
| - name: Log in to Amazon ECR | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| id: ecr-login | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| - name: Set vars | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> "$GITHUB_ENV" | |
| - name: Set vars | |
| run: echo "NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV" | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| - name: Set variables | |
| id: vars | |
| shell: bash | |
| run: | | |
| echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> "$GITHUB_ENV" | |
| echo "run_id=${GITHUB_RUN_ID}" >> "$GITHUB_OUTPUT" | |
| echo "run_num=${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT" | |
| - name: Get branch name | |
| shell: bash | |
| run: echo "BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_ENV" | |
| # - name: Configure ssh keys | |
| # uses: webfactory/ssh-agent@v0.7.0 | |
| # # https://github.com/marketplace/actions/webfactory-ssh-agent | |
| # # https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys | |
| # # ssh-keygen -t ed25519 -m pem -C "git@github.com:reachfh/api-utils.git" -f api-utils | |
| # with: | |
| # ssh-private-key: | | |
| # ${{ secrets.SSH_PRIVATE_KEY }} | |
| # Build assets on host and copy into container | |
| # Doing it outside of the prod image speeds up the build, as we don't | |
| # have to copy big static files around. | |
| # We do it here instead of a separate task, as we need | |
| # priv/static/cache_manifest.json in the build. | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 18 | |
| - name: Enable corepack | |
| run: corepack enable | |
| - name: Install Erlang and Elixir | |
| uses: erlef/setup-beam@v1 | |
| with: | |
| otp-version: ${{ matrix.otp }} | |
| elixir-version: ${{ matrix.elixir }} | |
| - name: Cache deps | |
| id: cache-deps | |
| uses: actions/cache@v4 | |
| env: | |
| cache-name: cache-elixir-deps-${{ github.ref_name }} | |
| with: | |
| path: | | |
| **/deps | |
| key: | | |
| ${{runner.os}}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }} | |
| restore-keys: | | |
| ${{runner.os}}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}- | |
| - name: Cache compiled build | |
| id: cache-build | |
| uses: actions/cache@v4 | |
| env: | |
| cache-name: cache-compiled-build-${{ github.ref_name }} | |
| with: | |
| path: | | |
| **/_build | |
| key: | | |
| ${{runner.os}}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}-${{ hashFiles( '**/lib/**/*.{ex,eex}', '**/config/*.exs', '**/mix.exs' ) }} | |
| restore-keys: | | |
| ${{runner.os}}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}- | |
| ${{runner.os}}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}- | |
| - name: Cache node-modules | |
| id: node-modules | |
| uses: actions/cache@v4 | |
| env: | |
| cache-name: cache-node-modules-${{ github.ref_name }} | |
| with: | |
| path: assets/node_modules | |
| key: | | |
| ${{runner.os}}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: | | |
| ${{runner.os}}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}- | |
| - name: Install build tools | |
| run: mix 'do' local.rebar --force, local.hex --force | |
| # Force full recompile on builds that are retried. | |
| - name: Clean deps to avoid flaky incremental builds | |
| if: ${{ github.run_attempt != '1' }} | |
| run: | | |
| mix deps.clean --all | |
| mix clean | |
| shell: sh | |
| - name: Get Elixir deps | |
| run: mix deps.get | |
| - name: Compile deps | |
| run: mix deps.compile | |
| - name: Compile | |
| run: mix compile | |
| - name: Setup | |
| run: | | |
| mkdir -p ./assets | |
| mix assets.setup | |
| # - name: npm install | |
| # working-directory: assets | |
| # run: npm install | |
| # - name: npm run deploy | |
| # working-directory: assets | |
| # run: npm run deploy | |
| # - name: yarn install | |
| # working-directory: assets | |
| # run: yarn install --prod | |
| # - name: Build digested assets | |
| # run: mix phx.digest --no-vsn | |
| - name: Build and deploy assets | |
| run: mix assets.deploy | |
| # Copying is faster than syncing. With digested files, multiple versions | |
| # can coexist on the server, and the app will choose the right ones. | |
| - name: Copy assets to S3 | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| run: aws s3 cp --recursive priv/static s3://${{ vars.S3_BUCKET_ASSETS }}/ | |
| # - name: Sync to S3 | |
| # working-directory: app | |
| # run: aws s3 sync priv/static s3://${{ vars.S3_BUCKET_ASSETS }} --delete | |
| # We don't have to invalidate with using digested files. | |
| # - name: Create CloudFront invalidation | |
| # run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_CDN_DISTRIBUTION_ID }} --paths "/*" | |
| - name: Set up Docker buildx | |
| id: buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| driver-opts: network=host | |
| - name: Build prod image and push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| file: ${{ env.DOCKER_FILE }} | |
| target: prod | |
| build-args: | | |
| ELIXIR_VER=${{ matrix.elixir }} | |
| OTP_VER=${{ matrix.otp }} | |
| BUILD_OS_VER=${{ matrix.build_os_ver }} | |
| PROD_OS_VER=${{ matrix.prod_os_ver }} | |
| BUILD_NUM=${{ github.run_number }} | |
| context: . | |
| builder: ${{ steps.buildx.outputs.name }} | |
| push: true | |
| cache-from: | | |
| type=registry,ref=ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:buildcache-prod${{ env.VAR }}-${{ env.MAIN_BRANCH }} | |
| type=registry,ref=ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:buildcache-prod${{ env.VAR }}-${{ github.ref_name }} | |
| cache-to: | | |
| type=registry,ref=ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:buildcache-prod${{ env.VAR }}-${{ github.ref_name }},mode=max | |
| no-cache: ${{ github.run_attempt != '1' }} | |
| # ssh: default | |
| # https://github.com/opencontainers/image-spec/blob/main/annotations.md | |
| # https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/ | |
| labels: | | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| org.opencontainers.image.created=${{ env.NOW }} | |
| org.opencontainers.image.revision=${{ github.sha }} | |
| org.opencontainers.image.version=${{ github.run_number }} | |
| app.kubernetes.io/name=foo-app | |
| app.kubernetes.io/part-of=${{ github.repository }} | |
| app.kubernetes.io/version=${{ github.run_number }} | |
| tags: | | |
| ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ github.sha }} | |
| ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }} | |
| ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH }}-${{ env.GITHUB_SHA_SHORT }} | |
| # ${{ env.ECR_REGISTRY }}/${{ env.ECR_IMAGE_OWNER }}${{ env.IMAGE_NAME }}:${{ github.sha }} | |
| # ${{ env.ECR_REGISTRY }}/${{ env.ECR_IMAGE_OWNER }}${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }} | |
| # ${{ env.ECR_REGISTRY }}/${{ env.ECR_IMAGE_OWNER }}${{ env.IMAGE_NAME }}:${{ env.BRANCH }}-${{ env.GITHUB_SHA_SHORT }} | |
| # secrets: | | |
| # "access_token=${{ secrets.DEVOPS_ACCESS_TOKEN }}" | |
| # "oban_key_fingerprint=${{ secrets.OBAN_KEY_FINGERPRINT }}" | |
| # "oban_license_key=${{ secrets.OBAN_LICENSE_KEY }}" | |
| test-prod: | |
| name: Run external API tests | |
| needs: [build-prod, config] | |
| permissions: | |
| id-token: write | |
| packages: read | |
| contents: read | |
| issues: read | |
| checks: write | |
| pull-requests: write | |
| runs-on: ubuntu-latest | |
| environment: ${{ needs.config.outputs.environment }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.config.outputs.prod-matrix) }} | |
| env: | |
| DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| # GH_TOKEN: ${{ github.token }} | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| # - name: Configure AWS credentials | |
| # if: ${{ env.AWS_ENABLED == 1 }} | |
| # uses: aws-actions/configure-aws-credentials@v5 | |
| # # https://github.com/aws-actions/configure-aws-credentials | |
| # # https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services | |
| # with: | |
| # role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| # aws-region: ${{ vars.AWS_REGION }} | |
| # - name: Get test data files | |
| # run: | | |
| # mkdir -p test-data | |
| # aws s3 cp s3://${{ vars.S3_BUCKET_CI }}/test-data.zip test-data.zip | |
| # unzip -o test-data.zip -d test-data | |
| - name: Pull repos | |
| # Pull images separately to make timing clearer | |
| # run: echo "newman postgres router prod" | xargs -n 1 -P 8 docker compose pull --quiet | |
| # docker compose pull --quiet newman | |
| # docker images --no-trunc | |
| run: | | |
| docker compose pull --quiet --include-deps prod | |
| - name: Start services | |
| env: | |
| LOG_CONSOLE: true | |
| LOG_LEVEL: debug | |
| run: | | |
| env | sort | |
| docker compose up --wait prod | |
| - name: Display logs | |
| if: failure() | |
| run: | | |
| echo "ps" | |
| docker compose ps --format json | jq . | |
| echo "postgres logs" | |
| docker compose logs --timestamps postgres | |
| echo "prod logs" | |
| docker compose logs --timestamps prod | |
| echo "postgres health" | |
| docker inspect --format "{{json .State.Health }}" "$(docker compose ps -q postgres)" | jq | |
| echo "prod health" | |
| docker inspect --format "{{json .State.Health }}" "$(docker compose ps -q prod)" | jq | |
| # - name: Debug env | |
| # if: failure() | |
| # run: | | |
| # docker compose run --entrypoint /bin/bash prod env | |
| # docker compose run --entrypoint /bin/bash prod env PGPASSWORD=postgres /usr/bin/psql -w -h postgres -U postgres -d postgres -c "SELECT 1" | |
| # # docker compose run --entrypoint /bin/bash prod pg_isready -h postgres -p 5432 -d postgres -U postgres | |
| # - name: Initialize database | |
| # run: | | |
| # # Rundandant with bin/start-docker | |
| # # docker compose run prod eval '${{ env.ELIXIR_MODULE }}.Release.create_repos()' | |
| # # docker compose run prod eval '${{ env.ELIXIR_MODULE }}.Release.migrate()' | |
| # docker compose run --entrypoint bin/prod prod eval '${{ env.ELIXIR_MODULE }}.Release.run_seeds()' | |
| # Basic smoke test to make sure service came up | |
| - name: Run health check | |
| run: for _i in 1 2 3 4 5 6 7 8 9 10; do curl -v "http://localhost:${APP_PORT}/healthz" && break || sleep 1; done | |
| - name: Display logs | |
| if: failure() | |
| run: | | |
| echo "postgres logs" | |
| docker compose logs --timestamps postgres | |
| echo "postgres health" | |
| docker inspect --format "{{json .State.Health }}" "$(docker compose ps -q postgres)" | jq | |
| echo "prod logs" | |
| docker compose logs --timestamps prod | |
| echo "prod health" | |
| docker inspect --format "{{json .State.Health }}" "$(docker compose ps -q prod)" | jq | |
| echo "ps" | |
| docker compose ps --format json | jq . | |
| # - name: Run API tests | |
| # env: | |
| # NEWMAN_ARGS: "--verbose -r cli,junitfull --reporter-junitfull-export /junit-reports/newman.xml -e ci.json" | |
| # # NEWMAN_ARGS: "--verbose -r cli,junit --reporter-junit-export /junit-reports/newman.xml -e ci.json" | |
| # # https://hub.docker.com/r/postman/newman/ | |
| # run: | | |
| # mkdir -p junit-reports | |
| # docker compose run newman run ${{env.NEWMAN_ARGS}} Queries.postman_collection.json | |
| # - name: Display logs | |
| # if: failure() | |
| # run: | | |
| # echo "postgres logs" | |
| # docker compose logs --timestamps postgres | |
| # echo "postgres health" | |
| # docker inspect --format "{{json .State.Health }}" $(docker compose ps -q postgres) | jq | |
| # echo "prod logs" | |
| # docker compose logs --timestamps prod | |
| # echo "prod health" | |
| # docker inspect --format "{{json .State.Health }}" $(docker compose ps -q prod) | jq | |
| # echo "ps" | |
| # docker compose ps --format json | jq . | |
| # docker compose logs --timestamps newman | |
| # - name: Publish unit test results to GitHub | |
| # uses: EnricoMi/publish-unit-test-result-action@v2 | |
| # # Run even if tests fail | |
| # if: (!cancelled()) | |
| # with: | |
| # # Volume mounted from local filesystem into build | |
| # junit_files: ./junit-reports/*.xml | |
| # check_name: "External API Tests" | |
| # - name: Copy CodeDeploy release files from container | |
| # run: | | |
| # UPLOAD_URL=${{ needs.create-release.outputs.upload-url }} | |
| # NAME="codedeploy-revision-${{ github.run_number }}-${{ env.VAR }}.zip" | |
| # COMMAND="curl -S -X POST -H 'authorization: Bearer ${{ github.token }}' \ | |
| # -H 'content-type: application/octet-stream' -H 'accept: application/vnd.github+json' \ | |
| # --data-binary @/revision.zip \"${UPLOAD_URL}?name=${NAME}\" " | |
| # echo "$COMMAND" | |
| # docker compose run --entrypoint /bin/sh prod -c "$COMMAND" | |
| scan: | |
| name: Security scan prod image | |
| needs: [build-prod, config] | |
| permissions: | |
| id-token: write | |
| contents: read | |
| packages: read | |
| checks: write | |
| pull-requests: write | |
| issues: read | |
| # Upload SARIF report files | |
| security-events: write | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.config.outputs.prod-matrix) }} | |
| env: | |
| DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Pull image | |
| run: docker pull "ghcr.io/${IMAGE_OWNER}/${IMAGE_NAME}:${VAR}${IMAGE_VER}" | |
| - name: Scan image with Trivy | |
| uses: aquasecurity/trivy-action@0.28.0 | |
| # https://github.com/aquasecurity/trivy-action | |
| # https://github.com/marketplace/actions/aqua-security-trivy#inputs | |
| with: | |
| image-ref: ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }} | |
| # exit-code: '1' # fail build | |
| # ignore-unfixed: true | |
| # vuln-type: 'os,library' | |
| # severity: 'CRITICAL,HIGH' | |
| # cache-dir: /var/cache | |
| # format: 'sarif' | |
| # format: 'table' | |
| format: ${{ env.GITHUB_ADVANCED_SECURITY == 1 && 'sarif' || 'table' }} | |
| # output: ${{ env.GITHUB_ADVANCED_SECURITY == 1 && 'trivy-results.sarif' }} | |
| - name: Display scan results | |
| if: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }} | |
| run: cat trivy-results.sarif | jq . | |
| - name: Upload Trivy scan results to GitHub Security tab | |
| if: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }} | |
| uses: github/codeql-action/upload-sarif@v3 | |
| # Requires GitHub Advanced Security | |
| # https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security | |
| # https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning | |
| # https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| category: trivy | |
| - name: Scan image with Grype | |
| uses: anchore/scan-action@v6 | |
| # https://github.com/marketplace/actions/anchore-container-scan | |
| id: scan-grype | |
| with: | |
| image: ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }} | |
| # severity-cutoff: critical | |
| fail-build: false | |
| # output-format: 'sarif' | |
| output-format: table | |
| # output-format: ${{ env.GITHUB_ADVANCED_SECURITY == 1 && 'sarif' || 'table' }} | |
| - name: Display scan results | |
| if: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }} | |
| run: cat ${{ steps.scan-grype.outputs.sarif }} | jq . | |
| - name: Upload Grype scan results to GitHub Security tab | |
| if: ${{ always() && env.GITHUB_ADVANCED_SECURITY == 1 }} | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: ${{ steps.scan-grype.outputs.sarif }} | |
| category: grype | |
| # - name: Scan image with Snyk | |
| # # if: github.event_name != 'pull_request' | |
| # uses: snyk/actions/docker@master | |
| # continue-on-error: true | |
| # env: | |
| # SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| # with: | |
| # command: test | |
| # image: ghcr.io/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.VAR }}${{ env.IMAGE_VER }} | |
| # args: --file=${{ env.DOCKER_FILE }} --project-name=api | |
| # upload-assets: | |
| # name: Upload assets to CDN | |
| # # Extract assets from container | |
| # if: github.event_name != 'pull_request' | |
| # # if: contains(github.ref, 'refs/heads/main') | |
| # # needs: [build-prod, test-prod, test] | |
| # # needs: [test-prod, test, test-dialyzer] | |
| # needs: [build-prod] | |
| # permissions: | |
| # # Interact with GitHub OIDC Token endpoint for AWS | |
| # id-token: write | |
| # contents: read | |
| # # Push to ghcr.io repository | |
| # packages: write | |
| # # Upload JUnit report files | |
| # # https://github.com/EnricoMi/publish-unit-test-result-action#permissions | |
| # checks: write | |
| # pull-requests: write | |
| # issues: read | |
| # runs-on: ubuntu-latest | |
| # environment: ${{ needs.config.outputs.environment }} | |
| # strategy: | |
| # fail-fast: false | |
| # matrix: ${{ fromJson(needs.config.outputs.assets-matrix) }} | |
| # env: | |
| # MIX_ENV: prod | |
| # steps: | |
| # - name: Debug environment | |
| # run: | | |
| # echo "ref_name: ${{ github.ref_name }}" | |
| # echo "environment: ${{ github.event.deployment.environment }}" | |
| # | |
| # - name: Log in to GHCR | |
| # uses: docker/login-action@v3 | |
| # with: | |
| # registry: ghcr.io | |
| # username: ${{ github.actor }} | |
| # password: ${{ secrets.GITHUB_TOKEN }} | |
| # | |
| # - name: Configure AWS credentials | |
| # if: env.AWS_ENABLED == 1 | |
| # uses: aws-actions/configure-aws-credentials@v5 | |
| # with: | |
| # role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| # aws-region: ${{ vars.AWS_REGION }} | |
| # | |
| # - name: Check out source | |
| # uses: actions/checkout@v4 | |
| # | |
| # - name: Set up Docker buildx | |
| # id: buildx | |
| # uses: docker/setup-buildx-action@v3 | |
| # with: | |
| # driver-opts: network=host | |
| # | |
| # - name: Get build artifacts on host | |
| # uses: docker/build-push-action@v6 | |
| # with: | |
| # file: ${{ env.DOCKER_FILE }} | |
| # target: artifacts | |
| # build-args: | | |
| # ELIXIR_VER=${{ matrix.elixir }} | |
| # OTP_VER=${{ matrix.otp }} | |
| # BUILD_OS_VER=${{ matrix.build_os_ver }} | |
| # PROD_OS_VER=${{ matrix.prod_os_ver }} | |
| # context: . | |
| # builder: ${{ steps.buildx.outputs.name }} | |
| # push: false | |
| # cache-from: type=gha,scope=${{ github.workflow }}-${{ env.VAR }} | |
| # outputs: type=local,dest=output | |
| # | |
| # - name: List output dir | |
| # run: find output/static -print | |
| # | |
| # - name: Sync to S3 | |
| # run: aws s3 sync output/static/ s3://${{ vars.S3_BUCKET_ASSETS }} | |
| # | |
| # - name: Create CloudFront invalidation | |
| # run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_CDN_DISTRIBUTION_ID }} --paths "/*" | |
| # upload-assets: | |
| # name: Build and upload assets to CDN | |
| # needs: [config] | |
| # # Build assets in host instead of Docker for better performance. | |
| # if: ${{ github.event_name != 'pull_request' }} | |
| # permissions: | |
| # # Interact with GitHub OIDC Token endpoint for AWS | |
| # id-token: write | |
| # contents: read | |
| # # Push to ghcr.io repository | |
| # packages: write | |
| # # Upload JUnit report files | |
| # # https://github.com/EnricoMi/publish-unit-test-result-action#permissions | |
| # checks: write | |
| # pull-requests: write | |
| # issues: read | |
| # runs-on: ubuntu-latest | |
| # environment: ${{ needs.config.outputs.environment }} | |
| # strategy: | |
| # fail-fast: false | |
| # matrix: ${{ fromJson(needs.config.outputs.assets-matrix) }} | |
| # env: | |
| # MIX_ENV: prod | |
| # steps: | |
| # - name: Check out source | |
| # uses: actions/checkout@v4 | |
| # | |
| # - uses: actions/setup-node@v4 | |
| # with: | |
| # node-version: 18 | |
| # | |
| # - name: Enable corepack | |
| # run: corepack enable | |
| # | |
| # - name: Install Erlang and Elixir | |
| # uses: erlef/setup-beam@v1 | |
| # with: | |
| # otp-version: ${{ matrix.otp }} | |
| # elixir-version: ${{ matrix.elixir }} | |
| # | |
| # - name: Cache deps | |
| # id: cache-deps | |
| # uses: actions/cache@v4 | |
| # env: | |
| # cache-name: cache-elixir-deps | |
| # with: | |
| # path: deps | |
| # key: ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }} | |
| # restore-keys: | | |
| # ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}- | |
| # | |
| # - name: Cache compiled build | |
| # id: cache-build | |
| # uses: actions/cache@v4 | |
| # env: | |
| # cache-name: cache-compiled-build | |
| # with: | |
| # path: _build | |
| # key: ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }} | |
| # restore-keys: | | |
| # ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}- | |
| # | |
| # - name: Cache node-modules | |
| # id: node-modules | |
| # uses: actions/cache@v4 | |
| # env: | |
| # cache-name: cache-node-modules | |
| # with: | |
| # path: assets/node_modules | |
| # key: ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} | |
| # restore-keys: | | |
| # ${{ runner.os }}-${{matrix.otp}}-${{matrix.elixir}}-mix-${{ env.cache-name }}- | |
| # | |
| # - name: Install build tools | |
| # run: mix do local.rebar --force, local.hex --force | |
| # | |
| # # Force full recompile on builds that are retried. | |
| # - name: Clean deps to avoid flaky incremental builds | |
| # if: github.run_attempt != '1' | |
| # run: | | |
| # mix deps.clean --all | |
| # mix clean | |
| # shell: sh | |
| # | |
| # - name: Get Elixir deps | |
| # run: mix deps.get | |
| # | |
| # - name: Compile | |
| # run: mix compile | |
| # | |
| # # - name: Install JavaScript libs | |
| # # # working-directory: assets | |
| # # # run: npm install && npm run deploy | |
| # # run: yarn --cwd ./assets install --prod | |
| # | |
| # - name: Deploy assets | |
| # run: mix assets.deploy | |
| # | |
| # - name: Configure AWS credentials | |
| # if: env.AWS_ENABLED == 1 | |
| # uses: aws-actions/configure-aws-credentials@v4 | |
| # with: | |
| # role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| # aws-region: ${{ vars.AWS_REGION }} | |
| # | |
| # - name: Sync to S3 | |
| # run: aws s3 sync priv/static s3://${{ vars.S3_BUCKET_ASSETS }} | |
| # | |
| # - name: Create CloudFront invalidation | |
| # run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_CDN_DISTRIBUTION_ID }} --paths "/*" | |
| deploy-ecs: | |
| name: Deploy to AWS ECS | |
| if: github.event_name != 'pull_request' | |
| # if: contains(github.ref, 'refs/heads/main') | |
| # needs: [test-prod, test, test-dialyzer, upload-assets, config] | |
| needs: [test-prod, test, test-dialyzer, config] | |
| permissions: | |
| # Interact with GitHub OIDC Token endpoint for AWS | |
| id-token: write | |
| contents: read | |
| runs-on: ubuntu-latest | |
| environment: ${{ needs.config.outputs.environment }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.config.outputs.deploy-matrix) }} | |
| env: | |
| DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| steps: | |
| - name: Configure AWS credentials | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| uses: aws-actions/configure-aws-credentials@v5 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| aws-region: ${{ vars.AWS_REGION }} | |
| - name: Log in to Amazon ECR | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| id: ecr-login | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| - name: Set vars | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> "$GITHUB_ENV" | |
| - name: Set vars | |
| shell: bash | |
| run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> "$GITHUB_ENV" | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| - name: Set vars for runtime info | |
| run: | | |
| echo "GIT_AUTHOR=$(git log -1 --pretty=format:'%ae')" >> "$GITHUB_ENV" | |
| # https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-resources.html | |
| # - name: Generate CodeDeploy appspec.yml | |
| # env: | |
| # CONTAINER_NAME: "foo-app" | |
| # PORT: "4000" | |
| # run: sed -i -e "s!<NAME>!${CONTAINER_NAME}!g" -e "s!<PORT>!${PORT}!g" ecs/appspec.yml | |
| - name: Generate ECS task-defintion.json | |
| env: | |
| AWSLOGS_REGION: ${{ vars.AWS_REGION }} | |
| AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} | |
| AWS_PS_PREFIX: ${{ vars.AWS_PS_PREFIX }} | |
| EXECUTION_ROLE_ARN: ${{ secrets.EXECUTION_ROLE_ARN}} | |
| TASK_ROLE_ARN: ${{ secrets.TASK_ROLE_ARN }} | |
| CPU: ${{ env.ECS_CPU }} | |
| RAM: ${{ env.ECS_RAM }} | |
| APP_IMAGE: "${ECR_REGISTRY}/${ECR_IMAGE_OWNER}${IMAGE_NAME}:${VAR}${IMAGE_VER}" | |
| # OTEL_IMAGE: "public.ecr.aws/aws-observability/aws-otel-collector:${{ vars.AWS_OTEL_VER }}" | |
| run: jq --null-input -f "${TASKDEF}.jq" | tee "$TASKDEF" | |
| - name: Put new image ID in ECS task definition | |
| id: task-def | |
| uses: aws-actions/amazon-ecs-render-task-definition@v1 | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| env: | |
| IMAGE: "${ECR_REGISTRY}/${ECR_IMAGE_OWNER}${IMAGE_NAME}:${VAR}${IMAGE_VER}" | |
| with: | |
| # https://github.com/aws-actions/amazon-ecs-render-task-definition | |
| task-definition: ${{ env.TASKDEF }} | |
| # task-definition-arn: ${{ secrets.TASK_ROLE_ARN }} | |
| # task-definition-family: ${{ env.ECS_FAMILY }} | |
| container-name: ${{ env.ECS_CONTAINER }} | |
| # This should match the image tag used in build-prod | |
| image: ${{env.IMAGE}} | |
| log-configuration-options: | | |
| awslogs-create-group=true | |
| awslogs-group=/ecs/${{ env.ECS_SERVICE }} | |
| awslogs-region=${{ env.AWSLOGS_REGION }} | |
| awslogs-stream-prefix=${{ env.ECS_SERVICE }} | |
| # environment-variables: | | |
| # ENV=${{ (github.ref == 'refs/heads/main' && 'staging') || (github.ref == 'refs/heads/qa' && 'qa') || 'staging | |
| - name: Deploy to Amazon ECS | |
| uses: aws-actions/amazon-ecs-deploy-task-definition@v2 | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| with: | |
| task-definition: ${{ steps.task-def.outputs.task-definition }} | |
| cluster: ${{ env.ECS_CLUSTER }} | |
| service: ${{ env.ECS_SERVICE }} | |
| # wait-for-service-stability: true | |
| # codedeploy-appspec: ${{ env.APPSPEC }} | |
| # codedeploy-application: ${{ env.CODEDEPLOY_APPLICATION }} | |
| # codedeploy-deployment-group: ${{ env.CODEDEPLOY_DEPLOYMENT_GROUP }} | |
| - name: Generate ECS task-defintion.json (worker) | |
| env: | |
| AWSLOGS_REGION: ${{ vars.AWS_REGION }} | |
| AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} | |
| AWS_PS_PREFIX: ${{ vars.AWS_PS_PREFIX }} | |
| EXECUTION_ROLE_ARN: ${{ secrets.EXECUTION_ROLE_ARN}} | |
| TASK_ROLE_ARN: ${{ secrets.TASK_ROLE_ARN }} | |
| CPU: ${{ env.ECS_CPU_WORKER }} | |
| RAM: ${{ env.ECS_RAM_WORKER }} | |
| run: jq --null-input -f "${TASKDEF_WORKER}.jq" | tee "$TASKDEF_WORKER" | |
| - name: Put new image ID in ECS task definition (worker) | |
| id: task-def-worker | |
| uses: aws-actions/amazon-ecs-render-task-definition@v1 | |
| with: | |
| task-definition: ${{ env.TASKDEF_WORKER }} | |
| container-name: ${{ env.ECS_CONTAINER_WORKER }} | |
| image: ${{env.ECR_REGISTRY}}/${{env.ECR_IMAGE_OWNER}}${{env.IMAGE_NAME}}:${{env.VAR}}${{env.IMAGE_VER}} | |
| - name: Deploy to Amazon ECS (worker) | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| uses: aws-actions/amazon-ecs-deploy-task-definition@v2 | |
| with: | |
| task-definition: ${{ steps.task-def-worker.outputs.task-definition }} | |
| cluster: ${{ env.ECS_CLUSTER }} | |
| service: ${{ env.ECS_SERVICE_WORKER }} | |
| # - name: Run CodeBuild | |
| # uses: aws-actions/aws-codebuild-run-build@v1 | |
| # with: | |
| # project-name: "foo-app" | |
| # # buildspec-override: ecs/buildspec-docker.yml | |
| # env-vars-for-codebuild: | | |
| # custom, | |
| # requester, | |
| # event-name | |
| # env: | |
| # custom: my environment variable | |
| # requester: ${{ github.actor }} | |
| # event-name: ${{ github.event_name }} | |
| - name: Create AppSignal revision | |
| env: | |
| APPSIGNAL_APP_ENV: ${{ (github.ref_name == 'main' && 'stage') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'prod') }} | |
| # APPSIGNAL_APP_ENV: ${{ (github.ref == 'refs/tags/prod' && 'prod') || (github.ref_name == 'main' && 'stage') || 'stage' }} | |
| APPSIGNAL_APP_NAME: ${{ env.ECS_SERVICE }} | |
| APPSIGNAL_PUSH_API_KEY: ${{ secrets.APPSIGNAL_PUSH_API_KEY }} | |
| REVISION: ${{ env.GITHUB_SHA_SHORT }} | |
| # REVISION: ${{ env.GITHUB_SHA }} | |
| if: env.AWS_ENABLED == 1 | |
| run: | | |
| printf '{"revision":"%s","user":"GitHub"}' "$REVISION" | tee revision.json | |
| APPSIGNAL_URL=$(printf 'https://push.appsignal.com/1/markers?api_key=%s&name=%s&environment=%s' "$APPSIGNAL_PUSH_API_KEY" "$APPSIGNAL_APP_NAME" "$APPSIGNAL_APP_ENV") | |
| export APPSIGNAL_URL | |
| # CodeBuild has an insane conflict with YAML syntax and the URL, so put it in a file | |
| echo "url = \"$APPSIGNAL_URL\"" | tee curl.txt | |
| curl -g --config curl.txt -d @revision.json | |
| tag: | |
| name: Tag prod images as latest | |
| if: github.event_name != 'pull_request' | |
| # if: contains(github.ref, 'refs/heads/main') | |
| needs: [deploy-ecs, config] | |
| # needs: [test-prod, test, test-dialyzer] | |
| permissions: | |
| id-token: write | |
| contents: read | |
| packages: write | |
| runs-on: ubuntu-latest | |
| environment: ${{ needs.config.outputs.environment }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.config.outputs.deploy-matrix) }} | |
| env: | |
| DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Configure AWS credentials | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| uses: aws-actions/configure-aws-credentials@v5 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| aws-region: ${{ vars.AWS_REGION }} | |
| - name: Log in to Amazon ECR | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| id: ecr-login | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| - name: Set vars | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> "$GITHUB_ENV" | |
| - name: Get branch name | |
| shell: bash | |
| run: echo "BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_ENV" | |
| # - name: Set vars | |
| # env: | |
| # IMAGE_TAG: ${{ needs.config.outputs.image-tag }} | |
| # run: echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" | |
| # - name: Check out source | |
| # uses: actions/checkout@v4 | |
| - name: Tag GHCR release as latest | |
| run: | | |
| skopeo copy \ | |
| "docker://ghcr.io/${IMAGE_OWNER}/${IMAGE_NAME}:${VAR}${IMAGE_VER}" \ | |
| "docker://ghcr.io/${IMAGE_OWNER}/${IMAGE_NAME}:${IMAGE_TAG}" | |
| # skopeo copy --multi-arch=index-only \ | |
| # "docker://ghcr.io/${IMAGE_OWNER}/${IMAGE_NAME}:${VAR}${IMAGE_VER}" \ | |
| # "docker://ghcr.io/${IMAGE_OWNER}/${IMAGE_NAME}:${IMAGE_TAG}" | |
| - name: Tag ECR release as latest | |
| if: ${{ env.AWS_ENABLED == 1 }} | |
| run: | | |
| MANIFEST=$(aws ecr batch-get-image --repository-name "${ECR_IMAGE_OWNER}${IMAGE_NAME}" \ | |
| --image-ids imageTag="${VAR}${IMAGE_VER}" \ | |
| --output json | jq --raw-output --join-output '.images[0].imageManifest') | |
| export MANIFEST | |
| aws ecr put-image --repository-name "${ECR_IMAGE_OWNER}${IMAGE_NAME}" \ | |
| --image-tag "$IMAGE_TAG" --image-manifest "$MANIFEST" | |
| # Requires higher permissions | |
| # aws ecr describe-images --repository-name "${ECR_IMAGE_OWNER}${IMAGE_NAME}" | |
| # deploy-codedeploy: | |
| # name: Deploy using CodeDeploy | |
| # # if: ${{ env.AWS_ENABLED == 1 }} | |
| # # needs: [test-prod, test, test-dialyzer, upload-assets, config] | |
| # needs: [test, test-dialyzer, test-prod, config, create-release] | |
| # permissions: | |
| # # Interact with GitHub OIDC Token endpoint for AWS | |
| # id-token: write | |
| # contents: read | |
| # # Push to ghcr.io repository | |
| # packages: write | |
| # runs-on: ubuntu-latest | |
| # environment: ${{ (github.ref_name == 'main' && 'staging') || (github.ref_name == 'qa' && 'qa') || (github.ref_name == 'prod' && 'production') }} | |
| # # environment: ${{ (github.ref == 'refs/tags/prod' && 'prod') || (github.ref_name == 'main' && 'staging') || 'staging' }} | |
| # strategy: | |
| # fail-fast: false | |
| # matrix: ${{ fromJson(needs.config.outputs.deploy-matrix) }} | |
| # env: | |
| # DOCKER_FILE: deploy/${{ matrix.os }}.Dockerfile | |
| # VAR: ${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.os }}-${{ matrix.build_os_ver }} | |
| # GH_TOKEN: ${{ github.token }} | |
| # RELEASE_TAG: ${{ needs.create-release.outputs.release-tag }} | |
| # steps: | |
| # - name: Configure AWS credentials | |
| # if: ${{ env.AWS_ENABLED == 1 }} | |
| # uses: aws-actions/configure-aws-credentials@v4 | |
| # with: | |
| # role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| # aws-region: ${{ vars.AWS_REGION }} | |
| # | |
| # - name: Check out source | |
| # uses: actions/checkout@v4 | |
| # | |
| # - name: Set variables | |
| # id: vars | |
| # run: | | |
| # echo "GITHUB_SHA_SHORT=$(echo $GITHUB_SHA | cut -c 1-7)" >> $GITHUB_ENV | |
| # | |
| # - name: Download release files | |
| # run: | | |
| # gh release download "${RELEASE_TAG}" -p "codebuild-revision-${VAR}.zip" | |
| # mkdir -p revision | |
| # unzip -o "codebuild-revision-${VAR}.zip" -d revision | |
| # | |
| # - name: Set variables | |
| # # run: echo "REVISION_FILE=${{env.CODEDEPLOY_APPLICATION}}-$(date +'%Y%m%d%H%M%S')-${{ github.run_number }}-${{env.VAR}}.zip" >> $GITHUB_ENV | |
| # # run: echo "REVISION_FILE=${{env.CODEDEPLOY_APPLICATION}}-${{github.event.head_commit.timestamp}}-${{env.GITHUB_SHA_SHORT}}.zip" >> $GITHUB_ENV | |
| # # echo "REVISION_FILE=${CODEDEPLOY_APPLICATION}-$(date +'%Y%m%d%H%M%S')-${RELEASE_TAG}-${VAR}.zip" >> $GITHUB_ENV | |
| # run: | | |
| # echo "RELEASE_TIMESTAMP=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV | |
| # echo "REVISION_FILE=${CODEDEPLOY_APPLICATION}-${RELEASE_TAG}-${VAR}.zip" >> $GITHUB_ENV | |
| # | |
| # # - name: Copy Ansible release files to S3 | |
| # # run: | | |
| # # gh release download "${RELEASE_TAG}" -p "ansible-release-${VAR}.zip" | |
| # # aws s3 cp "ansible-release-${VAR}.zip" "s3://${CODEDEPLOY_BUCKET}/ansible-release-${VAR}-${RELEASE_TIMESTAMP}.zip" | |
| # | |
| # - name: Create CodeDeploy revision | |
| # run: | | |
| # aws deploy push --application-name "${CODEDEPLOY_APPLICATION}" \ | |
| # --description "App ${RELEASE_TAG} ${RELEASE_TIMESTAMP} ${GITHUB_SHA_SHORT}" \ | |
| # --s3-location s3://${CODEDEPLOY_BUCKET}/${REVISION_FILE} \ | |
| # --source revision | |
| # | |
| # - name: Create CodeDeploy deployment for dev servers | |
| # run: | | |
| # aws deploy create-deployment --application-name "${CODEDEPLOY_APPLICATION}" \ | |
| # --deployment-group-name "${CODEDEPLOY_DEPLOYMENT_GROUP}-dev-${{ matrix.os }}" \ | |
| # --s3-location "bucket=${CODEDEPLOY_BUCKET},key=${REVISION_FILE},bundleType=zip" \ | |
| # --description "Bounce ${RELEASE_TAG} ${RELEASE_TIMESTAMP} ${GITHUB_SHA_SHORT}" | |
| # | |
| # - name: Create CodeDeploy deployment | |
| # # if: ${{ matrix.os == 'debian' }} | |
| # run: | | |
| # aws deploy create-deployment --application-name "${CODEDEPLOY_APPLICATION}" \ | |
| # --deployment-group-name "${CODEDEPLOY_DEPLOYMENT_GROUP}" \ | |
| # --s3-location "bucket=${CODEDEPLOY_BUCKET},key=${REVISION_FILE},bundleType=zip" \ | |
| # --description "App ${RELEASE_TAG} ${RELEASE_TIMESTAMP} ${GITHUB_SHA_SHORT}" | |
| # sync-images: | |
| # name: Sync images to GHCR | |
| # needs: [config] | |
| # permissions: | |
| # id-token: write | |
| # contents: read | |
| # packages: write | |
| # runs-on: ubuntu-latest | |
| # environment: ${{ needs.config.outputs.environment }} | |
| # steps: | |
| # - name: Debug environment | |
| # run: | | |
| # echo "ref: ${{ github.ref }}" | |
| # echo "ref_name: ${{ github.ref_name }}" | |
| # echo "environment: ${{ needs.config.outputs.environment }}" | |
| # - name: Log in to GHCR | |
| # uses: docker/login-action@v3 | |
| # with: | |
| # registry: ghcr.io | |
| # username: ${{ github.actor }} | |
| # password: ${{ secrets.GITHUB_TOKEN }} | |
| # # Pull public images without rate limits | |
| # - name: Log in to Docker Hub | |
| # uses: docker/login-action@v3 | |
| # with: | |
| # username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| # password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| # - name: Configure AWS credentials | |
| # if: ${{ env.AWS_ENABLED == 1 }} | |
| # uses: aws-actions/configure-aws-credentials@v5 | |
| # with: | |
| # role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| # aws-region: ${{ vars.AWS_REGION }} | |
| # - name: Log in to Amazon ECR | |
| # if: ${{ env.AWS_ENABLED == 1 }} | |
| # id: ecr-login | |
| # uses: aws-actions/amazon-ecr-login@v2 | |
| # - name: Set vars | |
| # if: ${{ env.AWS_ENABLED == 1 }} | |
| # run: echo "ECR_REGISTRY=${{ steps.ecr-login.outputs.registry }}" >> "$GITHUB_ENV" | |
| # - name: Check out source | |
| # uses: actions/checkout@v4 | |
| # # - name: Copy to GHCR | |
| # # run: | | |
| # # skopeo copy \ | |
| # # "docker://docker.io/amazon/aws-otel-collector:latest" \ | |
| # # "docker://ghcr.io/${IMAGE_OWNER}/aws-otel-collector:latest" | |
| # # skopeo copy \ | |
| # # "docker://docker.io/zenika/alpine-chrome:119" \ | |
| # # "docker://ghcr.io/${IMAGE_OWNER}/zenika-alpine-chrome:119" | |
| # # skopeo copy \ | |
| # # "docker://docker.io/ankane/pgvector:latest" \ | |
| # # "docker://ghcr.io/${IMAGE_OWNER}/ankane-pgvector:latest" | |
| # # skopeo copy \ | |
| # # "docker://docker.io/rabbitmq:3-management" \ | |
| # # "docker://ghcr.io/${IMAGE_OWNER}/rabbitmq:3-management" | |
| # # https://github.com/regclient/actions | |
| # - name: Install regsync | |
| # uses: regclient/actions/regsync-installer@v0.1.0 | |
| # with: | |
| # release: 'v0.11.1' # optional version | |
| # - name: Sync images to GHCR | |
| # run: regsync once -c deploy/regsync-ghcr.yml | |
| # - name: Sync images to ECR | |
| # run: regsync once -c deploy/regsync-ecr.yml | |
| # To see debug logs, create a secret named ACTIONS_STEP_DEBUG with value 'true' | |
| # https://github.com/marketplace/actions/slack-notify-build | |
| # https://github.com/marketplace/actions/post-slack-message |