Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .github/workflows/CompletePipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ jobs:
- Documentation
if: >-
${{ !failure() && !cancelled()
&& needs.Documentation.result == 'success'
&& contains(inputs.documentation_steps, 'latex')
&& contains(inputs.documentation_steps, 'pdf')
}}
Expand All @@ -464,6 +465,9 @@ jobs:
- StaticTypeCheck
if: >-
${{ !failure() && !cancelled()
&& needs.Documentation.result == 'success'
&& needs.PublishCoverageResults.result == 'success'
&& needs.StaticTypeCheck.result == 'success'
&& contains(inputs.documentation_steps, 'pages')
}}
with:
Expand Down Expand Up @@ -525,6 +529,7 @@ jobs:
- ReleasePage
if: >-
${{ !failure() && !cancelled()
&& needs.Package.result == 'success'
&& needs.Prepare.outputs.is_release_tag == 'true'
}}
with:
Expand Down
20 changes: 10 additions & 10 deletions .github/workflows/PrepareJob.yml
Original file line number Diff line number Diff line change
Expand Up @@ -418,18 +418,18 @@ jobs:
export GH_TOKEN=${{ github.token }}

printf "Read second parent of current SHA (%s) ... " "${{ github.ref }}"
FATHER_SHA=$(git rev-parse ${{ github.ref }}^2 2> /dev/null)
if [[ $? -ne 0 || "{FATHER_SHA}" == "" ]]; then
SECOND_PARENT_SHA=$(git rev-parse ${{ github.ref }}^2 2> /dev/null)
if [[ $? -ne 0 || "${SECOND_PARENT_SHA}" == "" ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Couldn't read second parent (father) of '%s'.${ANSI_NOCOLOR}\n" "${{ github.ref }}^2"
printf "::error title=GitCommitHistoryError::Couldn't read second parent (father) of '%s'. -> %s\n" "${{ github.ref }}^2" "${FATHER_SHA}"
printf "::error title=GitCommitHistoryError::Couldn't read second parent (father) of '%s'. -> %s\n" "${{ github.ref }}^2" "${SECOND_PARENT_SHA}"
exit 1
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
fi

printf "Search Pull Request to '%s' and branch containing SHA %s ... " "${{ inputs.release_branch }}" "${FATHER_SHA}"
PULL_REQUESTS=$(gh pr list --base "${{ inputs.release_branch }}" --search "${FATHER_SHA}" --state "merged" --json "title,number,headRefOid,mergedBy,mergedAt")
printf "Search Pull Request to '%s' and branch containing SHA %s ... " "${{ inputs.release_branch }}" "${SECOND_PARENT_SHA}"
PULL_REQUESTS=$(gh pr list --base "${{ inputs.release_branch }}" --search "${SECOND_PARENT_SHA}" --state "merged" --json "title,number,headRefOid,mergedBy,mergedAt")
if [[ $? -ne 0 ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Couldn't search Pull Requests to '%s'. -> %s${ANSI_NOCOLOR}\n" "${{ inputs.release_branch }}" "${PULL_REQUESTS}"
Expand All @@ -442,27 +442,27 @@ jobs:
# an older release Pull Request built from an ancestor. The second parent (father) of a merge commit is the
# head commit of the merged branch, so a Pull Request whose 'headRefOid' equals it is the one that created
# this merge commit. Newest first, in case a Pull Request was closed and redone from the same branch.
MATCHES=$(printf "%s\n" "${PULL_REQUESTS}" | jq --arg sha "${FATHER_SHA}" '[.[] | select(.headRefOid == $sha)] | sort_by(.mergedAt) | reverse')
MATCHES=$(printf "%s\n" "${PULL_REQUESTS}" | jq --arg sha "${SECOND_PARENT_SHA}" '[.[] | select(.headRefOid == $sha)] | sort_by(.mergedAt) | reverse')
MATCH_COUNT=$(printf "%s\n" "${MATCHES}" | jq 'length')

printf "Select Pull Request with head commit %s ... " "${FATHER_SHA}"
printf "Select Pull Request with head commit %s ... " "${SECOND_PARENT_SHA}"
if [[ "${MATCH_COUNT}" -eq 0 ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}No merged Pull Request to '%s' has head commit '%s'.${ANSI_NOCOLOR}\n" "${{ inputs.release_branch }}" "${FATHER_SHA}"
printf "${ANSI_LIGHT_RED}No merged Pull Request to '%s' has head commit '%s'.${ANSI_NOCOLOR}\n" "${{ inputs.release_branch }}" "${SECOND_PARENT_SHA}"
if [[ "$(printf "%s\n" "${PULL_REQUESTS}" | jq 'length')" -eq 0 ]]; then
printf "${ANSI_LIGHT_RED}The search returned no candidates at all.${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_RED}Candidates returned by the search: %s${ANSI_NOCOLOR}\n" "$(printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output '[.[] | "#\(.number) (\(.headRefOid[0:7]))"] | join(", ")')"
fi
printf "::error title=PullRequest::No merged Pull Request to '%s' has head commit '%s'.\n" "${{ inputs.release_branch }}" "${FATHER_SHA}"
printf "::error title=PullRequest::No merged Pull Request to '%s' has head commit '%s'.\n" "${{ inputs.release_branch }}" "${SECOND_PARENT_SHA}"
exit 1
fi
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"

if [[ "${MATCH_COUNT}" -gt 1 ]]; then
# Rare, but possible: a Pull Request was closed and recreated from the same branch, so both share a head
# commit. The most recently merged one describes this release.
printf "${ANSI_LIGHT_YELLOW}%s Pull Requests share head commit '%s': %s${ANSI_NOCOLOR}\n" "${MATCH_COUNT}" "${FATHER_SHA}" "$(printf "%s\n" "${MATCHES}" | jq --raw-output '[.[] | "#\(.number)"] | join(", ")')"
printf "${ANSI_LIGHT_YELLOW}%s Pull Requests share head commit '%s': %s${ANSI_NOCOLOR}\n" "${MATCH_COUNT}" "${SECOND_PARENT_SHA}" "$(printf "%s\n" "${MATCHES}" | jq --raw-output '[.[] | "#\(.number)"] | join(", ")')"
printf "${ANSI_LIGHT_YELLOW}Using the most recently merged one.${ANSI_NOCOLOR}\n"
fi

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/PublishReleaseNotes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -689,15 +689,15 @@ jobs:
# Search PR with that SHA
# Load description of that PR
printf "Read second parent of current SHA (%s) ... " "${{ github.ref }}"
FATHER_SHA=$(git rev-parse ${{ github.ref }}^2 2> /dev/null)
if [[ $? -ne 0 || "{FATHER_SHA}" == "" ]]; then
SECOND_PARENT_SHA=$(git rev-parse ${{ github.ref }}^2 2> /dev/null)
if [[ $? -ne 0 || "${SECOND_PARENT_SHA}" == "" ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "→ ${ANSI_LIGHT_YELLOW}Skipped readout of pull request description. This is not a merge commit.${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"

printf "Search pull-request to '%s' and branch containing SHA %s ... " "${{ inputs.release_branch }}" "${FATHER_SHA}"
PULL_REQUESTS=$(gh pr list --base "${{ inputs.release_branch }}" --search "${FATHER_SHA}" --state "merged" --json "title,number,mergedBy,mergedAt,body")
printf "Search pull-request to '%s' and branch containing SHA %s ... " "${{ inputs.release_branch }}" "${SECOND_PARENT_SHA}"
PULL_REQUESTS=$(gh pr list --base "${{ inputs.release_branch }}" --search "${SECOND_PARENT_SHA}" --state "merged" --json "title,number,mergedBy,mergedAt,body")
if [[ $? -ne 0 || "${PULL_REQUESTS}" == "" ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Couldn't find a merged Pull Request to '%s'. -> %s${ANSI_NOCOLOR}\n" "${{ inputs.release_branch }}" "${PULL_REQUESTS}"
Expand Down
28 changes: 28 additions & 0 deletions doc/Deveopment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,34 @@ Which function to choose:
Do not use ``success() || failure()`` as a substitute for ``!cancelled()``. It is ``false`` if a dependency was
*skipped*, because a skipped dependency is neither a success nor a failure.

A status check function tolerates a *skipped* dependency, which is exactly what it is for. A job downloading an
artifact must additionally demand that the job **producing** that artifact succeeded, because a tolerated skip leaves
the artifact missing:

.. code-block:: yaml

if: >-
${{ !failure() && !cancelled()
&& needs.Documentation.result == 'success'
&& contains(inputs.documentation_steps, 'pages')
}}

Without the ``needs.<job>.result`` term the job starts and fails while downloading:

.. code-block:: text

Unable to download artifact(s): Artifact not found for name: documentation-HTML

.. important::

The distinction is between an *unrelated* dependency and a *producing* one. ``AppTesting``, skipped by
``apptest: 'false'``, has nothing to do with the documentation and must not suppress it - that is the reason the
status check function is there. ``Documentation`` uploads what :ref:`JOBTMPL/PublishToGitHubPages` downloads, so
its skip has to suppress it.

Observed on a real pipeline: three packaging jobs were cancelled by GitHub, the cascade skipped the documentation
job, and the publishing job ran anyway on the strength of ``!failure() && !cancelled()`` alone.

Conditions combining a status check function with further terms are written as a folded block scalar, one term per
line, so a condition can be read - and reviewed - without horizontal scrolling:

Expand Down
2 changes: 1 addition & 1 deletion myFramework/Extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
__email__ = "Paebbels@gmail.com"
__copyright__ = "2017-2026, Patrick Lehmann"
__license__ = "Apache License, Version 2.0"
__version__ = "7.15.0"
__version__ = "7.15.1"
__keywords__ = ["GitHub Actions"]
__project_url__ = "https://github.com/pyTooling/Actions"
__documentation_url__ = "https://pyTooling.github.io/Actions"
Expand Down
2 changes: 1 addition & 1 deletion myPackage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
__email__ = "Paebbels@gmail.com"
__copyright__ = "2017-2026, Patrick Lehmann"
__license__ = "Apache License, Version 2.0"
__version__ = "7.15.0"
__version__ = "7.15.1"
__keywords__ = ["GitHub Actions"]
__project_url__ = "https://github.com/pyTooling/Actions"
__documentation_url__ = "https://pyTooling.github.io/Actions"
Expand Down
Loading