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
120 changes: 120 additions & 0 deletions .github/workflows/test-artifact-names.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,123 @@
exit 1
fi
echo "artifact-prefix correctly takes precedence: $ARTIFACT_NAME"

# Test artifact-name-exclude-features functionality
test-artifact-name-exclude-features:
runs-on: ubuntu-latest
steps:
- name: Checkout Action
uses: actions/checkout@v4

- name: Test partial exclusion keeps non-excluded features
shell: bash
run: |
ARTIFACT_NAME=$(./scripts/get-artifact-name.sh \
--artifact-prefix "MyApp" \
--target x86_64-unknown-linux-gnu \
--features "c-exports,simd" \
--exclude-features "c-exports" \
--use-friendly-target-names true \
--artifact-type binary)
EXPECTED="MyApp-linux-x64-simd"
echo "ARTIFACT_NAME=$ARTIFACT_NAME"
if [[ "$ARTIFACT_NAME" != "$EXPECTED" ]]; then
echo "::error::Expected '$EXPECTED' but got '$ARTIFACT_NAME' (only c-exports should be excluded)"
exit 1
fi
echo "Partial exclusion works: $ARTIFACT_NAME"

- name: Test all features excluded produces no suffix
shell: bash
run: |
ARTIFACT_NAME=$(./scripts/get-artifact-name.sh \
--artifact-prefix "MyApp" \
--target x86_64-unknown-linux-gnu \
--features "foo,bar" \
--exclude-features "foo,bar" \
--use-friendly-target-names true \
--artifact-type binary)
EXPECTED="MyApp-linux-x64"
echo "ARTIFACT_NAME=$ARTIFACT_NAME"
if [[ "$ARTIFACT_NAME" != "$EXPECTED" ]]; then
echo "::error::Expected '$EXPECTED' but got '$ARTIFACT_NAME' (all features should be excluded)"
exit 1
fi
echo "All features excluded works: $ARTIFACT_NAME"

- name: Test whitespace trimmed from exclude list
shell: bash
run: |
ARTIFACT_NAME=$(./scripts/get-artifact-name.sh \
--artifact-prefix "MyApp" \
--target x86_64-unknown-linux-gnu \
--features "c-exports,simd" \
--exclude-features " c-exports , simd " \
--use-friendly-target-names true \
--artifact-type binary)
EXPECTED="MyApp-linux-x64"
echo "ARTIFACT_NAME=$ARTIFACT_NAME"
if [[ "$ARTIFACT_NAME" != "$EXPECTED" ]]; then
echo "::error::Expected '$EXPECTED' but got '$ARTIFACT_NAME' (whitespace should be trimmed)"
exit 1
fi
echo "Whitespace trimming works: $ARTIFACT_NAME"

- name: Test empty exclude list includes all features
shell: bash
run: |
ARTIFACT_NAME=$(./scripts/get-artifact-name.sh \
--artifact-prefix "MyApp" \
--target x86_64-unknown-linux-gnu \
--features "c-exports" \
--exclude-features "" \
--use-friendly-target-names true \
--artifact-type binary)
EXPECTED="MyApp-linux-x64-c-exports"
echo "ARTIFACT_NAME=$ARTIFACT_NAME"
if [[ "$ARTIFACT_NAME" != "$EXPECTED" ]]; then
echo "::error::Expected '$EXPECTED' but got '$ARTIFACT_NAME' (empty exclude list should include all features)"
exit 1
fi
echo "Empty exclude list works: $ARTIFACT_NAME"

- name: Test exact match only - c-exports does not exclude c-exports-extended
shell: bash
run: |
ARTIFACT_NAME=$(./scripts/get-artifact-name.sh \
--artifact-prefix "MyApp" \
--target x86_64-unknown-linux-gnu \
--features "c-exports-extended" \
--exclude-features "c-exports" \
--use-friendly-target-names true \
--artifact-type binary)
EXPECTED="MyApp-linux-x64-c-exports-extended"
echo "ARTIFACT_NAME=$ARTIFACT_NAME"
if [[ "$ARTIFACT_NAME" != "$EXPECTED" ]]; then
echo "::error::Expected '$EXPECTED' but got '$ARTIFACT_NAME' (c-exports should NOT exclude c-exports-extended - exact match only)"
exit 1
fi
echo "Exact match only works: $ARTIFACT_NAME"

- name: Test action.yml integration pattern with whitespace in exclude-features
shell: bash
run: |
# Simulates the pattern used in action.yml where --exclude-features value is quoted
ARTIFACT_SCRIPT="./scripts/get-artifact-name.sh"
EXCLUDE_FEATURES_INPUT="c-exports, foo"

