Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/dependabot-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
ref: ${{ github.event.pull_request.head.ref }} # Check out the head of the actual branch, not the PR
fetch-depth: 0 # otherwise, you will fail to push refs to dest repo
token: ${{ secrets.DEPENDABOT_WORKFLOW_TOKEN }}
- uses: pyiron/actions/update-env-files@actions-4.0.13
- uses: pyiron/actions/update-env-files@main
- name: UpdateDependabotPR commit
run: |
git config --local user.email "pyiron@mpie.de"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/hatch-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ jobs:
- name: Install npm dependencies
if: inputs.use-node
run: npm install
- uses: pyiron/actions/cached-miniforge@actions-4.0.13
- uses: pyiron/actions/cached-miniforge@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.env-files }}
- uses: pyiron/actions/update-pyproject-dependencies@actions-4.0.13
- uses: pyiron/actions/update-pyproject-dependencies@main
with:
input-toml: ${{ inputs.input-toml }}
lower-bound-yaml: ${{ inputs.lower-bound-yaml }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pr-labeled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ jobs:

tests-and-coverage:
if: contains(github.event.pull_request.labels.*.name, 'run_coverage')
uses: pyiron/actions/.github/workflows/tests-and-coverage.yml@actions-4.0.13
uses: pyiron/actions/.github/workflows/tests-and-coverage.yml@main
secrets: inherit

code-ql:
if: contains(github.event.pull_request.labels.*.name, 'run_CodeQL')
uses: pyiron/actions/.github/workflows/codeql.yml@actions-4.0.13
uses: pyiron/actions/.github/workflows/codeql.yml@main
secrets: inherit
41 changes: 26 additions & 15 deletions .github/workflows/push-pull.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ on:
description: 'An optional path to a file containing the names of notebooks to NOT build'
default: .ci_support/exclude
required: false
notebooks-secret-env-map:
type: string
description: 'Optional newline-separated SECRET_NAME or ENV_NAME=SECRET_NAME entries to export for notebook execution'
default: ''
required: false
tests-env-files:
type: string
description: 'Paths to an arbitrary number of (space-separated) conda environment yaml files'
Expand Down Expand Up @@ -212,11 +217,11 @@ jobs:
ref: ${{ github.event.pull_request.head.ref }} # Check out the head of the actual branch, not the PR
fetch-depth: 0 # otherwise, you will fail to push refs to dest repo
if: ${{ inputs.do-commit-updated-env }}
- uses: pyiron/actions/write-docs-env@actions-4.0.13
- uses: pyiron/actions/write-docs-env@main
with:
env-files: ${{ inputs.docs-env-files }}
if: ${{ inputs.do-commit-updated-env }}
- uses: pyiron/actions/write-environment@actions-4.0.13
- uses: pyiron/actions/write-environment@main
with:
env-files: ${{ inputs.notebooks-env-files }}
output-env-file: .binder/environment.yml
Expand All @@ -243,11 +248,11 @@ jobs:
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@v4
- uses: pyiron/actions/add-to-python-path@actions-4.0.13
- uses: pyiron/actions/add-to-python-path@main
if: inputs.extra-python-paths != ''
with:
path-dirs: ${{ inputs.extra-python-paths }}
- uses: pyiron/actions/build-docs@actions-4.0.13
- uses: pyiron/actions/build-docs@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.docs-env-files }}
Expand All @@ -258,11 +263,17 @@ jobs:
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@v4
- uses: pyiron/actions/add-to-python-path@actions-4.0.13
- uses: pyiron/actions/add-to-python-path@main
if: inputs.extra-python-paths != ''
with:
path-dirs: ${{ inputs.extra-python-paths }}
- uses: pyiron/actions/build-notebooks@actions-4.0.13
- uses: pyiron/actions/export-secret-env@main
if: ${{ inputs.notebooks-secret-env-map != '' }}
env:
PYIRON_ALL_SECRETS_JSON: ${{ toJSON(secrets) }}
with:
secret-env-map: ${{ inputs.notebooks-secret-env-map }}
- uses: pyiron/actions/build-notebooks@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.notebooks-env-files }}
Expand Down Expand Up @@ -299,11 +310,11 @@ jobs:

