Manifest file (core.yml)
Want your project's settings in Git, next to your code? This RFC proposes a core.yml file in your repository that describes your project, and a core up command that makes Core match it.
We're thinking about building this and want your feedback first. See Send your feedback.
Idea
Today you set up a project and change its settings (PHP version, crons, databases) in the management interface or through the API. Your application code lives in Git; those settings don't.
With core.yml, you add a file named core.yml to your repository that describes your project. Your CI pipeline runs core up, and Core changes your project to match the file.
version: 1
workspace: acme
project: shop
template: PHP
environments:
production:
domain: shop.example.com
php_version: "8.4"
databases:
- name: shop
password: ${DATABASE_PASSWORD}
crons:
- name: cleanup
command: php bin/cleanup.php
schedule: "0 3 * * *"
staging:
domain: staging.shop.example.com
php_version: "8.4"
Advantages
- Code and settings ship together. If your next release needs PHP 8.4, you change
php_versionin the same commit. The pipeline switches to PHP 8.4 before it deploys the code, so the new code never runs on the old version. - One history for everything. Settings changes go through the same merge requests, reviews and
git logas your code. To undo a change, you revert the commit. - No clicking to set up. A new project, or a staging environment for an existing one, is created by its first pipeline run.
Questions
- Do these advantages matter to you? Which one most, and which not at all?
- Would you use
core.yml? For which of your projects? - If you wouldn't: what would stop you?
- Which settings would you want in the file first? The first version would cover the PHP or Node.js version, databases, crons and daemons.
- Which CI do you use: GitHub Actions, GitLab CI, or something else?
How it works
Running it
core up --environment productionchanges the project to match the file. It waits until the changes are live, and fails the pipeline if any change failed.core up --dry-runshows what would change, without changing anything. Run it on pull or merge requests, so reviewers see what a settings change will do.- Running
core uptwice with the same file changes nothing the second time. - Nothing is deleted unless you ask. Things in your project that the file doesn't mention are listed in the output, and only deleted when you add
--remove-orphans. - If the file has a mistake (a PHP version that isn't available, a name used twice), nothing changes at all.
Passwords
Passwords never go in the file. You refer to a secret in your CI, such as ${DATABASE_PASSWORD}, and core up fills in the value from your pipeline's environment variables, the same way docker compose does.
Templates
Some templates create things by themselves. For example, Laravel creates a database and a queue worker. These count as part of the file: core up doesn't create them a second time, and --remove-orphans doesn't delete them. To change one, add an entry with the same name to the file.
Changes in the management interface
The file decides. If someone changes the PHP version in the management interface, the next core up sets it back to the version in the file. A setting you leave out of an entry, such as a cron's timeout, gets Core's default, also when someone changed it in the management interface. Projects set up with core.yml show a notice about this in the management interface.
Environments
A project has a production and, if you want, a staging environment. Staging can only be created once production exists. As in the management interface today, a new staging environment starts as a copy of production.
Existing projects
Projects you already have can get a core.yml file too. The API returns all of a project's current settings, so you can give that output to an LLM and ask it to write the core.yml file for you.
Run core up --dry-run on the result. If it shows no changes, the file matches the project. When every project has a file, they all start from the same baseline, and differences between them show up in the files.
Examples
main deploys to production, develop to staging. Pull and merge requests show what would change in the environment of their target branch. CORE_API_KEY and DATABASE_PASSWORD are secrets in your CI.
GitHub Actions
.github/workflows/deploy.yml:
name: Deploy
on:
push:
branches: [main, develop]
pull_request:
env:
CORE_ENVIRONMENT: ${{ (github.base_ref || github.ref_name) == 'main' && 'production' || 'staging' }}
CORE_API_KEY: ${{ secrets.CORE_API_KEY }}
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
jobs:
plan:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pipx run --spec python3-core-cli core up --environment "$CORE_ENVIRONMENT" --dry-run
apply:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pipx run --spec python3-core-cli core up --environment "$CORE_ENVIRONMENT"
deploy-code:
needs: apply
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh # your existing code deploy
GitLab CI
.gitlab-ci.yml:
workflow:
rules:
- if: $CI_COMMIT_BRANCH == "main" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"
variables:
CORE_ENVIRONMENT: production
- if: $CI_COMMIT_BRANCH == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"
variables:
CORE_ENVIRONMENT: staging
stages: [core, deploy]
.core:
stage: core
image: python:3.13
before_script:
- pip install python3-core-cli
plan:
extends: .core
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- core up --environment "$CORE_ENVIRONMENT" --dry-run
apply:
extends: .core
rules:
- if: $CI_PIPELINE_SOURCE == "push"
script:
- core up --environment "$CORE_ENVIRONMENT"
deploy-code:
stage: deploy
rules:
- if: $CI_PIPELINE_SOURCE == "push"
script:
- ./deploy.sh # your existing code deploy
Future additions
- A ready-made GitHub Action and GitLab CI/CD component, so the examples above get shorter.
Names of commands, packages and fields may still change.