diff --git a/build/winsign/sign.ps1 b/build/winsign/sign.ps1 index 89e7b651..b9706ca8 100644 --- a/build/winsign/sign.ps1 +++ b/build/winsign/sign.ps1 @@ -1,136 +1,119 @@ param( + [ValidateNotNullOrEmpty()] [string][Parameter(Mandatory=$true)]$SignIdentity, + + [ValidateNotNullOrEmpty()] [string][Parameter(Mandatory=$true)]$GithubRunId ) $ErrorActionPreference = "Stop" -echo "Preparing environment" -git pull --recurse-submodules -mkdir windsign-temp -ErrorAction SilentlyContinue - -# Download in parallel - -#show output too -#Start-Job -Name "DownloadGitObjectsRepo" -ScriptBlock { -# param($PWD) -# echo "Downloading git objects repo to $PWD\windsign-temp\windows-binaries" -# git clone https://github.com/zen-browser/windows-binaries.git $PWD\windsign-temp\windows-binaries -# echo "Downloaded git objects repo to" -#} -Verbose -ArgumentList $PWD -Debug - -gh run download $GithubRunId --name windows-x64-obj-arm64 -D windsign-temp\windows-x64-obj-arm64 -echo "Downloaded arm64 artifacts" -gh run download $GithubRunId --name windows-x64-obj-x86_64 -D windsign-temp\windows-x64-obj-x86_64 -echo "Downloaded x86_64 artifacts" - - -#Wait-Job -Name "DownloadGitObjectsRepo" - -mkdir engine\obj-x86_64-pc-windows-msvc\ -ErrorAction SilentlyContinue - -pnpm surfer ci --brand release - -function SignAndPackage($name) { - echo "Executing on $name" - rmdir .\dist -Recurse -ErrorAction SilentlyContinue - rmdir engine\obj-x86_64-pc-windows-msvc\ -Recurse -ErrorAction SilentlyContinue - cp windsign-temp\windows-x64-obj-$name engine\obj-x86_64-pc-windows-msvc\ -Recurse - echo "Signing $name" - - # Collect all .exe and .dll files into a list - $files = Get-ChildItem engine\obj-x86_64-pc-windows-msvc\ -Recurse -Include *.exe - $files += Get-ChildItem engine\obj-x86_64-pc-windows-msvc\ -Recurse -Include *.dll - - signtool.exe sign /n "$SignIdentity" /t http://time.certum.pl/ /fd sha256 /v $files - echo "Packaging $name" - $env:SURFER_SIGNING_MODE="sign" - $env:MAR="$PWD\\build\\winsign\\mar.exe" - if ($name -eq "arm64") { - $env:SURFER_COMPAT="aarch64" - } else { - $env:SURFER_COMPAT="x86_64" - } - - echo "Compat Mode? $env:SURFER_COMPAT" - pnpm surfer package --verbose - - # In the release script, we do the following: - # tar -xvf .github/workflows/object/windows-x64-signed-x86_64.tar.gz -C windows-x64-signed-x86_64 - # We need to create a tar with the same structure and no top-level directory - # Inside, we need: - # - update_manifest/* - # - windows.mar - # - zen.installer.exe - # - zen.win-x86_64.zip - echo "Creating tar for $name" - rm .\windsign-temp\windows-x64-signed-$name -Recurse -ErrorAction SilentlyContinue - mkdir windsign-temp\windows-x64-signed-$name - - # Move the MAR, add the `-arm64` suffix if needed - echo "Moving MAR for $name" - if ($name -eq "arm64") { - mv .\dist\output.mar windsign-temp\windows-x64-signed-$name\windows-$name.mar - } else { - mv .\dist\output.mar windsign-temp\windows-x64-signed-$name\windows.mar - } - - # Move the installer - echo "Moving installer for $name" - if ($name -eq "arm64") { - mv .\dist\zen.installer.exe windsign-temp\windows-x64-signed-$name\zen.installer-$name.exe - } else { - mv .\dist\zen.installer.exe windsign-temp\windows-x64-signed-$name\zen.installer.exe - } - - # Move the zip - echo "Moving zip for $name" - if ($name -eq "arm64") { - mv (Get-Item .\dist\*.en-US.win64-aarch64.zip) windsign-temp\windows-x64-signed-$name\zen.win-arm64.zip - } else { - mv (Get-Item .\dist\*.en-US.win64.zip) windsign-temp\windows-x64-signed-$name\zen.win-$name.zip - } - - # Extract the zip, sign everything inside, and repackage it - Expand-Archive -Path windsign-temp\windows-x64-signed-$name\zen.win-$name.zip -DestinationPath windsign-temp\windows-x64-signed-$name\zen.win-$name - rm windsign-temp\windows-x64-signed-$name\zen.win-$name.zip - $files = Get-ChildItem windsign-temp\windows-x64-signed-$name\zen.win-$name -Recurse -Include *.exe - $files += Get-ChildItem windsign-temp\windows-x64-signed-$name\zen.win-$name -Recurse -Include *.dll - signtool.exe sign /n "$SignIdentity" /t http://time.certum.pl/ /fd sha256 /v $files - Compress-Archive -Path windsign-temp\windows-x64-signed-$name\zen.win-$name -DestinationPath windsign-temp\windows-x64-signed-$name\zen.win-$name.zip - rmdir windsign-temp\windows-x64-signed-$name\zen.win-$name -Recurse -ErrorAction SilentlyContinue - - # Move the manifest - mv .\dist\update\. windsign-temp\windows-x64-signed-$name\update_manifest - - echo "Invoking tar for $name" - # note: We need to sign it into a parent folder, called windows-x64-signed-$name - rmdir .\windsign-temp\windows-binaries\windows-x64-signed-$name -Recurse -ErrorAction SilentlyContinue - mv windsign-temp\windows-x64-signed-$name .\windsign-temp\windows-binaries -Force - - echo "Finished $name" +function Download-Artifacts { + param( + [string]$Name, + [string]$GithubRunId + ) + gh run download $GithubRunId --name $Name -D (Join-Path $PWD 'windsign-temp\windows-x64-obj-' + $Name) + Write-Verbose "Downloaded $Name artifacts" } -SignAndPackage arm64 -SignAndPackage x86_64 +function Sign-Files { + param( + [string]$Path + ) + $files = Get-ChildItem -Path $Path -Recurse -Include *.exe, *.dll + signtool.exe sign /n "$SignIdentity" /t http://time.certum.pl/ /fd sha256 /v $files +} -echo "All artifacts signed and packaged, ready for release!" -echo "Commiting the changes to the repository" -cd windsign-temp\windows-binaries +function Move-File { + param( + [string]$Source, + [string]$Destination + ) + if (Test-Path $Source) { + Move-Item $Source -Destination $Destination -Force + Write-Verbose "Moved $Source to $Destination" + } else { + Write-Warning "Source file $Source does not exist." + } +} + +function Create-Tar { + param( + [string]$Name + ) + $tarPath = Join-Path $PWD "windsign-temp\windows-x64-signed-$Name" + Remove-Item -Path $tarPath -Recurse -ErrorAction SilentlyContinue + New-Item -ItemType Directory -Path $tarPath | Out-Null + + Move-File -Source ".\dist\output.mar" -Destination (Join-Path $tarPath ("windows-$Name.mar")) + Move-File -Source ".\dist\zen.installer.exe" -Destination (Join-Path $tarPath ("zen.installer$($Name -eq 'arm64' ? '-arm64' : '') .exe")) + Move-File -Source (Get-ChildItem ".\dist\*.en-US.win64$($Name -eq 'arm64' ? '-aarch64' : '') .zip" | Select-Object -First 1) -Destination (Join-Path $tarPath ("zen.win-$Name.zip")) +} + +function SignAndPackage { + param( + [string]$Name + ) + + Write-Verbose "Executing on $Name" + Remove-Item -Path ".\dist" -Recurse -ErrorAction SilentlyContinue + Remove-Item -Path "engine\obj-x86_64-pc-windows-msvc\" -Recurse -ErrorAction SilentlyContinue + Copy-Item -Path (Join-Path $PWD "windsign-temp\windows-x64-obj-$Name") -Destination "engine\obj-x86_64-pc-windows-msvc\" -Recurse + Write-Verbose "Signing $Name" + + Sign-Files -Path "engine\obj-x86_64-pc-windows-msvc\" + + $env:SURFER_SIGNING_MODE = "sign" + $env:MAR = (Join-Path $PWD "build\winsign\mar.exe") + $env:SURFER_COMPAT = if ($Name -eq "arm64") { "aarch64" } else { "x86_64" } + Write-Verbose "Compat Mode? $env:SURFER_COMPAT" + + pnpm surfer package --verbose + + Create-Tar -Name $Name + + # Extract and sign the contents of the zip + Expand-Archive -Path (Join-Path $tarPath ("zen.win-$Name.zip")) -DestinationPath (Join-Path $tarPath ("zen.win-$Name")) + Remove-Item -Path (Join-Path $tarPath ("zen.win-$Name.zip")) -ErrorAction SilentlyContinue + + Sign-Files -Path (Join-Path $tarPath ("zen.win-$Name")) + Compress-Archive -Path (Join-Path $tarPath ("zen.win-$Name")) -DestinationPath (Join-Path $tarPath ("zen.win-$Name.zip")) + Remove-Item -Path (Join-Path $tarPath ("zen.win-$Name")) -Recurse -ErrorAction SilentlyContinue + + Move-File -Source ".\dist\update\*" -Destination (Join-Path $tarPath "update_manifest") + + Write-Verbose "Finished $Name" +} + +Write-Verbose "Preparing environment" +git pull --recurse-submodules +New-Item -ItemType Directory -Path "windsign-temp" -ErrorAction SilentlyContinue + +Download-Artifacts -Name "windows-x64-obj-arm64" -GithubRunId $GithubRunId +Download-Artifacts -Name "windows-x64-obj-x86_64" -GithubRunId $GithubRunId + +New-Item -ItemType Directory -Path "engine\obj-x86_64-pc-windows-msvc" -ErrorAction SilentlyContinue +pnpm surfer ci --brand release + +SignAndPackage -Name "arm64" +SignAndPackage -Name "x86_64" + +Write-Verbose "All artifacts signed and packaged, ready for release!" +Write-Verbose "Committing the changes to the repository" +cd (Join-Path $PWD "windsign-temp\windows-binaries") git add . git commit -m "Sign and package windows artifacts" git push -cd ..\.. +cd - # Cleaning up +Write-Verbose "Cleaning up" +Remove-Item -Path "windsign-temp\windows-x64-obj-x86_64" -Recurse -ErrorAction SilentlyContinue +Remove-Item -Path "windsign-temp\windows-x64-obj-arm64" -Recurse -ErrorAction SilentlyContinue -echo "All done!" -echo "All the artifacts (x86_64 and arm46) are signed and packaged, get a rest now!" -Read-Host "Press Enter to continue" - -echo "Cleaning up" -rmdir windsign-temp\windows-x64-obj-x86_64 -Recurse -ErrorAction SilentlyContinue -rmdir windsign-temp\windows-x64-obj-arm64 -Recurse -ErrorAction SilentlyContinue - -echo "Opening visual studio code" +Write-Verbose "Opening Visual Studio Code" code . +Write-Host "All done! Press Enter to continue." +Read-Host + diff --git a/scripts/check-rc-response.py b/scripts/check-rc-response.py index cf799495..5c9947bb 100644 --- a/scripts/check-rc-response.py +++ b/scripts/check-rc-response.py @@ -1,63 +1,61 @@ import json -import sys import os import requests +from typing import Optional -RESPONSE = 'rc-response.json' -METADATA = 'surfer.json' +RESPONSE_FILENAME = 'rc-response.json' +METADATA_FILENAME = 'surfer.json' -def get_current_version(): - with open(METADATA) as f: - metadata = json.load(f) - return metadata['version']['candidate'] +def get_current_version() -> Optional[str]: + """Retrieve the current version from the metadata file.""" + try: + with open(METADATA_FILENAME) as f: + metadata = json.load(f) + return metadata['version']['candidate'] + except (FileNotFoundError, json.JSONDecodeError) as e: + print(f"Error reading current version: {e}") + return None -def get_rc_response(): - with open(RESPONSE) as f: - data = json.load(f) - for tag_dict in data['tags']: - tag = tag_dict['tag'] - is_valid_tag = (tag.startswith('FIREFOX') and tag.endswith('_BUILD1') - and not 'ESR' in tag and not 'b' in tag) - if is_valid_tag: - return tag.replace('FIREFOX_', '').replace('_BUILD1', '').replace('_', '.') - return None +def get_rc_response() -> Optional[str]: + """Get the release candidate response from the response file.""" + try: + with open(RESPONSE_FILENAME) as f: + data = json.load(f) + for tag_dict in data['tags']: + tag = tag_dict['tag'] + if (tag.startswith('FIREFOX') and tag.endswith('_BUILD1') + and 'ESR' not in tag and 'b' not in tag): + return tag.replace('FIREFOX_', '').replace('_BUILD1', '').replace('_', '.') + except (FileNotFoundError, json.JSONDecodeError) as e: + print(f"Error reading RC response: {e}") + return None -def get_pings(): - pings = "" - for ping in os.getenv('DISCORD_PING_IDS').split(','): - pings += "<@%s> " % ping - return pings +def get_pings() -> str: + """Build a string of Discord user IDs for mentions.""" + ping_ids = os.getenv('DISCORD_PING_IDS', '') + return ' '.join(f"<@{ping.strip()}>" for ping in ping_ids.split(',') if ping.strip()) -def send_webhook(rc: str): - text = "||%s|| New Firefox RC version is available: **%s**" % (get_pings(), rc) - webhook_url = os.getenv('DISCORD_WEBHOOK_URL') #os.getenv('DISCORD_WEBHOOK_URL') - message = { - "content": text, - "username": "Firefox RC Checker", - "avatar_url": "https://avatars.githubusercontent.com/u/189789277?v=4", - } - response = requests.post(webhook_url, json=message) - if response.status_code == 204: - print("Message sent successfully!") - else: - print(f"Failed to send message: {response.status_code}") +def send_webhook(rc: str) -> None: + """Send a message to the Discord webhook.""" + text = f"||{get_pings()}|| New Firefox RC version is available: **{rc}**" + webhook_url = os.getenv('DISCORD_WEBHOOK_URL') + + if webhook_url: + message = { + "content": text, + "username": "Firefox RC Checker", + } + try: + response = requests.post(webhook_url, json=message) + response.raise_for_status() # Raise an error for bad responses + except requests.RequestException as e: + print(f"Error sending webhook: {e}") + else: + print("Webhook URL not set.") -def main(): - current = get_current_version() - if not current: - print('Could not find current version') - return 1 - rc = get_rc_response() - if not rc: - print('Could not find RC version') - return 1 - if current != rc: - print('Current version is %s, but RC version is %s' % (current, rc)) - # Here, we should update the current version in surfer.json - send_webhook(rc) - return 0 - print('Current version is %s, and RC version is %s' % (current, rc)) - return 1 - -if __name__ == '__main__': - sys.exit(main()) +if __name__ == "__main__": + current_version = get_current_version() + rc_response = get_rc_response() + + if rc_response and rc_response != current_version: + send_webhook(rc_response) diff --git a/scripts/prepare-flatpak-release.py b/scripts/prepare-flatpak-release.py index 9deec631..72ef4238 100644 --- a/scripts/prepare-flatpak-release.py +++ b/scripts/prepare-flatpak-release.py @@ -1,52 +1,61 @@ import hashlib import argparse import sys +import os FLATID = "io.github.zen_browser.zen" def get_sha256sum(filename): - """Calculate the SHA256 checksum of a file.""" - sha256 = hashlib.sha256() + """Calculate the SHA256 checksum of a file.""" + sha256 = hashlib.sha256() + try: with open(filename, "rb") as f: - for byte_block in iter(lambda: f.read(4096), b""): - sha256.update(byte_block) - return sha256.hexdigest() + for byte_block in iter(lambda: f.read(4096), b""): + sha256.update(byte_block) + except FileNotFoundError: + print(f"File {filename} not found.") + sys.exit(1) + return sha256.hexdigest() def build_template(template, linux_sha256, flatpak_sha256, version): - print(f"Building template with version {version}") - print(f"\tLinux archive sha256: {linux_sha256}") - print(f"\tFlatpak archive sha256: {flatpak_sha256}") - return template.format(linux_sha256=linux_sha256, - flatpak_sha256=flatpak_sha256, - version=version) + """Build the template with the provided hashes and version.""" + print(f"Building template with version {version}") + print(f"\tLinux archive sha256: {linux_sha256}") + print(f"\tFlatpak archive sha256: {flatpak_sha256}") + return template.format( + linux_sha256=linux_sha256, + flatpak_sha256=flatpak_sha256, + version=version + ) def get_template(template_root): - file = f"{template_root}/{FLATID}.yml.template" - print(f"Reading template {file}") - try: - with open(file, "r") as f: - return f.read() - except FileNotFoundError: - print(f"Template {file} not found") - sys.exit(1) + """Get the template content from the specified root directory.""" + file = os.path.join(template_root, f"{FLATID}.yml.template") + print(f"Reading template {file}") + try: + with open(file, "r") as f: + return f.read() + except FileNotFoundError: + print(f"Template {file} not found.") + sys.exit(1) def main(): - parser = argparse.ArgumentParser(description='Prepare flatpak release') - parser.add_argument('--version', help='Version of the release', required=True) - parser.add_argument('--linux-archive', help='Linux archive', required=True) - parser.add_argument('--flatpak-archive', help='Flatpak archive', required=True) - parser.add_argument('--output', help='Output file', default=f"{FLATID}.yml") - parser.add_argument('--template-root', help='Template root', default="flatpak") - args = parser.parse_args() + """Main function to parse arguments and process files.""" + parser = argparse.ArgumentParser(description='Prepare flatpak release') + parser.add_argument('--version', help='Version of the release', required=True) + parser.add_argument('--linux-archive', help='Linux archive', required=True) + parser.add_argument('--flatpak-archive', help='Flatpak archive', required=True) + parser.add_argument('--output', help='Output file', default=f"{FLATID}.yml") + parser.add_argument('--template-root', help='Template root', default="flatpak") + args = parser.parse_args() - linux_sha256 = get_sha256sum(args.linux_archive) - flatpak_sha256 = get_sha256sum(args.flatpak_archive) - template = build_template(get_template(args.template_root), linux_sha256, flatpak_sha256, args.version) + linux_sha256 = get_sha256sum(args.linux_archive) + flatpak_sha256 = get_sha256sum(args.flatpak_archive) + template = build_template(get_template(args.template_root), linux_sha256, flatpak_sha256, args.version) - print(f"Writing output to {args.output}") - with open(args.output, "w") as f: - f.write(template) + print(f"Writing output to {args.output}") + with open(args.output, "w") as f: + f.write(template) if __name__ == "__main__": - main() - + main() diff --git a/scripts/remove-failed-jobs.sh b/scripts/remove-failed-jobs.sh index 925b7319..1a872857 100644 --- a/scripts/remove-failed-jobs.sh +++ b/scripts/remove-failed-jobs.sh @@ -1,5 +1,7 @@ +#!/bin/bash + gh_bulk_delete_workflow_runs() { - repo=$1 + local repo=$1 # Ensure the repo argument is provided if [[ -z "$repo" ]]; then @@ -7,14 +9,37 @@ gh_bulk_delete_workflow_runs() { return 1 fi - runs=$(gh api repos/$repo/actions/runs --paginate | jq -r '.workflow_runs[] | select(.conclusion == "cancelled" or .conclusion == "failure" or .conclusion == "timed_out") | .id') + # Fetch workflow runs that are cancelled, failed, or timed out + local runs + runs=$(gh api repos/$repo/actions/runs --paginate | + jq -r '.workflow_runs[] | + select(.conclusion == "cancelled" or + .conclusion == "failure" or + .conclusion == "timed_out") | + .id') + if [[ -z "$runs" ]]; then + echo "No workflow runs found for $repo with the specified conclusions." + return 0 + fi + + # Loop through each run and delete it while IFS= read -r run; do - echo "Deleting run https://github.com/$repo/actions/runs/$run" - gh api -X DELETE repos/$repo/actions/runs/$run --silent + echo "Attempting to delete run: https://github.com/$repo/actions/runs/$run" + + # Perform the deletion + if gh api -X DELETE repos/$repo/actions/runs/$run --silent; then + echo "Successfully deleted run: $run" + else + echo "Error deleting run: $run" >&2 + fi + + # Optional delay to avoid hitting API rate limits + sleep 1 done <<< "$runs" - echo "All workflow runs for $repo have been deleted." + echo "Completed deletion process for workflow runs in $repo." } -gh_bulk_delete_workflow_runs $1 +# Execute the function with the provided argument +gh_bulk_delete_workflow_runs "$1" diff --git a/scripts/update_ff.py b/scripts/update_ff.py index ff124e58..1d88adde 100644 --- a/scripts/update_ff.py +++ b/scripts/update_ff.py @@ -1,37 +1,43 @@ - import os import json -last_version = "0.0.0" -new_version = "0.0.0" - def update_ff(): - os.system("npm run update-ff:raw") + """Runs the npm command to update the 'ff' component.""" + result = os.system("npm run update-ff:raw") + if result != 0: + raise RuntimeError("Failed to update 'ff' component.") -def get_version_before(): - global last_version - with open("surfer.json", "r") as f: - data = json.load(f) - last_version = data["version"]["version"] +def get_version_from_file(filename): + """Retrieves the version from the specified JSON file.""" + try: + with open(filename, "r") as f: + data = json.load(f) + return data["version"]["version"] + except (FileNotFoundError, json.JSONDecodeError) as e: + raise RuntimeError(f"Error reading version from {filename}: {e}") -def get_version_after(): - global new_version - with open("surfer.json", "r") as f: - data = json.load(f) - new_version = data["version"]["version"] +def update_readme(last_version, new_version): + """Updates the README.md file to reflect the new version.""" + try: + with open("README.md", "r") as f: + data = f.read() + updated_data = data.replace(last_version, new_version) -def update_readme(): - global last_version - global new_version - with open("README.md", "r") as f: - data = f.read() - data = data.replace(last_version, new_version) - with open("README.md", "w") as f: - f.write(data) + with open("README.md", "w") as f: + f.write(updated_data) + except FileNotFoundError as e: + raise RuntimeError(f"README.md file not found: {e}") + +def main(): + """Main function to update versions and README.""" + try: + last_version = get_version_from_file("surfer.json") + update_ff() + new_version = get_version_from_file("surfer.json") + update_readme(last_version, new_version) + print(f"Updated version from {last_version} to {new_version} in README.md.") + except Exception as e: + print(f"An error occurred: {e}") if __name__ == "__main__": - get_version_before() - update_ff() - get_version_after() - update_readme() - print("Updated from version {} to version {}".format(last_version, new_version)) + main() diff --git a/scripts/update_newtab.py b/scripts/update_newtab.py index e98a8ec6..175dc944 100644 --- a/scripts/update_newtab.py +++ b/scripts/update_newtab.py @@ -1,9 +1,40 @@ import os +import subprocess +import logging + +# Set up logging +logging.basicConfig(level=logging.INFO) + +# Constants for paths +NEW_TAB_DIR = './engine/browser/components/newtab' +ENGINE_DIR = './engine' +NPM_INSTALL_COMMANDS = [ + "npm install", + "npm install meow@9.0.0" +] +BUNDLE_COMMAND = "npm run bundle --prefix=browser/components/newtab" + +def install_dependencies(): + """Install necessary npm packages for the newtab component.""" + for command in NPM_INSTALL_COMMANDS: + logging.info(f"Running command: {command} in {NEW_TAB_DIR}") + subprocess.run(command.split(), cwd=NEW_TAB_DIR, check=True) + +def bundle_newtab_components(): + """Bundle the newtab components.""" + logging.info(f"Bundling newtab components in {ENGINE_DIR}") + subprocess.run(BUNDLE_COMMAND.split(), cwd=ENGINE_DIR, check=True) def update_newtab(init: bool = True): - if init: - os.system("(cd ./engine/browser/components/newtab && ../../../mach npm install && ../../../mach npm install meow@9.0.0)") - os.system("cd ./engine && ./mach npm run bundle --prefix=browser/components/newtab") + """Update the newtab components, optionally initializing dependencies.""" + try: + if init: + install_dependencies() + + bundle_newtab_components() + except subprocess.CalledProcessError as e: + logging.error(f"An error occurred: {e}") + raise if __name__ == "__main__": - update_newtab(False) + update_newtab(init=False)