diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 00e951f..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Docker build and publish - -on: - workflow_dispatch: - push: - branches: - - "main" - -jobs: - docker-build: - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.IAM_ROLE_FOR_ECS }} - aws-region: ${{ secrets.AWS_REGION }} - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - name: Build, tag, and push docker image to Amazon ECR - env: - REGISTRY: ${{ steps.login-ecr.outputs.registry }} - REPOSITORY: auth-incd-ca - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG -t $REGISTRY/$REPOSITORY:latest . - docker image push --all-tags $REGISTRY/$REPOSITORY diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml deleted file mode 100644 index 8995c05..0000000 --- a/.github/workflows/integration.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Run Docker test - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - docker-test: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - name: Run Docker integration tests - run: | - curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/4.0.0/hurl_4.0.0_amd64.deb - sudo dpkg -i hurl_4.0.0_amd64.deb - bin/integration.sh http://localhost:3000/auth diff --git a/.github/workflows/lint-format-test.yml b/.github/workflows/lint-format-test.yml deleted file mode 100644 index eba256f..0000000 --- a/.github/workflows/lint-format-test.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Lint, format and test - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - lint-format-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Cache node modules - uses: actions/cache@v4 - with: - path: node_modules - key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} - - name: Install dependencies and lint files - run: | - npm ci - npm run lint - npm run format - npm run test - - name: Save code coverage to artifact - uses: actions/upload-artifact@v4 - with: - name: code-coverage - path: "coverage/clover.xml" - retention-days: 5 - upload-coverage: - runs-on: ubuntu-latest - needs: - - lint-format-test - steps: - - name: Fetch code coverage artifact - uses: actions/download-artifact@v4 - with: - name: code-coverage - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: true diff --git a/.github/workflows/merge-release.yml b/.github/workflows/merge-release.yml new file mode 100644 index 0000000..ce09134 --- /dev/null +++ b/.github/workflows/merge-release.yml @@ -0,0 +1,134 @@ +name: Merge release + +on: + release: + types: [released] + workflow_dispatch: + inputs: + branch: + description: 'Branch to merge into' + required: false + type: string + default: main + tag: + description: 'Tag to merge' + required: true + type: string + +jobs: + merge-release: + timeout-minutes: 5 + runs-on: ubuntu-latest + permissions: + contents: write + strategy: + fail-fast: true + + name: Merge tag + + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.GHA_APP_ID }} + private-key: ${{ secrets.GHA_PRIVATE_KEY }} + + - name: Determine branch + run: | + echo 'BRANCH='${{ inputs.branch || 'main' }} >> $GITHUB_ENV + + - name: Checkout "${{ env.BRANCH }}" branch locally + uses: actions/checkout@v4 + with: + ref: ${{ env.BRANCH }} + fetch-tags: true + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Get GitHub App User ID + if: ${{ github.event_name == 'release' }} + id: get-user-id + run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Update values for git user config (release) + if: ${{ github.event_name == 'release' }} + run: | + echo "GIT_USER_NAME=${{ steps.app-token.outputs.app-slug }}[bot]" >> $GITHUB_ENV + echo "GIT_USER_EMAIL=${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>" >> $GITHUB_ENV + + - name: Update values for git user config (workflow_dispatch) + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + # fetch user info + user=$(gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /user/$ACCOUNT_ID ) + + # get user's name and email + # email will be set to null if it is private + name=$(echo $user | jq '.name') + email=$(echo $user | jq '.email') + + # store in environment variables to use for setting up git user + echo "GIT_USER_NAME=$name" >> $GITHUB_ENV + echo "GIT_USER_EMAIL=$email" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ACCOUNT_ID: ${{ github.actor_id }} + + - name: Merge tag to "${{ env.BRANCH }}" branch + run: | + git config --local user.email "$GIT_USER_EMAIL" + git config --local user.name "$GIT_USER_NAME" + git merge ${{ inputs.tag || github.event.release.tag_name }} + git push + + validate-code-and-image: + if: ${{ github.event_name == 'release' || inputs.branch == 'main' }} + needs: + - merge-release + uses: ./.github/workflows/validate-code-and-image.yml + secrets: inherit + + docker-build-and-publish: + name: Publish docker image + if: ${{ github.event_name == 'release' || inputs.branch == 'main' }} + timeout-minutes: 5 + runs-on: ubuntu-latest + needs: + - validate-code-and-image + permissions: + id-token: write + contents: read + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.IAM_ROLE_FOR_ECS }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + install: true + + - name: Build and push Docker image to Amazon ECR + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ${{ steps.login-ecr.outputs.registry }}/auth-incd-ca:${{ github.sha }} + ${{ steps.login-ecr.outputs.registry }}/auth-incd-ca:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index ae23ca1..db60eb0 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -7,8 +7,17 @@ on: jobs: release-please: runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: write + pull-requests: write steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.GHA_APP_ID }} + private-key: ${{ secrets.GHA_PRIVATE_KEY }} + - uses: googleapis/release-please-action@v4 - id: release with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/validate-code-and-image.yml b/.github/workflows/validate-code-and-image.yml new file mode 100644 index 0000000..c5a30f7 --- /dev/null +++ b/.github/workflows/validate-code-and-image.yml @@ -0,0 +1,96 @@ +name: Validate codebase and image + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_call: + +jobs: + lint-format-test: + timeout-minutes: 5 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + cache: npm + - name: Install dependencies and lint files + run: | + npm ci + npm run lint + npm run format + npm run test + - name: Save code coverage to artifact + uses: actions/upload-artifact@v4 + with: + name: code-coverage + path: "coverage/clover.xml" + retention-days: 5 + upload-coverage: + timeout-minutes: 5 + runs-on: ubuntu-latest + needs: + - lint-format-test + steps: + - name: Fetch code coverage artifact + uses: actions/download-artifact@v4 + with: + name: code-coverage + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + + docker-test: + timeout-minutes: 5 + runs-on: ubuntu-latest + needs: + - lint-format-test + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + install: true + + # This repository's Dockerfile uses $CACHE_BUST to invalidate Docker layer caching. The + # variable is passed to the Docker build action below. + - name: Export $CACHE_BUST environment variable + run: echo "CACHE_BUST=$(date +%FT%T%z)" >> $GITHUB_ENV + + # Docker Compose builds an image with a default name of 'tracktime-tracktime'. Building an + # image with that name here and then using Docker Compose to start it in the next step. This + # is a workaround because Compose does not support BuildKit/buildx, which is required for + # GHA caching. + - name: Build Docker image + uses: docker/build-push-action@v6 + with: + context: . + tags: idrc-cms-authenticator + build-args: CACHE_BUST=${{ env.CACHE_BUST }} + cache-from: type=gha + cache-to: type=gha,mode=max + load: true + + - name: Run Docker Compose using built image + run: docker compose up --detach + + - name: Run Docker integration tests + run: | + curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/4.0.0/hurl_4.0.0_amd64.deb + sudo dpkg -i hurl_4.0.0_amd64.deb + bin/integration.sh http://localhost:3000/auth + + - name: Scan image + uses: anchore/scan-action@v5 + with: + image: "idrc-cms-authenticator" + only-fixed: true + cache-db: true + output-format: table diff --git a/.grype.yml b/.grype.yml new file mode 100644 index 0000000..086778b --- /dev/null +++ b/.grype.yml @@ -0,0 +1 @@ +ignore: [] \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index 2bd5a0a..4099407 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22 +23 diff --git a/Dockerfile b/Dockerfile index bface00..f9f09fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,18 @@ -FROM node:22-alpine +FROM node:23-alpine WORKDIR /usr/src/app COPY package*.json ./ -# Do not remove the 'apk update && apk upgrade' commands below. Workaround for installing latest -# Alpine security updates in case upstream images don't get built and pushed regularly. -RUN apk update && \ - apk upgrade --no-cache && \ - npm i +# Do not remove the 'apk upgrade --no-cache' command below. Workaround for installing latest +# Alpine OS security updates in case upstream images don't get built and pushed regularly. +# +# Pass the following 'docker build' argument to invalidate layer caching and force this step to +# always run: --build-arg CACHE_BUST=$(date +%s) +ARG CACHE_BUST=1 +RUN apk upgrade --no-cache && \ + echo "Cache bust: $CACHE_BUST" && \ + npm ci COPY . . diff --git a/bin/integration.sh b/bin/integration.sh index 2038b93..8a547ae 100755 --- a/bin/integration.sh +++ b/bin/integration.sh @@ -7,8 +7,13 @@ wait_for_url () { return 0 } -echo "Starting container..." -docker compose up --detach --build +# GitHub Actions should use the docker/build-push-action action for building an image to make use +# of its GHA cache backend. GHA workflows should not use Docker Compose's '--build' option because +# Compose does not support BuildKit/buildx. +if [ "$GITHUB_ACTIONS" != "true" ]; then + echo "Starting container..." + docker compose up --build --detach +fi echo "Waiting for server to be ready..." wait_for_url "$1" 60 diff --git a/package.json b/package.json index be0e117..1f5aad6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "idrc-cms-authenticator", "version": "1.1.0", "engines": { - "node": ">=20" + "node": ">=23" }, "description": "GitHub and GitLab OAuth client for Sveltia CMS and Decap CMS", "repository": "https://github.com/inclusive-design/idrc-cms-authenticator", @@ -14,13 +14,7 @@ "prepare": "husky", "test": "c8 --all -r clover -r text ava" }, - "keywords": [ - "decap-cms", - "sveltia-cms", - "github", - "gitlab", - "auth" - ], + "keywords": ["decap-cms", "sveltia-cms", "github", "gitlab", "auth"], "author": "OCAD University ", "license": "BSD-3-Clause", "dependencies": { @@ -37,8 +31,6 @@ "supertest": "^7.0.0" }, "lint-staged": { - "*.{js,json}": [ - "biome check --write --no-errors-on-unmatched" - ] + "*.{js,json}": ["biome check --write --no-errors-on-unmatched"] } }