Feature Flag Cleanup #2
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Feature Flag Cleanup | |
| on: | |
| schedule: | |
| - cron: '0 15 * * *' # 10am EST | |
| workflow_dispatch: | |
| # Default to a read-only token. Jobs that need to push or open PRs widen | |
| # permissions explicitly below. | |
| permissions: | |
| contents: read | |
| jobs: | |
| analyze: | |
| name: Analyze feature flags | |
| runs-on: namespace-profile-ubuntu-small | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| outputs: | |
| flag_name: ${{ steps.select_flag.outputs.flag_name }} | |
| cargo_flag: ${{ steps.select_flag.outputs.cargo_flag }} | |
| reviewers: ${{ steps.select_flag.outputs.reviewers }} | |
| analyzed_sha: ${{ steps.record_sha.outputs.analyzed_sha }} | |
| artifact_url: ${{ steps.upload_feature_flag_log.outputs.artifact-url }} | |
| steps: | |
| - name: Check out code | |
| uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Record analyzed commit | |
| id: record_sha | |
| run: | | |
| echo "analyzed_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Find old feature flags | |
| uses: warpdotdev/oz-agent-action@main | |
| with: | |
| prompt: | | |
| Your task is to find and list any feature flags which have been enabled by default for more than 3 months. | |
| There will always be at least one such flag. | |
| ## Context | |
| Feature flags are defined in warp_core/src/features.rs. They're enabled by default in app/Cargo.toml, using the default feature list. The mapping between Cargo features and app features is in the enabled_features function of app/src/lib.rs. | |
| ## Recommended Procedure | |
| 1. Use `git blame` on the Cargo.toml file to identify when each feature flag was enabled by default. | |
| 2. Read the enabled_features function to map Cargo features to app features. | |
| 3. Write features that have been enabled by default for more than 3 months to the output file. | |
| You may create temporary files to store useful results, and write helper scripts for subtasks. It is not necessary to do everything in one shot. | |
| ## Result Format | |
| List the old feature flags in ${{ runner.temp }}/flags_to_cleanup.jsonl, in this format (one flag per line): | |
| { "flag_name": "MyFlag", "cargo_flag": "my_flag", "date_enabled": "2025-02-21", "enabling_commit": "aad3293b8fe" } | |
| ## Reminders | |
| * Fields in the output JSONL file have these meanings: | |
| * "flag_name" - the name of the FeatureFlag enum variant | |
| * "cargo_flag" - the name of the flag used in Cargo.toml | |
| * "date_enabled" - the date on which the feature flag was enabled by default | |
| * "enabling_commit" - the Git commit in which the feature flag was enabled by default | |
| * You are running as part of a GitHub automation and must not commit or push any changes. Another agent will process the file you produce. | |
| warp_api_key: ${{ secrets.WARP_API_KEY }} | |
| share: team | |
| - name: Upload feature flag log | |
| id: upload_feature_flag_log | |
| uses: namespace-actions/upload-artifact@f6ccaacc655aec41b93af180d1d7eef21af862d2 # v1.0.3 | |
| with: | |
| name: flags-to-cleanup | |
| path: ${{ runner.temp }}/flags_to_cleanup.jsonl | |
| # As we test out the workflow, only clean up one flag at a time. | |
| # We could also pick several flags, or fan out jobs for every single one. | |
| - name: Select a flag to clean up | |
| id: select_flag | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| cleanup_log="$RUNNER_TEMP/flags_to_cleanup.jsonl" | |
| # Skip over flags that are also used to enable/disable functionality at runtime. | |
| excluded_flags='["CreatingSharedSessions", "ViewingSharedSessions", "AgentMode"]' | |
| flag_name="" | |
| cargo_flag="" | |
| # Iterate flags in order (oldest first) and pick the first one that is not | |
| # excluded and does not already have an open cleanup PR. Sort by | |
| # date_enabled here rather than relying on the analysis agent to emit | |
| # the JSONL in any particular order. | |
| while IFS= read -r candidate; do | |
| [ -z "$candidate" ] && continue | |
| candidate_flag="$(jq -r '.flag_name' <<< "$candidate")" | |
| if [ "$(jq -n --arg flag "$candidate_flag" --argjson excluded "$excluded_flags" '$excluded | index($flag) != null')" = "true" ]; then | |
| echo "Skipping $candidate_flag: in excluded list" | |
| continue | |
| fi | |
| candidate_branch="oz-agent/cleanup-feature-flag-$candidate_flag" | |
| existing_pr="$(gh pr list --state open --head "$candidate_branch" --json number --jq '.[0].number')" | |
| if [ -n "$existing_pr" ] && [ "$existing_pr" != "null" ]; then | |
| echo "Skipping $candidate_flag: open cleanup PR #$existing_pr already exists on $candidate_branch" | |
| continue | |
| fi | |
| flag_name="$candidate_flag" | |
| cargo_flag="$(jq -r '.cargo_flag' <<< "$candidate")" | |
| break | |
| done < <(jq -c -s 'sort_by(.date_enabled) | .[]' "$cleanup_log") | |
| echo "flag_name=$flag_name" >> "$GITHUB_OUTPUT" | |
| echo "cargo_flag=$cargo_flag" >> "$GITHUB_OUTPUT" | |
| echo "Cleaning up $flag_name ($cargo_flag)" | |
| if [ -z "$flag_name" ] || [ "$flag_name" = "null" ]; then | |
| echo "No flag selected for cleanup" | |
| echo "reviewers=" >> "$GITHUB_OUTPUT" | |
| else | |
| enabling_commit="$(jq -r -cs --arg flag "$flag_name" '.[] | select(.flag_name == $flag) | .enabling_commit' "$cleanup_log")" | |
| committer_username="" | |
| # Resolve the GitHub username of the commit author directly via the | |
| # commits API. This is more reliable than mapping a commit email to | |
| # a GitHub user, since commit emails are often noreply addresses or | |
| # otherwise unsearchable. | |
| if [ -n "$enabling_commit" ] && [ "$enabling_commit" != "null" ]; then | |
| committer_username="$(gh api "repos/${{ github.repository }}/commits/$enabling_commit" --jq '.author.login // ""' 2>/dev/null || echo "")" | |
| fi | |
| default_reviewers="bennavetta" | |
| # Verify the committer is still a collaborator before assigning as reviewer. | |
| if [ -n "$committer_username" ] && gh api "repos/${{ github.repository }}/collaborators/$committer_username" --verbose 2>/dev/null; then | |
| echo "Flag originally enabled by $committer_username" | |
| echo "reviewers=$committer_username,$default_reviewers" >> "$GITHUB_OUTPUT" | |
| elif [ -n "$committer_username" ]; then | |
| echo "$committer_username is no longer a collaborator, using fallback" | |
| echo "reviewers=$default_reviewers" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Could not determine flag committer, using fallback" | |
| echo "reviewers=$default_reviewers" >> "$GITHUB_OUTPUT" | |
| fi | |
| fi | |
| cleanup: | |
| name: Run cleanup agent | |
| needs: analyze | |
| if: ${{ needs.analyze.outputs.flag_name && needs.analyze.outputs.flag_name != 'null' }} | |
| runs-on: namespace-profile-ubuntu-small | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Check out code | |
| uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| with: | |
| ref: ${{ needs.analyze.outputs.analyzed_sha }} | |
| fetch-depth: 0 | |
| - name: Setup Rust cache | |
| uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2 | |
| with: | |
| cache: rust | |
| - name: Clean up the unused feature flag | |
| id: cleanup_agent | |
| uses: warpdotdev/oz-agent-action@main | |
| with: | |
| prompt: | | |
| The feature flag ${{ needs.analyze.outputs.flag_name }} is now enabled by default. | |
| Clean up all references to this flag, modifying code to always assume it is enabled. Ensure that the code is correctly formatted and that tests still pass. | |
| Finally, remove the flag from the `FeatureFlag` enum, and remove the equivalent ${{ needs.analyze.outputs.cargo_flag }} feature in app/Cargo.toml | |
| You are running as part of a GitHub automation that runs with a read-only token and will package your changes into a patch for a separate job to commit. Do not create a branch, do not commit, do not push, do not create a PR, and do not call `gh`. Leave your changes in the working tree only. | |
| share: team | |
| warp_api_key: ${{ secrets.WARP_API_KEY }} | |
| - name: Generate cleanup patch | |
| env: | |
| ANALYZED_SHA: ${{ needs.analyze.outputs.analyzed_sha }} | |
| run: | | |
| # If the cleanup agent made any local commits, undo them while keeping | |
| # the changes in the working tree, so the resulting patch reflects the | |
| # full set of changes relative to the analyzed commit. | |
| git reset --mixed "$ANALYZED_SHA" | |
| # Stage all changes (modified, new, deleted) so the patch is complete. | |
| git add -A | |
| git diff --staged --binary > "$RUNNER_TEMP/cleanup.patch" | |
| if [ ! -s "$RUNNER_TEMP/cleanup.patch" ]; then | |
| echo "Cleanup agent produced no changes" >&2 | |
| exit 1 | |
| fi | |
| - name: Upload cleanup patch | |
| uses: namespace-actions/upload-artifact@f6ccaacc655aec41b93af180d1d7eef21af862d2 # v1.0.3 | |
| with: | |
| name: cleanup-patch | |
| path: ${{ runner.temp }}/cleanup.patch | |
| create_pr: | |
| name: Create cleanup PR | |
| needs: [analyze, cleanup] | |
| if: ${{ needs.analyze.outputs.flag_name && needs.analyze.outputs.flag_name != 'null' }} | |
| runs-on: namespace-profile-ubuntu-small | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Check out code | |
| uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| with: | |
| ref: ${{ needs.analyze.outputs.analyzed_sha }} | |
| fetch-depth: 0 | |
| - name: Download cleanup patch | |
| uses: namespace-actions/download-artifact@7cbad919e4b0e09f17e9d6311a444ff002992b5b # v2.0.1 | |
| with: | |
| name: cleanup-patch | |
| path: ${{ runner.temp }} | |
| - name: Apply cleanup patch | |
| run: | | |
| git apply --binary --whitespace=nowarn "$RUNNER_TEMP/cleanup.patch" | |
| - name: Create Pull Request | |
| id: create_pr | |
| uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 | |
| with: | |
| token: ${{ github.token }} | |
| base: ${{ github.event.repository.default_branch }} | |
| commit-message: "Clean up ${{ needs.analyze.outputs.flag_name }} feature flag" | |
| branch: "oz-agent/cleanup-feature-flag-${{ needs.analyze.outputs.flag_name }}" | |
| author: "Oz Agent <oz-agent@warp.dev>" | |
| delete-branch: true | |
| reviewers: ${{ needs.analyze.outputs.reviewers }} | |
| title: "Clean up ${{ needs.analyze.outputs.flag_name }} feature flag" | |
| body: | | |
| Automated cleanup of the `${{ needs.analyze.outputs.flag_name }}` feature flag. | |
| This PR was generated by the feature flag cleanup workflow. | |
| - run: | | |
| echo "Full feature flag analysis results: ${{ needs.analyze.outputs.artifact_url }}" | |
| echo "Pull request created: ${{ steps.create_pr.outputs.pull-request-url }}" | |
| - name: Send Slack notification | |
| uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1 | |
| if: ${{ steps.create_pr.outputs.pull-request-operation == 'created' }} | |
| with: | |
| method: chat.postMessage | |
| token: ${{ secrets.ACTION_MONITORING_SLACK }} | |
| payload: | | |
| channel: "#oncall-client" | |
| # Follows https://api.slack.com/reference/surfaces/formatting#mentioning-users. | |
| # S09SSGE729E is the user group ID for @eng-warp-3. | |
| text: "🧹 New feature flag cleanup PR for `${{ needs.analyze.outputs.flag_name }}`: ${{ steps.create_pr.outputs.pull-request-url }}! <!subteam^S09SSGE729E> please review\nSee logs at https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" |