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
107 changes: 107 additions & 0 deletions .github/workflows/release-actions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Cuts path-prefixed, per-action releases so downstream projects can pin a
# version (and Dependabot can propose bumps). On every push to main the
# release_actions.py script works out which action(s) changed, computes the
# next version from the newest existing <leaf>/vX.Y.Z tag, creates the tag,
# moves the <leaf>/vX major tag and publishes a GitHub Release. The bump type
# defaults to patch and can be raised per PR with a release:minor /
# release:major label (or suppressed with release:skip). See RELEASING.md.
name: Release actions

on:
push:
branches:
- main
paths:
- "allowlist-check/**"
- "pelican/**"
- "stash/**"
workflow_dispatch:
inputs:
action:
description: "Tag prefix to release (allowlist-check, pelican, save, restore). Leave empty to auto-detect from the latest commit."
required: false
default: ""
bump:
description: "Version bump to apply"
required: true
type: choice
default: patch
options:
- patch
- minor
- major

permissions:
contents: read

# Serialize releases so concurrent pushes to main can't race on tag creation.
concurrency:
group: release-actions
cancel-in-progress: false

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Credentials are persisted so the script can push tags; full
# history + tags are needed to diff the pushed range and to find
# the newest existing version tag per action.
persist-credentials: true
token: ${{ secrets.ALLOWLIST_WORKFLOW_TOKEN || github.token }} # zizmor: ignore[secrets-outside-env]
fetch-depth: 0
fetch-tags: true

- run: pipx install uv

- name: Configure git identity
env:
GH_TOKEN: ${{ secrets.ALLOWLIST_WORKFLOW_TOKEN || github.token }} # zizmor: ignore[secrets-outside-env]
run: |
AUTHOR_NAME=$(gh api /user --jq '.login' 2>/dev/null || echo "asfgit")
AUTHOR_EMAIL=$(gh api /user --jq '.email // "\(.login)@users.noreply.github.com"' 2>/dev/null || echo "asfgit@users.noreply.github.com")
git config --local user.name "${AUTHOR_NAME}"
git config --local user.email "${AUTHOR_EMAIL}"