steps:
- uses: actions/checkout@v4
- uses: pyiron/actions/add-to-python-path@actions-4.0.13
- uses: pyiron/actions/add-to-python-path@main
if: inputs.extra-python-paths != ''
with:
path-dirs: ${{ inputs.extra-python-paths }}
- uses: pyiron/actions/unit-tests@actions-4.0.13
- uses: pyiron/actions/unit-tests@main
with:
python-version: ${{ matrix.python-version }}
env-files: ${{ inputs.tests-env-files }}
Expand All @@ -316,7 +327,7 @@ jobs:
coverage:
needs: commit-updated-env
if: ${{ inputs.do-codecov || inputs.do-coveralls || inputs.do-codacy }}
uses: pyiron/actions/.github/workflows/tests-and-coverage.yml@actions-4.0.13
uses: pyiron/actions/.github/workflows/tests-and-coverage.yml@main
secrets: inherit
with:
tests-env-files: ${{ inputs.tests-env-files }}
Expand All @@ -336,11 +347,11 @@ jobs:
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@v4
- uses: pyiron/actions/add-to-python-path@actions-4.0.13
- uses: pyiron/actions/add-to-python-path@main
if: inputs.extra-python-paths != ''
with:
path-dirs: ${{ inputs.extra-python-paths }}
- uses: pyiron/actions/unit-tests@actions-4.0.13
- uses: pyiron/actions/unit-tests@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.tests-env-files }}
Expand All @@ -355,11 +366,11 @@ jobs:
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@v4
- uses: pyiron/actions/add-to-python-path@actions-4.0.13
- uses: pyiron/actions/add-to-python-path@main
if: inputs.extra-python-paths != ''
with:
path-dirs: ${{ inputs.extra-python-paths }}
- uses: pyiron/actions/unit-tests@actions-4.0.13
- uses: pyiron/actions/unit-tests@main
with:
python-version: ${{ inputs.alternate-tests-python-version }}
env-files: ${{ inputs.alternate-tests-env-files }}
Expand All @@ -375,7 +386,7 @@ jobs:
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@v4
- uses: pyiron/actions/pip-check@actions-4.0.13
- uses: pyiron/actions/pip-check@main
with:
python-version: ${{ inputs.python-version }}

Expand Down Expand Up @@ -430,7 +441,7 @@ jobs:
python-version: ${{ inputs.python-version }}
architecture: x64
- if: ${{ inputs.mypy-env-files != '' }}
uses: pyiron/actions/cached-miniforge@actions-4.0.13
uses: pyiron/actions/cached-miniforge@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.mypy-env-files }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pyproject-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ jobs:
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@v4
- uses: pyiron/actions/cached-miniforge@actions-4.0.13
- uses: pyiron/actions/cached-miniforge@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.env-files }}
- uses: pyiron/actions/update-pyproject-dependencies@actions-4.0.13
- uses: pyiron/actions/update-pyproject-dependencies@main
with:
input-toml: ${{ inputs.input-toml }}
lower-bound-yaml: ${{ inputs.lower-bound-yaml }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests-and-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ jobs:
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@v4
- uses: pyiron/actions/add-to-python-path@actions-4.0.13
- uses: pyiron/actions/add-to-python-path@main
if: inputs.extra-python-paths != ''
with:
path-dirs: ${{ inputs.extra-python-paths }}
- uses: pyiron/actions/unit-tests@actions-4.0.13
- uses: pyiron/actions/unit-tests@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.tests-env-files }}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
*.pyc
.DS_Store
.idea/
.dir-locals.el
.codex
.codex/
120 changes: 120 additions & 0 deletions .support/export_secret_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""Export selected GitHub Actions secrets into later-step environment variables."""

from __future__ import annotations

import json
import os
import re
import sys
import uuid

NAME_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")


def workflow_escape(value: str) -> str:
"""Escape text embedded in a GitHub workflow command."""
return value.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")


def fail(message: str) -> None:
print(f"::error::{workflow_escape(message)}", file=sys.stderr)
raise SystemExit(1)


def parse_secret_env_map(raw_map: str) -> dict[str, str]:
"""Parse mapping lines into environment-name to secret-name pairs."""
env_to_secret_name: dict[str, str] = {}

for line_number, raw_line in enumerate(raw_map.splitlines(), start=1):
line = raw_line.strip()
if not line or line.startswith("#"):
continue

if "=" in line:
env_name, secret_name = (part.strip() for part in line.split("=", 1))
else:
env_name = secret_name = line

if not env_name or not secret_name:
fail(f"Invalid secret env mapping on line {line_number}.")
if not NAME_PATTERN.fullmatch(env_name):
fail(
f"Invalid environment variable name {env_name!r} on line {line_number}."
)
if not NAME_PATTERN.fullmatch(secret_name):
fail(f"Invalid secret name {secret_name!r} on line {line_number}.")
if env_name in env_to_secret_name:
fail(f"Duplicate environment variable mapping for {env_name!r}.")

env_to_secret_name[env_name] = secret_name

return env_to_secret_name


def load_secrets() -> dict[str, str]:
"""Load the full secret object supplied only to the trusted export step."""
raw_secrets = os.environ.get("PYIRON_ALL_SECRETS_JSON")
if not raw_secrets:
fail("PYIRON_ALL_SECRETS_JSON is required when exporting selected secrets.")

try:
secrets = json.loads(raw_secrets)
except json.JSONDecodeError as exc:
fail(f"Failed to parse PYIRON_ALL_SECRETS_JSON: {exc}")

if not isinstance(secrets, dict):
fail("PYIRON_ALL_SECRETS_JSON must decode to a JSON object.")

return {str(name): str(value) for name, value in secrets.items()}


def choose_github_env_delimiter(value: str) -> str:
"""Choose a delimiter that is not present as a complete value line."""
value_lines = set(value.splitlines())
while True:
delimiter = f"PYIRON_SECRET_{uuid.uuid4().hex}"
if delimiter not in value_lines:
return delimiter


def append_github_env(env_name: str, value: str) -> None:
"""Append one environment variable using GitHub's multiline-safe format."""
github_env = os.environ.get("GITHUB_ENV")
if not github_env:
fail("GITHUB_ENV is not set.")

