Skip to content
Closed
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
76 changes: 58 additions & 18 deletions .github/workflows/build_LoopFollow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ name: 4. Build Loop Follow
run-name: Build Loop Follow (${{ github.ref_name }})
on:
workflow_dispatch:

## Remove the "#" sign from the beginning of the line below to get automated builds on push (code changes in your repository)
#push:

## Remove the "#" sign from the beginning of the two lines below to get automated builds every two months
#schedule:
#- cron: '0 17 1 */2 *' # Runs at 17:00 UTC on the 1st in Jan, Mar, May, Jul, Sep and Nov.

push:
schedule:
- cron: "0 9 * * 3" # Weekly trigger: every Wednesday at 09:00 UTC
- cron: "0 7 1 * *" # Monthly trigger: on the 1st of every month at 07:00 UTC

jobs:
validate:
Expand All @@ -22,25 +18,20 @@ jobs:
needs: validate
runs-on: macos-15
steps:
# Uncomment to manually select latest Xcode if needed
- name: Select Latest Xcode
run: "sudo xcode-select --switch /Applications/Xcode_16.2.app/Contents/Developer"

# Checks-out the repo

- name: Checkout Repo
uses: actions/checkout@v4
with:
submodules: recursive

# Patch Fastlane Match to not print tables
- name: Patch Match Tables
run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"

# Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996)

- name: Sync clock
run: sudo sntp -sS time.windows.com

# Build signed Loop Follow IPA file
- name: Fastlane Build & Archive
run: fastlane build_LoopFollow
env:
Expand All @@ -51,7 +42,6 @@ jobs:
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}

# Upload to TestFlight
- name: Fastlane upload to TestFlight
run: fastlane release
env:
Expand All @@ -61,12 +51,62 @@ jobs:
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}

# Upload IPA and Symbols

- name: Upload IPA and Symbol artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: |
artifacts
buildlog

check_certs:
name: Check Certificates
uses: ./.github/workflows/create_certs.yml
secrets: inherit

nuke_certs:
name: Nuke Certificates
needs: [validate, check_certs]
runs-on: macos-14
if: ${{ (needs.check_certs.outputs.new_certificate_needed == 'true' && vars.ENABLE_NUKE_CERTS == 'true') || vars.FORCE_NUKE_CERTS == 'true' }}
steps:
- name: Show certificate check output
run: echo "new_certificate_needed=${{ needs.check_certs.outputs.new_certificate_needed }}"

- name: Checkout repository
uses: actions/checkout@v4

- name: Install dependencies
run: bundle install

- name: Run Fastlane nuke_certs
run: |
set -e
bundle exec fastlane nuke_certs

- name: Recreate Distribution certificate after nuking
run: |
set -e
bundle exec fastlane certs

- name: Add success annotations
if: ${{ success() }}
run: |
echo "::warning::⚠️ All Distribution certificates and TestFlight profiles have been revoked and recreated."
echo "::warning::❗️ If you have other apps that do not auto-renew certificates, run their 'Create Certificates' workflow."
echo "::warning::✅ Your existing TestFlight builds will keep working!"

