Skip to content
Closed
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
76 changes: 76 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,82 @@ jobs:
exit 1
fi

output-contract:
runs-on: ubuntu-latest
strategy:
matrix:
scenario:
- name: custom
cleanup_profile: custom
remove_codeql: "true"
skip_components: ""
expected_component: codeql
- name: max
cleanup_profile: max
remove_codeql: "false"
skip_components: docker-engine
expected_component: dotnet
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Run action and capture outputs
id: cleanup
uses: ./
with:
cleanup-profile: ${{ matrix.scenario.cleanup_profile }}
remove-codeql: ${{ matrix.scenario.remove_codeql }}
skip-components: ${{ matrix.scenario.skip_components }}

- name: Verify structured outputs
shell: bash
env:
BEFORE_AVAILABLE_BYTES: ${{ steps.cleanup.outputs.before-available-bytes }}
AFTER_AVAILABLE_BYTES: ${{ steps.cleanup.outputs.after-available-bytes }}
RECLAIMED_BYTES: ${{ steps.cleanup.outputs.reclaimed-bytes }}
CLEANUP_PROFILE_USED: ${{ steps.cleanup.outputs.cleanup-profile }}
EXECUTED_COMPONENTS: ${{ steps.cleanup.outputs.executed-components }}
EXPECTED_PROFILE: ${{ matrix.scenario.cleanup_profile }}
EXPECTED_COMPONENT: ${{ matrix.scenario.expected_component }}
run: |
set -euo pipefail

if ! [[ "$BEFORE_AVAILABLE_BYTES" =~ ^[0-9]+$ ]]; then
echo "before-available-bytes must be numeric, got '$BEFORE_AVAILABLE_BYTES'"
exit 1
fi

if ! [[ "$AFTER_AVAILABLE_BYTES" =~ ^[0-9]+$ ]]; then
echo "after-available-bytes must be numeric, got '$AFTER_AVAILABLE_BYTES'"
exit 1
fi

if ! [[ "$RECLAIMED_BYTES" =~ ^-?[0-9]+$ ]]; then
echo "reclaimed-bytes must be numeric, got '$RECLAIMED_BYTES'"
exit 1
fi

if [ "$CLEANUP_PROFILE_USED" != "$EXPECTED_PROFILE" ]; then
echo "Expected cleanup-profile '$EXPECTED_PROFILE', got '$CLEANUP_PROFILE_USED'"
exit 1
fi

if [ -z "$EXECUTED_COMPONENTS" ]; then
echo "executed-components must not be empty"
exit 1
fi

if ! [[ ",$EXECUTED_COMPONENTS," == *",$EXPECTED_COMPONENT,"* ]]; then
echo "Expected executed-components to include '$EXPECTED_COMPONENT', got '$EXECUTED_COMPONENTS'"
exit 1
fi

expected_reclaimed=$((AFTER_AVAILABLE_BYTES - BEFORE_AVAILABLE_BYTES))
if [ "$RECLAIMED_BYTES" -ne "$expected_reclaimed" ]; then
echo "Expected reclaimed-bytes to be '$expected_reclaimed', got '$RECLAIMED_BYTES'"
exit 1
fi

invalid-input-validation:
runs-on: ubuntu-latest
strategy:
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
- Composite GitHub Action that frees disk space on `ubuntu-latest` runners by removing optional preinstalled software and caches.
- Main implementation lives in `action.yml` and is intended to run early in a workflow.
- Supports `cleanup-profile` modes (`custom` and `max`), `skip-components` to keep specific toolchains, and an opt-in `swapfile-size` input to manage `/mnt/swapfile` without changing it by default.
- Exposes structured outputs for `/` disk availability before/after cleanup, reclaimed bytes, normalized cleanup profile, and the scheduled component list.

## Repository Map

- `action.yml`: Action metadata, inputs, and all cleanup logic.
- `.github/workflows/test.yml`: CI matrix tests; each run enables exactly one removal input and verifies expected state.
- `.github/workflows/test.yml`: CI matrix tests; each run enables exactly one removal input and verifies expected state, plus output-contract checks for `custom` and `max`.
- `README.md`: User-facing usage and caveats.