delimiter = choose_github_env_delimiter(value)
with open(github_env, "a", encoding="utf-8") as env_file:
env_file.write(f"{env_name}<<{delimiter}\n{value}\n{delimiter}\n")


def main() -> int:
env_to_secret_name = parse_secret_env_map(
os.environ.get("PYIRON_SECRET_ENV_MAP", "")
)
if not env_to_secret_name:
return 0

secrets = load_secrets()
missing = [
secret_name
for secret_name in env_to_secret_name.values()
if secret_name not in secrets
]
if missing:
fail("Requested secret(s) are not available: " + ", ".join(sorted(missing)))

for env_name, secret_name in env_to_secret_name.items():
value = secrets[secret_name]
if value:
print(f"::add-mask::{workflow_escape(value)}")
append_github_env(env_name, value)

print(
f"Exported {len(env_to_secret_name)} selected secret environment variable(s)."
)
return 0


if __name__ == "__main__":
raise SystemExit(main())
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ Notebooks are found in all sub directories of `/notebooks`. The files listed in

A wrapper combining `conda-incubator/setup-miniconda` and `actions/cache` along with our own `write-environment` that allows you to easily make a cached conda environment from any number (>=1) of conda environment yaml files.

### `export-secret-env`

Exports a selected set of GitHub Actions secrets to the `$GITHUB_ENV` file so they are available in later steps of the same job.

The `secret-env-map` input accepts newline-separated entries as either `SECRET_NAME` or `ENV_NAME=SECRET_NAME`.
This is intended for reusable workflows that receive inherited secrets, but only want to pass an explicit allowlist onward to runtime code such as notebook execution.
Comment thread
mbruns91 marked this conversation as resolved.

The action needs the available secrets as JSON via `PYIRON_ALL_SECRETS_JSON`.
A minimal use looks like:

```yaml
- uses: pyiron/actions/export-secret-env@main
env:
PYIRON_ALL_SECRETS_JSON: ${{ toJSON(secrets) }}
with:
secret-env-map: |
SECRET_NAME
ENV_VAR_NAME=SECRET_NAME
```

### `pip-check`

Builds your environment with the `cached-minforge` action and then runs `pip check`.
Expand Down Expand Up @@ -119,7 +139,7 @@ on:

jobs:
pyiron:
uses: pyiron/actions/.github/workflows/push-pull.yml@actions-4.0.13
uses: pyiron/actions/.github/workflows/push-pull.yml@main
secrets: inherit
```

Expand Down
4 changes: 2 additions & 2 deletions build-docs/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ inputs:
runs:
using: 'composite'
steps:
- uses: pyiron/actions/cached-miniforge@actions-4.0.13
- uses: pyiron/actions/cached-miniforge@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.standard-docs-env-file }} ${{ inputs.env-files }}
- uses: pyiron/actions/pyiron-config@actions-4.0.13
- uses: pyiron/actions/pyiron-config@main
- name: Build sphinx documentation
shell: bash -l {0}
run: |
Expand Down
4 changes: 2 additions & 2 deletions build-notebooks/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ inputs:
runs:
using: 'composite'
steps:
- uses: pyiron/actions/cached-miniforge@actions-4.0.13
- uses: pyiron/actions/cached-miniforge@main
with:
python-version: ${{ inputs.python-version }}
env-files: ${{ inputs.standard-notebooks-env-file }} ${{ inputs.env-files }}
- uses: pyiron/actions/pyiron-config@actions-4.0.13
- uses: pyiron/actions/pyiron-config@main
- name: Build notebooks
shell: bash -l {0}
run: $GITHUB_ACTION_PATH/../.support/build_notebooks.sh ${{ inputs.notebooks-dir }} ${{ inputs.exclusion-file }} ${{ inputs.kernel }}
2 changes: 1 addition & 1 deletion cached-miniforge/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ inputs:
runs:
using: "composite"
steps:
- uses: pyiron/actions/write-environment@actions-4.0.13
- uses: pyiron/actions/write-environment@main
with:
env-files: ${{ inputs.env-files }}
- name: Calculate cache label info
Expand Down
Loading