# This is the fixed pattern from action.yml: --exclude-features "value" (quoted)
ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" \
--artifact-prefix "MyApp" \
--target x86_64-unknown-linux-gnu \
--features "c-exports,foo" \
--use-friendly-target-names true \
--exclude-features "$EXCLUDE_FEATURES_INPUT" \
--artifact-type binary)
EXPECTED="MyApp-linux-x64"
echo "ARTIFACT_NAME=$ARTIFACT_NAME"
if [[ "$ARTIFACT_NAME" != "$EXPECTED" ]]; then
echo "::error::Expected '$EXPECTED' but got '$ARTIFACT_NAME' (action.yml integration with whitespace should work)"
exit 1
fi
echo "action.yml integration pattern with whitespace works: $ARTIFACT_NAME"
26 changes: 21 additions & 5 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,12 @@ To use this action in your own repository:

#### Artifact & Output Settings

| Input | Required | Default | Description |
| --------------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `upload-artifacts` | No | `true` | Upload the built artifacts as a GitHub Actions artifact |
| `upload-symbols-separately` | No | `true` | Upload debug symbol files (.pdb, .dwp, .dSYM) as separate artifacts instead of bundling with main artifacts |
| `use-friendly-target-names` | No | `true` | Transform target triples to user-friendly names (e.g., `x86_64-unknown-linux-gnu` -> `linux-x64`) in artifact names. |
| Input | Required | Default | Description |
| -------------------------------- | -------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `upload-artifacts` | No | `true` | Upload the built artifacts as a GitHub Actions artifact |
| `upload-symbols-separately` | No | `true` | Upload debug symbol files (.pdb, .dwp, .dSYM) as separate artifacts instead of bundling with main artifacts |
| `use-friendly-target-names` | No | `true` | Transform target triples to user-friendly names (e.g., `x86_64-unknown-linux-gnu` -> `linux-x64`) in artifact names. |
| `artifact-name-exclude-features` | No | `c-exports` | Comma-separated list of features to exclude from artifact names. Features are still built but not included in artifact names. Set to empty string to include all. |

#### Advanced / Low-Level

Expand Down Expand Up @@ -296,6 +297,21 @@ When `use-friendly-target-names` is enabled (default), the target triple is repl
user-friendly platform identifier (e.g., `x86_64-unknown-linux-gnu` becomes `linux-x64`,
`aarch64-apple-darwin` becomes `macos-arm64`). Unmapped targets fall back to the raw target triple.

#### Excluding Features from Artifact Names

Use `artifact-name-exclude-features` to exclude specific features from artifact names while still building them.
This is useful when features are already distinguished by `artifact-prefix` (e.g., C library builds).

| Features | Exclude List | Resulting Suffix |
| ---------------- | ------------ | ---------------- |
| `c-exports` | `c-exports` | (none) |
| `c-exports,simd` | `c-exports` | `-simd` |
| `foo,bar` | `foo,bar` | (none) |
| `bar,foo` | `foo,bar` | (none) |

The default exclusion is `c-exports`, which is commonly redundant for C library builds in Reloaded projects;
where often the prefix is something like `c-library`. To include all features in names, set to an empty string `""`.

When `upload-symbols-separately` is enabled (default), debug symbols are uploaded as separate artifacts
with a `.symbols` suffix:

Expand Down
12 changes: 8 additions & 4 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ inputs:
'MyApp-linux-x64'). If empty, no prefix is used."
required: false
default: ""
artifact-name-exclude-features:
description: "Comma-separated list of features to exclude from artifact names. Set to empty string to include all."
required: false
default: "c-exports"

# ============================================================================
# Advanced / Low-Level
Expand Down Expand Up @@ -260,10 +264,10 @@ runs:
CRATE_NAME_ARG="--crate-name ${{ inputs.crate-name }}"
fi

