Repository: gohugoio/hugo (language: go)

Issue:
Improve the Hugo snap package
## Background

The Hugo snap package does not include external dependencies required for certain Hugo features. Under [strict&nbsp;confinement](https://snapcraft.io/docs/snap-confinement) these dependencies must be bundled within the snap.

We have an [open request](https://forum.snapcraft.io/t/19892) for [classic&nbsp;confinement](https://snapcraft.io/docs/snap-confinement) which would allow us to remove the dependencies from the snap[^1], but this would:

1. Result in site building errors if the dependencies have not been manually installed
2. Force users to manually install the dependencies (a non-trivial task in some cases)

We should try to make strict confinement work as much as possible.

## Dependencies

External Dependency|v0.88.1|v0.101.0
:--|:--|:--
Asciidoctor|:x:|:heavy_check_mark:
Embedded Dart Sass|:x:|:heavy_check_mark:
Git|:heavy_check_mark:|:heavy_check_mark:
Go|:x:|:heavy_check_mark:
Node.js|:x:[^2]|:heavy_check_mark:
Pandoc|:x:[^3]|:heavy_check_mark:
rst2html|:x:|:heavy_check_mark:

[^1]: Removing bundled dependencies is not the primary motivation behind our request for classic confinement.
[^2]: Based on [this comment](https://github.com/gohugoio/hugo/pull/9029#issuecomment-950146281), the Node.js version is too old (v10) on some platforms. We should be bundling v12.20 at a minimum, preferably v16.
[^3]: The existing snapcraft.yaml includes a part for pandoc, but it is not working. "error while loading shared libraries: libatomic.so.1: cannot open shared object file: No such file or directory"

## Testing

1) Install the extended version of Hugo from the Snap Store.

```bash
sudo snap install hugo --channel=extended/stable
```

2) Clone this test repository, install the npm packages, and build the site.

```bash
git clone https://github.com/jmooring/hugo-feature-test
cd hugo-feature-test
npm ci
hugo server
```



Discussion / hints:
Hugo was granted classic confinement by the Snap Store today:
<https://forum.snapcraft.io/t/19892/16>

> classic confinement granted for hugo - this is now live (ie future uploads of hugo which specify confinement: classic will pass automated review)
>
> Note existing users of the snap will not be automatically upgraded to the classic version - so you should publish an update which is still strictly confined that prompts them to manually refresh to get the classic version (or you could perhaps use a separate track for classic…?)
>
> ie. users will need to manually run: snap refresh --classic hugo

I recommend that we defer this for now, and instead attempt to fix the known issues (see above) with strict confinement. If we can fix all of the missing dependencies, including Go, then I think the only reason to offer classic confinement would be access to the file system outside of $HOME. In this case, I think classic confinement should be provided as a separate track.
Ping on this, as it seems like long lasting problem that was never fixed. It also means there is no decent installation option for newer versions of hugo (apt has version 0.92)
@Letme Thank you for the heads up!  I didn't realize that Ubuntu kinetic (the future 22.10 release) has Hugo stuck on 0.92.2-1 when it is already 0.101.0-1 on Debian.  It seems to be due to one of the updated dependencies (aws-sdk-go) not being synchronized from Debian.  The Ubuntu developers have just been notified and will hopefully have the issue resolved soon so that you'll get Hugo v0.101.0 in Ubuntu 22.10.

Meanwhile, about this issue: It might be partially solved (Node.js and pandoc are _probably_ updated and working), but I'll need to find time to actually sit down, test, and work on any remaining issues.  Sorry for the delay!

Fix patch:
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 2e547980b1f..8bf0126c51f 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,5 +1,8 @@
 name: hugo
 version: git
+issues: https://github.com/gohugoio/hugo/issues
+source-code: https://github.com/gohugoio/hugo.git
+website: https://gohugo.io/
 summary: Fast and Flexible Static Site Generator
 description: |
   Hugo is a static HTML and CSS website generator written in Go. It is
@@ -21,17 +24,26 @@ plugs:
   etc-gitconfig:
     interface: system-files
     read:
-    - /etc/gitconfig
+      - /etc/gitconfig
   gitconfig:
     interface: personal-files
     read:
-    - $HOME/.gitconfig
-    - $HOME/.config/git/config
+      - $HOME/.gitconfig
+      - $HOME/.config/git/config
+
+environment:
+  HOME: $SNAP_REAL_HOME
+  GIT_EXEC_PATH: $SNAP/usr/lib/git-core
+  GOCACHE: $SNAP_USER_DATA/.cache/go-build
+  npm_config_cache: $SNAP_USER_DATA/.npm
+  npm_config_init_module: $SNAP_USER_DATA/.npm-init.js
+  npm_config_userconfig: $SNAP_USER_DATA/.npmrc
+  pandoc_datadir: $SNAP/usr/share/pandoc
+  PYTHONHOME: /usr:$SNAP/usr
+  RUBYLIB: $SNAP/usr/lib/ruby/vendor_ruby/2.7.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ruby/vendor_ruby/2.7.0:$SNAP/usr/lib/ruby/vendor_ruby:$SNAP/usr/lib/ruby/2.7.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ruby/2.7.0
 
 apps:
   hugo:
-    environment:
-      HOME: $SNAP_REAL_HOME
     command: bin/hugo
     completer: hugo-completion
     plugs:
@@ -46,28 +58,48 @@ parts:
     plugin: nil
     stage-packages:
       - git
-    organize:
-      usr/bin/: bin/
     prime:
-      - bin/git
+      - usr/bin/git
+      - usr/lib
+
+  go:
+    plugin: nil
+    stage-snaps:
+      - go/1.19/stable
+    prime:
+      - bin/go
+      - pkg/tool
+      - -pkg/tool/*
 
   hugo:
     plugin: nil
-    build-snaps: [go/1.19/stable]
     source: .
+    after:
+      - git
+      - go
     override-build: |
+      echo "\nStarting override-build:"
       set -ex
 
-      echo "\nStarting override-build:"
       export GOPATH=$(realpath ../go)
       export PATH=$GOPATH/bin:$PATH
 
+      echo " * Patch securityConfig.go to allow"
+      echo "   - GIT_EXEC_PATH and LD_LIBRARY_PATH to be passed to git"
+      echo "   - npm_config_{cache,init_module,userconfig} to be passed to npx"
+      echo "   - pandoc_datadir to be passed to pandoc"
+      echo "   - PYTHONHOME and SNAP to be passed to rst2html"
+      echo "   - RUBYLIB to be passed to asciidoctor"
+      sed -i '/OsEnv: NewWhitelist/s/)\$/|GIT_EXEC_PATH|LD_LIBRARY_PATH|npm_config_(cache|init_module|userconfig)|pandoc_datadir|PYTHONHOME|RUBYLIB|SNAP&/' config/security/securityConfig.go
+      git diff config/security/securityConfig.go
+
       echo " * SNAPCRAFT_IMAGE_INFO=${SNAPCRAFT_IMAGE_INFO=}"
       # Example: SNAPCRAFT_IMAGE_INFO='{"build_url": "https://launchpad.net/~gohugoio/+snap/hugo-extended-dev/+build/344022"}'
       export HUGO_BUILD_TAGS=""
       if echo $SNAPCRAFT_IMAGE_INFO | grep -q '/+snap/hugo-extended'; then
         HUGO_BUILD_TAGS="extended"
       fi
+
       echo " * Building hugo (HUGO_BUILD_TAGS=\"$HUGO_BUILD_TAGS\")..."
       go build -v -ldflags '-X github.com/gohugoio/hugo/common/hugo.vendorInfo=snap' -tags "$HUGO_BUILD_TAGS"
       ./hugo version
@@ -86,25 +118,53 @@ parts:
       strip --remove-section=.comment --remove-section=.note $SNAPCRAFT_PART_INSTALL/bin/hugo
       ls -l $SNAPCRAFT_PART_INSTALL/bin/hugo
 
+  asciidoctor:
+    plugin: nil
+    stage-packages:
+      - asciidoctor
+    override-build: |
+      set -ex
+      snapcraftctl build
+      sed -i '1s|#!/usr/bin/ruby|#!/usr/bin/env ruby|' $SNAPCRAFT_PART_INSTALL/usr/bin/asciidoctor
+
+  dart-sass-embedded:
+    plugin: nil
+    build-packages:
+      - curl
+    override-build: |
+      set -ex
+      snapcraftctl build
+      case "$SNAPCRAFT_TARGET_ARCH" in
+        amd64)  arch=x64    ;;
+        arm64)  arch=arm64  ;;
+        i386)   arch=ia32   ;;
+        *)      arch=""     ;;
+      esac
+      if [[ -n $arch ]]; then
+        url=$(curl -s https://api.github.com/repos/sass/dart-sass-embedded/releases/latest | awk -F\" "/browser_download_url.*-linux-${arch}.tar.gz/{print \$(NF-1)}")
+        curl -LO --retry-connrefused --retry 10 "$url"
+        tar xf sass_embedded-*-linux-$arch.tar.gz sass_embedded/dart-sass-embedded
+        install -d $SNAPCRAFT_PART_INSTALL/bin
+        cp -av sass_embedded/dart-sass-embedded $SNAPCRAFT_PART_INSTALL/bin/
+      fi
+
   node:
     plugin: nil
     stage-packages:
       - nodejs
-    organize:
-      usr/bin/: bin/
-      usr/lib/: lib/
-    prime:
-      - bin/node
-      - lib/*/lib*.so*
 
   pandoc:
     plugin: nil
     stage-packages:
-      - libatomic1
       - pandoc
-      - pandoc-data
+
+  rst2html:
+    plugin: nil
+    stage-packages:
+      - python3-docutils
+    override-build: |
+      set -ex
+      snapcraftctl build
+      sed -i "s|'/usr/share/docutils/'|os.path.expandvars('\$SNAP/usr/share/docutils/')|" $SNAPCRAFT_PART_INSTALL/usr/lib/python3/dist-packages/docutils/__init__.py
     organize:
-      usr/bin/: bin/
-      usr/lib/: lib/
-    prime:
-      - bin/pandoc
+      usr/share/docutils/scripts/python3: usr/bin


Test patch: