I improved the custom module “GitHub Webhook” that triggers GitHub Actions from the Drupal admin panel.
https://github.com/nakamura196/Drupal-module-github_webhook
Originally a basic module with multi-repository support, I added features including UI tab separation, permission granularity, workflow status display, and auto-triggering.
Module Before Improvements# The original module had the following structure:
File count : 5 files (info.yml, routing.yml, links.menu.yml, permissions.yml, SettingsForm.php)Supported versions : Drupal 10 onlyRepositories : Multi-repository support (dynamic add/remove with AJAX)Screen : Settings and trigger on the same screen (2 accordions)Permissions : Only 1 permission access github webhook settings (same permission for both settings and trigger)Token management : #default_value set on password field (token output in plaintext in HTML source)HTTP client : Direct instantiation of new \GuzzleHttp\Client()Exception class : Written in catch block without use statement (incorrect namespace resolution)$ ] f ; o ' ' ' B r # # # e m t t d f [ y i e o ' p t f r s e l a e e ' e u : t ' l t = t T i > = _ o n > v k g ' a e s p $ l n ' a t u ] s h e w [ s i ' a ' w s s g o - = i r > > s t d t e h ' ( $ t u , ' c b G o i _ i n n t t f o H i # k u g d e b - e n > f ' T g a ] o e u k t l = e ( t n ' _ [ ' g v ) i a , t l h u u e b _ t o k e n ' ) , O u t p u t i n p l a i n t e x t i n H T M L $ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w Overview of Changes# Comparison of file structure before and after improvements. * indicates modified, + indicates newly added.
g i t E N + + + + + + + + + + + + + + + h x e u i g g g g s w c g g g g s s s s j c c t D d b s i i i i r o i i i i r r r r s s o r o o _ t t t t t c f m t t t t c c c c s n a c c w i h h h h / i p h h h h / / / g / f n k k e n u u u u F l o u u u u F F S C i g i s e e b g b b b b o e s b b b b o o e o t i g l r r h _ _ _ _ r s e _ _ _ _ r r r n h t / a f - o f w w w w m : r w w w w m m t u h s t i c o i e e e e / . e e e e / / i r b u c i l o k l b b b b S j b b b b T A c o - b h o e m / e h h h h e s h h h h r u e l w - e n p s o o o o t o o o o o i t l e w m s o o o o o t n o o o o g W e b e a s ( k k k k i k k k k g T e r h b j e m . . . . n . . . . e r b / o h g a . o i r l p g l s l m r i h S o o i . y d n o i e s i e i o F g o t k o t p m i f u n r F n r b d o g o a - k h o l f o t k m o k v r u r e k t s - u i . i s i r s i a l m r T u t s b e y n . s m . c r e . F r s a t _ d m g m s . t e i p o i C t a w ) l . e i p a s e h r g o u t e : y n o h s . s p m g n s u b m u n p k y . . e t . s h l . s . m y p r r j . o y . y l m h S o s c o m y m l p e l s k l m l r l s . l v e s i r c c . h e p e . h m # # # # # # # # # # # # p p # # a # # h . D 1 E 1 T C T S J E M A p S S y J D r n r o a e S n a u t t m a o u r t p i m b r / t n t a a l p c p o r e g p C i u o # # t t a k a u y r g o d i S t a u u n e l t m e s e c S y l t W W s s # e r e p i r e f e r e o s 1 o s r i l h t i b r p d C e e 1 → i s p n d i o r g h k o i o n n i a p i e b o i g o f l s n t v s 4 t o r a t f r k g e o l l p f r i u n t c i i a g r k o i l i a r p r c k o n r ( e w n a g n o p o h → s a n i y a r s e g y u s n o u a e g t u e x s r l m r t n 2 p e ( i d t s t e t s a a e t e g a 3 o e o c t c a t t t n , s e p r d n f r i u t y i i t d e a e t i t e n t u l o o P e r t f a n r e g i s e n n f H x m e i b i i n s o s o P p i d n s t g n J s r a s , i ) i g s S c r n s t o e c s O h t e d i K i n r r e N e e q e o e o ) e r m s u d n y n e v A a t i s n i P i r m c I n e s o e g m e d e p u n a l t r e a a t i d e n d d t e e d g r a t i o n a d d e d 1. From Single Screen to Tab Separation# Before# Settings and trigger were on a single screen with accordions. Since administrators and content editors used the same screen, the token input field was visible to general users.
/ ├ │ │ └ a ─ ─ d ─ ─ m i [ ├ └ [ └ n S ─ ─ T ─ / e ─ ─ r ─ c t i o t O S g T n i w u g r f n n b e i i g e m r g g s r i g / ] t W e g / e r i A b t c R h G h c e o i u o p o t b r o k H _ d ] u w i b e A b n T c W h o c e o k o b o ← e r h k n d o F i o o / o k r n E b a v u d e ← t m n t i t F o n o n i T r s y t p g r e e a n t e o r r a s l u s e r s After# Using Drupal’s Local Tasks (tabs), the screen was separated into 3 pages. General users only see the “Trigger” tab.
/ ├ ├ └ g ─ ─ ─ i ─ ─ ─ t h [ [ [ u T R A b r e u - i p t w g o o e g s b e i T h r t r o ] o i o r g k T i g / a e e s b s r e ] ] t t T T i a a n b b g s ← ← ← F A A o d d r m m i i g n n e i i n s s e t t r r r a a a l t t o o u r r s s s e r o o s n n l l ( y y d e f a u l t )
# g g g i i i g t r t b t r t b t r t b i h o i a h o i a h o i a t u u t s u u t s u u t s h b t l e b t l e b t l e u _ e e _ _ e e _ _ e e _ b w _ : r w _ : r w _ : r _ e n o e n o e n o w b a ' u b a ' u b a ' u e h m T t h m R t h m A t b o e r e o e e e o e u e h o : i : o : p : o : t : o k g k o k o o . g g g . g s g . g g k t i e i r i i i a i T i . r t r t e t t t u t r t l i h ' h p h o h t h i h i g u u o u r u o u g u n g b b s b i b _ b g b k e _ _ i _ e _ t _ e _ s r w w t w s w r w r w . _ e e o e ' e i e ' e t t b b r b b g b b a a h h i h h g h h s b o o e o o e o o k : o o s o o r o o . k k _ k k _ k k y . . t . . t . . m t t a r t a a t l r r b e r b u r i i : p i : t i ( g g o g o g n g g s g _ g e e e i e t e w r r t r r r ) o i r g i g e e s r
The most frequently used “Trigger” tab was set as the default route, designed so content editors would not get confused.
2. Permission Separation# Before# Only one custom permission access github webhook settings was defined, with the same permission used for both settings changes and triggering.
# a c B c t d e e i e f s t s o s l c r e r e g : i i p t ' t h A i u c o b c n e : w s e s ' b A h G l o i l o t o k H w u s b u e s t W e t e r i b s n h g o t s o o : k a S c e c t e t s i s n g G s i ' t H u b w e b h o o k c o n f i g u r a t i o n . '
After# Separated into two permissions for administrators and general users.
# a t d r g m t d r i t d i i i e e g i e t n t s s g t s h i l c t e l c u s e r r r e r b t : i i : i _ e p c g p w r ' t t i ' t e A i t T i b g d o a h r o h i m n c u i n o t i : c b g : o h n e g k u i ' s w e ' . b s C s e r T p t o : b r e w e n h G i r e r f t o i g m b i r o t g i h G g u k H e s o i u e : u r s o t r b i k H e r o : u W e n b r e p s e b o . W p h s y e o o i m b s o t l h i k o o t ' r o o y k r _ ' i d e i s s , p a t t o c k h e n w s e , b h a o n o d k s a . u ' t o - t r i g g e r s e t t i n g s . '
By adding restrict access: true, a warning mark is displayed for the admin permission on Drupal’s permissions screen.
With this separation, general users who do not have a GitHub account can trigger builds, as long as an administrator has registered the PAT (Personal Access Token). The success message content also switches depending on the role.
i } } f $ ) e $ ) S t ; l t ; h \ h $ s h $ o D i t e S i t w r s h h s h u - i { o - i l p > s w > s i a m - m - n l e > t e > k : s t e s t : s ( x s ( t c e ' t e " o u n . n G r g . o g i G r e . n e t i e r l r H t n ( < y ( u H t ) a ) b u U - f - b s > h o > w e a r r a e A r d e d b c ( d f g d h t ) M = e M o i - e " n e o o > s : e s k n h s u r s s a a r a a t s g l l g r f P e " e i o e ( u ( g r r t s g m a e e a i r r r d s g s e m s e d i i t n o = s i n " u s ( _ c t ' b c r a l e a d a s t m n s o i k f r n " u s i > l s V l t i y e e r w f o g A r i c t t @ h i r u o e b n p s o w < s e / i b a t h > o o ' r o , y k . ' [ " ) ' , ) : u [ { r . l . ' . ] = ) > $ a c t i o n s _ u r l ] ) 3. Token Security Improvements# Before# Setting #default_value on the password field caused the token to be output in plaintext in the HTML source.
' # d B e e f f a o u r l e t : _ v D a a l n u g e e ' r o = u > s $ c o n f i g - > g e t ( ' g i t h u b _ t o k e n ' ) , After# Removed #default_value and instead indicate whether a token is saved via the description text.
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 0
When saving, if the field is empty, the existing token is preserved.
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 1
Key Module Integration (New Feature)# When the Key module is installed, the token storage method can be switched. Drupal’s #states API is used to dynamically toggle form fields.
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 2
Using the Key module, tokens can be stored in environment variables or HashiCorp Vault, preventing tokens from being included in the Drupal database or drush config:export.
4. Configuration Schema Addition and Extended Configuration Structure# Before# Although multi-repository support was already in place, there was no configuration schema (schema.yml), so configuration validation and type checking were not applied. There were also no auto-trigger related settings.
After# A new configuration schema was defined, and token_source, token_key, and workflow_file fields were added to the repository settings. Auto-trigger related settings were also added.
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 3
Before# The webhook trigger processing was implemented directly in the form class’s triggerWebhook() method.
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 4
After# Extracted into a WebhookTriggerService service. It can be called from both the form and Entity hooks (auto trigger).
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 5
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 6
The HTTP client was also changed to \Drupal::httpClient(), retrieving it through Drupal’s service container.
6. GitHub Actions Status Display (New Feature)#
Added a feature to check workflow execution status on Drupal without needing to access GitHub after triggering.
Architecture# $ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 7
Since the PAT registered by administrators is used server-side to call the GitHub API, general users can check the status even without a GitHub account.
Status is visually displayed with colored dots.
Status Color Display Queued Yellow Static In progress Blue Pulse animation Success Green Static Failed Red Static Cancelled Gray Static
Administrators see the workflow run’s GitHub URL as a link, while general users see text only.
Operation Under Subdirectories# When Drupal is operated under a subdirectory (e.g., https://example.com/cms/), the status API path also needs to include the subdirectory. Initially, /github-webhook/api/status was hardcoded, which does not work under subdirectories.
Using Drupal’s URL generator, the base URL is dynamically generated from routes.
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 8
This correctly generates paths such as /cms/github-webhook/api/status.
Note that because the route parameter repo_index has a \d+ constraint, passing a string to the placeholder causes an error. The workaround is to pass the number 0 to build the base URL and then remove the trailing /0.
Note: Identifying Triggered Runs# The repository_dispatch API returns HTTP 204 (No Content), so the Run ID of the triggered workflow cannot be obtained directly. Therefore, the approach is to display a list of recent runs filtered by event=repository_dispatch.
7. Auto Trigger on Content Save (New Feature)# Implemented hook_entity_insert and hook_entity_update to automatically trigger a Webhook when a node is saved.
$ c l B i e e f n o t r e = : n G e u w z z \ l G e u z c z l l i e e H n t t t p w \ a C s l i d e i n r t e ( c ) t ; l y i n s t a n t i a t e d w i t h n e w 9
From the “Auto Trigger” tab in the admin panel, you can select the content types and repositories to trigger. Since the business logic was extracted into a service, the same processing can be called from both the form submit handler and Entity hooks.
8. Drupal 11 Support# Before# g i t E N + + + + + + + + + + + + + + + h x e u i g g g g s w c g g g g s s s s j c c t D d b s i i i i r o i i i i r r r r s s o r o o _ t t t t t c f m t t t t c c c c s n a c c w i h h h h / i p h h h h / / / g / f n k k e n u u u u F l o u u u u F F S C i g i s e e b g b b b b o e s b b b b o o e o t i g l r r h _ _ _ _ r s e _ _ _ _ r r r n h t / a f - o f w w w w m : r w w w w m m t u h s t i c o i e e e e / . e e e e / / i r b u c i l o k l b b b b S j b b b b T A c o - b h o e m / e h h h h e s h h h h r u e l w - e n p s o o o o t o o o o o i t l e w m s o o o o o t n o o o o g W e b e a s ( k k k k i k k k k g T e r h b j e m . . . . n . . . . e r b / o h g a . o i r l p g l s l m r i h S o o i . y d n o i e s i e i o F g o t k o t p m i f u n r F n r b d o g o a - k h o l f o t k m o k v r u r e k t s - u i . i s i r s i a l m r T u t s b e y n . s m . c r e . F r s a t _ d m g m s . t e i p o i C t a w ) l . e i p a s e h r g o u t e : y n o h s . s p m g n s u b m u n p k y . . e t . s h l . s . m y p r r j . o y . y l m h S o s c o m y m l p e l s k l m l r l s . l v e s i r c c . h e p e . h m # # # # # # # # # # # # p p # # a # # h . D 1 E 1 T C T S J E M A p S S y J D r n r o a e S n a u t t m a o u r t p i m b r / t n t a a l p c p o r e g p C i u o # # t t a k a u y r g o d i S t a u u n e l t m e s e c S y l t W W s s # e r e p i r e f e r e o s 1 o s r i l h t i b r p d C e e 1 → i s p n d i o r g h k o i o n n i a p i e b o i g o f l s n t v s 4 t o r a t f r k g e o l l p f r i u n t c i i a g r k o i l i a r p r c k o n r ( e w n a g n o p o h → s a n i y a r s e g y u s n o u a e g t u e x s r l m r t n 2 p e ( i d t s t e t s a a e t e g a 3 o e o c t c a t t t n , s e p r d n f r i u t y i i t d e a e t i t e n t u l o o P e r t f a n r e g i s e n n f H x m e i b i i n s o s o P p i d n s t g n J s r a s , i ) i g s S c r n s t o e c s O h t e d i K i n r r e N e e q e o e o ) e r m s u d n y n e v A a t i s n i P i r m c I n e s o e g m e d e p u n a l t r e a a t i d e n d d t e e d g r a t i o n a d d e d 0
After# g i t E N + + + + + + + + + + + + + + + h x e u i g g g g s w c g g g g s s s s j c c t D d b s i i i i r o i i i i r r r r s s o r o o _ t t t t t c f m t t t t c c c c s n a c c w i h h h h / i p h h h h / / / g / f n k k e n u u u u F l o u u u u F F S C i g i s e e b g b b b b o e s b b b b o o e o t i g l r r h _ _ _ _ r s e _ _ _ _ r r r n h t / a f - o f w w w w m : r w w w w m m t u h s t i c o i e e e e / . e e e e / / i r b u c i l o k l b b b b S j b b b b T A c o - b h o e m / e h h h h e s h h h h r u e l w - e n p s o o o o t o o o o o i t l e w m s o o o o o t n o o o o g W e b e a s ( k k k k i k k k k g T e r h b j e m . . . . n . . . . e r b / o h g a . o i r l p g l s l m r i h S o o i . y d n o i e s i e i o F g o t k o t p m i f u n r F n r b d o g o a - k h o l f o t k m o k v r u r e k t s - u i . i s i r s i a l m r T u t s b e y n . s m . c r e . F r s a t _ d m g m s . t e i p o i C t a w ) l . e i p a s e h r g o u t e : y n o h s . s p m g n s u b m u n p k y . . e t . s h l . s . m y p r r j . o y . y l m h S o s c o m y m l p e l s k l m l r l s . l v e s i r c c . h e p e . h m # # # # # # # # # # # # p p # # a # # h . D 1 E 1 T C T S J E M A p S S y J D r n r o a e S n a u t t m a o u r t p i m b r / t n t a a l p c p o r e g p C i u o # # t t a k a u y r g o d i S t a u u n e l t m e s e c S y l t W W s s # e r e p i r e f e r e o s 1 o s r i l h t i b r p d C e e 1 → i s p n d i o r g h k o i o n n i a p i e b o i g o f l s n t v s 4 t o r a t f r k g e o l l p f r i u n t c i i a g r k o i l i a r p r c k o n r ( e w n a g n o p o h → s a n i y a r s e g y u s n o u a e g t u e x s r l m r t n 2 p e ( i d t s t e t s a a e t e g a 3 o e o c t c a t t t n , s e p r d n f r i u t y i i t d e a e t i t e n t u l o o P e r t f a n r e g i s e n n f H x m e i b i i n s o s o P p i d n s t g n J s r a s , i ) i g s S c r n s t o e c s O h t e d i K i n r r e N e e q e o e o ) e r m s u d n y n e v A a t i s n i P i r m c I n e s o e g m e d e p u n a l t r e a a t i d e n d d t e e d g r a t i o n a d d e d 1
The following code improvements were also made:
Before After Reason new \GuzzleHttp\Client()\Drupal::httpClient()Should use Drupal’s service container Exception caught without use statement Added use GuzzleHttp\Exception\... Namespace resolution was incorrect \Drupal::messenger()->addMessage()$this->messenger()->addMessage()Should use MessengerTrait
A composer.json was also newly created, compliant with Drupal.org standards.
g i t E N + + + + + + + + + + + + + + + h x e u i g g g g s w c g g g g s s s s j c c t D d b s i i i i r o i i i i r r r r s s o r o o _ t t t t t c f m t t t t c c c c s n a c c w i h h h h / i p h h h h / / / g / f n k k e n u u u u F l o u u u u F F S C i g i s e e b g b b b b o e s b b b b o o e o t i g l r r h _ _ _ _ r s e _ _ _ _ r r r n h t / a f - o f w w w w m : r w w w w m m t u h s t i c o i e e e e / . e e e e / / i r b u c i l o k l b b b b S j b b b b T A c o - b h o e m / e h h h h e s h h h h r u e l w - e n p s o o o o t o o o o o i t l e w m s o o o o o t n o o o o g W e b e a s ( k k k k i k k k k g T e r h b j e m . . . . n . . . . e r b / o h g a . o i r l p g l s l m r i h S o o i . y d n o i e s i e i o F g o t k o t p m i f u n r F n r b d o g o a - k h o l f o t k m o k v r u r e k t s - u i . i s i r s i a l m r T u t s b e y n . s m . c r e . F r s a t _ d m g m s . t e i p o i C t a w ) l . e i p a s e h r g o u t e : y n o h s . s p m g n s u b m u n p k y . . e t . s h l . s . m y p r r j . o y . y l m h S o s c o m y m l p e l s k l m l r l s . l v e s i r c c . h e p e . h m # # # # # # # # # # # # p p # # a # # h . D 1 E 1 T C T S J E M A p S S y J D r n r o a e S n a u t t m a o u r t p i m b r / t n t a a l p c p o r e g p C i u o # # t t a k a u y r g o d i S t a u u n e l t m e s e c S y l t W W s s # e r e p i r e f e r e o s 1 o s r i l h t i b r p d C e e 1 → i s p n d i o r g h k o i o n n i a p i e b o i g o f l s n t v s 4 t o r a t f r k g e o l l p f r i u n t c i i a g r k o i l i a r p r c k o n r ( e w n a g n o p o h → s a n i y a r s e g y u s n o u a e g t u e x s r l m r t n 2 p e ( i d t s t e t s a a e t e g a 3 o e o c t c a t t t n , s e p r d n f r i u t y i i t d e a e t i t e n t u l o o P e r t f a n r e g i s e n n f H x m e i b i i n s o s o P p i d n s t g n J s r a s , i ) i g s S c r n s t o e c s O h t e d i K i n r r e N e e q e o e o ) e r m s u d n y n e v A a t i s n i P i r m c I n e s o e g m e d e p u n a l t r e a a t i d e n d d t e e d g r a t i o n a d d e d 2
9. Multilingual Support (New Feature)# All UI strings were wrapped with $this->t() / Drupal.t(), and a Japanese translation file was included.
g i t E N + + + + + + + + + + + + + + + h x e u i g g g g s w c g g g g s s s s j c c t D d b s i i i i r o i i i i r r r r s s o r o o _ t t t t t c f m t t t t c c c c s n a c c w i h h h h / i p h h h h / / / g / f n k k e n u u u u F l o u u u u F F S C i g i s e e b g b b b b o e s b b b b o o e o t i g l r r h _ _ _ _ r s e _ _ _ _ r r r n h t / a f - o f w w w w m : r w w w w m m t u h s t i c o i e e e e / . e e e e / / i r b u c i l o k l b b b b S j b b b b T A c o - b h o e m / e h h h h e s h h h h r u e l w - e n p s o o o o t o o o o o i t l e w m s o o o o o t n o o o o g W e b e a s ( k k k k i k k k k g T e r h b j e m . . . . n . . . . e r b / o h g a . o i r l p g l s l m r i h S o o i . y d n o i e s i e i o F g o t k o t p m i f u n r F n r b d o g o a - k h o l f o t k m o k v r u r e k t s - u i . i s i r s i a l m r T u t s b e y n . s m . c r e . F r s a t _ d m g m s . t e i p o i C t a w ) l . e i p a s e h r g o u t e : y n o h s . s p m g n s u b m u n p k y . . e t . s h l . s . m y p r r j . o y . y l m h S o s c o m y m l p e l s k l m l r l s . l v e s i r c c . h e p e . h m # # # # # # # # # # # # p p # # a # # h . D 1 E 1 T C T S J E M A p S S y J D r n r o a e S n a u t t m a o u r t p i m b r / t n t a a l p c p o r e g p C i u o # # t t a k a u y r g o d i S t a u u n e l t m e s e c S y l t W W s s # e r e p i r e f e r e o s 1 o s r i l h t i b r p d C e e 1 → i s p n d i o r g h k o i o n n i a p i e b o i g o f l s n t v s 4 t o r a t f r k g e o l l p f r i u n t c i i a g r k o i l i a r p r c k o n r ( e w n a g n o p o h → s a n i y a r s e g y u s n o u a e g t u e x s r l m r t n 2 p e ( i d t s t e t s a a e t e g a 3 o e o c t c a t t t n , s e p r d n f r i u t y i i t d e a e t i t e n t u l o o P e r t f a n r e g i s e n n f H x m e i b i i n s o s o P p i d n s t g n J s r a s , i ) i g s S c r n s t o e c s O h t e d i K i n r r e N e e q e o e o ) e r m s u d n y n e v A a t i s n i P i r m c I n e s o e g m e d e p u n a l t r e a a t i d e n d d t e e d g r a t i o n a d d e d 3
JavaScript-side strings also use Drupal.t(), allowing them to be managed through Drupal’s translation system.
Automatic Import of Translation Files# Drupal does not automatically import .po files from custom module translations/ directories. While you can specify interface translation server pattern in info.yml, this requires hardcoding the module’s installation path (modules/custom/ or modules/contrib/, etc.), which may not work in all environments.
Therefore, hook_locale_translation_projects_alter() was used to dynamically resolve the module’s path.
g i t E N + + + + + + + + + + + + + + + h x e u i g g g g s w c g g g g s s s s j c c t D d b s i i i i r o i i i i r r r r s s o r o o _ t t t t t c f m t t t t c c c c s n a c c w i h h h h / i p h h h h / / / g / f n k k e n u u u u F l o u u u u F F S C i g i s e e b g b b b b o e s b b b b o o e o t i g l r r h _ _ _ _ r s e _ _ _ _ r r r n h t / a f - o f w w w w m : r w w w w m m t u h s t i c o i e e e e / . e e e e / / i r b u c i l o k l b b b b S j b b b b T A c o - b h o e m / e h h h h e s h h h h r u e l w - e n p s o o o o t o o o o o i t l e w m s o o o o o t n o o o o g W e b e a s ( k k k k i k k k k g T e r h b j e m . . . . n . . . . e r b / o h g a . o i r l p g l s l m r i h S o o i . y d n o i e s i e i o F g o t k o t p m i f u n r F n r b d o g o a - k h o l f o t k m o k v r u r e k t s - u i . i s i r s i a l m r T u t s b e y n . s m . c r e . F r s a t _ d m g m s . t e i p o i C t a w ) l . e i p a s e h r g o u t e : y n o h s . s p m g n s u b m u n p k y . . e t . s h l . s . m y p r r j . o y . y l m h S o s c o m y m l p e l s k l m l r l s . l v e s i r c c . h e p e . h m # # # # # # # # # # # # p p # # a # # h . D 1 E 1 T C T S J E M A p S S y J D r n r o a e S n a u t t m a o u r t p i m b r / t n t a a l p c p o r e g p C i u o # # t t a k a u y r g o d i S t a u u n e l t m e s e c S y l t W W s s # e r e p i r e f e r e o s 1 o s r i l h t i b r p d C e e 1 → i s p n d i o r g h k o i o n n i a p i e b o i g o f l s n t v s 4 t o r a t f r k g e o l l p f r i u n t c i i a g r k o i l i a r p r c k o n r ( e w n a g n o p o h → s a n i y a r s e g y u s n o u a e g t u e x s r l m r t n 2 p e ( i d t s t e t s a a e t e g a 3 o e o c t c a t t t n , s e p r d n f r i u t y i i t d e a e t i t e n t u l o o P e r t f a n r e g i s e n n f H x m e i b i i n s o s o P p i d n s t g n J s r a s , i ) i g s S c r n s t o e c s O h t e d i K i n r r e N e e q e o e o ) e r m s u d n y n e v A a t i s n i P i r m c I n e s o e g m e d e p u n a l t r e a a t i d e n d d t e e d g r a t i o n a d d e d 4
This ensures that translation files are automatically imported regardless of which directory the module is placed in. Simply adding Japanese in Drupal’s admin panel will translate the UI.
10. Development Environment (Docker)# Added a Docker environment for local testing.
g i t E N + + + + + + + + + + + + + + + h x e u i g g g g s w c g g g g s s s s j c c t D d b s i i i i r o i i i i r r r r s s o r o o _ t t t t t c f m t t t t c c c c s n a c c w i h h h h / i p h h h h / / / g / f n k k e n u u u u F l o u u u u F F S C i g i s e e b g b b b b o e s b b b b o o e o t i g l r r h _ _ _ _ r s e _ _ _ _ r r r n h t / a f - o f w w w w m : r w w w w m m t u h s t i c o i e e e e / . e e e e / / i r b u c i l o k l b b b b S j b b b b T A c o - b h o e m / e h h h h e s h h h h r u e l w - e n p s o o o o t o o o o o i t l e w m s o o o o o t n o o o o g W e b e a s ( k k k k i k k k k g T e r h b j e m . . . . n . . . . e r b / o h g a . o i r l p g l s l m r i h S o o i . y d n o i e s i e i o F g o t k o t p m i f u n r F n r b d o g o a - k h o l f o t k m o k v r u r e k t s - u i . i s i r s i a l m r T u t s b e y n . s m . c r e . F r s a t _ d m g m s . t e i p o i C t a w ) l . e i p a s e h r g o u t e : y n o h s . s p m g n s u b m u n p k y . . e t . s h l . s . m y p r r j . o y . y l m h S o s c o m y m l p e l s k l m l r l s . l v e s i r c c . h e p e . h m # # # # # # # # # # # # p p # # a # # h . D 1 E 1 T C T S J E M A p S S y J D r n r o a e S n a u t t m a o u r t p i m b r / t n t a a l p c p o r e g p C i u o # # t t a k a u y r g o d i S t a u u n e l t m e s e c S y l t W W s s # e r e p i r e f e r e o s 1 o s r i l h t i b r p d C e e 1 → i s p n d i o r g h k o i o n n i a p i e b o i g o f l s n t v s 4 t o r a t f r k g e o l l p f r i u n t c i i a g r k o i l i a r p r c k o n r ( e w n a g n o p o h → s a n i y a r s e g y u s n o u a e g t u e x s r l m r t n 2 p e ( i d t s t e t s a a e t e g a 3 o e o c t c a t t t n , s e p r d n f r i u t y i i t d e a e t i t e n t u l o o P e r t f a n r e g i s e n n f H x m e i b i i n s o s o P p i d n s t g n J s r a s , i ) i g s S c r n s t o e c s O h t e d i K i n r r e N e e q e o e o ) e r m s u d n y n e v A a t i s n i P i r m c I n e s o e g m e d e p u n a l t r e a a t i d e n d d t e e d g r a t i o n a d d e d 5
The module directory is volume-mounted so that file changes on the host are immediately reflected in the container.
Appendix: Creating a Fine-grained PAT# This module uses GitHub’s Fine-grained Personal Access Token . It allows finer permission control than Classic PAT and can limit target repositories.
Creation Steps# Go to GitHub’s Settings > Developer settings > Personal access tokens > Fine-grained tokens Enter a descriptive Token name (e.g., drupal-webhook) Set the Expiration period Under Repository access , select Only select repositories and choose the target repositories Under Repository permissions , set the following: Permission Value Purpose Contents Read and write Sending repository_dispatch events (required) Actions Read Retrieving workflow execution status (optional)
Click Generate token and copy the generated token (starting with github_pat_) Notes# If Actions: Read is not granted, the status display feature will not work (triggering itself is still possible) The repo scope of Classic PAT has overly broad permissions, so Fine-grained PAT is recommended Be aware of token expiration. When the expiration approaches, you need to regenerate it from GitHub’s settings page Summary of Changes# Item Before After Supported versions Drupal 10 only Drupal 10 / 11 Screen layout Settings and trigger on same screen Separated into 3 tabs Permissions 1 permission (shared for settings and trigger) 2 levels for administrators / general users Token storage Set in #default_value (dangerous) Password field + Key module integration HTTP client new GuzzleHttp\Client()\Drupal::httpClient()Business logic Written directly in form class Extracted into service class Status display None GitHub API polling + JS rendering Status filtering None Filterable by workflow file name Auto trigger None Auto-execution on content save via Entity hooks Multilingual support None .po file (Japanese support)Configuration schema None github_webhook.schema.ymlcomposer.json None Drupal.org compliant Docker environment None Dockerfile + docker-compose.yml
General users who cannot access GitHub can now trigger builds and check status from Drupal’s admin panel, making it easier to leverage for workflow automation in headless CMS configurations.