π·πΊ Π ΡΡΡΠΊΠΈΠΌ Π³ΡΠ°ΠΆΠ΄Π°Π½Π°ΠΌ
ΠΡ, ΡΡΠ°ΡΡΠ½ΠΈΠΊΠΈ Laminas, ΡΠΎΠ΄ΠΈΠ»ΠΈΡΡ ΠΈ ΠΆΠΈΠ²Π΅ΠΌ Π² ΡΠ°Π·Π½ΡΡ ΡΡΡΠ°Π½Π°Ρ . Π£ ΠΌΠ½ΠΎΠ³ΠΈΡ ΠΈΠ· Π½Π°Ρ Π΅ΡΡΡ Π΄ΡΡΠ·ΡΡ, ΡΠΎΠ΄ΡΡΠ²Π΅Π½Π½ΠΈΠΊΠΈ ΠΈ ΠΊΠΎΠ»Π»Π΅Π³ΠΈ ΠΊΠ°ΠΊ Π² Π ΠΎΡΡΠΈΠΈ, ΡΠ°ΠΊ ΠΈ Π² Π£ΠΊΡΠ°ΠΈΠ½Π΅. ΠΠ΅ΠΊΠΎΡΠΎΡΡΠ΅ ΠΈΠ· Π½Π°Ρ ΡΠΎΠ΄ΠΈΠ»ΠΈΡΡ Π² Π ΠΎΡΡΠΈΠΈ. ΠΠ΅ΠΊΠΎΡΠΎΡΡΠ΅ ΠΈΠ· Π½Π°Ρ ΠΆΠΈΠ²ΡΡ Π² Π ΠΎΡΡΠΈΠΈ. Π£ Π½Π΅ΠΊΠΎΡΠΎΡΡΡ Π±Π°Π±ΡΡΠΊΠΈ ΠΈ Π΄Π΅Π΄ΡΡΠΊΠΈ ΡΡΠ°ΠΆΠ°Π»ΠΈΡΡ Ρ ΡΠ°ΡΠΈΡΡΠ°ΠΌΠΈ Π²ΠΎ ΠΡΠΎΡΠΎΠΉ ΠΌΠΈΡΠΎΠ²ΠΎΠΉ Π²ΠΎΠΉΠ½Π΅. ΠΠ΄Π΅ΡΡ Π½ΠΈΠΊΡΠΎ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ ΡΠ°ΡΠΈΠ·ΠΌ.
Π£ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈΠ· Π½Π°Ρ Π΅ΡΡΡ ΡΠΊΡΠ°ΠΈΠ½ΡΠΊΠ°Ρ ΡΠΎΠ΄ΡΡΠ²Π΅Π½Π½ΠΈΡΠ°, ΠΊΠΎΡΠΎΡΠ°Ρ ΡΠΏΠ°ΡΠ»Π°ΡΡ ΠΈΠ· Π΄ΠΎΠΌΠ° Π²ΠΌΠ΅ΡΡΠ΅ Ρ ΡΡΠ½ΠΎΠΌ. ΠΠΎΠ΅Π·Π΄ Π·Π°Π΄Π΅ΡΠΆΠ°Π»ΡΡ ΠΈΠ·-Π·Π° Π±ΠΎΠΌΠ±Π΅ΠΆΠΊΠΈ Π½Π° Π΄ΠΎΡΠΎΠ³Π΅ Π²ΠΏΠ΅ΡΠ΅Π΄ΠΈ. Π£ Π½Π°Ρ Π΅ΡΡΡ Π΄ΡΡΠ·ΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΏΡΡΡΡΡΡΡ Π² Π±ΠΎΠΌΠ±ΠΎΡΠ±Π΅ΠΆΠΈΡΠ°Ρ . ΠΡ Ρ ΡΡΠ΅Π²ΠΎΠ³ΠΎΠΉ ΠΆΠ΄Π΅ΠΌ Π²Π΅ΡΡΠΎΡΠΊΠΈ ΠΎΡ Π½ΠΈΡ ΠΏΠΎΡΠ»Π΅ Π²ΠΎΠ·Π΄ΡΡΠ½ΡΡ Π½Π°Π»Π΅ΡΠΎΠ², ΠΊΠΎΡΠΎΡΡΠ΅ Π±Π΅ΡΠΏΠΎΡΡΠ΄ΠΎΡΠ½ΠΎ Π½Π°Π½ΠΎΡΡΡ ΡΠ΄Π°ΡΡ ΠΈ ΠΏΠΎΠΏΠ°Π΄Π°ΡΡ ΠΏΠΎ Π±ΠΎΠ»ΡΠ½ΠΈΡΠ°ΠΌ, ΡΠΊΠΎΠ»Π°ΠΌ, Π΄Π΅ΡΡΠΊΠΈΠΌ ΡΠ°Π΄Π°ΠΌ ΠΈ Π΄ΠΎΠΌΠ°ΠΌ. ΠΡ Π½Π΅ Π±Π΅ΡΠ΅ΠΌ ΡΡΠΎ ΠΈΠ· ΠΊΠ°ΠΊΠΈΡ -Π»ΠΈΠ±ΠΎ Π‘ΠΠ. ΠΡ Π½Π°Π±Π»ΡΠ΄Π°Π΅ΠΌ ΡΡΠΎ Π½Π°ΠΏΡΡΠΌΡΡ.
ΠΡ Π΄ΠΎΠ²Π΅ΡΡΠ΅ΡΠ΅ Π½Π°ΠΌ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ, ΡΡΠΎΠ± ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π½Π°ΡΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ, ΠΈ ΠΌΡ ΠΏΡΠΎΡΠΈΠΌ Π²Π°Ρ Π΄ΠΎΠ²Π΅ΡΠΈΡΡΡΡ Π½Π°ΠΌ Π²Π½ΠΎΠ²Ρ. ΠΡ Π½ΡΠΆΠ΄Π°Π΅ΠΌΡΡ Π² ΠΏΠΎΠΌΠΎΡΠΈ. ΠΡΡ ΠΎΠ΄ΠΈΡΠ΅ ΠΈ ΠΏΡΠΎΡΠ΅ΡΡΡΠΉΡΠ΅ ΠΏΡΠΎΡΠΈΠ² ΡΡΠΎΠΉ Π±Π΅ΡΠΏΠΎΠ»Π΅Π·Π½ΠΎΠΉ Π²ΠΎΠΉΠ½Ρ. ΠΡΡΠ°Π½ΠΎΠ²ΠΈΡΠ΅ ΠΊΡΠΎΠ²ΠΎΠΏΡΠΎΠ»ΠΈΡΠΈΠ΅. Π‘ΠΊΠ°ΠΆΠΈΡΠ΅ "ΠΠ΅Ρ Π²ΠΎΠΉΠ½Π΅!"
πΊπΈ To Citizens of Russia
We at Laminas come from all over the world. Many of us have friends, family and colleagues in both Russia and Ukraine. Some of us were born in Russia. Some of us currently live in Russia. Some have grandparents who fought Nazis in World War II. Nobody here supports fascism.
One team member has a Ukrainian relative who fled her home with her son. The train was delayed due to bombing on the road ahead. We have friends who are hiding in bomb shelters. We anxiously follow up on them after the air raids, which indiscriminately fire at hospitals, schools, kindergartens and houses. We're not taking this from any media. These are our actual experiences.
You trust us enough to use our software. We ask that you trust us to say the truth on this. We need your help. Go out and protest this unnecessary war. Stop the bloodshed. Say "stop the war!"
This project is a Github Action that allows maintainers of open-source projects that follow SemVer to automate the automation of releases.
To use this automation in your own repository, copy the examples/.github
workflows into your own project:
cd /tmp
git clone https://github.com/laminas/automatic-releases.git
cd /path/to/your/project
mkdir -p .github/workflows
# Copy selected flow that fits for your project
cp /tmp/automatic-releases/examples/.github/release-on-milestone-closed.yml .github/workflows
# ... or:
cp /tmp/automatic-releases/examples/.github/release-on-milestone-closed-triggering-release-event.yml .github/workflows
git add .github/workflows
git commit -m "Added release automation"
To get started you need to create a branch for the next release. e.g. if your next milestone will be
3.2.0
a 3.2.x
branch is required.
Then add following secrets to your project or organization:
Secret | Description |
---|---|
GIT_AUTHOR_NAME |
full name of the author of your releases: can be the name of a bot account. |
GIT_AUTHOR_EMAIL |
email address of the author of your releases: can be an email address of a bot account. |
SIGNING_SECRET_KEY |
a password-less private GPG key in ASCII format, to be used for signing your releases: please use a dedicated GPG subkey for this purpose. Unsigned releases are not supported, and won't be supported. See Setting up GPG keys below for help. |
ORGANIZATION_ADMIN_TOKEN |
You have to provide an ORGANIZATION_ADMIN_TOKEN (with a full repo scope), which is a github token with administrative rights over your organization (or regular user project, for non-organization projects), issued by a user that has administrative rights over your project (that's you, if it is your own non-organization repository). This is required for the laminas:automatic-releases:switch-default-branch-to-next-minor command, because changing default branch of a repository currently requires administrative token rights. You can generate a token from your personal access tokens page. |
The GITHUB_TOKEN
secret you see in the examples is automatically created for
you when you enable GitHub Actions. To learn more about how it works, read
"Authenticating with the GITHUB_TOKEN"
in the GitHub Docs.
Workflow permissions for the target repository must be set to Read and Write.
Additionally, you must check the box to "Allow GitHub Actions to create and approve pull requests" in Settings > Actions > General.
You can find the settings in: github.com/ORG-NAME/REPO-NAME/settings/actions
First open your master key for editing (use --list-keys
to find it):
gpg --edit-key "<YOUR MASTER KEY ID>"
Type addkey
and select a type that is for signing, you might be asked about bit size depending on your choice.
When deciding over key expire, avoid setting to never expire, as recommendation of key bits will change over time.
Type save
to persist the new subkey to your master key. Make a note of the Key ID as you will need it in the next step.
Next export the new sub key:
gpg --output private.key --armor --export-secret-subkeys "<SubKey ID>!"
This will be exported to the file private.key
.
The !
at the end is important as it limits the export to just the sub key
Delete the file once you are done and don't share it with anyone else
If your master key is password protected, you will need to remove the password from the subkey before you can add it into github settings. You can skip this if your master key is not password protected.
To remove the password from the subkey, create an ephemeral gpg home directory:
install -d -m 700 gpg-tmp
Ensure that it works with gpg:
gpg --homedir gpg-tmp --list-keys
Import your subkey:
gpg --homedir gpg-tmp --import private.key
Enter edit mode:
gpg --homedir gpg-tmp --edit-key <SubKey ID>
Type passwd
, entering your current password and then set the password to "" to remove it.
The command may give error error changing passphrase: No secret key
when setting empty password.
You should ignore it as the password was really removed.
Type save
to exit edit mode and re-export your subkey:
gpg --homedir gpg-tmp --output private.key --armor --export-secret-subkeys "<SubKey ID>!"
Finally, remove the ephemeral directory:
rm -rf gpg-tmp
You will now need to export your master public key with the new subkey public key to the file public.key
:
gpg --output public.key --armor --export <YOUR MASTER KEY ID>
Then republish it to anywhere that you currently publish your public keys.
To generate a new GPG key use the following command:
gpg2 --full-generate-key
Pick option 4, then type 4096
for key size, select your desired expiry.
Fill out the user information and leave the password blank.
Once generated it will output something like gpg: key <Key ID> marked as ultimately trusted
. Take a note of this Key Id to use in the next step.
Now output the key to the file private.key
in the correct format to put into the environment variable required for setup:
gpg --output private.key --armor --export-secret-key <Key ID>
Delete the file once you are done and don't share it with anyone else
Optionally, you can export the corresponding public key to the file public.key
:
gpg --output public.key --armor --export <Key ID>
You can publish this key on your project webpage to allow users to verify your signed releases. You could sign this new key with your personal key and the keys of other project maintainers to establish its provenance.
Assuming your project has Github Actions enabled, each time you close a milestone, this action will perform all following steps (or stop with an error):
x.y.z
formatx.y.z
for the release (e.g. 1.1.x
for a 1.1.0
release)x.y.z
on the selected branch, with the generated changelogx.y.z
, with the generated tag and changelogx.y+1.z
Please read the feature/
specification for more detailed scenarios on how the tool is supposed
to operate.
In this model we operate with release branches (e.g. 1.0.x
, 1.1.x
, 1.2.x
).
This provides a lot of flexibility whilst keeping a single workflow.
The current default release branch should be used. The default branch is always automatically changed after a new release is created.
An example is Mezzio that has 3.2.x
as the current default release branch for simple features and
deprecation notices and 4.0.x
for the next major release.
Bug fixes should be applied on the version which introduced the issue and then synchronized all way to the current default release branch via merge-ups.
When releasing a new version x.y.z
, a new branch will be created x.y+1.z
and will be set as the next
default release branch. If a hotfix x.y.z+1
is released, a merge-up branch is automatically created.
To keep branches synchronized merge-ups are used.
That consists in getting the changes of a specific released branch merged all the way up to the current default branch. This ensures that all release branches are up-to-date and will never present a bug which has already been fixed. Merge-up branches are automatically created but needs to be merged manually into the targeted branch.
Let's say we've released the versions 1.0.0
and 1.1.0
.
New features are being developed on 1.2.x
.
After a couple weeks, a bug was found on version 1.0.0
.
The fix for that bug should be done based on the branch 1.0.x
and, once merged, the branches should be updated
in this way:
1.0.x-merge-up-into-1.1.x_*
, using 1.1.x
as destination.1.1.x
.1.1.x-merge-up-into-1.2.x_*
, using 1.2.x
as destination.1.2.x
.:warning: when the merge-up can't be merged due to conflicts, it needs to be synced with the destination branch. That's done by merging the destination into the merge-up branch and resolving the conflicts locally:
git fetch origin
)git checkout 1.1.x-merge-up-into-1.2.x_*
)git merge --no-ff origin/1.2.x
)git mergetool
or through an IDE)git merge --continue
)git push
)If needed you can create a merge-up branch manually: git checkout 1.0.x && git checkout -b 1.0.x-merge-up-into-1.1.x
Because the tokens generated by GitHub Actions are considered OAuth tokens, they are incapable of triggering further workflow events (see this document for an explanation).
As such, if you want to trigger a release event when automatic-releases runs,
you will need to modify your .github/workflows/release-on-milestone-closed.yml
file to assign the ORGANIZATION_ADMIN_TOKEN
as the value of the
GITHUB_TOKEN
environment variable for the Release
step:
- name: "Release"
uses: "./"
with:
command-name: "laminas:automatic-releases:release"
env:
"GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}