diff --git a/.github/workflows/bump-version-flutter-lib.yml b/.github/workflows/bump-version-flutter-lib.yml index 541a17c..f47cad7 100644 --- a/.github/workflows/bump-version-flutter-lib.yml +++ b/.github/workflows/bump-version-flutter-lib.yml @@ -26,7 +26,10 @@ jobs: run: | case "${INPUTS_LEVEL}" in major|minor|patch) ;; - *) echo "Invalid level: '${INPUTS_LEVEL}' (expected major|minor|patch)"; exit 1 ;; + *) + echo "::error::Invalid level '${INPUTS_LEVEL}' (expected major, minor, or patch)." + exit 1 + ;; esac - id: generate_token @@ -86,14 +89,6 @@ jobs: run: | echo "release_version=$(get-project-version)" >>"$GITHUB_OUTPUT" - - name: Verify changes exist - run: | - git update-index -q --really-refresh - if git diff-index --quiet HEAD --; then - echo "No changes detected; refusing to open PR." - exit 1 - fi - - id: cpr name: Create pull request uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 diff --git a/.github/workflows/bump-version-rust.yml b/.github/workflows/bump-version-rust.yml index 57e11ca..cfb0011 100644 --- a/.github/workflows/bump-version-rust.yml +++ b/.github/workflows/bump-version-rust.yml @@ -31,6 +31,18 @@ jobs: name: prepare next release runs-on: ubuntu-latest steps: + - name: Validate bump level + env: + INPUTS_LEVEL: ${{ inputs.level }} + run: | + case "${INPUTS_LEVEL}" in + major|minor|patch|release|rc|beta|alpha) ;; + *) + echo "::error::Invalid level '${INPUTS_LEVEL}' (expected major, minor, patch, release, rc, beta, or alpha)." + exit 1 + ;; + esac + - id: generate_token name: Generate a GitHub App token uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a diff --git a/.github/workflows/check-github-actions.yml b/.github/workflows/check-github-actions.yml index f47616f..f028034 100644 --- a/.github/workflows/check-github-actions.yml +++ b/.github/workflows/check-github-actions.yml @@ -31,6 +31,18 @@ jobs: with: persist-credentials: false + - name: Validate zizmor persona + env: + INPUTS_ZIZMOR_PERSONA: ${{ inputs.zizmor_persona }} + run: | + case "${INPUTS_ZIZMOR_PERSONA}" in + regular|pedantic|auditor) ;; + *) + echo "::error::Invalid zizmor_persona '${INPUTS_ZIZMOR_PERSONA}' (expected regular, pedantic, or auditor)." + exit 1 + ;; + esac + - name: Lint GitHub Actions workflow files uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d # v0.5.0 with: diff --git a/.github/workflows/detect-changed-files.yml b/.github/workflows/detect-changed-files.yml index 15bb192..0976362 100644 --- a/.github/workflows/detect-changed-files.yml +++ b/.github/workflows/detect-changed-files.yml @@ -77,3 +77,55 @@ jobs: zig: - '**/*.zig' - '**/*.zon' + + - name: Annotate workflow run with changed file categories + env: + ADDED_OR_MODIFIED: ${{ steps.filter.outputs.added_or_modified }} + ADDED_OR_MODIFIED_FILES: ${{ steps.filter.outputs.added_or_modified_files }} + FLUTTER: ${{ steps.filter.outputs.flutter }} + FLUTTER_FILES: ${{ steps.filter.outputs.flutter_files }} + GITHUB_ACTIONS: ${{ steps.filter.outputs.github_actions }} + GITHUB_ACTIONS_FILES: ${{ steps.filter.outputs.github_actions_files }} + MARKDOWN: ${{ steps.filter.outputs.markdown }} + MARKDOWN_FILES: ${{ steps.filter.outputs.markdown_files }} + RUST: ${{ steps.filter.outputs.rust }} + RUST_FILES: ${{ steps.filter.outputs.rust_files }} + ZIG: ${{ steps.filter.outputs.zig }} + ZIG_FILES: ${{ steps.filter.outputs.zig_files }} + run: | + set -euo pipefail + + append_summary() { + local label="$1" + local changed="$2" + local files="$3" + { + echo "- ${label}: ${changed}" + if [[ "${changed}" == "true" && -n "${files}" ]]; then + printf '%s\n' "${files}" | sed -e 's/^/ - `/' -e 's/$/`/' + fi + } >>"$GITHUB_STEP_SUMMARY" + } + + emit_category() { + local label="$1" + local changed="$2" + local files="$3" + if [[ "${changed}" == "true" ]]; then + echo "::notice::${label} changed." + if [[ -n "${files}" ]]; then + echo "::group::${label} changed files" + printf '%s\n' "${files}" + echo "::endgroup::" + fi + fi + append_summary "${label}" "${changed}" "${files}" + } + + echo "### :mag: Changed file categories" >>"$GITHUB_STEP_SUMMARY" + emit_category "Any tracked files" "${ADDED_OR_MODIFIED}" "${ADDED_OR_MODIFIED_FILES}" + emit_category "Flutter" "${FLUTTER}" "${FLUTTER_FILES}" + emit_category "GitHub Actions" "${GITHUB_ACTIONS}" "${GITHUB_ACTIONS_FILES}" + emit_category "Markdown" "${MARKDOWN}" "${MARKDOWN_FILES}" + emit_category "Rust" "${RUST}" "${RUST_FILES}" + emit_category "Zig" "${ZIG}" "${ZIG_FILES}" diff --git a/.github/workflows/ready-to-merge.yml b/.github/workflows/ready-to-merge.yml index 615a83c..5f4c8a0 100644 --- a/.github/workflows/ready-to-merge.yml +++ b/.github/workflows/ready-to-merge.yml @@ -20,6 +20,11 @@ jobs: NEEDS_CONTEXT="$INPUTS_NEEDS_CONTEXT" + if ! jq -e . <<<"$NEEDS_CONTEXT" >/dev/null; then + echo "::error::Invalid needs_context JSON input." + exit 1 + fi + if jq -e ' to_entries[] | select(.value.result != "success" and .value.result != "skipped") diff --git a/.github/workflows/tag-if-missing.yml b/.github/workflows/tag-if-missing.yml index d441670..2c1d63c 100644 --- a/.github/workflows/tag-if-missing.yml +++ b/.github/workflows/tag-if-missing.yml @@ -52,14 +52,19 @@ jobs: tag="$(get-project-version | tag-if-missing)" echo "tag=$tag" >>"$GITHUB_OUTPUT" - - name: Annotate workflow run with new tag - if: ${{ steps.tag.outputs.tag != '' }} - run: | - set -euo pipefail - { - echo "### :sparkles: New tag created" - echo - echo "- \`${STEPS_TAG_OUTPUTS_TAG}\`" - } >>"$GITHUB_STEP_SUMMARY" + - name: Annotate workflow run with tag result env: STEPS_TAG_OUTPUTS_TAG: ${{ steps.tag.outputs.tag }} + run: | + set -euo pipefail + + if [[ -n "${STEPS_TAG_OUTPUTS_TAG}" ]]; then + echo "::notice::Created missing tag ${STEPS_TAG_OUTPUTS_TAG}." + { + echo "### :sparkles: New tags:" + echo + echo "- \`${STEPS_TAG_OUTPUTS_TAG}\`" + } >>"$GITHUB_STEP_SUMMARY" + else + echo "::notice::No missing tags were found." + fi diff --git a/.github/workflows/tag-untagged-releases-rust.yml b/.github/workflows/tag-untagged-releases-rust.yml index f27f4ff..72e9704 100644 --- a/.github/workflows/tag-untagged-releases-rust.yml +++ b/.github/workflows/tag-untagged-releases-rust.yml @@ -83,7 +83,29 @@ jobs: - name: Annotate workflow run with new tags run: | - if [[ "${TAGS_BEFORE}" != "${TAGS_AFTER}" ]]; then - echo "### :sparkles: New tags:" >>"$GITHUB_STEP_SUMMARY"; - comm -3 <(echo "${TAGS_BEFORE}") <(echo "${TAGS_AFTER}") | sed -e 's/^[[:space:]]*//' -e 's/^/- /' >>"$GITHUB_STEP_SUMMARY"; + NEW_TAGS="$( + comm -13 \ + <(printf '%s\n' "${TAGS_BEFORE}" | sed '/^$/d' | sort) \ + <(printf '%s\n' "${TAGS_AFTER}" | sed '/^$/d' | sort) + )" + + if [[ -n "${NEW_TAGS}" ]]; then + NEW_TAG_COUNT="$(printf '%s\n' "${NEW_TAGS}" | sed '/^$/d' | wc -l | tr -d ' ')" + if [[ "${NEW_TAG_COUNT}" == "1" ]]; then + echo "::notice::Created missing tag $(printf '%s\n' "${NEW_TAGS}" | head -n1)." + else + echo "::notice::Created ${NEW_TAG_COUNT} missing tags." + fi + + echo "::group::New tags" + printf '%s\n' "${NEW_TAGS}" + echo "::endgroup::" + + { + echo "### :sparkles: New tags:" + echo + printf '%s\n' "${NEW_TAGS}" | sed -e 's/^/- /' + } >>"$GITHUB_STEP_SUMMARY" + else + echo "::notice::No new release tags were needed." fi diff --git a/.github/workflows/upload-build-android.yml b/.github/workflows/upload-build-android.yml index 2e823e0..ee38e81 100644 --- a/.github/workflows/upload-build-android.yml +++ b/.github/workflows/upload-build-android.yml @@ -65,6 +65,29 @@ jobs: common flutter + - name: Validate upload options + env: + INPUTS_TRACK: ${{ inputs.track }} + INPUTS_RELEASE_STATUS: ${{ inputs.release_status }} + run: | + set -euo pipefail + + case "${INPUTS_TRACK}" in + internal|alpha|beta|production) ;; + *) + echo "::error::Invalid track '${INPUTS_TRACK}' (expected internal, alpha, beta, or production)." + exit 1 + ;; + esac + + case "${INPUTS_RELEASE_STATUS}" in + draft|completed|inProgress|halted) ;; + *) + echo "::error::Invalid release_status '${INPUTS_RELEASE_STATUS}' (expected draft, completed, inProgress, or halted)." + exit 1 + ;; + esac + - name: Download release assets env: GH_TOKEN: ${{ github.token }} @@ -73,13 +96,15 @@ jobs: set -euo pipefail mkdir -p dist - gh release download "$RELEASE_TAG" \ + if ! gh release download "$RELEASE_TAG" \ --pattern "*.aab" \ --pattern "sha256sums.txt" \ - --dir dist + --dir dist; then + echo "::error::Failed to download release assets for tag '${RELEASE_TAG}'. Expected at least one .aab and sha256sums.txt." + exit 1 + fi - - id: verify_assets - name: Verify release assets + - name: Verify release assets run: verify-checksums --dir dist --pattern '*.aab' - id: gcp_auth @@ -97,3 +122,12 @@ jobs: releaseFiles: dist/*.aab track: ${{ inputs.track }} status: ${{ inputs.release_status }} + + - name: Annotate workflow run with uploaded aab + run: | + ASSET_NAME="$(basename dist/*.aab)" + { + printf '### :shipit: Uploaded Android build:\n' + printf '\n' + printf -- '- [%s]\n' "${ASSET_NAME}" + } >>"$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/upload-build-ios.yml b/.github/workflows/upload-build-ios.yml index 0640900..ae551f4 100644 --- a/.github/workflows/upload-build-ios.yml +++ b/.github/workflows/upload-build-ios.yml @@ -49,13 +49,15 @@ jobs: set -euo pipefail mkdir -p dist - gh release download "$RELEASE_TAG" \ + if ! gh release download "$RELEASE_TAG" \ --pattern "*.ipa" \ --pattern "sha256sums.txt" \ - --dir dist + --dir dist; then + echo "::error::Failed to download release assets for tag '${RELEASE_TAG}'. Expected at least one .ipa and sha256sums.txt." + exit 1 + fi - - id: verify_assets - name: Verify release assets + - name: Verify release assets run: verify-checksums --dir dist --pattern '*.ipa' - id: asc_auth @@ -70,7 +72,6 @@ jobs: API_PRIVATE_KEYS_DIR: ${{ steps.asc_auth.outputs.asc_key_dir }} ASC_ISSUER_ID: ${{ inputs.asc_issuer_id }} ASC_KEY_ID: ${{ inputs.asc_key_id }} - ASSET_PATH: ${{ steps.verify_assets.outputs.asset_path }} run: | set -euo pipefail xcrun altool --upload-app --type ios \ @@ -79,9 +80,8 @@ jobs: --apiIssuer "$ASC_ISSUER_ID" - name: Annotate workflow run with uploaded ipa - env: - ASSET_NAME: ${{ steps.verify_assets.outputs.asset_name }} run: | + ASSET_NAME="$(basename dist/*.ipa)" { printf '### :shipit: Uploaded iOS build:\n' printf '\n'