Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
318 changes: 190 additions & 128 deletions .github/workflows/README.md

Large diffs are not rendered by default.

229 changes: 229 additions & 0 deletions .github/workflows/docker_apply_cache.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
name: "03 Maintain: Apply Package Cache"
description: "Generate the package cache for the lesson after a pull request has been merged or via manual trigger, and cache in S3 or GitHub"
on:
workflow_dispatch:
inputs:
name:
description: 'Who triggered this build?'
required: true
default: 'Maintainer (via GitHub)'
pull_request:
types:
- closed
branches:
- main

# queue cache runs
concurrency:
group: docker-apply-cache
cancel-in-progress: false

jobs:
preflight:
name: "Preflight: PR or Manual Trigger?"
runs-on: ubuntu-latest
outputs:
do-apply: ${{ steps.check.outputs.merged_or_manual }}
steps:
- name: "Should we run cache application?"
id: check
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ||
("${{ github.ref }}" == "refs/heads/main" && "${{ github.event.action }}" == "closed" && "${{ github.event.pull_request.merged }}" == "true") ]]; then
echo "merged_or_manual=true" >> $GITHUB_OUTPUT
else
echo "This was not a manual trigger and no PR was merged. No action taken."
echo "merged_or_manual=false" >> $GITHUB_OUTPUT
fi
shell: bash

check-renv:
Comment on lines +23 to +40

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}

Copilot Autofix

AI about 2 months ago

In general, the fix is to add an explicit permissions block to the workflow, at either the workflow root (to apply to all jobs) or to individual jobs, and set the minimum required permissions. For jobs that do not need GITHUB_TOKEN at all, permissions: {} or permissions: read-all or permissions: contents: read are typical; for jobs that need to assume AWS roles via OIDC, id-token: write must be granted.

The best fix here without changing functionality is:

  • Add a root-level permissions block that restricts the default token permissions for all jobs to read-only repository contents:
    permissions: contents: read.
  • Keep the existing, more specific permissions block on check-renv (id-token: write), which will override the root default for that job.
  • The preflight, no-renv-cache-used, renv-cache-available, update-renv-cache, and record-cache-result jobs do not appear to need write permissions, so inheriting contents: read is safe and minimal.

Concretely, edit .github/workflows/docker_apply_cache.yaml:

  • Insert a root-level permissions block after the on: section (after line 14–15, before concurrency:), with:
    permissions:
      contents: read

No new methods, imports, or other definitions are needed; this is pure workflow configuration.

Suggested changeset 1
.github/workflows/docker_apply_cache.yaml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/docker_apply_cache.yaml b/.github/workflows/docker_apply_cache.yaml
--- a/.github/workflows/docker_apply_cache.yaml
+++ b/.github/workflows/docker_apply_cache.yaml
@@ -13,6 +13,9 @@
     branches:
       - main
 
+permissions:
+  contents: read
+
 # queue cache runs
 concurrency:
   group: docker-apply-cache
EOF
@@ -13,6 +13,9 @@
branches:
- main

permissions:
contents: read

# queue cache runs
concurrency:
group: docker-apply-cache
Copilot is powered by AI and may make mistakes. Always verify output.
name: "Check If We Need {renv}"
runs-on: ubuntu-latest
needs: preflight
if: needs.preflight.outputs.do-apply == 'true'
permissions:
id-token: write
outputs:
renv-needed: ${{ steps.check-for-renv.outputs.renv-needed }}
renv-cache-hashsum: ${{ steps.check-for-renv.outputs.renv-cache-hashsum }}
renv-cache-available: ${{ steps.check-for-renv.outputs.renv-cache-available }}
steps:
- name: "Check for renv"
id: check-for-renv
uses: carpentries/actions/renv-checks@main
with:
role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }}
aws-region: ${{ secrets.AWS_GH_OIDC_REGION }}
WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG || 'latest' }}
token: ${{ secrets.GITHUB_TOKEN }}

no-renv-cache-used:
name: "No renv cache used"
runs-on: ubuntu-latest
needs: check-renv
if: needs.check-renv.outputs.renv-needed != 'true'
steps:
- name: "No renv cache needed"
run: echo "No renv cache needed for this lesson"

renv-cache-available:
Comment on lines +62 to +70

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}

Copilot Autofix

AI about 2 months ago

