Compare commits
6 Commits
master
...
ci/docs-pr
Author | SHA1 | Date |
---|---|---|
|
202c0102fd | |
|
a0445bbe7c | |
|
d6540de00d | |
|
c63e31c92f | |
|
824fac9a4f | |
|
1d62265f53 |
|
@ -1,100 +1,45 @@
|
|||
name: 'Documentation (run)'
|
||||
name: 'Docs Preview (Deploy)'
|
||||
|
||||
on:
|
||||
# This workflow runs off the primary branch which provides access to the `secrets` context:
|
||||
workflow_run:
|
||||
workflows: ['Documentation (PR)']
|
||||
types:
|
||||
- completed
|
||||
workflow_call:
|
||||
inputs:
|
||||
preview-context:
|
||||
description: 'Preview Metadata (JSON)'
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
NETLIFY_AUTH_TOKEN:
|
||||
required: true
|
||||
NETLIFY_SITE_ID:
|
||||
required: true
|
||||
|
||||
env:
|
||||
BUILD_DIR: ${{ fromJSON( inputs.preview-context ).build_dir }}
|
||||
# PR head SHA (latest commit):
|
||||
PR_HEADSHA: ${{ fromJSON( inputs.preview-context ).pull_request.head_sha }}
|
||||
PR_NUMBER: ${{ fromJSON( inputs.preview-context ).pull_request.number }}
|
||||
# Deploy URL preview prefix (the site name for the prefix is managed at Netlify):
|
||||
PREVIEW_SITE_PREFIX: ${{ fromJSON( inputs.preview-context ).netlify.deploy_prefix }}
|
||||
|
||||
permissions:
|
||||
# Required by `actions/download-artifact`:
|
||||
actions: read
|
||||
# Required by `marocchino/sticky-pull-request-comment`:
|
||||
pull-requests: write
|
||||
# Required by `myrotvorets/set-commit-status-action`:
|
||||
statuses: write
|
||||
|
||||
jobs:
|
||||
# This could have been another step in the `deploy-preview` job and used `GITHUB_ENV` instead of `GITHUB_OUTPUT`.
|
||||
# It was split out into a separate job for a cleaner overview of `deploy-preview` ENV inputs and to minimize noise
|
||||
# from that job related to this workaround (_that is incompatible with PRs from forks_).
|
||||
pr-context:
|
||||
name: 'Restore PR Context'
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
PR_HEADSHA: ${{ steps.set-pr-context.outputs.PR_HEADSHA }}
|
||||
PR_NUMBER: ${{ steps.set-pr-context.outputs.PR_NUMBER }}
|
||||
# Requires a PR event triggered `docs-preview-prepare.yml` workflow run that was successful + ensure the head SHA belongs to an associated PR:
|
||||
# NOTE:
|
||||
# - The `contains` condition checks for event context that is not available when the PR is from a fork. An alternative method would be needed:
|
||||
# https://stackoverflow.com/questions/59077079/how-to-get-pull-request-number-within-github-actions-workflow/79017997#79017997
|
||||
# - A multi-line `if` GHA expression must avoid wrapping with `${{ }}`, otherwise it is unintentionally parsed as a string:
|
||||
# https://github.com/nikitastupin/pwnhub/blob/main/writings/if-condition.md
|
||||
if: |
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
&& github.event.workflow_run.event == 'pull_request'
|
||||
&& contains(github.event.workflow_run.pull_requests.*.head.sha, github.event.workflow_run.head_sha)
|
||||
steps:
|
||||
# NOTE:
|
||||
# - The `workflow_run` metadata contains an array of `pull_requests`:
|
||||
# 1. Take the `workflow_run` equivalent of `github.event.pull_request.number`.
|
||||
# 2. There should only be one PR item in the array, verify that it shares the same `head_sha` (latest commit of PR).
|
||||
# - Careful when using GHA context expressions that may have untrusted input here. The expressions are evaluated before the script content itself is run:
|
||||
# https://github.com/docker-mailserver/docker-mailserver/pull/4247#discussion_r1827067475
|
||||
- name: 'Get PR number'
|
||||
id: set-pr-context
|
||||
env:
|
||||
head_sha: ${{ github.event.workflow_run.head_sha }}
|
||||
pull_requests: ${{ tojson(github.event.workflow_run.pull_requests) }}
|
||||
run: |
|
||||
PR_NUMBER=$(jq -r '[.[] | select(.head.sha == "${{ env.head_sha }}")][0].number' <<< "${pull_requests}")
|
||||
{
|
||||
echo 'PR_HEADSHA=${{ env.head_sha }}'
|
||||
echo "PR_NUMBER=${PR_NUMBER}"
|
||||
} >> "${GITHUB_OUTPUT}"
|
||||
|
||||
deploy-preview:
|
||||
name: 'Deploy Preview'
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [pr-context]
|
||||
env:
|
||||
# NOTE: Keep this in sync with the equivalent ENV in `docs-preview-prepare.yml`:
|
||||
BUILD_DIR: docs/site/
|
||||
# PR head SHA (latest commit):
|
||||
PR_HEADSHA: ${{ needs.pr-context.outputs.PR_HEADSHA }}
|
||||
PR_NUMBER: ${{ needs.pr-context.outputs.PR_NUMBER }}
|
||||
# Deploy URL preview prefix (the site name for this prefix is managed at Netlify):
|
||||
PREVIEW_SITE_PREFIX: pullrequest-${{ needs.pr-context.outputs.PR_NUMBER }}
|
||||
steps:
|
||||
- name: 'Retrieve and extract the built docs preview'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: preview-build
|
||||
path: ${{ env.BUILD_DIR }}
|
||||
# These are needed due this approach relying on `workflow_run`, so that it can access the build artifact:
|
||||
# (uploaded from the associated `docs-preview-prepare.yml` workflow run)
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
# ==================== #
|
||||
# Deploy preview build #
|
||||
# ==================== #
|
||||
|
||||
# Manage workflow deployment status (Part 1/2):
|
||||
# NOTE:
|
||||
# - `workflow_run` trigger does not appear on the PR/commit checks status, only the initial prepare workflow triggered.
|
||||
# This adds our own status check for this 2nd half of the workflow starting as `pending`, followed by `success` / `failure` at the end.
|
||||
# - `enable-commit-status` from `nwtgck/actions-netlify` would have handled this,
|
||||
# but the context `github.sha` that action tries to use references the primary branch commit that this workflow runs from, not the relevant PR commit.
|
||||
- name: 'Commit Status (1/2) - Set Workflow Status as Pending'
|
||||
uses: myrotvorets/set-commit-status-action@v2.0.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
status: pending
|
||||
sha: ${{ env.PR_HEADSHA }}
|
||||
context: 'Deploy Preview (pull_request => workflow_run)'
|
||||
|
||||
- name: 'Send preview build to Netlify'
|
||||
uses: nwtgck/actions-netlify@v3.0
|
||||
id: preview-netlify
|
||||
|
@ -152,19 +97,3 @@ jobs:
|
|||
[Documentation preview for this PR](${{ steps.preview-netlify.outputs.deploy-url }}) is ready! :tada:
|
||||
|
||||
Built with commit: ${{ env.PR_HEADSHA }}
|
||||
|
||||
# Manage workflow deployment status (Part 2/2):
|
||||
- name: 'Commit Status (2/2) - Update deployment status'
|
||||
uses: myrotvorets/set-commit-status-action@v2.0.1
|
||||
# Always run this step regardless of the job failing early:
|
||||
if: ${{ always() }}
|
||||
# Custom status descriptions:
|
||||
env:
|
||||
DEPLOY_SUCCESS: Successfully deployed preview.
|
||||
DEPLOY_FAILURE: Failed to deploy preview.
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
status: ${{ job.status == 'success' && 'success' || 'failure' }}
|
||||
sha: ${{ github.event.workflow_run.head_sha }}
|
||||
context: 'Deploy Preview (pull_request => workflow_run)'
|
||||
description: ${{ job.status == 'success' && env.DEPLOY_SUCCESS || env.DEPLOY_FAILURE }}
|
||||
|
|
|
@ -1,28 +1,23 @@
|
|||
name: 'Documentation (PR)'
|
||||
name: 'Docs Preview (Build)'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/scripts/docs/build-docs.sh'
|
||||
- '.github/workflows/docs-preview-prepare.yml'
|
||||
|
||||
# If this workflow is triggered while already running for the PR, cancel any earlier running instances:
|
||||
# Instances of the 2nd phase of this workflow (via `workflow_run`) lack any concurrency limits due to added complexity.
|
||||
concurrency:
|
||||
group: deploypreview-pullrequest-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
workflow_call:
|
||||
inputs:
|
||||
preview-context:
|
||||
description: 'Preview Metadata (JSON)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
# Build output directory (created by the mkdocs-material container, keep this in sync with `build-docs.sh`):
|
||||
BUILD_DIR: docs/site/
|
||||
BUILD_DIR: ${{ fromJSON( inputs.preview-context ).build_dir }}
|
||||
# These two ensure git checkout of PR branch:
|
||||
PR_REF: ${{ fromJSON( inputs.preview-context ).pull_request.head_sha }}
|
||||
PR_REPO: ${{ fromJSON( inputs.preview-context ).pull_request.head_repo }}
|
||||
# These two are only needed to construct `PREVIEW_URL`:
|
||||
PREVIEW_SITE_NAME: dms-doc-previews
|
||||
PREVIEW_SITE_PREFIX: pullrequest-${{ github.event.pull_request.number }}
|
||||
PREVIEW_SITE_NAME: ${{ fromJSON( inputs.preview-context ).netlify.site_name }}
|
||||
PREVIEW_SITE_PREFIX: ${{ fromJSON( inputs.preview-context ).netlify.deploy_prefix }}
|
||||
|
||||
# `pull_request` workflow is unreliable alone: Non-collaborator contributions lack access to secrets for security reasons.
|
||||
# A separate workflow (docs-preview-deploy.yml) handles the deploy after the potentially untrusted code is first run in this workflow.
|
||||
# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
|
||||
permissions:
|
||||
# Required by `actions/checkout` for git checkout:
|
||||
contents: read
|
||||
|
@ -33,11 +28,21 @@ jobs:
|
|||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# As `on: pull_request_target` runs this workflow from the default branch of our repo,
|
||||
# Adjust to checkout the correct PR branch (including repo to support PRs from forks):
|
||||
ref: ${{ env.PR_REF }}
|
||||
repository: ${{ env.PR_REPO }}
|
||||
# Prevent `secrets.GITHUB_TOKEN` from being stored in a `.git/config` file, which could otherwise
|
||||
# be compromised when executing untrusted code from a PR (as is done in the build step below).
|
||||
persist-credentials: false
|
||||
|
||||
# ================== #
|
||||
# Build docs preview #
|
||||
# ================== #
|
||||
|
||||
# CAUTION: Security risk due to executing `build-docs.sh` (which a PR could modify to be malicious):
|
||||
# Care must be taken to run this workflow as if it were in an untrusted context (like the `on: pull_request` event trigger would).
|
||||
- name: 'Build with mkdocs-material via Docker'
|
||||
working-directory: docs/
|
||||
env:
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
name: 'Documentation (Preview)'
|
||||
|
||||
# INFO:
|
||||
# This workflow automates generating previews of our docs for PRs.
|
||||
# For security reasons, it is necessary to split the workflow into two separate jobs to manage trust safely.
|
||||
|
||||
# MAINTAINERS:
|
||||
# - This set of `pull_request_target` + `workflow_call` workflows replaces the prior `pull_request` (untrusted) + `workflow_run` (trusted) workflows approach
|
||||
# due to the need for acquiring PR metadata (Head SHA + PR number), which the `workflow_run` event context cannot provide when PRs are from forks.
|
||||
# - Please be mindful of the risks when maintaining this workflow:
|
||||
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/scripts/docs/build-docs.sh'
|
||||
|
||||
# If this workflow is triggered while an earlier instance is already running for the PR,
|
||||
# cancel that instance in favor of this newly triggered run:
|
||||
concurrency:
|
||||
group: deploypreview-pullrequest-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Common inputs shared between the two workflow jobs (`preview` + `deploy`).
|
||||
# Composed as JSON to pass as a single input which each called job will map into separate ENV for use.
|
||||
env:
|
||||
PREVIEW_CONTEXT: |
|
||||
{
|
||||
"build_dir": "docs/site/",
|
||||
"netlify": {
|
||||
"site_name": "dms-doc-previews",
|
||||
"deploy_prefix": "pullrequest-${{ github.event.pull_request.number }}"
|
||||
},
|
||||
"pull_request": {
|
||||
"head_repo": "${{ github.event.pull_request.head.repo.full_name }}",
|
||||
"head_sha": "${{ github.event.pull_request.head.sha }}",
|
||||
"number": "${{ github.event.pull_request.number }}"
|
||||
}
|
||||
}
|
||||
|
||||
# Grant `secrets.GITHUB_TOKEN` only the minimum permissions needed by actions to function:
|
||||
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token
|
||||
# NOTE: See the associated `preview` and `deploy` workflows called for when these permissions are needed.
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
# NOTE: Reusable workflows (`workflow_call`) default to empty secrets.
|
||||
# Avoid using `secrets: inherit` in favor of passing only the required secrets to the job.
|
||||
jobs:
|
||||
# WORKAROUND:
|
||||
# The alternative is to copy/paste the JSON value to the `preview-context` input of each job.
|
||||
#
|
||||
# PROBLEM:
|
||||
# - `<job-id>.with` restricts available contexts to only `github` and `needs`, it's not possible to use `env` context:
|
||||
# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idwithinput_id
|
||||
# - Likewise, workflows presently lack support for YAML anchors:
|
||||
# https://github.com/actions/runner/issues/1182
|
||||
create-context:
|
||||
name: 'Create Context'
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
preview-context: ${{ steps.set-preview-context.outputs.preview-context }}
|
||||
steps:
|
||||
- id: set-preview-context
|
||||
# The output value must be a single-line; use `jq` to convert the JSON to minified:
|
||||
# NOTE: YAML `>-` does not help fold this multi-line content due to use of indentation:
|
||||
# https://github.com/orgs/community/discussions/26105#discussioncomment-3250413
|
||||
run: echo "preview-context=$(jq --compact-output <<< "${PREVIEW_CONTEXT}")" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
# The `prepare` job is for running steps in an untrusted context (necessary to build the docs):
|
||||
# CAUTION: This runs a build script which the PR could modify for malicious purposes.
|
||||
prepare:
|
||||
needs: [create-context]
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/docs-preview-prepare.yml@master
|
||||
with:
|
||||
preview-context: ${{ needs.create-context.outputs.preview-context }}
|
||||
|
||||
# The `deploy` job is for running the remaining steps in a trusted context after building the PR branch:
|
||||
# CAUTION: Do not execute any content from untrusted sources (the PR branch or the retrieved artifact from the `prepare` job)
|
||||
deploy:
|
||||
needs: [create-context, prepare]
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/docs-preview-deploy.yml@master
|
||||
secrets:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
with:
|
||||
preview-context: ${{ needs.create-context.outputs.preview-context }}
|
|
@ -59,6 +59,7 @@ All notable changes to this project will be documented in this file. The format
|
|||
### CI
|
||||
|
||||
- Workflow for `CONTRIBUTORS.md` updates removed. `CONTRIBUTORS.md` file and dependencies removed. ([#4141](https://github.com/docker-mailserver/docker-mailserver/pull/4141))
|
||||
- Refactored the workflows for generating documentation previews on PRs ([#4264](https://github.com/docker-mailserver/docker-mailserver/pull/4264), [#4262](https://github.com/docker-mailserver/docker-mailserver/pull/4262), [#4247](https://github.com/docker-mailserver/docker-mailserver/pull/4247), [#4244](https://github.com/docker-mailserver/docker-mailserver/pull/4244))
|
||||
|
||||
## [v14.0.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v14.0.0)
|
||||
|
||||
|
|
Loading…
Reference in New Issue