# yaml-language-server: $schema=https://json.schemastore.org/github-action.json # permissions: # - contents: write # Required for creating releases --- name: GitHub Release description: 'Creates a GitHub release with a version and changelog.' author: 'Ismo Vuorinen' branding: icon: 'tag' color: 'blue' inputs: version: description: 'The version for the release.' required: true changelog: description: 'The changelog or description for the release.' required: false default: '' outputs: release_url: description: 'URL of the created GitHub release' value: ${{ steps.create-release.outputs.release_url || steps.create-release-custom.outputs.release_url }} release_id: description: 'ID of the created GitHub release' value: ${{ steps.create-release.outputs.release_id || steps.create-release-custom.outputs.release_id }} upload_url: description: 'Upload URL for the created GitHub release assets' value: ${{ steps.create-release.outputs.upload_url || steps.create-release-custom.outputs.upload_url }} runs: using: composite steps: - name: Validate Inputs id: validate shell: bash env: VERSION: ${{ inputs.version }} CHANGELOG: ${{ inputs.changelog }} run: | set -euo pipefail # Validate version format (semantic versioning) if ! [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then echo "::error::Invalid version format: '$VERSION'. Expected semantic version (e.g., '1.2.3', 'v1.2.3-alpha', '1.2.3+build')" exit 1 fi # Validate changelog content (if provided) if [[ -n "$CHANGELOG" ]] && [[ ${#CHANGELOG} -gt 10000 ]]; then echo "::warning::Changelog is very long (${#CHANGELOG} characters). Consider using shorter release notes." fi # Check if required tools are available if ! command -v gh >/dev/null 2>&1; then echo "::error::GitHub CLI (gh) is not available. Please ensure it's installed in the environment." exit 1 fi if ! command -v jq >/dev/null 2>&1; then echo "::error::jq is not available. Please ensure it's installed in the environment." exit 1 fi # Check GitHub authentication (requires GH_TOKEN or GITHUB_TOKEN with contents: write) if ! gh auth status >/dev/null 2>&1; then echo "::error::GitHub CLI (gh) is not authenticated. Ensure the workflow grants 'contents: write' and exports GITHUB_TOKEN (gh picks up GH_TOKEN/GITHUB_TOKEN)." exit 1 fi - name: Create GitHub Release with Autogenerated Changelog id: create-release if: ${{ inputs.changelog == '' }} shell: bash env: VERSION: ${{ inputs.version }} GITHUB_REPOSITORY: ${{ github.repository }} run: | set -euo pipefail gh release create "$VERSION" \ --repo="${GITHUB_REPOSITORY}" \ --title="$VERSION" \ --generate-notes # Get release info and set outputs RELEASE_INFO=$(gh release view "$VERSION" --repo="${GITHUB_REPOSITORY}" --json url,id,uploadUrl) echo "release_url=$(echo "$RELEASE_INFO" | jq -r '.url')" >> $GITHUB_OUTPUT echo "release_id=$(echo "$RELEASE_INFO" | jq -r '.id')" >> $GITHUB_OUTPUT echo "upload_url=$(echo "$RELEASE_INFO" | jq -r '.uploadUrl')" >> $GITHUB_OUTPUT - name: Create GitHub Release with Custom Changelog id: create-release-custom if: ${{ inputs.changelog != '' }} shell: bash env: VERSION: ${{ inputs.version }} CHANGELOG: ${{ inputs.changelog }} GITHUB_REPOSITORY: ${{ github.repository }} run: |- set -euo pipefail NOTES_FILE="$(mktemp)" # Preserve exact content without allowing shell evaluation printf '%s' "$CHANGELOG" > "$NOTES_FILE" gh release create "$VERSION" \ --repo="${GITHUB_REPOSITORY}" \ --title="$VERSION" \ --notes-file "$NOTES_FILE" rm -f "$NOTES_FILE" # Get release info and set outputs RELEASE_INFO=$(gh release view "$VERSION" --repo="${GITHUB_REPOSITORY}" --json url,id,uploadUrl) echo "release_url=$(echo "$RELEASE_INFO" | jq -r '.url')" >> $GITHUB_OUTPUT echo "release_id=$(echo "$RELEASE_INFO" | jq -r '.id')" >> $GITHUB_OUTPUT echo "upload_url=$(echo "$RELEASE_INFO" | jq -r '.uploadUrl')" >> $GITHUB_OUTPUT