keep_alive:
name: Keep Alive
needs: [validate]
runs-on: ubuntu-latest
if: ${{ github.event_name == 'schedule' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Keep alive branch
run: |
git checkout -B alive-main
git commit --allow-empty -m 'Keep alive commit'
git push origin alive-main
119 changes: 96 additions & 23 deletions .github/workflows/create_certs.yml
Original file line number Diff line number Diff line change
@@ -1,38 +1,111 @@
name: 3. Create Certificates
run-name: Create Certificates (${{ github.ref_name }})
on:
workflow_dispatch:

on: [workflow_call, workflow_dispatch]

env:
TEAMID: ${{ secrets.TEAMID }}
GH_PAT: ${{ secrets.GH_PAT }}
GH_TOKEN: ${{ secrets.GH_PAT }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}

jobs:
validate:
name: Validate
uses: ./.github/workflows/validate_secrets.yml
secrets: inherit

certificates:
name: Create Certificates
create_certs:
name: Certificates
needs: validate
runs-on: macos-15
outputs:
new_certificate_needed: ${{ steps.set_output.outputs.new_certificate_needed }}
steps:
# Checks-out the repo
- name: Checkout Repo
uses: actions/checkout@v4

# Patch Fastlane Match to not print tables

- name: Patch Match Tables
run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"

# Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996)
- name: Sync clock
run: sudo sntp -sS time.windows.com

# Create or update certificates for app
- name: Create Certificates
run: fastlane certs
env:
TEAMID: ${{ secrets.TEAMID }}
GH_PAT: ${{ secrets.GH_PAT }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
run: |
TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb
if [ -f "$TABLE_PRINTER_PATH" ]; then
sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH"
else
echo "table_printer.rb not found"
exit 1
fi

- name: Install Project Dependencies
run: bundle install

- name: Run Fastlane certs lane
run: |
echo "Running Fastlane certs lane..."
bundle exec fastlane certs || true

- name: Check Distribution certificate and renew if needed
run: bundle exec fastlane check_and_renew_certificates
id: check_certs

- name: Set output and annotations based on Fastlane result
id: set_output
run: |
CERT_STATUS_FILE="${{ github.workspace }}/fastlane/new_certificate_needed.txt"
ENABLE_NUKE_CERTS=${{ vars.ENABLE_NUKE_CERTS }}

if [ -f "$CERT_STATUS_FILE" ]; then
CERT_STATUS=$(cat "$CERT_STATUS_FILE" | tr -d '\n' | tr -d '\r')
echo "new_certificate_needed: $CERT_STATUS"
echo "new_certificate_needed=$CERT_STATUS" >> $GITHUB_OUTPUT
else
echo "Certificate status file not found. Defaulting to false."
echo "new_certificate_needed=false" >> $GITHUB_OUTPUT
fi

if [ "$CERT_STATUS" != "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then
echo "::notice::🔔 Automated renewal of certificates is disabled because ENABLE_NUKE_CERTS is not set to 'true'."
fi

if [ "$CERT_STATUS" = "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then
echo "::error::❌ No valid distribution certificate found. Automated renewal of certificates was skipped because ENABLE_NUKE_CERTS is not set to 'true'."
exit 1
fi

if [ "${{ vars.FORCE_NUKE_CERTS }}" = "true" ]; then
echo "::warning::‼️ Nuking of certificates was forced because FORCE_NUKE_CERTS is set to 'true'."
fi

nuke_certs:
name: Nuke certificates
needs: [validate, create_certs]
runs-on: macos-14
if: ${{ (needs.create_certs.outputs.new_certificate_needed == 'true' && vars.ENABLE_NUKE_CERTS == 'true') || vars.FORCE_NUKE_CERTS == 'true' }}
steps:
- name: Output from step id 'check_certs'
run: echo "new_certificate_needed=${{ needs.create_certs.outputs.new_certificate_needed }}"

- name: Checkout repository
uses: actions/checkout@v4

- name: Install dependencies
run: bundle install

- name: Run Fastlane nuke_certs
run: |
set -e
bundle exec fastlane nuke_certs

- name: Recreate Distribution certificate after nuking
run: |
set -e
bundle exec fastlane certs

- name: Add success annotations for nuke and certificate recreation
if: ${{ success() }}
run: |
echo "::warning::⚠️ All Distribution certificates and TestFlight profiles have been revoked and recreated."
echo "::warning::❗️ If you have other apps that do not auto-renew certificates, please run the '3. Create Certificates' workflow for each."
echo "::warning::✅ Your existing TestFlight builds will keep working!"
58 changes: 57 additions & 1 deletion fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,60 @@ platform :ios do
git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}")
)
end
end

#####################################################################
# Additional lanes to support scheduled building and certificate nuking
#####################################################################

desc "Check Certificates and Trigger Workflow for Expired or Missing Certificates"
lane :check_and_renew_certificates do
setup_ci if ENV['CI']
ENV["MATCH_READONLY"] = false.to_s

api_key = app_store_connect_api_key(
key_id: ENV["FASTLANE_KEY_ID"],
issuer_id: ENV["FASTLANE_ISSUER_ID"],
key_content: ENV["FASTLANE_KEY"] # Ensure valid key content
)

new_certificate_needed = false

require 'time'
certificates = Spaceship::ConnectAPI::Certificate.all

distribution_certs = certificates.select { |cert| cert.certificate_type == "DISTRIBUTION" }

if distribution_certs.empty?
puts "No Distribution certificates found! Triggering action to create certificate."
new_certificate_needed = true
else
distribution_certs.each do |cert|
expiration_date = Time.parse(cert.expiration_date)

puts "Current Distribution Certificate: #{cert.id}, Expiration date: #{expiration_date}"

if expiration_date < Time.now
puts "Distribution Certificate #{cert.id} is expired! Triggering action to renew certificate."
new_certificate_needed = true
else
puts "Distribution certificate #{cert.id} is valid. No action required."
end
end
end

file_path = File.expand_path('new_certificate_needed.txt')
File.write(file_path, new_certificate_needed ? 'true' : 'false')

puts ""
puts "Absolute path of new_certificate_needed.txt: #{file_path}"
new_certificate_needed_content = File.read(file_path)
puts "Certificate creation or renewal needed: #{new_certificate_needed_content}"
end

desc "Keep Alive: Create an empty commit to keep the branch active"
lane :keep_alive do
sh "git checkout -B alive-main"
sh "git commit --allow-empty -m 'Keep alive commit'"
sh "git push origin alive-main"
end
end
Loading