BINARY_ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" --artifact-prefix "${{ inputs.artifact-prefix }}" $CRATE_NAME_ARG --target "${{ inputs.target }}" $FEATURES_ARG --use-friendly-target-names "${{ inputs.use-friendly-target-names }}" --artifact-type binary)
BINARY_SYMBOLS_ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" --artifact-prefix "${{ inputs.artifact-prefix }}" $CRATE_NAME_ARG --target "${{ inputs.target }}" $FEATURES_ARG --use-friendly-target-names "${{ inputs.use-friendly-target-names }}" --artifact-type binary-symbols)
LIBRARY_ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" --artifact-prefix "${{ inputs.artifact-prefix }}" $CRATE_NAME_ARG --target "${{ inputs.target }}" $FEATURES_ARG --use-friendly-target-names "${{ inputs.use-friendly-target-names }}" --artifact-type library)
LIBRARY_SYMBOLS_ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" --artifact-prefix "${{ inputs.artifact-prefix }}" $CRATE_NAME_ARG --target "${{ inputs.target }}" $FEATURES_ARG --use-friendly-target-names "${{ inputs.use-friendly-target-names }}" --artifact-type library-symbols)
BINARY_ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" --artifact-prefix "${{ inputs.artifact-prefix }}" $CRATE_NAME_ARG --target "${{ inputs.target }}" $FEATURES_ARG --use-friendly-target-names "${{ inputs.use-friendly-target-names }}" --exclude-features "${{ inputs.artifact-name-exclude-features }}" --artifact-type binary)
BINARY_SYMBOLS_ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" --artifact-prefix "${{ inputs.artifact-prefix }}" $CRATE_NAME_ARG --target "${{ inputs.target }}" $FEATURES_ARG --use-friendly-target-names "${{ inputs.use-friendly-target-names }}" --exclude-features "${{ inputs.artifact-name-exclude-features }}" --artifact-type binary-symbols)
LIBRARY_ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" --artifact-prefix "${{ inputs.artifact-prefix }}" $CRATE_NAME_ARG --target "${{ inputs.target }}" $FEATURES_ARG --use-friendly-target-names "${{ inputs.use-friendly-target-names }}" --exclude-features "${{ inputs.artifact-name-exclude-features }}" --artifact-type library)
LIBRARY_SYMBOLS_ARTIFACT_NAME=$("$ARTIFACT_SCRIPT" --artifact-prefix "${{ inputs.artifact-prefix }}" $CRATE_NAME_ARG --target "${{ inputs.target }}" $FEATURES_ARG --use-friendly-target-names "${{ inputs.use-friendly-target-names }}" --exclude-features "${{ inputs.artifact-name-exclude-features }}" --artifact-type library-symbols)

echo "BINARY_ARTIFACT_NAME=${BINARY_ARTIFACT_NAME}" >> $GITHUB_ENV
echo "BINARY_SYMBOLS_ARTIFACT_NAME=${BINARY_SYMBOLS_ARTIFACT_NAME}" >> $GITHUB_ENV
Expand Down
53 changes: 50 additions & 3 deletions scripts/get-artifact-name.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Generates artifact names for Rust builds.
# Usage: ./get-artifact-name.sh --target <target> --artifact-type <type> [--artifact-prefix <prefix>] [--crate-name <name>] [--features <features>] [--use-friendly-target-names <true|false>]
# Usage: ./get-artifact-name.sh --target <target> --artifact-type <type> [--artifact-prefix <prefix>] [--crate-name <name>] [--features <features>] [--use-friendly-target-names <true|false>] [--exclude-features <features>]
# Outputs the artifact name to stdout.

set -euo pipefail
Expand All @@ -14,6 +14,7 @@ TARGET=""
FEATURES=""
USE_FRIENDLY_TARGET_NAMES="true"
ARTIFACT_TYPE=""
EXCLUDE_FEATURES=""

# Parse named parameters
while [[ $# -gt 0 ]]; do
Expand Down Expand Up @@ -42,6 +43,10 @@ while [[ $# -gt 0 ]]; do
ARTIFACT_TYPE="$2"
shift 2
;;
--exclude-features)
EXCLUDE_FEATURES="$2"
shift 2
;;
*)
echo "Unknown parameter: $1" >&2
exit 1
Expand Down Expand Up @@ -77,9 +82,51 @@ else
TARGET_FOR_ARTIFACT="$TARGET"
fi

# Filter features by excluding specified features from the list
# Args: $1 = features string (comma-separated), $2 = exclude list (comma-separated)
# Returns: filtered features (comma-separated) or empty string
filter_features() {
local features="$1"
local exclude_list="$2"

# If no features or no exclude list, return features as-is
if [[ -z "$features" ]] || [[ -z "$exclude_list" ]]; then
echo "$features"
return
fi

# Build associative array of features to exclude (with whitespace trimming)
declare -A exclude_map
IFS=',' read -ra exclude_array <<< "$exclude_list"
for item in "${exclude_array[@]}"; do
# Trim leading/trailing whitespace
trimmed=$(echo "$item" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [[ -n "$trimmed" ]]; then
exclude_map["$trimmed"]=1
fi
done

# Filter features, keeping only those not in exclude list
local result=()
IFS=',' read -ra features_array <<< "$features"
for feature in "${features_array[@]}"; do
# Trim leading/trailing whitespace
trimmed=$(echo "$feature" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [[ -n "$trimmed" ]] && [[ -z "${exclude_map[$trimmed]:-}" ]]; then
result+=("$trimmed")
fi
done

# Join result with commas
local IFS=','
echo "${result[*]}"
}

# Generate feature suffix: -features if set, empty if not
if [[ -n "$FEATURES" ]]; then
FEATURE_SUFFIX="-$FEATURES"
# Apply exclusion filter before generating suffix
FILTERED_FEATURES=$(filter_features "$FEATURES" "$EXCLUDE_FEATURES")
if [[ -n "$FILTERED_FEATURES" ]]; then
FEATURE_SUFFIX="-$FILTERED_FEATURES"
else
FEATURE_SUFFIX=""
fi
Expand Down
Loading