Skip to content
the-events-calendarPublic

About

Where most of the GitHub Workflow automations are hosted and distributed from.

Resources

Stars

1 star

Watchers

10 watching

Forks

Latest commit

 

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The Events Calendar: GitHub Actions

This repository contains the code for the Automations using GitHub Actions used by the The Events Calendar team.

Our goal here is to create Actions that will allow us to deprecate the use of our Internal Jenkins for most of the automation tasks.

Before the creation of this repository most of the automation were done with individual GitHub Actions that were scattered across the different repositories. This made it hard to maintain and to have a clear overview of what was happening.

For more global integrations we used Jenkins, but we want to move away from it and use GitHub Actions for everything.

Synchronization of Files

We are using the BetaHuhn/repo-file-sync-action action to enable us to synchronize files across different repositories.

So any files that need to be identical across different repositories can be synchronized using this action, and the original stored in this repository under the templates folder.

Note

If you are using this action, please make sure to include @tec-bot user as a collaborator with write permissions in the repository you want to sync the files to.

Sync groups

The groups in .github/sync.yml exist because our repositories are not all the same kind of thing. A WordPress plugin, a shared library and a Laravel service have genuinely different needs, and syncing a plugin's release machinery into a service would only give its developers instructions that do not apply.

Group Goes to Holds
Active Plugin Repositories the 8 plugin repos and tribe-common base config, the PR template, AGENTS.md (each repo keeps a CLAUDE.md symlink to it, seeded by hand and not synced), changelog tooling, release workflows
Promoter promoter the PR template and project linking
Stack neutral every active product, 16 repos the OpenSpec plan check and the actionlint config declaring our Blacksmith runner labels

WordPress test matrix