In general, fix this by explicitly setting a restrictive permissions block at the workflow root (so it applies to all jobs by default) and then overriding it only in jobs that require broader or specialized permissions. For jobs that do not need to write to the repository or other resources via GITHUB_TOKEN, permissions: contents: read (or even permissions: {} if truly no token is needed) is sufficient.

For this file, the best minimal change without altering functionality is:

  • Add a root-level permissions block after the on: section, setting contents: read. This will constrain the default GITHUB_TOKEN permissions for all jobs.
  • Keep the existing permissions block in the check-renv job as-is; GitHub will merge/override appropriately, and id-token: write is still allowed alongside the root defaults.
  • No changes are needed per-job (including no-renv-cache-used), since the root-level block is enough to satisfy the CodeQL warning and enforce least privilege.

Concretely, edit .github/workflows/docker_apply_cache.yaml to insert:

permissions:
  contents: read

between the on: block (ending at line 14) and the concurrency: block (starting at line 17). No imports or external libraries are involved.

Suggested changeset 1
.github/workflows/docker_apply_cache.yaml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/docker_apply_cache.yaml b/.github/workflows/docker_apply_cache.yaml
--- a/.github/workflows/docker_apply_cache.yaml
+++ b/.github/workflows/docker_apply_cache.yaml
@@ -13,6 +13,9 @@
     branches:
       - main
 
+permissions:
+  contents: read
+
 # queue cache runs
 concurrency:
   group: docker-apply-cache
EOF
@@ -13,6 +13,9 @@
branches:
- main

permissions:
contents: read

# queue cache runs
concurrency:
group: docker-apply-cache
Copilot is powered by AI and may make mistakes. Always verify output.
name: "renv cache available"
runs-on: ubuntu-latest
needs: check-renv
if: needs.check-renv.outputs.renv-cache-available == 'true'
steps:
- name: "renv cache available"
run: echo "renv cache available for this lesson"

update-renv-cache:
Comment on lines +71 to +79

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}

Copilot Autofix

AI about 2 months ago

In general, the fix is to explicitly add a permissions: block to the workflow or each job, setting the minimum required scopes (often contents: read, or even permissions: {} / permissions: read-all / permissions: write-all depending on needs). For jobs that do not require the GITHUB_TOKEN at all (no repository writes, no API calls, no actions that implicitly need it), we can set permissions: {} (or permissions: none on newer runners) at the job level to prevent the token from being granted.

In this specific workflow snippet, the renv-cache-available job only runs a single shell command that echoes a message and does not interact with the GitHub API, repository contents, or other securable resources. Therefore, the best minimal fix, without changing any behavior, is to add an explicit permissions: {} block to that job so that CodeQL recognizes that this job’s GITHUB_TOKEN is fully disabled. We will insert this directly under the job header (near the other job-level keys like runs-on and needs). Other jobs (e.g., update-renv-cache, record-cache-result) may legitimately require some permissions, but the error CodeQL highlighted is specifically for the renv-cache-available job; we will confine our change to that job only as per the instructions.

Suggested changeset 1
.github/workflows/docker_apply_cache.yaml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/docker_apply_cache.yaml b/.github/workflows/docker_apply_cache.yaml
--- a/.github/workflows/docker_apply_cache.yaml
+++ b/.github/workflows/docker_apply_cache.yaml
@@ -72,6 +72,7 @@
     runs-on: ubuntu-latest
     needs: check-renv
     if: needs.check-renv.outputs.renv-cache-available == 'true'
+    permissions: {}
     steps:
       - name: "renv cache available"
         run: echo "renv cache available for this lesson"
