Files
fleet/.github/workflows
George Karrandtest 6d68bd6487 Adding milestone release calendar sync workflow (#48593)
This is a calendar sync workflow to trigger off of milestones being
created or due dates being updated to keep the release calendar
automatically in sync

example of me running it locally

```
❯ python sync.py --oauth --client-secret ./gcal-releases-oauth.json --apply
# Fleet release calendar sync — 2026-07-01 14:50

Calendar: <redacted>@group.calendar.google.com
Repo:     fleetdm/fleet

## Milestones
  4.88.0  due 2026-07-01
  4.89.0  due 2026-07-10
  4.90.0  due 2026-07-31
  4.91.0  due 2026-08-21
  4.92.0  due 2026-09-11
  4.93.0  due 2026-10-02
  4.94.0  due 2026-10-23
  4.95.0  due 2026-11-13
  4.96.0  due 2026-12-04
  4.97.0  due 2026-12-25
  4.98.0  due 2027-01-15
  4.99.0  due 2027-02-05
  4.100.0  due 2027-02-26
  4.101.0  due 2027-03-19
  4.102.0  due 2027-04-09

## Events scanned
  [develop    ] 'Develop (next release - 4.87.0)'  start=2026-05-18  end=2026-06-06
  [develop    ] 'Develop (next release - 4.89.0)'  start=2026-06-08  end=2026-06-27
  [rc         ] 'Release candidate (next release - 4.87.0)'  start=2026-06-08  end=2026-06-20
  [release_day] 'Release day: minor release - 4.87.0'  start=2026-06-19  end=2026-06-20
  [develop    ] 'Develop (next release - 4.90.0)'  start=2026-06-29  end=2026-07-18
  [rc         ] 'Release candidate (next release - 4.89.0)'  start=2026-06-29  end=2026-07-11
  [release_day] 'Release day: minor release - 4.89.0'  start=2026-07-10  end=2026-07-11
  [develop    ] 'Develop (next release - 4.91.0)'  start=2026-07-20  end=2026-08-08
  [rc         ] 'Release candidate (next release - 4.90.0)'  start=2026-07-20  end=2026-08-01
  [release_day] 'Release day: minor release - 4.90.0'  start=2026-07-31  end=2026-08-01
  [develop    ] 'Develop (next release - 4.92.0)'  start=2026-08-10  end=2026-08-29
  [rc         ] 'Release candidate (next release - 4.91.0)'  start=2026-08-10  end=2026-08-22
  [release_day] 'Release day: minor release - 4.91.0'  start=2026-08-21  end=2026-08-22
  [develop    ] 'Develop (next release - 4.93.0)'  start=2026-08-31  end=2026-09-19
  [rc         ] 'Release candidate (next release - 4.92.0)'  start=2026-08-31  end=2026-09-12
  [release_day] 'Release day: minor release - 4.92.0'  start=2026-09-11  end=2026-09-12
  [develop    ] 'Develop (next release - 4.94.0)'  start=2026-09-21  end=2026-10-10
  [rc         ] 'Release candidate (next release - 4.93.0)'  start=2026-09-21  end=2026-10-03
  [release_day] 'Release day: minor release - 4.93.0'  start=2026-10-02  end=2026-10-03
  [develop    ] 'Develop (next release - 4.95.0)'  start=2026-10-12  end=2026-10-31
  [rc         ] 'Release candidate (next release - 4.94.0)'  start=2026-10-12  end=2026-10-24
  [release_day] 'Release day: minor release - 4.94.0'  start=2026-10-23  end=2026-10-24
  [develop    ] 'Develop (next release - 4.96.0)'  start=2026-11-02  end=2026-11-21
  [rc         ] 'Release candidate (next release - 4.95.0)'  start=2026-11-02  end=2026-11-14
  [release_day] 'Release day: minor release - 4.95.0'  start=2026-11-13  end=2026-11-14
  [develop    ] 'Develop (next release - 4.97.0)'  start=2026-11-23  end=2026-12-12
  [rc         ] 'Release candidate (next release - 4.96.0)'  start=2026-11-23  end=2026-12-05
  [release_day] 'Release day: minor release - 4.96.0'  start=2026-12-04  end=2026-12-05
  [develop    ] 'Develop (next release - 4.98.0)'  start=2026-12-14  end=2027-01-02
  [rc         ] 'Release candidate (next release - 4.97.0)'  start=2026-12-14  end=2026-12-26
  [release_day] 'Release day: minor release - 4.97.0'  start=2026-12-25  end=2026-12-26
  [rc         ] 'Release candidate (next release - 4.90.0)'  start=2027-01-04  end=2027-01-16

## Proposed actions
  RC 4.98.0: title 'Release candidate (next release - 4.90.0)' -> 'Release candidate (next release - 4.98.0)'
  + CREATE Release day 4.88.0 on 2026-07-01
  + CREATE RC 4.88.0 2026-06-27..2026-07-02
  + CREATE Release day 4.98.0 on 2027-01-15
  + CREATE Release day 4.99.0 on 2027-02-05
  + CREATE RC 4.99.0 2027-02-01..2027-02-06
  + CREATE Develop 4.99.0 2027-01-04..2027-01-23
  + CREATE Release day 4.100.0 on 2027-02-26
  + CREATE RC 4.100.0 2027-02-22..2027-02-27
  + CREATE Develop 4.100.0 2027-01-25..2027-02-13
  + CREATE Release day 4.101.0 on 2027-03-19
  + CREATE RC 4.101.0 2027-03-15..2027-03-20
  + CREATE Develop 4.101.0 2027-02-15..2027-03-06
  + CREATE Release day 4.102.0 on 2027-04-09
  + CREATE RC 4.102.0 2027-04-05..2027-04-10
  + CREATE Develop 4.102.0 2027-03-08..2027-03-27

## Warnings / skipped
  ! Develop event 'Develop (next release - 4.87.0)' ending 2026-06-05 has no matching minor milestone — skipped
  ! RC event 'Release candidate (next release - 4.87.0)' ending 2026-06-19 has no matching milestone — skipped
  ! Release day event 'Release day: minor release - 4.87.0' on 2026-06-19 has no matching milestone (within 5d) — skipped
  ! Skipping Develop create for 4.88.0: sprint ended 2026-06-17 (past)

Total: 1 rename, 0 move, 15 create, 4 warning

Applying 16 change(s)...
  ok: RC 4.98.0: title 'Release candidate (next release - 4.90.0)' -> 'Release candidate (next release - 4.98.0)'
  ok: + CREATE Release day 4.88.0 on 2026-07-01
  ok: + CREATE RC 4.88.0 2026-06-27..2026-07-02
  ok: + CREATE Release day 4.98.0 on 2027-01-15
  ok: + CREATE Release day 4.99.0 on 2027-02-05
  ok: + CREATE RC 4.99.0 2027-02-01..2027-02-06
  ok: + CREATE Develop 4.99.0 2027-01-04..2027-01-23
  ok: + CREATE Release day 4.100.0 on 2027-02-26
  ok: + CREATE RC 4.100.0 2027-02-22..2027-02-27
  ok: + CREATE Develop 4.100.0 2027-01-25..2027-02-13
  ok: + CREATE Release day 4.101.0 on 2027-03-19
  ok: + CREATE RC 4.101.0 2027-03-15..2027-03-20
  ok: + CREATE Develop 4.101.0 2027-02-15..2027-03-06
  ok: + CREATE Release day 4.102.0 on 2027-04-09
  ok: + CREATE RC 4.102.0 2027-04-05..2027-04-10
  ok: + CREATE Develop 4.102.0 2027-03-08..2027-03-27
```

We can now get rid of the recurring unnamed events and just rely on this
once it's merged and added to milestone updates to trigger automatically

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a release calendar synchronization tool that aligns Google
Calendar events with open release milestone dates.
* Supports dry-run previews and an optional apply mode to create,
update, and delete events.
* Categorizes and matches release-day, release-candidate, and
development entries, including duplicate and stale event cleanup.
* Added a manually triggered workflow to run the sync securely with
configurable apply behavior.
* Added support for service-account authentication and optional
interactive OAuth.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: test <test@test.com>
2026-07-22 14:57:50 -05:00
..
2026-07-14 12:26:24 -03:00

Github Actions

Fleet uses Github Actions for continuous integration (CI). This document describes best practices and at patterns for writing and maintaining Fleet's Github Actions workflows.

Bash

By default, Github Actions sets the shell to bash -e for linux and MacOS runners. To help write safer bash scripts in run jobs and avoid common issues, override the default by adding the following to the workflow file

defaults:
  run:
    # fail-fast using bash -eo pipefail. See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference
    shell: bash

By specifying the default shell to bash, some extra flags are set. The option pipefail changes the behaviour when using the pipe | operator such that if any command in a pipeline fails, that commands return code will be used a the return code for the whole pipeline. Consider the following example in test-go.yaml

    - name: Run Go Tests
      run: |
        # omitted ...
          make test-go 2>&1 | tee /tmp/gotest.log

If the pipefail option was not set, this job would always succeed because tee would always return success. This is not the intended behavior. Instead, we want the job to fail if make test-go fails.

Concurrency

Github Action runners are limited. If a lot of workflows are queued, they will wait in pending until a runner becomes available. This has caused issue in the past where workflows take an excessively long time to start. To help with this issue, use the following in workflows

# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id}}
  cancel-in-progress: true

When a workflow is triggered via a pull request, it will cancel previous running workflows for that pull request. This is especially useful when changes are pushed to a pull request frequently. Manually triggered workflows, workflows that run on a schedule, and workflows triggered by pushes to main are unaffected.