- name: Release changed actions
env:
GH_TOKEN: ${{ secrets.ALLOWLIST_WORKFLOW_TOKEN || github.token }} # zizmor: ignore[secrets-outside-env]
EVENT_NAME: ${{ github.event_name }}
REPO: ${{ github.repository }}
BEFORE: ${{ github.event.before }}
AFTER: ${{ github.sha }}
INPUT_ACTION: ${{ github.event.inputs.action }}
INPUT_BUMP: ${{ github.event.inputs.bump }}
run: |
set -euo pipefail
args=(--repo "${REPO}" --after "${AFTER}" --apply)
if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then
args+=(--bump "${INPUT_BUMP}")
if [ -n "${INPUT_ACTION}" ]; then
args+=(--action "${INPUT_ACTION}")
fi
else
args+=(--before "${BEFORE}")
fi
uv run python scripts/release_actions.py "${args[@]}"
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This repository hosts GitHub Actions developed by the ASF community and approved
- [Checking the Action Usage in an ASF Project](#checking-the-action-usage-in-an-asf-project)
- [Submitting an Action](#submitting-an-action)
- [Available GitHub Actions](#available-github-actions)
- [Versioning and Pinning Actions](#versioning-and-pinning-actions)
- [Organization-wide GitHub Actions Allow List](#management-of-organization-wide-github-actions-allow-list)
- [Pipeline Overview](#pipeline-overview)
- [Adding a New Action](#adding-a-new-action-to-the-allow-list)
Expand Down Expand Up @@ -56,6 +57,9 @@ jobs:
When calling the `check-project-actions` workflow from a `push` or `pull_request` event, it should work
automatically against the "right" reference. See the sample workflow linked above for more details.

To pin to an immutable, Dependabot-trackable version instead of `@main`, see
[Versioning and Pinning Actions](#versioning-and-pinning-actions).

## Submitting an Action

To contribute a GitHub Action to this repository:
Expand Down Expand Up @@ -93,6 +97,33 @@ correctness of the action.
- [Stash Action](/stash/README.md): Manage large build caches
- [ASF Allowlist Check](/allowlist-check/README.md): Verify workflow action refs are on the ASF allowlist

## Versioning and Pinning Actions

The actions in this repo are a *monorepo of actions*, and each one is released
under its own **path-prefixed tag** so you can pin a specific version and let
Dependabot propose bumps. The tag prefix is the action's leaf directory name,
which you repeat after the `@`:

| Action | Pin it like this |
| --------------------- | ----------------------------------------------------------------------------- |
| `allowlist-check` | `apache/infrastructure-actions/allowlist-check@<sha> # allowlist-check/v1.2.3`|
| `pelican` | `apache/infrastructure-actions/pelican@<sha> # pelican/v1.2.3` |
| `stash/save` | `apache/infrastructure-actions/stash/save@<sha> # save/v1.2.3` |
| `stash/restore` | `apache/infrastructure-actions/stash/restore@<sha> # restore/v1.2.3` |

Pinning to a commit SHA with the version in a trailing comment is the
recommended, [Zizmor](https://zizmor.sh/)-friendly form: the SHA is immutable,
and Dependabot's `github_actions` ecosystem recognises the `# <prefix>/vX.Y.Z`
comment and opens a PR (updating both the SHA and the comment) when a newer
tag for that prefix is published. Support for this monorepo leaf-prefix scheme
was added to Dependabot in
[dependabot/dependabot-core#11286](https://github.com/dependabot/dependabot-core/pull/11286),
contributed specifically for this repository.

Tracking `@main` (as in the quick-start above) also works and always gives you
the latest code, but it drifts from any pinned SHA and Zizmor will flag the
unpinned ref. See [RELEASING.md](RELEASING.md) for how releases are cut.

## Management of Organization-wide GitHub Actions Allow List

As stated in the [ASF GitHub Actions Policy](https://infra.apache.org/github-actions-policy.html), GitHub Actions from external sources are blocked by default in all `apache/*` repositories. Only actions from the following namespaces are automatically allowed:
Expand Down
105 changes: 105 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# Releasing the actions

This repository is a *monorepo of actions*: several independently-consumed
actions are served from one repo. To let downstream projects pin a specific
version — and let Dependabot propose bumps — each action is released under its
own **path-prefixed tag**.

## Tag scheme

The tag prefix is the action's **leaf directory name**, which is also the
prefix a consumer repeats in the `@ref`:

| Action (consumed path) | Tag prefix | Example tag |
| ----------------------------------------------- | ---------------- | ------------------------ |
| `allowlist-check` | `allowlist-check`| `allowlist-check/v1.2.3` |
| `pelican` | `pelican` | `pelican/v1.2.3` |
| `stash/save` | `save` | `save/v1.2.3` |
| `stash/restore` | `restore` | `restore/v1.2.3` |

For every `X.Y.Z` release we also move a **major** tag (`<prefix>/vN`) to the
same commit, so consumers can track a major line if they prefer.

This leaf-name prefix scheme is the format Dependabot's `github_actions`
ecosystem understands for monorepos
([dependabot/dependabot-core#11286][11286], added specifically for this repo).
Dependabot filters candidate tags by the prefix, so `save/*` and `restore/*`
bump independently.

[11286]: https://github.com/dependabot/dependabot-core/pull/11286

## How a release happens (automatic)

Releases are cut automatically by
[`.github/workflows/release-actions.yml`](.github/workflows/release-actions.yml)
on every push to `main` that touches an action's files. The workflow runs
[`scripts/release_actions.py`](scripts/release_actions.py), which:

1. Diffs the pushed range and maps changed files to the affected action(s). A
change under `stash/shared/` releases **both** stash actions, since they
import that shared code at runtime.
2. Picks the bump type (see below).
3. For each affected action, computes the next version from the newest
existing `<prefix>/vX.Y.Z` tag (the first release seeds `v1.0.0`), creates
the annotated tag, moves the `<prefix>/vN` major tag and publishes a GitHub
Release with auto-generated notes.

### Choosing the bump

The bump defaults to **patch**. Raise or suppress it per PR:

| Signal (PR label or commit-message token) | Effect |
| ----------------------------------------- | ------------------ |
| _none_ | `patch` (default) |
| `release:minor` / `[minor]` | `minor` |
| `release:major` / `[major]` | `major` |
| `release:skip` / `[skip release]` | no release cut |

Labels take precedence over commit-message tokens; `release:major` wins over
`release:minor`.

## Cutting a release manually

Use the **Run workflow** button on the *Release actions* workflow
(`workflow_dispatch`) to release out-of-band — for example to seed the very
first tags or to force a specific bump:

- **action** — a single tag prefix (`allowlist-check`, `pelican`, `save`,
`restore`). Leave empty to auto-detect from the latest commit.
- **bump** — `patch`, `minor` or `major`.

## Testing the release logic

The version math and changed-action detection are pure functions with unit
tests:

```bash
uv run pytest scripts/test_release_actions.py
```

A dry run (no `--apply`, so nothing is tagged or pushed) prints what *would*
be released:

```bash
python3 scripts/release_actions.py \
--repo apache/infrastructure-actions --action restore --bump minor
```
Loading