## Scripts / Execution Surfaces
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This action removes optional preinstalled SDKs, toolchains, and caches so your w
- [Quick start](#quick-start)
- [Cleanup Profiles](#cleanup-profiles)
- [Inputs reference](#inputs-reference)
- [Outputs reference](#outputs-reference)
- [Compatibility](#compatibility)
- [Contributing](#contributing)
- [Migration Guide](#migration-guide)
Expand Down Expand Up @@ -182,6 +183,36 @@ All `remove-*` inputs are optional toggles. In `cleanup-profile: max`, every com
| `skip-components` | N/A | Comma-separated components to keep when `cleanup-profile=max`. |
| `swapfile-size` | empty | Optional swapfile size (`0`, `1.5GiB`, `512MiB`, or a plain GiB value like `2`). Leaves swap untouched when omitted. |

## Outputs reference

The action now emits machine-readable outputs so later workflow steps can branch on actual cleanup results.

| Output | Description |
| ------------------------ | --------------------------------------------------------------- |
| `before-available-bytes` | Available bytes on `/` before cleanup starts. |
| `after-available-bytes` | Available bytes on `/` after cleanup completes. |
| `reclaimed-bytes` | Net bytes reclaimed on `/` (`after-available-bytes - before-available-bytes`). |
| `cleanup-profile` | Normalized cleanup profile actually used (`max` or `custom`). |
| `executed-components` | Comma-separated component names scheduled for execution. |

Example: write reclaimed space to the job summary.

```yaml
- name: Free Runner Space
id: free-space
uses: justinthelaw/maximize-github-runner-space@latest
with:
cleanup-profile: custom
remove-codeql: "true"

- name: Publish cleanup summary
shell: bash
run: |
set -euo pipefail
echo "Reclaimed bytes: ${{ steps.free-space.outputs.reclaimed-bytes }}" >> "$GITHUB_STEP_SUMMARY"
echo "Executed components: ${{ steps.free-space.outputs.executed-components }}" >> "$GITHUB_STEP_SUMMARY"
```

### Component toggles

Each row lists the `remove-*` input, its `skip-components` name, and the primary removal targets.
Expand Down
97 changes: 97 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -236,18 +236,39 @@ inputs:
required: false
default: "false"

outputs:
before-available-bytes:
description: "Available bytes on / before cleanup starts."
value: ${{ steps.structured-outputs.outputs.before_available_bytes }}
after-available-bytes:
description: "Available bytes on / after cleanup completes."
value: ${{ steps.structured-outputs.outputs.after_available_bytes }}
reclaimed-bytes:
description: "Net bytes reclaimed on / (after - before)."
value: ${{ steps.structured-outputs.outputs.reclaimed_bytes }}
cleanup-profile:
description: "Normalized cleanup profile used for the run (max or custom)."
value: ${{ steps.structured-outputs.outputs.cleanup_profile }}
executed-components:
description: "Comma-separated cleanup components scheduled for execution."
value: ${{ steps.structured-outputs.outputs.executed_components }}

runs:
using: "composite"
steps:
- name: Disk Space Report (BEFORE)
id: disk-before
shell: bash
run: |
set -euo pipefail
before_available_bytes=$(df --output=avail -B1 / | tail -n1 | tr -d '[:space:]')
echo "Available storage:"
sudo df -h
echo
echo "available_bytes=${before_available_bytes}" >> "$GITHUB_OUTPUT"

- name: Initialize Cleanup Plan
id: init-plan
shell: bash
env:
CLEANUP_PROFILE: ${{ inputs.cleanup-profile }}
Expand Down Expand Up @@ -701,6 +722,62 @@ runs:
echo
echo

scheduled_components=()
[[ "$COMPONENT_DOTNET" == "true" ]] && scheduled_components+=("dotnet")
[[ "$COMPONENT_ANDROID" == "true" ]] && scheduled_components+=("android")
[[ "$COMPONENT_HASKELL" == "true" ]] && scheduled_components+=("haskell")
[[ "$COMPONENT_CODEQL" == "true" ]] && scheduled_components+=("codeql")
[[ "$COMPONENT_CACHED_TOOLS" == "true" ]] && scheduled_components+=("cached-tools")
[[ "$COMPONENT_SWAPFILE" == "true" ]] && scheduled_components+=("swapfile")
[[ "$COMPONENT_SWIFT" == "true" ]] && scheduled_components+=("swift")
[[ "$COMPONENT_JULIA" == "true" ]] && scheduled_components+=("julia")
[[ "$COMPONENT_JAVA" == "true" ]] && scheduled_components+=("java")
[[ "$COMPONENT_BROWSERS" == "true" ]] && scheduled_components+=("browsers")
[[ "$COMPONENT_POWERSHELL" == "true" ]] && scheduled_components+=("powershell")
[[ "$COMPONENT_MINICONDA" == "true" ]] && scheduled_components+=("miniconda")
[[ "$COMPONENT_HOMEBREW" == "true" ]] && scheduled_components+=("homebrew")
[[ "$COMPONENT_VCPKG" == "true" ]] && scheduled_components+=("vcpkg")
[[ "$COMPONENT_CACHED_GO" == "true" ]] && scheduled_components+=("cached-go")
[[ "$COMPONENT_CACHED_NODE" == "true" ]] && scheduled_components+=("cached-node")
[[ "$COMPONENT_CACHED_PYTHON" == "true" ]] && scheduled_components+=("cached-python")
[[ "$COMPONENT_CACHED_PYPY" == "true" ]] && scheduled_components+=("cached-pypy")
[[ "$COMPONENT_CACHED_RUBY" == "true" ]] && scheduled_components+=("cached-ruby")
[[ "$COMPONENT_CHROME" == "true" ]] && scheduled_components+=("chrome")
[[ "$COMPONENT_CHROMIUM" == "true" ]] && scheduled_components+=("chromium")
[[ "$COMPONENT_EDGE" == "true" ]] && scheduled_components+=("edge")
[[ "$COMPONENT_FIREFOX" == "true" ]] && scheduled_components+=("firefox")
[[ "$COMPONENT_WEBDRIVERS" == "true" ]] && scheduled_components+=("webdrivers")
[[ "$COMPONENT_SELENIUM" == "true" ]] && scheduled_components+=("selenium")
[[ "$COMPONENT_AWS_CLI" == "true" ]] && scheduled_components+=("aws-cli")
[[ "$COMPONENT_AWS_SAM_CLI" == "true" ]] && scheduled_components+=("aws-sam-cli")
[[ "$COMPONENT_AZURE_CLI" == "true" ]] && scheduled_components+=("azure-cli")
[[ "$COMPONENT_GH_CLI" == "true" ]] && scheduled_components+=("gh-cli")
[[ "$COMPONENT_GCLOUD_CLI" == "true" ]] && scheduled_components+=("gcloud-cli")
[[ "$COMPONENT_AZCOPY" == "true" ]] && scheduled_components+=("azcopy")
[[ "$COMPONENT_KUBECTL" == "true" ]] && scheduled_components+=("kubectl")
[[ "$COMPONENT_HELM" == "true" ]] && scheduled_components+=("helm")
[[ "$COMPONENT_KIND" == "true" ]] && scheduled_components+=("kind")
[[ "$COMPONENT_MINIKUBE" == "true" ]] && scheduled_components+=("minikube")
[[ "$COMPONENT_KUSTOMIZE" == "true" ]] && scheduled_components+=("kustomize")
[[ "$COMPONENT_DOCKER_ENGINE" == "true" ]] && scheduled_components+=("docker-engine")
[[ "$COMPONENT_BUILDAH" == "true" ]] && scheduled_components+=("buildah")
[[ "$COMPONENT_PODMAN" == "true" ]] && scheduled_components+=("podman")
[[ "$COMPONENT_MAVEN" == "true" ]] && scheduled_components+=("maven")
[[ "$COMPONENT_GRADLE" == "true" ]] && scheduled_components+=("gradle")
[[ "$COMPONENT_ANT" == "true" ]] && scheduled_components+=("ant")
[[ "$COMPONENT_PHP" == "true" ]] && scheduled_components+=("php")
[[ "$COMPONENT_RUST" == "true" ]] && scheduled_components+=("rust")
[[ "$COMPONENT_POSTGRESQL" == "true" ]] && scheduled_components+=("postgresql")
[[ "$COMPONENT_MYSQL" == "true" ]] && scheduled_components+=("mysql")
[[ "$COMPONENT_APACHE" == "true" ]] && scheduled_components+=("apache")
[[ "$COMPONENT_NGINX" == "true" ]] && scheduled_components+=("nginx")
[[ "$COMPONENT_DOCKER_IMAGES" == "true" ]] && scheduled_components+=("docker-images")
[[ "$COMPONENT_LARGE_PACKAGES" == "true" ]] && scheduled_components+=("large-packages")

scheduled_components_csv=$(IFS=,; echo "${scheduled_components[*]}")
echo "cleanup_profile=${CLEANUP_PROFILE}" >> "$GITHUB_OUTPUT"
echo "executed_components=${scheduled_components_csv}" >> "$GITHUB_OUTPUT"

- name: Parallel File System Cleanup
shell: bash
run: |
Expand Down Expand Up @@ -1397,9 +1474,29 @@ runs:
rm -f /tmp/total_ops /tmp/completed_ops /tmp/completed_ops.lock

- name: Disk Space Report (AFTER)
id: disk-after
shell: bash
run: |
set -euo pipefail
after_available_bytes=$(df --output=avail -B1 / | tail -n1 | tr -d '[:space:]')
echo "Available storage:"
sudo df -h
echo
echo "available_bytes=${after_available_bytes}" >> "$GITHUB_OUTPUT"

- name: Emit Structured Outputs
id: structured-outputs
shell: bash
env:
BEFORE_AVAILABLE_BYTES: ${{ steps.disk-before.outputs.available_bytes }}
AFTER_AVAILABLE_BYTES: ${{ steps.disk-after.outputs.available_bytes }}
CLEANUP_PROFILE_USED: ${{ steps.init-plan.outputs.cleanup_profile }}
EXECUTED_COMPONENTS: ${{ steps.init-plan.outputs.executed_components }}
run: |
set -euo pipefail
reclaimed_bytes=$((AFTER_AVAILABLE_BYTES - BEFORE_AVAILABLE_BYTES))
echo "before_available_bytes=${BEFORE_AVAILABLE_BYTES}" >> "$GITHUB_OUTPUT"
echo "after_available_bytes=${AFTER_AVAILABLE_BYTES}" >> "$GITHUB_OUTPUT"
echo "reclaimed_bytes=${reclaimed_bytes}" >> "$GITHUB_OUTPUT"
echo "cleanup_profile=${CLEANUP_PROFILE_USED}" >> "$GITHUB_OUTPUT"
echo "executed_components=${EXECUTED_COMPONENTS}" >> "$GITHUB_OUTPUT"
17 changes: 17 additions & 0 deletions docs/MIGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,25 @@ This document captures breaking changes and notable behavior shifts between rele

## Table of Contents

- [0.7.x -> 0.8.0](#07x---080)
- [0.6.x -> 0.7.0](#06x---070)

## 0.7.x -> 0.8.0

**Summary**

`maximize-github-runner-space` now emits stable machine-readable outputs for disk-space reporting and cleanup planning.

**Behavior changes**

- Added composite action outputs:
- `before-available-bytes`
- `after-available-bytes`
- `reclaimed-bytes`
- `cleanup-profile`
- `executed-components`
- Output behavior is observational only and does not change cleanup/removal semantics.

## 0.6.x -> 0.7.0

**Summary**
Expand Down
Loading