From b0e207d47b3ab17f3c34f9554f3fa0c1bf0ad4a3 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 13:36:31 -0400 Subject: [PATCH 01/16] feat: add dx dora metric push shared action --- README.md | 8 +-- publish_dx_dora_metrics/README.md | 82 ++++++++++++++++++++++++++++++ publish_dx_dora_metrics/action.yml | 50 ++++++++++++++++++ 3 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 publish_dx_dora_metrics/README.md create mode 100644 publish_dx_dora_metrics/action.yml diff --git a/README.md b/README.md index e8cc3b5..f62a962 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,10 @@ Collection of GitHub actions for ecobee. While the current examples are all comp | Action | | ---------------------------------------------------------------------- | -| [`ecobee/github-actions/go_build_artifact@main`](../go_build_artifact) | -| [`ecobee/github-actions/go_test_and_lint@main`](../go_test_and_lint) | -| [`ecobee/github-actions/push_docker_gcr@main`](../push_docker_gcr) | +| [`ecobee/github-actions/go_build_artifact@v1`](../go_build_artifact) | +| [`ecobee/github-actions/go_test_and_lint@v1`](../go_test_and_lint) | +| [`ecobee/github-actions/push_docker_gcr@v1`](../push_docker_gcr) | +| [`ecobee/github-actions/publish_dx_dora_metrics@v1`](../publish_dx_dora_metrics) | ## Usage @@ -15,6 +16,7 @@ See individual action directory for details on usage and examples. - [Go Build artifact](../go_build_artifact) - builds golang binary and outputs build tag - [Go Test and Lint](../go_test_and_lint) - runs golang tests, and lints with golangci-lint - [Push Docker GCR](../push_docker_gcr) - creates docker file from repo's Dockerfile, pushed using supplied build tag +- [Publish DX DORA Metrics](../publish_dx_dora_metrics) - publishes deployment metrics to DX for DORA tracking diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..23e7282 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Simple release script for github-actions +# Usage: ./scripts/release.sh [version] + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + exit 1 +} + +info() { + echo -e "${GREEN}$1${NC}" +} + +warn() { + echo -e "${YELLOW}$1${NC}" +} + +# Compare semver versions +# Returns 0 if $1 > $2, 1 otherwise +version_gt() { + local ver1=$1 + local ver2=$2 + + # Strip 'v' prefix if present + ver1=${ver1#v} + ver2=${ver2#v} + + # Split into major.minor.patch + IFS='.' read -ra V1 <<< "$ver1" + IFS='.' read -ra V2 <<< "$ver2" + + # Compare major + if [[ ${V1[0]} -gt ${V2[0]} ]]; then return 0; fi + if [[ ${V1[0]} -lt ${V2[0]} ]]; then return 1; fi + + # Compare minor + if [[ ${V1[1]:-0} -gt ${V2[1]:-0} ]]; then return 0; fi + if [[ ${V1[1]:-0} -lt ${V2[1]:-0} ]]; then return 1; fi + + # Compare patch + if [[ ${V1[2]:-0} -gt ${V2[2]:-0} ]]; then return 0; fi + + return 1 +} + +# Validate semver format +validate_semver() { + local version=$1 + if [[ ! $version =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + error "Invalid version format: $version (expected: v1.2.3 or 1.2.3)" + fi +} + +main() { + # Check we're on main branch + local current_branch=$(git branch --show-current) + if [[ "$current_branch" != "main" ]]; then + error "Must be on 'main' branch to publish (currently on: $current_branch)" + fi + + # Check working tree is clean + if [[ -n $(git status --porcelain) ]]; then + error "Working tree is not clean. Commit or stash changes first." + fi + + # Fetch latest tags + info "Fetching latest tags..." + git fetch --tags + + # Get latest version tag + local latest_tag=$(git tag -l "v*.*.*" | sort -V | tail -n1) + + if [[ -z "$latest_tag" ]]; then + warn "No existing version tags found. This will be the first release." + latest_tag="v0.0.0" + else + info "Latest version: $latest_tag" + fi + + # Get new version + local new_version="${1:-}" + if [[ -z "$new_version" ]]; then + echo "" + read -p "Enter new version (e.g., v1.0.0): " new_version + fi + + # Add 'v' prefix if missing + if [[ ! $new_version =~ ^v ]]; then + new_version="v${new_version}" + fi + + # Validate format + validate_semver "$new_version" + + # Check if tag already exists + if git rev-parse "$new_version" >/dev/null 2>&1; then + error "Tag $new_version already exists" + fi + + # Validate new version is greater than latest + if [[ "$latest_tag" != "v0.0.0" ]]; then + if ! version_gt "$new_version" "$latest_tag"; then + error "New version $new_version must be greater than $latest_tag" + fi + fi + + # Extract major version for major tag (e.g., v1 from v1.2.3) + local major_version=$(echo "$new_version" | sed -E 's/^v([0-9]+)\..*/v\1/') + + echo "" + info "Release Summary:" + echo " Previous: $latest_tag" + echo " New: $new_version" + echo " Major: $major_version (will be updated)" + echo "" + + read -p "Create and push these tags? (y/N): " confirm + if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + warn "Release cancelled" + exit 0 + fi + + # Create and push version tag + info "Creating tag $new_version..." + git tag "$new_version" + git push origin "$new_version" + + # Update major version tag + info "Updating major version tag $major_version..." + git tag -f "$major_version" + git push -f origin "$major_version" + + echo "" + info "✓ Release complete!" + echo "" + echo "Tags created:" + echo " - $new_version" + echo " - $major_version" + echo "" + echo "Teams can now use: @$major_version or @$new_version" +} + +main "$@" From c80c4cd2b22170f543575dcab8afa4d6d7277aac Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 14:03:30 -0400 Subject: [PATCH 04/16] docs: clarify expectations --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index c6bc5ed..90045e3 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,14 @@ For maintainers releasing new versions: - Validate the new version is greater than the current version - Create and push both the version tag (`v1.2.3`) and major tag (`v1`) +> **⚠️ Important:** The release script only validates version format and ordering. **You are responsible for:** +> - Reviewing the changes since the last release +> - Determining if the version bump is appropriate (major/minor/patch) +> - Ensuring breaking changes are properly documented +> - Verifying all actions work as expected +> +> The script is a safeguard against simple mistakes, not a substitute for careful release management. + **Manual alternative:** ```bash git tag v1.0.0 From 522644a0b3902b299c75000d20673148bc1fefd0 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 14:05:27 -0400 Subject: [PATCH 05/16] fix: simplify script slightly --- scripts/release.sh | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/scripts/release.sh b/scripts/release.sh index 23e7282..fe7162b 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -53,8 +53,8 @@ version_gt() { # Validate semver format validate_semver() { local version=$1 - if [[ ! $version =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - error "Invalid version format: $version (expected: v1.2.3 or 1.2.3)" + if [[ ! $version =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + error "Invalid version format: $version (expected: v1.2.3)" fi } @@ -65,11 +65,6 @@ main() { error "Must be on 'main' branch to publish (currently on: $current_branch)" fi - # Check working tree is clean - if [[ -n $(git status --porcelain) ]]; then - error "Working tree is not clean. Commit or stash changes first." - fi - # Fetch latest tags info "Fetching latest tags..." git fetch --tags @@ -91,11 +86,6 @@ main() { read -p "Enter new version (e.g., v1.0.0): " new_version fi - # Add 'v' prefix if missing - if [[ ! $new_version =~ ^v ]]; then - new_version="v${new_version}" - fi - # Validate format validate_semver "$new_version" From 92c9f835266e4e5fd02b1674220bf2f393e3fd6b Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 14:08:10 -0400 Subject: [PATCH 06/16] fix: rename script to be intuitive + simplify implementation --- README.md | 6 +----- scripts/{release.sh => set-release-tag.sh} | 7 +++---- 2 files changed, 4 insertions(+), 9 deletions(-) rename scripts/{release.sh => set-release-tag.sh} (95%) diff --git a/README.md b/README.md index 90045e3..57f62e0 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,7 @@ For maintainers releasing new versions: 1. **Merge changes to main** 2. **Run the release script:** ```bash - ./scripts/release.sh v1.2.3 - ``` - Or run without a version to be prompted: - ```bash - ./scripts/release.sh + ./scripts/set-release-tag.sh v1.2.3 ``` The script will: - Validate you're on `main` with a clean working tree diff --git a/scripts/release.sh b/scripts/set-release-tag.sh similarity index 95% rename from scripts/release.sh rename to scripts/set-release-tag.sh index fe7162b..1c92676 100755 --- a/scripts/release.sh +++ b/scripts/set-release-tag.sh @@ -2,7 +2,7 @@ set -euo pipefail # Simple release script for github-actions -# Usage: ./scripts/release.sh [version] +# Usage: ./scripts/set-release-tag.sh RED='\033[0;31m' GREEN='\033[0;32m' @@ -79,11 +79,10 @@ main() { info "Latest version: $latest_tag" fi - # Get new version + # Get new version (required) local new_version="${1:-}" if [[ -z "$new_version" ]]; then - echo "" - read -p "Enter new version (e.g., v1.0.0): " new_version + error "Version argument required. Usage: ./scripts/set-release-tag.sh v1.2.3" fi # Validate format From ff5adcbf07aa93726aa59fb106b46f62baa0fdc0 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 15:14:04 -0400 Subject: [PATCH 07/16] fix: address security concern from gemini --- publish_dx_dora_metrics/action.yml | 45 +++++++++++++++++++----------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/publish_dx_dora_metrics/action.yml b/publish_dx_dora_metrics/action.yml index 5c62eee..ee18805 100644 --- a/publish_dx_dora_metrics/action.yml +++ b/publish_dx_dora_metrics/action.yml @@ -21,30 +21,43 @@ runs: using: 'composite' steps: - name: Publish deployment to DX + env: + REPOSITORY: ${{ inputs.repository }} + ENVIRONMENT: ${{ inputs.environment }} + SERVICE: ${{ inputs.service }} + COMMIT_SHA: ${{ inputs.commit-sha }} + DEPLOYED_AT: ${{ inputs.deployed-at }} run: | + set -euo pipefail + # Validate DX_API_TOKEN is available if [ -z "$DX_API_TOKEN" ]; then - echo "ERROR: DX_API_TOKEN environment variable is not set" - echo "This should be configured as an organizational secret by the SRE team" + echo "::error::DX_API_TOKEN environment variable is not set. This should be configured as an organizational secret by the SRE team" exit 1 fi - COMMIT_SHA="${{ inputs.commit-sha }}" - DEPLOYED_AT_UNIX="${{ inputs.deployed-at }}" - # Default to current time if not provided - if [ -z "$DEPLOYED_AT_UNIX" ]; then - DEPLOYED_AT_UNIX=$(date +%s) - fi + DEPLOYED_AT_UNIX="${DEPLOYED_AT:-$(date +%s)}" + + # Construct JSON payload safely using jq + PAYLOAD=$(jq -n \ + --arg repo "$REPOSITORY" \ + --arg env "$ENVIRONMENT" \ + --arg svc "$SERVICE" \ + --arg sha "$COMMIT_SHA" \ + --argjson ts "$DEPLOYED_AT_UNIX" \ + '{ + repository: $repo, + environment: $env, + service: {identifier: $svc}, + commit_sha: $sha, + deployed_at: $ts + }') - curl -X POST "https://ecobee.getdx.net/api/deployments.create" \ + # Make API call (fails automatically on 4xx/5xx) + curl --fail --silent --show-error \ + -X POST "https://ecobee.getdx.net/api/deployments.create" \ -H "Authorization: Bearer ${DX_API_TOKEN}" \ -H "Content-Type: application/json" \ - -d '{ - "repository": "${{ inputs.repository }}", - "environment": "${{ inputs.environment }}", - "service": {"identifier": "${{ inputs.service }}"}, - "commit_sha": "'"${COMMIT_SHA}"'", - "deployed_at": '"${DEPLOYED_AT_UNIX}"' - }' + -d "$PAYLOAD" shell: bash From 7b7092e7c29939ffec48b45672704b61b75019b8 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 15:19:29 -0400 Subject: [PATCH 08/16] docs: fix file refs --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 57f62e0..bdfa75f 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ Collection of GitHub actions for ecobee. While the current examples are all comp | Action | | ---------------------------------------------------------------------- | -| [`ecobee/github-actions/go_build_artifact@v1`](../go_build_artifact) | -| [`ecobee/github-actions/go_test_and_lint@v1`](../go_test_and_lint) | -| [`ecobee/github-actions/push_docker_gcr@v1`](../push_docker_gcr) | -| [`ecobee/github-actions/publish_dx_dora_metrics@v1`](../publish_dx_dora_metrics) | +| [`ecobee/github-actions/go_build_artifact@v1`](./go_build_artifact) | +| [`ecobee/github-actions/go_test_and_lint@v1`](./go_test_and_lint) | +| [`ecobee/github-actions/push_docker_gcr@v1`](./push_docker_gcr) | +| [`ecobee/github-actions/publish_dx_dora_metrics@v1`](./publish_dx_dora_metrics) | ## Usage From abab76a01e1296c83c31f663f046aab293d43061 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 15:59:37 -0400 Subject: [PATCH 09/16] fix: target trunk based, get rid of env --- publish_dx_dora_metrics/action.yml | 63 ------------ .../README.md | 0 .../action.yml | 96 +++++++++++++++++++ 3 files changed, 96 insertions(+), 63 deletions(-) delete mode 100644 publish_dx_dora_metrics/action.yml rename {publish_dx_dora_metrics => publish_dx_dora_metrics_trunk_based}/README.md (100%) create mode 100644 publish_dx_dora_metrics_trunk_based/action.yml diff --git a/publish_dx_dora_metrics/action.yml b/publish_dx_dora_metrics/action.yml deleted file mode 100644 index ee18805..0000000 --- a/publish_dx_dora_metrics/action.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: 'Publish DX DORA Metrics' -description: 'Publishes deployment metrics to DX for DORA tracking. Requires DX_API_TOKEN organizational secret.' -inputs: - repository: - description: 'Repository identifier (e.g., ecobee/service-name)' - required: true - environment: - description: 'Deployment environment (e.g., prod, staging, test)' - required: true - service: - description: 'Service identifier' - required: true - commit-sha: - description: 'Git commit SHA (defaults to current commit)' - required: false - default: ${{ github.sha }} - deployed-at: - description: 'Unix timestamp of deployment (defaults to current time)' - required: false -runs: - using: 'composite' - steps: - - name: Publish deployment to DX - env: - REPOSITORY: ${{ inputs.repository }} - ENVIRONMENT: ${{ inputs.environment }} - SERVICE: ${{ inputs.service }} - COMMIT_SHA: ${{ inputs.commit-sha }} - DEPLOYED_AT: ${{ inputs.deployed-at }} - run: | - set -euo pipefail - - # Validate DX_API_TOKEN is available - if [ -z "$DX_API_TOKEN" ]; then - echo "::error::DX_API_TOKEN environment variable is not set. This should be configured as an organizational secret by the SRE team" - exit 1 - fi - - # Default to current time if not provided - DEPLOYED_AT_UNIX="${DEPLOYED_AT:-$(date +%s)}" - - # Construct JSON payload safely using jq - PAYLOAD=$(jq -n \ - --arg repo "$REPOSITORY" \ - --arg env "$ENVIRONMENT" \ - --arg svc "$SERVICE" \ - --arg sha "$COMMIT_SHA" \ - --argjson ts "$DEPLOYED_AT_UNIX" \ - '{ - repository: $repo, - environment: $env, - service: {identifier: $svc}, - commit_sha: $sha, - deployed_at: $ts - }') - - # Make API call (fails automatically on 4xx/5xx) - curl --fail --silent --show-error \ - -X POST "https://ecobee.getdx.net/api/deployments.create" \ - -H "Authorization: Bearer ${DX_API_TOKEN}" \ - -H "Content-Type: application/json" \ - -d "$PAYLOAD" - shell: bash diff --git a/publish_dx_dora_metrics/README.md b/publish_dx_dora_metrics_trunk_based/README.md similarity index 100% rename from publish_dx_dora_metrics/README.md rename to publish_dx_dora_metrics_trunk_based/README.md diff --git a/publish_dx_dora_metrics_trunk_based/action.yml b/publish_dx_dora_metrics_trunk_based/action.yml new file mode 100644 index 0000000..4509a15 --- /dev/null +++ b/publish_dx_dora_metrics_trunk_based/action.yml @@ -0,0 +1,96 @@ +name: 'Publish DX DORA Metrics (Trunk-Based)' +description: 'Publishes deployment metrics to DX for trunk-based development workflows. Requires DX_API_TOKEN organizational secret.' +inputs: + repository: + description: 'Repository identifier (e.g., ecobee/service-name)' + required: true + service: + description: 'Service identifier' + required: true + commit-sha: + description: 'Git commit SHA (defaults to current commit)' + required: false + default: ${{ github.sha }} + deployed-at: + description: 'Unix timestamp of deployment (defaults to current time)' + required: false + commit-timestamp: + description: 'Unix timestamp of commit (for change lead time tracking)' + required: false +runs: + using: 'composite' + steps: + - name: Publish deployment to DX + env: + REPOSITORY: ${{ inputs.repository }} + SERVICE: ${{ inputs.service }} + COMMIT_SHA: ${{ inputs.commit-sha }} + DEPLOYED_AT: ${{ inputs.deployed-at }} + COMMIT_TIMESTAMP: ${{ inputs.commit-timestamp }} + run: | + set -euo pipefail + + # Validate required inputs + if [ -z "$REPOSITORY" ]; then + echo "::error::repository input is required" + exit 1 + fi + + if [ -z "$SERVICE" ]; then + echo "::error::service input is required" + exit 1 + fi + + if [ -z "$COMMIT_SHA" ]; then + echo "::error::commit-sha input is required" + exit 1 + fi + + # Validate DX_API_TOKEN is available + if [ -z "$DX_API_TOKEN" ]; then + echo "::error::DX_API_TOKEN environment variable is not set. This should be configured as an organizational secret by the SRE team" + exit 1 + fi + + # Default to current time if not provided + DEPLOYED_AT_UNIX="${DEPLOYED_AT:-$(date +%s)}" + + # Construct JSON payload safely using jq + # For trunk-based workflows, includes commit_timestamp in metadata + if [ -n "$COMMIT_TIMESTAMP" ]; then + PAYLOAD=$(jq -n \ + --arg repo "$REPOSITORY" \ + --arg svc "$SERVICE" \ + --arg sha "$COMMIT_SHA" \ + --argjson ts "$DEPLOYED_AT_UNIX" \ + --argjson ct "$COMMIT_TIMESTAMP" \ + '{ + repository: $repo, + service: {identifier: $svc}, + commit_sha: $sha, + deployed_at: $ts, + metadata: { + commit_timestamp: $ct + } + }') + else + PAYLOAD=$(jq -n \ + --arg repo "$REPOSITORY" \ + --arg svc "$SERVICE" \ + --arg sha "$COMMIT_SHA" \ + --argjson ts "$DEPLOYED_AT_UNIX" \ + '{ + repository: $repo, + service: {identifier: $svc}, + commit_sha: $sha, + deployed_at: $ts + }') + fi + + # Make API call (fails automatically on 4xx/5xx) + curl --fail --silent --show-error \ + -X POST "https://ecobee.getdx.net/api/deployments.create" \ + -H "Authorization: Bearer ${DX_API_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "$PAYLOAD" + shell: bash From b260279ed3ff19d9587bac6812477e857176c3e5 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 16:11:53 -0400 Subject: [PATCH 10/16] fix: tweak which inputs we accept to get proper workflows working --- publish_dx_dora_metrics_trunk_based/README.md | 22 ++++++++++++------- .../action.yml | 9 ++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/publish_dx_dora_metrics_trunk_based/README.md b/publish_dx_dora_metrics_trunk_based/README.md index f7a21e9..9d280b0 100644 --- a/publish_dx_dora_metrics_trunk_based/README.md +++ b/publish_dx_dora_metrics_trunk_based/README.md @@ -1,6 +1,6 @@ -# Publish DX DORA Metrics +# Publish DX DORA Metrics (Trunk-Based) -Publishes deployment metrics to DX (ecobee.getdx.net) for DORA tracking and visibility. +Publishes deployment metrics to DX (ecobee.getdx.net) for trunk-based development workflows where code is committed directly to the main branch and deployed. ## Prerequisites @@ -12,13 +12,16 @@ This secret is managed centrally by the SRE team and is automatically available ```yaml - name: Publish DORA metrics - uses: ecobee/github-actions/publish_dx_dora_metrics@v1 + uses: ecobee/github-actions/publish_dx_dora_metrics_trunk_based@v1 env: DX_API_TOKEN: ${{ secrets.DX_API_TOKEN }} with: repository: 'ecobee/my-service' - environment: 'production' service: 'my-service' + # Optional: defaults to 'production' - only needed for testing + environment: 'staging' + # Optional: for change lead time tracking + commit-timestamp: ${{ github.event.head_commit.timestamp }} ``` > **Note:** Use `@v1` for the latest stable version, `@v1.0.0` to pin to a specific release, or `@main` for the latest (unstable) version. @@ -28,10 +31,13 @@ This secret is managed centrally by the SRE team and is automatically available | Input | Description | Required | Default | |-------|-------------|----------|---------| | `repository` | Repository identifier (e.g., `ecobee/service-name`) | Yes | - | -| `environment` | Deployment environment (`prod`, `staging`, `test`, etc.) | Yes | - | +| `environment` | Deployment environment | No | `production` | | `service` | Service identifier | Yes | - | | `commit-sha` | Git commit SHA | No | `${{ github.sha }}` | | `deployed-at` | Unix timestamp of deployment | No | Current time | +| `commit-timestamp` | Unix timestamp of commit (for change lead time tracking) | No | - | + +> **Note on `commit-timestamp`:** This is optional but recommended for accurate change lead time metrics in trunk-based workflows. Use `${{ github.event.head_commit.timestamp }}` or compute from git history if needed. ## Environment Variables @@ -58,13 +64,13 @@ jobs: # ... your deployment steps ... - name: Publish deployment metrics - uses: ecobee/github-actions/publish_dx_dora_metrics@v1 + uses: ecobee/github-actions/publish_dx_dora_metrics_trunk_based@v1 env: DX_API_TOKEN: ${{ secrets.DX_API_TOKEN }} with: repository: 'ecobee/my-service' - environment: 'production' service: 'my-service' + commit-timestamp: ${{ github.event.head_commit.timestamp }} ``` ## Versioning @@ -79,4 +85,4 @@ This action follows [semantic versioning](https://semver.org/). When integrating - The `DX_API_TOKEN` is **never** passed as an action input to prevent accidental exposure in logs - The token is configured as a GitHub organizational secret, managed centrally by the SRE team - The action will fail explicitly if the token is not available -- Never commit tokens or credentials to the repository \ No newline at end of file +- Never commit tokens or credentials to the repository diff --git a/publish_dx_dora_metrics_trunk_based/action.yml b/publish_dx_dora_metrics_trunk_based/action.yml index 4509a15..00bffe8 100644 --- a/publish_dx_dora_metrics_trunk_based/action.yml +++ b/publish_dx_dora_metrics_trunk_based/action.yml @@ -4,6 +4,10 @@ inputs: repository: description: 'Repository identifier (e.g., ecobee/service-name)' required: true + environment: + description: 'Deployment environment (defaults to production)' + required: false + default: 'production' service: description: 'Service identifier' required: true @@ -23,6 +27,7 @@ runs: - name: Publish deployment to DX env: REPOSITORY: ${{ inputs.repository }} + ENVIRONMENT: ${{ inputs.environment }} SERVICE: ${{ inputs.service }} COMMIT_SHA: ${{ inputs.commit-sha }} DEPLOYED_AT: ${{ inputs.deployed-at }} @@ -60,12 +65,14 @@ runs: if [ -n "$COMMIT_TIMESTAMP" ]; then PAYLOAD=$(jq -n \ --arg repo "$REPOSITORY" \ + --arg env "$ENVIRONMENT" \ --arg svc "$SERVICE" \ --arg sha "$COMMIT_SHA" \ --argjson ts "$DEPLOYED_AT_UNIX" \ --argjson ct "$COMMIT_TIMESTAMP" \ '{ repository: $repo, + environment: $env, service: {identifier: $svc}, commit_sha: $sha, deployed_at: $ts, @@ -76,11 +83,13 @@ runs: else PAYLOAD=$(jq -n \ --arg repo "$REPOSITORY" \ + --arg env "$ENVIRONMENT" \ --arg svc "$SERVICE" \ --arg sha "$COMMIT_SHA" \ --argjson ts "$DEPLOYED_AT_UNIX" \ '{ repository: $repo, + environment: $env, service: {identifier: $svc}, commit_sha: $sha, deployed_at: $ts From 4db1aaa7c7b657e3de65ba840be278cf9c1d8e07 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 16:17:21 -0400 Subject: [PATCH 11/16] fix: only include inputs that we need --- publish_dx_dora_metrics_trunk_based/README.md | 6 +- .../action.yml | 79 ++++++++----------- 2 files changed, 35 insertions(+), 50 deletions(-) diff --git a/publish_dx_dora_metrics_trunk_based/README.md b/publish_dx_dora_metrics_trunk_based/README.md index 9d280b0..e1f47f6 100644 --- a/publish_dx_dora_metrics_trunk_based/README.md +++ b/publish_dx_dora_metrics_trunk_based/README.md @@ -33,11 +33,9 @@ This secret is managed centrally by the SRE team and is automatically available | `repository` | Repository identifier (e.g., `ecobee/service-name`) | Yes | - | | `environment` | Deployment environment | No | `production` | | `service` | Service identifier | Yes | - | -| `commit-sha` | Git commit SHA | No | `${{ github.sha }}` | -| `deployed-at` | Unix timestamp of deployment | No | Current time | -| `commit-timestamp` | Unix timestamp of commit (for change lead time tracking) | No | - | +| `commit-timestamp` | Commit timestamp (Unix timestamp or ISO 8601) | Yes | - | -> **Note on `commit-timestamp`:** This is optional but recommended for accurate change lead time metrics in trunk-based workflows. Use `${{ github.event.head_commit.timestamp }}` or compute from git history if needed. +> **Note:** `commit-timestamp` is required for change lead time tracking. Use `${{ github.event.head_commit.timestamp }}` (ISO 8601 format) - the action will automatically convert it to Unix timestamp. ## Environment Variables diff --git a/publish_dx_dora_metrics_trunk_based/action.yml b/publish_dx_dora_metrics_trunk_based/action.yml index 00bffe8..e1d48cf 100644 --- a/publish_dx_dora_metrics_trunk_based/action.yml +++ b/publish_dx_dora_metrics_trunk_based/action.yml @@ -11,16 +11,9 @@ inputs: service: description: 'Service identifier' required: true - commit-sha: - description: 'Git commit SHA (defaults to current commit)' - required: false - default: ${{ github.sha }} - deployed-at: - description: 'Unix timestamp of deployment (defaults to current time)' - required: false commit-timestamp: - description: 'Unix timestamp of commit (for change lead time tracking)' - required: false + description: 'Commit timestamp (Unix timestamp or ISO 8601 format). Needed to calculate lead time for changes in trunk-based workflows. Defaults to current time if not provided, but should be set for accurate metrics.' + required: true runs: using: 'composite' steps: @@ -29,8 +22,7 @@ runs: REPOSITORY: ${{ inputs.repository }} ENVIRONMENT: ${{ inputs.environment }} SERVICE: ${{ inputs.service }} - COMMIT_SHA: ${{ inputs.commit-sha }} - DEPLOYED_AT: ${{ inputs.deployed-at }} + COMMIT_SHA: ${{ github.sha }} COMMIT_TIMESTAMP: ${{ inputs.commit-timestamp }} run: | set -euo pipefail @@ -51,51 +43,46 @@ runs: exit 1 fi + if [ -z "$COMMIT_TIMESTAMP" ]; then + echo "::error::commit-timestamp input is required" + exit 1 + fi + # Validate DX_API_TOKEN is available if [ -z "$DX_API_TOKEN" ]; then echo "::error::DX_API_TOKEN environment variable is not set. This should be configured as an organizational secret by the SRE team" exit 1 fi - # Default to current time if not provided - DEPLOYED_AT_UNIX="${DEPLOYED_AT:-$(date +%s)}" + # Always use current time for deployment + DEPLOYED_AT_UNIX=$(date +%s) - # Construct JSON payload safely using jq - # For trunk-based workflows, includes commit_timestamp in metadata - if [ -n "$COMMIT_TIMESTAMP" ]; then - PAYLOAD=$(jq -n \ - --arg repo "$REPOSITORY" \ - --arg env "$ENVIRONMENT" \ - --arg svc "$SERVICE" \ - --arg sha "$COMMIT_SHA" \ - --argjson ts "$DEPLOYED_AT_UNIX" \ - --argjson ct "$COMMIT_TIMESTAMP" \ - '{ - repository: $repo, - environment: $env, - service: {identifier: $svc}, - commit_sha: $sha, - deployed_at: $ts, - metadata: { - commit_timestamp: $ct - } - }') + # Convert commit timestamp if ISO 8601 format + if [[ "$COMMIT_TIMESTAMP" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T ]]; then + COMMIT_TIMESTAMP_UNIX=$(date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$COMMIT_TIMESTAMP" "+%s" 2>/dev/null || date -d "$COMMIT_TIMESTAMP" +%s) else - PAYLOAD=$(jq -n \ - --arg repo "$REPOSITORY" \ - --arg env "$ENVIRONMENT" \ - --arg svc "$SERVICE" \ - --arg sha "$COMMIT_SHA" \ - --argjson ts "$DEPLOYED_AT_UNIX" \ - '{ - repository: $repo, - environment: $env, - service: {identifier: $svc}, - commit_sha: $sha, - deployed_at: $ts - }') + COMMIT_TIMESTAMP_UNIX="$COMMIT_TIMESTAMP" fi + # Construct JSON payload safely using jq + PAYLOAD=$(jq -n \ + --arg repo "$REPOSITORY" \ + --arg env "$ENVIRONMENT" \ + --arg svc "$SERVICE" \ + --arg sha "$COMMIT_SHA" \ + --argjson ts "$DEPLOYED_AT_UNIX" \ + --argjson ct "$COMMIT_TIMESTAMP_UNIX" \ + '{ + repository: $repo, + environment: $env, + service: {identifier: $svc}, + commit_sha: $sha, + deployed_at: $ts, + metadata: { + commit_timestamp: $ct + } + }') + # Make API call (fails automatically on 4xx/5xx) curl --fail --silent --show-error \ -X POST "https://ecobee.getdx.net/api/deployments.create" \ From f403893c98eff927ba6b69f723441724024f3d56 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 16:21:12 -0400 Subject: [PATCH 12/16] fix: make commit-time baked into the action --- publish_dx_dora_metrics_trunk_based/README.md | 6 +----- publish_dx_dora_metrics_trunk_based/action.yml | 12 +++--------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/publish_dx_dora_metrics_trunk_based/README.md b/publish_dx_dora_metrics_trunk_based/README.md index e1f47f6..9aa9d44 100644 --- a/publish_dx_dora_metrics_trunk_based/README.md +++ b/publish_dx_dora_metrics_trunk_based/README.md @@ -20,8 +20,6 @@ This secret is managed centrally by the SRE team and is automatically available service: 'my-service' # Optional: defaults to 'production' - only needed for testing environment: 'staging' - # Optional: for change lead time tracking - commit-timestamp: ${{ github.event.head_commit.timestamp }} ``` > **Note:** Use `@v1` for the latest stable version, `@v1.0.0` to pin to a specific release, or `@main` for the latest (unstable) version. @@ -33,9 +31,8 @@ This secret is managed centrally by the SRE team and is automatically available | `repository` | Repository identifier (e.g., `ecobee/service-name`) | Yes | - | | `environment` | Deployment environment | No | `production` | | `service` | Service identifier | Yes | - | -| `commit-timestamp` | Commit timestamp (Unix timestamp or ISO 8601) | Yes | - | -> **Note:** `commit-timestamp` is required for change lead time tracking. Use `${{ github.event.head_commit.timestamp }}` (ISO 8601 format) - the action will automatically convert it to Unix timestamp. +> **Note:** The action automatically extracts the commit timestamp from git for change lead time tracking. ## Environment Variables @@ -68,7 +65,6 @@ jobs: with: repository: 'ecobee/my-service' service: 'my-service' - commit-timestamp: ${{ github.event.head_commit.timestamp }} ``` ## Versioning diff --git a/publish_dx_dora_metrics_trunk_based/action.yml b/publish_dx_dora_metrics_trunk_based/action.yml index e1d48cf..3b76984 100644 --- a/publish_dx_dora_metrics_trunk_based/action.yml +++ b/publish_dx_dora_metrics_trunk_based/action.yml @@ -11,9 +11,6 @@ inputs: service: description: 'Service identifier' required: true - commit-timestamp: - description: 'Commit timestamp (Unix timestamp or ISO 8601 format). Needed to calculate lead time for changes in trunk-based workflows. Defaults to current time if not provided, but should be set for accurate metrics.' - required: true runs: using: 'composite' steps: @@ -23,7 +20,6 @@ runs: ENVIRONMENT: ${{ inputs.environment }} SERVICE: ${{ inputs.service }} COMMIT_SHA: ${{ github.sha }} - COMMIT_TIMESTAMP: ${{ inputs.commit-timestamp }} run: | set -euo pipefail @@ -43,17 +39,15 @@ runs: exit 1 fi - if [ -z "$COMMIT_TIMESTAMP" ]; then - echo "::error::commit-timestamp input is required" - exit 1 - fi - # Validate DX_API_TOKEN is available if [ -z "$DX_API_TOKEN" ]; then echo "::error::DX_API_TOKEN environment variable is not set. This should be configured as an organizational secret by the SRE team" exit 1 fi + # Get commit timestamp from git + COMMIT_TIMESTAMP_UNIX=$(git show -s --format=%ct "$COMMIT_SHA") + # Always use current time for deployment DEPLOYED_AT_UNIX=$(date +%s) From f0aa239f11890fc4f93eb5f4a2abe43f56d1a6c1 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 16:24:27 -0400 Subject: [PATCH 13/16] fix: get rid of old check --- publish_dx_dora_metrics_trunk_based/action.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/publish_dx_dora_metrics_trunk_based/action.yml b/publish_dx_dora_metrics_trunk_based/action.yml index 3b76984..1a96640 100644 --- a/publish_dx_dora_metrics_trunk_based/action.yml +++ b/publish_dx_dora_metrics_trunk_based/action.yml @@ -51,13 +51,6 @@ runs: # Always use current time for deployment DEPLOYED_AT_UNIX=$(date +%s) - # Convert commit timestamp if ISO 8601 format - if [[ "$COMMIT_TIMESTAMP" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T ]]; then - COMMIT_TIMESTAMP_UNIX=$(date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$COMMIT_TIMESTAMP" "+%s" 2>/dev/null || date -d "$COMMIT_TIMESTAMP" +%s) - else - COMMIT_TIMESTAMP_UNIX="$COMMIT_TIMESTAMP" - fi - # Construct JSON payload safely using jq PAYLOAD=$(jq -n \ --arg repo "$REPOSITORY" \ From 09abd3b9c0090a3eeecc2a59a7af831fc3e70ac3 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 17:30:38 -0400 Subject: [PATCH 14/16] docs: add more intuitive examples for inputs descriptions --- publish_dx_dora_metrics_trunk_based/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/publish_dx_dora_metrics_trunk_based/action.yml b/publish_dx_dora_metrics_trunk_based/action.yml index 1a96640..3762763 100644 --- a/publish_dx_dora_metrics_trunk_based/action.yml +++ b/publish_dx_dora_metrics_trunk_based/action.yml @@ -2,14 +2,14 @@ name: 'Publish DX DORA Metrics (Trunk-Based)' description: 'Publishes deployment metrics to DX for trunk-based development workflows. Requires DX_API_TOKEN organizational secret.' inputs: repository: - description: 'Repository identifier (e.g., ecobee/service-name)' + description: 'Repository identifier (e.g., ecobee/op-ts-server-core, ecobee/service-home)' required: true environment: description: 'Deployment environment (defaults to production)' required: false default: 'production' service: - description: 'Service identifier' + description: 'Service identifier (e.g. communicator, service-home)' required: true runs: using: 'composite' From 32191e043dc6836634e554475294c10edf0e2cfa Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 17:32:53 -0400 Subject: [PATCH 15/16] docs: elaborate on non-impact of DX API token --- publish_dx_dora_metrics_trunk_based/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/publish_dx_dora_metrics_trunk_based/action.yml b/publish_dx_dora_metrics_trunk_based/action.yml index 3762763..bf71bb3 100644 --- a/publish_dx_dora_metrics_trunk_based/action.yml +++ b/publish_dx_dora_metrics_trunk_based/action.yml @@ -1,5 +1,5 @@ name: 'Publish DX DORA Metrics (Trunk-Based)' -description: 'Publishes deployment metrics to DX for trunk-based development workflows. Requires DX_API_TOKEN organizational secret.' +description: 'Publishes deployment metrics to DX for trunk-based development workflows. Requires DX_API_TOKEN organizational secret which is managed globally by the SRE team. Teams should not have to worry about it.' inputs: repository: description: 'Repository identifier (e.g., ecobee/op-ts-server-core, ecobee/service-home)' From 09e913ccdf4935992f474e509d9bcf7922e93bc7 Mon Sep 17 00:00:00 2001 From: Stephen Kawaguchi Date: Fri, 22 May 2026 17:39:12 -0400 Subject: [PATCH 16/16] docs: point out that we don't want this blocking deploys --- publish_dx_dora_metrics_trunk_based/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/publish_dx_dora_metrics_trunk_based/README.md b/publish_dx_dora_metrics_trunk_based/README.md index 9aa9d44..3694b07 100644 --- a/publish_dx_dora_metrics_trunk_based/README.md +++ b/publish_dx_dora_metrics_trunk_based/README.md @@ -13,6 +13,7 @@ This secret is managed centrally by the SRE team and is automatically available ```yaml - name: Publish DORA metrics uses: ecobee/github-actions/publish_dx_dora_metrics_trunk_based@v1 + continue-on-error: true env: DX_API_TOKEN: ${{ secrets.DX_API_TOKEN }} with: @@ -23,6 +24,8 @@ This secret is managed centrally by the SRE team and is automatically available ``` > **Note:** Use `@v1` for the latest stable version, `@v1.0.0` to pin to a specific release, or `@main` for the latest (unstable) version. +> +> **Important:** Use `continue-on-error: true` to prevent metrics tracking failures from blocking deployments. ## Inputs @@ -60,6 +63,7 @@ jobs: - name: Publish deployment metrics uses: ecobee/github-actions/publish_dx_dora_metrics_trunk_based@v1 + continue-on-error: true env: DX_API_TOKEN: ${{ secrets.DX_API_TOKEN }} with: