Skip to content
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

ci: some more cirrus release tweaks #1531

Merged
merged 4 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ test_task:
auto_cancellation: "false" # We set this to false to prevent nightly builds from affecting this
only_if: $CIRRUS_BUILD_SOURCE != "api" && ($CIRRUS_BRANCH == "main" || $CIRRUS_PR != "")
timeout_in: "15m"
skip: "!changesInclude('.cargo/**', '.cirrus.yml', 'sample_configs/**', 'src/**', 'tests/**', 'build.rs', 'Cargo.lock', 'Cargo.toml', 'clippy.toml', 'rustfmt.toml')"
skip: "!changesInclude('.cargo/**', 'sample_configs/**', 'scripts/cirrus/**', 'src/**', 'tests/**', '.cirrus.yml', 'build.rs', 'Cargo.lock', 'Cargo.toml', 'clippy.toml', 'rustfmt.toml')"
matrix:
- name: "FreeBSD 14 Test"
freebsd_instance:
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ supply-chain/
# samply profiling
profile.json

**/venv/
**/venv/

# Sometimes used for scripts
.ruff_cache
125 changes: 112 additions & 13 deletions scripts/cirrus/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@
DL_URL_TEMPLATE = "https://api.cirrus-ci.com/v1/artifact/build/%s/%s/binaries/%s"


def make_query_request(key: str, branch: str, build_type: str):
def make_query_request(key: str, branch: str, mutation_id: str):
print("Creating query request.")
mutation_id = "Cirrus CI Build {}-{}-{}".format(build_type, branch, int(time()))

# Dumb but if it works...
config_override = (
Path(".cirrus.yml")
.read_text()
.replace("# -PLACEHOLDER FOR CI-", 'BTM_BUILD_RELEASE_CALLER: "ci"')
)

query = """
mutation CreateCirrusCIBuild (
$repo: ID!,
Expand All @@ -60,12 +60,14 @@ def make_query_request(key: str, branch: str, build_type: str):
}
}
"""

params = {
"repo": "6646638922956800",
"branch": branch,
"mutation_id": mutation_id,
"config_override": dedent(config_override),
}

data = {"query": dedent(query), "variables": params}
data = json.dumps(data).encode()

Expand All @@ -75,16 +77,17 @@ def make_query_request(key: str, branch: str, build_type: str):
return request


def check_build_status(key: str, id: str) -> Optional[str]:
def check_build_status(key: str, build_id: str) -> Optional[str]:
query = """
query BuildStatus($id: ID!) {
build(id: $id) {
status
}
}
}
"""

params = {
"id": id,
"id": build_id,
}

data = {"query": dedent(query), "variables": params}
Expand All @@ -102,10 +105,80 @@ def check_build_status(key: str, id: str) -> Optional[str]:
status = response["data"]["build"]["status"]
return status
except KeyError:
print("There was an issue with creating a build job.")
print("There was an issue with checking the build status.")
return None


def check_build_tasks(key: str, build_id: str) -> Optional[List[str]]:
query = """
query Build($id:ID!) {
build(id:$id){
tasks {
id
}
}
}
"""

params = {
"id": build_id,
}

data = {"query": dedent(query), "variables": params}
data = json.dumps(data).encode()

request = Request(URL, data=data, method="POST")
request.add_header("Authorization", "Bearer {}".format(key))
with urlopen(request) as response:
response = json.load(response)

if response.get("errors") is not None:
print("There was an error in the returned response.")
return None

try:
tasks = [task["id"] for task in response["data"]["build"]["tasks"]]
return tasks
except KeyError:
print("There was an issue with getting the list of task ids.")
return None


def stop_build_tasks(key: str, task_ids: List[str], mutation_id: str) -> bool:
query = """
mutation StopCirrusCiTasks (
$task_ids: [ID!]!,
$mutation_id: String!,
) {
batchAbort (
input: {
taskIds: $task_ids,
clientMutationId: $mutation_id
}
) {
tasks {
id
}
}
}
"""

params = {
"task_ids": task_ids,
"mutation_id": mutation_id,
}

data = {"query": dedent(query), "variables": params}
data = json.dumps(data).encode()

request = Request(URL, data=data, method="POST")
request.add_header("Authorization", "Bearer {}".format(key))

with urlopen(request) as response:
response = json.load(response)
return len(response["data"]["batchAbort"]["tasks"]) == len(task_ids)


def try_download(build_id: str, dl_path: Path):
for task, file in TASKS:
url = DL_URL_TEMPLATE % (build_id, task, file)
Expand Down Expand Up @@ -138,17 +211,36 @@ def main():
# Try up to three times
MAX_ATTEMPTS = 5
success = False
tasks = []
mutation_id = None

for i in range(MAX_ATTEMPTS):
if success:
break

print(f"Attempt {i + 1}:")

with urlopen(make_query_request(key, branch, build_type)) as response:
if tasks and mutation_id:
print("Killing previous tasks first...")

if stop_build_tasks(key, tasks, mutation_id):
print("All previous tasks successfully stopped.")
else:
print(
"Not all previous tasks stopped. This isn't a problem but it is a waste."
)

tasks = []
mutation_id = "Cirrus CI Build {}-{}-{}".format(
build_type, branch, int(time())
)

with urlopen(make_query_request(key, branch, mutation_id)) as response:
response = json.load(response)
errors = response.get("errors")

if response.get("errors") is not None:
print("There was an error in the returned response.")
if errors is not None:
print(f"There was an error in the returned response: {str(errors)}")
continue

try:
Expand All @@ -158,16 +250,22 @@ def main():
print("There was an issue with creating a build job.")
continue

# First, sleep 4 minutes, as it's unlikely it'll finish before then.
# First, sleep X minutes total, as it's unlikely it'll finish before then.
SLEEP_MINUTES = 4
print(f"Sleeping for {SLEEP_MINUTES} minutes.")
sleep(60 * SLEEP_MINUTES)
print("Mandatory nap over. Starting to check for completion.")

# Sleep and check for tasks out every 10 seconds
for _ in range(SLEEP_MINUTES * 6):
sleep(10)
if not tasks:
tasks = check_build_tasks(key, build_id)

MINUTES = 10
SLEEP_SEC = 30
TRIES = int(MINUTES * (60 / SLEEP_SEC)) # Works out to 20 tries.

print(f"Mandatory nap over. Checking for completion for {MINUTES} min.")

for attempt in range(TRIES):
print("Checking...")
try:
Expand All @@ -179,7 +277,8 @@ def main():
success = True
break
else:
print("Build status: {}".format(status or "unknown"))
print(f"Build status: {(status or 'unknown')}")

if status == "ABORTED":
print("Build aborted, bailing.")
break
Expand Down