-
Notifications
You must be signed in to change notification settings - Fork 69
NO-ISSUE: Add one-time TF state rm support for unrefreshable resources #162
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
f6cb726
40fdc27
165e414
ca3c702
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 |
|---|---|---|
|
|
@@ -11,6 +11,10 @@ on: | |
| description: "One-time: import ID for the resource above (e.g. the repo name for github_repository)." | ||
| required: false | ||
| default: "" | ||
| state_rm_addresses: | ||
| description: "One-time: comma-separated Terraform resource addresses to remove from state (e.g. for a member who left the org and whose resource can no longer be refreshed). Leave empty for a normal apply." | ||
| required: false | ||
| default: "" | ||
| schedule: | ||
| - cron: "17 */4 * * *" | ||
| push: | ||
|
|
@@ -21,8 +25,24 @@ on: | |
| - main | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | ||
| # This workflow has no pull_request trigger, so github.event.pull_request.number | ||
| # is always null here -- the group key collapsed to just github.ref in | ||
| # practice, which only serializes runs against the same ref. A manual | ||
| # workflow_dispatch run (e.g. one-time state rm) dispatched against a | ||
| # non-default ref could then run concurrently with a scheduled/push apply | ||
| # against main, racing on the same remote state. Use a single, unqualified | ||
| # group so every run of this workflow -- manual or automatic, any ref -- | ||
| # is always serialized against every other. | ||
| group: ${{ github.workflow }} | ||
| cancel-in-progress: false | ||
| # Default queue behavior only keeps the single most-recently-queued run | ||
| # pending in a group -- an older pending run gets canceled and replaced. | ||
| # A manual one-time recovery run (state rm, or the exclude_addresses | ||
| # escape hatch) dispatched while a scheduled/push run is in progress | ||
| # could get silently dropped and replaced by the next automatic trigger | ||
| # before it ever executes. queue: max keeps every pending run queued | ||
| # (up to GitHub's cap of 100) instead of dropping older ones. | ||
| queue: max | ||
|
coderabbitai[bot] marked this conversation as resolved.
Comment on lines
+28
to
+45
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. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*'
printf '%s\n' '--- apply.yaml structure ---'
wc -l .github/workflows/apply.yaml
sed -n '1,150p' .github/workflows/apply.yaml
printf '%s\n' '--- queue/concurrency references ---'
rg -n --hidden -S 'queue:|concurrency:|cancel-in-progress|workflow_dispatch|schedule:|push:' .github/workflows .github 2>/dev/null || trueRepository: osac-project/github-config Length of output: 7096 🌐 Web query:
💡 Result: GitHub Actions provides the Citations:
🌐 Web query:
💡 Result: When using the GitHub Actions Citations:
🌐 Web query:
💡 Result: According to official GitHub Actions documentation, jobs or workflow runs within the same concurrency group are processed in a first-in-first-out (FIFO) order, specifically based on the time each run started waiting on the concurrency group rather than the time the workflow was originally dispatched [1][2]. However, GitHub explicitly notes that ordering is not guaranteed [1][3]. This is because the actual start time of a job or workflow run—which determines its position in the queue—can vary due to system factors, meaning the sequence in which runs enter the "pending" state may not always strictly mirror the intended execution order [1][2]. When using concurrency controls: - By default, only one pending run is allowed in a group; new runs will cancel existing pending runs [1][4]. - You can enable queuing by using the queue property (e.g., queue: max), which allows multiple runs to wait in the concurrency group [1][2]. - Concurrency group names are case-insensitive, and using the same name across different workflows will cause them to share the same concurrency queue [1][5]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
from itertools import permutations
import re
path = Path(".github/workflows/apply.yaml")
text = path.read_text()
assert re.search(r"(?m)^ group:\s*\$\{\{\s*github\.workflow\s*\}\}\s*$", text)
assert re.search(r"(?m)^ cancel-in-progress:\s*false\s*$", text)
assert re.search(r"(?m)^ queue:\s*max\s*$", text)
assert re.search(r"(?m)^ - uses:\s*actions/checkout@[^ ]+\s*$", text)
assert re.search(r"(?m)^ - name:\s*TF Apply\s*$", text)
events = [
("push", "commit-A"),
("push", "commit-B"),
("push", "commit-C"),
]
# actions/checkout defaults to the triggering ref/SHA, so each queued run
# applies the commit attached to its event.
queued = [(event, sha) for event, sha in events]
orders = list(permutations(queued))
stale_order = next(order for order in orders if [sha for _, sha in order] !=
[sha for _, sha in queued])
print("shared concurrency group: Apply configuration")
print("pending-run capacity: 100")
print("checkout/apply revisions in trigger order:", [sha for _, sha in queued])
print("possible execution order:", [sha for _, sha in stale_order])
print("stale revision can execute after a newer revision:",
stale_order.index(("push", "commit-A")) >
stale_order.index(("push", "commit-B")))
PYRepository: osac-project/github-config Length of output: 436 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from itertools import permutations
revisions = ["commit-A", "commit-B", "commit-C"]
for order in permutations(revisions):
if any(order.index(older) > order.index(newer)
for older, newer in zip(revisions, revisions[1:])):
print("trigger order:", revisions)
print("possible execution order:", list(order))
print("older revision executes after newer revision: true")
break
else:
raise SystemExit("No stale execution order found")
PYRepository: osac-project/github-config Length of output: 330 Skip superseded automatic applies before
🧰 Tools🪛 actionlint (1.7.12)[error] 45-45: unexpected key "queue" for "concurrency" section. expected one of "cancel-in-progress", "group" (syntax-check) 🤖 Prompt for AI Agents |
||
|
|
||
| jobs: | ||
| apply: | ||
|
|
@@ -64,6 +84,31 @@ jobs: | |
| tofu import "${{ inputs.import_address }}" "${{ inputs.import_id }}" | ||
| env: | ||
| GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} | ||
| # Handles a resource whose live state can no longer be refreshed (e.g. | ||
| # a github_membership/github_team_membership for a user who left the | ||
| # org, which errors on read instead of just reporting "gone") -- | ||
| # refresh failures like this abort the whole apply before anything | ||
| # else in the plan can land, atomically, even resources unrelated to | ||
| # the broken one. Manual, one-time use via workflow_dispatch input; a | ||
| # no-op (skipped entirely) for the normal scheduled/push triggers, | ||
| # which never set this input. | ||
| - name: TF State Remove (one-time, manual only) | ||
| if: inputs.state_rm_addresses != '' | ||
| run: | | ||
| if [[ "${STATE_RM_ADDRESSES}" == *$'\n'* ]]; then | ||
| echo "::error::state_rm_addresses must be comma-separated on a single line, not newline-separated." >&2 | ||
| exit 1 | ||
| fi | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| IFS=',' read -ra ADDRS <<< "${STATE_RM_ADDRESSES}" | ||
| for addr in "${ADDRS[@]}"; do | ||
| if [[ -z "${addr}" || "${addr}" == -* ]]; then | ||
| echo "::error::invalid resource address '${addr}' -- addresses must be non-empty and cannot start with '-' (tofu would parse it as an option)." >&2 | ||
| exit 1 | ||
| fi | ||
| done | ||
| tofu state rm "${ADDRS[@]}" | ||
|
coderabbitai[bot] marked this conversation as resolved.
coderabbitai[bot] marked this conversation as resolved.
|
||
| env: | ||
| STATE_RM_ADDRESSES: ${{ inputs.state_rm_addresses }} | ||
| - name: TF Apply | ||
| run: | | ||
| tofu apply -concise -auto-approve | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.