Embed GitHub Gist in Hugo posts
Embed GitHub Gist in Hugo posts
I was approached by Divya Akula with a suggestion to use a link to GitHub for my script and I really bought in the idea because I used to do it on Wordpress. I started googling and trying out solutions which did not work and messed up my blog repo. Fortunately being MVP I have access to GitHub Copilot and asked Copilot with a simple prompt which gave me a simple straight forward looking solution.
Notes: This section is generated by GitHub Copilot using the prompt “how to embed github gist within hugo site” and I amended it with caveats and examples I used.
To create a custom shortcode in Hugo, follow these steps:
In your Hugo site’s directory, navigate to the layouts/shortcodes directory. If it doesn’t exist, create it.
Inside the shortcodes directory, create a new file with the name of your shortcode. For example, if you want to create a shortcode named gist, you would create a file named gist.html.
In this file, write the HTML that you want the shortcode to output. For a gist shortcode, you might write something like this:
<script src="https://gist.github.com/{{ .Get 0 }}/{{ .Get 1 }}.js"></script>
This code will create a script tag that links to the Gist specified by the parameters passed to the shortcode.
- Save the file. You can now use your custom shortcode in your markdown files like this: Replace username with your GitHub username and gist_id with the ID of the Gist you want to embed.
Example for gist url https://gist.github.com/reshmee011/c23123e5f1abedd1085876279ac17b7f
Clear-Host | |
$properties=@{SiteUrl='';SiteTitle='';ListTitle='';SensitivityLabel='';Type='';RelativeUrl='';ParentGroup='';MemberType='';MemberName='';MemberLoginName='';Roles='';}; | |
$dateTime = (Get-Date).toString("dd-MM-yyyy-hh-ss") | |
$invocation = (Get-Variable MyInvocation).Value | |
$directorypath = (Split-Path $invocation.MyCommand.Path) + "\Logs\" | |
$excludeLimitedAccess = $true; | |
$includeListsItems = $true; | |
$SiteCollectionUrl = Read-Host -Prompt "Enter site collection URL "; | |
$global:siteTitle= ""; | |
#Exclude certain libraries | |
$ExcludedLibraries = @("Form Templates", "Preservation Hold Library", "Site Assets", "Images", "Pages", "Settings", "Videos","Timesheet" | |
"Site Collection Documents", "Site Collection Images", "Style Library", "AppPages", "Apps for SharePoint", "Apps for Office") | |
$global:permissions =@(); | |
$global:sharingLinks = @(); | |
function Get-ListItems_WithUniquePermissions{ | |
param( | |
[Parameter(Mandatory)] | |
[Microsoft.SharePoint.Client.List]$List | |
) | |
$selectFields = "ID,HasUniqueRoleAssignments,FileRef,FileLeafRef,FileSystemObjectType" | |
$Url = $siteUrl + '/_api/web/lists/getbytitle(''' + $($list.Title) + ''')/items?$select=' + $($selectFields) | |
$nextLink = $Url | |
$listItems = @() | |
$Stoploop =$true | |
while($nextLink){ | |
do{ | |
try { | |
$response = invoke-pnpsprestmethod -Url $nextLink -Method Get | |
$Stoploop =$true | |
} | |
catch { | |
write-host "An error occured: $_ : Retrying" -ForegroundColor Red | |
$Stoploop =$true | |
Start-Sleep -Seconds 30 | |
} | |
} | |
While ($Stoploop -eq $false) | |
$listItems += $response.value | where-object{$_.HasUniqueRoleAssignments -eq $true} | |
if($response.'odata.nextlink'){ | |
$nextLink = $response.'odata.nextlink' | |
} else{ | |
$nextLink = $null | |
} | |
} | |
return $listItems | |
} | |
Function PermissionObject($_object,$_type,$_relativeUrl,$_siteUrl,$_siteTitle,$_listTitle,$_memberType,$_parentGroup,$_memberName,$_memberLoginName,$_roleDefinitionBindings,$_sensitivityLabel) | |
{ | |
$permission = New-Object -TypeName PSObject -Property $properties; | |
$permission.SiteUrl =$_siteUrl; | |
$permission.SiteTitle = $_siteTitle; | |
$permission.ListTitle = $_listTitle; | |
$permission.SensitivityLabel = $_sensitivityLabel; | |
$permission.Type = $_Type -eq 1 ? "Folder" : $_Type -eq 0 ? "File" : $_Type; | |
$permission.RelativeUrl = $_relativeUrl; | |
$permission.MemberType = $_memberType; | |
$permission.ParentGroup = $_parentGroup; | |
$permission.MemberName = $_memberName; | |
$permission.MemberLoginName = $_memberLoginName; | |
$permission.Roles = $_roleDefinitionBindings -join ","; | |
$global:permissions += $permission; | |
} | |
Function Extract-Guid ($inputString) { | |
$splitString = $inputString -split '\|' | |
return $splitString[2].TrimEnd('_o') | |
} | |
Function QueryUniquePermissionsByObject($_ctx,$_object,$_Type,$_RelativeUrl,$_siteUrl,$_siteTitle,$_listTitle) | |
{ | |
$roleAssignments = Get-PnPProperty -ClientObject $_object -Property RoleAssignments | |
switch ($_Type) { | |
0 { $sensitivityLabel = $_object.FieldValues["_DisplayName"] } | |
1 { $sensitivityLabel = $_object.FieldValues["_DisplayName"] } | |
"Site" { $sensitivityLabel = (Get-PnPSiteSensitivityLabel).displayname } | |
default { " " } | |
} | |
foreach($roleAssign in $roleAssignments){ | |
Get-PnPProperty -ClientObject $roleAssign -Property RoleDefinitionBindings,Member; | |
$PermissionLevels = $roleAssign.RoleDefinitionBindings | Select -ExpandProperty Name; | |
#Get all permission levels assigned (Excluding:Limited Access) | |
if($excludeLimitedAccess -eq $true){ | |
$PermissionLevels = ($PermissionLevels | Where { $_ -ne "Limited Access"}) -join "," | |
} | |
$Users = Get-PnPProperty -ClientObject ($roleAssign.Member) -Property Users -ErrorAction SilentlyContinue | |
#Get Access type | |
$AccessType = $roleAssign.RoleDefinitionBindings.Name | |
$MemberType = $roleAssign.Member.GetType().Name; | |
#Get the Principal Type: User, SP Group, AD Group | |
$PermissionType = $roleAssign.Member.PrincipalType | |
if( $_Type -eq 0){ | |
$sharingLinks = Get-PnPFileSharingLink -Identity $_object.FieldValues["FileRef"] | |
} | |
if( $_Type -eq 1){ | |
$sharingLinks = Get-PnPFolderSharingLink -Folder $_object.FieldValues["FileRef"] | |
} | |
If($PermissionLevels.Length -gt 0) { | |
$MemberType = $roleAssign.Member.GetType().Name; | |
#Sharing link is in the format SharingLinks.03012675-2057-4d1d-91e0-8e3b176edd94.OrganizationView.20d346d3-d359-453b-900c-633c1551ccaa | |
If ($roleAssign.Member.Title -like "SharingLinks*") | |
{ | |
if($sharingLinks){ | |
$sharingLinks | where-object {$roleAssign.Member.Title -match $_.Id } | ForEach-Object{ | |
If ($Users.Count -gt 0) | |
{ | |
ForEach ($User in $Users) | |
{ | |
PermissionObject $_object $_Type $_RelativeUrl $_siteUrl $_siteTitle $_listTitle "Sharing Links" $roleAssign.Member.LoginName $user.Title $User.LoginName $_.Link.Type $sensitivityLabel; | |
} | |
} | |
else { | |
PermissionObject $_object $_Type $_RelativeUrl $_siteUrl $_siteTitle $_listTitle "Sharing Links" $roleAssign.Member.LoginName $_.Link.Scope "" $_.Link.Type $sensitivityLabel; | |
} | |
} | |
} | |
} | |
ElseIf($MemberType -eq "Group" -or $MemberType -eq "User") | |
{ | |
$MemberName = $roleAssign.Member.Title; | |
$MemberLoginName = $roleAssign.Member.LoginName; | |
if($MemberType -eq "User") | |
{ | |
$ParentGroup = "NA"; | |
} | |
else | |
{ | |
$ParentGroup = $MemberName; | |
} | |
(PermissionObject $_object $_Type $_RelativeUrl $_siteUrl $_siteTitle $_listTitle $MemberType $ParentGroup $MemberName $MemberLoginName $PermissionLevels $sensitivityLabel); | |
} | |
if($_Type -eq "Site" -and $MemberType -eq "Group") | |
{ | |
$sensitivityLabel = (Get-PnPSiteSensitivityLabel).DisplayName | |
If($PermissionType -eq "SharePointGroup") { | |
#Get Group Members | |
$groupUsers = Get-PnPGroupMember -Identity $roleAssign.Member.LoginName | |
$groupUsers|foreach-object{ | |
if ($_.LoginName.StartsWith("c:0o.c|federateddirectoryclaimprovider|") -and $_.LoginName.EndsWith("_0")) { | |
$guid = Extract-Guid $_.LoginName | |
Get-PnPMicrosoft365GroupOwners -Identity $guid | ForEach-Object { | |
$user = $_ | |
(PermissionObject $_object "Site" $_RelativeUrl $_siteUrl $_siteTitle "" "GroupMember" $roleAssign.Member.LoginName $user.DisplayName $user.UserPrincipalName $PermissionLevels $sensitivityLabel); | |
} | |
} | |
elseif ($_.LoginName.StartsWith("c:0o.c|federateddirectoryclaimprovider|")) { | |
$guid = Extract-Guid $_.LoginName | |
Get-PnPMicrosoft365GroupMembers -Identity $guid | ForEach-Object { | |
$user = $_ | |
(PermissionObject $_object "Site" $_RelativeUrl $_siteUrl $_siteTitle "" "GroupMember" $roleAssign.Member.LoginName $user.DisplayName $user.UserPrincipalName $PermissionLevels $sensitivityLabel); | |
} | |
} | |
(PermissionObject $_object "Site" $_RelativeUrl $_siteUrl $_siteTitle "" "GroupMember" $roleAssign.Member.LoginName $_.Title $_.LoginName $PermissionLevels $sensitivityLabel); | |
} | |
} | |
} | |
} | |
} | |
} | |
Function QueryUniquePermissions($_web) | |
{ | |
##query list, files and items unique permissions | |
Write-Host "Querying web $($_web.Title)"; | |
$siteUrl = $_web.Url; | |
Write-Host $siteUrl -Foregroundcolor "Red"; | |
$global:siteTitle = $_web.Title; | |
$ll = Get-PnPList -Includes BaseType, Hidden, Title,HasUniqueRoleAssignments,RootFolder -Connection $siteconn | Where-Object {$_.Hidden -eq $False -and $_.Title -notin $ExcludedLibraries } #$_.BaseType -eq "DocumentLibrary" | |
Write-Host "Number of lists $($ll.Count)"; | |
QueryUniquePermissionsByObject $_web $_web "Site" "" $siteUrl $siteTitle ""; | |
foreach($list in $ll) | |
{ | |
$listUrl = $list.RootFolder.ServerRelativeUrl; | |
#Exclude internal system lists and check if it has unique permissions | |
if($list.Hidden -ne $True) | |
{ | |
Write-Host $list.Title -Foregroundcolor "Yellow"; | |
$listTitle = $list.Title; | |
#Check List Permissions | |
if($list.HasUniqueRoleAssignments -eq $True) | |
{ | |
$Type = $list.BaseType.ToString(); | |
QueryUniquePermissionsByObject $_web $list $Type $listUrl $siteUrl $siteTitle $listTitle; | |
} | |
if($includeListsItems){ | |
$collListItem = Get-ListItems_WithUniquePermissions -List $list | |
$count = $collListItem.Count | |
Write-Host "Number of items with unique permissions: $count within list $listTitle" | |
foreach($item in $collListItem) | |
{ | |
$Type = $item.FileSystemObjectType; | |
$fileUrl = $item.FileRef; | |
$i = Get-PnPListItem -List $list -Id $item.ID | |
QueryUniquePermissionsByObject $_web $i $Type $fileUrl $siteUrl $siteTitle $listTitle; | |
} | |
} | |
} | |
} | |
} | |
if(Test-Path $directorypath){ | |
Connect-PnPOnline -Url $SiteCollectionUrl -Interactive | |
#array storing permissions | |
$web = Get-PnPWeb | |
#root web , i.e. site collection level | |
QueryUniquePermissions($web); | |
Write-Host "Permission count: $($global:permissions.Count)"; | |
$exportFilePath = Join-Path -Path $directorypath -ChildPath $([string]::Concat($siteTitle,"-Permissions_",$dateTime,".csv")); | |
Write-Host "Export File Path is:" $exportFilePath | |
Write-Host "Number of lines exported is :" $global:permissions.Count | |
$global:permissions | Select-Object SiteUrl,SiteTitle,Type,SensitivityLabel,RelativeUrl,ListTitle,MemberType,MemberName,MemberLoginName,ParentGroup,Roles|Export-CSV -Path $exportFilePath -NoTypeInformation; | |
} | |
else{ | |
Write-Host "Invalid directory path:" $directorypath -ForegroundColor "Red"; | |
} |
When you build your Hugo site, the shortcode will be replaced with the HTML you specified in the shortcode file.
Caveats
Don’t update the theme layouts section
I did that and broke my theme.
11:51:20 PM: Starting to prepare the repo for build
11:51:21 PM: Failed during stage 'preparing repo': From https://github.com/reshmee011/m365blogs
* branch main -> FETCH_HEAD
12c52c1..89bd13c main -> origin/main
Fetching submodule Hugo/sites/M365Blog/themes/ananke
From https://github.com/theNewDynamic/gohugo-theme-ananke
a1a99cf..f34c219 master -> origin/master
fatal: remote error: upload-pack: not our ref a6af9573bf68429ff0eb1e66a0d9dd53d54654db
Errors during submodule fetch:
Hugo/sites/M365Blog/themes/ananke
: exit status 1
11:51:21 PM: Preparing Git Reference refs/heads/main
11:51:21 PM: Error pulling repository: From https://github.com/reshmee011/m365blogs
* branch main -> FETCH_HEAD
12c52c1..89bd13c main -> origin/main
Fetching submodule Hugo/sites/M365Blog/themes/ananke
From https://github.com/theNewDynamic/gohugo-theme-ananke
a1a99cf..f34c219 master -> origin/master
fatal: remote error: upload-pack: not our ref a6af9573bf68429ff0eb1e66a0d9dd53d54654db
Errors during submodule fetch:
Hugo/sites/M365Blog/themes/ananke
: exit status 1
11:51:21 PM: Failing build: Failed to prepare repo
I deleted and readded the theme to fix using
$ git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
However still had some issues building my site.
Potential errors
1:26:46 AM: Error: Error building site: failed to render pages: render of "home" failed: "/opt/build/repo/Hugo/sites/M365Blog/themes/ananke/layouts/index.html:48:23": execute of template failed: template: index.html:48:23: executing "main" at <.Site.GetPage>: can't evaluate field Site in type string
Total in 423 ms
1:26:46 AM:
1:26:46 AM: "build.command" failed
1:26:46 AM: ────────────────────────────────────────────────────────────────
1:26:46 AM:
1:26:46 AM: Error message
1:26:46 AM: Command failed with exit code 255: hugo --gc --minify -b $URL (https://ntl.fyi/exit-code-255)
1:26:46 AM:
1:26:46 AM: Error location
1:26:46 AM: In build.command from netlify.toml:
1:26:46 AM: hugo --gc --minify -b $URL
1:26:46 AM:
1:26:46 AM: Resolved config
1:26:46 AM: build:
1:26:46 AM: base: /opt/build/repo/Hugo/sites/M365Blog
1:26:46 AM: command: hugo --gc --minify -b $URL
1:26:46 AM: commandOrigin: config
1:26:46 AM: environment:
1:26:46 AM: - NODE_ENV
1:26:46 AM: - TZ
1:26:46 AM: - HUGO_VERSION
1:26:46 AM: - HUGO_ENV
1:26:46 AM: publish: /opt/build/repo/Hugo/sites/M365Blog/public
1:26:46 AM: publishOrigin: config
1:26:46 AM: Build failed due to a user error: Build script returned non-zero exit code: 2
1:26:46 AM: Failing build: Failed to build site
1:26:46 AM: Finished processing build request in 26.545s
Resolutions
Ultimately I reverted to a commit which worked using the git reset
git reset 12c52c1e031abf9fab7cdf077dd088644eb1e294
git push
I followed the instructions provided by github copilot under my site folder creating the layouts > shortcodes folder before creating the gist.html file and it worked.