EOF
@@ -72,6 +72,7 @@
runs-on: ubuntu-latest
needs: check-renv
if: needs.check-renv.outputs.renv-cache-available == 'true'
permissions: {}
steps:
- name: "renv cache available"
run: echo "renv cache available for this lesson"
Copilot is powered by AI and may make mistakes. Always verify output.
name: "Update renv Cache"
runs-on: ubuntu-latest
needs: check-renv
if: |
needs.check-renv.outputs.renv-needed == 'true' &&
needs.check-renv.outputs.renv-cache-available != 'true' &&
(
github.event_name == 'workflow_dispatch' ||
(
github.event.pull_request.merged == true &&
(
(
contains(
join(github.event.pull_request.labels.*.name, ','),
'type: package cache'
) &&
github.event.pull_request.head.ref == 'update/packages'
)
||
(
contains(
join(github.event.pull_request.labels.*.name, ','),
'type: workflows'
) &&
github.event.pull_request.head.ref == 'update/workflows'
)
||
(
contains(
join(github.event.pull_request.labels.*.name, ','),
'type: docker version'
) &&
github.event.pull_request.head.ref == 'update/workbench-docker-version'
)
)
)
)
permissions:
checks: write
contents: write
pages: write
id-token: write
container:
image: ghcr.io/carpentries/workbench-docker:${{ vars.WORKBENCH_TAG || 'latest' }}
env:
WORKBENCH_PROFILE: "ci"
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
RENV_PATHS_ROOT: /home/rstudio/lesson/renv
RENV_PROFILE: "lesson-requirements"
RENV_VERSION: ${{ needs.check-renv.outputs.renv-cache-hashsum }}
RENV_CONFIG_EXTERNAL_LIBRARIES: "/usr/local/lib/R/site-library"
volumes:
- ${{ github.workspace }}:/home/rstudio/lesson
options: --cpus 2
steps:
- uses: actions/checkout@v4

- name: "Debugging Info"
run: |
echo "Current Directory: $(pwd)"
ls -lah /home/rstudio/.workbench
ls -lah $(pwd)
Rscript -e 'sessionInfo()'
shell: bash

- name: "Mark Repository as Safe"
run: |
git config --global --add safe.directory $(pwd)
shell: bash

- name: "Ensure sandpaper is loadable"
run: |
.libPaths()
library(sandpaper)
shell: Rscript {0}

- name: "Setup Lesson Dependencies"
run: |
Rscript /home/rstudio/.workbench/setup_lesson_deps.R
shell: bash

- name: "Fortify renv Cache"
run: |
Rscript /home/rstudio/.workbench/fortify_renv_cache.R
shell: bash

- name: "Get Container Version Used"
id: wb-vers
uses: carpentries/actions/container-version@main
with:
WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG }}
renv-needed: ${{ needs.check-renv.outputs.renv-needed }}
token: ${{ secrets.GITHUB_TOKEN }}

- name: "Validate Current Org and Workflow"
id: validate-org-workflow
uses: carpentries/actions/validate-org-workflow@main
with:
repo: ${{ github.repository }}
workflow: ${{ github.workflow }}

- name: "Configure AWS credentials via OIDC"
id: aws-creds
env:
role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }}
aws-region: ${{ secrets.AWS_GH_OIDC_REGION }}
if: |
steps.validate-org-workflow.outputs.is_valid == 'true' &&
env.role-to-assume != '' &&
env.aws-region != ''
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
role-to-assume: ${{ env.role-to-assume }}
aws-region: ${{ env.aws-region }}
output-credentials: true

- name: "Upload cache object to S3"
id: upload-cache
uses: carpentries/actions-cache@frog-matchedkey-1
with:
accessKey: ${{ steps.aws-creds.outputs.aws-access-key-id }}
secretKey: ${{ steps.aws-creds.outputs.aws-secret-access-key }}
sessionToken: ${{ steps.aws-creds.outputs.aws-session-token }}
bucket: workbench-docker-caches
path: |
/home/rstudio/lesson/renv
/usr/local/lib/R/site-library
key: ${{ github.repository }}/${{ steps.wb-vers.outputs.container-version }}_renv-${{ needs.check-renv.outputs.renv-cache-hashsum }}
restore-keys:
${{ github.repository }}/${{ steps.wb-vers.outputs.container-version }}_renv-

record-cache-result:
name: "Record Caching Status"
runs-on: ubuntu-latest
needs: [check-renv, update-renv-cache]
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: "Record cache result"

run: |
echo "${{ needs.update-renv-cache.result == 'success' || needs.check-renv.outputs.renv-cache-available == 'true' || 'false' }}" > ${{ github.workspace }}/apply-cache-result
shell: bash

- name: "Upload cache result"
uses: actions/upload-artifact@v4
with:
name: apply-cache-result
path: ${{ github.workspace }}/apply-cache-result
Comment on lines +212 to +229

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}

Copilot Autofix

AI about 2 months ago

In general, the fix is to add an explicit permissions block that limits the GITHUB_TOKEN to the least privileges needed. Since the shown jobs only run shell commands, configure AWS credentials, upload to S3, and upload artifacts (all of which do not require repository write access), we can set contents: read at the workflow level, which is the minimal standard baseline and matches GitHub’s recommended starting point.

