diff --git a/.github/workflows/brakeman-scan-core.yml b/.github/workflows/brakeman-scan-core.yml index 998d0bedbdff..8ee3db571bd8 100644 --- a/.github/workflows/brakeman-scan-core.yml +++ b/.github/workflows/brakeman-scan-core.yml @@ -25,10 +25,12 @@ jobs: RUBY_GC_HEAP_INIT_SLOTS: 100000 steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Setup Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 - name: Setup Brakeman run: | @@ -44,6 +46,6 @@ jobs: --output output.sarif.json - name: Upload SARIF - uses: github/codeql-action/upload-sarif@v4 + uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4 with: sarif_file: output.sarif.json diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 14dd8d628841..8889a071a85f 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -19,7 +19,7 @@ jobs: steps: - name: "CLA Assistant" if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' - uses: contributor-assistant/github-action@v2.6.1 + uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 env: # https://github.com/contributor-assistant/github-action?tab=readme-ov-file#environmental-variables # Built-in GitHub token to make the API calls for interacting with GitHub. Does not need to be specified the secrets store. diff --git a/.github/workflows/codeql-scan-core.yml b/.github/workflows/codeql-scan-core.yml index 69b60912baa3..5a3b46991845 100644 --- a/.github/workflows/codeql-scan-core.yml +++ b/.github/workflows/codeql-scan-core.yml @@ -31,10 +31,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4 with: config-file: ./.github/codeql/config.yml languages: ${{ matrix.language }} @@ -42,6 +44,6 @@ jobs: queries: security-extended,security-and-quality - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/create-merge-from-previous-release-branch-pr.yml b/.github/workflows/create-merge-from-previous-release-branch-pr.yml index 688ce09e215b..0862bea6c7f5 100644 --- a/.github/workflows/create-merge-from-previous-release-branch-pr.yml +++ b/.github/workflows/create-merge-from-previous-release-branch-pr.yml @@ -59,7 +59,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ env.RELEASE_BRANCH }} diff --git a/.github/workflows/create-merge-release-into-dev-pr.yml b/.github/workflows/create-merge-release-into-dev-pr.yml index 853e71493be6..57e27fb2ac1f 100644 --- a/.github/workflows/create-merge-release-into-dev-pr.yml +++ b/.github/workflows/create-merge-release-into-dev-pr.yml @@ -44,7 +44,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ env.BASE_BRANCH }} diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 03f3c5ae8229..165df9c1f9dd 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -42,11 +42,11 @@ jobs: - dev - "${{ needs.setup.outputs.latest_release_branch }}" steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ matrix.branch }} fetch-depth: 1 - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 with: bundler-cache: true - name: "Set crowdin branch name" @@ -66,7 +66,7 @@ jobs: bundle exec script/i18n/generate_seeders_i18n_source_file fi - name: "Crowdin: upload sources and download translations" - uses: crowdin/github-action@v2 + uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2 with: # Upload current source files upload_sources: true diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 0c52141f1299..1fe39a338369 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -14,11 +14,15 @@ jobs: if: github.repository_owner == 'opf' runs-on: [ubuntu-latest] timeout-minutes: 10 + permissions: + contents: read + pull-requests: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - - uses: ruby/setup-ruby@v1 + persist-credentials: false + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 with: ruby-version: .ruby-version bundler-cache: true diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index e0eddeffe3e8..0d3ed9ba3a27 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -34,9 +34,11 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout repository' - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: 'Dependency Review' - uses: actions/dependency-review-action@v4.9.0 + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5 # Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options. with: comment-summary-in-pr: on-failure diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index dc7c7bf8eefc..8ff82e2ac7fc 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -10,18 +10,23 @@ on: description: "The tag to release. Note that this happens by default on the tag push. Only run this action when something went wrong!" required: false +permissions: {} + jobs: compute-inputs: if: github.repository == 'opf/openproject' runs-on: ubuntu-latest + permissions: + contents: read outputs: tag: ${{ steps.compute.outputs.tag }} branch: ${{ steps.compute.outputs.branch }} steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ github.ref }} + persist-credentials: false - name: Compute tags and branch id: compute env: @@ -47,6 +52,8 @@ jobs: build: needs: [compute-inputs] + permissions: + contents: read uses: ./.github/workflows/docker.yml with: branch: ${{ needs.compute-inputs.outputs.branch }} diff --git a/.github/workflows/docker-scheduled.yml b/.github/workflows/docker-scheduled.yml index ef7d7ac25d80..fa87a58375b6 100644 --- a/.github/workflows/docker-scheduled.yml +++ b/.github/workflows/docker-scheduled.yml @@ -6,9 +6,13 @@ on: - cron: "20 2 * * *" # Daily at 02:20 workflow_dispatch: # Allow manual trigger +permissions: {} + jobs: build-dev: if: github.repository == 'opf/openproject' + permissions: + contents: read uses: ./.github/workflows/docker.yml with: branch: dev @@ -20,9 +24,12 @@ jobs: OPS_MAIL_SMTP_TOKEN: ${{ secrets.OPS_MAIL_SMTP_TOKEN }} build-release-candidate: if: github.repository == 'opf/openproject' + permissions: + contents: read # References to release/X.Y and X.Y-rc are being # updated from the devkit (UpdateWorkflows step) whenever a new release branch is created - uses: opf/openproject/.github/workflows/docker.yml@release/17.4 + # Ignore unpinned uses as we reference the latest release branch and change it + uses: opf/openproject/.github/workflows/docker.yml@release/17.4 # zizmor: ignore[unpinned-uses] with: branch: release/17.4 tag: 17.4-rc diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c7f18f16f038..af04f3407fff 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -62,9 +62,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ inputs.branch }} + persist-credentials: false - name: Set version outputs and convert tags id: extract_version env: @@ -85,23 +86,27 @@ jobs: fi - name: Verify outputs run: | - if [ -z "${{ steps.extract_version.outputs.version }}" ]; then + if [ -z "${STEPS_EXTRACT_VERSION_OUTPUTS_VERSION}" ]; then echo "Error: version output is empty" exit 1 fi - if [ -z "${{ steps.extract_version.outputs.docker_tags }}" ]; then + if [ -z "${STEPS_EXTRACT_VERSION_OUTPUTS_DOCKER_TAGS}" ]; then echo "Error: docker_tags output is empty" exit 1 fi - if [ -z "${{ steps.extract_version.outputs.registry_image }}" ]; then + if [ -z "${STEPS_EXTRACT_VERSION_OUTPUTS_REGISTRY_IMAGE}" ]; then echo "Error: registry_image output is empty" exit 1 fi - echo "✓ version: ${{ steps.extract_version.outputs.version }}" - echo "✓ docker_tags: ${{ steps.extract_version.outputs.docker_tags }}" - echo "✓ registry_image: ${{ steps.extract_version.outputs.registry_image }}" + echo "✓ version: ${STEPS_EXTRACT_VERSION_OUTPUTS_VERSION}" + echo "✓ docker_tags: ${STEPS_EXTRACT_VERSION_OUTPUTS_DOCKER_TAGS}" + echo "✓ registry_image: ${STEPS_EXTRACT_VERSION_OUTPUTS_REGISTRY_IMAGE}" + env: + STEPS_EXTRACT_VERSION_OUTPUTS_VERSION: ${{ steps.extract_version.outputs.version }} + STEPS_EXTRACT_VERSION_OUTPUTS_DOCKER_TAGS: ${{ steps.extract_version.outputs.docker_tags }} + STEPS_EXTRACT_VERSION_OUTPUTS_REGISTRY_IMAGE: ${{ steps.extract_version.outputs.registry_image }} - name: Cache NPM - uses: runs-on/cache@v5 + uses: runs-on/cache@bd330c5a5f6cbb837823ee25864f3c71a211c2e3 # v5 with: path: | frontend/node_modules @@ -109,17 +114,18 @@ jobs: key: nodejs-x64-${{ hashFiles('**/package-lock.json') }} restore-keys: nodejs-x64- - name: Cache angular - uses: runs-on/cache@v5 + uses: runs-on/cache@bd330c5a5f6cbb837823ee25864f3c71a211c2e3 # v5 with: path: frontend/.angular key: angular-${{ github.ref }} restore-keys: angular- - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 with: bundler-cache: true - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 22.15 + package-manager-cache: false cache: npm cache-dependency-path: | package-lock.json @@ -129,7 +135,7 @@ jobs: ./docker/prod/setup/precompile-assets.sh # public/assets will be saved as artifact, so temporarily copying config file there as well cp config/frontend_assets.manifest.json public/assets/frontend_assets.manifest.json - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: path: public/ name: public-assets-${{ inputs.tag }}-${{ github.sha }} @@ -177,14 +183,15 @@ jobs: runner: runner=4cpu-linux-arm64 steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ inputs.branch }} + persist-credentials: false - name: Prepare docker files run: | cp ./docker/prod/Dockerfile ./Dockerfile - name: Download precompiled public assets - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: public-assets-${{ inputs.tag }}-${{ github.sha }} path: public/ @@ -198,23 +205,25 @@ jobs: echo '!/public/assets' >> .dockerignore echo '!/config/frontend_assets.manifest.json' >> .dockerignore - name: Add build information + env: + INPUTS_BRANCH: ${{ inputs.branch }} run: | - echo "${{ inputs.branch }}" > PRODUCT_VERSION - echo "https://github.com/opf/openproject/commits/${{ inputs.branch }}" > PRODUCT_URL + echo "$INPUTS_BRANCH" > PRODUCT_VERSION + echo "https://github.com/opf/openproject/commits/$INPUTS_BRANCH" > PRODUCT_URL date -u +"%Y-%m-%dT%H:%M:%SZ" > RELEASE_DATE - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Login to Docker Hub - uses: docker/login-action@v4 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Docker meta id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 with: context: git labels: | @@ -229,7 +238,7 @@ jobs: ${{ needs.setup.outputs.registry_image }} - name: Restore vendor/bundle id: restore-vendor-bundle - uses: actions/cache/restore@v5 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | vendor/bundle @@ -239,7 +248,7 @@ jobs: sed -i 's/vendor\/bundle//g' .dockerignore - name: Build image id: build - uses: docker/build-push-action@v7 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 with: context: . platforms: ${{ matrix.platform }} @@ -254,8 +263,10 @@ jobs: cache-from: type=s3,blobs_prefix=cache/${{ github.repository }}/trixie,manifests_prefix=cache/${{ github.repository }}/trixie,region=${{ env.RUNS_ON_AWS_REGION }},bucket=${{ env.RUNS_ON_S3_BUCKET_CACHE }} cache-to: type=s3,blobs_prefix=cache/${{ github.repository }}/trixie,manifests_prefix=cache/${{ github.repository }}/trixie,region=${{ env.RUNS_ON_AWS_REGION }},bucket=${{ env.RUNS_ON_S3_BUCKET_CACHE }},mode=max - name: Extract vendor/bundle from container + env: + STEPS_BUILD_OUTPUTS_IMAGEID: ${{ steps.build.outputs.imageid }} run: | - docker create --name bundle ${{ steps.build.outputs.imageid }} + docker create --name bundle $STEPS_BUILD_OUTPUTS_IMAGEID if [ -d vendor/bundle ]; then mv vendor/bundle vendor/bundle.bak fi @@ -263,7 +274,7 @@ jobs: docker rm bundle - name: Save vendor/bundle id: save-vendor-bundle - uses: actions/cache/save@v5 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | vendor/bundle @@ -276,14 +287,16 @@ jobs: mv vendor/bundle.bak vendor/bundle fi - name: Validate image + env: + STEPS_BUILD_OUTPUTS_IMAGEID: ${{ steps.build.outputs.imageid }} run: | ./script/ci/docker_validate_image.sh \ - --image "${{ steps.build.outputs.imageid }}" \ + --image "$STEPS_BUILD_OUTPUTS_IMAGEID" \ --target "${{ matrix.target }}" \ --platform "${{ matrix.platform }}" - name: Push image id: push - uses: docker/build-push-action@v7 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 with: context: . platforms: ${{ matrix.platform }} @@ -295,12 +308,14 @@ jobs: cache-from: type=s3,blobs_prefix=cache/${{ github.repository }}/trixie,manifests_prefix=cache/${{ github.repository }}/trixie,region=${{ env.RUNS_ON_AWS_REGION }},bucket=${{ env.RUNS_ON_S3_BUCKET_CACHE }} cache-to: type=s3,blobs_prefix=cache/${{ github.repository }}/trixie,manifests_prefix=cache/${{ github.repository }}/trixie,region=${{ env.RUNS_ON_AWS_REGION }},bucket=${{ env.RUNS_ON_S3_BUCKET_CACHE }},mode=max - name: Export digest + env: + STEPS_PUSH_OUTPUTS_DIGEST: ${{ steps.push.outputs.digest }} run: | mkdir -p /tmp/digests - digest="${{ steps.push.outputs.digest }}" + digest="$STEPS_PUSH_OUTPUTS_DIGEST" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: digests-${{ inputs.tag }}-${{ matrix.target }}--${{ matrix.digest }} path: /tmp/digests/* @@ -316,13 +331,13 @@ jobs: - build steps: - name: Merge digests - uses: actions/upload-artifact/merge@v7 + uses: actions/upload-artifact/merge@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: pattern: "digests-${{ inputs.tag }}-${{ matrix.target }}--*" overwrite: true name: "merged-digests-${{ inputs.tag }}-${{ matrix.target }}-${{ github.run_number }}-${{ github.run_attempt }}" - name: Download digests - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: "merged-digests-${{ inputs.tag }}-${{ matrix.target }}-${{ github.run_number }}-${{ github.run_attempt }}" path: /tmp/digests @@ -333,10 +348,10 @@ jobs: if [ "$suffix" = "-all-in-one" ]; then suffix="" ; fi echo "suffix=$suffix" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Docker meta id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 with: images: ${{ needs.setup.outputs.registry_image }} labels: | @@ -351,7 +366,7 @@ jobs: tags: | ${{ needs.setup.outputs.docker_tags }} - name: Login to Docker Hub - uses: docker/login-action@v4 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} @@ -359,10 +374,15 @@ jobs: working-directory: /tmp/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ needs.setup.outputs.registry_image }}@sha256:%s ' *) + $(printf '${NEEDS_SETUP_OUTPUTS_REGISTRY_IMAGE}@sha256:%s ' *) + env: + NEEDS_SETUP_OUTPUTS_REGISTRY_IMAGE: ${{ needs.setup.outputs.registry_image }} - name: Inspect image run: | - docker buildx imagetools inspect ${{ needs.setup.outputs.registry_image }}:${{ steps.meta.outputs.version }} + docker buildx imagetools inspect ${NEEDS_SETUP_OUTPUTS_REGISTRY_IMAGE}:${STEPS_META_OUTPUTS_VERSION} + env: + NEEDS_SETUP_OUTPUTS_REGISTRY_IMAGE: ${{ needs.setup.outputs.registry_image }} + STEPS_META_OUTPUTS_VERSION: ${{ steps.meta.outputs.version }} notify-failure: needs: [setup, build, merge] if: ${{ always() && contains(needs.*.result, 'failure') }} diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 3de0e880f005..b9545e43ed11 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -17,8 +17,10 @@ jobs: name: Check internal links in documentation runs-on: [ubuntu-latest] steps: - - uses: actions/checkout@v6 - - uses: ruby/setup-ruby@v1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 with: bundler-cache: true - run: bundle exec ./script/docs/check_links @@ -26,14 +28,18 @@ jobs: name: Check README.md case in documentation runs-on: [ubuntu-latest] steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - run: script/docs/check_readme_case docs-readme-yaml-header-syntax-check: name: Check README.md YAML header syntax in documentation runs-on: [ubuntu-latest] steps: - - uses: actions/checkout@v6 - - uses: ruby/setup-ruby@v1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 with: bundler-cache: true - run: script/docs/check_readme_yaml_header_syntax diff --git a/.github/workflows/email-notification.yml b/.github/workflows/email-notification.yml index 99326b91b4ee..cf5dba17ae0d 100644 --- a/.github/workflows/email-notification.yml +++ b/.github/workflows/email-notification.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Send mail - uses: dawidd6/action-send-mail@v16 + uses: dawidd6/action-send-mail@d38f3f7cd391cdebfe0d38efc3998b935e951c4f # v16 with: subject: ${{ inputs.subject }} body: ${{ inputs.body }} diff --git a/.github/workflows/eslint-core.yml b/.github/workflows/eslint-core.yml index 06372ca61b95..19e265162909 100644 --- a/.github/workflows/eslint-core.yml +++ b/.github/workflows/eslint-core.yml @@ -13,16 +13,21 @@ jobs: eslint: name: eslint runs-on: ubuntu-latest + permissions: + contents: read + checks: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - - uses: actions/setup-node@v6 + persist-credentials: false + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: '22.15' + package-manager-cache: false cache: npm cache-dependency-path: frontend/package-lock.json - - uses: reviewdog/action-eslint@v1 + - uses: reviewdog/action-eslint@556a3fdaf8b4201d4d74d406013386aa4f7dab96 # v1 with: reporter: github-pr-check workdir: 'frontend/' diff --git a/.github/workflows/hocuspocus-docker.yml b/.github/workflows/hocuspocus-docker.yml index 7dca9a23cf09..5fb928107f4a 100644 --- a/.github/workflows/hocuspocus-docker.yml +++ b/.github/workflows/hocuspocus-docker.yml @@ -54,11 +54,14 @@ jobs: build: if: github.repository == 'opf/openproject' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ inputs.branch }} # this is empty on push, so it just checks out the event SHA in that case + persist-credentials: false - name: Include Git SHA in package.json version id: short-sha run: | @@ -103,20 +106,20 @@ jobs: [ -n "$MAJOR_TAG" ] && echo $REGISTRY:$MAJOR_TAG >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT - name: Login to Docker Hub - uses: docker/login-action@v4 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Build and push id: build - uses: docker/build-push-action@v7 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 with: context: extensions/op-blocknote-hocuspocus push: true @@ -128,11 +131,13 @@ jobs: - name: Summarize build run: | echo "Built and pushed the following tags:" >> $GITHUB_STEP_SUMMARY - echo "${{ steps.tags.outputs.tags }}" >> $GITHUB_STEP_SUMMARY + echo "${STEPS_TAGS_OUTPUTS_TAGS}" >> $GITHUB_STEP_SUMMARY + env: + STEPS_TAGS_OUTPUTS_TAGS: ${{ steps.tags.outputs.tags }} - name: Generate GHA token id: generate-gha-token - uses: actions/create-github-app-token@v3 + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3 with: app-id: ${{ vars.DEPLOY_APP_ID }} private-key: ${{ secrets.DEPLOY_APP_PRIVATE_KEY }} diff --git a/.github/workflows/hocuspocus-test.yml b/.github/workflows/hocuspocus-test.yml index 17af080cb100..4977b3029abc 100644 --- a/.github/workflows/hocuspocus-test.yml +++ b/.github/workflows/hocuspocus-test.yml @@ -18,13 +18,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: '22.19' + package-manager-cache: false cache: npm cache-dependency-path: extensions/op-blocknote-hocuspocus/package-lock.json diff --git a/.github/workflows/i18n-tasks.yml b/.github/workflows/i18n-tasks.yml index 07dcacf8cb2f..e618e573fa06 100644 --- a/.github/workflows/i18n-tasks.yml +++ b/.github/workflows/i18n-tasks.yml @@ -28,10 +28,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Setup Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 - name: Setup i18n-tasks run: | diff --git a/.github/workflows/openapi.yaml b/.github/workflows/openapi.yaml index bd2218b39d89..57d427055d7a 100644 --- a/.github/workflows/openapi.yaml +++ b/.github/workflows/openapi.yaml @@ -17,12 +17,17 @@ jobs: name: APIv3 specification (OpenAPI 3.0) if: github.repository_owner == 'opf' runs-on: [ubuntu-latest] + permissions: + contents: read steps: - - uses: actions/checkout@v6 - - uses: ruby/setup-ruby@v1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - bundler-cache: true - - uses: actions/setup-node@v6 + persist-credentials: false + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 + with: + bundler-cache: false + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: '20' + package-manager-cache: false - run: ./script/api/validate_spec diff --git a/.github/workflows/packager.yml b/.github/workflows/packager.yml index 095491ece13c..96c2e9ae8b8d 100644 --- a/.github/workflows/packager.yml +++ b/.github/workflows/packager.yml @@ -15,6 +15,8 @@ jobs: if: github.repository == 'opf/openproject' name: ${{ matrix.target }} runs-on: ubuntu-latest + permissions: + contents: read services: postgres: image: postgres:16 @@ -35,22 +37,23 @@ jobs: - el:9 - sles:15 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false - name: Setup id: setup run: | VERSION=$(ruby -r ./lib/open_project/version.rb -e "puts OpenProject::VERSION") echo "version=$VERSION" >> $GITHUB_OUTPUT - if [[ "${{ github.ref_type }}" == "tag" ]]; then + if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then MAJOR=$(ruby -r ./lib/open_project/version.rb -e "puts OpenProject::VERSION::MAJOR") echo "channel=stable/${MAJOR}" >> $GITHUB_OUTPUT else - echo "channel=${{ github.ref_name }}" >> $GITHUB_OUTPUT + echo "channel=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT fi - name: Package - uses: pkgr/action/package@main + uses: pkgr/action/package@c5666febcd31750da6428042193fc5b2fb765435 # main id: package with: target: ${{ matrix.target }} @@ -60,7 +63,7 @@ jobs: env: | DATABASE_URL=postgres://app:p4ssw0rd@127.0.0.1:5432/app - name: Publish - uses: pkgr/action/publish@main + uses: pkgr/action/publish@c5666febcd31750da6428042193fc5b2fb765435 # main with: target: ${{ matrix.target }} token: ${{ secrets.PACKAGER_PUBLISH_TOKEN }} diff --git a/.github/workflows/pullpreview.yml b/.github/workflows/pullpreview.yml index bc62f22c36fc..03e5606bdcda 100644 --- a/.github/workflows/pullpreview.yml +++ b/.github/workflows/pullpreview.yml @@ -19,7 +19,9 @@ jobs: runs-on: ubuntu-slim timeout-minutes: 60 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Generate .env.pullpreview file run: | echo "OPENPROJECT_SEED_ADMIN_USER_PASSWORD_RESET=false" >> .env.pullpreview @@ -41,7 +43,7 @@ jobs: cp ./docker/pullpreview/docker-compose.yml ./docker-compose.pullpreview.yml cp ./docker/pullpreview/Caddyfile ./Caddyfile cp ./docker/prod/Dockerfile ./Dockerfile - - uses: pullpreview/action@v6 + - uses: pullpreview/action@0dc5a741255c0afdd5a9e23e9264d6376652ec8e # v6 with: # allows to ssh to the instance using our GitHub ssh key admins: "crohr,HDinger,machisuji,oliverguenther,ulferts,wielinde,cbliard,@collaborators/push" diff --git a/.github/workflows/rubocop-core.yml b/.github/workflows/rubocop-core.yml index 8a2b2b97295c..3071a5aeb7e4 100644 --- a/.github/workflows/rubocop-core.yml +++ b/.github/workflows/rubocop-core.yml @@ -9,13 +9,18 @@ jobs: rubocop: name: rubocop runs-on: ubuntu-latest + permissions: + contents: read + checks: write steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 - name: Run Rubocop - uses: reviewdog/action-rubocop@v2 + uses: reviewdog/action-rubocop@b6d5e953a5fc0bf3ab65254e77730ea2174d6d6d # v2 with: github_token: ${{ secrets.github_token }} rubocop_version: gemfile @@ -32,7 +37,7 @@ jobs: - name: Install erb_lint run: gem install -N erb_lint erblint-github - name: Run erb-lint - uses: tk0miya/action-erblint@v1 + uses: tk0miya/action-erblint@44c5fe3552356fe8bff23f30d534aa4258aa3f7b # v1 with: github_token: ${{ secrets.github_token }} reporter: github-pr-check diff --git a/.github/workflows/seed-all-locales.yml b/.github/workflows/seed-all-locales.yml index 8d90c03eb346..a6fea961d3d2 100644 --- a/.github/workflows/seed-all-locales.yml +++ b/.github/workflows/seed-all-locales.yml @@ -41,15 +41,18 @@ jobs: fi - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ steps.use_input_or_find_latest_release.outputs.ref }} + persist-credentials: false - name: Print ref to summary run: | SHA=$(git rev-parse HEAD) SHORT_SHA=$(git rev-parse --short HEAD) - echo "Testing seeding on **${{ steps.use_input_or_find_latest_release.outputs.ref }}** ([$SHORT_SHA](https://github.com/opf/openproject/commit/$SHA))" >> "$GITHUB_STEP_SUMMARY" + echo "Testing seeding on **${STEPS_USE_INPUT_OR_FIND_LATEST_RELEASE_OUTPUTS_REF}** ([$SHORT_SHA](https://github.com/opf/openproject/commit/$SHA))" >> "$GITHUB_STEP_SUMMARY" + env: + STEPS_USE_INPUT_OR_FIND_LATEST_RELEASE_OUTPUTS_REF: ${{ steps.use_input_or_find_latest_release.outputs.ref }} - name: List available locales id: list @@ -86,15 +89,16 @@ jobs: RAILS_ENV: development steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ needs.prepare.outputs.ref }} + persist-credentials: false - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libpq-dev - name: Setup Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 with: bundler-cache: true @@ -109,7 +113,8 @@ jobs: env: SILENCE_SQL_LOGS: 1 OPENPROJECT_EDITION: ${{ matrix.edition }} - run: ruby script/i18n/test_seed_all_locales ${{ matrix.locale }} + MATRIX_LOCALE: ${{ matrix.locale }} + run: ruby script/i18n/test_seed_all_locales ${MATRIX_LOCALE} notify-failure: needs: [prepare, seed] diff --git a/.github/workflows/sync-internal-fork.yml b/.github/workflows/sync-internal-fork.yml index 454ce6f4ddc7..fe526b8979c7 100644 --- a/.github/workflows/sync-internal-fork.yml +++ b/.github/workflows/sync-internal-fork.yml @@ -10,10 +10,11 @@ jobs: # Only run in the private fork, never in the public repo if: github.repository == 'opf/openproject-internal-fork' runs-on: ubuntu-latest - + permissions: + contents: write steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 token: ${{ secrets.OPENPROJECTCI_GH_FORK_TOKEN }} diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index bff718303ffc..b0d8c3d7633a 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -32,13 +32,15 @@ jobs: DOCKER_BUILDKIT: 1 CI_RETRY_COUNT: 3 steps: - - uses: runs-on/action@v2 + - uses: runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0 # v2.1.0 with: metrics: cpu,memory,disk,io,network - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Cache DOCKER id: cache_docker - uses: runs-on/cache@v5 + uses: runs-on/cache@bd330c5a5f6cbb837823ee25864f3c71a211c2e3 # v5 with: path: cache/docker # Note: no restore keys since whenever the files below change, we want to rebuild the full image from scratch @@ -47,28 +49,28 @@ jobs: if: steps.cache_docker.outputs.cache-hit == 'true' run: docker load -i cache/docker/image.tar - name: Cache GEM - uses: runs-on/cache@v5 + uses: runs-on/cache@bd330c5a5f6cbb837823ee25864f3c71a211c2e3 # v5 with: path: cache/bundle key: gem-trixie-${{ hashFiles('.ruby-version') }}-${{ hashFiles('Gemfile.lock') }} restore-keys: | gem-trixie-${{ hashFiles('.ruby-version') }}- - name: Cache NPM - uses: runs-on/cache@v5 + uses: runs-on/cache@bd330c5a5f6cbb837823ee25864f3c71a211c2e3 # v5 with: path: cache/node key: node-${{ hashFiles('package.json', 'frontend/package-lock.json') }} restore-keys: | node- - name: Cache ANGULAR - uses: runs-on/cache@v5 + uses: runs-on/cache@bd330c5a5f6cbb837823ee25864f3c71a211c2e3 # v5 with: path: cache/angular key: angular-${{ hashFiles('package.json', 'frontend/package-lock.json') }} restore-keys: | angular- - name: Cache TEST RUNTIME - uses: runs-on/cache@v5 + uses: runs-on/cache@bd330c5a5f6cbb837823ee25864f3c71a211c2e3 # v5 with: path: cache/runtime-logs key: runtime-logs-${{ github.head_ref || github.ref }}-${{ github.sha }} diff --git a/.github/workflows/test-frontend-unit.yml b/.github/workflows/test-frontend-unit.yml index dc70bbc92e4c..86a60d08db21 100644 --- a/.github/workflows/test-frontend-unit.yml +++ b/.github/workflows/test-frontend-unit.yml @@ -29,17 +29,17 @@ jobs: units: name: Units runs-on: ubuntu-latest - + steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - - - uses: actions/setup-node@v6 + persist-credentials: false + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: '22.15' - cache: npm - cache-dependency-path: frontend/package-lock.json + package-manager-cache: false - name: Install Dependencies id: npm-i diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index 3798a0269fc0..05b831e19f63 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -19,10 +19,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1 - name: Verify linked version matches core version id: version-check @@ -32,7 +34,7 @@ jobs: - name: Add comment if versions differ if: steps.version-check.outputs.version_mismatch == 'true' - uses: marocchino/sticky-pull-request-comment@v3 + uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff # v3 with: header: version-mismatch-comment message: | @@ -50,7 +52,7 @@ jobs: - The work package version OR your pull request target branch is correct - name: Version check passed if: steps.version-check.outputs.version_mismatch != 'true' - uses: marocchino/sticky-pull-request-comment@v3 + uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff # v3 with: header: version-mismatch-comment delete: true diff --git a/.github/workflows/zizmor-scan.yml b/.github/workflows/zizmor-scan.yml new file mode 100644 index 000000000000..fc5f85283d3c --- /dev/null +++ b/.github/workflows/zizmor-scan.yml @@ -0,0 +1,30 @@ +name: GitHub Actions Security Analysis with zizmor 🌈 + +on: + pull_request: + paths: + - ".github/workflows/**" + - ".github/actions/**" + schedule: + - cron: "0 2 * * *" + workflow_dispatch: + +permissions: {} + +jobs: + zizmor: + name: Run zizmor 🌈 + runs-on: ubuntu-latest + if: github.repository_owner == 'opf' + permissions: + security-events: write # Required for upload-sarif + contents: read + actions: read + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor 🌈 + uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 000000000000..d4ca85896d8a --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,16 @@ +rules: + artipacked: + ignore: + # These workflows intentionally persist credentials because they push commits/branches + - create-merge-from-previous-release-branch-pr.yml + - create-merge-release-into-dev-pr.yml + - crowdin.yml + - sync-internal-fork.yml + dangerous-triggers: + ignore: + # CLA workflow intentionally uses pull_request_target to handle outside contributors + - cla.yml + cache-poisoning: + ignore: + # Frontend unit tests cache npm packages; only triggered on push/PR from trusted branches + - test-frontend-unit.yml