From 0e46f16a35074608743726d7a4fd54bf25be01ba Mon Sep 17 00:00:00 2001 From: Simone Berni Date: Wed, 11 Feb 2026 09:05:45 +0100 Subject: [PATCH 1/3] Ecr fixes + opentofu (#224) * Ecr fixes * Opentofu stuff * Added pre commit * Update --- .github/actions/push_on_ecr/action.yml | 7 ++- .github/workflows/_detect_changes.yml | 40 +++++++++++-- .github/workflows/_opentofu.yml | 55 +++++++++++++++++ .github/workflows/_release_and_tag.yml | 59 +++++++++++++------ .github/workflows/pull_request_automation.yml | 8 +++ .github/workflows/release.yml | 13 +++- .pre-commit-config.yaml | 6 ++ actions/push_on_ecr/action.yml | 7 ++- workflows/_detect_changes.yml | 40 +++++++++++-- workflows/_opentofu.yml | 55 +++++++++++++++++ workflows/_release_and_tag.yml | 59 +++++++++++++------ workflows/pull_request_automation.yml | 8 +++ workflows/release.yml | 13 +++- 13 files changed, 320 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/_opentofu.yml create mode 100644 workflows/_opentofu.yml diff --git a/.github/actions/push_on_ecr/action.yml b/.github/actions/push_on_ecr/action.yml index f130e59..f7e182a 100644 --- a/.github/actions/push_on_ecr/action.yml +++ b/.github/actions/push_on_ecr/action.yml @@ -23,6 +23,10 @@ inputs: image_tag: description: Directory that must be run against the linters required: true + image_target: + description: Image target stage + required: true + default: BASE aws_region: description: Aws region @@ -46,7 +50,7 @@ runs: uses: docker/setup-buildx-action@v3 - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ${{ inputs.working_directory }} push: true @@ -54,3 +58,4 @@ runs: cache-to: type=gha,mode=max tags: ${{inputs.aws_account_id}}.dkr.ecr.${{inputs.aws_region}}.amazonaws.com/${{ inputs.repository }}:${{ inputs.image_tag }} file: ${{ inputs.working_directory }}/${{ inputs.dockerfile }} + target: ${{ inputs.image_target }} diff --git a/.github/workflows/_detect_changes.yml b/.github/workflows/_detect_changes.yml index 9e9b8a6..1a20cf9 100644 --- a/.github/workflows/_detect_changes.yml +++ b/.github/workflows/_detect_changes.yml @@ -16,12 +16,22 @@ on: description: Space separated list of frontend directories required: false type: string - + frontend_exclusions: description: Space separated list of frontend directories or files to be excluded required: false type: string + infrastructure_directories: + description: Space separated list of infrastructure directories + required: false + type: string + + infrastructure_exclusions: + description: Space separated list of infrastructure directories or files to be excluded + required: false + type: string + ubuntu_version: description: Ubuntu version to use type: string @@ -37,6 +47,10 @@ on: description: Number of files changed in frontend value: ${{ jobs.detect-changes.outputs.frontend }} + infrastructure: + description: Number of files changed in infrastructure + value: ${{ jobs.detect-changes.outputs.infrastructure }} + jobs: detect-changes: name: Detect changes @@ -44,6 +58,8 @@ jobs: outputs: backend: ${{steps.diff_check_backend.outputs.backend}} frontend: ${{steps.diff_check_frontend.outputs.frontend}} + infrastructure: ${{steps.diff_check_infrastructure.outputs.infrastructure}} + steps: - name: Check out PR target branch uses: actions/checkout@v4 @@ -56,7 +72,7 @@ jobs: clean: false - name: Generate summary - if: ${{inputs.backend_directories != ''}} | ${{inputs.frontend_directories != ''}} + if: ${{inputs.backend_directories != ''}} | ${{inputs.frontend_directories != ''}} | ${{inputs.infrastructure_directories != ''}} run: | echo "### Detect Changes summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY @@ -92,7 +108,23 @@ jobs: FRONTEND_CHANGES=$(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.frontend_directories }} $FRONTEND_EXCLUSIONS | head -n -1 | wc -l) echo "frontend=$FRONTEND_CHANGES" >> $GITHUB_OUTPUT echo "Frontend Changes: $FRONTEND_CHANGES" >> $GITHUB_STEP_SUMMARY - echo "::debug::diff command:git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.backend_directories }} $FRONTEND_EXCLUSIONS" - echo "::debug::diff command results: $(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.backend_directories }} $FRONTEND_EXCLUSIONS | head -n -1 )" + echo "::debug::diff command:git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.frontend_directories }} $FRONTEND_EXCLUSIONS" + echo "::debug::diff command results: $(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.frontend_directories }} $FRONTEND_EXCLUSIONS | head -n -1 )" echo "frontend $FRONTEND_CHANGES" + - name: Generate diffs for infrastructure + if: ${{inputs.infrastructure_directories != ''}} + id: diff_check_infrastructure + run: | + INFRASTRUCTURE_EXCLUSIONS="" + if ${{ inputs.infrastructure_exclusions != ''}}; then + for exclusion in ${{ inputs.infrastructure_exclusions }}; do + INFRASTRUCTURE_EXCLUSIONS+=":(glob,exclude)$exclusion " + done + fi + INFRASTRUCTURE_CHANGES=$(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.infrastructure_directories }} $INFRASTRUCTURE_EXCLUSIONS | head -n -1 | wc -l) + echo "infrastructure=$INFRASTRUCTURE_CHANGES" >> $GITHUB_OUTPUT + echo "Infrastructure Changes: $INFRASTRUCTURE_CHANGES" >> $GITHUB_STEP_SUMMARY + echo "::debug::diff command:git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.infrastructure_directories }} $INFRASTRUCTURE_EXCLUSIONS" + echo "::debug::diff command results: $(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.infrastructure_directories }} $INFRASTRUCTURE_EXCLUSIONS | head -n -1 )" + echo "infrastructure $INFRASTRUCTURE_CHANGES" diff --git a/.github/workflows/_opentofu.yml b/.github/workflows/_opentofu.yml new file mode 100644 index 0000000..0378f6e --- /dev/null +++ b/.github/workflows/_opentofu.yml @@ -0,0 +1,55 @@ +name: Reusable opentofu workflow +on: + workflow_call: + inputs: + working_directory: + description: Directory that must be run against the linters + type: string + required: true + + +jobs: + python: + name: Run opentofu test suite + runs-on: ubuntu-latest + env: + STAGE: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || ( github.base_ref == 'develop' || github.base_ref == 'dev' ) && 'stag' || 'test' }} + strategy: + matrix: + directory: ["infrastructure", "application" ] + fail-fast: false + environment: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || ( github.base_ref == 'develop' || github.base_ref == 'dev' ) && 'stag' || 'test' }} + permissions: write-all + steps: + - name: Check out latest commit + uses: actions/checkout@v4 + + - name: tofu fmt ${{ matrix.directory }} + uses: dflook/tofu-fmt-check@v2 + with: + path: ${{inputs.working_directory}}/opentofu/${{ env.STAGE }}/${{ matrix.directory }} + + - name: tofu validate ${{ matrix.directory }} + uses: dflook/tofu-validate@v2 + with: + path: ${{inputs.working_directory}}/opentofu/${{ env.STAGE }}/${{ matrix.directory }} + env: + TERRAFORM_SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: tofu plan ${{ matrix.directory }} + uses: dflook/tofu-plan@v2 + id: tofu_plan + env: + GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} + TF_VAR_github_token : ${{ secrets.GITHUB_TOKEN }} + TF_VAR_github_organization_name : certego + TF_VAR_aws_account_id : ${{ secrets.AWS_ACCOUNT_ID }} + TF_VAR_aws_access_key : ${{ secrets.AWS_ACCESS_KEY }} + TF_VAR_aws_secret_key : ${{ secrets.AWS_SECRET_ACCESS_KEY }} + TERRAFORM_SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + with: + path: ${{inputs.working_directory}}/opentofu/${{ env.STAGE }}/${{ matrix.directory }} + label: ${{ matrix.path }} + # TODO per qualche ragione di permessi (credo del token github) non riesce a recuperarsi le chiavi + exclude: | + module.github_ecr_access_keys diff --git a/.github/workflows/_release_and_tag.yml b/.github/workflows/_release_and_tag.yml index d4f97e2..663e2ca 100644 --- a/.github/workflows/_release_and_tag.yml +++ b/.github/workflows/_release_and_tag.yml @@ -49,16 +49,12 @@ on: type: boolean required: false default: false - repository: - description: Repository name - type: string - required: false - default: ${{ github.event.repository.name }} - dockerfiles: - description: Path for dockerfiles from working directory + docker_mapping: + description: List[Dict[dockerfile sep "dockerfile_path", repository sep "repository_name"]] type: string required: false + working_directory: description: Docker build context type: string @@ -170,39 +166,68 @@ jobs: access_token_secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} - push_on_ecr: + push_on_ecr_branch: runs-on: ubuntu-latest - needs: release_and_tag - if: github.event.pull_request.merged == true && inputs.publish_on_ecr == true + environment: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || ( github.base_ref == 'develop' || github.base_ref == 'dev' ) && 'stag' || 'test' }} + if: inputs.publish_on_ecr == true strategy: matrix: - dockerfile: ${{ fromJson(inputs.dockerfiles) }} + docker_mapping: ${{ fromJson(inputs.docker_mapping) }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # otherwise, you do not retrieve the tags - - name: Push on ecr branch + + - name: Push on ecr branch from merge uses: ./.github/actions/push_on_ecr - if: github.base_ref == 'master' || github.base_ref == 'main' || github.base_ref == 'develop' || github.base_ref == 'dev' + if: github.event.pull_request.merged == true && (github.base_ref == 'master' || github.base_ref == 'develop') with: - repository: ${{ inputs.repository }} + repository: ${{ matrix.docker_mapping.repository }} aws_account_id: ${{ secrets.AWS_ACCOUNT_ID }} aws_access_key: ${{ secrets.AWS_ACCESS_KEY}} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - dockerfile: ${{ matrix.dockerfile }} + dockerfile: ${{ matrix.docker_mapping.dockerfile }} image_tag: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || 'stag' }} aws_region: ${{ inputs.aws_region }} working_directory: ${{ inputs.working_directory }} + image_target: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || 'stag' }} + + - name: Push on ecr branch from push + uses: ./.github/actions/push_on_ecr + if: github.ref_name == 'test' || github.ref_name == 'opentofu' + with: + repository: ${{ matrix.docker_mapping.repository }} + aws_account_id: ${{ secrets.AWS_ACCOUNT_ID }} + aws_access_key: ${{ secrets.AWS_ACCESS_KEY}} + aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + dockerfile: ${{ matrix.docker_mapping.dockerfile }} + image_tag: 'test' + aws_region: ${{ inputs.aws_region }} + working_directory: ${{ inputs.working_directory }} + image_target: 'test' + + push_on_ecr_tag: + runs-on: ubuntu-latest + needs: release_and_tag + environment: "prod" + if: github.event.pull_request.merged == true && inputs.publish_on_ecr == true + strategy: + matrix: + docker_mapping: ${{ fromJson(inputs.docker_mapping) }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # otherwise, you do not retrieve the tags - name: Push on ecr new release if: needs.release_and_tag.outputs.match == 'true' && (github.base_ref == 'master' || github.base_ref == 'main' ) uses: ./.github/actions/push_on_ecr with: - repository: ${{ inputs.repository }} + repository: ${{ matrix.docker_mapping.repository }} aws_account_id: ${{ secrets.AWS_ACCOUNT_ID }} aws_access_key: ${{ secrets.AWS_ACCESS_KEY}} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - dockerfile: ${{ inputs.dockerfile }} + dockerfile: ${{ matrix.docker_mapping.dockerfile }} image_tag: ${{ github.event.pull_request.title }} aws_region: ${{ inputs.aws_region }} working_directory: ${{ inputs.working_directory }} \ No newline at end of file diff --git a/.github/workflows/pull_request_automation.yml b/.github/workflows/pull_request_automation.yml index 4b24080..2d8f285 100644 --- a/.github/workflows/pull_request_automation.yml +++ b/.github/workflows/pull_request_automation.yml @@ -106,3 +106,11 @@ jobs: ["3.12"] max_timeout: 15 ubuntu_version: latest + + opentofu_test: + needs: detect-changes + if: ${{ needs.detect-changes.outputs.infrastructure > 0 }} + uses: ./.github/workflows/_opentofu.yml + secrets: inherit + with: + working_directory: . diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8040450..a21c4c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,15 @@ on: pull_request: types: - closed + branches: + - 'master' + - 'main' + - 'develop' + - 'test' + push: + branches: + - 'test' + - 'opentofu' # discard previous execution if you commit to a branch that is already running concurrency: @@ -24,8 +33,6 @@ jobs: append_to_release: false branches_that_trigger_release: >- ["master", "main", "prod"] - repository: certego-test working_directory: .github/test/python_test - dockerfiles: >- - ["Dockerfile"] + docker_mapping: '[{ dockerfile: "docker/Dockerfile", repository: "test"}]' aws_region: eu-central-1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9697a09..f76324e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -52,3 +52,9 @@ repos: - id: ruff name: ruff-lint args: ["--config", "./.github/configurations/python_linters/.ruff.toml"] + +- repo: https://github.com/tofuutils/pre-commit-opentofu + rev: v2.2.1 + hooks: + - id: tofu_fmt + - id: tofu_validate \ No newline at end of file diff --git a/actions/push_on_ecr/action.yml b/actions/push_on_ecr/action.yml index f130e59..f7e182a 100644 --- a/actions/push_on_ecr/action.yml +++ b/actions/push_on_ecr/action.yml @@ -23,6 +23,10 @@ inputs: image_tag: description: Directory that must be run against the linters required: true + image_target: + description: Image target stage + required: true + default: BASE aws_region: description: Aws region @@ -46,7 +50,7 @@ runs: uses: docker/setup-buildx-action@v3 - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ${{ inputs.working_directory }} push: true @@ -54,3 +58,4 @@ runs: cache-to: type=gha,mode=max tags: ${{inputs.aws_account_id}}.dkr.ecr.${{inputs.aws_region}}.amazonaws.com/${{ inputs.repository }}:${{ inputs.image_tag }} file: ${{ inputs.working_directory }}/${{ inputs.dockerfile }} + target: ${{ inputs.image_target }} diff --git a/workflows/_detect_changes.yml b/workflows/_detect_changes.yml index 9e9b8a6..1a20cf9 100644 --- a/workflows/_detect_changes.yml +++ b/workflows/_detect_changes.yml @@ -16,12 +16,22 @@ on: description: Space separated list of frontend directories required: false type: string - + frontend_exclusions: description: Space separated list of frontend directories or files to be excluded required: false type: string + infrastructure_directories: + description: Space separated list of infrastructure directories + required: false + type: string + + infrastructure_exclusions: + description: Space separated list of infrastructure directories or files to be excluded + required: false + type: string + ubuntu_version: description: Ubuntu version to use type: string @@ -37,6 +47,10 @@ on: description: Number of files changed in frontend value: ${{ jobs.detect-changes.outputs.frontend }} + infrastructure: + description: Number of files changed in infrastructure + value: ${{ jobs.detect-changes.outputs.infrastructure }} + jobs: detect-changes: name: Detect changes @@ -44,6 +58,8 @@ jobs: outputs: backend: ${{steps.diff_check_backend.outputs.backend}} frontend: ${{steps.diff_check_frontend.outputs.frontend}} + infrastructure: ${{steps.diff_check_infrastructure.outputs.infrastructure}} + steps: - name: Check out PR target branch uses: actions/checkout@v4 @@ -56,7 +72,7 @@ jobs: clean: false - name: Generate summary - if: ${{inputs.backend_directories != ''}} | ${{inputs.frontend_directories != ''}} + if: ${{inputs.backend_directories != ''}} | ${{inputs.frontend_directories != ''}} | ${{inputs.infrastructure_directories != ''}} run: | echo "### Detect Changes summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY @@ -92,7 +108,23 @@ jobs: FRONTEND_CHANGES=$(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.frontend_directories }} $FRONTEND_EXCLUSIONS | head -n -1 | wc -l) echo "frontend=$FRONTEND_CHANGES" >> $GITHUB_OUTPUT echo "Frontend Changes: $FRONTEND_CHANGES" >> $GITHUB_STEP_SUMMARY - echo "::debug::diff command:git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.backend_directories }} $FRONTEND_EXCLUSIONS" - echo "::debug::diff command results: $(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.backend_directories }} $FRONTEND_EXCLUSIONS | head -n -1 )" + echo "::debug::diff command:git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.frontend_directories }} $FRONTEND_EXCLUSIONS" + echo "::debug::diff command results: $(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.frontend_directories }} $FRONTEND_EXCLUSIONS | head -n -1 )" echo "frontend $FRONTEND_CHANGES" + - name: Generate diffs for infrastructure + if: ${{inputs.infrastructure_directories != ''}} + id: diff_check_infrastructure + run: | + INFRASTRUCTURE_EXCLUSIONS="" + if ${{ inputs.infrastructure_exclusions != ''}}; then + for exclusion in ${{ inputs.infrastructure_exclusions }}; do + INFRASTRUCTURE_EXCLUSIONS+=":(glob,exclude)$exclusion " + done + fi + INFRASTRUCTURE_CHANGES=$(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.infrastructure_directories }} $INFRASTRUCTURE_EXCLUSIONS | head -n -1 | wc -l) + echo "infrastructure=$INFRASTRUCTURE_CHANGES" >> $GITHUB_OUTPUT + echo "Infrastructure Changes: $INFRASTRUCTURE_CHANGES" >> $GITHUB_STEP_SUMMARY + echo "::debug::diff command:git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.infrastructure_directories }} $INFRASTRUCTURE_EXCLUSIONS" + echo "::debug::diff command results: $(git diff --compact-summary origin/${{ github.base_ref }} -- ${{ inputs.infrastructure_directories }} $INFRASTRUCTURE_EXCLUSIONS | head -n -1 )" + echo "infrastructure $INFRASTRUCTURE_CHANGES" diff --git a/workflows/_opentofu.yml b/workflows/_opentofu.yml new file mode 100644 index 0000000..0378f6e --- /dev/null +++ b/workflows/_opentofu.yml @@ -0,0 +1,55 @@ +name: Reusable opentofu workflow +on: + workflow_call: + inputs: + working_directory: + description: Directory that must be run against the linters + type: string + required: true + + +jobs: + python: + name: Run opentofu test suite + runs-on: ubuntu-latest + env: + STAGE: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || ( github.base_ref == 'develop' || github.base_ref == 'dev' ) && 'stag' || 'test' }} + strategy: + matrix: + directory: ["infrastructure", "application" ] + fail-fast: false + environment: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || ( github.base_ref == 'develop' || github.base_ref == 'dev' ) && 'stag' || 'test' }} + permissions: write-all + steps: + - name: Check out latest commit + uses: actions/checkout@v4 + + - name: tofu fmt ${{ matrix.directory }} + uses: dflook/tofu-fmt-check@v2 + with: + path: ${{inputs.working_directory}}/opentofu/${{ env.STAGE }}/${{ matrix.directory }} + + - name: tofu validate ${{ matrix.directory }} + uses: dflook/tofu-validate@v2 + with: + path: ${{inputs.working_directory}}/opentofu/${{ env.STAGE }}/${{ matrix.directory }} + env: + TERRAFORM_SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: tofu plan ${{ matrix.directory }} + uses: dflook/tofu-plan@v2 + id: tofu_plan + env: + GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} + TF_VAR_github_token : ${{ secrets.GITHUB_TOKEN }} + TF_VAR_github_organization_name : certego + TF_VAR_aws_account_id : ${{ secrets.AWS_ACCOUNT_ID }} + TF_VAR_aws_access_key : ${{ secrets.AWS_ACCESS_KEY }} + TF_VAR_aws_secret_key : ${{ secrets.AWS_SECRET_ACCESS_KEY }} + TERRAFORM_SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + with: + path: ${{inputs.working_directory}}/opentofu/${{ env.STAGE }}/${{ matrix.directory }} + label: ${{ matrix.path }} + # TODO per qualche ragione di permessi (credo del token github) non riesce a recuperarsi le chiavi + exclude: | + module.github_ecr_access_keys diff --git a/workflows/_release_and_tag.yml b/workflows/_release_and_tag.yml index d4f97e2..663e2ca 100644 --- a/workflows/_release_and_tag.yml +++ b/workflows/_release_and_tag.yml @@ -49,16 +49,12 @@ on: type: boolean required: false default: false - repository: - description: Repository name - type: string - required: false - default: ${{ github.event.repository.name }} - dockerfiles: - description: Path for dockerfiles from working directory + docker_mapping: + description: List[Dict[dockerfile sep "dockerfile_path", repository sep "repository_name"]] type: string required: false + working_directory: description: Docker build context type: string @@ -170,39 +166,68 @@ jobs: access_token_secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} - push_on_ecr: + push_on_ecr_branch: runs-on: ubuntu-latest - needs: release_and_tag - if: github.event.pull_request.merged == true && inputs.publish_on_ecr == true + environment: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || ( github.base_ref == 'develop' || github.base_ref == 'dev' ) && 'stag' || 'test' }} + if: inputs.publish_on_ecr == true strategy: matrix: - dockerfile: ${{ fromJson(inputs.dockerfiles) }} + docker_mapping: ${{ fromJson(inputs.docker_mapping) }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # otherwise, you do not retrieve the tags - - name: Push on ecr branch + + - name: Push on ecr branch from merge uses: ./.github/actions/push_on_ecr - if: github.base_ref == 'master' || github.base_ref == 'main' || github.base_ref == 'develop' || github.base_ref == 'dev' + if: github.event.pull_request.merged == true && (github.base_ref == 'master' || github.base_ref == 'develop') with: - repository: ${{ inputs.repository }} + repository: ${{ matrix.docker_mapping.repository }} aws_account_id: ${{ secrets.AWS_ACCOUNT_ID }} aws_access_key: ${{ secrets.AWS_ACCESS_KEY}} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - dockerfile: ${{ matrix.dockerfile }} + dockerfile: ${{ matrix.docker_mapping.dockerfile }} image_tag: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || 'stag' }} aws_region: ${{ inputs.aws_region }} working_directory: ${{ inputs.working_directory }} + image_target: ${{ ( github.base_ref == 'main' || github.base_ref == 'master' ) && 'prod' || 'stag' }} + + - name: Push on ecr branch from push + uses: ./.github/actions/push_on_ecr + if: github.ref_name == 'test' || github.ref_name == 'opentofu' + with: + repository: ${{ matrix.docker_mapping.repository }} + aws_account_id: ${{ secrets.AWS_ACCOUNT_ID }} + aws_access_key: ${{ secrets.AWS_ACCESS_KEY}} + aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + dockerfile: ${{ matrix.docker_mapping.dockerfile }} + image_tag: 'test' + aws_region: ${{ inputs.aws_region }} + working_directory: ${{ inputs.working_directory }} + image_target: 'test' + + push_on_ecr_tag: + runs-on: ubuntu-latest + needs: release_and_tag + environment: "prod" + if: github.event.pull_request.merged == true && inputs.publish_on_ecr == true + strategy: + matrix: + docker_mapping: ${{ fromJson(inputs.docker_mapping) }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # otherwise, you do not retrieve the tags - name: Push on ecr new release if: needs.release_and_tag.outputs.match == 'true' && (github.base_ref == 'master' || github.base_ref == 'main' ) uses: ./.github/actions/push_on_ecr with: - repository: ${{ inputs.repository }} + repository: ${{ matrix.docker_mapping.repository }} aws_account_id: ${{ secrets.AWS_ACCOUNT_ID }} aws_access_key: ${{ secrets.AWS_ACCESS_KEY}} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - dockerfile: ${{ inputs.dockerfile }} + dockerfile: ${{ matrix.docker_mapping.dockerfile }} image_tag: ${{ github.event.pull_request.title }} aws_region: ${{ inputs.aws_region }} working_directory: ${{ inputs.working_directory }} \ No newline at end of file diff --git a/workflows/pull_request_automation.yml b/workflows/pull_request_automation.yml index 4b24080..2d8f285 100644 --- a/workflows/pull_request_automation.yml +++ b/workflows/pull_request_automation.yml @@ -106,3 +106,11 @@ jobs: ["3.12"] max_timeout: 15 ubuntu_version: latest + + opentofu_test: + needs: detect-changes + if: ${{ needs.detect-changes.outputs.infrastructure > 0 }} + uses: ./.github/workflows/_opentofu.yml + secrets: inherit + with: + working_directory: . diff --git a/workflows/release.yml b/workflows/release.yml index 8040450..a21c4c6 100644 --- a/workflows/release.yml +++ b/workflows/release.yml @@ -4,6 +4,15 @@ on: pull_request: types: - closed + branches: + - 'master' + - 'main' + - 'develop' + - 'test' + push: + branches: + - 'test' + - 'opentofu' # discard previous execution if you commit to a branch that is already running concurrency: @@ -24,8 +33,6 @@ jobs: append_to_release: false branches_that_trigger_release: >- ["master", "main", "prod"] - repository: certego-test working_directory: .github/test/python_test - dockerfiles: >- - ["Dockerfile"] + docker_mapping: '[{ dockerfile: "docker/Dockerfile", repository: "test"}]' aws_region: eu-central-1 From 84923d1be6179206f1112795350c0888d5023446 Mon Sep 17 00:00:00 2001 From: Alberto Cocheo Date: Wed, 11 Feb 2026 10:39:59 +0100 Subject: [PATCH 2/3] Fix Readme --- .github/configurations/node_linters/README.md | 2 +- configurations/node_linters/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/configurations/node_linters/README.md b/.github/configurations/node_linters/README.md index d730cc8..1d43f56 100644 --- a/.github/configurations/node_linters/README.md +++ b/.github/configurations/node_linters/README.md @@ -19,7 +19,7 @@ and add these scripts to `package.json`: "lint-fix": "npm run lint -- --fix", "lint-scss": "stylelint --config $npm_package_config_stylelint '**/*.{css,scss}'", "lint-scss-fix": "npm run lint-scss -- --fix", - "formatter": "prettier --config $npm_package_config_prettier . --check", + "formatter": "prettier --config $npm_package_config_prettier --check", "formatter-fix": "npm run formatter -- --write" }, ``` diff --git a/configurations/node_linters/README.md b/configurations/node_linters/README.md index 8f4438a..1d43f56 100644 --- a/configurations/node_linters/README.md +++ b/configurations/node_linters/README.md @@ -48,9 +48,9 @@ In `.vscode/settings.json` "editor.formatOnSave": true, "editor.semanticHighlighting.enabled": true, "eslint.options": { - "overrideConfigFile": ".github/configurations/node_linters/eslint/eslint.config.mjs" + "overrideConfigFile": ".github/configurations/node_linters/eslint.config.mjs" }, "stylelint.configFile": ".github/configurations/node_linters/.stylelintrc.json", - "prettier.configPath": ".github/configurations/node_linters/prettier/.prettierrc.js", + "prettier.configPath": ".github/configurations/node_linters/.prettierrc.json", } ``` From 68f505f5ebf38f4035f77a31a7ca8cd34d28383c Mon Sep 17 00:00:00 2001 From: Alberto Cocheo <88388252+acocheo@users.noreply.github.com> Date: Fri, 13 Feb 2026 11:09:07 +0100 Subject: [PATCH 3/3] Improve eslint (#232) * Better eslint * Fix dir --- .../node_linters/eslint.config.mjs | 90 +++- .../node_linters/packages-linters.txt | 4 + .github/test/node_test/package-lock.json | 383 +++++++++++++++++- .github/test/node_test/package.json | 4 + configurations/node_linters/eslint.config.mjs | 90 +++- .../node_linters/packages-linters.txt | 4 + 6 files changed, 531 insertions(+), 44 deletions(-) diff --git a/.github/configurations/node_linters/eslint.config.mjs b/.github/configurations/node_linters/eslint.config.mjs index 0aef7c5..7e19af7 100644 --- a/.github/configurations/node_linters/eslint.config.mjs +++ b/.github/configurations/node_linters/eslint.config.mjs @@ -1,36 +1,92 @@ -/* -import js from '@eslint/js'; -import pluginJest from 'eslint-plugin-jest'; -import globals from 'globals'; -import { defineConfig, globalIgnores } from 'eslint/config'; -import nextVitals from 'eslint-config-next/core-web-vitals'; -*/ -// Need require relative to the cwd +// Need inport relative to the cwd import path from 'path'; import { createRequire } from 'module'; const requireFromCwd = createRequire(path.resolve(process.cwd(), 'package.json')); const { defineConfig, globalIgnores } = requireFromCwd('eslint/config'); +const { FlatCompat } = requireFromCwd('@eslint/eslintrc'); const js = requireFromCwd('@eslint/js'); +const jsdoc = requireFromCwd('eslint-plugin-jsdoc'); const pluginJest = requireFromCwd('eslint-plugin-jest'); +// const importPlugin = requireFromCwd('eslint-plugin-import'); const globals = requireFromCwd('globals'); const nextVitals = requireFromCwd('eslint-config-next/core-web-vitals'); +// eslint-plugin-import +const compat = new FlatCompat({ + baseDirectory: import.meta.dirname, + resolvePluginsRelativeTo: process.cwd() +}); +const eslintImport = [ + ...compat.config({ + extends: ['plugin:import/recommended'], + settings: { + 'import/resolver': { + next: true, + }, + }, + }), +]; + export default defineConfig([ - globalIgnores(['node_modules/**', '.next/**', 'out/**', 'build/**', 'next-env.d.ts', 'public/sw.js']), + globalIgnores([ + 'node_modules/**', + '.next/**', + 'out/**', + 'build/**', + 'next-env.d.ts', + 'public/sw.js', + '.vscode/**', + '.github/**', + ]), ...nextVitals.map((config) => ({ ...config, - files: ['app/**/*.{js,mjs,cjs,jsx}'], // Specific for next app - })), + files: ['{app,tests}/**/*.{js,mjs,cjs,jsx}'], // Specific for next app + })), + ...eslintImport, // BUG in flat config importPlugin.flatConfigs.recommended (https://github.com/import-js/eslint-plugin-import/issues/3212) { files: ['**/*.{js,mjs,cjs,jsx}'], // For all files - plugins: { js }, - extends: ['js/recommended'], - languageOptions: { globals: globals.node }, - }, { - files: ['**/*.spec.js', '**/*.test.js'], + plugins: { js, jsdoc }, + extends: ['js/recommended', 'jsdoc/flat/recommended'], + languageOptions: { globals: { ...globals.node, ...globals.browser } }, + rules: { + 'prefer-destructuring': [ + 'error', + { + object: true, + array: false, + }, + ], + 'no-console': [ + 'error', + { + allow: ['warn', 'error'], + }, + ], + 'no-unused-vars': [ + 'error', + { + varsIgnorePattern: '^_', + argsIgnorePattern: '^_', + args: 'after-used', + }, + ], + 'no-underscore-dangle': [ + 'error', + { + allowAfterThis: true, + }, + ], + 'jsdoc/require-jsdoc': 0, + 'jsdoc/reject-any-type': 0, + 'jsdoc/reject-function-type': 0, + }, + }, + { + files: ['tests/**/*.{js,mjs,cjs,jsx}', '**/*.spec.js', '**/*.test.js'], plugins: { jest: pluginJest }, + extends: ['jest/recommended'], languageOptions: { globals: pluginJest.environments.globals.globals, }, }, -]); \ No newline at end of file +]); diff --git a/.github/configurations/node_linters/packages-linters.txt b/.github/configurations/node_linters/packages-linters.txt index 35e14b4..9922e5b 100644 --- a/.github/configurations/node_linters/packages-linters.txt +++ b/.github/configurations/node_linters/packages-linters.txt @@ -3,5 +3,9 @@ eslint @eslint/js eslint-config-next eslint-plugin-jest +eslint-plugin-import +eslint-import-resolver-next +eslint-import-resolver-typescript +eslint-plugin-jsdoc stylelint stylelint-config-standard-scss \ No newline at end of file diff --git a/.github/test/node_test/package-lock.json b/.github/test/node_test/package-lock.json index ae7313b..8349c77 100644 --- a/.github/test/node_test/package-lock.json +++ b/.github/test/node_test/package-lock.json @@ -19,7 +19,11 @@ "@testing-library/react": "^16.3.2", "eslint": "^9.39.2", "eslint-config-next": "^16.1.6", + "eslint-import-resolver-next": "^0.6.0", + "eslint-import-resolver-typescript": "^4.4.4", + "eslint-plugin-import": "^2.32.0", "eslint-plugin-jest": "^29.12.2", + "eslint-plugin-jsdoc": "^62.5.4", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", "prettier": "^3.8.1", @@ -833,6 +837,33 @@ "tslib": "^2.4.0" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.84.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.84.0.tgz", + "integrity": "sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.54.0", + "comment-parser": "1.4.5", + "esquery": "^1.7.0", + "jsdoc-type-pratt-parser": "~7.1.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@es-joy/resolve.exports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", + "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -2313,6 +2344,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@sindresorhus/base62": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz", + "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", @@ -3219,6 +3263,16 @@ "node": ">= 8" } }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3934,6 +3988,16 @@ "dev": true, "license": "MIT" }, + "node_modules/comment-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz", + "integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4652,6 +4716,41 @@ } } }, + "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, "node_modules/eslint-config-next/node_modules/globals": { "version": "16.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", @@ -4665,6 +4764,68 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-import-context": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz", + "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-tsconfig": "^4.10.1", + "stable-hash-x": "^0.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-context" + }, + "peerDependencies": { + "unrs-resolver": "^1.0.0" + }, + "peerDependenciesMeta": { + "unrs-resolver": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-next": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-next/-/eslint-import-resolver-next-0.6.0.tgz", + "integrity": "sha512-W+40rkKo50tmOVvB59RkJHKoqWfBywvJe+c+8NZ7WhoCk68ol9JWmbXXv1i0Y9+74xWcMm2ECObx8tMfjnMtlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bun-module": "^2.0.0", + "js-yaml": "^4.1.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.7.2" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/eslint-import-resolver-next/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/eslint-import-resolver-next/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -4688,22 +4849,22 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz", + "integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==", "dev": true, "license": "ISC", "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", + "debug": "^4.4.1", + "eslint-import-context": "^0.1.8", + "get-tsconfig": "^4.10.1", "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" + "stable-hash-x": "^0.2.0", + "tinyglobby": "^0.2.14", + "unrs-resolver": "^1.7.11" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^16.17.0 || >=18.6.0" }, "funding": { "url": "https://opencollective.com/eslint-import-resolver-typescript" @@ -4848,6 +5009,92 @@ } } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "62.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.5.4.tgz", + "integrity": "sha512-U+Q5ppErmC17VFQl542eBIaXcuq975BzoIHBXyx7UQx/i4gyHXxPiBkonkuxWyFA98hGLALLUuD+NJcXqSGKxg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.84.0", + "@es-joy/resolve.exports": "1.2.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.5", + "debug": "^4.4.3", + "escape-string-regexp": "^4.0.0", + "espree": "^11.1.0", + "esquery": "^1.7.0", + "html-entities": "^2.6.0", + "object-deep-merge": "^2.0.0", + "parse-imports-exports": "^0.2.4", + "semver": "^7.7.3", + "spdx-expression-parse": "^4.0.0", + "to-valid-identifier": "^1.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", + "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.0.tgz", + "integrity": "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -6002,6 +6249,23 @@ "node": ">=18" } }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -7786,6 +8050,16 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.1.1.tgz", + "integrity": "sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/jsdom": { "version": "26.1.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", @@ -8356,6 +8630,13 @@ "node": ">=0.10.0" } }, + "node_modules/object-deep-merge": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.0.tgz", + "integrity": "sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==", + "dev": true, + "license": "MIT" + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -8606,6 +8887,16 @@ "node": ">=6" } }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -8625,6 +8916,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, "node_modules/parse5": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", @@ -9108,6 +9406,19 @@ "node": ">=0.10.0" } }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -9572,6 +9883,31 @@ "source-map": "^0.6.0" } }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -9586,6 +9922,16 @@ "dev": true, "license": "MIT" }, + "node_modules/stable-hash-x": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", + "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -10630,6 +10976,23 @@ "node": ">=8.0" } }, + "node_modules/to-valid-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", + "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/base62": "^1.0.0", + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tough-cookie": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", diff --git a/.github/test/node_test/package.json b/.github/test/node_test/package.json index 8b362cb..717d706 100644 --- a/.github/test/node_test/package.json +++ b/.github/test/node_test/package.json @@ -31,7 +31,11 @@ "@testing-library/react": "^16.3.2", "eslint": "^9.39.2", "eslint-config-next": "^16.1.6", + "eslint-import-resolver-next": "^0.6.0", + "eslint-import-resolver-typescript": "^4.4.4", + "eslint-plugin-import": "^2.32.0", "eslint-plugin-jest": "^29.12.2", + "eslint-plugin-jsdoc": "^62.5.4", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", "prettier": "^3.8.1", diff --git a/configurations/node_linters/eslint.config.mjs b/configurations/node_linters/eslint.config.mjs index 0aef7c5..7e19af7 100644 --- a/configurations/node_linters/eslint.config.mjs +++ b/configurations/node_linters/eslint.config.mjs @@ -1,36 +1,92 @@ -/* -import js from '@eslint/js'; -import pluginJest from 'eslint-plugin-jest'; -import globals from 'globals'; -import { defineConfig, globalIgnores } from 'eslint/config'; -import nextVitals from 'eslint-config-next/core-web-vitals'; -*/ -// Need require relative to the cwd +// Need inport relative to the cwd import path from 'path'; import { createRequire } from 'module'; const requireFromCwd = createRequire(path.resolve(process.cwd(), 'package.json')); const { defineConfig, globalIgnores } = requireFromCwd('eslint/config'); +const { FlatCompat } = requireFromCwd('@eslint/eslintrc'); const js = requireFromCwd('@eslint/js'); +const jsdoc = requireFromCwd('eslint-plugin-jsdoc'); const pluginJest = requireFromCwd('eslint-plugin-jest'); +// const importPlugin = requireFromCwd('eslint-plugin-import'); const globals = requireFromCwd('globals'); const nextVitals = requireFromCwd('eslint-config-next/core-web-vitals'); +// eslint-plugin-import +const compat = new FlatCompat({ + baseDirectory: import.meta.dirname, + resolvePluginsRelativeTo: process.cwd() +}); +const eslintImport = [ + ...compat.config({ + extends: ['plugin:import/recommended'], + settings: { + 'import/resolver': { + next: true, + }, + }, + }), +]; + export default defineConfig([ - globalIgnores(['node_modules/**', '.next/**', 'out/**', 'build/**', 'next-env.d.ts', 'public/sw.js']), + globalIgnores([ + 'node_modules/**', + '.next/**', + 'out/**', + 'build/**', + 'next-env.d.ts', + 'public/sw.js', + '.vscode/**', + '.github/**', + ]), ...nextVitals.map((config) => ({ ...config, - files: ['app/**/*.{js,mjs,cjs,jsx}'], // Specific for next app - })), + files: ['{app,tests}/**/*.{js,mjs,cjs,jsx}'], // Specific for next app + })), + ...eslintImport, // BUG in flat config importPlugin.flatConfigs.recommended (https://github.com/import-js/eslint-plugin-import/issues/3212) { files: ['**/*.{js,mjs,cjs,jsx}'], // For all files - plugins: { js }, - extends: ['js/recommended'], - languageOptions: { globals: globals.node }, - }, { - files: ['**/*.spec.js', '**/*.test.js'], + plugins: { js, jsdoc }, + extends: ['js/recommended', 'jsdoc/flat/recommended'], + languageOptions: { globals: { ...globals.node, ...globals.browser } }, + rules: { + 'prefer-destructuring': [ + 'error', + { + object: true, + array: false, + }, + ], + 'no-console': [ + 'error', + { + allow: ['warn', 'error'], + }, + ], + 'no-unused-vars': [ + 'error', + { + varsIgnorePattern: '^_', + argsIgnorePattern: '^_', + args: 'after-used', + }, + ], + 'no-underscore-dangle': [ + 'error', + { + allowAfterThis: true, + }, + ], + 'jsdoc/require-jsdoc': 0, + 'jsdoc/reject-any-type': 0, + 'jsdoc/reject-function-type': 0, + }, + }, + { + files: ['tests/**/*.{js,mjs,cjs,jsx}', '**/*.spec.js', '**/*.test.js'], plugins: { jest: pluginJest }, + extends: ['jest/recommended'], languageOptions: { globals: pluginJest.environments.globals.globals, }, }, -]); \ No newline at end of file +]); diff --git a/configurations/node_linters/packages-linters.txt b/configurations/node_linters/packages-linters.txt index 35e14b4..9922e5b 100644 --- a/configurations/node_linters/packages-linters.txt +++ b/configurations/node_linters/packages-linters.txt @@ -3,5 +3,9 @@ eslint @eslint/js eslint-config-next eslint-plugin-jest +eslint-plugin-import +eslint-import-resolver-next +eslint-import-resolver-typescript +eslint-plugin-jsdoc stylelint stylelint-config-standard-scss \ No newline at end of file