The single best fix, without changing existing functionality, is:

  • Add a root-level permissions: block after the on: section, setting contents: read.
  • This applies to all jobs (preflight, check-renv, no-renv-cache-used, renv-cache-available, update-renv-cache, and record-cache-result) unless they override it.
  • No additional imports, methods, or YAML keys are necessary; this is a purely declarative change.

Concretely, in .github/workflows/docker_apply_cache.yaml, insert:

permissions:
  contents: read

between the existing on: block (line 3–14) and the concurrency: block (line 16–19). This keeps behavior identical while ensuring that GITHUB_TOKEN is restricted to read-only access to repository contents.

Suggested changeset 1
.github/workflows/docker_apply_cache.yaml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/docker_apply_cache.yaml b/.github/workflows/docker_apply_cache.yaml
--- a/.github/workflows/docker_apply_cache.yaml
+++ b/.github/workflows/docker_apply_cache.yaml
@@ -13,6 +13,9 @@
     branches:
       - main
 
+permissions:
+  contents: read
+
 # queue cache runs
 concurrency:
   group: docker-apply-cache
EOF
@@ -13,6 +13,9 @@
branches:
- main

permissions:
contents: read

# queue cache runs
concurrency:
group: docker-apply-cache
Copilot is powered by AI and may make mistakes. Always verify output.
161 changes: 161 additions & 0 deletions .github/workflows/docker_build_deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
name: "01 Maintain: Build and Deploy Site"
description: "Build and deploy the lesson site using the carpentries/workbench-docker container"
on:
push:
branches:
- 'main'
- 'l10n_main'
paths-ignore:
- '.github/workflows/**.yaml'
- '.github/workbench-docker-version.txt'
schedule:
- cron: '0 0 * * 2'
workflow_run:
workflows: ["03 Maintain: Apply Package Cache"]
types:
- completed
workflow_dispatch:
inputs:
name:
description: 'Who triggered this build?'
required: true
default: 'Maintainer (via GitHub)'
CACHE_VERSION:
description: 'Optional renv cache version override'
required: false
default: ''
reset:
description: 'Reset cached markdown files'
required: true
default: false
type: boolean
force-skip-manage-deps:
description: 'Skip build-time dependency management'
required: true
default: false
type: boolean

# only one build/deploy at a time
concurrency:
group: docker-build-deploy
cancel-in-progress: true

jobs:
preflight:
name: "Preflight: Schedule, Push, or PR?"
runs-on: ubuntu-latest
outputs:
do-build: ${{ steps.build-check.outputs.do-build }}
renv-needed: ${{ steps.build-check.outputs.renv-needed }}
renv-cache-hashsum: ${{ steps.build-check.outputs.renv-cache-hashsum }}
workbench-container-file-exists: ${{ steps.wb-vers.outputs.workbench-container-file-exists }}
wb-vers: ${{ steps.wb-vers.outputs.container-version }}
last-wb-vers: ${{ steps.wb-vers.outputs.last-container-version }}
workbench-update: ${{ steps.wb-vers.outputs.workbench-update }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: "Should we run build and deploy?"
id: build-check
uses: carpentries/actions/build-preflight@main

- name: "Checkout Lesson"
if: steps.build-check.outputs.do-build == 'true'
uses: actions/checkout@v4

- name: "Get container version info"
id: wb-vers
if: steps.build-check.outputs.do-build == 'true'
uses: carpentries/actions/container-version@main
with:
WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG }}
renv-needed: ${{ steps.build-check.outputs.renv-needed }}
token: ${{ secrets.GITHUB_TOKEN }}

full-build:
Comment on lines +45 to +75

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 23 days ago

In general, the fix is to explicitly define least-privilege permissions for any workflow or job that uses the default GITHUB_TOKEN permissions, instead of relying on repository/organization defaults. For this workflow, the preflight job does not appear to need write access: it runs checks and reads repository contents via actions/checkout and external actions, but the snippet shows no explicit writes (no pushes, PR creations, or artifact uploads). A safe and minimal starting point is contents: read at the job level for preflight.

Concretely, in .github/workflows/docker_build_deploy.yaml, add a permissions: block under the preflight job definition (around line 45–55), aligned with other job keys such as name, runs-on, outputs, and env. Set contents: read so the GITHUB_TOKEN can read the repository (for actions/checkout and any read-only API calls), but cannot write. No additional imports or dependencies are needed; this is a pure YAML/workflow configuration change and preserves all existing functionality while tightening security.

