-
Notifications
You must be signed in to change notification settings - Fork 1
Add tag-based Firebase distribution pipeline #145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| name: Firebase App Distribution | ||
|
|
||
| on: | ||
| push: | ||
| tags: [ "v*" ] | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| distribute: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| defaults: | ||
| run: | ||
| working-directory: Prezel | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Set up JDK 21 | ||
| uses: actions/setup-java@v5 | ||
| with: | ||
| distribution: 'temurin' | ||
| java-version: '21' | ||
| cache: 'gradle' | ||
| cache-dependency-path: | | ||
| Prezel/*.gradle* | ||
| Prezel/**/gradle-wrapper.properties | ||
|
|
||
| - name: Grant execute permission for gradlew | ||
| run: chmod +x gradlew | ||
|
|
||
| - name: Create local.properties | ||
| run: echo "${{ secrets.LOCAL_PROPERTIES_CONTENTS }}" > ./local.properties | ||
|
|
||
| - name: Create google-services.json | ||
| run: | | ||
| cat <<'EOF' > ./app/google-services.json | ||
| ${{ secrets.GOOGLE_SERVICES_JSON }} | ||
| EOF | ||
|
|
||
| - name: Prepare version metadata | ||
| run: | | ||
| echo "ANDROID_VERSION_CODE=${GITHUB_RUN_NUMBER}" >> "$GITHUB_ENV" | ||
| echo "ANDROID_VERSION_NAME=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV" | ||
|
|
||
| - name: Build Debug APK | ||
| run: ./gradlew assembleDebug --build-cache --parallel | ||
|
|
||
| - name: Upload to Firebase App Distribution | ||
| uses: wzieba/Firebase-Distribution-Github-Action@v1 | ||
| with: | ||
| appId: ${{ vars.FIREBASE_APP_ID }} | ||
| serviceCredentialsFileContent: ${{ secrets.CREDENTIAL_FILE_CONTENT }} | ||
| groups: ${{ vars.FIREBASE_APP_DISTRIBUTION_GROUPS }} | ||
| releaseNotes: | | ||
| Tag: ${{ github.ref_name }} | ||
| Commit: ${{ github.sha }} | ||
| Actor: ${{ github.actor }} | ||
| Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| file: ./Prezel/app/build/outputs/apk/debug/app-debug.apk | ||
|
|
||
| - name: Send Discord Notification | ||
| if: always() | ||
| env: | ||
| DISCORD_WEBHOOK: ${{ secrets.DISTRIBUTION_DISCORD_HOOK_URL }} | ||
| STATUS_TITLE: ${{ job.status == 'success' && 'โ Firebase Debug ๋ฐฐํฌ ์ฑ๊ณต' || 'โ Firebase Debug ๋ฐฐํฌ ์คํจ' }} | ||
| STATUS_COLOR: ${{ job.status == 'success' && '3066993' || '15158332' }} | ||
| BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| EVENT_TIMESTAMP: ${{ github.event.head_commit.timestamp || github.event.repository.pushed_at }} | ||
| run: | | ||
| if [ -z "$DISCORD_WEBHOOK" ]; then | ||
| echo "DISTRIBUTION_DISCORD_HOOK_URL is not set. Skipping Discord notification." | ||
| exit 0 | ||
| fi | ||
|
|
||
| curl -H "Content-Type: application/json" -X POST -d "$(cat <<EOF | ||
| { | ||
| "embeds": [ | ||
| { | ||
| "title": "$STATUS_TITLE", | ||
| "description": "Firebase Distribution Debug APK ๋ฐฐํฌ๊ฐ ์๋ฃ๋์์ต๋๋ค.", | ||
| "color": $STATUS_COLOR, | ||
| "fields": [ | ||
| { | ||
| "name": "๐ท Tag", | ||
| "value": "${{ github.ref_name }}" | ||
| }, | ||
| { | ||
| "name": "๐ฆ Version", | ||
| "value": "${{ env.ANDROID_VERSION_NAME }} ใป #${{ github.run_number }} <${{ env.ANDROID_VERSION_CODE }}>" | ||
| }, | ||
| { | ||
| "name": "๐ฅ Tester Group", | ||
| "value": "${{ vars.FIREBASE_APP_DISTRIBUTION_GROUPS }}" | ||
| }, | ||
| { | ||
| "name": "๐ Action Log", | ||
| "value": "[๋ฐ๋ก๊ฐ๊ธฐ]($BUILD_URL)" | ||
| } | ||
| ], | ||
| "footer": { | ||
| "text": "Prezel Firebase Distribution" | ||
| }, | ||
| "timestamp": "$EVENT_TIMESTAMP" | ||
| } | ||
| ] | ||
| } | ||
| EOF | ||
| )" "$DISCORD_WEBHOOK" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| name: Version Tag On Merge | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: [ "develop" ] | ||
| types: [ "closed" ] | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| jobs: | ||
| tag-version: | ||
| if: ${{ github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop' }} | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| fetch-depth: 0 | ||
|
Comment on lines
+17
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐งฉ Analysis chain๐ Web query:
๐ก Result: GitHub Actions is intentionally designed so that events triggered by the default GITHUB_TOKEN do not create new workflow runs [1][2][3]. This security feature prevents infinite loops and accidental recursive workflow execution [1][2][4]. Key details regarding this behavior: - Scope and Exception: When you perform an action (such as a git push) using the GITHUB_TOKEN, GitHub will not trigger any workflows that are configured to run on that specific event (e.g., on: push) [1][2][3]. Notable exceptions to this rule are the workflow_dispatch and repository_dispatch events, which can still be triggered by the GITHUB_TOKEN [2][3][5]. - Solution: If you require a workflow to be triggered by an action performed within another workflow, you must use a different authentication token that is not the default GITHUB_TOKEN [1][6][4]. Common alternatives include: - Personal Access Token (PAT): A classic or fine-grained PAT with the necessary repository permissions [1][4]. - GitHub App Token: A token generated from a GitHub App installation, which is often considered a more secure, principle-of-least-privilege approach [6][7]. - Implementation: When using an alternative token with the actions/checkout action, you must explicitly provide it to ensure your git operations use that token rather than the default one [1][8][9]: - uses: actions/checkout@v4 with: token: ${{ secrets.YOUR_PAT_OR_APP_TOKEN }} If you do not use the correct token in the checkout step, the actions/checkout action may persist the default GITHUB_TOKEN in the local repository configuration, causing subsequent git commands to authenticate as the default token despite other configurations [2][4]. Citations:
๐ Script executed: #!/bin/bash
set -euo pipefail
# Locate the workflows
ls -la .github/workflows || true
rg -n "version-tag-on-merge\.yml|version-tag-on-merge" .github/workflows -S || true
# Show the full relevant sections with line numbers for version-tag-on-merge.yml
if [ -f .github/workflows/version-tag-on-merge.yml ]; then
echo "==== version-tag-on-merge.yml (head) ===="
nl -ba .github/workflows/version-tag-on-merge.yml | head -n 120
fi
# Find firebase-app-distribution workflow and show its trigger config
fb_file="$(ls .github/workflows | rg -n "firebase-app-distribution" || true)"
echo "==== Candidate firebase-app-distribution workflow files ===="
ls .github/workflows | rg -i "firebase.*distribution" || true
for f in .github/workflows/*; do
if [ -f "$f" ] && rg -q "firebase-app-distribution" "$f"; then
echo "==== file: $f (trigger section) ===="
nl -ba "$f" | sed -n '1,220p'
fi
done
# If there is a file named exactly firebase-app-distribution.yml, dump it
if [ -f .github/workflows/firebase-app-distribution.yml ]; then
echo "==== firebase-app-distribution.yml ===="
nl -ba .github/workflows/firebase-app-distribution.yml | head -n 260
fi
# Generic search for tags trigger
rg -n "on:\s*push:|push:\s*tags|tags:" .github/workflows -S || trueRepository: Team-Prezel/Prezel-Android Length of output: 572 ๐ Script executed: #!/bin/bash
set -euo pipefail
echo "== Workflows =="
ls -la .github/workflows
echo "== version-tag-on-merge.yml =="
# show first 200 lines with line numbers
cat -n .github/workflows/version-tag-on-merge.yml | head -n 220
echo "== firebase-app-distribution.yml (top/trigger) =="
# show first 260 lines with line numbers
cat -n .github/workflows/firebase-app-distribution.yml | head -n 260
echo "== Find git push / tags usage in version-tag-on-merge.yml =="
rg -n "git push|push origin|TAG_NAME|tags|refs/tags" .github/workflows/version-tag-on-merge.yml -S || true
echo "== Find checkout token/persist-credentials in version-tag-on-merge.yml =="
rg -n "actions/checkout|persist-credentials|token:" .github/workflows/version-tag-on-merge.yml -S || true
echo "== Find push: tags trigger in firebase-app-distribution.yml =="
rg -n "on:\s*push|push:\s*tags|tags:" .github/workflows/firebase-app-distribution.yml -S || true
echo "== Also search for other workflows triggered by tags push =="
rg -n "push:\s*tags" .github/workflows -S || trueRepository: Team-Prezel/Prezel-Android Length of output: 8902 ๊ธฐ๋ณธ
๐งฐ Tools๐ช zizmor (1.25.2)[warning] 17-20: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) [error] 18-18: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) ๐ค Prompt for AI Agents |
||
|
|
||
| - name: Determine next version tag | ||
| env: | ||
| PR_LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }} | ||
| TARGET_SHA: ${{ github.event.pull_request.merge_commit_sha }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| git fetch --tags --force | ||
|
|
||
| existing_tag="$(git tag --points-at "$TARGET_SHA" --list 'v*' | sort -V | tail -n 1)" | ||
| if [ -n "$existing_tag" ]; then | ||
| echo "TAG_NAME=$existing_tag" >> "$GITHUB_ENV" | ||
| echo "TAG_ALREADY_EXISTS=true" >> "$GITHUB_ENV" | ||
| exit 0 | ||
| fi | ||
|
|
||
| bump="patch" | ||
| if [[ "$PR_LABELS_JSON" == *'"major"'* ]]; then | ||
| bump="major" | ||
| elif [[ "$PR_LABELS_JSON" == *'"minor"'* ]]; then | ||
| bump="minor" | ||
| elif [[ "$PR_LABELS_JSON" == *'"patch"'* ]]; then | ||
| bump="patch" | ||
| fi | ||
|
|
||
| latest_tag="$(git tag --list 'v*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1 || true)" | ||
| latest_tag="${latest_tag:-v0.0.0}" | ||
| version="${latest_tag#v}" | ||
|
|
||
| IFS='.' read -r major minor patch <<< "$version" | ||
|
|
||
| case "$bump" in | ||
| major) | ||
| major=$((major + 1)) | ||
| minor=0 | ||
| patch=0 | ||
| ;; | ||
| minor) | ||
| minor=$((minor + 1)) | ||
| patch=0 | ||
| ;; | ||
| patch) | ||
| patch=$((patch + 1)) | ||
| ;; | ||
| esac | ||
|
|
||
| echo "TAG_NAME=v${major}.${minor}.${patch}" >> "$GITHUB_ENV" | ||
| echo "TAG_ALREADY_EXISTS=false" >> "$GITHUB_ENV" | ||
| echo "BUMP_TYPE=$bump" >> "$GITHUB_ENV" | ||
|
|
||
| - name: Create version tag | ||
| if: ${{ env.TAG_ALREADY_EXISTS == 'false' }} | ||
| env: | ||
| TARGET_SHA: ${{ github.event.pull_request.merge_commit_sha }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
|
|
||
| git tag -a "$TAG_NAME" "$TARGET_SHA" -m "Release $TAG_NAME" | ||
| git push origin "$TAG_NAME" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,26 @@ plugins { | |
| alias(libs.plugins.prezel.android.application.compose) | ||
| alias(libs.plugins.prezel.hilt) | ||
| alias(libs.plugins.kotlinx.serialization) | ||
| alias(libs.plugins.google.gms.google.services) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐งฉ Analysis chain๐ Script executed: #!/bin/bash
# google-services.json ๊ด๋ จ ์
์
๋ฌธ์/์ธ๊ธ ํ์ธ
rg -n -i 'google-services.json' --glob '!*.gradle*' --glob '!**/.github/**'Repository: Team-Prezel/Prezel-Android Length of output: 52 ๐ Script executed: #!/bin/bash
set -euo pipefail
# 1) ํ์ธ ๋์ build.gradle.kts์ ๊ด๋ จ ๊ตฌ๊ฐ ์ถ๋ ฅ
echo "== Prezel/app/build.gradle.kts (lines ~1-80) =="
nl -ba Prezel/app/build.gradle.kts | sed -n '1,120p'
# 2) repo ์ ์ฒด์์ google-services.json / googleServices ๋ธ๋ก / MissingGoogleServicesStrategy ์ฌ์ฉ์ฒ ํ์
echo
echo "== rg for google-services.json =="
rg -n --hidden -S -i 'google-services\.json' .
echo
echo "== rg for googleServices { =="
rg -n --hidden -S -i 'googleServices\s*\{' .
echo
echo "== rg for MissingGoogleServicesStrategy =="
rg -n --hidden -S -i 'MissingGoogleServicesStrategy' .
echo
echo "== rg for missingGoogleServicesStrategy =="
rg -n --hidden -S -i 'missingGoogleServicesStrategy' .
# 3) CI/์ํฌํ๋ก์ฐ์์ ํ์ผ์ ์์ฑ/๋ณต์ฌํ๋์ง ํ์ธ (GitHub Actions)
echo
echo "== rg for google-services within .github/ =="
rg -n --hidden -S -i 'google-services' .github || trueRepository: Team-Prezel/Prezel-Android Length of output: 179 ๐ Script executed: #!/bin/bash
set -euo pipefail
# ํ์ํ ๊ฒฝ์ฐ, google-services plugin ์ ์ฉ์ด ๋ค๋ฅธ ๋ชจ๋/๋ฃจํธ์๋ ์๋์ง ๋น ๋ฅด๊ฒ ํ์ธ
echo "== rg for alias(libs.plugins.google.gms.google.services) =="
rg -n --hidden -S 'google\.services' Prezel || true
echo
echo "== rg for com.google.gms:google-services =="
rg -n --hidden -S 'com\.google\.gms:google-services' . || trueRepository: Team-Prezel/Prezel-Android Length of output: 346 ๐ Script executed: #!/bin/bash
set -euo pipefail
# build.gradle.kts ์๋ถ๋ถ(ํ๋ฌ๊ทธ์ธ/๊ด๋ จ ์ค์ ํฌํจ) ํ์ธ
echo "== Prezel/app/build.gradle.kts (1-120p) =="
cat -n Prezel/app/build.gradle.kts | sed -n '1,120p'
echo
echo "== googleServices ๋ธ๋ก(googleServices { ... }) ์ ๋ฌด: gradle ํ์ผ๋ค =="
rg -n --hidden -S -i 'googleServices\s*\{' Prezel -g'*.gradle*' -g'*.kts' || true
echo
echo "== MissingGoogleServicesStrategy / missingGoogleServicesStrategy ์ฌ์ฉ์ฒ =="
rg -n --hidden -S -i 'MissingGoogleServicesStrategy|missingGoogleServicesStrategy' Prezel -g'*.gradle*' -g'*.kts' || true
echo
echo "== google-services.json ์ ์ฒด ๋ฌธ์์ด(๋ฌธ์/์คํฌ๋ฆฝํธ ํฌํจ) ๊ฒ์ =="
rg -n --hidden -S -i 'google-services\.json' . || true
echo
echo "== GitHub Actions/์ํฌํ๋ก์ฐ์์ google-services.json ์์ฑ/๋ณต์ฌ ํ์ =="
rg -n --hidden -S -i 'google-services\.json|googleServices' .github || trueRepository: Team-Prezel/Prezel-Android Length of output: 4257 ๐ Script executed: #!/bin/bash
set -euo pipefail
echo "== top-level dirs =="
ls -la
echo
echo "== settings.gradle.kts (์์ผ๋ฉด) =="
ls -แแ settings.gradle.kts 2>/dev/null || true
ls -la Prezel/settings.gradle.kts 2>/dev/null || true
[ -f settings.gradle.kts ] && sed -n '1,200p' settings.gradle.kts || true
[ -f Prezel/settings.gradle.kts ] && sed -n '1,200p' Prezel/settings.gradle.kts || true
echo
echo "== .github/workflows: google-services.json ์์ฑ ์คํ
=="
rg -n --hidden -S -i "Create google-services\.json|google-services\.json" .github/workflows || true
echo
echo "== workflow๋ค์ด ์ด๋ ๋๋ ํ ๋ฆฌ์์ ์คํ๋๋์ง (working-directory ์กด์ฌ ์ฌ๋ถ) =="
rg -n --hidden -S -i "working-directory" .github/workflows || true
echo
echo "== .gitignore google-services.json ๋ผ์ธ =="
rg -n --hidden -S 'google-services\.json' .gitignore .gitignore* || trueRepository: Team-Prezel/Prezel-Android Length of output: 4101 ๐ Web query:
๐ก Result: The default value for Citations:
๐ Script executed: #!/bin/bash
set -euo pipefail
rg -n -i 'google-services\.json|google services|missingGoogleServicesStrategy|firebase' README.md scripts .github/workflows .github || trueRepository: Team-Prezel/Prezel-Android Length of output: 2872 ๐ Script executed: #!/bin/bash
set -euo pipefail
echo "== .gitignore (google-services.json ํฌํจ ๊ทผ์ฒ 20์ค) =="
sed -n '1,120p' .gitignore | nl -ba | sed -n '20,60p'
echo
echo "== README.md google-services ๊ด๋ จ ์ธ๊ธ (๊ฐ๋ฅํ ๊ฒฝ์ฐ) =="
rg -n -i 'google-services\.json|google services|Firebase' README.md || true
echo
echo "== ์ถ๊ฐ: local ์
์
๋ฌธ์/์คํฌ๋ฆฝํธ์์ google-services.json ์ธ๊ธ =="
rg -n -i 'google-services\.json|google services' scripts .github/workflows || trueRepository: Team-Prezel/Prezel-Android Length of output: 181 ๐ Script executed: #!/bin/bash
set -euo pipefail
echo "== .gitignore (google-services.json ํฌํจ ๊ทผ์ฒ 20์ค) =="
sed -n '20,45p' .gitignore
echo
echo "== README.md google-services/Firebase ๊ด๋ จ ์ธ๊ธ =="
rg -n -i 'google-services\.json|google services|MissingGoogleServicesStrategy|Firebase' README.md || true
echo
echo "== scripts/ ๋ด ์ธ๊ธ =="
rg -n -i 'google-services\.json|google services|MissingGoogleServicesStrategy|Firebase' scripts .github/workflows || trueRepository: Team-Prezel/Prezel-Android Length of output: 1793
๐ค Prompt for AI Agents |
||
| } | ||
|
|
||
| val androidVersionCodeProvider = providers | ||
| .gradleProperty("ANDROID_VERSION_CODE") | ||
| .orElse(providers.environmentVariable("ANDROID_VERSION_CODE")) | ||
| .orElse("1") | ||
| val androidVersionNameProvider = providers | ||
| .gradleProperty("ANDROID_VERSION_NAME") | ||
| .orElse(providers.environmentVariable("ANDROID_VERSION_NAME")) | ||
| .orElse("0.1.0") | ||
|
|
||
| android { | ||
| namespace = "com.team.prezel" | ||
|
|
||
| defaultConfig { | ||
| versionCode = androidVersionCodeProvider.get().toInt() | ||
| versionName = androidVersionNameProvider.get() | ||
| } | ||
|
|
||
| buildTypes { | ||
| debug { | ||
| isMinifyEnabled = false | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๐งฉ Analysis chain
๐ Script executed:
Repository: Team-Prezel/Prezel-Android
Length of output: 205
๐ Script executed:
Repository: Team-Prezel/Prezel-Android
Length of output: 1873
๐ Script executed:
Repository: Team-Prezel/Prezel-Android
Length of output: 3258
๐ Web query:
Discord embed timestamp format ISO8601 required๐ก Result:
When adding a timestamp to a Discord embed, the API strictly requires an ISO 8601 formatted string [1][2]. The expected format is a string representing UTC time [3][1]. While often referred to as ISO 8601, it is implemented as a specific subset of the standard, typically following the pattern YYYY-MM-DDTHH:MM:SS.MSSZ [3][1]. The Z suffix is essential to indicate Zulu (UTC) time [1]. Key technical considerations: - Data Type: The timestamp field in the embed object must be a string, not a Unix integer or other numeric type [1][2]. - Timezone: The input should be provided in UTC. The Discord client automatically adjusts the displayed time based on the local timezone settings of the viewing user [3][4][1]. - Format Constraints: Avoid using timezone-aware variations with offsets (such as +00:00 or +01:00) that deviate from the standard Z-suffix format, as these may cause API errors [5][6]. For developers, common practice is to use language-specific methods to generate this string, such as new Date.toISOString in JavaScript [7]. Passing raw timestamps or objects directly without conversion to this specific string format will result in a validation error [1][2].
Citations:
๐ Web query:
GitHub push event payload head_commit timestamp null pushed_at format๐ก Result:
In the GitHub โpushโ webhook payload,
head_commitis typed asobject or null[1][2]. When itโs present, itstimestampfield is an ISO 8601 / RFC 3339 date-time string (for example2018-06-19T19:03:12-07:00) [3]. So: -head_commit.timestampformat: ISO 8601 / RFC 3339 date-time string likeYYYY-MM-DDTHH:MM:SSยฑHH:MM(orZ) [3]. -head_commitcan be null: yes (object or null) [1][2]. - Ifhead_commitis null, use the webhookโscommits[]array (each commit object includes its owntimestamp) as the alternative source of commit times [1]. Regardingpushed_at: that field appears in other GitHub API contexts (e.g., repository REST responses) and is not the timestamp field inside the webhookโshead_commitobject [4].Citations:
๐ Web query:
GitHub Actions github.event.repository.pushed_at type ISO8601๐ก Result:
In GitHub Actions, the
github.eventcontext provides access to the full webhook payload of the event that triggered the workflow run [1][2]. When this event includes a repository object, thepushed_atproperty is represented as a string containing an ISO 8601-formatted timestamp [3][4]. The format follows the standard UTC timestamp convention:YYYY-MM-DDTHH:MM:SSZ[3]. Key details regarding this field include: - Context Availability: The structure ofgithub.eventdepends entirely on the specific event type that triggered the workflow (e.g.,push,pull_request) [1][2]. - Data Type: While it is parsed as a string in the JSON payload, it represents a point in time [3][4]. - Meaning: Thepushed_attimestamp indicates the date and time of the last commit pushed to the repository [3]. It is distinct fromupdated_at, which reflects changes to the repository object itself (such as updates to the description, homepage, or primary language) [3]. Becausegithub.eventis a direct mirror of the GitHub webhook payload for that event, it adheres to the data formats defined in the GitHub REST API and Webhook documentation [1][2].Citations:
๐ Web query:
GitHub push webhook payload repository.pushed_at format ISO8601๐ก Result:
In the GitHub API and webhook payloads, the pushed_at field is formatted as an ISO 8601 string, typically in the format YYYY-MM-DDTHH:MM:SSZ [1][2]. This timestamp represents the date and time of the last commit pushed to the repository [1][2]. Because GitHub standardizes its REST API and webhook timestamps to this ISO 8601 format, it is suitable for direct parsing by most modern programming languages and data processing tools [3][1].
Citations:
๐ Web query:
Discord embed timestamp accepts ISO8601 with timezone offset -07:00๐ก Result:
Discord embed
timestampdoes not reliably accept ISO8601 timezone-offset forms like-07:00(e.g.,2018-01-01T00:00:00-07:00) in the Discord API for embed timestamps. Evidence: the Discord API embedtimestampappears to be a restricted ISO8601/RFC3339 subset that (per documented discussion) does not accept timezone-aware ISO8601 offsets such as+00:00or+01:00, and only acceptsZor no timezone indicator (examples treated as valid are essentially the form with an optionalZ). Specifically, a Discord API docs issue notes the embed timestamp format matches a regex subset where the timezone component is(Z)?and states it โdoesnโt accept timezone aware ISO8601, e.g. +00:00 or +01:00 etc.โ [1][2]. Practical guidance: - PreferZ(UTC) timestamps, e.g.2026-05-31T18:23:45.000Z. - If you have a-07:00timestamp, convert it to UTC before sending so you end up with aZstring. Caveat: some third-party client libraries/documentation may imply broader ISO8601 support, but the Discord embed timestamp strictness issue above indicates offset forms are not consistently accepted by the API for embeds. [1][2][3]Citations:
๐ Web query:
discord webhook embed timestamp valid ISO8601 Z vs offset๐ก Result:
Discord webhooks and API objects generally expect timestamps in ISO 8601 format [1][2]. For the
timestampfield in an embed object, the API accepts ISO 8601 strings [1]. Regarding the specific format, the industry standard and Discord's expected input for ISO 8601 timestamps is a string that represents the date and time, typically ending in 'Z' (indicating UTC) [1][3]. While ISO 8601 allows for both 'Z' and numerical UTC offsets (e.g., +00:00), using 'Z' is the standard way to denote UTC [4]. Some implementation issues in third-party libraries have historically shown that passing an explicit offset (like +00:00) may be handled differently than 'Z' depending on how the underlying JSON serializer treats the string [5]. To ensure maximum compatibility and avoid potential serialization errors, it is recommended to format your timestamp as a UTC string ending with 'Z' [3]. In summary: - Thetimestampfield in Discord embeds accepts ISO 8601 format [1]. - You should use the 'Z' suffix to denote UTC rather than adding a numerical offset (like +00:00) to ensure consistent behavior across different environments [3][4]. - Always format as YYYY-MM-DDTHH:MM:SSZ [3].Citations:
Discord embed
timestamp๋ UTC ISO8601(...Z)๋ก ์ ๊ทํ ํ์.github/workflows/firebase-app-distribution.yml์์EVENT_TIMESTAMP: ${{ github.event.head_commit.timestamp || github.event.repository.pushed_at }}๋ฅผ ๊ทธ๋๋ก embedtimestamp์ ๋ฃ๊ณ ์๋๋ฐ,repository.pushed_at์ Unix epoch ์ ์๊ฐ ์๋๋ผ ISO8601 ๋ฌธ์์ด์ด๋ผ ํด๋น ์ฐ๋ ค๋ ๋ฎ์ต๋๋ค.head_commit.timestamp๋ RFC3339์ ํ์์กด ์คํ์ (-07:00 ๋ฑ)์ ํฌํจํ ์ ์๊ณ Discord embedtimestamp๋Zํํ๋ง ์์ ์ ์ผ๋ก ํ์ฉํ๋ฏ๋ก,run๋จ๊ณ์์date -u -d "$EVENT_TIMESTAMP" '+%Y-%m-%dT%H:%M:%SZ'์ฒ๋ผ UTC๋ก ๋ณํํด ๋ฃ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.๐ค Prompt for AI Agents