.github/actions/wp-test-matrix returns the most recent WordPress X.Y releases from the wordpress.org version-check API, newest first, as a JSON array (for example ["7.1","7.0.4","6.9.7"]). Feature pull requests test the latest release only. The release sanity check pull request (release/* into main) tests the latest three, so it is the last gate before main. Test workflows feed it into their matrix:

on: [ pull_request ]

jobs:
  wp-versions:
    runs-on: blacksmith-4vcpu-ubuntu-2404
    outputs:
      versions: ${{ steps.matrix.outputs.versions }}
    steps:
      - id: matrix
        uses: the-events-calendar/actions/.github/actions/wp-test-matrix@main
        with:
          count: ${{ startsWith(github.head_ref, 'release/') && 3 || 1 }}

  test:
    needs: wp-versions
    runs-on: blacksmith-4vcpu-ubuntu-2404
    name: ${{ matrix.suite }} (WP ${{ matrix.wp }})
    strategy:
      fail-fast: false
      matrix:
        suite: [ unit, wpunit ]
        wp: ${{ fromJSON(needs.wp-versions.outputs.versions) }}
    steps:
      - run: ${SLIC_BIN} wp core update --force --version=${{ matrix.wp }}

Because the version under test comes from the matrix, release-update-wp-version.yml no longer rewrites any wp core update line in the test workflows.

OpenSpec plan check

Work on TEC products is planned before it is written, and the plan lives in one shared store — the-events-calendar/plans — rather than in the product repositories. A feature routinely spans several repos, so a spec kept in any one of them is invisible from the others.

templates/workflows/openspec-plan.yml requires a complete, valid active plan for the ticket a pull request belongs to. It reads the ticket id from the branch name ({type}/{task-id}/{short-desc}), falls back to the PR title, and looks the change up through .github/actions/verify-openspec-plan. The store is read from a branch named like the PR branch when one exists there, so a plan still being written can be checked before it merges. Failing that, an open pull request in the store whose branch carries the ticket as its {task-id} segment is used, so stacked PRs can share one plan branch; otherwise the store's default branch is used.

What it reports:

Situation Result
Active plan has nonempty artifacts and passes strict validation passes, with a reminder to archive once every repo has merged
Plan exists but is already archived fails — a change must remain active until the last repo merges
No plan for that ticket fails, and the summary shows the command to create one
No valid ticket id fails with guidance to add an ID to the branch, title, or action's ticket-id input
Required artifacts are missing, blank, or invalid fails with artifact or validation diagnostics
Store or artifacts cannot be read fails because the plan could not be verified

Both the action and the synced workflow default to enforcement: 'block'. An explicit ticket-id takes precedence over the branch, which takes precedence over the title. IDs are normalized to lower case. Callers can explicitly select warn for advisory results; invalid enforcement values always fail. Outputs distinguish validated (true), missing (false), archived, invalid, and unknown results.

What the check does not run on

The action itself has no exemptions: given a pull request it always wants a ticket and a plan. The synced workflow decides whether to ask it at all, through a job-level condition, and skips three cases:

Case Why
Author is tec-bot Version bumps, changelog moves, POT files, TBD replacement and merge-forwards come off task/* branches that carry no ticket, because there is no change being proposed
Head branch starts with release/ The release pull request into main is the release, not a change to plan
[skip-openspec] in the pull request body The escape hatch for anything else, matching [skip-changelog]. edited is in the workflow's trigger types, so adding or removing it re-runs the check

A skipped job reports as successful to branch protection, so OpenSpec Plan stays a required check for the pull requests that do run it. Nothing here weakens the requirement for work somebody chose to do: a human pull request off a feature branch still needs its plan.

Check changelog carries the same three clauses, for the same reasons: a release/* branch's entries were processed into the changelog list before the pull request opened, and the bot's task/* pull requests add no entry. Three of those — version bump, TBD replacement and POT generation — never carried a [skip-changelog] marker in their body, so the author clause is what clears them.

Both bot exemptions are keyed on the login that GHA_BOT_TOKEN_MANAGER authenticates as; changing that token means changing the condition in both workflows with it.

Validation requires nonempty proposal.md, design.md, tasks.md, and at least one specs/<capability>/spec.md (nested capability paths are supported). The action downloads those files at one resolved store commit and runs OpenSpec 1.12.0 with validate --type change --strict --no-interactive. It uses the built-in spec-driven schema; plan metadata, custom schemas, and skip_specs cannot bypass the required artifacts. Task checkboxes can remain unchecked while implementation is in progress. The CLI validates spec structure; nonempty prose alone does not prove quality or agreement with code. Reviewers must still confirm the plan describes the PR.

The action uses Node 24, Python 3, gh, and a pinned OpenSpec installation on the Ubuntu runner. Plan content is read as data, and telemetry is disabled. Package installation failures also fail the job.

Changing the action default does not override existing callers that explicitly select warn. After publishing the shared change, the workflow template must land in each of the 16 repositories through the existing sync process. To prevent merges with a failing check, the repository's merge rules must require OpenSpec Plan. Editing the generated product workflows directly will be overwritten by sync.

The check needs GHA_BOT_TOKEN_MANAGER to have read access to the plans repository, which is internal.

Testing the check

Install the same validator used in CI, then run the regression suite:

npm install --prefix /tmp/tec-openspec-cli --ignore-scripts --no-audit --no-fund @fission-ai/openspec@1.12.0
PATH="/tmp/tec-openspec-cli/node_modules/.bin:$PATH" python3 tests/test-openspec-plan.py

The tests execute the actual action scripts and real OpenSpec validator. GitHub responses are simulated to cover missing plans and API failures without changing remote repositories. .github/workflows/check-templates.yml runs these tests and the existing workflow lint and WordPress version arithmetic checks.

The openspec-workflow skill covers the workflow itself — writing a proposal worth reviewing and keeping it current — and its TEC extension, tec-openspec, covers the shared store, the ticket-ID naming, and archiving once (after the last repository merges, not per repo).

About

Where most of the GitHub Workflow automations are hosted and distributed from.

Resources

Stars

1 star

Watchers

10 watching

Forks

Releases

Packages

Used by

Contributors

Languages