Suggested changeset 1
.github/workflows/docker_build_deploy.yaml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/docker_build_deploy.yaml b/.github/workflows/docker_build_deploy.yaml
--- a/.github/workflows/docker_build_deploy.yaml
+++ b/.github/workflows/docker_build_deploy.yaml
@@ -44,6 +44,8 @@
   preflight:
     name: "Preflight: Schedule, Push, or PR?"
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     outputs:
       do-build: ${{ steps.build-check.outputs.do-build }}
       renv-needed: ${{ steps.build-check.outputs.renv-needed }}
EOF
@@ -44,6 +44,8 @@
preflight:
name: "Preflight: Schedule, Push, or PR?"
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
do-build: ${{ steps.build-check.outputs.do-build }}
renv-needed: ${{ steps.build-check.outputs.renv-needed }}
Copilot is powered by AI and may make mistakes. Always verify output.
name: "Build Full Site"
runs-on: ubuntu-latest
needs: preflight
if: |
needs.preflight.outputs.do-build == 'true' &&
needs.preflight.outputs.workbench-update != 'true'
env:
RENV_EXISTS: ${{ needs.preflight.outputs.renv-needed }}
RENV_HASH: ${{ needs.preflight.outputs.renv-cache-hashsum }}
permissions:
checks: write
contents: write
pages: write
id-token: write
container:
image: ghcr.io/carpentries/workbench-docker:${{ vars.WORKBENCH_TAG || 'latest' }}
env:
WORKBENCH_PROFILE: "ci"
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
RENV_PATHS_ROOT: /home/rstudio/lesson/renv
RENV_PROFILE: "lesson-requirements"
RENV_CONFIG_EXTERNAL_LIBRARIES: "/usr/local/lib/R/site-library"
volumes:
- ${{ github.workspace }}:/home/rstudio/lesson
options: --cpus 1
steps:
- uses: actions/checkout@v4

- name: "Debugging Info"
run: |
cd /home/rstudio/lesson
echo "Current Directory: $(pwd)"
echo "RENV_HASH is $RENV_HASH"
ls -lah /home/rstudio/.workbench
ls -lah $(pwd)
Rscript -e 'sessionInfo()'
shell: bash

- name: "Mark Repository as Safe"
run: |
git config --global --add safe.directory $(pwd)
shell: bash

- name: "Setup Lesson Dependencies"
id: build-container-deps
uses: carpentries/actions/build-container-deps@main
with:
CACHE_VERSION: ${{ vars.CACHE_VERSION || github.event.inputs.CACHE_VERSION || '' }}
WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG || 'latest' }}
LESSON_PATH: ${{ vars.LESSON_PATH || '/home/rstudio/lesson' }}
role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }}
aws-region: ${{ secrets.AWS_GH_OIDC_REGION }}
token: ${{ secrets.GITHUB_TOKEN }}

- name: "Run Container and Build Site"
id: build-and-deploy
uses: carpentries/actions/build-and-deploy@main
with:
reset: ${{ vars.BUILD_RESET || github.event.inputs.reset || 'false' }}
skip-manage-deps: ${{ github.event.inputs.force-skip-manage-deps == 'true' || steps.build-container-deps.outputs.renv-cache-available || steps.build-container-deps.outputs.backup-cache-used || 'false' }}
lang-code: ${{ vars.LANG_CODE || '' }}

update-container-version:
name: "Update container version used"
runs-on: ubuntu-latest
needs: [preflight]
permissions:
actions: write
contents: write
pull-requests: write
id-token: write
if: |
needs.preflight.outputs.do-build == 'true' &&
(
needs.preflight.outputs.workbench-container-file-exists == 'false' ||
needs.preflight.outputs.workbench-update == 'true'
)
steps:
- name: "Record container version used"
uses: carpentries/actions/record-container-version@main
with:
CONTAINER_VER: ${{ needs.preflight.outputs.wb-vers }}
AUTO_MERGE: ${{ vars.AUTO_MERGE_CONTAINER_VERSION_UPDATE || 'true' }}
token: ${{ secrets.GITHUB_TOKEN }}
role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }}
aws-region: ${{ secrets.AWS_GH_OIDC_REGION }}
Loading
Loading